Интернационализация с Qt
Интернационализация приложения это процесс создания приложения удобным для людей в странах отличных от вашей.
В некоторых случаях интернационализация это просто, например, перевод приложения написанного в США для пользователей Англии или Австралии требует чуть больше чем несколько поправок орфографии. Но чтобы сделать приложение написанное в США полезным для пользователей в Японии или корейское приложение для немецких пользователей, потребуется не только что бы приложение работало на другом языке, но и использовало другие техники ввода, кодирования символов и соглашения о представлении.
Qt пытается сделать интернационализацию на столько безболезненной, для разработчиков, насколько это возможно. Все виджеты ввода и методы отрисовки текста в Qt предлагают встроенную поддержку для всех поддерживаемых языков. Встроенный механизм шрифтов в состоянии корректно и привлекательно отрисовывать текст, который содержит символы из множества разнообразных систем письменности в одно и то же время.
Qt поддерживает большинство языков, используемых сегодня, в частности:
- Все языки восточной Азии (китайский, японский и корейский)
- Все западные языки (использующие латинскую письменность)
- Арабский
- Кириллические языки (русский, украинский, и т.д.)
- Греческий
- Иврит
- Тайский и лаосский
- Все символы в Unicode 4.0, которые не требуют специальной обработки
В Windows, Unix/X11 с FontConfig (поддержка шрифтов с клиентской стороны) и в Qt для Embedded Linux также поддерживаются следующие языки:
- Бенгальский
- Индийский
- Мальдивский (тана)
- Гуджарати
- Гурмукхи
- Каннада
- Кхмерский
- Малайялам
- Мьянма
- Сирийский
- Тамильский
- Телугу
- Тибетский
Многие из этих систем письменности имеют особые свойства:
- Особое поведение разрыва линий. Некоторые азиатские языки пишутся без пробелов между словами. Разрыв строки может случиться или после каждого символа (с исключениями) как в китайском, японском и корейском, или после логических границ слова, например, как в тайском.
- Двунаправленное письмо. Арабский и Иврит пишутся справа налево, кроме чисел и вставленного английского текста, которые пишутся с лева на право. Точное поведение определено в Техническом приложении Unicode #9.
- Безпробельные или диактрические метки (акценты или умляуты в европейских языках). Некоторые языки, такие как вьетнамский, дают расширенное использование этим меткам и некоторые символы могут иметь более одной метки одновременно для уточнения произношения.
- Лигатуры. В особых контекстах некоторые пары символов заменяются комбинированным знаком, формируя лигатуру. Простой пример - fl и fi лигатуры, используемые в наборе текста в книгах США и Европы.
Qt пытается позаботится о всех специальных особенностях, перечисленных выше. Обычно не приходится беспокоиться об этих особенностях пока вы используете виджеты ввода Qt (например, QLineEdit, QTextEdit и наследуемые классы) и виджеты отображения Qt (например, QLabel).
Поддержка для этих систем письменности прозрачна для программиста и полностью инкапсулирована в текстовый механизм Qt. Это значит что вам не требуется иметь каких-либо знаний о системе письменности, используемой в конкретном языке, кроме следующих небольших замечаний:
- QPainter::drawText(int x, int y, const QString &str) всегда рисует строку с левым краем в позиции, указанной параметрами x и y. Это обычно дает строки, выровненные по левому краю. Приложения на арабском языке и на иврите обычно выровнены по правому краю, поэтому для таких программ используют версию drawText(), которая принимает QRect, поэтому выравнивание происходит в соответствии с языком.
- Когда вы пишете собственный элемент для ввода текста, используйте QFontMetrics::charWidth() для определения ширины символов в строке. В некоторых языках, (например, в арабском или в языках индонезийского полуострова), ширина и форма знака меняется в зависимости от окружающих символов. Написание объектов для ввода обычно требует определенные знания о текстах, для которых он будет использован. Обычно простой путь это наследовать QLineEdit или QTextEdit.
Следующая секция дает некоторую информацию о статусе поддержки интернационализации (i18n) в Qt. Смотрите также: Руководство по Qt Linguist.
Шаг за шагом
Написание кросс-платформенных интернациональных программ с Qt это мягкий, последовательный процесс. Ваше программное обеспечение может стать международным в следующих стадиях:
Использование QString для всех текстов, видимых пользователю
С тех пор как QString использует внутри кодирование в Unicode 4.0, любой язык в мире может быть обработан прозрачно используя общие операции обработки текста. Также, с тех пор как все функции Qt, которые представляют текст пользователю, принимают QString в качестве параметра, больше нет дополнительных преобразований char * в QString.
Строки, которые в "пространстве программиста" (такие как имена QObject и тексты форматов файлов), не требуют использования QString; традиционный char * или класс QByteArray будут достаточны.
Вы скорее всего не заметили что используете Unicode; QString, и QChar просто упрощенные версии необработанных const char * и char из традиционного C.
Использование tr() для всех константных текстов
Где бы ваша программа не использовала "текст в кавычках" для текста, который будет отображен пользователю, убедитесь что он обрабатывается функцией QCoreApplication::translate(). Все что для этого необходимо - использовать QObject::tr(). Например, предположим что LoginWidget это подкласс QWidget:
LoginWidget::LoginWidget()
{
QLabel *label = new QLabel(tr("Password:"));
...
}
Это справедливо для 99% строк видимых пользователю которые вы захотите написать.
Если текст в кавычках не находится в функции-члене подкласса QObject, используйте функцию tr() подходящего класса или функцию QCoreApplication::translate() напрямую:
void some_global_function(LoginWidget *logwid)
{
QLabel *label = new QLabel(
LoginWidget::tr("Password:"), logwid);
}
void same_global_function(LoginWidget *logwid)
{
QLabel *label = new QLabel(
qApp->translate("LoginWidget", "Password:"), logwid);
}
Если вам потребуется перевести текст, который находится полностью вне функции, есть два макроса для помощи: QT_TR_NOOP() и QT_TRANSLATE_NOOP(). Они незаметно помечают текст для извлечения утилитой lupdate, описанной ниже. Макрос распространяется только на текст (без контекста).
Пример QT_TR_NOOP():
QString FriendlyConversation::greeting(int type)
{
static const char *greeting_strings[] = {
QT_TR_NOOP("Hello"),
QT_TR_NOOP("Goodbye")
};
return tr(greeting_strings[type]);
}
Пример QT_TRANSLATE_NOOP():
static const char *greeting_strings[] = {
QT_TRANSLATE_NOOP("FriendlyConversation", "Hello"),
QT_TRANSLATE_NOOP("FriendlyConversation", "Goodbye")
};
QString FriendlyConversation::greeting(int type)
{
return tr(greeting_strings[type]);
}
QString global_greeting(int type)
{
return qApp->translate("FriendlyConversation",
greeting_strings[type]);
}
Если вы отключите автоматическое преобразование из const char * в QString путем компиляции вашей программы с определенным макросом QT_NO_CAST_FROM_ASCII, вы скорее всего найдете все строки, которые пропустили. Для получения более подробной информации смотрите QString::fromLatin1(). Отключение преобразования может сделать программирование слегка затруднительнее.
Если ваш исходный язык использует символы вне таблицы символов Latin1, вы можете найти QObject::trUtf8() более удобным чем QObject::tr(), так как tr() зависит от QTextCodec::codecForTr(), что делает его более "хрупким" чем QObject::trUtf8().
Использование QKeySequence() для горячих клавиш
Горячие клавиши, такие как Ctrl+Q или Alt+F, должны быть также переведены. Если вы жестко зададите Qt::CTRL + Qt::Key_Q для "выхода" в кашей программе, переводчики не смогут переназначить его. Правильный подход это
exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcut(tr("Ctrl+Q"));
Использование QString::arg() для динамического текста
Функции QString::arg() предлагают простое толкование для подстановочных аргументов:
void FileCopier::showProgress(int done, int total,
const QString ¤tFile)
{
label.setText(tr("%1 of %2 files copied.\nCopying: %3")
.arg(done)
.arg(total)
.arg(currentFile));
}
В некоторых языках может потребоваться изменить порядок аргументов, и это может быть легко достигнуто изменением порядка переменных с %. Например:
QString s1 = "%1 of %2 files copied. Copying: %3";
QString s2 = "Kopierer nu %3. Av totalt %2 filer er %1 kopiert.";
qDebug() << s1.arg(5).arg(10).arg("somefile.txt");
qDebug() << s2.arg(5).arg(10).arg("somefile.txt");
производит корректный вывод текста на английском и норвежском:
5 of 10 files copied. Copying: somefile.txt
Kopierer nu somefile.txt. Av totalt 10 filer er 5 kopiert.
Создание переводов
После того как вы использовали tr() по всему приложению, вы можете создавать перевод текста видимого пользователю вашей программы.
Справочное руководство Qt Linguist предоставляет дополнительную информацию об инструментах перевода Qt, Qt Linguist, lupdate и lrelease.
Перевод приложения Qt содержит 3 шага:
- Запуск lupdate для извлечения переводимого текста из исходного кода приложения Qt на C++ , создавая файл сообщений для переводчиков (файл .ts). Утилита распознает конструкторы tr() и макросы QT_TR*_NOOP(), описанные выше, и производит файлы .ts (обычно один на каждый язык).
- Обеспечение переводов для исходных текстов в файле .ts, используя Qt Linguist. Так как файлы .ts в формате XML, вы можете также отредактировать их вручную.
- Запуск lrelease для получения легкого файла сообщений (файл .qm) из файла .ts, удобного только для конечного пользования. Думайте о файлах .ts как об "исходных файлах", и о файлах .qm как об "объектных файлах". Переводчик редактирует файлы .ts, но пользователям вашего приложения требуются только файлы .qm. Оба типа файлов не зависят от платформы и локали.
Обычно вы будете повторять эти шаги для каждого выпуска приложения. Утилита lupdate делает все возможное по повторному использованию переводов от предыдущих релизов.
Перед запуском lupdate, вам потребуется подготовить файл проектов. Вот небольшой пример файла проекта (файл .pro):
HEADERS = funnydialog.h \
wackywidget.h
SOURCES = funnydialog.cpp \
main.cpp \
wackywidget.cpp
FORMS = fancybox.ui
TRANSLATIONS = superapp_dk.ts \
superapp_fi.ts \
superapp_no.ts \
superapp_se.ts
Когда вы запускаете lupdate или lrelease, вы должны предоставить имя файла проекта в качестве аргумента командной строки.
В данном примере поддерживаются четыре экзотических языка: датский, финский, норвежский и шведский. Если вы используете qmake, вам обычно не нужен дополнительный файл проекта для lupdate; ваш файл проекта qmake будет прекрасно работать после того как вы добавите запись TRANSLATIONS.
В вашем приложении вы должны загрузить QTranslator::load() файлы используя QCoreApplication::installTranslator().
linguist, lupdate и lrelease установлены в поддиректории bin базовой директории, в которой установлен Qt. Щелкните на Help|Manual в Qt Linguist для доступа к руководству пользователя; он содержит учебное пособие для начала работы.
Qt содержит внутри около 400 строк, которые так же должны быть переведены на языки которые вам необходимы. Вы найдете файлы переводов для французского, немецкого и упрощенного китайского в директории $QTDIR/translations, так же как и шаблоны для перевода на другие языки. (Эта директория так же содержит некоторые дополнительные неподдерживаемые переводы, которые могут быть полезны).
Обычно, функция main() вашего приложения будет выглядеть примерно так:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&qtTranslator);
QTranslator myappTranslator;
myappTranslator.load("myapp_" + QLocale::system().name());
app.installTranslator(&myappTranslator);
...
return app.exec();
}
Обратите внимание на использование QLibraryInfo::location() для обнаружения переводов Qt. Разработчик должен запросить путь к переводам во время выполнения обрабатывая QLibraryInfo::TranslationsPath для этой функции вместо использования переменной среды QTDIR в своих приложениях.
Поддержка для кодировок
Класс QTextCodec и его возможности в QTextStream делают поддержку множества входных и выходных кодировок пользовательских данных легкой. Когда приложение запущено, локаль машины определит 8-ми битную кодировку используемую для работы с 8-ми битными данными: например, для выбора шрифта, отображения текста, ввода-вывода 8-ми битного текста и ввода символов.
Приложению может внезапно потребоваться кодировка, отличная от 8-ми битной локальной кодировки по умолчанию. Например, приложение в кириллической локали KOI8-R (стандартная де-факто локаль в России) может потребовать вывод кириллицы в кодировке ISO 8859-5. Код может быть следующим:
QString string = ...; // некоторый текст в Unicode
QTextCodec *codec = QTextCodec::codecForName("ISO 8859-5");
QByteArray encodedString = codec->fromUnicode(string);
Для преобразования из Unicode в локальную 8-ми битную кодировку, доступен короткий путь: функция QString::toLocal8Bit() возвращает такие 8-ми битные данные. Другой полезный короткий путь это QString::toUtf8(), который возвращает текст из 8-ми битной кодировки в UTF-8: она идеально сохраняет информацию в Unicode и в то же время выглядит как простой текст в ASCII если весь текст полностью в ASCII.
Для преобразования в другую сторону есть QString::fromUtf8() и QString::fromLocal8Bit() вспомогательные функции, или основной код, продемонстрированный конвертацией из ISO 8859-5 в Unicode:
QByteArray encodedString = ...; // некоторый текст, закодированный в ISO 8859-5
QTextCodec *codec = QTextCodec::codecForName("ISO 8859-5");
QString string = codec->toUnicode(encodedString);
В идеале должен быть использован ввод-вывод Unicode, так как это увеличивает переносимость документов между пользователями по всему миру, но в реальности удобно поддерживать все подходящие кодировоки, которыми вашим пользователям может потребоваться обработать существующие документы. В общем, Unicode (UTF-16 или UTF-8) лучше для обменом информацией между различными людьми, в то время как внутри групп с одним языком или одной национальности локальный стандарт более подходящий. Наиболее важная кодировка для поддержки это та, которая возвращается функцией QTextCodec::codecForLocale(), так как это та кодировка, которая вероятнее всего необходима пользователю для общения с другими людьми и приложениями (этот кодек используется local8Bit()).
Qt наитивно поддерживает большинство из наиболее часто используемых кодировок. Для полного списка поддерживаемых кодировок смотрите документацию по QTextCodec.
В некоторых случаях и для менее часто используемых кодировок может потребоваться написать собственный дочерний класс от QTextCodec. В зависимости от срочности, может быть полезно связаться с командой технической поддержки Qt или спросить в списке рассылки qt-interest для поиска кого-нибудь еще, кто уже работает над поддержкой этой кодировки.
Локализация
Локализация это процесс по адаптированию к местным соглашениям, например, представление дат и времен используя местные предпочитаемые форматы. Такая локализация может быть сделана использованием подходящих строк tr().
void Clock::setTime(const QTime &time)
{
if (tr("AMPM") == "AMPM") {
// 12-часовые часы
} else {
// 24-часовые часы
}
}
В этом примере, для США можем оставить перевод "AMPM" как есть и использовать 12-часовую ветвь; Но в Европе мы переведем ее во что-нибудь еще и это заставит код использовать 24-часовую ветку.
Для локализации чисел используйте класс QLocale.
Локализация изображений не рекомендуется. Выберите ясные иконки, которые подходят для всех локализаций, вместо того что бы полагаться на местные каламбуры или метафоры. Исключение только для изображений стрелок, указывающих налево и направо, которые может потребоваться перевернуть для арабской и ивритской локалей.
Динамический перевод
Некоторые приложения, такие как Qt Linguist, должны обеспечивать изменения настроек языка пользователя во время работы. Что бы предупредить виджеты об изменениях установленного QTranslators, переопределите функцию виджета changeEvent() для проверки не является ли событие событием LanguageChange, и обновите текст, отображаемый виджетами, используя функцию tr() обычным способом. Например:
void QWidget::changeEvent(QEvent *event)
{
if (e->type() == QEvent::LanguageChange) {
titleLabel->setText(tr("Document Title"));
...
okPushButton->setText(tr("&OK"));
} else
QWidget::changeEvent(event);
}
Все остальные события изменения должны быть обработаны вызовом реализации по умолчанию данной функции.
Список установленных переводов может быть изменен в реакции на событие LocaleChange, или приложение может предоставлять интерфейс пользователю, который позволит ему изменить текущий язык приложения.
Обработчик событий по умолчанию для подклассов QWidget отвечает на событие QEvent::LanguageChange и вызовет эту функцию при необходимости; в других компонентах приложения можно так же заставить виджеты обновить себя отправив им событие LanguageChange.
Перевод классов, не входящих в Qt
Иногда необходимо обеспечить поддержку интернационализации для строк, используемых в классах, которые не наследуют QObject или не используют макрос Q_OBJECT для включения функций перевода. Так как Qt переводит строки во время выполнения основываясь на классах, с которыми они связаны, и lupdate ищет переводимые строки в исходном коде, классы, не входящие в Qt, должны использовать механизм, который так же обеспечивает данную информацию.
Один способ сделать это - добавить поддержку перевода для класса, не входящего в Qt, используя макрос Q_DECLARE_TR_FUNCTIONS(); например:
class MyClass
{
Q_DECLARE_TR_FUNCTIONS(MyClass)
public:
MyClass();
...
};
Это обеспечивает класс функцией tr(), которая может быть использована для перевода строк, связанных с классом, и делает возможным для lupdate найти переводимые строки в исходном коде.
Также, может быть вызвана с особым контекстом функция QCoreApplication::translate() и это будет распознано lupdate и Qt Linguist.
Системная поддержка
Некоторые операционные и оконные системы, на которых работает Qt, имеют ограниченную поддержку Unicode. Уровень поддержки, доступный в нижестоящей системе, влияет на поддержку, которую Qt может обеспечить на этих платформах, хотя в обычных программах на Qt не надо слишком беспокоиться о платформо-зависимых ограничениях.
Unix/X11
- Шрифты, ориентированные на локаль, и методы ввода. Qt прячет это и обеспечивает ввод и вывод на Unicode.
- Соглашения о файловых системах, такие как UTF-8 находятся в состоянии разработки в некоторых вариантах Unix. Все функции Qt для работы файлов позволяют Unicode, но преобразуют имена файлов в локальную 8-ми битную кодировку, так как это принято в Unix (смотри QFile::setEncodingFunction() для просмотра альтернативных кодировок).
- Ввод-вывод файла по умолчанию в локальной 8-ми битной кодировке, с возможностью вывода в Unicode в QTextStream.
- Многие дистрибутивы Unix содержат только частичную поддержку для некоторых локалей. Например, если у вас есть каталог /usr/share/locale/ja_JP.EUC то, это не означает, что вы можете отображать японский текст; вам так же необходимы шрифты в кодировке JIS (или шрифты Unicode), и нужен каталог /usr/share/locale/ja_JP.EUC для полного комплекта. Для лучших результатов, используйте полные локали от вашего поставщика системы.
Windows
- Qt обеспечивает полную поддержку Unicode, включая методы ввода, шрифты, буфер обмена, перетаскивания (drag-and-drop) и имена файлов.
- Ввод-вывод файла по умолчанию в кодировке Latin1, с возможностью вывода в Unicode в QTextStream. Заметьте, что некоторые программы в Windows не понимают текстовые файлы в big-endian Unicode, хотя этот порядок описан стандартом Unicode в отсутствии высокоуровневых протоколов.
- В отличие от программ написанных с MFC или простой winlib, программы на Qt переносимы между Windows 98 и Windows NT. Вам не потребуется иметь разные бинарные файлы для поддержки Unicode.
Mac OS X
За подробностями перевода, специфичными для Mac, обратитесь к документу "Проблемы, специфичные для Qt/Mac" здесь.
Связанные классы
Эти классы важны для интернационализации приложений на Qt.
QInputContext | Абстрактный метод ввода, зависящий от данных и состояния |
QLocale | Конвертирует числа в их строковые представления на различных языках |
QSystemLocale | Может быть использовать в регулировании локализаций системных выражений |
QTextCodec | Преобразования текста в различные кодировки |
QTextDecoder | Декодер, поддерживающий различные состояния |
QTextEncoder | Кодировщик, поддерживающий различные состояния |
QTranslator | Поддержка интернационализации выводимого текста |
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) |
Торговые марки |
Qt 4.5.3 |
|