Локализация в Perl с использованием gettext и Locale :: TextDomain, с запасным вариантом, если Locale :: TextDomain недоступен - PullRequest
8 голосов
/ 03 июня 2010

В блоге " О состоянии i18n в Perl " от 26 апреля 2009 г. рекомендуется использовать модуль Locale :: TextDomain из дистрибутива libintl-perl для l10n / i18n в Perl. Кроме того, я все равно должен использовать gettext, и поддержка gettext в Locale :: Messages / Locale :: TextDomain более естественна, чем в эмуляции gettext в Locale :: Maketext .

Подраздел " 15.5.18 Perl " в главе " 15 Другие языки программирования " в руководстве по gettext для GNU гласит:

Переносимость
Пакет libintl-perl не зависит от платформы, но не является частью ядра Perl. Программист отвечает за предоставление фиктивной реализации необходимых функций, если пакет не установлен в целевой системе.

Однако ни один из двух примеров в examples/hello-perl в источниках gettext (один с использованием Locale :: Messages более низкого уровня, другой с Locale :: TextDomain более высокого уровня) не включает обнаружение , если пакет установлен в целевой системе и обеспечивает фиктивную реализацию , если это не так.

Что усложняет вопрос (в отношении определения, установлен пакет или нет), так это следующий фрагмент справочной страницы Locale :: TextDomain:

СИНТАКСИС

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

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

* 1046 USAGE *

Важно помнить, что вы используете Locale :: TextDomain (3), как указано в разделе «СИНОПСИС», это означает, что вы должны использовать , а не требовать это , Модуль ведет себя совершенно иначе по сравнению с другими модулями.

Не могли бы вы рассказать, как определить, присутствует ли libintl-perl в целевой системе, и как обеспечить фиктивную реализацию, если она не установлена? Или привести примеры программ / модулей, которые это делают?

Ответы [ 4 ]

7 голосов
/ 03 июня 2010

В инструкции 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, и допустимо разделить его, чтобы заставить его выполняться во время выполнения.

4 голосов
/ 25 января 2013

Вы должны включить Locale :: TextDomain с использованием вместо require, поскольку оно предназначено именно для этого случая, когда вам нужен ненавязчивый i18n для 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") расширяется до вызова функции, которая содержит текстовый домен вашего пакета. Исходники Perl не могут быть предварительно обработаны переносимым образом, и поэтому Locale::TextDomain «злоупотребляет» механизмом импорта для этой цели использования прагмы, так что он может связать файл .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 в вашем скрипте configure и проверьте состояние выхода.

2 голосов
/ 25 апреля 2013

На основании ответа 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:». Я был бы заинтересован в более элегантном решении, особенно. для последнего пункта.

1 голос
/ 09 декабря 2016

Создайте каталог «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 будет всегда находить Locale / TextDomain.pm в @INC, если сомневаться в реализации заглушки в резервном каталоге.

...