This episode opens your app to a wider world: internationalization with flutter_localizations, RTL support and locale-aware formatting, accessibility best practices through semantics and focus order, and inclusive UX and accessibility testing.

A great app doesn't limit its users to one language or one way of interacting. Episode 17 builds the foundation for global users and users with different needs: internationalization, right-to-left text support, locale-aware formatting, and accessibility for screen readers and keyboards.
Add localization support from the SDK:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.19.0Add flutter_localizations and intl to the dependencies block. Then register the delegates and locale in MaterialApp:
MaterialApp(
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: const [
Locale('id'),
Locale('en'),
],
locale: const Locale('id'),
)supportedLocales declares the supported languages. GlobalMaterialLocalizations.delegates provides built-in translations for Material widgets — such as date labels and default button text.
App messages are managed in .arb files:
{
"@@locale": "id",
"appTitle": "Aplikasi Saya",
"greeting": "Selamat datang, {name}!"
}An .arb file is formatted as {"@@locale": "id", ...} and stores all strings under one key. The Flutter tool then generates a type-safe class from this file, so translation errors are caught at compile time, not runtime.
Flutter handles right-to-left layout automatically for Arabic, Hebrew, and similar locales. The Row and Column widgets flip direction according to the Directionality injected by the active locale. To test, switch the locale to an RTL language:
MaterialApp(
locale: const Locale('ar'),
supportedLocales: const [Locale('ar')],
)MaterialApp(locale: Locale('ar')) forces the app to use RTL direction. Test every screen with an RTL locale — margins, arrow icons, and element positions often need manual adjustment even when the core layout is automatic.
Numbers, dates, and currency should never be formatted manually. Use intl:
import 'package:intl/intl.dart';
final formatter = DateFormat.yMMMMd(Localizations.localeOf(context).toString());
print(formatter.format(DateTime.now()));DateFormat.yMMMMd(locale) formats the date according to the language and local conventions. The rule: store raw data (timestamps, numbers) and format it in the presentation layer — never store already-formatted strings.
Visual widgets are often unreadable to screen readers without help. Use Semantics to provide labels:
Semantics(
label: 'Tombol untuk menambah jumlah',
button: true,
child: FloatingActionButton(
onPressed: _tambah,
child: const Icon(Icons.add),
),
)Semantics(label: '...') provides the text a screen reader reads aloud. An icon button without text needs a label — otherwise screen reader users only hear "button" without meaning.
For keyboard and TV users, focus order matters:
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: [
TextField(decoration: const InputDecoration(labelText: 'Email')),
TextField(decoration: const InputDecoration(labelText: 'Password')),
ElevatedButton(onPressed: login, child: const Text('Masuk')),
],
),
)OrderedTraversalPolicy forces focus order to follow the children's visual order. Make sure every interactive element is reachable by keyboard and the order makes sense — from email, to password, to the sign-in button.
Low color contrast makes reading difficult. Use colors from ColorScheme, which are designed with contrast in mind, and test with MediaQuery.textScalerOf(context) to make sure the layout doesn't break when text size is increased.
Flutter provides tests for semantics:
testWidgets('label tombol tersedia untuk screen reader', (tester) async {
await tester.pumpWidget(const MaterialApp(home: MyButton()));
expect(
find.bySemanticsLabel('Tombol untuk menambah jumlah'),
findsOneWidget,
);
});find.bySemanticsLabel finds widgets by their semantics label. Tests like this ensure accessibility isn't broken by accidental changes.
Make accessibility part of the definition of done, not a feature bolted on at the end.
Key takeaways:
flutter_localizations and intl for language support..arb files so translations are type-safe.intl; store raw data.Semantics provides labels for screen readers; set focus order for keyboards.find.bySemanticsLabel and built-in tools.In the next episode 18 we discuss advanced rendering and architecture — custom render objects and widgets, platform-specific adaptation and responsive apps, micro frontends and plugin-driven architectures, and hybrid app patterns with web and desktop. You enter the advanced territory of Flutter.