我正在准备一个应用程序,其服务器端基于 PHP,并使用本机 gettext 函数实现翻译。
要加载应该用于检索 gettext 翻译的语言环境,我按照文档并使用 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
等等,对于上面指定的所有(非德语,因为 msgids 是德语/德语是根语言)区域设置。
所以我的问题是,为什么会出现这个问题?
我发现实际上是英语被用作非德语 gettext 检索的默认翻译语言。例如,如果您使用
/translations/en_GB.UTF-8/LC_MESSAGES/firstdomain.mo
的内容覆盖 /translations/pt_PT.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 终端会话运行脚本的情况。 为什么?
注意事项:
在设置区域设置之前执行
putenv("LANGUAGE=");
操作,或按照此处所述执行
putenv("LC_ALL=");
操作不会改变任何内容。
在设置区域设置之前执行
putenv("LANGUAGE=");
,无论是否使用区域设置,包括或排除 .UTF-8
后缀,或者仅像 here 那样完成前两个字符串字符也无法解决问题。
按照
here所述进行
putenv('LANGUAGE=nl_NL');
也没有达到目的。
我注意到setlocale
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 服务器上切换语言环境还不够/可以忽略。
this神圣帖子的接受答案,我可以找到解决方案。更改上面的代码示例:
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'));
}
对此:
foreach ($locales as $locale_value) {
var_dump(putenv("LC_ALL=$locale_value.UTF-8"));
var_dump(
setlocale(
LC_ALL,
"$locale_value.UTF-8"
)
);
bindtextdomain(
domain : 'firstdomain',
directory: MES_ROOT . '/translations'
);
bindtextdomain(
domain : 'seconddomain',
directory: MES_ROOT . '/translations'
);
// Set default textdomain to be first, as most translations are retrieved from there
textdomain('firstdomain');
var_dump(_(message: 'Beispiel'));
}
也就是说,每次迭代都重新启动文本域初始化和绑定。对于通常的 PHP 进程,这似乎不是必需的,但如果您在通过 ssh 命令将 PHP 作为 PHP-FPM (FCGI) 运行的 Apache 服务器上运行 PHP 脚本,则这是必需的。为什么它成功了?完全没有头绪!