J.A.Norton
J.A.Norton

Reputation: 115

Is it possible to dynamically translate Qt applications without code duplication?

I integrated the QTranslator class in my Project. So far everything works and on restart of the program all text fields are translated. Now I would like to provide dynamic translation, so the users don't need to restart the application.

What I found in my research is that its necessary to reimplement the changeEvent() function like this:

void MyWidget::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::LanguageChange) {
        titleLabel->setText(tr("Document Title"));
        ...
        okPushButton->setText(tr("&OK"));
    } else
        QWidget::changeEvent(event);
}

(Source: http://doc.qt.io/qt-5/internationalization.html#dynamic-translation)

For applications written with Qt designer it seems like its possible to just call

ui->retranslateUi(this);

within the changeEvent() function and all text fields will be translated. But for all other texts in the application the text must be set as in the example above. Which I find painful because I always need to update the text at two places when I change something (in the changeEvent function and in the main part of my program). With a lot of text fields it could easily happen to miss something.

Is there a way to update these text fields without the need to duplicate the "text-setting-methods"?

Upvotes: 3

Views: 1653

Answers (2)

kefir500
kefir500

Reputation: 4404

I'm not sure why would you need to duplicate the text setters.

The main idea is to set the translatable texts once in the changeEvent() handler and manually send the LanguageChange event as stated in the docs. This will also fire the event for the child widgets.

MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
    titleLabel = new QLabel(this);
    okPushButton = new QPushButton(this);
    // Fire the LanguageChange event - the event handler will set the texts:
    QEvent languageChangeEvent(QEvent::LanguageChange);
    QCoreApplication::sendEvent(this, &languageChangeEvent);
}

void MyWidget::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::LanguageChange) {
        titleLabel->setText(tr("Document Title"));
        okPushButton->setText(tr("&OK"));
    } else {
        QWidget::changeEvent(event);
    }
}

You can also use some initial QCoreApplication::installTranslator(). This will fire the LanguageChange event and there will be no need to manually post it.


Another approach is to use your own functions instead of events. This approach is generally the same except that you need to call your function manually for child widgets.

MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
    titleLabel = new QLabel(this);
    okPushButton = new QPushButton(this);
    myChildWidget = new MyChildWidget(this);
    retranslate();
}

void MyWidget::retranslate()
{
    titleLabel->setText(tr("Document Title"));
    okPushButton->setText(tr("&OK"));
    myChildWidget->retranslate();
}

Upvotes: 2

Caleth
Caleth

Reputation: 62684

In the general case, no, you can't avoid this.

One option is rather than having application code directly setting text, connect a signal to a lambda setting the text, then fire the signal. The event handler then just needs to fire the signal too.

An example:

MyWidget::someCalculation() {
    // Some stuff
    disconnect(this, &MyWidget::updateText, button, Q_NULLPTR);
    connect(this, &MyWidget::updateText, button, [someLocalString](){ button->setText(tr("Button Text %1").arg(someLocalString)); });
    // More stuff
    emit updateText();
}

// Other methods

void MyWidget::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::LanguageChange) {
        emit updateText();
    } else {
        QWidget::changeEvent(event);
    }
}

Upvotes: 1

Related Questions