Back to blog
March 27, 2025
4 min read

Implement App Localization Support

Multi-language app made simple

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

  1. Setup Dependencies

    # pubspec.yaml
    dependencies:
      flutter_localizations:
        sdk: flutter
      intl: ^0.18.1
      shared_preferences: ^2.2.2
      provider: ^6.1.1
    
  2. Create Localization Files

    # l10n.yaml
    arb-dir: lib/l10n
    template-arb-file: app_en.arb
    output-localization-file: app_localizations.dart
    output-class: AppLocalizations
    
  3. 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}}"
    }
    
  4. 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();
        }
      }
    }
    
  5. 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,
        );
      }
    }
    
  6. 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(),
              );
            },
          ),
        );
      }
    }
    
  7. 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

  1. Translation Management

    • Use ARB files for translations
    • Implement fallback languages
    • Handle missing translations
    • Support RTL layouts
  2. Performance

    • Lazy load translations
    • Cache translations
    • Optimize bundle size
    • Handle large translation files
  3. User Experience

    • Provide language selection UI
    • Show loading states
    • Handle language switching
    • Maintain user preferences
  4. 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.