Skip to main content
How can I add multilingual support to a React app?

How can I add multilingual support to a React app?

· 8 min read

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:

  1. Extract text strings from your components into translation files
  2. Load the appropriate language based on user preferences
  3. Provide a language switcher for users to change languages
  4. Handle pluralization (different languages have different plural rules)
  5. 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.

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.