From 59b8ddf5be0093b2161317ccb43ff368846061a0 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Thu, 10 Sep 2020 06:22:53 +0300 Subject: [PATCH] i18n: add the ability to register template funcs per locale --- HISTORY.md | 1 + _examples/README.md | 3 ++- _examples/i18n/i18n-template/main.go | 35 +++++++++++++++--------- _examples/i18n/main.go | 4 +++ aliases.go | 3 +++ i18n/loader.go | 40 ++++++++++++++++++---------- 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c7ed1118..55fa50bb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -650,6 +650,7 @@ New Context Methods: Breaking Changes: +- The `i18n#LoaderConfig.FuncMap template.FuncMap` field was replaced with `Funcs func(iris.Locale) template.FuncMap` in order to give current locale access to the template functions. A new `app.I18n.Loader` was introduced too, in order to make it easier for end-developers to customize the translation key values. - Request Logger's `Columns bool` field has been removed. Use the new [accesslog](https://github.com/kataras/iris/tree/master/_examples/logging/request-logger/accesslog/main.go) middleware instead. - The `.Binary` method of all view engines was removed: pass the go-bindata's latest version `AssetFile()` exported function as the first argument instead of string. All examples updated. - `ContextUploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error)` now returns the total files uploaded too (as its first parameter) and the "before" variadic option should return a boolean, if false then the specific file is skipped. diff --git a/_examples/README.md b/_examples/README.md index 48e6fec5..99ca6710 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -170,7 +170,8 @@ * [Client-Side](compression/client/main.go) * [Client-Side (using Iris)](compress/client-using-iris/main.go) * Localization and Internationalization - * [i18n](i18n/main.go) + * [i18n](i18n) + * [i18n templates and functions](i18n/i18n-template) * Authentication, Authorization & Bot Detection * [Basic Authentication](auth/basicauth/main.go) * [CORS](auth/cors) diff --git a/_examples/i18n/i18n-template/main.go b/_examples/i18n/i18n-template/main.go index 6a036f95..2dd398a3 100644 --- a/_examples/i18n/i18n-template/main.go +++ b/_examples/i18n/i18n-template/main.go @@ -1,12 +1,18 @@ package main import ( - "github.com/kataras/iris/v12" + "text/template" + "github.com/kataras/iris/v12" // go get -u github.com/gertd/go-pluralize "github.com/gertd/go-pluralize" ) +/* + Iris I18n supports text/template inside the translation values. + Follow this tutorial to learn how to use that feature. +*/ + func main() { app := newApp() app.Listen(":8080") @@ -16,18 +22,23 @@ func newApp() *iris.Application { app := iris.New() pluralize := pluralize.NewClient() - app.I18n.Loader.FuncMap = map[string]interface{}{ - "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 accept the language code - // and use a map with dictionaries to - // pluralize words based on the given language. - return pluralize.Pluralize(word, count, true) - }, + + // Set custom functions per locale! + app.I18n.Loader.Funcs = func(current iris.Locale) template.FuncMap { + return template.FuncMap{ + "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 accept the language code + // and use a map with dictionaries to + // pluralize words based on the given language. + return pluralize.Pluralize(word, count, true) + }, + } } + app.I18n.Load("./locales/*/*.yml", "en-US", "el-GR") app.Get("/", func(ctx iris.Context) { diff --git a/_examples/i18n/main.go b/_examples/i18n/main.go index 3b5a167e..51c21cb9 100644 --- a/_examples/i18n/main.go +++ b/_examples/i18n/main.go @@ -7,6 +7,10 @@ import ( "github.com/kataras/iris/v12" ) +/* + See i18n-template for a more advanced translation key-values. +*/ + func newApp() *iris.Application { app := iris.New() diff --git a/aliases.go b/aliases.go index 81a2ee60..e021e021 100644 --- a/aliases.go +++ b/aliases.go @@ -159,6 +159,9 @@ type ( // // An alias for the `context.N`. N = context.N + // Locale describes the i18n locale. + // An alias for the `context.Locale`. + Locale = context.Locale ) // Constants for input argument at `router.RouteRegisterRule`. diff --git a/i18n/loader.go b/i18n/loader.go index 6b9faf66..2cd581a0 100644 --- a/i18n/loader.go +++ b/i18n/loader.go @@ -26,8 +26,8 @@ type ( LoaderConfig struct { // Template delimeters, defaults to {{ }}. Left, Right string - // Template functions map, defaults to nil. - FuncMap template.FuncMap + // Template functions map per locale, defaults to nil. + Funcs func(context.Locale) template.FuncMap // If true then it will return error on invalid templates instead of moving them to simple string-line keys. // Also it will report whether the registered languages matched the loaded ones. // Defaults to false. @@ -43,17 +43,21 @@ type ( // Apply implements the `LoaderOption` interface. func (c *LoaderConfig) Apply(cfg *LoaderConfig) { - for k, v := range c.FuncMap { - if cfg.FuncMap == nil { - cfg.FuncMap = make(template.FuncMap) - } - - cfg.FuncMap[k] = v + if c.Left != "" { + cfg.Left = c.Left } - cfg.Left = c.Left - cfg.Right = c.Right - cfg.Strict = c.Strict + if c.Right != "" { + cfg.Right = c.Right + } + + if c.Funcs != nil { + cfg.Funcs = c.Funcs + } + + if c.Strict { + cfg.Strict = true + } } // Glob accepts a glob pattern (see: https://golang.org/pkg/path/filepath/#Glob) @@ -160,14 +164,22 @@ func load(assetNames []string, asset func(string) ([]byte, error), options ...Lo // we assume it's template? // each file:line has its own template funcs so, // just map it. - builtinFuncs := template.FuncMap{ + + // builtin funcs. + funcs := template.FuncMap{ "tr": locale.GetMessage, } + if c.Funcs != nil { + // set current locale's template's funcs. + for k, v := range c.Funcs(locale) { + funcs[k] = v + } + } + if t, err := template.New(k). Delims(c.Left, c.Right). - Funcs(builtinFuncs). - Funcs(c.FuncMap). + Funcs(funcs). Parse(value); err == nil { templateKeys[k] = t continue