Skip to content
Blog

Flutter L10n (Localization): The Complete Guide to Localizing Your App

Master Flutter localization (l10n) with flutter_localizations, code generation, and the official localization workflow for Android, iOS, and web.

Published on • September 19, 2026

AI Assistant

Flutter L10n (Localization): The Complete Guide

Flutter’s localization system (l10n) provides a comprehensive framework for translating your app into multiple languages. With automatic code generation, locale-aware widgets, and built-in support for Material and Cupertino components, Flutter makes localization accessible and maintainable.

Understanding L10n vs I18n vs A11y

Before diving in, let’s clarify the common abbreviations:

  • i18n (Internationalization) — designing your app to support multiple locales
  • l10n (Localization) — translating content for a specific locale
  • a11y (Accessibility) — making your app usable by everyone

Localization is the process of actually translating your app’s strings, adapting date formats, and handling locale-specific behavior.

Setting Up Flutter Localization

Step 1: Enable Code Generation

In your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

flutter:
  generate: true  # Enables code generation

Step 2: Create l10n.yaml

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
preferred-supported-locales: ["en"]
nullable-localization: false

Step 3: Create ARB Files

// lib/l10n/app_en.arb
{
  "@@locale": "en",
  "appTitle": "My App",
  "welcomeMessage": "Welcome to {appName}!",
  "loginButton": "Log In",
  "itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}"
}
// lib/l10n/app_es.arb
{
  "@@locale": "es",
  "appTitle": "Mi Aplicación",
  "welcomeMessage": "¡Bienvenido a {appName}!",
  "loginButton": "Iniciar Sesión",
  "itemCount": "{count, plural, =0{Sin artículos} =1{1 artículo} other{{count} artículos}}"
}

Step 4: Configure MaterialApp

import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: AppLocalizations.supportedLocales,
      home: const HomeScreen(),
    );
  }
}

Step 5: Use Localized Strings

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(title: Text(l10n.appTitle)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(l10n.welcomeMessage('My App')),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {},
              child: Text(l10n.loginButton),
            ),
            const SizedBox(height: 16),
            Text(l10n.itemCount(3)),
          ],
        ),
      ),
    );
  }
}

Advanced Localization Features

Date and Number Formatting

import 'package:intl/intl.dart';

class LocalizedDateFormat extends StatelessWidget {
  final DateTime date;
  
  const LocalizedDateFormat({super.key, required this.date});

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;
    final locale = l10n.localeName;
    
    return Text(
      DateFormat.yMMMMd(locale).format(date),
    );
  }
}

Handling Locale Changes

class LocaleProvider extends ChangeNotifier {
  Locale _locale = const Locale('en');

  Locale get locale => _locale;

  void setLocale(Locale locale) {
    if (AppLocalizations.supportedLocales.contains(locale)) {
      _locale = locale;
      notifyListeners();
    }
  }
}

Testing Localization

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  testWidgets('displays localized text', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: const [
          AppLocalizations.delegate,
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
        supportedLocales: const [Locale('es')],
        locale: const Locale('es'),
        home: const MyWidget(),
      ),
    );

    expect(find.text('Mi Aplicación'), findsOneWidget);
  });
}

Platform-Specific Configuration

Android

In android/app/src/main/res/values/strings.xml:

<resources>
    <string name="app_name">My App</string>
</resources>

In android/app/src/main/res/values-es/strings.xml:

<resources>
    <string name="app_name">Mi Aplicación</string>
</resources>

iOS

In ios/Runner/InfoPlist.strings:

CFBundleDisplayName = "My App";

In ios/Runner/es.lproj/InfoPlist.strings:

CFBundleDisplayName = "Mi Aplicación";

Best Practices

  1. Use the official Flutter l10n workflow — it’s the most maintained and documented approach
  2. Always create ARB files — they’re the standard for Flutter localization
  3. Test with pseudo-localizations — Flutter DevTools supports this
  4. Keep translations up to date — run code generation regularly
  5. Handle missing translations gracefully — provide fallback strings

Flutter’s localization system is powerful and well-integrated. By following these patterns, you can create apps that feel native in every language and locale they support.