文档

Lingui.js本地化:PO文件、React国际化与自动化

Lingui.js是一个轻量级且功能强大的JavaScript和React应用程序国际化(i18n)框架。它使用行业标准的PO(可移植对象)文件作为翻译目录,采用强大的编译时宏以提供简洁的开发者体验,并生成小于2 kB的优化包。本指南涵盖了从项目设置到PO文件工作流程、复数形式以及使用l10n.dev自动化翻译的所有内容。

为什么选择Lingui.js进行国际化?

Lingui.js在JavaScript国际化库中脱颖而出,因为它结合了开发者体验、性能和专业的翻译工作流程:

  • 简洁易读: 使用JSX宏自然地编写翻译——库会在底层将其编译为ICU消息格式。
  • 通用性: @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)在生产环境中是必需的,而命令行界面、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(可移植对象)是源自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消息格式语法、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命令行界面会在提取过程中自动创建和更新这些文件。编译后的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消息格式。

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>
  );
}

消息键与描述

Lingui支持两种消息识别方法:自动生成键(源自源消息内容)和显式键(开发者定义的标识符)。两种方法都适用于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消息格式进行复数处理,支持所有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的命令行界面提供了两个关键命令,构成了您翻译工作流程的核心: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消息格式感知: 插值({name})、索引标签(<0>)、复数形式和选择表达式在所有翻译中都能被正确保留。
  • 上下文感知AI翻译: 译员注释(#.)和消息上下文(msgctxt)被我们的AI用于生成更准确、上下文感知的翻译。
  • 复数生成: 为每种目标语言生成所有必需的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脚本运行整个管道;在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操作工作流程示例,请参阅本地化自动化指南。

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命令行界面进行自动化,或者直接在VS Code内进行翻译: