Qt - 国际化

Linguist简介

Qt提供了一款优秀的支持Qt C++和Qt Quick应用程序的翻译工具。发布者、翻译者和开发者可以使用这款工具来完成他们的任务。

发布者:承担了全面发布应用程序的责任。通常,他们协调开发者和翻译者的工作,可以使用lupdate工具同步源代码,进行翻译,使用lrelease同步工具为发布应用程序创建运行时使用的翻译文件。

翻译者:可以使用Qt Linguist工具翻译应用程序的文本。当然,这必须要有专业的翻译知识。

开发者:必须创建Qt应用程序能够使用的翻译文本。也应该帮助翻译者识别短语出现的场景。

以上有三种角色-发布者、翻译者、开发者,当然很多情况下,其实就是一种,那就是程序猿自己,大家都懂的。

 

使用流程

1. 使用tr包裹字符串

如果想让你的程序实现国际化,那么就在用户所有可见的字符串处都使用QObject::tr()。

[static] QString QObject::tr(const char *sourceText, const char *disambiguation = nullptr, int n = -1)

 

2. 修改pro文件

在.pro文件中添加翻译文件名称。

TRANSLATIONS += translations/Chinese.ts \
                translations/English.ts \
                translations/Japanese.ts

 

3. 生成翻译文件

点击 菜单栏->工具->外部->Qt语言家->更新翻译(lupdae),此时会在translations目录下面生成Chinese.ts和English.ts,Japanese.ts文件。

 

4. 打开翻译文件

4.1 修改xml,翻译

ts文件实际上是一个xml文件,直接可以用文本编辑器打开。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en">                            <!-- 版本和语言 -->
<context>
    <name>MainWindow</name>
    <message>
        <location filename="../mainwindow.ui" line="14"/>   <!-- 文本位置-->
        <source>MainWindow</source>                         <!-- 源文本-->
        <translatorcomment>title</translatorcomment>        <!-- 注释 -->
        <translation>Maye</translation>                     <!-- 翻译文本 -->
    </message>
    <message>
        <location filename="../mainwindow.ui" line="27"/>
        <source>用户名</source>
        <translation>username</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="40"/>
        <source>密码</source>
        <translation>password</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="73"/>
        <source>登录</source>
        <translation>login</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="86"/>
        <source>注册</source>
        <translation>register</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="100"/>
        <source>中文</source>
        <translation>chinese</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="105"/>
        <source>英文</source>
        <translation type="unfinished">English</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="119"/>
        <source>语言</source>
        <translation type="unfinished">language</translation>
    </message>
</context>
</TS>
​

4.1 使用语言家翻译

找到Linguist预言家打开,点击菜单栏->文件->打开,选择上面生成的.ts文件,并打开。

打开之后可以看到如下界面,主要就是对语言进行翻译。

翻译前面的图标及含义:

图标 含义

未翻译

已翻译,未标记完成

已翻译,且已标记完成

 

4.2 使用短语书翻译

短语书就是一个提前写好的翻译对照文件,在翻译ts文件的时候,可以打开短语书,很方便的就可以看到翻译。

(1)新建短语书

然后把需要的翻译提前写好,以后也可以重复使用。选择源语言和目标语言。

(2)编辑短语书

短语书创建完成之后,就可以开始编辑短语书了。

如下:先新建条目,再写上源文、译文、以及准确度(可以不写,也可以随便写点东西),最后点击保存,就可以关掉窗口了

(3)使用短语书

如果使用已经写好的短语书,而不是刚刚创建的,那么需要先打开短语书。如果是刚在预言家上创建的,则是打开状态的。

 

5. 发布翻译文件

翻译完成之后,就可以发布翻译了,为什么要发布呢?发布是什么意思?

  • ts文件是文本文件,占用内存比较大,发布之后会生成对应的二进制文件,内存较小

  • 通过发布就可以把ts文件转换成二进制文件

发布的方式有两种:

  • 通过语言家发布

    • 点击 菜单栏->文件->发布全部即可发布所有ts文件

  • 通过Qt Creator发布

    • 点击 菜单栏->工具->外部->Qt语言家->发布翻译(lrelease),会在translations目录中生成zc_CN.qm和en.qm两个文件。

发布成功后也会在translations目录下面生成Chinese.qm和English.qm,Japanese.qm文件。

 

6. 加载语言文件

QTranslator类为文本输出提供国际化支持。

该类的对象包含一组从源语言到目标语言的翻译。 QTranslator提供了在翻译文件中查找翻译的功能。 翻译文件使用Qt Linguist创建。

QTranslator translator;
if(translator.load("linguist_en.qm","F:\\MyCode\\QtCode\\Lingguist\\translate"))
{
      qApp->installTranslator(&translator);
      ui->retranslateUi(this);
}

注意:翻译文件加载的位置必须在界面实例化之前完成,否则是没有效果的.

 

7. 动态切换语言

注意:如果界面是通过Ui生成的,切换语言之后,可以通过调用函数retranslateUi翻译界面,否则需要重启程序.

  • 下拉框切换语言

void MainWindow::on_comboBox_currentIndexChanged(int index)
{
    QString filename;
    switch (index)
    {
    case 0:
        filename = "linguist_zh_CN.qm";      
        break;
    case 1:
        filename = "linguist_en.qm";
        break;
    }
    QTranslator translator;
    if(!translator.load(filename,"F:\\MyCode\\QtCode\\Lingguist\\translate"))
    {
        qDebug()<<"翻译文件加载失败";
        return;
    }
    if(qApp->installTranslator(&translator))
    {
        qDebug()<<"安装成功";
    }
    else
    {
        qDebug()<<"安装失败";
    }
    ui->retranslateUi(this);
}
  • 保存语言选择和恢复语言

保存

void MainWindow::on_comboBox_currentIndexChanged(int index)
{
    QString filename;
    switch (index)
    {
    case 0:
        filename = "linguist_zh_CN.qm";
        break;
    case 1:
        filename = "linguist_en.qm";
        break;
    }
    //保存配置
    QSettings setting("config.ini",QSettings::Format::IniFormat);
    setting.setValue("ts",filename);
    ...
}

恢复

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
​
    QSettings setting("config.ini",QSettings::Format::IniFormat);
    QString filename = setting.value("ts").toString();
​
    QTranslator translator;
    if(!translator.load(filename,"F:\\MyCode\\QtCode\\Lingguist\\translate"))
    {
        qDebug()<<"翻译文件加载失败";
    }
    if(qApp->installTranslator(&translator))
    {
        qDebug()<<"安装成功";
    }else
    {
        qDebug()<<"安装失败";
    }
​
    MainWindow w;
    w.show();
    return a.exec();
}
  • 重启程序

//重启程序才能生效,是否重启
auto ret = QMessageBox::information(this,"hit","是否重启",QMessageBox::StandardButton::Ok,QMessageBox::No);
if(ret == QMessageBox::Ok)
{
    QString exe =  QApplication::applicationDirPath()+"/"+qAppName()+".exe";
    QProcess::startDetached(exe);
    qApp->quit();
}

 

在论坛中漂,经常遇到有人遇到tr相关的问题。用tr的有两类人:

  • (1)因为发现中文老出问题,然后搜索,发现很多人用tr,于是他也开始用tr

  • (2)另一类人,确实是出于国际化的需要,将需要在界面上显示的文件都用tr包起来,这有分两种:

    • (2a) 用tr包住英文(最最推荐的用法,源码英文,然后提供英文到其他语言的翻译包)

    • (2b) 用tr包住中文(源码用中文,然后提供中文到其他语言的翻译包)

注意哦,如果你正在用tr包裹中文字符,却不属于(2b),那么:

  • 你在误用tr

  • 你需要的是QString,而不是tr

如果你确实属于(2b),请做好心理准备,你可能还会遇到很多困难,

tr 是做什么的?下面二者的区别是什么?

QString text1 = QObject::tr("hello"); 
QString text2 = QString("hello");

tr是用来实现国际化,如果你为这个程序提供了中文翻译包(其中hello被翻译成中文"你好"),那么text1的内容将是中文"你好";如果你为程序提供且使用日文翻译包,那么text1的内容将是日文。

tr是经过多级函数调用才实现了翻译操作,是有代价的,所以不该用的时候最好不要用。

 

8. 项目示例代码

工程文件预览

 

MyApplication.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    controller.cpp \
    global_function.cpp \
    main.cpp \
    mainwindow.cpp \
    settingsdialog.cpp

HEADERS += \
    controller.h \
    global_function.h \
    mainwindow.h \
    settingsdialog.h

FORMS += \
    mainwindow.ui \
    settingsdialog.ui

TRANSLATIONS += translations/Chinese.ts \
                translations/English.ts \
                translations/Japanese.ts

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    res.qrc

 

controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>

class SettingsDialog;
class AutoCloseFrame;
class Titlebar;

class Controller : public QObject
{
    Q_OBJECT
public:
    ~Controller();
    static Controller* GetInstance();

    void RetranslateUi();//重译所有界面(总接口)

    //注册界面,用处是Controller可以访问其他子界面的接口
    void RegisterVolumeAutoCloseFrame(AutoCloseFrame *w);
    void RegisterSettingWindow(SettingsDialog *w);
    void RegisterTitleBar(Titlebar* w);

signals:

private:
    Controller(QObject *parent = nullptr);

    SettingsDialog			*m_SettingsDialog;
    AutoCloseFrame			*m_AutoCloseFrame;
    Titlebar				*m_Titlebar;
};

#endif // CONTROLLER_H

 

global_function.h

#ifndef GLOBAL_FUNCTION_H
#define GLOBAL_FUNCTION_H

#include <QString>

extern void g_SetAppStyle(const QString strStyle);
extern QString g_GetStrStyle();



#endif // GLOBAL_FUNCTION_H

 

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "controller.h"
#include "settingsdialog.h"
#include "ui_mainwindow.h"



class MainWindow : public QMainWindow,public Ui_MainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();


public slots:
    void SettingsSlot();
    void slotSwitchLanguage(LANGUAGE language);

protected:
    //void changeEvent(QEvent *event);

private:
    //Ui::MainWindow *ui;

    Controller* m_Controller;
    SettingsDialog* m_SettingsDialog;
};
#endif // MAINWINDOW_H

 

settingsdialog.h

#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H

#include <QDialog>
#include "ui_settingsdialog.h"
#include "controller.h"

typedef enum{
    UI_ZH,
    UI_EN,
    UI_JP
}LANGUAGE;

Q_DECLARE_METATYPE(LANGUAGE)


class SettingsDialog : public QDialog,public Ui_SettingsDialog
{
    Q_OBJECT

public:
    explicit SettingsDialog(Controller* ctrl,QWidget *parent = nullptr);
    ~SettingsDialog();

    void CreateListWidget();
    void InitGeneralSetting();//初始化常规设置
    void InitVideo();          //初始化视频设置
    void InitAudio();          //初始化音频设置
    void InitSubtitle();       //初始化字幕设置
    void InitHotkeys();        //初始化快捷键设置

    void RetranslateUi();
signals:
    void signalSwitchLanguage(LANGUAGE language);

public slots:
    void onIndexChanged(int index);
    //void onSelectChanged(int);

protected:
    //void changeEvent(QEvent *event);

private:
    //Ui::SettingsDialog *ui;
    Controller*				m_ctrl;

    QListWidgetItem *General;
    QListWidgetItem *Video;
    QListWidgetItem *Audio;
    QListWidgetItem *Subtitles;
    QListWidgetItem *Shortcuts;
};

#endif // SETTINGSDIALOG_H

 

controller.cpp

#include "controller.h"
#include <QDebug>
#include "settingsdialog.h"

//Controller类的作用:就是相当于主界面与其他界面中间的一个桥梁,Controller可以访问子界面的接口,子界面也可以访Controller的接口

Controller::Controller(QObject *parent) : QObject(parent)
{
    qDebug() << "Controller构造函数";
}

Controller::~Controller()
{
    qDebug() << "~Controller析构函数";
}

Controller* Controller::GetInstance()
{
    static Controller* controller = NULL;
    if (controller == NULL)
    {
        controller = new Controller;
    }
    return controller;
}


void Controller::RetranslateUi()//重译所有界面(总接口)
{
    m_SettingsDialog->RetranslateUi(); //刷新设置页面的翻译
    //m_Titlebar->RetranslateUi();       //刷新标题栏的翻译
    //m_PlayListWidget->RetranslateUi(); //刷新播放列表的翻译
}

void Controller::RegisterVolumeAutoCloseFrame(AutoCloseFrame *w)
{
    m_AutoCloseFrame = w;
}

void Controller::RegisterSettingWindow(SettingsDialog *w)
{
    m_SettingsDialog = w;
}

void Controller::RegisterTitleBar(Titlebar* w)
{
    m_Titlebar = w;
    //connect(m_Titlebar, SIGNAL(sigMove(int)), this, SLOT(slotPLMove(int)));
}

 

global_function.cpp

#include "global_function.h"

#include <QApplication>
#include <QFont>
#include <QFile>

void g_SetAppStyle(const QString strStyle)//设置App的整体样式
{
    qApp->setStyleSheet(strStyle);//设置App样式
}

QString g_GetStrStyle()
{
    QFile qss("C:\\Users\\Administrator\\Documents\\MyApplication\\qss\\Style.qss");
    if(!qss.open(QFile::ReadOnly| QIODevice::Text))
    {
        qWarning("Style.css open falied");
        return 0;
    }
    QString strStyle = qss.readAll();
    qss.close();

    return strStyle;
}

 

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTranslator>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    setupUi(this);

    m_Controller = NULL;
    m_Controller = Controller::GetInstance();

    /*嵌入窗体*/
    m_SettingsDialog = new SettingsDialog(m_Controller,this);
    m_SettingsDialog->setHidden(true);

    connect(BtnSettings,  SIGNAL(clicked()), this, SLOT(SettingsSlot()));
    connect(m_SettingsDialog, SIGNAL(signalSwitchLanguage(LANGUAGE)), this, SLOT(slotSwitchLanguage(LANGUAGE)));
}

MainWindow::~MainWindow()
{
    //delete ui;
}

void MainWindow::SettingsSlot()
{
    m_SettingsDialog->exec();
}

void MainWindow::slotSwitchLanguage(LANGUAGE index)
{
    QString filename;
    switch(index)
    {
    case UI_ZH:
        filename = QString("Chinese.qm");
        break;
    case UI_EN:
        filename = QString("English.qm");
        break;
    case UI_JP:
        filename = QString("Japanese.qm");
        break;
    }

    QTranslator translator;
    if(!translator.load(filename,"C:\\Users\\Administrator\\Documents\\MyApplication\\translations"))
    {
        qDebug()<<"翻译文件加载失败";
        return;
    }
    else
    {
        if(qApp->installTranslator(&translator))
        {
            qDebug()<<"安装成功";
        }
        else
        {
            qDebug()<<"安装失败";
        }
    }
    m_Controller->RetranslateUi();
    retranslateUi(this);
}

 

settingsdialog.cpp

#include "settingsdialog.h"

SettingsDialog::SettingsDialog(Controller* ctrl,QWidget *parent) :QDialog(parent), m_ctrl(ctrl)
{
    setupUi(this);

    ctrl->RegisterSettingWindow(this);//注册界面

    CreateListWidget();
    InitGeneralSetting();

    connect(combo_language,  SIGNAL(activated(int)), this, SLOT(onIndexChanged(int)));
    connect(listWidget,  SIGNAL(currentRowChanged(int)), stackedWidget, SLOT(setCurrentIndex(int)));
}

SettingsDialog::~SettingsDialog()
{
    //delete ui;
}

void SettingsDialog::CreateListWidget()
{
    General = new QListWidgetItem(QIcon(":/resources/general.png"), tr("General"), listWidget);
    Video = new QListWidgetItem(QIcon(":/resources/video.png"), tr("Video"), listWidget);
    Audio = new QListWidgetItem(QIcon(":/resources/audio.png"), tr("Audio"), listWidget);
    Subtitles = new QListWidgetItem(QIcon(":/resources/subtitle.png"), tr("Subtitles"), listWidget);
    Shortcuts = new QListWidgetItem(QIcon(":/resources/shortcut.png"), tr("KeyShortcuts"), listWidget);

    General->setSizeHint(QSize(80,60));
    Video->setSizeHint(QSize(80,60));
    Audio->setSizeHint(QSize(80,60));
    Subtitles->setSizeHint(QSize(80,60));
    Shortcuts->setSizeHint(QSize(80,60));

    listWidget->addItem(General);
    listWidget->addItem(Video);
    listWidget->addItem(Audio);
    listWidget->addItem(Subtitles);
    listWidget->addItem(Shortcuts);
}

void SettingsDialog::InitGeneralSetting()
{
    combo_language->clear();
    combo_sking->clear();

    combo_language->addItem("Chinese", QVariant::fromValue(UI_ZH));
    combo_language->addItem("English", QVariant::fromValue(UI_EN));
    combo_language->addItem("Japanese", QVariant::fromValue(UI_JP));
    combo_language->setCurrentIndex(UI_EN);

    combo_sking->addItem(tr("Dark"));
    combo_sking->addItem(tr("Light"));
}

void SettingsDialog::RetranslateUi()
{
    this->setWindowTitle(tr("setting dialog"));

    General->setText(tr("General"));
    Video->setText(tr("Video"));
    Audio->setText(tr("Audio"));
    Subtitles->setText(tr("Subtitles"));
    Shortcuts->setText(tr("KeyShortcuts"));

    combo_language->setItemText(UI_ZH, tr("Chinese"));
    combo_language->setItemText(UI_EN, tr("English"));
    combo_language->setItemText(UI_JP, tr("Japanese"));
    combo_sking->setItemText(0,tr("Dark"));
    combo_sking->setItemText(1,tr("Light"));

    retranslateUi(this);
}

void SettingsDialog::onIndexChanged(int index)
{
    LANGUAGE language = (LANGUAGE)index;
    emit signalSwitchLanguage(language);
}

 

main.cpp

#include "mainwindow.h"

#include <QApplication>
#include <QDebug>
#include <QFile>
#include "global_function.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //qApp->setStyleSheet(g_GetStrStyle());//错误,需要界面显示后再设置app的全部样式

    MainWindow w;
    w.show();
    qApp->setStyleSheet(g_GetStrStyle());

    return a.exec();
}

运行结果:

翻译后:

 

9. 各国语言代码

网页链接:http://www.lingoes.cn/zh/translator/langcode.htm

posted @ 2022-07-17 09:58  [BORUTO]  阅读(234)  评论(0编辑  收藏  举报