mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-24 08:01:03 +00:00
198 lines
5.6 KiB
Go
198 lines
5.6 KiB
Go
package i18n
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"text/template"
|
|
|
|
"github.com/nicksnyder/go-i18n/v2/internal"
|
|
"github.com/nicksnyder/go-i18n/v2/internal/plural"
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
// Localizer provides Localize and MustLocalize methods that return localized messages.
|
|
type Localizer struct {
|
|
// bundle contains the messages that can be returned by the Localizer.
|
|
bundle *Bundle
|
|
|
|
// tags is the list of language tags that the Localizer checks
|
|
// in order when localizing a message.
|
|
tags []language.Tag
|
|
}
|
|
|
|
// NewLocalizer returns a new Localizer that looks up messages
|
|
// in the bundle according to the language preferences in langs.
|
|
// It can parse Accept-Language headers as defined in http://www.ietf.org/rfc/rfc2616.txt.
|
|
func NewLocalizer(bundle *Bundle, langs ...string) *Localizer {
|
|
bundle.init()
|
|
return &Localizer{
|
|
bundle: bundle,
|
|
tags: parseTags(langs),
|
|
}
|
|
}
|
|
|
|
func parseTags(langs []string) []language.Tag {
|
|
tags := []language.Tag{}
|
|
for _, lang := range langs {
|
|
t, _, err := language.ParseAcceptLanguage(lang)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
tags = append(tags, t...)
|
|
}
|
|
return tags
|
|
}
|
|
|
|
// LocalizeConfig configures a call to the Localize method on Localizer.
|
|
type LocalizeConfig struct {
|
|
// MessageID is the id of the message to lookup.
|
|
// This field is ignored if DefaultMessage is set.
|
|
MessageID string
|
|
|
|
// TemplateData is the data passed when executing the message's template.
|
|
// If TemplateData is nil and PluralCount is not nil, then the message template
|
|
// will be executed with data that contains the plural count.
|
|
TemplateData interface{}
|
|
|
|
// PluralCount determines which plural form of the message is used.
|
|
PluralCount interface{}
|
|
|
|
// DefaultMessage is used if the message is not found in any message files.
|
|
DefaultMessage *Message
|
|
|
|
// Funcs is used to extend the Go template engines built in functions
|
|
Funcs template.FuncMap
|
|
}
|
|
|
|
type invalidPluralCountErr struct {
|
|
messageID string
|
|
pluralCount interface{}
|
|
err error
|
|
}
|
|
|
|
func (e *invalidPluralCountErr) Error() string {
|
|
return fmt.Sprintf("invalid plural count %#v for message id %q: %s", e.pluralCount, e.messageID, e.err)
|
|
}
|
|
|
|
type messageNotFoundErr struct {
|
|
messageID string
|
|
}
|
|
|
|
func (e *messageNotFoundErr) Error() string {
|
|
return fmt.Sprintf("message %q not found", e.messageID)
|
|
}
|
|
|
|
type pluralizeErr struct {
|
|
messageID string
|
|
tag language.Tag
|
|
}
|
|
|
|
func (e *pluralizeErr) Error() string {
|
|
return fmt.Sprintf("unable to pluralize %q because there no plural rule for %q", e.messageID, e.tag)
|
|
}
|
|
|
|
// Localize returns a localized message.
|
|
func (l *Localizer) Localize(lc *LocalizeConfig) (string, error) {
|
|
messageID := lc.MessageID
|
|
if lc.DefaultMessage != nil {
|
|
messageID = lc.DefaultMessage.ID
|
|
}
|
|
|
|
var operands *plural.Operands
|
|
templateData := lc.TemplateData
|
|
if lc.PluralCount != nil {
|
|
var err error
|
|
operands, err = plural.NewOperands(lc.PluralCount)
|
|
if err != nil {
|
|
return "", &invalidPluralCountErr{messageID: messageID, pluralCount: lc.PluralCount, err: err}
|
|
}
|
|
if templateData == nil {
|
|
templateData = map[string]interface{}{
|
|
"PluralCount": lc.PluralCount,
|
|
}
|
|
}
|
|
}
|
|
tag, template := l.getTemplate(messageID, lc.DefaultMessage)
|
|
if template == nil {
|
|
return "", &messageNotFoundErr{messageID: messageID}
|
|
}
|
|
pluralForm := l.pluralForm(tag, operands)
|
|
if pluralForm == plural.Invalid {
|
|
return "", &pluralizeErr{messageID: messageID, tag: tag}
|
|
}
|
|
return template.Execute(pluralForm, templateData, lc.Funcs)
|
|
}
|
|
|
|
func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *internal.MessageTemplate) {
|
|
// Fast path.
|
|
// Optimistically assume this message id is defined in each language.
|
|
fastTag, template := l.matchTemplate(id, l.bundle.matcher, l.bundle.tags)
|
|
if template != nil {
|
|
return fastTag, template
|
|
}
|
|
if fastTag == l.bundle.DefaultLanguage {
|
|
if defaultMessage == nil {
|
|
return fastTag, nil
|
|
}
|
|
return fastTag, internal.NewMessageTemplate(defaultMessage)
|
|
}
|
|
if len(l.bundle.tags) > 1 {
|
|
// Slow path.
|
|
// We didn't find a translation for the tag suggested by the default matcher
|
|
// so we need to create a new matcher that contains only the tags in the bundle
|
|
// that have this message.
|
|
foundTags := make([]language.Tag, 0, len(l.bundle.messageTemplates))
|
|
if l.bundle.DefaultLanguage != fastTag {
|
|
foundTags = append(foundTags, l.bundle.DefaultLanguage)
|
|
}
|
|
for t, templates := range l.bundle.messageTemplates {
|
|
if t == fastTag {
|
|
// We already tried this tag in the fast path
|
|
continue
|
|
}
|
|
template := templates[id]
|
|
if template == nil || template.Other == "" {
|
|
continue
|
|
}
|
|
foundTags = append(foundTags, t)
|
|
}
|
|
tag, template := l.matchTemplate(id, language.NewMatcher(foundTags), foundTags)
|
|
if template != nil {
|
|
return tag, template
|
|
}
|
|
}
|
|
if defaultMessage == nil {
|
|
return l.bundle.DefaultLanguage, nil
|
|
}
|
|
return l.bundle.DefaultLanguage, internal.NewMessageTemplate(defaultMessage)
|
|
}
|
|
|
|
func (l *Localizer) matchTemplate(id string, matcher language.Matcher, tags []language.Tag) (language.Tag, *internal.MessageTemplate) {
|
|
_, i, _ := matcher.Match(l.tags...)
|
|
tag := tags[i]
|
|
templates := l.bundle.messageTemplates[tag]
|
|
if templates != nil && templates[id] != nil {
|
|
return tag, templates[id]
|
|
}
|
|
return tag, nil
|
|
}
|
|
|
|
func (l *Localizer) pluralForm(tag language.Tag, operands *plural.Operands) plural.Form {
|
|
if operands == nil {
|
|
return plural.Other
|
|
}
|
|
pluralRule := l.bundle.pluralRules.Rule(tag)
|
|
if pluralRule == nil {
|
|
return plural.Invalid
|
|
}
|
|
return pluralRule.PluralFormFunc(operands)
|
|
}
|
|
|
|
// MustLocalize is similar to Localize, except it panics if an error happens.
|
|
func (l *Localizer) MustLocalize(lc *LocalizeConfig) string {
|
|
localized, err := l.Localize(lc)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return localized
|
|
}
|