Help Center

Lingui.js Localization: PO Files, React i18n & Automation

Lingui.js is a lightweight yet powerful internationalization (i18n) framework for JavaScript and React applications. It uses industry-standard PO (Portable Object) files for translation catalogs, powerful compile-time macros for clean developer experience, and produces optimized bundles under 2 kB. This guide covers everything from project setup to PO file workflows, pluralization, and automating translations with l10n.dev.

Why Lingui.js for i18n?

Lingui.js stands out among JavaScript i18n libraries for its combination of developer experience, performance, and professional translation workflows:

  • Clean and Readable: Write translations naturally using JSX macros - the library compiles them to ICU MessageFormat under the hood.
  • Universal: @lingui/core works in any JavaScript project, while @lingui/react adds React-specific components including React Server Components (RSC) support.
  • Full Rich-Text Support: Use React components inside translatable messages seamlessly - links, bold text, icons, and any JSX are fully supported.
  • PO File Format: Uses the industry-standard PO format supported by virtually every translation tool, TMS, and professional translator workflow.
  • Lightweight and Optimized: Core library is under 2 kB gzipped, React components add only 1.3 kB. Non-essential data is stripped at compile time.
  • AI Translations Ready: Lingui's message descriptions and context comments give AI translators the information they need to produce accurate, context-aware translations.

Installation

Install Lingui's core packages and development tools. The runtime packages (@lingui/core and @lingui/react) are needed in production, while the CLI, Vite plugin, and Babel macro are dev-only:

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

Configuration

Lingui requires two configuration files: the Lingui config (defining locales and catalog paths) and your build tool config (to enable compile-time macros).

Lingui Configuration

Create a lingui.config.js file at your project root. This defines your source locale, target locales, where to store PO catalogs, and the catalog format. The PO format is the default and recommended choice:

// 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 Configuration

If you use Vite, add the Lingui plugin alongside the React plugin with the Babel macro. This enables compile-time message transformation and on-the-fly catalog compilation:

// 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(),
  ],
});

The PO File Format

PO (Portable Object) is the industry-standard translation file format, originating from the GNU gettext system. Lingui uses PO as its default catalog format because it's widely supported by translation tools, TMS platforms, and professional translators worldwide.

  • Readable: Even large translation files remain human-readable with clear msgid/msgstr pairs.
  • Translator Comments: Supports #. comments that carry context descriptions directly to translators.
  • Source References: Each message includes #: file:line comments showing exactly where it's used in your codebase.
  • Context Support: Use msgctxt to disambiguate identical strings that have different meanings in different contexts.
  • Universal Tool Support: Compatible with virtually every translation management system (TMS), CAT tool, and localization platform.

Extracted PO file (source messages)

#: 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 ""

Translated PO file (French)

After translation, each msgstr is populated with the localized version. Lingui preserves ICU MessageFormat syntax, indexed tags for React components, and placeholder variables across all translations.

#: 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}}"

Project Structure

Organize your PO catalogs inside a locales directory with one subfolder per locale. The Lingui CLI automatically creates and updates these files during extraction. Compiled JS modules are generated at build time and should not be edited manually.

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
└── ...

Translating React Components

Lingui provides two main approaches for marking messages: JSX macros for component content and the useLingui hook for imperative strings. Both are compile-time macros that produce optimized output.

The Trans Macro

Wrap any JSX content in the Trans macro to make it translatable. Variables, HTML elements, and React components are fully supported - the macro transforms them into ICU MessageFormat with indexed tags automatically.

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

The useLingui Hook

For strings outside JSX - alert messages, aria labels, attribute values, or any imperative code - use the useLingui macro hook with tagged template literals:

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

Message IDs and Descriptions

Lingui supports two approaches for message identification: auto-generated IDs (derived from the source message content) and explicit IDs (developer-defined keys). Both approaches work with PO files.

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>

Adding Context for Translators and AI

Use the comment prop on any macro to add a description. This comment is extracted into the PO file as a translator note, providing critical context about where and how the message appears.

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

<Trans comment="Shown on the homepage hero section above the fold">
  Start building with confidence
</Trans>
Message descriptions are especially valuable for AI-powered translation. They provide the context that AI needs to disambiguate short UI strings - turning generic translations into accurate, context-aware ones.

Pluralization

Lingui uses ICU MessageFormat for pluralization, which supports all CLDR plural categories. The Plural macro makes it easy to handle different plural forms directly in JSX:

  • Use the Plural component with value, one, other, and optional exact forms (_0, _2, etc.).
  • The # placeholder is automatically replaced with the plural value.
  • l10n.dev generates all required plural forms for every target language - including complex languages like Arabic (6 forms), Russian, and Polish - without manual intervention.
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>
  );
}

Runtime Initialization

After extracting and compiling your messages, set up the i18n instance and wrap your app in the I18nProvider.

i18n Module Setup

Create an i18n module that loads compiled message catalogs dynamically. The Lingui Vite plugin enables direct imports of PO files, which are compiled on the fly during development:

// 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 Setup

Wrap your entire app with I18nProvider to make translations available to all components via React context:

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

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

Language Switching at Runtime

Switching languages is straightforward: load the new catalog, activate the locale, and React automatically re-renders all translated content. Here's a complete language switcher component:

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

Extract and Compile Workflow

Lingui's CLI provides two key commands that form the core of your translation workflow: extract to pull messages from source code into PO catalogs, and compile to convert PO files into optimized JavaScript modules for production.

# 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. Extract: Scans your source files and creates or updates PO catalogs with all translatable messages.
  2. Translate: Fill in the msgstr fields in your PO files - manually, with a TMS, or automatically with AI-powered translation.
  3. Compile: Converts PO catalogs into lightweight JS modules that are loaded at runtime. Only translated messages are included.

Full PO Support in l10n.dev

l10n.dev provides native support for PO files, making it the perfect companion for Lingui.js projects. Upload your source PO catalog and receive accurately translated files back:

  • Native PO Format: Upload PO files directly - l10n.dev reads msgid entries, translates them, and writes back properly formatted PO files with correct msgstr values.
  • ICU MessageFormat Aware: Placeholders ({name}), indexed tags (<0>), plural forms, and select expressions are preserved correctly across all translations.
  • Context-Aware AI Translation: Translator comments (#.) and message context (msgctxt) are used by our AI to produce more accurate, context-aware translations.
  • Plural Generation: All required CLDR plural forms are generated for every target language. Languages with complex plural rules (Arabic, Russian, Polish) are handled automatically.
  • Batch and Incremental Updates: Translate entire catalogs at once or use the --update flag to translate only new and changed messages, preserving existing translations.

Automate Translations with npm

Use the ai-l10n npm package to translate your Lingui PO catalogs from the command line or as part of a CI/CD pipeline. Install once, then translate to any number of languages in a single command.

# 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

Integration into Your Build Process

Wire ai-l10n directly into your Lingui build process so translations are always up to date. Combine extraction, translation, and compilation in a single workflow.

Via package.json scripts

Add extract, translate, and compile as scripts and chain them together. The i18n script runs the full pipeline; use it in prebuild to ensure translations are always current before shipping.

{
  "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"
  }
}

Via Makefile

If your team uses Make, declare extraction, translation, and compilation as separate targets with proper dependencies:

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

For CI/CD integration, incremental updates, batch translation across multiple files, and GitHub Actions workflow examples, see the Localization Automation Guide.

VS Code Extension

The l10n.dev VS Code extension brings PO file translation directly into your editor. Translate your Lingui catalogs without leaving VS Code.

How It Works in VS Code:

  1. Open your source PO file (e.g., src/locales/en/messages.po) in the editor.
  2. Right-click in the editor and select "Translate..." (works with PO files too).
  3. Choose one or more target languages (e.g., French, Japanese, German).
  4. The extension creates translated PO files in the corresponding locale folders, ready to compile.

Start Localizing Your React App with Lingui

Ready to reach global users? Translate your Lingui PO files directly in the l10n.dev workspace, automate with the npm CLI, or translate right inside VS Code: