Traduzindo software a usar GNU gettext

GNU gettext é uma das ferramentas mais utilizadas para internacionalização de software livre. Ele fornece uma maneira simples, mas flexível, de localizar o software. Ele tem um ótimo suporte para plurais, pode adicionar mais contexto à cadeia traduzida e há várias ferramentas construídas em torno dele. Claro que tem um ótimo suporte no Weblate (veja a descrição do formato de ficheiro GNU gettext).

Nota

Se estiver prestes a usá-lo num software proprietário, consulte o licenciamento primeiro. Ele pode não ser adequado para si.

GNU gettext pode ser usado a partir de uma variedade de linguagens (C, Python, PHP, Ruby, JavaScript e muitos mais) e geralmente os frameworks de UI já vêm com algum suporte para isso. O uso padrão é através da chamada de função gettext(), que geralmente é apelidada de _() para tornar o código mais simples e fácil de ler.

Além disso, ele fornece a chamada pgettext() para fornecer contexto adicional para tradutores e ngettext() que pode lidar com tipos plurais conforme definido para o idioma alvo.

Como uma ferramenta amplamente difundida, ela possui muitos wrappers que tornam o uso dele realmente simples, ao invés de invocar manualmente o gettext descrito abaixo, quererá tentar um deles, por exemplo, o intltool.

Visão geral do fluxo de trabalho

O GNU gettext usa vários ficheiros para gerir a localização:

  • PACKAGE.pot contém cadeias extraídas do seu código-fonte, normalmente a usar xgettext ou alguns wrappers de alto nível como intltool.

  • LANGUAGE.po contém cadeias com uma tradução para um único idioma. Ele deve ser atualizado por msgmerge uma vez que o PACKAGE.pot seja atualizado. Pode criar novos ficheiros de idioma a usar msginit ou dentro do Weblate.

  • LANGUAGE.mo contém a representação binária de LANGUAGE.po e é usado no tempo de execução da aplicação. Normalmente não é mantido num controle de versão, mas gerado em tempo de compilação usando msgfmt. No caso de desejar tê-lo no controle de versão, pode gerá-lo no Weblate usando a extensão Gerar ficheiros MO.

No geral, o fluxo de trabalho do GNU gettext é assim:

digraph translations { graph [fontname = "sans-serif", fontsize=10]; node [fontname = "sans-serif", fontsize=10, shape=note, margin=0.1, height=0]; edge [fontname = "monospace", fontsize=10]; "Source code" -> "PACKAGE.pot" [label=" xgettext "]; "PACKAGE.pot" -> "LANGUAGE.po" [label=" msgmerge "]; "LANGUAGE.po" -> "LANGUAGE.mo" [label=" msgfmt "]; }

Amostra de programa

O programa simples em C a usar gettext pode ter a seguinte aparência:

#include <libintl.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int count = 1;
    setlocale(LC_ALL, "");
    bindtextdomain("hello", "/usr/share/locale");
    textdomain("hello");
    printf(
        ngettext(
            "Orangutan has %d banana.\n",
            "Orangutan has %d bananas.\n",
            count
        ),
        count
    );
    printf("%s\n", gettext("Thank you for using Weblate."));
    exit(0);
}

A extrair cadeias traduzíveis

Assim que tiver o código a usar as chamadas gettext, pode usar xgettext para extrair mensagens dele e armazená-las num .pot:

$ xgettext main.c -o po/hello.pot

Nota

Existem programas alternativos para extrair cadeias do código, por exemplo pybabel.

Isso cria um ficheiro de modelo, que pode usar para iniciar novas traduções (a usar msginit) ou atualizar as existentes após a mudança de código (usaria msgmerge para isso). O ficheiro resultante é simplesmente um ficheiro texto estruturado:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-10-23 11:02+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#: main.c:14
#, c-format
msgid "Orangutan has %d banana.\n"
msgid_plural "Orangutan has %d bananas.\n"
msgstr[0] ""
msgstr[1] ""

#: main.c:20
msgid "Thank you for using Weblate."
msgstr ""

Cada linha msgid define uma cadeia a ser traduzido, a cadeia vazia especial no início é o cabeçalho do ficheiro a conter metadados sobre a tradução.

Iniciando nova tradução

Com o modelo no lugar, podemos começar nossa primeira tradução:

$ msginit -i po/hello.pot -l cs --no-translator -o po/cs.po
Created cs.po.

O recém-criado cs.po já tem algumas informações preenchidas. Mais importante ainda, ele obteve a definição de formas plurais apropriada para o idioma escolhido e pode ver que a quantidade de plurais mudou de acordo com isso:

# Czech translations for PACKAGE 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-10-23 11:02+0200\n"
"PO-Revision-Date: 2015-10-23 11:02+0200\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"

#: main.c:14
#, c-format
msgid "Orangutan has %d banana.\n"
msgid_plural "Orangutan has %d bananas.\n"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""

#: main.c:20
msgid "Thank you for using Weblate."
msgstr ""

Este ficheiro é compilado num formato binário otimizado, o ficheiro .mo usado pelas funções do GNU gettext em tempo de execução.

A atualizar cadeias

Depois de adicionar mais textos ou alterar algumas cadeias no seu programa, executa novamente xgettext que gera novamente o ficheiro de modelo:

$ xgettext main.c -o po/hello.pot

Em seguida, pode atualizar ficheiros de tradução individuais para corresponder aos modelos recém-criados (isso inclui reordenar as cadeias para corresponder ao novo modelo):

$ msgmerge --previous --update po/cs.po po/hello.pot

Importando para o Weblate

Para importar tal tradução para o Weblate, tudo que precisa definir são os seguintes campos ao criar o componente (veja Configuração de componente para uma descrição detalhada dos campos):

Campo

Valor

Repositório do código-fonte

URL do repositório VCS com o seu projeto

Máscara de ficheiros

po/*.po

Modelo para novas traduções

po/hello.pot

Formato de ficheiro

Escolha ficheiro PO gettext

Novo idioma

Escolha Criar novo ficheiro de idioma

E é isso. Agora está pronto para começar a traduzir o seu software!

Veja também

Pode encontrar um exemplo de gettext com muitos idiomas no projeto Weblate Hello no GitHub: <https://github.com/WeblateOrg/hello>.