Flutter (87): Implementing Localizations

Time: Column:Mobile & Frontend views:193

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.