In today's global market, catering to users from diverse linguistic and cultural backgrounds is crucial. Localization, the process of adapting an application to different languages, regions, and cultures, is an essential aspect of mobile development. It not only helps your app reach a broader audience but also enhances the user experience by providing a personalized feel.

If you're working with Flutter, Google's UI toolkit for building natively compiled applications, you're in luck! Dart, the programming language used for Flutter development, offers two powerful packages to simplify the process of localization: dart intl and dart intl_translation. These packages provide a set of tools and functionalities that streamline Flutter internationalization, making it easier for you to localize your app for different languages.

In this blog post, I'll walk you through the process of implementing localization in your Flutter app using the Dart intl and intl_translation packages. We'll start by setting up the packages and creating a new Flutter project. Then, we'll dive into the basics of Flutter internationalization, learn how to use advanced features, and wrap up with some best practices for Flutter translation and localization. So let's get started!

Getting Started with Dart intl and intl_translation

Installing and setting up Dart intl and intl_translation packages

Before diving into the implementation, we need to set up the Dart intl and intl_translation packages in our development environment. To do this, follow the steps below:

1. Open your pubspec.yaml file and add the following dependencies under the dependencies section:

dependencies:
  flutter:
    sdk: flutter
  intl: ^0.17.0

2. In the same pubspec.yaml file, add the intl_translation package under the dev_dependencies section:

dev_dependencies:
  flutter_test:
    sdk: flutter
  intl_translation: ^0.17.10

3. Save the pubspec.yaml file and run flutter pub get in your terminal to download the packages.

Creating a new Flutter project for demonstration purposes

Now that we have the necessary packages installed, let's create a new Flutter project to demonstrate how to use them for localization. If you already have a project you'd like to use, feel free to skip this step.

1. Open your terminal and run the following command to create a new Flutter project:

flutter create flutter_localization_demo

2. Change your working directory to the newly created project:

cd flutter_localization_demo

3. Open the project in your favorite code editor.

Integrating the packages into the Flutter project

To integrate the Dart intl and intl_translation packages into your Flutter project, follow these steps:

1. Create a new directory named l10n at the root level of your project. This directory will store the translation files for your app.

2. Inside the l10n directory, create a file named app_en.arb with the following content:

{
  "helloWorld": "Hello, World!"
}

This file will store the English translations for your app. We'll create more translation files for different languages later in this tutorial.

3. Open the lib directory, and create a new file named app_localizations.dart. This file will contain the code for loading translations and handling localization.

That's it! We've successfully set up the Dart intl and intl_translation packages in our Flutter project. Next, we'll learn the basics of Flutter internationalization and start implementing localization for our app.

Flutter Internationalization Basics

Understanding .arb files and their role in Flutter localization

In Flutter, translations are stored in Application Resource Bundle (.arb) files, which are JSON-like files containing key-value pairs representing messages and their translations. These files play a crucial role in Flutter localization, as they store the translated text for each language supported by your app.

For example, the app_en.arb file we created earlier contains the English translation for the "Hello, World!" message. To add support for another language, like Spanish, you would create another .arb file (e.g., app_es.arb) and provide the corresponding translations.

Generating .arb files for different languages

Now that we understand the role of .arb files in Flutter localization, let's generate .arb files for a few additional languages.

1. Inside the l10n directory, create a new file named app_es.arb for the Spanish translations, with the following content:

{
  "helloWorld": "¡Hola, Mundo!"
}

2. Similarly, create a file named app_fr.arb for the French translations:

{
  "helloWorld": "Bonjour, Monde!"
}

Feel free to add more .arb files for other languages you want to support in your app.

Creating a localization delegate to load translations

Now that we have our .arb files in place, we need to create a localization delegate to load the translations at runtime. To do this, follow these steps:

1. Open the app_localizations.dart file created earlier in the lib directory.
2. Add the following imports at the top of the file:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
import 'package:intl/src/intl_helpers.dart';

3. Create a new class named AppLocalizations that extends MessageLookupByLibrary. This class will be responsible for loading the translations:

class AppLocalizations extends MessageLookupByLibrary {
  // Add implementation details here
}

4. Inside the AppLocalizations class, add a localeName getter to return the current locale's name:

String get localeName => Intl.getCurrentLocale();

5. Define the translations for each language by overriding the message() method:

@override
String message(String messageName, [List<Object> args]) {
  switch (localeName) {
    case 'en':
      return _enMessages[messageName]?.call(args) ?? messageName;
    case 'es':
      return _esMessages[messageName]?.call(args) ?? messageName;
    case 'fr':
      return _frMessages[messageName]?.call(args) ?? messageName;
    default:
      return messageName;
  }
}

6. Define the _enMessages, _esMessages, and _frMessages maps containing the translations from the .arb files:

final _enMessages = {
  "helloWorld": (List<Object> args) => "Hello, World!",
};

final _esMessages = {
  "helloWorld": (List<Object> args) => "¡Hola, Mundo!",
};

final _frMessages = {
  "helloWorld": (List<Object> args) => "Bonjour, Monde!",
};

7. Finally, create a localization delegate class named AppLocalizationsDelegate that extends LocalizationsDelegate<AppLocalizations>:

class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  // Add implementation details here
}

8. Inside the AppLocalizationsDelegate class, override the following methods:

// This method checks whether the current locale is supported by our app.
@override
bool isSupported(Locale locale) {
  return ['en', 'es', 'fr'].contains(locale.languageCode);
}

// This method loads the AppLocalizations instance containing the translations.
@override
Future<AppLocalizations> load(Locale locale) {
  final String name = locale.countryCode == null ? locale.languageCode : locale.toString();
  final String localeName = Intl.canonicalizedLocale(name);
  Intl.defaultLocale = localeName;
  return AppLocalizations.load(localeName);
}

// This method returns true if the delegate should be rebuilt when the Flutter app's locale changes.
@override
bool shouldReload(AppLocalizationsDelegate old) => false;

9. Add a static method named load to the AppLocalizations class that initializes an instance of the class and loads the translations:

static Future<AppLocalizations> load(String localeName) async {
  final AppLocalizations localizations = AppLocalizations();
  await localizations.load(localeName);
  return localizations;
}

Future<void> load(String localeName) async {
  await initializeMessages(localeName);
  Intl.defaultLocale = localeName;
}

10. Add the following import at the top of the app_localizations.dart file to import the initializeMessages() function:

import 'package:flutter_localization_demo/l10n/messages_all.dart';

Incorporating translated strings in the Flutter app

Now that we have our localization delegate set up, let's incorporate the translated strings into our Flutter app.

1. Open the lib/main.dart file and add the following imports at the top:

import 'package:flutter_localization_demo/app_localizations.dart';
import 'package:flutter_localization_demo/app_localizations_delegate.dart';

2. Inside the MyApp widget, add the localizationsDelegates and supportedLocales properties to the MaterialApp widget:

MaterialApp(
  localizationsDelegates: [
    AppLocalizationsDelegate(),
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
  ],
  supportedLocales: [
    Locale('en', ''),
    Locale('es', ''),
    Locale('fr', ''),
  ],
  // Other properties
)

3. Replace the Text widget inside the MyHomePage widget with the following code to display the translated "Hello, World!" message:

Text(AppLocalizations.of(context).message('helloWorld')),

That's it! You've successfully implemented basic Flutter internationalization using the Dart intl and intl_translation packages. You can now run your app and see the translated strings displayed based on your device's language settings. In the next section, we'll explore advanced features of the packages to enhance your app's localization capabilities.

Advanced Features of Dart intl and intl_translation

Plurals and gender-specific translations in Flutter localization

The Dart intl package also provides support for handling plurals and gender-specific translations. To demonstrate this, let's create a message that displays the number of items in a shopping cart.

Update your .arb files to include the plural message:

In app_en.arb:

{
  "helloWorld": "Hello, World!",
  "itemsInCart": "{count,plural, =0{No items}=1{1 item}other{{count} items}}"
}

In app_es.arb:

{
  "helloWorld": "¡Hola, Mundo!",
  "itemsInCart": "{count,plural, =0{Ningún artículo}=1{1 artículo}other{{count} artículos}}"
}

In app_fr.arb:

{
  "helloWorld": "Bonjour, Monde!",
  "itemsInCart": "{count,plural, =0{Aucun article}=1{1 article}other{{count} articles}}"
}

2. Update your _enMessages, _esMessages, and _frMessages maps in app_localizations.dart to include the plural message:

final _enMessages = {
  "helloWorld": (List<Object> args) => "Hello, World!",
  "itemsInCart": (List<Object> args) => "{count,plural, =0{No items}=1{1 item}other{{count} items}}",
};

final _esMessages = {
  "helloWorld": (List<Object> args) => "¡Hola, Mundo!",
  "itemsInCart": (List<Object> args) => "{count,plural, =0{Ningún artículo}=1{1 artículo}other{{count} artículos}}",
};

final _frMessages = {
  "helloWorld": (List<Object> args) => "Bonjour, Monde!",
  "itemsInCart": (List<Object> args) => "{count,plural, =0{Aucun article}=1{1 article}other{{count} articles}}",
};

3. Add a method to the AppLocalizations class to handle plurals:

String itemsInCart(int count) => Intl.plural(
      count,
      zero: message('itemsInCart', [count]),
      one: message('itemsInCart', [count]),
      other: message('itemsInCart', [count]),
      name: 'itemsInCart',
      args: [count],
      examples: const {'count': 42},
    );

4. Use the itemsInCart() method in your app:

Text(AppLocalizations.of(context).itemsInCart(3)),

Formatting numbers, dates, and currencies with Dart intl 

The Dart intl package also provides utilities for formatting numbers, dates, and currencies, making it easy to display data in a localized format.

1. Add the following import at the top of the app_localizations.dart file:

import 'package:intl/number_symbols_data.dart';

2. Create a method in the AppLocalizations class for formatting numbers:

String formatNumber(num number) {
  return NumberFormat().format(number);
}

3. Use the formatNumber() method in your app:

Text(AppLocalizations.of(context).formatNumber(12345)),

Customizing the app's locale and overriding the system default

In some cases, you may want to allow users to change the app's language independently of their device's language settings. To do this, you can create a language selection screen and use the Locale class to override the system default locale.

1. Create a new StatefulWidget named LanguageSelector in your lib directory:

class LanguageSelector extends StatefulWidget {
  @override
  _LanguageSelectorState createState() => _LanguageSelectorState();
}

class _LanguageSelectorState extends State<LanguageSelector> {
  @override
  Widget build(BuildContext context) {
    // Add implementation details here
  }
}

2. Inside the _LanguageSelectorState class, add a List<Locale> variable containing the supported locales:

List<Locale> supportedLocales = [
  Locale('en', ''),
  Locale('es', ''),
  Locale('fr', ''),
];

3. Implement the build() method of the _LanguageSelectorState class to display a list of supported languages:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Language Selector'),
    ),
    body: ListView.builder(
      itemCount: supportedLocales.length,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          title: Text(supportedLocales[index].toLanguageTag()),
          onTap: () {
            _changeLanguage(supportedLocales[index]);
          },
        );
      },
    ),
  );
}

4. Add the _changeLanguage() method to the _LanguageSelectorState class:

void _changeLanguage(Locale locale) {
  setState(() {
    MyApp.localeOverride = locale;
  });
  Navigator.pop(context);
}

5. Update the MyApp widget to accept a Locale variable named localeOverride:

class MyApp extends StatelessWidget {
  static Locale localeOverride;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: localeOverride,
      // Other properties
    );
  }
}

Now, when users select a language from the LanguageSelector screen, the app's locale will be updated accordingly, overriding the system default.

In conclusion, Dart intl and intl_translation packages provide a powerful set of tools for implementing localization in Flutter apps. By following this tutorial, you can successfully localize your app to cater to users from diverse linguistic and cultural backgrounds, enhancing their experience and expanding your app's reach. Don't forget to always test your app thoroughly to ensure that your translations are accurate and your localization implementation is working correctly.

Summary and Closing Thoughts

In this blog post, we explored the intl and intl_translation packages of Dart and Flutter and learned how to use them to implement localization in a Flutter app. Here's a brief summary of the topics we covered:

  1. Importance of localization in mobile development and an introduction to Dart intl and intl_translation packages
  2. Setting up a Flutter project with the necessary dependencies and directory structure
  3. Flutter internationalization basics, including .arb files, localization delegate, and incorporating translated strings
  4. Advanced features of Dart intl and intl_translation, such as handling plurals, gender-specific translations, formatting numbers, dates, and currencies, and customizing the app's locale
  5. Practical examples and code snippets to demonstrate each step of the localization process

By following the steps outlined in this tutorial, you can create a fully localized Flutter app that caters to a diverse audience, providing an enhanced user experience and expanding your app's reach. Remember to test your app thoroughly to ensure accurate translations and a smooth localization implementation. With a solid understanding of Flutter localization using Dart intl and intl_translation packages, you're well-equipped to create apps that resonate with users around the globe. Happy coding!