"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.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)

   Return the localized translation of *message*, based on the current
   global domain, language, and locale directory.  This function is
   usually aliased as "_()" in the local namespace (see examples
   below).

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.

Note that GNU **gettext** also defines a "dcgettext()" method, but
this was deemed not useful and so it is currently unimplemented.

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
====================

The class-based API of the "gettext" module gives you more flexibility
and greater convenience than the GNU **gettext** API.  It is the
recommended way of localizing your Python applications and modules.
"gettext" defines a "GNUTranslations" class which implements the
parsing of GNU ".mo" format files, and has methods for returning
strings. Instances of this class can also install themselves in the
built-in namespace as the function "_()".

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)

   Return a "*Translations" instance based on the *domain*,
   *localedir*, and *languages*, which are first passed to "find()" to
   get a list of the associated ".mo" file paths.  Instances with
   identical ".mo" file names are cached.  The actual class
   instantiated is *class_* if provided, otherwise "GNUTranslations".
   The class's constructor must take a single *file object* argument.

   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" used to be raised, it is now
   an alias of "OSError".

   Distinto en la versión 3.11: el parámetro *codeset* se removió.

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

   This installs the function "_()" in Python's builtins namespace,
   based on *domain* and *localedir* which are passed to the function
   "translation()".

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

   As seen below, you usually mark the strings in your application
   that are candidates for translation, by wrapping them in a call to
   the "_()" function, like this:

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

   For convenience, you want the "_()" function to be installed in
   Python's builtins namespace, so it is easily accessible in all
   modules of your application.

   Distinto en la versión 3.11: *names* ahora es un parámetro de solo
   palabra clave.


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 operativo en la clase base, este método toma el objeto de
      archivo *fp* y lee los datos del archivo, inicializando su
      catálogo de mensajes. Si tiene un formato de archivo de catálogo
      de mensajes no admitido, debe sobrescribir este método para
      analizar su formato.

   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.

   info()

      Return a dictionary containing the metadata found in the message
      catalog file.

   charset()

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

   install(names=None)

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

      If the *names* parameter is given, it must be a sequence
      containing the names of functions you want to install in the
      builtins namespace in addition to "_()".  Supported names are
      "'gettext'", "'ngettext'", "'pgettext'", and "'npgettext'".

      Note that this is only one way, albeit the most convenient way,
      to make the "_()" function available to your application.
      Because it affects the entire application globally, and
      specifically the built-in namespace, localized modules should
      never install "_()". Instead, they should use this code to make
      "_()" available to their module:

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

      This puts "_()" only in the module's global namespace and so
      only affects calls within this module.

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


La clase "GNUTranslations"
--------------------------

The "gettext" module provides one additional class derived from
"NullTranslations": "GNUTranslations".  This class overrides
"_parse()" to enable reading GNU **gettext** format ".mo" files in
both big-endian and little-endian format.

"GNUTranslations" parses optional metadata out of the translation
catalog. It is convention with GNU **gettext** to include metadata as
the translation for the empty string. This metadata is in **RFC
822**-style "key: value" pairs, and should contain the "Project-Id-
Version" key.  If the key "Content-Type" is found, then the "charset"
property is used to initialize the "protected" "_charset" instance
variable, defaulting to "None" if not found.  If the charset encoding
is specified, then all message ids and message strings read from the
catalog are converted to Unicode using this encoding, else ASCII is
assumed.

Since message ids are read as Unicode strings too, all "*gettext()"
methods will assume message ids as Unicode strings, not byte strings.

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.


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'))

For compatibility with this older module, the function "Catalog()" is
an alias for the "translation()" function described above.

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

In order to prepare your code for I18N, you need to look at all the
strings in your files.  Any string that needs to be translated should
be marked by wrapping it in "_('...')" --- that is, a call to the
function "_".  For example:

   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.

Hay algunas herramientas para extraer las cadenas destinadas a la
traducción. El GNU **gettext** original 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 po-utils package.

(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.


Agregar configuración regional a su módulo
------------------------------------------

Si está aplicando configuración regional a su módulo, debe tener
cuidado de no realizar cambios globales, por ejemplo, al espacio de
nombres integrado. No debe utilizar la API GNU **gettext**, sino la
API basada en clases.

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


Agregar configuración regional a su aplicación
----------------------------------------------

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))

This works because the dummy definition of "_()" simply returns the
string unchanged.  And this dummy definition will temporarily override
any definition of "_()" in the built-in namespace (until the "del"
command). Take care, though if you have a previous definition of "_()"
in the local namespace.

Note that the second use of "_()" will not identify "a" as being
translatable to the **gettext** program, because the parameter is not
a string literal.

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))

In this case, you are marking translatable strings with the function
"N_()", which won't conflict with any definition of "_()". However,
you will need to teach your message extraction program to look for
translatable strings marked with "N_()". **xgettext**, **pygettext**,
"pybabel extract", and **xpot** all support this through the use of
the "-k" command-line switch. The choice of "N_()" here is totally
arbitrary; it could have just as easily been
"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] The default locale directory is system dependent; for example, on
    Red Hat Linux it is "/usr/share/locale", but on Solaris it is
    "/usr/lib/locale". The "gettext" module does not try to support
    these system dependent defaults; instead its default is
    "*sys.base_prefix*/share/locale" (see "sys.base_prefix"). For this
    reason, it is always best to call "bindtextdomain()" with an
    explicit absolute path at the start of your application.

[2] Vea la nota al pie de página para "bindtextdomain()" arriba.
