Internacionalização de ficheiros de JavaScript

2010-08-29

Os últimos posts no meu blog têm sido acerca do tema da internacionalização de aplicações, nomeadamente:

  • ASP.Net – Método alternativo de internacionalização (i18n)
  • Localizar Enumerações
  • Adicionar novas línguas ao Windows (edições Home)

O post de hoje contínua o sobre o método alternativo com ASP.NET, recorrendo a ao GetText do GNU. Como vimos, a aplicação do método _() para substituir texto localizável tornava-se um recurso simples e eficiente na internacionalização de ASP.NET. Em especial, é um método adoptado pela restantes linguagens e evita o esforço extra necessário para introduzir o texto em ficheiros .resx.

Procurei uma solução semelhante para Javascript, uma vez que a aplicação que estou a internacionalizar recorre muito a funcionalidade em javascript. Em especial, há mensagens de erro específicos e mensagens de confirmação contextualizadas. Especifico a ASP.Net, não encontrei nada de útil. Até achei muito estranho não detectar nada de muito útil nesse campo.

De qualquer forma, há alguns métodos genéricos a aplicar, incluíndo plugins em jQuery (http://plugins.jquery.com/project/gettext), mas gostei da descrição dada em 24ways.org. Simplesmente introduzi a função para _() no meu ficheiro core de javascript (é uma pequena biblioteca e funções e abstracções comuns a todas as páginas das minhas aplicações, e incluído em todas as páginas):

Com isto é esperado existir uma variável i18n definido com a lista de traduções em pares chave-valor em JSON, do tipo:

O método em 24ways.oprg refere ainda funções para formatar strings que podem ser uteis. Eu no entanto já tinha uma implementação de string.format para javascript (http://www.geekdaily.net/2007/06/21/cs-stringformat-for-javascript/).

Portanto, com a função no core, e a variável i18n adicionada, nos diversos ficheiros de javascript, onde fosse usado uma string, este deveria ser englobado por _():

Assim, quando executado, e porque o método _() está sempre presente, o texto será substituído pelo existente no dicionário ou se não encontrado, o próprio texto usado como argumento da função.

Resta apenas dois passos – gerar os ficheiros .po, que deve seguir um esquema semelhante ao mencionado no artigo de ASP.Net, usando instruções póst-build e o gettext, e depois transformar os registos dos ficheiros .po em json. O primeiro dos passos é relativamente directa, apenas necessitando de construir uma nova lista de ficheiros (apenas de .js) e adicionar as entradas envolvidas em _() no .pot.

O segundo passo é mais “dificil”. Não há nada para o fazer directamentem senão um script em perl (http://jsgettext.berlios.de/doc/html/po2json.html), e como não me servia, decidi construir um script T4 simples, para usar no processo.

O script assume alguns pressupostos, pelo que, se utilizar, deve de os ter em conta e alterar consoante o necessário. O primeiro é que os ficheiros resultantes, quer os .po, quer os .js vão estar cada um numa pasta dedicada à língua específica como por exemplo:

/Scripts/locale/xx/messages.po
/Scripts/locale/xx/i18n.js

Também, considero que o ficheiro .po será gerado e unido com recurso a msgmerge, e portanto apenas devo transformar os ficheiros .po em ficheiros .js

Assim, o po2json.tt, colocado na pasta (/Scripts/locale/) pode ser executado após o build. Ele procurará o ficheiro .po de cada língua no array, e transformarará em um ficheiro i18n.js. Eu decidi definir as linguas manualmente, mas com pequenas alterações, podem ser detectadas automáticamente.

O T4 é o seguinte:

Procura cada “msgid” e processa as línguas seguintes convenientemente. Se houver erros, o processo será abandonado. O tt recorre ainda a um MultiOutput.t4, para gerar mais que um ficheiro a partir do .tt, convenientemente localizado. Não necessita de estar incluído no projecto (para não obrigar a incluir as dependências de TextTempleting).

Para incluir o ficheiro na lista de scripts a puxar, basta regista-lo no ScriptManager, no Page_Load da MasterPage :

onde a variável lang é uma string com as letras da língua, para identificar a pasta onde será armazenado. Eu obtenho a a língua para a aplicação (transversal), mas outra possibilidade seria definir a língua pelas preferências do utilizador. Também, o caminho poderia ser proveniente de uma chave de configuração no web.config, se necessário.

Creio que está é uma solução simples, que pode, se necessário ser melhorado com mais funções (especialmente de formatações e plural / singular) como descrito no artigo do 24ways.org. Para já, esta é me suficiente.

Download po2json t4