Skip to content

Internationalization

ROADMAP.md 0.19.0 — a user can pick their own UI language from the Settings page instead of only ever seeing English.

Changing the language

Settings → General (/settings/general/) has a single Display language dropdown — English, Español, or Português (Brasil). It's an instance-wide preference (legendarr has a single shared admin account, no multi-user model), persisted to config.yaml the same way every other setting is, and applies to the whole dashboard on the next page load, not just the Settings page itself.

This is unrelated to a language profile's source/target languages, which set what language a subtitle's content is translated to or from — the UI locale only changes what language the dashboard's own text is written in.

Settings → General, with the Display language dropdown

How it works

legendarr_web resolves the active locale once per request (an app-wide dependency, same shape as the session-auth check) and exposes it to every template as a t(key) Jinja function. Translated strings live in flat, dot-namespaced key → string catalogs — src/web/src/legendarr_web/i18n/locales/{en,es,pt-BR}.json — with en as the reference locale; a key missing from another locale falls back to en, and a key missing everywhere falls back to the key itself, so a gap degrades to something visible instead of crashing the page.

Coverage

Every page across every slice uses t() for its UI text in this pass, including the persistent chrome (topbar, sidebar navigation) and the toast messages generated by Settings/Language-profile actions. Left untranslated on purpose:

  • Data from the backend — media titles, arr-service/provider names, error details returned by the API, log lines, and similar dynamic content.
  • The login page's locale/login is exempt from the locale-resolving backend call (it can't call an auth-gated endpoint before a session exists), so it always renders in en regardless of the saved preference, until logged in.

Adding a translation

New UI text goes through t("some.key") in the template and gets an entry added to all three locale JSON files — there's no extraction tooling yet, catalogs are hand-maintained. src/web/tests/i18n/test_translations.py fails the build if a locale file's key set drifts out of sync with en.json.