1
0
mirror of https://github.com/kataras/iris.git synced 2026-06-10 07:33:42 +00:00

New: i18n pluralization and variables support and more...

fixes: #1649, #1648, #1641, #1650

relative to: #1597
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-09-29 19:19:19 +03:00
parent f224ded740
commit 4065819688
63 changed files with 2054 additions and 684 deletions
@@ -0,0 +1 @@
key1: "αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first"
@@ -0,0 +1 @@
key1: "this is a value from the first file: locale_multi_first"
@@ -1,2 +0,0 @@
Dog: "σκυλί"
HiDogs: Γειά {{plural (tr "Dog") .count }}
@@ -1,2 +0,0 @@
Dog: "dog"
HiDogs: Hi {{plural (tr "Dog") .count }}
@@ -1 +0,0 @@
key1 = αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first
@@ -1 +0,0 @@
key1 = this is a value from the first file: locale_multi_first
@@ -0,0 +1,5 @@
[message]
Encrypted = Encrypted
Message = Message
EncryptedMessage = {{tr "message.Encrypted"}} {{tr "message.Message"}}
HostResult = Store {{tr "message.EncryptedMessage"}} Online
@@ -0,0 +1,89 @@
# Locale variables
#
# Unlike normal keys, the variables
# have limitations of: no ">x", "zero", "two" and template functions are supported.
# This is done to force developers to use small and easy to read variables for easier maintain process.
Vars:
- Minutes:
# possible keys:
# one
# "=x" - where x is a number
# "<x"
# other
# format - to customize the format, which defaults to %d .
one: "minute"
other: "minutes"
format: "%d" # defaults to that.
- Dogs:
"=5": "dogsssss"
one: "dog"
other: "dogs"
- Houses:
one: "house"
other: "houses"
- Gender:
"=1": "She" # 1 for female
"=2": "He" # 2 for male
# Using variables in raw string
YouLate: "You are %[1]d ${Minutes} late."
# Just a simple raw value
Classic: "classic"
# Pluralization, translate based on the plural count
# including the variables and their counts
FreeDay:
# possible keys:
# zero
# one
# two
# "=x"
# "<x"
# ">x"
# other
"=3": "You have three days and %[2]d ${Minutes} off." # "FreeDay" 3, 15 (plurals + variable pluralization)
one: "You have a day off" # "FreeDay", 1
other: "You have %[1]d free days" # "FreeDay", 5
# Sprintf-like raw translation
HeIsHome: "%s is home"
# Value without plural of its self but variables except pluralization
HouseCount: "${Gender} (%[3]s) has %[2]d ${Houses}"
# Same as above but with a template instead
VarTemplate: (${Gender}) {{tr "HeIsHome" .Name}}
# Template and non template with variables in the same plural key
VarTemplatePlural:
one: "${Gender} is awesome"
other: "other (${Gender}) has %[3]d ${Houses}"
"=5": "{{call .InlineJoin .Names}} are awesome"
TemplatePlural:
one: "{{.Name}} is unique"
"=5": "{{call .InlineJoin .Names}} are awesome"
# Same as above but it takes the variable counting through the map argument
TemplateVarTemplatePlural:
other: "These {{.PluralCount}} are wonderful, feeding {{.DogsCount}} ${Dogs} in total!"
# Local variables and section.
LocalVarsHouseCount:
Text: "${Gender} has %[2]d ${Houses}"
Vars:
- Gender:
"=3": "She"
"=4": "He"
- Houses:
one: "house"
other: "houses"
# Sections:
root:
user: Account
nav:
home: Home # nav.home
user: '{{tr "root.user"}}' # nav.user
more:
what: "this" # nav.more.what
even:
more: "yes" # nav.more.even.more
aplural: "You are %[1]d ${Minutes} late." # Tr("nav.more.even.aplural", 15)
+175
View File
@@ -0,0 +1,175 @@
package main
import (
"strings"
"github.com/kataras/iris/v12"
)
const (
female = iota + 1
male
)
const tableStyle = `
<style>
a {
padding: 8px 8px;
text-decoration:none;
cursor:pointer;
color: #10a2ff;
}
table {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
border-collapse: collapse;
border-spacing: 0;
empty-cells: show;
border: 1px solid #cbcbcb;
}
table caption {
color: #000;
font: italic 85%/1 arial, sans-serif;
padding: 1em 0;
text-align: center;
}
table td,
table th {
border-left: 1px solid #cbcbcb;
border-width: 0 0 0 1px;
font-size: inherit;
margin: 0;
overflow: visible;
padding: 0.5em 1em;
}
table thead {
background-color: #10a2ff;
color: #fff;
text-align: left;
vertical-align: bottom;
}
table td {
background-color: transparent;
}
.table-odd td {
background-color: #f2f2f2;
}
.table-bordered td {
border-bottom: 1px solid #cbcbcb;
}
.table-bordered tbody > tr:last-child > td {
border-bottom-width: 0;
}
</style>
`
/*
$ go run .
Visit http://localhost:8080
*/
func main() {
app := iris.New()
err := app.I18n.Load("./locales/*/*", "en-US")
// ^ here we only use a single locale for the sake of the example,
// on a real app you can register as many languages as you want to support.
if err != nil {
panic(err)
}
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<html><body>\n")
ctx.WriteString(tableStyle)
ctx.WriteString(`<table class="table-bordered table-odd">
<thead>
<tr>
<th>Key</th>
<th>Translation</th>
<th>Arguments</th>
</tr>
</thead><tbody>
`)
defer ctx.WriteString("</tbody></table></body></html>")
tr(ctx, "Classic")
tr(ctx, "YouLate", 1)
tr(ctx, "YouLate", 2)
tr(ctx, "FreeDay", 1)
tr(ctx, "FreeDay", 5)
tr(ctx, "FreeDay", 3, 15)
tr(ctx, "HeIsHome", "Peter")
tr(ctx, "HouseCount", female, 2, "Maria")
tr(ctx, "HouseCount", male, 1, "Peter")
tr(ctx, "nav.home")
tr(ctx, "nav.user")
tr(ctx, "nav.more.what")
tr(ctx, "nav.more.even.more")
tr(ctx, "nav.more.even.aplural", 1)
tr(ctx, "nav.more.even.aplural", 15)
tr(ctx, "VarTemplate", iris.Map{
"Name": "Peter",
"GenderCount": male,
})
tr(ctx, "VarTemplatePlural", 1, female)
tr(ctx, "VarTemplatePlural", 2, female, 1)
tr(ctx, "VarTemplatePlural", 2, female, 5)
tr(ctx, "VarTemplatePlural", 1, male)
tr(ctx, "VarTemplatePlural", 2, male, 1)
tr(ctx, "VarTemplatePlural", 2, male, 2)
tr(ctx, "VarTemplatePlural", iris.Map{
"PluralCount": 5,
"Names": []string{"Makis", "Peter"},
"InlineJoin": func(arr []string) string {
return strings.Join(arr, ", ")
},
})
tr(ctx, "TemplatePlural", iris.Map{
"PluralCount": 1,
"Name": "Peter",
})
tr(ctx, "TemplatePlural", iris.Map{
"PluralCount": 5,
"Names": []string{"Makis", "Peter"},
"InlineJoin": func(arr []string) string {
return strings.Join(arr, ", ")
},
})
tr(ctx, "VarTemplatePlural", 2, male, 4)
tr(ctx, "TemplateVarTemplatePlural", iris.Map{
"PluralCount": 3,
"DogsCount": 5,
})
tr(ctx, "message.HostResult")
tr(ctx, "LocalVarsHouseCount.Text", 3, 4)
})
app.Listen(":8080")
}
func tr(ctx iris.Context, key string, args ...interface{}) {
translation := ctx.Tr(key, args...)
ctx.Writef("<tr><td>%s</td><td>%s</td><td>%v</td></tr>\n", key, translation, args)
}
+131
View File
@@ -0,0 +1,131 @@
package main_test
import (
"strings"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/httptest"
)
const (
female = iota + 1
male
)
func TestI18nPlurals(t *testing.T) {
handler := func(ctx iris.Context) {
tr(ctx, "Classic")
tr(ctx, "YouLate", 1)
tr(ctx, "YouLate", 2)
tr(ctx, "FreeDay", 1)
tr(ctx, "FreeDay", 5)
tr(ctx, "FreeDay", 3, 15)
tr(ctx, "HeIsHome", "Peter")
tr(ctx, "HouseCount", female, 2, "Maria")
tr(ctx, "HouseCount", male, 1, "Peter")
tr(ctx, "nav.home")
tr(ctx, "nav.user")
tr(ctx, "nav.more.what")
tr(ctx, "nav.more.even.more")
tr(ctx, "nav.more.even.aplural", 1)
tr(ctx, "nav.more.even.aplural", 15)
tr(ctx, "VarTemplate", iris.Map{
"Name": "Peter",
"GenderCount": male,
})
tr(ctx, "VarTemplatePlural", 1, female)
tr(ctx, "VarTemplatePlural", 2, female, 1)
tr(ctx, "VarTemplatePlural", 2, female, 5)
tr(ctx, "VarTemplatePlural", 1, male)
tr(ctx, "VarTemplatePlural", 2, male, 1)
tr(ctx, "VarTemplatePlural", 2, male, 2)
tr(ctx, "VarTemplatePlural", iris.Map{
"PluralCount": 5,
"Names": []string{"Makis", "Peter"},
"InlineJoin": func(arr []string) string {
return strings.Join(arr, ", ")
},
})
tr(ctx, "TemplatePlural", iris.Map{
"PluralCount": 1,
"Name": "Peter",
})
tr(ctx, "TemplatePlural", iris.Map{
"PluralCount": 5,
"Names": []string{"Makis", "Peter"},
"InlineJoin": func(arr []string) string {
return strings.Join(arr, ", ")
},
})
tr(ctx, "VarTemplatePlural", 2, male, 4)
tr(ctx, "TemplateVarTemplatePlural", iris.Map{
"PluralCount": 3,
"DogsCount": 5,
})
tr(ctx, "message.HostResult")
tr(ctx, "LocalVarsHouseCount.Text", 3, 4)
}
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
defer r.Body.Close()
httptest.Do(w, r, handler, func(app *iris.Application) {
err := app.I18n.Load("./locales/*/*", "en-US", "el-GR")
if err != nil {
panic(err)
}
})
expected := `Classic=classic
YouLate=You are 1 minute late.
YouLate=You are 2 minutes late.
FreeDay=You have a day off
FreeDay=You have 5 free days
FreeDay=You have three days and 15 minutes off.
HeIsHome=Peter is home
HouseCount=She (Maria) has 2 houses
HouseCount=He (Peter) has 1 house
nav.home=Home
nav.user=Account
nav.more.what=this
nav.more.even.more=yes
nav.more.even.aplural=You are 1 minute late.
nav.more.even.aplural=You are 15 minutes late.
VarTemplate=(He) Peter is home
VarTemplatePlural=She is awesome
VarTemplatePlural=other (She) has 1 house
VarTemplatePlural=other (She) has 5 houses
VarTemplatePlural=He is awesome
VarTemplatePlural=other (He) has 1 house
VarTemplatePlural=other (He) has 2 houses
VarTemplatePlural=Makis, Peter are awesome
TemplatePlural=Peter is unique
TemplatePlural=Makis, Peter are awesome
VarTemplatePlural=other (He) has 4 houses
TemplateVarTemplatePlural=These 3 are wonderful, feeding 5 dogsssss in total!
message.HostResult=Store Encrypted Message Online
LocalVarsHouseCount.Text=She has 4 houses
`
if got := w.Body.String(); expected != got {
t.Fatalf("expected:\n'%s'\n\nbut got:\n'%s'", expected, got)
}
}
func tr(ctx iris.Context, key string, args ...interface{}) {
translation := ctx.Tr(key, args...)
ctx.Writef("%s=%s\n", key, translation)
}
@@ -0,0 +1 @@
HiDogs: Hi %d {{plural (tr "Dog") .count }}
@@ -0,0 +1,2 @@
Dog: "dog"
HiDogs: Hi %d {{plural (tr "Dog") .count }}
@@ -5,13 +5,20 @@ import (
"text/template"
"github.com/kataras/iris/v12"
// go get -u github.com/gertd/go-pluralize
"github.com/gertd/go-pluralize"
// go get -u golang.org/x/text/message
"golang.org/x/text/feature/plural"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
/*
Iris I18n supports text/template inside the translation values.
Follow this example to learn how to use that feature.
This is just an example on how to use template functions.
See the "plurals" example for a more comprehensive pluralization support instead.
*/
func main() {
@@ -22,7 +29,20 @@ func main() {
func newApp() *iris.Application {
app := iris.New()
pluralize := pluralize.NewClient()
// set the printers after load, so they can be done by loop of available languages.
printers := make(map[string]*message.Printer)
message.Set(language.Greek, "Hello %d dog",
plural.Selectf(1, "%d",
"one", "Γεια σου σκυλί",
"other", "Γεια σας %[1]d σκυλιά",
))
/* by variable, single word:
message.Set(language.Greek, "Hi %d dog(s)",
catalog.Var("dogs", plural.Selectf(1, "%d", "one", "σκυλί", "other", "σκυλιά")),
catalog.String("Γεια %[1]d ${dogs}"))
*/
// Set custom functions per locale!
app.I18n.Loader.Funcs = func(current iris.Locale) template.FuncMap {
@@ -30,12 +50,7 @@ func newApp() *iris.Application {
"plural": func(word string, count int) string {
// Your own implementation or use a 3rd-party package
// like we do here.
//
// Note that this is only for english,
// but you can use the "current" locale
// and make a map with dictionaries to
// pluralize words based on the given language.
return pluralize.Pluralize(word, count, true)
return printers[current.Language()].Sprintf(word, count)
},
"uppercase": func(word string) string {
return strings.ToUpper(word)
@@ -51,6 +66,12 @@ func newApp() *iris.Application {
panic(err)
}
for _, tag := range app.I18n.Tags() {
printers[tag.String()] = message.NewPrinter(tag)
}
message.NewPrinter(language.Greek).Printf("Hello %d dog", 2)
app.Get("/", func(ctx iris.Context) {
text := ctx.Tr("HiDogs", iris.Map{
"count": 2,