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)
 }