Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Lingui
https://github.com/lingui/js-lingui
Admin
π π A readable, automated, and optimized (3 kb) internationalization for JavaScript
Tokens:
73,643
Snippets:
924
Trust Score:
8.1
Update:
2 days ago
Context
Skills
Chat
Benchmark
95.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Lingui.js Lingui is a powerful, lightweight (2kb gzipped) internationalization (i18n) framework for JavaScript and React applications. It provides a developer-friendly way to handle translations using ICU MessageFormat under the hood, supporting rich-text messages with React components, pluralization, and dynamic values. The framework consists of core packages (`@lingui/core` for JavaScript, `@lingui/react` for React integration) and tooling (`@lingui/cli` for message extraction and compilation, `@lingui/macro` for compile-time transformations). The library offers a complete i18n workflow: developers write translatable messages using intuitive macros, extract messages into standard PO or JSON catalogs, send catalogs for translation, and compile them for production use. Lingui emphasizes clean code through macros that transform at build time, automatic message ID generation, and type safety with TypeScript support. It integrates seamlessly with modern build tools like Vite, Webpack, and Metro (React Native). ## Core API - i18n Instance The `@lingui/core` package provides the main `i18n` object that manages message catalogs, active locale, and translation/formatting of messages. It's the foundation for all Lingui functionality. ```javascript import { i18n } from "@lingui/core"; import { messages as messagesEn } from "./locales/en/messages"; import { messages as messagesFr } from "./locales/fr/messages"; // Load message catalogs for multiple locales i18n.load({ en: messagesEn, fr: messagesFr, }); // Activate a locale i18n.activate("en"); // Translate a simple message const greeting = i18n._("Hello World"); // Output: "Hello World" // Translate with variables const welcome = i18n._("Welcome, {name}!", { name: "Alice" }); // Output: "Welcome, Alice!" // Translate with custom message ID and default message const customId = i18n._("nav.home", {}, { message: "Home" }); // Using message descriptor object const descriptor = i18n._({ id: "msg.greeting", message: "Hello {name}", values: { name: "Bob" }, comment: "Greeting message shown on homepage", }); // Format dates using locale conventions i18n.activate("en"); i18n.date(new Date("2024-01-15")); // Output: "1/15/2024" i18n.date(new Date("2024-01-15"), { dateStyle: "full" }); // Output: "Monday, January 15, 2024" // Format numbers using locale conventions i18n.number(12345.67); // Output: "12,345.67" i18n.number(1234.5, { style: "currency", currency: "USD" }); // Output: "$1,234.50" // Listen for locale changes i18n.on("change", () => { console.log("Locale changed to:", i18n.locale); }); // Listen for missing translations i18n.on("missing", (event) => { console.warn(`Missing translation: ${event.id} in ${event.locale}`); }); ``` ## setupI18n - Custom i18n Instance The `setupI18n` function creates a new i18n instance with custom configuration. Useful for server-side rendering or when you need multiple i18n instances. ```javascript import { setupI18n } from "@lingui/core"; // Create custom i18n instance with initial configuration const i18n = setupI18n({ locale: "en", locales: ["en-US", "en-GB"], // Alternative locales for formatting messages: { en: { "Hello": "Hello", "Goodbye": "Goodbye" }, es: { "Hello": "Hola", "Goodbye": "AdiΓ³s" }, }, // Custom handler for missing translations (useful for debugging) missing: (locale, id) => { console.warn(`[i18n] Missing: "${id}" in ${locale}`); return id; }, }); // Use the custom instance const message = i18n._("Hello"); // Load and activate in one step (optimized for dynamic loading) const { messages } = await import(`./locales/fr/messages.js`); i18n.loadAndActivate({ locale: "fr", messages, }); ``` ## React Integration - I18nProvider and useLingui The `@lingui/react` package provides React components and hooks for seamless integration. The `I18nProvider` makes the i18n instance available throughout your component tree. ```jsx import React, { useEffect, useState } from "react"; import { i18n } from "@lingui/core"; import { I18nProvider, useLingui, Trans } from "@lingui/react"; // i18n setup helper async function loadCatalog(locale) { const { messages } = await import(`./locales/${locale}/messages.js`); i18n.load(locale, messages); i18n.activate(locale); } // Root component with I18nProvider function App() { const [isReady, setIsReady] = useState(false); useEffect(() => { loadCatalog("en").then(() => setIsReady(true)); }, []); if (!isReady) return <div>Loading...</div>; return ( <I18nProvider i18n={i18n}> <MainContent /> </I18nProvider> ); } // Component using useLingui hook function MainContent() { const { i18n, _ } = useLingui(); // The _ function is safe to use in dependency arrays const translatedTitle = _({ id: "page.title", message: "Welcome" }); const handleLanguageChange = async (locale) => { await loadCatalog(locale); }; return ( <div> <h1>{translatedTitle}</h1> <p>Current locale: {i18n.locale}</p> {/* Runtime Trans component */} <Trans id="greeting" message="Hello {name}" values={{ name: "User" }} /> {/* Language switcher */} <button onClick={() => handleLanguageChange("en")}>English</button> <button onClick={() => handleLanguageChange("fr")}>French</button> </div> ); } ``` ## Trans Macro - JSX Translation The `Trans` macro is the primary way to translate JSX content. It supports variables, React components, and rich-text formatting while extracting messages at build time. ```jsx import { Trans } from "@lingui/react/macro"; function InboxPage({ user, messageCount, lastLogin }) { return ( <div> {/* Simple static message */} <h1><Trans>Message Inbox</Trans></h1> {/* Message with variable */} <p><Trans>Welcome back, {user.name}!</Trans></p> {/* Message with inline React components */} <p> <Trans> Read the <a href="/docs">documentation</a> for more info. </Trans> </p> {/* Message with custom ID for translators */} <Trans id="inbox.header" comment="Page header for inbox"> Your Messages </Trans> {/* Message with context for disambiguation */} <Trans context="navigation">Home</Trans> <Trans context="page-title">Home</Trans> {/* Complex message with nested components */} <p> <Trans> Contact <strong>{user.email}</strong> or visit{" "} <a href="/support">support</a> for help. </Trans> </p> {/* Custom rendering */} <Trans render={({ translation }) => <title>{translation}</title>}> My App Title </Trans> </div> ); } // Macro transforms to: // <Trans id="abc123" message="Welcome back, {name}!" values={{ name: user.name }} /> ``` ## Plural Macro - Pluralization The `Plural` macro handles plural forms following CLDR plural rules. It supports all plural categories (zero, one, two, few, many, other) and exact matches. ```jsx import { Plural } from "@lingui/react/macro"; function MessageCount({ count }) { return ( <div> {/* Basic plural */} <Plural value={count} one="# message" other="# messages" /> {/* count=1: "1 message", count=5: "5 messages" */} {/* Plural with zero case */} <Plural value={count} zero="No messages" one="# message" other="# messages" /> {/* Plural with exact matches */} <Plural value={count} _0="No messages" _1="One message" other="# messages" /> {/* Plural with offset (for "you and others" patterns) */} <Plural value={count} offset={1} _0="Nobody liked this" _1="You liked this" one="You and # other person liked this" other="You and # other people liked this" /> {/* Plural with variables in text */} <Plural value={count} one={<Trans>{user.name} has # new message</Trans>} other={<Trans>{user.name} has # new messages</Trans>} /> </div> ); } ``` ## Select and SelectOrdinal Macros The `Select` macro handles gender and other categorical selections. The `SelectOrdinal` macro handles ordinal numbers (1st, 2nd, 3rd, etc.). ```jsx import { Select, SelectOrdinal } from "@lingui/react/macro"; function UserProfile({ user, position }) { return ( <div> {/* Gender-based selection */} <Select value={user.gender} _male="He is online" _female="She is online" other="They are online" /> {/* Any categorical selection */} <Select value={user.status} _active="User is active" _pending="User is pending approval" _banned="User is banned" other="Unknown status" /> {/* Ordinal numbers */} <SelectOrdinal value={position} one="#st place" two="#nd place" few="#rd place" other="#th place" /> {/* position=1: "1st place", position=2: "2nd place", position=3: "3rd place", position=4: "4th place" */} </div> ); } ``` ## t Macro - JavaScript Translation The `t` macro translates messages in JavaScript code (outside JSX). It returns a string and can be used anywhere in your code. ```javascript import { t, plural, select } from "@lingui/core/macro"; // Simple message const title = t`Welcome to our app`; // Message with variables const greeting = t`Hello ${userName}!`; // Using template literal with expressions const info = t`Today is ${new Date().toLocaleDateString()}`; // Plural in JavaScript const itemCount = plural(count, { one: "# item", other: "# items", }); // Select in JavaScript const pronoun = select(gender, { male: "his", female: "her", other: "their", }); // With custom ID and comment const customMessage = t({ id: "validation.required", comment: "Error message for required fields", message: `${fieldName} is required`, }); // Nested plurals const complexMessage = plural(books, { one: plural(articles, { one: "1 book and 1 article", other: `1 book and ${articles} articles`, }), other: plural(articles, { one: `${books} books and 1 article`, other: `${books} books and ${articles} articles`, }), }); // Using with alert or other non-JSX contexts function showConfirmation() { alert(t`Are you sure you want to delete this item?`); } function validateForm(data) { const errors = []; if (!data.email) { errors.push(t`Email is required`); } if (!data.password) { errors.push(t`Password is required`); } return errors; } ``` ## defineMessage / msg Macro - Lazy Translations The `msg` macro (alias: `defineMessage`) creates message descriptors for later translation. Useful for constants, configuration objects, and when you need to separate message definition from translation. ```javascript import { msg, defineMessage } from "@lingui/core/macro"; import { useLingui } from "@lingui/react"; // Define messages as constants (module level is OK for msg) const messages = { welcome: msg`Welcome to our application`, goodbye: msg`Thanks for visiting!`, error: msg`An error occurred`, }; // With custom ID const navItems = [ { path: "/", label: msg({ id: "nav.home", message: "Home" }) }, { path: "/about", label: msg({ id: "nav.about", message: "About Us" }) }, { path: "/contact", label: msg({ id: "nav.contact", message: "Contact" }) }, ]; // With plurals const countMessages = { items: msg`${count} items`, users: defineMessage({ id: "user.count", message: plural(count, { one: "# user", other: "# users" }), }), }; // Usage in components function Navigation() { const { _ } = useLingui(); return ( <nav> {navItems.map((item) => ( <a key={item.path} href={item.path}> {_(item.label)} </a> ))} </nav> ); } function StatusMessage({ type }) { const { _ } = useLingui(); // Translate when needed return <div>{_(messages[type])}</div>; } ``` ## useLingui Macro - React Hook with t The `useLingui` macro provides a `t` function bound to the React context, safe for use in dependency arrays and memoization. ```jsx import { useLingui } from "@lingui/react/macro"; import { useMemo, useCallback } from "react"; function SearchComponent({ onSearch }) { const { t, i18n } = useLingui(); // t is stable across re-renders (changes only when locale changes) const placeholder = t`Search...`; const buttonText = t`Find`; // Safe to use in useMemo const options = useMemo(() => [ { value: "all", label: t`All categories` }, { value: "books", label: t`Books` }, { value: "music", label: t`Music` }, ], [t]); // t changes when locale changes // Safe to use in useCallback const handleError = useCallback((error) => { alert(t`Error: ${error.message}`); }, [t]); return ( <div> <input placeholder={placeholder} /> <select> {options.map(opt => ( <option key={opt.value} value={opt.value}>{opt.label}</option> ))} </select> <button>{buttonText}</button> </div> ); } ``` ## CLI - Extract Command The `lingui extract` command scans source files for translatable messages and generates message catalogs in PO or JSON format. ```bash # Install CLI npm install --save-dev @lingui/cli # Basic extraction lingui extract # Extract with specific locales lingui extract --locale en,fr,de # Extract and clean obsolete messages lingui extract --clean # Watch mode for development lingui extract --watch # Extract from specific files/directories lingui extract src/components src/pages # Verbose output lingui extract --verbose # Example package.json scripts # { # "scripts": { # "extract": "lingui extract", # "extract:clean": "lingui extract --clean" # } # } # Output example: # Catalog statistics: # ββββββββββββ¬ββββββββββββββ¬ββββββββββ # β Language β Total count β Missing β # ββββββββββββΌββββββββββββββΌββββββββββ€ # β en β 42 β 0 β # β fr β 42 β 15 β # β de β 42 β 42 β # ββββββββββββ΄ββββββββββββββ΄ββββββββββ ``` ## CLI - Compile Command The `lingui compile` command converts message catalogs into optimized JavaScript modules for production use. ```bash # Basic compilation lingui compile # Compile with strict mode (fail on missing translations) lingui compile --strict # Generate TypeScript files lingui compile --typescript # Watch mode lingui compile --watch # Custom output prefix (for different linters) lingui compile --output-prefix "/*biome-ignore lint*/" # Example package.json scripts # { # "scripts": { # "compile": "lingui compile", # "compile:strict": "lingui compile --strict", # "build": "lingui compile && vite build" # } # } # Generated output (messages.js): # /*eslint-disable*/export const messages = JSON.parse("{\"Hello\":\"Hello\",\"Goodbye\":\"Goodbye\"}"); ``` ## Configuration - lingui.config.js Lingui configuration file defines locales, catalog paths, and extraction settings. ```javascript // lingui.config.js or lingui.config.ts import { defineConfig } from "@lingui/cli"; import { formatter } from "@lingui/format-po"; export default defineConfig({ // Supported locales locales: ["en", "fr", "de", "es"], // Source locale (messages extracted as-is) sourceLocale: "en", // Fallback chain for missing translations fallbackLocales: { "en-US": ["en-GB", "en"], "es-MX": "es", default: "en", }, // Catalog configuration catalogs: [ { path: "<rootDir>/src/locales/{locale}/messages", include: ["<rootDir>/src"], exclude: ["**/node_modules/**", "**/*.test.js"], }, ], // Catalog format (po, json, csv) format: formatter({ lineNumbers: false }), // Message ordering in catalogs orderBy: "messageId", // or "message", "origin" // Compiled output format compileNamespace: "es", // "cjs", "es", "ts", "json" // Merge compiled catalogs into single file per locale catalogsMergePath: "<rootDir>/src/locales/{locale}", // Custom i18n instance location (for macros) runtimeConfigModule: ["./src/i18n", "i18n"], // Pseudolocalization for testing pseudoLocale: "pseudo", }); ``` ## Vite Plugin Integration The `@lingui/vite-plugin` compiles message catalogs on the fly during development and builds. ```typescript // 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(), ], }); // Usage in code - dynamic imports with Vite // src/i18n.ts import { i18n } from "@lingui/core"; export async function loadCatalog(locale: string) { // Vite plugin compiles .po files on import const { messages } = await import(`./locales/${locale}/messages.po`); i18n.load(locale, messages); i18n.activate(locale); } // For non-.po formats, use ?lingui suffix // const { messages } = await import(`./locales/${locale}.json?lingui`); ``` ## Dynamic Catalog Loading Pattern for loading message catalogs on demand to reduce initial bundle size. ```typescript // src/i18n.ts import { i18n } from "@lingui/core"; export const locales = { en: "English", fr: "FranΓ§ais", de: "Deutsch", es: "EspaΓ±ol", }; export type LocaleKey = keyof typeof locales; export const defaultLocale: LocaleKey = "en"; // Load catalog dynamically export async function dynamicActivate(locale: LocaleKey) { const { messages } = await import(`./locales/${locale}/messages.js`); i18n.load(locale, messages); i18n.activate(locale); } // React component with language switching import React, { useEffect, useState } from "react"; import { I18nProvider } from "@lingui/react"; import { i18n } from "@lingui/core"; import { dynamicActivate, locales, defaultLocale, LocaleKey } from "./i18n"; function App() { const [locale, setLocale] = useState<LocaleKey>(defaultLocale); const [isLoading, setIsLoading] = useState(true); useEffect(() => { setIsLoading(true); dynamicActivate(locale).then(() => setIsLoading(false)); }, [locale]); if (isLoading) return <div>Loading translations...</div>; return ( <I18nProvider i18n={i18n}> <LanguageSwitcher currentLocale={locale} onLocaleChange={setLocale} /> <MainApp /> </I18nProvider> ); } function LanguageSwitcher({ currentLocale, onLocaleChange }: { currentLocale: LocaleKey; onLocaleChange: (locale: LocaleKey) => void; }) { return ( <select value={currentLocale} onChange={(e) => onLocaleChange(e.target.value as LocaleKey)} > {Object.entries(locales).map(([code, name]) => ( <option key={code} value={code}>{name}</option> ))} </select> ); } ``` ## Summary Lingui provides a complete internationalization solution for JavaScript and React applications. The main use cases include: translating static and dynamic text with the `Trans` and `t` macros, handling pluralization rules across languages with `Plural`, managing gender and categorical selections with `Select`, extracting messages for translation teams using the CLI, and dynamically loading catalogs based on user locale preferences. The library integrates with modern build tools through plugins for Vite, Webpack, and Metro, making it suitable for web, server-side, and React Native applications. For integration, developers typically follow this workflow: configure `lingui.config.js` with locales and catalog paths, wrap the React app with `I18nProvider`, use macros throughout components for translations, run `lingui extract` to generate message catalogs, send catalogs for translation, run `lingui compile` before deployment, and implement dynamic loading for production optimization. The macro-based approach ensures type safety, automatic ID generation, and production optimizations like stripping default messages. Lingui's small bundle size (2kb core + 1.3kb React) and comprehensive tooling make it ideal for applications requiring professional-grade internationalization.