Internacionalización y localizacion con gettext

Trataré de explicar algo de terminología antes de entrar en los detalles técnicos de este artículo:

Gettext es un conjunto de herramientas que sirven para que un programa pueda producir mensajes en múltiples idiomas.
Los ficheros po son ficheros de traducciones empleados por el conjunto de herramientas gettext de GNU. Un fichero po contiene una relación entre una cadena no traducida a otra cadena traducida en un idioma en particular.
A la operación por el cual un programa ha sido preparado y es capaz de soportar múltiples idiomas se le llama internacionalización, abreviado por I18n (en inglés internazionalization tiene 18 caracteres entre la i y la n, igual que en castellano)
A la operación por la cual un programa, ya internacionalizado, se le pasa toda la información necesaria para que pueda adaptar su entrada (stdin) y su salida (stdout) de manera que sea correcta para diferentes idiomas y hábitos culturales de un idioma o país en concreto se le llama localización, y suele ser abreviado por L10n (localization, 10 caracteres entre la ele y la ene)
El soporte de lenguaje nativo (Native Language Support o NLS) engloba ambos procesos; internacionalización y localización.
Un locale es un conjunto de componentes de los datos culturales de idiomas y/o países. El soporte de mensajes es uno de los locales más importantes, además hay como locales: los conjuntos de caracteres a utilizar (charsets), la representación de números (numbers), la representación de fechas (dates) y la representación de monedas (currency).

El po de la extensión de los ficheros de traducción significa Portable Object, para distinguirlo de los ficheros mo que significa Machine Object. Los ficheros po son legibles y editables por humanos. Cada fichero po está dedicado a sólo un idioma. Si un programa necesita traducciones en más idiomas se precisa crear un fichero po por idioma. Gettext tiene utilidades para detectar cadenas marcadas para traducir en un programa (xgettext) que crea un fichero tipo pot Portable Object Template que sirve de planilla para iniciar el trabajo de traducción, y utilidades para comentar cadenas que ya no se utilizan en futuras versiones del programa (msgmerge). También lógicamente para convertir los po’s en mo’s mediante fmtstr.

Los fichero mo son ficheros compilados para la lectura por parte del programa y son de tipo binario. El formato de los ficheros mo es a menudo diferente de sistema a sistema y cada sistema debe tener sus propias utilidades para convertir po’s a mo’s.

El formato del fichero po

Esta es la estructura general de una entrada en un fichero po

white-space
# translator-comments
#. extracted-comments
#: reference…
#, flag…
#| msgid previous-untranslated-string
msgid untranslated-string
msgstr translated-string

El fichero po esta formada por múltiples entrada de este estilo un ejemplo podría ser:

#: hello.c:17
msgid “hello world!”
msgstr “¡hola mundo!”

Las entradas comienzan por white-space opcional (generalmente un linea en blanco). Luego vienen los comentarios, todos ellos comienzan por el caracter #. Un espacio en blanco después del # indican un comentario creado y mantenido exclusivamente por el traductor. Si al caracter le sigue un punto este indica que el comentario esta sacado del código del programa, es decir, es un comentario del programador. Si el caracter tras el # es el de dos puntos : indica una referencia al punto en el programa donde esta la cadena por traducir, si hay más de una se separan por un espacio. Si la línea comienza por ‘#|’ indica la última cadena no traducida por el traductor. Y finalmente ‘#,’ contiene flags que veremos más adelante. Todos estos comentarios son también opcionales.

Todas las entradas tienen 2 cadenas una muestra la cadena no traducida (msgid) en la misma manera que aparece en el programa fuente y la otra contiene la cadena traducida (msgstr). Las cadenas traducidas y no traducidas usan comilla doble ‘ ” ‘ como delimitador y barra invertida ‘ \ ‘ como escapador (inicio de secuencia de escape). Pero el traductor no necesita tener esto en cuenta a la hora de editar los po’s.

El comentario ‘#,’ es una lista separada por comas de una o varias opciones. Estas son

fuzzy – indica que la traducción ya no tiene por que ser correcta
xxx-format o no-xxx-format – Donde xxx indica el lenguaje que se emplea en la programación (C, Lisp, PHP, C++, Python etc) o entornos específicos de desarrollo (KDE, GCC, Qt, …)

Contextualizar con gettext

También es posible tener entradas con un contexto específico. Por ejemplo:

white-space
# translator-comments
#. extracted-comments
#: reference…
#, flag…
#| msgctxt previous-context
#| msgid previous-untranslated-string
msgctxt context
msgid untranslated-string
msgstr translated-string

El contexto se emplea para desambiguar mensajes con la misma cadena no traducida ya que esto es posible en un fichero po haciendo que cada una tenga un contexto específico. Observa que es diferente tener un contexto vacío que no tenerlo en absoluto.

Es posible, sobre todo en cadenas cortas, que existan varias traducciones para una misma palabra o grupo de palabras. Para tener con el mismo identificador de mensajes msgid varios mensajes debemos emplear msgctx seguido de una cadena para desambiguar los diferentes mensajes. En el programa para indicar el contexto adecuado de un mensaje que necesita ser contextualizado basta con utilizar la función C (o la equivalente en otros lenguajes soportados):

const char *pgettext (const char *msgctxt, const char *msgid);

En la llamada a esta macro, msgctxt y msgid deben ser cadenas literales. La macro devuelve la traducción del msgid pero restringida al contexto dado por msgctxt.

La ‘p’ en ‘pgettext’ significa “particular: pgettext devuelve una traducción en particular de msgid

También existen estas funciones

const char *dpgettext (const char *domain_name,
const char *msgctxt, const char *msgid);
const char *dcpgettext (const char *domain_name,
const char *msgctxt, const char *msgid,
int category);

Que son generalizaciones de pgettext. Estas funciones permiten especificar el nombre de dominio para la traducción y la categoría permite usar otra categoría locale que la proporcionada por la variable de entorno LC_MESSAGES

Para tener una información completa sobre internacionalización y localización, junto con gettext visita el Manual sobre gettext que está disponible en múltiples formatos.

Formas en plural/singular con gettext

Uno de los problemas que no se han tenido en cuanta hasta ahora es el que tiene que ver con las modificaciones en las cadenas debido al número. Existen muchas diferencias en la cadena e incluso en las diferentes formas que adopta una cadena dependiendo del número. Por ejemplo en español se emplea lo que llamamos la forma plural para el 0 y todos los números mayores que uno y para el 1 se emplea la forma singular. Esto puede no ser así para otros idiomas o tener incluso más casos diferenciados para distintas cantidades (por ejemplo, entre otros muchos, el Polaco, el turco o el Japonés).

En la cabecera del fichero po se puede introducir una cabecera para indicar las reglas de las formas plurales en el idioma específico. Este es un ejemplo:

Plural-Forms: nplurals=2; plural=n == 1 ? 0 : 1;

nplurals es un entero que indica cuantas formas de plural hay en un idioma en concreto, la cadena que sigue a ‘plural=’ es una expresión en C que cuya una variable permitida es la n y que no debe contener números negativos.

Para el español podríamos utilizar:

Plural-Forms: nplurals=2; plural=n != 1;

Atención ten en cuenta que la expresión emplea la característica de C que devuelve 0 para false y 1 para true.

La sintaxis para el tipo de entradas que sirven para resolver formas en plural/singular tiene la siguiente formato:

white-space
# translator-comments
#. extracted-comments
#: reference…
#, flag…
#| msgid previous-untranslated-string-singular
#| msgid_plural previous-untranslated-string-plural
msgid untranslated-string-singular
msgid_plural untranslated-string-plural
msgstr[0] translated-string-case-0

msgstr[N] translated-string-case-n

Las diferentes versiones pluralizadas aparecen con indices del array msgstr y el indice se escoge de la expresión en C anterior. Observa que ahora tenemos dos identificadores de cadena no traducida una para el singular y otra para el plural.

La función que permite traducir estos formatos en el programa es ngettext:

char * ngettext (const char *msgid1, const char *msgid2, unsigned long int n)

Un ejemplo de su uso sería este:

printf (ngettext (“%d objeto encontrado”, “%d objetos encontrados”, n), n);

Fijate que hay que pasarle también el valor n a printf, además de a ngettext. Es posible además no tener cardinales en la función aprovechando la característica de que printf descarta los parámetros por defecto. Ejemplo

printf(ngettext(“Un objeto encontrado”,”Varios objetos encontrados”,n),n);

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *