Help Center

React Localization with i18next

Building a React application for a global audience requires a robust internationalization (i18n) strategy. react-i18next is the most popular React localization library, built on top of the battle-tested i18next framework. This guide walks you through everything you need to localize your React app—from initial setup to production-ready automation with AI-powered translation.

Why Choose react-i18next for React Localization?

react-i18next is the de facto standard for React internationalization, trusted by thousands of production applications worldwide. Here's why developers choose it:

  • Hooks-First API - The useTranslation() hook provides clean, idiomatic React integration with automatic re-rendering on language change.
  • Rich Ecosystem - Plugins for language detection, HTTP backends, caching, and more. Extend functionality without reinventing the wheel.
  • JSON Translation Format - Uses standard JSON files that are easy to edit, version-control, and integrate with translation tools and AI services.
  • Namespace Support - Organize translations into logical groups (auth, dashboard, errors) for better maintainability and lazy loading.
  • SSR & Next.js Ready - Works seamlessly with server-side rendering, React Server Components, and the Next.js App Router.
  • TypeScript Support - Full type safety for translation keys with autocomplete, catching missing keys at compile time.

Installation

Install react-i18next and the core i18next library:

npm install react-i18next i18next

Optional Plugins

These popular plugins enhance your i18next setup with automatic language detection and lazy-loaded translations:

# Language detector (auto-detects user language)
npm install i18next-browser-languagedetector

# HTTP backend (load translations from server)
npm install i18next-http-backend

Recommended Project Structure

A well-organized project structure keeps your translations maintainable as you add more languages:

src/
├── i18n/
│   └── i18n.ts                  ← i18next configuration
├── locales/
│   ├── en/
│   │   └── translation.json     ← English (source)
│   ├── fr/
│   │   └── translation.json     ← French
│   ├── de/
│   │   └── translation.json     ← German
│   ├── ja/
│   │   └── translation.json     ← Japanese
│   └── es/
│       └── translation.json     ← Spanish
├── components/
├── App.tsx
└── index.tsx

Translation JSON File Format

react-i18next uses nested JSON files for translations. Keys support dot notation, interpolation with {{variables}}, and built-in pluralization:

// locales/en/translation.json
{
  "welcome": {
    "title": "Welcome to our platform",
    "greeting": "Hello, {{name}}!",
    "description": "Explore features and get started."
  },
  "nav": {
    "home": "Home",
    "dashboard": "Dashboard",
    "settings": "Settings",
    "logout": "Log out"
  },
  "cart": {
    "empty": "Your cart is empty",
    "item_one": "{{count}} item in your cart",
    "item_other": "{{count}} items in your cart",
    "checkout": "Proceed to checkout"
  }
}
  • Nested Keys - Organize translations hierarchically (e.g., welcome.title, nav.home) for clear structure.
  • Interpolation - Use {{variableName}} syntax for dynamic values that get inserted at runtime.
  • Pluralization - Add _one and _other suffixes (or _zero, _two, _few, _many for complex languages) for automatic plural handling.
  • Context - Use _male, _female suffixes for gender-specific translations when needed.

Configuring i18next

i18next supports two main approaches for loading translations: HTTP backend (lazy loading) and bundled resources. Choose based on your app's needs.

HTTP Backend (Recommended for Large Apps)

Load translations on demand from JSON files on your server. This keeps your initial bundle small and is ideal for apps with many languages or large translation files:

// src/i18n/i18n.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpBackend from "i18next-http-backend";

i18n
  .use(HttpBackend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: "en",
    supportedLngs: ["en", "fr", "de", "ja", "es"],
    defaultNS: "translation",
    interpolation: {
      escapeValue: false, // React already escapes by default
    },
    backend: {
      loadPath: "/locales/{{lng}}/{{ns}}.json",
    },
    detection: {
      order: ["localStorage", "navigator", "htmlTag"],
      caches: ["localStorage"],
    },
  });

export default i18n;

Bundled Resources (Simple Approach)

Import translations directly into the bundle. Best for smaller apps or when you need translations available immediately without network requests:

// src/i18n/i18n.ts — Bundled approach (no HTTP backend)
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";

import en from "../locales/en/translation.json";
import fr from "../locales/fr/translation.json";
import de from "../locales/de/translation.json";

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: "en",
    supportedLngs: ["en", "fr", "de"],
    defaultNS: "translation",
    interpolation: {
      escapeValue: false,
    },
    resources: {
      en: { translation: en },
      fr: { translation: fr },
      de: { translation: de },
    },
  });

export default i18n;

Initializing in Your App

Import the i18n configuration file in your app entry point before rendering any components. This ensures translations are loaded before your UI renders:

// src/index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./i18n/i18n"; // Initialize i18next before rendering

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Translating React Components

react-i18next provides two primary APIs for translating components: the useTranslation hook for programmatic access and the Trans component for rich text with JSX.

useTranslation Hook

The useTranslation hook is the most common way to access translations in functional components. It provides the t() function and automatically re-renders when the language changes:

import { useTranslation } from "react-i18next";

function WelcomeBanner() {
  const { t } = useTranslation();

  return (
    <div>
      <h1>{t("welcome.title")}</h1>
      <p>{t("welcome.greeting", { name: "Alice" })}</p>
      <p>{t("welcome.description")}</p>
    </div>
  );
}

Trans Component for Rich Text

When translations contain HTML or JSX elements (bold, links, etc.), use the Trans component. It preserves your React component tree within translations:

import { Trans } from "react-i18next";

function RichTextExample() {
  return (
    <p>
      <Trans i18nKey="richText.welcome">
        Welcome to <strong>our platform</strong>.
        Visit your <a href="/dashboard">dashboard</a> to get started.
      </Trans>
    </p>
  );
}

// In the translation file:
// "richText.welcome": "Welcome to <1>our platform</1>. Visit your <3>dashboard</3> to get started."

Handling Pluralization

i18next handles plural forms automatically based on the ICU plural rules for each language. Pass a count parameter, and i18next selects the correct plural form:

// In translation JSON:
// "cart.item_one": "{{count}} item in your cart"
// "cart.item_other": "{{count}} items in your cart"

import { useTranslation } from "react-i18next";

function CartSummary({ itemCount }: { itemCount: number }) {
  const { t } = useTranslation();

  return (
    <p>{t("cart.item", { count: itemCount })}</p>
  );
}

// itemCount=0 → "0 items in your cart"
// itemCount=1 → "1 item in your cart"
// itemCount=5 → "5 items in your cart"
  • Add _one and _other suffixes to your translation keys for English
  • For languages with complex plural rules (Arabic, Polish, Russian), add _zero, _two, _few, _many suffixes as needed
  • i18next automatically selects the correct form based on the target language's CLDR plural rules

Organizing with Namespaces

Namespaces let you split translations into separate files by feature or domain. This improves maintainability and enables lazy loading of translation bundles:

// src/i18n/i18n.ts
i18n.init({
  defaultNS: "common",
  ns: ["common", "dashboard", "auth", "errors"],
  backend: {
    loadPath: "/locales/{{lng}}/{{ns}}.json",
  },
});

// Usage in components:
import { useTranslation } from "react-i18next";

function DashboardPage() {
  // Load specific namespace
  const { t } = useTranslation("dashboard");

  return <h1>{t("title")}</h1>;
}

function LoginForm() {
  // Load multiple namespaces
  const { t } = useTranslation(["auth", "common"]);

  return (
    <form>
      <h2>{t("auth:login.title")}</h2>
      <button>{t("common:submit")}</button>
    </form>
  );
}

Dynamic Language Switching

react-i18next makes language switching seamless. Call i18n.changeLanguage() and all components using useTranslation automatically re-render with the new language:

import { useTranslation } from "react-i18next";

const LANGUAGES = [
  { code: "en", label: "English" },
  { code: "fr", label: "Français" },
  { code: "de", label: "Deutsch" },
  { code: "ja", label: "日本語" },
  { code: "es", label: "Español" },
];

function LanguageSwitcher() {
  const { i18n } = useTranslation();

  const changeLanguage = (lng: string) => {
    i18n.changeLanguage(lng);
  };

  return (
    <select
      value={i18n.language}
      onChange={(e) => changeLanguage(e.target.value)}
    >
      {LANGUAGES.map(({ code, label }) => (
        <option key={code} value={code}>
          {label}
        </option>
      ))}
    </select>
  );
}

React Suspense and Lazy Loading

react-i18next integrates with React Suspense to handle asynchronous translation loading gracefully. When using HTTP backend, Suspense provides a clean loading state while translations are fetched:

import { Suspense } from "react";
import { useTranslation } from "react-i18next";

// i18next supports React Suspense for async translation loading
function App() {
  return (
    <Suspense fallback={<div>Loading translations...</div>}>
      <MainContent />
    </Suspense>
  );
}

function MainContent() {
  const { t, ready } = useTranslation();

  // With Suspense, 'ready' is always true here
  return <h1>{t("welcome.title")}</h1>;
}

TypeScript Type-Safe Translation Keys

i18next supports full TypeScript integration. By augmenting the i18next module, you get autocomplete and compile-time checking for all translation keys—preventing typos and missing translations:

// src/i18n/types.ts
import "i18next";
import translation from "../locales/en/translation.json";

declare module "i18next" {
  interface CustomTypeOptions {
    defaultNS: "translation";
    resources: {
      translation: typeof translation;
    };
  }
}

// Now t() calls are fully type-safe:
// t("welcome.title")       ✅ Autocomplete works
// t("welcome.greeting")    ✅ Valid key
// t("invalid.key")         ❌ TypeScript error
Type-safe keys work with useTranslation, Trans component, and all i18next APIs. Missing or mistyped keys are flagged as TypeScript errors in your IDE.

Server-Side Rendering with Next.js

For Next.js applications using the App Router, you can use i18next on both server and client components. Create a separate i18next instance for server-side translations to avoid sharing state between requests:

// next-i18next.config.js (Next.js App Router)
// For Next.js 13+ with App Router, use next-intl or i18next directly

// src/i18n/server.ts
import { createInstance } from "i18next";
import { initReactI18next } from "react-i18next/initReactI18next";

export async function getServerTranslation(lng: string) {
  const i18nInstance = createInstance();
  await i18nInstance.use(initReactI18next).init({
    lng,
    fallbackLng: "en",
    resources: {
      [lng]: {
        translation: (await import(`../locales/${lng}/translation.json`)).default,
      },
    },
  });
  return i18nInstance;
}

// In a Server Component:
export default async function Page({ params }: { params: { lng: string } }) {
  const i18n = await getServerTranslation(params.lng);
  const t = i18n.t.bind(i18n);

  return <h1>{t("welcome.title")}</h1>;
}
  • Create a new i18next instance per request on the server to prevent state leakage between users
  • Use the same translation JSON files for both server and client rendering
  • Consider i18next-resources-to-backend or next-intl as alternatives for complex Next.js setups

Accelerate Translation with AI

Setting up react-i18next is only half the battle—you still need high-quality translations for every target language. Manually translating dozens of JSON files is slow, expensive, and error-prone. AI-powered translation changes the game.

Why Use l10n.dev for React i18next?

l10n.dev is purpose-built for developer localization workflows. It understands your JSON translation files natively and produces translations that feel natural, not machine-generated:

  • Native JSON support — Upload your translation.json and get back perfectly formatted, ready-to-use JSON files
  • Context-aware AI — Understands your app's domain, preserving technical terms and brand voice across languages
  • Interpolation-safe — Preserves {{variables}}, plural suffixes (_one, _other), and nested key structures
  • Batch translation — Translate to 10+ languages simultaneously in seconds, not days
  • CI/CD integration — Automate translations as part of your build pipeline with the ai-l10n npm package

AI Translation Workflow

  1. Write your source translation.json in English (or your base language)
  2. Upload to l10n.dev or run the ai-l10n CLI to translate
  3. AI generates contextually accurate translations for all target languages
  4. Drop the translated JSON files into your locales/ directory and deploy

Automate Translations with ai-l10n CLI

The ai-l10n npm package integrates AI translation directly into your development workflow. Translate your JSON files from the command line:

# Install the l10n.dev CLI
npm install ai-l10n

# Translate your JSON files to multiple languages at once
npx ai-l10n translate src/locales/en/translation.json \
  --languages fr,de,ja,es,ko,zh-CN

Build Integration

Add translation automation to your build pipeline so translations are always up to date:

package.json Scripts

{
  "scripts": {
    "translate": "ai-l10n translate src/locales/en/translation.json --languages fr,de,ja,es,ko --update",
    "pretranslate": "npm run translate",
    "dev": "vite",
    "build": "npm run translate && vite build"
  }
}

Makefile

LANGUAGES = fr,de,ja,es,ko,zh-CN

translate:
	npx ai-l10n translate src/locales/en/translation.json --languages $(LANGUAGES) --update

dev: translate
	npx vite

build: translate
	npx vite build

For more details on CI/CD integration and automation, see our Localization Automation Guide

VS Code Extension for JSON Translation

Translate your React i18next JSON files directly from VS Code. The l10n.dev extension lets you translate files without leaving your editor:

How It Works

  1. Install the l10n.dev extension from the VS Code Marketplace
  2. Open your source translation.json file
  3. Right-click and select 'Translate with l10n.dev'
  4. Choose your target languages and get translated files instantly

Start Localizing Your React App Today

You now have everything you need to build a fully localized React application with react-i18next. Combine the power of i18next's mature ecosystem with AI-powered translation to ship your app in any language, fast.