1 Introduction
If our application needs to support multiple languages, we need to "internationalize" it. This means that during development, we need to set "localized" values for each language environment that the application supports, such as text and layout. The Flutter SDK provides several components and classes to help us achieve internationalization. Below, we will introduce the steps for implementing internationalization in Flutter.
Next, we will illustrate how to support internationalization using an application that has MaterialApp
as its entry point.
Most applications use MaterialApp
as the entry point, but applications built using the lower-level WidgetsApp
class can also use the same classes and logic for internationalization. In fact, MaterialApp
is a wrapper around WidgetsApp
.
Note that "localized values and resources" refer to the different resources we prepare for different languages, generally consisting of text (strings). There may also be other resources that vary by language region, such as images of flags for the countries where the app is available; different locales will require different flag images.
2 Supporting Internationalization
By default, the components in the Flutter SDK only provide localization resources for American English (primarily text). To add support for other languages, the application must add a package dependency called flutter_localizations
, and some configuration is also required in MaterialApp
. To use the flutter_localizations
package, you first need to add the dependency to the pubspec.yaml
file:
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter
Next, download the flutter_localizations
library and specify the localizationsDelegates
and supportedLocales
in MaterialApp
:
import 'package:flutter_localizations/flutter_localizations.dart'; MaterialApp( localizationsDelegates: [ // Localization delegate GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: [ const Locale('en', 'US'), // American English const Locale('zh', 'CN'), // Simplified Chinese // Other locales ], // ... )
Unlike applications that use MaterialApp
as the entry point, applications based on WidgetsApp
do not require GlobalMaterialLocalizations.delegate
for internationalization.
The elements in the localizationsDelegates
list are factory classes that generate the set of localized values. GlobalMaterialLocalizations.delegate
provides localized strings and other values for the Material component library, enabling it to support multiple languages. GlobalWidgetsLocalizations.delegate
defines the default text direction of components, either left-to-right or right-to-left, because some languages, such as Arabic, are read from right to left.
SupportedLocales
also accepts an array of Locale
, indicating the list of languages supported by our application. In this example, our application supports only American English and Simplified Chinese.
3 Getting the Current Locale
The Locale
class is used to identify the user's language environment, including both language and country indicators, such as:
const Locale('zh', 'CN') // Simplified Chinese
We can always get the application's current locale using the following method:
Locale myLocale = Localizations.localeOf(context);
The Localizations
component is typically positioned at the top of the widget tree, above other business components. Its purpose is to define the locale and set the localized resources that depend on the subtree. If the system's language environment changes, the corresponding localized resources will be used.
4 Listening for System Language Changes
When we change the system language settings, the Localizations
component in the app will rebuild, and the Locale
obtained from Localizations.localeOf(context)
will be updated, resulting in the interface being rebuilt to reflect the language switch. However, this process is implicit, and we do not actively listen for system language changes. Sometimes, we may need to perform certain actions when the system language changes, such as setting a default language when the system switches to a language unsupported by our app. In such cases, we need to listen for locale change events.
We can listen for locale change events using the localeResolutionCallback
or localeListResolutionCallback
callbacks. Let’s first look at the signature of the localeResolutionCallback
:
Locale Function(Locale locale, Iterable<Locale> supportedLocales)
The locale
parameter represents the current system language setting. When the application starts or when the user dynamically changes the system language setting, this locale
reflects the current system locale. If the developer manually specifies the app’s locale, this locale
parameter will represent the specified locale, and the system locale will be ignored, for example:
MaterialApp( ... locale: const Locale('en', 'US'), // Manually specify locale ... )
In the above example, the application's locale is manually set to American English. After specifying this, even if the device's current language is Simplified Chinese, the locale in the app remains American English. If locale
is null
, it means Flutter was unable to retrieve the device's locale information, so we must always check for null before using the locale.
The supportedLocales
parameter represents the list of locales supported by the current application, which is registered by the developer in the MaterialApp
through the supportedLocales
property.
The return value is a Locale
, which is the final locale that the Flutter app will use. Typically, a default locale is returned when the language region is unsupported.
The only difference between localeListResolutionCallback
and localeResolutionCallback
is the type of the first parameter; the former accepts a list of locales, while the latter accepts a single locale.
Locale Function(List<Locale> locales, Iterable<Locale> supportedLocales)
In newer Android systems, users can set a list of languages. Consequently, applications that support multiple languages will receive this list. The usual handling method is to sequentially try to load the corresponding locale according to the list's order; if a language loads successfully, it will stop. Figure shows a screenshot of setting the language list in the Android system:
Setting Language List
In Flutter, it is preferable to use localeListResolutionCallback
. You need not worry about differences in Android versions; Flutter will automatically handle situations in lower versions, where the locale list will contain only one item.
5 Localization Component
The Localizations
component is used to load and retrieve localized values or resources for the application's current language. The application references these objects through Localizations.of(context, type)
. If the device's locale settings change, the Localizations
component will automatically load the locale values for the new region and then rebuild any components that depend on them. This occurs because Localizations
internally uses InheritedWidget
, as discussed earlier: when a child component's build
function references an InheritedWidget
, it creates an implicit dependency on it. Therefore, when the InheritedWidget
changes—specifically, when the locale setting of Localizations
changes—all dependent child components will be rebuilt.
The localized values are loaded from the LocalizationsDelegates
list of Localizations
. Each delegate must define an asynchronous load()
method to generate an object that encapsulates a series of localized values. Typically, these objects define a method for each localized value.
In large applications, different modules or packages may bundle their localized values together. This is why we use Localizations
to manage the object table. To use an object produced by one of the LocalizationsDelegate
's load
methods, you can specify a BuildContext
and the type of the object to find it. For example, the localized strings for the Material component library are defined by the MaterialLocalizations
class, and instances of this class are provided by the LocalizationDelegate
from MaterialApp
. They can be accessed as follows:
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
This specific Localizations.of()
expression is frequently used, so the MaterialLocalizations
class provides a convenient method:
static MaterialLocalizations of(BuildContext context) { return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations); } // Conveniently call the method tooltip: MaterialLocalizations.of(context).backButtonTooltip,
6 Using Pre-packaged LocalizationsDelegates
To keep it as small and simple as possible, the Flutter package only provides implementations of the MaterialLocalizations and WidgetsLocalizations interfaces for American English values. These implementation classes are called DefaultMaterialLocalizations
and DefaultWidgetsLocalizations
, respectively. The flutter_localizations
package includes multilingual implementations of the localization interfaces for GlobalMaterialLocalizations
and GlobalWidgetsLocalizations
. Internationalized applications must specify the localization delegate classes for these as described at the beginning of this section.
The aforementioned GlobalMaterialLocalizations
and GlobalWidgetsLocalizations
are only the localization implementations for the Material component library. If we want our own layouts to support multiple languages, we will need to implement our own Localizations
, which we will discuss in the next section.