i18n in Flutter: the best alternative
By: Patricia Juane
Tags: iOS, I18n, Flutter, Android, Dart

In this post, I will explain how I managed to make my Flutter application translatable with the I18n_extension package, which in my opinion is the best alternative for it.

Hello everyone! Today, I’m going to explain which library I chose to translate my Flutter app and why did I choose it.

First of all, I want to mention that the most popular alternatives that I found available in 2020 were the following ones:

i18n plugin : The Flutter i18n Plugin for IntelliJ IDEA created by Razvan Lung. A plugin that simplify i18n managment in your project. You can find it perfectly explained on this article: https://dev.to/pedromassango/how-to-i18n-in-flutter-46km

: The Flutter i18n Plugin for IntelliJ IDEA created by Razvan Lung. A plugin that simplify i18n managment in your project. You can find it perfectly explained on this article: https://dev.to/pedromassango/how-to-i18n-in-flutter-46km i18n : dart package that translates automatically your labels in YAML files to Dart classes. I didn’t find it really useful in my app, because you need to access manually to the labels you are interested in for each language in your code. You can find documentation here https://pub.flutter-io.cn/packages/i18n

: dart package that translates automatically your labels in YAML files to Dart classes. I didn’t find it really useful in my app, because you need to access manually to the labels you are interested in for each language in your code. You can find documentation here https://pub.flutter-io.cn/packages/i18n i18n_extension: personally, the option I found suits best for a Flutter app. It is about non-boilerplate translations. Besides, this package was mentioned by Google during the Dart 2.7 announcement.

So let me explain why and how I introduced the i18n_extension to my project.

First, you need to add the next dependencies in your pubspeck.yaml file:

flutter_localizations:

sdk: flutter

i18n_extension: any

The package “flutter_localizations” is also needed to track the device’s locale (the user’s preferred language).

After that, you need to go to your main.dart file and add the next:

import 'package:i18n_extension/i18n_widget.dart';

import 'package:flutter_localizations/flutter_localizations.dart';

...



@override

Widget build(BuildContext context) {

return MaterialApp(

localizationsDelegates: [

GlobalMaterialLocalizations.delegate,

GlobalWidgetsLocalizations.delegate,

GlobalCupertinoLocalizations.delegate,

],

supportedLocales: [

const Locale('en'), // you can Add Country code if you want

const Locale('es'),

const Locale('fr'),

const Locale('pt'),

],

debugShowCheckedModeBanner: false,

theme: ThemeData(

primarySwatch: Colors.blue,

),

home: I18n(

initialLocale: Locale("es"),

child: MyStatefulWidget()

),

);

}

}

Now it’s time to start creating our translation files. To do so, we start by creating a dart file to hold them. This file can have any name, but I suggest you give it the same name as your widget and change the termination to .i18n.dart.

For example, if your widget is in file my_widget.dart, the translations could be in file my_widget.i18n.dart.

You must then remove the default import:

import 'package:i18n_extension/i18n_widget.dart';

And instead import your own translation file:

import 'my_widget.i18n.dart';

Your translations file will be something like this:

import 'package:i18n_extension/i18n_extension.dart';



extension Localization on String {



static var _t = Translations("en_us") +

{

"en_us": "Hello, how are you?",

"pt_br": "Olá, como vai você?",

"es": "¿Hola! Cómo estás?",

"fr": "Salut, comment ca va?",

"de": "Hallo, wie geht es dir?",

};



String get i18n => localize(this, _t);

}

BUT: if your Dart version is smaller than:

sdk: ">=2.6.0 <3.0.0"

You might have issues creating your extension files!

You can, however, translate as many strings as you want, by simply adding more translation maps:

import 'package:i18n_extension/i18n_extension.dart';



extension Localization on String {



static var _t = Translations("en_us") +

{

"en_us": "Hello, how are you?",

"pt_br": "Olá, como vai você?",

} +

{

"en_us": "Hi",

"pt_br": "Olá",

} +

{

"en_us": "Goodbye",

"pt_br": "Adeus",

};



String get i18n => localize(this, _t);

}

String themselves are the translation keys

The locale you pass in the Translations("en_us") constructor is called the default locale. All translatable strings in the widget file should be in the language of that locale.

The strings themselves are used as keys when searching for translations to the other locales. For example, in the Text below, "Hello, how are you?" is both the translation to English and the key to use when searching for its other translations:

Text("Hello, how are you?".i18n)

Managing keys

Other translation packages ask you to define key identifiers for each translation, and use those. For example, the above text key could be helloHowAreYou or simply greetings . And then you could access it like this: MyLocalizations.of(context).greetings .

However, having to define identifiers is not only a boring task, but it also forces you to navigate to the translation if you need to remember the exact text of the widget.

With i18n_extension you can simply type the text you want and that's it.

If some string is already translated and you later change it in the widget file,

this will break the link between the key and the translation map. However, the package is smart enough to let you know when that happens, so it's easy to fix.

Last points

Finally, I think is important to mention that you can also provide the translations by locale/language, instead of by key. You can do that by using the Translations.byLocale() constructor.

static var _t = Translations.byLocale("en_us") +

{

"en_us": {

"Hi.": "Hi.",

"Goodbye.": "Goodbye.",

},

"es_es": {

"Hi.": "Hola.",

"Goodbye.": "Adiós.",

}

}

You can also combine different translations and use translation modifiers (for example, you can change a sentence and have different translations that depend on a number quantity. Instead of .i18n you can use .plural() and pass it a number. For example: