Flutter 国际化
1.介绍
App内可能会使用不同的语言,进行国际化是有必要的, 而且是开发时就应该首先准备的,等到后期维护阶段在进行国际化,会非常麻烦,建议在开发时就准备国际化,方便后期维护
2.使用
依赖:
flutter_localizations:
sdk: flutter
flutter_cupertino_localizations: ^1.0.1 //适配IOS
1.自定义LocalizationsDelegate
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_first/day56Language/day3/MyLocations.dart';
class MyLocationDelegate extends LocalizationsDelegate<MyLocations> {
// 1.
static MyLocationDelegate delegate = MyLocationDelegate();
// 2.
@override
bool isSupported(Locale locale) {
return ["en", "zh"].contains(locale.languageCode);
}
// 3
@override
Future<MyLocations> load(Locale locale) async {
return SynchronousFuture(MyLocations(locale));
}
// 4
@override
bool shouldReload(MyLocationDelegate old) {
return false;
}
}
- isSupported:用于当前环境的Locale,是否在我们支持的语言范围
- shouldReload:当Localizations Widget重新build时,是否调用load方法重新加载Locale资源
- 一般情况下,Locale资源只应该在Locale切换时加载一次,不需要每次Localizations重新build时都加载一遍;
- 所以一般情况下返回false即可;
- load方法:当Locale发生改变时(语言环境),加载对应的HYLocalizations资源
- 这个方法返回的是一个Future,因为有可能是异步加载的;
- 但是我们这里是直接定义的一个Map,因此可以直接返回一个同步的
2.定义资源
import 'package:flutter/material.dart';
class MyLocations {
final Locale locale;
MyLocations(this.locale);
static Map<String, Map<String, String>> _localizedValues = {
"en": {
"title": "home",
"greet": "hello~",
},
"zh": {
"title": "首页",
"greet": "你好~",
}
};
String get title {
return _localizedValues[locale.languageCode]["title"];
}
String get greet {
return _localizedValues[locale.languageCode]["greet"];
}
}
3.设置MaterialApp
1.localizationsDelegates
指定哪些Widget需要进行国际化
- 用于生产本地化值集合的工厂
- 我们这里指定了Material、Widgets、Cupertino都使用国际化
localizationsDelegates: [
GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值
GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格
GlobalWidgetsLocalizations.delegate, // 指定默认的文本排列方向, 由左到右或由右到左
//1.注意,一定要添加自己的国际化
MyLocationDelegate.delegate
],
2.如果要指定语言代码、文字代码和国家代码,支持的
supportedLocales: [
const Locale.fromSubtags(languageCode: 'en'),
const Locale.fromSubtags(languageCode: 'zh'),
],
3.显示
Localizations.of(context, MyLocations).title
稍微封装一下
static MyLocations of(BuildContext context) {
return Localizations.of(context, MyLocations);
}
代码如下
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/cupertino.dart';
import 'MyAppLocationDelegate.dart';
import 'MyLocations.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值
GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格
GlobalWidgetsLocalizations.delegate, // 指定默认的文本排列方向, 由左到右或由右到左
MyLocationDelegate.delegate
],
locale: Locale("en"),
// supportedLocales: [Locale("en"), Locale("zh")],
supportedLocales: [
const Locale.fromSubtags(languageCode: 'en'),
const Locale.fromSubtags(languageCode: 'zh'),
],
home: Scaffold(
//标题栏
appBar: AppBar(
title: Text("Flutter Demo"),
),
//内容区域
body: LanguageView(),
),
);
}
}
class LanguageView extends StatefulWidget {
LanguageView({Key key}) : super(key: key);
@override
_LanguageViewState createState() {
return _LanguageViewState();
}
}
class _LanguageViewState extends State<LanguageView> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Text(MyLocationDelegate.of(context).title),
);
}
}
3.异步加载数据
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AppLocalizations {
// 1
final Locale locale;
AppLocalizations(this.locale);
// 2
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static Map<String, Map<String, String>> _localizedStrings = {};
// 3 异步加载数据
Future loadJson() async {
final jsonString = await rootBundle.loadString("assets/json/i18n.json");
Map<String, dynamic> map = json.decode(jsonString);
_localizedStrings = map.map((key, value) => MapEntry(key, value.cast<String, String>()));
}
// 4
String get title => _localizedStrings[this.locale.languageCode]["title"];
String get button => _localizedStrings[this.locale.languageCode]["button"];
String get drawerTip =>
_localizedStrings[this.locale.languageCode]["drawer_tip"];
String get closeDrawer =>
_localizedStrings[this.locale.languageCode]["close_drawer"];
}
在自定义的LocalizationsDelegate中
// 3
@override
Future<AppLocalizations> load(Locale locale) async {
final appLocalizations = AppLocalizations(locale);
await appLocalizations.loadJson();
return appLocalizations;
}
4.国际化
1.下载插件Flutter Intl
2.配置文件(为整个项目)
会在 pubspec.yaml中增加以下字段**
flutter_intl:
enabled: true
会在lib目录下增加 generated 和 l10n两个包
generated包下的intl包默认存在一个messages_all.dart和messages_en.dart文件,messages开头的文件会在添加语言后自动生成
l10n包下存在一个intl_en.arb文件
3.使用Add Locale
生成其他语言的arb文件
例如:zh_CN , 会在l10n包下生成Intl_zh_CN.arb文件 , 可在其中填入对应的文案.
4.在MaterialApp中配置
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
localizationsDelegates: const [
S.delegate, //记得这个一定要写
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
localeListResolutionCallback: (locales, supportedLocales) {
print(locales);
return;
},
locale: Locale('zh','TW'),
home: Scaffold(
//标题栏
appBar: AppBar(
title: Text("Flutter Demo"),
),
//内容区域
body: LanguageView(),
),
);
}
5.使用
child: Text(S.of(context).title),
注意:
- 假设系统语言为
zh_TW
, 项目并不支持,会优先寻找zh
的其他语言 , 以supportedLocales
顺序从上向下寻找. - 支持语言的顺序为字符串排序 (如zh_A会在zh_B之前) , 并非添加语言的顺序.
完整代码:
import 'package:flutter/material.dart';
import 'package:flutter_first/generated/l10n.dart';
import 'package:get/get.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return GetMaterialApp(
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
///当传入的是不支持的语种,可以根据这个回调,返回相近,并且支持的语种
localeResolutionCallback: (local, support) {
///当前软件支行的语言 也就是[supportedLocales] 中配制的语种
if (support.contains(local)) {
print('support $local');
return local;
}
///如果当前软件运行的手机环境不在 [supportedLocales] 中配制的语种范围内
///返回一种默认的语言环境,这里使用的是中文
print('no_support local is $local and support is $support');
return const Locale('zh', 'CN');
},
locale: Locale('zh', 'TW'),
home: Scaffold(
//标题栏
appBar: AppBar(
title: Text("Flutter Demo"),
),
//内容区域
body: LanguageView(),
),
);
}
}
class LanguageView extends StatefulWidget {
LanguageView({Key key}) : super(key: key);
@override
_LanguageViewState createState() {
return _LanguageViewState();
}
}
class _LanguageViewState extends State<LanguageView> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Text(S.of(context).title),
);
}
}
————————localeListResolutionCallback可以不用写,intl自由规则识别,写了反而不好
5.使用Get切换语言
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Column(
children: [
OutlinedButton(onPressed: (){
var locale = Locale.fromSubtags(languageCode: 'en', countryCode: 'US2');
Get.updateLocale(locale);
}, child: Text('点击')),
Text(S.of(context).title)
],
),
);
}
6.使用Provider切换
if (i != null) {
if (i == 1) {
Provider.of<CurrentLocale>(context, listen: false)
.setLocale(const Locale('zh', "CH"));
} else {
Provider.of<CurrentLocale>(context, listen: false)
.setLocale(const Locale('en', "US"));
}
}