文件

Lingui.js 本地化:PO 檔案、React i18n 與自動化

Lingui.js 是一個輕量級且強大的 JavaScript 和 React 應用程式國際化 (i18n) 框架。它使用業界標準的 PO (Portable Object) 檔案作為翻譯目錄,利用強大的編譯時巨集提供乾淨的開發者體驗,並產生小於 2 kB 的最佳化套件。本指南涵蓋了從專案設定到 PO 檔案工作流程、複數形式以及使用 l10n.dev 自動化翻譯的所有內容。

為什麼選擇 Lingui.js 進行 i18n?

Lingui.js 在 JavaScript i18n 函式庫中脫穎而出,結合了開發者體驗、效能和專業的翻譯工作流程:

  • 乾淨且易讀: 使用 JSX 巨集自然地編寫翻譯,函式庫會在底層將其編譯為 ICU MessageFormat。
  • 通用性: @lingui/core 適用於任何 JavaScript 專案,而 @lingui/react 則新增了 React 專用元件,包括對 React Server Components (RSC) 的支援。
  • 完整的富文本支援: 在可翻譯訊息中無縫使用 React 元件,完全支援連結、粗體文字、圖示和任何 JSX。
  • PO 檔案格式: 使用幾乎所有翻譯工具、TMS 和專業翻譯工作流程都支援的業界標準 PO 格式。
  • 輕量且最佳化: 核心函式庫經 Gzip 壓縮後小於 2 kB,React 元件僅增加 1.3 kB。非必要資料會在編譯時被剝離。
  • AI 翻譯就緒: Lingui 的訊息描述和語境註解為 AI 翻譯者提供了產生準確、語境感知翻譯所需的資訊。

安裝

安裝 Lingui 的核心套件和開發工具。執行階段套件 (@lingui/core 和 @lingui/react) 在生產環境中是必需的,而 CLI、Vite 插件和 Babel 巨集僅用於開發:

npm install @lingui/core @lingui/react
npm install --save-dev @lingui/cli @lingui/vite-plugin @lingui/babel-plugin-lingui-macro

設定

Lingui 需要兩個設定檔案:Lingui 設定(定義語系和目錄路徑)以及您的建置工具設定(以啟用編譯時巨集)。

Lingui 設定

在您的專案根目錄建立一個 lingui.config.js 檔案。這定義了您的來源語系、目標語系、儲存 PO 目錄的位置以及目錄格式。PO 格式是預設且推薦的選擇:

// lingui.config.js
import { defineConfig } from "@lingui/cli";
import { formatter } from "@lingui/format-po";

export default defineConfig({
  sourceLocale: "en",
  locales: ["en", "fr", "de", "ja", "es", "zh-CN"],
  catalogs: [
    {
      path: "<rootDir>/src/locales/{locale}/messages",
      include: ["src"],
    },
  ],
  format: formatter({ lineNumbers: false }),
});

Vite 設定

如果您使用 Vite,請在 React 插件旁加入 Lingui 插件並搭配 Babel 巨集。這將啟用編譯時的訊息轉換以及即時的目錄編譯:

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { lingui } from "@lingui/vite-plugin";

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: ["@lingui/babel-plugin-lingui-macro"],
      },
    }),
    lingui(),
  ],
});

PO 檔案格式

PO (Portable Object) 是源自 GNU gettext 系統的業界標準翻譯檔案格式。Lingui 使用 PO 作為其預設目錄格式,因為它受到全球翻譯工具、TMS 平台和專業翻譯者的廣泛支援。

  • 易讀: 即使是大型翻譯檔案,透過清晰的 msgid/msgstr 對,依然保持人類可讀性。
  • 翻譯者註解: 支援 #. 註解,直接將語境描述傳達給翻譯者。
  • 來源參考: 每則訊息都包含 #: file:line 註解,精確顯示其在程式碼庫中的使用位置。
  • 語境支援: 使用 msgctxt 來消歧義在不同語境下具有不同含義的相同字串。
  • 通用工具支援: 與幾乎所有翻譯管理系統 (TMS)、CAT 工具和本地化平台相容。

已擷取的 PO 檔案 (來源訊息)

#: src/components/WelcomeBanner.js:6
msgid "Welcome to our platform"
msgstr ""

#: src/components/WelcomeBanner.js:8
msgid "Hello {userName}, check out your <0>dashboard</0> to get started."
msgstr ""

#: src/components/CartSummary.js:5
msgid "{itemCount, plural, =0 {Your cart is empty} one {You have # item in your cart} other {You have # items in your cart}}"
msgstr ""

翻譯後的 PO 檔案 (法文)

翻譯後,每個 msgstr 都會填入本地化版本。Lingui 在所有翻譯中保留 ICU MessageFormat 語法、React 元件的索引標籤以及佔位符變數。

#: src/components/WelcomeBanner.js:6
msgid "Welcome to our platform"
msgstr "Bienvenue sur notre plateforme"

#: src/components/WelcomeBanner.js:8
msgid "Hello {userName}, check out your <0>dashboard</0> to get started."
msgstr "Bonjour {userName}, consultez votre <0>tableau de bord</0> pour commencer."

#: src/components/CartSummary.js:5
msgid "{itemCount, plural, =0 {Your cart is empty} one {You have # item in your cart} other {You have # items in your cart}}"
msgstr "{itemCount, plural, =0 {Votre panier est vide} one {Vous avez # article dans votre panier} other {Vous avez # articles dans votre panier}}"

專案結構

將您的 PO 目錄組織在 locales 目錄內,每個語系一個子資料夾。Lingui CLI 會在擷取過程中自動建立和更新這些檔案。編譯後的 JS 模組在建置時產生,不應手動編輯。

src/
├── locales/
│   ├── en/
│   │   └── messages.po       ← Source (English)
│   ├── fr/
│   │   └── messages.po       ← French
│   ├── de/
│   │   └── messages.po       ← German
│   ├── ja/
│   │   └── messages.po       ← Japanese
│   ├── es/
│   │   └── messages.po       ← Spanish
│   └── zh-CN/
│       └── messages.po       ← Chinese Simplified
├── components/
├── i18n.ts
├── App.tsx
└── ...

翻譯 React 元件

Lingui 提供了兩種主要的訊息標記方法:用於元件內容的 JSX 巨集和用於指令式字串的 useLingui 鉤子。兩者皆為編譯時巨集,可產生最佳化輸出。

Trans 巨集

將任何 JSX 內容包裹在 Trans 巨集中以使其可翻譯。完全支援變數、HTML 元素和 React 元件 - 巨集會自動將它們轉換為帶有索引標籤的 ICU MessageFormat。

import { Trans } from "@lingui/react/macro";

function WelcomeBanner({ userName }) {
  return (
    <div>
      <h1>
        <Trans>Welcome to our platform</Trans>
      </h1>
      <p>
        <Trans>
          Hello {userName}, check out your <a href="/dashboard">dashboard</a> to
          get started.
        </Trans>
      </p>
    </div>
  );
}

useLingui 鉤子

對於 JSX 之外的字串(例如警告訊息、aria 標籤、屬性值或任何指令式程式碼),請使用帶有標記範本字面值的 useLingui 巨集鉤子:

import { useLingui } from "@lingui/react/macro";

function NotificationButton() {
  const { t } = useLingui();

  const showAlert = () => {
    alert(t`Your changes have been saved.`);
  };

  return (
    <button onClick={showAlert} aria-label={t`Save changes`}>
      <Trans>Save</Trans>
    </button>
  );
}

訊息 ID 與描述

Lingui 支援兩種訊息識別方法:自動產生 ID(衍生自來源訊息內容)和明確 ID(開發者定義的鍵)。兩種方法都適用於 PO 檔案。

import { Trans } from "@lingui/react/macro";

// Auto-generated ID (from message content)
<Trans>Welcome to our platform</Trans>

// Explicit ID (developer-defined key)
<Trans id="nav.welcome">Welcome to our platform</Trans>

為翻譯者和 AI 新增語境

在任何巨集上使用 comment 屬性來新增描述。此註解會被擷取到 PO 檔案中作為翻譯者註解,提供關於訊息出現位置和方式的關鍵語境。

import { Trans } from "@lingui/react/macro";

<Trans comment="Shown on the homepage hero section above the fold">
  Start building with confidence
</Trans>
訊息描述對於 AI 驅動的翻譯特別有價值。它們提供了 AI 消歧義簡短 UI 字串所需的語境,將通用的翻譯轉變為準確、語境感知的翻譯。

複數形式

Lingui 使用 ICU MessageFormat 進行複數形式處理,支援所有 CLDR 複數類別。Plural 巨集讓您可以輕鬆直接在 JSX 中處理不同的複數形式:

  • 使用帶有 value、one、other 和可選精確形式 (_0, _2 等) 的 Plural 元件。
  • # 佔位符會自動替換為複數值。
  • l10n.dev 為每種目標語言產生所有必要的複數形式(包括阿拉伯語(6 種形式)、俄語和波蘭語等複雜語言),無需手動介入。
import { Plural } from "@lingui/react/macro";

function CartSummary({ itemCount }) {
  return (
    <p>
      <Plural
        value={itemCount}
        _0="Your cart is empty"
        one="You have # item in your cart"
        other="You have # items in your cart"
      />
    </p>
  );
}

執行階段初始化

擷取並編譯訊息後,設定 i18n 實例並將您的應用程式包裹在 I18nProvider 中。

i18n 模組設定

建立一個動態載入編譯後訊息目錄的 i18n 模組。Lingui Vite 插件支援直接匯入 PO 檔案,這些檔案會在開發過程中即時編譯:

// src/i18n.ts
import { i18n } from "@lingui/core";

export async function loadCatalog(locale: string) {
  const { messages } = await import(`./locales/${locale}/messages.po`);
  i18n.load(locale, messages);
  i18n.activate(locale);
}

// Initialize with default locale
loadCatalog("en");

export default i18n;

I18nProvider 設定

使用 I18nProvider 包裹您的整個應用程式,透過 React context 將翻譯提供給所有元件:

// src/App.tsx
import { I18nProvider } from "@lingui/react";
import { i18n } from "./i18n";

function App() {
  return (
    <I18nProvider i18n={i18n}>
      <YourAppContent />
    </I18nProvider>
  );
}

執行階段語言切換

切換語言非常簡單:載入新目錄,啟用語系,React 會自動重新渲染所有翻譯後的內容。這是一個完整的語言切換器元件:

import { useState } from "react";
import { loadCatalog } from "./i18n";
import { Trans } from "@lingui/react/macro";

const LANGUAGES = {
  en: "English",
  fr: "Français",
  de: "Deutsch",
  ja: "日本語",
  es: "Español",
};

function LanguageSwitcher() {
  const [currentLocale, setCurrentLocale] = useState("en");

  const handleChange = async (locale: string) => {
    await loadCatalog(locale);
    setCurrentLocale(locale);
  };

  return (
    <select
      value={currentLocale}
      onChange={(e) => handleChange(e.target.value)}
    >
      {Object.entries(LANGUAGES).map(([code, name]) => (
        <option key={code} value={code}>
          {name}
        </option>
      ))}
    </select>
  );
}

擷取與編譯工作流程

Lingui 的 CLI 提供了兩個關鍵指令,構成了您翻譯工作流程的核心:extract 用於將訊息從原始碼提取到 PO 目錄,compile 用於將 PO 檔案轉換為生產環境的最佳化 JavaScript 模組。

# Extract messages from source code into PO catalogs
npx lingui extract

# Translate your PO files (manually, with a TMS, or with AI)

# Compile PO catalogs into optimized JS modules for production
npx lingui compile
  1. 擷取:掃描您的來源檔案,並使用所有可翻譯訊息建立或更新 PO 目錄。
  2. 翻譯:填寫 PO 檔案中的 msgstr 欄位 - 手動、使用 TMS,或使用 AI 驅動的翻譯自動完成。
  3. 編譯:將 PO 目錄轉換為在執行階段載入的輕量級 JS 模組。僅包含已翻譯的訊息。

l10n.dev 對 PO 的完整支援

l10n.dev 提供對 PO 檔案的原生支援,使其成為 Lingui.js 專案的完美夥伴。上傳您的來源 PO 目錄,即可獲得準確翻譯後的檔案:

  • 原生 PO 格式: 直接上傳 PO 檔案 - l10n.dev 讀取 msgid 條目,進行翻譯,並寫回格式正確且帶有正確 msgstr 值的 PO 檔案。
  • ICU MessageFormat 感知: 佔位符 ({name})、索引標籤 (<0>)、複數形式和選擇表達式在所有翻譯中都能正確保留。
  • 語境感知 AI 翻譯: 我們的 AI 使用翻譯者註解 (#.) 和訊息語境 (msgctxt) 來產生更準確、語境感知的翻譯。
  • 複數產生: 為每種目標語言產生所有必要的 CLDR 複數形式。具有複雜複數規則的語言(阿拉伯語、俄語、波蘭語)會自動處理。
  • 批次與增量更新: 一次翻譯整個目錄,或使用 --update 標誌僅翻譯新增和變更的訊息,同時保留現有的翻譯。

使用 npm 自動化翻譯

使用 ai-l10n npm 套件從命令列或作為 CI/CD 流水線的一部分來翻譯您的 Lingui PO 目錄。安裝一次,即可透過單一指令翻譯成任意數量的語言。

# Install the CLI
npm install ai-l10n

# Translate your source PO file to multiple languages
npx ai-l10n translate src/locales/en/messages.po \
  --languages fr,de,ja,zh-CN,es,ko

整合至您的建置流程

將 ai-l10n 直接連接到您的 Lingui 建置流程中,確保翻譯始終保持最新。將擷取、翻譯和編譯結合在一個工作流程中。

透過 package.json 指令碼

將 extract、translate 和 compile 新增為指令碼並將它們串聯起來。i18n 指令碼執行完整的流程;在 prebuild 中使用它以確保在出貨前翻譯始終是最新的。

{
  "scripts": {
    "extract": "lingui extract",
    "compile": "lingui compile",
    "translate": "ai-l10n translate src/locales/en/messages.po --languages fr,de,ja,zh-CN,es,ko --update",
    "i18n": "npm run extract && npm run translate && npm run compile",
    "prebuild": "npm run i18n",
    "build": "vite build",
    "dev": "vite"
  }
}

透過 Makefile

如果您的團隊使用 Make,請將擷取、翻譯和編譯宣告為具有適當依賴關係的獨立目標:

LANGUAGES = fr,de,ja,zh-CN,es,ko

extract:
	npx lingui extract

translate:
	npx ai-l10n translate src/locales/en/messages.po --languages $(LANGUAGES) --update

compile:
	npx lingui compile

i18n: extract translate compile

dev: i18n
	npx vite

build: i18n
	npx vite build

關於 CI/CD 整合、增量更新、多檔案批次翻譯以及 GitHub Actions 工作流程範例,請參閱 本地化自動化指南。

VS Code 擴充功能

l10n.dev VS Code 擴充功能將 PO 檔案翻譯直接帶入您的編輯器。無需離開 VS Code 即可翻譯您的 Lingui 目錄。

在 VS Code 中的運作方式:

  1. 在編輯器中開啟您的來源 PO 檔案 (例如 src/locales/en/messages.po)。
  2. 在編輯器中按右鍵並選擇 "Translate..." (也適用於 PO 檔案)。
  3. 選擇一種或多種目標語言 (例如法文、日文、德文)。
  4. 擴充功能會在對應的語系資料夾中建立翻譯後的 PO 檔案,準備進行編譯。

開始使用 Lingui 本地化您的 React 應用程式

準備好接觸全球用戶了嗎?直接在 l10n.dev 工作區翻譯您的 Lingui PO 檔案,使用 npm CLI 自動化,或直接在 VS Code 內翻譯: