mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
262 lines
5.3 KiB
Go
262 lines
5.3 KiB
Go
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'
|
|
}
|