一篇搞定 i18n!Vue 與 React 多語系網站的關鍵技巧與實踐

一篇搞定 i18n!Vue 與 React 多語系網站的關鍵技巧與實踐

在現今網路無遠弗屆的時代,開發一個能夠服務全球使用者的應用程式已是常態。為了讓不同地區、使用不同語言的用戶都能無礙地使用您的產品,國際化(Internationalization)便成為一項不可或缺的開發技能。

國際化,常簡寫為 i18n,這個縮寫的由來相當有趣:取其英文單字 “internationalization” 的頭尾字母 “i” 和 “n”,中間恰好省略了 18 個字母。其核心理念在於,於應用程式設計之初,就將多語言、地區格式(如日期 datetime、貨幣、number)等差異納入考量,使程式碼 code 不需大幅修改,就能輕鬆適配不同地區的文化與語言習慣。與之相關的另一個詞是本地化(Localization, L10n),指的是將國際化設計好的應用程式,實際翻譯成特定語言並調整其內容的過程。

本篇文將從 i18n 的基礎核心概念出發,深入探討如何在當前最熱門的兩大前端框架 Vue.js 與 React 中,利用主流套件 vue-i18n 和 react-i18next 進行多國語系的實戰部署。

i18n 的核心概念

在深入框架實作前,我們必須先理解幾個共通的核心概念:

地區資訊 (Locale)

代表特定的地理、政治或文化區域。通常由一個語言編碼(ISO 639)和一個可選的區域編碼(ISO 3166)組成,慣例上以連字號 – 或底線 _ 分隔。例如:

  • en-US: 美國英語
  • zh-TW: 臺灣繁體中文
  • fr: 法語 (未指定區域)

翻譯詞彙檔 (Translation Files)

用來儲存不同語言翻譯文案的檔案,也就是所謂的 locale messages。常見格式為 json 格式 或 YAML,以「鍵 (Key) – 值 (Value)」的結構來組織。開發者在程式碼中引用「鍵」,i18n 套件會根據當前設定的地區,自動查找並顯示對應的「值」。

  • locales/en.json 範例:
    json { “header”: { “title”: “Welcome to our App” }, “buttons”: { “submit”: “Submit” } }
  • locales/zh-TW.json 範例:
    json { “header”: { “title”: “歡迎使用我們的應用程式” }, “buttons”: { “submit”: “送出” } }

插值 (Interpolation)

指在翻譯文字中嵌入動態變數。例如,您不會想為「Hello, John」和「Hello, Mary」分別建立詞條,而是建立一個像 {“greeting”: “Hello, {name}”} 的詞條,並在執行時動態傳入 name 的值。In addition to simple text, i18n libraries also support localization for more complex cases like pluralization (複數處理),能根據數量顯示不同形式的文案。

Vue.js 的 i18n 實戰 (vue-i18n)

在 Vue 中實作多國語系,vue-i18n 是最普及的選擇,它是一個強大的 internationalization plugin。以下我們將以 Vue 3 的 Composition API 為主軸進行說明。

安裝與設定

最快速的方式是透過 Vue CLI:

# 透過 Vue CLI 添加 i18n 插件 (會引導式地進行設定)
vue add i18n

安裝過程中,CLI 會詢問預設地區、備援地區、語系檔案目錄等問題,並自動生成所需檔案,包含:

  • locales/ 資料夾:存放如 en.json, zh-TW.json 等語系檔。
  • i18n.js:vue-i18n 的主要設定檔,負責載入所有語系檔並建立 i18n 實例。
  • main.js:會自動引入 i18n 實例並注入到 Vue project 中。

一個自動生成的 i18n.js 可能如下所示:

import { createI18n } from 'vue-i18n';

function loadLocaleMessages() {
  const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i);
  const messages = {};
  locales.keys().forEach(key => {
    const matched = key.match(/([A-Za-z0-9-_]+)\./i);
    if (matched && matched.length > 1) {
      const locale = matched[1];
      messages[locale] = locales(key).default;
    }
  });
  return messages;
}

export default createI18n({
  legacy: false, // 必須設為 false 才能使用 Composition API
  locale: process.env.VUE_APP_I18N_LOCALE || 'en',
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: loadLocaleMessages(),
  globalInjection: true // 全局注入,方便在模板中使用 $t
});

基礎用法與語系切換

在元件中,我們透過 useI18n 鉤子 (hook) 來取得翻譯函式 t 和當前語系 locale。

<template>
  <div>
    <select v-model="locale">
      <option value="en">English</option>
      <option value="zh-TW">繁體中文</option>
    </select>

    <h1>{{ $t('header.title') }}</h1>
    <button>{{ $t('buttons.submit') }}</button>
  </div>
</template>

<script>
import { useI18n } from 'vue-i18n';

export default {
  setup() {
    // 從 useI18n() 取得 locale 控制項
    // 因為在 i18n.js 已設定 globalInjection: true,模板可直接用 $t
    // 但若要在 <script> setup 內使用,仍需解構出 t
    const { locale, t } = useI18n();

    return {
      locale, // 將 locale 回傳至模板,使其能透過 v-model 雙向綁定
      t       // 若需在 setup 內進行翻譯操作,則回傳 t
    };
  }
}
</script>

透過將 useI18n 回傳的 locale 變數與 <select> 進行 v-model 綁定,當使用者切換選項時,vue-i18n 會自動重新渲染所有使用 t 或 $t 的文本,實現即時語系切換。

React 的 i18n 實戰 (react-i18next)

react-i18next 是建構在 i18next 核心之上的強力框架,為 React 應用提供了靈活的多國語系解決方案。

安裝與設定

首先,安裝必要的套件:

npm install react-i18next i18next i18next-http-backend --save
  • react-i18next: React 的整合層。
  • i18next: 核心邏輯。
  • i18next-http-backend: 一個後端插件,能非同步地從伺服器(或 public 資料夾)載入語系檔,在某些情況下推薦使用。

接著,建立設定檔 src/i18n.js:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import HttpApi from 'i18next-http-backend';

i18n
  .use(HttpApi) // 使用 http-backend 插件
  .use(initReactI18next) // 將 i18n 實例傳遞給 react-i18next
  .init({
    supportedLngs: ['en', 'zh-TW'], // 支援的語言
    fallbackLng: 'en', // 備援語言
    debug: process.env.NODE_ENV === 'development', // 開發模式下開啟 debug
    backend: {
      // 語系檔案的路徑
      // ns: namespace, lng: language
      loadPath: '/locales/{{lng}}/{{ns}}.json',
    },
    interpolation: {
      escapeValue: false, // React 已有 XSS 保護機制
    },
  });

export default i18n;

最後,在專案入口 src/index.js 引入此設定檔:

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './i18n'; // 引入 i18n 設定

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Suspense fallback="loading...">
    <App />
  </Suspense>
);

語系檔應放置在 public/locales/ 目錄下,例如 public/locales/en/translation.json。

基礎用法與語系切換

在函式元件中,使用 useTranslation 鉤子是標準做法。

import React from 'react';
import { useTranslation, Trans } from 'react-i18next';

function MyComponent() {
  const { t, i18n } = useTranslation();

  const changeLanguage = (lng) => {
    i18n.changeLanguage(lng);
  };

  return (
    <div>
      <div>
        <button onClick={() => changeLanguage('en')}>English</button>
        <button onClick={() => changeLanguage('zh-TW')}>中文</button>
      </div>

      {/* 基本用法 */}
      <h1>{t('header.title')}</h1>

      {/* 處理包含 JSX 元件的複雜翻譯 */}
      <Trans i18nKey="buttons.learnMore">
        點擊<a href="/learn">這裡</a>瞭解更多
      </Trans>
    </div>
  );
}

export default MyComponent;

useTranslation 回傳了 t 函式和 i18n 實例。呼叫 i18n.changeLanguage(‘語言代碼’) 即可輕鬆切換語言。對於包含 HTML 標籤或 React 元件的複雜翻譯,Trans 元件是 react-i18next 提供的一個極為強大的工具,能確保結構的完整性。

功能比較

功能 Vue (vue-i18n) React (react-i18next)
核心套件 vue-i18n react-i18next, i18next
安裝設定 vue add i18n 或手動設定 createI18n 建立 i18n.js 並初始化 i18next
取得翻譯函式 useI18n() Hook useTranslation() Hook
渲染複雜內容 使用 v-html (有風險) 或透過 Slot 插槽 使用 <Trans> 元件 (推薦)
切換語系 修改從 useI18n() 取得的 locale 變數 呼叫 i18n.changeLanguage(‘lng’) 方法
非同步載入 可手動實現 透過 i18next-http-backend 等插件輕鬆配置

常見問題

Q: i18n 和 L10n 有什麼區別?

A: i18n(國際化)是技術層面的準備工作,指設計與開發應用程式使其能適應不同語言和地區,而不需更動程式碼。L10n(本地化)則是基於這個架構,實際進行內容翻譯、調整日期/貨幣格式、符合當地文化習慣的過程。簡單來說,先有 i18n,纔有 L10n。

Q: 翻譯檔案應該放在 src 還是 public 目錄?

A: 這取決於專案需求。放在 src 目錄下,語系檔會被 Webpack 等工具打包進最終的程式碼中,適合語系少、檔案小的專案,優點是載入快速。放在 public 目錄,則可以像 react-i18next 的範例那樣,透過網路請求非同步載入,適合語系眾多或詞條量龐大的大型專案,可以有效減小初始載入的檔案體積。

Q: 如何處理包含 HTML 標籤的翻譯?

A: 在 React 中,強烈建議使用 react-i18next 提供的 <Trans> 元件,它能安全且直觀地處理內嵌的 JSX。在 Vue 中,雖然可以使用 v-html 指令,但有 XSS 安全風險,必須確保內容來源可靠。更好的做法是利用元件的插槽 (slots) 功能,將靜態文本與動態元件分開處理。

Q: 除了文字,還有哪些項目需要本地化?

A: 完整的本地化不僅僅是文字翻譯,還包括:

日期與時間格式 (e.g., MM/DD/YYYY vs. DD/MM/YYYY)

數字格式 (e.g., 1,234.56 vs. 1.234,56)

貨幣符號與格式

圖片或影片 (可能包含地域性內容)

排版方向** (e.g., 支援阿拉伯語等 RTL,從右至左的語言)#

總結

國際化 (i18n) 不再是選配功能,而是現代網站開發的基礎建設。它讓我們的產品能夠跨越語言的隔閡,觸及更廣泛的受眾。透過 vue-i18n 和 react-i18next 這樣成熟的套件,前端開發者可以非常有系統且高效地在 Vue 和 React 專案中導入多國語系功能。

從定義詞彙、切換語言到處理複雜的動態內容,這些工具都提供了完整且易於上手的解決方案。將 i18n 的思維融入開發初期,將為產品的未來擴展性奠定堅實的基礎。這些強大的開源工具通常由充滿熱情的社羣維護,有時也由 sponsors 支持,使用前請務必詳閱其授權與 copyright 相關說明。

資料來源

返回頂端