前阵子对一个历史项目做了国际化改造,时下流行框架基本上都提供了自己的国际化标准,预留了国际化模块。本文讲的是从零开始的国际化方案,适用于改造已有项目。如果您使用的是这些标准规范的框架,那可以参考您的框架文档。
市面上提供国际化解决方案有很多种,大致原理都是标记一个带翻译的key, 提供一个语言转换函数和对应的语言包,翻译这些标记的key。这里介绍一个使用广泛的,提供跨语言支持的方案 —GNU gettext utilities。规范完整,考虑周全,提供了各种语言示例,就是它了,我调研时整理了一份国际化解读。
建议如果做之前,详细的阅读3遍,如果遇到问题了,仔细看规范,上面有你想要的一切答案。
由于gettext运行时,跟系统已安装的本地化语言包有关。如果你要设置的语言和编码,系统没有会导致翻译失效。可以通过locale -m
查看环境支持的编码列表。locale -a
查看已安装的语言列表。如果没有,请参考规范安装对应的语言包。
标记完成待翻译的内容之后,使用命令可以提取这些待翻译标签到po文件中,由翻译人员去审核校验。
#: lib/error.c:116
msgid "Unknown system error"
msgstr "Error desconegut del sistema"
#: src/msgcmp.c:338 src/po-lex.c:699
#, c-format
msgid "found %d fatal error"
msgid_plural "found %d fatal errors"
msgstr[0] "s'ha trobat %d error fatal"
msgstr[1] "s'han trobat %d errors fatals"
po文件校验无误之后,使用编译命令,将po文件编译成mo文件,项目加载时,读取的是mo文件。
这里以后端语言PHP为例来演示下,如何实现国际化。
brew install gettext
$lang = "zh_CN";//语言为了方便映射翻译目录,最好跟规范保持一致
$codeset = "UTF-8";
$language = $lang.'.'.$codeset;
//坑1:必须先更改环境变量LANG或LC_ALL的值(不能是默认值'C'),才能通过LANGUAGE使用优先级列表
$putRes1 = putenv('LANG='.$language);// 坑2: 如果失败NULL,说明当前运行环境不支持该语言或编码
$putRes2 = putenv('LC_ALL='.$language);
$putRes3 = putenv('LANGUAGE='.$language);
$category = defined('LC_MESSAGES') ? LC_MESSAGES : LC_ALL;
//兼容优化: 提供一个优先级列表,可以优先设置目标语言,万一失败,可以向后兼容,直到默认。
setlocale($category,array($language,$lang,'zh_CN.UTF-8'));
//设置默认语言包位置
bindtextdomain("messages", LOCALE_ROOT);
bind_textdomain_codeset("messages", 'UTF-8');
textdomain("messages");
/** php **/
//正常翻译 gettext || _
_(),gettext(),ngettext()
//多个domain切换 dgettext()/dcgettext()
bindtextdomain("module1", "/path/to/my/locale/folder");
bindtextdomain("module2", "/path/to/my/locale/folder");
textdomain("module1");
// this call will get the message from module1
echo _("Label1");
// this call will get the message from module2
echo dgettext("module2", "Label1");
xgettext --add-comments=TRANSLATORS: --keyword=gettext --keyword=_ --output=messages.po
msgfmt -o test.mo test.po
- 除了php需要国际化外,有些模板引擎也需要,比如smarty,有插件支持,js 不需要这个,js依赖json。也可以先生成po,再转换成json。
- gettext 命令功能强大,支持你想要的各种需求处理,包括合并,分割,去重等等,建议仔细看下。
- gettext需要固定的语言包子路径:
locale/en_US/LC_MESSAGES/messages.po
,不可以随便建。
当然,如果遇到错误异常,仍然需要人工干预,但是正常是不需要的。下面是脚本演示:
MacBook-2:www lemon$ php MarkTag.php -f project/index.php
No syntax errors detected in project/index.php
Mark ok, total: 1, pid is 43884, start translating ...
1.read cn ok
2.get en ok
3.merged cn & en ok
4.replace cn ok
Great, translate ok! start compiling ...
check en-us fuzzy and deal with it
msg unique and compile ok !
No js msg to translate
(完)