1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package message // import "golang.org/x/text/message"
6
7import (
8	"io"
9	"os"
10
11	// Include features to facilitate generated catalogs.
12	_ "golang.org/x/text/feature/plural"
13
14	"golang.org/x/text/internal/number"
15	"golang.org/x/text/language"
16	"golang.org/x/text/message/catalog"
17)
18
19// A Printer implements language-specific formatted I/O analogous to the fmt
20// package.
21type Printer struct {
22	// the language
23	tag language.Tag
24
25	toDecimal    number.Formatter
26	toScientific number.Formatter
27
28	cat catalog.Catalog
29}
30
31type options struct {
32	cat catalog.Catalog
33	// TODO:
34	// - allow %s to print integers in written form (tables are likely too large
35	//   to enable this by default).
36	// - list behavior
37	//
38}
39
40// An Option defines an option of a Printer.
41type Option func(o *options)
42
43// Catalog defines the catalog to be used.
44func Catalog(c catalog.Catalog) Option {
45	return func(o *options) { o.cat = c }
46}
47
48// NewPrinter returns a Printer that formats messages tailored to language t.
49func NewPrinter(t language.Tag, opts ...Option) *Printer {
50	options := &options{
51		cat: DefaultCatalog,
52	}
53	for _, o := range opts {
54		o(options)
55	}
56	p := &Printer{
57		tag: t,
58		cat: options.cat,
59	}
60	p.toDecimal.InitDecimal(t)
61	p.toScientific.InitScientific(t)
62	return p
63}
64
65// Sprint is like fmt.Sprint, but using language-specific formatting.
66func (p *Printer) Sprint(a ...interface{}) string {
67	pp := newPrinter(p)
68	pp.doPrint(a)
69	s := pp.String()
70	pp.free()
71	return s
72}
73
74// Fprint is like fmt.Fprint, but using language-specific formatting.
75func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
76	pp := newPrinter(p)
77	pp.doPrint(a)
78	n64, err := io.Copy(w, &pp.Buffer)
79	pp.free()
80	return int(n64), err
81}
82
83// Print is like fmt.Print, but using language-specific formatting.
84func (p *Printer) Print(a ...interface{}) (n int, err error) {
85	return p.Fprint(os.Stdout, a...)
86}
87
88// Sprintln is like fmt.Sprintln, but using language-specific formatting.
89func (p *Printer) Sprintln(a ...interface{}) string {
90	pp := newPrinter(p)
91	pp.doPrintln(a)
92	s := pp.String()
93	pp.free()
94	return s
95}
96
97// Fprintln is like fmt.Fprintln, but using language-specific formatting.
98func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
99	pp := newPrinter(p)
100	pp.doPrintln(a)
101	n64, err := io.Copy(w, &pp.Buffer)
102	pp.free()
103	return int(n64), err
104}
105
106// Println is like fmt.Println, but using language-specific formatting.
107func (p *Printer) Println(a ...interface{}) (n int, err error) {
108	return p.Fprintln(os.Stdout, a...)
109}
110
111// Sprintf is like fmt.Sprintf, but using language-specific formatting.
112func (p *Printer) Sprintf(key Reference, a ...interface{}) string {
113	pp := newPrinter(p)
114	lookupAndFormat(pp, key, a)
115	s := pp.String()
116	pp.free()
117	return s
118}
119
120// Fprintf is like fmt.Fprintf, but using language-specific formatting.
121func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error) {
122	pp := newPrinter(p)
123	lookupAndFormat(pp, key, a)
124	n, err = w.Write(pp.Bytes())
125	pp.free()
126	return n, err
127
128}
129
130// Printf is like fmt.Printf, but using language-specific formatting.
131func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) {
132	pp := newPrinter(p)
133	lookupAndFormat(pp, key, a)
134	n, err = os.Stdout.Write(pp.Bytes())
135	pp.free()
136	return n, err
137}
138
139func lookupAndFormat(p *printer, r Reference, a []interface{}) {
140	p.fmt.Reset(a)
141	var id, msg string
142	switch v := r.(type) {
143	case string:
144		id, msg = v, v
145	case key:
146		id, msg = v.id, v.fallback
147	default:
148		panic("key argument is not a Reference")
149	}
150
151	if p.catContext.Execute(id) == catalog.ErrNotFound {
152		if p.catContext.Execute(msg) == catalog.ErrNotFound {
153			p.Render(msg)
154			return
155		}
156	}
157}
158
159// Arg implements catmsg.Renderer.
160func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool
161	i--
162	if uint(i) < uint(len(p.fmt.Args)) {
163		return p.fmt.Args[i]
164	}
165	return nil
166}
167
168// Render implements catmsg.Renderer.
169func (p *printer) Render(msg string) {
170	p.doPrintf(msg)
171}
172
173// A Reference is a string or a message reference.
174type Reference interface {
175	// TODO: also allow []string
176}
177
178// Key creates a message Reference for a message where the given id is used for
179// message lookup and the fallback is returned when no matches are found.
180func Key(id string, fallback string) Reference {
181	return key{id, fallback}
182}
183
184type key struct {
185	id, fallback string
186}
187