首先,我们需要定义这两个相似的概念,还有相关的概念:
最简便的方式是使用数组键值对应的方式如 <?=$TRANS['title_about_page']?>
,不过在比较正经的项目中,不建议这么做。因为会随着项目代码慢慢变多,维护的难度将会增加,尤其会阻碍后续本地化实施。
您可能需要使用包管理器安装 Gettext 和相关的 PHP 库,如 apt-get 或 yum。 安装后,通过将 extension = gettext.so
(Linux / Unix)或 extension = php_gettext.dll
(Windows)添加到 php.ini 来启用它。
要使用 Gettext,我们需要遵循特定的文件夹结构。 首先,您需要为源存储库中的l10n文件选择任意根。 在其中,您将拥有每个所需区域设置的文件夹,以及包含所有 PO / MO 对的固定 LC_MESSAGES 文件夹(pot文件,pot是Portable Object Template的首字母缩写,与po对应的是mo,mo是Machine Object的首字母缩写)。 例:
<project root> ├─ src/ ├─ templates/ └─ locales/ ├─ forum.pot ├─ site.pot ├─ de/ │ └─ LC_MESSAGES/ │ ├─ forum.mo │ ├─ forum.po │ ├─ site.mo │ └─ site.po ├─ es_ES/ │ └─ LC_MESSAGES/ │ └─ ... ├─ fr/ │ └─ ... ├─ pt_BR/ │ └─ ... └─ pt_PT/ └─ ...
正如我们在介绍中所说,不同的语言可能会有不同的复数规则。 但是,gettext再一次让我们免于这个麻烦。 在创建新的.po文件时,您必须声明该语言的复数规则,并且对于复数敏感的翻译片段将针对每个规则具有不同的形式。 在代码中调用Gettext时,您必须指定与句子相关的数字,并且它将确定要使用的正确形式 - 如果需要,甚至使用字符串替换。
多个规则包括可用复数的数量和带有n的布尔测试,它将定义给定数字落在哪个规则中(以0开始计数)。 例如:
现在您了解了复数规则如何工作的基础 - 如果您没有,请查看LingoHub教程的更深层次的解释 - 您可能希望从列表中复制所需的规则而不是手动编写它们。
当调用Gettext来对带有计数器的句子进行本地化时,你也必须给他相关的数字。 Gettext将确定应该生效的规则并使用正确的本地化版本。 您需要在.po文件中为每个定义的复数规则添加不同的句子。
这是一个.po文件的摘录 - 不介意其格式,而是整体内容,您将学习如何在以后轻松编辑它:
msgid "" msgstr "" "Language: pt_BR\n" "Content-Type: text/plain; charset=UTF-8\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgid "We're now translating some strings" msgstr "Nós estamos traduzindo algumas strings agora" msgid "Hello %1$s! Your last visit was on %2$s" msgstr "Olá %1$s! Sua última visita foi em %2$s" msgid "Only one unread message" msgid_plural "%d unread messages" msgstr[0] "Só uma mensagem não lida" msgstr[1] "%d mensagens não lidas"
第一部分像标题一样工作,msgid
和 msgstr
特别空。 它描述了文件编码,复数形式和其他不太相关的东西。
第二部分将简单的字符串从英语翻译成巴西葡萄牙语,第三部分则相同,但利用 sprintf
中的字符串替换,因此翻译可能包含用户名和访问日期。
最后一部分是复数形式的样本,将单数和复数形式显示为英语中的msgid,并将它们对应的翻译显示为 msgstr 0
和 1
(遵循复数规则给出的数字)。 在那里,也使用字符串替换,因此可以使用 %d
直接在句子中看到数字。 复数形式总是有两个 msgid
(单数和复数),因此建议不要使用复杂的语言作为翻译的来源。
<?php /** * 验证项目中是否支持给定变量$locale * @参数字符串 $locale * @返回布尔 */ function valid($locale) { return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es'); } //设置source/default语言环境,以供参考 $lang = 'en_US'; if (isset($_GET['lang']) && valid($_GET['lang'])) { // 可以通过查询字符串更改语言环境 //这里只是个示例,真正开发中,对于GPC(GET/POST/COOKIE),一定要过滤! $lang = $_GET['lang']; setcookie('lang', $lang); //存储到COOKIE以便读取使用 } elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) { // 如果COOKIE已经有存储直接读取语言配置 $lang = $_COOKIE['lang']; //记得,GPC真正开发中,要过滤! } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // 默认: 通过$_SERVER环境变量,读取默认判断用户浏览器的语言设置 $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); } ); foreach ($langs as $browser_lang) { if (valid($browser_lang)) { $lang = $browser_lang; break; } } } // 在这里,我们根据找到的语言定义全局系统区域设置 putenv("LANG=$lang"); // 例如,这可能对日期函数(LC_TIME)或货币格式(LC_MONETARY) setlocale(LC_ALL, $lang); // 这将使Gettext找到../locales//LC_MESSAGES/main.mo bindtextdomain('main', '../locales'); // 表示应该读取文件的编码 bind_textdomain_codeset('main', 'UTF-8'); // 如果你的应用程序有其他域, //如前所述,你应该在这里绑定它们,以便表明应该读取文件的编码 bindtextdomain('forum', '../locales'); bind_textdomain_codeset('forum', 'UTF-8'); // 这里我们指出gettext()调用将响应的默认域 textdomain('main'); // 这将在forum.mo而不是main.mo中查找字符串 // echo dgettext('forum', 'Welcome back!'); ?>
Gettext 需要理解下面几点:
gettext()
还有简写函数 _()
以相同的方式工作;