Internationalization
UI strings go through Paraglide JS, which compiles messages/<locale>.json into typed functions
at build time.
import * as m from '$lib/paraglide/messages';
m.some_key();
This is separate from the operator-facing multilingual config values (DESCRIPTION,
TOPBAR.TITLE, …), which are resolved at runtime by resolveI18nValue().
Rules
- Key format is
underscore_separated—menu_graph_panel, notmenu.graphPanel. - Generated code in
src/lib/paraglide/is never hand-edited, and is gitignored. - Locales are registered in
project.inlang/settings.json(baseLocale+locales). The Vite plugin regeneratessrc/lib/paraglide/on dev, build and test. - Switch language with
setLocale('ko')(alsogetLocale,locales) from$lib/paraglide/runtime.
Paraglide's compiler falls back to baseLocale (en) per key automatically — no build error, no
runtime crash. Contributors can add partial translations; untranslated strings just render in English
until someone fills them in.
npm run check does not go through Vite, so on a fresh clone $lib/paraglide/* would have no
type declarations. check therefore runs npm run i18n:compile first — the Paraglide CLI with the
same options as the Vite plugin, so keep the two in sync. prepare runs it too, so a plain npm ci
leaves the tree typecheckable.
Adding a language
- Add the locale code to
localesinproject.inlang/settings.json. - Create
messages/<locale>.json.
That's it — the MiscPanel picker reads locales and labels them via Intl.DisplayNames, so no
component edit is needed. Keys fill in over time; see the fallback note above.
Translation contribution flow
Translators never touch messages/<locale>.json directly. They edit a generated staging file that
contains only the keys still needing work:
| Command | Who | What it does |
|---|---|---|
npm run i18n:check | anyone | Reports missing / stale keys per locale. Changes nothing. |
npm run i18n:missing | maintainer | Writes messages/_missing_translations_<locale>.json — the gap keys, pre-filled with the en source text. |
npm run i18n:apply | maintainer | Merges those staging files back into messages/<locale>.json (in en key order) and deletes them. |
- A key is added to
messages/en.json→ runnpm run i18n:missingto regenerate the staging files. - A translator edits
messages/_missing_translations_ko.jsonand opens a PR. Keys they're unsure about can be left out entirely — those keep falling back to English. - After merging the PR, run
npm run i18n:applyand commit the result.
i18n:apply skips keys not present in en.json and keys left blank, and preserves stale keys rather
than dropping them. Use --dry-run to preview.
Paraglide ignores _missing_translations_*.json — it only compiles the locales listed in
project.inlang/settings.json.
Run npm run i18n:check after adding, renaming or removing keys in messages/en.json. It is
informational and non-blocking, but it is the fastest way to see what a change costs translators.