54.2. Программисту

54.2.1. Механизмы

В данном разделе описывается, как добавить языковую поддержку в программе или библиотеке, которая является частью дистрибутива PostgreSQL. В настоящий момент это относится только к программам на языке С.

Добавление языковой поддержки для программы

  1. Вставьте этот код в начало программы:

    #ifdef ENABLE_NLS
    #include <locale.h>
    #endif
    
    ...
    
    #ifdef ENABLE_NLS
    setlocale(LC_ALL, "");
    bindtextdomain("progname", LOCALEDIR);
    textdomain("progname");
    #endif

    (progname фактически может быть выбрана произвольно.)

  2. Везде, где сообщение нуждается в переводе, необходимо вставить вызов gettext(). Например:

    fprintf(stderr, "panic level %d\n", lvl);

    нужно заменить на:

    fprintf(stderr, gettext("panic level %d\n"), lvl);

    (gettext определяется как холостая команда, если NLS поддержка не настроена.)

    Это часто приводит к немалой путанице. Один из распространённых подходов в этом случае:

    #define _(x) gettext(x)

    Ещё одно решение допустимо, если программа часто выполняет обмен данными через одну или несколько функций, таких как ereport() в серверном процессе. Тогда вы выполняете внутренний вызов функции gettext для каждой входящей строки.

  3. Добавьте файл nls.mk в каталог с исходными кодами программы. Данный файл будет считаться сборочным файлом (makefile). В нём необходимо выполнить присваивание значений для следующих переменных:

    CATALOG_NAME

    Имя программы, которое указано в вызове textdomain().

    AVAIL_LANGUAGES

    Список выполненных переводов (изначально пустой).

    GETTEXT_FILES

    Список файлов, которые содержат подлежащие переводу строки, т. е. помеченные gettext или альтернативным решением. В итоге, в него будут включены почти все исходные файлы программы. Если список станет слишком длинным, можно первый «file» сделать + а второе слово — файлом, который содержит по одному имени файла на строку.

    GETTEXT_TRIGGERS

    Утилитам, которые генерируют каталоги сообщений для работы переводчиков, должно быть известно, какие вызовы функции содержат строки, подлежащие переводу. По умолчанию распознаются только вызовы gettext(). Если вы использовали _ или другие идентификаторы, необходимо перечислить их здесь. Если подлежащая переводу строка не является первым аргументом, необходимо, чтобы элемент имел форму func:2 (для второго аргумента). Если функция поддерживает сообщения в форме множественного числа, элемент должен выглядеть следующим образом func:1,2 (идентификация аргументов в виде сообщений в форме единственного и множественного числа).

Система сборки автоматически соберёт и установит каталоги сообщений.

54.2.2. Рекомендации по написанию сообщений

Ниже описаны некоторые рекомендации по написанию сообщений, которые легко перевести.

  • Не составляйте предложения во время выполнения. Например:

    printf("Files were %s.\n", flag ? "copied" : "removed");

    Порядок слов в предложении может отличаться в других языках. Также, даже если вы не забываете вызывать gettext() для каждого фрагмента, возможно, что по отдельности они не будут переведены хорошо. Лучше продублировать небольшую часть кода, чтобы каждое сообщение было переведено как единое целое. Лишь цифры, имена файлов и подобные текущие переменные следует вставлять в текст сообщения во время выполнения.

  • По тем же причинам следующий подход не будет работать:

    printf("copied %d file%s", n, n!=1 ? "s" : "");

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

    if (n==1)
        printf("copied 1 file");
    else
        printf("copied %d files", n):

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

    printf("number of copied files: %d", n);

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

    errmsg_plural("copied %d file",
                  "copied %d files",
                  n,
                  n)

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

    Если вам нужно составить сообщение в форме множественного числа, которое не используется непосредственно при выводе сообщений в errmsg или errdetail, вы должны воспользоваться базовой функцией ngettext. См. документацию по gettext.

  • Если вы хотите передать какую-либо информацию переводчику, например о том, насколько сообщение соотносится с другими выходными данными, перед строкой должен появиться комментарий, который начинается с translator, например:

    /* translator: This message is not what it seems to be. */

    Эти комментарии копируются в файлы каталога сообщений, чтобы переводчик мог их видеть.