github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/golang.org/x/text/feature/plural/message.go (about) 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. 4 5 package plural 6 7 import ( 8 "fmt" 9 "io" 10 "reflect" 11 "strconv" 12 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" 17 ) 18 19 // TODO: consider deleting this interface. Maybe VisibleDigits is always 20 // sufficient and practical. 21 22 // Interface is used for types that can determine their own plural form. 23 type Interface interface { 24 // PluralForm reports the plural form for the given language of the 25 // underlying value. It also returns the integer value. If the integer value 26 // is larger than fits in n, PluralForm may return a value modulo 27 // 10,000,000. 28 PluralForm(t language.Tag, scale int) (f Form, n int) 29 } 30 31 // Selectf returns the first case for which its selector is a match for the 32 // arg-th substitution argument to a formatting call, formatting it as indicated 33 // by format. 34 // 35 // The cases argument are pairs of selectors and messages. Selectors are of type 36 // string or Form. Messages are of type string or catalog.Message. A selector 37 // matches an argument if: 38 // - it is "other" or Other 39 // - it matches the plural form of the argument: "zero", "one", "two", "few", 40 // or "many", or the equivalent Form 41 // - it is of the form "=x" where x is an integer that matches the value of 42 // the argument. 43 // - it is of the form "<x" where x is an integer that is larger than the 44 // argument. 45 // 46 // The format argument determines the formatting parameters for which to 47 // determine the plural form. This is especially relevant for non-integer 48 // values. 49 // 50 // The format string may be "", in which case a best-effort attempt is made to 51 // find a reasonable representation on which to base the plural form. Examples 52 // of format strings are: 53 // - %.2f decimal with scale 2 54 // - %.2e scientific notation with precision 3 (scale + 1) 55 // - %d integer 56 func Selectf(arg int, format string, cases ...interface{}) catalog.Message { 57 var p parser 58 // Intercept the formatting parameters of format by doing a dummy print. 59 fmt.Fprintf(io.Discard, format, &p) 60 m := &message{arg, kindDefault, 0, cases} 61 switch p.verb { 62 case 'g': 63 m.kind = kindPrecision 64 m.scale = p.scale 65 case 'f': 66 m.kind = kindScale 67 m.scale = p.scale 68 case 'e': 69 m.kind = kindScientific 70 m.scale = p.scale 71 case 'd': 72 m.kind = kindScale 73 m.scale = 0 74 default: 75 // TODO: do we need to handle errors? 76 } 77 return m 78 } 79 80 type parser struct { 81 verb rune 82 scale int 83 } 84 85 func (p *parser) Format(s fmt.State, verb rune) { 86 p.verb = verb 87 p.scale = -1 88 if prec, ok := s.Precision(); ok { 89 p.scale = prec 90 } 91 } 92 93 type message struct { 94 arg int 95 kind int 96 scale int 97 cases []interface{} 98 } 99 100 const ( 101 // Start with non-ASCII to allow skipping values. 102 kindDefault = 0x80 + iota 103 kindScale // verb f, number of fraction digits follows 104 kindScientific // verb e, number of fraction digits follows 105 kindPrecision // verb g, number of significant digits follows 106 ) 107 108 var handle = catmsg.Register("golang.org/x/text/feature/plural:plural", execute) 109 110 func (m *message) Compile(e *catmsg.Encoder) error { 111 e.EncodeMessageType(handle) 112 113 e.EncodeUint(uint64(m.arg)) 114 115 e.EncodeUint(uint64(m.kind)) 116 if m.kind > kindDefault { 117 e.EncodeUint(uint64(m.scale)) 118 } 119 120 forms := validForms(cardinal, e.Language()) 121 122 for i := 0; i < len(m.cases); { 123 if err := compileSelector(e, forms, m.cases[i]); err != nil { 124 return err 125 } 126 if i++; i >= len(m.cases) { 127 return fmt.Errorf("plural: no message defined for selector %v", m.cases[i-1]) 128 } 129 var msg catalog.Message 130 switch x := m.cases[i].(type) { 131 case string: 132 msg = catalog.String(x) 133 case catalog.Message: 134 msg = x 135 default: 136 return fmt.Errorf("plural: message of type %T; must be string or catalog.Message", x) 137 } 138 if err := e.EncodeMessage(msg); err != nil { 139 return err 140 } 141 i++ 142 } 143 return nil 144 } 145 146 func compileSelector(e *catmsg.Encoder, valid []Form, selector interface{}) error { 147 form := Other 148 switch x := selector.(type) { 149 case string: 150 if x == "" { 151 return fmt.Errorf("plural: empty selector") 152 } 153 if c := x[0]; c == '=' || c == '<' { 154 val, err := strconv.ParseUint(x[1:], 10, 16) 155 if err != nil { 156 return fmt.Errorf("plural: invalid number in selector %q: %v", selector, err) 157 } 158 e.EncodeUint(uint64(c)) 159 e.EncodeUint(val) 160 return nil 161 } 162 var ok bool 163 form, ok = countMap[x] 164 if !ok { 165 return fmt.Errorf("plural: invalid plural form %q", selector) 166 } 167 case Form: 168 form = x 169 default: 170 return fmt.Errorf("plural: selector of type %T; want string or Form", selector) 171 } 172 173 ok := false 174 for _, f := range valid { 175 if f == form { 176 ok = true 177 break 178 } 179 } 180 if !ok { 181 return fmt.Errorf("plural: form %q not supported for language %q", selector, e.Language()) 182 } 183 e.EncodeUint(uint64(form)) 184 return nil 185 } 186 187 func execute(d *catmsg.Decoder) bool { 188 lang := d.Language() 189 argN := int(d.DecodeUint()) 190 kind := int(d.DecodeUint()) 191 scale := -1 // default 192 if kind > kindDefault { 193 scale = int(d.DecodeUint()) 194 } 195 form := Other 196 n := -1 197 if arg := d.Arg(argN); arg == nil { 198 // Default to Other. 199 } else if x, ok := arg.(number.VisibleDigits); ok { 200 d := x.Digits(nil, lang, scale) 201 form, n = cardinal.matchDisplayDigits(lang, &d) 202 } else if x, ok := arg.(Interface); ok { 203 // This covers lists and formatters from the number package. 204 form, n = x.PluralForm(lang, scale) 205 } else { 206 var f number.Formatter 207 switch kind { 208 case kindScale: 209 f.InitDecimal(lang) 210 f.SetScale(scale) 211 case kindScientific: 212 f.InitScientific(lang) 213 f.SetScale(scale) 214 case kindPrecision: 215 f.InitDecimal(lang) 216 f.SetPrecision(scale) 217 case kindDefault: 218 // sensible default 219 f.InitDecimal(lang) 220 if k := reflect.TypeOf(arg).Kind(); reflect.Int <= k && k <= reflect.Uintptr { 221 f.SetScale(0) 222 } else { 223 f.SetScale(2) 224 } 225 } 226 var dec number.Decimal // TODO: buffer in Printer 227 dec.Convert(f.RoundingContext, arg) 228 v := number.FormatDigits(&dec, f.RoundingContext) 229 if !v.NaN && !v.Inf { 230 form, n = cardinal.matchDisplayDigits(d.Language(), &v) 231 } 232 } 233 for !d.Done() { 234 f := d.DecodeUint() 235 if (f == '=' && n == int(d.DecodeUint())) || 236 (f == '<' && 0 <= n && n < int(d.DecodeUint())) || 237 form == Form(f) || 238 Other == Form(f) { 239 return d.ExecuteMessage() 240 } 241 d.SkipMessage() 242 } 243 return false 244 }