How to Test Localization and i18n Flows
Short answer
i18n failures ship as raw translation keys, clipped RTL navigation, wrong currency decimals, and locale-unaware validation messages—not missing language toggles. Seed user locale preferences, assert no i18n key patterns in DOM, run ExploreChimp on RTL and long-string locales, and compare prod locale traffic to test coverage via TrueCoverage.
Part of Testing Guides by UI patterns.
Who this is for
Teams shipping global SaaS, ecommerce, or mobile web with language switchers, RTL layouts (ar, he), currency/locale number formats, and translated error strings. Typical stacks: i18next, react-intl, FormatJS, Crowdin/Lokalise pipelines.
Why testing localization matters
i18n bugs erode trust and revenue:
- Raw keys in prod —
checkout.titlevisible during failed deploy; conversion drops. - RTL layout breaks — fixed sidebars overlap checkout in
ar-SA; unusable without manual QA. - Wrong currency — EUR shown with USD amounts; charge disputes and regulatory scrutiny.
- Validation mismatch — server errors English-only while UI is localized; support cannot help users.
- SEO and legal — missing translated policy pages; GDPR consent text wrong language.
Test locale persistence, format correctness, and layout at longest locale strings—not just that French appears once.
Complexity map
| Scenario | Edge case | Why tests break | Approach |
|---|---|---|---|
| Missing translation key | Fallback broken | Key in DOM | Regex assert no foo.bar.baz |
| Locale switch mid-flow | Cart locale drift | Wrong tax | Probe cart currency after switch |
| RTL mirror | Icons not flipped | Wrong affordance | dir=rtl + ExploreChimp |
| Long German strings | Button overflow | Clipped CTA | Snapshot optional; role visible |
| Currency format | 1.234,56 vs 1,234.56 | Parse errors | Probe numeric value |
| Date format | DD/MM vs MM/DD | Wrong stored date | Pair with datetime guide |
| Plural rules | one/other | Grammar wrong | Fixture counts 0,1,2 |
| Timezone + locale | Independent prefs | Display mismatch | Seed both in Arrange |
| Accept-Language | Server-driven locale | Client switch ignored | Probe HTML lang attribute |
| Lazy-loaded bundles | Flash of default lang | FOUC | waitForResponse i18n chunk |
| Number input | Comma decimal | Invalid submit | keyboard type locale sep |
| hreflang routes | Wrong canonical | SEO | Assert path prefix /de/ |
Locale Arrange patterns
// Seed user preference
await request.post('/api/test/seed-user', {
data: { runId, locale: 'de-DE', currency: 'EUR' },
});
// Or set cookie / localStorage before goto
await context.addCookies([{ name: 'locale', value: 'ar-SA', domain: 'localhost', path: '/' }]);
await page.goto('/dashboard');
await expect(page.locator('html')).toHaveAttribute('dir', 'rtl');
await expect(page.locator('html')).toHaveAttribute('lang', 'ar');
Assert no missing keys
await expect(page.locator('body')).not.toHaveText(/\b[a-z]+\.[a-z]+\.[a-z]+\b/); // tune regex
await expect(page.getByRole('heading', { level: 1 })).not.toContainText('dashboard.title');
Combine with pseudo-locale (en-XA) in staging to visually spot unwrapped strings during ExploreChimp sessions (explorations).
Currency and numbers
await page.goto('/pricing?locale=de-DE');
await expect(page.getByTestId('price')).toContainText('€');
const probe = await request.get(`/api/test/cart?runId=${runId}`);
expect((await probe.json()).currency).toBe('EUR');
expect((await probe.json()).totalCents).toBe(1999); // authoritative
ExploreChimp for UX/i18n
Automated asserts catch keys and currency; ExploreChimp finds navigation overlap, modal cutoff, and icon direction bugs in RTL—especially on checkout and settings wizards. Convert high-value manual paths to SmartTests with locale in test title // @Scenario: checkout ar-SA RTL.
Compare locale × currency in TrueCoverage—when ar-SA is 12% prod traffic but 0% test runs, run /testchimp evolve.
CI checklist
- Matrix project:
en-US, one RTL, one long-word locale (de-DE) - No raw key regex on critical pages post-switch
- Probe currency/locale on transactional endpoints
- Lazy bundle: waitForResponse before string asserts
- ExploreChimp scheduled on RTL after major UI releases
- Link scenarios to requirement traceability for market launch gates
Anti-patterns
| Anti-pattern | Why it fails | Better approach |
|---|---|---|
| Test English only | RTL/clipping untested | Locale project matrix |
| Assert full translated sentence | Copy changes flake | Key absence + probe |
| Manual screenshot every locale | Unmaintainable | ExploreChimp sampling |
| Ignore server locale | Switcher cosmetic | Probe API Accept-Language |
| Skip currency probe | Wrong charge | totalCents assert |
| One short string locale | Overflow hidden | de-DE or pseudo-locale |
Example scenario
Situation: Arabic-speaking user completes checkout with RTL layout and SAR pricing.
Expected outcome: RTL dir correct; prices in SAR; order probe currency=SAR; no English keys.
Why UI-only automation breaks: UI shows SAR symbol but probe charges USD—FX compliance failure.
- Arrange: Seed user locale ar-SA, currency SAR, runId cart.
- Act: Walk checkout with RTL navigation.
- Assert: html dir=rtl; no i18n keys; probe order currency SAR and localized receipt PDF lang.
TestChimp workflow: Compare locale × currency prod vs test; evolve ar-SA checkout if gap >5% traffic.
Same Arrange/Act/Assert pattern as expired-coupon checkout.
Connect scenarios to your QA workflow
Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).
Related scenarios
- Date/time/timezones — locale date formats
- Form validation — translated errors
- Regional pricing flows — tax + currency
- SaaS onboarding — localized signup
External references
Frequently asked questions
Which locales drive prod traffic but lack tests?
Compare locale × currency in TrueCoverage prod vs test-run. When gaps appear, run /testchimp evolve and ExploreChimp on RTL/long-string paths—link // @Scenario: in markdown plans.
How do I detect missing translation keys in E2E?
Assert DOM does not match key patterns like segment.segment.name and does not contain known default keys. Pseudo-locale in staging helps manual ExploreChimp passes.
How do I test RTL layouts in Playwright?
Set locale ar-SA, assert html dir=rtl, run critical flows. Use ExploreChimp to find clipped nav and misaligned icons automation misses.
Should I assert translated button labels?
Prefer stable roles and probe transactional data. Exact copy asserts break when translators update strings—use regex only for legally mandated phrases.
How do currency display and charge differ in tests?
Display may show € symbol while probe must assert ISO currency and integer cents on order API—never UI symbol alone.
How does locale switching mid-session work?
Switch language, assert URL or cookie persistence, complete transaction, probe server records locale/currency—not just document.lang snapshot.
Can manual i18n QA become SmartTests?
Yes—record ExploreChimp session on problematic locale, convert steps to SmartTests with locale in Arrange seed; avoids re-recording entire suite per language.
Apply these patterns in your repo
Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.