Локализация в Perl с использованием gettext и Locale:: TextDomain, с резервным вариантом, если Locale:: TextDomain недоступен
В "о состоянии 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 Других Программ Языки " в руководстве GNU gettext говорится:
мобильность
наlibintl-perl
пакет не зависит от платформы, но не является частью ядра Perl. Программист несет ответственность за обеспечение фиктивной реализации необходимых функций, если пакет не установлен в целевой системе.
однако ни один из двух примеров в examples/hello-perl
в источниках gettext (один с использованием нижнего уровня Locale:: Messages, один из которых использует язык более высокого уровня:: TextDomain) включает определения если пакет установлен в целевой системе и предоставляет пустышками если это не так.
что усложняет дело (в отношении обнаружения, установлен ли пакет или нет), это следующий фрагмент локали:: TextDomain manpage:
справка
use Locale::TextDomain ('my-package', @locale_dirs); use Locale::TextDomain qw (my-package);
использование
важно помнить, что вы используете Locale:: TextDomain(3), как указано в разделе "синопсис", это означает, что вы должны использовать, а не требуются его. Модуль ведет себя совершенно иначе по сравнению с другими модулями.
не могли бы вы рассказать мне, как следует определить, присутствует ли libintl-perl в целевой системе, и как обеспечить фиктивную реализацию fallthrough, если она не установлена? Или приведите примеры программ / модулей, которые это делают?
4 ответов
руководство 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
и вполне приемлемо разделите его, чтобы заставить это выполнить во время выполнения.
вы должны включить 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")
расширяется до вызова функции, содержащей textdomain пакета. Источники Perl не могут быть предварительно обработаны, и поэтому Locale::TextDomain
"злоупотребляет" механизмом импорта прагмы use для этой цели, чтобы он мог связать a .pm файл с конкретным .МО файл. Textdomain является стволом имени файла .mo файлы, которые устанавливает ваш пакет.
Если вам не нравится такой подход, не используйте его. Вы также можете обойтись без него:
require Locale::Messages;
print Locale::Messages::dgettext ("com.example.myapp", "Hello world!\n");
однако, Locale::TextDomain
популярен, потому что он делает то же самое гораздо менее навязчивым способом.
о зависимости от библиотеки, которая не является основной для Perl:
принадлежит ли модуль Perl ядру Perl или нет, зависит от версии Perl. И каждый пользователь может установить другую версию модуля Perl core поверх той, которая поставляется с ней или его Perl. Поэтому надежная конфигурация пакета всегда будет проверять наличие требуемой версии библиотеки Perl, как это было бы проверено для обязательная версия любой другой библиотеки. Предполагая, что проверка perl такая же, как. проверка наличия определенной версии определенного модуля Perl-это рецепт для неприятностей.
кстати, Try::Tiny
также не является частью ядра Perl. Возможно, не лучший выбор, используя его для проверки наличия других модулей Perl. Если вы хотите протестировать libintl-perl, просто выполните perl -MLocale::TextDomain -e exit
в вашем сценарии настройки и проверьте состояние выхода.
основываясь на ответе 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{}/sg;
return $s;
}
EOF
;
eval($subCode);
}
}
Я думаю, что весь код должен жить внутри BEGIN, иначе вызовы __ и _ _ x в вашем коде вызывают ошибки. Кроме того, резервные функции создаются с помощью eval (), чтобы избежать предупреждений "несоответствие прототипа:". Мне было бы интересно ... более элегантное решение ЕСП. по последнему пункту.
создайте каталог "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 в точку входа вашего приложения (обычно это a .сценарий pl, а не a .модуль pm):
BEGIN {
push @INC, "fallback";
}
теперь Perl будет всегда найти языковой стандарт / TextDomain.pm В @INC, сомневается в реализации заглушки в резервном каталоге.