GNU gettext II - Un ejemplo en C
Una vez vistas las nociones básicas en el documento anterior, vamos a
poner en prática los conceptos básicos.
Primer ejemplo
Este ejemplo servirá para mostrar el proceso en detalle de internacionalización y localización
de un programa simple en C, y qué es lo que debe hacer el usuario para localizar el programa.
Supongamos para ello el ejemplo (archivo hola.c):
#include <stdio.h>
int main(void)
{
printf("Hello World");
}
Evidentemente, si lo compilamos y ejecutamos, el resultado por pantalla será Hello World,
ya que todavía no hemos hecho nada para internacionalizar. Empecemos por marcar aquellas
cadenas que creemos que deben ser traducidas:
#include <stdio.h>
#define _(String) gettext(String) // cadenas traducibles
#define N_(String) (String) // cadenas no traducibles
int main(void)
{
printf(_("Hello World"));
}
Lo siguiente es incluir las funciones setlocale(), bindtextdomain() y textdomain():
- setlocale(LC_ALL,""): Usar LC_ALL afectará a todas las categorías de
localización. Al darle el valor "" al locale, estaremos usando el locale definido por
el usuario (obtenido de las variables de entorno LANG o LC). Si no hay un locale definido por el
usuario, se utiliza el valor por defecto "C".
- bindtextdomain("foo","ruta"): Asigna el nombre foo al directorio donde almacenaremos
los archivos de localización. El nombre de foo debería corresponderse con el
nombre de la aplicación. La ruta puede ser absoluta, o relativa al directorio donde se
encuentre el programa que estamos tecleando.
- textdomain("foo"): Esto hace que gettext busque el archivo foo.mo en el directorio
indicado anteriormente. En tiempo de ejecución se puede cambiar entre varios dominios, pero
esto no va a ser nuestro caso.
El programa quedaría de la siguiente forma:
#include <stdio.h>
#include <libintl.h>
#include <locale.h>
#define _(String) gettext(String) // cadenas traducibles
#define N_(String) (String) // cadenas no traducibles
int main(void)
{
setlocale(LC_ALL,"");
bindtextdomain("hola", "locale");
textdomain("hola");
printf(_("Hello World"));
}
Al compilarlo volvemos a observar que la salida por pantalla sigue siendo Hello World. Eso es
debido a que hemos preparado las fuentes para la localización, pero no hemos realizado la
localización en sí misma.
Para ello, comenzamos por extraer las cadenas marcadas del código fuente con la utilidad
xgettext. Con esto obtendremos un archivo .po que podremos utilizar de plantilla. En nuestro
caso deberemos teclear lo siguiente:
xgettext -k_ hola.c -o hola.po
El resultado será el archivo hola.po. Prestamos especial atención a las siguientes
líneas:
#: hola.c:15
msgid "Hello World\n"
msgstr ""
Tal como se explicó en el documento anterior sobre el tema, la cadena tras msgid nos
indica la cadena original en el código fuente, mientras que en msgstr deberímos
indicar la traducción de la misma. Este documento debe ser usado de plantilla para todas
las traducciones que deseemos realizar.
Debemos tener en cuenta que los archivos de localización deberán estar situados en
el directorio [RAIZ_LOCALE]/[CODIGO_LENGUAJE]/LC_MESSAGES, donde RAIZ_LOCALE es el
directorio indicado mediante la función bindtextdomain (en nuestro ejemplo, sería
el directorio locale), y CODIGO_LENGUAJE es el código del lenguaje para el que deseamos
realizar la localización. Por lo tanto, si deseamos la traducción españa, debemos
realizar los siguientes pasos:
- Creamos el directorio locale/es/LC_MESSAGES/ en el mismo directorio donde está situado
hola.c
- Copiamos el archivo plantilla hola.po a dicho directorio
- Lo editamos, poniendo la traducción correcta:
#: hola.c:15
msgid "Hello World\n"
msgstr "Hola Mundo\n"
- Y finalmente generamos el archivo binario de localización dependiente de la máquina:
msgfmt hola.po -o hola.mo
Ya tenemos el programa localizado. Tan solo queda darle a la variable de entorno LANG el valor es, compilar
el programa, y tras ejecutarlo, ya podremos ver el mensaje en español.
Segundo ejemplo
A continuación tenemos un ejemplo más detallado en las que se presentan varias de las
situaciones comentadas en el documento anterior sobre gettext. Los pasos a seguir serán los mismos
que en el caso anterior. Puede ser un interesante ejercicio para el lector conseguir la localización
de este programa (adivinar.c):
// Programa de demostración del uso de
// gettext - adivinar.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> // Para inicializar la semilla aleatoría
// Incluimos la librería de gettext
#include <libintl.h>
#include <locale.h>
#define _(String) gettext(String) // cadenas traducibles
#define N_(String) (String) // cadenas no traducibles
#define MAX_INTENTOS 10
int main(char **argv, int argc) {
int numero, intentos, respuesta;
short int acertado;
static const char *mensajes[] = {
N_("winer"),
N_("loser")};
setlocale(LC_ALL, "");
bindtextdomain("adivinar", "locale");
textdomain("adivinar");
// Inicializamos la semilla de la secuencia pseudoaleatoria
srand(time(0));
// Obtenemos el número a adivinar entre 0 y 20
numero = (int)(((float)rand()/(float)RAND_MAX)*20);
// Inicialización del resto de variables
intentos = 0;
acertado = 0;
printf(_("Welcome to this test program\n\n"));
do {
intentos++;
do {
printf(_("%d - Enter a number (0-20) : "),intentos);
scanf("%d", &respuesta);
if (respuesta < 0 || respuesta > 20)
printf(_("You must enter a value lesser than 21 and greater than -1\n"));
} while (respuesta < 0 || respuesta > 20);
if (respuesta == numero)
acertado = 1;
} while (!acertado && intentos < MAX_INTENTOS);
// Lo siguiente no es muy correcto pero es ilustrativo
acertado ? printf(_("You've become a %s after %d guesses\n"),_(mensajes[0]),intentos) : printf(_("You've become a %s after %d guesses\n"), _(mensajes[1]), intentos);
printf(_("The secret number was %d\n"), numero);
return 0;
}
Para que se tengan en cuenta las cadenas constantes del array de mensajes ("winner" y "loser") se
debe indicar a xgettext que busque cadenas marcadas con _ y con N_, tal como se
ve a continuación:
xgettext -k_ -kN_ adivinar.c -o adivinar.po
El archivo .po resultante, junto con las traducciones, sería el siguiente (es interesante ver
como se resuleve el problema de los parámetros de printf en distinto orden al original
en las líneas resaltadas):
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Free Software Foundation, Inc.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2002-02-28 21:52+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: adivinar.c:21
msgid "winner"
msgstr "ganador"
#: adivinar.c:22
msgid "loser"
msgstr "perdedor"
#: adivinar.c:37
msgid ""
"Welcome to this test program\n"
"\n"
msgstr ""
"Bienvenido a este programa de prueba\n"
"\n"
#: adivinar.c:41
#, c-format
msgid "%d - Enter a number (0-20) : "
msgstr "%d - Introduce un número (0-20) : "
#: adivinar.c:44
msgid "You must enter a value lesser than 21 and greater than -1\n"
msgstr "Debes introducir un valor menor que 21 y mayor que -1\n"
#: adivinar.c:50
#, c-format
msgid "You've become a %s after %d guesses\n"
msgstr "Tras %2$d te has convertido en un %1$s\n"
#: adivinar.c:51
#, c-format
msgid "The secret number was %d\n"
msgstr "El número secreto era %d\n"
[VOLVER]