123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- package internal
- import (
- "strconv"
- "github.com/kataras/iris/v12/context"
- "golang.org/x/text/feature/plural"
- "golang.org/x/text/message"
- "golang.org/x/text/message/catalog"
- )
- // PluralCounter if completes by an input argument of a message to render,
- // then the plural renderer will resolve the plural count
- // and any variables' counts. This is useful when the data is not a type of Map or integers.
- type PluralCounter interface {
- // PluralCount returns the plural count of the message.
- // If returns -1 then this is not a valid plural message.
- PluralCount() int
- // VarCount should return the variable count, based on the variable name.
- VarCount(name string) int
- }
- // PluralMessage holds the registered Form and the corresponding Renderer.
- // It is used on the `Message.AddPlural` method.
- type PluralMessage struct {
- Form PluralForm
- Renderer Renderer
- }
- type independentPluralRenderer struct {
- key string
- printer *message.Printer
- }
- func newIndependentPluralRenderer(c *Catalog, loc *Locale, key string, msgs ...catalog.Message) (Renderer, error) {
- builder := catalog.NewBuilder(catalog.Fallback(c.Locales[0].tag))
- if err := builder.Set(loc.tag, key, msgs...); err != nil {
- return nil, err
- }
- printer := message.NewPrinter(loc.tag, message.Catalog(builder))
- return &independentPluralRenderer{key, printer}, nil
- }
- func (m *independentPluralRenderer) Render(args ...interface{}) (string, error) {
- return m.printer.Sprintf(m.key, args...), nil
- }
- // A PluralFormDecoder should report and return whether
- // a specific "key" is a plural one. This function
- // can be implemented and set on the `Options` to customize
- // the plural forms and their behavior in general.
- //
- // See the `DefaultPluralFormDecoder` package-level
- // variable for the default implementation one.
- type PluralFormDecoder func(loc context.Locale, key string) (PluralForm, bool)
- // DefaultPluralFormDecoder is the default `PluralFormDecoder`.
- // Supprots "zero", "one", "two", "other", "=x", "<x", ">x".
- var DefaultPluralFormDecoder = func(_ context.Locale, key string) (PluralForm, bool) {
- if isDefaultPluralForm(key) {
- return pluralForm(key), true
- }
- return nil, false
- }
- func isDefaultPluralForm(s string) bool {
- switch s {
- case "zero", "one", "two", "other":
- return true
- default:
- if len(s) > 1 {
- ch := s[0]
- if ch == '=' || ch == '<' || ch == '>' {
- if isDigit(s[1]) {
- return true
- }
- }
- }
- return false
- }
- }
- // A PluralForm is responsible to decode
- // locale keys to plural forms and match plural forms
- // based on the given pluralCount.
- //
- // See `pluralForm` package-level type for a default implementation.
- type PluralForm interface {
- String() string
- // the string is a verified plural case's raw string value.
- // Field for priority on which order to register the plural cases.
- Less(next PluralForm) bool
- MatchPlural(pluralCount int) bool
- }
- type pluralForm string
- func (f pluralForm) String() string {
- return string(f)
- }
- func (f pluralForm) Less(next PluralForm) bool {
- form1 := f.String()
- form2 := next.String()
- // Order by
- // - equals,
- // - less than
- // - greater than
- // - "zero", "one", "two"
- // - rest is last "other".
- dig1, typ1, hasDig1 := formAtoi(form1)
- if typ1 == eq {
- return true
- }
- dig2, typ2, hasDig2 := formAtoi(form2)
- if typ2 == eq {
- return false
- }
- // digits smaller, number.
- if hasDig1 {
- return !hasDig2 || dig1 < dig2
- }
- if hasDig2 {
- return false
- }
- if form1 == "other" {
- return false // other go to last.
- }
- if form2 == "other" {
- return true
- }
- if form1 == "zero" {
- return true
- }
- if form2 == "zero" {
- return false
- }
- if form1 == "one" {
- return true
- }
- if form2 == "one" {
- return false
- }
- if form1 == "two" {
- return true
- }
- if form2 == "two" {
- return false
- }
- return false
- }
- func (f pluralForm) MatchPlural(pluralCount int) bool {
- switch f {
- case "other":
- return true
- case "=0", "zero":
- return pluralCount == 0
- case "=1", "one":
- return pluralCount == 1
- case "=2", "two":
- return pluralCount == 2
- default:
- // <5 or =5
- n, typ, ok := formAtoi(string(f))
- if !ok {
- return false
- }
- switch typ {
- case eq:
- return n == pluralCount
- case lt:
- return pluralCount < n
- case gt:
- return pluralCount > n
- default:
- return false
- }
- }
- }
- func makeSelectfVars(text string, vars []Var, insidePlural bool) ([]catalog.Message, []Var) {
- newVars := sortVars(text, vars)
- newVars = removeVarsDuplicates(newVars)
- msgs := selectfVars(newVars, insidePlural)
- return msgs, newVars
- }
- func selectfVars(vars []Var, insidePlural bool) []catalog.Message {
- msgs := make([]catalog.Message, 0, len(vars))
- for _, variable := range vars {
- argth := variable.Argth
- if insidePlural {
- argth++
- }
- msg := catalog.Var(variable.Name, plural.Selectf(argth, variable.Format, variable.Cases...))
- // fmt.Printf("%s:%d | cases | %#+v\n", variable.Name, variable.Argth, variable.Cases)
- msgs = append(msgs, msg)
- }
- return msgs
- }
- const (
- eq uint8 = iota + 1
- lt
- gt
- )
- func formType(ch byte) uint8 {
- switch ch {
- case '=':
- return eq
- case '<':
- return lt
- case '>':
- return gt
- }
- return 0
- }
- func formAtoi(form string) (int, uint8, bool) {
- if len(form) < 2 {
- return -1, 0, false
- }
- typ := formType(form[0])
- if typ == 0 {
- return -1, 0, false
- }
- dig, err := strconv.Atoi(form[1:])
- if err != nil {
- return -1, 0, false
- }
- return dig, typ, true
- }
- func isDigit(ch byte) bool {
- return '0' <= ch && ch <= '9'
- }
|