Svelte/SvelteKit 多语言配置指南
方案对比
| 方案 | 适用场景 | 复杂度 |
|---|---|---|
| svelte-i18n | 纯 Svelte 应用 | 低 |
| typesafe-i18n | 类型安全优先 | 中 |
| 自定义 Store | SvelteKit 全栈 | 低 |
| paraglide-js | 编译时优化 | 中 |
方案一:自定义 Store(推荐 SvelteKit)
最轻量的方案,无需额外依赖。
1. 目录结构
src/lib/i18n/
├── translations.ts # 翻译数据
├── index.ts # 导出接口
└── locales/
├── zh.ts # 中文
└── en.ts # 英文
2. 翻译文件
// src/lib/i18n/locales/zh.ts
export const zh = {
nav: {
home: '首页',
about: '关于'
},
welcome: '欢迎'
};
// src/lib/i18n/locales/en.ts
export const en = {
nav: {
home: 'Home',
about: 'About'
},
welcome: 'Welcome'
};
3. 核心实现
// src/lib/i18n/translations.ts
import { zh } from './locales/zh';
import { en } from './locales/en';
export const translations = { zh, en };
export type Language = keyof typeof translations;
// 检测浏览器语言
export function detectLang(): Language {
if (typeof navigator === 'undefined') return 'zh';
const lang = navigator.language.toLowerCase();
return lang.startsWith('zh') ? 'zh' : 'en';
}
// src/lib/i18n/index.ts
import { writable, derived } from 'svelte/store';
import { translations, detectLang, type Language } from './translations';
// 当前语言
export const currentLang = writable<Language>(detectLang());
// 翻译函数
export const t = derived(currentLang, ($lang) => {
return (key: string) => {
const keys = key.split('.');
let value: any = translations[$lang];
for (const k of keys) {
value = value?.[k];
}
return value || key;
};
});
// 切换语言
export function setLang(lang: Language) {
currentLang.set(lang);
if (typeof localStorage !== 'undefined') {
localStorage.setItem('lang', lang);
}
}
4. 组件中使用
<!-- +layout.svelte -->
<script>
import { currentLang, t, setLang } from '$lib/i18n';
</script>
<nav>
<a href="/">{$t('nav.home')}</a>
<a href="/about">{$t('nav.about')}</a>
<button on:click={() => setLang($currentLang === 'zh' ? 'en' : 'zh')}>
{$currentLang === 'zh' ? 'EN' : '中文'}
</button>
</nav>
方案二:svelte-i18n
适合已有项目快速接入。
npm install svelte-i18n
// src/i18n.ts
import { register, init, getLocaleFromNavigator } from 'svelte-i18n';
register('zh', () => import('./locales/zh.json'));
register('en', () => import('./locales/en.json'));
init({
fallbackLocale: 'zh',
initialLocale: getLocaleFromNavigator()
});
<script>
import { _ } from 'svelte-i18n';
</script>
<h1>{$_('welcome')}</h1>
方案三:URL 路由级多语言(SvelteKit)
SEO 友好的方案,语言体现在 URL 中。
// src/params/lang.ts
import type { ParamMatcher } from '@sveltejs/kit';
export const match: ParamMatcher = (param) => {
return ['zh', 'en'].includes(param);
};
src/routes/
├── [[lang]]/ # 可选语言前缀
│ ├── +page.svelte
│ └── about/
│ └── +page.svelte
└── +layout.ts # 加载翻译
// src/routes/+layout.ts
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = ({ params }) => {
const lang = params.lang || 'zh';
return { lang };
};
关键决策点
| 场景 | 推荐方案 |
|---|---|
| 快速上线 | svelte-i18n |
| 类型安全 | typesafe-i18n |
| SEO 优先 | URL 路由级 |
| 极简依赖 | 自定义 Store |
| 大型应用 | paraglide-js |
最佳实践
- 延迟加载:翻译文件按需加载,不要打包进主 bundle
-
SSR 兼容:
onMount中访问localStorage,避免服务端报错 - 回退机制:找不到翻译时显示 key 或默认语言
- 类型安全:为翻译 key 定义类型,获得 IDE 提示
// 类型安全示例
import type { zh } from './locales/zh';
type Paths<T, D extends string = ''> = ... // 递归生成路径
type TranslationKey = Paths<typeof zh>;
export function t(key: TranslationKey): string;