Archive:Development/Tutorials/Localization/i18n Build Systems (zh CN)
摘要
现在您的程序能够本地化了,我们接下来考虑怎样把这个重要的机能合并到您程序的Cmake系统中去。 首先我们“理论”上解释用于安装.po文件时解压缩信息字符串所需要的步骤。然后我们来看怎么实现这些步骤(#Theory: The xgettext toolchain)。如果您的程序书用KDE的subversion开发的,很多步骤是自动完成的(参见 #handling i18n in KDE's subversion repository)。 如果不是,请参见 #handling i18n in third party applications 部分。
理论: xgettext 工具
翻译需要以下步骤:
- 提取出要翻译的字符串
- 将新翻译的字串和已有的翻译合并在一起
- 将翻译编译进消息目录
- 安装消息目录
- 在程序中使用消息目录
提取字符串
本步骤中,所有您代码中标记为 i18n()/ki18n()/etc. 的字串需要被收集到一个临时翻译文件 (.pot) 文件中。 有些待翻译字串在.ui, .rc, or .kcfg 文件中. 此外也要把每日一帖搜集到.pot文件中.
这个步骤是由 xgettext, extractrc, 和 preparetips 这些程序分别完成的。有些情况下,您也会用到 extractattr.
合并翻译结果
一般来讲,某次只有少部分的可翻译字串需要更改:有些需要被移除,有些需要被添加,有些要被替换,有些要改变在代码中的位置。这些改变需要在翻译中得到体现,但显然每次(更新)都重做所有的翻译是一件耗力的工作。 把原有的翻译和那些改变的东西合并在一起会更好。这时需要 msgmerge 工具。
编译翻译结果
为了让信息查找更快,.po文件需要编译成所谓的 "消息目录" (.mo / .gmo)。这可由 msgfmt 工具完成.
安装消息目录
编译好的信息目录需和程序一起安装。KDE系统中,标准的消息目录的路径是 $KDEDIR/share/locale/xx/LC_MESSAGES/。
使用消息目录
最后,但程序能够运行,程序需要载入消息目录来查找和显示翻译的字串。 对KDE程序而言,在大多数情形,这是由KLocale 类自动完成的。
一些特殊的情形,比如说插件,请参看 #Runtime Loading Of Catalogs.
在KDE的 subversion库中处理
如果您使用KDE subversion库做开发,上述大部分的步骤都是自动完成的。此时,您只需要提供一个Messages.sh脚本。我们下面会提到这个脚本。
此外,如果您的根目录和.pot文件重名,当您对代码打包时,svn2dist脚本会自动包含.po文件
当然,进行这一步骤所做的详细幕后信息会被提供。
编辑 Messages.sh 脚本
通常地,准备和安装一个KDE subversion 库程序的翻译只需要提供“信息”。包括了需要翻译的代码, ui文件和提示。就此而言,您只要写一小段 Messages.sh 代码,并把他放置到您的代码中就好了。下面是一个带注释的例子:
- !bin/sh
- invoke the extractrc script on all .ui, .rc, and .kcfg files in the sources
- the results are stored in a pseudo .cpp file to be picked up by xgettext.
$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp
- if your application contains tips-of-the-day, call preparetips as well.
$PREPARETIPS > tips.cpp
- call xgettext on all source files. If your sources have other filename
- extensions besides .cc, .cpp, and .h, just add them in the find call.
$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/APPNAME.pot
可以发现,这段脚本实际上只含有三行代码,而且这三段并不都是必要的。$XGETTEXT, $PREPARETIPS, $EXTRACTRC 和 $podir 环境变量是预先定义好的,不需要考虑。您只需要将 "APPNAME" 替换成您的程序的名字 (参见 #Naming .pot Files 描述了例外的情形)。
请确定您的Messages.sh 脚本概括了所有需要的信息。如有疑问,请查看其它KDE subversion库中程序的示例,或者,询问。
幕后的故事
每隔一段时间, KDE 服务器会运行extract-messages.sh (又称为scripty) 脚本。这个脚本会使用适当的参数调用库中所有的Messages.sh。 提取所得的信息胡储存在一个l10n模块的一个临时文件夹的临时的 (.pot) 文件。参见 What Is Scripty。
The KDE translation teams translate the messages and commit them into a messages folder corresponding to the language, which is further broken down by module. For example, German translated messages for konqueror are committed to l10n/de/messages/kdebase/konqueror.po.
When the l10n module is built, the .po files are compiled into a binary format for fast lookup and installed as .mo files to $KDEDIR/share/locale/xx/LC_MESSAGES/, where xx is the two-letter ISO 639 code for the language. These are called the message catalogs.
At runtime, the i18n(...) function, using the original string you coded, looks up the string in the message catalog of the user's desktop language and returns the translated string. If the message catalog is missing, or the specific string is not found, i18n(...) falls back to the original string in your code.
.desktop files in your project are handled separately. makemessages extracts strings, such as Name and Comment from the .desktop files and places them into a file named desktop_mmmm.pot, where mmmm is the module name, in the templates folder. Once translators have translated this file, makemessages inserts the translated strings back into the .desktop files. The list of strings extracted is in l10n/scripts/apply.cc. Here's the code that checks for them:
if (checkTag("Name", in, argc, argv, newFile))
continue;
if (checkTag("Comment", in, argc, argv, newFile))
continue;
if (checkTag("Language", in, argc, argv, newFile))
continue;
if (checkTag("Keywords", in, argc, argv, newFile))
continue;
if (checkTag("About", in, argc, argv, newFile))
continue;
if (checkTag("Description", in, argc, argv, newFile))
continue;
if (checkTag("GenericName", in, argc, argv, newFile))
continue;
if (checkTag("Query", in, argc, argv, newFile))
continue;
if (checkTag("ExtraNames", in, argc, argv, newFile))
continue;