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

更新日期: 2022-06-09 阅读次数: 7965 字数: 815 分类: golang

需求

我想在 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 聊聊, 查看更多联系方式