taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

plural.go (5420B)


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