目錄
1. 國際化Material組件庫
2. 國際化開發(fā)人員的UI(Localizations)
國際化:支持多種語言。為應(yīng)用程序支持的每種語言設(shè)置本地化值,如文本(語言差異)、布局(閱讀方向差異)、圖片(國旗)。
默認(rèn)情況下,flutterSDK(為了盡可能小而且簡單)中的組件僅提供美國英語值的本地化資源(文本)。為Material組件庫添加其他語言的支持,須添加一個名為“flutter_localizations”的包依賴,然后在MaterialApp中進(jìn)行一些配置。
/*
flutter_localizations 包(Material組件庫的本地化實(shí)現(xiàn))包含:
GlobalMaterialLocalizations和GlobalWidgetsLocalizations的本地化接口的多語言實(shí)現(xiàn)。
*/
Material組件庫和開發(fā)人員的UI都需要進(jìn)行國際化:
1. Material組件庫
比如:日歷組件默認(rèn)在任何環(huán)境下都會以英文顯示,所以需要國際化。
需要依賴flutter_localizations包。
2. 開發(fā)人員的UI。
需要實(shí)現(xiàn)Localizations。
/*
iOS需要在info.plist中添加Localizations項(xiàng)(在其中添加語言,默認(rèn)有一個英文)
*/
簡介
- 獲取當(dāng)前區(qū)域Locale
Locale類(包括語言和國家兩個標(biāo)志)用來標(biāo)識用戶的語言環(huán)境。
Locale('zh', 'CN') // 中文簡體
獲取應(yīng)用的當(dāng)前區(qū)域Locale
// Localizations 組件一般位于widget樹中其它業(yè)務(wù)組件的頂部,它的作用是定義區(qū)域Locale以及設(shè)置子樹依賴的本地化資源。
// 如果系統(tǒng)的語言環(huán)境發(fā)生變化,WidgetsApp將創(chuàng)建一個新的Localizations 組件并重建它,這樣子樹中通過Localizations.localeOf(context) 獲取的Locale就會更新。
Locale myLocale = Localizations.localeOf(context);
// 返回:zh_CN
myLocale.toString()
- 監(jiān)聽系統(tǒng)語言切換
當(dāng)更改系統(tǒng)語言設(shè)置時,APP中的Localizations組件會重新構(gòu)建,Localizations.localeOf(context) 獲取的Locale就會更新,最終界面會重新build達(dá)到切換語言的效果。
因?yàn)長ocalizations組件內(nèi)部使用了InheritedWidget ,當(dāng)子組件的build函數(shù)引用了InheritedWidget時會創(chuàng)建對InheritedWidget的隱式依賴關(guān)系,因此當(dāng)InheritedWidget發(fā)生更改時(即Localizations的Locale設(shè)置發(fā)生更改時)將重建所有依賴它的子組件。
這個過程是隱式完成的,并沒有主動去監(jiān)聽系統(tǒng)語言切換。
但是有時需要在系統(tǒng)語言發(fā)生改變時做一些事,比如系統(tǒng)語言切換為一種APP不支持的語言時,需要設(shè)置一個默認(rèn)的語言,這時就需要 監(jiān)聽locale改變事件。
可以通過MaterialApp的以下2種方法來監(jiān)聽locale改變的事件
方法1. localeResolutionCallback回調(diào)函數(shù):
Locale Function(Locale locale, Iterable<Locale> supportedLocales)
方法2. localeListResolutionCallback(推薦)回調(diào)函數(shù):
// 和localeResolutionCallback唯一的不同就在第一個參數(shù)類型
Locale Function(List<Locale> locales, Iterable<Locale> supportedLocales)
說明:
1. locale:當(dāng)前手機(jī)系統(tǒng)設(shè)置的語言區(qū)域。
當(dāng)應(yīng)用啟動時或用戶動態(tài)改變系統(tǒng)語言設(shè)置時此locale即為系統(tǒng)的當(dāng)前l(fā)ocale。
如果locale為null,則表示Flutter未能獲取到設(shè)備的Locale信息,所以在使用locale之前一定要先判空。
注意:當(dāng)開發(fā)者手動指定APP的locale時(MaterialApp的locale屬性),那么此locale參數(shù)代表開發(fā)者指定的locale,將忽略系統(tǒng)locale,不再會因?yàn)樵O(shè)備語言改變而發(fā)生變化。
2. supportedLocales:當(dāng)前應(yīng)用支持的locale列表,MaterialApp通過supportedLocales屬性注冊的。
3. 返回值:一個Locale(應(yīng)用最終使用的Locale)
通常會在不支持的語言區(qū)域時返回一個默認(rèn)的Locale(默認(rèn)使用語言)。
4. locales
用戶在較新的Android系統(tǒng)手機(jī)設(shè)置中可以設(shè)置一個語言列表,應(yīng)用通常的處理方式就是按照列表的順序依次嘗試加載相應(yīng)的Locale,如果某一種語言加載成功則會停止。
在Flutter中,應(yīng)該優(yōu)先使用localeListResolutionCallback,不必?fù)?dān)心Android系統(tǒng)的差異性,如果在低版本的Android系統(tǒng)中,F(xiàn)lutter會自動處理這種情況,這時Locale列表只會包含一項(xiàng)。
- 引用本地化值
通過Localizations.of(context,type)來引用本地化值。
Localizations組件用于加載和查找應(yīng)用當(dāng)前語言下的本地化值或資源。
本地化值由Localizations組件的LocalizationsDelegates列表加載。 每個委托必須定義一個異步load() 方法,以生成封裝了一系列本地化值的對象。通常這些對象為每個本地化值定義一個方法。
在大型應(yīng)用程序中,不同模塊或Package可能會與自己的本地化值捆綁在一起, 這就是為什么要用Localizations組件 管理對象表的原因。 要使用由LocalizationsDelegate的load方法之一產(chǎn)生的對象,可以指定一個BuildContext和對象的類型來找到它。
例如:Material組件庫的本地化字符串由MaterialLocalizations類定義,此類的實(shí)例由MaterialApp類提供的LocalizationDelegate創(chuàng)建。通過如下方式獲取到:
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
這個特殊的Localizations.of()表達(dá)式會經(jīng)常使用,所以MaterialLocalizations類提供了一個便捷方法:
static MaterialLocalizations of(BuildContext context) {
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}
使用:MaterialLocalizations.of(context).國際化字段名
1. 國際化Material組件庫
第一步: 在pubspec.yaml文件中添加flutter_localizations依賴包(支持十幾種語言),并下載
dependencies:
flutter_localizations:
sdk: flutter
第二步:配置MaterialApp(指定localizationsDelegates和supportedLocales)
import 'package:flutter_localizations/flutter_localizations.dart';
new MaterialApp(
localizationsDelegates: [
// 本地化的代理類
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [ // 應(yīng)用支持的語言
const Locale('en', 'US'), // 美國英語(美國地區(qū)的英文)
const Locale('zh', 'CN'), // 中文簡體
// 其它Locales
],
// ...
)
說明:
1. 多數(shù)應(yīng)用程序都是通過MaterialApp(對WidgetsApp進(jìn)行了包裝)為入口,低級別的WidgetsApp為入口時也可以使用相同的類和邏輯進(jìn)行國際化。與MaterialApp類為入口的應(yīng)用不同, 對基于WidgetsApp類為入口的應(yīng)用程序進(jìn)行國際化時,不需要GlobalMaterialLocalizations.delegate。
2. localizationsDelegates列表中的元素是生成本地化值集合的工廠。
每個委托必須定義一個異步load() 方法,以生成封裝了一系列本地化值的對象。
GlobalMaterialLocalizations.delegate為Material組件庫提供的本地化的字符串和其他值,它可以使Material 組件支持多語言。GlobalMaterialLocalizations.delegate是一個產(chǎn)生GlobalMaterialLocalizations實(shí)例的LocalizationsDelegate。
GlobalWidgetsLocalizations.delegate定義組件默認(rèn)的文本方向,從左到右或從右到左。
3. supportedLocales接收一個Locale數(shù)組,表示應(yīng)用支持的語言列表。
當(dāng)沒有精確匹配(語言和地區(qū)同時匹配)時,使用語言。
如果沒有匹配則使用supportedLocales列表項(xiàng)的第一個。
2. 國際化開發(fā)人員的UI(Localizations)
示例
第一步:實(shí)現(xiàn)Localizations資源類(提供本地化資源值,如文本)
// Locale資源類
// 會根據(jù)當(dāng)前的語言(獲取本地化資源值)返回不同的文本??梢詫⑺行枰С侄嗾Z言的文本都在此類中定義,該類的實(shí)例會在Delegate類的load方法中創(chuàng)建。
class DemoLocalizations {
DemoLocalizations(this.isZh);
// 是否為中文
bool isZh = false;
// 為了使用方便,定義一個靜態(tài)方法
static DemoLocalizations of(BuildContext context) {
// MaterialApp組件內(nèi)部嵌套了Localizations組件,通過第三步配置MaterialApp的localizationsDelegates,會將DemoLocalizationsDelegate傳給Localizations組件
// 獲取DemoLocalizations實(shí)例
return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
}
// Locale相關(guān)值,title為應(yīng)用標(biāo)題
String get title {
return isZh ? "Flutter應(yīng)用" : "Flutter APP";
}
//... 其它的值
}
/*
方式2
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
}
static Map<String, Map<String, String>> _localizedValues = {
'en': {
'title': 'Hello World',
},
'es': {
'title': 'Hola Mundo',
},
};
String get title {
return _localizedValues[locale.languageCode]['title'];
}
}
*/
第二步:實(shí)現(xiàn)Delegate類(在Locale改變時會從DemoLocalizations中加載新的本地化資源值)
// Locale代理類
// Delegate類需要繼承自LocalizationsDelegate類并實(shí)現(xiàn)相應(yīng)的接口。有一個load方法。
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
// 是否支持某個Local
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
// Flutter會調(diào)用此類加載相應(yīng)的Locale資源類
@override
Future<DemoLocalizations> load(Locale locale) {
print("$locale");
return SynchronousFuture<DemoLocalizations>(
DemoLocalizations(locale.languageCode == "zh")
);
}
// shouldReload的返回值決定當(dāng)Localizations組件重新build時,是否調(diào)用load方法重新加載Locale資源。一般情況下,Locale資源只應(yīng)該在Locale切換時加載一次,不需要每次在Localizations重新build時都加載,所以返回false即可。
// 事實(shí)上,無論shouldReload返回true還是false,每當(dāng)Locale改變時Flutter都會再調(diào)用load方法加載新的Locale。
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}
/*
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['en', 'es'].contains(locale.languageCode);
@override
Future<DemoLocalizations> load(Locale locale) {
return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale));
}
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}
*/
第三步:配置MaterialApp的localizationsDelegates
在MaterialApp或WidgetsApp的localizationsDelegates列表中添加Delegate實(shí)例即可完成注冊
localizationsDelegates: [
// 本地化的代理類
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
// 注冊我們的Delegate
DemoLocalizationsDelegate(), // 或DemoLocalizationsDelegate.delegate
],
第四步:在Widget中使用本地化資源值
return Scaffold(
appBar: AppBar(
// 使用Locale title
title: Text(DemoLocalizations.of(context).title),
),
... //省略無關(guān)代碼
)
完整代碼如下
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/foundation.dart' show SynchronousFuture;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.yellow,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home:MyHomePage(),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
DemoLocalizationsDelegate.delegate,
],
supportedLocales: [
const Locale('zh', 'CH'),
const Locale('en', 'US'),
],
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text(DemoLocalizations.of(context).title),
),
body: new Center(
child: new Text(DemoLocalizations.of(context).content),
),
);
}
}
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
}
static Map<String, Map<String, String>> _localizedValues = {
'en': {
'title': 'Home',
'content': 'Hello World'
},
'zh': {
'title': '首頁',
'content': '世界 你好'
},
};
String get title {
return _localizedValues[locale.languageCode]['title'];
}
String get content {
return _localizedValues[locale.languageCode]['content'];
}
}
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
@override
Future<DemoLocalizations> load(Locale locale) {
return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale));
}
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}


當(dāng)要支持的語言不是兩種而是8種甚至20幾種時,如果為每個文本屬性都要分別去判斷到底是哪種Locale從而獲取相應(yīng)語言的文本將會是一件非常復(fù)雜的事。而且通常情況下翻譯人員并不是開發(fā)人員,能不能像i18n或l10n標(biāo)準(zhǔn)那樣可以將翻譯單獨(dú)保存為一個arb文件交由翻譯人員去翻譯,翻譯好之后開發(fā)人員再通過工具將arb文件轉(zhuǎn)為代碼呢。
解決:使用Intl包
使用Intl包的好處:
1. 輕松實(shí)現(xiàn)國際化
2. 將字符串文本分離成單獨(dú)的文件,方便開發(fā)人員和翻譯人員分工協(xié)作。
2. 使用Intl包
第一步:添加依賴、創(chuàng)建必要目錄
添加依賴
dependencies:
#...省略無關(guān)項(xiàng)
intl: ^0.15.7
dev_dependencies:
#...省略無關(guān)項(xiàng)
intl_translation: ^0.17.2
說明:
1. intl_translation包主要包含了一些工具,它在開發(fā)階段主要主要的作用是從代碼中提取要國際化的字符串到單獨(dú)的arb文件和根據(jù)arb文件生成對應(yīng)語言的dart代碼。
2. intl包主要是引用和加載intl_translation生成后的dart代碼。
創(chuàng)建必要目錄
在項(xiàng)目根目錄下創(chuàng)建一個l10n-arb目錄,該目錄保存接下來通過intl_translation命令生成的arb文件。
在lib目錄下創(chuàng)建一個l10n的目錄,該目錄用于保存從arb文件生成的dart代碼文件。
/*
arb文件示例(通過intl_translation命令自動生成)JSON格式:
{
"@@last_modified": "2020-09-23T12:54:51.602843",
"@@locale":"zh_CH",
"title": "Flutter應(yīng)用",
"@title": {
"description": "Title for the Demo application",
"type": "text",
"placeholders": {}
}
}
*/
第二步:實(shí)現(xiàn)Localizations類(添加需要國際化的屬性)和Delegate類
在lib/l10n目錄下新建一個“l(fā)ocalization_intl.dart”的文件
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart'; // 該文件會由intl_translation命令生成(從arb文件生成的dart代碼)
// 可以在DemoLocalizations類中添加需要國際化的屬性或方法
class DemoLocalizations {
static Future<DemoLocalizations> load(Locale locale) {
final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
// intl_translation命令會生成對應(yīng)的方法
// initializeMessages()用來加載翻譯的字符串
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( // Intl.message用來查找
'Flutter APP',
name: 'title',
desc: 'Title for the Demo application',
);
}
/*
// 添加方法
String helloTitle(String name) {
return Intl.message( // Intl.message用來查找
'hello $name',
name: 'helloTitle',
desc: 'helloTitle',
args:[name],
);
}
*/
// Intl.plural方法可以在howMany值不同時輸出不同的提示信息
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': 110, 'userName': 'Fred'});
}
// Locale代理類
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
// 是否支持某個Local
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
// Flutter會調(diào)用此類加載相應(yīng)的Locale資源類
@override
Future<DemoLocalizations> load(Locale locale) {
return DemoLocalizations.load(locale);
}
// 當(dāng)Localizations Widget重新build時,是否調(diào)用load重新加載Locale資源.
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}
第三步: 生成arb文件、根據(jù)arb文件生成dart代碼
生成arb文件
提取localization_intl.dart代碼中的字符串到一個arb文件(通intl_translation包的工具),運(yùn)行如下命令:
flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart
運(yùn)行此命令后,會將之前通過Intl包的API標(biāo)識的屬性和字符串提取到“l(fā)10n-arb/intl_messages.arb”文件中,看看其內(nèi)容:
{
"@@last_modified": "2020-09-23T12:54:51.602843",
"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": 110
}
}
}
}
// 中文簡體:intl_zh_Hans_CN.arb
// const Locale.fromSubtags(languageCode: 'zh',scriptCode: 'Hans',countryCode: 'CN'),
這個是默認(rèn)的Locale資源文件,如果現(xiàn)在要支持中文大陸,只需要在該文件同級目錄創(chuàng)建一個"intl_zh_CN.arb"文件,然后將"intl_messages.arb"的內(nèi)容拷貝到"intl_zh_CN.arb"文件,接下來將英文翻譯為中文即可,翻譯后的"intl_zh_CN.arb"文件內(nèi)容如下:
{
"@@last_modified": "2018-12-10T15:46:20.897228",
"@@locale":"zh_CN",
"title": "Flutter應(yīng)用",
"@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中缺失某個屬性,那么應(yīng)用將會加載默認(rèn)的arb文件(intl_messages.arb)中的相應(yīng)屬性,這是Intl的托底策略。
2. 每次運(yùn)行提取命令時,intl_messages.arb都會根據(jù)代碼重新生成,但其他arb文件不會,所以當(dāng)要添加新的字段或方法時,其他arb文件是增量的,不用擔(dān)心會覆蓋。
3. arb文件是標(biāo)準(zhǔn)的。通常會將arb文件交給翻譯人員,當(dāng)他們完成翻譯后,再通過下面的步驟根據(jù)arb文件生成最終的dart代碼。
生成dart代碼
根據(jù)arb生成dart文件:
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb
在首次運(yùn)行時會在"lib/l10n"目錄下生成多個文件,對應(yīng)多種Locale,這些代碼便是最終要使用的dart代碼。
優(yōu)化第三步
在根目錄下創(chuàng)建一個intl.sh的腳本,內(nèi)容為:
flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb
授予執(zhí)行權(quán)限:
chmod +x intl.sh
執(zhí)行intl.sh
./intl.sh
完整代碼
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'l10n/localization_intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.yellow,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home:MyHomePage(),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
DemoLocalizationsDelegate.delegate,
],
supportedLocales: [
const Locale('zh', 'CH'),
const Locale('en', 'US'),
],
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text(DemoLocalizations.of(context).title),
),
body: new Center(
child: new Text(DemoLocalizations.of(context).content),
),
);
}
}
localization_intl.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart'; // intl_translation從arb文件生成的dart代碼
class DemoLocalizations {
static Future<DemoLocalizations> load(Locale locale) {
final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
// initializeMessages()方法和"messages_all.dart"文件一樣,是同時生成的。
// initializeMessages()用來加載翻譯的字符串
return initializeMessages(localeName).then((b) {
Intl.defaultLocale = localeName;
return new DemoLocalizations();
});
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
}
// Intl.message用來查找
String get title {
return Intl.message(
'Home',
name: 'title',
desc: 'Title for the Demo application',
);
}
String get content {
return Intl.message(
'Hello world',
name: 'content',
desc: 'Content for the Demo application',
);
}
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': 110, 'userName': 'Fred'});
}
// Locale代理類
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
//是否支持某個Local
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
// Flutter會調(diào)用此類加載相應(yīng)的Locale資源類
@override
Future<DemoLocalizations> load(Locale locale) {
return DemoLocalizations.load(locale);
}
// 當(dāng)Localizations Widget重新build時,是否調(diào)用load重新加載Locale資源.
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}

常見問題
- 默認(rèn)語言區(qū)域不對
在一些非大陸行貨渠道買的一些Android和iOS設(shè)備,會出現(xiàn)默認(rèn)的Locale不是中文簡體的情況。這屬于正?,F(xiàn)象,但是為了防止設(shè)備獲取的Locale與實(shí)際的地區(qū)不一致,所有的支持多語言的APP都必須提供一個手動選擇語言的入口。
- 對應(yīng)用標(biāo)題進(jìn)行國際化
MaterialApp有一個title屬性,用于指定APP的標(biāo)題。在Android系統(tǒng)中,APP的標(biāo)題會出現(xiàn)在任務(wù)管理器中。所以也需要對title進(jìn)行國際化。
但是問題是很多國際化的配置都是在MaterialApp上設(shè)置的,無法在構(gòu)建MaterialApp時通過Localizations.of來獲取本地化資源,如:
MaterialApp(
title: DemoLocalizations.of(context).title, //不能正常工作!
localizationsDelegates: [
// 本地化的代理類
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
DemoLocalizationsDelegate() // 設(shè)置Delegate
],
);
運(yùn)行后,DemoLocalizations.of(context).title會報錯,這里DemoLocalizations.of(context)會返回null,這里的context找不到MaterialApp,繼而找不到DemoLocalizationsDelegate。
只需要設(shè)置一個onGenerateTitle回調(diào)即可:
MaterialApp(
onGenerateTitle: (context){
// 此時context在Localizations的子樹中
return DemoLocalizations.of(context).title;
},
localizationsDelegates: [
DemoLocalizationsDelegate(),
...
],
);
- 為英語系的國家指定同一個locale
英語系的國家非常多,如美國、英國、澳大利亞等,這些英語系國家雖然說的都是英語,但也會有一些區(qū)別。如果APP只想提供一種英語(如美國英語)供所有英語系國家使用則在localeListResolutionCallback中來做兼容。
localeListResolutionCallback:
(List<Locale> locales, Iterable<Locale> supportedLocales) {
// 判斷當(dāng)前l(fā)ocale是否為英語系國家,如果是直接返回Locale('en', 'US')
}