Answer
Overview
Multi-language (internationalization/i18n) support in Flutter allows your app to display content in multiple languages based on user preferences or device locale. Flutter provides built-in tools and packages for localization.
Setup Methods
Method 1: Flutter Intl (Recommended)
Official Flutter approach using ARB files.
Method 2: Easy Localization Package
Popular third-party package with simpler setup.
Method 1: Flutter Intl (Official)
Step 1: Add Dependencies
yamldependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.18.0 flutter: generate: true
Step 2: Create l10n.yaml
yamlarb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart
Step 3: Create ARB Files
lib/l10n/app_en.arb (English)
json{ "@@locale": "en", "hello": "Hello", "welcome": "Welcome, {name}", "@welcome": { "placeholders": { "name": { "type": "String" } } }, "itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}", "@itemCount": { "placeholders": { "count": { "type": "int" } } } }
lib/l10n/app_es.arb (Spanish)
json{ "@@locale": "es", "hello": "Hola", "welcome": "Bienvenido, {name}", "itemCount": "{count, plural, =0{Sin elementos} =1{1 elemento} other{{count} elementos}}" }
lib/l10n/app_fr.arb (French)
json{ "@@locale": "fr", "hello": "Bonjour", "welcome": "Bienvenue, {name}", "itemCount": "{count, plural, =0{Aucun élément} =1{1 élément} other{{count} éléments}}" }
Step 4: Configure MaterialApp
dartimport 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( localizationsDelegates: [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('en'), // English Locale('es'), // Spanish Locale('fr'), // French Locale('ar'), // Arabic ], locale: Locale('en'), // Default locale home: HomeScreen(), ); } }
Step 5: Use Translations
dartclass HomeScreen extends StatelessWidget { Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar(title: Text(l10n.hello)), body: Column( children: [ Text(l10n.welcome('John')), Text(l10n.itemCount(5)), ], ), ); } }
Method 2: Easy Localization Package
Step 1: Install Package
yamldependencies: easy_localization: ^3.0.0
Step 2: Create Translation Files
assets/translations/en.json
json{ "hello": "Hello", "welcome": "Welcome, {}", "item_count": { "zero": "No items", "one": "1 item", "other": "{} items" } }
assets/translations/es.json
json{ "hello": "Hola", "welcome": "Bienvenido, {}", "item_count": { "zero": "Sin elementos", "one": "1 elemento", "other": "{} elementos" } }
Step 3: Update pubspec.yaml
yamlflutter: assets: - assets/translations/
Step 4: Initialize
dartimport 'package:easy_localization/easy_localization.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); runApp( EasyLocalization( supportedLocales: [Locale('en'), Locale('es'), Locale('fr')], path: 'assets/translations', fallbackLocale: Locale('en'), child: MyApp(), ), ); } class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, home: HomeScreen(), ); } }
Step 5: Use Translations
dartimport 'package:easy_localization/easy_localization.dart'; class HomeScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('hello'.tr())), body: Column( children: [ Text('welcome'.tr(args: ['John'])), Text('item_count'.plural(5)), // Change language ElevatedButton( onPressed: () => context.setLocale(Locale('es')), child: Text('Español'), ), ElevatedButton( onPressed: () => context.setLocale(Locale('en')), child: Text('English'), ), ], ), ); } }
Dynamic Language Switching
dartclass LanguagePicker extends StatelessWidget { Widget build(BuildContext context) { return DropdownButton<Locale>( value: context.locale, items: [ DropdownMenuItem( value: Locale('en'), child: Text('English'), ), DropdownMenuItem( value: Locale('es'), child: Text('Español'), ), DropdownMenuItem( value: Locale('fr'), child: Text('Français'), ), ], onChanged: (locale) { if (locale != null) { context.setLocale(locale); } }, ); } }
Handling RTL Languages (Arabic, Hebrew)
dartMaterialApp( localizationsDelegates: [...], supportedLocales: [ Locale('en'), Locale('ar'), // Arabic (RTL) Locale('he'), // Hebrew (RTL) ], // Auto-detects RTL builder: (context, child) { return Directionality( textDirection: _getTextDirection(context), child: child!, ); }, ) TextDirection _getTextDirection(BuildContext context) { final locale = Localizations.localeOf(context); return ['ar', 'he', 'fa', 'ur'].contains(locale.languageCode) ? TextDirection.rtl : TextDirection.ltr; }
Best Practices
Tip: Use context-aware translations, not hardcoded strings
✅ Do
dartText(AppLocalizations.of(context)!.hello)
❌ Don't
dartText('Hello') // Hardcoded
Use Pluralization
dart// Good Text(l10n.itemCount(items.length)) // Bad Text('${items.length} items') // Doesn't work for all languages
Extract All Strings
dart// ✅ Good final title = l10n.appTitle; final subtitle = l10n.appSubtitle; // ❌ Bad final title = 'My App'; final subtitle = 'Welcome';
Comparison Table
| Feature | Flutter Intl | Easy Localization |
|---|---|---|
| Setup | Moderate | Easy |
| File Format | ARB | JSON/YAML |
| Type Safety | ✅ Strong | ⚠️ Weak |
| Code Generation | ✅ Yes | ❌ No |
| Hot Reload | ✅ Yes | ✅ Yes |
| Pluralization | ✅ Built-in | ✅ Built-in |
| Dynamic Switch | ⚠️ Complex | ✅ Simple |
| Official | ✅ Yes | ❌ No |