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():
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:
  1. Creamos el directorio locale/es/LC_MESSAGES/ en el mismo directorio donde está situado hola.c
  2. Copiamos el archivo plantilla hola.po a dicho directorio
  3. Lo editamos, poniendo la traducción correcta:

    #: hola.c:15
    msgid "Hello World\n"
    msgstr "Hola Mundo\n"

  4. 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]