implement path prefx for i18n middleware, as requested at: #1369

Former-commit-id: b0d6b6e7f368e710b01faad9b70dfa4cebdd8c4d
This commit is contained in:
Gerasimos (Makis) Maropoulos 2019-11-19 23:36:18 +02:00
parent 1faea8b2e8
commit 0844c109d9
16 changed files with 304 additions and 130 deletions

View File

@ -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. 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). 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)

View File

@ -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. 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 ## Aprende Iris

View File

@ -11,13 +11,7 @@
برای این که بدانید دیگران در مورد آیریس چه می گویند لطفا در این لینک کلیک کنید [دیگران در مورد آیریس چه می گویند](https://iris-go.com/testimonials/) لطفا این پروژه را در گیتاب **استار** کنید. برای این که بدانید دیگران در مورد آیریس چه می گویند لطفا در این لینک کلیک کنید [دیگران در مورد آیریس چه می گویند](https://iris-go.com/testimonials/) لطفا این پروژه را در گیتاب **استار** کنید.
[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
> نسخه 11.2 **آماده شد**
<div dir="ltr" align="left">
[![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)
</div>
## آموزش آیریس ## آموزش آیریس

View File

@ -6,6 +6,8 @@
Μάθετε τι [λένε οι άλλοι για το Iris](https://iris-go.com/testimonials/) και δώστε ένα **αστεράκι** στο GitHub. Μάθετε τι [λένε οι άλλοι για το Iris](https://iris-go.com/testimonials/) και δώστε ένα **αστεράκι** στο GitHub.
[![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
## Μαθαίνοντας το Iris ## Μαθαίνοντας το Iris
<details> <details>

View File

@ -1,5 +1,3 @@
<!-- # Iris Web Framework <a href="README_ZH.md"> <img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg?v=10" /></a> <a href="README_ID.md"> <img width="20px" src="https://iris-go.com/images/flag-indonesia.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_PT_BR.md"><img width="20px" src="https://iris-go.com/images/flag-pt-br.svg?v=10" /></a> <a href="README_JPN.md"><img width="20px" src="https://iris-go.com/images/flag-japan.svg?v=10" /></a> -->
# Iris <a href="README.md"> <img width="20px" src="https://iris-go.com/images/flag-unitedkingdom.svg?v=10" /></a> <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> # Iris <a href="README.md"> <img width="20px" src="https://iris-go.com/images/flag-unitedkingdom.svg?v=10" /></a> <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a>
[![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)<!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://godoc.org/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) [![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)<!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://godoc.org/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**하세요. [여러 사람들의 의견](https://iris-go.com/testimonials/)을 둘러보세요. 그리고 이 github repository을 **star**하세요.
> Version 11.2가 **릴리스되었습니다!** [![](https://media.giphy.com/media/eGku4UbilECflFjcNj/giphy.gif)](https://iris-go.com/testimonials/)
[![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)
## Iris 배우기 ## Iris 배우기

View File

@ -1,5 +1,3 @@
<!-- # Iris Web Framework <a href="README_ZH.md"> <img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg?v=10" /></a> <a href="README_ID.md"> <img width="20px" src="https://iris-go.com/images/flag-indonesia.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_PT_BR.md"><img width="20px" src="https://iris-go.com/images/flag-pt-br.svg?v=10" /></a> <a href="README_JPN.md"><img width="20px" src="https://iris-go.com/images/flag-japan.svg?v=10" /></a> -->
# Iris <a href="README.md"> <img width="20px" src="https://iris-go.com/images/flag-unitedkingdom.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> # Iris <a href="README.md"> <img width="20px" src="https://iris-go.com/images/flag-unitedkingdom.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a>
[![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)<!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://godoc.org/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) [![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)<!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://godoc.org/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** 看看 [其他人如何评价 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 ## 学习 Iris

View File

@ -1 +1 @@
12.0.1:https://github.com/kataras/iris/releases/tag/v12.0.1 12.0.2:https://github.com/kataras/iris/releases/tag/v12.0.2

View File

@ -7,20 +7,29 @@ import (
func newApp() *iris.Application { func newApp() *iris.Application {
app := iris.New() app := iris.New()
app.Logger().SetLevel("debug")
globalLocale := i18n.New(i18n.Config{ i18nConfig := i18n.Config{
Default: "en-US", Default: "en-US",
URLParameter: "lang", URLParameter: "lang",
PathParameter: "lang",
Languages: map[string]string{ Languages: map[string]string{
"en-US": "./locales/locale_en-US.ini", "en-US": "./locales/locale_en-US.ini",
"el-GR": "./locales/locale_el-GR.ini", "el-GR": "./locales/locale_el-GR.ini",
"zh-CN": "./locales/locale_zh-CN.ini", "zh-CN": "./locales/locale_zh-CN.ini",
}, },
}) Alternatives: map[string]string{"greek": "el-GR"},
app.Use(globalLocale) }
// 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) { app.Get("/", func(ctx iris.Context) {
// it tries to find the language by: // Ir tries to find the language by:
// ctx.Values().GetString("language") // ctx.Values().GetString("language")
// if that was empty then // if that was empty then
// it tries to find from the URLParameter set on the configuration // it tries to find from the URLParameter set on the configuration
@ -35,6 +44,7 @@ func newApp() *iris.Application {
// or: // or:
hi := i18n.Translate(ctx, "hi", "iris") hi := i18n.Translate(ctx, "hi", "iris")
// GetTranslateLanguageContextKey() == "language"
language := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()) language := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey())
// return is form of 'en-US' // return is form of 'en-US'
@ -43,6 +53,14 @@ func newApp() *iris.Application {
ctx.Writef("From the language %s translated output: %s", language, hi) 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{ multiLocale := i18n.New(i18n.Config{
Default: "en-US", Default: "en-US",
URLParameter: "lang", URLParameter: "lang",
@ -68,7 +86,8 @@ func newApp() *iris.Application {
app.Get("/templates", func(ctx iris.Context) { app.Get("/templates", func(ctx iris.Context) {
ctx.View("index.html", iris.Map{ 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" // it will return "hello, iris"
// when {{call .tr "hi" "iris"}} // when {{call .tr "hi" "iris"}}
@ -81,7 +100,9 @@ func newApp() *iris.Application {
func main() { func main() {
app := newApp() 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 (default is en-US)
// or http://localhost:8080/?lang=zh-CN // or http://localhost:8080/?lang=zh-CN
// //
@ -90,5 +111,6 @@ func main() {
// or http://localhost:8080/multi?lang=en-US // or http://localhost:8080/multi?lang=en-US
// //
// or use cookies to set the language. // or use cookies to set the language.
//
app.Run(iris.Addr(":8080")) app.Run(iris.Addr(":8080"))
} }

View File

@ -1 +1,9 @@
<h3>Test translate current locale template function <i>[dynamic]</i> ("word", arguments...) <br/> <code>call .tr "hi" "iris"</code></h3>
{{call .tr "hi" "iris"}} {{call .tr "hi" "iris"}}
<hr/>
<h3>Test translate of any language template function <i>[static]</i> ("language", "word", arguments...) <br/> <code>call .trLang "zh-CN" "hi" "iris"</code></h3>
{{call .trLang "zh-CN" "hi" "iris"}}

View File

@ -705,14 +705,17 @@ type Configuration struct {
// Context values' keys for various features. // Context values' keys for various features.
// //
// TranslateLanguageContextKey & TranslateFunctionContextKey are used by i18n handlers/middleware // TranslateLanguageContextKey & TranslateLangFunctionContextKey & TranslateFunctionContextKey are used by i18n handlers/middleware to set the selected locale's translate function.
// currently we have only one: https://github.com/kataras/iris/tree/master/middleware/i18n.
// //
// Defaults to "iris.translate" and "iris.language" // Defaults to "iris.translate".
TranslateFunctionContextKey string `json:"translateFunctionContextKey,omitempty" yaml:"TranslateFunctionContextKey" toml:"TranslateFunctionContextKey"` 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"` TranslateLanguageContextKey string `json:"translateLanguageContextKey,omitempty" yaml:"TranslateLanguageContextKey" toml:"TranslateLanguageContextKey"`
// GetViewLayoutContextKey is the key of the context's user values' key // 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, // GetTranslateFunctionContextKey returns the configuration's TranslateFunctionContextKey value,
// used for i18n. // used for i18n inside templates.
func (c Configuration) GetTranslateFunctionContextKey() string { func (c Configuration) GetTranslateFunctionContextKey() string {
return c.TranslateFunctionContextKey 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, // GetTranslateLanguageContextKey returns the configuration's TranslateLanguageContextKey value,
// used for i18n. // used for i18n.
func (c Configuration) GetTranslateLanguageContextKey() string { func (c Configuration) GetTranslateLanguageContextKey() string {
@ -1019,6 +1028,7 @@ func DefaultConfiguration() Configuration {
// or `context#SetMaxRequestBodySize`. // or `context#SetMaxRequestBodySize`.
PostMaxMemory: 32 << 20, // 32MB PostMaxMemory: 32 << 20, // 32MB
TranslateFunctionContextKey: "iris.translate", TranslateFunctionContextKey: "iris.translate",
TranslateLangFunctionContextKey: "iris.translateLang",
TranslateLanguageContextKey: "iris.language", TranslateLanguageContextKey: "iris.language",
ViewLayoutContextKey: "iris.viewLayout", ViewLayoutContextKey: "iris.viewLayout",
ViewDataContextKey: "iris.viewData", ViewDataContextKey: "iris.viewData",

View File

@ -68,9 +68,11 @@ type ConfigurationReadOnly interface {
GetPostMaxMemory() int64 GetPostMaxMemory() int64
// GetTranslateLanguageContextKey returns the configuration's TranslateFunctionContextKey value, // GetTranslateLanguageContextKey returns the configuration's TranslateFunctionContextKey value,
// used for i18n. // used for i18n inside templates.
GetTranslateFunctionContextKey() string GetTranslateFunctionContextKey() string
// GetTranslateLangFunctionContextKey returns the configuration's TranslateLangFunctionContextKey value,
// used for i18n inside templates.
GetTranslateLangFunctionContextKey() string
// GetTranslateLanguageContextKey returns the configuration's TranslateLanguageContextKey value, // GetTranslateLanguageContextKey returns the configuration's TranslateLanguageContextKey value,
// used for i18n. // used for i18n.
GetTranslateLanguageContextKey() string GetTranslateLanguageContextKey() string

View File

@ -294,10 +294,16 @@ type Context interface {
Values() *memstore.Store Values() *memstore.Store
// Translate is the i18n (localization) middleware's function, // Translate is the i18n (localization) middleware's function,
// it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()) // 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 // Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
Translate(format string, args ...interface{}) string 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... | // | Path, Host, Subdomain, IP, Headers etc... |
@ -1499,17 +1505,30 @@ func (ctx *context) Values() *memstore.Store {
// Translate is the i18n (localization) middleware's function, // Translate is the i18n (localization) middleware's function,
// it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()) // 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 // Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
func (ctx *context) Translate(format string, args ...interface{}) string { 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 cb(format, args...)
} }
return "" 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... | // | Path, Host, Subdomain, IP, Headers etc... |
// +------------------------------------------------------------+ // +------------------------------------------------------------+

View File

@ -397,6 +397,9 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
var route *Route // the last one is returned. var route *Route // the last one is returned.
for _, route = range routes { for _, route = range routes {
if route == nil {
break
}
// global // global
route.topLink = api.routes.getRelative(route) route.topLink = api.routes.getRelative(route)

2
doc.go
View File

@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
12.0.1 12.0.2
Installation Installation

View File

@ -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
}

View File

@ -3,6 +3,8 @@
package i18n package i18n
import ( import (
"fmt"
"net/http"
"reflect" "reflect"
"strings" "strings"
@ -10,20 +12,139 @@ import (
"github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/context"
) )
// 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
}
// 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
}
}
return i18n.IsExistSimilar(lang)
}
func (c *Config) loadLanguages() {
if len(c.Languages) == 0 {
panic("field Languages is empty")
}
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".
languages := strings.Split(langFileOrFiles, ",")
for _, v := range languages { // loop each of the files separated by comma, if any.
if !strings.HasSuffix(v, ".ini") {
v += ".ini"
}
err := i18n.SetMessage(k, v)
if err != nil && err != i18n.ErrLangAlreadyExist {
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 "Languages".
if c.Default == "" {
c.Default = firstlanguage
}
i18n.SetDefaultLang(c.Default)
}
// test file: ../../_examples/miscellaneous/i18n/main_test.go // test file: ../../_examples/miscellaneous/i18n/main_test.go
type i18nMiddleware struct { type i18nMiddleware struct {
config Config config Config
} }
// ServeHTTP serves the request, the actual middleware's job is here // 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) { func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
wasByCookie := false wasByCookie := false
langKey := ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey() langKey := ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()
language := ctx.Values().GetString(langKey) 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 == "" { if language == "" {
// try to get by url parameter // try to get by url parameter
language = ctx.URLParam(i.config.URLParameter) language = ctx.URLParam(i.config.URLParameter)
if language == "" { if language == "" {
// then try to take the lang field from the cookie // then try to take the lang field from the cookie
language = ctx.GetCookie(langKey) language = ctx.GetCookie(langKey)
@ -36,7 +157,7 @@ func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
if len(langHeader) > 0 { if len(langHeader) > 0 {
for _, langEntry := range strings.Split(langHeader, ",") { for _, langEntry := range strings.Split(langHeader, ",") {
lc := strings.Split(langEntry, ";")[0] lc := strings.Split(langEntry, ";")[0]
if lc, ok := i18n.IsExistSimilar(lc); ok { if lc, ok := i.config.Exists(lc); ok {
language = lc language = lc
break break
} }
@ -44,71 +165,84 @@ func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
} }
} }
} }
// 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 == "" { // 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 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) 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 // Set iris.translate and iris.translateLang functions (they can be passed to templates as they are later on).
// also this language instead of the user-given ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey(), getTranslateFunction(language))
if indexLang := locale.Index(); indexLang == -1 { // Note: translate (global) language function input argument should match exactly, case-sensitive and "Alternatives" field is not part of the fetch progress.
locale.Lang = i.config.Default ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetTranslateLangFunctionContextKey(), i18n.Tr)
}
translateFuncKey := ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()
ctx.Values().Set(translateFuncKey, locale.Tr)
ctx.Next() ctx.Next()
} }
// Translate returns the translated word from a context func getTranslateFunction(lang string) func(string, ...interface{}) string {
// the second parameter is the key of the world or line inside the .ini file 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 // 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 { func Translate(ctx context.Context, format string, args ...interface{}) string {
return ctx.Translate(format, args...) return ctx.Translate(format, args...)
} }
// New returns a new i18n middleware // TranslateLang returns the translated word from a context based on the given "lang".
func New(c Config) context.Handler { // The second parameter is the language key which the word "format" is translated to and
if len(c.Languages) == 0 { // the third parameter is the key of the world or line inside the .ini file and
panic("You cannot use this middleware without set the Languages option, please try again and read the _example.") // 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 {
i := &i18nMiddleware{config: c} return ctx.TranslateLang(lang, format, args...)
firstlanguage := ""
// load the files
for k, langFileOrFiles := range c.Languages {
// remove all spaces.
langFileOrFiles = strings.Replace(langFileOrFiles, " ", "", -1)
// note: if only one, then the first element is the "v".
languages := strings.Split(langFileOrFiles, ",")
for _, v := range languages { // loop each of the files separated by comma, if any.
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())
}
if firstlanguage == "" {
firstlanguage = k
}
}
}
// if not default language set then set to the first of the i.config.Languages
if c.Default == "" {
c.Default = firstlanguage
}
i18n.SetDefaultLang(i.config.Default)
return i.ServeHTTP
} }
// TranslatedMap returns translated map[string]interface{} from i18n structure. // TranslatedMap returns translated map[string]interface{} from i18n structure.