多语言PHP网站的最高效方法

25
我正在处理一个大型多语言网站,并考虑不同的方法来实现多语言化。我能想到的可能的选择有:
1. 使用Gettext函数生成.po文件 2. 使用一个MySQL表来存储翻译,并为每个文本分配一个唯一的字符串ID 3. 使用包含不同翻译和唯一字符串ID的PHP文件数组
据我了解,Gettext函数应该是最高效的,但我的要求是在原始参考语言(英语)中更改文本字符串时,不会自动将该字符串的其他翻译恢复为英语,只因为有几个单词发生了变化。使用Gettext能实现这一点吗?
哪种解决方案对资源要求最低? 使用Gettext函数还是使用包含数组的PHP文件对资源要求更高或更低? 还有其他更高效的解决方案建议吗?
3个回答

28

需要考虑以下几点:

1. 翻译
谁来负责翻译?与网站有关联的人员还是翻译机构?使用Gettext时,您将使用'pot'(.po)文件。这些文件包含消息ID和消息字符串(翻译)。例如:

msgid "A string to be translated would go here"  
msgstr ""

现在,对于需要翻译这个内容的人来说,它看起来非常好理解。但是如果像Mike建议的那样使用关键字而不是完整的句子,会发生什么情况呢?如果有人需要翻译一个名为“address_home”的msgid,他或她就无法确定这是应该作为一个标题“家庭地址”还是一个完整的句子。在这种情况下,请确保在调用gettext函数之前在文件中添加注释,如下所示:

/// This is a comment that will be included in the pot file for the translators
gettext("ready_for_lost_episode");

使用xgettext --add-comments=///创建.po文件时会添加这些注释。但我认为 Gettext 并不是用这种方式的。此外,如果您需要在每个要显示的文本中添加注释,那么a) 您可能会在某个地方出现错误,b) 整个脚本将被填满文本,只是以注释形式,c) 注释需要直接放置在 Gettext 函数上方,这并不总是很方便,具体取决于函数在代码中的位置。

2. 维护
一旦您的网站(更进一步)扩展,随之增长的语言文件可能会变得非常难以维护。每次添加文本,您都需要创建新文件,将文件发送给翻译人员,接收文件返回,确保结构仍然完整(热心的翻译人员总是很高兴地翻译语法,从而使整个文件无法使用 :)),最后导入新的翻译。当然可以完成,但请注意在大型网站和多种语言环境下可能会遇到的问题。


另一个选择:结合第二种和第三种方法:

个人认为,使用(简单的)CMS 管理翻译更加有用,将变量和翻译存储在数据库中,并自己将相关文本导出到语言文件中:

  1. 将变量添加到数据库(例如:id、页面、变量);
  2. 为这些变量添加翻译(例如:id、varId、language、翻译);
  3. 选择相关的变量和翻译,将它们写入文件;
  4. 在网站中包含相应的语言文件;
  5. 创建自己的函数来显示变量的文本:

text('var'); 或类似于 __('faq','register','lost_password_text');

第 3 步可以非常简单,只需从数据库中选择所有相关的变量和翻译,将它们放入数组中并将序列化后的数组写入文件中。

优点:

  1. 维护。对于大型项目来说,维护文本可能会更加容易。您可以通过向数据库添加一个定义该变量属于站点哪个部分的列来按页面、部分或其他方式对变量进行分组。这样,您可以快速查看在 FAQ 页面中使用的所有变量的列表。

  2. 翻译。您可以在单个页面上显示所有不同语言的变量和翻译。这对于能够同时将文本翻译成多种语言的人可能很有用。并且查看其他翻译以了解上下文可能非常有用,以便翻译尽可能好。您还可以查询数据库以了解什么已经翻译了,什么没有翻译。可以添加时间戳以跟踪可能过时的翻译。

  3. 访问。这取决于谁将进行翻译。如果需要,您可以使用简单的登录将 CMS 包装起来,授予翻译机构的人员访问权限,并只允许他们更改某些语言或甚至站点的某些部分。如果这不是选项,则仍然可以将数据输出到手动翻译的文件中,然后进行导入(尽


谢谢您的全面回复!我们将自己进行一些翻译,但其余的将由翻译机构完成。我知道在gettext中使用键的问题,但如果我选择这种方法,我很可能会创建自己的解析器来创建PO文件,以便从xgettext创建的PO文件中组合英语和翻译语言,而不是使用键。我一直在考虑使用数据库,但对于实际上可以被视为静态内容的内容进行数据库请求似乎效率低下。 - alexteg
我正在考虑在后端使用数据库来为翻译人员创建前端的解决方案,然后生成静态的PO文件->MO文件或包含语言文件的PHP数组,具体取决于所请求的语言。就性能而言,从我的研究来看,PO文件仍然是最有效的,因为它们被缓存、机器翻译(MO文件)并且比PHP写的更低级。但是,我将对不同技术进行一些性能测试,并很快在这里发布结果。 - alexteg

13
经过一些测试,我最终决定基本上采用Alecs的第二和第三种选择的组合。 Gettext问题
我试图先设置整个gettext系统来试用一下,但结果比我想象的要复杂得多。问题在于Windows和Unix系统对setlocale()使用不同的语言简称。目前,我在Windows上用Wamp运行我的开发服务器,而最终的网站将在Linux上运行。在查阅了几十个指南论坛问题等,并在每次修改后重新启动服务器后,我发现没有找到任何简单的设置方法。此外,gettext不是线程安全的,要更新语言文件,服务器需要重新启动或者需要使用某种技巧,没有简单的方法来处理不同版本的语言文件或者处理原始的英文文本而不修改源代码或使用Mikes的建议,正如Alec指出的那样,这并不是最佳选择。
解决方案 根据Alec的回答,我最终得出了我认为是最佳解决方案:
1. 将所有翻译保存在数据库中,包括语言、页面、变量键、版本、修订和最后修改时间等字段。其中,版本对应原始翻译(英文)的不同版本,而修订允许翻译人员在版本内修改/修正最终翻译。
2. 使用一种与数据库连接的CMS进行翻译,该CMS可以处理不同版本,并且可以轻松查看已翻译的语言、版本以及翻译的完整程度。
3. 当某个版本的修订完成后,将生成缓存文件。每个文件都包含一个数组,只包含一个语言和一个页面的变量键和文本翻译,并以ISO 639-1语言代码和页面名称命名,例如:lang/en_index.php。然后,这些语言文件只需被包含并封装在一个名为t($var_key)的函数中,这样在开发过程中可以使用数据库,而后续只需使用缓存文件。
性能 我从来没有测试过gettext,但根据Mike发布的链接,对于上述自定义系统所提供的好处,使用数组和gettext之间的性能差异对我来说完全可以接受。然而,我将一个包含20个翻译文本字符串的数组与从MySQL数据库检索相同的20个文本字符串进行了比较。结果表明,使用从文件中包含的数组比同时从MySQL数据库中检索所有20个字符串要快6倍。这并不是一个真正科学的基准测试,结果在不同的系统和设置上可能会有所不同,但它清楚地展示了我预期的情况 - 使用数据库比直接使用数组要慢得多,这就是为什么我选择为数组生成缓存文件而不使用数据库的原因。
作为对比,我还测试了只输出简单回显的速度,使用相同的文本。结果发现,与使用包含文件中的数组相比,速度大约快了20倍。但是,这样就无法在不同语言版本的页面上进行翻译,这违背了动态页面的目的。这时最好也使用一个良好的缓存系统。
性能测试源文件: PHP:[链接2](http://pastie.org/964082) MySQL表格:[链接3](http://pastie.org/964115) 虽然肯定不是完美的,但至少能给出性能差异的一个概念。

小文件比从数据库读取更有效率。这是因为它们被缓存在多个级别上,从系统缓存到php缓存。数据库查询不会被缓存,因为假定数据库会发生变化。 - Michał Leon
@Michał Leon:非常有用的评论!“小”文件是多少KB或MB?我在哪里可以查看/设置这些文件缓存值? - CoR
所有的链接都失效了 :( - The Godfather

3

可以任意使用文本作为键,同时提供英语翻译,例如:

gettext密钥是“hello”

然后您可以拥有此字符串的各种语言翻译以及相应的英语翻译,如果要更新该字符串的英语版本,则可以保留密钥并仅更新英语翻译。


听起来是个好主意。但是gettext函数是最省资源的方法吗?还是有更好的解决方案? - alexteg
我从未真正投入太多时间来研究这个问题,因为即使对于我参与的一些大型网站,gettext也一直足够快。但是,快速浏览一篇基准测试文章: http://mel.melaxis.com/devblog/2006/04/10/benchmarking-php-localization-is-gettext-fast-enough/ 似乎提出了一些关于资源效率的想法。 - Pollett
好的,感谢您的帮助和提供链接。我想我会使用gettext方法,将键作为原始翻译。 - alexteg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接