How to Translate a Flutter App: Complete Guide

Learn how to localize your Flutter app using Dart and the intl package. This comprehensive guide covers string localization, pluralization, date formatting, and best practices for Flutter ap

  • date icon

    Friday, Nov 14, 2025

How to Translate a Flutter App: Complete Guide

Translating your Flutter app is essential for reaching a global audience. Flutter provides excellent built-in support for localization through the intl package and ARB (Application Resource Bundle) files. This guide will walk you through the process of translating a Flutter app using Dart.

Understanding Flutter Localization

Flutter uses ARB files (Application Resource Bundle) to store translations. These JSON-like files contain key-value pairs for each language. Flutter’s intl package handles the localization automatically based on the user’s device language settings.

Step 1: Add Dependencies

First, add the necessary packages to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

flutter:
  generate: true

Run flutter pub get to install the packages.

Step 2: Configure l10n.yaml

Create a l10n.yaml file in your project root to configure localization:

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

Step 3: Create ARB Files

Create a lib/l10n directory and add ARB files for each language:

lib/l10n/app_en.arb (English - default):

{
  "@@locale": "en",
  "appName": "My App",
  "@appName": {
    "description": "The application name"
  },
  "welcomeMessage": "Welcome to our app!",
  "@welcomeMessage": {
    "description": "Welcome message displayed on home screen"
  },
  "buttonSubmit": "Submit",
  "@buttonSubmit": {
    "description": "Submit button label"
  },
  "errorNetwork": "Network error. Please try again.",
  "@errorNetwork": {
    "description": "Network error message"
  },
  "itemsCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}",
  "@itemsCount": {
    "description": "Number of items",
    "placeholders": {
      "count": {
        "type": "int"
      }
    }
  },
  "userGreeting": "Hello, {name}! You have {messageCount, plural, =0{no messages} one{1 message} other{{messageCount} messages}}.",
  "@userGreeting": {
    "description": "Greeting with user name and message count",
    "placeholders": {
      "name": {
        "type": "String"
      },
      "messageCount": {
        "type": "int"
      }
    }
  }
}

lib/l10n/app_es.arb (Spanish):

{
  "@@locale": "es",
  "appName": "Mi Aplicación",
  "welcomeMessage": "¡Bienvenido a nuestra aplicación!",
  "buttonSubmit": "Enviar",
  "errorNetwork": "Error de red. Por favor, inténtalo de nuevo.",
  "itemsCount": "{count, plural, =0{No hay elementos} one{1 elemento} other{{count} elementos}}",
  "userGreeting": "¡Hola, {name}! Tienes {messageCount, plural, =0{no hay mensajes} one{1 mensaje} other{{messageCount} mensajes}}."
}

lib/l10n/app_fr.arb (French):

{
  "@@locale": "fr",
  "appName": "Mon Application",
  "welcomeMessage": "Bienvenue dans notre application!",
  "buttonSubmit": "Soumettre",
  "errorNetwork": "Erreur réseau. Veuillez réessayer.",
  "itemsCount": "{count, plural, =0{Aucun élément} one{1 élément} other{{count} éléments}}",
  "userGreeting": "Bonjour, {name}! Vous avez {messageCount, plural, =0{aucun message} one{1 message} other{{messageCount} messages}}."
}

Step 4: Generate Localization Files

Run the code generator:

flutter gen-l10n

Or use:

flutter pub run intl_utils:generate

This generates app_localizations.dart and language-specific files in .dart_tool/flutter_gen/gen_l10n/.

Step 5: Configure MaterialApp

Update your main.dart to enable localization:

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Localization Demo',
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en'), // English
        Locale('es'), // Spanish
        Locale('fr'), // French
      ],
      home: const HomePage(),
    );
  }
}

Step 6: Use Localized Strings

Access localized strings using AppLocalizations.of(context):

Before:

Text('Welcome to our app!')

After:

Text(AppLocalizations.of(context)!.welcomeMessage)

Or with a null-safe approach:

final l10n = AppLocalizations.of(context);
if (l10n != null) {
  Text(l10n.welcomeMessage)
}

Step 7: Handle String Formatting with Variables

Use placeholders in ARB files and Flutter handles the formatting:

ARB file:

{
  "userGreeting": "Hello, {name}!",
  "@userGreeting": {
    "placeholders": {
      "name": {
        "type": "String"
      }
    }
  }
}

Dart:

Text(AppLocalizations.of(context)!.userGreeting('John'))
// English: "Hello, John!"
// Spanish: "¡Hola, John!"

Step 8: Pluralization

Flutter supports pluralization using ICU message format:

ARB file:

{
  "itemsCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}"
}

Dart:

Text(AppLocalizations.of(context)!.itemsCount(5))
// English: "5 items"
// Spanish: "5 elementos"

Text(AppLocalizations.of(context)!.itemsCount(1))
// English: "1 item"
// Spanish: "1 elemento"

Text(AppLocalizations.of(context)!.itemsCount(0))
// English: "No items"
// Spanish: "No hay elementos"

Step 9: Format Dates and Numbers

Use intl package for localized formatting:

Dates:

import 'package:intl/intl.dart';

final date = DateTime.now();
final formatter = DateFormat.yMMMMEEEEd(Intl.getCurrentLocale());
final localizedDate = formatter.format(date);
// English: "Wednesday, March 20, 2025"
// Spanish: "miércoles, 20 de marzo de 2025"

Numbers:

import 'package:intl/intl.dart';

final number = 1234.56;
final formatter = NumberFormat.decimalPattern(Intl.getCurrentLocale());
final localizedNumber = formatter.format(number);
// English: "1,234.56"
// Spanish: "1.234,56"

Currency:

import 'package:intl/intl.dart';

final amount = 1234.56;
final formatter = NumberFormat.currency(locale: Intl.getCurrentLocale());
final localizedCurrency = formatter.format(amount);
// English (US): "$1,234.56"
// Spanish (ES): "1.234,56 €"

Step 10: Handle Right-to-Left (RTL) Languages

Flutter automatically handles RTL layouts. Use Directionality widget if needed:

Directionality(
  textDirection: TextDirection.rtl, // or TextDirection.ltr
  child: YourWidget(),
)

Or detect automatically:

final locale = Localizations.localeOf(context);
final isRTL = locale.languageCode == 'ar' || locale.languageCode == 'he';

Step 11: Test Your Localization

Using Flutter DevTools:

  1. Run your app
  2. Open DevTools
  3. Go to Inspector tab
  4. Change locale in device settings

Using Device Settings:

  1. Go to device Settings > Language & Region
  2. Change the device language
  3. Restart your app

Programmatically (for testing):

import 'package:flutter/material.dart';

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Locale _locale = const Locale('en');

  void _changeLanguage(Locale locale) {
    setState(() {
      _locale = locale;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en'),
        Locale('es'),
        Locale('fr'),
      ],
      home: LanguageSelector(
        onLanguageChanged: _changeLanguage,
      ),
    );
  }
}

Best Practices

1. Use Descriptive Keys

Bad:

{
  "msg1": "Submit"
}

Good:

{
  "buttonSubmit": "Submit",
  "@buttonSubmit": {
    "description": "Submit button label"
  }
}

2. Provide Context in ARB Files

Always include @ metadata entries to help translators:

{
  "deleteButton": "Delete",
  "@deleteButton": {
    "description": "Button to delete an item. Shown in the item detail view."
  }
}

3. Avoid String Concatenation

Bad:

Text(AppLocalizations.of(context)!.hello + ' ' + name)

Good:

{
  "helloUser": "Hello, {name}!",
  "@helloUser": {
    "placeholders": {
      "name": {"type": "String"}
    }
  }
}
Text(AppLocalizations.of(context)!.helloUser(name))

4. Use Pluralization Correctly

Always use ICU plural format for countable items:

{
  "messagesCount": "{count, plural, =0{No messages} one{1 message} other{{count} messages}}"
}

5. Test String Lengths

Some languages are longer than others. Design your UI to accommodate:

  • German and Finnish: 30-50% longer than English
  • Asian languages: May need more vertical space
  • Arabic and Hebrew: RTL layout considerations

6. Handle Missing Translations

Flutter falls back to the default locale if a translation is missing. Always provide a fallback:

final l10n = AppLocalizations.of(context);
final text = l10n?.welcomeMessage ?? 'Welcome'; // Fallback

Common Pitfalls

1. Forgetting to Run Code Generation

After updating ARB files, always run:

flutter gen-l10n

2. Not Including All Required Delegates

Make sure to include all four delegates:

localizationsDelegates: const [
  AppLocalizations.delegate,
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
],

3. Hardcoding Format Strings

Bad:

Text('\$${amount.toStringAsFixed(2)}')

Good:

final formatter = NumberFormat.currency(locale: Intl.getCurrentLocale());
Text(formatter.format(amount))

4. Not Testing Edge Cases

Test with:

  • Very long strings
  • Special characters
  • Numbers and dates
  • Plural forms (0, 1, many)
  • RTL languages

Advanced: Dynamic Language Switching

To allow users to change language within the app:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class LocaleProvider extends ChangeNotifier {
  Locale _locale = const Locale('en');
  
  Locale get locale => _locale;
  
  Future<void> loadLocale() async {
    final prefs = await SharedPreferences.getInstance();
    final languageCode = prefs.getString('language_code') ?? 'en';
    _locale = Locale(languageCode);
    notifyListeners();
  }
  
  Future<void> setLocale(Locale locale) async {
    if (_locale == locale) return;
    
    _locale = locale;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('language_code', locale.languageCode);
    notifyListeners();
  }
}

Usage:

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => LocaleProvider()..loadLocale(),
      child: Consumer<LocaleProvider>(
        builder: (context, localeProvider, _) {
          return MaterialApp(
            locale: localeProvider.locale,
            localizationsDelegates: const [
              AppLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
              GlobalCupertinoLocalizations.delegate,
            ],
            supportedLocales: const [
              Locale('en'),
              Locale('es'),
              Locale('fr'),
            ],
            home: const HomePage(),
          );
        },
      ),
    );
  }
}

Using with GetX (Alternative)

If you’re using GetX for state management:

import 'package:get/get.dart';

class LocaleController extends GetxController {
  var locale = const Locale('en').obs;
  
  void changeLocale(Locale newLocale) {
    locale.value = newLocale;
    Get.updateLocale(newLocale);
  }
}

Conclusion

Localizing your Flutter app is straightforward when you follow these steps:

  1. Add flutter_localizations and intl packages
  2. Create ARB files for each language in lib/l10n/
  3. Configure l10n.yaml and run code generation
  4. Set up localizationsDelegates in MaterialApp
  5. Use AppLocalizations.of(context) throughout your app
  6. Handle pluralization with ICU message format
  7. Format dates and numbers using intl package
  8. Test thoroughly in all supported languages

By following these practices, you’ll create an app that provides a native experience for users worldwide, significantly expanding your potential user base.

Streamline Your Flutter Localization Workflow

Managing translations for multiple languages can become complex as your app grows. Consider using a translation management platform to:

  • Collaborate with translators
  • Keep translations in sync with your codebase
  • Automate the translation workflow
  • Maintain consistency across all languages
  • Generate ARB files automatically
  • Integrate with your CI/CD pipeline

Ready to take your Flutter app global? Explore AZbox’s localization platform and streamline your translation workflow:

View AZbox Plans and Pricing

Blog

Latest Posts

Discover our latest articles and updates.

Why Translations Have Always Been a Problem in Software Development
date icon

Sunday, Dec 21, 2025

Why Translations Have Always Been a Problem in Software Development

For decades, software developers have struggled with translations and localization. What should be a straightforward pro

Read More
Cómo Traducir una App Flutter con Azbox: Guía Completa
date icon

Saturday, Dec 20, 2025

Cómo Traducir una App Flutter con Azbox: Guía Completa

Traducir tu app Flutter es esencial para llegar a una audiencia global. Azbox proporciona un potente SDK de Flutter que

Read More
How to Translate a Flutter App with Azbox: Complete Guide
date icon

Saturday, Dec 20, 2025

How to Translate a Flutter App with Azbox: Complete Guide

Translating your Flutter app is essential for reaching a global audience. Azbox provides a powerful Flutter SDK that sim

Read More
cta-image

Start Global Growth Today

Join hundreds of successful companies already using AZbox to reach customers worldwide. Start with a free trial, no credit card required.

Get Started - It's Free