幫助中心

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 格式。
  • 輕量且優化: 核心函式庫壓縮後小於 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,請將 Lingui 外掛與 React 外掛及 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 內進行翻譯: