SvelteKitでi18n(多言語対応)を実装した話
SvelteKitでi18n(国際化・多言語対応)を実装しました。まずシンプルな実装で概念を理解し、本番向けのアプローチも紹介します。
シンプルなi18nの実装
1. 翻訳ファイルを作成
// src/lib/i18n.ts
export const messages = {
ja: {
title: 'TODOリスト',
add: '追加',
delete: '削除',
complete: '完了',
undo: '戻す',
placeholder: 'タスクを入力',
welcome: (name: string) => `ようこそ、${name}さん`,
},
en: {
title: 'TODO List',
add: 'Add',
delete: 'Delete',
complete: 'Complete',
undo: 'Undo',
placeholder: 'Enter a task',
welcome: (name: string) => `Welcome, ${name}`,
},
} as const;
export type Locale = keyof typeof messages;
as const をつけることで、各言語のキーが型として推論されます。関数を使うと変数を含む文字列も型安全に扱えます。
2. 言語状態をグローバルに管理
// src/lib/locale.svelte.ts
import type { Locale } from './i18n';
let locale = $state<Locale>('ja');
export function getLocale() {
return locale;
}
export function setLocale(l: Locale) {
locale = l;
}
Svelte 5の $state をモジュールレベルで使うことで、どのコンポーネントからでも同じ言語状態を参照・更新できます。
3. コンポーネントで使う
<script lang="ts">
import { messages } from '$lib/i18n';
import { getLocale, setLocale } from '$lib/locale.svelte';
const m = $derived(messages[getLocale()]);
</script>
<button onclick={() => setLocale('ja')}>日本語</button>
<button onclick={() => setLocale('en')}>English</button>
<h1>{m.title}</h1>
<p>{m.welcome('Nori')}</p>
<input placeholder={m.placeholder} />
<button>{m.add}</button>
$derived(messages[getLocale()]) で言語が変わるたびに m が自動更新されます。
シンプル実装の課題
| 課題 | 内容 |
|---|---|
| URLに言語が出ない | /en/todo のようなURL対応が必要 |
| SSR時の言語検出 | Accept-Language ヘッダーから言語を自動判定できない |
| 翻訳ファイルの管理 | 規模が大きくなるとTypeScriptファイルでは管理しにくい |
本番向け:paraglide-js(推奨)
SvelteKit公式が推奨する Inlang の paraglide-js はこれらの課題を解決します。
npx @inlang/paraglide-sveltekit init
特徴
- JSONファイルで翻訳を管理 — チームでの分業がしやすい
- URLベースの言語切り替え —
/ja/todo//en/todoのようなルート対応 - ビルド時コンパイル — 実行時のオーバーヘッドがゼロ
- Cloudflare Workers対応 — エッジ環境でも動作する
- TypeScript完全対応 — 翻訳キーが型として補完される
まとめ
- シンプルな実装は翻訳オブジェクト +
$stateで言語切り替えができる $derivedで言語が変わるたびにUIが自動更新される- 関数を使うと変数を含む文字列も型安全に扱える
- 本番ではURLベースの言語管理ができる
paraglide-jsが推奨