iris/i18n/internal/locale.go
2022-06-17 22:03:18 +03:00

175 lines
4.0 KiB
Go

package internal
import (
"fmt"
"text/template"
"github.com/kataras/iris/v12/context"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
)
// Locale is the default Locale.
// Created by Catalog.
// One Locale maps to one registered and loaded language.
// Stores the translation variables and most importantly, the Messages (keys and their renderers).
type Locale struct {
// The index of the language registered by the user, starting from zero.
index int
tag language.Tag
// ID is the tag.String().
ID string
// Options given by the Catalog
Options Options
// Fields set by Catalog.
FuncMap template.FuncMap
Printer *message.Printer
//
// Fields set by this Load method.
Messages map[string]Renderer
Vars []Var // shared per-locale variables.
}
// Ensures that the Locale completes the context.Locale interface.
var _ context.Locale = (*Locale)(nil)
// Load sets the translation messages based on the Catalog's key values.
func (loc *Locale) Load(c *Catalog, keyValues Map) error {
return loc.setMap(c, "", keyValues)
}
func (loc *Locale) setMap(c *Catalog, key string, keyValues Map) error {
// unique locals or the shared ones.
isRoot := key == ""
vars := getVars(loc, VarsKey, keyValues)
if isRoot {
loc.Vars = vars
} else {
vars = removeVarsDuplicates(append(vars, loc.Vars...))
}
for k, v := range keyValues {
form, isPlural := loc.Options.PluralFormDecoder(loc, k)
if isPlural {
k = key
} else if !isRoot {
k = key + "." + k
}
switch value := v.(type) {
case string:
if err := loc.setString(c, k, value, vars, form); err != nil {
return fmt.Errorf("%s:%s parse string: %w", loc.ID, key, err)
}
case Map:
// fmt.Printf("%s is map\n", fullKey)
if err := loc.setMap(c, k, value); err != nil {
return fmt.Errorf("%s:%s parse map: %w", loc.ID, key, err)
}
default:
return fmt.Errorf("%s:%s unexpected type of %T as value", loc.ID, key, value)
}
}
return nil
}
func (loc *Locale) setString(c *Catalog, key string, value string, vars []Var, form PluralForm) (err error) {
isPlural := form != nil
// fmt.Printf("setStringVars: %s=%s\n", key, value)
msgs, vars := makeSelectfVars(value, vars, isPlural)
msgs = append(msgs, catalog.String(value))
m := &Message{
Locale: loc,
Key: key,
Value: value,
Vars: vars,
Plural: isPlural,
}
var (
renderer, pluralRenderer Renderer = m, m
)
if stringIsTemplateValue(value, loc.Options.Left, loc.Options.Right) {
t, err := NewTemplate(c, m)
if err != nil {
return err
}
pluralRenderer = t
if !isPlural {
renderer = t
}
} else {
if isPlural {
pluralRenderer, err = newIndependentPluralRenderer(c, loc, key, msgs...)
if err != nil {
return fmt.Errorf("<%s = %s>: %w", key, value, err)
}
} else if err = c.Set(loc.tag, key, msgs...); err != nil {
// let's make normal keys direct fire:
// renderer = &simpleRenderer{key, loc.Printer}
return fmt.Errorf("<%s = %s>: %w", key, value, err)
}
}
if isPlural {
if existingMsg, ok := loc.Messages[key]; ok {
if msg, ok := existingMsg.(*Message); ok {
msg.AddPlural(form, pluralRenderer)
return
}
}
m.AddPlural(form, pluralRenderer)
}
loc.Messages[key] = renderer
return
}
/* context.Locale interface */
// Index returns the current locale index from the languages list.
func (loc *Locale) Index() int {
return loc.index
}
// Tag returns the full language Tag attached to this Locale,
// it should be unique across different Locales.
func (loc *Locale) Tag() *language.Tag {
return &loc.tag
}
// Language should return the exact languagecode of this `Locale`
// that the user provided on `New` function.
//
// Same as `Tag().String()` but it's static.
func (loc *Locale) Language() string {
return loc.ID
}
// GetMessage should return translated text based on the given "key".
func (loc *Locale) GetMessage(key string, args ...interface{}) string {
if msg, ok := loc.Messages[key]; ok {
result, err := msg.Render(args...)
if err != nil {
result = err.Error()
}
return result
}
return ""
}