sveltekit-i18n Internationalization for SvelteKit

Playground

Every panel below drives a real instance through the public API. Change an input and the library answers — nothing here is a recording.

Message formats

The core holds no message format of its own: a parser fills the slot. Curly comes with this package; ICU, MessageFormat 2 and i18next are the alternatives, on the core directly.

Output

You have 1,234 messages.

No reports. A report never raises — the placeholder takes its fallback and the rest of the message resolves.

Show the code
import { I18n } from 'sveltekit-i18n';

const i18n = new I18n({
  initLocale: locale,
  translations: { [locale]: { inbox: message } },
  parserOptions: { onReport: (report) => reports.push(report) },
});

i18n.t('inbox', payload);

Preprocessing

Nested translations become the flat keys t() reads. The mode decides how far that goes.

Output

{
  "nav.home": "Home",
  "nav.about": "About",
  "tags.0": "new",
  "tags.1": "beta"
}

rawTranslations keeps the input as it arrived; translations is what you see here.

Show the code
const i18n = new I18n({ initLocale: 'en', preprocess: 'full' });

i18n.addTranslations({ en: input });

i18n.translations.en;

Loaders

A loader runs when its locale matches and its routes match the current route, once per freshness window and route params. Each loader is recorded on its own. Load the same route twice and watch the second attempt do nothing.

Pick a locale and a route, then load.

Show the code
const i18n = new I18n({
  initLocale: 'en',
  loaders: [
    // No routes: every route, so it loads once and never again.
    { locale: ['en', 'cs'], namespace: 'common', loader },
    // A string route is an exact match.
    { locale: ['en', 'cs'], namespace: 'home', routes: ['/'], loader },
    // A whole section needs a pattern.
    { locale: ['en', 'cs'], namespace: 'docs', routes: [/^\/docs(?:\/|$)/], loader },
  ],
});

await i18n.loadTranslations(locale, route);

Fallback locale

A key the active locale does not carry resolves through fallbackLocale instead of rendering empty.

Keycst(key)
titlePřekladyPřeklady
subtitle—Loaded per route
cta—Read the docs

Only the missing keys fall back. What a key missing everywhere renders as is fallbackValue.

Show the code
const i18n = new I18n({
  initLocale: 'cs',
  fallbackLocale: 'en',
  translations: { en: complete, cs: partial },
});

i18n.t('subtitle'); // the English text: 'cs' has no such key