Internationalization in Flutter 1.22+

This is an updated version of a previous post, that described this feature during its beta period. This post is an updated version of that post, compatible with Flutter 1.22+.

Earlier I wrote about Flutter's new internationalization support, then still in beta.
Now in Flutter 1.22 this feature went stable and it's even better than it was during the beta!

In short, the new internationalization support works like this:

  1. Messages that should be localized are defined in an ARB template file.
  2. Translations are defined in an ARB file per language that should be supported.
  3. Flutter's internationalization support generates Dart code for localized messages automatically.

Message lookup uses a class generated by Flutter, which is automatically aware of the current locale via the BuildContext:

Translations.of(context).myMessage

Advantages

Compared to some other packages available on pub.dev this has a few advantages in my opinion:

In the following section I'll describe how to adopt this workflow for an existing project.

Localize an existing project

To get started we need a few new dependencies.

Add gen_l10n dependencies

The code generated by gen_l10n depends on the intl and flutter_localizations package, so add these to your dependencies in pubspec.yaml:

dependencies:
# ... other dependencies ...
flutter_localizations:
sdk: flutter
intl: ^0.16.0

We also need to enable code generation support in pubspec.yaml:

flutter:
# Adds code generation (synthetic package) support
generate: true

Configure Flutter localization support

In the root directory of your Flutter application, create a new l10n.yaml file that contains the following:

arb-dir: lib/l10n
template-arb-file: messages_en.arb
output-localization-file: translations.dart
output-class: Translations

Define translations

Ok, so now we have all required setup, let's start by defining some localized messages.

Create a directory in your project to store your localized messages. This can be any directory you like, I'm using lib/i18n:

mkdir lib/l10n

In this directory create a file called messages_en.arb. The name messages here can be anything you want, but the last part of the file name should refer to the locale of the messages inside.

As you can see this file is an ARB file, an Application Resource Bundle. It's a localization resource format designed by Google. You can read the specification here.

Now to define localised messages we'll start by adding the messages we want to localize to the messages_en.arb file we created earlier. I won't dive into everything the ARB format supports, but it's a pretty versatile format so be sure to read the specification I linked above!

{
"greeting": "Hello, world!",
"@greeting": {
"type": "text",
"description": "A friendly greeting."
},

"newMessages": "{newMessages, plural, =0 {No new messages} =1 {One new message} other {{newMessages} new messages}}",
"@newMessages": {
"type": "text",
"description": "Number of new messages in inbox.",
"placeholders": {
"newMessages": {}
}
}
}

As you can see for every message we want to localize we add both a key and a meta key to the file. The key is how you will refer to the localized message from your code later and the meta key is that same key prefixed with @. These meta keys only need to be added in one of the locales; the locale you will use as a template.

In the example above we have both a simple message greeting and a more complex, pluralized, message that changes depending on a piece of context'; the number of new messages in this case.

Now for every other language we want to support we need to create matching files with the desired locale in the lib/i18n directory we created earlier. For example I'm going to translate these messages in Dutch so I'll add a file named messages_nl_NL.arb with the following contents:

{
"greeting": "Hallo, wereld!",
"newMessages": "{newMessages, plural, =0 {Geen nieuwe berichten} =1 {Één nieuw bericht} other {{newMessages} nieuwe berichten}}"
}

As you can see in this case the file only contains the keys themselves and no metadata.

Generating localization classes

With all this in place Flutter will automatically generate code to use the localized messages in your app. All that is left is to include this generated class in your code and use it!

Make sure to trigger a build using flutter run or flutter build so the code generation is triggered. Flutter generates this code in .dart_tool/flutter_gen/gen_l10n. This code should not be checked into version control.

Make localized messages available via BuildContext

To make Flutter aware of our localized messages we need to configure the localizationsDelegates and supportedLocales properties on the MaterialApp widget.

In your lib/main.dart file import the generated translations.dart file:

import 'package:flutter_gen/gen_l10n/translations.dart';

And configure the MaterialApp widget to pick up your translations:

MaterialApp(
// ... other properties ...
localizationsDelegates: Translations.localizationsDelegates,
supportedLocales: Translations.supportedLocales
)

Note that for iOS you should also add all supported locales to the info.plist file. See the Flutter docs for details.

Use localized messages

With all setup out of the way, we can now actually use our localized messages. From any widget in the widget tree below MaterialApp you can now access your localized messages.

Start by importing the generated translations.dart file:

import 'package:flutter_gen/gen_l10n/translations.dart';

And use the messages defined in the ARB files:

class MyLocalizedWidget extends StatelessWidget {

Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text(Translations.of(context).greeting), // "greeting"
Text(Translations.of(context).newMessages(10)) // "newMessages"
],
);
}
}

The compiler will check you're using your messages correctly and the IDE will help you with great autocompletion:

autocomplete.

For more details check out the draft docs.

Happy i18n-ing! 🎉