Книга: Linux программирование в примерах

13.3.8. Создание переводов

13.3.8. Создание переводов

После интернационализации программы необходимо подготовить переводы. Это осуществляется с помощью нескольких инструментов уровня оболочки. Мы начнем с интернационализированной версии ch06-echodate.c из раздела 6.1.4 «Преобразование разложенного времени в time_t»:

/* ch13-echodate.c --- демонстрация переводов */
#include <stdio.h>
#include <time.h>
#include <locale.h>
#define ENABLE_NLS 1
#include "gettext.h"
#define _(msgid) gettext(msgid)
#define N_(msgid) msgid
int main (void) {
 struct tm tm;
 time_t then;
 setlocale(LC_ALL, "");
 bindtextdomain("echodate", ".");
 textdomain("echodate");
 printf("%s", _("Enter a Date/time as YYYY/MM/DD HH:MM:SS : "));
 scanf("%d/%d/%d %d:%d:%d",
  &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
  &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
 /* Проверка ошибок для краткости опущена. */
 tm.tm_year -= 1900;
 tm.tm_mon -= 1;
 tm.tm_isdst = -1; /* О летнем времени ничего не известно */
 then = mktime(&tm);
 printf(_("Got: %s"), ctime(&then));
 exit(0);
}

Мы намеренно использовали "gettext.h", а не <gettext.h>. Если наше приложение поставляется с отдельной копией библиотеки gettext, тогда "gettext.h" найдет ее, избежав использования системной копии. С другой стороны, если имеется лишь системная копия, она будет найдена, если локальной копии нет. Общеизвестно, что ситуация усложнена фактом наличия на системах Solaris библиотеки gettext, которая не имеет всех возможностей версии GNU.

Переходя к созданию переводов, первым шагом является извлечение переводимых строк. Это осуществляется программой xgettext:

$ xgettext --keyword=_ --keyword=N_
> --default-domain=echodate ch13-echodate.с

Опции --keyword сообщает xgettext, что нужно искать макросы _() и N_(). Программа уже знает, как извлекать строки из gettext() и ее вариантов, а также из gettext_noop().

Вывод xgettext называется переносимым объектным файлом. Имя файла по умолчанию messages.ро, что соответствует текстовому домену по умолчанию "messages". Опция --default-domain обозначает текстовый домен для использования в имени выходного файла. В данном случае, файл назван echodate.ро. Вот его содержание:

# SOME DESCRIPTIVE TITLE. /* Шаблон, нужно отредактировать */
# Copyright (С) 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 VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2003-07-14 18:46-0700n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>n"
"Language-Team: LANGUAGE <[email protected]>n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=CHARSETn"
"Content-Transfer-Encoding: 8bitn"
#: ch13-echodate.c:19 /* Местоположение сообщения */
msgid "Enter a Date/time as YYYY/MM/DD HH:MM:SS : " /* Оригинальное
                                                       сообщение */
msgstr "" /* Здесь перевод */
#: ch13-echodate.с:32 /* To же самое для каждого сообщения */
#, с-format
msgid "Got: %s"
msgstr ""

Этот первоначальный файл используется повторно для каждого перевода. Таким образом, это шаблон для переводов, и по соглашению, для отображения этого факта он должен быть переименован с расширением .pot (portable object template — переносимый объектный шаблон):

$ mv echodate.ро echodate.pot

He владея свободно несколькими языками, мы решили перевести сообщения на свинский латинский. Следующим шагом является создание перевода. Это осуществляется копированием файла шаблона и добавлением к новой копии перевода:

$ cp echodate.pot piglat.po
$ vi piglat.po /* Добавить переводы, используя любимый редактор */

Имя по соглашению должно быть язык.po, где язык является стандартным международным сокращением из двух или трех букв для обозначения языка. Иногда используется форма язык_страна.po: например, pt_BR.po для португальского в Бразилии. Поскольку свинский латинский не является настоящим языком, мы назвали файл piglat.ро.[147] Вот содержание после добавления перевода:

# echodate translations into pig Latin
# Copyright (C) 2004 Prentice-Hall
# This file is distributed under the same license as the echodate package.
# Arnold Robbins <[email protected]> 2004
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: echodate 1.0n"
"Report-Msgid-Bugs-To: [email protected]"
"POT-Creation-Date: 2003-07-14 18:46-0700n"
"PO-Revision-Date: 2003-07-14 19:00+8n"
"Last-Translator: Arnold Robbins <[email protected]>n"
"Language-Team: Pig Latin <[email protected]>n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=ASCIIn"
"Content-Transfer-Encoding: 8bitn"
#: ch13-echodate.с:19
msgid "Enter a Date/time as YYYY/MM/DD HH:MM:SS : "
msgstr "Enteray A Ateday/imetay asay YYYY/MM/DD HH:MM:SS : "
#: ch13-echodate.c:32
#, c-format
msgid "Got: %s"
msgstr "Otgay: %s"

Хотя можно было бы произвести линейный поиск в переносимом объектном файле, такой поиск был бы медленным. Например, в gawk имеется примерно 350 отдельных сообщений, а в GNU Coreutils — свыше 670. Линейный поиск в файле с сотнями сообщений был бы заметно медленным. Поэтому GNU gettext использует для быстрого поиска сообщений двоичный формат. Сравнение осуществляет msgfmt, выдавая объектный файл сообщений:

$ msgfmt piglat.po -о piglat.mo

При сопровождении программы изменяются строки, используемые программой: добавляются новые, другие удаляются или изменяются. По крайней мере, может измениться положение строки в исходном файле. Таким образом, файлы переводов .ро, вероятно, устареют. Программа msgmerge объединяет старые файлы переводов с новым файлом .pot. Затем результат может быть обновлен. Этот пример выполняет объединение и повторное компилирование:

$ msgmerge piglat.po echodate.pot -o piglat.new.po /* Объединить файлы */
$ mv piglat.new.po piglat.po /* Переименовать результат */
$ vi piglat.po /* Модернизировать перевод */
$ msgfmt piglat.po -o piglat.mo /* Восстановить файл .mo */

Откомпилированные файлы .mo помещаются в файл base/locale/category/textdomain.mo. На системах GNU/Linux base является /usr/share/locale. locale является обозначением языка, например, 'es', 'fr' и т.д. category является категорией локали; для сообщений это LC_MESSAGES. textdomain является текстовым доменом программы, в нашем случае это echodate. В качестве реального примера в /usr/share/locale/es/LC_MESSAGES/coreutils.mo находится перевод GNU Coreutils на испанский.

Функция bindtextdomain() изменяет в местоположении часть base. В ch13-echodate.c мы меняем ее на '.'. Таким образом, нужно создать соответствующие каталоги и поместить туда перевод на свинский латинский:

$ mkdir -р en_US/LC_MESSAGES /* Нужно использовать реальную локаль */
$ cp piglat.mo en_US/LC_MESSAGES/echodate.mo /* Поместить файл в нужное место */

Должна использоваться реальная локаль[148]; мы «притворяемся» использующими "en_US". Разместив перевод, устанавливаем соответствующим образом LC_ALL, скрещиваем пальцы и запускаем программу:

$ LC_ALL=en_US ch13-echodate /* Запуск программы */
Enteray A Ateday/imetay asay YYYY/MM/DD HH:MM:SS : 2003/07/14 21:19:26
Otgay: Mon Jul 14 21:19:26 2003

Последнюю версию GNU gettext можно найти в каталоге дистрибутива GNU gettext.[149]

Этот раздел лишь слегка коснулся поверхности процесса локализации. GNU gettext предоставляет множество инструментов для работы с переводами, и в особенности для облегчения поддержания современности переводов по мере развития исходного кода программы. Процесс ручного обновления переводов осуществим, но утомителен. Эта задача легко автоматизируется с помощью make; в частности, GNU gettext хорошо интегрируется для обеспечения этой возможности с Autoconf и Automake, снимая с программиста значительный груз по разработке.

Рекомендуем прочесть документацию GNU gettext, чтобы больше узнать как об этих проблемах в частности, так и о GNU gettext в общем.

Оглавление книги


Генерация: 1.276. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз