1

我目前正在致力于国际化大型 Perl/Mason Web 应用程序(Perl 5.8.0、Mason 1.48、mod_perl 和 Apache)。在选择本地化模块时,我决定使用Locale::TextDomain而不是 Locale::Maketext,主要是因为后者的复数形式支持不如我想的那么好。

我对 Locale::TextDomain 的困扰是它根据进程的语言环境解析要用于翻译的目录。当我意识到这一点时,我担心如果我希望用户能够使用不同的语言环境,这将如何影响我的应用程序——改变语言环境以适应一个用户的设置是否会影响另一个用户的会话?例如,是否会出现这样一种情况:由于德国用户的会话更改了进程的区域设置,因此英语用户收到了德语页面?我不太了解 Apache 的线程/进程模型是如何工作的,尽管似乎如果多个用户可以由同一个线程提供服务,这可能会发生。

此电子邮件线程将表明这是可能的;在这里,OP描述了我正在考虑的情况。

如果这是真的,有没有办法可以在仍然使用 Locale::TextDomain 的同时防止这种情况?我想我总是可以破解模块以独立于语言环境(可能使用 DBD::PO)加载目录,但希望我只是错过了一些可以解决我的问题的东西......

4

1 回答 1

0

您可以通过使用来完全避免这些setlocale问题web_set_locale

(邮件列表上的那条消息比该功能的添加早了大约 4 年。)


编辑:您是正确的,Apache 子代中持续存在全局行为,导致错误行为

我写了一个测试用例:

应用程序.psgi

use 5.010;
use strictures;
use Foo::Bar qw(run);

my $app = sub {
    my ($env) = @_;
    run($env);
};

Foo/Bar.pm

package Foo::Bar;
use 5.010;
use strictures;
use Encode qw(encode);
use File::Basename qw(basename);
use Locale::TextDomain __PACKAGE__, '/tmp/Foo-Bar/share/locale';
use Locale::Util qw(web_set_locale);
use Plack::Request qw();
use Sub::Exporter -setup => { exports => [ 'run' ] };

our $DEFAULT_LANGUAGE = 'en'; # untranslated source strings

sub run {
    my ($env) = @_;
    my $req = Plack::Request->new($env);
    web_set_locale($env->{HTTP_ACCEPT_LANGUAGE}, undef, undef, [
        map { basename $_ } grep { -d } glob '/tmp/Foo-Bar/share/locale/*'
    ]); # XXX here
    return $req
        ->new_response(
            200, 
            ['Content-Type' => 'text/plain; charset=UTF-8'],
            [encode('UTF-8', __ 'Hello, world!')],
        )->finalize;
}

该应用程序作为 PerlResponseHandler 运行。当用户请求无法设置的语言时,呼叫会静默失败,最后成功使用的语言仍处于启用状态。

解决此问题的技巧是始终设置为具有回退机制的语言。在标记为 XXX 的位置添加代码or web_set_locale($DEFAULT_LANGUAGE),这样尽管使用了全局设置,但该行为不会持续存在,因为我们保证每个请求都会设置/更改一次。


编辑 2:进一步的测试表明它不是线程安全的,抱歉。仅使用prefork将请求隔离为进程的 MPM;但是worker并且event受到影响,因为它们是基于线程的。

于 2011-12-07T20:13:00.973 回答