Skip to content

Commit 0954986

Browse files
committed
add LoadKV method on Iris.Application.I18N instance
1 parent 41326d4 commit 0954986

File tree

9 files changed

+108
-11
lines changed

9 files changed

+108
-11
lines changed

HISTORY.md

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
2323

2424
Change applies to `master` branch.
2525

26+
- Add `LoadKV` method on `Iris.Application.I18N` instance. It should be used when no locale files are available. It loads locales via pure Go Map (or database decoded values).
27+
2628
- Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago.
2729

2830
# Sa, 11 March 2023 | v12.2.0

_examples/i18n/template-embedded/embedded/locales/el-GR/other.ini

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ User = Λογαριασμός
44
[debug]
55
Title = Μενού προγραμματιστή
66
AccessLog = Πρόσβαση στο αρχείο καταγραφής
7-
AccessLogClear = Καθαρισμός {{tr "debug.AccessLog"}}
7+
AccessLogClear = Καθαρισμός {{ tr "debug.AccessLog" }}
88

99
[user.connections]
10-
Title = {{tr "nav.User"}} Συνδέσεις
10+
Title = {{ tr "nav.User" }} Συνδέσεις
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[forms]
22
member = μέλος
3-
register = Γίνε {{uppercase (tr "forms.member") }}
3+
register = Γίνε {{ uppercase (tr "forms.member") }}
44
registered = εγγεγραμμένοι

_examples/i18n/template-embedded/embedded/locales/en-US/other.ini

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ User = Account
66
[debug]
77
Title = Developer Menu
88
AccessLog = Access Log
9-
AccessLogClear = Clear {{tr "debug.AccessLog"}}
9+
AccessLogClear = Clear {{ tr "debug.AccessLog" }}
1010

1111
[user.connections]
12-
Title = {{tr "nav.User"}} Connections
12+
Title = {{ tr "nav.User" }} Connections
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[forms]
22
member = member
3-
register = Become a {{uppercase (tr "forms.member") }}
3+
register = Become a {{ uppercase (tr "forms.member") }}
44
registered = registered

_examples/i18n/template-embedded/main.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ var embeddedFS embed.FS
1313

1414
func main() {
1515
app := newApp()
16+
// http://localhost:8080
17+
// http://localhost:8080?lang=el
18+
// http://localhost:8080?lang=el
19+
// http://localhost:8080?lang=el-GR
20+
// http://localhost:8080?lang=en
21+
// http://localhost:8080?lang=en-US
22+
//
23+
// http://localhost:8080/title
24+
// http://localhost:8080/title?lang=el-GR
25+
// ...
1626
app.Listen(":8080")
1727
}
1828

@@ -30,11 +40,14 @@ func newApp() *iris.Application {
3040

3141
// Instead of:
3242
// err := app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR")
33-
// Apply the below in order to build with embedded locales inside your executable binary.
43+
// apply the below in order to build with embedded locales inside your executable binary.
3444
err := app.I18n.LoadFS(embeddedFS, "./embedded/locales/*/*.ini", "en-US", "el-GR")
3545
if err != nil {
3646
panic(err)
37-
}
47+
} // OR to load all languages by filename:
48+
// app.I18n.LoadFS(embeddedFS, "./embedded/locales/*/*.ini")
49+
// Then set the default language using:
50+
// app.I18n.SetDefault("en-US")
3851

3952
app.Get("/", func(ctx iris.Context) {
4053
text := ctx.Tr("forms.register") // en-US: prints "Become a MEMBER".

i18n/i18n.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ func (i *I18n) LoadAssets(assetNames func() []string, asset func(string) ([]byte
141141
return i.Reset(Assets(assetNames, asset, i.Loader), languages...)
142142
}
143143

144-
// LoadFS is a method shortcut to load files using `embed.FS` or `fs.FS` or
145-
// `http.FileSystem` or `string` (local directory).
144+
// LoadFS is a method shortcut to load files using
145+
// an `embed.FS` or `fs.FS` or `http.FileSystem` value.
146146
// The "pattern" is a classic glob pattern.
147147
//
148148
// See `New` and `FS` package-level functions for more.
@@ -156,6 +156,13 @@ func (i *I18n) LoadFS(fileSystem fs.FS, pattern string, languages ...string) err
156156
return i.Reset(loader, languages...)
157157
}
158158

159+
// LoadKV is a method shortcut to load locales from a map of specified languages.
160+
// See `KV` package-level function for more.
161+
func (i *I18n) LoadKV(langMap LangMap, languages ...string) error {
162+
loader := KV(langMap, i.Loader)
163+
return i.Reset(loader, languages...)
164+
}
165+
159166
// Reset sets the locales loader and languages.
160167
// It is not meant to be used by users unless
161168
// a custom `Loader` must be used instead of the default one.
@@ -300,6 +307,16 @@ func parsePath(m *Matcher, path string) int {
300307
return -1
301308
}
302309

310+
func parseLanguageName(m *Matcher, name string) int {
311+
if t, err := language.Parse(name); err == nil {
312+
if _, index, conf := m.MatchOrAdd(t); conf > language.Low {
313+
return index
314+
}
315+
}
316+
317+
return -1
318+
}
319+
303320
func reverseStrings(s []string) []string {
304321
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
305322
s[i], s[j] = s[j], s[i]

i18n/internal/catalog.go

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"text/template"
66

77
"github.com/kataras/iris/v12/context"
8+
89
"golang.org/x/text/language"
910
"golang.org/x/text/message"
1011
"golang.org/x/text/message/catalog"

i18n/loader.go

+65-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,70 @@ func FS(fileSystem fs.FS, pattern string, options LoaderConfig) (Loader, error)
7373
return load(assetNames, assetFunc, options), nil
7474
}
7575

76+
// LangMap key as language (e.g. "el-GR") and value as a map of key-value pairs (e.g. "hello": "Γειά").
77+
type LangMap = map[string]map[string]interface{}
78+
79+
// KV is a loader which accepts a map of language(key) and the available key-value pairs.
80+
// Example Code:
81+
//
82+
// m := i18n.LangMap{
83+
// "en-US": map[string]interface{}{
84+
// "hello": "Hello",
85+
// },
86+
// "el-GR": map[string]interface{}{
87+
// "hello": "Γειά",
88+
// },
89+
// }
90+
//
91+
// app := iris.New()
92+
// [...]
93+
// app.I18N.LoadKV(m)
94+
// app.I18N.SetDefault("en-US")
95+
func KV(langMap LangMap, opts ...LoaderConfig) Loader {
96+
return func(m *Matcher) (Localizer, error) {
97+
options := DefaultLoaderConfig
98+
if len(opts) > 0 {
99+
options = opts[0]
100+
}
101+
102+
languageIndexes := make([]int, 0, len(langMap))
103+
keyValuesMulti := make([]map[string]interface{}, 0, len(langMap))
104+
105+
for languageName, pairs := range langMap {
106+
langIndex := parseLanguageName(m, languageName) // matches and adds the language tag to m.Languages.
107+
languageIndexes = append(languageIndexes, langIndex)
108+
keyValuesMulti = append(keyValuesMulti, pairs)
109+
}
110+
111+
cat, err := internal.NewCatalog(m.Languages, options)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
for _, langIndex := range languageIndexes {
117+
if langIndex == -1 {
118+
// If loader has more languages than defined for use in New function,
119+
// e.g. when New(KV(m), "en-US") contains el-GR and en-US but only "en-US" passed.
120+
continue
121+
}
122+
123+
kv := keyValuesMulti[langIndex]
124+
err := cat.Store(langIndex, kv)
125+
if err != nil {
126+
return nil, err
127+
}
128+
}
129+
130+
if n := len(cat.Locales); n == 0 {
131+
return nil, fmt.Errorf("locales not found in map")
132+
} else if options.Strict && n < len(m.Languages) {
133+
return nil, fmt.Errorf("locales expected to be %d but %d parsed", len(m.Languages), n)
134+
}
135+
136+
return cat, nil
137+
}
138+
}
139+
76140
// DefaultLoaderConfig represents the default loader configuration.
77141
var DefaultLoaderConfig = LoaderConfig{
78142
Left: "{{",
@@ -88,7 +152,7 @@ var DefaultLoaderConfig = LoaderConfig{
88152
// and any Loader options.
89153
// It returns a valid `Loader` which loads and maps the locale files.
90154
//
91-
// See `Glob`, `Assets` and `LoaderConfig` too.
155+
// See `FS`, `Glob`, `Assets` and `LoaderConfig` too.
92156
func load(assetNames []string, asset func(string) ([]byte, error), options LoaderConfig) Loader {
93157
return func(m *Matcher) (Localizer, error) {
94158
languageFiles, err := m.ParseLanguageFiles(assetNames)

0 commit comments

Comments
 (0)