Language Switching Testing Checklist (2026)
Language Switching Testing Checklist (2026)
Language Switching Testing Checklist (2026)
The Language Switching Testing Checklist (2026) gives you a concrete, step‑by‑step matrix you can run manually or automate to verify that an application behaves correctly when users change its UI language. It covers the happy path, error conditions, edge cases, accessibility, security, performance, and release‑readiness items that teams repeatedly miss in production. By following the checklist you can catch missing translations, layout breaks, ANRs, WCAG failures, and even privacy leaks before they reach users.
---
Happy Path Testing
UI Language Change
When a user selects a new language from the settings menu, the UI must instantly reflect the chosen locale without requiring a full app restart. Verify that every visible string—labels, placeholders, toast messages, and dialog titles—appears in the target language.
Pass criteria
- All static text updates within 500 ms of the selection event.
- No hard‑coded English strings remain visible.
- The language indicator (e.g., a globe icon or text) shows the newly selected locale.
Manual test
- Open Settings → Language.
- Choose “Español”.
- Verify navigation drawer, home screen tabs, and any on‑boarding screens show Spanish text.
Automation snippet (Playwright)
test('switches to French instantly', async ({ page }) => {
await page.goto('/settings');
await page.selectOption('#language-select', 'fr');
await expect(page.locator('button:has-text("Commencer")')).toBeVisible({ timeout: 800 });
});
Persistent Preference
The chosen language must survive process kills, device reboots, and app updates. Store the preference in a secure, backed‑up location (e.g., SharedPreferences on Android, UserDefaults on iOS, or async storage on web).
Pass criteria
- After a force‑stop and relaunch, the UI language remains the last selected one.
- After a device reboot, the language persists without user interaction.
- Clearing app data resets the language to the device default or a defined fallback.
Automation snippet (Android ADB)
# Set language to Japanese via adb
adb shell settings put system locale ja_JP
# Launch app and verify
adb shell monkey -p com.example.app -c android.intent.category.LAUNCHER 1
adb shell uiautomator dump /tmp/ui.xml
grep -i "日本語" /tmp/ui.xml && echo "PASS" || echo "FAIL"
Dynamic Content Update
Content fetched from remote APIs (e.g., news feeds, product catalogs) must be re‑requested or filtered according to the new locale.
Pass criteria
- When language changes, any visible list or grid refreshes to show items with matching language metadata.
- No stale items in the previous language remain on screen after the switch.
Manual test
- Load a feed in English.
- Switch to German.
- Pull‑to‑refresh; confirm all card titles and descriptions are now German.
Automation snippet (Cypress)
it('refreshes feed after language change', () => {
cy.visit('/news');
cy.contains('English Headline').should('be.visible');
cy.get('#lang-select').select('de');
cy.contains('Deutsche Überschrift').should('be.visible');
cy.contains('English Headline').should('not.exist');
});
Fallback Mechanisms
If a translation for a specific key is missing, the app should fall back to a defined base language (often English) without crashing or showing raw keys.
Pass criteria
- Missing keys render the fallback string, not the key itself (e.g., “welcome_message” → “Welcome”).
- A console warning or log entry is emitted for developers to track gaps.
Manual test
- Temporarily rename
es.jsontoes.json.bakto simulate missing Spanish file. - Switch to Spanish; verify UI shows English strings and no exception dialogs appear.
---
Error Handling
Missing Translation Files
When a locale’s resource bundle is absent, the app must degrade gracefully.
Pass criteria
- App launches successfully; UI shows base language strings.
- An error is logged to the crash‑reporting service with locale and missing file details.
Automation (iOS XCTest)
func testLaunchWithMissingSpanishBundle() {
// Remove es.lproj from bundle resources before launch
let app = XCUIApplication()
app.launchEnvironment["SIMULATE_MISSING_ES"] = "true"
app.launch()
XCTAssertTrue(app.staticTexts["Welcome"].exists)
}
Invalid Locale Codes
Users or integrations may pass malformed locale identifiers (e.g., “en-USX”, “fr_FR@Euro”).
Pass criteria
- The app normalizes or rejects the input, defaulting to the base language.
- No crash or infinite loop occurs.
- A user‑visible toast informs the user of an unsupported language (if appropriate).
Manual test
- Via ADB shell, set
persist.sys.languagetozz. - Open the app; confirm it falls back to English and shows a toast “Language not supported”.
Partial Load Failures
Network requests for language packs may time‑out or return corrupted JSON.
Pass criteria
- Retry mechanism attempts up to three times with exponential back‑off.
- After retries fail, the app displays the base language and a non‑intrusive banner indicating translation load failure.
Automation snippet (Mock Service Worker)
// Simulate 500 error on /assets/locales/de.json
rest.get('*/locales/de.json', () => {
return HttpResponse.error({ status: 500 });
});
Network Errors During Language Fetch
If the device loses connectivity while fetching a remote translation bundle, the UI must not hang.
Pass criteria
- The language selector remains usable; selecting another locale works offline if the bundle is cached.
- A clear offline indicator appears, and the app logs the failure for later retry.
---
Edge and Boundary Cases
Right‑to‑Left (RTL) Languages
Switching to Arabic, Hebrew, or Urdu must mirror layout, adjust alignment, and reverse scroll direction where appropriate.
Pass criteria
- All horizontal containers (toolbars, tabs, lists) flip direction.
- Text alignment changes from left‑justified to right‑justified.
- Icons that denote direction (e.g., arrow‑right) swap to arrow‑left.
Manual test
- Switch to Arabic.
- Verify the navigation drawer opens from the right side.
- Confirm that a horizontally scrollable carousel now scrolls left‑to‑right.
Complex Scripts (Indic, Thai, Complex East Asian)
Languages such as Hindi, Thai, or Japanese may require glyph substitution, ligature shaping, or line‑break rules that differ from Latin scripts.
Pass criteria
- Text renders without missing glyphs or overlapping characters.
- Line breaks follow Unicode grapheme cluster boundaries.
- Input fields allow proper composition (e.g., Thai vowel placement).
Automation (Appium + uiautomator2)
def test_hindi_rendering():
driver.find_element_by_id('language_switch').click()
driver.find_element_by_xpath("//android.widget.TextView[@text='हिंदी']").click()
hindi_text = driver.find_element_by_id('welcome_msg').text
assert "स्वागत" in hindi_text # Devanagari rendering check
Pluralization Rules
Different languages have varying plural forms (e.g., Russian has three, Czech has four).
Pass criteria
- The app selects the correct plural form based on the numeric value and locale rules.
- No hard‑coded “1 item”, “2 items” strings appear.
Manual test
- Set language to Russian.
- Navigate to a screen showing a count of messages (e.g., “3 сообщения”).
- Change the count to 1, 2, 5 and verify the correct suffix appears.
Date/Time/Formatting
Locale‑specific date, time, number, and currency formats must adjust.
Pass criteria
- Dates appear as
dd/MM/yyyyforfr_FR,MM/dd/yyyyforen_US, etc. - Times respect 12‑ vs 24‑hour conventions.
- Numbers use the correct decimal separator (
,vs.). - Currency symbols precede or follow the amount as per locale.
Manual test
- Switch to Japanese.
- Verify a timestamp shows
2025/11/02 14:30(year/month/day). - Confirm a price of ¥1,234 displays as
¥1,234(no decimal).
Currency and Monetary Symbols
Some locales use non‑Western symbols (₹, ¥, ₽) or place the symbol after the amount.
Pass criteria
- Currency conversion respects locale formatting rules.
- No hard‑coded
$appears in non‑USD locales.
Emoji and Special Characters
Emoji, zero‑width joiners, and directional isolates must not break layout or be stripped.
Pass criteria
- Emoji render as a single glyph, not as separate code points.
- Zero‑width characters do not affect hit‑testing or accessibility tree.
Language Mixing (Code‑Switching)
Users may intentionally mix languages (e.g., English nouns with Spanish verbs).
Pass criteria
- The app does not auto‑correct or replace user‑generated mixed strings.
- Search and filter functions treat the string as a literal query.
Very Long Strings
Some languages (German, Finnish) produce substantially longer translations.
Pass criteria
- UI containers truncate with ellipsis or expand gracefully without overlapping.
- Buttons retain minimum touch target size (48 dp) after expansion.
---
Accessibility
Screen Reader Announcements
When language changes, screen readers must switch to the appropriate voice and announce the new locale.
Pass criteria
- TalkBack (Android) or VoiceOver (iOS) reads newly visible elements in the selected language.
- The language change itself is announced (“Language changed to Español”).
Manual test
- Enable TalkBack.
- Switch language to French.
- Focus moves to a button; Verify TalkBack says the button label in French.
Contrast after Language Switch
Some languages may use glyphs with different visual weight, affecting contrast ratios.
Pass criteria
- All text meets WCAG AA contrast (≥ 4.5:1) against its background in every supported language.
- Icons retain sufficient contrast after mirroring for RTL.
Automated check (axe‑core)
import { axe } from 'axe-core';
axe.run().then(results => {
expect(results.violations).toHaveLength(0);
});
Focus Management
Changing language should not trap focus or move it to an invisible element.
Pass criteria
- Focus remains on the currently active element or moves to a logical next element (e.g., the language selector).
- No focus loss occurs when opening a dialog after a language change.
ARIA Labels Localization
ARIA attributes (aria-label, aria-describedby) must be translated alongside visible text.
Pass criteria
- Inspecting the accessibility tree shows localized values for all ARIA labels.
- Missing ARIA translations fall back to the base language.
Touch Target Size
Localized strings may cause buttons to shrink or grow; ensure they stay within accessibility guidelines.
Pass criteria
- All interactive elements maintain a minimum height and width of 48 dp (or 44 pt on iOS).
- No overlapping of touch targets after language‑induced layout changes.
Keyboard Navigation
Users navigating via Tab or arrow keys must experience predictable order after a language switch.
Pass criteria
- Tab order follows the visual layout (LTR or RTL).
- Arrow‑key navigation in grids or lists respects the new direction.
Reduced Motion
If the user has reduced motion enabled, language‑change animations (e.g., fade‑in of new strings) should be disabled or shortened.
Pass criteria
- No animation exceeds 200 ms when
prefers-reduced-motionis reduced. - UI updates instantly without causing layout thrash.
---
Security and Privacy
Locale Injection
Malicious inputs may attempt to inject locale strings into HTTP headers, URLs, or deep links to trigger unintended behavior.
Pass criteria
- The app sanitizes locale values, allowing only a whitelist of supported tags (e.g.,
en-US,fr-FR). - Any invalid locale results in a fallback to the base language and a security log entry.
Automated test (OWASP ZAP)
zap-cli open-url https://example.com/setlang?lang=en-US%00script
zap-cli alert -q | grep -i "locale injection"
Data Leakage via Language Headers
Apps that send the Accept-Language header may inadvertently expose user preferences to third‑party endpoints.
Pass criteria
- The header is stripped or replaced with a generic value when communicating with analytics or advertising endpoints that do not require localization.
- A network‑sniffing test confirms no locale data leaves the app for non‑essential calls.
GDPR Consent Localization
Consent dialogs must present the same legal text in every language, and the user’s choice must be stored separately from the locale preference.
Pass criteria
- Changing language does not reset previously given consent.
- The consent modal’s text matches the official translation provided by the legal team.
Secure Storage of Preference
The selected language should be stored in a location inaccessible to other apps or unauthorized processes.
Pass criteria
- On Android, the preference is saved in
MODE_PRIVATESharedPreferences or EncryptedSharedPreferences. - On iOS, it resides in the app’s
UserDefaultssuite, not in the global domain. - On web, it is kept in
sessionStorageor a SameSite‑strict cookie, never inlocalStoragefor cross‑site exposure.
Third‑Party SDK Localization
If you embed analytics, ads, or social SDKs, they must respect the app’s language setting or provide a way to override it.
Pass criteria
- SDK initialization calls receive the locale as a parameter, or the SDK reads from a shared config you control.
- No SDK forces its own language, causing UI mismatch.
---
Performance
Load Time Impact
Loading additional language bundles should not noticeably increase startup time.
Pass criteria
- Bundle load adds ≤ 50 ms to cold start on median‑tier devices.
- Language switch at runtime adds ≤ 150 ms (including JSON parse and UI rebuild).
Measurement script (Android Studio Profiler)
adb shell am start -W -S com.example.app/.MainActivity
# Capture "TotalTime" from output; repeat with different locale pre‑loaded
Memory Usage
Each locale’s resource bundle resides in memory until explicitly cleared.
Pass criteria
- Peak RAM increase per additional locale ≤ 2 MB.
- Unused locales are purged from cache after a configurable timeout (e.g., 30 min).
Automated check (LeakCanary)
// After switching language five times, assert no memory leak
assertFalse(LeakCanary.isInAnalyzerProcess)
Bundle Size
Including all translations can inflate the APK/IPA size.
Pass criteria
- Total size of all locale files ≤ 8 % of the base APK.
- Use resource splitting or dynamic feature modules to load locales on demand.
Manual test
- Generate APK with all locales.
- Run
apkanalyzerto inspectassets/locales/size. - Confirm it stays under the budget.
Lazy Loading of Translations
Load language packs only when the user first selects that language.
Pass criteria
- Initial launch does not fetch any locale assets.
- First switch triggers a network request (if remote) and caches the result.
- Subsequent switches are instantaneous (cache hit).
Code snippet (i18next + react-i18next)
i18next
.use(LazyLoadBackend)
.init({
lng: 'en',
fallbackLng: 'en',
backend: {
loadPath: '/locales/{{lng}}.json',
},
});
Network Caching
If translations are fetched from a CDN, proper caching headers prevent redundant downloads.
Pass criteria
Cache-Control: max-age=86400(or similar) is present on locale assets.- DevTools Network tab shows
(from cache)after the first request.
FPS Impact
Frequent language switches during a session should not cause frame‑rate drops that impair interaction.
Pass criteria
- Maintain ≥ 55 fps on a mid‑range device during rapid language toggling (e.g., five switches in two seconds).
- No jank spikes > 16 ms in the UI thread.
---
Release Readiness
CI Pipeline Integration
Automate the checklist as part of pull‑request validation.
Pass criteria
- A dedicated job runs the language‑switch matrix on emulator/farm devices.
- Job fails if any mandatory item (happy path, error handling, accessibility) is unsatisfied.
GitHub Actions example
name: Language Switch Tests
on: [pull_request]
jobs:
lang-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run language matrix
run: |
npm run test:lang-switch # custom script that drives Playwright + axe
Automated Regression Script Generation
After exploratory testing, generate reusable scripts that lock in language behavior.
Pass criteria
- The generated script covers at least 90 % of the manual checklist items.
- Scripts are stored in version control and run on every nightly build.
SUSA‑generated script (Appium)
@Test
public void testLanguageSwitchSpanish() {
driver.findElement(By.id("settings_lang")).click();
driver.findElement(By.androidUIAutomator("new UiSelector().text(\"Español\")")).click();
Assert.assertEquals(driver.findElement(By.id("welcome_msg")).getText(), "Bienvenido");
}
Rollback Plan
If a language‑related defect escapes to production, you must be able to revert quickly.
Pass criteria
- Feature flag or remote config can disable dynamic language loading, forcing the base language.
- Rollback procedure documented and tested in staging.
Monitoring and Alerts
Instrument the app to emit metrics on language‑switch latency, fallback events, and error logs.
Pass criteria
- Dashboard shows alert when > 2 % of switches result in fallback to base language.
- Log‑based alert triggers on any locale‑injection security event.
Documentation
Maintain a living language‑support guide that lists supported locales, known gaps, and owner contacts.
Pass criteria
- Document updated within 24 h of adding or removing a locale.
- Accessible to developers, QA, and localization vendors via internal wiki.
Sign‑off Checklist
Before a release, the release manager must verify a signed‑off checklist.
| Area | Item | Verified? (✓/✗) |
|---|---|---|
| Happy Path | UI updates instantly, persists after reboot | |
| Error Handling | Missing file shows fallback, logs error | |
| Edge Cases | RTL layout mirrors, complex script renders | |
| Accessibility | Screen reader speaks new language, contrast OK | |
| Security | Locale injection blocked, header sanitized | |
| Performance | Switch latency <150 ms, bundle size <8 % | |
| Release | CI job passes, regression scripts generated |
---
Autonomous Exploration with SUSA
SUSA (SUSATest) can exercise most of the language‑switching checklist without writing a single test case. By pointing the agent at an APK or a web URL and enabling the “language‑switcher” persona, the platform autonomously:
- Discovers every language selector in the UI (settings, deep‑link query params, HTTP headers).
- Triggers a switch for each supported locale, measures the time to first painted string, and captures screenshots.
- Validates persistence by backgrounding the app, killing the process, and relaunching to confirm the locale survives.
- Detects missing translations by scanning the accessibility tree for raw resource keys or untranslated placeholders.
- Checks RTL mirroring by comparing layout bounds before and after a switch to a right‑to‑left language.
- Flags accessibility regressions using its built‑in axe engine, reporting contrast failures and missing ARIA labels.
- Logs performance metrics (CPU, memory, frame time) for each switch and surfaces outliers.
Configuring Personas for Language Testing
In the SUSA dashboard, create a custom persona that:
- Sets the device or browser locale to a target language before each action.
- Enables the “impatient” temperament to rapid‑fire switches (simulating a power user toggling settings).
- Turns on the “accessibility” mode to ensure TalkBack/VoiceOver is active.
Run the persona against a build; SUSA will output a JSON report that maps each discovered selector to a PASS/FAIL verdict for the categories above.
Interpreting SUSA Reports
The report includes sections like:
{
"language_switch": {
"total_attempts": 27,
"successful_switches": 24,
"failures": {
"missing_translation": 3,
"rtl_layout_break": 0,
"accessibility_contrast": 1
},
"metrics": {
"avg_switch_latency_ms": 112,
"p95_switch_latency_ms": 180,
"memory_increase_mb": 1.4
}
}
}
Use the failures array to prioritize tickets: missing translations go to the localization team, contrast failures to the design squad, and latency outliers to performance engineers.
Integrating SUSA Findings into the Manual Checklist
After each SUSA run, export the failures into a spreadsheet and map them to the corresponding checklist items:
| SUSA Failure Type | Checklist Item | Action |
|---|---|---|
| missing_translation | Error Handling → Missing Translation Files | Add translation or mark as fallback. |
| rtl_layout_break | Edge/Boundary Cases → RTL Languages | Fix layout direction flags. |
| accessibility_contrast | Accessibility → Contrast after Language Switch | Adjust colors or increase font weight. |
| high_latency | Performance → Load Time Impact | Enable lazy loading or bundle split. |
By coupling SUSA’s autonomous exploration with the manual checklist, you achieve near‑complete coverage in a fraction of the time a script‑only approach would demand.
---
Checklist Summary (One‑Page View)
| # | Category | Test Description | Pass Criteria | Automation Hint |
|---|---|---|---|---|
| 1 | Happy Path | Switch language via settings UI | All visible text updates ≤ 500 ms, persists after reboot | Playwright selectOption |
| 2 | Happy Path | Persist preference after force‑stop & reboot | Language unchanged after kill/reboot | ADB settings put system locale |
| 3 | Error Handling | Launch app with missing locale JSON | Falls back to base language, logs warning | XCTest bundle removal |
| 4 | Error Handling | Set invalid locale code (e.g., zz) | Defaults to base, shows unsupported toast | ADB property |
| 5 | Edge Cases | Switch to Arabic (RTL) | Layout mirrors, navigation opens from right | UIAutomator dump |
| 6 | Edge Cases | Render Hindi string with complex shaping | No missing glyphs, correct line breaks | Appium find element |
| 7 | Accessibility | Verify TalkBack reads new language after switch | All labels spoken in selected language, change announced | Manual + uiautomator |
| 8 | Accessibility | Contrast check for each locale (axe) | WCAG AA ≥ 4.5:1 for all text | axe.run() |
| 9 | Security | Send malformed Accept-Language header | Header sanitized, request uses base locale | OWASP ZAP |
| 10 | Performance | Measure cold‑start latency with 10 locales | ≤ 50 ms added per locale | Android Profiler |
| 11 | Release | CI job runs language‑switch matrix | Job fails on any mandatory item | Github Actions |
| 12 | SUSA | Run autonomous explorer with language persona | Report shows ≤ 5 % failures, latency <150 ms | susatest-agent run |
---
Takeaways
*Treat language switching as a first‑class feature, not an after‑thought.*
- Start with the happy path and make it bullet‑proof: instant UI update, persistent storage, and graceful fallback.
- Error handling must be exhaustive—missing files, bad locale tags, and partial loads should never crash the app.
- Edge cases expose the most embarrassing bugs: RTL layout breaks, complex script rendering, and overly long strings can slip through only when a real user hits them.
- Accessibility is not a checklist add‑on; language changes affect screen‑reader voice, contrast, and focus, so validate them for every locale.
- Security and privacy concerns appear subtle—locale injection or header leakage can give attackers a foothold or leak user preferences.
- Performance matters: users notice lag when switching settings, and bloated bundles affect download size. Measure, cache, and lazy‑load.
- Release readiness hinges on automation: CI pipelines, regression scripts generated from exploratory runs, and clear sign‑off gates keep regressions out of production.
- Autonomous tools like SUSA can cover the majority of these items in a single pass, freeing you to focus on the tricky, human‑judgment areas (UX flow, visual design, legal copy).
Apply this checklist on every release candidate, track the metrics in your dashboard, and you’ll ship an app that respects the world’s linguistic diversity without sacrificing stability, quality, or speed. Happy testing.
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free