Traduir programari amb GNU gettext

GNU gettext és una de les eines més utilitzades per a la internacionalització del programari lliure. Proporciona una manera senzilla però flexible de localitzar el programari. Té un gran suport per als plurals, pot afegir més context a la cadena traduïda i hi ha un munt d’eines construïdes al seu voltant. Per descomptat, té un gran suport a Weblate (vegeu la descripció del format de fitxer GNU gettext PO (objecte portàtil)).

Nota

Si esteu a punt d’utilitzar-lo en programari propietari, consulteu primer les llicències, potser no us convé.

GNU gettext es pot utilitzar des d’una varietat d’idiomes (C, Python, PHP, Ruby, JavaScript i molts més) i normalment els marcs d’interfície d’usuari ja tenen suport per a això. L’ús estàndard és a través de la crida a la funció gettext(), que sovint s’anomena _() per fer que el codi sigui més senzill i fàcil de llegir.

A més, proporciona una crida a pgettext() per proporcionar context addicional als traductors i ngettext() que pot gestionar els tipus plurals tal com es defineix per a la llengua de destinació.

Com a eina àmpliament estesa, té molts embolcalls que fan que el seu ús sigui molt senzill, en lloc d’invocar manualment el text que es descriu a continuació, potser voldreu provar-ne un, per exemple intltool.

Workflow overview

El GNU gettext utilitza diversos fitxers per gestionar la localització:

  • PACKAGE.pot conté cadenes extretes del vostre codi font, normalment utilitzant xgettext o alguns embolcalls d’alt nivell com ara intltool.

  • LANGUAGE.po conté cadenes amb una traducció a un sol idioma. L’ha d’actualitzar msgmerge un cop actualitzat el PACKAGE.pot. Podeu crear fitxers d’idioma nous mitjançant msginit o dins de Weblate.

  • LANGUAGE.mo conté la representació binària de LANGUAGE.po i s’utilitza en temps d’execució de l’aplicació. Normalment no es manté sota control de versions, sinó que es genera en el moment de la compilació mitjançant msgfmt. En cas de voler tenir-lo al control de versions, el podeu generar a Weblate mitjançant el complement Genera els fitxers MO.

En general, el flux de treball de GNU gettext té aquest aspecte:

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 "]; }

Programa de mostra

El programa senzill en C que utilitza gettext pot semblar el següent:

#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);
}

Extracció de cadenes traduïbles

Un cop tingueu codi mitjançant les trucades gettext, podeu utilitzar xgettext per extreure’n els missatges i emmagatzemar-los en un .pot:

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

Nota

Hi ha programes alternatius per extreure cadenes del codi, per exemple pybabel.

Això crea un fitxer de plantilla, que podeu utilitzar per iniciar noves traduccions (utilitzant msginit) o per actualitzar-ne les existents després del canvi de codi (usaríeu msgmerge per a això). El fitxer resultant és simplement un fitxer de text estructurat:

# 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 línia msgid defineix una cadena per traduir, la cadena buida especial al principi és la capçalera del fitxer que conté metadades sobre la traducció.

S’està iniciant una nova traducció

Amb la plantilla al seu lloc, podem començar la nostra primera traducció:

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

El cs.po acabat de crear ja té una mica d’informació emplenada. El més important, té la definició adequada de les formes de plural per a l’idioma escollit i podeu veure que el nombre de plurals ha canviat d’acord amb això:

# 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 ""

Aquest fitxer es compila en una forma binària optimitzada, el fitxer .mo utilitzat per les funcions GNU gettext en temps d’execució.

Actualització de cadenes

Un cop afegiu més cadenes o canvieu algunes cadenes al vostre programa, torneu a executar xgettext que regenera el fitxer de plantilla:

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

A continuació, podeu actualitzar fitxers de traducció individuals perquè coincideixin amb les plantilles acabades de crear (això inclou reordenar les cadenes perquè coincideixin amb la plantilla nova):

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

Nota

Quan utilitzeu Weblate, normalment no cal que executeu msgmerge manualment. Weblate pot gestionar-ho automàticament mitjançant el complement Actualitza els fitxers PO perquè coincideixin amb el POT (msgmerge), o bé podeu carregar el fitxer POT actualitzat mitjançant el mètode de càrrega Actualitza les cadenes d’origen. Vegeu Updating target-language files per a més detalls.

Importació a Weblate

Per importar aquesta traducció a Weblate, tot el que necessiteu definir són els camps següents quan creeu un component (vegeu Configuració dels components per a una descripció detallada dels camps):

Field

Value

Repositori del codi font

URL del repositori VCS amb el vostre projecte

Filtre de fitxers

po/*.po

Plantilla per a traduccions noves

po/hello.pot

Format de fitxer

Choose gettext PO file

Llengua nova

Trieu Crea un fitxer d’idioma nou

I això és tot, ja esteu preparat per començar a traduir el vostre programari!

Vegeu també

Podeu trobar un exemple de gettext amb molts idiomes al projecte Weblate Hello a GitHub: <https://github.com/WeblateOrg/hello>.