Skip to main content

SvelteKit i18n: Hướng dẫn thiết lập quốc tế hóa

Từ con số không đến ứng dụng đa ngôn ngữ: thiết lập svelte-i18n trong ứng dụng SvelteKit bằng định dạng thông điệp ICU, định tuyến theo ngôn ngữ và chuỗi dự phòng thông minh.

1

Cài đặt svelte-i18n

svelte-i18n là thư viện quốc tế hóa tiêu chuẩn cho Svelte và SvelteKit. Thư viện cung cấp store phản ứng, hỗ trợ ICU MessageFormat và tải lười ngôn ngữ ngay khi cài đặt.

svelte-i18n dùng ICU MessageFormat cho số nhiều và biến, cùng tiêu chuẩn mà FormatJS/react-intl sử dụng. Nếu chuyển từ React sang, bạn sẽ thấy cú pháp thông điệp quen thuộc.
Terminal
npm install svelte-i18n
2

Cấu hình svelte-i18n

Tạo tệp cấu hình i18n để đăng ký các ngôn ngữ bằng hàm import tải lười. svelte-i18n chỉ lấy thông điệp của một ngôn ngữ khi ngôn ngữ đó được kích hoạt.

src/lib/i18n.ts
// src/lib/i18n.ts
import { register, init, getLocaleFromNavigator } from 'svelte-i18n';

// Register locale loaders (lazy-loaded)
register('en', () => import('../locales/en.json'));
register('de', () => import('../locales/de.json'));
register('ja', () => import('../locales/ja.json'));
register('es', () => import('../locales/es.json'));

init({
  fallbackLocale: 'en',
  initialLocale: getLocaleFromNavigator(),  // Auto-detect browser language
});
Bạn phải import tệp cấu hình i18n trong +layout.svelte trước khi bất kỳ component nào kết xuất. Nếu bản dịch hiện khóa thô như 'nav.home', cấu hình đã không được import đủ sớm.

Tích hợp layout SvelteKit

Import cấu hình i18n trong layout gốc và kiểm soát việc kết xuất bằng store $isLoading. Cách này ngăn khóa chưa dịch lóe lên trong khi dữ liệu ngôn ngữ tải bất đồng bộ.

src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script>
  // Import i18n config — must run before any component renders
  import '../lib/i18n';
  import { isLoading } from 'svelte-i18n';
</script>

{#if $isLoading}
  <p>Loading translations...</p>
{:else}
  <slot />
{/if}

Định tuyến theo ngôn ngữ trong SvelteKit

Để có URL thân thiện với SEO như /en/about và /de/about, hãy dùng tham số route [lang]. Đặt ngôn ngữ svelte-i18n trong hàm load của layout dựa trên tham số URL.

SvelteKit locale routing
// src/routes/[lang]/+layout.ts
import { locale } from 'svelte-i18n';

export function load({ params }) {
  // Set the active locale from the URL parameter
  locale.set(params.lang);
  return {};
}

// src/routes/[lang]/+layout.svelte
<script>
  import '../../lib/i18n';
  import { isLoading } from 'svelte-i18n';
</script>

{#if $isLoading}
  <p>Loading...</p>
{:else}
  <slot />
{/if}

Định dạng tệp bản dịch

Tạo một tệp JSON cho mỗi ngôn ngữ. svelte-i18n hỗ trợ khóa lồng nhau và cú pháp ICU MessageFormat cho số nhiều, biến và biểu thức select.

Translation files
// src/locales/en.json
{
  "nav": {
    "home": "Home",
    "about": "About",
    "settings": "Settings"
  },
  "greeting": "Hello, {name}!",
  "cart": {
    "itemCount": "{count, plural, one {# item} other {# items}}"
  }
}

// src/locales/de.json
{
  "nav": {
    "home": "Startseite",
    "about": "Uber uns",
    "settings": "Einstellungen"
  },
  "greeting": "Hallo, {name}!",
  "cart": {
    "itemCount": "{count, plural, one {# Artikel} other {# Artikel}}"
  }
}
Đặt tên khóa theo nội dung mô tả, không theo vị trí hiển thị: 'cart.itemCount' tốt hơn 'homepageCartLabel'. Khóa nên tiếp tục dùng được sau khi thiết kế lại UI.
3

Dùng bản dịch trong component

Import store $_ (hoặc $format) từ svelte-i18n rồi dùng trong template Svelte. Store có tính phản ứng: khi ngôn ngữ thay đổi, mọi chuỗi dịch tự động cập nhật.

Component.svelte
<script>
  import { _ } from 'svelte-i18n';
</script>

<h1>{$_('greeting', { values: { name: 'World' } })}</h1>

<nav>
  <a href="/">{$_('nav.home')}</a>
  <a href="/about">{$_('nav.about')}</a>
</nav>
Formatting helpers
<script>
  import { _, date, number, time } from 'svelte-i18n';
</script>

<!-- Simple string -->
<p>{$_('greeting', { values: { name: userName } })}</p>

<!-- ICU plurals — handled automatically -->
<p>{$_('cart.itemCount', { values: { count: 3 } })}</p>

<!-- Date formatting -->
<p>{$date(new Date(), { format: 'long' })}</p>

<!-- Number formatting -->
<p>{$number(1999.99, { style: 'currency', currency: 'USD' })}</p>
$_ là một store Svelte, vì vậy bạn phải dùng tiền tố $ trong template. Viết _('key') mà thiếu dấu đô la sẽ trả về đối tượng store thay vì chuỗi đã dịch.

Chuyển đổi ngôn ngữ

Tạo bộ chọn ngôn ngữ liên kết với store $locale. Khi giá trị thay đổi, svelte-i18n tải thông điệp của ngôn ngữ mới và cập nhật mọi chuỗi dịch theo cơ chế phản ứng.

LanguageSwitcher.svelte
<script>
  import { locale, locales } from 'svelte-i18n';

  const LANGUAGE_NAMES = {
    en: 'English',
    de: 'Deutsch',
    ja: '日本語',
    es: 'Espanol',
  };
</script>

<select bind:value={$locale}>
  {#each $locales as loc}
    <option value={loc}>{LANGUAGE_NAMES[loc] ?? loc}</option>
  {/each}
</select>
4

Xử lý số nhiều bằng ICU MessageFormat

svelte-i18n dùng ICU MessageFormat cho số nhiều, tiêu chuẩn quốc tế xử lý mọi nhóm số nhiều CLDR. Tiếng Ả Rập có 6 dạng, tiếng Nga có 4, tiếng Nhật có 1. Hãy khai báo các dạng mà ngôn ngữ đích cần, svelte-i18n sẽ tự động chọn đúng dạng.

ICU plural forms by language
// svelte-i18n uses ICU MessageFormat for plurals
// English:
{
  "items": "{count, plural, one {# item} other {# items}}"
}

// Arabic (6 forms):
{
  "items": "{count, plural, zero {no items} one {item} two {two items} few {# items} many {# items} other {# items}}"
}

// Japanese (1 form):
{
  "items": "{count, plural, other {#個のアイテム}}"
}
Không bao giờ mã hóa cứng logic số ít/số nhiều trong component. Các ngôn ngữ như tiếng Pháp coi 0 là số ít. Tiếng Ả Rập, Nga và Ba Lan có những dạng số nhiều mà tiếng Anh không có. Hãy để cú pháp số nhiều ICU xử lý.

Chuỗi dự phòng ngôn ngữ thông minh với svelte-i18n-locale-chain

svelte-i18n chuyển thẳng sang fallbackLocale khi thiếu khóa, không có bước dự phòng trung gian. Người dùng pt-BR sẽ thấy tiếng Anh thay vì bản dịch pt-PT hoàn toàn phù hợp. svelte-i18n-locale-chain khắc phục bằng chuỗi dự phòng thông minh hợp nhất sâu thông điệp từ các biến thể vùng.

Terminal
npm install svelte-i18n-locale-chain svelte-i18n
src/lib/i18n.ts
// src/lib/i18n.ts
import { initLocaleChain, setLocale } from 'svelte-i18n-locale-chain';

// Replace svelte-i18n's init + register with initLocaleChain
await initLocaleChain({
  loadMessages: (locale) =>
    import(`../locales/${locale}.json`).then(m => m.default),
  defaultLocale: 'en',
  initialLocale: 'pt-BR',
});

// Later, to change locale:
await setLocale('fr-CA');
// fr-CA user sees: fr-CA messages -> fr messages -> en messages
// No missing keys — deep-merged automatically
svelte-i18n-locale-chain tự quản lý toàn bộ việc tải thông điệp. Không dùng đồng thời hàm register() của svelte-i18n vì initLocaleChain đã xử lý đăng ký, tải và hợp nhất sâu.

Tự động hóa dịch thuật

Sau khi thiết lập xong i18n, hãy dùng AI để dịch các tệp ngôn ngữ. Trong IDE, bạn có thể yêu cầu trợ lý AI dịch tệp nguồn hoặc dùng i18n Agent CLI trong quy trình CI/CD.

Terminal
# In your IDE, ask your AI assistant:
> Translate src/locales/en.json to German, Japanese, and Spanish

✓ de.json created (1.2s)
✓ ja.json created (1.5s)
✓ es.json created (1.1s)

# Or use the CLI in CI/CD:
npx i18n-agent translate src/locales/en.json --lang de,ja,es
Dịch tăng dần: khi thêm khóa mới vào tệp nguồn, chỉ dịch phần thay đổi thay vì tạo lại mọi tệp. Cách này giữ nguyên các bản dịch đã qua con người duyệt.

Tự động kiểm soát chất lượng bản dịch

Dùng i18n-validate để phát hiện khóa thiếu và placeholder hỏng trước khi phát hành. Dùng i18n-pseudo để kiểm thử UI bằng bản dịch giả trước khi có bản dịch thật.

Lỗi thường gặp

Rò rỉ ngôn ngữ SSR trong SvelteKit

Các store svelte-i18n là singleton. Trong SSR của SvelteKit, các yêu cầu đồng thời dùng chung một store nên ngôn ngữ của một người dùng có thể lọt vào phản hồi của người khác. Cách khắc phục: gọi locale.set() trong hook handle hoặc hàm load của layout để mỗi yêu cầu có đúng ngữ cảnh ngôn ngữ.

Dùng register() với svelte-i18n-locale-chain

Không dùng hàm register() của svelte-i18n nếu đang dùng svelte-i18n-locale-chain. initLocaleChain tự xử lý toàn bộ việc tải thông điệp. Kết hợp cả hai sẽ khiến thông điệp bị tải trùng hoặc xung đột.

Lỗi cú pháp ICU không hiện cảnh báo

Dấu ngoặc không khớp hoặc thiếu nhóm số nhiều trong chuỗi ICU MessageFormat có thể gây lỗi âm thầm: hệ thống hiện chuỗi thông điệp thô thay vì kết quả đã định dạng. Hãy xác thực cú pháp ICU trong quy trình CI.

Nội dung chưa dịch lóe lên

Nếu kết xuất component trước khi tải xong bản dịch, người dùng sẽ thấy khóa thô. Hãy bảo vệ layout bằng {#if $isLoading}...{:else}...{/if} để hiện trạng thái đang tải cho đến khi thông điệp sẵn sàng.

Cấu trúc tệp khuyên dùng

Project Structure
my-sveltekit-app/
├── src/
│   ├── lib/
│   │   └── i18n.ts              # i18n configuration
│   ├── locales/
│   │   ├── en.json              # Source language
│   │   ├── de.json              # German
│   │   ├── ja.json              # Japanese
│   │   └── es.json              # Spanish
│   └── routes/
│       ├── +layout.svelte       # Import i18n, guard isLoading
│       ├── +page.svelte
│       └── [lang]/              # Optional: locale-based routing
│           ├── +layout.ts       # Set locale from URL param
│           ├── +layout.svelte
│           └── +page.svelte
├── svelte.config.js
└── package.json

Dùng thử i18n Agent ngay

Thả tệp bản dịch của bạn vào đây

JSON, YAML, PO, XML, CSV, Markdown, Properties

hoặc nhấp để duyệt

Ngôn ngữ đích

Không cần đăng kýBáo giá tức thì

Dự phòng ngôn ngữ với svelte-i18n-locale-chain

Khi thiếu khóa bản dịch trong một ngôn ngữ vùng như pt-BR, svelte-i18n chuyển thẳng sang ngôn ngữ mặc định thay vì kiểm tra ngôn ngữ cha pt trước.

Terminal
npm install svelte-i18n-locale-chain
Configuration
import { initLocaleChain } from 'svelte-i18n-locale-chain';

initLocaleChain({
  fallbacks: {
    'pt-BR': ['pt', 'en'],
    'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
  },
  defaultLocale: 'en',
});

Xem Hướng dẫn dự phòng ngôn ngữ để biết danh sách đầy đủ các framework được hỗ trợ và 75 chuỗi tích hợp sẵn. Learn more →

Câu hỏi thường gặp