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

发布于 2024-09-04 05:36:48 字数 1699 浏览 8 评论 0 原文

关于 Perl 中的 i18n 状态 " 2009 年 4 月 26 日的博文建议使用 libintl-perl 发行版中的 Locale::TextDomain 模块进行 l10n / Perl 中的 i18n。此外,无论如何我都必须使用 gettext,并且 Locale::Messages / Locale::TextDomain 中的 gettext 支持比 区域设置::Maketext

15.5.18 Perl”小节GNU gettext 手册中的 href="http://www.gnu.org/software/gettext/manual/gettext.html#Programming-Languages" rel="noreferrer">15 种其他编程语言" 说:

可移植性

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

但是 examples/hello-perl(一个使用较低级别的 Locale::Messages,一个使用较高级别的 Locale::TextDomain)包括检测是否安装了软件包在目标系统上,如果不是,则提供虚拟实现

使事情变得复杂的事情(关于检测软件包是否安装)是 Locale::TextDomain 联机帮助页的以下片段:

概要

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

使用 Locale::TextDomain qw (my-package);

使用

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

您能否告诉我如何检测目标系统上是否存在 libintl-perl,以及如何在未安装时提供虚拟失败实现?或者给出执行此操作的程序/模块的示例?

The "On the state of i18n in Perl" blog post from 26 April 2009 recommends using Locale::TextDomain module from libintl-perl distribution for l10n / i18n in Perl. Besides I have to use gettext anyway, and gettext support in Locale::Messages / Locale::TextDomain is more natural than in gettext emulation in Locale::Maketext.

The subsection "15.5.18 Perl" in chapter "15 Other Programming Languages" in GNU gettext manual says:

Portability

The libintl-perl package is platform independent but is not part of the Perl core. The programmer is responsible for providing a dummy implementation of the required functions if the package is not installed on the target system.

However neither of two examples in examples/hello-perl in gettext sources (one using lower level Locale::Messages, one using higher level Locale::TextDomain) includes detecting if the package is installed on the target system, and providing dummy implementation if it is not.

What is complicating matter (with respect to detecting if package is installed or not) is the following fragment of Locale::TextDomain manpage:

SYNOPSIS

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

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

USAGE

It is crucial to remember that you use Locale::TextDomain(3) as specified in the section "SYNOPSIS", that means you have to use it, not require it. The module behaves quite differently compared to other modules.

Could you please tell me how one should detect if libintl-perl is present on target system, and how to provide dummy fallthrough implementation if it is not installed? Or give examples of programs / modules which do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

榕城若虚 2024-09-11 05:36:48

gettext 手册错误地建议您要求 CPAN先决条件。在 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 只是 require 在编译时,然后是 import,并且可以将其拆分以强制其在运行时执行。

The gettext manual is wrong to suggest that it is not okay for you to demand a CPAN prerequisite. Everyone does this in the Perl world, and thanks to the CPAN infrastructure and toolchain, it works just fine. In the worst case you can bundle the dependencies you need.

The straight answer to your question is:

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;
};

Explanation: use is just require at compile time followed by import, and it is acceptable to split it in order to force this to execute at run time.

方觉久 2024-09-11 05:36:48

你必须使用 use 而不是 require 来包含 Locale::TextDomain ,因为它正是针对这种情况,当你想要 Perl 的不显眼的 i18n 时,当国际化你的 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") 扩展为包含您的文本域的函数调用包裹。 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 模块的特定版本是否存在会带来麻烦。

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

You have to include Locale::TextDomain with use instead of require because it is intended for exactly this case, when you want unobtrusive i18n for Perl, when all it takes to internationalize your Perl code is to exchange that:

print "Hello world!\n";

with this:

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

print __"Hello world!\n";

In preprocessed languages like C this is easier to achieve. About all internationalized C libraries contain a #define like this:

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

That means that _("Hello world!\n") expands to a function call that contains the textdomain of your package. Perl sources cannot be portably preprocessed, and therefore Locale::TextDomain “abuses” the import mechanism of the use pragma for this purpose, so that it can associate a .pm file with a particular .mo file. The textdomain is the filename stem of the .mo files your package installs.

If you don't like that approach, don't use it. You can also make do without it:

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

However, Locale::TextDomain is popular because it does the same in a much less obtrusive way.

About depending on a library that is non-core for Perl:

Whether a Perl module belongs to the Perl core or not depends on the Perl version. And every user can install a different version of a Perl core module over the one that ships with her or his Perl. Therefore, a robust package configuration will always check for the required version of a Perl library like it would check for the required version of any other library. Assuming that checking for perl is the same as. checking for the presence of a particular version of a particular Perl module is a recipe for trouble.

BTW, Try::Tiny is also not part of the Perl core. Maybe not the best choice using it for checking for the presence of other Perl modules. When you want to test for libintl-perl, just execute perl -MLocale::TextDomain -e exit in your configure script and check the exit status.

山色无中 2024-09-11 05:36:48

根据 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() 创建的,以避免“原型不匹配:”警告。我对更优雅的解决方案感兴趣,尤其是。对于后一点。

Based on daxim's answer, here's a possible implementation. It detects whether Locale::TextDomain is available, and provides simple no-op fallbacks for __ and __x functions. I'd appreciate improvements and suggestions for this code.

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);
    }
}

I think the whole code needs to live inside BEGIN, otherwise the __ and __x calls in your code cause errors. Also, the fallback functions are created with eval() to avoid "Prototype mismatch:" warnings. I'd be interested in a more elegant solution esp. for the latter point.

涫野音 2024-09-11 05:36:48

创建一个目录“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,对后备目录中的存根实现有疑问。

Create a directory "fallback/Locale" and create a module TextDomain.pm there with stub implementations for all functions that you need:

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.

Now insert a BEGIN block into the entry point of your application (that is typically a .pl script, not a .pm module):

BEGIN {
    push @INC, "fallback";
}

Now Perl will always find Locale/TextDomain.pm in @INC, in doubt the stub implementation in the fallback directory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文