i18n / RTL (1.13 / 1.45 / 1.54 / 2.12 / 2.20 full Gallery switch / 2.35 wave 4)¶
Gallery and starter examples wrap UI strings in qsTr. This page covers:
- Extracting / shipping
.ts→.qmcatalogs (~3600 Gallery strings) - Live locale switch in Gallery (
GalleryLanguage) or--langat startup - RTL via
LayoutMirroringwithout breaking LTR
| Surface | Where |
|---|---|
| Gallery Settings | Display language — live switch + persist |
| Gallery demo | Layout → i18n / RTL — same GalleryLanguage API |
| Gallery Settings | Right-to-left layout toggle (separate from translation) |
| Catalogs | src/gallery/translations/ — en, zh_CN, ja_JP, ko_KR, de_DE (2.35) |
qsTr + Linguist workflow (1.45)¶
1. Mark strings¶
Use qsTr("…") in QML and tr() / QCoreApplication::translate in C++ for user-visible text.
2. Extract (full Gallery)¶
lupdate src/gallery -ts src/gallery/translations/qwinui3_gallery_en.ts ^
src/gallery/translations/qwinui3_gallery_zh_CN.ts ^
src/gallery/translations/qwinui3_gallery_ja_JP.ts ^
src/gallery/translations/qwinui3_gallery_ko_KR.ts ^
src/gallery/translations/qwinui3_gallery_de_DE.ts -no-obsolete
lupdate src/gallery \
-ts src/gallery/translations/qwinui3_gallery_en.ts \
src/gallery/translations/qwinui3_gallery_zh_CN.ts \
src/gallery/translations/qwinui3_gallery_ja_JP.ts \
src/gallery/translations/qwinui3_gallery_ko_KR.ts \
src/gallery/translations/qwinui3_gallery_de_DE.ts \
-no-obsolete
Each locale file holds the same ~3600 source ids. Finish translations in Qt Linguist; unfinished entries fall back to English source at runtime.
2.35 wave 4 — control page rules: Gallery pages for 2.21…2.34 should use title: qsTr(...). Run lupdate after adding pages.
4. Translate + release¶
copy src\gallery\translations\qwinui3_gallery_en.ts src\gallery\translations\qwinui3_gallery_de.ts
linguist src\gallery\translations\qwinui3_gallery_de.ts
lrelease src\gallery\translations\qwinui3_gallery_de.ts -qm src\gallery\translations\qwinui3_gallery_de.qm
Shipped seeds: zh_CN (1.45), ja_JP (1.54), ko_KR (2.12), and de_DE (2.35) cover the same extracted catalog (~3600 strings). zh_CN is the reference filled locale; other seeds may stay partially unfinished until a Linguist pass.
Localization wave 4 (2.35)¶
| Item | Detail |
|---|---|
| Fourth seed locale | de_DE — German catalog copied from en extract; selectable in Settings / i18n / RTL |
| Smoke | python scripts/smoke_gallery.py |
Out: Crowdin portal; full ja_JP / ko_KR / de_DE Linguist completion (community or follow-up minors).
5. Load at runtime¶
Gallery — live switch (Settings / i18n page):
GalleryLanguage.applyLocale("zh_CN") // installs QTranslator + engine.retranslate()
GalleryLanguage.currentLocale // persisted in QSettings Gallery/uiLocale
Gallery — startup override:
Release embeds .qm under :/i18n via qt_add_translations in src/gallery/CMakeLists.txt. Dev fallback paths: translations/ beside exe, src/gallery/translations/, or QWINUI3_GALLERY_TRANSLATIONS.
Consumer main:
QTranslator tr;
if (tr.load(QLocale(QLocale::Japanese, QLocale::Japan),
QStringLiteral("myapp"),
QStringLiteral("_"),
QStringLiteral(":/i18n"))) {
app.installTranslator(&tr);
}
Gallery does not auto-pick OS language — apps own selection (CLI / Settings / installer).
Examples under examples/ also use qsTr; point lupdate at those folders the same way.
Folder details: src/gallery/translations/README.md.
Consumer lrelease recipe (2.x)¶
Ship translations in your app — not only Gallery. Pattern used by examples/gallery-shell (2.12).
1. Keep catalogs small¶
Same rule as Gallery: seed .ts files with the strings you actually ship. Run lupdate against your QML/C++ tree only:
2. CMake — qt_add_translations (Qt 6.5+)¶
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
qt_add_executable(myapp main.cpp)
qt_add_translations(myapp
TS_FILES
translations/myapp_en.ts
translations/myapp_ko_KR.ts
RESOURCE_PREFIX "/i18n"
)
lrelease runs at build time. .qm files embed under :/i18n/ when RESOURCE_PREFIX is set.
Reference: examples/gallery-shell/CMakeLists.txt.
3. Load before QML¶
#include <QTranslator>
static bool installAppTranslator(QGuiApplication &app, QTranslator *tr, const QString &lang)
{
if (lang.isEmpty())
return false;
const QString qm = QStringLiteral(":/i18n/myapp_%1.qm").arg(lang);
if (tr->load(qm)) {
app.installTranslator(tr);
return true;
}
// Packaged deploy: translations/ beside the exe (windeploy / zip layout)
return tr->load(QCoreApplication::applicationDirPath()
+ QStringLiteral("/translations/myapp_") + lang + QStringLiteral(".qm"));
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QTranslator translator;
installAppTranslator(app, &translator, argValue(argc, argv, "--lang"));
// … QQmlApplicationEngine …
}
Gallery shell demo: qwinui3_example_gallery_shell.exe --lang ko_KR after Release build.
4. Ship .qm in packages¶
| Path | When |
|---|---|
:/i18n/*.qm |
Default with qt_add_translations + RESOURCE_PREFIX |
<app>/translations/*.qm |
Optional loose files for hot-swappable language packs |
| Shared zip / vcpkg prefix | Not included — apps own .ts / .qm; kit strings stay English unless you translate control templates |
After windeployqt / linuxdeploy, copy extra .qm into translations/ if not embedded. See packaging-consumer.md.
5. CI without Qt Linguist¶
Commit .ts seeds with the Gallery tree. After adding pages, run lupdate and a Release build.
6. RTL unchanged¶
Translation and RTL stay independent — see RTL / LayoutMirroring below. Korean / Chinese / Japanese seeds do not enable RTL.
RTL / LayoutMirroring¶
StandardWindow {
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
// …
}
// Session toggle (Gallery Settings / i18n page / nav-settings Settings):
Qt.application.layoutDirection = checked ? Qt.RightToLeft : Qt.LeftToRight
Default remains LTR. RTL is session-only unless your app persists the choice.
Translation and RTL are independent: --lang zh_CN does not force RTL; Arabic apps usually enable both.
High-traffic fixes (1.13)¶
| Area | Change |
|---|---|
HeaderedTextBox / HeaderedComboBox / HeaderedContentControl |
Layout.alignment uses Qt.AlignLeading so left-header labels stay on the start edge under mirroring |
| Gallery / nav-settings shells | Root LayoutMirroring bound to Qt.application.layoutDirection |
| Accessibility demo | Removed hard-coded LTR arrow glyph in helper text |
FormLayout { fieldHeaderPlacement: "left" } + SettingsCard rows are exercised on the Gallery i18n / RTL page — enable RTL and confirm labels/actions flip.
Caption chrome note¶
System caption buttons on Windows stay OS-owned. Content under PlatformTitleBar / NavigationView follows LayoutMirroring; do not assume every decorative chevron is locale-aware yet.
RTL regression pass (1.45)¶
After adding strings or shell chrome, smoke these with Settings → Right-to-left layout:
- [ ] Gallery Home featured cards / nav rail
- [ ] i18n page FormLayout left headers + SettingsCard rows
- [ ] ContentDialog / CommandPalette (overlay centering still OK)
- [ ] ListDetailsView / TwoPaneView (master on start edge)
- [ ]
examples/nav-settingsSettings RTL toggle
Checklist for product apps¶
- Wrap strings in
qsTr/tr. - Ship
.ts/.qmvia Linguist; validate XML in CI if you keep seeds. - Install
QTranslatorbefore loading QML. - Mirror the shell when
layoutDirection === RightToLeft. - Prefer
AlignLeading/ start-edge anchors over physicalAlignLeftfor form labels. - Prefer stable-api.md controls when copying recipes.
- Keep LTR as the default path in automated tests.
Out of scope¶
Full Gallery localization into many languages, macOS-first locale packs, chart axis BiDi, renaming SettingsCard contentLeft / contentRight APIs, auto-detecting OS language inside Gallery.