Languages
The eight locales that ship, where the message files live, how the panel picks one, and what happens to a key nobody translated.
OSS Panel ships in eight languages. This page is the reference behind them — what resolves a locale, where the text lives, and how to change or extend it.
For using the switcher day to day, see Account → Language.
The eight locales
| Language | Code | Message file |
|---|---|---|
| English | en | messages/en.json |
| Español | es | messages/es.json |
| Deutsch | de | messages/de.json |
| Français | fr | messages/fr.json |
| हिन्दी | hi | messages/hi.json |
| Português | pt | messages/pt.json |
| 日本語 | ja | messages/ja.json |
| Русский | ru | messages/ru.json |
English is the default and the fallback. The list is deliberately derived from
the files: a locale appears in the switcher only when
frontend/messages/<code>.json exists.
How a locale is picked
In order, stopping at the first answer:
The NEXT_LOCALE cookie, if it holds one of the eight codes. This is what
the switcher sets, for a year.
The browser's Accept-Language header — the first entry, cut to its two
letter prefix. fr-CA therefore lands on fr.
English.
No locale in the URL
There is no /en/… or /fr/… prefix and no routing middleware. /account
is /account in every language, which is why a link is safe to paste
between two people reading the panel in different languages.
What a missing translation does
Two separate safety nets, and neither one shows the user a blank.
Per key, not per file. A locale's messages are deep-merged over English, so a partially translated language is a usable language: translated keys appear in that language, untranslated ones in English. A half-finished locale never takes a page down.
A key missing everywhere is rendered as a readable version of itself rather
than a dotted identifier — the last segment, un-camel-cased and sentence-cased,
so set_ownership becomes "Set ownership". Many keys are built from API
values, so a value the frontend has never seen produces readable text instead
of activity.actions.set_ownership.
Missing keys are logged in development and never thrown in production.
Changing or adding wording
The message files are plain JSON, one per locale, and they are bundled into the frontend at build time. So editing them is a build, not a restart:
Edit the file for the locale under /var/www/panel/frontend/messages/.
Keep the key structure identical to en.json — the merge is by key path.
Rebuild and restart the frontend.
cd /var/www/panel/frontend
npm run build
systemctl restart panel-frontendThis is also how a re-brander changes wording rather than just the logo — and
there is a shortcut for the most common case. Sentences that name the product
use a {brand} placeholder filled from
BRANDING_NAME, not a hardcoded word:
"subtitle": "Update {brand} itself. Hosted applications stay online; only this panel restarts."Setting BRANDING_NAME therefore renames the product across all eight
languages at once, with no message file to touch.
Caveats
- The choice is per browser, not per account. It lives in a cookie, so signing in from another machine starts at the default again, and there is no language field on a user's profile.
- Your own data is never translated — application names, database names, usernames, paths, log lines, command output and version numbers stay as they are.
- Dates and numbers follow the chosen language, not the server's timezone. The clock and timezone are set separately in Settings → Server.
- A locale with no message file cannot be selected, even if you add it to the switcher's name list — the list of names and the list of locales are checked against each other.
- Upgrading the panel overwrites the message files. Customised wording
needs re-applying after an update; prefer
BRANDING_NAMEwhere it is enough.