在Lua中使用gettext实现多语言支持
Using gettext in lua
2016-06-07更新:加入gettext包含的工具简介。
GNU gettext 是一套优秀的国际化工具。在 linux 中被大量采用。wordpress 也使用 gettext 实现多语言支持。
本文介绍如何在 quick-cocos2d-x 中使用 gettext 做多语言支持。同时介绍多语言翻译工具 poedit对 Lua 语言的支持。
gettext 是一套工具集的名称。这套工具集包含 xgettext/msginit/msgfmt 等一套建立模版(POT)、创建PO文件和编译MO文件的工具。
gettext 包含的工具如下:
使用 gettext 需要涉及这样几个概念:
一般的工作流程是这样的:
gettext("my text")
。在本文中,将使用 _("my text")
;_
函数,这个函数将读取并解析 MO 文件,根据调用的原始语言文本返回翻译之后的文本。gettext 提供的工具集都是基于命令行的,有些程序员并不习惯命令行。它也没有提供用于翻译工作者的对照翻译工具。
而 Poedit 则提供了一切。
Poedit 可以从源码中提取文本生成 PO 文件,也提供了一个GUI界面用于对照翻译。它还可以直接生成最终的 MO 文件。
当然,在GUI之后,它依然是使用 gettext 来处理的,但这些具体的细节被隐藏了。
1.开启 Poedit,执行 File – Preferences 命令,填入自己的个人信息。
2.切换到 Editor tab,选中 Automatically compile .mo file on save 和 Show summary after catalog update 两项。
3.切换到 Parsers tab,这里提供了几种源码解析器,但默认没有Lua。我们下面将加入Lua源码解析。
4.点击 New 新建一个源码解析器,进行如下图的设置。
这些设置基本上与 C/C++ 的设置一致,只是修改了扩展名支持以及 Parser command 栏位。在 xgettext 命令的参数中,-C 就是 –language=C 的简写。
接下来将新建一个项目进行设置。
1.执行 File – New catelog… 命令,在 Translation properties tab 中加入自己的信息。这里的 Language 选项只是个显示选项而已,并不能决定你使用的是何种语言,也不和刚才的软件设置中的 Parsers 相关。
2.设定源代码路径,这个非常重要。
我采用 quick 默认的目录风格,所有的源码放在 scripts 中。同时我建立了一个新的 i18n 目录,这个目录与 scripts 目录同级。生成的 PO 文件将保存在这个目录中。
那么对于当前正在编辑的 PO 文件来说, Base path 就应该设置成 ..\scripts
, 下面的 Paths 就应该加入 . 这个路径。注意在 Windows 下, Base path 的路径分隔符必须采用 Windows 的格式(反斜杠 \ ),而不应该使用 / 。
3.设定源代码关键词
前面我们已经确定了自己的关键词,那就是 _
。当然我们也可以使用 C 语言的默认关键词gettext
,但那样未免长了一点。
4.设定完毕后,将项目保存为 i18n/zh_CN.po
,请注意我上面强调过的第2步。
一切设定成功之后,直接按下主界面上的 Update
按钮即可解析源码。Poedit 会将源码中解析到的使用 _("my text")
格式的关键词,将其中的字符串提取处理显示在主界面中。
我们可以在主界面中对关键词进行翻译。
如果源码有更新,再次单击 Update
按钮重新解析源码。这是如果解析到新的字符串,或者有字符串不再使用,Poedit都会进行提示。
需要注意的一点是,在 Lua 的注释中不要包含半角的单引号。
这是由于 Lua 的注释使用 — 符号,而 C 语言使用 // ,所以 gettext 会认为 Lua 中的注释是代码的一部分而不会忽略解析。在一般情况没有什么问题,但如果在注释中包含了半角的单引号(这是在 C 语言中代表字面值),gettext 就会认为语法错误因此产生解析错误。
在软件设置中,我们设置了在保存项目的时候自动生成 MO 文件,所以这点不用操心了。生成的 MO 文件与 PO 文件在相同的目录。
MO 是个二进制格式的文件,我们的程序在读取它的时候,需要分析 它的结构 ,将它解析成 原始文本 - 翻译文本
的键值对形式,供我们使用。
许多语言中已经包含了 MO 文件的解析库,但是 Lua 没有。所以我们需要自己写一个。
不过 J.J?rgen von Bargen 已经完成了这件事,我将他的代码进行了简单封装,将其放在我的 lua 库中了,名称是 utils.Gettext。
由于要支持跨平台的原因,读取 MO 文件采用的是 CCFileUtils ,如果要在其他环境中使用,可以改用 io 库。我在 Gettext._getFileData
中保留了使用 io 库的代码,只是将其注释了。
使用这种方法,MO 文件会被解析成功一个 table 返回:
local mo_data=assert(require("utils.Gettext").loadMOFromFile("res/zh_CN.mo")) print(mo_data["Hello World!"]) -- 你好,世界 print(mo_data["Foobar"]) -- nil
更通用的方法是这样:
_ = assert(require("utils.Gettext").gettextFromFile("res/zh_CN.mo")) print(_("Hello World!"))
在我的游戏中,则是这样使用的:
LANG = "res/zh_CN.mo" function _(__text) return __text end if CCFileUtils:sharedFileUtils():isFileExist(LANG) then _ = assert(require("utils.Gettext").gettextFromFile(LANG)) end