Translating software using GNU gettext

GNU gettext is one of the most widely used tool for internationalization of free software. It provides a simple yet flexible way to localize the software. It has great support for plurals, it can add further context to the translated string and there are quite a lot of tools built around it. Of course it has great support in Weblate (see GNU gettext file format description).

Примечание

Если вы собираетесь использовать его в несвободном программном обеспечении, пожалуйста, сначала ознакомьтесь с его лицензией, она может оказаться для вас непригодной.

GNU gettext can be used from a variety of languages (C, Python, PHP, Ruby, JavaScript and many more) and usually the UI frameworks already come with some support for it. The standard usage is through the gettext() function call, which is often aliased to _() to make the code simpler and easier to read.

Дополнительно он предоставляет вызовы pgettext(), позволяющий предоставлять дополнительный контекст для переводчиков, и ngettext(), который умеет обрабатывать формы множественного числа по правилам целевого языка.

As a widely spread tool, it has many wrappers which make its usage really simple, instead of manual invoking of gettext described below, you might want to try one of them, for example intltool.

Общий обзор последовательности действий

The GNU gettext uses several files to manage the localization:

  • В PACKAGE.pot находятся строки, извлечённые из вашего исходного кода; обычно это делается с помощью xgettext или какой-нибудь высокоуровневой обёрткой для него вроде intltool.

  • В LANGUAGE.po находятся строки с переводом на конкретный язык. Его нужно обновлять с помощью msgmerge после каждого обновления PACKAGE.pot. Вы можете создать новый файл языка с помощью msginit или прямо через Weblate.

  • В LANGUAGE.mo находится бинарное представление LANGUAGE.po. Он используется непосредственно при работе самого приложения. Обычно он не хранится в системе контроля версий, а генерируется при компиляции с помощью msgfmt. Если вы всё же хотите хранить его в системе контроля версий, то вы можете генерировать его в Weblate с помощью соответствующей надстройки.

Overall the GNU gettext workflow looks like this:

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

См.также

Overview of GNU gettext

Пример программы

The simple program in C using gettext might look like following:

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

Извлечение строк для перевода

После того, как вы добавите в свой код вызововы gettext, вы можете извлечь сообщения из нег и записать их в .pot-файлы с помощью xgettext :

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

Примечание

Существуют альтернативные программы для извлечения строк из кода, например pybabel.

После этого вызова создастся шаблонный файл, который можно использовать для начала новых переводов (используя программу msginit) или обновления существующих после изменения кода (для этого можно использовать программу msgmerge). В результате получается простой структурированный текстовый файл:

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

Каждая запись с msgid в начале определяет переводимую строку текста, специальная пустая запись в начале файла — это заголовок файла, содержащий метаданные о переводе.

Начало нового перевода

Имея шаблон, мы можем начать наш первый перевод:

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

Только что созданный файл cs.po уже содержит некоторую информацию. Самое главное заключается в том, что в нём правильно определены формы множественного числа для выбранного языка, и вы можете видеть, что, отражая этот факт, количество форм множественного числа изменилось:

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

This file is compiled into an optimized binary form, the .mo file used by the GNU gettext functions at runtime.

Обновление строк

Если вы добавите в свою программу ещё нескольких строк или измените в ней какие-то из них, то после этого вы снова должны будете запустить xgettext, который пересоздаст файл шаблона:

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

Затем вы можете обновить отдельные файлы перевода, чтобы они соответствовали вновь созданным шаблонам (обновление, в том числе, переупорядочивает строки для соответствия их новому шаблону):

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

Импорт в Weblate

Для импорта такого перевода в Weblate, всё, что вам нужно — это определить следующие поля при создании компонента (подробное описание полей смотрите в разделе в Настройки компонента):

Поле

Значение

Репозиторий исходного кода

URL-адрес репозитория системы контроля версий с вашим проектом

Маска файла

po/*.po

Шаблон для новых переводов

po/hello.pot

Формат файла

Choose gettext PO file

Новый язык

Выберите Создать новый язык

Вот и всё, теперь вы готовы начать переводить свою программу!

См.также

You can find a gettext example with many languages in the Weblate Hello project on GitHub: <https://github.com/WeblateOrg/hello>.