Setup
Every step below is required for the configuration this site runs — locale in the URL, remembered in a cookie between visits. Follow them in order and the last one puts a translated line on the page.
Set up with the Svelte CLI #
In an existing SvelteKit 3 project, one command does steps 1–5 below:
npx sv add svelte-dialect # or: bunx sv add svelte-dialect / pnpm dlx sv add svelte-dialectIt asks for your locales (the first one is the default) and whether the locale goes in the URL, then:
- adds svelte-dialect to dependencies;
- writes src/lib/i18n.ts with createDialect;
- writes src/lib/messages/<locale>/common.json for every locale, with one greeting to translate;
- exports handle from src/hooks.server.ts, wrapping a handle you already have in sequence(), and, with the locale in the URL, reroute from src/hooks.ts;
- adds the lang and dir placeholders to the <html> tag in src/app.html.
Files you already have are left as they are. To skip the prompts, pass the answers inline:
npx sv add svelte-dialect=locales:en,de+routing:yesThen render a message as in step 6. The sections below are the same setup done by hand.
Install #
npm install svelte-dialect # or: bun add svelte-dialect / pnpm add svelte-dialectRequires Svelte 5 and, for this setup, SvelteKit 3. Without SvelteKit, import createDialect from svelte-dialect instead and skip the hooks: messages, t() and lazy namespaces work the same, while URL routing and server-side negotiation need Kit.
1. Write a message #
One JSON file per locale and namespace, under src/lib/messages/<locale>/<namespace>.json. The value is MessageFormat 2; add a ru/common.json with the same key for a second language.
{ "greeting": "Hello, {$name}!" }2. Create the dialect #
Once, in src/lib/i18n.ts. routing puts the locale in the URL; without it every export below still works, just against a cookie-only setup with no prefix.
import { createDialect } from 'svelte-dialect/kit';
export const { t, handle, reroute, localizeHref, locales } = createDialect({
messages: import.meta.glob('./messages/**/*.json', { eager: true }),
defaultLocale: 'en',
routing: true
});3. The handle hook #
SvelteKit calls this on every request. It negotiates the locale — the URL prefix first, then the cookie, then Accept-Language — and writes it into the response as the page streams out.
import type { Handle } from '@sveltejs/kit/hooks';
import { handle as dialect } from '#lib/i18n.js';
export const handle: Handle = dialect;4. The reroute hook #
Goes in src/hooks.ts — universal, not hooks.server.ts. Strips the prefix before SvelteKit matches routes, so /ru/about renders src/routes/about with no [locale] directory to add.
import type { Reroute } from '@sveltejs/kit/hooks';
import { reroute as dialect } from '#lib/i18n.js';
export const reroute: Reroute = dialect;5. The document #
handle fills these two placeholders as the page renders.
<html lang="en" dir="ltr">6. Use it #
Import t anywhere. Start the dev server: the page reads “Hello, world!”, and /ru reads the Russian line once ru/common.json exists.
<script lang="ts">
import { t } from '#lib/i18n.js';
</script>
<p>{t('common:greeting', { name: 'world' })}</p>Next #
With that in place: write more message files, then read how the URL prefix and the cookie decide which locale a visitor sees.