Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

245 рядки
6.3 KiB

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