Skip to main content
Urbicon UI

Provider & SSR

Mount one provider, read through useI18n(), and resolve the locale per request so server and client agree.

I18nProvider

One provider at the app root holds the single request-scoped locale state. Feed it a server-resolved locale so the first client render matches the server.

Loading...
Loading syntax highlighting...
PropType
localeLocale
fallbackLocaleLocale
onLocaleChange(locale: Locale) => void
childrenSnippet

useI18n()

The general hook for locale control and locale-aware formatting. Call it during component init and capture the result; every member reads the context locale at call time, so wrapping a read in markup or $derived re-renders on a locale switch.

Loading...
Loading syntax highlighting...
MemberSignature
localeLocale (readonly)
availableLocalesLocale[] (readonly)
isLoadingboolean (readonly)
setLocale(locale) => boolean
t(key, params?, options?) => string
plural(key, params, options?) => string
exists(key, packageName?) => boolean
formatNumber(value, options?) => string
formatDate(date, options?) => string
formatRelativeTime(value, unit) => string
formatTimeAgo(date) => string

Read-tolerant, write-strict

The contract that lets components ship before a consumer wires up i18n, while still catching a real bug:

  • Reading (locale, t, formatters) without a provider resolves against the constant base locale (en) — SSR-safe, identical on server and client.
  • Writing (setLocale) without a provider throws. There is no request-scoped state to change — the error names the fix (mount <I18nProvider>).

setLocale returns false (and reports unsupported-locale) for a locale outside SUPPORTED_LOCALES, without switching; otherwise it switches and returns true.

Switching & Persistence

setLocale mutates the request-scoped state and re-renders reactively in place — no reload. The built-in LocaleSwitcher does this for you; programmatically:

Loading...
Loading syntax highlighting...

An in-place switch lasts only for the current page session. To make the choice survive the next SSR request, persist it where resolveLocale reads — the provider's onLocaleChange is the hook:

Loading...
Loading syntax highlighting...

Root-layout chrome that itself is translated

A child <I18nProvider> can't serve the parent that mounts it — context only flows downward. When the same root component both provides i18n and renders translated chrome (header/footer), call provideI18n in its own script. Pass a reactive getter (() => data.locale) to keep it controlled: a load change flows in, while an in-place setLocale switch is never clobbered.

Loading...
Loading syntax highlighting...

SSR — resolving the initial locale

resolveLocale derives the request's locale server-side from the persisted cookie, then Accept-Language, then a default. It is framework-agnostic — pass a Request or a { cookie, acceptLanguage } object. Feed the result to the provider so SSR and the first client render agree (no hydration mismatch, no navigator.language guess).

Loading...
Loading syntax highlighting...

Fully prerendered (static) sites have no per-request server, so resolve on the client after mount instead:

Loading...
Loading syntax highlighting...

Error Handling

Loader failures and unsupported-locale switches default to console.warn. Route them to telemetry with an app-global handler — set once at startup (it lives on the process-wide registry; a per-request assignment would race under concurrent SSR).

Loading...
Loading syntax highlighting...

Coexisting with an app-level i18n (e.g. Paraglide)

If your app already uses Paraglide (or any other i18n) for its own strings, don't run two locale states — make Urbicon's provider follow the app's locale. Pass the app-i18n locale into the provider as a controlled (reactive) value:

Loading...
Loading syntax highlighting...

When the app switches language, getLocale() updates, the provider's controlled-sync pushes it into Urbicon's state, and every Urbicon component re-renders — one switch, both layers. If you also expose an Urbicon <LocaleSwitcher>, route its onLocaleChange back into the app's setLocale so the two never diverge. Map locale codes if they differ (e.g. Paraglide en-US → Urbicon en).