Qt Linguist 实战:为你的应用程序添加多语言支持 – wiki基地


Qt Linguist 实战:为你的应用程序添加多语言支持

在全球化的今天,让软件产品跨越语言和文化的障碍,是其能否取得成功的关键因素之一。一个只支持单一语言的应用程序,会天然地将潜在用户群限制在一个特定的国家或地区。国际化(Internationalization, i18n)和本地化(Localization, l10n)正是解决这一问题的核心技术。

Qt 作为一个成熟的跨平台 C++ 开发框架,提供了一套极为强大且流程清晰的翻译解决方案。其核心工具 Qt Linguist 扮演着连接开发者与翻译人员的桥梁角色。本文将通过一篇详尽的实战指南,带你从零开始,一步步为你的 Qt 应用程序添加完整的多语言支持。

第一章:核心概念与工作流程

在深入代码之前,我们必须理解 Qt 翻译体系的几个核心概念和完整的工作流程。这能帮助我们从宏观上把握全局,使后续的实践事半功倍。

1.1 国际化 (i18n) 与本地化 (l10n)

  • 国际化 (Internationalization):这是开发者的工作。指的是在编写代码的阶段,就将所有需要展示给用户的文本(如标签、按钮文字、提示信息等)进行特殊处理,使其能够被翻译工具识别和提取。这个过程是“一次性”的工程改造,目标是让程序不依赖于任何特定语言。
  • 本地化 (Localization):这是翻译人员的工作。指的是将国际化处理后的文本,翻译成目标用户所在地的语言,并可能根据当地文化习惯进行调整(如日期格式、货币符号等)。这个过程可以针对多种语言,多次进行。

1.2 Qt 翻译工作流

Qt 的翻译流程设计得非常优雅,分工明确,主要包含以下五个步骤:

  1. 开发者标记文本:在 C++ 代码或 .ui 设计文件中,使用 tr() 函数包裹所有需要翻译的字符串。
  2. 生成翻译源文件 (.ts):使用 Qt 提供的命令行工具 lupdate 扫描整个项目,自动提取所有被 tr() 包裹的字符串,并生成或更新一个或多个 .ts 文件。
  3. 翻译文本:翻译人员使用 Qt Linguist 工具打开 .ts 文件,逐条进行翻译。Linguist 提供了友好的用户界面,让非技术人员也能轻松上手。
  4. 发布翻译文件 (.qm):开发者或项目经理使用另一个命令行工具 lrelease,将翻译完成的 .ts 文件编译成一个紧凑、高效的二进制格式 .qm 文件。.qm 文件才是最终应用程序在运行时加载的文件。
  5. 在程序中加载翻译:开发者在应用程序启动时,根据用户的系统语言或用户的选择,加载相应的 .qm 文件,从而动态地改变界面语言。

1.3 关键文件类型解析

  • .pro 文件:Qt 项目的配置文件。我们需要在这里指定我们的翻译文件,以便 lupdatelrelease 工具能够找到它们。
  • .cpp / .h / .ui 文件:应用程序的源代码和界面设计文件。这些是我们进行国际化改造的主要场所。
  • .ts (Translation Source) 文件:这是一种基于 XML 的可读文件,由 lupdate 生成。它包含了原文和译文的配对信息,是 Qt Linguist 的工作文件。
  • .qm (Qt Message) 文件:由 .ts 文件编译而来的二进制文件。它体积小,读取速度快,专门为程序在运行时高效加载而设计。我们最终随应用程序一同发布的是 .qm 文件。

第二章:实战第一步 – 代码国际化

万事开头难,但 Qt 的国际化开头却异常简单。核心只有一个函数:tr()

2.1 tr() 函数的魔力

tr()QObject 类的一个成员函数。当你在一个继承自 QObject 的类(几乎所有的 Qt 部件都如此)中使用它时,它不仅标记了字符串用于翻译,还提供了一个重要的“上下文(Context)”信息,这个上下文就是类名本身。

基础用法:

假设我们有一个 QLabel,我们想设置它的文本:

“`cpp
// 之前
label->setText(“Hello, World!”);

// 国际化之后
label->setText(tr(“Hello, World!”));
“`

就是这么简单!只要将字符串字面量用 tr() 包裹起来即可。

在非 QObject 子类中使用:

如果你的代码不在一个 QObject 子类中(比如一个独立的工具函数),你可以使用 QCoreApplication::translate(),并手动指定上下文:

cpp
QString message = QCoreApplication::translate("GlobalContext", "An error occurred.");

2.2 为翻译人员提供上下文和注释

有时候,一个单词在不同场景下有不同的含义。例如 “Open”,可以指“打开文件”,也可以指“营业中”。为了避免歧义,我们可以为 tr() 提供额外的参数。

消歧义 (Disambiguation):

“`cpp
// 场景一:菜单项
QAction *openAction = new QAction(tr(“Open”, “File menu”), this);

// 场景二:商店状态
QLabel *statusLabel = new QLabel(tr(“Open”, “Store status”), this);
“`

在 Qt Linguist 中,翻译人员会看到这两个 “Open” 是不同的条目,并且能看到我们提供的 “File menu” 和 “Store status” 注释,从而做出正确的翻译。

添加开发者注释:

cpp
// 告诉翻译人员这个字符串的最大长度限制
QString limitedText = tr("Settings", "Keep the translation short, max 10 chars");

这些注释是给翻译人员的宝贵信息,善用它们能极大提高翻译质量。

2.3 处理带参数的字符串

一个常见的错误是使用 + 来拼接字符串,这在翻译中是灾难性的,因为不同语言的语法结构和词序千差万别。

错误的做法:

cpp
// 错误!词序在不同语言中可能完全不同
QString status = tr("File ") + fileName + tr(" processed."); // 英语: File a.txt processed. 法语可能需要: Le fichier a.txt a été traité.

正确的做法:使用 QString::arg()

Qt 的 QString::arg() 方法是处理此类问题的标准方案。它使用位置标记符 %1, %2 等。

cpp
int current = 5;
int total = 100;
QString status = tr("Processing file %1 of %2...").arg(current).arg(total);

这样,翻译人员在翻译时可以自由调整 %1%2 的位置,以适应目标语言的语法。例如,某个虚构的语言可能会翻译成:

"总共 %2 个文件,正在处理第 %1 个..."

2.4 UI 文件中的国际化

如果你使用 Qt Designer 或 Qt Creator 的 UI 设计器来创建界面,事情会更简单。当你拖放一个部件并设置其 text 或其他字符串属性时,Qt Creator 会自动将这些字符串标记为可翻译。

打开一个 .ui 文件(用文本编辑器),你会看到类似这样的结构:

xml
<widget class="QLabel" name="label">
<property name="text">
<string>Welcome</string>
</property>
</widget>

lupdate 工具会自动识别 <string> 标签内的文本并将其提取出来。你无需在 UI 文件中手动添加 tr()

第三章:实战第二步 – 生成与管理翻译文件

代码改造完成后,接下来就是让工具登场了。

3.1 配置 .pro 项目文件

首先,我们需要告诉 Qt 我们的项目需要哪些语言的翻译。打开你的 .pro 文件,添加 TRANSLATIONS 变量:

pro
TRANSLATIONS += \
myapp_zh_CN.ts \
myapp_fr_FR.ts \
myapp_de.ts

这里的命名规范推荐使用 appname_language_COUNTRY.ts 的格式(如 myapp_zh_CN.ts),language 是小写 ISO 639 语言代码,COUNTRY 是大写 ISO 3166 国家代码。这是一种清晰且广泛接受的实践。

3.2 运行 lupdate

lupdate 是扫描项目并生成/更新 .ts 文件的工具。

通过 Qt Creator 运行:

最简单的方式是在 Qt Creator 中,点击菜单栏的 “工具” -> “外部” -> “Linguist” -> “更新翻译(lupdate)”。

通过命令行运行:

打开一个配置好 Qt 环境的终端(如 Qt Creator 自带的终端),进入项目根目录,然后执行:

bash
lupdate myapp.pro

执行完毕后,你会在项目目录中看到你刚才在 .pro 文件里定义的 .ts 文件。如果你是第一次运行,这些文件会被创建。如果之后你修改了代码,增加了新的 tr() 字符串,再次运行 lupdate,它会智能地在已有的 .ts 文件中添加新字符串,而保留已有的翻译。

第四章:实战第三步 – 使用 Qt Linguist 进行翻译

现在,轮到我们的主角 Qt Linguist 登场了。这个工具是为翻译人员设计的,界面直观,无需任何编程知识。

4.1 Linguist 界面导览

启动 Qt Linguist(通常在你的 Qt 安装目录的 bin 文件夹下),然后打开一个刚才生成的 .ts 文件(例如 myapp_zh_CN.ts)。

界面主要分为几个区域:

  1. 上下文列表 (Context List):左上角。这里列出了所有字符串的来源,通常是类名(如 MainWindow, MyDialog)或我们手动指定的上下文。这有助于翻译人员理解字符串的语境。
  2. 字符串列表 (String List):左侧中间。列出当前选定上下文中的所有源字符串。每个字符串前都有一个状态图标:
    • ? (黄色问号):未翻译。
    • ! (黄色感叹号):预翻译或模糊匹配,需要人工确认。
    • (绿色对勾):已翻译并确认。
  3. 翻译区域 (Translation Area):右侧核心区域。这里显示:
    • 源文本 (Source text):需要翻译的原文。
    • 开发者注释 (Developer comments):你在 tr() 中提供的额外信息。
    • 译文输入框:翻译人员在这里输入目标语言的文本。
  4. 短语与猜测 (Phrases and Guesses):下方区域。Linguist 会根据已有翻译,智能地提供翻译建议。
  5. 源码位置 (Sources and Forms):右下角。精确显示该字符串在哪个文件的哪一行,方便开发者定位。

(这是一个描述性的 UI 布局示意图)

4.2 翻译流程

  1. 上下文列表中选择一个上下文,比如 MainWindow
  2. 字符串列表中点击一个未翻译的条目(带黄色问号的)。
  3. 在右侧的翻译区域,仔细阅读源文本和开发者注释。
  4. 在下方的译文输入框中,输入准确的中文翻译。
  5. 按下快捷键 Ctrl + Enter。你会发现:
    • 你输入的译文被接受。
    • 左侧该条目的图标变成了绿色对勾。
    • 列表自动跳转到下一个未翻译的条目。
  6. 重复此过程,直到所有条目都变成绿色对勾。
  7. 完成后,点击菜单栏“文件” -> “保存”。

4.3 处理复数形式

不同语言对复数的处理规则不同。英语只有单数和复数两种形式,而一些斯拉夫语系语言则有更复杂的形式。Qt 对此有完美的支持。

在代码中,我们需要使用 tr() 的一个特殊重载:

cpp
int fileCount = ...; // 文件的数量
QString message = tr("%n file(s) found.", "", fileCount);

这里的 %n 是一个特殊的占位符,它会被 fileCount 的值替换。

在 Qt Linguist 中,当你遇到这样的条目时,它会提供多个输入框,让你根据目标语言的语法规则提供不同的翻译。对于中文,单数和复数形式通常一样,但对于英语,你会看到:

  • Singular (%n = 1): %n file found.
  • Plural (%n > 1): %n files found.

翻译人员只需填写所有形式即可。

第五章:实战第四步 – 发布与加载翻译

翻译工作完成后,我们回到开发者角色,完成最后两步。

5.1 运行 lrelease

lrelease 工具将我们人类可读的 .ts 文件编译成机器高效读取的 .qm 文件。

通过 Qt Creator 运行:

通常,当你在 Release 模式下构建项目时,如果 .pro 文件配置了 TRANSLATIONS,Qt Creator 会自动运行 lrelease

通过命令行运行:

bash
lrelease myapp.pro

这会为每个 .ts 文件生成一个对应的 .qm 文件(例如 myapp_zh_CN.qm)。这些 .qm 文件就是我们需要随应用程序一起打包发布的文件。通常,我们会把它们放在一个名为 translations 的子目录中。

5.2 在应用程序中加载翻译

最后一步,让我们的应用程序在启动时智能地加载正确的翻译文件。最佳实践是在 main.cpp 文件中,在创建主窗口之前完成加载。

“`cpp

include

include

include

include

include “mainwindow.h”

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

// 1. 创建翻译家对象
QTranslator translator;

// 2. 获取系统当前的语言环境
QLocale locale = QLocale::system(); // 例如,返回 "zh_CN"

// 3. 构建 .qm 文件路径并加载
// 假设 .qm 文件在可执行文件同级的 translations 目录下
// 路径会是 ":/translations/myapp_zh_CN.qm" 或 "translations/myapp_zh_CN.qm"
// 注意:推荐使用 Qt 资源系统 (qrc) 来打包翻译文件
if (translator.load(locale, "myapp", "_", ":/translations")) {
    // 4. 安装翻译家到应用程序
    a.installTranslator(&translator);
}

// (可选)加载 Qt 自身的标准翻译(如“OK”, “Cancel”按钮)
QTranslator qtBaseTranslator;
if (qtBaseTranslator.load("qtbase_" + locale.name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
    a.installTranslator(&qtBaseTranslator);
}


MainWindow w;
w.show();

return a.exec();

}
“`

代码解释:

  1. 我们创建一个 QTranslator 对象。
  2. QLocale::system() 获取操作系统的语言设置。
  3. translator.load() 是一个非常方便的函数。它会根据 locale (如 zh_CN),自动尝试加载 myapp_zh_CN.qm。如果找不到,它会尝试更通用的 myapp_zh.qm。我们指定了文件名前缀 “myapp”,分隔符 “_”,以及搜索路径。强烈建议使用 Qt 资源系统(.qrc)将翻译文件编译进可执行文件,这样路径就是 ":/translations",可以避免找不到文件的问题。
  4. a.installTranslator(&translator) 将这个“翻译家”安装到我们的应用程序实例中。从这一刻起,所有后续创建的 tr() 字符串都会被自动翻译。

现在,编译并运行你的程序。如果你的操作系统是中文环境,你应该能看到一个全中文的界面!

5.3 动态切换语言

更进一步,我们还可以让用户在程序运行时动态切换语言。这需要多做一步:在语言切换后,通知所有UI元素重新加载它们的文本。

  1. 实现切换逻辑:比如在一个菜单中,当用户选择“法语”时:
    • 加载 myapp_fr_FR.qm 到一个新的 QTranslator 对象。
    • qApp->installTranslator() 安装新的翻译。
    • 移除旧的翻译器(如果需要)。
  2. 响应语言变化事件:所有 QWidget 都可以重写 changeEvent() 函数。当语言变化时,Qt 会发送一个 QEvent::LanguageChange 事件。

在你的主窗口 mainwindow.h 中:

cpp
protected:
void changeEvent(QEvent *event) override;

mainwindow.cpp 中:

cpp
void MainWindow::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange) {
// ui->retranslateUi() 是由 uic 自动生成的函数
// 它的作用就是重新设置 UI 文件中定义的所有部件的文本
ui->retranslateUi(this);
} else {
QMainWindow::changeEvent(event);
}
}

ui->retranslateUi(this) 这个函数是关键。它是由 uic(UI 编译器)从你的 .ui 文件自动生成的,专门用于重新应用所有翻译。对于那些在代码中动态创建的文本,你需要在这里手动再次调用 setText(tr("...")) 来更新它们。

总结

通过遵循 Qt 提供的这套成熟工作流,为应用程序添加多语言支持不再是一项艰巨而混乱的任务,而是一个清晰、可维护的工程实践。

让我们再次回顾这个流程:

  1. 编码:用 tr() 包裹所有用户可见字符串,为复杂情况提供注释。
  2. 提取:用 lupdate 从代码和 UI 文件中提取字符串到 .ts 文件。
  3. 翻译:使用 Qt Linguist 这个专业工具,高效、准确地完成翻译工作。
  4. 发布:用 lrelease.ts 文件编译成紧凑的 .qm 文件。
  5. 加载:在 main.cpp 中使用 QTranslator 加载对应的 .qm 文件,让应用程序“开口说外语”。

掌握 Qt Linguist 和 Qt 的翻译体系,不仅能显著提升你应用程序的用户体验和市场范围,也是衡量一个 Qt 开发者专业程度的重要标志。现在就开始,为你的下一个项目赋予走向世界的能力吧!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部