阅读(3987) (0)

Flutter实战 让App支持多语言

2021-03-09 11:58:21 更新

如果我们的应用要支持多种语言,那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值,如文本和布局。Flutter SDK 已经提供了一些组件和类来帮助我们实现国际化,下面我们来介绍一下 Flutter 中实现国际化的步骤。

接下来我们以MaterialApp类为入口的应用来说明如何支持国际化。

大多数应用程序都是通过MaterialApp为入口,但根据低级别的WidgetsApp类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。MaterialApp实际上也是WidgetsApp的一个包装。

注意,”本地化的值和资源“是指我们针对不同语言准备的不同资源,这些资源一般是指文案(字符串),当然也会有一些其他的资源会根据不同语言地区而不同,比如我们需要显示一个APP上架地的国旗图片,那么不同 Locale 区域我们就需要提供不同的的国旗图片。

#支持国际化

默认情况下,Flutter SDK 中的组件仅提供美国英语本地化资源(主要是文本)。要添加对其他语言的支持,应用程序须添加一个名为“flutter_localizations”的包依赖,然后还需要在MaterialApp中进行一些配置。 要使用flutter_localizations包,首先需要添加依赖到pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

接下来,下载flutter_localizations库,然后指定MaterialApplocalizationsDelegatessupportedLocales

import 'package:flutter_localizations/flutter_localizations.dart';


new MaterialApp(
 localizationsDelegates: [
   // 本地化的代理类
   GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
 ],
 supportedLocales: [
    const Locale('en', 'US'), // 美国英语
    const Locale('zh', 'CN'), // 中文简体
    //其它Locales
  ],
  // ...
)

MaterialApp类为入口的应用不同, 对基于WidgetsApp类为入口的应用程序进行国际化时,不需要GlobalMaterialLocalizations.delegate

localizationsDelegates列表中的元素是生成本地化值集合的工厂。GlobalMaterialLocalizations.delegate 为 Material 组件库提供的本地化的字符串和其他值,它可以使 Material 组件支持多语言。 GlobalWidgetsLocalizations.delegate定义组件默认的文本方向,从左到右或从右到左,这是因为有些语言的阅读习惯并不是从左到右,比如如阿拉伯语就是从右向左的。

supportedLocales也接收一个 Locale 数组,表示我们的应用支持的语言列表,在本例中我们的应用只支持美国英语和中文简体两种语言。

#获取当前区域Locale

Locale (opens new window)类是用来标识用户的语言环境的,它包括语言和国家两个标志如:

const Locale('zh', 'CN') // 中文简体

我们始终可以通过以下方式来获取应用的当前区域 Locale:

Locale myLocale = Localizations.localeOf(context);

Localizations (opens new window)组件一般位于 widget 树中其它业务组件的顶部,它的作用是定义区域 Locale 以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化,WidgetsApp (opens new window)将创建一个新的 Localizations 组件并重建它,这样子树中通过Localizations.localeOf(context) 获取的 Locale 就会更新。

#监听系统语言切换

当我们更改系统语言设置时,APP 中的 Localizations 组件会重新构建,Localizations.localeOf(context) 获取的 Locale 就会更新,最终界面会重新 build 达到切换语言的效果。但是这个过程是隐式完成的,我们并没有主动去监听系统语言切换,但是有时我们需要在系统语言发生改变时做一些事,比如系统语言切换为一种我们 APP 不支持的语言时,我们需要设置一个默认的语言,这时我们就需要监听 locale 改变事件。

我们可以通过localeResolutionCallbacklocaleListResolutionCallback回调来监听 locale 改变的事件,我们先看看localeResolutionCallback的回调函数签名:

Locale Function(Locale locale, Iterable<Locale> supportedLocales)

  • 参数locale的值为当前的当前的系统语言设置,当应用启动时或用户动态改变系统语言设置时此 locale 即为系统的当前 locale。当开发者手动指定 APP 的 locale 时,那么此 locale 参数代表开发者指定的 locale,此时将忽略系统 locale 如:

  MaterialApp(
   ...
   locale: const Locale('en', 'US'), //手动指定locale
   ...
  )

上面的例子中手动指定了应用 locale 为美国英语,指定后即使设备当前语言是中文简体,应用中的 locale 也依然是美国英语。如果localenull,则表示 Flutter 未能获取到设备的 Locale 信息,所以我们在使用locale之前一定要先判空。

  • supportedLocales 为当前应用支持的 locale 列表,是开发者在 MaterialApp 中通过supportedLocales属性注册的。

  • 返回值是一个Locale,此Locale为 Flutter APP 最终使用的Locale。通常在不支持的语言区域时返回一个默认的Locale

localeListResolutionCallbacklocaleResolutionCallback唯一的不同就在第一个参数类型,前者接收的是一个Locale列表,而后者接收的是单个Locale

Locale Function(List<Locale> locales, Iterable<Locale> supportedLocales)

在较新的 Android 系统中,用户可以设置一个语言列表,这样一来,支持多语言的应用就会得到这个列表,应用通常的处理方式就是按照列表的顺序依次尝试加载相应的 Locale,如果某一种语言加载成功则会停止。图13-1是 Android 系统中设置语言列表的截图:

设置语言列表

在 Flutter 中,应该优先使用localeListResolutionCallback,当然你不必担心 Android 系统的差异性,如果在低版本的 Android 系统中,Flutter 会自动处理这种情况,这时 Locale 列表只会包含一项。

#Localization 组件

Localizations 组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过Localizations.of(context,type) (opens new window)来引用这些对象。 如果设备的 Locale 区域设置发生更改,则 Localizations 组件会自动加载新区域的 Locale 值,然后重新 build 使用(依赖)了它们的组件,之所以会这样,是因为Localizations内部使用了InheritedWidget (opens new window),我们在介绍该组件时讲过:当子组件的build函数引用了InheritedWidget时,会创建对InheritedWidget的隐式依赖关系。因此,当InheritedWidget发生更改时,即Localizations的 Locale 设置发生更改时,将重建所有依赖它的子组件。

本地化值由LocalizationsLocalizationsDelegates (opens new window)列表加载 。 每个委托必须定义一个异步 load() 方法,以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。

在大型应用程序中,不同模块或 Package 可能会与自己的本地化值捆绑在一起。 这就是为什么要用Localizations 管理对象表的原因。 要使用由LocalizationsDelegateload方法之一产生的对象,可以指定一个BuildContext和对象的类型来找到它。例如,Material 组件库的本地化字符串由MaterialLocalizations (opens new window)类定义,此类的实例由 MaterialApp (opens new window)类提供的LocalizationDelegate创建, 它们可以如下方式获取到:

Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);

这个特殊的Localizations.of()表达式会经常使用,所以 MaterialLocalizations 类提供了一个便捷方法:

static MaterialLocalizations of(BuildContext context) {
  return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}


// 可以直接调用便捷方法
tooltip: MaterialLocalizations.of(context).backButtonTooltip,

#使用打包好的LocalizationsDelegates

为了尽可能小而且简单,flutter 软件包中仅提供美国英语值的MaterialLocalizationsWidgetsLocalizations接口的实现。 这些实现类分别称为DefaultMaterialLocalizationsDefaultWidgetsLocalizations。flutter_localizations 包包含GlobalMaterialLocalizationsGlobalWidgetsLocalizations的本地化接口的多语言实现, 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化 Delegate。

上述的GlobalMaterialLocalizationsGlobalWidgetsLocalizations只是 Material 组件库的本地化实现,如果我们要让自己的布局支持多语言,那么就需要实现在即的Localizations,我们将在下一节介绍其具体的实现方式。