diff --git a/README.md b/README.md
index 9bb81b62..548a5d07 100644
--- a/README.md
+++ b/README.md
@@ -81,3 +81,7 @@ If you discover a security vulnerability within Iris, please send an e-mail to [
The project name "Iris" was inspired by the Greek mythology.
Iris Web Framework is free and open-source software licensed under the [3-Clause BSD License](LICENSE).
+
+## Stargazers over time
+
+[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris)
diff --git a/README_ES.md b/README_ES.md
index 41970b97..4ebcbb1b 100644
--- a/README_ES.md
+++ b/README_ES.md
@@ -8,7 +8,7 @@ Iris es un framework web rápido, simple pero con muchas funcionalidades y muy e
Descubra lo que [otros dicen sobre Iris](https://iris-go.com/testimonials/) y **siga** :star: este repositorio github.
-> Iris **version 12 liberado**. Lea mas [aquí](HISTORY_ES.md#sábado-26-de-octubre-2019--v1200).
+[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
## Aprende Iris
diff --git a/README_FA.md b/README_FA.md
index c6794eb8..7a9fb7dc 100644
--- a/README_FA.md
+++ b/README_FA.md
@@ -11,13 +11,7 @@
برای این که بدانید دیگران در مورد آیریس چه می گویند لطفا در این لینک کلیک کنید [دیگران در مورد آیریس چه می گویند](https://iris-go.com/testimonials/) لطفا این پروژه را در گیتاب **استار** کنید.
-
-> نسخه 11.2 **آماده شد**
-
-
-
-[![https://www.facebook.com/iris.framework/posts/3276606095684693](https://iris-go.com/images/iris-112-released.png)](https://www.facebook.com/iris.framework/posts/3276606095684693)
-
+[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
## آموزش آیریس
diff --git a/README_GR.md b/README_GR.md
index 19f1a88b..5045e8b8 100644
--- a/README_GR.md
+++ b/README_GR.md
@@ -6,6 +6,8 @@
Μάθετε τι [λένε οι άλλοι για το Iris](https://iris-go.com/testimonials/) και δώστε ένα **αστεράκι** στο GitHub.
+[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
+
## Μαθαίνοντας το Iris
diff --git a/README_KO.md b/README_KO.md
index e93d2c18..5f92d772 100644
--- a/README_KO.md
+++ b/README_KO.md
@@ -1,5 +1,3 @@
-
-
# Iris
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=blue&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![release](https://img.shields.io/badge/release%20-v11.2-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases)
@@ -8,9 +6,7 @@ Iris는 단순하고 빠르며 좋은 성능과 모든 기능을 갖춘 Go언어
[여러 사람들의 의견](https://iris-go.com/testimonials/)을 둘러보세요. 그리고 이 github repository을 **star**하세요.
-> Version 11.2가 **릴리스되었습니다!**
-
-[![https://www.facebook.com/iris.framework/posts/3276606095684693](https://iris-go.com/images/iris-112-released.png)](https://www.facebook.com/iris.framework/posts/3276606095684693)
+[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
## Iris 배우기
diff --git a/README_ZH.md b/README_ZH.md
index e29d91b8..caf78eee 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -1,5 +1,3 @@
-
-
# Iris
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=blue&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![release](https://img.shields.io/badge/release%20-v11.2-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases)
@@ -8,7 +6,7 @@ Iris 是基于 Go 编写的一个快速,简单但功能齐全且非常高效
看看 [其他人如何评价 Iris](https://iris-go.com/testimonials/),同时欢迎各位点亮 **star**。
-> 新版本 11.2 发布! [散布消息](https://www.facebook.com/iris.framework/posts/3276606095684693).
+[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
## 学习 Iris
diff --git a/VERSION b/VERSION
index ca308d35..50c9b748 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.0.1:https://github.com/kataras/iris/releases/tag/v12.0.1
\ No newline at end of file
+12.0.2:https://github.com/kataras/iris/releases/tag/v12.0.2
\ No newline at end of file
diff --git a/_examples/miscellaneous/i18n/main.go b/_examples/miscellaneous/i18n/main.go
index 15fdbb5f..6afdd300 100644
--- a/_examples/miscellaneous/i18n/main.go
+++ b/_examples/miscellaneous/i18n/main.go
@@ -7,20 +7,29 @@ import (
func newApp() *iris.Application {
app := iris.New()
+ app.Logger().SetLevel("debug")
- globalLocale := i18n.New(i18n.Config{
- Default: "en-US",
- URLParameter: "lang",
+ i18nConfig := i18n.Config{
+ Default: "en-US",
+ URLParameter: "lang",
+ PathParameter: "lang",
Languages: map[string]string{
"en-US": "./locales/locale_en-US.ini",
"el-GR": "./locales/locale_el-GR.ini",
"zh-CN": "./locales/locale_zh-CN.ini",
},
- })
- app.Use(globalLocale)
+ Alternatives: map[string]string{"greek": "el-GR"},
+ }
+
+ // See https://github.com/kataras/iris/issues/1369
+ // if you want to enable this (SEO) feature.
+ app.WrapRouter(i18n.NewWrapper(i18nConfig))
+
+ i18nMiddleware := i18n.New(i18nConfig)
+ app.Use(i18nMiddleware)
app.Get("/", func(ctx iris.Context) {
- // it tries to find the language by:
+ // Ir tries to find the language by:
// ctx.Values().GetString("language")
// if that was empty then
// it tries to find from the URLParameter set on the configuration
@@ -35,14 +44,23 @@ func newApp() *iris.Application {
// or:
hi := i18n.Translate(ctx, "hi", "iris")
+ // GetTranslateLanguageContextKey() == "language"
language := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey())
// return is form of 'en-US'
// The first succeed language found saved at the cookie with name ("language"),
- // you can change that by changing the value of the: iris.TranslateLanguageContextKey
+ // you can change that by changing the value of the: iris.TranslateLanguageContextKey
ctx.Writef("From the language %s translated output: %s", language, hi)
})
+ app.Get("/some-path", func(ctx iris.Context) {
+ ctx.Writef("%s", ctx.Translate("hi", "iris"))
+ })
+
+ app.Get("/sitemap.xml", func(ctx iris.Context) {
+ ctx.WriteString("sitemap")
+ })
+
multiLocale := i18n.New(i18n.Config{
Default: "en-US",
URLParameter: "lang",
@@ -68,7 +86,8 @@ func newApp() *iris.Application {
app.Get("/templates", func(ctx iris.Context) {
ctx.View("index.html", iris.Map{
- "tr": ctx.Translate,
+ "tr": ctx.Translate, // word, arguments...
+ "trLang": ctx.TranslateLang, // locale, word, arguments...
})
// it will return "hello, iris"
// when {{call .tr "hi" "iris"}}
@@ -81,7 +100,9 @@ func newApp() *iris.Application {
func main() {
app := newApp()
- // go to http://localhost:8080/?lang=el-GR
+ // go to http://localhost:8080/el-GR/some-path
+ // or http://localhost:8080/zh-cn/templates
+ // or http://localhost:8080/some-path?lang=el-GR
// or http://localhost:8080 (default is en-US)
// or http://localhost:8080/?lang=zh-CN
//
@@ -90,5 +111,6 @@ func main() {
// or http://localhost:8080/multi?lang=en-US
//
// or use cookies to set the language.
+ //
app.Run(iris.Addr(":8080"))
}
diff --git a/_examples/miscellaneous/i18n/templates/index.html b/_examples/miscellaneous/i18n/templates/index.html
index 7fc7cae8..f0dc0cad 100644
--- a/_examples/miscellaneous/i18n/templates/index.html
+++ b/_examples/miscellaneous/i18n/templates/index.html
@@ -1 +1,9 @@
-{{call .tr "hi" "iris"}}
\ No newline at end of file
+Test translate current locale template function [dynamic] ("word", arguments...)
call .tr "hi" "iris"
+
+{{call .tr "hi" "iris"}}
+
+
+
+Test translate of any language template function [static] ("language", "word", arguments...)
call .trLang "zh-CN" "hi" "iris"
+
+{{call .trLang "zh-CN" "hi" "iris"}}
diff --git a/configuration.go b/configuration.go
index 401bf0d5..f9287859 100644
--- a/configuration.go
+++ b/configuration.go
@@ -705,14 +705,17 @@ type Configuration struct {
// Context values' keys for various features.
//
- // TranslateLanguageContextKey & TranslateFunctionContextKey are used by i18n handlers/middleware
- // currently we have only one: https://github.com/kataras/iris/tree/master/middleware/i18n.
+ // TranslateLanguageContextKey & TranslateLangFunctionContextKey & TranslateFunctionContextKey are used by i18n handlers/middleware to set the selected locale's translate function.
//
- // Defaults to "iris.translate" and "iris.language"
+ // Defaults to "iris.translate".
TranslateFunctionContextKey string `json:"translateFunctionContextKey,omitempty" yaml:"TranslateFunctionContextKey" toml:"TranslateFunctionContextKey"`
- // TranslateLanguageContextKey used for i18n.
+ // TranslateLangFunctionContextKey & TranslateFunctionContextKey & TranslateLanguageContextKey are used by i18n handlers/middleware to set the global translate function.
//
- // Defaults to "iris.language"
+ // Defaults to "iris.languageGlobal".
+ TranslateLangFunctionContextKey string `json:"translateLangFunctionContextKey,omitempty" yaml:"TranslateLangFunctionContextKey" toml:"TranslateLangFunctionContextKey"`
+ // TranslateLanguageContextKey used to report the i18n selected locale.
+ //
+ // Defaults to "iris.language".
TranslateLanguageContextKey string `json:"translateLanguageContextKey,omitempty" yaml:"TranslateLanguageContextKey" toml:"TranslateLanguageContextKey"`
// GetViewLayoutContextKey is the key of the context's user values' key
@@ -842,11 +845,17 @@ func (c Configuration) GetPostMaxMemory() int64 {
}
// GetTranslateFunctionContextKey returns the configuration's TranslateFunctionContextKey value,
-// used for i18n.
+// used for i18n inside templates.
func (c Configuration) GetTranslateFunctionContextKey() string {
return c.TranslateFunctionContextKey
}
+// GetTranslateLangFunctionContextKey returns the configuration's TranslateLangFunctionContextKey value,
+// used for i18n inside templates.
+func (c Configuration) GetTranslateLangFunctionContextKey() string {
+ return c.TranslateLangFunctionContextKey
+}
+
// GetTranslateLanguageContextKey returns the configuration's TranslateLanguageContextKey value,
// used for i18n.
func (c Configuration) GetTranslateLanguageContextKey() string {
@@ -1017,13 +1026,14 @@ func DefaultConfiguration() Configuration {
// The request body the size limit
// can be set by the middleware `LimitRequestBodySize`
// or `context#SetMaxRequestBodySize`.
- PostMaxMemory: 32 << 20, // 32MB
- TranslateFunctionContextKey: "iris.translate",
- TranslateLanguageContextKey: "iris.language",
- ViewLayoutContextKey: "iris.viewLayout",
- ViewDataContextKey: "iris.viewData",
- RemoteAddrHeaders: make(map[string]bool),
- EnableOptimizations: false,
- Other: make(map[string]interface{}),
+ PostMaxMemory: 32 << 20, // 32MB
+ TranslateFunctionContextKey: "iris.translate",
+ TranslateLangFunctionContextKey: "iris.translateLang",
+ TranslateLanguageContextKey: "iris.language",
+ ViewLayoutContextKey: "iris.viewLayout",
+ ViewDataContextKey: "iris.viewData",
+ RemoteAddrHeaders: make(map[string]bool),
+ EnableOptimizations: false,
+ Other: make(map[string]interface{}),
}
}
diff --git a/context/configuration.go b/context/configuration.go
index 9eaee9f2..516de450 100644
--- a/context/configuration.go
+++ b/context/configuration.go
@@ -68,9 +68,11 @@ type ConfigurationReadOnly interface {
GetPostMaxMemory() int64
// GetTranslateLanguageContextKey returns the configuration's TranslateFunctionContextKey value,
- // used for i18n.
+ // used for i18n inside templates.
GetTranslateFunctionContextKey() string
-
+ // GetTranslateLangFunctionContextKey returns the configuration's TranslateLangFunctionContextKey value,
+ // used for i18n inside templates.
+ GetTranslateLangFunctionContextKey() string
// GetTranslateLanguageContextKey returns the configuration's TranslateLanguageContextKey value,
// used for i18n.
GetTranslateLanguageContextKey() string
diff --git a/context/context.go b/context/context.go
index 16a490de..e6d6a6c1 100644
--- a/context/context.go
+++ b/context/context.go
@@ -294,10 +294,16 @@ type Context interface {
Values() *memstore.Store
// Translate is the i18n (localization) middleware's function,
// it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey())
- // to execute the translate function and return the localized text value.
+ // to execute the translate function and returns the current localized text value.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
Translate(format string, args ...interface{}) string
+ // TranslateLang is the i18n (localization) middleware's function,
+ // it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateLangFunctionContextKey())
+ // to execute the translate function and returns the localized text value based on the "lang".
+ //
+ // Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
+ TranslateLang(lang, format string, args ...interface{}) string
// +------------------------------------------------------------+
// | Path, Host, Subdomain, IP, Headers etc... |
@@ -1499,17 +1505,30 @@ func (ctx *context) Values() *memstore.Store {
// Translate is the i18n (localization) middleware's function,
// it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey())
-// to execute the translate function and return the localized text value.
+// to execute the translate function and return the current localized text value.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
func (ctx *context) Translate(format string, args ...interface{}) string {
- if cb, ok := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()).(func(format string, args ...interface{}) string); ok {
+ if cb, ok := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()).(func(string, ...interface{}) string); ok {
return cb(format, args...)
}
return ""
}
+// TranslateLang is the i18n (localization) middleware's function,
+// it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateLangFunctionContextKey())
+// to execute the translate function and returns the localized text value based on the "lang".
+//
+// Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
+func (ctx *context) TranslateLang(lang, format string, args ...interface{}) string {
+ if cb, ok := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetTranslateLangFunctionContextKey()).(func(string, string, ...interface{}) string); ok {
+ return cb(lang, format, args...)
+ }
+
+ return ""
+}
+
// +------------------------------------------------------------+
// | Path, Host, Subdomain, IP, Headers etc... |
// +------------------------------------------------------------+
diff --git a/core/router/api_builder.go b/core/router/api_builder.go
index c90b36d1..b789bb3b 100644
--- a/core/router/api_builder.go
+++ b/core/router/api_builder.go
@@ -397,6 +397,9 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
var route *Route // the last one is returned.
for _, route = range routes {
+ if route == nil {
+ break
+ }
// global
route.topLink = api.routes.getRelative(route)
diff --git a/doc.go b/doc.go
index 397e29a1..29b99189 100644
--- a/doc.go
+++ b/doc.go
@@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
Current Version
-12.0.1
+12.0.2
Installation
diff --git a/middleware/i18n/config.go b/middleware/i18n/config.go
deleted file mode 100644
index becc89fc..00000000
--- a/middleware/i18n/config.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package i18n
-
-// Config the i18n options
-type Config struct {
- // Default set it if you want a default language
- //
- // Checked: Configuration state, not at runtime
- Default string
- // URLParameter is the name of the url parameter which the language can be indentified
- //
- // Checked: Serving state, runtime
- URLParameter string
- // Languages is a map[string]string which the key is the language i81n and the value is the file location
- //
- // Example of key is: 'en-US'
- // Example of value is: './locales/en-US.ini'
- Languages map[string]string
-}
diff --git a/middleware/i18n/i18n.go b/middleware/i18n/i18n.go
index 5a504997..13f4bc97 100644
--- a/middleware/i18n/i18n.go
+++ b/middleware/i18n/i18n.go
@@ -3,6 +3,8 @@
package i18n
import (
+ "fmt"
+ "net/http"
"reflect"
"strings"
@@ -10,80 +12,84 @@ import (
"github.com/kataras/iris/v12/context"
)
-// test file: ../../_examples/miscellaneous/i18n/main_test.go
-type i18nMiddleware struct {
- config Config
+// Config the i18n options.
+type Config struct {
+ // Default set it if you want a default language.
+ //
+ // Checked: Configuration state, not at runtime.
+ Default string
+ // URLParameter is the name of the url parameter which the language can be indentified,
+ // e.g. "lang" for ?lang=.
+ //
+ // Checked: Serving state, runtime.
+ URLParameter string
+ // PathParameter is the name of the path parameter which the language can be indentified,
+ // e.g. "lang" for "{lang:string}".
+ //
+ // Checked: Serving state, runtime.
+ //
+ // You can set custom handler to set the language too.
+ // Example:
+ // setLangMiddleware := func(ctx iris.Context){
+ // langKey := ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()
+ // languageByPath := ctx.Params().Get("lang") // see {lang}
+ // ctx.Values().Set(langKey, languageByPath)
+ // ctx.Next()
+ // }
+ // app.Use(setLangMiddleware)
+ // app.Use(theI18nMiddlewareInstance)
+ PathParameter string
+
+ // Languages is a map[string]string which the key is the language i81n and the value is the file location.
+ //
+ // Example of key is: 'en-US'.
+ // Example of value is: './locales/en-US.ini'.
+ Languages map[string]string
+ // Alternatives is a language map which if it's filled,
+ // it tries to associate its keys with a value of "Languages" field when a possible value of "Language" was not present.
+ // Example of
+ // Languages: map[string]string{"en-US": "./locales/en-US.ini"} set
+ // Alternatives: map[string]string{ "en":"en-US", "english": "en-US"}.
+ Alternatives map[string]string
+
+ // If SetCookie is true then it will set the cookie to the language found by URLParameter, PathParameter or by Context's Value's "lang" key.
+ // Defaults to false.
+ SetCookie bool
}
-// ServeHTTP serves the request, the actual middleware's job is here
-func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
- wasByCookie := false
-
- langKey := ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()
- language := ctx.Values().GetString(langKey)
- if language == "" {
- // try to get by url parameter
- language = ctx.URLParam(i.config.URLParameter)
- if language == "" {
- // then try to take the lang field from the cookie
- language = ctx.GetCookie(langKey)
-
- if len(language) > 0 {
- wasByCookie = true
- } else {
- // try to get by the request headers.
- langHeader := ctx.GetHeader("Accept-Language")
- if len(langHeader) > 0 {
- for _, langEntry := range strings.Split(langHeader, ",") {
- lc := strings.Split(langEntry, ";")[0]
- if lc, ok := i18n.IsExistSimilar(lc); ok {
- language = lc
- break
- }
- }
- }
- }
+// Exists returns true if the language, or something similar
+// exists (e.g. en-US maps to en or Alternatives[key] == lang).
+// it returns the found name and whether it was able to match something.
+func (c *Config) Exists(lang string) (string, bool) {
+ for k, v := range c.Alternatives {
+ if k == lang {
+ lang = v
+ break
}
- // if it was not taken by the cookie, then set the cookie in order to have it
- if !wasByCookie {
- ctx.SetCookieKV(langKey, language)
- }
-
- if language == "" {
- language = i.config.Default
- }
-
- ctx.Values().Set(langKey, language)
- }
- locale := i18n.Locale{Lang: language}
-
- // if unexpected language given, the middleware will transtlate to the default language, the language key should be
- // also this language instead of the user-given
- if indexLang := locale.Index(); indexLang == -1 {
- locale.Lang = i.config.Default
}
- translateFuncKey := ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()
- ctx.Values().Set(translateFuncKey, locale.Tr)
- ctx.Next()
+ return i18n.IsExistSimilar(lang)
}
-// Translate returns the translated word from a context
-// the second parameter is the key of the world or line inside the .ini file
-// the third parameter is the '%s' of the world or line inside the .ini file
-func Translate(ctx context.Context, format string, args ...interface{}) string {
- return ctx.Translate(format, args...)
-}
-
-// New returns a new i18n middleware
-func New(c Config) context.Handler {
+func (c *Config) loadLanguages() {
if len(c.Languages) == 0 {
- panic("You cannot use this middleware without set the Languages option, please try again and read the _example.")
+ panic("field Languages is empty")
}
- i := &i18nMiddleware{config: c}
+
+ for k, v := range c.Alternatives {
+ if _, ok := c.Languages[v]; !ok {
+ panic(fmt.Sprintf("language alternative '%s' does not map to a valid language '%s'", k, v))
+ }
+ }
+
firstlanguage := ""
// load the files
for k, langFileOrFiles := range c.Languages {
+ if i18n.IsExist(k) {
+ // if it is already stored through middleware (`New`) then skip it.
+ continue
+ }
+
// remove all spaces.
langFileOrFiles = strings.Replace(langFileOrFiles, " ", "", -1)
// note: if only one, then the first element is the "v".
@@ -93,24 +99,152 @@ func New(c Config) context.Handler {
if !strings.HasSuffix(v, ".ini") {
v += ".ini"
}
+
err := i18n.SetMessage(k, v)
if err != nil && err != i18n.ErrLangAlreadyExist {
- panic("Failed to set locale file'" + k + "' Error:" + err.Error())
+ panic(fmt.Sprintf("Failed to set locale file' %s' with error: %v", k, err))
}
if firstlanguage == "" {
firstlanguage = k
}
}
}
- // if not default language set then set to the first of the i.config.Languages
+ // if not default language set then set to the first of the "Languages".
if c.Default == "" {
c.Default = firstlanguage
}
- i18n.SetDefaultLang(i.config.Default)
+ i18n.SetDefaultLang(c.Default)
+}
+
+// test file: ../../_examples/miscellaneous/i18n/main_test.go
+type i18nMiddleware struct {
+ config Config
+}
+
+// New returns a new i18n middleware.
+func New(c Config) context.Handler {
+ c.loadLanguages()
+ i := &i18nMiddleware{config: c}
return i.ServeHTTP
}
+// ServeHTTP serves the request, the actual middleware's job is located here.
+func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
+ wasByCookie := false
+
+ langKey := ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()
+ language := ctx.Values().GetString(langKey)
+ if language == "" {
+ // try to get by path parameter
+ if i.config.PathParameter != "" {
+ language = ctx.Params().Get(i.config.PathParameter)
+ }
+
+ if language == "" {
+ // try to get by url parameter
+ language = ctx.URLParam(i.config.URLParameter)
+
+ if language == "" {
+ // then try to take the lang field from the cookie
+ language = ctx.GetCookie(langKey)
+
+ if len(language) > 0 {
+ wasByCookie = true
+ } else {
+ // try to get by the request headers.
+ langHeader := ctx.GetHeader("Accept-Language")
+ if len(langHeader) > 0 {
+ for _, langEntry := range strings.Split(langHeader, ",") {
+ lc := strings.Split(langEntry, ";")[0]
+ if lc, ok := i.config.Exists(lc); ok {
+ language = lc
+ break
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // returns the original key of the language and true
+ // when the language, or something similar exists (e.g. en-US maps to en).
+ if lc, ok := i.config.Exists(language); ok {
+ language = lc
+ } else {
+ // if unexpected language given, the middleware will translate to the default language,
+ // the language key should be also this language instead of the user-given.
+ language = i.config.Default
+ }
+
+ // if it was not taken by the cookie, then set the cookie in order to have it.
+ if !wasByCookie && i.config.SetCookie {
+ ctx.SetCookieKV(langKey, language)
+ }
+
+ ctx.Values().Set(langKey, language)
+
+ // Set iris.translate and iris.translateLang functions (they can be passed to templates as they are later on).
+ ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey(), getTranslateFunction(language))
+ // Note: translate (global) language function input argument should match exactly, case-sensitive and "Alternatives" field is not part of the fetch progress.
+ ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetTranslateLangFunctionContextKey(), i18n.Tr)
+
+ ctx.Next()
+}
+
+func getTranslateFunction(lang string) func(string, ...interface{}) string {
+ return func(format string, args ...interface{}) string {
+ return i18n.Tr(lang, format, args...)
+ }
+}
+
+// NewWrapper accepts a Config and returns a new router wrapper.
+// The result function can be passed on `Application.WrapRouter`.
+// It compares the path prefix for translated language and
+// local redirects the requested path with the selected (from the path) language to the router.
+//
+// In order this to work as expected, it should be combined with `Application.Use(New)`
+// which registers the i18n middleware itself.
+func NewWrapper(c Config) func(http.ResponseWriter, *http.Request, http.HandlerFunc) {
+ c.loadLanguages()
+
+ return func(w http.ResponseWriter, r *http.Request, routerHandler http.HandlerFunc) {
+ path := r.URL.Path[1:]
+
+ if idx := strings.IndexRune(path, '/'); idx > 0 {
+ path = path[:idx]
+ }
+
+ if lang, ok := c.Exists(path); ok {
+ path = r.URL.Path[len(path)+1:]
+ if path == "" {
+ path = "/"
+ }
+ r.RequestURI = path
+ r.URL.Path = path
+ r.Header.Set("Accept-Language", lang)
+ }
+
+ routerHandler(w, r)
+ }
+}
+
+// Translate returns the translated word from a context based on the current selected locale.
+// The second parameter is the key of the world or line inside the .ini file and
+// the third parameter is the '%s' of the world or line inside the .ini file
+func Translate(ctx context.Context, format string, args ...interface{}) string {
+ return ctx.Translate(format, args...)
+}
+
+// TranslateLang returns the translated word from a context based on the given "lang".
+// The second parameter is the language key which the word "format" is translated to and
+// the third parameter is the key of the world or line inside the .ini file and
+// the forth parameter is the '%s' of the world or line inside the .ini file
+func TranslateLang(ctx context.Context, lang, format string, args ...interface{}) string {
+ return ctx.TranslateLang(lang, format, args...)
+}
+
// TranslatedMap returns translated map[string]interface{} from i18n structure.
func TranslatedMap(ctx context.Context, sourceInterface interface{}) map[string]interface{} {
iType := reflect.TypeOf(sourceInterface).Elem()