mirror of
https://github.com/kataras/iris.git
synced 2025-12-17 18:07:01 +00:00
implement #1593 - Read HISTORY.md
updated example: https://github.com/kataras/iris/blob/master/_examples/i18n/main.go#L28-L50
This commit is contained in:
@@ -359,6 +359,8 @@ Response:
|
|||||||
|
|
||||||
Other Improvements:
|
Other Improvements:
|
||||||
|
|
||||||
|
- Implement feature request [Log when I18n Translation Fails?](https://github.com/kataras/iris/issues/1593) by using the new `Application.I18n.DefaultMessageFunc` field **before** `I18n.Load`. [Example of usage](https://github.com/kataras/iris/blob/master/_examples/i18n/main.go#L28-L50).
|
||||||
|
|
||||||
- Fix [#1594](https://github.com/kataras/iris/issues/1594) and add a new `PathAfterHandler` which can be set to true to enable the old behavior (not recommended though).
|
- Fix [#1594](https://github.com/kataras/iris/issues/1594) and add a new `PathAfterHandler` which can be set to true to enable the old behavior (not recommended though).
|
||||||
|
|
||||||
- New [apps](https://github.com/kataras/iris/tree/master/apps) subpackage. [Example of usage](https://github.com/kataras/iris/tree/master/_examples/routing/subdomains/redirect/multi-instances).
|
- New [apps](https://github.com/kataras/iris/tree/master/apps) subpackage. [Example of usage](https://github.com/kataras/iris/tree/master/_examples/routing/subdomains/redirect/multi-instances).
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -8,12 +10,7 @@ func newApp() *iris.Application {
|
|||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
|
||||||
// Configure i18n.
|
// Configure i18n.
|
||||||
// First parameter: Glob filpath patern,
|
//
|
||||||
// Second variadic parameter: Optional language tags, the first one is the default/fallback one.
|
|
||||||
err := app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR", "zh-CN")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// app.I18n.Subdomain = false to disable resolve lang code from subdomain.
|
// app.I18n.Subdomain = false to disable resolve lang code from subdomain.
|
||||||
// app.I18n.LoadAssets for go-bindata.
|
// app.I18n.LoadAssets for go-bindata.
|
||||||
|
|
||||||
@@ -27,6 +24,31 @@ func newApp() *iris.Application {
|
|||||||
//
|
//
|
||||||
// See `app.I18n.ExtractFunc = func(ctx iris.Context) string` or
|
// See `app.I18n.ExtractFunc = func(ctx iris.Context) string` or
|
||||||
// `ctx.SetLanguage(langCode string)` to change the extracted language from a request.
|
// `ctx.SetLanguage(langCode string)` to change the extracted language from a request.
|
||||||
|
//
|
||||||
|
// Use DefaultMessageFunc to customize the return value of a not found key or lang.
|
||||||
|
// All language inputs fallback to the default locale if not matched.
|
||||||
|
// This is why this one accepts both input and matched languages,
|
||||||
|
// so the caller can be more expressful knowing those.
|
||||||
|
// Defaults to nil.
|
||||||
|
app.I18n.DefaultMessageFunc = func(langInput, langMatched, key string, args ...interface{}) string {
|
||||||
|
msg := fmt.Sprintf("user language input: %s: matched as: %s: not found key: %s: args: %v", langInput, langMatched, key, args)
|
||||||
|
app.Logger().Warn(msg)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
// Load i18n when customizations are set in place.
|
||||||
|
//
|
||||||
|
// First parameter: Glob filpath patern,
|
||||||
|
// Second variadic parameter: Optional language tags, the first one is the default/fallback one.
|
||||||
|
err := app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR", "zh-CN")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Get("/not-matched", func(ctx iris.Context) {
|
||||||
|
text := ctx.Tr("not_found_key", "some", "values", 42)
|
||||||
|
ctx.WriteString(text)
|
||||||
|
// user language input: en-gb: matched as: en-US: not found key: not_found_key: args: [some values 42]
|
||||||
|
})
|
||||||
|
|
||||||
app.Get("/", func(ctx iris.Context) {
|
app.Get("/", func(ctx iris.Context) {
|
||||||
hi := ctx.Tr("hi", "iris")
|
hi := ctx.Tr("hi", "iris")
|
||||||
|
|||||||
@@ -83,4 +83,6 @@ func TestI18n(t *testing.T) {
|
|||||||
e.GET("/el-templates").Expect().Status(httptest.StatusNotFound)
|
e.GET("/el-templates").Expect().Status(httptest.StatusNotFound)
|
||||||
|
|
||||||
e.GET("/el/templates").Expect().Status(httptest.StatusOK).Body().Contains(elGR).Contains(zhCN)
|
e.GET("/el/templates").Expect().Status(httptest.StatusOK).Body().Contains(elGR).Contains(zhCN)
|
||||||
|
|
||||||
|
e.GET("/not-matched").WithQuery("lang", "en-gb").Expect().Status(httptest.StatusOK).Body().Equal("user language input: en-gb: matched as: en-US: not found key: not_found_key: args: [some values 42]")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -752,6 +752,11 @@ type Configuration struct {
|
|||||||
// See `i18n.ExtractFunc` for a more organised way of the same feature.
|
// See `i18n.ExtractFunc` for a more organised way of the same feature.
|
||||||
// Defaults to "iris.locale.language".
|
// Defaults to "iris.locale.language".
|
||||||
LanguageContextKey string `json:"languageContextKey,omitempty" yaml:"LanguageContextKey" toml:"LanguageContextKey"`
|
LanguageContextKey string `json:"languageContextKey,omitempty" yaml:"LanguageContextKey" toml:"LanguageContextKey"`
|
||||||
|
// LanguageInputContextKey is the context key of a language that is given by the end-user.
|
||||||
|
// It's the real user input of the language string, matched or not.
|
||||||
|
//
|
||||||
|
// Defaults to "iris.locale.language.input".
|
||||||
|
LanguageInputContextKey string `json:"languageInputContextKey,omitempty" yaml:"LanguageInputContextKey" toml:"LanguageInputContextKey"`
|
||||||
// VersionContextKey is the context key which an API Version can be modified
|
// VersionContextKey is the context key which an API Version can be modified
|
||||||
// via a middleware through `SetVersion` method, e.g. `versioning.SetVersion(ctx, "1.0, 1.1")`.
|
// via a middleware through `SetVersion` method, e.g. `versioning.SetVersion(ctx, "1.0, 1.1")`.
|
||||||
// Defaults to "iris.api.version".
|
// Defaults to "iris.api.version".
|
||||||
@@ -952,6 +957,11 @@ func (c Configuration) GetLanguageContextKey() string {
|
|||||||
return c.LanguageContextKey
|
return c.LanguageContextKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLanguageInputContextKey returns the LanguageInputContextKey field.
|
||||||
|
func (c Configuration) GetLanguageInputContextKey() string {
|
||||||
|
return c.LanguageInputContextKey
|
||||||
|
}
|
||||||
|
|
||||||
// GetVersionContextKey returns the VersionContextKey field.
|
// GetVersionContextKey returns the VersionContextKey field.
|
||||||
func (c Configuration) GetVersionContextKey() string {
|
func (c Configuration) GetVersionContextKey() string {
|
||||||
return c.VersionContextKey
|
return c.VersionContextKey
|
||||||
@@ -1107,6 +1117,10 @@ func WithConfiguration(c Configuration) Configurator {
|
|||||||
main.LanguageContextKey = v
|
main.LanguageContextKey = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.LanguageInputContextKey; v != "" {
|
||||||
|
main.LanguageInputContextKey = v
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.VersionContextKey; v != "" {
|
if v := c.VersionContextKey; v != "" {
|
||||||
main.VersionContextKey = v
|
main.VersionContextKey = v
|
||||||
}
|
}
|
||||||
@@ -1184,15 +1198,16 @@ func DefaultConfiguration() Configuration {
|
|||||||
// The request body the size limit
|
// The request body the size limit
|
||||||
// can be set by the middleware `LimitRequestBodySize`
|
// can be set by the middleware `LimitRequestBodySize`
|
||||||
// or `context#SetMaxRequestBodySize`.
|
// or `context#SetMaxRequestBodySize`.
|
||||||
PostMaxMemory: 32 << 20, // 32MB
|
PostMaxMemory: 32 << 20, // 32MB
|
||||||
LocaleContextKey: "iris.locale",
|
LocaleContextKey: "iris.locale",
|
||||||
LanguageContextKey: "iris.locale.language",
|
LanguageContextKey: "iris.locale.language",
|
||||||
VersionContextKey: "iris.api.version",
|
LanguageInputContextKey: "iris.locale.language.input",
|
||||||
ViewEngineContextKey: "iris.view.engine",
|
VersionContextKey: "iris.api.version",
|
||||||
ViewLayoutContextKey: "iris.view.layout",
|
ViewEngineContextKey: "iris.view.engine",
|
||||||
ViewDataContextKey: "iris.view.data",
|
ViewLayoutContextKey: "iris.view.layout",
|
||||||
RemoteAddrHeaders: nil,
|
ViewDataContextKey: "iris.view.data",
|
||||||
RemoteAddrHeadersForce: false,
|
RemoteAddrHeaders: nil,
|
||||||
|
RemoteAddrHeadersForce: false,
|
||||||
RemoteAddrPrivateSubnets: []netutil.IPRange{
|
RemoteAddrPrivateSubnets: []netutil.IPRange{
|
||||||
{
|
{
|
||||||
Start: net.ParseIP("10.0.0.0"),
|
Start: net.ParseIP("10.0.0.0"),
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ type ConfigurationReadOnly interface {
|
|||||||
GetLocaleContextKey() string
|
GetLocaleContextKey() string
|
||||||
// GetLanguageContextKey returns the LanguageContextKey field.
|
// GetLanguageContextKey returns the LanguageContextKey field.
|
||||||
GetLanguageContextKey() string
|
GetLanguageContextKey() string
|
||||||
|
// GetLanguageInputContextKey returns the LanguageInputContextKey field.
|
||||||
|
GetLanguageInputContextKey() string
|
||||||
// GetVersionContextKey returns the VersionContextKey field.
|
// GetVersionContextKey returns the VersionContextKey field.
|
||||||
GetVersionContextKey() string
|
GetVersionContextKey() string
|
||||||
|
|
||||||
|
|||||||
@@ -1203,6 +1203,7 @@ func (ctx *Context) SetLanguage(langCode string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLocale returns the current request's `Locale` found by i18n middleware.
|
// GetLocale returns the current request's `Locale` found by i18n middleware.
|
||||||
|
// It always fallbacks to the default one.
|
||||||
// See `Tr` too.
|
// See `Tr` too.
|
||||||
func (ctx *Context) GetLocale() Locale {
|
func (ctx *Context) GetLocale() Locale {
|
||||||
// Cache the Locale itself for multiple calls of `Tr` method.
|
// Cache the Locale itself for multiple calls of `Tr` method.
|
||||||
@@ -1225,11 +1226,13 @@ func (ctx *Context) GetLocale() Locale {
|
|||||||
// See `GetLocale` too.
|
// See `GetLocale` too.
|
||||||
//
|
//
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/i18n
|
// Example: https://github.com/kataras/iris/tree/master/_examples/i18n
|
||||||
func (ctx *Context) Tr(message string, values ...interface{}) string { // other name could be: Localize.
|
func (ctx *Context) Tr(message string, values ...interface{}) string {
|
||||||
if locale := ctx.GetLocale(); locale != nil { // TODO: here... I need to change the logic, if not found then call the i18n's get locale and set the value in order to be fastest on routes that are not using (no need to reigster a middleware.)
|
if locale := ctx.GetLocale(); locale != nil {
|
||||||
return locale.GetMessage(message, values...)
|
return locale.GetMessageContext(ctx, message, values...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This should never happen as the locale fallbacks to
|
||||||
|
// the default.
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ type I18nReadOnly interface {
|
|||||||
Tr(lang string, format string, args ...interface{}) string
|
Tr(lang string, format string, args ...interface{}) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locale is the interface which returns from a `Localizer.GetLocale` metod.
|
// Locale is the interface which returns from a `Localizer.GetLocale` method.
|
||||||
// It serves the transltions based on "key" or format. See `GetMessage`.
|
// It serves the translations based on "key" or format. See `GetMessage`.
|
||||||
type Locale interface {
|
type Locale interface {
|
||||||
// Index returns the current locale index from the languages list.
|
// Index returns the current locale index from the languages list.
|
||||||
Index() int
|
Index() int
|
||||||
@@ -23,6 +23,12 @@ type Locale interface {
|
|||||||
//
|
//
|
||||||
// Same as `Tag().String()` but it's static.
|
// Same as `Tag().String()` but it's static.
|
||||||
Language() string
|
Language() string
|
||||||
// GetMessage should return translated text based n the given "key".
|
// GetMessage should return translated text based on the given "key".
|
||||||
GetMessage(key string, args ...interface{}) string
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
98
i18n/i18n.go
98
i18n/i18n.go
@@ -29,6 +29,19 @@ type (
|
|||||||
// It may return the default language if nothing else matches based on custom localizer's criteria.
|
// It may return the default language if nothing else matches based on custom localizer's criteria.
|
||||||
GetLocale(index int) context.Locale
|
GetLocale(index int) context.Locale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
MessageFunc func(langInput, langMatched, key string, args ...interface{}) string
|
||||||
)
|
)
|
||||||
|
|
||||||
// I18n is the structure which keeps the i18n configuration and implements localization and internationalization features.
|
// I18n is the structure which keeps the i18n configuration and implements localization and internationalization features.
|
||||||
@@ -43,6 +56,15 @@ type I18n struct {
|
|||||||
// to extract the language tag name.
|
// to extract the language tag name.
|
||||||
// Defaults to nil.
|
// Defaults to nil.
|
||||||
ExtractFunc func(ctx *context.Context) string
|
ExtractFunc func(ctx *context.Context) string
|
||||||
|
// DefaultMessageFunc is the field which can be used
|
||||||
|
// 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 one accepts both input and matched languages,
|
||||||
|
// so the caller can be more expressful knowing those.
|
||||||
|
//
|
||||||
|
// Defaults to nil.
|
||||||
|
DefaultMessageFunc MessageFunc
|
||||||
|
|
||||||
// If not empty, it is language identifier by url query.
|
// If not empty, it is language identifier by url query.
|
||||||
//
|
//
|
||||||
// Defaults to "lang".
|
// Defaults to "lang".
|
||||||
@@ -115,9 +137,10 @@ func (i *I18n) Reset(loader Loader, languages ...string) error {
|
|||||||
|
|
||||||
i.loader = loader
|
i.loader = loader
|
||||||
i.matcher = &Matcher{
|
i.matcher = &Matcher{
|
||||||
strict: len(tags) > 0,
|
strict: len(tags) > 0,
|
||||||
Languages: tags,
|
Languages: tags,
|
||||||
matcher: language.NewMatcher(tags),
|
matcher: language.NewMatcher(tags),
|
||||||
|
defaultMessageFunc: i.DefaultMessageFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
return i.reload()
|
return i.reload()
|
||||||
@@ -193,6 +216,8 @@ type Matcher struct {
|
|||||||
strict bool
|
strict bool
|
||||||
Languages []language.Tag
|
Languages []language.Tag
|
||||||
matcher language.Matcher
|
matcher language.Matcher
|
||||||
|
// defaultMessageFunc passed by the i18n structure.
|
||||||
|
defaultMessageFunc MessageFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ language.Matcher = (*Matcher)(nil)
|
var _ language.Matcher = (*Matcher)(nil)
|
||||||
@@ -295,24 +320,32 @@ func (i *I18n) TryMatchString(s string) (language.Tag, int, bool) {
|
|||||||
// Tr returns a translated message based on the "lang" language code
|
// 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(format) with any optional arguments attached to it.
|
||||||
//
|
//
|
||||||
// It returns an empty string if "format" not matched.
|
// It returns an empty string if "lang" not matched, unless DefaultMessageFunc.
|
||||||
func (i *I18n) Tr(lang, format string, args ...interface{}) string {
|
// It returns the default language's translation if "key" not matched, unless DefaultMessageFunc.
|
||||||
|
func (i *I18n) Tr(lang, format string, args ...interface{}) (msg string) {
|
||||||
_, index, ok := i.TryMatchString(lang)
|
_, index, ok := i.TryMatchString(lang)
|
||||||
if !ok {
|
if !ok {
|
||||||
index = 0
|
index = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
langMatched := ""
|
||||||
|
|
||||||
loc := i.localizer.GetLocale(index)
|
loc := i.localizer.GetLocale(index)
|
||||||
if loc != nil {
|
if loc != nil {
|
||||||
msg := loc.GetMessage(format, args...)
|
langMatched = loc.Language()
|
||||||
if msg == "" && !i.Strict && index > 0 {
|
|
||||||
|
msg = loc.GetMessage(format, args...)
|
||||||
|
if msg == "" && i.DefaultMessageFunc == nil && !i.Strict && index > 0 {
|
||||||
// it's not the default/fallback language and not message found for that lang:key.
|
// it's not the default/fallback language and not message found for that lang:key.
|
||||||
return i.localizer.GetLocale(0).GetMessage(format, args...)
|
msg = i.localizer.GetLocale(0).GetMessage(format, args...)
|
||||||
}
|
}
|
||||||
return msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
if msg == "" && i.DefaultMessageFunc != nil {
|
||||||
|
msg = i.DefaultMessageFunc(lang, langMatched, format, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const acceptLanguageHeaderKey = "Accept-Language"
|
const acceptLanguageHeaderKey = "Accept-Language"
|
||||||
@@ -321,12 +354,19 @@ const acceptLanguageHeaderKey = "Accept-Language"
|
|||||||
// It will return the first registered language if nothing else matched.
|
// It will return the first registered language if nothing else matched.
|
||||||
func (i *I18n) GetLocale(ctx *context.Context) context.Locale {
|
func (i *I18n) GetLocale(ctx *context.Context) context.Locale {
|
||||||
var (
|
var (
|
||||||
index int
|
index int
|
||||||
ok bool
|
ok bool
|
||||||
|
extractedLang string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
languageInputKey := ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey()
|
||||||
|
|
||||||
if contextKey := ctx.Application().ConfigurationReadOnly().GetLanguageContextKey(); contextKey != "" {
|
if contextKey := ctx.Application().ConfigurationReadOnly().GetLanguageContextKey(); contextKey != "" {
|
||||||
if v := ctx.Values().GetString(contextKey); v != "" {
|
if v := ctx.Values().GetString(contextKey); v != "" {
|
||||||
|
if languageInputKey != "" {
|
||||||
|
ctx.Values().Set(languageInputKey, v)
|
||||||
|
}
|
||||||
|
|
||||||
if v == "default" {
|
if v == "default" {
|
||||||
index = 0 // no need to call `TryMatchString` and spend time.
|
index = 0 // no need to call `TryMatchString` and spend time.
|
||||||
} else {
|
} else {
|
||||||
@@ -344,30 +384,35 @@ func (i *I18n) GetLocale(ctx *context.Context) context.Locale {
|
|||||||
|
|
||||||
if !ok && i.ExtractFunc != nil {
|
if !ok && i.ExtractFunc != nil {
|
||||||
if v := i.ExtractFunc(ctx); v != "" {
|
if v := i.ExtractFunc(ctx); v != "" {
|
||||||
|
extractedLang = v
|
||||||
_, index, ok = i.TryMatchString(v)
|
_, index, ok = i.TryMatchString(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok && i.URLParameter != "" {
|
if !ok && i.URLParameter != "" {
|
||||||
if v := ctx.URLParam(i.URLParameter); v != "" {
|
if v := ctx.URLParam(i.URLParameter); v != "" {
|
||||||
|
extractedLang = v
|
||||||
_, index, ok = i.TryMatchString(v)
|
_, index, ok = i.TryMatchString(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok && i.Cookie != "" {
|
if !ok && i.Cookie != "" {
|
||||||
if v := ctx.GetCookie(i.Cookie); v != "" {
|
if v := ctx.GetCookie(i.Cookie); v != "" {
|
||||||
|
extractedLang = v
|
||||||
_, index, ok = i.TryMatchString(v) // url.QueryUnescape(cookie.Value)
|
_, index, ok = i.TryMatchString(v) // url.QueryUnescape(cookie.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok && i.Subdomain {
|
if !ok && i.Subdomain {
|
||||||
if v := ctx.Subdomain(); v != "" {
|
if v := ctx.Subdomain(); v != "" {
|
||||||
|
extractedLang = v
|
||||||
_, index, ok = i.TryMatchString(v)
|
_, index, ok = i.TryMatchString(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
if v := ctx.GetHeader(acceptLanguageHeaderKey); v != "" {
|
if v := ctx.GetHeader(acceptLanguageHeaderKey); v != "" {
|
||||||
|
extractedLang = v // note.
|
||||||
desired, _, err := language.ParseAcceptLanguage(v)
|
desired, _, err := language.ParseAcceptLanguage(v)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if _, idx, conf := i.matcher.Match(desired...); conf > language.Low {
|
if _, idx, conf := i.matcher.Match(desired...); conf > language.Low {
|
||||||
@@ -380,8 +425,14 @@ func (i *I18n) GetLocale(ctx *context.Context) context.Locale {
|
|||||||
// locale := i.localizer.GetLocale(index)
|
// locale := i.localizer.GetLocale(index)
|
||||||
// ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetLocaleContextKey(), locale)
|
// ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetLocaleContextKey(), locale)
|
||||||
|
|
||||||
// // if 0 then it defaults to the first language.
|
if languageInputKey != "" {
|
||||||
// return locale
|
// Set the user input we wanna use it on DefaultMessageFunc.
|
||||||
|
// Even if matched because it may be en-gb or en but if there is a language registered
|
||||||
|
// as en-us it will be successfully matched ( see TrymatchString and Low conf).
|
||||||
|
ctx.Values().Set(languageInputKey, extractedLang)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if index == 0 then it defaults to the first language.
|
||||||
locale := i.localizer.GetLocale(index)
|
locale := i.localizer.GetLocale(index)
|
||||||
if locale == nil {
|
if locale == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -391,18 +442,27 @@ func (i *I18n) GetLocale(ctx *context.Context) context.Locale {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMessage returns the localized text message for this "r" request based on the key "format".
|
// GetMessage returns the localized text message for this "r" request based on the key "format".
|
||||||
// It returns an empty string if locale or format not found.
|
// It returns an empty string if context's locale not matched, unless DefaultMessageFunc.
|
||||||
func (i *I18n) GetMessage(ctx *context.Context, format string, args ...interface{}) string {
|
// 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)
|
loc := i.GetLocale(ctx)
|
||||||
|
langMatched := ""
|
||||||
if loc != nil {
|
if loc != nil {
|
||||||
|
langMatched = loc.Language()
|
||||||
// it's not the default/fallback language and not message found for that lang:key.
|
// it's not the default/fallback language and not message found for that lang:key.
|
||||||
msg := loc.GetMessage(format, args...)
|
msg = loc.GetMessage(format, args...)
|
||||||
if msg == "" && !i.Strict && loc.Index() > 0 {
|
if msg == "" && i.DefaultMessageFunc == nil && !i.Strict && loc.Index() > 0 {
|
||||||
return i.localizer.GetLocale(0).GetMessage(format, args...)
|
return i.localizer.GetLocale(0).GetMessage(format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
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) {
|
func (i *I18n) setLangWithoutContext(w http.ResponseWriter, r *http.Request, lang string) {
|
||||||
|
|||||||
@@ -149,6 +149,8 @@ func load(assetNames []string, asset func(string) ([]byte, error), options ...Lo
|
|||||||
templateKeys: templateKeys,
|
templateKeys: templateKeys,
|
||||||
lineKeys: lineKeys,
|
lineKeys: lineKeys,
|
||||||
other: other,
|
other: other,
|
||||||
|
|
||||||
|
defaultMessageFunc: m.defaultMessageFunc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +219,8 @@ type defaultLocale struct {
|
|||||||
templateKeys map[string]*template.Template
|
templateKeys map[string]*template.Template
|
||||||
lineKeys map[string]string
|
lineKeys map[string]string
|
||||||
other map[string]interface{}
|
other map[string]interface{}
|
||||||
|
|
||||||
|
defaultMessageFunc MessageFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLocale) Index() int {
|
func (l *defaultLocale) Index() int {
|
||||||
@@ -232,6 +236,15 @@ func (l *defaultLocale) Language() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLocale) GetMessage(key string, args ...interface{}) string {
|
func (l *defaultLocale) GetMessage(key string, args ...interface{}) string {
|
||||||
|
return l.getMessage(l.id, key, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLocale) GetMessageContext(ctx *context.Context, key string, args ...interface{}) string {
|
||||||
|
langInput := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey())
|
||||||
|
return l.getMessage(langInput, key, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLocale) getMessage(langInput, key string, args ...interface{}) string {
|
||||||
n := len(args)
|
n := len(args)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
// search on templates.
|
// search on templates.
|
||||||
@@ -254,6 +267,11 @@ func (l *defaultLocale) GetMessage(key string, args ...interface{}) string {
|
|||||||
return fmt.Sprintf("%v", v)
|
return fmt.Sprintf("%v", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if l.defaultMessageFunc != nil {
|
||||||
|
// let langInput to be empty if that's the case.
|
||||||
|
return l.defaultMessageFunc(langInput, l.id, key, args...)
|
||||||
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user