summaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/text/message/message.go
blob: 91a972642143203b139a60381d1f9bfadae037a2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package message // import "golang.org/x/text/message"

import (
	"io"
	"os"

	// Include features to facilitate generated catalogs.
	_ "golang.org/x/text/feature/plural"

	"golang.org/x/text/internal/number"
	"golang.org/x/text/language"
	"golang.org/x/text/message/catalog"
)

// A Printer implements language-specific formatted I/O analogous to the fmt
// package.
type Printer struct {
	// the language
	tag language.Tag

	toDecimal    number.Formatter
	toScientific number.Formatter

	cat catalog.Catalog
}

type options struct {
	cat catalog.Catalog
	// TODO:
	// - allow %s to print integers in written form (tables are likely too large
	//   to enable this by default).
	// - list behavior
	//
}

// An Option defines an option of a Printer.
type Option func(o *options)

// Catalog defines the catalog to be used.
func Catalog(c catalog.Catalog) Option {
	return func(o *options) { o.cat = c }
}

// NewPrinter returns a Printer that formats messages tailored to language t.
func NewPrinter(t language.Tag, opts ...Option) *Printer {
	options := &options{
		cat: DefaultCatalog,
	}
	for _, o := range opts {
		o(options)
	}
	p := &Printer{
		tag: t,
		cat: options.cat,
	}
	p.toDecimal.InitDecimal(t)
	p.toScientific.InitScientific(t)
	return p
}

// Sprint is like fmt.Sprint, but using language-specific formatting.
func (p *Printer) Sprint(a ...interface{}) string {
	pp := newPrinter(p)
	pp.doPrint(a)
	s := pp.String()
	pp.free()
	return s
}

// Fprint is like fmt.Fprint, but using language-specific formatting.
func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
	pp := newPrinter(p)
	pp.doPrint(a)
	n64, err := io.Copy(w, &pp.Buffer)
	pp.free()
	return int(n64), err
}

// Print is like fmt.Print, but using language-specific formatting.
func (p *Printer) Print(a ...interface{}) (n int, err error) {
	return p.Fprint(os.Stdout, a...)
}

// Sprintln is like fmt.Sprintln, but using language-specific formatting.
func (p *Printer) Sprintln(a ...interface{}) string {
	pp := newPrinter(p)
	pp.doPrintln(a)
	s := pp.String()
	pp.free()
	return s
}

// Fprintln is like fmt.Fprintln, but using language-specific formatting.
func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
	pp := newPrinter(p)
	pp.doPrintln(a)
	n64, err := io.Copy(w, &pp.Buffer)
	pp.free()
	return int(n64), err
}

// Println is like fmt.Println, but using language-specific formatting.
func (p *Printer) Println(a ...interface{}) (n int, err error) {
	return p.Fprintln(os.Stdout, a...)
}

// Sprintf is like fmt.Sprintf, but using language-specific formatting.
func (p *Printer) Sprintf(key Reference, a ...interface{}) string {
	pp := newPrinter(p)
	lookupAndFormat(pp, key, a)
	s := pp.String()
	pp.free()
	return s
}

// Fprintf is like fmt.Fprintf, but using language-specific formatting.
func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error) {
	pp := newPrinter(p)
	lookupAndFormat(pp, key, a)
	n, err = w.Write(pp.Bytes())
	pp.free()
	return n, err

}

// Printf is like fmt.Printf, but using language-specific formatting.
func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) {
	pp := newPrinter(p)
	lookupAndFormat(pp, key, a)
	n, err = os.Stdout.Write(pp.Bytes())
	pp.free()
	return n, err
}

func lookupAndFormat(p *printer, r Reference, a []interface{}) {
	p.fmt.Reset(a)
	switch v := r.(type) {
	case string:
		if p.catContext.Execute(v) == catalog.ErrNotFound {
			p.Render(v)
			return
		}
	case key:
		if p.catContext.Execute(v.id) == catalog.ErrNotFound &&
			p.catContext.Execute(v.fallback) == catalog.ErrNotFound {
			p.Render(v.fallback)
			return
		}
	default:
		panic("key argument is not a Reference")
	}
}

type rawPrinter struct {
	p *printer
}

func (p rawPrinter) Render(msg string)     { p.p.WriteString(msg) }
func (p rawPrinter) Arg(i int) interface{} { return nil }

// Arg implements catmsg.Renderer.
func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool
	i--
	if uint(i) < uint(len(p.fmt.Args)) {
		return p.fmt.Args[i]
	}
	return nil
}

// Render implements catmsg.Renderer.
func (p *printer) Render(msg string) {
	p.doPrintf(msg)
}

// A Reference is a string or a message reference.
type Reference interface {
	// TODO: also allow []string
}

// Key creates a message Reference for a message where the given id is used for
// message lookup and the fallback is returned when no matches are found.
func Key(id string, fallback string) Reference {
	return key{id, fallback}
}

type key struct {
	id, fallback string
}