sveltekit-i18n Internationalization for SvelteKit

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

Read the documentation GitHub

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

  1. 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';
  2. 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>

The full getting started guide