国际化(i18n)的本质是为不同语言准备一套映射,然后根据用户的语言需求提供对应的文本。在 Golang 中,借助 GNU gettext 及其封装库可以轻松实现这一功能。本文将深入探讨如何使用 gettext-go 实现 Golang 项目的国际化,尤其是在 Gin 框架中的集成方式。
GNU gettext 简介
Golang 的 i18n 多语言方案网上查了一下,文章都讲的不太细致,而且代码看起来也不太好理解。
之前写 Python 代码时有使用过 pybabel 做多语言集成,通过命令生成 pot、po、mo 等文件,然后代码中通过函数取 msgid 就能动态获取到指定的语言,pybabel 实现原理其实就是对 GNU gettext 进行了封装。
gettext 是专门用来做多语言的,它由一系列命令行工具,比如 xgettext、msginit、msgmerge、msgfmt 等,像 linux 和 mac 默认都自带安装好了。
pybabel 使用流程主要是先提取代码中需要设置多语言的文字生成一个 .pot 格式的模板文件,然后根据这个模板创建对应语言的 .po 翻译文件,然后把 .po 文件编译成 .mo 文件就可以被函数动态读取。
当代码中的多语言文字修改或新增了,就需要再次生成一下新的 pot 模板文件,把新增的多语言文字加到模板中,然后讲新模板和之前翻译好的 po 文件进行合并,合并后原来的 po 文件会将本次新增的多语言加入进来,然后对其进行翻译,完成后编译新的 mo 文件。
以上这个流程在 gettext 命令工具中分别需要使用 xgettext 提取代码中的多语言生成模板、msginit 创建 po 文件、msgfmt 编译为 mo 文件、msgmerge 合并新的 po 文件。当拥有 po、mo 文件时,就能使用多种支持的语言进行读取了。
由于比较熟悉这一套流程,因此研究了一下在 golang 和其 web 框架比如 gin 中如何使用 gettext 实现多语言集成,这里记录一下要点。
在 Golang 中使用 gettext-go 实现 i18n 多语言
golang 的 gettext 库也有好几个,这里我选择使用 gettext-go ,代码比较简洁而且支持 embed。唯一折腾了半天的是 mo 文件的路径问题,必要按固定规则才行,尝试了很多遍才成功。
实现的基本步骤如下:
(1)生成 PO 和 MO 文件
gettext 的工作流程与许多其他编程语言类似。你需要先通过 gettext 工具生成.pot 文件模板,并创建对应语言的.po 文件进行翻译。最后,编译成.mo 文件以供程序动态读取。
(2)绑定 MO 翻译文件
要使程序能够正确使用翻译文件,需要先绑定路径:
gettext.BindLocale(gettext.New("domain", "path"))
这里的 domain 是 MO 文件名,而 path 是翻译文件的父目录路径。例如,路径格式应为 path/xx/LC_MESSAGES/yy.mo
,其中 xx 代表语言代码,yy 是对应的域名(即 MO 文件名)。
(3)设置目标语言
在绑定了 MO 文件后,通过调用gettext.SetLanguage("xx")
来设定当前语言,确保系统根据用户需求切换到合适的语言。
(4)动态获取翻译
最后,通过gettext.Gettext("msgid")
,可以根据传入的 msgid 获取相应的翻译文本。
在 Gin 框架中集成 gettext-go 实现 i18n 多语言
golang 中获取多语言成功了,在 gin 中就容易了。将 gettext-go 应用于 Gin 框架非常简单,只需借助中间件管理语言选择即可。以下是 Gin 框架中的主要实现步骤:
(1)获取用户语言偏好
可以通过 URL 参数、Cookie 或请求头中的 Accept-Language 字段确定用户偏好的语言。
我在 Gin web 开发项目模版——pink-lady。使用中间件获取用户指定的或者请求头中可能的语言来设置目标语言,代码中的返回都使用 gettext.Gettext 获取即可,需要注意的时 gettext.Gettext 的参数必须有对应的翻译文字才行。
网上可以搜到的文章基本都是使用类似 gin-contib/i18n的实现方案,个人觉得使用起来不是很好用,因为在多语言处理时,使用 POEdit 这种专业翻译的软件可以很友好的完成人工的翻译 review,他可以提示你各种错误,类似于我们编程使用的 IDE。
获取用户的语言首先用户可以通过 url 参数指定语言,一旦指定语言,就将该语言保存到 cookie 中,下次从 cookie 获取,如果 cookie 没有则获取用户请求头中的 Accept-Language,可以使用 golang.org/x/text/language
这个包提供的 ParseAcceptLanguage
方法获取最匹配的语言作为用户语言。
相关阅读:Go 语言中处理多语言支持:如何实现语言与地区匹配
(2)动态获取翻译
在项目中,所有返回给用户的文字内容都可以通过 gettext.Gettext()
方法实现多语言支持。可以在响应内容中动态替换 msgid 为相应的翻译。
自定义 Gin i18n 中间件实现
中间件实现可参考 pink-lady GinSetLanguage 中间件设置 i18n 语言。
如果要在 html 模板中替换多语言,可以将 gettext.Gettext 注册到 gin 的自定义模板方法中,然后在 html 中所有需要被翻译的地方都使用这个模板方法处理一下,这样就能得到多语言。
模板注册参考 pink-lady 模板方法注册
需要注意的问题:
- 不要在一个请求处理时创建 gettexter,当你的翻译文件变大后,服务会卡死。
- 为每一种语言初始化一个对应的 gettexter,不要所有语言共用一个 gettexter,否则并发量上来后会出现语言错乱。
gettext 自动化提取翻译文本生成 pot 模板
通过 xgettext 无法直接提取 golang 代码和 golang html 模板中的翻译文字,解决办法是 sed 替换为 xgettext 能识别的字符,然后再提取。
比如 html 模板中的模板方法写法 {{ _text "翻译我"}}
无法被识别,可以替换为 c 语言版本的 gettext("翻译我")
后在提取为 html.pot:
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 生成各种语言的翻译文件后编译即可。
SEO 与国际化优化
目前 pink-lady 的语言切换方式和 gin-contrib/i18n 的实现是类似的,是通过请求头中的 Accept-Language 或 url querystring 参数来判断的,这对 i18n 功能的实现来说是完全满足要求的,但是对于 SEO 却不友好,同一个 url 有多种语言,内容相同但语言不同,搜索引擎它不好理解,这也是待优化的地方。
通常情况下,URL 中的语言路径是 SEO 友好的,例如使用 https://example.com/en/post/1 的形式明确表示语言。与之相反,通过 Accept-Language 或 URL 参数进行语言切换虽然方便,但会导致 SEO 问题:同一 URL 下有多种语言版本,内容重复但语言不同,可能导致搜索引擎无法正确索引。
为了优化 SEO,建议在 URL 结构中包含语言路径,例如:
/en/
/de/
这种方式不仅提升了搜索引擎的理解能力,也有助于用户清晰区分不同语言版本。
提升网站的国际化质量
除了技术实现外,提升国际化的内容质量也至关重要。可以通过以下几点优化国际化内容:
- 使用专业翻译工具:如 POEdit,可以有效进行翻译的质量检查,避免常见的语法和逻辑错误。
- 减少请求中的动态语言创建:不要在每次请求中重新生成翻译器,特别是在翻译文件较大时,容易导致性能瓶颈。
- 合理管理并发请求:为每种语言单独初始化翻译器,避免出现语言错乱的问题。
实现全站内容的自动化翻译
以上我们实现了 Golang 代码和 HTML 代码中的文字支持多语言,但数据库中的动态内容依然还不支持多语言,如何自动化翻译数据库中的 i18n 多语言内容呢?
相关阅读:《从自动翻译到全站内容国际化:使用 Golang 和 GNU Gettext 打造多语言 i18n 网站》
总结
通过本文,你可以学会如何在 Golang 和 Gin 框架中使用 GNU gettext 实现国际化支持。这一解决方案不仅简单高效,还能通过模板注册和中间件处理,实现灵活的多语言支持。希望通过 SEO 优化的内容策略和全面的 i18n 方案,帮助提升你的网站排名,吸引更多全球用户。