能否在不重复编写代码的情况下动态翻译Qt应用程序?

3
我在我的项目中集成了QTranslator类。到目前为止,一切都正常,重启程序后所有文本字段都已翻译。现在我想提供动态翻译,这样用户就不需要重新启动应用程序。
根据我的研究,必须这样重新实现changeEvent()函数:
void MyWidget::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::LanguageChange) {
        titleLabel->setText(tr("Document Title"));
        ...
        okPushButton->setText(tr("&OK"));
    } else
        QWidget::changeEvent(event);
}

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

对于使用Qt Designer编写的应用程序,似乎可以直接调用

ui->retranslateUi(this);

changeEvent()函数内所有文本字段都将被翻译。 但对于应用程序中的所有其他文本,文本必须设置为上面的示例。这让我感到痛苦,因为当我更改某些内容时,我总是需要在两个地方更新文本(在changeEvent函数和程序的主要部分)。 对于很多文本字段来说,很容易遗漏一些。

有没有一种方法可以更新这些文本字段,而不需要复制“文本设置方法”?


1
在我的项目中,我只是创建了一些类,在这些类中需要翻译方法,比如retranslate(); 当需要重新翻译时就会调用该方法。基本上,在父类上调用retranslate,然后他会依次调用子类的retranslate方法。 - Shf
这些retranslate()方法内部会发生什么? - J.A.Norton
@J.A.Norton 这些是由Qt MOC工具生成的方法。它们相当于一长串的 widget->setText(tr("WidgetText", "WidgetContext")); 调用。 - Caleth
2个回答

2

我不确定为什么需要重复文本设置器。

主要思想是在changeEvent()处理程序中设置可翻译的文本,并手动发送LanguageChange事件,如文档中所述。这也会触发子窗口小部件的事件。

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

你也可以使用一些初始的QCoreApplication::installTranslator()。这将触发LanguageChange事件,而无需手动发布它。


另一种方法是使用自己的函数而不是事件。这种方法通常是相同的,只是你需要手动为子窗口小部件调用你的函数。

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

重复的文本设置器:因为当我浏览代码时,我希望能够立即看到该(例如)按钮上显示的文本。当然,我可以选择一个适当的变量名并添加一个包含文本的注释,但我更喜欢将所有内容放在一个地方。当按钮的目的发生变化时,我需要在changeEvent()中更改文本和变量名称(和/或注释)。但我想我只能接受它。谢谢您的回答! - J.A.Norton
1
更有趣的情况是当翻译是由于某些用户交互(例如多次按下连接字符串或从文件中读取可翻译数据)而生成时。在这些情况下,您必须能够存储当前状态并复制所采取的操作。 - cbuchart
1
此外,请注意一个已知的错误,当调用retranslateUI()方法时,会使QComboBoxes丢弃当前选择。快速解决方案类似于我的先前评论:在翻译后保存currentIndex()并重新选择,使用setCurrentIndex() - cbuchart
1
如果您的数据已排序,则在翻译后顺序将发生变化,可能会出现问题。在这种情况下,请为每个项目添加唯一ID,使用currentData()保存它,并在翻译后使用findData(...)查找相应的索引。 - cbuchart
1
更进一步,要注意连接的插槽可能在当前索引更改时被调用。 - cbuchart
1
最后,根据您的应用程序,如果您基于小部件大小做出了任何决定,则需要再次检查它们,因为按钮、标签等可能已由于更长或更短的文本而被调整大小。是的,动态翻译很棒,但可能有许多棘手的角落;)。 - cbuchart

1
一般情况下,不行,你不能避免这种情况。
一个选项是,不要让应用程序代码直接设置文本,而是将信号连接到一个设置文本的 lambda 函数,然后触发信号。事件处理程序只需要触发信号即可。
例如:
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);
    }
}

我明白了。但是这需要为每个文本字段使用一个信号。我理解得对吗? - J.A.Norton
1
不,一个信号只能连接到整个表单,每个小部件都需要连接到一个插槽。 - Caleth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接