IT Notes

QTranslator: Интернационализация в Qt

Разработаем приложение, которое поддерживает два языка: русский и английский. Чтобы не усложнять задачу, составим его из двух окон. Первое позволит выбрать локаль и ввести имя пользователя. А второе всего лишь отобразит приветствие на основе указанного имени. Получится примерно следующее:

qt-i10n-thumbnail

Даем пользователю выбор

Начнем с окна выбора локали и имени пользователя. Мы уже подробно рассматривали создание диалоговых окон в Qt, поэтому в этот раз обойдемся без лишних рассуждений. Файл introdialog.h:

#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:

#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();
}

Обратим внимание на две строки из фрагмента выше:

…
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 (английская локаль):

m_localeCmb->addItems( QStringList() << "en_US" << "ru_RU" );

Логичным допущением становится то, что по умолчанию выбирается местная системная локаль, которую мы узнаем с помощью QLocale::system().name():

int index = m_localeCmb->findText( QLocale::system().name() );
if( index != -1 ) {
    m_localeCmb->setCurrentIndex( index );
}

Инициализация перевода

Теперь займемся файлом main.cpp:

#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, то загрузка файла с переводом прошла успешна, и мы можем установить его для приложения:

if( translator.load( generateTrFileName( SYSTEM_LOCALE ) ), "." ) {
    a.installTranslator( &translator );
}

Когда пользователь выбирает в диалоговом окне локаль и нажимает OK, мы повторно ее загружаем в уже существующий экземпляр translator'а:

translator.load( generateTrFileName( w.getLocale() ), "." );

Далее мы просто создаем и отображаем QLabel с приветственной надписью. Вновь с использованием QObject::trUtf8().

Приложение уже можно запустить, но поскольку у нас нет переводов, то все надписи будут на английском. Что можно считать вполне уместным поведением по умолчанию. Но нас такой вариант не устраивает.

Готовим перевод

Для добавления переводов подправим pro-файл I18nDemo.pro:

#-------------------------------------------------
#
# 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-файлов с переводом. Мы определили всего один: для русского языка. Аналогично можно расширить список на произвольное количество переводов:

TRANSLATIONS = i18ndemo_ru.ts \
               i18ndemo_fr.ts \
               i18ndemo_de.ts

Далее необходимо выполнить в консоли команду из каталога проекта:

lupdate I18nDemo.pro

В результате появится файл: i18ndemo_ru.ts. Это обычный текстовый файл в формате XML. Его можно редактировать в любом текстовом редакторе или с помощью специального приложения Qt Linguist. Воспользуемся вторым вариантом при подготовке русского перевода. Интерфейс приложения достаточно очевиден, поэтому обойдемся без комментариев:

qt-linguist-thumbnail

После сохранения содержимое файла i18ndemo_ru.ts станет примерно таким:

<?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, &lt;strong&gt;%1&lt;/strong&gt;!</source>
        <translation>Здравствуйте, &lt;strong&gt;%1&lt;/strong&gt;!</translation>
    </message>
</context>
</TS>

Но и это еще не все. После завершения работы над переводом нужно его скомпилировать. Это можно сделать в консоли с помощью следующей команды:

lrelease I18nDemo.pro

После этого появится соответствующий бинарный файл i18ndemo_ru.qm. Его нужно поместить в каталог с исполняемым файлом нашего проекта. Вот теперь можно считать, что работа окончена. Перевод успешно подключен и будет использоваться в приложении.

Несколько замечаний

  1. Поиск файла перевода довольно интеллектуален. Например, при вызове QTranslator::load() мы указываем имя i18ndemo_ru_RU, а находится i18ndemo_ru.qm. Это связано с тем, что у языков бывают диалекты. В частности, английский может быть американским en_US или британским en_GB. Если не получится найти перевод для project_name_en_GB, то QTranslator попытается загрузить просто project_name_en;
  2. Имеет смысл задуматься о помещении переводов в файл-ресурсов;
  3. Выбранный язык нужно хранить в конфигурационном файле, чтобы пользователю не приходилось выбирать его каждый раз при запуске приложения. Системную локаль в этом вопросе можно рассматривать лишь в качестве отправной точки.

Исходники

 Скачать исходники с примером многоязычного Qt-приложения

Похожие публикации