【问题标题】:Gettext will always use system default localeGettext 将始终使用系统默认语言环境
【发布时间】:2013-10-24 14:54:46
【问题描述】:

我需要本地化一个仅限 Windows 的 PHP Web 应用程序,我正在评估 gettext extension,但我很难让它在我的 Windows 7 开发框中工作。我已经与Process Monitor 一起使用反复试验来克服糟糕和不准确的文档,并且我已经设法使_() 显示与计算机默认语言环境相对应的 *.po 目录中的字符串(在我的情况下为现代西班牙语)。 我所有设置不同语言环境的尝试都被忽略了。

我编写了一个包含大量冗余内容的测试脚本:

<dl><?php

define('DIR_LOCALE', __DIR__ . DIRECTORY_SEPARATOR . 'locale');
bindtextdomain('general', DIR_LOCALE);
bind_textdomain_codeset('general', 'UTF-8');
textdomain('general');

if(!defined('LC_MESSAGES')){
    define('LC_MESSAGES', 5);
}

$pruebas = array(
    'enu',
    'es_ES',
    'en_GB',
    'english-uk',
    'Spanish_Spain.1252',
    'esn',
    'spanish',
    'spanish-modern',
);
foreach($pruebas as $locale){
    putenv("LC_ALL=$locale");
    setlocale(LC_ALL, $locale);

    putenv("LC_MESSAGES=$locale");
    setlocale(LC_MESSAGES, $locale);

    putenv("LANGUAGE=$locale");
    putenv("LANG=$locale");
?>
    <dt><?=htmlspecialchars($locale)?></dt>
    <dd><?=_('codigo_idioma')?></dd>
<?php } ?>
</dl>

在我的例子中,&lt;?=_('codigo_idioma')?&gt; 总是打印 es_ES@modern

我有 PHP/5.4.5,但我希望它能够在我们客户拥有的任何合理的最新服务器上运行。

我已经阅读了很多关于即使在 Windows 上也需要安装语言环境的模糊参考,但没有确切的细节。 问题可能是什么?

(我知道常见的建议是转储 gettext 并使用任何其他库。)


进一步测试:

我的代码在另外两台计算机上按原样完美运行:32 位 Windows Vista 和 32 位 Windows 7 32 位。它在我的计算机(64 位 Windows 7)和另一台计算机(32 位 Windows Server 2003)中失败

  • Apache 版本似乎无关紧要(命令行解释器也会出现这种情况)。
  • PHP 版本似乎无关紧要(也在我的 PC 中尝试过最新的 32 位 PHP/5.5.5)。
  • 我的[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls] 注册表树与其他七框相同。

编辑:在命令行测试时,我发现在运行 PHP 脚本之前设置 LANG 环境变量最终会改变语言:

C:\>set LANG=en_GB
C:\>php C:\test\gettext.php

这明确地证明了我的计算机具有正确的资产,但也让我想知道为什么 PHP 声称 putenv() 有效然后忽略它:

var_dump( getenv('LANG'), putenv('LANG=en_GB'), getenv('LANG') );
bool(false)
bool(true)
string(5) "en_GB"

即使这样也没有任何效果:

$_ENV['LANG'] = 'en_GB';
$_SERVER['LANG'] = 'en_GB';

【问题讨论】:

  • 您解决了这个问题吗?我有同样的问题(64 位 Win 8.1)。正如你所提到的,如果我在运行脚本之前使用“set LANG”,它就可以工作。我需要让它通过 Apache 工作。
  • @fromvega 一点也不。相同的启用 gettext 的代码在某些 Windows 计算机中有效,而在某些其他计算机中无效——这是我目前所知道的唯一内容。
  • 您好。我知道这已经有一段时间了,但是很久以前就点击了这篇文章并记得我对其他类型的环境变量也有类似的问题。您是否尝试过通过 Apache 模块添加环境变量?就像这里说的:httpd.apache.org/docs/2.2/env.html
  • @JorgeCampos 我想您提出了一种解决方法。我想我可以想办法将我的语言选择逻辑从 PHP 会话移动到 Apache(可能使用 cookie),但我想我应该提到我已经用 pure PHP class that reads gettext catalogues 解决了这个问题,并在 gettext 拒绝工作时作为后备财产。此类尚未积极开发,可能缺少一些高级功能,但到目前为止运行良好。
  • 是的,这是一种解决方法。当我遇到这样的问题时,我意识到当时的 PHP 遇到了问题,因为它不是进程执行的所有者(在操作系统上的意思)所以我想如果我在调用它的进程上设置变量,它会起作用,并且在当时起作用。但很高兴知道你已经想通了。因此,您应该发布您的解决方案作为答案。正如我的一个朋友常说的:最好的解决方案是你至少可以做到,直到有人与另一个更好的人一起来。 :) 干杯

标签: php windows apache gettext


【解决方案1】:

这是一个 issue 已被 PHP 团队承认和 partially fixed

这是一个相当技术性的事情,显然与底层平台处理环境变量(gettext 严重依赖)的方式有关。此外,从 VC9 到 VC11 的 Visual C 运行时库中的某些变化会影响这一切。

总结一下:

  • 已于 2014 年 11 月 21 日st 在源代码树中修复了非线程安全构建。
  • 线程安全的构建(例如 Apache 模块)还没有,也没有明确的解决方案。

【讨论】:

  • @ChrisChilvers - 你说得对,我已经编辑了答案。
  • 我也有同样的问题。肖,现在怎么样了?如果我想将 PHP 7.1 与 Apache 2.4 和 gettext 一起使用,我该怎么办?有什么想法吗?
  • @BuzoganyLaszlo 替代方案:1) 使用不受影响的构建 2) 放弃 gettext 以支持其他库 3) 自己解决问题 ;-)
  • 经过几天的研究,我终于得到了一个使用 Apache 2.4 x64 VC14 + FCGID + PHP 7.1 NTS x64 VC14 的工作版本。
  • @BuzoganyLaszlo 感谢您的报告。这与我们目前所知的一致:非线程安全构建已在 2014 年底修复。
【解决方案2】:

关键是使用 PHP 的非线程安全 (=NTS) 版本

不幸的是,Windows 和 PHP 不能很好地处理线程进程的环境,所以 putenv('LC_ALL='.$locale);命令不起作用。

最后我得到了 Apache 2.4 + FCGID + PHP 7.1 NTS,它现在在 Windows 7 上运行良好,而且它是一个非线程安全的安装。

有关如何安装此类系统的分步说明在这里:https://www.youtube.com/watch?v=UXrJPrGaPB0

我对所有组件都使用了 VC14 和 x64 版本(VC 是“Microsoft Visual C++ Redistributable”的缩写)。为此我首先安装了 VC14,从这里下载:https://www.microsoft.com/en-us/download/details.aspx?id=48145

【讨论】:

    【解决方案3】:

    我在 Windows 10 上使用 PHP 5.6.30 VC11 Theard Safe 时遇到了同样的问题。通过 sirio3mil 找到了解决方法并修复了此问题here

    显然带有 TS 的 PHP 只能访问 Locale 语言文件夹。因此,当 setlocale 和 putenv 函数使用系统语言以外的另一种语言调用时,无法读取带有 .mo 和 .po 的文件夹。

    解决方法是只有一个包含系统语言的语言文件夹和多对 .mo/.po 文件用于每种翻译的语言。域将设置为所需的语言。

    以瑞士法语、德语和意大利语为例:

    文件夹结构

    \Locale\fr_CH\LC_MESSAGES

    • fr_CH.mo + fr_CH.po // 系统语言
    • de_CH.mo + de_CH.po
    • it_CH.mo + it_CH.po

    代码

    $lang = 'fr_CH' or 'de_CH' or 'it_CH'
    
    bindtextdomain($lang, '.\Locale');
    textdomain($lang);
    bind_textdomain_codeset($lang, 'UTF-8');
    setlocale (LC_ALL, $lang);
    putenv('LC_ALL=' . $lang);
    

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效
    • 完成!我复制了我的解释而不是链接:-)
    • 此解决方法确实有效,谢谢。即使我所有的 mo/po 文件现在都位于 ru_RU 文件夹中,这很奇怪而且很难看。但是此代码在 Unix 系统上部署后将不起作用,因为 Unix 仍会尝试搜索 en_GB/ 和 zh_CN/ 文件夹(在我的情况下)。因此,作为另一个小的解决方法/修复,我在 Unix 上创建了符号链接。两个符号链接(en_GB 和 zh_CN)都指向 ru_RU 文件夹。现在也可以在 Unix 上使用此文件夹结构和此代码。
    【解决方案4】:

    在 Ubuntu 上,如果您设置默认 LC_ALL 并将 LANG 和 LANGUAGE 留空,则它可以工作,如下所示:

    LANG=
    LANGUAGE=
    LC_ALL= "en_US.utf8"
    

    【讨论】:

      【解决方案5】:

      第一个问题:setlocale()

      要完成这项工作,您需要设置有效的语言环境。在 Windows 上, setlocale() 不会按预期工作。您需要设置一个环境变量,如下所示:

      // putenv("LANG=$lang");  <- WRONG!
      putenv('LC_ALL='.$locale);
      

      第二个问题:地区名称。

      Windows 语言环境名称与 Linux 不同。尝试使用“ita”、“eng”、“deu”、“ger”、“esp”。 您可以在此处获取完整列表:http://www.microsoft.com/resources/msdn/goglobal/default.mspx

      例子:

      //putenv("LANG=esp");  <- WRONG!
      putenv('LC_ALL=esp');
      

      第三个问题,一个大问题:Windows 上的 gettext 扩展不是线程安全的。每次更改语言时,更改都会涉及整个过程。如果您将 php 作为 fast-cgi 运行,则可以。如果您将 php 作为 apache 模块运行(例如),那将是一团糟,因为每个 php 实例的语言都会发生变化。问题是 gettext() 依赖于语言环境设置。此设置在 Windows PHP 上是进程范围的。您不能更改 PHP 线程的区域设置,只能更改 PHP 进程。

      说到这里,下面是一些工作代码:

      // $MAINPATH is your document root
      
      $locales=array(
        'it'=>'ita',
        'en'=>'eng',
        'de'=>'deu',
        'fr'=>'fra',
        'es'=>'esp',
        'ru'=>'rus'
      );
      
      $locale = $locales[$lang];
      $res=putenv('LC_ALL='.$locale);
      $rres=bindtextdomain('default', $MAINPATH.'locale');
      $dres=textdomain('default');
      

      语言环境目录结构必须如下:

      deu
        LC_MESSAGES
          default.mo
      esp
        LC_MESSAGES
          default.mo
      fra
        LC_MESSAGES
          default.mo
      ita
        LC_MESSAGES
          default.mo
      rus
        LC_MESSAGES
          default.mo
      

      【讨论】:

      • 除非我遗漏了什么,否则我并没有按照你说的做任何事情(我在这里发帖时省略了setlocale() 返回值和eng,但实际上我已经验证)。线程安全是一个好点,但是: 1) 语言环境不会从非 PHP 进程中泄漏,对吗? 2) 对命令行有影响吗?
      • 这个设置在我的机器上运行,Win7 64bit ita。 1) 不,语言环境设置始终与 PHP 相关。 2) 不,不同的命令行是不同的 PHP 进程,它们是密封的。
      • 我更正了示例,必须使用 LC_ALL 而不是 LANG。下面的代码是正确的。
      • 再一次,我已经设置了LC_ALL。 (在我最近的测试中,整个 gettext 功能似乎在 PHP/5.3 和 PHP/5.5 中被破坏,但在 PHP/5.4 中工作正常。我可能会在有时间时编辑这个问题。)
      【解决方案6】:

      添加一个系统变量“LANG”,该变量具有所需的区域设置(例如“en_US”或“nl_NL”),重新启动 apache 并显示与该区域设置对应的翻译。

      Environment variables

      这适用于 Windows 10 上带有 php 7.1(线程安全)的 XAMPP,语言环境目录结构如下:

          // Directory structure
          <locale_dir>\en_US\LC_MESSAGES\bundle.po
          <locale_dir>\nl_NL\LC_MESSAGES\bundle.po
          ...
      

      【讨论】:

        【解决方案7】:

        您必须升级到适用于 Windows 的 PHP 5.6.6,它才有效!

        【讨论】:

        • 如果这实际上是该版本的 PHP 中修复的问题,请考虑包含一些证据(例如更改日志条目或错误跟踪器错误)。
        • 您使用的是官方版本吗?我刚刚尝试过它们(x64 和 x86),但仍然失败。 PHP/5.4.x 是在我当前 PC 上工作的最后一个分支。 :-?
        猜你喜欢
        • 2013-06-14
        • 2014-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-12
        • 2014-03-09
        • 1970-01-01
        相关资源
        最近更新 更多