Gin-i18n国际化多语言
前言
使用了国际化之后,模板中的文字就存放的都是以变量存在了,这会让结构完全变化,例如导航栏 页尾栏这种每个页面都可能需要的,为了不在每个路由都去传递这些数据,所以把导航栏和页尾很长的代码,直接放在全局变量中
首先因以英文为主体来生成其他翻译文件,
尽可能少存在复数(复数要额外处理不然翻译效果不好),能多用符号或者不用文字尽量少用
对动态内容,应使用数据库分类查询
mol
避免错误
json字段名不要使用“description”不然会造成整个翻译文件不可用
示范:
package main
import (
"github.com/gin-gonic/gin" //导入gin包
"github.com/BurntSushi/toml" //翻译文件使用toml也可以json
"github.com/nicksnyder/go-i18n/v2/i18n" //翻译主体
"golang.org/x/text/language" //语言标签
)
func main() {
bundle := i18n.NewBundle(language.English) //初始化,并指定一个默认语言
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) //指定翻译文件,使用toml,也可以是js
bundle.LoadMessageFile("locale/en.toml") //注意这里的文件名必须和语言代码匹配,例如en,zh-Hans
bundle.LoadMessageFile("locale/zh-Hans.toml") //加载另一个翻译文件
r := gin.Default() //初始化gin
r.LoadHTMLGlob("templates/*") //加载html模板全局,指定templates目录下的所有文件
r.Static("/static", "./static") //静态文件处理,参数:路由,本地路径
//开始路由函数
r.GET("/", func(c *gin.Context) {
lang := c.Query("lang") //如果传递了查询参数lang则指定语言
accept := c.GetHeader("Accept-Language") //从浏览器标头读取语言
localizer := i18n.NewLocalizer(bundle, lang, accept) //生成对象
// 获取翻译后的文本
helloMessage := localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "HelloPerson", //字段ID,对应翻译文件的字段名
TemplateData: map[string]interface{}{
"Name": "李坤朋", // 给翻译字段的内容使用变量,这个变量会在翻译时被替换,因为翻译内容不支持使用变量,只能在放入模板前先使用变量
},
})
//取当前文件语言代码
// 获取当前文件语言代码,用于传递给 <html lang>
langtag := localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "LangTag",
})
//将翻译对象传递给模板
c.HTML(200, "index.html", gin.H{"helloMessage":helloMessage,"langtag":langtag})
})
}
zh-Hans.toml:
#语言代码,供返回给<html lang>
LangTag = "zh-Hans"
HelloPerson = "是台湾和中国大陆 {{.Name}}"
en.toml
LangTag = "en"
HelloPerson = "Taiwan and china {{.Name}}"
index.html:
<!DOCTYPE html>
<html lang="{{.langtag}}">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/static/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试</title>
</head>
<body>
{{.helloMessage}}
</body>
</html>
英语翻译存在复数
使用[helle]这种标签也代表ID,而下面的字段则代表这个翻译的属性
查看https://github.com/nicksnyder/go-i18n/blob/main/i18n/example_test.go
使用goi18n命令
安装go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest
生成翻译文件,自动将代码中的i18n.Message 生成翻译内容,一般用不到goi18n extract
以英语为原本生成其他翻译文件:goi18n merge locale/en.toml locale/zh-Hans.toml
//该方法会在项目目录下生成去掉注释的active.en.toml,创建的翻译文件为translate.zh-Hans.toml
自己完整的架构和踩坑
- 翻译内容如果有
"
符号,需要加转义符\"
才可正常使用,否则会触发类似message "LangTag" not found in language "en"
的错误 - 翻译内容如果包含html标签,需自定义一个模板函数,将它不转义
- 翻译内容内部无法使用外部变量,只能通过i18n预先设置变量
自定义过滤器在模板中翻译
在模板中就像这样:{{t .c "app.hello"}}
t是自定义的翻译函数名,".c"是传递的gin上下文,“app.hello”对应翻译文件的字段id
//自动取翻译字段过滤器
func Localize(c *gin.Context,id string) string {
localizer := c.MustGet("localizer").(*i18n.Localizer) //从下面的中间件取对象
return localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: id})
}
//中间件实现最有选择语言
func Localizer(c *gin.Context) {
lang := c.Param("lang") //取语言代码
accept := c.GetHeader("Accept-Language") //从浏览器标头读取语言
localizer := i18n.NewLocalizer(Bundle, lang, accept) //生成对象
c.Set("localizer", localizer) //将取到的对象保存在localizer中
c.Next()
}
func Router() *gin.Engine {
r := gin.Default() //初始化gin
//自定义过滤器
r.SetFuncMap(template.FuncMap{
"t": Localize, //自己实现的翻译过滤器
})
r.LoadHTMLGlob("templates/*") //加载html模板全局,指定templates目录下的所有文件
//上下文
//自定义中间件
r.Use(Localizer)
//假设这里是其他路由规则
}
//首页重定向,如果未传递语言,首页专用
func Chongdingxiang(c *gin.Context) {
accept := c.GetHeader("Accept-Language") //从浏览器标头读取语言
localizer := i18n.NewLocalizer(Bundle, accept) //生成对象
//这里面的LANG是每个翻译文件的字段,分别对应自己的语言,这样提取LANG就知道当前是哪个语言
lang := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "LANG"})
//重定向
c.Redirect(302, "/"+lang)
}
翻译html时不转义
//自定义过滤器让翻译不转义
func Html(x string) interface{} {
return template.HTML(x)
}