Перевод программ с помощью GNU Gettext

GNU Gettext является одним из наиболее широко используемых инструментов интернационализации свободного программного обеспечения. Он предоставляет простой, но гибкий способ локализации программного обеспечения. Он имеет отличную поддержку форм множественного числа, может добавлять к переводимым строкам дополнительный контекст и на его основе построено довольно большое количество инструментов. И конечно, он имеет отличную поддержку в Weblate (смотрите описание формата файла GNU gettext).

Примечание

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

GNU Gettext можно использовать со множеством различных языков (C, Python, PHP, Ruby, JavaScript и со многими другими), и обычно во фреймворках пользовательского интерфейса уже есть некоторая его поддержка. Стандартным его использованием является вызов функции gettext(), которая часто сокращается до своего псевдонима _(), чтобы сделать код более простым и лёгким для чтения.

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

Как широко распространённый инструмент, для него существует множество обёрток, значительно упрощающих его использование; вместо описанного ниже ручного вызова Gettext, вы, возможно, захотите попробовать одну из них, например, intltool.

Workflow overview

The GNU Gettext uses several files to manage the localization:

  • PACKAGE.pot contains strings extracted from your source code, typically using xgettext or some high level wrappers such as intltool.

  • LANGUAGE.po contains strings with a translation to single language. It has to be updated by msgmerge once the PACKAGE.pot is updated. You can create new language files using msginit or within Weblate.

  • LANGUAGE.mo contains binary representation of LANGUAGE.po and is used at application runtime. Typically it is not kept under version control, but generated at compilation time using msgfmt. In case you want to have it in the version control, you can generate it in Weblate using Генерирование MO-файлов addon.

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

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

Простая программа на C, использующая Gettext, может выглядеть следующим образом:

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

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

Once you have code using the gettext calls, you can use xgettext to extract messages from it and store them into a .pot:

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

Этот файл компилируется в оптимизированную двоичную форму — файл .mo, используемый функциями GNU Gettext времени выполнения.

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

Once you add more strings or change some strings in your program, you execute again xgettext which regenerates the template file:

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

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

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

Импорт в Weblate

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

Поле

Значение

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

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

Маска файла

po/*.po

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

po/hello.pot

Формат файла

Выберите gettext PO-файл

Новый язык

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

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

См.также

Пример использования Gettext со многими языками можно найти в проекте Weblate Hello на GitHub’е: <https://github.com/WeblateOrg/hello>.