๐๐ผ๐ ๐๐ผ ๐๐ฐ๐ต๐ถ๐ฒ๐๐ฒ ๐๐ผ๐ฐ๐ฎ๐น๐ถ๐๐ฎ๐๐ถ๐ผ๐ป ๐ถ๐ป ๐ฅ๐ฒ๐ฎ๐ฐ๐.๐ท๐
๐ ๐๐ผ๐ ๐๐ผ ๐๐ฐ๐ต๐ถ๐ฒ๐๐ฒ ๐๐ผ๐ฐ๐ฎ๐น๐ถ๐๐ฎ๐๐ถ๐ผ๐ป ๐ถ๐ป ๐ฅ๐ฒ๐ฎ๐ฐ๐.๐ท๐ (Step by Step)
Your React app works perfectly โ in one language.
The moment you go global? Everything breaks. ๐ถ
Here's the complete step-by-step localisation setup ๐
๐ง ๐ช๐ต๐ฎ๐ ๐ช๐ฒ'๐ฟ๐ฒ ๐๐๐ถ๐น๐ฑ๐ถ๐ป๐ด
๐ Multi-language React app
๐ Auto language detection
๐ Dynamic language switching
๐ Formatted dates, numbers & currencies
English โ French โ Japanese โ Arabic (RTL)
All from one codebase. Zero rewrites.
๐ฆ 1๏ธโฃ ๐๐ป๐๐๐ฎ๐น๐น ๐๐ฒ๐ฝ๐ฒ๐ป๐ฑ๐ฒ๐ป๐ฐ๐ถ๐ฒ๐
npm install i18next react-i18next
npm install i18next-browser-languagedetector
npm install i18next-http-backend
โ i18next โ core translation engine
โ react-i18next โ React bindings & hooks
โ browser-languagedetector โ auto detects user's language
โ http-backend โ lazy loads translation files
๐๏ธ 2๏ธโฃ ๐๐ผ๐น๐ฑ๐ฒ๐ฟ ๐ฆ๐๐ฟ๐๐ฐ๐๐๐ฟ๐ฒ
๐ Keep translations outside your components
src/
โโโ i18n/
โ โโโ index.js โ i18n config
โ โโโ locales/
โ โโโ en/
โ โ โโโ translation.json
โ โโโ fr/
โ โ โโโ translation.json
โ โโโ ja/
โ โโโ translation.json
โ One folder per language
โ Same key structure across all files
โ Easy to hand off to translators
๐ 3๏ธโฃ ๐๐ฟ๐ฒ๐ฎ๐๐ฒ ๐ง๐ฟ๐ฎ๐ป๐๐น๐ฎ๐๐ถ๐ผ๐ป ๐๐ถ๐น๐ฒ๐
๐ Same keys, different values per language
// locales/en/translation.json
{
"welcome": "Welcome, {{name}}!",
"logout": "Logout",
"items_one": "{{count}} item",
"items_other": "{{count}} items",
"errors": {
"required": "This field is required"
}
}
// locales/fr/translation.json
{
"welcome": "Bienvenue, {{name}}!",
"logout": "Se dรฉconnecter",
"items_one": "{{count}} article",
"items_other": "{{count}} articles",
"errors": {
"required": "Ce champ est obligatoire"
}
}
โ Use nested keys for grouping
โ {{variable}} syntax for dynamic values
โ _one / _other suffix for plurals
โ๏ธ 4๏ธโฃ ๐๐ผ๐ป๐ณ๐ถ๐ด๐๐ฟ๐ฒ ๐ถ๐ญ๐ด๐ป
// src/i18n/index.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import HttpBackend from 'i18next-http-backend';
i18n
.use(HttpBackend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en', // default if language not found
debug: false,
detection: {
order: ['localStorage', 'navigator'],
cacheUserLanguage: true // remembers user choice
},
backend: {
loadPath: '/locales/{{lng}}/translation.json'
},
interpolation: {
escapeValue: false // React handles XSS
}
});
export default i18n;
โ fallbackLng โ safety net if translation key missing
โ cacheUserLanguage โ persists language in localStorage
โ loadPath โ lazy loads only needed language file
๐ 5๏ธโฃ ๐๐ผ๐ป๐ป๐ฒ๐ฐ๐ ๐๐ผ ๐ฅ๐ฒ๐ฎ๐ฐ๐ ๐๐ฝ๐ฝ
// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './i18n/index.js'; // โ import before App renders
ReactDOM.createRoot(document.getElementById('root'))
.render(<App />);
โ Import i18n before App renders
โ No Provider needed โ i18next is global
โ Translations load automatically on mount
๐ค 6๏ธโฃ ๐จ๐๐ฒ ๐ง๐ฟ๐ฎ๐ป๐๐น๐ฎ๐๐ถ๐ผ๐ป๐ ๐ถ๐ป ๐๐ผ๐บ๐ฝ๐ผ๐ป๐ฒ๐ป๐๐
import { useTranslation } from 'react-i18next';
function Dashboard() {
const { t } = useTranslation();
return (
<div>
{/* Basic translation */}
<h1>{t('welcome', { name: 'John' })}</h1>
{/* Nested key */}
<span>{t('errors.required')}</span>
{/* Pluralisation โ auto picks _one or _other */}
<p>{t('items', { count: 5 })}</p>
</div>
);
}
โ t('key') โ translate a string
โ t('key', { var }) โ with dynamic values
โ t('key', { count }) โ automatic pluralisation
๐ 7๏ธโฃ ๐๐ฎ๐ป๐ด๐๐ฎ๐ด๐ฒ ๐ฆ๐๐ถ๐๐ฐ๐ต๐ฒ๐ฟ ๐๐ผ๐บ๐ฝ๐ผ๐ป๐ฒ๐ป๐
import { useTranslation } from 'react-i18next';
const languages = [
{ code: 'en', label: 'English' },
{ code: 'fr', label: 'Franรงais' },
{ code: 'ja', label: 'ๆฅๆฌ่ช' },
{ code: 'ar', label: 'ุงูุนุฑุจูุฉ' }
];
function LanguageSwitcher() {
const { i18n } = useTranslation();
const changeLanguage = (code) => {
i18n.changeLanguage(code);
// Handle RTL languages
document.dir = code === 'ar' ? 'rtl' : 'ltr';
};
return (
<div>
{languages.map(({ code, label }) => (
<button
key={code}
onClick={() => changeLanguage(code)}
className={i18n.language === code ? 'active' : ''}
>
{label}
</button>
))}
</div>
);
}
โ i18n.changeLanguage() โ switches instantly
โ document.dir = 'rtl' โ flips layout for Arabic
โ Language saved in localStorage automatically
๐ 8๏ธโฃ ๐๐ฎ๐๐ฒ๐, ๐ก๐๐บ๐ฏ๐ฒ๐ฟ๐ & ๐๐๐ฟ๐ฟ๐ฒ๐ป๐ฐ๐
function PriceDisplay({ amount, date }) {
const { i18n } = useTranslation();
const locale = i18n.language;
// Format currency
const price = new Intl.NumberFormat(locale, {
style: 'currency',
currency: locale === 'ja' ? 'JPY' : 'USD'
}).format(amount);
// Format date
const formattedDate = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
return (
<div>
<p>{price}</p>
<p>{formattedDate}</p>
</div>
);
}
โ Always use Intl API โ never manual formatting
โ Currency code changes per locale
โ Date format adapts automatically
โณ 9๏ธโฃ ๐๐ฎ๐ป๐ฑ๐น๐ถ๐ป๐ด ๐๐ผ๐ฎ๐ฑ๐ถ๐ป๐ด ๐ฆ๐๐ฎ๐๐ฒ
๐ Translations load async โ handle the loading state
import { useTranslation } from 'react-i18next';
import { Suspense } from 'react';
// Wrap app in Suspense
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Dashboard />
</Suspense>
);
}
// Or check inside component
function Dashboard() {
const { t, ready } = useTranslation();
if (!ready) return <Spinner />;
return <h1>{t('welcome')}</h1>;
}
โ ready โ true when translations are loaded
โ Suspense โ cleaner loading handling at app level
โ Never render untranslated content to users
๐จ ๐๐ผ๐บ๐บ๐ผ๐ป ๐ ๐ถ๐๐๐ฎ๐ธ๐ฒ๐
โ Hardcoding strings inside JSX
โ Not handling RTL layout for Arabic/Hebrew
โ Using English keys as translation keys t('Welcome back!')
โ Formatting dates manually instead of Intl API
โ Bundling all language files โ lazy load instead
โ Adding i18n after the app is built โ costs 10x more
๐ก ๐ฆ๐ฒ๐ป๐ถ๐ผ๐ฟ-๐๐ฒ๐๐ฒ๐น ๐๐ป๐๐ถ๐ด๐ต๐
Translation is 10% of localisation. Dates, RTL, plurals, and culture are the other 90%.
Build the infrastructure on day one โ adding it later is a full rewrite.
๐ฏ ๐๐ป๐๐ฒ๐ฟ๐๐ถ๐ฒ๐ ๐ข๐ป๐ฒ-๐๐ถ๐ป๐ฒ๐ฟ
Localisation in React is achieved using react-i18next for translation management, the browser LanguageDetector for auto-detection, the Intl API for dates, numbers and currencies, and dynamic RTL switching via document.dir โ all configured once and consumed anywhere via the useTranslation hook.
#ReactJS #Localisation #i18n #Frontend #WebDevelopment #JavaScript #InterviewPrep #GlobalProduct #EngineeringMindset
Mid-Year Sale โ Unlock Full Article
Base plan from just $4.99/mo or $49/yr
Sign in to read the full article and unlock all tutorials.
Sign In / Register โ Start Free Trial7-day free trial ยท Cancel anytime ยท 30-day money-back
