diff --git a/HISTORY.md b/HISTORY.md index 6b81232e..36b386fd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -394,7 +394,8 @@ Other Improvements: New Context Methods: -- `Context.ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` methods to throttle the "download" speed of the client. +- `Context.SetLanguage(langCode string)` force-sets a language code from inside a middleare, similar to the `app.I18n.ExtractFunc` +- `Context.ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` methods to throttle the "download" speed of the client - `Context.IsHTTP2() bool` reports whether the protocol version for incoming request was HTTP/2 - `Context.IsGRPC() bool` reports whether the request came from a gRPC client - `Context.UpsertCookie(*http.Cookie, cookieOptions ...context.CookieOption)` upserts a cookie, fixes [#1485](https://github.com/kataras/iris/issues/1485) too diff --git a/_examples/i18n/main.go b/_examples/i18n/main.go index 438e5acb..11b15d4c 100644 --- a/_examples/i18n/main.go +++ b/_examples/i18n/main.go @@ -23,6 +23,7 @@ func newApp() *iris.Application { // Set to false to disallow path (local) redirects, // see https://github.com/kataras/iris/issues/1369. // app.I18n.PathRedirect = true + // See app.I18n.ExtractFunc to change the way a language is extracted from a request. app.Get("/", func(ctx iris.Context) { hi := ctx.Tr("hi", "iris") diff --git a/configuration.go b/configuration.go index a0248296..985974ce 100644 --- a/configuration.go +++ b/configuration.go @@ -845,7 +845,17 @@ type Configuration struct { // // Defaults to "iris.locale". LocaleContextKey string `json:"localeContextKey,omitempty" yaml:"LocaleContextKey" toml:"LocaleContextKey"` - + // LanguageContextKey is the context key which a language can be modified by a middleware. + // It has the highest priority over the rest and if it is empty then it is ignored, + // if it set to a static string of "default" or to the default language's code + // then the rest of the language extractors will not be called at all and + // the default language will be set instead. + // + // Use with `Context.SetLanguage("el-GR")`. + // + // See `i18n.ExtractFunc` for a more organised way of the same feature. + // Defaults to "iris.locale.language". + LanguageContextKey string `json:"languageContextKey,omitempty" yaml:"LanguageContextKey" toml:"LanguageContextKey"` // GetViewLayoutContextKey is the key of the context's user values' key // which is being used to set the template // layout from a middleware or the main handler. @@ -998,6 +1008,12 @@ func (c Configuration) GetLocaleContextKey() string { return c.LocaleContextKey } +// GetLanguageContextKey returns the configuration's LanguageContextKey value, +// used for i18n. +func (c Configuration) GetLanguageContextKey() string { + return c.LanguageContextKey +} + // GetViewLayoutContextKey returns the key of the context's user values' key // which is being used to set the template // layout from a middleware or the main handler. @@ -1132,6 +1148,10 @@ func WithConfiguration(c Configuration) Configurator { main.LocaleContextKey = v } + if v := c.LanguageContextKey; v != "" { + main.LanguageContextKey = v + } + if v := c.ViewLayoutContextKey; v != "" { main.ViewLayoutContextKey = v } @@ -1185,6 +1205,7 @@ func DefaultConfiguration() Configuration { // or `context#SetMaxRequestBodySize`. PostMaxMemory: 32 << 20, // 32MB LocaleContextKey: "iris.locale", + LanguageContextKey: "iris.locale.language", ViewLayoutContextKey: "iris.viewLayout", ViewDataContextKey: "iris.viewData", RemoteAddrHeaders: make(map[string]bool), diff --git a/context/configuration.go b/context/configuration.go index c920730b..2d9e8f1a 100644 --- a/context/configuration.go +++ b/context/configuration.go @@ -78,6 +78,9 @@ type ConfigurationReadOnly interface { // GetTranslateLanguageContextKey returns the configuration's LocaleContextKey value, // used for i18n. Defaults to "iris.locale". GetLocaleContextKey() string + // GetLanguageContextKey returns the configuration's LanguageContextKey value, + // used for i18n. Defaults to "iris.locale.language". + GetLanguageContextKey() string // GetViewLayoutContextKey returns the key of the context's user values' key // which is being used to set the template diff --git a/context/context.go b/context/context.go index ab92b9f2..9db59000 100644 --- a/context/context.go +++ b/context/context.go @@ -409,6 +409,14 @@ type Context interface { // in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy // or by the URL query parameter "referer". GetReferrer() Referrer + // SetLanguage force-sets the language for i18n, can be used inside a middleare. + // It has the highest priority over the rest and if it is empty then it is ignored, + // if it set to a static string of "default" or to the default language's code + // then the rest of the language extractors will not be called at all and + // the default language will be set instead. + // + // See `app.I18n.ExtractFunc` for a more organised way of the same feature. + SetLanguage(langCode string) // GetLocale returns the current request's `Locale` found by i18n middleware. // See `Tr` too. GetLocale() Locale @@ -1975,9 +1983,21 @@ func (ctx *context) GetReferrer() Referrer { return emptyReferrer } +// SetLanguage force-sets the language for i18n, can be used inside a middleare. +// It has the highest priority over the rest and if it is empty then it is ignored, +// if it set to a static string of "default" or to the default language's code +// then the rest of the language extractors will not be called at all and +// the default language will be set instead. +// +// See `i18n.ExtractFunc` for a more organised way of the same feature. +func (ctx *context) SetLanguage(langCode string) { + ctx.Values().Set(ctx.app.ConfigurationReadOnly().GetLanguageContextKey(), langCode) +} + // GetLocale returns the current request's `Locale` found by i18n middleware. // See `Tr` too. func (ctx *context) GetLocale() Locale { + // Cache the Locale itself for multiple calls of `Tr` method. contextKey := ctx.app.ConfigurationReadOnly().GetLocaleContextKey() if v := ctx.Values().Get(contextKey); v != nil { if locale, ok := v.(Locale); ok { diff --git a/i18n/i18n.go b/i18n/i18n.go index fceec970..410a7a5c 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -320,21 +320,31 @@ const acceptLanguageHeaderKey = "Accept-Language" // GetLocale returns the found locale of a request. // It will return the first registered language if nothing else matched. func (i *I18n) GetLocale(ctx context.Context) context.Locale { - // if v := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetLocaleContextKey()); v != nil { - // if locale, ok := v.(context.Locale); ok { - // return locale - // } - // } - var ( index int ok bool ) + if contextKey := ctx.Application().ConfigurationReadOnly().GetLanguageContextKey(); contextKey != "" { + if v := ctx.Values().GetString(contextKey); v != "" { + if v == "default" { + index = 0 // no need to call `TryMatchString` and spend time. + } else { + _, index, _ = i.TryMatchString(v) + } + + locale := i.localizer.GetLocale(index) + if locale == nil { + return nil + } + + return locale + } + } + if !ok && i.ExtractFunc != nil { if v := i.ExtractFunc(ctx); v != "" { _, index, ok = i.TryMatchString(v) - } }