🌐 Language
Common mistake
A common mistake is not setting a locale on the application or its content. When no language is declared, a screen reader falls back to its own default language and may apply incorrect pronunciation rules, making the content difficult or impossible to understand for users who rely on speech output.
🎯 Relevant elements
This guideline applies to the application as a whole and any content that may differ in language from the surrounding text:
- The root
MaterialApporCupertinoApp- Individual
TextorRichTextwidgets that display content in a different language- Dynamically loaded content from an API that may arrive in a different locale
WCAG Guideline
This guideline is based on two related criteria:
- WCAG 2.2 - 3.1.1 Language of Page (Level A). The default human language of the content must be programmatically determinable.
- WCAG 2.2 - 3.1.2 Language of Parts (Level AA). The human language of each passage or phrase that differs from the default language must be programmatically determinable.
Solution
3.1.1 — Language of page
Set the locale on MaterialApp (or CupertinoApp) to declare the default language of the application. Flutter propagates this locale to the accessibility tree, allowing TalkBack and VoiceOver to apply the correct pronunciation rules.
// ❌ Don't - no locale set, screen readers fall back to their own default
MaterialApp(
home: const HomeScreen(),
);
// ✅ Do - declare the locale explicitly
MaterialApp(
locale: const Locale('en'),
supportedLocales: const [
Locale('en'),
Locale('zh', 'Hans'), // Simplified Chinese
],
localizationsDelegates: AppLocalizations.localizationsDelegates,
home: const HomeScreen(),
);
When the user’s preferred language should be respected, resolve the locale from the device settings using the default Flutter locale resolution:
// ✅ Do - resolve locale from device settings
MaterialApp(
supportedLocales: const [Locale('en'), Locale('zh', 'Hans')],
localizationsDelegates: AppLocalizations.localizationsDelegates,
// Flutter automatically picks the best supported locale from the device settings
home: const HomeScreen(),
);
3.1.2 - Language of parts (optional for native mobile)
Use the locale parameter on a Text or TextSpan widget to mark a passage that differs from the page language. Screen readers use this to switch pronunciation rules for that specific segment.
// ✅ Do - mark an inline French phrase with its own locale
RichText(
text: const TextSpan(
text: 'The French word for cat is ',
children: [
TextSpan(
text: 'chat',
locale: Locale('fr'),
),
TextSpan(text: '.'),
],
),
);
Validation & Testing
See the Validation & Testing setup guide for tool configuration.
TalkBack & VoiceOver
Enable a screen reader in the target language and navigate through the app. Verify that text is pronounced correctly and that a language switch is audible when moving between content in different languages.
flutter_test
testWidgets('MaterialApp declares the correct default locale', (tester) async {
await tester.pumpWidget(const MyApp());
final materialApp = tester.widget<MaterialApp>(find.byType(MaterialApp));
expect(materialApp.locale, const Locale('en'));
expect(
materialApp.supportedLocales,
containsAll([const Locale('en'), const Locale('zh', 'Hans')]),
);
});
🛠️ Usage baseflow-a11y-components library
🛠️ Work in progress…