plural.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package internal
  2. import (
  3. "strconv"
  4. "github.com/kataras/iris/v12/context"
  5. "golang.org/x/text/feature/plural"
  6. "golang.org/x/text/message"
  7. "golang.org/x/text/message/catalog"
  8. )
  9. // PluralCounter if completes by an input argument of a message to render,
  10. // then the plural renderer will resolve the plural count
  11. // and any variables' counts. This is useful when the data is not a type of Map or integers.
  12. type PluralCounter interface {
  13. // PluralCount returns the plural count of the message.
  14. // If returns -1 then this is not a valid plural message.
  15. PluralCount() int
  16. // VarCount should return the variable count, based on the variable name.
  17. VarCount(name string) int
  18. }
  19. // PluralMessage holds the registered Form and the corresponding Renderer.
  20. // It is used on the `Message.AddPlural` method.
  21. type PluralMessage struct {
  22. Form PluralForm
  23. Renderer Renderer
  24. }
  25. type independentPluralRenderer struct {
  26. key string
  27. printer *message.Printer
  28. }
  29. func newIndependentPluralRenderer(c *Catalog, loc *Locale, key string, msgs ...catalog.Message) (Renderer, error) {
  30. builder := catalog.NewBuilder(catalog.Fallback(c.Locales[0].tag))
  31. if err := builder.Set(loc.tag, key, msgs...); err != nil {
  32. return nil, err
  33. }
  34. printer := message.NewPrinter(loc.tag, message.Catalog(builder))
  35. return &independentPluralRenderer{key, printer}, nil
  36. }
  37. func (m *independentPluralRenderer) Render(args ...interface{}) (string, error) {
  38. return m.printer.Sprintf(m.key, args...), nil
  39. }
  40. // A PluralFormDecoder should report and return whether
  41. // a specific "key" is a plural one. This function
  42. // can be implemented and set on the `Options` to customize
  43. // the plural forms and their behavior in general.
  44. //
  45. // See the `DefaultPluralFormDecoder` package-level
  46. // variable for the default implementation one.
  47. type PluralFormDecoder func(loc context.Locale, key string) (PluralForm, bool)
  48. // DefaultPluralFormDecoder is the default `PluralFormDecoder`.
  49. // Supprots "zero", "one", "two", "other", "=x", "<x", ">x".
  50. var DefaultPluralFormDecoder = func(_ context.Locale, key string) (PluralForm, bool) {
  51. if isDefaultPluralForm(key) {
  52. return pluralForm(key), true
  53. }
  54. return nil, false
  55. }
  56. func isDefaultPluralForm(s string) bool {
  57. switch s {
  58. case "zero", "one", "two", "other":
  59. return true
  60. default:
  61. if len(s) > 1 {
  62. ch := s[0]
  63. if ch == '=' || ch == '<' || ch == '>' {
  64. if isDigit(s[1]) {
  65. return true
  66. }
  67. }
  68. }
  69. return false
  70. }
  71. }
  72. // A PluralForm is responsible to decode
  73. // locale keys to plural forms and match plural forms
  74. // based on the given pluralCount.
  75. //
  76. // See `pluralForm` package-level type for a default implementation.
  77. type PluralForm interface {
  78. String() string
  79. // the string is a verified plural case's raw string value.
  80. // Field for priority on which order to register the plural cases.
  81. Less(next PluralForm) bool
  82. MatchPlural(pluralCount int) bool
  83. }
  84. type pluralForm string
  85. func (f pluralForm) String() string {
  86. return string(f)
  87. }
  88. func (f pluralForm) Less(next PluralForm) bool {
  89. form1 := f.String()
  90. form2 := next.String()
  91. // Order by
  92. // - equals,
  93. // - less than
  94. // - greater than
  95. // - "zero", "one", "two"
  96. // - rest is last "other".
  97. dig1, typ1, hasDig1 := formAtoi(form1)
  98. if typ1 == eq {
  99. return true
  100. }
  101. dig2, typ2, hasDig2 := formAtoi(form2)
  102. if typ2 == eq {
  103. return false
  104. }
  105. // digits smaller, number.
  106. if hasDig1 {
  107. return !hasDig2 || dig1 < dig2
  108. }
  109. if hasDig2 {
  110. return false
  111. }
  112. if form1 == "other" {
  113. return false // other go to last.
  114. }
  115. if form2 == "other" {
  116. return true
  117. }
  118. if form1 == "zero" {
  119. return true
  120. }
  121. if form2 == "zero" {
  122. return false
  123. }
  124. if form1 == "one" {
  125. return true
  126. }
  127. if form2 == "one" {
  128. return false
  129. }
  130. if form1 == "two" {
  131. return true
  132. }
  133. if form2 == "two" {
  134. return false
  135. }
  136. return false
  137. }
  138. func (f pluralForm) MatchPlural(pluralCount int) bool {
  139. switch f {
  140. case "other":
  141. return true
  142. case "=0", "zero":
  143. return pluralCount == 0
  144. case "=1", "one":
  145. return pluralCount == 1
  146. case "=2", "two":
  147. return pluralCount == 2
  148. default:
  149. // <5 or =5
  150. n, typ, ok := formAtoi(string(f))
  151. if !ok {
  152. return false
  153. }
  154. switch typ {
  155. case eq:
  156. return n == pluralCount
  157. case lt:
  158. return pluralCount < n
  159. case gt:
  160. return pluralCount > n
  161. default:
  162. return false
  163. }
  164. }
  165. }
  166. func makeSelectfVars(text string, vars []Var, insidePlural bool) ([]catalog.Message, []Var) {
  167. newVars := sortVars(text, vars)
  168. newVars = removeVarsDuplicates(newVars)
  169. msgs := selectfVars(newVars, insidePlural)
  170. return msgs, newVars
  171. }
  172. func selectfVars(vars []Var, insidePlural bool) []catalog.Message {
  173. msgs := make([]catalog.Message, 0, len(vars))
  174. for _, variable := range vars {
  175. argth := variable.Argth
  176. if insidePlural {
  177. argth++
  178. }
  179. msg := catalog.Var(variable.Name, plural.Selectf(argth, variable.Format, variable.Cases...))
  180. // fmt.Printf("%s:%d | cases | %#+v\n", variable.Name, variable.Argth, variable.Cases)
  181. msgs = append(msgs, msg)
  182. }
  183. return msgs
  184. }
  185. const (
  186. eq uint8 = iota + 1
  187. lt
  188. gt
  189. )
  190. func formType(ch byte) uint8 {
  191. switch ch {
  192. case '=':
  193. return eq
  194. case '<':
  195. return lt
  196. case '>':
  197. return gt
  198. }
  199. return 0
  200. }
  201. func formAtoi(form string) (int, uint8, bool) {
  202. if len(form) < 2 {
  203. return -1, 0, false
  204. }
  205. typ := formType(form[0])
  206. if typ == 0 {
  207. return -1, 0, false
  208. }
  209. dig, err := strconv.Atoi(form[1:])
  210. if err != nil {
  211. return -1, 0, false
  212. }
  213. return dig, typ, true
  214. }
  215. func isDigit(ch byte) bool {
  216. return '0' <= ch && ch <= '9'
  217. }