阅读视图

发现新文章,点击刷新页面。

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

最佳实践

  1. 延迟加载:翻译文件按需加载,不要打包进主 bundle
  2. SSR 兼容onMount 中访问 localStorage,避免服务端报错
  3. 回退机制:找不到翻译时显示 key 或默认语言
  4. 类型安全:为翻译 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;
❌