Golang基于GNU gettext方式的i18n国际化多语言集成方式总结
2022-6-23 23:37:12 Author: axiaoxin.com(查看原文) 阅读量:55 收藏

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

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的库也有好几个,这里我选择使用https://github.com/chai2010/gettext-go,代码比较简洁而且支持embed。唯一折腾了半天的是mo文件的路径问题,必要按固定规则才行,尝试了很多遍才成功。


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

  1. 生成你的po、mo文件。
  2. 必须先绑定你的mo翻译文件:gettext.BindLocale(gettext.New("domain", "path")),这里的domain和path一定要注意命名,domain你可以记忆为mo文件的名称,path是存放你所有多语言的父目录路径,整个路径为 path/xx/LC_MESSSAGES/yy.mo,其中path就是New方法中的参数路径,xx为任意名字标识语言,LC_MESSSAGES路径必须要,yy为domain参数值,只有这样才能读取到。
  3. 完成绑定后调用gettext.SetLanguage("xx")指定你要取的目标语言
  4. 再调用gettext.Gettext传入po文件中msgid的值就能返回对应语言的msgstr

gin中集成i18n:

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

获取用户的语言首先用户可以通过url参数指定语言,一旦指定语言,就将该语言保存到cookie中,下次从cookie获取,如果cookie没有则获取用户请求头中的Accept-Language,可以使用golang.org/x/text/language这个包提供的ParseAcceptLanguage方法获取最匹配的语言作为用户语言。

中间件实现可参考pink-lady GinSetLanguage中间件设置i18n语言

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

模板注册参考pink-lady模板方法注册


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

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

比如html模板中的模板方法写法{{ _text "翻译我"}}无法被识别,可以替换为c语言版本的gettext("翻译我")后在提取为html.pot:

find . -name "*.html" | xargs sed "s/{{\s\{0,1\}_text \(\".*\"\)\s\{0,1\}}}/{{ gettext(\1) }}/g" | xgettext --no-wrap --no-location --language=c --from-code=UTF-8 --output=html.pot -

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

find . -name "*.go" | xargs sed "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生成各种语言的翻译文件后编译即可。


文章来源: http://axiaoxin.com/article/263
如有侵权请联系:admin#unsafe.sh