首先,我们需要定义这两个相似的概念,还有相关的概念:
最简便的方式是使用数组键值对应的方式如 <?=$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() 还有简写函数 _() 以相同的方式工作;