DEV Community

Cover image for đ—Ÿđ—Œđ—°đ—źđ—čđ—¶đ˜€đ—źđ˜đ—¶đ—Œđ—» đ—¶đ—» đ—Šđ—Œđ—łđ˜đ˜„đ—źđ—żđ—Č 𝗗đ—Č𝘃đ—Čđ—čđ—Œđ—œđ—șđ—Čđ—»đ˜ (Frontend Developers)
Kiran
Kiran

Posted on

đ—Ÿđ—Œđ—°đ—źđ—čđ—¶đ˜€đ—źđ˜đ—¶đ—Œđ—» đ—¶đ—» đ—Šđ—Œđ—łđ˜đ˜„đ—źđ—żđ—Č 𝗗đ—Č𝘃đ—Čđ—čđ—Œđ—œđ—șđ—Čđ—»đ˜ (Frontend Developers)

A startup built a product for 6 months.
Launched in Japan. Zero users. App was fully in English. đŸ€Š

Here's what Localisation actually means — and how to do it right 👇


🧠 đ—Șđ—”đ—źđ˜ đ—¶đ˜€ đ—Ÿđ—Œđ—°đ—źđ—čđ—¶đ˜€đ—źđ˜đ—¶đ—Œđ—»?

👉 i18n (Internationalisation) — designing app to support multiple languages
👉 L10n (Localisation) — actually adapting it for a specific region
👉 They are NOT the same thing

i18n = building the infrastructure
L10n = filling it with regional content

i18n is done once.
L10n is done for every new market.
Enter fullscreen mode Exit fullscreen mode

✔ Language translation
✔ Date, time & number formats
✔ Currency symbols
✔ Text direction (LTR vs RTL)
✔ Cultural sensitivities


📝 1ïžâƒŁ đ—§đ—żđ—źđ—»đ˜€đ—čđ—źđ˜đ—¶đ—Œđ—» — 𝗡đ—Č𝘃đ—Č𝗿 đ—›đ—źđ—żđ—±đ—°đ—Œđ—±đ—Č đ—Šđ˜đ—żđ—¶đ—»đ—Žđ˜€

👉 Every visible string must live outside your code

// ❌ Hardcoded — impossible to translate
<h1>Welcome back, John!</h1>

// ✅ Using i18n keys
<h1>{t('welcome', { name: 'John' })}</h1>

// en.json
{ "welcome": "Welcome back, {{name}}!" }

// ja.json
{ "welcome": "おかえりăȘさい、{{name}}ă•ă‚“ïŒ" }
Enter fullscreen mode Exit fullscreen mode

✔ Use libraries: react-i18next, i18next, react-intl
✔ Keys should be semantic — not the English text itself
✔ Never use Google Translate for production — hire translators


📅 2ïžâƒŁ 𝗗𝗼𝘁đ—Č & đ—§đ—¶đ—șđ—Č đ—™đ—Œđ—żđ—ș𝗼𝘁𝘀

👉 Date formats are wildly different across regions

// ❌ Hardcoded format — confusing globally
"04/05/2025" // Is this April 5 or May 4?

// ✅ Use Intl.DateTimeFormat
new Intl.DateTimeFormat('en-US').format(date)
// → "4/5/2025"

new Intl.DateTimeFormat('en-GB').format(date)
// → "05/04/2025"

new Intl.DateTimeFormat('ja-JP').format(date)
// → "2025/4/5"
Enter fullscreen mode Exit fullscreen mode

✔ Always store dates in UTC internally
✔ Format only at display time
✔ Never hardcode date separators or order


💰 3ïžâƒŁ 𝗖𝘂𝗿𝗿đ—Čđ—»đ—°đ˜† & 𝗡𝘂đ—ș𝗯đ—Č𝗿𝘀

👉 Numbers & currency look different in every country

// ❌ Hardcoded — wrong for most of the world
"$1,000.50"

// ✅ Use Intl.NumberFormat
new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(1000.50)
// → "$1,000.50"

new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
}).format(1000.50)
// → "1.000,50 €"
Enter fullscreen mode Exit fullscreen mode

✔ Decimal separator: . in US, , in Germany
✔ Currency symbol position varies by locale
✔ Always use Intl.NumberFormat — never manual formatting


↔ 4ïžâƒŁ đ—„đ—§đ—Ÿ đ—Ÿđ—źđ—»đ—Žđ˜‚đ—źđ—Žđ—Č𝘀 (đ—„đ—¶đ—Žđ—”đ˜-đ˜đ—Œ-𝗟đ—Č𝗳𝘁)

👉 Arabic, Hebrew, Urdu — entire layout flips direction

<!-- ❌ Hardcoded direction — breaks RTL languages -->
<div style="text-align: left">Content</div>

<!-- ✅ Dynamic direction -->
<html dir="rtl" lang="ar">

<!-- ✅ Tailwind RTL support -->
<div class="text-left rtl:text-right">Content</div>
<div class="ml-4 rtl:mr-4 rtl:ml-0">Icon</div>
Enter fullscreen mode Exit fullscreen mode

✔ Use dir="rtl" on <html> tag
✔ CSS logical properties: margin-inline-start instead of margin-left
✔ Test your entire layout in RTL — icons, buttons, animations all flip


🕐 5ïžâƒŁ đ—§đ—¶đ—șđ—Čđ˜‡đ—Œđ—»đ—Č𝘀

👉 One of the most common bugs in global apps

// ❌ Using local time — breaks for other timezones
new Date().toString()

// ✅ Always work in UTC, format locally
new Intl.DateTimeFormat('en-US', {
  timeZone: 'Asia/Tokyo',
  hour: '2-digit',
  minute: '2-digit'
}).format(new Date())

// ✅ Use date-fns-tz or Luxon for complex timezone logic
import { zonedTimeToUtc } from 'date-fns-tz';
Enter fullscreen mode Exit fullscreen mode

✔ Store all timestamps in UTC in your database
✔ Convert to user's timezone only at display
✔ Never use new Date() for timezone-sensitive features


đŸ”€ 6ïžâƒŁ 𝗣đ—č𝘂𝗿𝗼đ—čđ—¶đ˜€đ—źđ˜đ—¶đ—Œđ—» & 𝗚đ—Čđ—»đ—±đ—Č𝗿

👉 "1 item" vs "2 items" — every language handles this differently

// ❌ English-only logic — fails in Russian, Arabic
`You have ${count} message${count !== 1 ? 's' : ''}`

// ✅ i18next plural handling
t('messages', { count: 5 })

// en.json
{
  "messages_one": "You have {{count}} message",
  "messages_other": "You have {{count}} messages"
}

// ar.json — Arabic has 6 plural forms!
{
  "messages_zero": "...",
  "messages_one": "...",
  "messages_two": "...",
  "messages_few": "...",
  "messages_many": "...",
  "messages_other": "..."
}
Enter fullscreen mode Exit fullscreen mode

✔ Never build plural logic manually
✔ Use CLDR plural rules via i18next
✔ Arabic has 6 forms. Russian has 3. Plan for this.


đŸ–Œïž 7ïžâƒŁ 𝗜đ—ș𝗼𝗮đ—Č𝘀, đ—œđ—°đ—Œđ—»đ˜€ & đ—–đ—Œđ—čđ—Œđ˜‚đ—żđ˜€

👉 Visuals carry cultural meaning — not just language

✔ 👍 Thumbs up = positive in West, offensive in Middle East
✔ âšȘ White = purity in West, mourning in parts of Asia
✔ Avoid country-specific idioms in illustrations
✔ Flag icons can be politically sensitive

// ❌ Using flags to represent languages
đŸ‡ș🇾 English  đŸ‡«đŸ‡· French

// ✅ Use language names — not flags
EN  FR  DE  JA
Enter fullscreen mode Exit fullscreen mode

⚛ đ—„đ—Č𝗼đ—č 𝗩đ—Čđ˜đ˜‚đ—œ đ—¶đ—» đ—„đ—Č𝗼𝗰𝘁

npm install react-i18next i18next
Enter fullscreen mode Exit fullscreen mode
// i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

i18n.use(initReactI18next).init({
  lng: 'en',
  fallbackLng: 'en',
  resources: {
    en: { translation: require('./locales/en.json') },
    ja: { translation: require('./locales/ja.json') }
  }
});

// Component usage
const { t, i18n } = useTranslation();
<h1>{t('welcome')}</h1>
<button onClick={() => i18n.changeLanguage('ja')}>æ—„æœŹèȘž</button>
Enter fullscreen mode Exit fullscreen mode

✔ Lazy load translations — don't bundle all languages upfront
✔ Detect browser language automatically with i18next-browser-languagedetector
✔ Store user language preference in localStorage


🚹 đ—–đ—Œđ—șđ—șđ—Œđ—» đ— đ—¶đ˜€đ˜đ—źđ—žđ—Č𝘀

❌ Hardcoding strings directly in components
❌ Using new Date() without timezone handling
❌ Building plural logic manually in English
❌ Using flag icons to represent languages
❌ Adding i18n as an afterthought — design for it from day one
❌ Assuming all text expands the same — German text is 30% longer than English


💡 𝗩đ—Čđ—»đ—¶đ—Œđ—ż-𝗟đ—Č𝘃đ—Čđ—č đ—œđ—»đ˜€đ—¶đ—Žđ—”đ˜

i18n is architecture. L10n is content. Confuse the two and you rebuild from scratch.
Design for localisation on day one — retrofitting it costs 10x more.


🎯 đ—œđ—»đ˜đ—Čđ—żđ˜ƒđ—¶đ—Č𝘄 đ—ąđ—»đ—Č-đ—Ÿđ—¶đ—»đ—Č𝗿

Localisation (L10n) is the process of adapting software for a specific region — covering language translation, date/time/currency formatting, RTL support, pluralisation, and cultural nuances — built on top of Internationalisation (i18n), which is the architecture that makes localisation possible without rewriting code.


Is your current app localised? Which language did you add first? 👇

#Localisation #i18n #Frontend #ReactJS #WebDevelopment #JavaScript #InterviewPrep #GlobalProduct #EngineeringMindset

Top comments (0)