Implementing Internationalization (i18n) and Localization (l10n) in Flutter

In today’s globalized world, making your app accessible to a diverse audience is crucial. Implementing internationalization (i18n) and localization (l10n) ensures your Flutter application can adapt to different languages, regions, and cultures. This article will guide you through the process of internationalizing and localizing your Flutter app.

What are Internationalization (i18n) and Localization (l10n)?

  • Internationalization (i18n): The process of designing an application so that it can be adapted to various languages and regions without engineering changes. It involves abstracting all strings and user interface elements into external resources.
  • Localization (l10n): The process of adapting an internationalized application for a specific region or language by translating text and adding locale-specific components.

Why Implement i18n and l10n in Flutter?

  • Reach a Wider Audience: Increase user adoption by supporting multiple languages.
  • Improve User Experience: Provide a more personalized and culturally relevant experience.
  • Competitive Advantage: Stand out by catering to global markets.

How to Implement i18n and l10n in Flutter

Implementing i18n and l10n in Flutter involves the following key steps:

Step 1: Add Dependencies

Add the flutter_localizations and intl dependencies to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any

Run flutter pub get to install the dependencies.

Step 2: Configure MaterialApp for Localization

In your main.dart file, configure the MaterialApp widget to support localization:

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(
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', 'US'), // English, United States
        const Locale('es', 'ES'), // Spanish, Spain
      ],
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Localization Example'),
      ),
      body: Center(
        child: Text('Hello, World!'), // Replace with localized text later
      ),
    );
  }
}

Explanation:

  • localizationsDelegates: Provides delegates for core Flutter widgets and localization functionality.
  • supportedLocales: Defines the locales that your app supports.

Step 3: Create Localization Files

Create a directory named l10n at the root of your Flutter project. Inside this directory, create ARB (Application Resource Bundle) files for each supported locale.

For English (en_US.arb):

{
  "@@locale": "en_US",
  "helloWorld": "Hello, World!",
  "@helloWorld": {
    "description": "The conventional greeting"
  }
}

For Spanish (es_ES.arb):

{
  "@@locale": "es_ES",
  "helloWorld": "¡Hola, Mundo!",
  "@helloWorld": {
    "description": "El saludo convencional"
  }
}

The @@locale field specifies the locale of the file, and the @helloWorld field provides metadata about the helloWorld string.

Step 4: Generate Localization Classes

To generate the necessary Dart classes for accessing your localized strings, you can use the intl_utils package and the flutter gen-l10n command. First, add intl_utils as a dev dependency:

dev_dependencies:
  intl_utils: ^2.8.2

Next, add the following configuration to your pubspec.yaml file under the flutter section:

flutter:
  generate: true

Then, run the following command in your terminal:

flutter gen-l10n

This command generates the AppLocalizations class in lib/l10n/app_localizations.dart, which you’ll use to access your localized strings.
If you get error
flutter gen-l10n error: Directory lookup failed for: Packagesflutter_toolslibsrcproject, searching ../../../..
Run command “flutter packages pub get”
it could be solved with `flutter pub upgrade`

Step 5: Use Localized Strings in Your App

Update your MyHomePage widget to use the localized strings:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        AppLocalizations.delegate, // Add this line
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', 'US'),
        const Locale('es', 'ES'),
      ],
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.helloWorld), // Use localized string here
      ),
      body: Center(
        child: Text(AppLocalizations.of(context)!.helloWorld), // Use localized string here
      ),
    );
  }
}

Now, the text in your app will change based on the device’s locale.

Step 6: Handling Pluralization

To handle pluralization, you can use the intl package’s plural format. Create a new entry in your ARB files:

In en_US.arb:

{
  "@@locale": "en_US",
  "itemCount": "{count,plural, =0{No items} =1{1 item} other{{count} items}}",
  "@itemCount": {
    "description": "Describes the number of items"
  }
}

In es_ES.arb:

{
  "@@locale": "es_ES",
  "itemCount": "{count,plural, =0{Sin elementos} =1{1 elemento} other{{count} elementos}}",
  "@itemCount": {
    "description": "Describe el número de elementos"
  }
}

Update your Dart code to use the pluralized string:

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
  int _itemCount = 0;

  void _incrementItemCount() {
    setState(() {
      _itemCount++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.helloWorld),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(AppLocalizations.of(context)!.itemCount(_itemCount)),
            ElevatedButton(
              onPressed: _incrementItemCount,
              child: Text('Add Item'),
            ),
          ],
        ),
      ),
    );
  }
}

Step 7: Handling Gender

To handle gender-specific text, use the intl package’s select format. Define gender-specific entries in your ARB files:

In en_US.arb:


{
  "@@locale": "en_US",
  "profileMessage": "{gender, select, male{He is awesome} female{She is awesome} other{They are awesome}}",
  "@profileMessage": {
    "description": "A message about a person based on their gender"
  }
}

In es_ES.arb:


{
  "@@locale": "es_ES",
  "profileMessage": "{gender, select, male{Él es increíble} female{Ella es increíble} other{Ellos son increíbles}}",
  "@profileMessage": {
    "description": "Un mensaje sobre una persona basado en su género"
  }
}

Update your Dart code to use the gender-specific string:


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

class MyHomePage extends StatelessWidget {
  final String gender;

  MyHomePage({Key? key, required this.gender}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.helloWorld),
      ),
      body: Center(
        child: Text(AppLocalizations.of(context)!.profileMessage(gender: gender)),
      ),
    );
  }
}

ElevatedButton(
    onPressed: () {
        Navigator.push(
            context,
            MaterialPageRoute(
            builder: (context) => MyHomePage(gender: "male")),
        );
    },
    child: Text('Go to male profile'),
),
ElevatedButton(
    onPressed: () {
        Navigator.push(
            context,
            MaterialPageRoute(
            builder: (context) => MyHomePage(gender: "female")),
        );
    },
    child: Text('Go to female profile'),
),

Best Practices for i18n and l10n in Flutter

  • Externalize All Strings: Ensure all user-facing text is stored in localization files.
  • Use Metadata: Provide descriptions for each localized string to aid translators.
  • Test Thoroughly: Test your app with multiple locales to catch any localization issues.
  • Consider RTL Languages: Support right-to-left languages by using the Directionality widget and checking TextDirection.ltr.
  • Handle Date, Time, and Currency Formatting: Use the intl package to format dates, times, and currencies according to the user’s locale.

Conclusion

Implementing internationalization and localization is essential for creating Flutter apps that appeal to a global audience. By following the steps outlined in this article, you can efficiently adapt your app to different languages and regions, providing a better user experience and increasing your app’s reach. The flutter_localizations and intl packages provide robust tools for handling various aspects of i18n and l10n, making it easier to create a truly global Flutter app.