【问题标题】:Localization in Perl using gettext and Locale::TextDomain, with fallback if Locale::TextDomain is not available在 Perl 中使用 gettext 和 Locale::TextDomain 进行本地化,如果 Locale::TextDomain 不可用则回退
【发布时间】:2011-02-27 06:45:00
【问题描述】:

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

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

便携性

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

用法

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

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

【问题讨论】:

    标签: perl localization internationalization gettext


    【解决方案1】:

    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,可以将其拆分以强制它在运行时执行。

    【讨论】:

      【解决方案2】:

      您必须将 Locale::TextDomain 包含在 use 而不是 require 中,因为它正是针对这种情况而设计的,当您希望 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 并检查退出状态。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 也许是 Package::Stash 或直接操作符号表而不是文本 eval。
        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-05-06
          • 2017-04-26
          • 2022-08-03
          • 2019-10-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多