// OPENBOX DOCS
Localization
OpenBox ships in 5 languages. How the i18n system works, and how to add more.
Localization
OpenBox ships locale catalogs for English, Spanish, German, French, and Portuguese. The locale selector is in Settings > Interface language; switching re-translates the keyed UI surfaces without a page reload. Some operational and browser fallback labels remain English.
How it works
The i18n system uses three layers:
- Locale files: JSON files in
locales/{en,es,de,fr,pt}.jsonwith a nested key structure (e.g.nav.library,sidebar.search,settings.title). - HTML translation:
data-i18n="key"attributes on translatable elements;data-i18n-placeholder,data-i18n-title, anddata-i18n-aria-labelfor attribute translation. - JS translation:
t(key, params)fromstatic/i18n.jswith{placeholder}interpolation and automatic English fallback for missing keys.
The locale is loaded via fetch('/locales/{locale}.json') on page load, with en.json as the canonical fallback. The available locales are exposed in public_settings as available_locales.
Gate enforcement
scripts/check_i18n.py runs on every make check and verifies:
- All 5 locale files have 100% key coverage (no missing keys in any locale).
- All
data-i18nandt()references in the codebase have corresponding keys inen.json. - No locale file carries extra keys outside
en.json(check_i18n.py:9): an extra key fails the gate the same way a missing key does.
A locale file with missing keys will fail CI. This prevents shipping partial translations.
Adding a new language
- Create
locales/{code}.jsonwith the same key structure aslocales/en.json. - Add the locale code to
SUPPORTED_LOCALESinstatic/i18n.js. - Add the locale file route in
routes.pyandweb_app.py. - Add the locale to
PUBLIC_GET_PATHSinroutes.py. - Add the locale to
available_localesinpkg/state/cache.py. - Run
python3 scripts/check_i18n.pyto verify 100% key coverage. - Run
make checkto verify the full gate passes.
Each entry in available_locales carries both a plain name and a native name, but the picker renders only the native one — the option label is the native name, falling back to the plain name and then the code. So the shipped list shows English, Español, Deutsch, Français, and Português; the plain names (English, Spanish, German, French, Portuguese) exist in the payload but are never displayed. Add a native name to a new locale or its picker entry will fall back to the plain name.