Skip to content

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().

  • Key format is underscore_separated — menu_graph_panel, not menu.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 regenerates src/lib/paraglide/ on dev, build and test.
  • Switch language with setLocale('ko') (also getLocale, locales) from $lib/paraglide/runtime.

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.

  1. Add the locale code to locales in project.inlang/settings.json.
  2. 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.

Translators never touch messages/<locale>.json directly. They edit a generated staging file that contains only the keys still needing work:

CommandWhoWhat it does
npm run i18n:checkanyoneReports missing / stale keys per locale. Changes nothing.
npm run i18n:missingmaintainerWrites messages/_missing_translations_<locale>.json — the gap keys, pre-filled with the en source text.
npm run i18n:applymaintainerMerges those staging files back into messages/<locale>.json (in en key order) and deletes them.
  1. A key is added to messages/en.json → run npm run i18n:missing to regenerate the staging files.
  2. A translator edits messages/_missing_translations_ko.json and opens a PR. Keys they’re unsure about can be left out entirely — those keep falling back to English.
  3. After merging the PR, run npm run i18n:apply and 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.

The Frequency Tutorial overlay’s labels are ordinary UI strings, one _name / _desc pair per band, keyed tutorial_freq_<band>_name and tutorial_freq_<band>_desc, where the bands are sub_bass, bass, lower_mids, upper_mids, presence and brilliance:

{
"tutorial_freq_sub_bass_name": "Sub Bass",
"tutorial_freq_sub_bass_desc": "The Rumble, usually out of human's hearing range...",
"tutorial_freq_bass_name": "Bass",
"tutorial_freq_bass_desc": "Determines how 'fat' or 'thin' the sound is..."
}

Because they compile into the bundle, operators on a CDN or pre-built deployment cannot change them — rewording one is a source build, or a PR.