iris/i18n/internal/plural.go

262 lines
5.3 KiB
Go
Raw Normal View History

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'
}