Qt 项目国际化从零到一:用 Qt Linguist 实现多语言动态切换(含源码与踩坑指南)

目录

  1. 为什么需要国际化(i18n)

  2. 环境与目录结构

  3. 第一个带翻译标记的 Qt 界面

  4. 生成 .ts 翻译文件(lupdate)

  5. 用 Qt Linguist 翻译

  6. 生成 .qm 文件(lrelease)

  7. 程序运行时加载 .qm

  8. 通式多语言切换(LanguageManager)

  9. 自动记忆 & 系统语言优先

  10. 打包与部署(含 Qt 基础控件翻译)

  11. 常见问题与踩坑总结

  12. 源码清单(可直接复制运行)


1. 为什么需要国际化(i18n)

  • 走向全球:桌面应用需要满足不同地区用户。

  • 体验一致:菜单、对话框、提示信息一致切换。

  • Qt 自带完整方案tr() 标记 → .ts 翻译(XML) → .qm 运行时资源。


2. 环境与目录结构

本文以最小工程示例:

复制代码
MyI18nApp/
├─ main.cpp
├─ mainwindow.h / mainwindow.cpp / mainwindow.ui
├─ languagemanager.h / languagemanager.cpp
├─ translations/
│   ├─ app_en_US.ts / app_zh_CN.ts / app_ja_JP.ts   ← lupdate 生成
│   ├─ app_en_US.qm / app_zh_CN.qm / app_ja_JP.qm   ← lrelease 生成
│   └─ qtbase_zh_CN.qm / qtbase_ja_JP.qm            ← 可选(Qt 基础控件翻译)
├─ resources.qrc
├─ MyI18nApp.pro          (qmake 方案)
└─ CMakeLists.txt         (CMake 方案)

你可选 qmake 或 CMake,其它文件通用。


3. 第一个带翻译标记的 Qt 界面

3.1 在 .ui / .cpp 中使用 tr()

mainwindow.ui 放一个 QLabel 与一个 QPushButton

mainwindow.cpp 中(或直接在 .ui 属性里)加入可翻译文本:

复制代码
// mainwindow.cpp(示例)
ui->label->setText(tr("Hello, World!"));
ui->pushButton->setText(tr("Switch Language"));

关键 :只有被 tr() 包裹的字符串会被提取到 .ts


4. 生成 .ts 翻译文件(lupdate)

qmake 项目

MyI18nApp.pro 中加入:

复制代码
QT += widgets
TEMPLATE = app
TARGET = MyI18nApp
SOURCES += main.cpp mainwindow.cpp languagemanager.cpp
HEADERS += mainwindow.h languagemanager.h
FORMS   += mainwindow.ui
RESOURCES += resources.qrc

TRANSLATIONS += translations/app_en_US.ts \
                translations/app_zh_CN.ts \
                translations/app_ja_JP.ts

命令行执行:

复制代码
lupdate MyI18nApp.pro

CMake 项目(Qt 6)

CMakeLists.txt

复制代码
cmake_minimum_required(VERSION 3.16)
project(MyI18nApp)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets LinguistTools)

set(SRCS
  main.cpp
  mainwindow.cpp
  languagemanager.cpp
  resources.qrc
)
set(HDRS mainwindow.h languagemanager.h)
set(UIS  mainwindow.ui)

set(TS_FILES
  translations/app_en_US.ts
  translations/app_zh_CN.ts
  translations/app_ja_JP.ts
)

qt_add_executable(MyI18nApp ${SRCS} ${HDRS} ${UIS})

# 生成 QM 文件(Qt6 推荐)
qt_add_translations(MyI18nApp QM_FILES ${TS_FILES})
target_link_libraries(MyI18nApp PRIVATE Qt6::Widgets)

命令行执行:

复制代码
cmake -S . -B build
cmake --build build
# 或单独生成 ts:lupdate . -ts translations/app_zh_CN.ts ...

5. 用 Qt Linguist 翻译

  1. 打开 translations/*.ts

  2. 在右侧填入译文;

  3. 保存后状态会从 unfinished 变为 finished

.ts 结构示例(节选):

复制代码
<TS version="2.1" language="zh_CN">
  <context>
    <name>MainWindow</name>
    <message>
      <source>Hello, World!</source>
      <translation>你好,世界!</translation>
    </message>
    <message>
      <source>Switch Language</source>
      <translation>切换语言</translation>
    </message>
  </context>
</TS>

6. 生成 .qm 文件(lrelease)

复制代码
lrelease translations/app_en_US.ts
lrelease translations/app_zh_CN.ts
lrelease translations/app_ja_JP.ts

生成的 .qm 文件放进资源或部署目录。


7. 程序运行时加载 .qm

resources.qrc 中把 .qm 打到资源里:

复制代码
<RCC>
  <qresource prefix="/">
    <file alias="translations/app_en_US.qm">translations/app_en_US.qm</file>
    <file alias="translations/app_zh_CN.qm">translations/app_zh_CN.qm</file>
    <file alias="translations/app_ja_JP.qm">translations/app_ja_JP.qm</file>
    <!-- 可选:Qt 基础控件翻译 -->
    <file alias="translations/qtbase_zh_CN.qm">translations/qtbase_zh_CN.qm</file>
    <file alias="translations/qtbase_ja_JP.qm">translations/qtbase_ja_JP.qm</file>
  </qresource>
</RCC>

8. 通式多语言切换(LanguageManager)

一次实现,支持任意多语言;动态切换后 UI 自动刷新;兼容 Qt5/Qt6。

8.1 languagemanager.h

复制代码
#pragma once
#include <QObject>
#include <QTranslator>
#include <QLocale>
#include <QSet>

class LanguageManager : public QObject {
    Q_OBJECT
public:
    explicit LanguageManager(QObject* parent=nullptr);

    void addSupported(const QString& localeName); // "en_US" / "zh_CN" / "ja_JP"
    QStringList supported() const;
    QString current() const;

    bool switchTo(const QString& localeName); // 动态切换

signals:
    void languageChanged(const QString& localeName);

private:
    bool loadTranslators(const QString& localeName);
    void unloadTranslators();
    QString m_current;
    QTranslator m_appTr;
    QTranslator m_qtbaseTr; // 可选
    QSet<QString> m_supported;
};

8.2 languagemanager.cpp

复制代码
#include "languagemanager.h"
#include <QApplication>

LanguageManager::LanguageManager(QObject* p): QObject(p) {}

void LanguageManager::addSupported(const QString& n){ m_supported.insert(n); }

QStringList LanguageManager::supported() const {
    auto list = QStringList(m_supported.begin(), m_supported.end());
    std::sort(list.begin(), list.end());
    return list;
}

QString LanguageManager::current() const { return m_current; }

bool LanguageManager::switchTo(const QString& name) {
    if (!m_supported.contains(name)) return false;
    if (m_current == name) return true;

    unloadTranslators();
    if (!loadTranslators(name)) return false;
    m_current = name;
    emit languageChanged(name);
    return true;
}

bool LanguageManager::loadTranslators(const QString& name) {
    const QString app = QString(":/translations/app_%1.qm").arg(name);
    if (!m_appTr.load(app)) return false;

    const QString qtbase = QString(":/translations/qtbase_%1.qm").arg(name);
    m_qtbaseTr.load(qtbase); // 允许失败

    qApp->installTranslator(&m_appTr);
    if (!m_qtbaseTr.isEmpty()) qApp->installTranslator(&m_qtbaseTr);
    return true;
}

void LanguageManager::unloadTranslators() {
    if (!m_appTr.isEmpty())   qApp->removeTranslator(&m_appTr);
    if (!m_qtbaseTr.isEmpty()) qApp->removeTranslator(&m_qtbaseTr);
    m_appTr = QTranslator();
    m_qtbaseTr = QTranslator();
}

8.3 mainwindow.h

复制代码
#pragma once
#include <QMainWindow>
class LanguageManager;
namespace Ui { class MainWindow; }

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MainWindow(LanguageManager* lm, QWidget* parent=nullptr);
    ~MainWindow();

private slots:
    void onLanguageChanged(const QString&); // 信号响应
private:
    void rebuildLanguageMenu(); // 动态构建"语言"菜单
    void retranslateUi();       // 统一刷新 UI
    Ui::MainWindow* ui;
    LanguageManager* m_lang;
};

8.4 mainwindow.cpp

复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "languagemanager.h"
#include <QActionGroup>
#include <QSettings>
#include <QLocale>

static QString pretty(const QString& n){
    const QLocale loc(n);
    return QString("%1 (%2)")
        .arg(QLocale::languageToString(loc.language()))
        .arg(QLocale::countryToString(loc.country()));
}

MainWindow::MainWindow(LanguageManager* lm, QWidget* parent)
    : QMainWindow(parent), ui(new Ui::MainWindow), m_lang(lm) {
    ui->setupUi(this);
    connect(m_lang, &LanguageManager::languageChanged,
            this, &MainWindow::onLanguageChanged);
    rebuildLanguageMenu();
    retranslateUi();
}

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

void MainWindow::rebuildLanguageMenu() {
    // 在 .ui 中放一个菜单:objectName = menuLanguage
    ui->menuLanguage->clear();
    auto* group = new QActionGroup(this); group->setExclusive(true);

    for (const auto& n : m_lang->supported()){
        auto* act = ui->menuLanguage->addAction(pretty(n));
        act->setCheckable(true);
        act->setData(n);
        if (n == m_lang->current()) act->setChecked(true);
        group->addAction(act);
    }
    connect(group, &QActionGroup::triggered, this, [this](QAction* a){
        m_lang->switchTo(a->data().toString());
    });
}

void MainWindow::onLanguageChanged(const QString&){
    retranslateUi();
    rebuildLanguageMenu();
    QSettings s("YourCompany","MyI18nApp");
    s.setValue("lang", m_lang->current());
}

void MainWindow::retranslateUi(){
    ui->retranslateUi(this); // 刷新 .ui 文本
    // 如果有纯代码里的动态字符串,也在这里用 tr() 重新设置
}

8.5 main.cpp

复制代码
#include "mainwindow.h"
#include "languagemanager.h"
#include <QApplication>
#include <QSettings>
#include <QLocale>

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

    LanguageManager lm;
    // 新增语言只需要在这里加一行,或做目录扫描自动添加
    lm.addSupported("en_US");
    lm.addSupported("zh_CN");
    lm.addSupported("ja_JP");

    // 1) 读取上次选择;2) 否则按系统语言;3) 再回退英文
    QSettings s("YourCompany","MyI18nApp");
    QString target = s.value("lang").toString();
    if (target.isEmpty()) {
        const QString sys = QLocale().name();
        target = lm.supported().contains(sys) ? sys : "en_US";
    }
    lm.switchTo(target);

    MainWindow w(&lm);
    w.show();
    return a.exec();
}

9. 自动记忆 & 系统语言优先

  • QSettings 持久化当前语言,下次启动沿用;

  • 首次运行优先用 QLocale().name() 与已支持语言匹配;

  • 不匹配时回退到 en_US(或你希望的默认语言)。


10. 打包与部署(含 Qt 基础控件翻译)

  • 资源内置 :使用 :/translations/... 最省心;

  • 外部目录:用磁盘路径加载,便于热更新;

  • Qt 基础控件翻译 :拷贝 qtbase_xx_YY.qm(随 Qt 安装提供)一起部署,文件对话框等系统控件才会被翻译;

  • 路径排查QTranslator::load() 失败 90% 是路径问题,建议配合 QFile::exists() 调试。


11. 常见问题与踩坑总结

现象 常见原因 解决方案
切换后部分文本不变 忘记刷新 UI 切换后统一调用 ui->retranslateUi(this),并在其中重设纯代码字符串
.ts 内容很少/为空 忘记 tr() 或未扫描 .ui 所有文本用 tr();确保 .pro/CMake 把 .ui/.cpp/.h 都包含
.qm 无效 installTranslator() 或加载到旧文件 removeTranslator()installTranslator();确认 .qm 最近 lrelease 过
对话框仍英文 未加载 qtbase_*.qm 额外加载 qtbase 对应语言
多窗口没刷新 新窗口在切换后才创建 languageChanged 信号或在构造时再次 retranslateUi()

12. 源码清单(可直接复制运行)

提示 :把以下所有文件按章节名保存到你的工程里,lupdate / lrelease 后即刻运行。

12.1 MyI18nApp.pro(qmake 选其一)

复制代码
QT += widgets
TEMPLATE = app
TARGET = MyI18nApp

SOURCES += main.cpp mainwindow.cpp languagemanager.cpp
HEADERS += mainwindow.h languagemanager.h
FORMS   += mainwindow.ui
RESOURCES += resources.qrc

TRANSLATIONS += translations/app_en_US.ts \
                translations/app_zh_CN.ts \
                translations/app_ja_JP.ts

12.2 CMakeLists.txt(CMake 选其一)

复制代码
cmake_minimum_required(VERSION 3.16)
project(MyI18nApp)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

find_package(Qt6 REQUIRED COMPONENTS Widgets LinguistTools)

set(SRCS
  main.cpp
  mainwindow.cpp
  languagemanager.cpp
  resources.qrc
)
set(HDRS mainwindow.h languagemanager.h)
set(UIS  mainwindow.ui)

set(TS_FILES
  translations/app_en_US.ts
  translations/app_zh_CN.ts
  translations/app_ja_JP.ts
)

qt_add_executable(MyI18nApp ${SRCS} ${HDRS} ${UIS})
qt_add_translations(MyI18nApp QM_FILES ${TS_FILES})

target_link_libraries(MyI18nApp PRIVATE Qt6::Widgets)

12.3 resources.qrc

复制代码
<RCC>
  <qresource prefix="/">
    <file alias="translations/app_en_US.qm">translations/app_en_US.qm</file>
    <file alias="translations/app_zh_CN.qm">translations/app_zh_CN.qm</file>
    <file alias="translations/app_ja_JP.qm">translations/app_ja_JP.qm</file>

    <!-- 可选:Qt 基础控件翻译 -->
    <file alias="translations/qtbase_zh_CN.qm">translations/qtbase_zh_CN.qm</file>
    <file alias="translations/qtbase_ja_JP.qm">translations/qtbase_ja_JP.qm</file>
  </qresource>
</RCC>

12.4 main.cpp

复制代码
#include "mainwindow.h"
#include "languagemanager.h"
#include <QApplication>
#include <QSettings>
#include <QLocale>

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

    LanguageManager lm;
    lm.addSupported("en_US");
    lm.addSupported("zh_CN");
    lm.addSupported("ja_JP");

    QSettings s("YourCompany","MyI18nApp");
    QString target = s.value("lang").toString();
    if (target.isEmpty()) {
        const QString sys = QLocale().name();
        target = lm.supported().contains(sys) ? sys : "en_US";
    }
    lm.switchTo(target);

    MainWindow w(&lm);
    w.show();
    return a.exec();
}

12.5 languagemanager.h / languagemanager.cpp

见第 8 章,原样复制即可。

12.6 mainwindow.h

复制代码
#pragma once
#include <QMainWindow>
class LanguageManager;
namespace Ui { class MainWindow; }

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MainWindow(LanguageManager* lm, QWidget* parent=nullptr);
    ~MainWindow();
private slots:
    void onLanguageChanged(const QString&);
private:
    void rebuildLanguageMenu();
    void retranslateUi();
    Ui::MainWindow* ui;
    LanguageManager* m_lang;
};

12.7 mainwindow.cpp

复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "languagemanager.h"
#include <QActionGroup>
#include <QSettings>
#include <QLocale>

static QString pretty(const QString& n){
    const QLocale loc(n);
    return QString("%1 (%2)")
      .arg(QLocale::languageToString(loc.language()))
      .arg(QLocale::countryToString(loc.country()));
}

MainWindow::MainWindow(LanguageManager* lm, QWidget* parent)
 : QMainWindow(parent), ui(new Ui::MainWindow), m_lang(lm) {
    ui->setupUi(this);
    connect(m_lang, &LanguageManager::languageChanged,
            this, &MainWindow::onLanguageChanged);
    rebuildLanguageMenu();
    retranslateUi();
}

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

void MainWindow::rebuildLanguageMenu() {
    ui->menuLanguage->clear();
    auto* group = new QActionGroup(this); group->setExclusive(true);
    for (const auto& n : m_lang->supported()) {
        auto* act = ui->menuLanguage->addAction(pretty(n));
        act->setCheckable(true); act->setData(n);
        if (n == m_lang->current()) act->setChecked(true);
        group->addAction(act);
    }
    connect(group, &QActionGroup::triggered, this, [this](QAction* a){
        m_lang->switchTo(a->data().toString());
    });
}

void MainWindow::onLanguageChanged(const QString&) {
    retranslateUi();
    rebuildLanguageMenu();
    QSettings s("YourCompany","MyI18nApp");
    s.setValue("lang", m_lang->current());
}

void MainWindow::retranslateUi() {
    ui->retranslateUi(this);
    // 代码构造的字符串也在这里使用 tr() 重新设置
}

12.8 mainwindow.ui 关键点

  • 放一个 QMenuBar,增加一个菜单,objectName = menuLanguage(作为"语言"菜单)。

  • 放一个 QLabel(文本:Hello, World!),一个 QPushButton(文本:Switch Language)。

  • .ui 自动使用 tr(),会被 lupdate 提取到 .ts


结束语

到这里,你已经完成了 Qt 国际化的全链路

  • tr() 标记 → .ts 提取(lupdate) → Linguist 翻译 → .qm 编译(lrelease)

  • 程序运行时加载/卸载翻译器

  • 通式多语言切换 + 自动记忆 + Qt 基础控件翻译

  • qmake/CMake 均支持,工程可即刻运行

相关推荐
春蕾夏荷_7282977251 天前
qcustomplot 显示坐标轴
qt·qcustomplot·坐标轴
郝学胜-神的一滴1 天前
Qt删除布局与布局切换技术详解
开发语言·数据库·c++·qt·程序人生·系统架构
yy_xzz1 天前
Debian 系统中 Qt Creator 用 sudo 启动后权限问题
c++·qt
夏玉林的学习之路1 天前
正则表达式
数据库·c++·qt·mysql·正则表达式
_OP_CHEN2 天前
从零开始的QT开发指南:(一)背景、特性与环境搭建
qt·qt下载·图形化界面·gui框架·qt环境配置·qt sdk配置·qt环境变量配置
「QT(C++)开发工程师」2 天前
VTK开源视觉库 | 行业应用第一篇
linux·qt·物联网·计算机视觉·信息可视化·vtk
weixin_467209282 天前
Qt Creator打开项目提示no valid settings file could be found
开发语言·qt
合作小小程序员小小店2 天前
舆情,情感微博系统demo,基于python+qt+nlp,开发语言python,界面库qt,无数据库版,数据来自第三方网站获取,
开发语言·pytorch·qt·自然语言处理·nlp
Larry_Yanan3 天前
QML学习笔记(四十八)QML与C++交互:QML中可实例化C++对象
c++·笔记·qt·学习·ui·交互
伐尘3 天前
【Qt】实现单例程序,禁止程序多开的几种方式
qt