Flutter官方提供的实现国际化有些繁琐,需要自己实现LocalizationsDelegate,并且国际化的strings都是在代码中写的,而i18n插件可以自动生成这些代码,并且国际化的strings也是使用文件以json形式配置。加以使用redux实现语言切换及持久化。
Android Studio安装i18n插件
安装后在菜单栏中会有如下图标,点击用于生成i18n代码
项目中设置国际化
要使用flutter_localizations,将软件包作为依赖项添加到pubspec.yaml
文件中:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
点击i18n插件按钮,在项目中会生成lib/generated/i18n.dart和lib/res/values/strings_en.arb。在strings_en.arb中配置英语显示的字符,如果要支持中文简体和中文繁体(香港),需要在lib/res/values文件夹中添加strings_zh_CN.arb、strings_zh_HK.arb,i18n插件也提供了图形化添加不同国家语言arb文件,并在arb文件中添加文本,如下所示:
点击i18n按钮,就会重新生成lib/generated/i18n.dart文件,并且在values中添加的国际化文本,都会自动生成中在i18n.dart中。
接下来,使用flutter_localizations库,并在MaterialApp中
指定 localizationsDelegates
和supportedLocales
为:
MaterialApp(
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: I18nUtil.localeResolutionCallback(),
locale: store.state.locale,//如果locale设置为null或者不指定,那么会跟随系统的Locale从supportedLocales中找是否支持,不支持可以使用localeResolutionCallback来指定支持的Locale
title: 'Flutter Basic', //在安卓任务管理列表中显示的名称
theme: defaultTargetPlatform == TargetPlatform.iOS
? kIOSTheme
: kDefaultTheme,
routes: {
'/':(BuildContext context) => new StoreConnector(
builder: (BuildContext context,dynamic isAuthenticated) =>
isAuthenticated ? MainPage() : LoginPage(),
converter: (Store store) => store.state.authState.isAuthenticated??false,
),
'/main':(BuildContext context) => MainPage(),
'/login':(BuildContext context) => LoginPage(),
},
)
localeResolutionCallback: I18nUtil.localeResolutionCallback()可以不指定,主要作用是在设置locale或者系统切换的语言该应用不支持的时候,通过该方法过滤,并且重新指定supportedLocales支持语言中的一个。不指定也不会报错,可能会根据不同手机随机指定supportedLocales中的一种语言。在i18n.dart中GeneratedLocalizationsDelegate也有相应的实现resolution,但是可能并不是你想要的实现,也可以自己实现。
static LocaleResolutionCallback localeResolutionCallback(){
return (Locale locale, Iterable supported) {
if (locale == null || !S.delegate.isSupported(locale)) {
return Locale('en','');
}
for(var element in supported){
if(element.languageCode == locale.languageCode &&
element.countryCode == locale.countryCode){
return element;
}
}
return Locale('en','');
};
}
Redux配合使用,实现切换语言和持久化
包依赖,在pubspec.yaml
文件中添加
//redux
redux: ^3.0.0
flutter_redux: ^0.5.2
//redux log
redux_logging: ^0.3.0
//redux中间件
redux_thunk: ^0.2.0
//redux持久化
redux_persist_flutter: ^0.6.0-rc.1
redux_persist: ^0.7.0-rc.2
state代码
@immutable
class AppState {
/*
* 国际化
*
* 设置为null,就相当于auto跟随系统,
* 将locale持久化,让切换语言在关闭应用时也能生效
*
* 如果MaterialApp.locale设置为null或者不指定,
* 那么会跟随系统的Locale从supportedLocales中找是否支持,
* 不支持可以使用localeResolutionCallback来指定支持的Locale
*/
final Locale locale;
AppState({Locale locale}):
this.locale = locale;
static AppState fromJson(dynamic json) => new AppState(
locale: LocaleExtension.fromJson(json['locale'])
);
//toJson名字固定
Map toJson() => {
'locale':locale == null ? null : locale.toJson()
};
}
//以下是Locale的扩展函数
import 'package:flutter/material.dart';
extension LocaleExtension on Locale {
Map toJson() {
var map = Map();
if(this.languageCode != null && this.languageCode != "")
map["languageCode"] = this.languageCode;
if(this.scriptCode != null && this.scriptCode != "")
map["scriptCode"] = this.scriptCode;
if(this.countryCode != null && this.countryCode != "")
map["countryCode"] = this.countryCode;
return map;
}
static Locale fromJson(Map json) =>
json != null ? Locale.fromSubtags(languageCode:json['languageCode'],
scriptCode:json['scriptCode'],
countryCode:json['countryCode']) : null;
int compareTo(Locale other){
if(other == null) return -1;
return this.toString().compareTo(other.toString());
}
}
action代码
import 'package:flutter_basic/models/app_state.dart';
import 'package:redux/redux.dart';
class LocaleAction{
Locale locale;
LocaleAction({this.locale});
}
Function changeLocale = (Locale locale){
return (Store store){
store.dispatch(LocaleAction(locale: locale));
};
};
reducer代码
import 'package:flutter_basic/actions/locale_action.dart';
import 'package:redux/redux.dart';
Reducer localeReducer = combineReducers([
new TypedReducer(changeLocaleReducer),
]);
Locale changeLocaleReducer(Locale locale,LocaleAction action){
locale = action.locale;
return locale;
}
store和middleware代码
//store
Store createStore(){
Store store = new Store(
appReducer,
initialState: new AppState(),
middleware: createMiddleware()
);
persistor.start(store);
return store;
}
//middleware
final persistor = new Persistor(storage: new FlutterStorage('basic-app'),decoder: AppState.fromJson);
List> createMiddleware() => >[
thunkMiddleware,
persistor.createMiddleware(),
LoggingMiddleware.printer(),
];
程序入口代码改造
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final store = createStore();
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return PersistorGate(
persistor: persistor,
loading: LoadingPage(),
builder: (context) => new StoreProvider(store: store, child: StoreBuilder(//国际化必须要用StoreBuilder包裹一下
builder: (context,store){
return MaterialApp(
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: I18nUtil.localeResolutionCallback(),
locale: store.state.locale,//如果locale设置为null或者不指定,那么会跟随系统的Locale从supportedLocales中找是否支持,不支持可以使用localeResolutionCallback来指定支持的Locale
title: 'Flutter Basic', //在安卓任务管理列表中显示的名称
theme: defaultTargetPlatform == TargetPlatform.iOS
? kIOSTheme
: kDefaultTheme,
routes: {
'/':(BuildContext context) => new StoreConnector(
builder: (BuildContext context,dynamic isAuthenticated) =>
isAuthenticated ? MainPage() : LoginPage(),
converter: (Store store) => store.state.authState.isAuthenticated??false,
),
'/main':(BuildContext context) => MainPage(),
'/login':(BuildContext context) => LoginPage(),
},
);
},
)),
);
}
}
切换语言代码,主要代码store.dispatch(changeLocale(locale))
class LanguagePageState extends State{
List supports = [];
@override
Widget build(BuildContext context) {
supports.clear();
supports.addAll(I18nUtil.getSupportLanguagesSort(context));
return Scaffold(
appBar: AppBar(
title: Text(I18nUtil.getS(context).multi_language),
),
body: StoreConnector(
converter: (store){
return (Locale locale)=>store.dispatch(changeLocale(locale));
},
builder: (context,changeLocale){
return ListView.builder(
itemCount: supports.length,
itemBuilder: (BuildContext context,int index){
LanguageModel model = supports[index];
return ListTile(
title: Text(model?.title),
onTap: ()=>switchLanguage(model.locale,changeLocale),
trailing: new Radio(
value: true,
groupValue: model.isSelected == true,
activeColor: colorStyles['primary'],
onChanged: (value) {
switchLanguage(model.locale,changeLocale);
}),
);
},
);
},
),
);
}
switchLanguage(Locale locale,changeLocale){
setState(() {
changeLocale(locale);
});
}
}
iOS工程还需要单独进行本地化配置,添加所支持的语言,如下图:
项目地址(国际化代码在tag:v_language):
https://github.com/xiaojinwei/FlutterBasic