Localization
SSR-correct, package-scoped, type-safe internationalization for Svelte 5. A request-scoped locale lives in context (never a module-global singleton), reactive translations come from runes, and every Urbicon package plugs into one shared registry — all with zero runtime dependencies.
@urbicon-ui/i18n is the localization layer the design system runs on: <Pagination>, <Menu>, the data table, and the auth pages
all resolve their text through it. You can also use it for your own app strings — it is a
complete i18n solution, not just an internal helper.
It exists because the system is zero-dependency by design, and the established libraries each
miss a requirement: svelte-i18n predates Svelte 5 runes, i18next ships
a large generic runtime, and Paraglide compiles per app (so it can't ship as a reusable component-library
locale source). This package gives exactly four things:
Architecture
The package splits state by lifetime. Static translation data is
module-global — it is request-identical, so sharing it is safe. The mutable active locale is request-scoped, held in a Svelte context created by the provider. That split is what makes SSR
correct: two requests rendering de and en at the same time each read their
own locale, never a shared global.
Read-tolerant, write-strict
- Reading without a provider → the constant base
locale (
en). A provider-less<Button>renders its ARIA strings out of the box, identical on server and client (no hydration mismatch). Zero-config. - Writing (
setLocale) without a provider → throws. There is no request-scoped state to mutate — you forgot the provider. Loud by design.
Read tolerant, write strict: a component library must render before any consumer wires up i18n, but a locale switch with nowhere to store the choice is a real bug, not a default.
Quick Start
Two steps: mount a provider fed by a server-resolved locale, then read through a hook.
1. Mount the provider
2. Resolve the locale per request
resolveLocale reads the persisted cookie, then Accept-Language, then
a default — so SSR and the first client render agree.
3. Read translations
Switching language at runtime needs no reload — the built-in LocaleSwitcher calls setLocale for you. See Provider & SSR.
Guides
Provider & SSR
<I18nProvider>, the useI18n() API, locale switching &
persistence, resolveLocale, error handling, and coexisting with an app-level
i18n.Package Integration
createPackageI18n, namespaced keys, the use<Package>I18n hook pattern, the <T> component, and registering your own package.Type Safety
en bundle, eager vs. lazy parity, the DeepKeys utilities, and CI parity validation.Formatting & Plurals
Intl.PluralRules and locale-aware number, date, and relative-time
formatting.Lazy Loading
Locale Routing
/de/…) and drive it from the <LocaleSwitcher> — a SvelteKit reroute hook plus the
provider's onLocaleChange, with hreflang.API Surface
Everything is a named export from the package root.
Supported Locales
en and de ship translation data for every Urbicon package. fr, es, it, and nl are declared target
locales (in the Locale union and SUPPORTED_LOCALES) — register your
own bundles for them via createPackageI18n.
| Locale | Status |
|---|---|
| en | Ships data (base) |
| de | Ships data |
| fr | Declared |
| es | Declared |
| it | Declared |
| nl | Declared |