Skip to content

Documentation

System Languages (Paraglide)

English and German ship by default. Add more admin UI languages via the setup wizard or project.inlang/settings.json, then machine-translate with bun translate.

10/13/2026
5 min read Edit on GitHub

This is how an administrator adds a locale for the admin chrome. How catalogs are compiled is Language architecture. Entry translations are a separate path: Multilingual data loading.

SveltyCMS splits system language (admin chrome: menus, buttons, setup) from content language (collection fields). Content languages are any ISO 639-1 code at runtime. System languages use the same ISO picker; English and German ship compiled. Extra system locales are written to Inlang and machine-translated first — they are a draft until someone reviews them (Fink or a GitHub PR). Until a catalog exists, the UI stays in English and still sets html lang / dir (RTL included).

Note

Content localization (translated: true fields) is separate. This page is only the admin UI language (systemLanguage). See Language Architecture.


What ships by default

Locale Role
en Source catalog (src/messages/en.json) and Paraglide baseLocale
de Bundled translation (src/messages/de.json)
fr es it nl pl Seeded EU pack (scripts/seed-language-packs.ts) — chrome overlay + English fallback
ar Seeded Arabic catalog; RTL via getTextDirection()

RTL layout uses getTextDirection() in @utils/string (ar, he, fa, ur, …). Selecting Arabic as a system language sets html dir="rtl" even before a catalog is compiled.


Add a language in System Settings (after install)

Config → System Settings → Languages & Localization (/config/system-settings?group=languages):

  1. Add any ISO 639-1 code to System Languages (same chip + search UI as content languages). RTL codes show an RTL badge.
  2. Set Default System Language if needed.
  3. Save. The page writes project.inlang/settings.json, creates src/messages/{locale}.json if missing, then runs bun translate and bun run paraglide.
  4. Switch language from the sidebar. Leave the dev server running so Vite picks up src/paraglide/.

See System Settings.

Add a language in Setup

On first-run System configuration, add ISO codes to System Languages. Completing setup runs the same Inlang write + machine-translate path.


Add a language by hand (Git / community)

// project.inlang/settings.json
{
  "$schema": "https://inlang.com/schema/project-settings",
  "baseLocale": "en",
  "locales": ["en", "de", "fr"],
  "modules": [
    "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4.5.0/dist/index.js",
    "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.3.0/dist/index.js"
  ],
  "plugin.inlang.messageFormat": {
    "pathPattern": "./src/messages/{locale}.json"
  }
}

Then:

bun translate          # machine-fill src/messages/fr.json from English
bun run paraglide      # compile src/paraglide/

bun translate fills missing keys with a machine translation (or leaves English if the service is unavailable). That is not the same as a human-reviewed pack. Treat new catalogs as a draft:

  1. Review src/messages/{locale}.json (terminology, length, formal/informal).
  2. Open a PR, or edit in Fink.
  3. Ship the reviewed file in git so the next build does not depend on the translate service.

Catalogs live in src/messages/{locale}.json — not under src/paraglide/ (that folder is generated).


Dev server: first-run compile

vite dev compiles every configured locale once and caches the result in project.inlang/cache/paraglide-js/. The first start after a fresh install, a cleared cache, or any catalog / settings.json change is the slow one — the compile is ~linear in messages, so the full admin catalog (~3.4k keys × N locales) is the cost. Every later start logs Compilation skipped — inputs unchanged and is instant.

A cold start is announced in the dev log before the pause:

[SveltyCMS] ℹ️ Paraglide is compiling 8 admin locales (27,072 messages) — first run only; later starts skip it.

Fewer locales = faster first start: a stock install (EN/DE) compiles in ~2s, while all eight seeded packs cost ~7s. See What ships by default above.


Workstream status (P0–P4)

Not implemented as a finished pack. Current order and snapshot: Roadmap — System language P0–P4.

Pri Item Now
P0 Key remaining admin chrome + hardcoded-copy CI Open
P1 FR, ES, IT, NL, PL + AR (reviewed) Open (en/de only)
P2 No compile cliff after Settings save Partial (toast only)
P3 Admin + content translation cockpit 🟢 Shipped
P4 Buyer-facing UWG copy Partial

Non-goals: do not switch to i18next; do not advertise dozens of picker codes while P0 is open.

When the admin catalog is stable (planned)

Today EN/DE ship compiled; extra locales are added one-by-one. Once admin chrome is fully keyed in Paraglide (no leftover English in the GUI), the same pipeline can fill many locales in one pass:

  1. Add the target ISO codes to project.inlang/settings.json locales (or via System Settings). Keep baseLocale — sourceLanguageTag/languageTags are deprecated.
  2. bun translate — machine-fill every missing key in every new src/messages/{locale}.json.
  3. bun run paraglide.
  4. The SveltyCMS Fink project is the community review surface: translators correct machine drafts in the browser; changes land as git PRs on src/messages/.

Machine output is the seed. Fink + git is the lektorat. EN/DE stay the maintained source pair; other locales improve in the open as the product stabilizes.

Tracked on the 2026 roadmap.


How messages are used in the admin UI

<script lang="ts">
	import { button_save, button_cancel } from '@src/paraglide/messages';
</script>

<button type="button">{button_save()}</button>
<button type="button">{button_cancel()}</button>

Until a catalog is compiled, uncompiled locales keep English copy and still apply lang + dir (RTL included).


Related

i18nparaglideinlangsetuprtl
Was this page helpful?