i18n & RTL Markup
i18n & rtl markup i18n and rtl markup requires correct lang/dir on the document root, hreflang alt i18n and rtl
Introduction
i18n and RTL markup goes beyond string translation: lang and dir attributes, logical CSS properties, bidirectional text isolation, and locale-aware formats must be correct in HTML before CSS and JS run. Wrong direction or lang breaks screen readers, SEO hreflang, and layout for Arabic, Hebrew, and mixed-language content.
Business problem
Business pressure: Global launch in MENA used LTR shell with translated strings only — icons mirrored wrong, prices misread by VoiceOver, Google hreflang pointed to English URLs. Revenue target missed; fix required HTML template overhaul, not copy updates.
- Market access: RTL markets expect native direction in first paint — not JS flip after load.
- Legal: Some locales require language declaration for consumer protection.
- Support cost: Mixed LTR brand names in RTL paragraphs need bidi isolation — otherwise garbled UI.
Why this feature exists
Platform motivation: Unicode and HTML provide dir, lang, and <bdi> for international text. CSS logical properties (margin-inline-start) align layout with direction without duplicate stylesheets.
- History: Physical CSS (margin-left) duplicated for RTL → logical properties and dir on html.
- Alternative rejected: Separate RTL subdomain with forked templates — doubles maintenance.
- Modern role: Single template; locale sets lang+dir on root; logical CSS in design system.
Browser internals
Inside the engine: Browser bidi algorithm uses dir and Unicode marks. Screen readers pick voice from lang attribute — wrong lang = wrong pronunciation. Parser applies dir to whole subtree unless overridden.
- html[dir=rtl]: Default text flow and scrollbar behavior change.
- bdi/isolate: User-generated LTR inside RTL needs isolation — e.g. usernames, codes.
Rendering workflow
Rendering path: Server selects locale from URL prefix or Accept-Language → renders <html lang="ar" dir="rtl"> → CSS logical properties mirror layout → JS reads document.documentElement.dir for component behavior.
- SSR: First byte must have correct dir — flipping in JS causes CLS and a11y confusion.
- Fonts: Subset Arabic glyphs in linked font — preload Noto Naskh in head for LCP.
Feature deep dive
Markup checklist: lang on html, dir on html (or body if exceptional), hreflang link alternates, translated title/meta, bdi around embedded LTR tokens, datetime formatted per locale in visible text and datetime attribute.
- hreflang:
<link rel="alternate" hreflang="ar-SA" href="...">per locale URL. - Numbers: Display locale formatting; store ISO in data attributes.
- Icons: Mirror directional icons (arrows) with CSS — not flip entire layout incorrectly.
<!DOCTYPE html><html lang="ar" dir="rtl"><head><meta charset="UTF-8"><title>المنتج — Acme</title><link rel="alternate" hreflang="en" href="https://acme.com/en/product"><link rel="alternate" hreflang="ar" href="https://acme.com/ar/product"></head><body><p>مرحباً <bdi dir="ltr">user@email.com</bdi></p></body></html>
Accessibility analysis
A11y architecture: lang switches on inline quotes with lang="en" on span. Screen reader language table documented in VPAT. RTL focus order follows visual reading order — tab order must not follow DOM source if CSS reorder breaks it.
- Forms: Labels translated; error messages in same lang as page.
- Icons: Decorative vs meaningful — chevron direction announced correctly in RTL.
SEO impact
SEO architecture: hreflang cluster must be reciprocal and self-referencing. Separate URLs per locale — not cookie-only lang switch (Googlebot sees default). Title and description translated in HTML — not client-only.
- x-default: For global landing when geo unknown.
- Canonical: Per-locale self canonical — not all pointing to English.
Security considerations
Security boundary: Locale parameters in URLs are injection points — validate against allowlist. RTL override query params must not enable open redirect in hreflang links.
- Unicode: Homograph attacks in RTL domains — punycode display in UI.
Performance impact
Performance: Additional webfont weights for CJK/Arabic affect LCP — subset and preload per locale template. Avoid shipping all locales' JSON in one HTML page.
- Split: Per-locale static HTML at CDN edge — not one bundle with 40 lang files inline.
Real production example
Production pattern: Shopify-style /en /fr /ar paths; Next.js i18n routing sets lang/dir on html in layout.tsx server component; design system uses logical properties exclusively; CI visual regression on ar snapshot.
- Testing: Manual SR pass in Arabic; automated check html[lang] matches URL locale.
Enterprise usage
Enterprise: TMS (Phrase, Lokalise) integrates with CMS; translators never edit HTML tags — only string fields. Template locks dir; translators cannot break markup.
- Legal review: Locale-specific disclaimer blocks in template slots.
Common production failures
What breaks in prod: Forgot dir=rtl on html — layout CSS wrong until client patch. hreflang missing return links — Google ignored alternates. Phone numbers reversed visually in RTL table.
- Incident: Mixed bidi in chat widget — usernames unreadable; fixed with bdi wrapper in template.
Architecture review questions
- Does view-source show correct lang and dir before JS?
- Is hreflang reciprocal for all locale pairs?
- Are LTR tokens isolated with bdi in RTL pages?
- Does design system use logical CSS properties only?
Hands-on project
Project: Build EN + AR pair of same page with hreflang, dir, bdi email example, and logical CSS layout. Validate with WAVE and hreflang checker.
- Deliverable: two HTML files + hreflang link set documentation.
Interview questions
What HTML attributes are required for proper RTL support?(Advanced)
lang and dir on html (or consistent root), hreflang alternates for SEO, bdi/isolation for embedded LTR in RTL text, lang on inline foreign phrases, and datetime/number formatting in visible content — with logical CSS not physical left/right.
Follow-up: Why is client-side dir flip problematic?
Try it yourself
Edit the HTML, CSS, or JS panels — the preview updates as you type.
Try it yourself
Summary
i18n and RTL markup requires correct lang/dir on the document root, hreflang alternates, bidi isolation for mixed text, and logical CSS — validated in SSR HTML before paint for global SEO and accessibility.