在本页

基于 ID 翻译的本地化时钟

该示例展示了在 CMake 和Qt Quick 中使用 Qt 基于 ID 的翻译功能的最佳实践,包括处理不同语言中的复数以及本地化的时间格式。

用户界面

该示例以您系统的语言显示当前时间和日期。支持这些语言的翻译:英语、德语、法语、西班牙语、意大利语、日语、韩语、葡萄牙语、阿拉伯语和中文。如果您的桌面使用其他语言,则会返回到英语。

该示例还接受 locale 作为命令行参数,以便在不更改系统的情况下测试不同的语言和语言区:localizedClockIdBased --locale en_GBlocalizedClockIdBased --locale de

默认情况下,应用程序以当前语言显示时间和日期,但也提供了一个按钮,用于打开一个包含时区列表的对话框。用户可以使用该对话框更改时钟的时区。

基于 ID 的翻译

在本例中,我们使用了基于 ID 的翻译,可翻译文本由唯一的 ID标识,而不是传统的上下文 + 文本组合(请参阅基于文本 ID 的翻译)。这种方法允许在不同上下文中重复使用翻译。我们通过共享Main.qml时区对话框(一个用 C++ 编写的基于QWidget 的表单)之间的翻译来演示这一点。基于 ID 的翻译的另一个方面是,它将用户界面中显示的文本与开发人员分离开来,使源代码独立于呈现给用户的实际文字。

在下面两张英文应用截图中,主窗口(QML)中 "Select time zone:在主窗口(QML)和对话框(C++)中的文本 "选择时区:"通过使用相同的 ID 共享相同的翻译。

英文版应用程序窗口截图:

显示时区列表的对话框(英文版):

德语版应用程序主窗口:

显示时区列表的对话框(德语版):

执行

应用程序由五个部分组成:

CMakeLists.txt

应用程序的 CMake 文件支持 Qt 基于 ID 的翻译和本地化。以下是相关部分:

find_package(Qt6 REQUIRED COMPONENTS Core Linguist Qml Quick):查找并链接国际化所需的 Qt 6 模块,包括Linguistqt_standard_project_setup(): 设置国际化系统,以支持列出的本地化。虽然源语言是英语,但在使用基于 ID 的翻译时仍需要英语翻译。源代码只包含 ID,看不到源文本。因此,我们需要在项目中设置英文翻译,以便 qt_add_translations 为英文创建 TS 文件;否则,运行时就会丢失它们。

qt_standard_project_setup(REQUIRES 6.8
    I18N_TRANSLATED_LANGUAGES de ar ko zh ja fr it es pt en)

qt_add_translations(...):捆绑lupdatelrelease 的功能,在 "i18n "目录下生成翻译源文件(TS 文件),以clock 作为基名,如果包含翻译,则将其编译为二进制.qm 文件。

  • I18N_TRANSLATED_LANGUAGES 中列出的每种语言生成一个 TS 文件,qt_standard_project_setup
  • MERGE_QT_TRANSLATIONSQT_TRANSLATION_CATALOGS qtbase 中包含 Qt 翻译的项目。这是翻译时区对话框QDialog widget 的按钮所必需的。由于这些按钮上的文本是由QDialog 控制的,如果不包含 Qt 翻译,这些按钮就不会被翻译(请参阅基于 ID 的翻译中德语截图中对话框的翻译文本)。
qt_add_translations(localizedClockIdBased
    TS_FILE_BASE i18n/clock
    MERGE_QT_TRANSLATIONS
    QT_TRANSLATION_CATALOGS qtbase
    RESOURCE_PREFIX i18n
)

qt_add_qml_module(...):在 URIqtexamples.localizedclock 下添加 QML 模块,包括Main.qml文件,并将时区管理器的源文件和头文件导入 QML 模块。

qt_add_qml_module(localizedClockIdBased
    URI qtexamples.localizedclock
    VERSION 1.0
    QML_FILES
        Main.qml
    RESOURCES dialog.ui
    SOURCES
        timezonemanager.h timezonemanager.cpp
        dialog.h dialog.cpp
)
main.cpp

应用程序的起点。该部分负责设置本地语言、安装所需的翻译和加载用户界面。下面是相关代码的解释:

定义 locale 参数,例如--locale en_US--locale de_DE

    QCommandLineParser parser;
    QCommandLineOption localeOption("locale"_L1, "Locale to be used in the user interface"_L1,
                                    "locale"_L1);
    parser.addOption(localeOption);
    parser.addHelpOption();
    parser.process(app);

解析参数,获取所提供的 locale,并将输入的 locale 设置为应用程序的默认 locale:

        QLocalelocale(parser.value(localeOption));        qInfo() << "Setting locale to" << locale.name();
        QLocale::setDefault(locale);

安装英语翻译,而不考虑本地语言,以允许其他语言的不完整翻译。QTranslator 按照安装翻译的相反顺序查询文本的翻译:

    QTranslatorenPlurals;const autoenPluralsPath= ":/i18n/clock_en.qm"_L1;if(!enPlurals.load(enPluralsPath))        qFatal("Could not load %s!", qUtf8Printable(enPluralsPath));
    app.installTranslator(&enPlurals);

根据给定的本地语言安装翻译:

    QTranslator翻译;if(QLocale().language()!=QLocale::English) {if(translation.load(QLocale(), "clock"_L1, "_"_L1, ":/i18n/"_L1)) {            qInfo("Loading translation %s",
                  qUtf8Printable(QDir::toNativeSeparators(translation.filePath())));if(!app.installTranslator(&translation))                qWarning("Could not install %s!",
                         qUtf8Printable(QDir::toNativeSeparators(translation.filePath()))); }else{            qInfo("Could not load translation to %s. Using English.",
                  qUtf8Printable(QLocale().name())); } }

由于我们在上一步中安装了英文翻译,因此最终可能会安装两个翻译。Qt 会使用最近安装的翻译来处理任何重叠的键。因此,本地特定翻译将优先于英文翻译,如果缺少任何翻译,QTranslator 将返回到英文翻译。

时区对话框

该类是一个基于 C++QWidget 的对话框 (QDialog) ,显示一个包含时区列表的QComboBox 。下面是对代码的解释:

在用户界面窗体 (dialog.ui) 中启用基于 ID 的翻译:

<ui version="4.0" idbasedtr="true">

使用基于 ID 的翻译设置标题 (dialog.ui)。这里,"title "是翻译的唯一 ID:

<property name="windowTitle">
    <string id="title">Time Zone</string>
</property>

添加基于 ID 翻译的标签(dialog.ui),ID 为 "timezonelabel":

<widget class="QLabel" name="label">
...
<property name="text">
<string id="timezonelabel">Select time zone</string>
</property>
...
</widget>
时区管理器

C++ 中负责处理时区变化的QML_SINGLETON 类。

用户选择时区后,TimeZoneManager 的实例会记住所选的时区:

        connect(m_dialog.get(), &Dialog::timeZoneSelected, this, &TimeZoneManager::setTimeZone);

更新时区时会发出 TimeZoneManager::timeZoneChanged 信号:

        connect(m_dialog.get(), &Dialog::timeZoneSelected, this, &TimeZoneManager::setTimeZone);
    }
    m_dialog->show();
}

TimeZoneManager::currentTimeZoneOffsetMs() 标记为Q_INVOKABLE ,并返回所选时区的时间偏移。由于TimeZoneManager 类是用QML_ELEMENTQML_SINGLETON 声明的,因此可直接从 QML 访问该方法以更新显示的时间;另请参阅Main.qml

qint64 TimeZoneManager::currentTimeZoneOffsetMs()
{
    const QTimeZone tz(m_timeZone.toLatin1());
    if (!tz.isValid())
        return 0;
    const QDateTime nowUtc = QDateTime::currentDateTimeUtc();

    const int targetOffset = tz.offsetFromUtc(nowUtc);
    const int systemOffset = QTimeZone::systemTimeZone().offsetFromUtc(nowUtc);

    return (targetOffset - systemOffset) * 1000;
}
Main.qml

主 QML 文件定义了应用程序的用户界面。用户界面显示时间、日期、当前时区和秒计数器。它还提供了一个按钮,用于打开时区对话框以更改时区。下面是相关代码的解释:

使用qsTrId() 定义窗口并设置其标题,以便进行基于 ID 的翻译。使用元字符串符号//% 指定源语言文本(请参阅基于文本 ID 的翻译)。lupdate 会解析元字符串,并将定义的源文本写入 TS 文件。由于源文本是使用元字符串并以注释的形式指定的,应用程序在运行时看不到它们。因此,当应用程序在英语区域加载时,即使源语言是英语,它仍然需要安装英语翻译来显示英语文本。否则,就会显示原始 ID "Main-Digital-Clock"。这也是我们在I18N_TRANSLATED_LANGUAGES 中通过CMakeLists.txt 设置指定 "en "的原因。

//% "Digital Clock"
title: qsTrId("Main-Digital-Clock")

使用qsTrId() 显示支持复数的秒数。同样,这里使用//% 元字符串指定源语言文本。通过在元字符串的源文本中使用特殊符号"%n",可以启用复数形式(请参阅处理复数形式)。根据 n 的值,翻译功能会返回不同的译文,并为目标语言提供正确的语法编号。例如,在英语中,如果root.seconds 的值大于 1,则使用复数形式,否则使用单数形式。在复数形式的翻译规则中,您可以找到不同语言的复数规则。

//% "%n second(s)"
text: qsTrId("Main-n-second-s", root.seconds)

使用基于 ID 的翻译显示当前选择的时区, //% "Time zone: " 指定源语言文本:

//% "Time zone: "
text: qsTrId("timezone") + TimeZoneManager.timeZone;

打开时区对话框的按钮。按钮上的文本是使用qsTrId() 指定的,用于基于 ID 的翻译。在这种情况下,源文本不再由元字符串定义,因为这是重复使用之前在 dialog.ui 文档中使用的 ID "timezonelabel"(请参阅时区对话框)。在基于 ID 的翻译中,只需在项目中为每个 ID 指定一次源文本即可:

        Button {
            text: qsTrId("timezonelabel")
            onClicked: TimeZoneManager.openDialog()
            Layout.alignment: Qt.AlignHCenter
        }

在更改时区并接收到TimeZoneManager::timeZoneChanged() 信号时(请参阅时区管理器),用选定时区的时间偏移更新diff 变量:

    Connections {
        target: TimeZoneManager
        function onTimeZoneChanged() {
            root.diff = TimeZoneManager.currentTimeZoneOffsetMs();
        }
    }

声明一个每秒触发并更新时间、日期和秒属性的定时器。定时器通过将当前时间与所选时区的时间偏移量相加来计算时间:

    Timer {
        interval: 1000
        running: true
        repeat: true
        triggeredOnStart: true

        onTriggered: {
            const now = new Date(new Date().getTime() + root.diff);
            const locale = Qt.locale();
            root.time = now.toLocaleTimeString(locale, Locale.ShortFormat);
            root.date = now.toLocaleDateString(locale);
            root.seconds = now.getSeconds();
        }
    }

地域会影响日期和时间的显示方式。日期和时间会根据当前地区的国家惯例进行格式化。例如,德国地区使用 24 小时时间和DD.MM. YYYY日期格式,而美国地区使用 12 小时时钟和MM/DD/YYYY日期格式。

示例项目 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.