鑒于Flutter高性能渲染和跨平臺的優(yōu)勢雏赦,閃點清單在移動端APP上奴潘,使用了完整的Flutter框架來開發(fā)凳鬓。既然是完整APP呼巴,架構(gòu)搭建完全不受歷史Native APP的影響,沒有歷史包袱的沉淀御蒲,設(shè)計也能更靈活和健壯衣赶。
國際化語言的支持,是很多APP都有的一個強需求厚满,APP無論大小府瞄,只要還不想放棄國外的客戶,一般就需要支持國際化碘箍。
官方支持
Flutter官方方案提供了國際化的基礎(chǔ)支持遵馆,如Flutter內(nèi)置組件的國際化、語言代理丰榴、Widget使用語言包货邓、語言設(shè)置回調(diào)等,并支持自定義第三方類來擴展四濒,可以參考Flutter國際化文檔换况。
官方支持代碼示例:
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'];
}
}
官方方案的缺陷
官方的支持有幾個缺陷:
- 依賴于BuildContext對象,在非Widget中調(diào)用時盗蟆,需要層層傳遞BuildContext對象戈二,或存儲全局BuildContext對象。
- 在MaterialApp初始化前無法使用國際化(原因也是依賴于BuildContext對象)喳资。
- 語言包定義推薦使用Map方式觉吭,無法利用靜態(tài)語言的優(yōu)勢(語法提示、錯誤檢查等)仆邓;而為語言包每個屬性自定義類和類字段鲜滩,成本較高伴鳖、使用和更新靈活性差。
i18n介紹
鑒于Flutter官方支持的缺陷绒北,我們調(diào)研了很多第三方庫黎侈,最終發(fā)現(xiàn)了i18n,并在此基礎(chǔ)上闷游、結(jié)合Flutter官方支持和自身封裝峻汉,實現(xiàn)了更靈活易用的方案。
基礎(chǔ)使用
i18n使用yaml格式來定義語言包脐往,同時提供構(gòu)建腳本一鍵生成Dart語言包Class休吠。如下:
lib/messages.i18n.yaml
button:
save: Save
load: Load
users:
welcome(String name): "Hello $name!"
logout: Logout
該配置會生成幾個Class:Messages、ButtonMessages业簿、UserMessages瘤礁,生成后的Dart文件使用方式如下:
Messages m = Messages();
debugPrint(m.users.logout);
debugPrint(m.users.welcome('World'));
生成的Dart文件預(yù)覽(開發(fā)時無需關(guān)心):
class Messages {
const Messages();
ButtonMessages get button => ButtonExampleMessages(this);
UsersMessages get users => UsersExampleMessages(this);
}
class ButtonMessages {
final Messages _parent;
const ButtonMessages(this._parent);
String get save => "Save";
String get load => "Load";
}
class UsersMessages {
final Messages _parent;
const UsersMessages(this._parent);
String get logout => "Logout";
String welcome(String name) => "Hello $name!";
}
進階功能
下面講解一些進階用法。
函數(shù)定義
i18n支持函數(shù)定義梅尤,并支持傳參柜思,如上述的welcome
函數(shù):
debugPrint(m.users.welcome('World'));
參數(shù)定義基本沒有限制,可以隨意定義參數(shù)個數(shù)和類型巷燥。
內(nèi)置函數(shù)
i18n支持了一些內(nèi)置函數(shù)赡盘,用于做不同語言解析的體驗優(yōu)化,如:plural缰揪、cardinal陨享、ordinal。具體規(guī)則和使用钝腺,可以參考這里:http://cldr.unicode.org/index/cldr-spec/plural-rules
使用Dart字符串模板
Dart字符串模板是非常強大的抛姑,而在i18n中,你可以使用字符串模板(這點非常贊)艳狐,如:
count(int cnt): "You have created $cnt ${_plural(cnt, one:'invoice', many:'invoices')}."
前置編譯
i18n依然依賴了Dart官方提供的builder_runner工具定硝,來從yaml文件生成Dart文件,使用方式: flutter pub run build_runner build
毫目。
語言包使用
前置編譯后喷斋,每個語言包會生成N個Class(語言包的每一個分類或組合會生成一個Class文件),然后會生成一個根Class蒜茴,我們可以直接使用根Class(當(dāng)然也可以使用任何一個分類層級的Class)星爪。
比如兩個語言包文件: AppMessages.i18n.yaml
和AppMessages_en.i18n.yaml
(未加語言后綴的,會認為是默認語言包粉私,因此AppMessages.i18n.yaml是默認語言包)顽腾,會生成2個根Dart Class: class AppMessages
和class AppMessages_en extends AppMessages
。
AppMessages_en
自動繼承自AppMessages
,因此我們可以直接使用AppMessages
類型來存儲語言包抄肖,并在語言切換時重新為其實例化對應(yīng)的子類:
AppMessages appMessages = new AppMessages();
resetLocalLang(String localeName) {
switch (localeName) {
case 'en':
appMessages = AppMessages_en();
break;
case 'zh':
default:
appMessages = AppMessages();
break;
}
}
然后你可以在任意地方使用語言包:
debugPrint('Load Button: ${appMessages.button.load}');
FlatButton(
child: Text(appMessages.button.save),
onPressed: () {
/// 干點什么
},
)
Flutter集成
集成到Flutter久信,依然要依賴于官方的支持,在MaterialApp中設(shè)置和監(jiān)聽本地語言包:
@override
Widget build(BuildContext context) {
return MaterialApp(
localeResolutionCallback:
(Locale locale, Iterable<Locale> supportedLocales) {
/// Local changed
},
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: ['zh', 'en']
.map((loc) => new Locale(loc))
.toList(growable: false),
/// ...
);
}
結(jié)尾
國際化支持漓摩,是一個移動端APP框架層的基礎(chǔ)能力裙士,設(shè)計原則應(yīng)該是使用無感知、靈活易擴展管毙;但維護成本是難免有增加的腿椎,比如每次改文案要所有語言包同時更改。
講到這里夭咬,還并沒有完成基礎(chǔ)框架的搭建啃炸,后面我們會講解更多的Flutter架構(gòu)設(shè)計內(nèi)容,比如:通知卓舵、分享南用、UI設(shè)計等等。
持續(xù)分享閃點清單在Flutter上的開發(fā)經(jīng)驗掏湾。閃點清單裹虫,一款懸浮清單軟件: