2020-09-29 18:19:19 +02:00
|
|
|
package internal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/kataras/iris/v12/context"
|
2023-03-18 14:43:18 +01:00
|
|
|
|
2020-09-29 18:19:19 +02:00
|
|
|
"golang.org/x/text/language"
|
|
|
|
"golang.org/x/text/message"
|
|
|
|
"golang.org/x/text/message/catalog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MessageFunc is the function type to modify the behavior when a key or language was not found.
|
|
|
|
// All language inputs fallback to the default locale if not matched.
|
|
|
|
// This is why this signature accepts both input and matched languages, so caller
|
|
|
|
// can provide better messages.
|
|
|
|
//
|
|
|
|
// The first parameter is set to the client real input of the language,
|
|
|
|
// the second one is set to the matched language (default one if input wasn't matched)
|
|
|
|
// and the third and forth are the translation format/key and its optional arguments.
|
|
|
|
//
|
|
|
|
// Note: we don't accept the Context here because Tr method and template func {{ tr }}
|
|
|
|
// have no direct access to it.
|
|
|
|
type MessageFunc func(langInput, langMatched, key string, args ...interface{}) string
|
|
|
|
|
|
|
|
// Catalog holds the locales and the variables message storage.
|
|
|
|
type Catalog struct {
|
|
|
|
builder *catalog.Builder
|
|
|
|
Locales []*Locale
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Options of the Catalog and its Locales.
|
|
|
|
type Options struct {
|
|
|
|
// Left delimiter for template messages.
|
|
|
|
Left string
|
|
|
|
// Right delimeter for template messages.
|
|
|
|
Right string
|
|
|
|
// Enable strict mode.
|
|
|
|
Strict bool
|
|
|
|
// Optional functions for template messages per locale.
|
|
|
|
Funcs func(context.Locale) template.FuncMap
|
|
|
|
// Optional function to be called when no message was found.
|
|
|
|
DefaultMessageFunc MessageFunc
|
|
|
|
// Customize the overall behavior of the plurazation feature.
|
|
|
|
PluralFormDecoder PluralFormDecoder
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewCatalog returns a new Catalog based on the registered languages and the loader options.
|
|
|
|
func NewCatalog(languages []language.Tag, opts Options) (*Catalog, error) { // ordered languages, the first should be the default one.
|
|
|
|
if len(languages) == 0 {
|
|
|
|
return nil, fmt.Errorf("catalog: empty languages")
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Left == "" {
|
|
|
|
opts.Left = "{{"
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Right == "" {
|
|
|
|
opts.Right = "}}"
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.PluralFormDecoder == nil {
|
|
|
|
opts.PluralFormDecoder = DefaultPluralFormDecoder
|
|
|
|
}
|
|
|
|
|
|
|
|
builder := catalog.NewBuilder(catalog.Fallback(languages[0]))
|
|
|
|
|
|
|
|
locales := make([]*Locale, 0, len(languages))
|
|
|
|
for idx, tag := range languages {
|
|
|
|
locale := &Locale{
|
|
|
|
tag: tag,
|
|
|
|
index: idx,
|
|
|
|
ID: tag.String(),
|
|
|
|
Options: opts,
|
|
|
|
Printer: message.NewPrinter(tag, message.Catalog(builder)),
|
|
|
|
Messages: make(map[string]Renderer),
|
|
|
|
}
|
|
|
|
locale.FuncMap = getFuncs(locale)
|
|
|
|
|
|
|
|
locales = append(locales, locale)
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &Catalog{
|
|
|
|
builder: builder,
|
|
|
|
Locales: locales,
|
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set sets a simple translation message.
|
|
|
|
func (c *Catalog) Set(tag language.Tag, key string, msgs ...catalog.Message) error {
|
|
|
|
// fmt.Printf("Catalog.Set[%s] %s:\n", tag.String(), key)
|
|
|
|
// for _, msg := range msgs {
|
|
|
|
// fmt.Printf("%#+v\n", msg)
|
|
|
|
// }
|
|
|
|
return c.builder.Set(tag, key, msgs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store stores the a map of values to the locale derives from the given "langIndex".
|
|
|
|
func (c *Catalog) Store(langIndex int, kv Map) error {
|
|
|
|
loc := c.getLocale(langIndex)
|
|
|
|
if loc == nil {
|
|
|
|
return fmt.Errorf("expected language index to be lower or equal than %d but got %d", len(c.Locales), langIndex)
|
|
|
|
}
|
|
|
|
return loc.Load(c, kv)
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Localizer interface. */
|
|
|
|
|
|
|
|
// SetDefault changes the default language based on the "index".
|
|
|
|
// See `I18n#SetDefault` method for more.
|
|
|
|
func (c *Catalog) SetDefault(index int) bool {
|
|
|
|
if index < 0 {
|
|
|
|
index = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if maxIdx := len(c.Locales) - 1; index > maxIdx {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// callers should protect with mutex if called at serve-time.
|
|
|
|
loc := c.Locales[index]
|
|
|
|
loc.index = 0
|
|
|
|
f := c.Locales[0]
|
|
|
|
c.Locales[0] = loc
|
|
|
|
f.index = index
|
|
|
|
c.Locales[index] = f
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLocale returns a valid `Locale` based on the "index".
|
|
|
|
func (c *Catalog) GetLocale(index int) context.Locale {
|
|
|
|
return c.getLocale(index)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Catalog) getLocale(index int) *Locale {
|
|
|
|
if index < 0 {
|
|
|
|
index = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if maxIdx := len(c.Locales) - 1; index > maxIdx {
|
|
|
|
// panic("expected language index to be lower or equal than %d but got %d", maxIdx, langIndex)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
loc := c.Locales[index]
|
|
|
|
return loc
|
|
|
|
}
|