Implement App Localization Support
Adding localization support to your Flutter app allows you to reach a global audience by providing content in multiple languages. This tutorial will guide you through implementing a robust localization system.
Features
- Multiple language support
- RTL (Right-to-Left) layout support
- Dynamic language switching
- Fallback language handling
- Number and date formatting
- Currency formatting
- Pluralization support
- Gender-specific translations
Implementation Steps
-
Setup Dependencies
# pubspec.yaml dependencies: flutter_localizations: sdk: flutter intl: ^0.18.1 shared_preferences: ^2.2.2 provider: ^6.1.1
-
Create Localization Files
# l10n.yaml arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart output-class: AppLocalizations
-
Define Translation Files
// lib/l10n/app_en.arb { "appTitle": "My App", "@appTitle": { "description": "The title of the application" }, "welcome": "Welcome, {name}!", "@welcome": { "description": "Welcome message with name parameter", "placeholders": { "name": { "type": "String" } } }, "itemsCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}", "@itemsCount": { "description": "Number of items message", "placeholders": { "count": { "type": "num" } } } } // lib/l10n/app_es.arb { "appTitle": "Mi Aplicación", "welcome": "¡Bienvenido, {name}!", "itemsCount": "{count, plural, =0{Sin elementos} =1{1 elemento} other{{count} elementos}}" }
-
Create Localization Provider
class LocalizationProvider extends ChangeNotifier { final SharedPreferences _prefs; Locale _locale; LocalizationProvider(this._prefs) : _locale = Locale(_prefs.getString('languageCode') ?? 'en'); Locale get locale => _locale; Future<void> setLocale(Locale locale) async { if (_locale == locale) return; _locale = locale; await _prefs.setString('languageCode', locale.languageCode); notifyListeners(); } Future<void> loadSavedLocale() async { final languageCode = _prefs.getString('languageCode'); if (languageCode != null) { _locale = Locale(languageCode); notifyListeners(); } } }
-
Create Localization Widgets
class LanguageSelector extends StatelessWidget { final List<Locale> supportedLocales; final Locale currentLocale; final Function(Locale) onLocaleChanged; const LanguageSelector({ required this.supportedLocales, required this.currentLocale, required this.onLocaleChanged, }); @override Widget build(BuildContext context) { return DropdownButton<Locale>( value: currentLocale, items: supportedLocales.map((Locale locale) { return DropdownMenuItem<Locale>( value: locale, child: Text(_getLanguageName(locale)), ); }).toList(), onChanged: (Locale? newLocale) { if (newLocale != null) { onLocaleChanged(newLocale); } }, ); } String _getLanguageName(Locale locale) { switch (locale.languageCode) { case 'en': return 'English'; case 'es': return 'Español'; default: return locale.languageCode.toUpperCase(); } } } class LocalizedText extends StatelessWidget { final String key; final Map<String, dynamic>? args; final TextStyle? style; const LocalizedText( this.key, { this.args, this.style, }); @override Widget build(BuildContext context) { return Text( AppLocalizations.of(context)!.translate(key, args), style: style, ); } }
-
Configure App for Localization
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider( create: (context) => LocalizationProvider( SharedPreferences.getInstance(), ), ), ], child: Consumer<LocalizationProvider>( builder: (context, provider, child) { return MaterialApp( title: 'Localized App', locale: provider.locale, supportedLocales: [ Locale('en'), // English Locale('es'), // Spanish Locale('fr'), // French Locale('de'), // German Locale('ja'), // Japanese ], localizationsDelegates: [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], home: HomeScreen(), ); }, ), ); } }
-
Create Localized Screen
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { final localizations = AppLocalizations.of(context)!; final provider = Provider.of<LocalizationProvider>(context); return Scaffold( appBar: AppBar( title: Text(localizations.appTitle), actions: [ LanguageSelector( supportedLocales: [ Locale('en'), Locale('es'), Locale('fr'), Locale('de'), Locale('ja'), ], currentLocale: provider.locale, onLocaleChanged: provider.setLocale, ), ], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ LocalizedText( 'welcome', args: {'name': 'John'}, style: Theme.of(context).textTheme.headlineMedium, ), SizedBox(height: 16), LocalizedText( 'itemsCount', args: {'count': 5}, style: Theme.of(context).textTheme.bodyLarge, ), ], ), ), ); } }
Best Practices
-
Translation Management
- Use ARB files for translations
- Implement fallback languages
- Handle missing translations
- Support RTL layouts
-
Performance
- Lazy load translations
- Cache translations
- Optimize bundle size
- Handle large translation files
-
User Experience
- Provide language selection UI
- Show loading states
- Handle language switching
- Maintain user preferences
-
Testing
- Test all supported languages
- Verify RTL support
- Check fallback behavior
- Test dynamic switching
Conclusion
This tutorial has shown you how to implement localization in your Flutter app with features like:
- Multiple language support
- Dynamic language switching
- RTL layout support
- Number and date formatting
- Pluralization support
You can extend this implementation by adding:
- More languages
- Region-specific formatting
- Custom date formats
- Currency conversion
- Translation management system
Remember to:
- Follow localization best practices
- Test thoroughly
- Consider cultural differences
- Handle edge cases
- Keep translations updated
This implementation provides a solid foundation for creating a truly global app.