diff --git a/_example/i18n/de.json b/_example/i18n/de.json new file mode 100644 index 0000000..71231d2 --- /dev/null +++ b/_example/i18n/de.json @@ -0,0 +1,4 @@ +{ + "welcome": "hallo", + "welcomeWithName": "hallo {{ .name }}" +} diff --git a/_example/i18n/en.json b/_example/i18n/en.json new file mode 100644 index 0000000..120a5be --- /dev/null +++ b/_example/i18n/en.json @@ -0,0 +1,4 @@ +{ + "welcome": "hello", + "welcomeWithName": "hello {{ .name }}" +} diff --git a/_example/i18n/fr.json b/_example/i18n/fr.json new file mode 100644 index 0000000..e07d847 --- /dev/null +++ b/_example/i18n/fr.json @@ -0,0 +1,4 @@ +{ + "welcome": "bonjour", + "welcomeWithName": "bonjour {{ .name }}" +} \ No newline at end of file diff --git a/_example/i18n/zh.json b/_example/i18n/zh.json new file mode 100644 index 0000000..caf17e0 --- /dev/null +++ b/_example/i18n/zh.json @@ -0,0 +1,4 @@ +{ + "welcomeWithNameAndFood":"欢迎宝贝{{ .name }}, 我喜欢吃 {{ .food }}", + "welcome": "欢迎" +} \ No newline at end of file diff --git a/_example/main.go b/_example/main.go index d94063a..c03560d 100644 --- a/_example/main.go +++ b/_example/main.go @@ -1,12 +1,14 @@ package main import ( + "encoding/json" "log" "net/http" ginI18n "github.com/gin-contrib/i18n" "github.com/gin-gonic/gin" "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" ) func main() { @@ -15,7 +17,13 @@ func main() { router := gin.New() // apply i18n middleware - router.Use(ginI18n.Localize()) + r.Use(ginI18n.Localize(ginI18n.WithBundle(&ginI18n.BundleCfg{ + RootPath: "./i18n", + AcceptLanguage: []language.Tag{language.Chinese, language.English}, + DefaultLanguage: language.English, + FormatBundleFile: "json", + UnmarshalFunc: json.Unmarshal, + }))) router.GET("/", func(context *gin.Context) { context.String(http.StatusOK, ginI18n.MustGetMessage("welcome")) diff --git a/ginI18n.go b/ginI18n.go index 6c8c686..dc83aa0 100644 --- a/ginI18n.go +++ b/ginI18n.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "reflect" "github.com/gin-gonic/gin" "github.com/nicksnyder/go-i18n/v2/i18n" @@ -50,6 +51,28 @@ func (i *ginI18nImpl) setCurrentContext(ctx context.Context) { i.currentContext = ctx.(*gin.Context) } +func (ginI18nImpl) setCustomerBundle(cfg *BundleCfg) { + if !reflect.DeepEqual(cfg.DefaultLanguage, language.Tag{}) { + defaultBundleConfig.DefaultLanguage = cfg.DefaultLanguage + } + + if len(cfg.AcceptLanguage) != 0 { + defaultBundleConfig.AcceptLanguage = cfg.AcceptLanguage + } + + if cfg.FormatBundleFile != "" { + defaultBundleConfig.FormatBundleFile = cfg.FormatBundleFile + } + + if cfg.RootPath != "" { + defaultBundleConfig.RootPath = cfg.RootPath + } + + if cfg.UnmarshalFunc != nil { + defaultBundleConfig.UnmarshalFunc = cfg.UnmarshalFunc + } +} + func (i *ginI18nImpl) setBundle(cfg *BundleCfg) { bundle := i18n.NewBundle(cfg.DefaultLanguage) bundle.RegisterUnmarshalFunc(cfg.FormatBundleFile, cfg.UnmarshalFunc) diff --git a/i18n.go b/i18n.go index fa803bd..2c14907 100644 --- a/i18n.go +++ b/i18n.go @@ -12,13 +12,14 @@ func newI18n(opts ...Option) { ins := &ginI18nImpl{ getLngHandler: defaultGetLngHandler, } - ins.setBundle(defaultBundleConfig) // overwrite default value by options for _, opt := range opts { opt(ins) } + ins.setBundle(defaultBundleConfig) + atI18n = ins } diff --git a/i18n_test.go b/i18n_test.go index 1488804..2102a1d 100644 --- a/i18n_test.go +++ b/i18n_test.go @@ -1,6 +1,7 @@ package i18n import ( + "encoding/json" "net/http" "net/http/httptest" "testing" @@ -14,6 +15,55 @@ func init() { gin.SetMode(gin.ReleaseMode) } +func newPartCustomerConfigServer() *gin.Engine { + router := gin.New() + router.Use(Localize(WithBundle(&BundleCfg{ + RootPath: "./_example/i18n", + FormatBundleFile: "json", + }))) + + router.GET("/", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage("welcome")) + }) + + router.GET("/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(&i18n.LocalizeConfig{ + MessageID: "welcomeWithName", + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + return router +} +func newCustomerServer() *gin.Engine { + router := gin.New() + router.Use(Localize(WithBundle(&BundleCfg{ + RootPath: "./_example/i18n", + AcceptLanguage: []language.Tag{language.Chinese, language.English}, + DefaultLanguage: language.English, + FormatBundleFile: "json", + UnmarshalFunc: json.Unmarshal, + }))) + + router.GET("/", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage("welcome")) + }) + + router.GET("/:name/:food", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(&i18n.LocalizeConfig{ + MessageID: "welcomeWithNameAndFood", + TemplateData: map[string]string{ + "name": context.Param("name"), + "food": context.Param("food"), + }, + })) + }) + + return router +} + // newServer ... func newServer() *gin.Engine { router := gin.New() @@ -35,6 +85,39 @@ func newServer() *gin.Engine { return router } +func makeRequest3( + lng language.Tag, + name string, +) string { + path := "/" + name + req, _ := http.NewRequest("GET", path, nil) + req.Header.Add("Accept-Language", lng.String()) + + // Perform the request + w := httptest.NewRecorder() + r := newPartCustomerConfigServer() + r.ServeHTTP(w, req) + + return w.Body.String() +} + +func makeRequest2( + lng language.Tag, + name string, + food string, +) string { + path := "/" + name + "/" + food + req, _ := http.NewRequest("GET", path, nil) + req.Header.Add("Accept-Language", lng.String()) + + // Perform the request + w := httptest.NewRecorder() + r := newCustomerServer() + r.ServeHTTP(w, req) + + return w.Body.String() +} + // makeRequest ... func makeRequest( lng language.Tag, @@ -159,3 +242,78 @@ func TestI18nFR(t *testing.T) { }) } } + +func TestCustomerI18n(t *testing.T) { + type args struct { + lng language.Tag + name string + food string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "1st", + args: args{ + name: "watch", + food: "apple", + lng: language.Chinese, + }, + want: "欢迎宝贝watch, 我喜欢吃 apple", + }, + { + name: "2nd", + args: args{ + name: "issues", + food: "orange", + lng: language.English, + }, + want: "welcome my baby issues,I like orange", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := makeRequest2(tt.args.lng, tt.args.name, tt.args.food); got != tt.want { + t.Errorf("makeRequest2() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPartCustomerConfigI18n(t *testing.T) { + type args struct { + lng language.Tag + name string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "hello world", + args: args{ + name: "", + lng: language.English, + }, + want: "hello", + }, + { + name: "hello alex", + args: args{ + name: "alex", + lng: language.English, + }, + want: "hello alex", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := makeRequest3(tt.args.lng, tt.args.name); got != tt.want { + t.Errorf("makeRequest3() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/interface.go b/interface.go index 8650b4f..34029ce 100644 --- a/interface.go +++ b/interface.go @@ -11,5 +11,6 @@ type GinI18n interface { setCurrentContext(ctx context.Context) setBundle(cfg *BundleCfg) + setCustomerBundle(cfg *BundleCfg) setGetLngHandler(handler GetLngHandler) } diff --git a/option.go b/option.go index 794e459..c1d0b6b 100644 --- a/option.go +++ b/option.go @@ -17,7 +17,7 @@ type BundleCfg struct { // WithBundle ... func WithBundle(config *BundleCfg) Option { return func(g GinI18n) { - g.setBundle(config) + g.setCustomerBundle(config) } }