前言
關於flutter國際化的具體介紹,大家可以移步國際化Flutter App
本文主要介紹在flutter如何使用多語言並且如何像web使用i18n一樣使用多語言($t)和實現語言切換,這對前端開發人員會更加友好。
專案地址
flutter-ui, 這是包含flutter元件介紹的開源專案,歡迎star
flutter_intl 本教程的專案原始碼,歡迎star
效果
如何使用
新增依賴
- 在pubspec.yaml中引入依賴
dependencies:
flutter_localizations:
sdk: flutter
複製程式碼
- 執行
flutter packages get
複製程式碼
新建檔案locale
locale
|-en.json
|-zh.json
複製程式碼
多語言的檔案
- en.json
{
"title_page": "i18n",
"title_appbar": "i18n",
"content": {
"currentLanguage": "The current language is English",
"zh": "zh",
"en": "en"
}
}
複製程式碼
- zh.json
{
"title_page": "國際化例子",
"title_appbar": "國際化例子",
"content": {
"currentLanguage": "當前語言是中文",
"zh": "中文",
"en": "英文"
}
}
複製程式碼
lib下新建lang
lang
|- config.dart
|- index.dart
複製程式碼
- config.dart
import 'package:flutter/material.dart';
class ConfigLanguage {
static List<Locale> supportedLocales = [
Locale('zh', 'CH'),
Locale('en', 'US')
];
static Map<String, dynamic> supportLanguage = {
"zh": {"code": "zh", "country_code": "CH"},
"en": {"code": "en", "country_code": "US"},
};
static dynamic defaultLanguage = {
"code": "zh",
"country_code": "CH"
};
}
複製程式碼
config.dart作用主要是將配置性的內容統一到一個檔案中
- index.dart
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/foundation.dart' show SynchronousFuture;
import 'package:flutter_intl/lang/config.dart' as I18NConfig;
class AppLocalizations {
Locale _locale;
static Map<String, dynamic> jsonLanguage; // 語言包
static AppLocalizations _inst; // inst
AppLocalizations(this._locale);
// 初始化 localizations
static Future<AppLocalizations> init(Locale locale) async {
_inst = AppLocalizations(locale);
await getLanguageJson();
return _inst;
}
// 獲取語言包
static Future getLanguageJson() async {
Locale _tmpLocale = _inst._locale;
print('獲取語言包的語種; ${_tmpLocale.languageCode}');
String jsonLang;
try {
jsonLang = await rootBundle.loadString('locale/${_tmpLocale.languageCode}.json');
} catch (e) {
print('出錯了');
_inst._locale = Locale(I18NConfig.ConfigLanguage.defaultLanguage['code']);
jsonLang = await rootBundle.loadString('locale/${I18NConfig.ConfigLanguage.defaultLanguage['code']}.json');
}
jsonLanguage = json.decode(jsonLang);
print("當前語言: ${_inst._locale}");
print("資料: $jsonLanguage");
}
// $t 封裝,目的是為了可以使用$t來獲取多語言資料
static String $t(String key) {
var _array = key.split('.');
var _dict = jsonLanguage;
var retValue = '';
try {
_array.forEach((item) {
if(_dict[item].runtimeType == Null) {
retValue = key;
return;
}
if (_dict[item].runtimeType != String) {
_dict = _dict[item];
} else {
retValue = _dict[item];
}
});
retValue = retValue.isEmpty ? _dict : retValue;
} catch (e) {
print('i18n exception');
print(e);
retValue = key;
}
return retValue ?? '';
}
}
// 實現LocalizationsDelegate協議,用於初始化Localizations類
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
final Locale locale;
AppLocalizationsDelegate([this.locale]);
@override
bool isSupported(Locale locale) {
return I18NConfig.ConfigLanguage.supportLanguage.keys
.toList()
.contains(locale.languageCode);
}
// 在這裡初始化Localizations類
@override
Future<AppLocalizations> load(Locale _locale) async {
print('將要載入的語言: $_locale');
return await AppLocalizations.init(_locale);
// return SynchronousFuture<AppLocalizations>(
// AppLocalizations(_locale)
// );
}
@override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) {
// flase時,不執行上述重寫函式
return false;
}
}
複製程式碼
詳情請看程式碼註釋~~~~~~~~~
概況一下就是
實現一個LocalizationsDelegate協議和實現一個Localizations類,然後引入到main.dart中的MaterialApp中
處理main.dart檔案
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_intl/lang/index.dart'
show AppLocalizations, AppLocalizationsDelegate;
import 'package:flutter_intl/lang/config.dart' show ConfigLanguage;
void main () => runApp(MainApp());
GlobalKey<_ChangeLocalizationsState> changeLocalizationsStateKey = new GlobalKey<_ChangeLocalizationsState>();
class MainApp extends StatefulWidget {
@override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
// 定義全域性 語言代理
AppLocalizationsDelegate _delegate;
@override
void initState() {
// TODO: implement initState
_delegate = AppLocalizationsDelegate();
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
// locale: Locale('zh', 'CH'),
localeResolutionCallback: (deviceLocale, supportedLocal) {
print('當前裝置語種 deviceLocale: $deviceLocale, 支援語種 supportedLocale: $supportedLocal}');
// 判斷傳入語言是否支援
Locale _locale = supportedLocal.contains(deviceLocale) ? deviceLocale : Locale('zh', 'CN');
return _locale;
},
onGenerateTitle: (context) {
// 設定多語言代理
// AppLocalizations.setProxy(setState, _delegate);
return AppLocalizations.$t('title_page');
},
// localizationsDelegates 列表中的元素時生成本地化集合的工廠
localizationsDelegates: [
GlobalMaterialLocalizations.delegate, // 為Material Components庫提供本地化的字串和其他值
GlobalWidgetsLocalizations.delegate, // 定義widget預設的文字方向,從左往右或從右往左
_delegate
],
supportedLocales: ConfigLanguage.supportedLocales,
initialRoute: '/',
routes: {
'/': (context) =>
// Home()
Builder(builder: (context) {
return ChangeLocalizations(
key: changeLocalizationsStateKey,
child: Home()
);
})
}
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
Locale locale = Localizations.localeOf(context);
return Scaffold(
appBar: AppBar(title: Text('${AppLocalizations.$t('title_appbar')}'),),
body: ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 60),
alignment: Alignment.center,
child: Text('${locale.languageCode} ${locale.toString()}'),
),
Container(
alignment: Alignment.center,
child: Text('${AppLocalizations.$t('content.currentLanguage')}'),
),
Wrap(
spacing: 8.0,
alignment: WrapAlignment.center,
children: <Widget>[
ActionChip(
backgroundColor: Theme.of(context).primaryColor,
onPressed: () {
changeLocalizationsStateKey.currentState.changeLocale(Locale('en', 'US'));
},
label: Text('${AppLocalizations.$t('content.en')}'),
),
ActionChip(
backgroundColor: Theme.of(context).primaryColor,
onPressed: () {
changeLocalizationsStateKey.currentState.changeLocale(Locale('zh', 'CH'));
},
label: Text('${AppLocalizations.$t('content.zh')}'),
)
],
)
],
)
);
}
}
class ChangeLocalizations extends StatefulWidget {
final Widget child;
ChangeLocalizations({Key key, this.child}):super(key: key);
@override
_ChangeLocalizationsState createState() => _ChangeLocalizationsState();
}
class _ChangeLocalizationsState extends State<ChangeLocalizations> {
Locale _locale;
@override
void initState() {
super.initState();
}
@override
void didChangeDependencies() async {
super.didChangeDependencies();
// 獲取當前裝置的語言
_locale = Localizations.localeOf(context);
print('裝置語言: $_locale');
}
changeLocale(Locale locale) {
setState(() {
_locale = locale;
});
}
@override
Widget build(BuildContext context) {
return Localizations.override(
context: context,
locale: _locale,
child: widget.child,
);
}
}
複製程式碼
- 在MaterialApp中指定localizationsDelegate和supportedLocales
- localeResolutionCallback:在應用獲取使用者設定的語言區域時回撥,可以根據需要return對應的Locale
- onGenerateTitle: 返回對應的多語言應用標題
- localizationsDelegates:localizationsDelegates 列表中的元素時生成本地化集合的工廠
- supportedLocales: app支援的語言種類
- Home類就是顯示的類
- Localizations.localeOf(context).languageCode可以獲取當前app的語言型別
- AppLocalizations.$t('content.currentLanguage')像web一樣玩耍多語言內容
到這裡就已經可以愉快的玩耍多語言了,使用者設定不同的語言就會載入不同的語言包
下面實現在app內的語言切換
- ChangeLocalizations類使用Localizations的override方法,程式碼如上
- 使用GlobalKey呼叫ChangeLocalizations的內部方法,GlobalKey<_ChangeLocalizationsState> changeLocalizationsStateKey = new GlobalKey<_ChangeLocalizationsState>(); 我們也可以將GlobalKey放入到provide中,這樣可以實現多個頁面進行changeLocalizationsStateKey的訪問
- 修改語言呼叫changeLocale方法,changeLocalizationsStateKey.currentState.changeLocale(Locale('en', 'US'));
最後
歡迎更多學習flutter的小夥伴加入QQ群 Flutter UI: 798874340
敬請關注我們正在開發的: efoxTeam/futter-ui