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:
- Install
i18nextandreact-i18nextpackages - Create translation JSON files for each language
- Configure i18next with language detection
- Import i18n configuration in your App component
- Use
useTranslationhook throughout your components - Handle pluralization and string interpolation
- Format dates and numbers using Intl API
- Allow users to switch languages
- 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: