golang gin 的多语言网站国际化支持 i18n

文章目录

    需求

    我想在 golang gin 同一套网站模板中同时支持中文和英文,类似 SpringBoot 中的 Thymeleaf I18N 那样,能在模板中调用翻译。

    <label th:text="#{label}"></label>
    

    改造思路

    但是 gin i18n 的文档里并没有介绍如何在 template 中使用翻译。大概是很少有人用 gin 写网站吧,都是如何在 api 中使用翻译的方式。

    Google 搜素了半天,几乎没找到几个模板国际化相关的参考。好不容易找到一个台湾大哥的实现方案,给了我很大启发。

    其实就是使用 golang template 的自定义函数功能。


    https://siongui.github.io/2016/01/19/i18n-go-web-application-by-gettext-html-template/

    const tmpl = `
    <span>{{gettext "Home"}}</span>
    <span>{{gettext "About"}}</span>
    `
    
    func main() {
    	setup("zh_TW", "messages", "locale")
    	setup("vi_VN", "messages", "locale")
    	funcMap := template.FuncMap{
    		"gettext": translate,
    	}
    
    	t, _ := template.New("foo").Funcs(funcMap).Parse(tmpl)
    

    这个方案的实现非常像 IBM 的风格,从作者资料看确实在 IBM 呆过。。。

    The new template package allows you to at add a function to template’s function map, that would transform the given string to a localized version.

    golang gin 模板 i18n 实现方案

    参考 gin i18n 的这个封装库的 example 目录里的代码

    https://github.com/gin-contrib/i18n

    我感觉只要把 ginI18n.MustGetMessage 封装一下,映射到模板中,就基本满足我的使用需求了。

    修改默认配置

    https://github.com/gin-contrib/i18n/blob/master/constant.go

    里面显式指定了翻译文件的文件类型,及所在目录。

    所以项目里可以分离出一个 i18n 文件来写死这些配置。也方便在其他不需要此功能的项目,能快速删除代码。

    开干!

    安装 gin-contrib/i18n

    go get github.com/gin-contrib/i18n
    

    需要注意的是:

    • gin-contrib/i18n 同时自动安装了 nicksnyder/go-i18n
    • 自动升级了 gin 的版本
      go: added github.com/gin-contrib/i18n v0.0.1 go: upgraded github.com/gin-gonic/gin v1.7.2 => v1.7.4 go: added github.com/nicksnyder/go-i18n/v2 v2.1.2

    代码实现

    i18n.go

    • 将默认语言设置为了中文。因为主要客户在国内,但是对于某些业务,还是设置为英文合适
    • 只判断 url 中的语言参数,而不判断 http 头里的 Accept-Language。默认配置会优先使用 Accept-Language,但是我不喜欢这种逻辑
    • 选择 toml 是因为示例基本都是 toml 的,看起来对复杂场景(复数、整句带变量)支持较好。
      package main import ( "github.com/BurntSushi/toml" ginI18n "github.com/gin-contrib/i18n" "github.com/gin-gonic/gin" "golang.org/x/text/language" ) // apply i18n middleware // router.Use(GinI18nLocalize()) func GinI18nLocalize() gin.HandlerFunc { return ginI18n.Localize( ginI18n.WithBundle(&ginI18n.BundleCfg{ RootPath: "./lang", AcceptLanguage: []language.Tag{language.Chinese, language.English}, DefaultLanguage: language.Chinese, FormatBundleFile: "toml", UnmarshalFunc: toml.Unmarshal, }), ginI18n.WithGetLngHandle( func(context *gin.Context, defaultLng string) string { lng := context.Query("lang") if lng == "" { return defaultLng } return lng }, ), ) }

    main.go

    r := gin.Default()
    
    // apply i18n middleware
    r.Use(GinI18nLocalize())
    
    // 自定义模板函数
    r.SetFuncMap(template.FuncMap{
    	"Localize":     ginI18n.GetMessage,
    })
    

    lang 目录,存放翻译资源文件

    > tree lang/
    lang/
    ├── en.toml
    └── zh.toml
    
    > cat lang/en.toml
    News = "News"
    
    > cat lang/zh.toml
    News = "新闻资讯"
    

    模板 index.html 中使用

    <h2>{{ Localize "News" }}</h2>
    

    链接格式:

    默认中文
    http://localhost:9014/
    
    英文
    http://localhost:9014/?lang=en
    
    中文
    http://localhost:9014/?lang=zh
    
    没有法语资源,所以使用默认的中文翻译
    http://localhost:9014/?lang=fr
    

    测试了一下,成功!

    在线效果演示

    例如,我写了个小 demo:

    感慨

    • golang gin 的文档真是少,即便找到了 i18n 的库,也得看代码才知道怎么用。相比 SpringBoot 详尽的文档,感觉看 go 的源码更爽一点。。。
    • 缺乏标准,就得花大精力去选库,确实挺耗费精力

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式