Making a React App Multilingual
If you think making an app multilingual is just about swapping “Hello” for “Hola”, you are in for a rough sprint. True localization is an architectural challenge. It affects your database schema, your CSS logic, your CI/CD pipeline, and your layout integrity.
In this React app localization guide, we will talk about how to build an app that doesn’t just “support” other languages but is internally structured for them.
1. Stop Hardcoding Everything
Before you install a single library, you need to change your mindset from text to tokens.
i18n vs. l10n: What’s the difference
- Internationalization (i18n): You are replacing hardcoded strings with keys (e.g., user_welcome_message). You are also ensuring your app can handle 24-hour clocks, metric units, and different currency symbols.
- Localization (l10n): This is when a translator (or AI) takes your keys and provides the French, Japanese, or Arabic equivalent.
Use UTF-8
If your database or your API headers are not explicitly set to UTF-8, stop now. You will eventually hit a character (like a German umlaut or a Chinese kanji) that turns into a “” or a bunch of empty boxes.
Ensure your entire stack (from the DB collation to the HTML meta tags) is unified.
Logical Properties in CSS
Stop using margin-left. If a user switches to Arabic (a Right-to-Left or RTL language), your “left” margin is now on the wrong side. Use CSS Logical Properties:
- margin-inline-start instead of margin-left.
- padding-inline-end instead of padding-right. These automatically flip based on the dir=”rtl” or dir=”ltr” attribute on your <html> tag.
2. Technical Implementation
React is the best sandbox for i18n because of the context. You wrap your app in a provider, and every component suddenly knows exactly which language to speak.
When we talk about react i18n, we are referring to the ecosystem of hooks and components that allow your UI to react instantly when a user changes their language setting.
Choose Your Library: i18next vs. Lingui
i18next is the industry standard. It has been around for over a decade and has a plugin for almost everything (browser language detection, backend loading, etc.).
- Workflow: You maintain JSON files (en.json, de.json).
- Hook: const { t } = useTranslation();
Note: Your bundle size grows as your app grows, and you have to manually manage keys, which leads to “key rot” (keys that exist in code but not in the translation file).
I18next Pros and Cons
- Pros: Massive community, extremely flexible, supports namespaces (loading only the translations needed for a specific page).
- Cons: The configuration can be verbose; it uses a key-based approach, which can lead to key sprawl.
Lingui is a modern and lightweight alternative. It uses a compiler to optimize your translations, resulting in a smaller final bundle size.
- Workflow: You write actual English in your code: <Trans>Hello {name}</Trans>.
- Hook: A CLI tool “extracts” that text into a catalog for translators.
Note: It requires a slightly more complex build setup (Babel/Vite plugins).
Lingui Pros and Cons
- Pros: Excellent Developer Experience (DX). You can write actual text in your code, and the tool extracts it into translation files automatically.
- Cons: Smaller ecosystem than i18next.
Implementation Tutorial (using i18next)
Step 1: Install the core packages
npm install i18next react-i18next i18next-browser-languagedetector
Step 2: Config (src/i18n.js)
Don’t just load all translations at once. Use a detector so the app remembers the user’s preference.
import i18n from ‘i18next’;
import { initReactI18next } from ‘react-i18next’;
import LanguageDetector from ‘i18next-browser-languagedetector’;
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: ‘en’,
debug: process.env.NODE_ENV === ‘development’,
interpolation: {
escapeValue: false, // React already escapes by default
},
resources: {
en: {
translation: {
“welcome_back”: “Welcome back, {{name}}!”,
“items_count”: “You have {{count}} item”,
“items_count_plural”: “You have {{count}} items”
}
}
}
});
export default i18n;
Step 3: Usage in Components
import { useTranslation } from ‘react-i18next’;
function Header({ user }) {
const { t } = useTranslation();
return <h1>{t(‘welcome’, { name: user.name })}</h1>;
}
Step 4: Context
Before moving to automation, you must handle contextual metadata. A translator looking at a JSON file sees “cancel”: “Cancel” and has no idea if it refers to a button closing a window or one stopping a $10,000 bank transfer. In many languages, the verb differs based on the action’s weight.
Modern react i18n practices suggest naming keys by location and intent (e.g., btn_cancel_settings vs btn_cancel_payment) to prevent “translation collisions” where one change accidentally breaks multiple UIs.
3. Modern Localization Workflow
If you are manually copying and pasting strings into a Google Sheet for a translator, you are failing. That process is slow, error-prone, and impossible to scale.
Continuous Localization
This aligns localization with your development sprints. Integrate your repo (GitHub/GitLab) with a Translation Management System (TMS) like Crowdin or Phrase.
When you push code, new strings are auto-extracted. Translators are notified, and once they finish, the TMS opens a Pull Request back to your repo with the updated files.
AI Localization
Use LLMs for instant pre-translation. This allows developers to check for UI breaks (e.g., German words being 30% longer than English) before the code even leaves the staging environment.
Tip: Feed the AI your brand voice and glossaries to ensure it maintains your product’s specific tone.
Give enough context. Tell the AI (and humans) exactly what the string is. Instead of just Book, give it a context: button_action_book_flight. This prevents the AI from thinking you are talking about a novel.
4. Advanced Challenges: OTA and QA
Over-the-Air (OTA) Delivery: Bypassing the App Store Reviews
One of the biggest headaches in mobile development is finding a typo in a translation. For a web app, it is a quick deploy. For a React Native app, it means waiting a week for App Store reviews.
OTA Localization allows you to host your translation JSON files on a server (like an S3 bucket or a specialized CDN). When your app starts, it checks for a newer version of the strings. If it finds one, it downloads it and updates the UI instantly – no app update required.
Do QA Checks Before Deploying
German words are, on average, 30% longer than English words. If your “Save” button is exactly 60px wide, “Speichern” is going to break your layout.
- Pseudolocalization is a developer’s best friend. It’s a tool that transforms your English strings into a weird, elongated version (e.g., [!!! Šáâvé !!!]). If your UI looks okay in Pseudolanguage, it will likely survive German or Finnish.
- Linguistic QA (LQA) is checking for functional context. Does the “Submit” button make sense in the middle of a signup flow in Japanese culture?
Summary and Best Practices
To successfully make your React app multilingual, follow this checklist:
- Choose your i18n library early: i18next for stability, Lingui for DX and performance.
- Use unique, descriptive keys: Avoid generic keys like title1 or text_blue. Use home_hero_title.
- Never concatenate strings: Don’t do {t(‘hello’)} + ” ” + user.name. Use interpolation: {t(‘hello_user’, { name: user.name })}. Different languages have different word orders.
- Automate the sync: Connect your repo to a TMS.
- Test for expansion: Use pseudolocalization early to ensure your CSS is flexible (flexbox and grid are your friends).
If you hardcode strings today, you will pay for it much more when you decide to launch in a second market next year. Build for localization now so you can focus on the features later.
