Introduction#
Building a React app for global users? This guide walks you through internationalization (i18n) from setup to deployment. We'll use react-i18next, the most popular i18n library for React.
Prerequisites#
- React 18+ application
- npm or yarn
- Basic React knowledge
Step 1: Install Dependencies#
npm install i18next react-i18next i18next-http-backend i18next-browser-languagedetector
| Package | Purpose |
|---|---|
| i18next | Core i18n framework |
| react-i18next | React bindings |
| i18next-http-backend | Load translations from files |
| i18next-browser-languagedetector | Auto-detect user language |
Step 2: Create Translation Files#
Create a folder structure for translations:
public/
locales/
en/
translation.json
de/
translation.json
es/
translation.json
public/locales/en/translation.json:
{
"welcome": "Welcome to our app",
"nav": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"buttons": {
"submit": "Submit",
"cancel": "Cancel",
"save": "Save Changes"
}
}
public/locales/de/translation.json:
{
"welcome": "Willkommen in unserer App",
"nav": {
"home": "Startseite",
"about": "Über uns",
"contact": "Kontakt"
},
"buttons": {
"submit": "Absenden",
"cancel": "Abbrechen",
"save": "Änderungen speichern"
}
}
Step 3: Configure i18next#
Create 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)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
debug: process.env.NODE_ENV === 'development',
interpolation: {
escapeValue: false, // React already escapes
},
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
});
export default i18n;
Step 4: Initialize in Your App#
src/index.js:
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './i18n'; // Import i18n configuration
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Suspense fallback={<div>Loading...</div>}>
<App />
</Suspense>
</React.StrictMode>
);
Step 5: Use Translations in Components#
Using the useTranslation Hook#
import { useTranslation } from 'react-i18next';
function HomePage() {
const { t } = useTranslation();
return (
<div>
<h1>{t('welcome')}</h1>
<nav>
<a href="/">{t('nav.home')}</a>
<a href="/about">{t('nav.about')}</a>
<a href="/contact">{t('nav.contact')}</a>
</nav>
<button>{t('buttons.submit')}</button>
</div>
);
}
Using the Trans Component#
For complex translations with JSX:
import { Trans } from 'react-i18next';
function WelcomeMessage({ name }) {
return (
<Trans i18nKey="greeting" values={{ name }}>
Hello <strong>{{ name }}</strong>, welcome back!
</Trans>
);
}
Translation file:
{
"greeting": "Hello <1>{{name}}</1>, welcome back!"
}
Step 6: Add Language Switcher#
import { useTranslation } from 'react-i18next';
function LanguageSwitcher() {
const { i18n } = useTranslation();
const languages = [
{ code: 'en', name: 'English' },
{ code: 'de', name: 'Deutsch' },
{ code: 'es', name: 'Español' },
];
return (
<select
value={i18n.language}
onChange={(e) => i18n.changeLanguage(e.target.value)}
>
{languages.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.name}
</option>
))}
</select>
);
}
Step 7: Handle Pluralization#
Translation file:
{
"items_count": "{{count}} item",
"items_count_plural": "{{count}} items"
}
Component:
function CartCount({ count }) {
const { t } = useTranslation();
return <span>{t('items_count', { count })}</span>;
}
i18next automatically selects the plural form based on count.
Step 8: Interpolation with Variables#
Translation file:
{
"greeting": "Hello, {{name}}!",
"order_status": "Your order #{{orderId}} is {{status}}"
}
Component:
function Greeting({ user, order }) {
const { t } = useTranslation();
return (
<div>
<h1>{t('greeting', { name: user.name })}</h1>
<p>{t('order_status', {
orderId: order.id,
status: order.status
})}</p>
</div>
);
}
Best Practices#
1. Organize Translation Keys#
Use nested objects for organization:
{
"pages": {
"home": {
"title": "Welcome",
"description": "..."
},
"checkout": {
"title": "Checkout",
"steps": {
"shipping": "Shipping",
"payment": "Payment"
}
}
}
}
2. Extract Strings Early#
Don't hardcode strings. Use translations from the start:
// Bad
<button>Submit</button>
// Good
<button>{t('buttons.submit')}</button>
3. Use Namespaces for Large Apps#
Split translations into multiple files:
// i18n.js
i18n.init({
ns: ['common', 'home', 'checkout'],
defaultNS: 'common',
});
// In components
const { t } = useTranslation('checkout');
4. Handle Loading States#
function App() {
const { t, ready } = useTranslation();
if (!ready) return <LoadingSpinner />;
return <div>{t('welcome')}</div>;
}
Translating Your React App#
Once your app is set up for i18n, you need translations. Here's the workflow:
- Export your
en/translation.json - Upload to shipglobal.dev
- Select target languages (German, Spanish, etc.)
- Download translated JSON files
- Place in
public/locales/{lang}/ - Deploy
Your React app now speaks multiple languages!
Conclusion#
Internationalizing a React app with react-i18next is straightforward:
- Install dependencies
- Create translation files
- Configure i18next
- Use
useTranslationhook - Add language switcher
Start with your primary languages and expand based on user demand. With proper setup, adding new languages takes minutes, not days.