1 Adding Dependencies
By using the Intl
package, we can not only easily implement internationalization but also separate string texts into individual files, facilitating collaboration between developers and translators. To use the Intl
package, we need to add two dependencies:
dependencies: # ...other dependencies intl: ^0.17.0 dev_dependencies: # ...other dev dependencies intl_generator: 0.2.1
The intl_generator
package primarily contains tools that extract strings to be internationalized from the code into separate arb files during the development phase, and generates corresponding Dart code from the arb files. The intl
package is mainly used to reference and load the Dart code generated by intl_generator
. Below, we will explain how to use it step by step.
2 Step One: Create Necessary Directories
First, create an l10n-arb
directory in the root of the project to store the arb files generated by the intl_generator
command. A simple arb file content is as follows:
{ "@@last_modified": "2018-12-10T15:46:20.897228", "@@locale": "zh_CH", "title": "Flutter应用", "@title": { "description": "Title for the Demo application", "type": "text", "placeholders": {} }}
From the "@@locale"
field, we can see that this arb corresponds to the translation in Simplified Chinese, and the title
field corresponds to the Chinese translation of our application title. The @title
field provides descriptive information about the title
.
Next, create a l10n
directory under the lib
directory, which will be used to store the Dart code files generated from the arb files.
3 Step Two: Implement the Localizations and Delegate Classes
Similar to the previous section's steps, we still need to implement the Localizations and Delegate classes. The difference is that we will now use some methods from the intl
package (some of which are dynamically generated).
Now, create a file named localization_intl.dart
in the lib/l10n
directory with the following content:
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'messages_all.dart'; //1 class DemoLocalizations { static Future<DemoLocalizations> load(Locale locale) { final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString(); final String localeName = Intl.canonicalizedLocale(name); //2 return initializeMessages(localeName).then((b) { Intl.defaultLocale = localeName; return DemoLocalizations(); }); } static DemoLocalizations of(BuildContext context) { return Localizations.of<DemoLocalizations>(context, DemoLocalizations); } String get title { return Intl.message( 'Flutter APP', name: 'title', desc: 'Title for the Demo application', ); } } // Locale delegate class class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> { const DemoLocalizationsDelegate(); // Check if a specific locale is supported @override bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode); // Flutter will call this method to load the corresponding Locale resource class @override Future<DemoLocalizations> load(Locale locale) { //3 return DemoLocalizations.load(locale); } // Whether to call load to reload Locale resources when the Localizations Widget rebuilds @override bool shouldReload(DemoLocalizationsDelegate old) => false; }
Note:
The
messages_all.dart
file (comment 1) is generated from the arb files using theintl_generator
tool, so this file does not exist before running the generation command for the first time.The
initializeMessages()
method (comment 2) is generated along with themessages_all.dart
file.At comment 3, unlike the previous section's example code, we directly call
DemoLocalizations.load()
here.
4 Step Three: Adding Internationalized Properties
Now we can add the properties or methods that need to be internationalized in the DemoLocalizations
class, such as the title
property in the example code above. At this point, we will utilize some methods provided by the Intl
library that help us easily implement grammatical features of different languages, such as pluralization. For example, if we have an email list page, we may need to display the number of unread emails at the top, and the text we show might differ depending on the count of unread emails:
Unread Emails Count | Message |
---|---|
0 | There are no emails left |
1 | There is 1 email left |
n (n > 1) | There are n emails left |
We can implement this using Intl.plural(...)
:
remainingEmailsMessage(int howMany) => Intl.plural(howMany, zero: 'There are no emails left', one: 'There is $howMany email left', other: 'There are $howMany emails left', name: "remainingEmailsMessage", args: [howMany], desc: "How many emails remain after archiving.", examples: const {'howMany': 42, 'userName': 'Fred'});
As seen, the Intl.plural
method allows us to output different messages based on the value of howMany
.
The Intl
package also has other methods, which readers can refer to in its documentation; this book will not elaborate further.
5 Step Four: Generating arb Files
Now we can use the tools in the intl_generator
package to extract strings from the code into an arb file. Run the following command:
flutter pub run intl_generator:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart
After running this command, the properties and strings we identified using the Intl
API will be extracted to the “l10n-arb/intl_messages.arb” file. Let’s take a look at its content:
{ "@@last_modified": "2018-12-10T17:37:28.505088", "title": "Flutter APP", "@title": { "description": "Title for the Demo application", "type": "text", "placeholders": {} }, "remainingEmailsMessage": "{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}", "@remainingEmailsMessage": { "description": "How many emails remain after archiving.", "type": "text", "placeholders": { "howMany": { "example": 42 } } }}
This is the default locale resource file. If we want to support Simplified Chinese now, we simply need to create an "intl_zh_CN.arb" file in the same directory and copy the contents of "intl_messages.arb" into "intl_zh_CN.arb." Next, we will translate the English text into Chinese. The translated content of "intl_zh_CN.arb" is as follows:
{ "@@last_modified": "2018-12-10T15:46:20.897228", "@@locale": "zh_CN", "title": "Flutter应用", "@title": { "description": "Title for the Demo application", "type": "text", "placeholders": {} }, "remainingEmailsMessage": "{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}", "@remainingEmailsMessage": { "description": "How many emails remain after archiving.", "type": "text", "placeholders": { "howMany": { "example": 42 } } }}
We must translate the title
and remainingEmailsMessage
fields; the description
provides explanations for these fields, usually for the translators' reference and not used in the code.
Two points to note:
If a specific arb file is missing a property, the application will load the corresponding property from the default arb file (
intl_messages.arb
). This is the fallback strategy ofIntl
.Every time we run the extraction command,
intl_messages.arb
will be regenerated based on the code, while other arb files will not be overwritten. Therefore, when adding new fields or methods, the other arb files are incrementally updated, so there’s no need to worry about overwriting.
The arb file format is standard, and you can find more details on its specifications. Typically, arb files are handed to translators, and after they complete the translations, we follow the steps below to generate the final Dart code based on the arb files.
6 Step Five: Generating Dart Code
The final step is to generate Dart files from the arb files:
flutter pub run intl_generator:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb
When run for the first time, this command will generate multiple files in the "lib/l10n" directory corresponding to various locales, and this code will be the Dart code we ultimately use.
7 Summary
At this point, we have introduced the process of internationalizing the app using the Intl
package. We can see that the first two steps are only necessary during the initial setup, while the main development work occurs in the third step. Since the last two steps need to be executed each time after completing the third step, we can place them in a shell script. After completing the third step or translating the arb files, we can simply run this script. We create a script named intl.sh
in the root directory with the following content:
flutter pub run intl_generator:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart flutter pub run intl_generator:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb
Then, grant it execution permissions:
chmod +x intl.sh
Execute intl.sh
:
./intl.sh