In the previous section, we discussed how the Material component library supports internationalization. In this section, we will explore how to support multiple languages in our own UI. As mentioned earlier, we need to implement two classes: a Delegate class and a Localizations class. Below, we illustrate this with an example.
1 Implementing the Localizations Class
We know that the Localizations class mainly provides localized values, such as text:
// Locale resource class class DemoLocalizations { DemoLocalizations(this.isZh); // Is it Chinese? bool isZh = false; // For convenience, we define a static method static DemoLocalizations of(BuildContext context) { return Localizations.of<DemoLocalizations>(context, DemoLocalizations); } // Locale-related values; title is the app title String get title { return isZh ? "Flutter应用" : "Flutter APP"; } // ... other values }
In DemoLocalizations
, different text will be returned based on the current language, such as title
. We can define all the texts that need to support multiple languages in this class. An instance of DemoLocalizations
will be created in the load
method of the Delegate class.
2 Implementing the Delegate Class
The Delegate class is responsible for loading new locale resources when the locale changes, so it includes a load
method. The Delegate class needs to extend LocalizationsDelegate
and implement the corresponding interfaces, as shown below:
// 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 to load the corresponding locale resource class @override Future<DemoLocalizations> load(Locale locale) { print("$locale"); return SynchronousFuture<DemoLocalizations>( DemoLocalizations(locale.languageCode == "zh") ); } @override bool shouldReload(DemoLocalizationsDelegate old) => false; }
The return value of shouldReload
determines whether the load
method will be called to reload the locale resources when the Localizations
component rebuilds. Generally, locale resources should only be loaded once when the locale switches, so returning false
is appropriate. Some might worry that returning false
would prevent the load
method from being called if the user changes the system language after the app starts, thus not loading the locale resources. In fact, Flutter will call the load
method every time the locale changes, regardless of whether shouldReload
returns true or false.
3 Adding Multilingual Support
As described in the previous section, we first need to register the DemoLocalizationsDelegate
class, and then dynamically obtain the current locale text using DemoLocalizations.of(context)
.
This can be accomplished by simply adding our Delegate instance to the localizationsDelegates
list in MaterialApp
or WidgetsApp
:
localizationsDelegates: [ // Localization delegate classes GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, // Register our delegate DemoLocalizationsDelegate() ],
Next, we can use the locale value in the Widget:
return Scaffold( appBar: AppBar( // Use locale title title: Text(DemoLocalizations.of(context).title), ), ... // Other unrelated code );
Thus, when switching between American English and Simplified Chinese system languages, the app's title will display as “Flutter APP” and “Flutter应用,” respectively.
4 Summary
In this section, we explained the basic process and principles of internationalizing a Flutter application through a simple example. However, the example has a significant drawback: we need to manually check the current language locale to return the appropriate text in the DemoLocalizations
class. Imagine if we need to support not two languages but eight or even twenty; determining the correct locale for each text attribute would become quite complex. Moreover, translators are typically not developers. Is it possible, like the i18n or l10n standards, to save translations in a separate ARB file for translators to work on, which developers can then convert into code after translation? The answer is yes! We will discuss how to achieve this using the Dart intl package in the next section.