Aplicaciones multilingües: gettext

A menudo se piensa que una aplicación estaría mucho mejor si se encontrara en varios idiomas. Podemos pensar en distintas formas de soportar esto, pero otros ya lo han pensado por nosotros y han creado las fabulosas herramientas gettext. Este documento pretende ser una guía para el uso rápido (rápido de verdad) de estas herramientas.

Ingredientes

  • gettext

Modificaciones en el código

Lo primero es acordarse de evitar cadenas sin más (al menos las que se muestran) en nuestro código. Ahora importaremos la librería gettext y utilizaremos la función gettext. Se encuentra disponible para numerosos lenguajes, aunque aquí se explicará con ejemplos en python (las diferencias serán mínimas en el resto de lenguajes).

En Python queda así:

import gettext
gettext.textdomain("programita")
gettext.bindtextdomain("programita", "./mo")
gettext.gettext("hello, world!")

No os asustéis: Las funciones “textdomain” y “bindtextdomain” sirven para decirle a gettext dónde se encuentran las traducciones. Aquí se está poniendo un ejemplo, así que sustituid “programita” por el nombre de vuestro programa, y “./mo” por el directorio donde vamos a meter las traducciones.

A menudo, además, se suele importar la función gettext cambiándole el nombre a algo más sencillo como la barra baja. De esta manera, nos evitamos tener que escribir “gettext.gettext“ cada vez. Para hacer esto hay distintas maneras, una de ellas es durante la importación:

from gettext import gettext as _

Generación de la plantilla de traducción (.pot)

Podemos obtener la plantilla del código en casi cualquier lenguaje: C, C++, Python, Perl, Glade, Java, … Y se hace así:

$ xgettext —language=Python -j programita.py

Las opciones son: —language indicando el lenguaje del código, y -j que indica que, si hay ya un .pot existente, se deben mezclar los mensajes.

Si hemos usado algún cambio y no aparece la función como “gettext”, será necesario usar también la opción —keyword. Por ejemplo:

$ xgettext —language=Python -j -k _ programita.py

Ale, ya tenemos el messages.pot.

Generar el fichero de traducciones (*.po)

Ahora se utiliza el messages.pot para generar cada plantilla. Nosotros queremos la traducción al castellano, así que será es.po:

$ msginit -i messages.pot -o es.po

Ahora viene la parte en la que editamos el fichero es.po (yo suelo usar gtranslator o kbabel) y demás.

Mezclar el fichero de traducciones (.po) con una nueva plantilla (.pot)

Este sistema sería poco eficiente si tuviéramos que traducirlo todo de nuevo cada vez que se cambie el fuente. Por ello, hay un sistema por el que se mezcla el fichero ya traducido con la plantilla nueva (es decir: volvemos al paso de creación de plantilla y después ejecutamos esto):

$ msgmerge -U es.po messages.pot

Y, nuevamente, volvemos a traducir Laughing out loud

Creación del fichero de equivalencias (.mo)

Realmente, el ficherito .po no se utiliza en el ejecutable. Es necesario utilizar un ficherito .mo. Aquí crearemos nuestro es.mo, como es lógico:

$ msgfmt es.po mo/es/LC_MESSAGES/programita.mo

Como podéis observar, el resultado lo dejamos en mo/es/LC_MESSAGES/. Eso lo hacemos así porque en el código fuente dijimos que las traducciones colgarían del directorio mo, el idioma es Español (las locales son “es”), y porque todos los .mo deben colgar de un directorio LC_MESSAGES dentro de las locales para que sea encontrado correctamente.

Utilización

Ya está. No hay nada más que contar. Si las locales son “es”, escribirá nuestra traducción, y si son “en”, lo escribirá en inglés. Eso es todo.

Ejemplo

Veamos un ejemplo, sugerencia hecha por david.villa:

#!/usr/bin/python
 
import gettext
 
APP = "hello"
 
gettext.textdomain ( APP )
gettext.bindtextdomain ( APP, "./mo" )
 
_ = gettext.gettext
 
print _("hello, world!")

Como requisito, necesitaremos el directorio donde vamos adejar las cosas. En el código hemos puesto “./mo”, así que hay que ser consecuentes:

$ mkdir -p  mo/es_ES/LC_MESSAGES

Ahora inicializamos (llamaremos al .pot “messages.pot:

$ xgettext —language=Python —keyword=_ -o messages.pot hello.py

Y generamos nuestro primer .po:

$ msginit -i messages.pot -l es_ES

Editamos el archivo es.po que se nos habrá creado, traduciendo la única cadena. Mi archivo pinta así:

# Spanish translations for PACKAGE package.
# Copyright © 2010 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# miguel <miguelangel.garcia@gmail.com>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-05-25 07:31+0200\n"
"PO-Revision-Date: 2010-05-25 07:32+0200\n"
"Last-Translator: miguel <miguelangel.garcia@gmail.com>\n"
"Language-Team: Spanish <>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
#: hello.py:12
msgid "hello, world!"
msgstr "hola, mundo!"

Por ahora no nos fijaremos mucho en las cosa que deberíamos cambiar, como el PACKAGE y demás. Sólo nos queda compilar:

$ msgfmt -o mo/es_ES/LC_MESSAGES/hello.mo es.po

y probarlo:

$ LANG=es_ES ./hello.py 
hola, mundo!
$ LANG=en_US ./hello.py 
hello, world!
$ 

¡¡Gracias, David, por tu propuesta!!

Referencias

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
david.villa's picture

El PyMOTW de gettext

El PyMOTW de gettext: http://www.doughellmann.com/PyMOTW/gettext/index.html

No soy portavoz de ningún colectivo, grupo o facción. Mi opinión es personal e intransferible.

david.villa's picture

Ejemplo aclaratorio

Hola:

Para quién no sepa lo que es gettext, la verdad es que tu receta es un poco durilla, aunque obviamente eso es culpa nuestra por nuestra infinita ignorancia. Para intentar simplificar las cosas a los que se quieran acercar a esto de la internacionalización he puesto un ejemplo en el repo con un README explicando paso a paso lo que hay que hacer. Si he metido la pata en el ejemplo, por favor, corrígeme magmax.

El ejemplo en cuestión está en:
https://bitbucket.org/crysol_org/samples/src/aa0f5e85b6cd/python-gettext

El ejemplo es más complejo de lo que pones en la receta, pero se supone que así es más genérico (yo no lo tengo tan claro). Lo he cogido de aquí: http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-ap...

Saludos

No soy portavoz de ningún colectivo, grupo o facción. Mi opinión es personal e intransferible.

magmax's picture

Corregido.

Hola, David.

He corregido la receta. Verás que no he utilizado el ejemplo que proponías, ya que lo he visto algo más complejo para comenzar. Con lo poquito que se indica aquí es suficiente como para que funcione.

He añadido algún cambio extra, ya que fue hace mucho tiempo cuando escribí esta receta.

Y respecto a lo de que es un poco dura... Traté de ser escueto. Creo que ahora, con el ejemplo, eso se corrige.

Un saludo.

Miguel Ángel García
http://magmax.org

david.villa's picture

Mucho mejor, gracias

Mucho mejor, gracias.

Por cierto, para los comandos de consola es [code class=console] en lugar de class=bash, que sería para scripts bash. Te he cambiado uno, cambia tú el resto, please, que la receta gana mucho en legibilidad.

No soy portavoz de ningún colectivo, grupo o facción. Mi opinión es personal e intransferible.

magmax's picture

También corregido

Pues sí, queda mucho mejor.

Gracias de nuevo!

Miguel Ángel García
http://magmax.org