翻译"动态"使用Qt国际化的内容

时间:2015-03-31 19:28:59

标签: c++ qt internationalization translation

我目前正在评估C ++框架Qt v5.4.1。目前我是 试图了解并应用Internationalization with Qt

我在运行时成功实现了人类语言的切换 文章How to create a multi lingual application that can switch the language at runtime?的帮助,我知道如何使用Qt Linguist Translation 源(.ts)文件以及如何生成Qt Linguist Message(.qm)文件。我用 构建系统CMake以自动化生成,并且它非常好用。

在我的项目中,翻译是从Qt资源集合(.qrc)加载的 文件编译到应用程序中。我知道如何通过转换“静态”字符串 成员函数QObject::tr()QObject::translate()

现在出现了棘手的部分:我想在应用程序发展的同时添加语言。 目前我有以下两个.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和 通过rcc将该文件编译到可执行文件中。

声明文件(.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. 如果应用程序已启动,则使用当前系统区域设置 重新翻译用户界面。这有效,我可以看到菜单中的项目 Languages出现在德语人类语言中(我翻译了两个项目 在.ts文件中)。但是当我通过菜单从德语切换到语言时 英语,两个项目标签得到翻译。
  2. 这种方法通常不是最优的,因为我不想修改 源代码(上面为QT_TR_NOOP),如果添加了新的人类语言 应用。最佳工作流程是:
    1. 以适当的语言将所有支持的语言添加到所有.ts文件中。
    2. 动态翻译菜单项。
  3. 我认为我误解了一些东西,但我找不到解决方案 搜索WWW一段时间。

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

1 个答案:

答案 0 :(得分:1)

正如您已经提到的,您需要实时查找功能。 我建议这样的黑客: 在创建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()));
        }
    }
}