使用Qt的国际化,则翻译"dynamic"内容

Translation of "dynamic" content if using Internationalization with Qt

本文关键字:翻译 dynamic 内容 Qt 国际化 使用      更新时间:2023-10-16

我目前正在评估C++框架Qt v.4.1。目前我尝试用Qt来理解和应用国际化。

我通过文章的帮助如何创建一个可以切换的多语言应用程序运行时的语言?我知道如何使用Qt语言学家翻译源(.ts)文件以及如何生成Qt-Linguist Message(.qm)文件。我使用构建系统CMake可以自动生成,效果非常好。

在我的项目中,翻译是从Qt资源集合(.qrc)加载的文件编译到应用程序中。我知道如何通过成员函数CCD_ 1和CCD_。

现在是棘手的部分:我想在应用程序发展的同时添加语言。目前我有以下两个.ts文件:

  • foo_ui_de_DE.ts
  • foo_ui_en_US.ts

这些文件通过lrelease编译为以下两个.qm文件过程:

  • foo_ui_de_DE.qm
  • foo_ui_en_US.qm

构建过程会自动生成一个.qrc文件translations.qrc通过CCD_ 9将该文件编译成可执行文件。

申报文件中的相关源代码(.h):

#include <QMainWindow>
#include <QLocale>
#include <QString>
#include <QTranslator>
class MainWindow : public QMainWindow {
  Q_OBJECT
 public:
  /**
   * Initializes a new instance of the MainWindow class with the given parent.
   *
   * @param parent The parent.
   */
  explicit MainWindow(QWidget* parent = 0);
 private slots:
  /**
   * Loads a language by the given language shortcut (e.g. `de_DE`, `en_US`).
   */
  void LoadLanguage(QLocale const& kLocale);
  void SwitchTranslator(QTranslator& translator,
                        QString const& kLocale,
                        QString const& kFilename);
  /**
   * Creates the language menu dynamically.
   */
  void CreateLanguageMenu();
 protected:
  /**
   * Handler which is activated when a new translator is loaded or the system
   * language is changed.
   */
  void changeEvent(QEvent* event);
 protected slots:
  /**
   * Slot which is called by the language menu actions.
   */
  void slotLanguageChanged(QAction* action);
 private:
  /**
   * The translations for this application.
   */
  QTranslator translator_;
  /**
   * The translations for the Qt Widgets used in this application.
   */
  QTranslator qt_translator_;
  /**
   * Contains the currently loaded locale.
   */
  QLocale locale_;
  /**
   * The main window of the application.
   */
  Ui::MainWindow* ui_;
};

定义文件(.cc)中的相关源代码:

#include <QLibraryInfo>
#include "main_window.h"
#include "ui_main_window.h"
MainWindow::MainWindow(QWidget* the_parent)
    : QMainWindow{the_parent},
      ui_{new Ui::MainWindow} {
  ui_->setupUi(this);
  CreateLanguageMenu();
}
MainWindow::~MainWindow() {
  delete ui_;
}
void MainWindow::LoadLanguage(QLocale const& kNewLocale) {
  QLocale::setDefault(kNewLocale);
  QString const kLanguageName{QLocale::languageToString(kNewLocale.language())};
  SwitchTranslator(translator_, "qt_" + kNewLocale.bcp47Name(),
                   QLibraryInfo::location(QLibraryInfo::TranslationsPath));
  SwitchTranslator(qt_translator_,
                   qApp->applicationName() + '_' + kNewLocale.name(),
                   ":/translations");
  statusBar()->showMessage(
      tr("Language changed to %1").arg(kLanguageName));
  locale_ = kNewLocale;
}
void MainWindow::SwitchTranslator(QTranslator& translator,
                                  QString const& kLocale,
                                  QString const& kFilename) {
  qApp->removeTranslator(&translator);
  if (translator.load(kLocale, kFilename)) {
    qApp->installTranslator(&translator);
  }
}
void MainWindow::CreateLanguageMenu() {
  // TODO(wolters): This is not optimal, since it does not work automatically
  // with the .qm files added as a resource to the application.
  //: Translation for the human language German.
  QT_TR_NOOP("German");
  //: Translation for the human language English.
  QT_TR_NOOP("English");
  QActionGroup* language_group{new QActionGroup(ui_->menuLanguage)};
  language_group->setExclusive(true);
  connect(language_group, SIGNAL(triggered(QAction*)), this,
          SLOT(slotLanguageChanged(QAction*)));
  QLocale const kDefaultLocale{QLocale::system()};
  QDir const kDirectory{QApplication::applicationDirPath() + "/.."};
  QStringList const kFileNames{kDirectory.entryList(QStringList("*.qm"))};
  for (QString const& kFileName : kFileNames) {
    QLocale const kLocale{QFileInfo{kFileName}.completeBaseName().replace(
        qApp->applicationName() + "_", "")};
    QString const kCountryCode{
        kLocale.name().toLower().mid(kLocale.name().lastIndexOf('_') + 1)};
    QIcon const kIcon{":/icons/flags/" + kCountryCode + ".png"};
    QAction* action{new QAction{
        kIcon,
        // TODO(wolters): This does not work.
        tr(QLocale::languageToString(kLocale.language()).toStdString().c_str()),
        this}};
    action->setCheckable(true);
    action->setData(kLocale);
    ui_->menuLanguage->addAction(action);
    language_group->addAction(action);
    if (kDefaultLocale == kLocale) {
      action->setChecked(true);
    }
  }
}
void MainWindow::changeEvent(QEvent* the_event) {
  if (nullptr != the_event) {
    switch (the_event->type()) {
      // QEvent::LanguageChange is send if a translator is loaded.
      case QEvent::LanguageChange:
        ui_->retranslateUi(this);
        break;
      // QEvent::LocaleChange is send, if the system language changes.
      case QEvent::LocaleChange:
        LoadLanguage(QLocale::system());
        break;
      default:
        break;
    }
  }
  QMainWindow::changeEvent(the_event);
}
void MainWindow::slotLanguageChanged(QAction* action) {
  if (nullptr != action) {
    LoadLanguage(qvariant_cast<QLocale>(action->data()));
  }
}

源代码已经在注释中描述了我遇到的问题。

  1. 如果应用程序已启动,则使用当前系统区域设置重新翻译UI。这很有效,我可以看到菜单中的项目QObject::tr()0出现在德语中(这两项我都翻译过在.ts文件中)。但当我通过菜单将语言从德语切换到英文,两个项目标签都不会被翻译
  2. 这种方法通常不是最优的,因为我不想修改源代码(上面的QT_TR_NOOP),如果将新的人类语言添加到应用最佳工作流程为:
    1. 将所有支持的语言以其正确的语言添加到所有.ts文件中
    2. 动态转换菜单项

我想我误解了什么,但我找不到解决办法在WWW上搜索一段时间。

更新2015-04-01:我认为我使用了错误的方法。重要的是Languages菜单是在成员函数CreateLanguageMenu()中动态创建的。我需要一个如何翻译动态创建的菜单项的问题的答案。所以这都是关于函数中的QAction* action{new QAction{kIcon, tr(QLocale::languageToString(kLocale.language()).toStdString().c_str()), this}};行。我想我需要一些在编译时可用的查找功能。。。

正如您已经提到的,您需要一个实时查找功能。我建议这样的破解:创建QAction对象时,使用对象名称作为语言标识符

QT_TR_NOOP("LANG_ENG")
QAction* langAction = ...;
langAction->setObjectName("LANG_ENG");

在"语言更改"事件上调用一些方法来检索此操作

void retranslateLangActions()
{
    QList<QAction*> widgetActions = this->findChildren<QAction*>();
    foreach(QAction* act, widgetActions) // qt foreach macro
    {
        QString objName = act->objectName();
        if (objName.startsWith("LANG_"))
        {
            act->setText(tr(objName.toStdString().c_str()));
        }
    }
}