【问题标题】:Is it possible to dynamically translate Qt applications without code duplication?是否可以在不重复代码的情况下动态翻译 Qt 应用程序?
【发布时间】:2017-03-14 13:56:14
【问题描述】:

我在我的项目中集成了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 设计器编写的应用程序,似乎可以调用

ui->retranslateUi(this);

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

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

【问题讨论】:

  • 在我的项目中,我只是在需要翻译方法的类中创建,例如 retranslate();当需要重新翻译时会调用它。基本上是在父类上调用 retranslate,然后他又在子类上调用 retranslate 方法
  • 在这些 retranslate() 方法中会发生什么?
  • @J.A.Norton 这些是由 Qt MOC 工具生成的方法。它们相当于一长串widget->setText(tr("WidgetText", "WidgetContext")); 电话

标签: c++ qt qtranslate


【解决方案1】:

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

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

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

【讨论】:

  • 一个更有趣的场景是由于某些用户交互(例如多次按下连接字符串的按钮或从文件中读取可翻译数据)而生成了翻译。在这些情况下,您必须能够存储当前状态并复制已采取的操作。
  • 另外,请注意 existing bug 在调用 retraslateUI() 方法时使 QComboBoxes 放弃当前选择。快速解决方法类似于我之前的评论:保存currentIndex()并在翻译后使用setCurrentIndex()重新选择。
  • 如果您的数据被排序,您可能会遇到问题,因为翻译后顺序会改变。本例为每一项添加唯一ID,使用currentData()保存,翻译后使用findData(...)查找对应索引。
  • 更多,注意当前索引更改时可能调用的连接槽。
  • 最后,根据您的应用程序,如果您根据小部件的大小做出任何决定,您需要再次检查它们,因为按钮、标签等可能由于较长或较短的文本。是的,动态翻译很棒,但可能有很多棘手的角落;)。
【解决方案2】:

在一般情况下,不,您无法避免这种情况。

一种选择不是让应用程序代码直接设置文本,而是将信号连接到设置文本的 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);
    }
}

【讨论】:

  • 我明白了。但这需要每个文本字段一个信号。我理解对了吗?
  • 不,一个信号代表整个表单,连接到每个小部件的插槽
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-12
  • 1970-01-01
  • 2020-12-17
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多