* author:lebonbill
* E-mail:lebonbillwu@gmail.com
前言
如果APP有需要支持多種語言,就需要支持國際化,無論是android和ios,現(xiàn)在針對flutter的國際化做了一些學(xué)習(xí)理解和總結(jié),這里主要是講解一下Flutter國際化的一些用法,包括Localization widget,intl包和flutter_i18n包的用法
支持國際化
在默認(rèn)情況下,Flutter只支持英文,要添加多國語言支持,需要在pubspec.yaml文件中添加“flutter_localizations”依賴,如下
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
然后指定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
],
// ...
)
localizationsDelegates列表中的元素是生成本地化值集合的工廠。
GlobalMaterialLocalizations.delegate 為Material 組件庫提供的本地化的字符串和其他值,
它可以使Material Widget支持多語言。 GlobalWidgetsLocalizations.delegate定義widget默認(rèn)的文本方向厢洞,
從左到右或從右到左父款,這是因?yàn)橛行┱Z言的閱讀習(xí)慣并不是從左到右筏餐,比如如阿拉伯語就是從右向左的可免。
Locale區(qū)域
Locale類是用來標(biāo)識用戶的語言環(huán)境的,它包括語言和國家兩個標(biāo)志,languageCode和countryCode:
const Locale(languageCode,countryCode)
const Locale('zh', 'CN') // 中文簡體
獲取當(dāng)前的語言環(huán)境:
Locale currentLocale = Localizations.localeOf(context);
Localizations Widget一般位于Widget樹中其它業(yè)務(wù)組件的頂部算色,它的作用是定義區(qū)域Locale以及設(shè)置子樹依賴的本地化資源。 如果系統(tǒng)的語言環(huán)境發(fā)生變化螟够,WidgetsApp將創(chuàng)建一個新的Localizations Widget并重建它灾梦,這樣子樹中通過Localizations.localeOf(context) 獲取的Locale就會更新
監(jiān)聽系統(tǒng)語言切換
當(dāng)我們更改系統(tǒng)語言設(shè)置時峡钓,APP中的Localizations widget會重新構(gòu)建,最終界面會重新build達(dá)到切換語言的效果若河。
我們可以通過localeResolutionCallback或localeListResolutionCallback回調(diào)來監(jiān)聽locale改變的事件
localeResolutionCallback:(Locale locale, Iterable<Locale> supportedLocales){};
locale返回值是如果用戶沒有指定locale的話,是默認(rèn)當(dāng)前系統(tǒng)語言,如果用戶切換系統(tǒng)語言,locale也會變更為相應(yīng)的語言,如果客戶自己指定語言的話,則改變系統(tǒng)語言不會變更locale的值
用戶可以自己指定locale的值如下:
MaterialApp{
locale:const('zh','CN')//指定簡體中文
}
supportedLocales返回的是系統(tǒng)支持的所有已添加的語言,如下圖
Localizations widget
Flutter的世界都是widget,當(dāng)中包括語言"組件",定義Localization widget,用來加載查找本地化的對象,通過Localization.of(context,type)使用這些語言資源,定義一個
實(shí)現(xiàn)Localizations類
class DemoLocalizations {
DemoLocalizations(this._locale);
final Locale _locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of(context, DemoLocalizations);
}
static Map<String, Map<String, String>> _localValues = {
'en': {"title": "title"},
"zh": {"title": "標(biāo)題"}
};
String get title {
return _localValues[_locale.languageCode]["title"];
}
}
其中of是常用的方法,所以為了方便會定義靜態(tài)方法of并且返回自身對象
實(shí)現(xiàn)LocalizationDelegate代理
繼承LocalizationDelegate是為了加載新的locale資源,這里有一個重載的load方法需要實(shí)現(xiàn),
class DemoLocalizationDelegate
extends LocalizationsDelegate<DemoLocalizations> {
@override
bool isSupported(Locale locale) {
return ['en', 'zh'].contains(locale.languageCode);//是否支持的語言
}
@override
Future<DemoLocalizations> load(Locale locale) {
return SynchronousFuture(DemoLocalizations(locale));//異步記載資源
}
@override
bool shouldReload(LocalizationsDelegate<DemoLocalizations> old) => false;
}
最后在MaterialApp和WidgetsApp的localizationsDelegates添加定義的代理
localizationsDelegates: [
// 本地化代理
DemoLocalizationDelegate();//自定義代理
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
然后通過Localization widget的of方法使用資源:
DemoLocalization lo= DemoLocalization.of(context);
String title=lo.title;//默認(rèn)是英文的話會返回"title",切換語言的時候該值會變成"標(biāo)題"
使用intl包
為了方便翻譯人員和開發(fā)人員分工協(xié)作,使用intl包可以很好的將翻譯資源文件和代碼分離,arb文件用做于翻譯資源,dart用于開發(fā)人員編寫相關(guān)的翻譯代碼,因此在pubspec.yaml中引用intl包依賴:
dependencies:
intl: ^0.15.7
dev_dependencies:
intl_translation: ^0.17.2
intl包是用來翻譯前面所說的Localization widget相關(guān)的dart的文件生成arb文件(arb文件下面會講解),如前面所說的Localization widget類會改成這樣:
import 'dart:async';
import 'package:intl/intl.dart';
import 'package:flutter/widgets.dart';
class DemoLocalizations {
DemoLocalizations(this._locale);
final Locale _locale;
static Future<DemoLocalizations> load(Locale locale) async {
String name =
locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(locale.toString())
.then((Object _) {
return new DemoLocalizations(locale);
});
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of(context, DemoLocalizations);
}
String get title =>
Intl.message("這個是一個標(biāo)題", name: "title", desc: "標(biāo)題用的翻譯文本", args: []);
}
}
這里的靜態(tài)函數(shù)load,是為了方便代理類調(diào)用新增,里面有一個initializeMessages會顯示報錯,先可以忽略,上面Localization類中使用了Intl.message,Intl.message的相關(guān)用法可以參考官方api(點(diǎn)擊查看),這里就是需要翻譯的文字,通過intl包會把Intl.message相關(guān)的資源"提取"生成arb文件,需要運(yùn)行命令行如下:
$ flutter pub pub run intl_translation:extract_to_arb --output-dir="保存arb文件的目錄" "需要提取翻譯的dart文件"
例子:
$ flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/demo_localizations.dart
需要在lib中新建l10n文件夾用于保存arb文件,后面帶的參數(shù)就是上述的DemoLocalizations類所在位置,運(yùn)行后會在l10n看到一個intl_messages.arb文件能岩。
把上面的intl_messgaes文件當(dāng)成模板復(fù)制粘貼intl_zh.arb和intl_en.arb,然后翻譯相關(guān)的內(nèi)容,arb是一個json結(jié)構(gòu)的文件,intl_zh.arb文件結(jié)構(gòu)如下
{
"@@last_modified": "2019-01-10T14:17:00.088434",
"title": "這個是一個標(biāo)題",
"@title": {
"description": "標(biāo)題用的翻譯文本",
"type": "text",
"placeholders": {}
}
}
翻譯完所有arb資源后,intl_translation包就是用來把a(bǔ)rb生成相關(guān)的dart代碼文件,運(yùn)行命令行:
$ flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n \
--no-use-deferred-loading lib/demo_localizations.dart lib/l10n/intl_*.arb
運(yùn)行結(jié)果會出現(xiàn)
No @@locale or _locale field found in intl_en, assuming 'en' based on the file name.
No @@locale or _locale field found in intl_messages, assuming 'messages' based on the file name.
No @@locale or _locale field found in intl_zh, assuming 'zh' based on the file name.
各自生成如下的新文件
然后在剛才initializeMessages保存的DemoLocalizations中引入
import 'l10n/messages_all.dart';
警告就會消失
然后修改Localization代理中的load,引用上面的Localization widget load方法即可
調(diào)用資源的方法和上面的一樣
DemoLocalizations.of(context).title
DemoLocalizations.of(context).name
使用flutter_i18n
flutter_i18n是另外一種進(jìn)行國際化的方法,有別于intl,它主要是利用json文件來進(jìn)行翻譯,個人認(rèn)為最簡單的一種,可以自己定app語言,方便刷新切換語言,不依賴系統(tǒng)語言
使用方法步驟如下:
在pubspec.yaml引用依賴并且添加資源目錄這里我定義存放資源文件目錄是flutter_i18n
dependencies:
flutter:
sdk: flutter
flutter_i18n: ^0.5.2
flutter:
assets:
- flutter_i18n/
新建json文件,命名規(guī)則可以這樣{languageCode}_{countryCode}.json或者{languageCode}.json,這里我命名為zh_CN.json,內(nèi)容如下:
{
"test": {
"title": "這個是一個標(biāo)題哦",
"name": "我的名字叫{name}"
}
}
添加Localization代理
localizationsDelegates: [
FlutterI18nDelegate(useCountryCode, [fallbackFile, basePath]),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
useCountryCode參數(shù)是用于json文件的命名規(guī)則:
如果你是用{languageCode}_{countryCode}來命名,useCountryCode參數(shù)必須為true
如果你是用{languageCode}來命名, useCountryCode就必須為false
fallbackFile參數(shù)引入版本0.1.0并提供默認(rèn)語言萧福,在未提供當(dāng)前運(yùn)行系統(tǒng)的轉(zhuǎn)換時使用拉鹃。這應(yīng)該包含assets文件夾中有效的json文件的名稱如"zh_CN"
basePath參數(shù)可選地用于設(shè)置翻譯的基本路徑。如果未設(shè)置此選項(xiàng)鲫忍,則默認(rèn)路徑為assets / flutter_i18n膏燕。此路徑必須與pubspec.yaml中定義的路徑相同。
配置例子如:
FlutterI18nDelegate(true, "en_US", "flutter_i18n")
flutter_i18n實(shí)現(xiàn)
配置完成后,可以通過以下方法調(diào)用:
FlutterI18n.translate(buildContext, "your.key")
FlutterI18n.translate(buildContext, "test.title");//這是一個標(biāo)題哦
FlutterI18n.translate(context, "test.name", {"name": "AAA"});我的名字叫做AAA
如果你想切換應(yīng)用語言可以這樣做:
await FlutterI18n.refresh(buildContext, languageCode, {countryCode});
await FlutterI18n.refresh(buildContext, "en", "US");//如切換英語
flutter_i18n 支持復(fù)數(shù),你可以調(diào)用以下方法:
FlutterI18n.plural(buildContext, "your.key", pluralValue);如
"clicked": {
"times-0": "You clicked zero!",
"times-1": "You clicked {time} time!",
"times-2": "You clicked {times} times!"
}
FlutterI18n.plural(buildContext, "clicked.times", 0)//You clicked zero!
FlutterI18n.plural(buildContext, "clicked.times", 1)//You clicked 1 time!
FlutterI18n.plural(buildContext, "clicked.times", 2)//You clicked 2 times!