概述
- 国际化的认识
- 国际化的适配
- 国际化的工具
一、国际化的认识
开发一个App,如果我们的App需要面向不同的语种(比如中文、英文、繁体等),那么我们需要对齐进行国际化开发。
国际化的英文称呼:internationalization
(简称为i18n
,取前后两个字母,18表示中间省略字母的个数)
App国际化开发主要包括:文本国际化(包括文本的顺序),Widget显示的国际化:
比如我们下面开发的这个App
某些文本在英文环境下应该显示为英文;
某些Widget在中文环境下,应该显示中文(比如弹出的时间选择器);
二、国际化的适配
- 2.1、Widget的国际化Flutter给我们提供的Widget默认情况下就是支持国际化,但是在没有进行特别的设置之前,它们无论在什么环境都是以英文的方式显示的。如果想要添加其他语言,你的应用必须指定额外的
MaterialApp
属性并且添加一个单独的package
,叫做flutter_localizations
。截至到 2020 年 2 月份,这个 package 已经支持大约 77 种语言。
- 2.1.1、
pubspec.ymal
添加依赖,想要使用 flutter_localizations 的话,我们需要在 pubspec.yaml 文件中添加它作为依赖如下,记得点击Pub get
更新一下
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter
- 2.1.2. 设置
MaterialApp
- 在localizationsDelegates中指定哪些Widget需要进行国际化
- 用于生产
本地化值
集合的工厂 - 我们这里指定了Material、Widgets、Cupertino都使用国际化
- supportedLocales指定要支持哪些国际化:我们这里指定
中文
和英文
(也可以指定国家编码)
MaterialApp( localizationsDelegates: [ // 指定本地化的字符串和一些其他的值 GlobalMaterialLocalizations.delegate, // 对应的Cupertino风格 GlobalCupertinoLocalizations.delegate, // 指定默认的文本排列方向, 由左到右或由右到左 GlobalWidgetsLocalizations.delegate ], supportedLocales: [ Locale('en'), Locale('zh') ], );
提示:如果要指定语言代码、文字代码和国家代码,可以进行如下指定方式:
// Full Chinese support for CN, TW, and HK supportedLocales: [ const Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh' const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans' const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant' const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'), // 'zh_Hans_CN' const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'), // 'zh_Hant_TW' const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'), // 'zh_Hant_HK' ],
- 2.1.3. 查看Widget结果设置完成后,我们在Android上将语言切换为中文,查看结果,我们可以看到英文的 时间Widget里面的文字是 中文了
- 但是对于iOS,将语言切换为中文,依然显示是英文的Widget
- 这是因为iOS定义了一些应用的元数据,其中包括支持的语言环境;
- 我们必须将其对应的元数据中支持的语言添加进去;
- 元数据的设置在iOS项目中对应的info.plist文件中;
- 修改 iOS 的
info.plist
文件配置:
配置完成后,卸载之前的app,重新安装
:我们可以看到和安卓模拟器一样的效果
2.2、其它文本国际化
App中除了有默认的Widget,我们也希望对自己的文本进行国际化
- 2.2.1、创建本地化类,该类用于定义我们需要进行本地化的字符串等信息:
- 1.我们需要一个构造器,并且传入一个Locale对象(后续会使用到)
- 2.定义一个Map,其中存放我们不同语言对应的显示文本
- 3.定义它们对应的getter方法,根据语言环境返回不同的结果
import 'package:flutter/material.dart'; class JKLocalizations { final Locale locale; JKLocalizations(this.locale); static JKLocalizations of(BuildContext context) { return Localizations.of(context, JKLocalizations); } static Map<String, Map<String, String>> _localizedValues = { "en": { "title": "home", "greet": "hello~", "picktime": "Pick a Time" }, "zh": { "title": "首页", "greet": "你好~", "picktime": "选择一个时间" } }; // 标题 String get title { return _localizedValues[locale.languageCode]["title"]; } // 问候 String get greet { return _localizedValues[locale.languageCode]["greet"]; } // 时间 String get pickTime { return _localizedValues[locale.languageCode]["picktime"]; } }
- 2.2.2、自定义Delegate
- 上面的类定义好后,我们在什么位置或者说如何对它进行初始化呢?
- 答案是我们可以像Flutter Widget中的国际化方式一样对它们进行初始化;
- 也就是我们也定义一个对象的Delegate类,并且将其传入localizationsDelegates中;
- Delegate的作用就是当Locale发生改变时,调用对应的load方法,重新加载新的Locale资源;
JKLocalizationsDelegate
需要继承自LocalizationsDelegate
,并且有三个方法必须重写:
- isSupported:用于当前环境的Locale,是否在我们支持的语言范围
- shouldReload:当Localizations Widget重新build时,是否调用load方法重新加载Locale资源
- 一般情况下,Locale资源只应该在Locale切换时加载一次,不需要每次Localizations重新build时都加载一遍;
- 所以一般情况下返回false即可;
- load方法:当Locale发生改变时(语言环境),加载对应的HYLocalizations资源
- 这个方法返回的是一个Future,因为有可能是异步加载的;
- 但是我们这里是直接定义的一个Map,因此可以直接返回一个同步的Future(SynchronousFuture)
class JKLocalizationsDelegate extends LocalizationsDelegate<JKLocalizations> { @override // 支持的语言 bool isSupported(Locale locale) { return ["en", "zh"].contains(locale.languageCode); } @override bool shouldReload(LocalizationsDelegate<JKLocalizations> old) { return false; } @override Future<JKLocalizations> load(Locale locale) { return SynchronousFuture(JKLocalizations(locale)); } static JKLocalizationsDelegate delegate = JKLocalizationsDelegate(); }
- 2.2.3. 使用本地化类
- 1>、 在
MaterialApp
的localizationsDelegates
里里添加我们的 delegeate
- 2>、接着我们可以在代码中使用JKLocalization类。
我们可以通JKYLocalizations.of(context)
获取到JKLocalizations
对象,本质上是在调用Localizations.of(context, JKLocalizations)
Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(JKLocalizations.of(context).greet), RaisedButton( child: Text(JKLocalizations.of(context).pickTime, style: TextStyle(fontSize: 20, color: Colors.orange),), onPressed: () { showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(3000) ); } ) ], ),
- 2.2.4. 异步加载数据,也就是 JKLocalizations 里面的 _localizedValues 我们做一个抽取
static Map<String, Map<String, String>> _localizedValues = {}; // 获取配置的 Map Future<bool> loadJson() async { // 1.加载json文件 String jsonString = await rootBundle.loadString("assets/json/i18n.json"); // 2.转成map类型 final Map<String, dynamic> map = json.decode(jsonString); // 3.注意:这里是将Map<String, dynamic>转成Map<String, Map<String, String>>类型 _localizedValues = map.map((key, value) { return MapEntry(key, value.cast<String, String>()); }); return true; }
提示:这里我们也可以改为网络请求,拿到数据后一样转换类型
- 在
JKLocalizationsDelegate
中使用异步
进行加载:
@override Future<JKLocalizations> load(Locale locale) async { final localization = JKLocalizations(locale); await localization.loadJson(); return localization; }
提示:我们自己的配置 其它文本国际化,我们每次都要
- 1、修改 json文件
- 2、在 JKLocalizations 里面增加 对应的 get 方法,每次还要写 key ,很容易写错,要注意
- 3、如果增加新的语言,我们还要 修改JKLocalizationsDelegate 增加支持的语言 和 在 iOS的 info.plist 文件里面增加新的语言类型