perl localization internationalization gettext

Localización en Perl usando gettext y Locale:: TextDomain, con recuperación si Locale:: TextDomain no está disponible



localization internationalization (4)

Cree un directorio "alternativo / Configuración regional" y cree un módulo TextDomain.pm allí con implementaciones de stub para todas las funciones que necesite:

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.

Ahora inserte un bloque BEGIN en el punto de entrada de su aplicación (que normalmente es un script .pl, no un módulo .pm):

BEGIN { push @INC, "fallback"; }

Ahora Perl siempre encontrará Locale / TextDomain.pm en @INC, en duda la implementación de stub en el directorio de respaldo.

El blog " Sobre el estado de i18n en Perl " del 26 de abril de 2009 recomienda usar el módulo Locale :: TextDomain de la distribución libintl-perl para l10n / i18n en Perl. Además, tengo que usar gettext de todos modos, y obtener soporte de texto en Locale :: Messages / Locale :: TextDomain es más natural que en la emulación gettext en Locale :: Maketext .

La subsección " 15.5.18 Perl " en el capítulo " 15 Otros lenguajes de programación " en el manual de GNU gettext dice:

Portabilidad

El paquete libintl-perl es independiente de la plataforma pero no es parte del núcleo de Perl. El programador es responsable de proporcionar una implementación ficticia de las funciones requeridas si el paquete no está instalado en el sistema de destino.

Sin embargo, ninguno de los dos ejemplos en examples/hello-perl en gettext sources (uno usa Locale :: Messages de nivel inferior, uno con Locale :: DomainDomain de nivel superior) incluye detectar si el paquete está instalado en el sistema de destino y proporcionar implementación ficticia si no lo es.

Lo que es una cuestión complicada (con respecto a detectar si el paquete está instalado o no) es el siguiente fragmento de la página de manual de Locale :: TextDomain:

SINOPSIS

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

USO

Es crucial recordar que utiliza Locale :: TextDomain (3) como se especifica en la sección "SINOPSIS", lo que significa que debe usarlo , no requerirlo . El módulo se comporta de manera bastante diferente en comparación con otros módulos.

¿Podrían decirme cómo se debe detectar si libintl-perl está presente en el sistema de destino y cómo proporcionar la implementación falsa ficticia si no está instalado? ¿O dar ejemplos de programas / módulos que hacen esto?


El manual gettext es incorrecto al sugerir que no está bien que exija un requisito previo de CPAN . Todo el mundo lo hace en el mundo de Perl, y gracias a la infraestructura y cadena de herramientas de CPAN, funciona perfectamente. En el peor de los casos, puede agrupar las dependencias que necesita.

La respuesta directa a su pregunta es:

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

Explicación: el use solo es require en el momento de la compilación seguido de la import , y es aceptable dividirlo para forzar que se ejecute en tiempo de ejecución.


Tienes que incluir Locale :: TextDomain con el uso en lugar de requerir porque está destinado exactamente para este caso, cuando quieres i18n discreto para Perl, cuando todo lo que se necesita para internacionalizar tu código Perl es intercambiar eso:

print "Hello world!/n";

con este:

use Locale::TextDomain qw (com.example.myapp); print __"Hello world!/n";

En lenguajes preprocesados ​​como C, esto es más fácil de lograr. Acerca de todas las bibliotecas C internacionalizadas contienen un #define como este:

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

Eso significa que _("Hello world!/n") expande a una llamada de función que contiene el dominio de texto de tu paquete. Las fuentes de Perl no se pueden preprocesar portátilmente y, por lo tanto, Locale::TextDomain "abusa" del mecanismo de importación del pragma de uso para este fin, de modo que pueda asociar un archivo .pm con un archivo .mo particular. El dominio de texto es la raíz del nombre de archivo de los archivos .mo que instala su paquete.

Si no te gusta ese enfoque, no lo uses. También puede prescindir de esto:

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

Sin embargo, Locale::TextDomain es popular porque hace lo mismo de una manera mucho menos molesta.

Acerca de depender de una biblioteca que no sea central para Perl:

Si un módulo Perl pertenece o no al núcleo Perl depende de la versión Perl. Y cada usuario puede instalar una versión diferente de un módulo central de Perl sobre el que se envía con su Perl. Por lo tanto, una configuración de paquete robusta siempre buscará la versión requerida de una biblioteca de Perl, como si comprueba la versión requerida de cualquier otra biblioteca. Suponiendo que la comprobación de perl es la misma que. verificar la presencia de una versión particular de un módulo Perl particular es una receta para problemas.

Por cierto, Try::Tiny tampoco es parte del núcleo de Perl. Tal vez no sea la mejor opción para usarlo para verificar la presencia de otros módulos de Perl. Cuando desee probar libintl-perl, simplemente ejecute perl -MLocale::TextDomain -e exit en su script de configuración y verifique el estado de salida.


Según la respuesta de daxim, aquí hay una posible implementación. Detecta si Locale :: TextDomain está disponible y proporciona retrocesos no operativos simples para las funciones __ y __x. Agradecería las mejoras y sugerencias para este código.

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

Creo que todo el código necesita vivir dentro de BEGIN, de lo contrario, las llamadas __ y __x en su código causan errores. Además, las funciones secundarias se crean con eval () para evitar advertencias de "Prototipos no coincidentes". Me interesaría una solución más elegante, especialmente. para el último punto