使用PHP Gettext而无需安装locale


Use PHP Gettext without having to install locales

我一直在寻找国际化开源项目的选项:

  • Gettext,似乎每个人都推荐它,显然需要"安装在您的系统上"才能使用。请参阅PHP手册中的这条注释,它正好反映了我的情况。这个SO问题也问了同样的问题。这不适用于开放源码项目,因为我不能相信最终用户的系统上安装了适当的语言环境。此外,你必须安装locale才能使用你翻译过的字符串(IMO),这是非常非常奇怪的。

  • Zend_Translate有时也推荐使用gettext,但我不使用Zend框架,所以我认为这不是我的选择。有些人说可以把它从Zend框架中分离出来,但我不知道怎么做。如果有人能告诉我我需要什么文件(我已经下载了Zend框架的tarball)来挑选,我会开放使用Zend_Translate。

  • 数组。这就是我现在正在做的,但这并不理想,因为:

    • 它将使用大量的内存来定义每个翻译,当大多数不会被当前页面使用时。
    • 我在发现自己复制数组中的键时遇到了问题,这个数组已经变成了1000行代码,我几乎没有添加任何东西……
    • 这意味着非程序员不能真正翻译,而POedit是每个人都期望使用的标准。

我能以某种方式读取.mo文件没有Gettext或Zend_Translate,或者我必须使用Gettext?如果是这样,我如何使所有的区域工作,就像我在上面链接到的问题?

编辑:我现在愿意使用Zend_Translate。我只需要弄清楚我需要什么文件(如果它们能组合成一个文件就太好了)——我不想在我的项目中使用整个Zend框架。

更新:我很想看看大型开源项目如何处理i18n:

    Moodle使用数组
  • Drupal似乎使用了数组,有一个数据库回退。他们还运行setlocale('C')以获得通用语言环境,并绕过整个setlocale混乱。Wordpress为i18n使用了自己的函数堆,尽管下面使用了gettext。

所以这三个随机项目都没有使用Zend_Translate,也没有直接使用gettext,据我所知。

也许使用C区域设置是一个好主意,将语言的名称存储在文本域名中,然后从那里开始。


这就是我目前所做的:

$lang = 'de'; //debug
setlocale( LC_ALL, 'C' );
bindtextdomain( 'default', PATH . "/locale/$lang" );
bind_textdomain_codeset( 'default', 'UTF-8' );
textdomain( 'default' );
var_dump( file_exists( PATH . "/locale/$lang/C/LC_MESSAGES/default.mo" ) ); //bool(true)

但我仍然只是得到英文字符串,即使我已经使用poedit, msgfmt等来制作适当的文件。我也试过重新启动Apache

试试gettext-php。它是用PHP编写的gettext的直接替代品。它最初是为WordPress做的,我想,因为WP需要在共享主机上运行,而这些主机并不总是为每个地区配置。在我看来,这也是你的问题。

它对性能有一点影响,但对我来说根本不是问题。

解决方案如下:

$lang = 'de'; //debug
setlocale( LC_ALL, 'C.UTF-8' );
bindtextdomain( 'default', PATH . "/locale/$lang" );
bind_textdomain_codeset( 'default', 'UTF-8' );
textdomain( 'default' );

这与我在回答底部发布的示例之间的唯一区别是它使用C.UTF-8而不仅仅是C

我会做更多的测试,如果它可以跨平台工作,如果我发现任何其他的,我会更新这个答案。

对于仍然对此有一些问题的人,您可以尝试下面的代码,我从这里得到:58310年http://php.net/manual/en/function.gettext.php

它解决了我在freebsd服务器上,没有额外的区域设置安装(fr_FR和my_MY)当您遇到gettext缓存问题时,此代码也很有用。

<?php
function initialize_i18n($locale) {
  $locales_root="/app/php/locale"; // change This to where you locale folder at
  putenv('LANG='.$locale);
  setlocale(LC_ALL,"");
  setlocale(LC_MESSAGES,$locale);
  setlocale(LC_CTYPE,$locale);
  $domains = glob($locales_root.'/'.$locale.'/LC_MESSAGES/*.mo');
  $current = basename($domains[0],'.mo');
  $timestamp = preg_replace('{messages-}i','',$current);
  bindtextdomain($current,$locales_root);
  textdomain($current);
}
?>