我正在准备一个应用程序,它的服务器端基于PHP,翻译使用原生的gettext函数实现。
为了加载应该用于检索gettext翻译的locale,我遵循文档并使用this:
// Set language to German
putenv('LC_ALL=de_DE');
setlocale(LC_ALL, 'de_DE');
到目前为止,对于我的REST API来说,这总是以一种可靠的方式工作,它以链接/描述的方式使用gettext翻译。然而,我有一个案例,它并不像我预期的那样工作。
我有以下PHP脚本:
$locales = [
'de_CH',
'en_GB',
'fr_FR',
'es_ES',
'pt_PT',
'it_IT'
];
$textdomain_loaded = false;
foreach ($locales as $locale_value) {
var_dump(putenv("LC_ALL=$locale_value.UTF-8"));
var_dump(
setlocale(
LC_ALL,
"$locale_value.UTF-8"
)
);
if ($textdomain_loaded === false) {
bindtextdomain(
domain : 'firstdomain',
directory: ROOT . '/translations'
);
bindtextdomain(
domain : 'seconddomain',
directory: ROOT . '/translations'
);
// Set default textdomain to be first, as most translations are retrieved from there
textdomain('firstdomain');
$textdomain_loaded = true;
}
var_dump(_(message: 'Beispiel'));
}
所有的. mo文件都被正确地翻译和生成。如果我通过.sh
脚本(.zsh
)执行脚本,该脚本通过php -r 'require "path/to/php/script.php"; MyClass::for_loop();'
或者,如果我向执行脚本的控制器发出HTTP请求,我会得到以下输出:
bool(true)
string(11) "de_CH.UTF-8"
string(8) "Beispiel"
bool(true)
string(11) "en_GB.UTF-8"
string(7) "Example"
bool(true)
string(11) "fr_FR.UTF-8"
string(7) "Exemple"
bool(true)
string(11) "es_ES.UTF-8"
string(7) "Ejemplo"
bool(true)
string(11) "pt_PT.UTF-8"
string(7) "Esemplo"
bool(true)
string(11) "it_IT.UTF-8"
string(7) "Esempio“
然而,如果我通过终端中的ssh连接在服务器上执行完全相同的.sh
脚本(当然具有相应的适应路径,但完全相同的php脚本),我得到的输出是:
bool(true)
string(11) "de_CH.UTF-8"
string(8) "Beispiel"
bool(true)
string(11) "en_GB.UTF-8"
string(7) "Example"
bool(true)
string(11) "fr_FR.UTF-8"
string(7) "Example"
bool(true)
string(11) "es_ES.UTF-8"
string(7) "Example"
bool(true)
string(11) "pt_PT.UTF-8"
string(7) "Example"
bool(true)
string(11) "it_IT.UTF-8"
string(7) "Example“
因此,区域设置(环境和PHP区域设置)的切换看起来是有效的,但检索到的gettext翻译总是福尔斯返回到英语翻译。
我已经在我的服务器上运行了locale -a
;我设置/调用的所有区域设置都是在我的服务器上定义/安装的。
当运行gettext --help
时,我可以在输出中看到以下部分:Standard search directory: /usr/local/share/locale
而我的.mo
文件存储在根目录的translations
目录下,如上所述,例如:/translations/en_GB.UTF-8/LC_MESSAGES/firstdomain.mo
/translations/en_GB.UTF-8/LC_MESSAGES/seconddomain.mo
/translations/fr_FR.UTF-8/LC_MESSAGES/firstdomain.mo
/translations/fr_FR.UTF-8/LC_MESSAGES/seconddomain.mo
等等,对于上面指定的所有区域设置(非德语,因为msgid是德语/德语是根语言)。
所以我的问题是,为什么会出现这个问题?
我发现实际上英语被用作非德语gettext检索的默认翻译语言。例如,如果你用/translations/pt_PT.UTF-8/LC_MESSAGES/firstdomain.mo
的内容覆盖/translations/en_GB.UTF-8/LC_MESSAGES/firstdomain.mo
文件,上面的输出显示,如果脚本在服务器上每个ssh运行:
bool(true)
string(11) "de_CH.UTF-8"
string(8) "Beispiel"
bool(true)
string(11) "en_GB.UTF-8"
string(7) "Esemplo"
bool(true)
string(11) "fr_FR.UTF-8"
string(7) "Esemplo"
bool(true)
string(11) "es_ES.UTF-8"
string(7) "Esemplo"
bool(true)
string(11) "pt_PT.UTF-8"
string(7) "Esemplo"
bool(true)
string(11) "it_IT.UTF-8"
string(7) "Esemplo“
因此,脚本似乎总是福尔斯返回到通过文件/translations/en_GB.UTF-8/LC_MESSAGES/firstdomain.mo
提供的翻译,但如果脚本是在每个ssh终端会话中运行的,则只返回到通过文件/translations/en_GB.UTF-8/LC_MESSAGES/firstdomain.mo
提供的翻译。为什么
注意:
在设置区域设置之前执行putenv("LANGUAGE=");
,或者像here那样执行putenv("LC_ALL=");
,都不会改变任何东西。
在设置locale之前执行putenv("LANGUAGE=");
,无论是使用locale,包括或排除.UTF-8
后缀,还是像here那样只使用前两个字符串字符,也没有解决问题。
执行putenv('LANGUAGE=nl_NL');
(如here所述)也没有达到目的。
我注意到the PHP manual for setlocale
中关于“多线程服务器API”的注解,并看到了this existing question,但我不确定这是否与本例相关。
我现在发现,如果我替换零件,我实际上可以在本地重现从服务器获得的输出:
var_dump(putenv("LC_ALL=$locale_value.UTF-8"));
var_dump(
setlocale(
LC_ALL,
"$locale_value.UTF-8"
)
);
使用:
var_dump(putenv("LANG=$locale_value.UTF-8"));
对我来说,这表明出于某种原因,
putenv("LC_ALL=$locale_value.UTF-8");
setlocale(LC_ALL,"$locale_value.UTF-8");
在本地运行脚本时,足以切换gettext区域设置,但在apache服务器上切换区域设置是不够的/被忽略的。
1条答案
按热度按时间bfhwhh0e1#
感谢this神圣帖子的接受答案,我可以找到解决方案。修改上面的代码示例:
这是:
也就是说,为每次迭代重新初始化textdomain初始化和绑定。对于普通的PHP进程,这似乎不是必需的,但是如果您通过ssh命令在运行PHP的Apache服务器上以PHP-FPM(FCGI)运行PHP脚本,那么它是必需的。为什么会这样?完全没有线索;仍然会对其中的原因非常感兴趣