国际化那点儿事儿

国际化那点儿事儿

  • 前言
    • 方案调研
    • 规范说明
      • 系统运行说明
      • 重要概念
      • 实施流程
      • 改造过程

前言

前阵子对一个历史项目做了国际化改造,时下流行框架基本上都提供了自己的国际化标准,预留了国际化模块。本文讲的是从零开始的国际化方案,适用于改造已有项目。如果您使用的是这些标准规范的框架,那可以参考您的框架文档。

方案调研

市面上提供国际化解决方案有很多种,大致原理都是标记一个带翻译的key, 提供一个语言转换函数和对应的语言包,翻译这些标记的key。这里介绍一个使用广泛的,提供跨语言支持的方案 —GNU gettext utilities。规范完整,考虑周全,提供了各种语言示例,就是它了,我调研时整理了一份国际化解读。

建议如果做之前,详细的阅读3遍,如果遇到问题了,仔细看规范,上面有你想要的一切答案。

规范说明

系统运行说明

由于gettext运行时,跟系统已安装的本地化语言包有关。如果你要设置的语言和编码,系统没有会导致翻译失效。可以通过locale -m 查看环境支持的编码列表。locale -a 查看已安装的语言列表。如果没有,请参考规范安装对应的语言包。

重要概念

  • po文件 可移植对象(Portable Object)

标记完成待翻译的内容之后,使用命令可以提取这些待翻译标签到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"
  • mo文件 机器对象(Machine Object)

po文件校验无误之后,使用编译命令,将po文件编译成mo文件,项目加载时,读取的是mo文件。

实施流程

这里以后端语言PHP为例来演示下,如何实现国际化。

  1. 预装gettext系统命令及php gettext扩展。如Mac下:brew install gettext
  2. 设置语言和编码环境变量,兼容写法,环境变量参考规范
$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");
  1. 代码标记
/** 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"); 
  1. 使用xgettext提取翻译标记生成po文件
xgettext --add-comments=TRANSLATORS: --keyword=gettext --keyword=_  --output=messages.po
  1. 使用msgfmt编译po文件生成mo文件
msgfmt -o test.mo test.po
  1. 切换语言en_US验证
  1. 除了php需要国际化外,有些模板引擎也需要,比如smarty,有插件支持,js 不需要这个,js依赖json。也可以先生成po,再转换成json。
  2. gettext 命令功能强大,支持你想要的各种需求处理,包括合并,分割,去重等等,建议仔细看下。
  3. gettext需要固定的语言包子路径:locale/en_US/LC_MESSAGES/messages.po,不可以随便建。

改造过程

  • 已有代码处理
    历史代码,因为没有什么规范,各种情况都有,主要靠写正则脚本去替换了99%的翻译标记。有些特殊写法的,靠脚本和人工去审核标记。
  • 日常开发规范约定
    处理完历史代码之后,为了减少国际化的开发工作量,约定了国际化规范,待翻译的内容必须带翻译后缀,例如: $msg_i18n = “我是待翻译的内容”,方便自动化脚本去匹配处理。
  • 自动化脚本功能说明
    由于国际化流程比较复杂,需要标记->生成po文件->去重->编译。为了尽可能的降低国际化带来的开发速度影响,我开发了几个脚本,来自动化处理翻译内容,实现了:
  1. 自动标记并生成po文件
  2. 自动翻译并生成英文版po文件
  3. 自动去重并编译成mo文件

当然,如果遇到错误异常,仍然需要人工干预,但是正常是不需要的。下面是脚本演示:

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

(完)

你可能感兴趣的:(PHP,php)