Internationalization
Use internationalization with the current published Askr packages.
@askrjs/i18n0.0.2
How to use internationalization
Create the platform service at the application composition root and pass its provider-neutral contract into routes or components.
- Import the published @askrjs/i18n entrypoint.
- Keep internationalization configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { createI18n } from '@askrjs/i18n';
export const i18n = createI18n('en', {
en: { greeting: (name: string) => 'Hello, ' + name },
fr: { greeting: (name: string) => 'Bonjour, ' + name },
});
<i18n.Scope locale="en">
{i18n.text('greeting', 'Askr')}
</i18n.Scope>Locale ownership
`@askrjs/i18n` deliberately doesn't decide which locale is active — no cookie parsing, no `Accept-Language` sniffing, no global state. Your app resolves the locale however it wants (URL prefix, subdomain, user profile) and installs it by rendering `<i18n.Scope locale="fr" dir="ltr">` around the relevant subtree. Because `Scope` is a component rather than a module-level singleton, you can nest scopes and run several locales in the same tree if you need to.
Message lookup
Catalog entries are typed functions, not string templates — `welcome: ({ name }: { name: string }) => ...` — so `i18n.text('welcome', { name: 'Ada' })` is checked against the exact parameter type of that message. `CatalogKey` and `MessageArgs` are derived automatically from your catalogs, so autocomplete and argument checking work without any codegen step. `i18n.format(locale, key, ...args)` gives you the same lookup for a specific locale rather than whichever one is currently scoped.
Formatting
Number, date, and currency formatting isn't a separate API — it's just `Intl` calls written inside your catalog functions, as shown in the README's `total` message using `Intl.NumberFormat`. This keeps formatting rules colocated with the locale that defines them instead of scattered through a generic formatter service, and it means anything `Intl` supports is available with no wrapper API to learn.
SSR consistency
`i18n.dehydrate()` captures the active locale, text direction, and catalog identity as a versioned snapshot (`I18nHydration`), which you serialize into the server-rendered page. On the client, pass that same snapshot to `<i18n.Scope hydration={snapshot}>` instead of `locale`/`dir`, and the two render passes stay in sync without re-deriving the locale from scratch in the browser. `I18nScopeProps` enforces this at the type level — you provide either `locale`/`dir` or `hydration`, never a mix of both.