Localize Text, RTL Layouts, Placeholders and Plural Forms
Deconstruct how to configure Flutter localization with ARB bundles, enforce mandatory ICU fallback branches, handle literal quote escaping, and mirror UI layouts with Directionality.
1. The Core Challenge & Problem
2. Architectural Principles & Resolution
3. Complete Tested Flutter Code
main.dart (Flutter 3.x+ ready)import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() => runApp(const LocalizedApp());
class LocalizedApp extends StatefulWidget {
const LocalizedApp({super.key});
@override
State<LocalizedApp> createState() => _LocalizedAppState();
}
class _LocalizedAppState extends State<LocalizedApp> {
Locale _locale = const Locale('en');
int _itemCount = 1;
void _toggleLocale() {
setState(() {
_locale = _locale.languageCode == 'en'
? const Locale('ar') // Demonstrates RTL layout mirroring
: const Locale('en');
});
}
@override
Widget build(BuildContext context) {
final isRtl = _locale.languageCode == 'ar';
return MaterialApp(
debugShowCheckedModeBanner: false,
locale: _locale,
supportedLocales: const [Locale('en'), Locale('es'), Locale('ar')],
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: const Color(0xFF8467D7),
),
home: Scaffold(
appBar: AppBar(
title: Text(isRtl ? 'معاينة التعريب' : 'Localization Preview'),
actions: [
IconButton(
icon: const Icon(Icons.language),
tooltip: 'Switch Language',
onPressed: _toggleLocale,
),
],
),
body: Padding(
padding: const EdgeInsetsDirectional.all(16.0), // Mirrors in RTL
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// User Card with Directional Avatar Alignment
Card(
child: Padding(
padding: const EdgeInsetsDirectional.all(16.0),
child: Row(
children: [
const CircleAvatar(child: Icon(Icons.person)),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isRtl ? 'الملف الشخصي' : 'User Profile',
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
Text(
// In English: "Chirag Raval (Lead)"
// In RTL/Arabic: "راوال، شيراغ (المسؤول)"
isRtl ? 'راوال، شيراغ (المسؤول)' : 'Chirag Raval (Lead Engineer)',
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
],
),
),
],
),
),
),
const SizedBox(height: 16),
// Interactive Plural Counter
Card(
child: Padding(
padding: const EdgeInsetsDirectional.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isRtl
? (_itemCount == 0 ? 'لا توجد عناصر' : '$_itemCount عناصر')
: (_itemCount == 0
? 'No items'
: _itemCount == 1
? '1 item'
: '$_itemCount items'),
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Row(
children: [
OutlinedButton.icon(
onPressed: () {
if (_itemCount > 0) setState(() => _itemCount--);
},
icon: const Icon(Icons.remove, size: 14),
label: Text(isRtl ? 'إنقاص' : 'Decrement'),
),
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: () => setState(() => _itemCount++),
icon: const Icon(Icons.add, size: 14),
label: Text(isRtl ? 'زيادة' : 'Increment'),
),
],
),
],
),
),
),
],
),
),
),
);
}
}
4. Expected Visual & Behavioral Result
5. Common Pitfalls & Traps
Pitfall #1: Omitting the mandatory "other" fallback branch in plural or select ARB expressions.
Remedy: Always supply an "other" branch in every plural and select message (e.g. "{count, plural, =0{none} =1{one} other{{count} items}}"). Flutter's gen-l10n strictly requires "other" and aborts compilation if it is missing.
Pitfall #2: Using physical EdgeInsets.only(left: ...) and Alignment.topLeft in translatable screens.
Remedy: Use EdgeInsetsDirectional.only(start: ...) and AlignmentDirectional.topStart so layouts automatically mirror in RTL languages like Arabic and Hebrew.
Pitfall #3: Forgetting --use-escaping in l10n.yaml when using apostrophes or literal braces.
Remedy: Set "use-escaping: true" in l10n.yaml so that pairs of single quotes ("''") produce literal single quotes and "'{'" produces literal braces.
Pitfall #4: Treating placeholder reordering in translation bundles as syntax errors.
Remedy: Translations often require different grammatical word orders. Reordered placeholders (e.g. "{lastName}, {firstName}") are completely valid as long as all required variables are preserved.