Multi-Language Support
Multi-language support allows users to use an application in the language and regional format they understand best.
Translation key + active locale + dynamic values
↓
Localized UI message
For example, the same key can produce different text:
en → "Log in"
hi → "लॉग इन करें"
fr → "Se connecter"
Instead of hardcoding visible text inside components, the application stores messages separately and resolves them for the selected locale.
Core idea: Components should describe which message they need; the localization layer decides how that message is presented for the user's locale.
1. Internationalization vs Localization
The two terms are related but not identical.
| Term | Meaning |
|---|---|
| Internationalization (i18n) | Designing the application so it can support different languages, regions, and writing systems |
| Localization (l10n) | Adapting the application for a particular locale, including translations and cultural conventions |
The numbers in i18n and l10n represent the letters omitted between the first and last characters.
Localization is more than translating words. It can also affect:
- dates and times;
- numbers, currencies, and units;
- plural rules;
- text direction;
- sorting and search;
- names, addresses, images, and culturally appropriate content.
2. Language Is Not the Same as Locale
A language identifies how people communicate. A locale also captures regional conventions.
en → English language
en-US → English used in the United States
en-GB → English used in the United Kingdom
pt-BR → Portuguese used in Brazil
Two users may read the same language but expect different date or currency formats.
Use standard BCP 47 language tags such as en, en-IN, or fr-FR rather than inventing identifiers.
3. Basic Architecture
A multilingual interface usually has four parts:
User or browser chooses a locale
↓
Locale manager stores the active locale
↓
Translation service loads matching resources
↓
Components request messages by stable keys
↓
Dates, numbers, plurals, and layout are localized
Example translation resources:
export const translations = {
en: {
welcomeTitle: "Welcome",
loginButton: "Log in",
},
hi: {
welcomeTitle: "स्वागत है",
loginButton: "लॉग इन करें",
},
};
Example component:
import { useState } from "react";
import { translations } from "./translations";
export default function App() {
const [locale, setLocale] = useState("en");
const messages = translations[locale] ?? translations.en;
return (
<main>
<h1>{messages.welcomeTitle}</h1>
<button>{messages.loginButton}</button>
<label>
Language
<select
value={locale}
onChange={event => setLocale(event.target.value)}
>
<option value="en">English</option>
<option value="hi">हिन्दी</option>
</select>
</label>
</main>
);
}
This is enough for a small application, prototype, or learning project.
4. Why Applications Use an i18n Library
A simple object lookup becomes harder to maintain when an application needs:
- hundreds or thousands of messages;
- interpolation and plural rules;
- fallback languages;
- translations split by page or feature;
- asynchronous resource loading;
- language detection and persistence;
- integration with a translation workflow.
Libraries such as i18next with react-i18next provide these capabilities without requiring the application to build them from scratch.
Example Resource Structure
public/
└── locales/
├── en/
│ ├── common.json
│ └── checkout.json
├── fr/
│ ├── common.json
│ └── checkout.json
└── hi/
├── common.json
└── checkout.json
{
"navigation": {
"home": "Home",
"orders": "Orders"
},
"greeting": "Hello, {{name}}!"
}
{
"navigation": {
"home": "होम",
"orders": "ऑर्डर"
},
"greeting": "नमस्ते, {{name}}!"
}
React Usage
import { useTranslation } from "react-i18next";
function Header({ user }) {
const { t, i18n } = useTranslation("common");
return (
<header>
<p>{t("greeting", { name: user.name })}</p>
<button onClick={() => i18n.changeLanguage("hi")}>
हिन्दी
</button>
</header>
);
}
The component uses a stable key while the library handles lookup, interpolation, loading, and re-rendering after a locale change.
5. Choosing the Initial Locale
A predictable priority order avoids surprising the user.
1. Explicit account preference
2. Previously saved device preference
3. Locale in the URL
4. Browser language preference
5. Application default
The exact order is a product decision. For a public localized website, the URL may have higher priority. For a signed-in application, the account preference may be more important.
const supportedLocales = ["en", "fr", "hi"];
function getSupportedLocale(candidate) {
const baseLanguage = candidate?.split("-")[0];
return supportedLocales.includes(candidate)
? candidate
: supportedLocales.includes(baseLanguage)
? baseLanguage
: "en";
}
Always validate a detected locale against the languages the application actually supports.
6. Locale-Aware Formatting with Intl
Translation files should contain messages, but dates, numbers, lists, and currencies should be formatted with locale-aware APIs.
const locale = "en-IN";
const price = new Intl.NumberFormat(locale, {
style: "currency",
currency: "INR",
}).format(125000);
const deliveryDate = new Intl.DateTimeFormat(locale, {
dateStyle: "long",
}).format(new Date("2026-09-20"));
Possible output:
₹1,25,000.00
20 September 2026
Useful JavaScript internationalization APIs include:
| API | Purpose |
|---|---|
Intl.NumberFormat | numbers, percentages, units, and currencies |
Intl.DateTimeFormat | dates and times |
Intl.RelativeTimeFormat | messages such as “2 days ago” |
Intl.ListFormat | locale-correct lists |
Intl.Collator | locale-aware sorting and comparison |
Intl.PluralRules | selecting locale-specific plural forms |
Do not manually create formats such as DD/MM/YYYY or place currency symbols with string concatenation. The correct convention depends on the locale.
7. Interpolation and Pluralization
Interpolation
Keep a complete sentence in the translation resource and insert dynamic values through placeholders.
{
"orderReady": "Order {{orderId}} is ready for {{customerName}}."
}
t("orderReady", {
orderId: order.id,
customerName: customer.name,
});
Avoid assembling translated sentences from fragments:
// Avoid: word order may differ between languages.
`${t("hello")} ${name} ${t("welcomeBack")}`
Pluralization
Languages do not all use the same singular and plural rules. Let the i18n library select the correct form from the count.
{
"cartItem_one": "{{count}} item",
"cartItem_other": "{{count}} items"
}
t("cartItem", { count: cartItems.length });
Do not assume that every language has only two forms or that adding s creates a plural.
8. Right-to-Left Languages
Arabic, Hebrew, and some other languages are written primarily from right to left.
Update the document language and direction whenever the locale changes:
const rtlLanguages = new Set(["ar", "he", "fa", "ur"]);
const language = locale.split("-")[0];
document.documentElement.lang = locale;
document.documentElement.dir = rtlLanguages.has(language) ? "rtl" : "ltr";
Prefer CSS logical properties so the same styles adapt automatically:
.card {
margin-inline-start: 1rem;
padding-inline: 1.25rem;
border-inline-start: 4px solid rebeccapurple;
}
Use inline-start and inline-end rather than assuming left and right. Also verify icons, navigation order, mixed-direction text, tables, and animations in an actual RTL locale.
9. Loading and Scaling Translation Resources
Bundling every language into the initial JavaScript increases download size even though a user usually needs only one locale.
For larger applications:
- split messages into feature-based namespaces;
- load the active language on demand;
- load a namespace when its route is visited;
- cache versioned translation files;
- prefetch a likely next locale only when beneficial.
Open checkout route
↓
Load current locale's checkout namespace
↓
Render localized checkout screen
Provide a loading state or server-render the required messages so untranslated keys do not flash on the screen.
10. Fallback Strategy
A missing translation should degrade predictably.
fr-CA checkout.pay
↓ missing
fr checkout.pay
↓ missing
en checkout.pay
↓ missing
Visible development warning or safe placeholder
A fallback is a safety net, not a replacement for translation quality checks. Track missing keys so incomplete locales are discovered before release.
Avoid silently showing an empty string because users cannot understand what went wrong and automated tests may miss it.
11. Accessibility and SEO
Accessibility
- Set the root
<html lang="...">value correctly. - Add
langto passages written in a different language. - Do not place important translated text only inside images.
- Translate accessible names, form errors, alternative text, and announcements.
- Test keyboard navigation and screen-reader output after layout direction changes.
SEO for Public Pages
Public content often uses locale-specific URLs:
/en/products/42
/fr/products/42
/hi/products/42
Each localized page should have the correct document language, localized metadata, canonical strategy, and hreflang links where appropriate. Server rendering or static generation also prevents crawlers and users from initially receiving the wrong language.
Internal dashboards may not need localized URLs because search indexing and link sharing are different product requirements.
12. Translation Workflow
As the product grows, localization becomes a content workflow as well as a coding concern.
Developer adds a stable key and source message
↓
Translation platform or resource file receives it
↓
Translators add locale-specific messages
↓
Automated checks validate keys and placeholders
↓
Reviewers test the message in the real interface
Stable keys should describe meaning rather than visual position.
Good: checkout.payment.submit
Good: profile.deleteAccount.confirmation
Weak: blueButtonText
Weak: homepageText3
Give translators context for ambiguous messages, variables, character limits, and where the text appears.
13. Testing Multi-Language Support
Test more than whether a key returns a string.
| Test type | What to verify |
|---|---|
| Unit | locale selection, fallback rules, and formatting helpers |
| Integration | switching language re-renders the page and persists preference |
| Visual | long translations do not overflow or hide controls |
| RTL | layout direction, icons, ordering, and mixed-direction text |
| Accessibility | language metadata, translated names, errors, and announcements |
| Resource validation | missing keys, extra keys, and mismatched placeholders |
A pseudo-locale can expand and decorate text to expose hardcoded strings and layout assumptions before real translations arrive.
Save → [Šàṽë-----]
14. Common Mistakes
| Mistake | Better approach |
|---|---|
| Hardcoding user-visible strings in components | Resolve messages through translation keys |
| Treating localization as translation only | Include formats, plural rules, direction, content, and cultural conventions |
| Concatenating translated fragments | Translate complete messages with interpolation |
| Assuming English plural rules | Use locale-aware plural selection |
| Using the browser language without validation | Match it against supported locales and provide a default |
| Loading every language at startup | Lazy-load locale and feature resources when scale requires it |
Forgetting <html lang> and dir | Update both when the active locale changes |
| Using left/right CSS everywhere | Prefer logical layout properties |
| Using text as the translation key everywhere | Prefer stable semantic keys for long-lived applications |
| Letting missing keys fail silently | Monitor them and fail appropriate development or CI checks |
15. System Design Checklist
When designing multi-language support, decide:
- Which locales are supported?
- Where does the user's preference come from?
- Is the locale stored in the account, device, URL, or all three?
- What is the default and fallback chain?
- Are translations bundled or loaded on demand?
- How are messages divided into namespaces?
- How are dates, money, units, and plural forms formatted?
- Does the interface support RTL and text expansion?
- How do translators receive context and deliver updates?
- How are missing keys and incomplete locales detected?
- Do public pages need localized URLs and metadata?
Official References
- W3C: Localization vs. Internationalization
- MDN: JavaScript Internationalization
- react-i18next Documentation
- react-i18next:
useTranslation - i18next Documentation
Summary
Multi-language support separates meaning from presentation so one application can adapt its messages, formats, and layout to each user's locale.
Think of localization as a rendering pipeline:
Meaningful key
+ active locale
+ dynamic values
+ regional formatting rules
+ writing direction
↓
Localized user experience
The goal is not merely to replace English words. It is to make the complete experience feel natural and usable in every supported locale.