Skip to content

Localization (v0.9.0 R5)

The desktop GUI ships with English as the source language and Russian (ru_RU) as the first translated locale. Switching the language is hot — Settings → Appearance → Language re-installs the translator and persists the choice to state.json. Restarting the app, or opening a screen for the first time after a switch, picks up the new strings.

Available locales (v0.9)

Locale Status Coverage (CTA strings) Maintainer
en_US source n/a yt-uniquifier core
ru_RU translated ≥ 50 % yt-uniquifier core

The coverage figure is the fraction of SOURCE_KEYS in gui/i18n/translations.py that have a non-empty entry in the locale's table. It is checked at CI time by tests/unit/test_i18n.py::test_coverage_ratio_ru_is_substantial, which fails if ru_RU slides below 50 %. Strings without an entry fall back to the source verbatim — by design.

Why no .ts / .qm?

The canonical Qt pipeline is pylupdate6.tslrelease.qm. v0.9 deliberately skips it:

  • the catalogue is small (≈ 40 keys) — a Python dict is faster to edit and review;
  • contributors don't need to install Qt's developer tools;
  • the wheel ships exactly the bytes a maintainer wrote, no build-time compilation step that can drift across releases.

The runtime translator (gui/i18n.RuntimeTranslator) subclasses QTranslator and overrides translate() to read out of TRANSLATIONS[locale]. QObject.tr() and QCoreApplication.translate() route through it without code changes elsewhere.

If a future locale grows past a few hundred strings the trade-off flips: migrate to .ts + .qm, keep the same public API (install_translator, available_locales), and the dict goes away as an implementation detail.

Adding a new locale

  1. Open src/yt_uniquifier/gui/i18n/translations.py.
  2. Copy the ru_RU block, rename the key to your locale code (use the POSIX form, e.g. de_DE, fr_FR).
  3. Replace each right-hand value with your translation. Leave a key out to fall back to English. Do not invent new keys — the test suite rejects entries that do not map a string in SOURCE_KEYS.
  4. Preserve mnemonic markers (&Run&Запустить). Pick a letter close to the English one; Qt highlights whatever character follows the &.
  5. Run pytest tests/unit/test_i18n.py -v. The coverage test prints your locale's percentage in its log line.

Adding a new translatable string in the GUI

  1. Wrap it in self.tr("…") (or QObject.tr("…") for module- level use).
  2. Add the exact source string to SOURCE_KEYS in gui/i18n/translations.py. Keep the list alphabetised within its commented section.
  3. Optionally add a ru_RU entry. Missing entries fall back.

Caveats

  • Strings cached on a widget that has already painted stay in the language they were rendered with until that widget rebuilds. v0.9 does not implement per-screen retranslate_ui hooks; the Settings combo flashes a status hint reminding the user to reopen screens for a full refresh.
  • Numbers and units are not currently formatted through QLocale1,234 stays comma-separated regardless of locale. v1.0 will revisit when more European locales land.
  • CLI is English-only. Translating developer-facing output (Typer help text, log lines) was explicitly deferred — the audience is global English-speaking developers and changing it would break script grep'ability.