Internationalization
Aglaïa is localized with Qt Linguist (PySide6), not gettext. English
(en_US) and French (fr_FR) ship today.
Layout
aglaia/i18n/ __init__.py install_translator(app, lang_pref) — loads a .qm at startup aglaia_en_US.ts source catalogue (Qt Linguist XML) aglaia_fr_FR.ts source catalogue qm/ compiled .qm binaries (generated; loaded at runtime)User-facing strings are wrapped in self.tr("…") (or
QCoreApplication.translate). The active locale comes from the
language config key (KEY_LANGUAGE: "" = follow QLocale.system(),
else "en_US" / "fr_FR"), applied before the first widget is built.
Workflow
-
Extract / update after wrapping new strings:
Terminal window scripts/i18n_extract.sh # pyside6-lupdate → updates the .ts catalogues -
Translate — edit the
.tsfiles in Qt Linguist (pyside6-linguist) or by hand; fill each<translation>and drop thetype="unfinished". -
Compile to the binaries the runtime loads:
Terminal window scripts/i18n_compile.sh # pyside6-lrelease → aglaia/i18n/qm/*.qm
To add a locale, add its .ts to scripts/i18n_extract.sh’s TS_FILES,
translate, compile, and add it to SUPPORTED_LOCALES in
aglaia/i18n/__init__.py so Settings offers it.
Shipping them
A compiled catalogue has to be in three lists, and they are maintained separately:
| list | what it feeds |
|---|---|
scripts/i18n_compile.sh | generates aglaia/i18n/qm/*.qm |
pyproject.toml [tool.setuptools.package-data] | the wheel (pip install aglaia) |
Aglaia.spec datas | the frozen macOS app |
This paragraph used to assert the third one and be wrong: the .qm files
were in the wheel but not in the spec, so the packaged app had no app
catalogue at all. install_translator loaded nothing, every string fell
back to the en-US source, and Settings still said Français. Nothing reported
it, because QTranslator.load() returning False was not checked (#170).
Two guards now. install_translator logs the miss, naming the locale and the
directory it looked in — but only for a locale we actually ship or the user
explicitly chose, so following a system locale we do not translate stays
silent. And tests/test_i18n_catalogues.py fails if a locale in
SUPPORTED_LOCALES has no .ts or .qm, if either ship list stops naming
i18n/qm, or if French has an unfinished string — it is the project’s own
second language, so a gap there is an oversight, not a backlog.
That guard sits in tests/, not tests/gui/, and reads SUPPORTED_LOCALES
with ast rather than importing aglaia.i18n: that module imports PySide6,
which the CI test job does not install, and a guard that skips on CI does not
guard the thing CI builds.