sveltekit-i18n
Internationalization for SvelteKit
npx sv add @sveltekit-i18n
# or by hand:
npm install sveltekit-i18n
# bun add sveltekit-i18n
# deno add npm:sveltekit-i18n Svelte 5 and Node 22, Bun 1.2 or Deno 2 or newer. ESM only.
Why sveltekit-i18n
- Built for SvelteKit sveltekit-i18n/kit wires hooks, layouts and components: an instance per request on the server, so one visitor's language never reaches another visitor's page, and one per tab in the browser.
- One install The core and the Curly parser come with it. Nothing else to add, and only one reactive graph.
- Loads what a route needs A loader fires when both its locale and its route match, once per freshness window and route params.
- One reactive instance Svelte 5 runes instead of stores: properties you read, methods you await.
- Typed keys and payloads A schema slot narrows what t() and l() accept, down to each message's parameters. The @sveltekit-i18n/typegen Vite plugin fills it from your translations.
- Extensible The extensions pipe folds further surfaces onto the instance — Svelte stores, keys as members of the t() function, or HTML markup rendered as elements and components.
Quick start
Describe the translations your app loads, and wire them into SvelteKit.
// src/lib/i18n.js import { defineI18n } from 'sveltekit-i18n/kit'; export const config = { fallbackLocale: 'en', loaders: [ { locale: ['en', 'cs'], namespace: 'common', loader: async ({ locale, namespace }) => (await import(`./translations/${locale}/${namespace}.json`)).default, }, ], }; export const { handle, load, use, get } = defineI18n(config, { preferredLocale: (event) => event.cookies?.get('lang'), }); // src/hooks.server.js — fills <html lang="%lang%" dir="%dir%"> in src/app.html export { handle } from '#lib/i18n.js'; // src/routes/+layout.server.js and src/routes/+layout.js export { load } from '#lib/i18n.js';Read them off the instance. The text follows the active locale.
<!-- src/routes/+layout.svelte --> <script> import { use } from '#lib/i18n.js'; let { data, children } = $props(); use(() => data); </script> {@render children()} <!-- any component below it --> <script> import { get } from '#lib/i18n.js'; const i18n = get(); </script> <h1>{i18n.t('common.greeting', { name: 'World' })}</h1> <button onclick={() => { i18n.locale = 'cs'; }}> {i18n.t('common.switch')} </button>