php-logo

[译]PHP国际化-i18n机制教程

根据W3Tech的统计, PHP 被80.5%的网站 [1] 使用并且份额正在持续增 [2]. 我们可以负责任的说web “讲” PHP. 但是网站内容呢? 自PHP问世如此长的时间  (18 年), 它的历史也述说着网站国际化的进化故事. 本系统的第一篇文章尝试从不同维度来说明PHP国际化编程.

use_of_server-side_programming_languages_for_websites
use_of_server-side_programming_languages_for_websites

 

PHP 国际化(初期): 静态Web国际化

1234567891011121314
de
images
products
main.html
en
images
products
main.html
fr
images
products
main.html
index.html

在静态网站的日子, 开发者需要按照支持的 区域设置 数量多次复制整个网站结构. 翻者们需要深入目录结构直接修改页面. 既烦琐又容易出错. 要么翻者需要了解基本的 HTML ,并且知道HTML文件中的哪些部分 应该或不应该被翻译, 要么开发者必须提取所有的文本字符串, 发给翻者然后把它们(翻译结果)插回合适的位置. 对开发者或翻者来说都不是太友好的流程.

PHP 国际化 (今天): 动态web应用

然后PHP和动态web来了. 在动态网站中内容存储在数据库或文件系统, 在每个用户请求中插入到模板里. 内容,结构和设计分离成了web开发的新准则. 继而,国际化网站需要以下两步:

  1. 开发者提供一个内容可被最终翻译及其接口 (国际化)访问的机制和方法
  2. 译者适配特定的语言和文化进化真正的本地化.

有很多种国际化的机制,开发者处理它们有很大的差别,如复杂性,实现时间,灵活性,效率及译者的易用性.

某些机制可以大致的分为下面几类:

  • 在代码中直接本地化字符串
  • 存储字符串到关系型数据库
  • 存储字符串到字符串数组
  • 存储字符串到JSON
  • 使用资源文件

在代码中直接本地化字符串

本地化文本字符串可以在代码中直接处理. 开发者根据当前语言,书写条件判断给变量赋值. 译者通过浏览代码,编辑字符串值来进行翻译. 这会产生  “混乱” 代码,并且对译者来说非常不方便. 后期的任何更改都非常难以进行及跟踪.

字符串也可以从 代码/模板 中提取. 当前语言和本地化字符串由程序确定.

存储字符串到关系型数据库

storing_strings_in_db-localized_columns
storing_strings_in_db-localized_columns

PHP 国际化 的另一种方法是存储静态字符串到 数据库. 最简单的实现是添加与支持语言同样多的文本列(字段). 特定的翻译能通过简单的查询获取, 所以没有性能损失. 当需要添加一种额外的区域设置时, 如果列名一致,代码提取翻译的字符串时并不需要过多修改, 但是每个单表都包含可译内容(需要添加新列).

storing_strings_in_db-localized_tables
storing_strings_in_db-localized_tables

不想为每种 区域设置 增加列的话, 可以添加额外的表. 这种情况下大部分代码可以重用.为了添加新的区域设置,需要新增不存在的表,并且所有包含翻译内容的旧表要添加新的版本号. 当通过 SQL 获取翻译内容是, 需要关联它的本地化版本.

storing_strings_in_db-localized_rows
storing_strings_in_db-localized_rows

模型可以设计为一种在添加新的区域设置时,不需要修改表结构的方式. 任何数量的区域设置中,所有的可翻译的内容从表中提取到存储翻译版本的关联表中在任何数量的地方。添加一个新的区域设置就是在区域设置表中添加新的一行那么简单.通过 SQL 访问翻译字符串也不会比之前的例子更复杂 – 一个左联接.

所有的这些实现都有共同的缺点. 最严重的一个是开发者需要实现并维护一个译者用来访问并进行翻译的管理员界面. 另外, 译者的翻译会受限于给定的界面,除非开发者提供导出翻译文件的 I/O 脚本,让译者能在文本编辑器中打开翻译文件. 译者必须对文件的编码及文件格式非常小心, 否则在把它们导入回数据库时就会出现问题.

存储字符串到消息目录 (字符串数组)

123456789101112131415161718192021222324252627282930313233343536373839404142
<?php
$GLOBALS[“messages”] = array (
‘en’=> array(
‘Monday’ => ‘Monday’,
‘Tuesday’ => ‘Tuesday’,
‘Wednesday’ => ‘Wednesday’,
‘Thursday’ => ‘Thursday’,
‘Friday’ => ‘Friday’,
‘Saturday’ => ‘Saturday’,
‘Sunday’ => ‘Sunday’
),
‘de’=> array(
‘Monday’ => ‘Montag’,
‘Tuesday’ => ‘Dienstag’,
‘Wednesday’ => ‘Mittwoch’,
‘Thursday’ => ‘Donnerstag’,
‘Friday’ => ‘Frietag’,
‘Saturday’ => ‘Samstag’,
‘Sunday’ => ‘Sontag’
)
);
function msg($s) {
locale = $_SESSION[“locale”];
if (isset($GLOBALS[“messages”][$locale][$s])) {
return $GLOBALS[“messages”][$locale][$s];
} else {
error_log(“l10n error: locale: “.$locale, message:’$s‘”);
}
}
?>
<?php
// example of usage
session_start();
// if _SESSION[“locale”] is set to “de”, it will output “Sonntag”
print msg(‘Sunday’);
?>

关联数组 能被用于网站添加 i18n 支持. 如果翻译的内容增加,数组可以被分成每个文件保存一个数组, 每个区域设置一个文件. 但这些数组依然会变得相当大, 开发者必须手工维护并同步它们. 然而, 这可能对开发者来说是最简单的解决方案, 却对译者并不那么方便. 译者们依然必须在编辑时非常小心不要搞乱数组并且需要知道如何处理编码. 对他们来说是很高的技术障碍.

存储字符串到JSON

123456789
{
“Monday”:”Montag”,
“Tuesday”:”Dienstag”,
“Wednesday”:”Mittwoch”,
“Thursday:”Donnerstag”,
“Friday”:”Freitag”,
“Saturday”:”Samstag”,
“Sunday”:”Sonntag”
}
view rawde.txt hosted with ❤ by GitHub
12345678910111213141516171819202122
<?php
// the original of this code can be found on:
// http://www.mind-it.info/2010/02/22/a-simple-approach-to-localization-in-php/
function localize($phrase) {
/* Static keyword is used to ensure the file is loaded only once */
static $translations = NULL;
if (is_null($translations)) {
$lang_file = ‘/lang/’ . $_SESSION[“locale”] . ‘.txt’;
/* If no instance of $translations has occured load the language file */
if (!file_exists($lang_file)) {
$lang_file = ‘/lang/’ . ‘en.txt’;
}
$lang_file_content = file_get_contents($lang_file);
/* Load the language file as a JSON object
and transform it into an associative array */
$translations = json_decode($lang_file_content, true);
}
return $translations[$phrase];
}
?>

当使用这种机制,语言文件会基于配置的语言进行加载. 数组 $translations 用于确保语言文件只加载一次, 随后的每个调用通过内存中的 $translations 数组处理而不是重新加载语言文件. 语言文件由 JSON 格式构成,并且当资源文件加载到 $translations 数组时,PHP json_decode 函数用于转换 JSON 到 PHP 关联数组. 当调用函数时, 一个语言短语传递到函数中,会使用该语言短语作为键名从 $translations 数组中找到匹配的值.  .txt 文件需要是 utf-8 编码 (无 BOM), 否则 JSON PHP 不能正常处理 [3].

PHP 国际化 (现代): 使用资源文件

PHP localization programming目前应用程序的通行的做法是把文本设置到资源字符串,当程序运行需要时就会加载.  PHP 项目通常使用的资源文件格式是 Gettext, 它可以在下面的文章里看到. 点击查看早前的 本地化资源文件系列文章.

LingoHub如何 增强翻译流程

上面列出的所有 PHP 国际化机制和方法都有些共同的缺点, 但 LingoHub 把它们都处理得很好. 如果资源文件作为主要的 i18n 方法, 如果资源文件上传为 LingoHub 项目的一部分, 它能够:

  • 让开发者自动同步
  • 让译者专注于翻译, 而不需要关心国际化使用的技术方法
  • 让开发者查看翻译的上下文信息 (如元信息, 摩羯座, 截图)
  • 让开发者可以添加规则和指引以进行特定的翻译(例如通过定义 LingoChecks, 设置特定的区域语言, 设置正式/非正式)
  • 译者,评论者,开发者在LingoHub平台上交流,以解决任何翻译疑点, 减少Email
  • 让项目所有者跟踪单独翻译和整个项目的翻译状态
  • 让译者通过过滤器,高级搜索和提醒 快速找到新的或未翻译的短语
  • 让多人(译者评论者)工作在相同的项目,同时不用担心他们会取消对方的更改(例如. 通过角色和权限)

结果, PHP 国际化 的流程并不再麻烦, 自动化程序更高,也可以进行质量控制, 比以前快了几倍. 如果您对如何最好的国际化您的项目有疑问,请联系我们,请检出 LingoHub 来本地化您的产品. 零开销,舒适的集成和可扩展性会像您期待的云服务一样

更多关于 PHP 国际化编程在下面的文章.

参考:

  1. Usage Statistics and Market Share of Server-side Programming Languages for Websites – W3Techs
  2. Historical yearly trends in the usage of server-side programming languages for websites – W3Techs
  3. A simple approach to Localization in PHP – Mind IT | Mind IT