使用gettext和Locale :: TextDomain在Perl中进行本地化,如果Locale :: TextDomain不可用则使用回退

时间:2010-06-03 11:51:03

标签: perl localization internationalization gettext

2009年4月26日的“On the state of i18n in Perl”博文建议在libll-perl发行版中使用Locale::TextDomain模块,用于Perl中的l10n / i18n。此外,我必须使用gettext,并且Locale :: Messages / Locale :: TextDomain中的gettext支持比Locale::Maketext中的gettext仿真更自然。

GNU gettext手册中“15.5.18 Perl”一章中的“15 Other Programming Languages”小节说:

  

可移植性

  
libintl-perl包与平台无关,但不属于Perl核心。如果目标系统上没有安装软件包,程序员负责提供所需功能的虚拟实现。

然而,gettext源中的examples/hello-perl中的两个示例(一个使用较低级别的Locale :: Messages,一个使用更高级别的Locale :: TextDomain)中的两个示例都包含检测(如果已安装包)在目标系统上,如果不是,则提供虚拟实现

什么是复杂的问题(关于检测是否安装了包)是Locale :: TextDomain联机帮助页的以下片段:

  

概要

use Locale::TextDomain ('my-package', @locale_dirs);

use Locale::TextDomain qw (my-package);
     

USAGE

     

至关重要的是要记住你使用“概要”一节中指定的Locale :: TextDomain(3),这意味着你必须使用,而不是需要它。与其他模块相比,该模块的行为完全不同。

您能否告诉我如何检测目标系统上是否存在libintl-perl,以及如果未安装虚拟防护实现,如何提供?或者举例说明这样做的程序/模块?

4 个答案:

答案 0 :(得分:7)

gettext手册错误地表明您demand a CPAN prerequisite不合适。每个人都在Perl世界中这样做,并且由于CPAN基础设施和工具链,它的工作正常。在最坏的情况下,您可以捆绑所需的依赖项。

您问题的直接答案是:

use Try::Tiny;
try {
    require Locale::TextDomain;
    Locale::TextDomain->import('my-package', @locale_dirs);
} catch {
    warn 'Soft dependency could not be loaded, using fallback.';
    require inc::Local::Dummy::Locale::TextDomain;
}

说明:use is just require at compile time followed by import,可以拆分它以强制它在运行时执行。

答案 1 :(得分:4)

你必须使用locale :: TextDomain而不是require,因为它只适用于这种情况,当你想要不显眼的i18n for Perl时,当国际化你的Perl代码需要交换时:

print "Hello world!\n";

用这个:

use Locale::TextDomain qw (com.example.myapp);

print __"Hello world!\n";

在像C这样的预处理语言中,这更容易实现。关于所有国际化C库包含#define,如下所示:

#define _(s) dgettext (GETTEXT_PACKAGE, s)

这意味着_("Hello world!\n")扩展为包含程序包的textdomain的函数调用。 Perl源无法进行可移植的预处理,因此Locale::TextDomain“滥用”use pragma的导入机制,以便它可以将.pm文件与特定的.mo文件相关联。 textdomain是程序包安装的.mo文件的文件名。

如果您不喜欢这种方法,请不要使用它。你也可以不用它来做:

require Locale::Messages;
print Locale::Messages::dgettext ("com.example.myapp", "Hello world!\n");

然而,Locale::TextDomain很受欢迎,因为它以不那么突兀的方式做同样的事情。

关于依赖于Perl的非核心库:

Perl模块是否属于Perl核心取决于Perl版本。每个用户都可以在Perl核心模块上安装不同版本的Perl核心模块。因此,强大的软件包配置将始终检查所需的Perl库版本,就像检查任何其他库的所需版本一样。假设检查perl与。检查是否存在特定版本的特定Perl模块是一个麻烦的方法。

BTW,Try::Tiny也不是Perl核心的一部分。也许不是使用它来检查其他Perl模块是否存在的最佳选择。如果要测试libintl-perl,只需在配置脚本中执行perl -MLocale::TextDomain -e exit并检查退出状态。

答案 2 :(得分:2)

根据daxim的回答,这是一个可能的实现。它检测Locale :: TextDomain是否可用,并为__和__x函数提供简单的无操作回退。我很感激这段代码的改进和建议。

BEGIN
{
    if (eval("require Locale::TextDomain; 1;"))
    {
        Locale::TextDomain->import('my-package', @locale_dirs);
    }
    else
    {
        my $subCode = <<'EOF'

        sub __
        {
            return $_[0];
        }

        sub __x
        {
            my $s = shift;
            my %args = @_;
            $s =~ s/\{(\w+)\}/$args{$1}/sg;
            return $s;
        }
EOF
;
        eval($subCode);
    }
}

我认为整个代码需要存在于BEGIN中,否则代码中的__和__x调用会导致错误。此外,使用eval()创建回退函数以避免“Prototype mismatch:”警告。我会对更优雅的解决方案感兴趣。对于后一点。

答案 3 :(得分:1)

创建一个目录“fallback / Locale”并在那里创建一个模块TextDomain.pm,其中包含您需要的所有函数的存根实现:

package Locale::TextDomain;

use strict;

sub __($) { return $_[0] }
sub __n($$$) { return $_[2] == 1 ? $_[0] : $_[1] }

# And so on, see the source of Locale::TextDomain for getting an
# idea how to implement the other stubs.

现在将BEGIN块插入应用程序的入口点(通常是.pl脚本,而不是.pm模块):

BEGIN {
    push @INC, "fallback";
}

现在Perl将始终在@INC中找到Locale / TextDomain.pm,但是对备用目录中的存根实现存在疑问。