| |
Разработка графического интерфейса с помощью библиотеки Qt3 | ||
---|---|---|
Пред. | Глава 15. Интернационализация | След. |
Если необходимо предусмотреть возможность перевода приложения на разные языки, следует соблюдать следующие положения:
Весь текст, который будет отображаться перед пользователем, должен пропускаться через функцию tr().
На запуске, приложение должно подгружать файл с переводом (.qm).
Функция tr() -- статическая, она определена в классе QObject и перекрывается в каждом классе-потомке, который включает в свое определение макрос Q_OBJECT. Она возвращает перевод заданной строки, если он существует, или оригинальную версию строки -- в противном случае.
Для подготовки файла перевода необходимо запустить утилиту Qt -- lupdate. Она извлечет из исходного текста программы все строки, которые передаются функции tr() и создаст файл перевода. Этот файл может быть передан переводчику, который добавит в него перевод для каждой из строк. Более подробно процесс перевода описан в разделе Перевод существующих приложений.
Функция tr() имеет следующий синтаксис вызова:
Context::tr(sourceText, comment)Часть имени Context -- это имя класса, производного от QObject. Если функция вызывается в контексте класса, то указание имени класса не обязательно. sourceText -- это строка символов, которая должна быть переведена. comment -- не обязательный аргумент, может использоваться для предоставления дополнительной информации переводчику.
Еще один пример:
BlueWidget::BlueWidget(QWidget *parent, const char *name) : QWidget(parent, name) { QString str1 = tr("Legal"); QString str2 = BlueWidget::tr("Legal"); QString str3 = YellowDialog::tr("Legal"); QString str4 = YellowDialog::tr("Legal", "US paper size"); }Первые два вызова производятся в контексте класса BlueWidget, последние два -- YellowDialog. Все четыре вызова получают строку "Legal" в качестве исходной, кроме того, последний из них имеет дополнительный комментарий, который поможет переводчику понять смысл исходной строки.
Перевод строк в разных контекстах выполняется независимо друг от друга. Переводчики обычно учитывают контекст в своей работе, часто выполняя пробные запуски приложения и оценивая качество перевода.
При вызове tr() из глобальных функций, необходимо явно указывать контекст, в качестве которого может использоваться любой класс, наследник от QObject. Если в приложении нет ничего подходящего, всегда можно прибегнуть к услугам самого QObject, например:
int main(int argc, char *argv[]) { QApplication app(argc, argv); ... QPushButton button(QObject::tr("Hello Qt!"), 0); app.setMainWidget(&button); button.show(); return app.exec(); }Очень часто полезной оказывается следующая методика, которая может быть применена к переводу названия приложения: вместо того, чтобы всякий раз набивать строки с именем приложения и вынуждать переводчика переводить их для каждого из контекстов, в котором они используются, более удобным будет определить его в виде макроса APPNAME, поместить макрос в заголовочный файл и использовать его по мере необходимости:
#define APPNAME MainWindow::tr("OpenDrawer 2D")До сих пор, в качестве контекста мы рассматривали имя класса. Это довольно удобно, поскольку в большинстве случаев мы можем не указывать контекст перевода явно, при вызове функции tr(). Но более универсальный способ подготовки строк к переводу состоит в использовании функции QApplication:: translate(), которая принимает три аргумента: контекст, исходный текст и необязательный комментарий. Например, еще один способ определения макроса APPNAME:
#define APPNAME qApp->translate("Global Stuff", "OpenDrawer 2D")На этот раз текст помещен в контекст "Global Stuff".
Функции tr() и translate() имеют двойное назначение: они служат маркерами для утилиты lupdate и в то же самое время -- это обычные функции C++, которые выполняют перевод текста. Такая двойственность накладывает некоторые ограничения на то, как записывается исходный код. Например, следующий отрывок не будет выполнять перевод строки на другой язык:
// НЕВЕРНО const char *appName = "OpenDrawer 2D"; QString translated = tr(appName);Проблема состоит в том, что lupdate не сможет отыскать строку "OpenDrawer 2D", поскольку она явно не передается функции tr(). Эта проблема очень часто проявляется при работе с динамическими строками:
// НЕВЕРНО statusBar()->message(tr("Host " + hostName + " found"));Здесь строка изменяется динамически, в зависимости от значения переменной hostName, таким образом мы не можем требовать от tr() корректного перевода.
Как одно из решений проблемы -- используйте QString::arg():
statusBar()->message(tr("Host %1 found").arg(hostName));Остановимся в этом месте чуть подробнее: функции tr() передается строка символов "Host %1 found". Допустим, что приложение загрузило файл с русским переводом, тогда функция tr() должна вернуть примерно такую строку: "Обнаружен узел сети %1". После этого аргумент '%1' замещается содержимым переменной hostName. В результате мы получаем вполне корректный перевод сообщения, которое демонстрируется русскоговорящему пользователю.
В случае, когда необходимо записать перевод строки в переменную, следует использовать макрос QT_TR_NOOP(). Чаще всего этот прием используется при создании статических массивов строк, например:
void OrderForm::init() { static const char * const flowers[] = { QT_TR_NOOP("Medium Stem Pink Roses"), QT_TR_NOOP("One Dozen Boxed Roses"), QT_TR_NOOP("Calypso Orchid"), QT_TR_NOOP("Dried Red Rose Bouquet"), QT_TR_NOOP("Mixed Peonies Bouquet"), 0 }; int i = 0; while (flowers[i]) { comboBox->insertItem(tr(flowers[i])); ++i; } }Макрос QT_TR_NOOP() фактически ничего не делает, но он служит маркером для lupdate. Строки, передаваемые этому макросу попадут в файл перевода и затем tr() переведет содержимое переменной обычным образом. Как видите, даже не смотря на то, что функции tr() передается не текст, а переменная, перевод будет выполнен корректно.
Есть еще один макрос -- QT_TRANSLATE_NOOP(), который похож на QT_TR_NOOP(), только в отличие от последнего, ему можно задать контекст перевода. Этот макрос найдет применение, когда необходимо инициализировать переменные за пределами класса:
static const char * const flowers[] = { QT_TRANSLATE_NOOP("OrderForm", "Medium Stem Pink Roses"), QT_TRANSLATE_NOOP("OrderForm", "One Dozen Boxed Roses"), QT_TRANSLATE_NOOP("OrderForm", "Calypso Orchid"), QT_TRANSLATE_NOOP("OrderForm", "Dried Red Rose Bouquet"), QT_TRANSLATE_NOOP("OrderForm", "Mixed Peonies Bouquet"), 0 };причем контекст должен совпадать с контекстом вызова функции tr(), которая будет выполнять перевод этих строк.
При использовании tr() в приложении не так уж и сложно забыть заключить какие нибудь строки в вызов этой функции, особенно если вы еще новичок. Эти досадные промахи будут проявляться в локализованных приложениях в виде непереведенных сообщений или надписей, вызывая чувство недовольства у пользователя. Чтобы избежать этой проблемы, мы можем запретить неявное преобразование из const char * в QString, определив символ препроцессора QT_NO_CAST_ASCII, перед директивой подключения заголовочного файла <qstring.h>. Самый простой способ определить этот символ -- поместить в файл .pro следующую строку:
DEFINES += QT_NO_CAST_ASCIIВ результате, каждая строка, которая не пропускается через вызов tr() или QString:: fromAscii() (в зависимости от того, должна строка подвергаться переводу или нет), будет вызывать ошибку времени компиляции.
После того, как все строки будут "завернуты" в вызовы tr(), остается соблюсти еще одно важное условие -- загрузить на запуске файл с переводом. Обычно это делается в функции main(). Например, следующий код загрузит файл с переводом, с учетом региональных настроек пользователя:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTranslator appTranslator; appTranslator.load(QString("app_") + QTextCodec::locale(), qApp->applicationDirPath()); app.installTranslator(&appTranslator); ... return app.exec(); }Функция QTextCodec::locale() возвращает строку -- имя локали пользователя, запустившего приложение. Локаль может быть определена более или менее точно, например, ru определяет русскую локаль, ru_RU -- русскую локаль для России, ru_RU.KOI8-R -- русскую локаль для России, с кодировкой символов KOI8-R.
Предположим, что приложение получило строку с именем локали -- ru_RU.KOI8-R, тогда load() попытается сначала загрузить файл app_ru_RU.KOI8-R.qm. Если этот файл отсутствует, то load() попытается загрузить файл app_ru_RU.qm, затем app_ru.qm и наконец app.qm. Обычно, в таких случаях достаточно создать один файл, с именем app_ru.qm. Однако, если перевод предполагает более точный учет региональных настроек, как например в случае fr_FR (французский язык для Франции) и fr_CA (французский язык для Канады), то может потребоваться создать отдельные файлы с переводом для каждого из регионов.
Второй аргумент функции load() -- это каталог, где находится файл с переводом. Компания Trolltech предоставляет файлы с французским и немецким переводами Qt в каталоге translations. (Переводы на некоторые другие языки так же могут поставляться вместе с библиотекой, но все они выполняются командами добровольцев и официально не поддерживаются.) Так же должен подгружаться библиотечный файл с переводом:
QTranslator qtTranslator; qtTranslator.load(QString("qt_") + QTextCodec::locale(), qApp->applicationDirPath()); app.installTranslator(&qtTranslator);Экземпляр класса QTranslator может хранить только один файл с переводом, поэтому следует использовать различные QTranslator. Но это не является большой проблемой, так как мы можем создать столько экземпляров класса QTranslator, сколько потребуется. Все они будут использоваться приложением при поиске перевода.
В некоторых языках, таких как арабский и иврит, строки пишутся справа-налево. В этих случаях приложению необходимо сообщить о порядке вывода строк вызовом QApplication::setReverseLayout(true). Для таких языков, файл перевода должен содержать специальный маркер -- "LTR", который обеспечивает корректный вывод переведенных строк.
Для пользователей программы может оказаться более удобным вариант, когда файлы перевода внедряются в тело исполняемого файла программы. Мало того, что этот прием уменьшает количество файлов, которые придется распространять вместе сприложением, но это так же сведет к минимуму риск случайной потери файлов с переводами. Для реализации этой возможности, в составе Qt распространяется утилита qembed, которая преобразует файлы с переводами в массивы C++, которые могут передаваться функции QTranslator::load().
Мы описали все, что необходимо сделать, чтобы подготовить приложение к интернационализации. Но язык и направление письма это еще не все, что отличает страны и культуры. Интернационализированная программа должна принимать во внимание формат представления даты, времени, национальной валюты, чисел и порядок сортировки строк. Для этого в Qt 3.2 не существует никаких специальных функций, но мы можем использовать стандартные функции setlocale() и localeconv(). [1]
Некоторые функции и классы Qt адаптируют свое поведение под настройки локали:
Функция QString::localeAwareCompare() выполняет сравнение строк в зависимости от настроек локали. Она используется классами QIconView и QListView для выполнения сортировки своих элементов.
Функция toString() используется классами QDate, QTime и QDateTime, возвращающими локализованное представление даты и времени, когда вызываются с аргументом Qt::LocalDate.
По-умолчанию QDateEdit, QTimeEdit и QDateTimeEdit представляют дату и время в локализованном виде.
if (QApplication::reverseLayout()) { backAct->setIconSet(forwardIcon); forwardAct->setIconSet(backIcon); } else { backAct->setIconSet(backIcon); forwardAct->setIconSet(forwardIcon); }Иконки, изображение на которых соответствует алфавитным символам, очень часто должны быть адаптированы, в соответствии с конкретными языковыми настройками. Например, в текстовых процессорах, иконка с изображением символа "I" (что означает "Italic" -- Курсив) должна быть заменена на "C" для Испании (Cursivo) или на "K" -- для России (Курсив). Самый простой способ:
if (tr("Italic")[0] == 'C') { italicAct->setIconSet(iconC); } else if (tr("Italic")[0] == 'K') { italicAct->setIconSet(iconK); } else { italicAct->setIconSet(iconI); }
[1] |
Вероятно, в состав Qt 3.3 будет включен класс QLocale, который будет обслуживать представление числовых форматов. |
Пред. | В начало | След. |
Интернационализация | На уровень выше | Динамическое переключение языков. |
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |