Gettext 将始终使用系统默认区域设置


Gettext will always use system default locale

我需要本地化一个仅限Windows的PHP Web应用程序,我正在评估gettext扩展,但我很难让它在我的Windows 7开发框中工作。我使用反复试验和过程监视器来克服糟糕和不准确的文档,并设法从 *.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>

就我而言,<?=_('codigo_idioma')?>总是打印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';

这是一个由PHP团队承认并部分修复的问题。

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

总结一下:

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

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

不幸的是,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

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

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

解决方法是只有一个包含系统语言的语言文件夹,并且每种翻译语言都有多对 .mo/.po 文件。域将使用所需的语言进行设置。

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

文件夹结构

''

区域设置''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);

第一个问题: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

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

LANG=
LANGUAGE=
LC_ALL= "en_US.utf8"
添加一个

系统变量"LANG",其中包含该值所需的区域设置(例如"en_US"或"nl_NL"),重新启动 apache 并显示与该区域设置对应的翻译。

环境变量

这适用于 Windows 7.1 上的 XAMPP 10 和区域设置目录,结构如下:

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

您必须升级到适用于Windows的PHP 5.6.6,它可以工作!