diff --git a/context/context.go b/context/context.go index 40294276..b9af9e3c 100644 --- a/context/context.go +++ b/context/context.go @@ -1254,14 +1254,8 @@ func (ctx *Context) GetLocale() Locale { // See `GetLocale` too. // // Example: https://github.com/kataras/iris/tree/master/_examples/i18n -func (ctx *Context) Tr(message string, values ...interface{}) string { - if locale := ctx.GetLocale(); locale != nil { - return locale.GetMessageContext(ctx, message, values...) - } - - // This should never happen as the locale fallbacks to - // the default. - return message +func (ctx *Context) Tr(key string, args ...interface{}) string { + return ctx.app.I18nReadOnly().TrContext(ctx, key, args...) } // +------------------------------------------------------------+ diff --git a/context/i18n.go b/context/i18n.go index 31be602c..b4a903b3 100644 --- a/context/i18n.go +++ b/context/i18n.go @@ -7,7 +7,8 @@ import "golang.org/x/text/language" type I18nReadOnly interface { Tags() []language.Tag GetLocale(ctx *Context) Locale - Tr(lang string, format string, args ...interface{}) string + Tr(lang string, key string, args ...interface{}) string + TrContext(ctx *Context, key string, args ...interface{}) string } // Locale is the interface which returns from a `Localizer.GetLocale` method. @@ -25,10 +26,4 @@ type Locale interface { Language() string // GetMessage should return translated text based on the given "key". GetMessage(key string, args ...interface{}) string - // GetMessageContext same as GetMessage - // but it accepts the Context as its first input. - // If DefaultMessageFunc was not nil then this Context - // will provide the real language input instead of the locale's which - // may be the default language one. - GetMessageContext(ctx *Context, key string, args ...interface{}) string } diff --git a/i18n/i18n.go b/i18n/i18n.go index 8253f466..3d970a8f 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -79,8 +79,11 @@ type I18n struct { // // Defaults to true. Subdomain bool - // If true then it will return empty string when translation for a a specific language's key was not found. + // If a DefaultMessageFunc is NOT set: + // If true then it will return empty string when translation for a + // specific language's key was not found. // Defaults to false, fallback defaultLang:key will be used. + // Otherwise, DefaultMessageFunc is called in either case. Strict bool // If true then Iris will wrap its router with the i18n router wrapper on its Build state. @@ -96,6 +99,8 @@ var _ context.I18nReadOnly = (*I18n)(nil) // makeTags converts language codes to language Tags. func makeTags(languages ...string) (tags []language.Tag) { + languages = removeDuplicates(languages) + for _, lang := range languages { tag, err := language.Parse(lang) if err == nil && tag != language.Und { @@ -324,31 +329,43 @@ func (i *I18n) TryMatchString(s string) (language.Tag, int, bool) { } // Tr returns a translated message based on the "lang" language code -// and its key(format) with any optional arguments attached to it. +// and its key with any optional arguments attached to it. // // It returns an empty string if "lang" not matched, unless DefaultMessageFunc. // It returns the default language's translation if "key" not matched, unless DefaultMessageFunc. -func (i *I18n) Tr(lang, format string, args ...interface{}) (msg string) { +func (i *I18n) Tr(lang, key string, args ...interface{}) string { _, index, ok := i.TryMatchString(lang) if !ok { index = 0 } + loc := i.localizer.GetLocale(index) + return i.getLocaleMessage(loc, lang, key, args...) +} +// TrContext returns the localized text message for this Context. +// It returns an empty string if context's locale not matched, unless DefaultMessageFunc. +// It returns the default language's translation if "key" not matched, unless DefaultMessageFunc. +func (i *I18n) TrContext(ctx *context.Context, key string, args ...interface{}) string { + loc := ctx.GetLocale() + langInput := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey()) + return i.getLocaleMessage(loc, langInput, key, args...) +} + +func (i *I18n) getLocaleMessage(loc context.Locale, langInput string, key string, args ...interface{}) (msg string) { langMatched := "" - loc := i.localizer.GetLocale(index) if loc != nil { langMatched = loc.Language() - msg = loc.GetMessage(format, args...) - if msg == "" && i.DefaultMessageFunc == nil && !i.Strict && index > 0 { + msg = loc.GetMessage(key, args...) + if msg == "" && i.DefaultMessageFunc == nil && !i.Strict && loc.Index() > 0 { // it's not the default/fallback language and not message found for that lang:key. - msg = i.localizer.GetLocale(0).GetMessage(format, args...) + msg = i.localizer.GetLocale(0).GetMessage(key, args...) } } if msg == "" && i.DefaultMessageFunc != nil { - msg = i.DefaultMessageFunc(lang, langMatched, format, args) + msg = i.DefaultMessageFunc(langInput, langMatched, key, args) } return @@ -447,30 +464,6 @@ func (i *I18n) GetLocale(ctx *context.Context) context.Locale { return locale } -// GetMessage returns the localized text message for this "r" request based on the key "format". -// It returns an empty string if context's locale not matched, unless DefaultMessageFunc. -// It returns the default language's translation if "key" not matched, unless DefaultMessageFunc. -func (i *I18n) GetMessage(ctx *context.Context, format string, args ...interface{}) (msg string) { - loc := i.GetLocale(ctx) - langMatched := "" - if loc != nil { - langMatched = loc.Language() - // it's not the default/fallback language and not message found for that lang:key. - msg = loc.GetMessage(format, args...) - if msg == "" && i.DefaultMessageFunc == nil && !i.Strict && loc.Index() > 0 { - return i.localizer.GetLocale(0).GetMessage(format, args...) - } - } - - if msg == "" && i.DefaultMessageFunc != nil { - langInput := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey()) - - msg = i.DefaultMessageFunc(langInput, langMatched, format, args...) - } - - return -} - func (i *I18n) setLangWithoutContext(w http.ResponseWriter, r *http.Request, lang string) { if i.Cookie != "" { http.SetCookie(w, &http.Cookie{ @@ -541,3 +534,17 @@ func (i *I18n) Wrapper() router.WrapperFunc { next(w, r) } } + +func removeDuplicates(elements []string) (result []string) { + seen := make(map[string]struct{}) + + for v := range elements { + val := elements[v] + if _, ok := seen[val]; !ok { + seen[val] = struct{}{} + result = append(result, val) + } + } + + return result +} diff --git a/i18n/internal/locale.go b/i18n/internal/locale.go index 5d6e859c..c3596c28 100644 --- a/i18n/internal/locale.go +++ b/i18n/internal/locale.go @@ -163,20 +163,6 @@ func (loc *Locale) Language() string { // GetMessage should return translated text based on the given "key". func (loc *Locale) GetMessage(key string, args ...interface{}) string { - return loc.getMessage(loc.ID, key, args...) -} - -// GetMessageContext same as GetMessage -// but it accepts the Context as its first input. -// If DefaultMessageFunc was not nil then this Context -// will provide the real language input instead of the locale's which -// may be the default language one. -func (loc *Locale) GetMessageContext(ctx *context.Context, key string, args ...interface{}) string { - langInput := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey()) - return loc.getMessage(langInput, key, args...) -} - -func (loc *Locale) getMessage(langInput, key string, args ...interface{}) string { if msg, ok := loc.Messages[key]; ok { result, err := msg.Render(args...) if err != nil { @@ -186,10 +172,5 @@ func (loc *Locale) getMessage(langInput, key string, args ...interface{}) string return result } - if fn := loc.Options.DefaultMessageFunc; fn != nil { - // let langInput to be empty if that's the case. - return fn(langInput, loc.ID, key, args...) - } - return "" }