Qt 项目国际化从零到一:用 Qt Linguist 实现多语言动态切换(含源码与踩坑指南)
目录
为什么需要国际化(i18n)
环境与目录结构
第一个带翻译标记的 Qt 界面
生成
.ts
翻译文件(lupdate)用 Qt Linguist 翻译
生成
.qm
文件(lrelease)程序运行时加载
.qm
通式多语言切换(LanguageManager)
自动记忆 & 系统语言优先
打包与部署(含 Qt 基础控件翻译)
常见问题与踩坑总结
源码清单(可直接复制运行)
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.qrcTRANSLATIONS += 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(SRCSmain.cppmainwindow.cpplanguagemanager.cppresources.qrc
)
set(HDRS mainwindow.h languagemanager.h)
set(UIS mainwindow.ui)set(TS_FILEStranslations/app_en_US.tstranslations/app_zh_CN.tstranslations/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 翻译
打开
translations/*.ts
;在右侧填入译文;
保存后状态会从 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(); // 统一刷新 UIUi::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 = menuLanguageui->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 = MyI18nAppSOURCES += main.cpp mainwindow.cpp languagemanager.cpp
HEADERS += mainwindow.h languagemanager.h
FORMS += mainwindow.ui
RESOURCES += resources.qrcTRANSLATIONS += 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(SRCSmain.cppmainwindow.cpplanguagemanager.cppresources.qrc
)
set(HDRS mainwindow.h languagemanager.h)
set(UIS mainwindow.ui)set(TS_FILEStranslations/app_en_US.tstranslations/app_zh_CN.tstranslations/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 均支持,工程可即刻运行