domingo, 19 de julho de 2015

Traduzindo programas usando gettext

Você provavelmente já deve ter percebido que alguns aplicativos de linha de comando, e também aplicativos de interface gráfica (hexchat, firefox), tem suporte para idiomas diferentes do inglês, vou mostrar como é fácil fazer um programa ,em C, com suporte a vários idiomas.

Iremos usar as seguintes libs e funções:
locale.h
setlocale

libintl.h
bindtextdomain, textdomain, gettext

setlocale


char * setlocale (int category, const char *locale);
O padrão ISO da linguagem C diz que todos os programas iniciam ,por padrão, na localidade 'C', para usar uma localidade definida por variável de ambiente (LANG, LANGUAGE), temos que usar a função setlocale da seguinte maneira:

setlocale(LC_ALL, "");

textdomain


char *textdomain (const char * domainname);

A função textdomain seta ou retorna o nome do arquivo .mo que sera procurado para traduzir as mensagens

para pegar o default é só setar o valor NULL, e ele ira retornar o padrão
  printf("%s\n", texdomain(NULL)); // 'messages'

para setar você coloca o nome da sua aplicação, aqui eu vou por como "tutorial"
  textdomain("tutorial");

bindtextdomain


char *bindtextdomain(const char * domainname, const char * dirname);

A função bindtextdomain define o diretório base onde serao procurados as mensagens traduzidas. suponhamos que o nosso idioma seja "pt_BR.UTF-8" ele procura da seguinte forma:
[dirname]/pt_BR.UTF-8/LC_MESSAGES/[domainname].mo
[dirname]/pt_BR.utf8/LC_MESSAGES/[domainname].mo
[dirname]/pt_BR/LC_MESSAGES/[domainname].mo
[dirname]/pt.UTF-8/LC_MESSAGES/[domainname].mo
[dirname]/pt.utf8/LC_MESSAGES/[domainname].mo
[dirname]/pt/LC_MESSAGES/[domainname].mo
É importante o domainname da função textdomain ser o mesmo passado pra bindtextdomain, caso contrario os arquivos .mo serão procurados em outro diretório (não no dirname).

bindtextdomain("tutorial", "traslate");

gettext


char * gettext (const char *msgid)

A função gettext vai procurar pela string passada nos arquivos .mo, se for encontrado ela retorna a mensagem traduzida, se não ela retorna a string que foi passada.

para ficar mais pratico e fácil de escrever o código definimos o seguinte macro:
#define _(str) gettext(str)

Feito tudo isso chegamos a nosso codigo final:

tutorial.c
#include <stdio.h>
#include <libintl.h>
#include <locale.h>

#define _(str) gettext(str)

int main(void){
 setlocale(LC_ALL, "");
 textdomain("tutorial");
 bindtextdomain("tutorial","translate");

 puts(_("Welcome"));

 return 0;
}

Criando os arquivos de tradução


Já temos nosso programa pronto, agora temos que criar as traduções.
Criando a arvore de diretórios onde o programa vai procurar por traduções:
mkdir -p translate/pt_BR.UTF-8/LC_MESSAGES

Usamos o xgettext para extrair as strings traduzíveis do programa:
xgettext -d tutorial -o tutorial.pot -k_ -s tutorial.c
msginit --no-translator --locale pt_BR --output-file tutorial.po --input tutorial.pot

Um arquivo ,fácil de configurar, vai ser criado:
# Portuguese translations for PACKAGE package
# Traduções em português brasileiro para o pacote PACKAGE.
# Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-07-19 13:57-0300\n"
"PO-Revision-Date: 2015-07-19 13:57-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: tutorial.c:12
msgid "Welcome"
msgstr ""
Alteramos o arquivo, incluindo a tradução:
# Portuguese translations for PACKAGE package
# Traduções em português brasileiro para o pacote PACKAGE.
# Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-07-19 13:57-0300\n"
"PO-Revision-Date: 2015-07-19 13:57-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: tutorial.c:12
msgid "Welcome"
msgstr "Bem Vindo"
Criamos agora o arquivo .mo:
msgfmt tutorial.po -o tutorial.mo

lembre-se o arquivo final deve ser a string que você passou na função textdomain
movemos ele para a arvore de diretorios que criamos anteriormente:
mv tutorial.mo translate/pt_BR.UTF-8/LC_MESSAGES/

Agora é só testar e ver a magica acontecendo:
$ gcc tutorial.c -o tutorial -Wall -Wextra
$ LANG=en_US.UTF-8 ./tutorial
Welcome
$ LANG=pt_BR.UTF-8 ./tutorial
Bem vindo

Referências


[1] How to do I18N through gettext (Acessado em Julho/2015)
https://fedoraproject.org/wiki/How_to_do_I18N_through_gettext
[2] The GNU C Library: Setting the Locale (Acessado em Julho/2015)
http://www.gnu.org/software/libc/manual/html_node/Setting-the-Locale.html
[3] The GNU C Library: Standard Locales (Acessado em Julho/2015)
http://www.gnu.org/software/libc/manual/html_node/Standard-Locales.html
[4] textdomain(3) - Linux man page (Acessado em Julho/2015)
http://linux.die.net/man/3/textdomain
[5] bindtextdomain(3) - Linux man page (Acessado em Julho/2015)
http://linux.die.net/man/3/bindtextdomain
[6] Max's Blog: A Tutorial on GNU gettext (Acessado em Julho/2015)
http://maxillusion.blogspot.com.br/2007/12/tutorial-on-gnu-gettext.html

Um comentário:

  1. Mano, como te acho? kkkkk

    Me chama no gtalk, twitter, facebok, whatsapp, skype.....qlqr um.....trocar uma ideia contigo......

    ResponderExcluir