23.1. "gettext" --- Serviços de internacionalização multilíngues
****************************************************************

**Código Fonte:** Lib/gettext.py

======================================================================

The "gettext" module provides internationalization (I18N) and
localization (L10N) services for your Python modules and applications.
It supports both the GNU "gettext" message catalog API and a higher
level, class-based API that may be more appropriate for Python files.
The interface described below allows you to write your module and
application messages in one natural language, and provide a catalog of
translated messages for running under different natural languages.

Algumas dicas sobre localização de seus módulos e aplicativos Python
também são fornecidas.


23.1.1. API do GNU **gettext**
==============================

O módulo "gettext" define a API a seguir, que é muito semelhante à API
do GNU **gettext**. Se você usar esta API, você afetará a tradução de
todo o seu aplicativo globalmente. Geralmente, é isso que você deseja
se o seu aplicativo for monolíngue, com a escolha do idioma dependente
da localidade do seu usuário. Se você estiver localizando um módulo
Python, ou se seu aplicativo precisar alternar idiomas rapidamente,
provavelmente desejará usar a API baseada em classe.

gettext.bindtextdomain(domain, localedir=None)

   Bind the *domain* to the locale directory *localedir*.  More
   concretely, "gettext" will look for binary ".mo" files for the
   given domain using the path (on Unix):
   "localedir/language/LC_MESSAGES/domain.mo", where *languages* is
   searched for in the environment variables "LANGUAGE", "LC_ALL",
   "LC_MESSAGES", and "LANG" respectively.

   Se *localedir* for omitido ou "None", a ligação atual para *domain*
   será retornada. [1]

gettext.bind_textdomain_codeset(domain, codeset=None)

   Liga o *domain* ao *codeset*, alterando a codificação das strings
   de bytes retornadas pelas funções "lgettext()", "ldgettext()",
   "lngettext()" e "ldngettext()". Se *codeset* for omitido, a ligação
   atual será retornada.

gettext.textdomain(domain=None)

   Altera ou consulta o domínio global atual. Se *domain* for "None",
   o domínio global atual será retornado; caso contrário, o domínio
   global será definido como *domain*, que será retornado.

gettext.gettext(message)

   Retorna a tradução localizada de *message*, com base no diretório
   global atual de domínio, idioma e localidade. Essa função
   geralmente é apelidada como "_()" no espaço de nomes local (veja
   exemplos abaixo).

gettext.dgettext(domain, message)

   Semelhante a "gettext()", mas procura a mensagem no *domain*
   especificado.

gettext.ngettext(singular, plural, n)

   Semelhante a "gettext()", mas considera formas plurais. Se uma
   tradução for encontrada, aplica a fórmula do plural a *n* e retorne
   a mensagem resultante (alguns idiomas têm mais de duas formas no
   plural). Se nenhuma tradução for encontrada, retorna *singular* se
   *n* for 1; retorna *plural* caso contrário.

   A fórmula de Plural é retirada do cabeçalho do catálogo. É uma
   expressão C ou Python que possui uma variável livre *n*; a
   expressão é avaliada para o índice do plural no catálogo. Veja a
   documentação do gettext GNU para obter a sintaxe precisa a ser
   usada em arquivos ".po" e as fórmulas para um variedade de idiomas.

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

   Semelhante a "ngettext()", mas procura mensagens no *domain*
   especificado.

gettext.lgettext(message)

gettext.ldgettext(domain, message)

gettext.lngettext(singular, plural, n)

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

   Equivalente às funções correspondentes sem o prefixo "l"
   ("gettext()", "dgettext()", "ngettext()" e "dngettext()"), mas a
   tradução é retornada como uma string de bytes codificada na
   codificação preferida do sistema, se nenhuma outra codificação foi
   definida explicitamente com "bind_textdomain_codeset()".

   Aviso:

     These functions should be avoided in Python 3, because they
     return encoded bytes.  It's much better to use alternatives which
     return Unicode strings instead, since most Python applications
     will want to manipulate human readable text as strings instead of
     bytes.  Further, it's possible that you may get unexpected
     Unicode-related exceptions if there are encoding problems with
     the translated strings.  It is possible that the "l*()" functions
     will be deprecated in future Python versions due to their
     inherent problems and limitations.

Note que GNU **gettext** também define um método "dcgettext()", mas
isso não foi considerado útil e, portanto, atualmente não está
implementado.

Aqui está um exemplo de uso típico para esta API:

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


23.1.2. API baseada em classe
=============================

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 "translations" class which implements the parsing
of GNU ".mo" format files, and has methods for returning strings.
Instances of this "translations" class can also install themselves in
the built-in namespace as the function "_()".

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

   This function implements the standard ".mo" file search algorithm.
   It takes a *domain*, identical to what "textdomain()" takes.
   Optional *localedir* is as in "bindtextdomain()"  Optional
   *languages* is a list of strings, where each string is a language
   code.

   Se *localedir* não for fornecido, o diretório local do sistema
   padrão será usado. [2] Se *languages* não for fornecido, as
   seguintes variáveis de ambiente serão pesquisadas: "LANGUAGE",
   "LC_ALL", "LC_MESSAGES" e "LANG". O primeiro retornando um valor
   não vazio é usado para a variável *languages*. As variáveis de
   ambiente devem conter uma lista de idiomas separada por dois
   pontos, que será dividida nos dois pontos para produzir a lista
   esperada de cadeias de código de idioma.

   "find()" expande e normaliza os idiomas e itera através deles,
   procurando por um arquivo existente construído com esses
   componentes:

   "*localedir*/*language*/LC_MESSAGES/*domain*.mo"

   O primeiro nome de arquivo existente é retornado por "find()". Se
   nenhum desses arquivos for encontrado, será retornado "None". Se
   *all* for fornecido, ele retornará uma lista de todos os nomes de
   arquivos, na ordem em que aparecem na lista de idiomas ou nas
   variáveis de ambiente.

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

   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 either *class_* if provided, otherwise
   "GNUTranslations".  The class's constructor must take a single
   *file object* argument.  If provided, *codeset* will change the
   charset used to encode translated strings in the "lgettext()" and
   "lngettext()" methods.

   Se vários arquivos forem encontrados, os arquivos posteriores serão
   usados como fallbacks para os anteriores. Para permitir a
   configuração do fallback, "copy.copy()" é usado para clonar cada
   objeto de conversão do cache; os dados reais da instância ainda são
   compartilhados com o cache.

   Se nenhum arquivo ".mo" for encontrado, essa função levanta
   "OSError" se *fallback* for falso (que é o padrão) e retorna uma
   instância "NullTranslations" se *fallback* for verdadeiro.

   Alterado na versão 3.3: "IOError" costumava ser levantado em vez do
   "OSError".

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

   Isso instala a função "_()" no espaço de nomes interno do Python,
   com base em *domain*, *localedir* e *codeset* que são passados para
   a função "translation()".

   Para o parâmetro *names*, por favor, veja a descrição método
   "install()" do objeto de tradução.

   Como visto abaixo, você normalmente marca as strings candidatas à
   tradução em seu aplicativo, envolvendo-as em uma chamada para a
   função "_()", assim:

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

   Por conveniência, você deseja que a função "_()" seja instalada no
   espaço de nomes interno do Python, para que seja facilmente
   acessível em todos os módulos do seu aplicativo.


23.1.2.1. A classe "NullTranslations"
-------------------------------------

As classes de tradução são o que realmente implementa a tradução de
strings de mensagens do arquivo-fonte original para strings de
mensagens traduzidas. A classe base usada por todas as classes de
tradução é "NullTranslations"; isso fornece a interface básica que
você pode usar para escrever suas próprias classes de tradução
especializadas. Aqui estão os métodos de "NullTranslations":

class gettext.NullTranslations(fp=None)

   Recebe um *objeto arquivo* opcional *fp*, que é ignorado pela
   classe base. Inicializa as variáveis de instância "protegidas"
   *_info* e *_charset*, que são definidas por classes derivadas, bem
   como *_fallback*, que é definido através de "add_fallback()". Ele
   então chama "self._parse(fp)" se *fp* não for "None".

   _parse(fp)

      No-op'd 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)

      Adiciona *fallback* como o objeto reserva para o objeto de
      tradução atual. Um objeto de tradução deve consultar o fallback
      se não puder fornecer uma tradução para uma determinada
      mensagem.

   gettext(message)

      Se um fallback tiver sido definido, encaminhe "Gettext()" para o
      fallback. Caso contrário, retorne *message*. Sobrecarregado em
      classes derivadas.

   ngettext(singular, plural, n)

      Se um fallback tiver sido definido, encaminha "ngettext()" para
      o fallback. Caso contrário, retorne *singular* se *n* for 1; do
      contrário, retorna *plural*. Sobrecarregado em classes
      derivadas.

   lgettext(message)

   lngettext(singular, plural, n)

      Equivale a "gettext()" e "ngettext()", mas a tradução é
      retornada como uma string de bytes codificada na codificação
      preferida do sistema, se nenhuma codificação foi explicitamente
      definida com "set_output_charset()". Sobrecarregado em classes
      derivadas.

      Aviso:

        Esses métodos devem ser evitados no Python 3. Veja o aviso
        para a função "lgettext()".

   info()

      Return the "protected" "_info" variable.

   charset()

      Retorna a codificação do arquivo de catálogo de mensagens.

   output_charset()

      Retorna a codificação usada para retornar as mensagens
      traduzidas em "lgettext()" e "lngettext()".

   set_output_charset(charset)

      Altera a codificação usada para retornar mensagens traduzidas.

   install(names=None)

      Este método instala "gettext()" no espaço de nomes embutido,
      vinculando-o 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'", "'lgettext'" and "'lngettext'".

      Observe que esta é apenas uma maneira, embora a maneira mais
      conveniente, de disponibilizar a função "_()" para o seu
      aplicativo. Como afeta o aplicativo inteiro globalmente, e
      especificamente o espaço de nomes embutido, os módulos
      localizados nunca devem instalar "_()". Em vez disso, eles devem
      usar este código para disponibilizar "_()" para seu módulo:

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

      Isso coloca "_()" apenas no espaço de nomes global do módulo e,
      portanto, afeta apenas as chamadas dentro deste módulo.


23.1.2.2. A classe "GNUTranslations"
------------------------------------

O módulo "gettext" fornece uma classe adicional derivada de
"NullTranslations": "GNUTranslations". Esta classe sobrecarrega
"_parse()" para permitir a leitura de arquivos ".mo" do formato GNU
**gettext** nos formatos big-endian e little-endian.

"GNUTranslations" parses optional meta-data out of the translation
catalog.  It is convention with GNU **gettext** to include meta-data
as the translation for the empty string.  This meta-data 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
encoding is assumed.

Como os IDs de mensagens também são lidos como strings Unicode, todos
os métodos "*gettext()" presumem os IDs de mensagens como sendo
strings Unicode, não como strings de bytes.

Todo o conjunto de pares chave/valor é colocado em um dicionário e
definido como a variável de instância "_info" "protegida".

Se o número mágico do arquivo ".mo" for inválido, o número principal
da versão é inesperado ou se ocorrerem outros problemas durante a
leitura do arquivo, instanciando uma classe "GNUTranslations" pode
levantar "OSError".

class gettext.GNUTranslations

   Os seguintes métodos são substituídos a partir da implementação da
   classe base:

   gettext(message)

      Procura o ID da *message* no catálogo e retorna a sequência de
      mensagens correspondente, como uma string Unicode. Se não houver
      entrada no catálogo para o ID da *message* e um fallback tiver
      sido definido, a pesquisa será encaminhada para o método do
      fallback "gettext()". Caso contrário, o ID da *message* é
      retornado.

   ngettext(singular, plural, n)

      faz uma pesquisa de plural-forms de um ID de mensagem.
      *singular* é usado como o ID da mensagem para fins de pesquisa
      no catálogo, enquanto *n* é usado para determinar qual forma
      plural usar. A string de mensagens retornada é uma string
      Unicode.

      Se o ID da mensagem não for encontrado no catálogo e um fallback
      for especificado, a solicitação será encaminhada para o método
      do fallback "ngettext()". Caso contrário, quando *n* for 1,
      *singular* será retornado e *plural* será retornado em todos os
      outros casos.

      Aqui está um exemplo:

         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}

   lgettext(message)

   lngettext(singular, plural, n)

      Equivale a "gettext()" e "ngettext()", mas a tradução é
      retornada como uma string de bytes codificada na codificação
      preferida do sistema, se nenhuma codificação foi explicitamente
      definida com "set_output_charset()".

      Aviso:

        Esses métodos devem ser evitados no Python 3. Veja o aviso
        para a função "lgettext()".


23.1.2.3. Suporte a catálogo de mensagens do Solaris
----------------------------------------------------

O sistema operacional Solaris define seu próprio formato de arquivo
binário ".mo", mas como nenhuma documentação pode ser encontrada nesse
formato, ela não é suportada no momento.


23.1.2.4. O construtor Catalog
------------------------------

O GNOME usa uma versão do módulo "gettext" de James Henstridge, mas
esta versão tem uma API um pouco diferente. Seu uso documentado foi:

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

Para compatibilidade com este módulo mais antigo, a função "Catalog()"
é um apelido para a função "translation()" descrita acima.

Uma diferença entre este módulo e o de Henstridge: seus objetos de
catálogo suportavam o acesso por meio de uma API de mapeamento, mas
isso parece não ser utilizado e, portanto, não é atualmente suportado.


23.1.3. Internacionalizando seus programas e módulos
====================================================

Internationalization (I18N), ou internacionalização (I17O) em
português, refere-se à operação pela qual um programa é informado
sobre vários idiomas. Localization (L10N), ou localização em
português, refere-se à adaptação do seu programa, uma vez
internacionalizado, aos hábitos culturais e de idioma local. Para
fornecer mensagens multilíngues para seus programas Python, você
precisa executar as seguintes etapas:

1. preparar seu programa ou módulo marcando especialmente strings
   traduzíveis

2. executar um conjunto de ferramentas nos arquivos marcados para
   gerar catálogos de mensagens não tratadas

3. create language specific translations of the message catalogs

4. usar o módulo "gettext" para que as strings das mensagens sejam
   traduzidas corretamente

Para preparar seu código para I18N, você precisa examinar todas as
strings em seus arquivos. Qualquer string que precise ser traduzida
deve ser marcada envolvendo-a em "_('...')" --- isto é, uma chamada
para a função "_()". Por exemplo:

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

Neste exemplo, a string "'writing a log message'" está marcada como um
candidato para tradução, enquanto as strings "'mylog.txt'" e "'w'" não
estão.

Existem algumas ferramentas para extrair as strings destinadas à
tradução. O GNU **gettext** original tem suporte apenas ao código-
fonte C ou C++, mas sua versão estendida **xgettext** varre o código
escrito em várias linguagens, incluindo Python, para encontrar strings
marcadas como traduzíveis. Babel é uma biblioteca de
internacionalização do Python que inclui um script "pybabel" para
extrair e compilar catálogos de mensagens. O programa de François
Pinard chamado **xpot** faz um trabalho semelhante e está disponível
como parte de seu pacote po-utils.

(O Python também inclui versões em Python puro desses programas,
chamadas **pygettext.py** e **msgfmt.py**; algumas distribuições do
Python as instalam para você. O **pygettext.py** é semelhante ao
**xgettext**, mas apenas entende o código-fonte do Python e não
consegue lidar com outras linguagens de programação como C ou C++. O
**pygettext.py** possui suporte a uma interface de linha de comando
semelhante à do **xgettext**; para detalhes sobre seu uso, execute
"pygettext.py --help". O **msgfmt.py** é compatível com binários com
GNU **msgfmt**. Com esses dois programas, você pode não precisar do
pacote GNU :program`gettext` para internacionalizar seus aplicativos
Python.)

**xgettext**, **pygettext** e ferramentas similares geram ".po" que
são catálogos de mensagens. Eles são arquivos legíveis por humanos
estruturados que contêm todas as strings marcadas no código-fonte,
junto com um espaço reservado para as versões traduzidas dessas
strings.

Cópias destes arquivos ".po" são entregues aos tradutores humanos
individuais que escrevem traduções para todos os idiomas naturais
suportados. Eles enviam de volta as versões completas específicas do
idioma como um arquivo "<nome-do-idioma>.po" que é compilado em um
arquivo de catálogo binário legível por máquina ".mo" usando o
programa **msgfmt**. Os arquivos ".mo" são usados pelo módulo
"gettext" para o processamento de tradução real em tempo de execução.

Como você usa o módulo "gettext" no seu código depende se você está
internacionalizando um único módulo ou sua aplicação inteira. As
próximas duas seções discutirão cada caso.


23.1.3.1. Localizando seu módulo
--------------------------------

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 seu módulo seja chamado "spam" e as várias traduções do
idioma natural do arquivo ".mo" residam em "/usr/share/locale" no
formato GNU **gettext**. Aqui está o que você colocaria sobre o seu
módulo:

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


23.1.3.2. Localizando sua aplicação
-----------------------------------

Se você estiver localizando sua aplicação, poderá instalar a função
"_()" globalmente no espaço de nomes embutidos, geralmente no arquivo
principal do driver do sua aplicação. Isso permitirá que todos os
arquivos específicos de sua aplicação usem "_('...')" sem precisar
instalá-la explicitamente em cada arquivo.

No caso simples, você precisa adicionar apenas o seguinte código ao
arquivo do driver principal da sua aplicação:

   import gettext
   gettext.install('myapplication')

Se você precisar definir o diretório da localidade, poderá passá-lo
para a função "install()":

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


23.1.3.3. Alterando os idiomas durante o uso
--------------------------------------------

Se o seu programa precisar oferecer suporte a vários idiomas ao mesmo
tempo, convém criar várias instâncias de tradução e alternar entre
elas explicitamente, assim:

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


23.1.3.4. Traduções adiadas
---------------------------

Na maioria das situações de codificação, as strings são traduzidas
onde são codificadas. Ocasionalmente, no entanto, é necessário marcar
strings para tradução, mas adiar a tradução real até mais tarde. Um
exemplo clássico é:

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

Aqui, você deseja marcar as strings na lista "animals" como
traduzíveis, mas na verdade não deseja traduzi-las até que sejam
impressas.

Aqui está uma maneira de lidar com esta situação:

   def _(message): return message

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

   del _

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

Isso funciona porque a definição fictícia de "_()" simplesmente
retorna a string inalterada. E essa definição fictícia va substituir
temporariamente qualquer definição de "_()" no espaço de nomes
embutido (até o comando "del"). Tome cuidado, se você tiver uma
definição anterior de "_()" no espaço de nomes local.

Observe que o segundo uso de "_()" não identificará "a" como
traduzível para o programa **gettext**, porque o parâmetro não é uma
string literal.

Outra maneira de lidar com isso é com o seguinte exemplo:

   def N_(message): return message

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

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

Nesse caso, você está marcando strings traduzíveis com a função
"N_()", que não entra em conflito com nenhuma definição de "_()". No
entanto, você precisará ensinar seu programa de extração de mensagens
a procurar strings traduzíveis marcadas com "N_()". **xgettext**,
**pygettext**, "pybabel extract" e **xpot** possuem suporte a isso
através do uso da opção de linha de comando "-k". A escolha de "N_()"
aqui é totalmente arbitrária; poderia facilmente ter sido
"MarkThisStringForTranslation()".


23.1.4. Reconhecimentos
=======================

As seguintes pessoas contribuíram com código, feedback, sugestões de
design, implementações anteriores e experiência valiosa para a criação
deste 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 de rodapé ]-

[1] The default locale directory is system dependent; for example, on
    RedHat 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.prefix/share/locale". For this reason, it is always best to
    call "bindtextdomain()" with an explicit absolute path at the
    start of your application.

[2] Consulte a nota de rodapé para a "bindtextdomain()" acima.
