@lingui/macro vs react-i18next vs react-intl
React Internationalization Libraries Compared
@lingui/macroreact-i18nextreact-intlSimilar Packages:

React Internationalization Libraries Compared

@lingui/macro, react-i18next, and react-intl are the leading solutions for adding multi-language support to React applications. @lingui/macro focuses on developer experience by allowing translations to live directly in the code using Babel macros. react-i18next provides flexible React bindings for the i18next ecosystem, supporting various backends and middleware. react-intl is the React integration for FormatJS, offering robust ICU message formatting and strong separation of code and content. Each tool solves the same core problem but takes a different approach to syntax, workflow, and runtime behavior.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@lingui/macro05,8486.32 kB654 months agoMIT
react-i18next010,0341.53 MB1a month agoMIT
react-intl014,744180 kB52 days agoBSD-3-Clause

React Internationalization: Lingui, i18next, and React-Intl Compared

Adding multi-language support to a React app is more than just swapping text. You need to handle plurals, dates, currencies, and dynamic content without slowing down your site. @lingui/macro, react-i18next, and react-intl are the top choices for this task. They all get the job done, but they work differently under the hood. Let's compare how they tackle common engineering challenges.

✍️ Writing Translations: Macros vs Hooks vs Components

How you write translations in your code affects daily developer experience. Some teams prefer functions, others prefer components, and some want macros.

@lingui/macro uses Babel macros to let you write translations as template literals.

  • You import t and wrap strings directly.
  • The build tool compiles these into lightweight function calls.
  • No need to manage manual keys for simple strings.
// @lingui/macro: Direct template literal
import { t } from '@lingui/macro';

function Welcome({ name }) {
  return <h1>{t`Hello ${name}`}</h1>;
}

react-i18next relies on a hook or a higher-order component to access the translation function.

  • You call t('key') with a string identifier.
  • Separates content from logic using keys.
  • Works well with existing JSON translation files.
// react-i18next: Hook-based access
import { useTranslation } from 'react-i18next';

function Welcome({ name }) {
  const { t } = useTranslation();
  return <h1>{t('welcome.message', { name })}</h1>;
}

react-intl uses React components to wrap translated content.

  • You use <FormattedMessage> with an ID and values.
  • Keeps JSX declarative and clear.
  • Enforces separation of message IDs and content.
// react-intl: Component-based declaration
import { FormattedMessage } from 'react-intl';

function Welcome({ name }) {
  return (
    <h1>
      <FormattedMessage id="welcome.message" values={{ name }} />
    </h1>
  );
}

πŸ—‚οΈ Managing Files: Extraction vs Manual JSON

Where do the actual translation strings live? This impacts how translators work and how you update content.

@lingui/macro extracts messages from your source code automatically.

  • Run a CLI command to scan files.
  • Generates a catalog (PO or JSON) for translators.
  • Reduces the risk of unused keys lingering in files.
# Lingui: Extract messages from code
lingui extract

react-i18next typically uses manual JSON files or a backend service.

  • You create en.json, fr.json, etc.
  • Keys must be managed by developers.
  • Easy to integrate with translation management platforms.
// react-i18next: Manual JSON structure
{
  "welcome": {
    "message": "Hello {{name}}"
  }
}

react-intl supports both manual JSON and extraction via CLI tools.

  • Uses the FormatJS CLI to extract from components.
  • Generates JSON files with message descriptors.
  • Strong validation for ICU syntax during extraction.
// react-intl: Extract using FormatJS CLI
formatjs extract 'src/**/*.ts' --out-file dist/en.json

πŸ”’ Plurals and Formatting: ICU vs Custom Syntax

Handling "1 item" vs "2 items" or currency formatting varies by library.

@lingui/macro supports ICU plurals directly in the macro syntax.

  • Uses standard ICU syntax inside the template.
  • Compile-time validation ensures correctness.
  • Clean syntax for complex cases.
// @lingui/macro: ICU plural support
import { Plural } from '@lingui/macro';

function Cart({ count }) {
  return <p><Plural value={count} one="# item" other="# items" /></p>;
}

react-i18next uses a custom interpolation syntax for plurals.

  • Defines plural forms in the JSON file (e.g., key_one, key_other).
  • Flexible but requires learning specific rules.
  • Supports nesting and conditional logic.
// react-i18next: Plural keys in JSON
// JSON: { "items_one": "# item", "items_other": "# items" }
function Cart({ count }) {
  const { t } = useTranslation();
  return <p>{t('items', { count })}</p>;
}

react-intl is built on the ICU MessageFormat standard.

  • Strict adherence to international standards.
  • Handles gender, select, and plural logic robustly.
  • Best for complex linguistic rules.
// react-intl: ICU plural in message definition
// Message: "{count, plural, one {# item} other {# items}}"
function Cart({ count }) {
  return (
    <FormattedMessage
      id="cart.items"
      values={{ count }}
    />
  );
}

⚑ Performance: Lazy Loading and Bundle Size

Loading all translations at once can slow down initial page load. All three support splitting.

@lingui/macro compiles to lightweight functions that support dynamic imports.

  • You can load catalogs per route.
  • Runtime overhead is minimal after compilation.
  • Tree-shaking works well with standard bundlers.
// @lingui/macro: Dynamic catalog loading
async function loadLanguage(lang) {
  const { messages } = await import(`./locales/${lang}.js`);
  i18n.loadAndActivate({ lang, messages });
}

react-i18next has built-in support for lazy loading namespaces.

  • Use useTranslation with namespace arguments.
  • Fetches JSON chunks on demand.
  • Integrates easily with React Suspense.
// react-i18next: Lazy loading namespaces
import { useTranslation } from 'react-i18next';

function Dashboard() {
  const { t } = useTranslation(['dashboard', 'common']);
  return <div>{t('dashboard:title')}</div>;
}

react-intl requires manual setup for lazy loading providers.

  • You manage the IntlProvider wrapper per route.
  • Load locale data and messages asynchronously.
  • Gives full control over when data loads.
// react-intl: Manual provider setup
async function RouteComponent({ lang }) {
  const messages = await import(`./locales/${lang}.json`);
  return (
    <IntlProvider locale={lang} messages={messages}>
      <App />
    </IntlProvider>
  );
}

🀝 Similarities: Shared Ground Between Libraries

Despite different approaches, these libraries solve the same core problems with similar tools. Here are key overlaps:

1. βš›οΈ React Integration

  • All three provide hooks or components designed for React.
  • Support functional components and React Context.
// Shared pattern: Context usage
// All libraries wrap the app in a Provider at the root
<Provider i18n={i18n}><App /></Provider>

2. 🌐 Runtime Language Switching

  • All support changing languages without reloading the page.
  • Trigger re-renders when the locale updates.
// Shared pattern: Changing language
i18n.changeLanguage('fr'); // Works in all three ecosystems

3. πŸ› οΈ TypeScript Support

  • All offer type definitions for better developer experience.
  • Help catch missing keys or wrong variable types.
// Shared pattern: Typed translation functions
const { t } = useTranslation(); // Infers types in all three

4. πŸ”Œ Ecosystem Extensions

  • All have plugins for backend integration, caching, and detection.
  • Community tools exist for translation management systems.
// Shared pattern: Backend loading
// All can fetch translations from an API endpoint
backend: { loadPath: '/locales/{{lng}}.json' }

πŸ“Š Summary: Key Similarities

FeatureShared by All Three
Core Techβš›οΈ React Hooks & Context
Switching🌐 Dynamic language change
TypesπŸ› οΈ TypeScript definitions
Loading⚑ Lazy loading support
FormattingπŸ“… Date & Number formatting

πŸ†š Summary: Key Differences

Feature@lingui/macroreact-i18nextreact-intl
Syntax✍️ Template macros (t\...``)πŸͺ Hooks (t('key'))🧩 Components (<FormattedMessage>)
WorkflowπŸ” Auto-extraction from codeπŸ“„ Manual JSON or BackendπŸ” CLI Extraction or Manual
PluralsπŸ…°οΈ ICU StandardπŸ”’ Custom Key SuffixesπŸ…°οΈ ICU Standard
SetupπŸ› οΈ Requires Babel Pluginβš™οΈ Flexible ConfigurationπŸ—οΈ Provider Wrapper Required
FocusπŸ§‘β€πŸ’» Developer Experience🌍 Ecosystem Flexibility🏒 Enterprise Standards

πŸ’‘ The Big Picture

@lingui/macro is like a developer-first toolkit 🧰 β€” great for teams that want translations close to the code and automated workflows. Ideal for modern React apps where developers manage content directly.

react-i18next is like a swiss army knife πŸ”ͺ β€” perfect for teams who need flexibility, backend integration, and a vast plugin ecosystem. Shines in complex apps with dynamic content needs.

react-intl is like a precision instrument πŸ“ β€” best for enterprise projects requiring strict ICU compliance and clear separation between code and content. The go-to for large-scale, regulated applications.

Final Thought: All three libraries are mature and capable. The best choice depends on whether you prioritize developer workflow (@lingui/macro), ecosystem flexibility (react-i18next), or standard compliance (react-intl).

How to Choose: @lingui/macro vs react-i18next vs react-intl

  • @lingui/macro:

    Choose @lingui/macro if your team prefers keeping translation strings close to the component logic and wants automatic extraction via CLI. It is ideal for projects that value a clean codebase without scattered JSON files and want strong TypeScript support out of the box. This approach works well when developers want to see context directly in the source code rather than managing external keys.

  • react-i18next:

    Choose react-i18next if you need a highly flexible ecosystem with support for various storage backends, language detection, and middleware. It is suitable for applications that require dynamic language switching, backend-driven translations, or integration with existing i18next infrastructure. This library shines when you need granular control over how and where translation files are loaded.

  • react-intl:

    Choose react-intl if your project requires strict adherence to the ICU message format for complex plurals, genders, and select statements. It is best for enterprise applications where separation of concerns between developers and translators is critical. This package is the right choice when you need robust formatting for dates, numbers, and currencies across many locales.

README for @lingui/macro

License Version Downloads Babel Macro

@lingui/macro

Babel Macros which transforms tagged template literals and JSX components to ICU MessageFormat.

@lingui/macro is part of LinguiJS. See the documentation for all information, tutorials and examples.

Installation & Usage

See the reference documentation.

License

MIT