帮助中心

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 服务器组件 (RSC) 支持在内的 React 专用组件。
  • 全面的富文本支持: 在可翻译消息中无缝使用 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 上下文使所有组件都能使用翻译:

// 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 脚本

将提取、翻译和编译添加为脚本并串联起来。i18n 脚本运行整个流水线;在预构建中使用它,以确保在发布前翻译始终是最新的。

{
  "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 中翻译: