gettext — Servicios de internacionalización multilingües

Código fuente: Lib/gettext.py


El módulo gettext proporciona servicios de internacionalización (I18N) y localización (L10N) para sus módulos y aplicaciones Python. Admite tanto la API del catálogo de mensajes GNU gettext como una API basada en clases de nivel superior que puede ser más apropiada para los archivos Python. La interfaz que se describe a continuación le permite escribir sus mensajes de módulo y aplicación en un idioma natural, y proporcionar un catálogo de mensajes traducidos para ejecutar en diferentes idiomas naturales.

También se dan algunas sugerencias para localizar sus módulos y aplicaciones Python.

GNU API gettext

El módulo gettext define la siguiente API, que es muy similar al programa GNU gettext API. Si usa esta API, afectará la traducción de toda su aplicación a nivel mundial. A menudo, esto es lo que desea si su aplicación es monolingüe, y la elección del idioma depende de la configuración regional de su usuario. Si está localizando un módulo de Python, o si su aplicación necesita cambiar idiomas sobre la marcha, probablemente desee utilizar la API basada en clases.

gettext.bindtextdomain(domain, localedir=None)

Enlaza (bind) el domain al directorio de configuración regional localedir. Más concretamente, gettext buscará archivos binarios .mo para el dominio dado usando la ruta (en Unix): localedir/language/LC_MESSAGES /domain.mo, donde se busca language en las variables de entorno LANGUAGE, LC_ALL, LC_MESSAGES y LANG respectivamente.

Si localedir se omite o es None, se retorna el enlace actual para domain. 1

gettext.bind_textdomain_codeset(domain, codeset=None)

Enlaza (bind) el domain al codeset, cambiando la codificación de las cadenas de bytes retornadas por las funciones lgettext(), ldgettext(), lngettext() y ldngettext(). Si se omite codeset, se retorna el enlace (binding) actual.

Deprecated since version 3.8, will be removed in version 3.10.

gettext.textdomain(domain=None)

Cambia o consulta el dominio global actual. Si domain es None, se retorna el dominio global actual; de lo contrario, el dominio global se establece en domain, que se retorna.

gettext.gettext(message)

Retorna la traducción localizada de message, en función del dominio global actual, el idioma y el directorio de configuración regional. Esta función generalmente tiene un alias como _() en el espacio de nombres local (ver ejemplos a continuación).

gettext.dgettext(domain, message)

Como gettext(), pero busque el mensaje en el domain especificado.

gettext.ngettext(singular, plural, n)

Como gettext(), pero considere formas plurales. Si se encuentra una traducción, aplique la fórmula plural a n y retorna el mensaje resultante (algunos idiomas tienen más de dos formas plurales). Si no se encuentra ninguna traducción, retorna singular if n es 1; retorna plural de lo contrario.

La fórmula Plural se toma del encabezado del catálogo. Es una expresión C o Python que tiene una variable libre n; la expresión se evalúa como el índice del plural en el catálogo. Consulte la documentación de GNU gettext para conocer la sintaxis precisa que se utilizará en archivos .po y las fórmulas para un variedad de idiomas.

gettext.dngettext(domain, singular, plural, n)

Como ngettext(), pero busque el mensaje en el domain especificado.

gettext.pgettext(context, message)
gettext.dpgettext(domain, context, message)
gettext.npgettext(context, singular, plural, n)
gettext.dnpgettext(domain, context, singular, plural, n)

Similar a las funciones correspondientes sin la p en el prefijo (es decir, gettext(), dgettext(), ngettext(), dngettext()), pero la traducción está restringida al mensaje dado context.

Nuevo en la versión 3.8.

gettext.lgettext(message)
gettext.ldgettext(domain, message)
gettext.lngettext(singular, plural, n)
gettext.ldngettext(domain, singular, plural, n)

Equivalente a las funciones correspondientes sin el prefijo l (gettext(), dgettext(), ngettext() y dngettext()), pero la traducción se retorna como una cadena de bytes codificada en la codificación del sistema preferida si no se estableció explícitamente otra codificación con bind_textdomain_codeset().

Advertencia

Estas funciones deben evitarse en Python 3, ya que retornan bytes codificados. Es mucho mejor usar alternativas que retornan cadenas Unicode, ya que la mayoría de las aplicaciones de Python querrán manipular el texto legible por humanos como cadenas en lugar de bytes. Además, es posible que obtenga excepciones inesperadas relacionadas con Unicode si hay problemas de codificación con las cadenas traducidas.

Deprecated since version 3.8, will be removed in version 3.10.

Tenga en cuenta que GNU gettext también define un método dcgettext(), pero esto no se consideró útil, por lo que actualmente no está implementado.

Aquí hay un ejemplo del uso típico de esta API:

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))

API basada en clases

La API basada en clases del módulo gettext le brinda más flexibilidad y mayor comodidad que la API GNU gettext. Es la forma recomendada de localizar sus aplicaciones y módulos de Python. gettext define una clase GNUTranslations que implementa el análisis de archivos de formato GNU .mo, y tiene métodos para retornar cadenas. Las instancias de esta clase también pueden instalarse en el espacio de nombres incorporado como la función _().

gettext.find(domain, localedir=None, languages=None, all=False)

Esta función implementa el algoritmo de búsqueda de archivos estándar .mo. Toma un domain, idéntico al que toma textdomain(). Opcional localedir es como en bindtextdomain(). languages opcionales es una lista de cadenas, donde cada cadena es un código de idioma.

Si no se proporciona localedir, se utiliza el directorio de configuración regional predeterminado del sistema. 2 Si no se proporcionan languages, se buscan las siguientes variables de entorno: LANGUAGE, LC_ALL, LC_MESSAGES, y LANG. El primero que retorna un valor no vacío se usa para la variable languages. Las variables de entorno deben contener una lista de idiomas separados por dos puntos, que se dividirá en dos puntos para producir la lista esperada de cadenas de código de idioma.

find() luego expande y normaliza los idiomas, y luego los itera, buscando un archivo existente construido con estos componentes:

localedir/language/LC_MESSAGES/domain.mo

El primer nombre de archivo que existe es retornado por find(). Si no se encuentra dicho archivo, se retorna None. Si se proporciona all, retorna una lista de todos los nombres de archivo, en el orden en que aparecen en la lista de idiomas o las variables de entorno.

gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False, codeset=None)

Retorna una instancia de *Translations basada en domain, localedir y languages, que primero se pasan a find() para obtener una lista del archivo asociado de rutas .mo. Instancias con idéntico nombres de archivo .mo se almacenan en caché. La clase real instanciada es class_ si se proporciona, de lo contrario GNUTranslations. El constructor de la clase debe tomar un solo argumento file object. Si se proporciona, codeset cambiará el juego de caracteres utilizado para codificar cadenas traducidas en los métodos lgettext() y lngettext().

Si se encuentran varios archivos, los archivos posteriores se utilizan como retrocesos para los anteriores. Para permitir la configuración de la reserva, copy.copy() se utiliza para clonar cada objeto de traducción del caché; los datos de la instancia real aún se comparten con el caché.

Si no se encuentra el archivo .mo, esta función genera OSError si fallback es falso (que es el valor predeterminado) y retorna una instancia de NullTranslations si fallback is verdadero.

Distinto en la versión 3.3: IOError solía aparecer en lugar de OSError.

Deprecated since version 3.8, will be removed in version 3.10: El parámetro codeset.

gettext.install(domain, localedir=None, codeset=None, names=None)

Esto instala la función _() en el espacio de nombres incorporado de Python, basado en domain, localedir y codeset que se pasan a la función translation().

Para el parámetro names, consulte la descripción del método del objeto de traducción install().

Como se ve a continuación, generalmente marca las cadenas en su aplicación que son candidatas para la traducción, envolviéndolas en una llamada a la función _(), como esta:

print(_('This string will be translated.'))

Para mayor comodidad, desea que la función _() se instale en el espacio de nombres integrado de Python, para que sea fácilmente accesible en todos los módulos de su aplicación.

Deprecated since version 3.8, will be removed in version 3.10: El parámetro codeset.

La clase NullTranslations

Las clases de traducción son las que realmente implementan la traducción de cadenas de mensajes del archivo fuente original a cadenas de mensajes traducidas. La clase base utilizada por todas las clases de traducción es NullTranslations; Esto proporciona la interfaz básica que puede utilizar para escribir sus propias clases de traducción especializadas. Estos son los métodos de NullTranslations:

class gettext.NullTranslations(fp=None)

Toma un término opcional file object fp, que es ignorado por la clase base. Inicializa las variables de instancia «protegidas» _info y _charset que se establecen mediante clases derivadas, así como _fallback, que se establece a través de add_fallback(). Luego llama a self._parse(fp) if fp no es None.

_parse(fp)

No-op in the base class, this method takes file object fp, and reads the data from the file, initializing its message catalog. If you have an unsupported message catalog file format, you should override this method to parse your format.

add_fallback(fallback)

Agrega fallback como el objeto de respaldo para el objeto de traducción actual. Un objeto de traducción debe consultar la reserva si no puede proporcionar una traducción para un mensaje dado.

gettext(message)

Si se ha establecido una reserva, reenvíe gettext() a la reserva. De lo contrario, retorna message. Anulado en clases derivadas.

ngettext(singular, plural, n)

Si se ha establecido una reserva, reenvíe ngettext() a la reserva. De lo contrario, retorna singular si n es 1; volver plural de lo contrario. Anulado en clases derivadas.

pgettext(context, message)

Si se ha establecido una reserva, reenvía pgettext() a la reserva. De lo contrario, retorna el mensaje traducido. Anulado en clases derivadas.

Nuevo en la versión 3.8.

npgettext(context, singular, plural, n)

Si se ha establecido una reserva, reenvía npgettext() a la reserva. De lo contrario, retorna el mensaje traducido. Anulado en clases derivadas.

Nuevo en la versión 3.8.

lgettext(message)
lngettext(singular, plural, n)

Equivalente a gettext() y ngettext(), pero la traducción se retorna como una cadena de bytes codificada en la codificación del sistema preferida si no se estableció explícitamente ninguna codificación con set_output_charset(). Anulado en clases derivadas.

Advertencia

Estos métodos deben evitarse en Python 3. Consulte la advertencia para la función lgettext().

Deprecated since version 3.8, will be removed in version 3.10.

info()

Retorna la variable «protected» _info, un diccionario que contiene los metadatos encontrados en el archivo del catálogo de mensajes.

charset()

Retorna la codificación del archivo de catálogo de mensajes.

output_charset()

Retorna la codificación utilizada para retornar mensajes traducidos en lgettext() y lngettext().

Deprecated since version 3.8, will be removed in version 3.10.

set_output_charset(charset)

Cambiar la codificación utilizada para retornar mensajes traducidos.

Deprecated since version 3.8, will be removed in version 3.10.

install(names=None)

Este método instala gettext() en el espacio de nombres incorporado, vinculándolo a _.

Si se proporciona el parámetro names, debe ser una secuencia que contenga los nombres de las funciones que desea instalar en el espacio de nombres incorporado además de _(). Los nombres admitidos son 'gettext', 'ngettext', 'pgettext', 'npgettext', 'lgettext', y 'lngettext'.

Tenga en cuenta que esta es solo una forma, aunque la más conveniente, para que la función _() esté disponible para su aplicación. Debido a que afecta a toda la aplicación globalmente, y específicamente al espacio de nombres incorporado, los módulos localizados nunca deberían instalarse _(). En su lugar, deberían usar este código para hacer que _() esté disponible para su módulo:

import gettext
t = gettext.translation('mymodule', ...)
_ = t.gettext

Esto pone _() solo en el espacio de nombres global del módulo y, por lo tanto, solo afecta las llamadas dentro de este módulo.

Distinto en la versión 3.8: Se agregó 'pgettext' y 'npgettext'.

La clase GNUTranslations

El módulo gettext proporciona una clase adicional derivada de NullTranslations: GNUTranslations. Esta clase anula _parse() para permitir la lectura de formato GNU gettext archivos .mo en formato big-endian y little-endian.

GNUTranslations analiza los metadatos opcionales del catálogo de traducción. Es una convención con GNU gettext para incluir metadatos como la traducción de la cadena vacía. Estos metadatos están en estilos pares RFC 822key: value, y deben contener la clave Project-Id-Version. Si se encuentra la clave Content-Type, entonces la propiedad charset se usa para inicializar la variable de instancia «protected» _charset, con el valor predeterminado None si no se encuentra. Si se especifica la codificación del juego de caracteres, todos los identificadores de mensajes y las cadenas de mensajes leídos del catálogo se convierten a Unicode utilizando esta codificación, de lo contrario se asume ASCII.

Dado que los identificadores de mensaje también se leen como cadenas Unicode, todos los métodos *gettext() asumirán los identificadores de mensaje como cadenas Unicode, no cadenas de bytes.

The entire set of key/value pairs are placed into a dictionary and set as the «protected» _info instance variable.

Si el número mágico del archivo .mo no es válido, el número de versión principal es inesperado, o si se producen otros problemas al leer el archivo, se crea una instancia de GNUTranslations puede generar OSError.

class gettext.GNUTranslations

Los siguientes métodos se anulan desde la implementación de la clase base:

gettext(message)

Busca el message id en el catálogo y retorna la cadena de caracteres de mensaje correspondiente, como una cadena Unicode. Si no hay una entrada en el catálogo para el message id, y se ha establecido una retroceso (fallback), la búsqueda se reenvía al método de retroceso gettext(). De lo contrario, se retorna el message id.

ngettext(singular, plural, n)

Realiza una búsqueda de formas plurales de una identificación de mensaje. singular se usa como la identificación del mensaje para fines de búsqueda en el catálogo, mientras que n se usa para determinar qué forma plural usar. La cadena del mensaje retornado es una cadena de caracteres Unicode.

Si la identificación del mensaje no se encuentra en el catálogo y se especifica una reserva, la solicitud se reenvía al método de reserva ngettext(). De lo contrario, cuando n es 1 se retorna singular, y plural se retorna en todos los demás casos.

Aquí hay un ejemplo:

n = len(os.listdir('.'))
cat = GNUTranslations(somefile)
message = cat.ngettext(
    'There is %(num)d file in this directory',
    'There are %(num)d files in this directory',
    n) % {'num': n}
pgettext(context, message)

Busca el context y message id en el catálogo y retorna la cadena de de caracteres mensaje correspondiente, como una cadena de caracteres Unicode. Si no hay ninguna entrada en el catálogo para el message id y context, y se ha establecido un retroceso (fallback), la búsqueda se reenvía al método de retroceso pgettext(). De lo contrario, se retorna el message id.

Nuevo en la versión 3.8.

npgettext(context, singular, plural, n)

Realiza una búsqueda de formas plurales de una identificación de mensaje. singular se usa como la identificación del mensaje para fines de búsqueda en el catálogo, mientras que n se usa para determinar qué forma plural usar.

Si la identificación del mensaje para context no se encuentra en el catálogo y se especifica una reserva, la solicitud se reenvía al método de reserva npgettext(). De lo contrario, cuando n * es 1 se retorna *singular, y plural se retorna en todos los demás casos.

Nuevo en la versión 3.8.

lgettext(message)
lngettext(singular, plural, n)

Equivalente a gettext() y ngettext(), pero la traducción se retorna como una cadena de bytes codificada en la codificación del sistema preferida si no se estableció explícitamente ninguna codificación con set_output_charset().

Advertencia

Estos métodos deben evitarse en Python 3. Consulte la advertencia para la función lgettext().

Deprecated since version 3.8, will be removed in version 3.10.

Soporte de catálogo de mensajes de Solaris

El sistema operativo Solaris define su propio formato de archivo binario .mo, pero como no se puede encontrar documentación sobre este formato, no es compatible en este momento.

El constructor del catálogo

GNOME usa una versión del módulo gettext de James Henstridge, pero esta versión tiene una API ligeramente diferente. Su uso documentado fue:

import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))

Para la compatibilidad con este módulo anterior, la función Catalog() es un alias para la función translation() descrita anteriormente.

Una diferencia entre este módulo y el de Henstridge: sus objetos de catálogo admitían el acceso a través de una API de mapeo, pero esto parece estar sin usar y, por lo tanto, actualmente no es compatible.

Internacionalizando sus programas y módulos

La internacionalización (I18N) se refiere a la operación mediante la cual un programa conoce varios idiomas. La localización (L10N) se refiere a la adaptación de su programa, una vez internacionalizado, al idioma local y los hábitos culturales. Para proporcionar mensajes multilingües para sus programas de Python, debe seguir los siguientes pasos:

  1. prepare su programa o módulo marcando especialmente cadenas traducibles

  2. ejecuta un conjunto de herramientas sobre tus archivos marcados para generar catálogos de mensajes sin procesar

  3. crear traducciones específicas del idioma de los catálogos de mensajes

  4. use el módulo gettext para que las cadenas de caracteres de mensajes se traduzcan correctamente

Para preparar su código para I18N, debe mirar todas las cadenas en sus archivos. Cualquier cadena que deba traducirse debe marcarse envolviéndola en _('...') — es decir, una llamada a la función _(). Por ejemplo:

filename = 'mylog.txt'
message = _('writing a log message')
with open(filename, 'w') as fp:
    fp.write(message)

En este ejemplo, la cadena de caracteres 'writing a log message' está marcada como candidata para la traducción, mientras que las cadenas 'mylog.txt' y 'w' no lo están.

Existen algunas herramientas para extraer las cadenas destinadas a la traducción. El programa GNU original gettext solo admitía el código fuente C o C++, pero su versión extendida xgettext escanea el código escrito en varios idiomas, incluido Python, para encontrar cadenas marcadas como traducibles. Babel es una biblioteca de internacionalización de Python que incluye un script pybabel para extraer y compilar catálogos de mensajes. El programa de François Pinard llamado xpot hace un trabajo similar y está disponible como parte de su paquete po-utils.

(Python también incluye versiones de Python puro de estos programas, llamadas pygettext.py y msgfmt.py; algunas distribuciones de Python las instalarán por usted. pygettext.py es similar a xgettext, pero solo entiende el código fuente de Python y no puede manejar otros lenguajes de programación como C o C++. pygettext.py admite una interfaz de línea de comandos similar a xgettext; para detalles sobre su uso, ejecute pygettext.py --help. msgfmt.py es binario compatible con GNU msgfmt. Con estos dos programas, es posible que no necesite el paquete GNU gettext para internacionalizar sus aplicaciones Python.)

xgettext, pygettext, y herramientas similares generan archivos .po que son catálogos de mensajes. Son archivos estructurados legibles por humanos que contienen cada cadena marcada en el código fuente, junto con un marcador de posición para las versiones traducidas de estas cadenas.

Las copias de estos archivos .po se entregan a los traductores humanos individuales que escriben traducciones para cada lenguaje natural compatible. Envían las versiones completas específicas del idioma como un archivo <nombre-idioma >.po que se compila en un archivo binario .mo de lectura mecánica utilizando el programa msgfmt . Los archivos .mo son utilizados por el módulo gettext para el procesamiento de traducción real en tiempo de ejecución.

Como use el módulo gettext en su código depende de si está internacionalizando un solo módulo o toda su aplicación. Las siguientes dos secciones discutirán cada caso.

Localizing your module

If you are localizing your module, you must take care not to make global changes, e.g. to the built-in namespace. You should not use the GNU gettext API but instead the class-based API.

Digamos que su módulo se llama «spam» y los diversos archivos .mo de traducciones del lenguaje natural del módulo residen en /usr/share/locale en formato GNU gettext. Esto es lo que pondría en la parte superior de su módulo:

import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext

Localizing your application

If you are localizing your application, you can install the _() function globally into the built-in namespace, usually in the main driver file of your application. This will let all your application-specific files just use _('...') without having to explicitly install it in each file.

En el caso simple, entonces, solo necesita agregar el siguiente bit de código al archivo del controlador principal de su aplicación:

import gettext
gettext.install('myapplication')

Si necesita establecer el directorio de configuración regional, puede pasarlo a la función install():

import gettext
gettext.install('myapplication', '/usr/share/locale')

Cambiar idiomas sobre la marcha

Si su programa necesita admitir muchos idiomas al mismo tiempo, es posible que desee crear varias instancias de traducción y luego cambiar entre ellas explícitamente, de esta manera:

import gettext

lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])

# start by using language1
lang1.install()

# ... time goes by, user selects language 2
lang2.install()

# ... more time goes by, user selects language 3
lang3.install()

Traducciones diferidas

En la mayoría de las situaciones de codificación, las cadenas se traducen donde se codifican. Sin embargo, ocasionalmente, debe marcar cadenas para la traducción, pero diferir la traducción real hasta más tarde. Un ejemplo clásico es:

animals = ['mollusk',
           'albatross',
           'rat',
           'penguin',
           'python', ]
# ...
for a in animals:
    print(a)

Aquí, desea marcar las cadenas en la lista de animals como traducibles, pero en realidad no desea traducirlas hasta que se impriman.

Aquí hay una manera de manejar esta situación:

def _(message): return message

animals = [_('mollusk'),
           _('albatross'),
           _('rat'),
           _('penguin'),
           _('python'), ]

del _

# ...
for a in animals:
    print(_(a))

Esto funciona porque la definición ficticia de _() simplemente retorna la cadena sin cambios. Y esta definición ficticia anulará temporalmente cualquier definición de _() en el espacio de nombres incorporado (hasta el comando del). Sin embargo, tenga cuidado si tiene una definición previa de _() en el espacio de nombres local.

Tenga en cuenta que el segundo uso de _() no identificará «a» como traducible al programa gettext, porque el parámetro no es un literal de cadena.

Otra forma de manejar esto es con el siguiente ejemplo:

def N_(message): return message

animals = [N_('mollusk'),
           N_('albatross'),
           N_('rat'),
           N_('penguin'),
           N_('python'), ]

# ...
for a in animals:
    print(_(a))

En este caso, está marcando cadenas traducibles con la función N_(), que no entrará en conflicto con ninguna definición de _(). Sin embargo, deberá enseñar a su programa de extracción de mensajes a buscar cadenas traducibles marcadas con N_(). xgettext, pygettext, pybabel extract, y xpot todos soportan esto mediante el uso de -k interruptor de línea de comando. La elección de N_() aquí es totalmente arbitraria; podría haber sido igual de fácil MarkThisStringForTranslation().

Agradecimientos

Las siguientes personas contribuyeron con código, comentarios, sugerencias de diseño, implementaciones anteriores y una valiosa experiencia para la creación de este módulo:

  • Peter Funk

  • James Henstridge

  • Juan David Ibáñez Palomar

  • Marc-André Lemburg

  • Martin von Löwis

  • François Pinard

  • Barry Warsaw

  • Gustavo Niemeyer

Notas al pie

1

El directorio de configuración regional predeterminado depende del sistema; por ejemplo, en RedHat Linux es /usr/share/locale, pero en Solaris es /usr/lib/locale. El módulo gettext no intenta admitir estos valores predeterminados dependientes del sistema; en cambio, su valor predeterminado es sys.base_prefix/share/locale (ver sys.base_prefix). Por esta razón, siempre es mejor llamar a bindtextdomain() con una ruta absoluta explícita al inicio de su aplicación.

2

Vea la nota al pie de página para bindtextdomain() arriba.