How to Translate a React App with i18next: Complete Guide

Learn how to localize your React app using i18next. This comprehensive guide covers string translation, pluralization, date formatting, and best practices for React app translation.

  • date icon

    Saturday, Nov 15, 2025

How to Translate a React App with i18next: Complete Guide

Translating your React app is essential for reaching a global audience. i18next is one of the most popular and powerful internationalization frameworks for React, providing a robust solution for managing translations. This guide will walk you through the process of translating a React app using i18next.

Understanding React Localization with i18next

i18next is an internationalization framework that works seamlessly with React through the react-i18next package. It supports multiple languages, pluralization, interpolation, and many other features needed for professional localization.

Step 1: Install Dependencies

First, install the necessary packages:

npm install i18next react-i18next i18next-browser-languagedetector

Or with yarn:

yarn add i18next react-i18next i18next-browser-languagedetector

Step 2: Create Translation Files

Create a locales directory in your src folder and add translation files for each language:

src/locales/en/translation.json (English - default):

{
  "welcome": "Welcome to our app!",
  "button": {
    "submit": "Submit",
    "cancel": "Cancel",
    "delete": "Delete"
  },
  "error": {
    "network": "Network error. Please try again.",
    "notFound": "Page not found"
  },
  "itemsCount": "{{count}} items",
  "itemsCount_one": "{{count}} item",
  "itemsCount_zero": "No items",
  "userGreeting": "Hello, {{name}}! You have {{count}} messages.",
  "userGreeting_one": "Hello, {{name}}! You have {{count}} message.",
  "userGreeting_zero": "Hello, {{name}}! You have no messages."
}

src/locales/es/translation.json (Spanish):

{
  "welcome": "¡Bienvenido a nuestra aplicación!",
  "button": {
    "submit": "Enviar",
    "cancel": "Cancelar",
    "delete": "Eliminar"
  },
  "error": {
    "network": "Error de red. Por favor, inténtalo de nuevo.",
    "notFound": "Página no encontrada"
  },
  "itemsCount": "{{count}} elementos",
  "itemsCount_one": "{{count}} elemento",
  "itemsCount_zero": "No hay elementos",
  "userGreeting": "¡Hola, {{name}}! Tienes {{count}} mensajes.",
  "userGreeting_one": "¡Hola, {{name}}! Tienes {{count}} mensaje.",
  "userGreeting_zero": "¡Hola, {{name}}! No tienes mensajes."
}

src/locales/fr/translation.json (French):

{
  "welcome": "Bienvenue dans notre application!",
  "button": {
    "submit": "Soumettre",
    "cancel": "Annuler",
    "delete": "Supprimer"
  },
  "error": {
    "network": "Erreur réseau. Veuillez réessayer.",
    "notFound": "Page non trouvée"
  },
  "itemsCount": "{{count}} éléments",
  "itemsCount_one": "{{count}} élément",
  "itemsCount_zero": "Aucun élément",
  "userGreeting": "Bonjour, {{name}}! Vous avez {{count}} messages.",
  "userGreeting_one": "Bonjour, {{name}}! Vous avez {{count}} message.",
  "userGreeting_zero": "Bonjour, {{name}}! Vous n'avez aucun message."
}

Step 3: Configure i18next

Create an i18n configuration file:

src/i18n.js:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

import translationEN from './locales/en/translation.json';
import translationES from './locales/es/translation.json';
import translationFR from './locales/fr/translation.json';

const resources = {
  en: {
    translation: translationEN
  },
  es: {
    translation: translationES
  },
  fr: {
    translation: translationFR
  }
};

i18n
  .use(LanguageDetector) // Detects user language
  .use(initReactI18next) // Passes i18n down to react-i18next
  .init({
    resources,
    fallbackLng: 'en', // Default language
    debug: false, // Set to true for debugging
    
    interpolation: {
      escapeValue: false // React already escapes values
    },
    
    react: {
      useSuspense: false // Set to true if using Suspense
    }
  });

export default i18n;

Or with TypeScript (src/i18n.ts):

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

import translationEN from './locales/en/translation.json';
import translationES from './locales/es/translation.json';
import translationFR from './locales/fr/translation.json';

const resources = {
  en: {
    translation: translationEN
  },
  es: {
    translation: translationES
  },
  fr: {
    translation: translationFR
  }
} as const;

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    resources,
    fallbackLng: 'en',
    debug: false,
    interpolation: {
      escapeValue: false
    },
    react: {
      useSuspense: false
    }
  });

export default i18n;

Step 4: Initialize i18n in Your App

Import i18n in your main App component:

src/App.js:

import React from 'react';
import './i18n'; // Import i18n configuration
import HomePage from './components/HomePage';

function App() {
  return (
    <div className="App">
      <HomePage />
    </div>
  );
}

export default App;

Or with TypeScript (src/App.tsx):

import React from 'react';
import './i18n';
import HomePage from './components/HomePage';

const App: React.FC = () => {
  return (
    <div className="App">
      <HomePage />
    </div>
  );
};

export default App;

Step 5: Use Translations in Components

Use the useTranslation hook to access translations:

Before:

function Welcome() {
  return <h1>Welcome to our app!</h1>;
}

After:

import { useTranslation } from 'react-i18next';

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

With TypeScript:

import { useTranslation } from 'react-i18next';

const Welcome: React.FC = () => {
  const { t } = useTranslation();
  
  return <h1>{t('welcome')}</h1>;
};

Step 6: Handle Nested Translation Keys

Access nested keys using dot notation:

import { useTranslation } from 'react-i18next';

function Buttons() {
  const { t } = useTranslation();
  
  return (
    <div>
      <button>{t('button.submit')}</button>
      <button>{t('button.cancel')}</button>
      <button>{t('button.delete')}</button>
    </div>
  );
}

Step 7: String Interpolation with Variables

Pass variables to translations:

translation.json:

{
  "userGreeting": "Hello, {{name}}! You have {{count}} messages."
}

Component:

import { useTranslation } from 'react-i18next';

function UserGreeting({ name, messageCount }) {
  const { t } = useTranslation();
  
  return (
    <p>
      {t('userGreeting', { 
        name: name, 
        count: messageCount 
      })}
    </p>
  );
}

// Usage: <UserGreeting name="John" messageCount={5} />
// English: "Hello, John! You have 5 messages."
// Spanish: "¡Hola, John! Tienes 5 mensajes."

Step 8: Pluralization

i18next handles pluralization automatically based on the count:

translation.json:

{
  "itemsCount": "{{count}} items",
  "itemsCount_one": "{{count}} item",
  "itemsCount_zero": "No items"
}

Component:

import { useTranslation } from 'react-i18next';

function ItemCount({ count }) {
  const { t } = useTranslation();
  
  return <p>{t('itemsCount', { count })}</p>;
}

// Usage examples:
// <ItemCount count={0} /> → "No items" (English) / "No hay elementos" (Spanish)
// <ItemCount count={1} /> → "1 item" (English) / "1 elemento" (Spanish)
// <ItemCount count={5} /> → "5 items" (English) / "5 elementos" (Spanish)

Step 9: Format Dates and Numbers

Use i18next’s formatting features or JavaScript’s Intl API:

Dates:

import { useTranslation } from 'react-i18next';

function DateDisplay({ date }) {
  const { t, i18n } = useTranslation();
  
  const formattedDate = new Intl.DateTimeFormat(i18n.language, {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    weekday: 'long'
  }).format(date);
  
  return <p>{formattedDate}</p>;
}

// English: "Wednesday, March 20, 2025"
// Spanish: "miércoles, 20 de marzo de 2025"

Numbers:

import { useTranslation } from 'react-i18next';

function NumberDisplay({ number }) {
  const { i18n } = useTranslation();
  
  const formattedNumber = new Intl.NumberFormat(i18n.language).format(number);
  
  return <p>{formattedNumber}</p>;
}

// English: "1,234.56"
// Spanish: "1.234,56"

Currency:

import { useTranslation } from 'react-i18next';

function CurrencyDisplay({ amount }) {
  const { i18n } = useTranslation();
  
  const formattedCurrency = new Intl.NumberFormat(i18n.language, {
    style: 'currency',
    currency: 'USD'
  }).format(amount);
  
  return <p>{formattedCurrency}</p>;
}

// English (US): "$1,234.56"
// Spanish (ES): "1.234,56 €"

Step 10: Change Language Programmatically

Allow users to switch languages:

import { useTranslation } from 'react-i18next';

function LanguageSwitcher() {
  const { i18n } = useTranslation();
  
  const changeLanguage = (lng) => {
    i18n.changeLanguage(lng);
  };
  
  return (
    <div>
      <button onClick={() => changeLanguage('en')}>English</button>
      <button onClick={() => changeLanguage('es')}>Español</button>
      <button onClick={() => changeLanguage('fr')}>Français</button>
    </div>
  );
}

With language detection:

function LanguageSwitcher() {
  const { i18n } = useTranslation();
  
  const currentLanguage = i18n.language;
  
  return (
    <select 
      value={currentLanguage} 
      onChange={(e) => i18n.changeLanguage(e.target.value)}
    >
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="fr">Français</option>
    </select>
  );
}

Step 11: Handle Right-to-Left (RTL) Languages

For RTL languages like Arabic and Hebrew:

import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';

function App() {
  const { i18n } = useTranslation();
  
  useEffect(() => {
    const isRTL = ['ar', 'he', 'fa'].includes(i18n.language);
    document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
    document.documentElement.lang = i18n.language;
  }, [i18n.language]);
  
  return (
    <div className="App">
      {/* Your app content */}
    </div>
  );
}

CSS for RTL support:

/* Use logical properties */
.container {
  padding-inline-start: 1rem;
  padding-inline-end: 1rem;
  margin-inline-start: auto;
  margin-inline-end: auto;
}

/* Or use CSS variables */
:root[dir="rtl"] {
  --text-align: right;
}

:root[dir="ltr"] {
  --text-align: left;
}

Best Practices

1. Use Descriptive Translation Keys

Bad:

{
  "msg1": "Submit"
}

Good:

{
  "button": {
    "submit": "Submit"
  }
}

2. Organize Translations by Feature

translation.json:

{
  "auth": {
    "login": "Login",
    "logout": "Logout",
    "signup": "Sign Up"
  },
  "dashboard": {
    "title": "Dashboard",
    "welcome": "Welcome back!"
  },
  "settings": {
    "title": "Settings",
    "language": "Language"
  }
}

3. Provide Context in Translation Keys

Use descriptive keys that indicate context:

{
  "button": {
    "delete": "Delete"
  },
  "action": {
    "deleteItem": "Delete Item",
    "deleteAccount": "Delete Account"
  }
}

4. Use Namespaces for Large Apps

For large applications, split translations into namespaces:

i18n.js:

import commonEN from './locales/en/common.json';
import authEN from './locales/en/auth.json';
import dashboardEN from './locales/en/dashboard.json';

const resources = {
  en: {
    common: commonEN,
    auth: authEN,
    dashboard: dashboardEN
  },
  // ... other languages
};

Usage:

const { t } = useTranslation('auth');
return <h1>{t('login')}</h1>;

5. Handle Missing Translations

Configure fallback behavior:

i18n.init({
  fallbackLng: 'en',
  saveMissing: true, // Logs missing keys
  missingKeyHandler: (lng, ns, key) => {
    console.warn(`Missing translation: ${key} for language: ${lng}`);
  }
});

6. Test String Lengths

Some languages are longer than others. Design your UI to accommodate:

  • German and Finnish: 30-50% longer than English
  • Asian languages: May need more vertical space

Common Pitfalls

1. Forgetting to Import i18n

Always import i18n configuration in your main App component:

import './i18n'; // Don't forget this!

2. Not Using the useTranslation Hook

Bad:

import i18n from './i18n';
return <p>{i18n.t('welcome')}</p>; // Works but not reactive

Good:

const { t } = useTranslation();
return <p>{t('welcome')}</p>; // Reactive to language changes

3. Hardcoding Format Strings

Bad:

const price = `$${amount.toFixed(2)}`;

Good:

const { i18n } = useTranslation();
const price = new Intl.NumberFormat(i18n.language, {
  style: 'currency',
  currency: 'USD'
}).format(amount);

4. Not Testing All Languages

Always test your app in all supported languages to catch:

  • Missing translations
  • UI layout issues
  • Text overflow
  • RTL layout problems

Advanced: Lazy Loading Translations

For better performance, load translations on demand:

i18n.js:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend'; // Install: npm install i18next-http-backend

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: 'en',
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json'
    }
  });

Advanced: TypeScript Support

For better TypeScript support, create type definitions:

src/types/i18next.d.ts:

import 'react-i18next';
import translationEN from '../locales/en/translation.json';

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: 'translation';
    resources: {
      translation: typeof translationEN;
    };
  }
}

Advanced: Persist Language Preference

Save user’s language choice to localStorage:

import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';

function App() {
  const { i18n } = useTranslation();
  
  useEffect(() => {
    const savedLanguage = localStorage.getItem('language');
    if (savedLanguage) {
      i18n.changeLanguage(savedLanguage);
    }
  }, [i18n]);
  
  const changeLanguage = (lng) => {
    i18n.changeLanguage(lng);
    localStorage.setItem('language', lng);
  };
  
  // ... rest of component
}

Using with Next.js

For Next.js applications, use next-i18next:

npm install next-i18next

next-i18next.config.js:

module.exports = {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'es', 'fr'],
  },
};

next.config.js:

const { i18n } = require('./next-i18next.config');

module.exports = {
  i18n,
};

Conclusion

Localizing your React app with i18next is straightforward when you follow these steps:

  1. Install i18next and react-i18next packages
  2. Create translation JSON files for each language
  3. Configure i18next with language detection
  4. Import i18n configuration in your App component
  5. Use useTranslation hook throughout your components
  6. Handle pluralization and string interpolation
  7. Format dates and numbers using Intl API
  8. Allow users to switch languages
  9. Test thoroughly in all supported languages

By following these practices, you’ll create an app that provides a native experience for users worldwide, significantly expanding your potential user base.

Streamline Your React Localization Workflow

Managing translations for multiple languages can become complex as your app grows. Consider using a translation management platform to:

  • Collaborate with translators
  • Keep translations in sync with your codebase
  • Automate the translation workflow
  • Maintain consistency across all languages
  • Generate translation files automatically
  • Integrate with your CI/CD pipeline

Ready to take your React app global? Explore AZbox’s localization platform and streamline your translation workflow:

View AZbox Plans and Pricing

Blog

Latest Posts

Discover our latest articles and updates.

Why Translations Have Always Been a Problem in Software Development
date icon

Sunday, Dec 21, 2025

Why Translations Have Always Been a Problem in Software Development

For decades, software developers have struggled with translations and localization. What should be a straightforward pro

Read More
Cómo Traducir una App Flutter con Azbox: Guía Completa
date icon

Saturday, Dec 20, 2025

Cómo Traducir una App Flutter con Azbox: Guía Completa

Traducir tu app Flutter es esencial para llegar a una audiencia global. Azbox proporciona un potente SDK de Flutter que

Read More
How to Translate a Flutter App with Azbox: Complete Guide
date icon

Saturday, Dec 20, 2025

How to Translate a Flutter App with Azbox: Complete Guide

Translating your Flutter app is essential for reaching a global audience. Azbox provides a powerful Flutter SDK that sim

Read More
cta-image

Start Global Growth Today

Join hundreds of successful companies already using AZbox to reach customers worldwide. Start with a free trial, no credit card required.

Get Started - It's Free