Traduzindo Seu Programa usando gettext
Gettext é um conjunto de bibliotecas e ferramentas muito utilizado no meio software livre para implementar a tradução de programas. Esta ferramenta é multiplataforma e multilinguagem, isto é, roda em vários sistemas operacionais e também em várias linguagens, como C, Ruby, Python, Java, ...
http://www.gnu.org/software/gettext/gettext.html
Introdução ao Gettext
Seu uso é fácil: você "instala" um catálogo de traduções para seu programa e utiliza a chamada gettext(), muito comumente renomeada para _() para as mensagens a serem traduzidas. Esta função tem o seguinte comportamento: Quando o texto passado estiver no catálogo de tradução requerido, esta tradução será utilizada, caso contrário o texto original será retornado.
Depois você deverá rodar o utilitário xgettext em todos os arquivos python que contém mensagem a ser traduzida, isto gerará um arquivo .pot (Portable Object Template), o qual é um arquivo texto puro (plain/text) o qual deverá ser copiado para a língua que se quer a tradução, e então traduzí-lo. Para auxiliar na tradução existem utilitários como o kbabel o qual oferece uma interface amigável e várias facilidades, como a conferência em um Vocabulário Padrão (http://br.tldp.org/ferramentas/vp/vp-kbabel-info.html).
Após traduzido, este arquivo deverá ser compilado em um arquivo .mo (Machine Object) o qual será usado por nosso programa. Para isso utilizamos o utilitário msgfmt.
Toda vez que modificamos alguma mensagem de texto em nosso programa, precisaremos gerar o .pot novamente e também modificar o arquivo .po com a tradução, para isso utilizamos a ferramente msgmerge. Lembre-se de compilar o arquivo traduzido novamente!
Para que o arquivo .mo seja encontrado por nosso programa, ele precisará estar em algum lugar específico como o /usr/share/locale/$LANG/LC_MESSAGES/$PROGRAM.mo, no caso dos Unix. Você pode especificar um caminho alternativo no seu programa ao instalar o catálogo. Note que por $LANG entendemos o código da linguagem, como pt_BR para o Português do Brasil e por $PROGRAM o nome do programa e consequentemente o nome do catálogo.
Para facilitar o processo de muda-gera-mistura-compila eu utilizo o seguinte bash script:
PROGRAM="pytvgrab" # my program name SRC_DIR="./lib" # where the .py files are DST_DIR="./share/locale" # where to store translation files DST_FILE="$DST_DIR/${PROGRAM}.pot" # template file function regen_pot() { echo -e "Regenerate template file: \c" find "$SRC_DIR" -name "*.*py" | xargs xgettext -L Python -o "$DST_FILE" echo -e "done." echo } function compile() { echo -e "Compile message catalog to new format:" for file in `find "$DST_DIR" -name "${PROGRAM}.po"`; do out=`echo "$file" | sed 's/\.po$/.mo/'` outn=${out/$DST_DIR\//} echo -e " Generating: $outn" msgfmt -o "$out" "$file" 2> /dev/null done echo } function merge() { echo -e "Merge new strings into translations:" for file in `find "$DST_DIR" -name "${PROGRAM}.po"`; do filen=${file/$DST_DIR\//} echo -e " Merging: $filen\c" msgmerge --update --backup=off -i -F "$file" "$DST_FILE" done echo } function check() { echo -e "Checking translations:" for file in `find "$DST_DIR" -name "${PROGRAM}.po"`; do filen=${file/$DST_DIR\//} filen=${filen/\/${PROGRAM}.po/} echo -e " Check: $filen: \c" msgfmt -o /dev/null --statistics -v -c "$file" done echo } case $1 in check | -C | --check ) shift check $@ ;; compile | -c | --compile ) shift compile $@ ;; merge | -m | --merge ) shift merge $@ ;; regen_pot | -r | --regen_pot | --regen-pot | regen | --regen ) shift regen_pot $@ ;; all | -a | --all | -A | --ALL | --All ) shift regen_pot $@ merge $@ compile $@ check $@ ;; *) cat <<EOF Usage: $0 [OPTION] OPTION is one of: -r, --regen regenerates the po template (.pot) -m, --merge merge new strings from .pot into existing .po files -c, --compile compile .po files in .mo -C, --check check translation files -a, --all do in order: regen, merge, compile and check. EOF ;; esac
Adaptando seu Programa
Para que seu programa faça uso do gettext você terá que fazer algumas modificações:
- Importar o módulo gettext e instalar o catálogo
- Envolver as mensagens a serem traduzidas com _()
Eu costumo fazer a primeira parte em um arquivo em separado chamado "i18n.py" o qual replico abaixo:
PROGRAM="pytvgrab" # my program name import locale # lê variáveis de ambiente LC_ALL, LANG, ... LC_ALL = locale.setlocale(locale.LC_ALL, '') try: import gettext from gettext import gettext as _ # Install internationalization stuff and define _() gettext.install( PROGRAM, unicode=True ) except ImportError: import sys print >> sys.stderr, ( "You don't have gettext module, no " \ "internationalization will be used." ) # define _() so program will not fail import __builtin__ __builtin__.__dict__[ "_" ] = lambda x: x
importe este arquivo no seu código e você terá acesso à função _() e um catálogo instalado.
Um exemplo de programa que use isso é:
import i18n print _( "Hello World!" )
Como Distribuir?
Podemos utilizar o DistUtils para distribuir nosso programa, para isso teremos que acrescentar alguns parâmetros à chamada setup() do setup.py e algumas linhas no MANIFEST.in:
MANIFEST.in
Inclua os arquivos .pot, .po e .mo nos pacotes:
recursive-include share/locale *.pot recursive-include share/locale *.po recursive-include share/locale *.mo
setup.py
1 # ... code ...
2
3 # Find locale files:
4 setup_args[ "data_files" ] = []
5 def mo_find( result, dirname, fnames ):
6 files = []
7 for f in fnames:
8 p = os.path.join( dirname, f )
9 if os.path.isfile( p ) and f.endswith( ".mo" ):
10 files.append( p )
11
12 if files:
13 result.append( ( dirname, files ) )
14 # po_find()
15 os.path.walk( os.path.join( "share", "locale" ),
16 mo_find,
17 setup_args[ "data_files" ]
18 )
19
20 setup( **setup_args )
Isto é, passe como parâmetro para setup() data_files e uma lista de tuplas com as tuplas tendo o primeiro argumento o diretório destino e o segundo uma lista de arquivos a serem colocados neste diretório. Por exemplo:
Arquivos a serem incluídos: share/locale/pt_BR/LC_MESSAGES/pytvgrab.mo e share/locale/pt_BR/LC_MESSAGES/pytvgrab2.mo em share/locale/pt_BR/LC_MESSAGES
Mais arquivos a serem incluídos: share/locale/es_ES/LC_MESSAGES/pytvgrab.mo e share/locale/es_ES/LC_MESSAGES/pytvgrab2.mo em share/locale/es_ES/LC_MESSAGES
1 data_files=[
2 ( 'share/locale/pt_BR/LC_MESSAGES',
3 [ 'share/locale/pt_BR/LC_MESSAGES/pytvgrab.mo',
4 'share/locale/pt_BR/LC_MESSAGES/pytvgrab2.mo' ] ),
5
6 ( 'share/locale/es_ES/LC_MESSAGES',
7 [ 'share/locale/es_ES/LC_MESSAGES/pytvgrab.mo',
8 'share/locale/es_ES/LC_MESSAGES/pytvgrab2.mo' ],
9 ) ]