55.2. Программисту #
55.2.1. Механизмы #
В данном разделе описывается, как добавить языковую поддержку в программе или библиотеке, которая является частью дистрибутива PostgreSQL. В настоящий момент это относится только к программам на языке С.
Добавление языковой поддержки для программы
Вставьте этот код в начало программы:
#ifdef ENABLE_NLS #include <locale.h> #endif ... #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain("
progname
", LOCALEDIR); textdomain("progname
"); #endif(
progname
фактически может быть выбрана произвольно.)Везде, где сообщение нуждается в переводе, необходимо вставить вызов
gettext()
. Например:fprintf(stderr, "panic level %d\n", lvl);
нужно заменить на:
fprintf(stderr, gettext("panic level %d\n"), lvl);
(
gettext
определяется как холостая команда, если NLS поддержка не настроена.)Это часто приводит к немалой путанице. Один из распространённых подходов в этом случае:
#define _(x) gettext(x)
Ещё одно решение допустимо, если программа часто выполняет обмен данными через одну или несколько функций, таких как
ereport()
в серверном процессе. Тогда вы выполняете внутренний вызов функцииgettext
для каждой входящей строки.Добавьте файл
nls.mk
в каталог с исходными кодами программы. Данный файл будет считаться сборочным файлом (makefile). В нём необходимо выполнить присваивание значений для следующих переменных:CATALOG_NAME
Имя программы, которое указано в вызове
textdomain()
.GETTEXT_FILES
Список файлов, которые содержат подлежащие переводу строки, т. е. помеченные
gettext
или альтернативным решением. В итоге, в него будут включены почти все исходные файлы программы. Если список станет слишком длинным, можно первый «file» сделать+
а второе слово — файлом, который содержит по одному имени файла на строку.GETTEXT_TRIGGERS
Утилитам, которые генерируют каталоги сообщений для работы переводчиков, должно быть известно, какие вызовы функции содержат строки, подлежащие переводу. По умолчанию распознаются только вызовы
gettext()
. Если вы использовали_
или другие идентификаторы, необходимо перечислить их здесь. Если подлежащая переводу строка не является первым аргументом, необходимо, чтобы элемент имел формуfunc:2
(для второго аргумента). Если функция поддерживает сообщения в форме множественного числа, элемент должен выглядеть следующим образомfunc:1,2
(идентификация аргументов в виде сообщений в форме единственного и множественного числа).
Добавьте файл
po/LINGUAS
, содержащий список языков, для которых есть переведённые файлы (изначально пустой).
Система сборки автоматически соберёт и установит каталоги сообщений.
55.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. */
Эти комментарии копируются в файлы каталога сообщений, чтобы переводчик мог их видеть.
55.2. For the Programmer #
55.2.1. Mechanics #
This section describes how to implement native language support in a program or library that is part of the PostgreSQL distribution. Currently, it only applies to C programs.
Adding NLS Support to a Program
Insert this code into the start-up sequence of the program:
#ifdef ENABLE_NLS #include <locale.h> #endif ... #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain("
progname
", LOCALEDIR); textdomain("progname
"); #endif(The
progname
can actually be chosen freely.)Wherever a message that is a candidate for translation is found, a call to
gettext()
needs to be inserted. E.g.:fprintf(stderr, "panic level %d\n", lvl);
would be changed to:
fprintf(stderr, gettext("panic level %d\n"), lvl);
(
gettext
is defined as a no-op if NLS support is not configured.)This tends to add a lot of clutter. One common shortcut is to use:
#define _(x) gettext(x)
Another solution is feasible if the program does much of its communication through one or a few functions, such as
ereport()
in the backend. Then you make this function callgettext
internally on all input strings.Add a file
nls.mk
in the directory with the program sources. This file will be read as a makefile. The following variable assignments need to be made here:CATALOG_NAME
The program name, as provided in the
textdomain()
call.GETTEXT_FILES
List of files that contain translatable strings, i.e., those marked with
gettext
or an alternative solution. Eventually, this will include nearly all source files of the program. If this list gets too long you can make the first “file” be a+
and the second word be a file that contains one file name per line.GETTEXT_TRIGGERS
The tools that generate message catalogs for the translators to work on need to know what function calls contain translatable strings. By default, only
gettext()
calls are known. If you used_
or other identifiers you need to list them here. If the translatable string is not the first argument, the item needs to be of the formfunc:2
(for the second argument). If you have a function that supports pluralized messages, the item should look likefunc:1,2
(identifying the singular and plural message arguments).
Add a file
po/LINGUAS
, which will contain the list of provided translations — initially empty.
The build system will automatically take care of building and installing the message catalogs.
55.2.2. Message-Writing Guidelines #
Here are some guidelines for writing messages that are easily translatable.
Do not construct sentences at run-time, like:
printf("Files were %s.\n", flag ? "copied" : "removed");
The word order within the sentence might be different in other languages. Also, even if you remember to call
gettext()
on each fragment, the fragments might not translate well separately. It's better to duplicate a little code so that each message to be translated is a coherent whole. Only numbers, file names, and such-like run-time variables should be inserted at run time into a message text.For similar reasons, this won't work:
printf("copied %d file%s", n, n!=1 ? "s" : "");
because it assumes how the plural is formed. If you figured you could solve it like this:
if (n==1) printf("copied 1 file"); else printf("copied %d files", n):
then be disappointed. Some languages have more than two forms, with some peculiar rules. It's often best to design the message to avoid the issue altogether, for instance like this:
printf("number of copied files: %d", n);
If you really want to construct a properly pluralized message, there is support for this, but it's a bit awkward. When generating a primary or detail error message in
ereport()
, you can write something like this:errmsg_plural("copied %d file", "copied %d files", n, n)
The first argument is the format string appropriate for English singular form, the second is the format string appropriate for English plural form, and the third is the integer control value that determines which plural form to use. Subsequent arguments are formatted per the format string as usual. (Normally, the pluralization control value will also be one of the values to be formatted, so it has to be written twice.) In English it only matters whether
n
is 1 or not 1, but in other languages there can be many different plural forms. The translator sees the two English forms as a group and has the opportunity to supply multiple substitute strings, with the appropriate one being selected based on the run-time value ofn
.If you need to pluralize a message that isn't going directly to an
errmsg
orerrdetail
report, you have to use the underlying functionngettext
. See the gettext documentation.If you want to communicate something to the translator, such as about how a message is intended to line up with other output, precede the occurrence of the string with a comment that starts with
translator
, e.g.:/* translator: This message is not what it seems to be. */
These comments are copied to the message catalog files so that the translators can see them.