Golang基于GNU gettext方式的i18n国际化多语言集成方式总结

18n国际化多语言本质上就是先写好一堆映射,在根据想要的语言取对应的文字。

Golang的i18n网上查了一下,文章都讲的不太细致,而且代码看起来也不太好理解。

之前写Python代码时有使用过babel做多语言集成,通过命令生存pot、po、mo等文件,然后动态获取即可,当时没有详细去研究其原理,今天了解了一下,其实是封装的的GNU gettext,gettext就是专门用来做多语言的,它由一系列命令行工具,比如xgettext、msginit、msgmerge、msgfmt等,像linux和mac默认都自带安装好了。

Python的babel流程主要是先提取代码中需要设置多语言的文字生成一个.pot格式的模板文件,然后根据这个模板创建对应语言的.po翻译文件,然后把.po文件编译成.mo文件就可以被函数动态读取。当代码中的多语言文字修改或新增了,就需要再次生成一下新的pot模板文件,把新增的多语言文字加到模板中,然后讲新模板和之前翻译好的po文件进行合并,合并后原来的po文件会将本次新增的多语言加入进来,然后对其进行翻译,完成后编译新的mo文件。

以上这个流程在gettext命令工具中分别需要使用xgettext提取代码中的多语言生成模板、msginit创建po文件、msgfmt编译为mo文件、msgmerge合并新的po文件。当拥有po、mo文件时,就能使用多种支持的语言进行读取了。

由于比较熟悉这一套流程,因此研究了一下在golang和其web框架比如gin中如何使用gettext实现多语言集成,这里记录一下要点。

golang的库也有好几个,这里我选择使用GitHub - chai2010/gettext-go: GNU gettext for Go (Imported By Kubernetes),代码比较简洁而且支持embed。唯一折腾了半天的是mo文件的路径问题,必要按固定规则才行,尝试了很多遍才成功。

Golang中使用的大致流程说明:

gettext.BindLocale(gettext.New("domain", "path"))path/xx/LC_MESSSAGES/yy.moLC_MESSSAGESgettext.SetLanguage("xx")gettext.Gettext

gin中集成i18n:

golang中获取多语言成功了,在gin中就容易了。可以使用中间件获取用户指定的获取可能的语言来设置目标语言,代码中的返回都使用gettext.Gettext获取即可,需要注意的时gettext.Gettext的参数必须有对应的翻译文字才行。

golang.org/x/text/languageParseAcceptLanguage

如果要在html模板中替换多语言,可以将gettext.Gettext注册到gin的自定义模板方法中,然后在html中所有需要被翻译的地方都使用这个模板方法处理一下,这样就能得到多语言。

关于自动提取代码中的待翻译的文字生成pot模板:

通过xgettext无法直接提取golang代码和golang html模板中的翻译文字,解决办法是sed替换为xgettext能识别的字符,然后再提取。

{{ _text "翻译我"}}gettext("翻译我")
find . -name "*.html" | xargs perl -pe "s/{{\s*_text [\"\`](.+?)[\"\`]\s*}}/{{ gettext(\"\1\") }}/g" | xgettext --no-wrap --no-location --language=
   │ c --from-code=UTF-8 --output=html.pot -

go文件中也无法识别,同样操作替换后再提取为go.pot

find . -name "*.go" | xargs perl -pe "s/gettext.Gettext/gettext/g"  | xgettext --no-wrap --no-location --language=c --from-code=UTF-8 --output=go.pot -

然后再把两个pot合并为一个pot文件(messages.pot):

xgettext --no-wrap --no-location *.pot -o messages.pot

最后我们统一使用这个pot生成各种语言的翻译文件后编译即可。