mirror of
https://github.com/kataras/iris.git
synced 2026-01-08 12:31:58 +00:00
New: i18n pluralization and variables support and more...
fixes: #1649, #1648, #1641, #1650 relative to: #1597
This commit is contained in:
26
_examples/i18n/basic/hosts
Normal file
26
_examples/i18n/basic/hosts
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 1993-2009 Microsoft Corp.
|
||||
#
|
||||
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
|
||||
#
|
||||
# This file contains the mappings of IP addresses to host names. Each
|
||||
# entry should be kept on an individual line. The IP address should
|
||||
# be placed in the first column followed by the corresponding host name.
|
||||
# The IP address and the host name should be separated by at least one
|
||||
# space.
|
||||
#
|
||||
# Additionally, comments (such as these) may be inserted on individual
|
||||
# lines or following the machine name denoted by a '#' symbol.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# 102.54.94.97 rhino.acme.com # source server
|
||||
# 38.25.63.10 x.acme.com # x client host
|
||||
|
||||
# localhost name resolution is handled within DNS itself.
|
||||
# 127.0.0.1 localhost
|
||||
# ::1 localhost
|
||||
127.0.0.1 mydomain.com
|
||||
127.0.0.1 en.mydomain.com
|
||||
127.0.0.1 el.mydomain.com
|
||||
127.0.0.1 el-gr.mydomain.com
|
||||
127.0.0.1 zh.mydomain.com
|
||||
2
_examples/i18n/basic/locales/el-GR/locale_el-GR.ini
Normal file
2
_examples/i18n/basic/locales/el-GR/locale_el-GR.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
hi = γεια, %s
|
||||
userProfilePublicDescription = <a href="%s">περιγραφή προφιλ</a>
|
||||
@@ -0,0 +1 @@
|
||||
key1: "αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first"
|
||||
@@ -0,0 +1 @@
|
||||
key2 = αυτό είναι μια τιμή από το δεύτερο αρχείο μετάφρασης: locale_multi_second
|
||||
2
_examples/i18n/basic/locales/en-US/locale_en-US.ini
Normal file
2
_examples/i18n/basic/locales/en-US/locale_en-US.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
hi = hello, %s
|
||||
userProfilePublicDescription = <a href="%s">profile description</a>
|
||||
@@ -0,0 +1 @@
|
||||
key1: "this is a value from the first file: locale_multi_first"
|
||||
@@ -0,0 +1 @@
|
||||
key2 = this is a value from the second file: locale_multi_second
|
||||
2
_examples/i18n/basic/locales/zh-CN/locale_zh-CN.ini
Normal file
2
_examples/i18n/basic/locales/zh-CN/locale_zh-CN.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
hi = 您好,%s
|
||||
userProfilePublicDescription = <a href="%s">个人资料描述</a>
|
||||
125
_examples/i18n/basic/main.go
Normal file
125
_examples/i18n/basic/main.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
/*
|
||||
See i18n-template for a more advanced translation key-values.
|
||||
*/
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
||||
// Configure i18n.
|
||||
//
|
||||
// app.I18n.Subdomain = false to disable resolve lang code from subdomain.
|
||||
// app.I18n.LoadAssets for go-bindata.
|
||||
|
||||
// Default values:
|
||||
// app.I18n.URLParameter = "lang"
|
||||
// app.I18n.Subdomain = true
|
||||
//
|
||||
// Set to false to disallow path (local) redirects,
|
||||
// see https://github.com/kataras/iris/issues/1369.
|
||||
// app.I18n.PathRedirect = true
|
||||
//
|
||||
// See `app.I18n.ExtractFunc = func(ctx iris.Context) string` or
|
||||
// `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) {
|
||||
hi := ctx.Tr("hi", "iris")
|
||||
locale := ctx.GetLocale()
|
||||
|
||||
ctx.Writef("From the language %s translated output: %s", locale.Language(), hi)
|
||||
})
|
||||
|
||||
app.Get("/some-path", func(ctx iris.Context) {
|
||||
ctx.Writef("%s", ctx.Tr("hi", "iris"))
|
||||
})
|
||||
|
||||
app.Get("/other", func(ctx iris.Context) {
|
||||
language := ctx.GetLocale().Language()
|
||||
|
||||
fromFirstFileValue := ctx.Tr("key1")
|
||||
fromSecondFileValue := ctx.Tr("key2")
|
||||
ctx.Writef("From the language: %s, translated output:\n%s=%s\n%s=%s",
|
||||
language, "key1", fromFirstFileValue,
|
||||
"key2", fromSecondFileValue)
|
||||
})
|
||||
|
||||
// using in inside your views:
|
||||
view := iris.HTML("./views", ".html")
|
||||
app.RegisterView(view)
|
||||
|
||||
app.Get("/templates", func(ctx iris.Context) {
|
||||
ctx.View("index.html", iris.Map{
|
||||
"tr": ctx.Tr, // word, arguments... {call .tr "hi" "iris"}}
|
||||
"trUnsafe": func(message string, args ...interface{}) template.HTML {
|
||||
return template.HTML(ctx.Tr(message, args...))
|
||||
},
|
||||
})
|
||||
|
||||
// Note that,
|
||||
// Iris automatically adds a "tr" global template function as well,
|
||||
// the only difference is the way you call it inside your templates and
|
||||
// that it accepts a language code as its first argument: {{ tr "el-GR" "hi" "iris"}}
|
||||
})
|
||||
//
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
|
||||
// go to http://localhost:8080/el-gr/some-path
|
||||
// ^ (by path prefix)
|
||||
//
|
||||
// or http://el.mydomain.com8080/some-path
|
||||
// ^ (by subdomain - test locally with the hosts file)
|
||||
//
|
||||
// or http://localhost:8080/zh-CN/templates
|
||||
// ^ (by path prefix with uppercase)
|
||||
//
|
||||
// or http://localhost:8080/some-path?lang=el-GR
|
||||
// ^ (by url parameter)
|
||||
//
|
||||
// or http://localhost:8080 (default is en-US)
|
||||
// or http://localhost:8080/?lang=zh-CN
|
||||
//
|
||||
// go to http://localhost:8080/other?lang=el-GR
|
||||
// or http://localhost:8080/other (default is en-US)
|
||||
// or http://localhost:8080/other?lang=en-US
|
||||
//
|
||||
// or use cookies to set the language.
|
||||
app.Listen(":8080", iris.WithSitemap("http://localhost:8080"))
|
||||
}
|
||||
88
_examples/i18n/basic/main_test.go
Normal file
88
_examples/i18n/basic/main_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
func TestI18n(t *testing.T) {
|
||||
app := newApp()
|
||||
|
||||
const (
|
||||
expectedf = "From the language %s translated output: %s"
|
||||
|
||||
enUS = "hello, iris"
|
||||
elGR = "γεια, iris"
|
||||
zhCN = "您好,iris"
|
||||
)
|
||||
|
||||
var (
|
||||
tests = map[string]string{
|
||||
"en-US": fmt.Sprintf(expectedf, "en-US", enUS),
|
||||
"el-GR": fmt.Sprintf(expectedf, "el-GR", elGR),
|
||||
"zh-CN": fmt.Sprintf(expectedf, "zh-CN", zhCN),
|
||||
}
|
||||
|
||||
elgrMulti = fmt.Sprintf("From the language: %s, translated output:\n%s=%s\n%s=%s", "el-GR",
|
||||
"key1",
|
||||
"αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first",
|
||||
"key2",
|
||||
"αυτό είναι μια τιμή από το δεύτερο αρχείο μετάφρασης: locale_multi_second")
|
||||
enusMulti = fmt.Sprintf("From the language: %s, translated output:\n%s=%s\n%s=%s", "en-US",
|
||||
"key1",
|
||||
"this is a value from the first file: locale_multi_first",
|
||||
"key2",
|
||||
"this is a value from the second file: locale_multi_second")
|
||||
)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
// default should be en-US.
|
||||
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(tests["en-US"])
|
||||
|
||||
for lang, body := range tests {
|
||||
e.GET("/").WithQueryString("lang=" + lang).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(body)
|
||||
|
||||
// test lowercase.
|
||||
e.GET("/").WithQueryString("lang=" + strings.ToLower(lang)).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(body)
|
||||
|
||||
// test first part (e.g. en instead of en-US).
|
||||
langFirstPart := strings.Split(lang, "-")[0]
|
||||
e.GET("/").WithQueryString("lang=" + langFirstPart).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(body)
|
||||
|
||||
// test accept-language header prefix (i18n wrapper).
|
||||
e.GET("/"+lang).WithHeader("Accept-Language", lang).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(body)
|
||||
|
||||
// test path prefix (i18n router wrapper).
|
||||
e.GET("/" + lang).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(body)
|
||||
|
||||
// test path prefix with first part.
|
||||
e.GET("/" + langFirstPart).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(body)
|
||||
}
|
||||
|
||||
e.GET("/other").WithQueryString("lang=el-GR").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(elgrMulti)
|
||||
e.GET("/other").WithQueryString("lang=en-US").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(enusMulti)
|
||||
|
||||
// test path prefix (i18n router wrapper).
|
||||
e.GET("/el-gr/other").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(elgrMulti)
|
||||
e.GET("/en/other").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(enusMulti)
|
||||
|
||||
e.GET("/el-GRtemplates").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("/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]")
|
||||
}
|
||||
13
_examples/i18n/basic/views/index.html
Normal file
13
_examples/i18n/basic/views/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<h3>Test translate current locale template function <i>[dynamic]</i> ("word", arguments...) <br/> <code>call .tr "hi" "iris"</code></h3>
|
||||
|
||||
{{call .tr "hi" "iris"}}
|
||||
|
||||
<hr/>
|
||||
|
||||
<h3>Test translate of any language template function <i>[static]</i> ("language", "word", arguments...) <br/> <code>tr "zh-CN" "hi" "iris"</code></h3>
|
||||
|
||||
{{tr "zh-CN" "hi" "iris"}}
|
||||
|
||||
<h3>Test HTML link ("word", arguments...) <br/> <code>call .trUnsafe "trUnsafe" "userProfilePublicDescription" "https://iris-go.com"</code></h3>
|
||||
|
||||
{{call .trUnsafe "userProfilePublicDescription" "https://iris-go.com"}}
|
||||
Reference in New Issue
Block a user