$set 1
Глава 4. Локализация и интернационализация - L10N и I18N
Этот перевод может быть устаревшим. Для того, чтобы помочь с переводом, пожалуйста, обратитесь к Сервер переводов FreeBSD.
Содержание
4.1. Программирование приложений с поддержкой I18N
Чтобы сделать ваше приложение более полезным для пользователей, говорящих на других языках, мы надеемся, что вы будете разрабатывать его с поддержкой интернационализации (I18N). Компилятор GNU gcc и библиотеки для графического интерфейса, такие как QT и GTK, поддерживают I18N за счёт специальной обработки строк. Создание программы с поддержкой I18N очень просто. Это позволяет участникам быстро адаптировать ваше приложение для других языков. Для получения более подробной информации обратитесь к документации по I18N для конкретной библиотеки.
Вопреки распространённому мнению, написание кода, соответствующего стандарту I18N, является простой задачей. Обычно оно заключается лишь в оборачивании строк в специфичные для библиотеки функции. Кроме того, убедитесь, что поддерживаются широкие или многобайтовые символы.
4.1.1. Призыв к объединению усилий по интернационализации
Нам стало известно, что индивидуальные усилия по интернационализации (I18N) и локализации (L10N) в каждой стране дублируют работу друг друга. Многие из нас снова и снова неэффективно изобретают велосипед. Мы надеемся, что различные крупные группы разработчиков в области I18N смогут объединится для совместной работы и ответственности, подобно Core Team в FreeBSD.
В настоящее время мы надеемся, что при написании или портировании I18N-программ вы будете отправлять их в соответствующие списки рассылки FreeBSD каждой страны для тестирования. В будущем мы надеемся создать приложения, которые будут работать на всех языках "из коробки" без грязных хаков.
Создана группа Список рассылки, посвящённый интернационализации FreeBSD. Если вы разработчик в области I18N/L10N, присылайте свои комментарии, идеи, вопросы и всё, что относится к этой теме по вашему мнению.
4.2. Локализованные сообщения с поддержкой родного языка POSIX.1 (NLS — Native Language Support)
Помимо основных функций интернационализации (I18N), таких как поддержка различных кодировок ввода или национальных соглашений, например, различных разделителей десятичных знаков, на более высоком уровне I18N можно локализовать сообщения, выводимые различными программами. Распространённый способ сделать это — использовать функции NLS POSIX.1, которые предоставляются как часть базовой системы FreeBSD.
4.2.1. Организация локализованных сообщений в файлы каталогов
POSIX.1 NLS использует файлы каталогов. Эти файлы содержат локализованные сообщения в желаемой кодировке. Сообщения организованы в наборы, и каждое сообщение идентифицируется целым числом в соответствующем наборе. Файлы каталогов традиционно называются по имени локали, для которой они содержат локализованные сообщения, с добавлением расширения .msg
. Например, венгерские сообщения для кодировки ISO8859-2 должны храниться в файле с именем hu_HU.ISO8859-2.
Эти файлы каталогов представляют собой обычные текстовые файлы, содержащие нумерованные сообщения. Можно добавлять комментарии, начиная строку со знака $
. Границы наборов также разделяются специальными комментариями, где ключевое слово set
должно следовать непосредственно за знаком $
. После ключевого слова set
указывается номер набора. Например:
Фактические записи сообщений начинаются с номера сообщения, за которым следует локализованное сообщение. Допускаются известные модификаторы из printf(3):
15 "File not found: %s\n"
Файлы языкового каталога должны быть скомпилированы в бинарный формат перед тем, как они могут быть открыты программой. Это преобразование выполняется с помощью утилиты gencat(1). Её первый аргумент — это имя файла скомпилированного каталога, а последующие аргументы — входные каталоги. Локализованные сообщения также могут быть организованы в несколько файлов каталогов, и затем все они могут быть обработаны с помощью gencat(1).
4.2.2. Использование файлов каталога из исходного кода
Использование файлов каталогов простое. Чтобы вызвать функции, работающие с ними, необходимо включить файл nl_types.h. Перед использованием каталога его нужно открыть с помощью catopen(3). Функция принимает два аргумента. Первый параметр — это имя установленного и скомпилированного каталога. Обычно используется имя программы, например, grep. Это имя будет использоваться при поиске скомпилированного файла каталога. Вызов catopen(3) ищет этот файл в /usr/share/nls/locale/catname и в /usr/local/share/nls/locale/catname, где locale
— установленная локализация, а catname
— имя обсуждаемого каталога. Второй параметр — это константа, которая может принимать два значения:
NL_CAT_LOCALE
, что означает, что используемый файл каталога будет основан наLC_MESSAGES
.0
, что означает, что для открытия соответствующего каталога необходимо использоватьLANG
.
Вызов catopen(3) возвращает идентификатор каталога типа nl_catd
. Обратитесь к справочной странице для получения списка возможных кодов ошибок.
После открытия каталога catgets(3) может быть использована для извлечения сообщения. Первый параметр — это идентификатор каталога, возвращаемый catopen(3), второй — номер набора, третий — номер сообщения, а четвертый — резервное сообщение, которое будет возвращено, если запрошенное сообщение не может быть извлечено из файла каталога.
После использования файла каталога его необходимо закрыть, вызвав catclose(3), которая принимает один аргумент — идентификатор каталога.
4.2.3. Практический пример
Следующий пример демонстрирует простое решение по гибкому использованию каталогов NLS.
Нижеследующие строки необходимо поместить в общий заголовочный файл программы, который включается во все исходные файлы, где необходимы локализованные сообщения:
#ifdef WITHOUT_NLS #define getstr(n) nlsstr[n] #else #include nl_types.h extern nl_catd catalog; #define getstr(n) catgets(catalog, 1, n, nlsstr[n]) #endif extern char *nlsstr[];
Далее, добавьте эти строки в глобальную секцию объявлений основного исходного файла:
#ifndef WITHOUT_NLS #include nl_types.h nl_catd catalog; #endif /* * Default messages to use when NLS is disabled or no catalog * is found. */ char *nlsstr[] = { "", /* 1*/ "some random message", /* 2*/ "some other message" };
Далее следуют реальные фрагменты кода, которые открывают, читают и закрывают каталог:
#ifndef WITHOUT_NLS catalog = catopen("myapp", NL_CAT_LOCALE); #endif ... printf(getstr(1)); ... #ifndef WITHOUT_NLS catclose(catalog); #endif
4.2.3.1. Уменьшение количества строк для локализации
Хороший способ уменьшить количество строк, требующих локализации, — использовать сообщения об ошибках из libc. Это также полезно для избежания дублирования и обеспечения единообразия сообщений об ошибках, с которыми может столкнуться множество программ.
Вот пример, который не использует сообщения об ошибках из libc:
#include err.h ... if (!S_ISDIR(st.st_mode)) errx(1, "argument is not a directory");
Это можно преобразовать для вывода сообщения об ошибке, считывая errno
и выводя соответствующее сообщение об ошибке:
#include err.h #include errno.h ... if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; err(1, NULL); }
В этом примере пользовательская строка исключена, что упростит работу переводчиков при локализации программы, а пользователи увидят стандартное сообщение об ошибке "Not a directory" при возникновении данной ошибки. Это сообщение, вероятно, будет выглядеть более привычным для них. Обратите внимание, что для прямого доступа к errno
потребовалось включить errno.h.
Стоит отметить, что бывают случаи, когда errno
устанавливается автоматически предыдущим вызовом, поэтому нет необходимости устанавливать его явно:
#include err.h ... if ((p = malloc(size)) == NULL) err(1, NULL);
4.2.4. Использование bsd.nls.mk
Использование файлов каталогов требует нескольких повторяемых шагов, таких как компиляция каталогов и их установка в нужное место. Чтобы ещё больше упростить этот процесс, bsd.nls.mk вводит некоторые макросы. Нет необходимости явно включать bsd.nls.mk, он подключается автоматически из общих Makefiles, таких как bsd.prog.mk или bsd.lib.mk.
Обычно достаточно определить NLSNAME
, которое должно содержать имя каталога, указанное в качестве первого аргумента catopen(3), и перечислить файлы каталогов в NLS
без расширения .msg
. Вот пример, который позволяет отключить NLS при использовании с предыдущими примерами кода. Для сборки программы без поддержки NLS необходимо определить переменную WITHOUT_NLS
make(1).
.if !defined(WITHOUT_NLS) NLS= es_ES.ISO8859-1 NLS+= hu_HU.ISO8859-2 NLS+= pt_BR.ISO8859-1 .else CFLAGS+= -DWITHOUT_NLS .endif
Обычно файлы каталогов размещаются в подкаталоге nls, и это поведение по умолчанию для bsd.nls.mk. Однако можно переопределить расположение каталогов с помощью переменной NLSSRCDIR
make(1). Имя по умолчанию для предварительно скомпилированных файлов каталогов также следует упомянутому ранее соглашению об именовании. Его можно переопределить, установив переменную NLSNAME
. Существуют и другие параметры для точной настройки обработки файлов каталогов, но обычно в этом нет необходимости, поэтому они здесь не описаны. Для получения дополнительной информации о bsd.nls.mk обратитесь к самому файлу — он короткий и легко понятен.
Изменено: 12 октября 2025 г. by Vladlen Popolitov