diff --git a/README.md b/README.md index 58ae10e..69e5deb 100644 --- a/README.md +++ b/README.md @@ -46,17 +46,40 @@ func main() { ctx.String(http.StatusOK, ginI18n.MustGetMessage(ctx, "welcome")) }) - router.GET("/:name", func(ctx *gin.Context) { - ctx.String(http.StatusOK, ginI18n.MustGetMessage( - ctx, - &i18n.LocalizeConfig{ - MessageID: "welcomeWithName", - TemplateData: map[string]string{ - "name": ctx.Param("name"), - }, - })) + router.GET("/messageId/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + MessageID: "welcomeWithName", + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/messageType/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + DefaultMessage: &i18n.Message{ + ID: "welcomeWithName", + }, + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/exist/:lang", func(ctx *gin.Context) { + ctx.String(http.StatusOK, "%v", ginI18n.HasLang(ctx, ctx.Param("lang"))) }) + // get the default and current language + router.GET("/lang/default", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetDefaultLanguage(context).String()) + }) + + // get the current language + router.GET("/lang/current", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetCurrentLanguage(context).String()) + }) + if err := router.Run(":8080"); err != nil { log.Fatal(err) } @@ -97,17 +120,40 @@ func main() { ctx.String(http.StatusOK, ginI18n.MustGetMessage(ctx, "welcome")) }) - router.GET("/:name", func(ctx *gin.Context) { - ctx.String(http.StatusOK, ginI18n.MustGetMessage( - ctx, - &i18n.LocalizeConfig{ - MessageID: "welcomeWithName", - TemplateData: map[string]string{ - "name": ctx.Param("name"), - }, - })) + router.GET("/messageId/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + MessageID: "welcomeWithName", + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/messageType/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + DefaultMessage: &i18n.Message{ + ID: "welcomeWithName", + }, + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/exist/:lang", func(ctx *gin.Context) { + ctx.String(http.StatusOK, "%v", ginI18n.HasLang(ctx, ctx.Param("lang"))) }) + // get the default and current language + router.GET("/lang/default", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetDefaultLanguage(context).String()) + }) + + // get the current language + router.GET("/lang/current", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetCurrentLanguage(context).String()) + }) + if err := router.Run(":8080"); err != nil { log.Fatal(err) } @@ -150,17 +196,40 @@ func main() { ctx.String(http.StatusOK, ginI18n.MustGetMessage(ctx, "welcome")) }) - router.GET("/:name", func(ctx *gin.Context) { - ctx.String(http.StatusOK, ginI18n.MustGetMessage( - ctx, - &i18n.LocalizeConfig{ - MessageID: "welcomeWithName", - TemplateData: map[string]string{ - "name": ctx.Param("name"), - }, - })) + router.GET("/messageId/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + MessageID: "welcomeWithName", + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/messageType/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + DefaultMessage: &i18n.Message{ + ID: "welcomeWithName", + }, + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/exist/:lang", func(ctx *gin.Context) { + ctx.String(http.StatusOK, "%v", ginI18n.HasLang(ctx, ctx.Param("lang"))) }) + // get the default and current language + router.GET("/lang/default", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetDefaultLanguage(context).String()) + }) + + // get the current language + router.GET("/lang/current", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetCurrentLanguage(context).String()) + }) + if err := router.Run(":8080"); err != nil { log.Fatal(err) } diff --git a/ginI18n.go b/ginI18n.go index 7f69b18..5c144d5 100644 --- a/ginI18n.go +++ b/ginI18n.go @@ -1,4 +1,4 @@ -// ginI18nImpl is an implementation of the GinI18n interface, providing +// Package i18n ginI18nImpl is an implementation of the GinI18n interface, providing // localization support for Gin applications. It uses the go-i18n library // to manage and retrieve localized messages. // @@ -11,6 +11,9 @@ // Methods: // - GetMessage: Retrieves a localized message based on the provided context and parameter. // - MustGetMessage: Retrieves a localized message and returns an empty string if retrieval fails. +// - HasLang: Checks if a specific language is supported. +// - GetCurrentLanguage: Retrieves the current language based on the Gin context.. +// - GetDefaultLanguage: Retrieves the default language // - SetBundle: Sets the i18n.Bundle configuration. // - SetGetLngHandler: Sets the handler function to retrieve the language tag from the Gin context. // - loadMessageFiles: Loads all localization files into the bundle. @@ -42,6 +45,55 @@ type ginI18nImpl struct { getLngHandler GetLngHandler } +// GetDefaultLanguage retrieves the default language tag for the application. +// +// This method returns the default language tag that is used when no specific +// language is specified by the client or in the context. +// +// Parameters: +// - ctx: The Gin context from which to retrieve the message. +// +// Returns: +// - language.Tag: The default language tag. +func (i *ginI18nImpl) GetDefaultLanguage() language.Tag { + return i.defaultLanguage +} + +// GetCurrentLanguage retrieves the current language tag from the Gin context. +// +// This method extracts the language tag from the Gin context using the provided +// `getLngHandler` function. It uses this handler to obtain the language tag for +// the current request. If the language is not provided, it returns the default language. +// +// Parameters: +// - ctx: The Gin context from which to retrieve the message. +// +// Returns: +// - language.Tag: The language tag based on the context (either the specified language or the default language). +func (i *ginI18nImpl) GetCurrentLanguage(context *gin.Context) language.Tag { + return language.Make(i.getLngHandler(context, i.defaultLanguage.String())) +} + +// HasLang checks whether the specified language is supported by the application. +// +// This method checks if a language tag is available in the localizer map (`localizerByLng`), +// which stores localizers for all supported languages. If the language is supported, +// it returns `true`; otherwise, it returns `false`. +// +// Parameters: +// - ctx: The Gin context from which to retrieve the message. +// - language (string): The language tag (e.g., "en", "zh") to check. +// +// Returns: +// - bool: `true` if the language is supported, otherwise `false`. +func (i *ginI18nImpl) HasLang(language string) bool { + if _, exist := i.localizerByLng[language]; exist { + return true + } + + return false +} + // GetMessage retrieves a localized message based on the provided context and parameter. // If the message cannot be retrieved, it returns an empty string. // @@ -183,6 +235,16 @@ func (i *ginI18nImpl) getLocalizeConfig(param interface{}) (*i18n.LocalizeConfig MessageID: paramValue, } return localizeConfig, nil + case *i18n.Message: + localizeConfig := &i18n.LocalizeConfig{ + DefaultMessage: paramValue, + } + return localizeConfig, nil + case i18n.Message: + localizeConfig := &i18n.LocalizeConfig{ + DefaultMessage: ¶mValue, + } + return localizeConfig, nil case *i18n.LocalizeConfig: return paramValue, nil case i18n.LocalizeConfig: diff --git a/i18n.go b/i18n.go index a6e7781..54c41bf 100644 --- a/i18n.go +++ b/i18n.go @@ -2,6 +2,7 @@ package i18n import ( "github.com/gin-gonic/gin" + "golang.org/x/text/language" ) // newI18n ... @@ -66,3 +67,32 @@ func MustGetMessage(context *gin.Context, param interface{}) string { atI18n := context.MustGet("i18n").(GinI18n) return atI18n.MustGetMessage(context, param) } + +// HasLang check all i18n lang exists +// Example: +// HasLang(context, "ZH-cn") // return false or true +func HasLang(context *gin.Context, language string) bool { + atI18n := context.MustGet("i18n").(GinI18n) + return atI18n.HasLang(language) +} + +// GetDefaultLanguage get the default language +// Example: +// GetDefaultLanguage(context) +func GetDefaultLanguage(context *gin.Context) language.Tag { + atI18n := context.MustGet("i18n").(GinI18n) + return atI18n.GetDefaultLanguage() +} + +// GetCurrentLanguage get the current language +// Example: +// GetCurrentLanguage(context) +func GetCurrentLanguage(context *gin.Context) language.Tag { + atI18n := context.MustGet("i18n").(GinI18n) + return atI18n.GetCurrentLanguage(context) +} + +// I18n get GinI18n from gin.Context +func I18n(context *gin.Context) GinI18n { + return context.MustGet("i18n").(GinI18n) +} diff --git a/i18n_test.go b/i18n_test.go index 58a3ed7..4955464 100644 --- a/i18n_test.go +++ b/i18n_test.go @@ -2,6 +2,7 @@ package i18n import ( "context" + "fmt" "net/http" "net/http/httptest" "testing" @@ -20,7 +21,7 @@ func newServer() *gin.Engine { context.String(http.StatusOK, MustGetMessage(context, "welcome")) }) - router.GET("/:name", func(context *gin.Context) { + router.GET("/messageId/:name", func(context *gin.Context) { context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ MessageID: "welcomeWithName", TemplateData: map[string]string{ @@ -28,6 +29,27 @@ func newServer() *gin.Engine { }, })) }) + + router.GET("/messageType/:name", func(context *gin.Context) { + context.String(http.StatusOK, MustGetMessage(context, &i18n.LocalizeConfig{ + DefaultMessage: &i18n.Message{ + ID: "welcomeWithName", + }, + TemplateData: map[string]string{ + "name": context.Param("name"), + }, + })) + }) + + router.GET("/exist/:lang", func(context *gin.Context) { + context.String(http.StatusOK, "%v", HasLang(context, context.Param("lang"))) + }) + router.GET("/lang/default", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetDefaultLanguage(context).String()) + }) + router.GET("/lang/current", func(context *gin.Context) { + context.String(http.StatusOK, "%s", GetCurrentLanguage(context).String()) + }) router.GET("/age/:age", func(context *gin.Context) { context.String(http.StatusOK, MustGetMessage(context, i18n.LocalizeConfig{ MessageID: "welcomeWithAge", @@ -75,9 +97,17 @@ func TestI18nEN(t *testing.T) { want: "hello", }, { - name: "hello alex", + name: "hello alex - messageId", + args: args{ + path: "/messageId/alex", + lng: language.English, + }, + want: "hello alex", + }, + { + name: "hello alex - messageType", args: args{ - path: "/alex", + path: "/messageType/alex", lng: language.English, }, want: "hello alex", @@ -100,9 +130,17 @@ func TestI18nEN(t *testing.T) { want: "hallo", }, { - name: "hallo alex", + name: "hallo alex - messageId", args: args{ - path: "/alex", + path: "/messageId/alex", + lng: language.German, + }, + want: "hallo alex", + }, + { + name: "hallo alex - messageType", + args: args{ + path: "/messageType/alex", lng: language.German, }, want: "hallo alex", @@ -125,9 +163,17 @@ func TestI18nEN(t *testing.T) { want: "bonjour", }, { - name: "bonjour alex", + name: "bonjour alex - messageId", + args: args{ + path: "/messageId/alex", + lng: language.French, + }, + want: "bonjour alex", + }, + { + name: "bonjour alex - messageType", args: args{ - path: "/alex", + path: "/messageType/alex", lng: language.French, }, want: "bonjour alex", @@ -140,6 +186,57 @@ func TestI18nEN(t *testing.T) { }, want: "j'ai 18 ans", }, + // has exist + { + name: "i81n lang exist", + args: args{ + path: fmt.Sprintf("/exist/%s", language.English.String()), + lng: language.English, + }, + want: "true", + }, + { + name: "i81n lang not exist", + args: args{ + path: fmt.Sprintf("/exist/%s", language.SimplifiedChinese.String()), + lng: language.English, + }, + want: "false", + }, + // default lang + { + name: "i81n is default " + language.English.String(), + args: args{ + path: "/lang/default", + lng: language.English, + }, + want: language.English.String(), + }, + { + name: "i81n is not default " + language.German.String(), + args: args{ + path: "/lang/default", + lng: language.German, + }, + want: language.English.String(), + }, + // current lang + { + name: "i81n is current " + language.English.String(), + args: args{ + path: "/lang/current", + lng: language.English, + }, + want: language.English.String(), + }, + { + name: "i81n is not current " + language.English.String(), + args: args{ + path: "/lang/current", + lng: language.German, + }, + want: language.German.String(), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/interface.go b/interface.go index 11f9fe9..5bd9bca 100644 --- a/interface.go +++ b/interface.go @@ -2,6 +2,7 @@ package i18n import ( "github.com/gin-gonic/gin" + "golang.org/x/text/language" ) // GinI18n is an interface that defines methods for internationalization (i18n) in a Gin web framework context. @@ -20,4 +21,16 @@ type GinI18n interface { // SetGetLngHandler sets the handler function to determine the language from the context. SetGetLngHandler(handler GetLngHandler) + + // HasLang checks if the given language is supported by the i18n bundle. + // It returns true if the language is supported, false otherwise. + HasLang(language string) bool + + // GetDefaultLanguage returns the default language tag. + // It returns the default language tag. + GetDefaultLanguage() language.Tag + + // GetCurrentLanguage returns the current language tag from the context. + // It returns the current language tag. + GetCurrentLanguage(context *gin.Context) language.Tag }