4. QObject::tr() 与 QCoreApplication::translate():静态翻译与动态翻译的区别

说实话,我刚接触Qt国际化那会儿,也被这两个函数搞晕过。tr()和translate(),看起来都能翻译文本,但到底什么时候用哪个?

今天咱们就把这事儿彻底说清楚。我保证,看完这一节,你心里就有谱了。

4.1 先看个直观的例子

假设你有一个按钮,上面显示"确定"。在代码里,你可能会这样写:

// 方式一:用 tr()
button->setText(tr("确定"));

// 方式二:用 translate()
button->setText(QCoreApplication::translate("MyDialog", "确定"));

看起来差不多,对吧?但背后的机制完全不同。

4.2 核心区别:谁在管理上下文

tr() 是 QObject 的静态成员函数。它自动从调用它的类名中提取上下文。比如你在 MyDialog 类里写 tr("确定"),Qt 就知道上下文是 "MyDialog"。

translate() 需要你手动指定上下文。第一个参数就是上下文字符串,第二个参数才是要翻译的文本。

我刚开始用的时候,觉得 tr() 更方便,毕竟不用写上下文。但后来发现,有些场景下 tr() 根本用不了。

一句话总结:tr() 是静态翻译的标配,translate() 是动态翻译的利器。

4.3 静态翻译 vs 动态翻译

这两个概念,说白了就是:

  • 静态翻译:编译时就能确定的翻译。比如界面上的固定标签、按钮文字。
  • 动态翻译:运行时才能确定的翻译。比如从数据库读出来的数据、用户输入的文本。

为什么会这样?因为 tr() 在编译时会被 lupdate 工具扫描,提取到 .ts 文件中。而 translate() 不会自动被扫描,你得手动处理。

特性 tr() translate()
上下文来源 自动从类名获取 手动指定
lupdate 扫描 自动提取 不自动提取
适用场景 静态文本 动态文本
性能开销 较小 稍大(需查找上下文)
灵活性 低 高

4.4 什么时候用 tr()?

我个人习惯,所有在 UI 文件或代码中硬编码的文本,都用 tr()。比如:

// 按钮文字
QPushButton *btn = new QPushButton(tr("保存"), this);

// 标签文字
QLabel *label = new QLabel(tr("用户名:"), this);

// 菜单项
QAction *action = new QAction(tr("退出(&Q)"), this);

// 状态栏消息
statusBar()->showMessage(tr("文件已保存"));

这些文本在写代码时就已经确定了,用 tr() 最省事。lupdate 一扫描,全给你提取出来。

小技巧:如果你在非 QObject 子类里想用 tr(),可以继承 QObject 或者用 Q_DECLARE_TR_FUNCTIONS() 宏。我一般直接继承,省心。

4.5 什么时候用 translate()?

嗯,这里要注意了。有些场景 tr() 搞不定,必须上 translate()。

场景一:非 QObject 类中的翻译

比如你有一个纯 C++ 的数据处理类,没有继承 QObject:

class DataProcessor {
public:
    QString process(const QString& input) {
        // 这里不能用 tr(),因为没有 QObject 上下文
        return QCoreApplication::translate("DataProcessor", "处理中...");
    }
};

场景二:动态生成的文本

我在项目中遇到过,从数据库读取的配置项需要翻译。比如用户自定义的字段名:

QString fieldName = db->getFieldName(); // 可能是 "姓名" 或 "Name"
QString translated = QCoreApplication::translate("UserFields", fieldName.toUtf8());

这种文本在编译时根本不知道是什么,lupdate 没法提取。你得在运行时用 translate() 去查找翻译。

场景三:跨模块翻译

如果你在插件或动态库中需要翻译,而且不想依赖具体的类名,translate() 更灵活。你可以统一用一个上下文名称:

QCoreApplication::translate("PluginCommon", "加载中...");

4.6 避坑指南

我曾经踩过的坑:

  • 在非 QObject 类里用 tr(),编译能过,但翻译不生效。因为上下文是空的。
  • translate() 的上下文字符串必须和 .ts 文件中的一致,大小写敏感。我吃过这个亏。
  • 动态翻译时,记得把字符串转成 UTF-8,否则中文可能乱码。

4.7 知识体系图

下面这张图,帮你理清 tr() 和 translate() 的定位:

Qt 翻译机制核心 QObject::tr() QCoreApplication::translate() 静态翻译 • 编译时提取 • 自动获取上下文 • 适用于固定文本 • 性能开销小 动态翻译 • 运行时查找 • 手动指定上下文 • 适用于动态文本 • 灵活性高 适用场景:UI固定文本、菜单、按钮、标签 适用场景:数据库数据、用户输入、插件文本、非QObject类 选择原则:能静态就静态,需动态才动态

4.8 实际项目中的选择策略

说了这么多,到底怎么选?我总结了一个简单的判断流程:

  1. 文本是否在编译时已知? → 用 tr()
  2. 是否在 QObject 子类中? → 用 tr()
  3. 文本来自外部输入? → 用 translate()
  4. 需要跨模块共享上下文? → 用 translate()
  5. 不确定? → 用 translate() 更安全

我的建议:80% 的场景用 tr() 就够了。剩下 20% 的复杂场景,才需要 translate() 出马。别为了追求灵活性,把所有代码都改成 translate(),那样反而增加了维护成本。

记住,tr() 是 Qt 国际化的基石,translate() 是补充。两者配合使用,才能写出既高效又灵活的国际化代码。

可可精品资料库