Разработаем приложение, которое поддерживает два языка: русский и английский. Чтобы не усложнять задачу, составим его из двух окон. Первое позволит выбрать локаль и ввести имя пользователя. А второе всего лишь отобразит приветствие на основе указанного имени. Получится примерно следующее.
РЕКОМЕНДУЕМ: Обработка ошибок: Защитное программирование
Даем пользователю выбор
Начнем с окна выбора локали и имени пользователя. Мы уже подробно рассматривали создание диалоговых окон в Qt, поэтому в этот раз обойдемся без лишних рассуждений. Файл introdialog.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#ifndef INTRODIALOG_H #define INTRODIALOG_H #include <QDialog> class QComboBox; class QLineEdit; class IntroDialog : public QDialog { Q_OBJECT public: IntroDialog( QWidget* parent = 0 ); ~IntroDialog(); QString getLocale() const; QString getName() const; private: QComboBox* m_localeCmb; QLineEdit* m_nameEdit; }; #endif // INTRODIALOG_H |
Соответствующая реализация introdialog.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include "introdialog.h" #include <QGridLayout> #include <QComboBox> #include <QLabel> #include <QLineEdit> #include <QPushButton> IntroDialog::IntroDialog( QWidget* parent ) : QDialog( parent ) { QGridLayout* l = new QGridLayout; QLabel* localeLbl = new QLabel( trUtf8( "Choose locale:" ) ); localeLbl->setAlignment( Qt::AlignRight ); l->addWidget( localeLbl, 0, 0 ); m_localeCmb = new QComboBox; m_localeCmb->addItems( QStringList() << "en_US" << "ru_RU" ); int index = m_localeCmb->findText( QLocale::system().name() ); if( index != -1 ) { m_localeCmb->setCurrentIndex( index ); } l->addWidget( m_localeCmb, 0, 1 ); l->addWidget( new QLabel( trUtf8( "Enter your name:" ) ), 1, 0 ); m_nameEdit = new QLineEdit; l->addWidget( m_nameEdit, 1, 1 ); QPushButton* okBtn = new QPushButton( "OK" ); connect( okBtn, SIGNAL( clicked( bool ) ), SLOT( accept() ) ); l->addWidget( okBtn, 2, 1 ); setLayout( l ); } IntroDialog::~IntroDialog() { } QString IntroDialog::getLocale() const { return m_localeCmb->currentText(); } QString IntroDialog::getName() const { return m_nameEdit->text(); } |
Обратим внимание на две строки из фрагмента выше:
1 2 3 4 5 |
... QLabel* localeLbl = new QLabel( trUtf8( "Choose locale:" ) ); ... l->addWidget( new QLabel( trUtf8( "Enter your name:" ) ), 1, 0 ); ... |
При инициализации текста меток QLabel мы используем функцию QObject::trUtf8(). Она позволяет ввести текст по умолчанию в кодировке UTF-8. Затем мы займемся его переводом на русский. Важным моментом является то, что строки не должны инициализировать статически. Вызов tfUtf8() должен происходить после установки объекта-переводчика.
Обратите внимание, что мы поместили в комбо-бокс имена локалей ru_RU (русская локаль) и en_US (английская локаль):
1 |
m_localeCmb->addItems( QStringList() << "en_US" << "ru_RU" ); |
Логичным допущением становится то, что по умолчанию выбирается местная системная локаль, которую мы узнаем с помощью QLocale::system().name():
1 2 3 4 |
int index = m_localeCmb->findText( QLocale::system().name() ); if( index != -1 ) { m_localeCmb->setCurrentIndex( index ); } |
Инициализация перевода
Теперь займемся файлом main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include "introdialog.h" #include <QApplication> #include <QTranslator> #include <QLocale> #include <QLabel> #include <QDebug> static const QString SYSTEM_LOCALE = QLocale::system().name(); QString generateTrFileName( const QString& locale ) { return QString( "i18ndemo_%1" ).arg( locale ); } int main( int argc, char* argv[] ) { QApplication a( argc, argv ); QTranslator translator; if( translator.load( generateTrFileName( SYSTEM_LOCALE ) ), "." ) { a.installTranslator( &translator ); } IntroDialog w; if( w.exec() != QDialog::Accepted ) { return 0; } translator.load( generateTrFileName( w.getLocale() ), "." ); QLabel lbl( QObject::trUtf8( "Hello, <strong>%1</strong>!" ).arg( w.getName() ) ); lbl.setAlignment( Qt::AlignHCenter | Qt::AlignVCenter ); lbl.resize( 400, 400 ); lbl.show(); return a.exec(); } |
До отображения диалогового окна IntroDialog мы создаем объект QTranslator. Он умеет загружать файл перевода с помощью вызова load(). Первым аргументом эта функция-член принимает базовое имя файла перевода, а во втором — путь в файловой системе, где этот файл нужно искать.
Формирование базового имени файла перевода осуществляется в нашей функции generateTrFileName(). Она принимает имя локали, и возвращает строку вида i18ndemo_ru_RU.
Если QTranslator::load() возвращает true, то загрузка файла с переводом прошла успешна, и мы можем установить его для приложения:
1 2 3 |
if( translator.load( generateTrFileName( SYSTEM_LOCALE ) ), "." ) { a.installTranslator( &translator ); } |
Когда пользователь выбирает в диалоговом окне локаль и нажимает OK, мы повторно ее загружаем в уже существующий экземпляр translator’а:
1 |
translator.load( generateTrFileName( w.getLocale() ), "." ); |
Далее мы просто создаем и отображаем QLabel с приветственной надписью. Вновь с использованием QObject::trUtf8().
Приложение уже можно запустить, но поскольку у нас нет переводов, то все надписи будут на английском. Что можно считать вполне уместным поведением по умолчанию. Но нас такой вариант не устраивает.
Готовим перевод
Для добавления переводов подправим pro-файл I18nDemo.pro:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#------------------------------------------------- # # Project created by QtCreator 2016-05-09T12:29:18 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = I18nDemo TEMPLATE = app SOURCES += main.cpp\ introdialog.cpp HEADERS += introdialog.h TRANSLATIONS = i18ndemo_ru.ts |
Нас интересует переменная TRANSLATIONS, в которой указывается список ts-файлов с переводом. Мы определили всего один: для русского языка. Аналогично можно расширить список на произвольное количество переводов:
1 2 3 |
TRANSLATIONS = i18ndemo_ru.ts \ i18ndemo_fr.ts \ i18ndemo_de.ts |
Далее необходимо выполнить в консоли команду из каталога проекта:
1 |
lupdate I18nDemo.pro |
В результате появится файл: i18ndemo_ru.ts. Это обычный текстовый файл в формате XML. Его можно редактировать в любом текстовом редакторе или с помощью специального приложения Qt Linguist. Воспользуемся вторым вариантом при подготовке русского перевода. Интерфейс приложения достаточно очевиден, поэтому обойдемся без комментариев:
После сохранения содержимое файла i18ndemo_ru.ts станет примерно таким:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ru_RU"> <context> <name>IntroDialog</name> <message> <location filename="introdialog.cpp" line="14"/> <source>Choose locale:</source> <translation>Выберите локаль:</translation> </message> <message> <location filename="introdialog.cpp" line="25"/> <source>Enter your name:</source> <translation>Введите ваше имя:</translation> </message> </context> <context> <name>QObject</name> <message> <location filename="main.cpp" line="30"/> <source>Hello, <strong>%1</strong>!</source> <translation>Здравствуйте, <strong>%1</strong>!</translation> </message> </context> </TS> |
Но и это еще не все. После завершения работы над переводом нужно его скомпилировать. Это можно сделать в консоли с помощью следующей команды:
1 |
lrelease I18nDemo.pro |
После этого появится соответствующий бинарный файл i18ndemo_ru.qm. Его нужно поместить в каталог с исполняемым файлом нашего проекта. Вот теперь можно считать, что работа окончена. Перевод успешно подключен и будет использоваться в приложении.
Несколько замечаний
- Поиск файла перевода довольно интеллектуален. Например, при вызове QTranslator::load() мы указываем имя i18ndemo_ru_RU, а находится i18ndemo_ru.qm. Это связано с тем, что у языков бывают диалекты. В частности, английский может быть американским en_US или британским en_GB. Если не получится найти перевод для project_name_en_GB, то QTranslator попытается загрузить просто project_name_en;
- Имеет смысл задуматься о помещении переводов в файл-ресурсов;
- Выбранный язык нужно хранить в конфигурационном файле, чтобы пользователю не приходилось выбирать его каждый раз при запуске приложения. Системную локаль в этом вопросе можно рассматривать лишь в качестве отправной точки.
РЕКОМЕНДУЕМ: Разработка через тестирование в Qt