Website Internationalization (i18n) Guide: Building Multilingual Sites

Internationalization (i18n) is the technical approach for adapting websites to different languages and regions. 75% of consumers prefer buying in their native language.

Content Types to Localize

Type Example Notes
UI Text Buttons, menus Basic translation
Dates 2026/07/18 vs July 18, 2026 Format differences
Currency $1,000.50 vs 1.000,50€ Symbol and format
Numbers 1,234.56 vs 1.234,56 Decimal/thousand separators
Images Text in images Prepare per language

Implementation Options

Solution Framework Features
react-i18next React Most features, SSR support
Vue I18n Vue Official recommendation
nuxt/i18n Nuxt Auto routing and SEO
next-i18next Next.js SSR and SSG integration

react-i18next Example

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

const resources = {
  'zh-CN': { translation: { welcome: 'Welcome to our site' } },
  'en-US': { translation: { welcome: 'Welcome to our website' } }
};

i18n.use(initReactI18next).init({ resources, lng: 'zh-CN', fallbackLng: 'en-US' });

Organizing translation files

A common mistake is dumping every string into one giant JSON file. A more maintainable approach is splitting by page or module:

// locales/en/checkout.json
{
  "title": "Checkout",
  "subtotal": "Subtotal: {{amount}}",
  "freeShipping": "Free shipping over {{threshold}}",
  "items": {
    "one": "{{count}} item",
    "other": "{{count}} items"
  }
}

A naming convention such as checkout.subtotal keeps the context obvious, and paired with a platform like Crowdin or Lokalise, non-engineers can review translations too. Prefer a "module + semantic name" two-level scheme over numbered keys like key1 or key2 — the latter are nearly impossible to maintain during refactors and give translators no context at all.

URL Strategies

  • Sub-path: example.com/zh-cn/page (best SEO, easy)
  • Subdomain: zh.example.com/page (independent, complex TLS)
  • Domain: example.cn/page (strongest localization, costly)

SEO

<link rel="alternate" hreflang="zh-CN" href="https://example.com/zh-cn/page" />
<link rel="alternate" hreflang="en-US" href="https://example.com/en-us/page" />

Language Switching and UX

Switching languages is not just swapping strings; it involves routing, state, and overall experience:

  • After a user switches language, remember the choice in localStorage or a cookie so the next visit opens in that language;
  • Default to the browser's Accept-Language header, but always keep a manual switch available;
  • Store language preference on the server for signed-in users so it syncs across devices;
  • Avoid a full-page flash on switch — render immediately with already-loaded translations, then fetch new language resources asynchronously.

Common Pitfalls

  • Will German or French break the layout? German typically runs about 30% longer than English, while Chinese is shorter but denser. Leave slack in buttons and cards, and back up long strings with max-width and wrapping.
  • Whose date format do you use? Follow the target region, not the translation language: within the same English interface, US users see 07/18/2026 while European users see 18/07/2026. That is a locale difference, not a language one.
  • How do I fill in hreflang? Write language codes per BCP 47 (zh-CN, en-US); if you localize by language only, use x-default as the fallback.
  • What about text inside images? Prefer CSS or SVG so you do not re-render an image per language; when a bitmap is unavoidable, serve different assets per language through srcset.

Reference: MDN on internationalization and localization https://developer.mozilla.org/en-US/docs/Glossary/Internationalization_and_localization
Reference: react-i18next official docs https://react.i18next.com/

Summary

Plan i18n from project start. Choose mature libraries like react-i18next or Vue I18n. Consider translation management platforms (Lokalise, Crowdin) rather than hardcoding.