一. 国际化的认识
国际化的英文称呼:internationalization(简称为i18n)
App国际化开发主要包括:文本国际化(包括文本的顺序),Widget显示的国际化:
- 某些文本在英文环境下应该显示为英文;
- 某些Widget在中文环境下,应该显示中文(比如弹出的时间选择器);
二. 国际化的适配
2.1. Widget的国际化
Flutter给我们提供的Widget默认情况下就是支持国际化,没有进行设置,无论在什么环境都是以英文的方式显示的。如果想要添加其他语言,必须指定额外的 MaterialApp 属性并且添加一个单独的 package,叫做 flutter_localizations。2.1.1. pubspec添加依赖
想要使用 flutter_localizations 的话,需要在 pubspec.yaml 文件中添加它作为依赖:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
2.1.2. 设置MaterialApp
- 在localizationsDelegates中指定哪些Widget需要进行国际化
- 用于生产本地化值集合的工厂
- 这里指定了Material、Widgets、Cupertino都使用国际化
- supportedLocales指定要支持哪些国际化
- 这里指定中文和英文(也可以指定国家编码)
注意:如果要指定语言代码、文字代码和国家代码,可以进行如下指定方式:MaterialApp(localizationsDelegates: [GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格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结果
但是对于iOS,将语言切换为中文,依然显示是英文的Widget
- 这是因为iOS定义了一些应用的元数据,其中包括支持的语言环境;
- 必须将其对应的元数据中支持的语言添加进去;
- 元数据的设置在iOS项目中对应的info.plist文件中;
修改iOS的info.plist文件配置:
- 选择 Information Property List 项;
- 从 Editor 菜单中选择 Add Item,然后从弹出菜单中选择 Localizations;
- 为array添加一项选择 Add Item,选择Chinese;
2.2. 其它文本国际化
App中除了有默认的Widget,也希望对自己的文本进行国际化,如何做到呢?
2.2.1. 创建本地化类
该类用于定义我们需要进行本地化的字符串等信息:
- 1.需要一个构造器,并且传入一个Locale对象
- 2.定义一个Map,其中存放我们不同语言对应的显示文本
- 3.定义它们对应的getter方法,根据语言环境返回不同的结果 ```dart import ‘package:flutter/material.dart’;
class WLLocalizations { final Locale locale;
HYLocalizations(this.locale);
static Map
String get title { return _localizedValues[locale.languageCode][“title”]; }
String get greet { return _localizedValues[locale.languageCode][“greet”]; }
String get pickTime { return _localizedValues[locale.languageCode][“picktime”]; } }
<a name="OQseE"></a>#### 2.2.2. 自定义Delegate上面的类定义好后,在什么位置或者说如何对它进行初始化呢?- 可以像Flutter Widget中的国际化方式一样对它们进行初始化;- 也就是我们也定义一个对象的Delegate类,并且将其传入localizationsDelegates中;- Delegate的作用就是当Locale发生改变时,调用对应的load方法,重新加载新的Locale资源;WLLocalizationsDelegate需要继承自LocalizationsDelegate,并且有三个方法必须重写:- isSupported:用于当前环境的Locale,是否在我们支持的语言范围- shouldReload:当Localizations Widget重新build时,是否调用load方法重新加载Locale资源- 一般情况下,Locale资源只应该在Locale切换时加载一次,不需要每次Localizations重新build时都加载一遍;- 所以一般情况下返回false即可;- load方法:当Locale发生改变时(语言环境),加载对应的HYLocalizations资源- 这个方法返回的是一个Future,因为有可能是异步加载的;- 但是我们这里是直接定义的一个Map,因此可以直接返回一个同步的Future(SynchronousFuture)```dartimport 'package:flutter/cupertino.dart';import 'package:flutter/foundation.dart';import 'package:i18n_demo/i18n/localizations.dart';class HYLocalizationsDelegate extends LocalizationsDelegate<HYLocalizations> {@overridebool isSupported(Locale locale) {return ["en", "zh"].contains(locale.languageCode);}@overridebool shouldReload(LocalizationsDelegate<HYLocalizations> old) {return false;}@overrideFuture<HYLocalizations> load(Locale locale) {return SynchronousFuture(HYLocalizations(locale));}static HYLocalizationsDelegate delegate = HYLocalizationsDelegate();}
2.2.3. 使用本地化类
我们可以通过Localizations.of(context, HYLocalizations)获取到HYLocalizations对象
Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(Localizations.of(context, HYLocalizations).title),),body: Center(child: Column(children: <Widget>[Text(Localizations.of(context, HYLocalizations).greet),RaisedButton(child: Text(Localizations.of(context, HYLocalizations).pickTime),onPressed: () {showDatePicker(context: context,initialDate: DateTime.now(),firstDate: DateTime(2019),lastDate: DateTime(2022)).then((pickTime) {});},)],),),);}
可以对Localizations.of(context, WLLocalizations)进行一个优化
给HYLocalizations定义一个of的静态方法
class WLLocalizations {static WLLocalizations of(BuildContext context) {return Localizations.of(context, WLLocalizations);}}
使用:
appBar: AppBar(title: Text(WLLocalizations.of(context).title),)
2.2.4. 异步加载数据
假如数据是异步加载的,比如来自Json文件或者服务器,应该如何处理呢?
修改WLLocalizations的数据加载: ```dart static Map> _localizedValues = {}; Future
loadJson() async { // 1.加载json文件 String jsonString = await rootBundle.loadString(“assets/json/i18n.json”); // 2.转成map类型 final Map
map = json.decode(jsonString); // 3.注意:这里是将Map
转成Map >类型 _localizedValues = map.map((key, value) { return MapEntry(key, value.cast<String, String>());
}); return true; }
在HYLocalizationsDelegate中使用异步进行加载:
@override
Future
<a name="DJhS5"></a>## 三. 国际化的工具<a name="xodHQ"></a>### 3.1. 认识arb文件目前已经可以通过加载对应的json文件来进行本地化了。有另外一个问题,我们在进行国际化的过程中,下面的代码依然需要根据json文件手动编写:```dartString get title {return _localizedValues[locale.languageCode]["title"];}String get greet {return _localizedValues[locale.languageCode]["greet"];}String get pickTime {return _localizedValues[locale.languageCode]["picktime"];}
arb文件可以快速在本地化文件-dart代码文件直接来转换:
- arb文件全称Application Resource Bundle,表示应用资源包,目前已经得到Google的支持;
其本质就是一个json文件,但是可以根据该文件转成对应的语言环境;
3.2. intl package
3.3. 使用IDE插件
插件:Flutter Intl
该插件更新维护频率很高,并且广受好评;
-
3.3.1. 安装插件
3.3.2. 初始化intl
选择工具栏Tools - Flutter Intl - Initialize for the Project
完成上面的操作之后会自动生成如下文件目录: generated是自动生成的dart代码
-
3.3.3. 使用intl
在localizationsDelegates中配置生成的class,名字是S
1.添加对应的delegate
2.supportedLocales使用S.delegate.supportedLocales
localizationsDelegates: [GlobalMaterialLocalizations.delegate,GlobalWidgetsLocalizations.delegate,GlobalCupertinoLocalizations.delegate,HYLocalizationsDelegate.delegate,S.delegate],supportedLocales: S.delegate.supportedLocales,
因为目前还没有对应的本地化字符串,所以需要在intl_en.arb文件中编写:
编写后ctrl(command) + s保存即可
{
“title”: “home”,
“greet”: “hello~”,
“picktime”: “Pick a time”
}
使用
编写intl_zh.arb文件:
{
“title”: “首页”,
“greet”: “您好~”,
“picktime”: “选择一个时间”
}
3.4. arb其它语法
如果我们希望在使用本地化的过程中传递一些参数:
- 比如hello kobe或hello james
- 比如你好啊,李银河 或 你好啊,王小波
修改对应的arb文件:
- {name}:表示传递的参数
{
“title”: “home”,
“greet”: “hello~”,
“picktime”: “Pick a time”,
“sayHello”: “hello {name}”
}
使用:
Text(S.of(context).sayHello("李银河")),
