# 讓App支持多語言
如果我們的應用要支持多種語言,那么我們需要“國際化”它。這意味著我們在開發時需要為應用程序支持的每種語言環境設置“本地化”的一些值,如文本和布局。Flutter提供一些widget和類以幫助實現國際化,而Flutter的庫本身也是國際化的。
接下來我們以MaterialApp類為入口的應用來說明如何支持國際化。
> 大多數應用程序都是通過MaterialApp為入口,但根據低級別的WidgetsApp類為入口編寫的應用程序也可以使用相同的類和邏輯進行國際化。MaterialApp實際上也是WidgetsApp的一個包裝。
## 支持國際化
默認情況下,Flutter僅提供美國英語本地化。要添加對其他語言的支持,應用程序必須指定其他MaterialApp屬性,并包含一個名為“flutter\_localizations”的包。 截至2018年5月,該包支持24種語言。要使用flutter\_localizations包,首先需要添加依賴到`pubspec.yaml`文件中:
```
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
```
接下來,下載flutter\_localizations庫,然后指定MaterialApp的`localizationsDelegates`和`supportedLocales`:
```
import 'package:flutter_localizations/flutter_localizations.dart';
new MaterialApp(
localizationsDelegates: [
// 本地化的代理類
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', 'US'), // 美國英語
const Locale('zh', 'CN'), // 中文簡體
//其它Locales
],
// ...
)
```
基于WidgetsApp的應用程序類似,只是不需要`GlobalMaterialLocalizations.delegate`。
`localizationsDelegates`列表中的元素是生成本地化值集合的工廠。`GlobalMaterialLocalizations.delegate` 為Material 組件庫提供的本地化的字符串和其他值,它可以使Material Widget支持多語言。 `GlobalWidgetsLocalizations.delegate`定義widget默認的文本方向,從左到右或從右到左,這是因為有些語言的閱讀習慣并不是從左到右,比如如阿拉伯語就是從右向左的。
## 獲取當前區域Locale
[`Locale`](https://docs.flutter.io/flutter/dart-ui/Locale-class.html)類是用來標識用戶的語言環境的,它包括語言和國家兩個標志如:
```
const Locale('zh', 'CN') // 中文簡體
```
我們始終可以通過以下方式來獲取應用的當前區域Locale:
```
Locale myLocale = Localizations.localeOf(context);
```
[`Localizations`](https://docs.flutter.io/flutter/widgets/Localizations-class.html) Widget一般位于Widget樹中其它業務組件的頂部,它的作用是定義區域Locale以及設置子樹依賴的本地化資源。 如果系統的語言環境發生變化,[WidgetsApp](https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html)將創建一個新的Localizations Widget并重建它,這樣子樹中通過`Localizations.localeOf(context)` 獲取的Locale就會更新。
## 監聽系統語言切換
當我們更改系統語言設置時,APP中的Localizations widget會重新構建,Localizations.localeOf(context)` 獲取的Locale就會更新,最終界面會重新build達到切換語言的效果。但是這個過程是隱式完成的,我們并沒有主動去監聽系統語言切換,但是有時我們需要在系統語言發生改變時做一些事,這時我們就需要監聽locale改變事件。
我們可以通過localeResolutionCallback或localeListResolutionCallback回調來監聽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也依然是美國英語。
如果`locale`為null,則表示Flutter未能獲取到設備的Locale信息,所以我們在使用`locale`之前一定要先判空。
- `supportedLocales` 為當前應用支持的locale列表,是開發者在MaterialApp中通過`supportedLocales`屬性注冊的。
- 返回值是一個Locale,此Locale為Flutter APP最終的Locale。通常在不支持的語言區域時返回一個默認的Locale。
localeListResolutionCallback和localeResolutionCallback唯一的不同就在第一個參數類型,前者接收的是一個Locale列表,而后者接收的是單個Locale。
```
Locale Function(List<Locale> locales, Iterable<Locale> supportedLocales)
```
在新版的Android系統中,用戶可以設置一個語言列表,這樣一來,支持多語言的應用就會得到這個列表,應用通常的處理方式就是按照列表的順序依次嘗試加載相應的Locale,如果某一種語言加載成功則會停止。下面是Android中設置語言列表的截圖:

在Flutter中,應該優先使用localeListResolutionCallback,當然你不必擔心Android系統的差異性,如果在低版本的Android系統中,Flutter會自動處理這種情況,這時Locale列表只會包含一項。
## Localization widget
Localizations widget用于加載和查找包含本地化值的對象。應用程序通過[`Localizations.of(context,type)`](https://docs.flutter.io/flutter/widgets/Localizations/of.html)來引用這些對象。 如果設備的Locale區域設置發生更改,則Localizations widget會自動加載新區域的Locale值,然后重新構建使用了它們的widget。 發生這種情況是因為Localizations內部使用了[InheritedWidget](https://book.flutterchina.club/chapter7/inherited_widget.html) ,當build函數引用了InheritedWidget時,會創建對InheritedWidget的隱式依賴關系。當InheritedWidget發生更改時,即Localizations widget的Locale設置發生更改時,將重建其依賴的上下文。
本地化值由Localizations的 [LocalizationsDelegates](https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html) 列表加載 。 **每個委托必須定義一個異步load() 方法**,以生成封裝了一系列本地化值的對象。通常這些對象為每個本地化值定義一個方法。
在大型應用程序中,不同模塊或Package可能會與自己的本地化值捆綁在一起。 這就是為什么要用Localizations 管理對象表的原因。 要檢索由LocalizationsDelegate `load`方法之一產生的對象,可以指定一個BuildContext和對象的類型。例如,Material 組件庫的本地化字符串由[MaterialLocalizations](https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html)類定義, 此類的實例由[MaterialApp](https://docs.flutter.io/flutter/material/MaterialApp-class.html)類提供的LocalizationDelegate創建, 它們可以通過`Localizations.of`被獲取到:
```
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軟件包中僅提供美國英語值的MaterialLocalizations和WidgetsLocalizations接口的實現。 這些實現類分別稱為DefaultMaterialLocalizations和DefaultWidgetsLocalizations。flutter\_localizations Package包含GlobalMaterialLocalizations和GlobalWidgetsLocalizations的本地化接口的多語言實現, 國際化的應用程序必須按照本節開頭說明的那樣為這些類指定本地化Delegate。
上述的GlobalMaterialLocalizations和GlobalWidgetsLocalizations只是Material組件庫的本地化實現,如果我們要讓自己的布局支持多語言,那么就需要實現在即的Localizations,我們將在下一節介紹其具體的實現方式。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示