How can I add multilingual support to a React app?
Adding multilingual support (also known as internationalization or i18n) to a React application is essential if you want to reach a global audience. While React doesn't include built-in i18n functionality, there are several excellent libraries and approaches you can use to implement multilingual support in your React app.
In this guide, we'll walk through the most common approaches, from simple solutions for small projects to more robust solutions for production applications.
Understanding React Internationalization
Internationalization (i18n) is the process of designing your application to support multiple languages and regions. When implementing i18n in React, you typically need to:
- Extract text strings from your components into translation files
- Load the appropriate language based on user preferences
- Provide a language switcher for users to change languages
- Handle pluralization (different languages have different plural rules)
- Format dates, numbers, and currencies according to locale conventions
The challenge is that React's JSX makes it easy to embed text directly in components, but this doesn't scale when you need multiple languages. You need a system that can replace hardcoded strings with translated versions at runtime.
Method 1: Using react-i18next (Most Popular)
react-i18next is the most popular React internationalization library, built on top of i18next. It's battle-tested, has a large community, and supports all the features you'd need.
Installation
npm install react-i18next i18next
Basic Setup
First, create translation files for each language:
public/locales/en/translation.json:
{
"welcome": "Welcome to our app",
"greeting": "Hello, {{name}}!",
"items": {
"one": "{{count}} item",
"other": "{{count}} items"
}
}
public/locales/es/translation.json:
{
"welcome": "Bienvenido a nuestra aplicación",
"greeting": "¡Hola, {{name}}!",
"items": {
"one": "{{count}} artículo",
"other": "{{count}} artículos"
}
}
src/i18n.js:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(Backend) // Load translations from /public/locales
.use(LanguageDetector) // Detect user language
.use(initReactI18next) // Pass i18n down to react-i18next
.init({
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false // React already escapes values
}
});
export default i18n;
src/App.js:
import React from 'react';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
import MyComponent from './MyComponent';
function App() {
return (
<I18nextProvider i18n={i18n}>
<MyComponent />
</I18nextProvider>
);
}
export default App;
src/MyComponent.jsx:
import React from 'react';
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t, i18n } = useTranslation();
const changeLanguage = (lng) => {
i18n.changeLanguage(lng);
};
return (
<div>
<h1>{t('welcome')}</h1>
<p>{t('greeting', { name: 'John' })}</p>
<p>{t('items', { count: 5 })}</p>
<button onClick={() => changeLanguage('en')}>English</button>
<button onClick={() => changeLanguage('es')}>Español</button>
</div>
);
}
export default MyComponent;
Key Features
- Pluralization: Automatically handles plural forms based on count
- Interpolation: Insert variables into translations using
{{variable}} - Namespaces: Organize translations into separate files
- Lazy loading: Load translations on demand
- TypeScript support: Full TypeScript definitions available
Method 2: Using react-intl (Format.js)
react-intl is part of the Format.js suite, maintained by Yahoo. It uses the ICU message format, which is an industry standard.
Installation
npm install react-intl
Basic Setup
src/i18n/messages.js:
export const messages = {
en: {
welcome: 'Welcome to our app',
greeting: 'Hello, {name}!',
items: '{count, plural, =0 {no items} one {# item} other {# items}}'
},
es: {
welcome: 'Bienvenido a nuestra aplicación',
greeting: '¡Hola, {name}!',
items: '{count, plural, =0 {ningún artículo} one {# artículo} other {# artículos}}'
}
};
src/App.js:
import React, { useState } from 'react';
import { IntlProvider, FormattedMessage, FormattedNumber } from 'react-intl';
import { messages } from './i18n/messages';
function App() {
const [locale, setLocale] = useState('en');
return (
<IntlProvider messages={messages[locale]} locale={locale}>
<div>
<h1><FormattedMessage id="welcome" /></h1>
<p>
<FormattedMessage
id="greeting"
values={{ name: 'John' }}
/>
</p>
<p>
<FormattedMessage
id="items"
values={{ count: 5 }}
/>
</p>
<button onClick={() => setLocale('en')}>English</button>
<button onClick={() => setLocale('es')}>Español</button>
</div>
</IntlProvider>
);
}
export default App;
Key Features
- ICU Message Format: Industry-standard message formatting
- Rich Formatting: Built-in support for dates, numbers, currencies, and relative time
- Component-based: Uses React components for translations
- Type-safe: Good TypeScript support
Method 3: Using SejHey React i18n (Integrated Platform)
SejHey's @sejhey/react-i18n provides both a React library and a translation management platform. This integrated approach means you don't need to manage translation files manually.
Installation
npm install @sejhey/react-i18n
Basic Setup
src/App.js:
import React from 'react';
import { SejheyI18n, SejHeyProvider, useTranslate } from '@sejhey/react-i18n';
// Create and configure the i18n instance
const i18n = new SejheyI18n({
defaultLanguage: 'en',
languageDetectionSettings: {
detectionOrder: ['querystring', 'cookie', 'localStorage', 'navigator']
}
})
.useCdnLoader({
sejheyProjectId: 'your-project-id',
otaEnvName: 'production'
})
.useInContextEditor({
sejheyProjectId: 'your-project-id',
enableByQueryParam: 'in_context'
})
.useLanguagePicker({ position: 'bottom-right' });
function App() {
return (
<SejHeyProvider i18n={i18n}>
<MyComponent />
</SejHeyProvider>
);
}
export default App;
src/MyComponent.jsx:
import React from 'react';
import { useTranslate, T } from '@sejhey/react-i18n';
function MyComponent() {
const { t, currentLanguage, changeLanguage } = useTranslate();
return (
<div>
<h1>{t('welcome')}</h1>
<p>{t('greeting', { name: 'John' })}</p>
{/* Component-based translation */}
<T keyName="welcome" />
{/* Language switcher */}
<select
value={currentLanguage}
onChange={(e) => changeLanguage(e.target.value)}
>
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
</select>
</div>
);
}
export default MyComponent;
Key Features
- CDN-based loading: Translations loaded from CDN, can be updated without redeploying
- In-context editing: Translators can edit translations directly in your running app
- Translation management: Built-in platform for managing translations
- Automatic language detection: Detects user's preferred language
- TypeScript support: Full TypeScript definitions
Method 4: Simple Custom Hook (For Small Projects)
For very small projects, you can create a simple custom hook:
src/hooks/useTranslation.js:
import { useState, useEffect } from 'react';
const translations = {
en: {
welcome: 'Welcome',
greeting: 'Hello, {name}!'
},
es: {
welcome: 'Bienvenido',
greeting: '¡Hola, {name}!'
}
};
export function useTranslation(locale = 'en') {
const [language, setLanguage] = useState(locale);
const t = (key, params = {}) => {
let translation = translations[language]?.[key] || key;
// Simple parameter replacement
Object.keys(params).forEach(param => {
translation = translation.replace(`{${param}}`, params[param]);
});
return translation;
};
return { t, language, setLanguage };
}
Usage:
import { useTranslation } from './hooks/useTranslation';
function MyComponent() {
const { t, language, setLanguage } = useTranslation();
return (
<div>
<h1>{t('welcome')}</h1>
<p>{t('greeting', { name: 'John' })}</p>
<button onClick={() => setLanguage('es')}>Español</button>
</div>
);
}
This approach is fine for prototypes or very small projects, but lacks features like pluralization, date formatting, and proper locale handling.
Best Practices for React Multilingual Support
Regardless of which library you choose, here are some best practices:
1. Organize Translation Keys
Use a hierarchical structure for your translation keys:
// Good
t('auth.login.title')
t('auth.login.button')
t('dashboard.stats.revenue')
// Avoid
t('loginTitle')
t('loginButton')
t('revenue')
2. Provide Context
Always provide context for translators. A word like "charge" could mean billing, a call to action, or battery status. Use descriptions, screenshots, or usage notes.
3. Handle Pluralization Correctly
Different languages have different plural rules. Most libraries handle this automatically:
// English: 1 item, 2 items
// Polish: 1 przedmiot, 2-4 przedmioty, 5+ przedmiotów
t('items.count', { count: 5 })
4. Test with Different Languages
Test with languages that have:
- Long translations (German, Finnish) - can break layouts
- Right-to-left (Arabic, Hebrew) - requires RTL support
- Different character sets (Chinese, Japanese) - may need different fonts
- Different plural rules - tests your pluralization
5. Optimize Performance
- Lazy load translations: Only load the language the user needs
- Code splitting: Split translation files by route or feature
- Caching: Cache translations on client and server
- CDN delivery: For large apps, CDN-based loading can improve performance
6. Plan for Updates
Consider how you'll update translations after deployment:
- Bundled translations: Require redeployment for updates
- CDN-based: Can update without redeploying
- Over-the-air updates: Some platforms support OTA translation updates
Choosing the Right Solution
Choose react-i18next if:
- You want maximum flexibility
- You have an existing translation workflow
- You need extensive plugin ecosystem support
- You're comfortable managing translation files
Choose react-intl if:
- You're building an enterprise application
- You need complex formatting (dates, numbers, currencies)
- You need ICU message format support
- You have dedicated i18n specialists
Choose SejHey if:
- You want an integrated solution (library + platform)
- You need in-context editing
- You want CDN-based translations without manual setup
- You prefer to avoid managing translation files yourself
Choose a custom solution if:
- You have very simple requirements
- You're building a prototype
- You want minimal dependencies
Conclusion
Adding multilingual support to a React app is essential for reaching a global audience. While React doesn't include built-in i18n, there are excellent libraries available:
- react-i18next: Most popular, flexible, large ecosystem
- react-intl: Enterprise-grade, comprehensive formatting
- SejHey: Integrated platform approach with CDN loading and in-context editing
- Custom solution: For very simple use cases
The most important thing is to start thinking about internationalization early in your project. Even if you're only supporting English initially, structure your code to make adding more languages straightforward.
Whichever solution you choose, follow best practices around key organization, providing context, handling pluralization, and performance optimization. These practices will pay off as your application grows and you add more languages.
If you're looking for a modern, integrated solution that handles both the React library and translation management, check out SejHey's React integration or explore the npm package directly.

