Traducción de aplicaciones Python con gettext
Los conceptos de internacionalización y localización —abreviados como i18n y L10n por el número de letras que tienen los términos en inglés— se refieren a:
- Internacionalización:
- Proceso por el que se adapta una aplicación para que pueda presentar los mensajes —cadenas, fechas, números y otros elementos— en diferentes lenguas.
- Localización:
- Proceso por el que se traducen todos los mensajes de una aplicación a una nueva lengua.
La idea fundamental es que los usuarios han de poder utilizar las aplicaciones en su lengua nativa. Por lo que debe ser posible traducir todas las cadenas de una aplicación a otra lengua sin tener que cambiar el código de la aplicación. Es decir, se debe facilitar que los traductores puedan realizar traducciones a nuevas lenguas sin depender de los programadores.
GNU gettext es la herramienta fundamental para realizar esta tarea. Cuando se utiliza GNU gettext cada mensaje de la aplicación se busca en un catálogo correspondiente a la lengua que tenga configurado el usuario, así la aplicación presentará al usuario los mensajes en su lengua. Y los traductores podrán nuevas versiones del catálogo para soportar nuevos idiomas sin que los programadores tengan que cambiar la aplicación.
En este artículo solo se pretende mostrar unas breves notas sobre el proceso, utilizando GNU gettext para internacionalizar y localizar una aplicación de ejemplo en Python.
Una pequeña aplicación Python
El objetivo de este artículo es ilustrar el proceso de internacionalización y localización de una aplicación. A modo de ejemplo se va a utilizar una aplicación mínima en Python. Evidentemente una aplicación real tendrá muchas más cadenas y realizar la traducción a una nueva lengua tendrá más trabajo.
El ejemplo de aplicación en Python puede ser el fichero helloworld.py:
def print_text():
print("Hello world")
print("Goodbye")
if __name__ == '__main__':
print_text()
Que al ejecutarse imprime dos cadenas:
vcarceler@luna:~/ejemplo$ python helloworld.py
Hello world
Goodbye
vcarceler@luna:~/ejemplo$
Esta aplicación todavía no puede mostrar los mensajes en diferentes idiomas, así que será necesario transformarla.
Internacionalización
GNU gettext está incluido en la biblioteca de Python y se puede importar en la aplicación para internacionalizar las dos cadenas.
import gettext
_ = gettext.gettext
def print_text():
print(_("Hello world"))
print(_("Goodbye"))
if __name__ == '__main__':
print_text()
Se puede observar cómo se ha definido la función _() que se puede utilizar para internacionalizar cualquier cadena. Durante la ejecución gettext buscará la cadena en el catálogo de la lengua adecuada y retornará su valor para que se muestre al usuario.
Como de momento no se ha creado ningún catálogo el programa sigue haciendo exactamente lo mismo que antes, mostrar las cadenas en su versión original. Es decir en inglés.
vcarceler@luna:~/ejemplo$ python helloworld.py
Hello world
Goodbye
vcarceler@luna:~/ejemplo$
Localización
Aprovechando que en el código de la aplicación ya están marcadas las cadenas a localizar es posible extraer de manera automática estas cadenas a un catálogo que servirá como plantilla para las diferentes lenguas.
Se creará el directorio locales para contener los diferentes catálogos y el fichero locales/helloworld.pot será la plantilla para los catálogos. La herramienta pygettext es la encargada de recorrer el código fuente de la aplicación para generar el fichero locales/helloworld.py.
vcarceler@luna:~/ejemplo$ mkdir locales
vcarceler@luna:~/ejemplo$ pygettext -d helloworld -o locales/helloworld.pot helloworld.py
El fichero locales/helloworld.pot contendrá todas las cadenas localizables de nuestra aplicación:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2019-05-11 10:29+CEST\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=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: helloworld.py:5
msgid "Hello world"
msgstr ""
#: helloworld.py:6
msgid "Goodbye"
msgstr ""
En este punto será necesario crear dos subdirectorios de locales para cada lengua a la que se vaya a traducir la aplicación. Por ejemplo, para el caso del castellano se crearán los directorios locales/es/LC_MESSAGES/ y para el ruso locales/ru/LC_MESSAGES/. En su interior se colocará una copia del catálogo traducido a esa lengua: locales/es/LC_MESSAGES/helloworld.po y locales/ru/LC_MESSAGES/helloworld.po.
Como se trata de ficheros de texto los traductores lo podrán editar con su herramienta favorita. Los resultados en castellano y ruso serán:
locales/es/LC_MESSAGES/helloworld.po
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2019-05-11 10:29+CEST\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=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: helloworld.py:5
msgid "Hello world"
msgstr "Hola mundo"
#: helloworld.py:6
msgid "Goodbye"
msgstr "Adiós"
locales/es/LC_MESSAGES/helloworld.po
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2019-05-11 10:29+CEST\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=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: helloworld.py:5
msgid "Hello world"
msgstr "Привет, мир!"
#: helloworld.py:6
msgid "Goodbye"
msgstr "Пока"
Finalmente hay que procesar los catálogos con extensión .po para obtener una versión binaria con extensión .mo que es el fichero que utilizará la aplicación.
La herramienta msgfmt hará el trabajo:
vcarceler@luna:~/ejemplo$ cd locales/es/LC_MESSAGES/
vcarceler@luna:~/ejemplo/locales/es/LC_MESSAGES$ msgfmt -o helloworld.mo helloworld
vcarceler@luna:~/ejemplo/locales/es/LC_MESSAGES$ cd ~/ejemplo/locales/ru/LC_MESSAGES/
vcarceler@luna:~/ejemplo/locales/ru/LC_MESSAGES$ msgfmt -o helloworld.mo helloworld
Selección del lenguaje en la aplicación
La aplicación tiene varios métodos para seleccionar en el código el lenguaje que se va a utilizar.
Por ejemplo, si se modifica el programa de la siguiente manera:
import gettext
language = gettext.translation('helloworld', localedir='locales', languages=['ru'])
language.install()
_ = language.gettext
def print_text():
print(_("Hello world"))
print(_("Goodbye"))
if __name__ == '__main__':
print_text()
Se utilizará el catálogo correspondiente al ruso:
vcarceler@luna:~/ejemplo$ python helloworld.py
Привет, мир!
Пока
vcarceler@luna:~/ejemplo$
Más información: