El inglés es el idioma oficial por regla general en la práctica totalidad de proyectos de software libre en la actualidad. Este idioma se ha convertido en este siglo en el idioma internacional por excelencia, y es una realidad que afecta, no sólo a los usuarios dentro del mundo GNU/Linux sino a cualquier usuario de los sistemas de la información. Es por esto que el inglés es el idioma en el que habitualmente los programas presentan sus mensajes, y la mayoría de la documentación está realizada en inglés.
No cabe duda que el uso de un único lenguaje facilita la coordinación del trabajo de los desarrolladores, a la hora de intercambiar ideas, y la difusión de un proyecto, pero los usuarios desean más, desean que los entornos en los que trabajen estén adaptados a sus entornos específicos.
Esta adaptación no es exclusivamente, aunque sí es la primera evidencia, la traducción de todos y cada uno de los mensajes, menús, botones, etc. que muestra el programa al usuario. También incluye la adaptación del programa para que al utilizar otras características eminentemente locales, se "adapte" al entorno. Por ejemplo, al utilizar unidades monetarias muestre preferentemente las unidades locales con las expresiones habituales de separación de decimales, nomenclatura, etc. o que la representación de fechas y horas se haga en la manera acostumbrada al usuario (día-mes-año en lugar de mes-día-año por ejemplo), o incluso que las unidades métricas utilizadas para la representación de distancias, pesos, etc. sean las utilizadas por el usuario.
La modificación de un software para que sea capaz de ofrecer estas funciones es lo que se conoce como internacionalización (muchas veces se reduce al acrónimo 'i18n'). Mientras que la modificación de estas funciones para adaptar un programa a un determinado entorno se conoce como localización (muchas veces reducido a 'l10n'). Explicándolo en forma breve, los programadores internacionalizan y los traductores localizan.
Dentro de la internacionalización de programas, uno de los aspectos más críticos es la posibilidad de traducir los mensajes que presenta el programa al usuario. No sólo los mensajes que puedan aparecer en el interfaz gráfico, por ejemplo, sino todo tipo de mensajes de error que genere el programa, ayuda en las opciones al ejecutarse, etc.
La herramienta GNU para la internacionalización de mensajes en programas es Gettext. Esta herramienta, desarrollada entre 1994 y 1995 por un grupo variado de programadores, facilita la creación de programas que pueden distribuirse con múltiples catálogos de mensajes en distintos idiomas. Posteriormente, en entornos localizados, los programas pueden presentar los mensajes correspondientes al entorno declarado por el usuario.
Esta herramienta es relativamente transparente al programador, ya que sólo tiene que marcar los mensajes que cree que deben traducirse. Asimismo, la modificación del código fuente y reubicación de los mensajes es relativamente transparente al traductor, que sólo tiene que mantener actualizado un listado de traducciones de los mensajes. Las herramientas de gettext se encargan, por debajo, de homogeneizar los catálogos y modificar éstos cuando las fuentes cambian, pero preservando las traducciones ya realizadas.
De esta forma, el trabajo de traducción de los mensajes de un programa se reduce a una traducción inicial de todos los mensajes y al mantenimiento de los pequeños (o grandes) cambios en el código que puedan suponer la introducción (o desaparición) de mensajes. Y el trabajo del programador se limita a incorporar las funciones necesarias en su programa. Una vez hecho esto, el trabajo de ambos grupos puede proceder por separado, lo cual facilita el desarrollo en ambos sentidos. Es decir, un traductor no tiene que depender del programador para incorporar un nuevo idioma y un programador no depende del esfuerzo de traducción para la generación de nuevas versiones de su programa.
Lo primero que debe hacer un programador para internacionalizar su software es detectar en qué puntos de éste se introducen mensajes que van destinados al usuario (bien porque sean mensajes que aparezcan en pantalla durante el uso del programa o porque se envíen en determinadas circunstancias de error). Estos mensajes (cadenas de caracteres) tienen que ser marcados de forma que pueda extraerse esta información.
Algunos de los cambios que son necesarios hacer a las fuentes del programa las realiza el programa gettextize. Este programa, invocado directamente en el directorio raíz de las fuentes realiza las siguientes funciones:
ABOUT-NLS
que describe el entorno de
internacionalización utilizado.intl/
y pone allí todos los ficheros
que se distribuyen con gettext, estos ficheros
son los ficheros de cabecera de la librería
libintl y deben ser idénticos entre distintos
proyectos de software internacionalizados.po/
y el fichero
Makefile.in.in
dentro de éste. Este directorio
será el repositorio de la traducción de mensajes que se
incluirá con la distribución del programa.Sin embargo, aunque se hayan realizado estos cambios a las fuentes, el desarrollador tiene que realizar otros de forma manual para asegurar que la internacionalización pueda llevarse fácilmente a cabo:
po/POTFILES.in
e introducir en él una
lista (un fichero por línea) con los ficheros que
contienen mensajes marcados para traducción.
configure.in
(del
sistema de auto configuración) para:
PACKAGE=programa
VERSION=X.Y.Z
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
AC_SUBST(PACKAGE)
AC_SUBST(VERSION)
intl/Makefile
y po/Makefile.in
.acconfig.h
y aclocal.m4
(si existen) para incluir las funciones necesarias para
que se detecte el soporte de internacionalización al
configurar las fuentes.
Makefile.in
en el directorio raíz
y en los de las fuentes de forma que se incluyan las
librerías de internacionalización al compilar (tanto al
generar los ficheros objeto como al enlazar
dinámicamente), y para que se instalen los ficheros po en
el sistema al ejecutar a make install.
Con estos cambios, el desarrollador se asegura que, al configurar el programa se detecte el soporte (o no) de internacionalización del sistema y que, tras esto, se compilen las fuentes de la manera adecuada y se instalen con soporte de internacionalización (incluyendo todos los ficheros de las traducciones) si el sistema no carece de soporte para éste.
Una vez realizado estos cambios es necesario cambiar las fuentes C en sí del programa, para marcar los mensajes que se deban traducir. La búsqueda de estos mensajes puede ser algo tediosa, aunque dependerá del programa en sí. Se puede seguir la máxima de "todo aquello que se presenta al usuario debe ser internacionalizado" y así buscar mensajes que se presenten a este (mediante funciones de salida como printf) o cadenas que se utilicen para generar salidas (como botones o menús). Una vez localizadas, estos mensajes deben rodearse con una llamada a gettext. Así, una línea de código del estilo de
printf("Hola, mundo\n");
se convertirá a
printf(gettext("Hola, mundo\n"));
También es común utilizar la forma abreviada _() con
un #DEFINE previo, en lugar de la llamada a gettext. En la
mayoría de estos casos estos cambios no afectan mucho al
código así, sin embargo habrá casos en los que sea necesario
modificar ligeramente el código. Por ejemplo, cuando la cadena
de caracteres a traducir se había supuesto de un tamaño fijo
de caracteres. No olvidemos que lo que hará la función
gettext en tiempo de ejecución será sustituir esta
cadena por el equivalente traducido dentro de la ejecución del
programa.
En muchos casos, esta modificación de las fuentes, cuando los mensajes de salida están definidos claramente, se puede hacer incluso de forma directa mediante la sustitución de expresiones regulares con un sencillo programa en PERL o awk.
En cualquier caso, la documentación de gettext incluye precisas explicaciones de cómo modificar convenientemente las fuentes para que tengan soporte de internacionalización.
En el listado 1 se muestra un ejemplo de la internacionalización de un programa. En este caso se trata del programa hello, un programa de demostración de GNU que muestra el consabido "Hello, world!" por pantalla al ejecutarse. Como puede verse (con '+' se indican las líneas añadidas y con '-' las eliminadas) se han modificado, rodeándolos con una llamada a la función gettext todos los mensajes que se envían por pantalla. También se han añadido las directivas de compilador necesarias para incluir las funciones de internacionalización.
El resultado de ejecutar xgettext hello.c se muestra en el
listado 2. Como puede verse aquí, todas las cadenas marcadas con
gettext()
han sido extraídas y el fichero resultante es una
plantilla que puede utilizarse para traducir directamente éstas.
Una vez que se han modificado de forma adecuada las fuentes, se utilizará la herramienta xgettext para extraer de éstas las cadenas que pueden ser traducidas. Estas cadenas se extraen todas juntas a un sólo fichero po/messages.pot. Este fichero será el que se pueda utilizar como plantilla posteriormente por los traductores para generar los ficheros LENGUAJE.po.
Este proceso se muestra de forma completa en la figura 1. Como
puede verse allí éste fichero se obtiene a través de las fuentes ya
preparadas para internacionalizarse. Si se dispone de una traducción
de una versión previa del programa, podrá utilizarse el programa
msgmerge para utilizar las traducciones ya realizadas de
mensajes en la generación del fichero a
traducir. Msgmerge tiene la ventaja de que es capaz
de juntar ficheros .po
de forma difusa. Es
decir, aunque las fuentes hayan modificado la localización de
los mensajes a internacionalizar, es capaz de encontrar dónde
debería incluir un texto ya traducido.
Con el fichero .po
generado, con o sin traducciones
previas, éste puede ser ya distribuido a los responsables de
las traducciones para que incorporen a éste la traducción de
los mensajes existentes. Si nos encontramos ante un programa
que se va a internacionalizar en este momento a un nuevo
idioma, el fichero .po
tendrá un aspecto similar al
del Listado 2. Sin embargo, si el programa ya ha sido
internacionalizado previamente, el fichero tendrá cadenas
traducidas y cadenas sin traducir, o incluso cadenas que han
sido modificadas en la última versión del programa y
necesitan ser revisadas.
¿Cómo evolucionan los ficheros .po
? Uno podría pensar que
una vez realizada la traducción de los mensajes a un
determinado idioma para un programa no es necesario realizar
nada más. Sin embargo esto no es así, los programas son entes
vivos, cambian, evolucionan, incorporan nuevas
funcionalidades, etc. Esto trae consigo, como uno pudiera
esperar, que los mensajes que se ofrecen al usuario, los
botones, los menús, etc. cambien también con el tiempo. Aunque
el grueso de los mensajes pueda no variar, es necesario
revisar, con cada nueva versión que se distribuye de un
programa los mensajes internacionalizados.
Pero sin embargo, y he aquí una de las maravillas de gettext, esta revisión no influye para nada la tarea del programador o programadores encargados de mejorar y distribuir las nuevas versiones del programa. Es decir, el hecho de que no haya una persona encargada de una determinada traducción no tiene por qué interrumpir la distribución de un programa. Sí, los mensajes no estarán todos traducidos, pero ésto "sólo" se traducirá en que un usuario verá los mensajes en dos idiomas. Los traducidos, en su idioma nativo, y los no traducidos en el idioma original.
Evidentemente, esto es mucho mejor que no disponer de una nueva versión del programa por no poder contactar con los traductores cuando a lo mejor se está hablando de un programa internacionalizado a más de diez idiomas. También es mejor que ver los mensajes en el idioma original cuando no se ha podido llegar a traducir el 100% de los mensajes.
Asisten en esta tarea las herramientas, msgmerge y msgcmp. La segunda permite determinar si se han traducido todos los mensajes con respecto a la última versión disponible del programa, y la primera permite distribuir una nueva versión de un fichero de mensajes "mezclando" los mensajes ya traducidos con los nuevos a traducir.
Como se puede ver en listado 2, la traducción de mensajes es muy
sencilla, y cualquier usuario puede coger un fichero
.po
traducido parcialmente y rellenar los "huecos"
que falten. Este hecho, en el mundo GNU lleno de personas de
muchas capacidades distintas dispuestas a colaborar, garantiza
la posibilidad de colaborar y de formar parte de un equipo de
traducción a usuarios que no tengan ningún tipo de
conocimiento de programación. Para mantener un fichero
.po
al día sólo es necesario conocer tanto el idioma
original como el idioma final de la traducción.
Así pues, uno de los objetivos logrados de la librería gettext es que cualquier persona, con unos mínimos conocimientos, pueda colaborar en la internacionalización de los programas derivados del software libre. No siendo necesario ser un desarrollador de programas, ni una persona experta en programación para llevar estas tareas a cabo.
En el proyecto GNU se dan soporte a distintos grupos de internacionalización que son las personas responsables de la traducción de los programas. Aún así no es necesario una dedicación permanente a estos grupos para internacionalizar un programa, como ya se ha visto, la internacionalización puede ser un esfuerzo puntual y concreto. La existencia de los grupos garantiza, sin embargo, la correcta revisión de estos trabajos puntuales que pueden realizar los usuarios, la elaboración de glosarios que den uniformidad a los programas traducidos y la actualización de las traduccioens en vistas de nuevas versiones de programas.
Con las traducciones ya realizadas de los ficheros .po
de
los distintos lenguajes disponibles, ya sólo queda generar el
formato necesario para su distribución. En la distribución de
mensajes traducidos, se utilizan ficheros .mo
que
genera de forma automática la herramienta
msgftm. Estos ficheros binarios se distribuyen de
forma conjunta con el código fuente, instalándose en
ubicaciones predefinidas.
La librería gettext cargará dichos binarios cuando el entorno del usuario indique un idioma del que está disponible la correspondiente versión traducida.
Esta ubicación suele ser
/usr/share/locale/LENGUAJE/LC_MESSAGES
, lo que
permite que, el usuario que no desee mensajes en otros idiomas
distintos del suyo, pueda eliminar aquellos que no considere
importantes. En cualquier caso, los binarios del programa se
distribuyen habitualmente con todos los mensajes traducidos a
todos los idiomas, y tendrá que ser el sistema de instalación
del sistema operativo del usuario el que le de la opción de
eliminar dichos idiomas.
Ya se han visto, en la descripción de las tareas de internacionalización, algunos de los programas que incorpora la librería de GNU gettext para ayudar a la tarea de internacionalización. La librería incorpora, en total, los siguientes programas:
messages.po
correspondiente..mo
.mo
genera su equivalente .po
.po
.
.po
para comprobar que
ambos contienen el mismo número de cadenas traducidas, con
lo que permite verificar si se han traducido todos y cada
uno de los mensajes del programa.
En este artículo se ha visto cómo se internacionaliza un programa, y qué herramientas se pueden utilizar para ello, particularizando a la librería más utilizada del mundo de software libre, la librería Gettext. Aunque éste artículo se ha centrado en la internacionalización de fuentes en C/C++, también es posible internacionalizar fuentes escritas en otros lenguajes de programación. Por ejemplo, PERL cuenta con una librería de adaptación a gettext llamada Locale::gettext que permite la internacionalización de programas interpretados en este lenguaje.
El usuario avanzado, que desee encontrar más información, puede
acudir a http://www.gnu.org/software/gettext/gettext.html (y
gettext.gnu.org). Si tiene instalado el software de gettext,
también dispondrá de la ayuda en línea, que podrá consultar
ejecutando info gettext
Si desea ayudar al esfuerzo de internacionalización de programas de GNU, contacte con su equipo local de trabajo. Puede contribuir sus trabajos a través del Proyecto de Traducciones Libres (Free Translation Proyect, n. del a.) en http://www.iro.umontreal.ca/contrib/po/. Si desea ver el estado de las traducciones existe una base de datos de traducciones y traductores que puede consultar en http://www2.iro.umontreal.ca/ pinard/po/registry.cgi?team=es. En cualquier caso, algunos programas internacionalizados pueden no haberse incluido en el proyecto GNU, para hacerse una idea de la internacionalización de programas en un sistema operativo GNU/Linux, pruebe a consultar, por ejemplo, el Monitor de traducciones de Debian en http://www.debian.org/intl/l10n/po-es
El inglés es el idioma oficial de la mayoría de los proyectos de software libre.
Los usuarios desean que sus entornos estén adaptados a ellos.
La modificación del software es parte de la internacionalización.
La librería gettext es el estándar para internacionalizar programas.
Para preparar un programa hay que detectar dónde se ofrecen mensajes al usuario.
Algunos cambios para la internacionalización se pueden hacer de forma automática.
El soporte de internacionalización se puede detectar al compilar el programa.
Es necesario marcar los mensajes en las fuentes en C.
La plantilla de mensajes está en el fichero messages.pot
Los mensajes se traducen en los ficheros .po
Los ficheros .po se pueden utilizar aunque estén parcialmente traducidos.
Gettext incorpora una variedad de herramientas para ayudar a la internacionalización.
La traducción de mensajes es tan sencilla que cualquier usuario puede colaborar.
PERL también incorpora una librería de adaptación a gettext.
LISTADO 1
$ diff -ur hello/hello-1.3/hello.c ./hello-int/hello-1.3/hello.c
--- hello/hello-1.3/hello.c Sun Jun 10 18:30:47 2001
+++ ./hello.c Tue May 16 21:49:28 2000
@@ -64,6 +64,12 @@
#endif /* HAVE_ALLOCA_H. */
#endif /* GCC. */
+#include >libintl.h<
+#define _(String) gettext (String)
+
+#include "config.h"
#define the (1)
@@ -79,7 +85,7 @@
extern char version[];
-char usage[] = "Usage: %s [-htvm] [--help] [--traditional] [--version] [--mail]\n";
+char usage[] = gettext("Usage: %s [-htvm] [--help] [--traditional] [--version] [--mail]\n");
static char *progname;
@@ -91,6 +97,14 @@
int optc;
int h = 0, v = 0, t = 0, m = 0, lose = 0, z = 0;
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
+
+
+
progname = argv[0];
#define king
@@ -136,15 +150,15 @@
if (h)
{
/* Print help info and exit. */
- fputs ("This is GNU Hello, THE greeting printing program.\n",
+ fputs (gettext("This is GNU Hello, THE greeting printing program.\n"),
stderr);
fprintf (stderr, usage, progname);
- fputs (" -h, --help\t\t\tPrint a summary of the options\n", stderr);
- fputs (" -t, --traditional\t\tUse traditional greeting format\n",
+ fputs (gettext(" -h, --help\t\t\tPrint a summary of the options\n"), stderr);
+ fputs (gettext(" -t, --traditional\t\tUse traditional greeting format\n"),
stderr);
- fputs (" -v, --version\t\t\tPrint the version number\n", stderr);
- fputs (" -m, --mail\t\t\tPrint your mail\n", stderr);
+ fputs (gettext(" -v, --version\t\t\tPrint the version number\n"), stderr);
+ fputs (gettext(" -m, --mail\t\t\tPrint your mail\n"), stderr);
exit (0);
}
@@ -177,7 +191,7 @@
struct passwd *pwd = getpwuid (getuid ());
if (! pwd)
{
- fprintf (stderr, "%s: Who are you?\n", progname);
+ fprintf (stderr, gettext("%s: Who are you?\n"), progname);
exit (1);
}
user = pwd->pw_name;
@@ -239,13 +253,13 @@
}
}
else if (z)
- puts ("Nothing happens here.");
+ puts (gettext("Nothing happens here."));
else
{
if (t)
- printf ("hello, world\n");
+ printf (gettext("hello, world\n"));
else
- puts ("Hello, world!");
+ puts (gettext("Hello, world!"));
}
exit (0);
@@ -260,7 +274,7 @@
char *ptr = malloc (size);
if (! ptr)
{
- fprintf (stderr, "%s: virtual memory exhausted\n", progname);
+ fprintf (stderr, gettext("%s: virtual memory exhausted\n"), progname);
exit (1);
}
return ptr;
PIE LISTADO 1: Modificaciones realizadas al código de hello.c para internacionalización
LISTADO 2
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Free Software Foundation, Inc.
# FIRST AUTHOR >EMAIL@ADDRESS<, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2001-06-10 19:29+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME >EMAIL@ADDRESS<\n"
"Language-Team: LANGUAGE >LL@li.org<\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
#: hello.c:88
#, c-format
msgid "Usage: %s [-htvm] [--help] [--traditional] [--version] [--mail]\n"
msgstr ""
#: hello.c:153
msgid "This is GNU Hello, THE greeting printing program.\n"
msgstr ""
#: hello.c:156
msgid " -h, --help\t\t\tPrint a summary of the options\n"
msgstr ""
#: hello.c:157
msgid " -t, --traditional\t\tUse traditional greeting format\n"
msgstr ""
#: hello.c:160
msgid " -v, --version\t\t\tPrint the version number\n"
msgstr ""
#: hello.c:161
msgid " -m, --mail\t\t\tPrint your mail\n"
msgstr ""
#: hello.c:194
#, c-format
msgid "%s: Who are you?\n"
msgstr ""
#: hello.c:256
msgid "Nothing happens here."
msgstr ""
#: hello.c:260
msgid "hello, world\n"
msgstr ""
#: hello.c:262
msgid "Hello, world!"
msgstr ""
#: hello.c:277
#, c-format
msgid "%s: virtual memory exhausted\n"
msgstr ""
PIE LISTADO 2: Fichero po de hello.c
Figura 1: El proceso completo de internacionalización con gettext