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,以及如果未安装虚拟防护实现,如何提供?或者举例说明这样做的程序/模块?
答案 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,但是对备用目录中的存根实现存在疑问。