Flutter Internationalization

Flutter - Introduction to Dart Programming

Flutter Internationalization

Internationalization (often abbreviated as i18n) is the process of designing and developing software so that it can be easily adapted to different languages, regions, and cultures without engineering changes. Essentially, it's the practice of preparing your app to handle different locales (languages and regions) in the future. Internationalization allows you to provide different translations of your app's text, formatting, and layout, making it suitable for a global audience.


Why Internationalization is Important

  • Global Reach: Internationalizing your app enables you to reach users from different parts of the world who speak different languages and have different cultural preferences.
  • Localization Support: Internationalization is a prerequisite for localization (l10n), which is the process of translating and adapting your app for a specific region or language.
  • Cultural Sensitivity: Different regions have different formats for dates, currencies, numbers, and text directions. Internationalization ensures that these are handled appropriately.

Key Concepts of Internationalization

1. Locale:A locale is a combination of language and region. For example:

  1. en_US–English as spoken in the United States.
  2. fr_FR–French as spoken in France.
  3. es_ES –Spanish as spoken in Spain.

Each locale has specific cultural preferences such as date formats, currency formats, and language direction (LTR/RTL).


2:String Externalization

Instead of hardcoding text directly into your code (e.g., "Hello, World!"), internationalization involves storing text in external files that can be swapped out depending on the user's locale. This allows you to easily provide translations without changing the core code.


Number and Date Formatting

In some regions, dates are written as MM/DD/YYYY, while in others, it's DD/MM/YYYY. Similarly, currencies, numbers, and measurements can be formatted differently. Internationalization ensures that these are formatted according to the region's conventions.


Text Direction

Some languages, like English, are written from left to right (LTR), while others, like Arabic and Hebrew, are written from right to left (RTL). Internationalization ensures that the layout and text flow correctly based on the locale.


Pluralization

Different languages have different rules for pluralization. For instance, English has two forms (one and many), while languages like Russian or Arabic may have more complex pluralization rules. Internationalization must handle these correctly.


Steps to Implement Internationalization in Flutter

Flutter provides robust support for internationalization, including tools and libraries to help you create a globally accessible app.


Add Dependencies: To enable internationalization in your Flutter app, you'll first need to add the necessary dependencies to your pubspec.yaml file. The main package for internationalization in Flutter is flutter_localizations, and you may also use the intl package for working with translations and formatting.


  dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.17.0

2.Setup Supported Locales: In the MaterialApp widget, you specify which locales your app supports. You also provide the list of localized delegates that help Flutter understand which resources it should load based on the current locale.


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

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

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Internationalization Example',
        supportedLocales: [
          Locale('en', 'US'), // English
          Locale('es', 'ES'), // Spanish
          Locale('fr', 'FR'), // French
        ],
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
        ],
        home: MyHomePage(),
      );
    }
  }

Create Translations:You'll store translations for your app's text in separate .arb files. These files are JSON-like and contain key-value pairs where the key is the text identifier and the value is the translation.

Example of an en_US.arb file:


  {
    "hello": "Hello",
    "welcome": "Welcome to Flutter"
  }

And for Spanish (es_ES.arb):


  {
    "hello": "Hola",
    "welcome": "Bienvenido a Flutter"
  }

Configure pubspec.yaml

Ensure you declare your assets in pubspec.yaml:

flutter: assets:
- assets/lang/en.json
- assets/lang/es.json
- assets/lang/fr.json


Use the Translations in Code After setting up the .arb files and running the flutter_localizations package, you can load and use these translations in your app.


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

  class MyHomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(Intl.message('Welcome to Flutter', name: 'welcome')),
        ),
        body: Center(
          child: Text(Intl.message('Hello', name: 'hello')),
        ),
      );
    }
  }

Localization and Formatting With the intl package, you can also handle date, number, and currency formatting in a locale-sensitive way. For example:


  import 'package:intl/intl.dart';

  // Formatting current date
  String formattedDate = DateFormat.yMMMd().format(DateTime.now());

  // Formatting currency
  String formattedCurrency = NumberFormat.simpleCurrency(locale: 'en_US').format(1234.56);


  <!-- Import required packages -->
  import 'dart:convert';
  import 'package:flutter/material.dart';
  import 'package:flutter/services.dart';

  <!-- Localization class -->
  class AppLocalizations {
    final Locale locale;
    AppLocalizations(this.locale);

    <!-- Retrieve instance from context -->
    static AppLocalizations of(BuildContext context) {
      return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
    }

    <!-- Localization delegate -->
    static const LocalizationsDelegate<AppLocalizations> delegate = 
      _AppLocalizationsDelegate();

    late Map<String, String> _localizedStrings;

    <!-- Load localization JSON file -->
    Future<void> load() async {
      String jsonString =
          await rootBundle.loadString('assets/lang/${locale.languageCode}.json');
      Map<String, dynamic> jsonMap = json.decode(jsonString);
      _localizedStrings =
          jsonMap.map((key, value) => MapEntry(key, value.toString()));
    }

    <!-- Translate text -->
    String translate(String key) {
      return _localizedStrings[key] ?? key;
    }
  }

  <!-- Localization delegate class -->
  class _AppLocalizationsDelegate 
      extends LocalizationsDelegate<AppLocalizations> {
    
    const _AppLocalizationsDelegate();

    @override
    bool isSupported(Locale locale) => 
      ['en', 'es', 'fr'].contains(locale.languageCode);

    @override
    Future<AppLocalizations> load(Locale locale) async {
      AppLocalizations localizations = AppLocalizations(locale);
      await localizations.load();
      return localizations;
    }

    @override
    bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
  }

Switching Locales:You can provide a way for users to change the language of your app dynamically. One approach is to use a state management solution to track the current locale and update the Locale in the MaterialApp widget.


Example: Full Internationalization Setup

  // Import required packages
  import 'package:flutter/material.dart';
  import 'package:flutter_localizations/flutter_localizations.dart';
  import 'package:intl/intl.dart';

  // Main function
  void main() {
    runApp(MyApp());
  }

  // Root widget of the application
  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Internationalization Example',
        supportedLocales: [
          Locale('en', 'US'), // English
          Locale('es', 'ES'), // Spanish
        ],
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
        ],
        home: MyHomePage(),
      );
    }
  }

  // Home page widget
  class MyHomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(Intl.message('Welcome to Flutter', name: 'welcome')),
        ),
        body: Center(
          child: Text(Intl.message('Hello', name: 'hello')),
        ),
      );
    }
  }

Using intl Package

The intl package in Flutter is a powerful tool for handling internationalization (i18n). It provides features like number formatting, date/time formatting, and message translation for multiple languages.

Add the intl Package to pubspec.yaml

First, you need to include the intl package in your Flutter project’s dependencies:


  dependencies:
    flutter:
      sdk: flutter
    intl: ^0.18.0 # Latest version

Run the command:

flutter pub get


Generate Dart Code from ARB Files

To make localization easier, intl supports Application Resource Bundle (ARB) files, which are JSON-like files that contain translations for your app. You can use these ARB files to store translations for different languages.

Create lib/l10n/ Folder: Store your ARB files here (e.g., intl_en.arb for English, intl_es.arb for Spanish).


Step 1: Create ARB Files

intl_en.arb (for English)


  {
  "title": "Welcome",
  "message": "Hello, how are you?"
  }

Step 2: Create Localization Delegate

Create a new file, lib/l10n/intl.dart, where you'll define the logic to handle translations:


  import 'package:intl/intl.dart';

  class AppLocalizations {
    static Future<AppLocalizations> load(Locale locale) {
      return initializeMessages(locale.toString()).then((_) {
        return AppLocalizations();
      });
    }

    static AppLocalizations of(BuildContext context) {
      return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
    }

    String get title {
      return Intl.message(
        'Welcome',
        name: 'title',
        desc: 'App title',
      );
    }

    String get message {
      return Intl.message(
        'Hello, how are you?',
        name: 'message',
        desc: 'Greeting message',
      );
    }
  }

Step 3: Generate Dart Code Using intl Package

The intl package comes with a tool to generate Dart code from the ARB files. To run this tool:

Create a l10n.yaml file in the root of the project:


  arb-dir: lib/l10n
template-arb-file: intl_en.arb
output-localization-file: lib/l10n/intl.dart

Run the following command in the terminal:

flutter pub run intl_utils:generate


Step 4: Implement Localization in main.dart

  import 'package:flutter/material.dart';
  import 'l10n/intl.dart';

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

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        supportedLocales: [
          Locale('en', 'US'),
          Locale('es', 'ES'),
        ],
        localizationsDelegates: [
          AppLocalizations.delegate,
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
        localeResolutionCallback: (locale, supportedLocales) {
          for (var supportedLocale in supportedLocales) {
            if (supportedLocale.languageCode == locale?.languageCode) {
              return supportedLocale;
            }
          }
          return supportedLocales.first;
        },
        home: HomeScreen(),
      );
    }
  }

  class HomeScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(AppLocalizations.of(context).title),
        ),
        body: Center(
          child: Text(AppLocalizations.of(context).message),
        ),
      );
    }
  }

Step 5: Enable Multiple Language Support

You can now switch between English and Spanish (or any other languages you add) by changing the language in the app settings, or by using a language picker in the app. For manual language switching, follow the same approach as I showed earlier with the locale object


Step 6: Use the intl Package for Date and Time Formatting

The intl package can also be used to format dates and numbers according to the user's locale. For example, formatting a date:


  import 'package:intl/intl.dart';

  void main() {
    var now = DateTime.now();
    print(DateFormat('yMMMd').format(now)); // E.g., January 29, 2025
  }
أحدث أقدم