# 使用Intl包
使用[Intl](https://pub.dartlang.org/packages/intl)包我們不僅可以非常輕松的實現國際化,而且也可以將字符串文本分離成單獨的文件,方便開發人員和翻譯人員分工協作。為了使用[Intl](https://pub.dartlang.org/packages/intl)包我們需要添加兩個依賴:
```
dependencies:
#...省略無關項
intl: ^0.15.7
dev_dependencies:
#...省略無關項
intl_translation: ^0.17.2
```
[intl\_translation](https://pub.dartlang.org/packages/intl_translation) 包主要包含了一些工具,它在開發階段主要主要的作用是從代碼中提取要國際化的字符串到單獨的arb文件和根據arb文件生成對應語言的dart代碼,而intl包主要是引用和加載intl\_translation生成后的dart代碼。下面我們將一步步來說明如何使用:
### 第一步:創建必要目錄
首先,在項目根目錄下創建一個i10n-arb目錄,該目錄保存我們接下來通過intl\_translation命令生成的arb文件。一個簡單的arb文件內容如下:
```
{
"@@last_modified": "2018-12-10T15:46:20.897228",
"@@locale":"zh_CH",
"title": "Flutter應用",
"@title": {
"description": "Title for the Demo application",
"type": "text",
"placeholders": {}
}
}
```
我們根據"@@locale"字段可以看出這個arb對應的是中文簡體的翻譯,里面的`title`字段對應的正是我們應用標題的中文簡體翻譯。`@title`字段是對`title`的一些描述信息。
接下來,我們在lib目錄下創建一個i10n的目錄,該目錄用于保存從arb文件生成的dart代碼文件。
### 第二步:實現Localizations和Delegate類
和上一節中的步驟類似,我們仍然要實現Localizations和Delegate類,不同的是,現在我們在實現時要使用intl包的一些方法(有些是動態生成的)。
下面我們在`lib/i10n`目錄下新建一個“localization\_intl.dart”的文件,文件內容如下:
```
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart'; //1
class DemoLocalizations {
static Future<DemoLocalizations> load(Locale locale) {
final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
//2
return initializeMessages(localeName).then((b) {
Intl.defaultLocale = localeName;
return new DemoLocalizations();
});
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
}
String get title {
return Intl.message(
'Flutter APP',
name: 'title',
desc: 'Title for the Demo application',
);
}
}
//Locale代理類
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
//是否支持某個Local
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
// Flutter會調用此類加載相應的Locale資源類
@override
Future<DemoLocalizations> load(Locale locale) {
//3
return DemoLocalizations.load(locale);
}
// 當Localizations Widget重新build時,是否調用load重新加載Locale資源.
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
}
```
注意:
- 注釋1的"messages\_all.dart"文件是通過[intl\_translation](https://pub.dartlang.org/packages/intl_translation)工具從arb文件生成的代碼,所以在第一次運行生成命令之前,此文件不存在。注釋2處的`initializeMessages()`方法和"messages\_all.dart"文件一樣,是同時生成的。
- 注釋3處和上一節示例代碼不同,這里我們直接調用`DemoLocalizations.load()`即可。
### 第三部:添加需要國際化的屬性
現在我們可以在DemoLocalizations類中添加需要國際化的屬性或方法,如上面示例代碼中的`title`屬性,這時我們就要用到Intl庫提供的一些方法,這些方法可以幫我們輕松實現不同語言的一些語法特性,如復數語境,舉個例子,比如我們有一個電子郵件列表頁,我們需要在頂部顯示未讀郵件的數量,在未讀數量不同事,我們展示的文本可能會不同:
未讀郵件數提示語0There are no emails left1There is 1 email leftn(n>1)There are n emails left我們可以通過`Intl.plural(...)`來實現:
```
remainingEmailsMessage(int howMany) => Intl.plural(howMany,
zero: 'There are no emails left',
one: 'There is $howMany email left',
other: 'There are $howMany emails left',
name: "remainingEmailsMessage",
args: [howMany],
desc: "How many emails remain after archiving.",
examples: const {'howMany': 42, 'userName': 'Fred'});
```
可以看到通過`Intl.plural`方法可以在`howMany`值不同時輸出不同的提示信息。
[Intl](https://pub.dartlang.org/packages/intl)包還有一些其他的方法,讀者可以自行查看其文檔,本書不在贅述。
### 第四步:生成arb文件
現在我們可以通[intl\_translation](https://pub.dartlang.org/packages/intl_translation)包的工具來提取代碼中的字符串到一個arb文件,運行如下命名:
```
flutter pub pub run intl_translation:extract_to_arb --output-dir=i10n-arb \ lib/i10n/localization_intl.dart
```
運行此命令后,會將我們之前通過Intl API標識的屬性和字符串提取到“i10n-arb/intl\_messages.arb”文件中,我們看看其內容:
```
{
"@@last_modified": "2018-12-10T17:37:28.505088",
"title": "Flutter APP",
"@title": {
"description": "Title for the Demo application",
"type": "text",
"placeholders": {}
},
"remainingEmailsMessage": "{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}",
"@remainingEmailsMessage": {
"description": "How many emails remain after archiving.",
"type": "text",
"placeholders": {
"howMany": {
"example": 42
}
}
}
}
```
這個是默認的Locale資源文件,如果我們現在要支持中文簡體,只需要在該文件同級目錄創建一個"intl\_zh\_CN.arb"文件,然后將"intl\_messages.arb"的內容拷貝到"intl\_zh\_CN.arb"文件,接下來將英文翻譯為中文即可,翻譯后的"intl\_zh\_CN.arb"文件內容如下:
```
{
"@@last_modified": "2018-12-10T15:46:20.897228",
"@@locale":"zh_CH",
"title": "Flutter應用",
"@title": {
"description": "Title for the Demo application",
"type": "text",
"placeholders": {}
},
"remainingEmailsMessage": "{howMany,plural, =0{沒有未讀郵件}=1{有{howMany}封未讀郵件}other{有{howMany}封未讀郵件}}",
"@remainingEmailsMessage": {
"description": "How many emails remain after archiving.",
"type": "text",
"placeholders": {
"howMany": {
"example": 42
}
}
}
}
```
我們必須要翻譯`title`和`remainingEmailsMessage`字段,`description`是該字段的說明,通常給翻譯人員看,代碼中不會用到。
有兩點需要說明:
1. 如果某個特定的arb中缺失某個屬性,那么應用將會加載默認的arb文件(intl\_messages.arb)中的相應屬性,這是Intl的托底策略。
2. 每次運行提取命令時,intl\_messages.arb都會根據代碼重新生成,但其他arb文件不會,所以當要添加新的字段或方法時,其他arb文件是增量的,不用擔心會覆蓋。
3. arb文件是標準的,其格式規范可以自行了解。通常會將arb文件交給翻譯人員,當他們完成翻譯后,我們再通過下面的步驟根據arb文件生成最終的dart代碼。
### 第五步:生成dart代碼
最后一步就是根據arb生成dart文件:
```
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/i10n --no-use-deferred-loading lib/i10n/localization_intl.dart i10n-arb/intl_*.arb
```
這句命令在首次運行時會在"lib/i10n"目錄下生成多個文件,對應多種Locale,這些代碼便是最終要使用的dart代碼。
### 總結
至此,我們將使用[Intl](https://pub.dartlang.org/packages/intl)包對APP進行國際化的流程介紹完了,我們可以發現,其中第一步和第二步只在第一次需要,而我們開發時的主要的工作都是在第三步。由于最后兩步在第三步完成后每次也都需要,所以我們可以將最后兩步放在一個shell腳本里,當我們完成第三步或完成arb文件翻譯后只需要分別執行該腳本即可。我們在根目錄下創建一個intl.sh的腳本,內容為:
```
flutter pub pub run intl_translation:extract_to_arb --output-dir=i10n-arb lib/i10n/localization_intl.dart
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/i10n --no-use-deferred-loading lib/i10n/localization_intl.dart i10n-arb/intl_*.arb
```
然后授予執行權限:
```
chmod +x intl.sh
```
執行intl.sh
```
./intl.sh
```
- 緣起
- 起步
- 移動開發技術簡介
- 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從啟動到顯示