1 // Copyright 2017 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.
13 "golang.org/x/text/internal/catmsg"
14 "golang.org/x/text/internal/number"
15 "golang.org/x/text/language"
16 "golang.org/x/text/message/catalog"
19 // Interface is used for types that can determine their own plural form.
20 type Interface interface {
21 // PluralForm reports the plural form for the given language of the
22 // underlying value. It also returns the integer value. If the integer value
23 // is larger than fits in n, PluralForm may return a value modulo
25 PluralForm(t language.Tag, scale int) (f Form, n int)
28 // Selectf returns the first case for which its selector is a match for the
29 // arg-th substitution argument to a formatting call, formatting it as indicated
32 // The cases argument are pairs of selectors and messages. Selectors are of type
33 // string or Form. Messages are of type string or catalog.Message. A selector
34 // matches an argument if:
35 // - it is "other" or Other
36 // - it matches the plural form of the argument: "zero", "one", "two", "few",
37 // or "many", or the equivalent Form
38 // - it is of the form "=x" where x is an integer that matches the value of
40 // - it is of the form "<x" where x is an integer that is larger than the
43 // The format argument determines the formatting parameters for which to
44 // determine the plural form. This is especially relevant for non-integer
47 // The format string may be "", in which case a best-effort attempt is made to
48 // find a reasonable representation on which to base the plural form. Examples
49 // of format strings are:
50 // - %.2f decimal with scale 2
51 // - %.2e scientific notation with precision 3 (scale + 1)
53 func Selectf(arg int, format string, cases ...interface{}) catalog.Message {
55 // Intercept the formatting parameters of format by doing a dummy print.
56 fmt.Fprintf(ioutil.Discard, format, &p)
57 m := &message{arg, kindDefault, 0, cases}
60 m.kind = kindPrecision
66 m.kind = kindScientific
72 // TODO: do we need to handle errors?
82 func (p *parser) Format(s fmt.State, verb rune) {
85 if prec, ok := s.Precision(); ok {
98 // Start with non-ASCII to allow skipping values.
99 kindDefault = 0x80 + iota
100 kindScale // verb f, number of fraction digits follows
101 kindScientific // verb e, number of fraction digits follows
102 kindPrecision // verb g, number of significant digits follows
105 var handle = catmsg.Register("golang.org/x/text/feature/plural:plural", execute)
107 func (m *message) Compile(e *catmsg.Encoder) error {
108 e.EncodeMessageType(handle)
110 e.EncodeUint(uint64(m.arg))
112 e.EncodeUint(uint64(m.kind))
113 if m.kind > kindDefault {
114 e.EncodeUint(uint64(m.scale))
117 forms := validForms(cardinal, e.Language())
119 for i := 0; i < len(m.cases); {
120 if err := compileSelector(e, forms, m.cases[i]); err != nil {
123 if i++; i >= len(m.cases) {
124 return fmt.Errorf("plural: no message defined for selector %v", m.cases[i-1])
126 var msg catalog.Message
127 switch x := m.cases[i].(type) {
129 msg = catalog.String(x)
130 case catalog.Message:
133 return fmt.Errorf("plural: message of type %T; must be string or catalog.Message", x)
135 if err := e.EncodeMessage(msg); err != nil {
143 func compileSelector(e *catmsg.Encoder, valid []Form, selector interface{}) error {
145 switch x := selector.(type) {
148 return fmt.Errorf("plural: empty selector")
150 if c := x[0]; c == '=' || c == '<' {
151 val, err := strconv.ParseUint(x[1:], 10, 16)
153 return fmt.Errorf("plural: invalid number in selector %q: %v", selector, err)
155 e.EncodeUint(uint64(c))
160 form, ok = countMap[x]
162 return fmt.Errorf("plural: invalid plural form %q", selector)
167 return fmt.Errorf("plural: selector of type %T; want string or Form", selector)
171 for _, f := range valid {
178 return fmt.Errorf("plural: form %q not supported for language %q", selector, e.Language())
180 e.EncodeUint(uint64(form))
184 func execute(d *catmsg.Decoder) bool {
186 argN := int(d.DecodeUint())
187 kind := int(d.DecodeUint())
188 scale := -1 // default
189 if kind > kindDefault {
190 scale = int(d.DecodeUint())
194 if arg := d.Arg(argN); arg == nil {
196 } else if x, ok := arg.(Interface); ok {
197 // This covers lists and formatters from the number package.
198 form, n = x.PluralForm(lang, scale)
200 var f number.Formatter
206 f.InitScientific(lang)
210 f.SetPrecision(scale)
214 if k := reflect.TypeOf(arg).Kind(); reflect.Int <= k && k <= reflect.Uintptr {
220 var dec number.Decimal // TODO: buffer in Printer
221 dec.Convert(f.RoundingContext, arg)
222 v := number.FormatDigits(&dec, f.RoundingContext)
223 if !v.NaN && !v.Inf {
224 form, n = cardinal.matchDisplayDigits(d.Language(), &v)
229 if (f == '=' && n == int(d.DecodeUint())) ||
230 (f == '<' && 0 <= n && n < int(d.DecodeUint())) ||
233 return d.ExecuteMessage()