Flutter的国际化方式
目录
- 引言
- 系统控件的多语言配置
- yaml文件的配置
- app增加多语言配置
- 对app的改写
- 自定义文本的多语言配置
引言
国际化(Localization)对于app来说是一个非常常见的需求。得益于flutter的StatefulWidge
,实时切换app的语言环境是非常简单的。
flutter的Localization包含两个部分,预设控件的Localization配置以及自定义文本的Localization配置。
系统控件的多语言配置
flutter自带很多预设的控件,这些控件使用到的文本是可以根据app设定的语言环境来展示相应的语言文本的。默认的情况下,这些控件使用的是英文文案,即使你的手机系统是中文环境,flutter的控件仍然展示的是英文文案。
举个例子,我们使用flutter的DatePicker来演示一下:
import 'package:flutter/material.dart';
import 'widget/DemoWidget.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(// ignore: non_constant_identifier_namestitle: "test",theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue,),home: DemoWidget(),);}
}
main.dart
import 'package:flutter/material.dart';class DemoWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return new Scaffold(appBar: AppBar(title: new Text('Demo页面'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text(Localizations.localeOf(context).toString()),MaterialButton(child: Text('选择时间'),color: Colors.grey,onPressed: () {showDatePicker(context: context,initialDate: DateTime.now(),firstDate: DateTime(2018),lastDate: DateTime(2030));},),],),),);}
}
demoWidget.dart
我们先确认一下系统的语言设置确实是中文的:
然后我们看一下运行结果:
可以看见,即使系统的语言是中文的,由于没有在flutter app中配置多语言支持,预设控件使用的语言仍然默认为英文。
要使预设控件使用的语言与系统的语言保持一致,我们需要进行如下配置:
yaml文件的配置
首先我们需要在pubspec.yaml
文件中的dependencies
下,增加flutter_localizations
的配置,修改之后在terminal执行一下flutter packages get
,或者在android studio的yaml文件右上角直接点击Packages get按钮。
denpendencies:flutter:sdk: flutter# 以下是新增部分flutter_loclizations:sdk: flutter
app增加多语言配置
在main.dart中增加import项
import 'package:flutter_localizations/flutter_localizations.dart';
并且为MaterialApp的构造函数增加localizationsDelegates
和supportedLocales
参数的赋值。
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';import 'widget/demoWidget.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(// ignore: non_constant_identifier_namestitle: "test",theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue,),home: DemoWidget(),/*=====以下为新增部分========*/localizationsDelegates: [GlobalMaterialLocalizations.delegate,GlobalWidgetsLocalizations.delegate,],supportedLocales: [Locale('zh', 'CH'), Locale('en', 'US')],/*=====以上为新增部分========*/);}
}
supportedLocales
这个参数很好理解,我们看一下localizationsDelegates
这个参数的注释:
/// Internationalized apps that require translations for one of the locales/// listed in [GlobalMaterialLocalizations] should specify this paramter/// and list the [supportedLocales] that the application can handle.
意思是说国际化的(在GlobalMaterialLocalization
支持的语言范围内的)的app需要指定这个参数,并且需要同时指定supportedLocales
这个参数。
跟踪localizationsDelegates
这个参数,发现它一路传递到了Localizations
这个类里。
Localizations
是一个StatefulWidget
,我们可以在_LocalizationsState
的void load(Locale locale)
方法中看到这个类对Delegates
的初始化和使用。
@overridevoid initState() {super.initState();load(widget.locale);}@overridevoid didUpdateWidget(Localizations old) {super.didUpdateWidget(old);if (widget.locale != old.locale|| (widget.delegates == null && old.delegates != null)|| (widget.delegates != null && old.delegates == null)|| (widget.delegates != null && _anyDelegatesShouldReload(old)))load(widget.locale);}void load(Locale locale) {final Iterable<LocalizationsDelegate<dynamic>> delegates = widget.delegates;if (delegates == null || delegates.isEmpty) {_locale = locale;return;}Map<Type, dynamic> typeToResources;final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(locale, delegates).then<Map<Type, dynamic>>((Map<Type, dynamic> value) {return typeToResources = value;});if (typeToResources != null) {// All of the delegates' resources loaded synchronously._typeToResources = typeToResources;_locale = locale;} else {// - Don't rebuild the dependent widgets until the resources for the new locale// have finished loading. Until then the old locale will continue to be used.// - If we're running at app startup time then defer reporting the first// "useful" frame until after the async load has completed.WidgetsBinding.instance.deferFirstFrameReport();typeToResourcesFuture.then<void>((Map<Type, dynamic> value) {WidgetsBinding.instance.allowFirstFrameReport();if (!mounted)return;setState(() {_typeToResources = value;_locale = locale;});});}}
结合我们对StatefulWidget
生命周期的理解,至此我们已经知道flutter是如何去初始化Delegates
和更新他的locale的了。具体如何去绑定资源的我们不再深入去看。
GlobalMaterialLocalizations
这个类点进去看,可以知道它是提供了一些预设控件的多语言文案,而GlobalWidgetsLocalizations
点进去看,则可以看到它是对文本排列是从左到右还是从右到左作了支持。由于文本排列从右到左的语言只有阿拉伯语、希伯来语、波斯语、普图什语和乌尔都语,如果你的app不支持这些语言的话,这个参数可以不添加也没关系。
对app的改写
由于我们需要对app的语言环境进行切换,也就意味着app是要保存当前选择的语言状态的,所以我们的app应该使用StatefulWidget
来保存以及更新它的状态。
在main.dart
中找到你的app类,将你的app类改为继承自StatefulWidget
,并且创建它的State类:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';import 'widget/demoWidget.dart';void main() => runApp(MyApp());class MyApp extends StatefulWidget {@overrideState<StatefulWidget> createState() {return AppState();}
}class AppState extends State<MyApp> {Locale _locale;List<Locale> supportedLocales = [Locale('zh', 'CH'), Locale('en', 'US')];void changeLocale(Locale locale) {if (supportedLocales.map((locale) {return locale.languageCode;}).toSet().contains(locale?.languageCode)) {setState(() {_locale = locale;});}}@overrideWidget build(BuildContext context) {return MaterialApp(// ignore: non_constant_identifier_namestitle: "myApp",locale: _locale,theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue,),home: DemoWidget(),localizationsDelegates: [GlobalMaterialLocalizations.delegate,GlobalWidgetsLocalizations.delegate,],supportedLocales: supportedLocales,);}
}
这样一来,每当我们调用app的changeLocale
方法,更新MaterialApp
的locale
属性,app就会将其语言更新为新的语言并且更新整个widget树。
自定义文本的多语言配置
通过上面的步骤我们可以知道,每当我们通过setState
方法改变MaterialApp
的locale
,会触发Widget的更新。因此我们可以写一个我们自己的LocalizationsDelegate
,将其赋值到MaterialApp
的localizationsDelegates
参数中即可。下面我们来按照GlobalWidgetsLocalizations
仿写一下。
class GlobalWidgetsLocalizations implements WidgetsLocalizations {/// Creates an object that provides localized resource values for the/// lowest levels of the Flutter framework.////// This method is typically used to create a [LocalizationsDelegate]./// The [WidgetsApp] does so by default.static Future<WidgetsLocalizations> load(Locale locale) {return SynchronousFuture<WidgetsLocalizations>(GlobalWidgetsLocalizations(locale));}......static const LocalizationsDelegate<WidgetsLocalizations> delegate = _WidgetsLocalizationsDelegate();
}
class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {const _WidgetsLocalizationsDelegate();@overridebool isSupported(Locale locale) => true;@overrideFuture<WidgetsLocalizations> load(Locale locale) => GlobalWidgetsLocalizations.load(locale);@overridebool shouldReload(_WidgetsLocalizationsDelegate old) => false;@overrideString toString() => 'GlobalWidgetsLocalizations.delegate(all locales)';
}
参照GlobalWidgetsLocalizations
,我们新建一个Translations.dart,在内声明Translations
类和_TranslationsDelegate
类,
import 'dart:async';import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';class Translations {Locale _locale;// 多语言文本资源,为了演示上的方便,将文本资源放到这个map里。// 实际工程中可以将资源放到本地的文件中,通过rootBundle去加载。// 也可以将资源放到服务器上,通过网络请求加载。Map<String, Map<String, String>> _resourceMap = {'zh': {'btnTextZh': '中文文案','pageTitle': '演示页面','btnTextEn': '英文文案'},'en': {'btnTextZh': 'lang:zh','pageTitle': 'demo page','btnTextEn': 'lang:en'},};Translations(this._locale);String text(textKey) {return _resourceMap[_locale.languageCode][textKey];}static Translations of(BuildContext context) {return Localizations.of<Translations>(context, Translations);}// 加载资源的方式。// 可以看到这个方法返回的类型是一个Future// 因为我们可以将多语言文本资源放到服务端或者本地文件里,// 因此加载多文本资源可能是耗时的,所以这里返回的类型是Future// 这里为了演示上的方便,将文本资源直接硬编码到代码里了。static Future<Translations> load(Locale locale) async {return SynchronousFuture<Translations>(Translations(locale));}static const _TranslationDelegate delegate = _TranslationDelegate();
}class _TranslationDelegate extends LocalizationsDelegate<Translations> {const _TranslationDelegate();@overridebool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);@overrideFuture<Translations> load(Locale locale) => Translations.load(locale);@overridebool shouldReload(LocalizationsDelegate<Translations> old) => false;
}
在使用的地方,我们可以通过Translations
类的of
方法获取Translations
类的实例。为了方便我们修改AppState
的Locale
,我们在AppState
中设置一个static
的变量供我们使用。当然,这只是为了演示上的方便,实际工程中我们可以使用单例,或者使用flutter-redux来保存app的状态。
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';import 'localization/translations.dart';
import 'widget/demoWidget.dart';void main() => runApp(MyApp());class MyApp extends StatefulWidget {@overrideState<StatefulWidget> createState() {return AppState();}
}class AppState extends State<MyApp> {// 供外部使用的_AppSetting实例,用于修改app的状态static _AppSetting setting = _AppSetting();@overridevoid initState() {super.initState();setting.changeLocale = (Locale locale) {if (setting.supportedLocales.map((locale) {return locale.languageCode;}).toSet().contains(locale?.languageCode)) {setState(() {setting._locale = locale;});}};}@overrideWidget build(BuildContext context) {return MaterialApp(// ignore: non_constant_identifier_namestitle: "myApp",locale: setting._locale,theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue,),home: DemoWidget(),localizationsDelegates: [// 不要忘了将Translates.delegate添加到localizationsDelegates的列表中Translations.delegate,GlobalMaterialLocalizations.delegate,GlobalWidgetsLocalizations.delegate,],supportedLocales: setting._supportedLocales,);}
}class _AppSetting {_AppSetting();Null Function(Locale locale) changeLocale;Locale _locale;List<Locale> _supportedLocales = [Locale('zh', 'CH'), Locale('en', 'US')];
}
此外我们再新建一个演示页面来演示app内的语言环境切换:
import 'package:demoApp/localization/translations.dart';
import 'package:demoApp/main.dart';
import 'package:flutter/material.dart';class DemoWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return new Scaffold(appBar: AppBar(title: Text(Translations.of(context).text("pageTitle")),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [MaterialButton(child: Text(Translations.of(context).text('btnTextZh')),color: Colors.grey,onPressed: () {AppState.setting.changeLocale(Locale('zh'));}),MaterialButton(child: Text(Translations.of(context).text('btnTextEn')),color: Colors.grey,onPressed: () {AppState.setting.changeLocale(Locale('en'));})],),),);}
}
至此我们已经完成了flutter app的国际化配置。
Flutter的国际化方式相关推荐
- Flutter 语言国际化状态管理
Flutter语言国际化,手动切换App语言 1.配置字体文件:custom_localization.dart import 'dart:async';import 'package:flutter ...
- Flutter - International 国际化,Localization 本地化, 使用Intl
新建项目,得到一个示例工程.本例中使用intl包来管理文字资源. 项目地址: https://github.com/RustFisher/localization_demo 步骤: 添加依赖项 - i ...
- Flutter网络请求方式总结
转载于:https://www.jianshu.com/p/59fc5ed37453 前言 编写一个 App,最离不开的就是网络请求了.在Android 原生中,网络请求库一直在更新,网络请求库甚多: ...
- 浅谈Flutter跨平台调用方式MethodChannel
Flutter是目前非常流行的跨平台方案,由于它的性能接近于原生应用,因而被越来越多的开发者所采用.既然是跨平台方案,那么久必然存在调用系统功能的需求,在Flutter中,Flutter层与nativ ...
- flutter 关于国际化l10n失效问题
前言 在flutter开发过程中,可能下载他人的demo,而demo中有做国际化,在第一次运行的时候可能发现l10n不生效的问题: import 'package:flutter_learning/g ...
- Flutter之国际化多语言
1.用多个文件来配各个国家的语言: 在lib 文件夹中创建新文件夹名为locale,目前文件配置支持中文和美文: lib/locale/i18n_zh.json lib/locale/i18n_en. ...
- 国际象棋ai下载_国际象棋AI的解剖
国际象棋ai下载 Chess-playing programs made their grand debut in the 50's. They were unsurprisingly fairly ...
- Flutter实践:深入探索 flutter 中的状态管理方式(1)
利用 Flutter 内置的许多控件我们可以打造出一款不仅漂亮而且完美跨平台的 App 外壳,我利用其特性完成了类似知乎App的UI界面,然而一款完整的应用程序显然不止有外壳这么简单.填充在外壳里面的 ...
- flutter 全选_Flutter ios 国际化(复制粘贴 中英文切换等问题)
前提 在做flutter ios 国际化的时候遇到长按文本框崩溃的问题,然后google到一堆写法是重写cupertinoLocalization的奇怪做法,然后还千篇一律都是这么改的,其实不用那么麻 ...
最新文章
- spring MVC做form提交Neither BindingResult nor plain target object for bean name 'command' available...
- spring中Converter如何注入的
- asp.net中此页的状态信息无效,可能已损坏的解决之道
- SQLServer之事务简介
- 第五章(1)Libgdx应用框架之生命周期
- pom.xml中依赖的optionaltrue/optional标签
- Linux 部分命令无法使用-bash: /usr/bin/*: Permission denied
- Docker:(四)docker网络模式
- 2年前端 杭州 面试 集合 面经 前端
- 计算机领域顶级会议列表
- gulp压缩html
- linux内top命令,Linux中的top命令的详细解释
- 请将磁盘插入“U盘(F)“
- 智禾教育:淘宝店铺的常见类型有哪些,智禾为你分享介绍
- 解密刷子是如何进行APP刷量的?
- 删除 Windows 的默认打开方式
- vue+高德地图实现地图搜索及点击定位
- linux生成随机数
- ch340c(cH340C与8266)
- 网站建设服务办理增值电信业务经营许可证