文章目录
国际化就是让我们的应用支持多种语言,例如运行在国内的使用中文简体、在港澳台的使用繁体字、美国的使用英文、日本的用户显示的是日文等等类似场景,也可以把国际化称为本地化处理。Flutter 本身的 API 是支持国际化处理的,当然也可以用官方提供的插件库来实现。
那么这节课我们将介绍 Flutter 中应用国际化处理的基本使用详解,并配合一些案例。
1.实现应用国际化
如果我们的应用想提供多种语言模式,那么就需要进行国际化处理,Flutter 本身是支持国际化处理的。
在 Flutter 中使用国际化一般要配合 MaterialApp 或 WidgetsApp 的国际化属性localizationsDelegates 和 supportedLocales。并且在 pubspec.yaml 配置 flutter_localizations 的一个单独包。截至 2017 年 10 月,该软件包支持 15 种语言(来源于官方)。
接下来我们看下 Flutter 实现国际化的步骤。
首先需要配置 pubspec.yaml:
dependencies:
flutter:
sdk: flutter
// 添加国际化包
flutter_localizations:
sdk:
接下来在使用的页面导入包:
import 'package:flutter_localizations/flutter_localizations.dart';
使用 MaterialApp 或 WidgetsApp 的属性来配置:
class LocalizationsSamplesState extends State<LocalizationsSamples> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
// 这里要自己实现一个Localizations.delegate
],
supportedLocales: [
const Locale('en', 'US'), // English
const Locale('zh', 'CN'), // Chinese
// ... 其他语言支持
],
home: getBody(),
);
}
...
}
supportedLocales 里定义的是语种,Locale 来定义语言语种,参数包括语言和国家两个标志。
接下来我们需要自己实现一个 GlobalMaterialLocalizations,本地化需要 Localizations 和Delegate 两个类才可以,我们先实现 Localizations:
import 'package:flutter/widgets.dart';
class PageLocalizations {
final Locale locale;
PageLocalizations(this.locale);
static Map<String, Map<String, String>> _localizedValues = {
'en': {
'task title': 'Flutter Demo',
'titlebar title': 'Flutter Demo Home Page',
'click tip': 'You have pushed the button this many times:',
'inc': 'Increment'
},
'zh': {
'task title': 'Flutter 示例',
'titlebar title': 'Flutter 示例主页面',
'click tip': '你一共点击了这么多次按钮:',
'inc': '增加'
}
};
get taskTitle {
return _localizedValues[locale.languageCode]['task title'];
}
get titleBarTitle {
return _localizedValues[locale.languageCode]['titlebar title'];
}
get clickTop {
return _localizedValues[locale.languageCode]['click tip'];
}
get inc {
return _localizedValues[locale.languageCode]['inc'];
}
static PageLocalizations of(BuildContext context) {
return Localizations.of(context, PageLocalizations);
}
}
接下来实现 Delegate:
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_samples/samples/pageLocalizations.dart';
class GlobalPagesLocalizations
extends LocalizationsDelegate<PageLocalizations> {
const GlobalPagesLocalizations();
// 是否支持某个语言
@override
bool isSupported(Locale locale) {
return ['en', 'zh'].contains(locale.languageCode);
}
// 加载对应的语言资源,自动调用
@override
Future<PageLocalizations> load(Locale locale) {
return new SynchronousFuture<PageLocalizations>(
new PageLocalizations(locale));
}
// 重新加载
@override
bool shouldReload(LocalizationsDelegate<PageLocalizations> old) {
return false;
}
static GlobalPagesLocalizations delegate = const GlobalPagesLocalizations();
}
最后调用使用即可:
class LocalizationsSamples extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return LocalizationsSamplesState();
}
}
class LocalizationsSamplesState extends State<LocalizationsSamples> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalPagesLocalizations.delegate,
],
supportedLocales: [
const Locale('en', 'US'), // English
const Locale('zh', 'CN'), // Chinese
// ... 其他语言支持
],
home: WelcomePage(),
);
}
}
class WelcomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return WelcomeState();
}
}
class WelcomeState extends State<WelcomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Localizations'),
primary: true,
),
body: Column(
children: <Widget>[
// 调用国际化后的属性资源
Text(PageLocalizations.of(context).taskTitle,)
],
),
);
}
}
可以看到调用的方式是:PageLocalizations.of(context).taskTitle
。
这样,当我们的手机在切换语言环境时,应用便会自动显示当前语言环境下的字符资源,达到国际化目的。
2.使用插件库实现应用国际化
接下来我们看下通过插件库来实现的应用国际化的用法,如 intl 和 flutter_i18n 插件。这两个插件库都是 Flutter 官方的,这两个基本原理是一样的,只不过 flutter_i18n 是自动化执行了将 arb文件转为 dart 的等操作。那么这里直接以 flutter_i18n 插件为例给大家讲解通过插件库实现应用国际化。这里需要使用 Android Studio 安装一个 Flutter i18n 插件:
Flutter i18n 插件可以帮助我们简化手动编写 arb 和生成 dart 这个过程。其原理是通过 arb 文件来自动生成所需要的代码。
插件安装完后悔自动出现一个按钮,按这个按钮就可以自动根据项目根目录的 res 里的 arb 文件来自动在 lib/generated 生成名字叫 i18n.dart 的 dart 文件。
我们可以右键新建新的需要支持的语言 arb 文件。
我们分别看下 strings_en.arb、strings_zh_CN.arb、i18n.dart 文件内容:
// i18n.dart文件内容,这个是自动生成
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes
//This file is automatically generated. DO NOT EDIT, all your changes would be lost.
class S implements WidgetsLocalizations {
const S();
static const GeneratedLocalizationsDelegate delegate =
GeneratedLocalizationsDelegate();
static S of(BuildContext context) => Localizations.of<S>(context, S);
@override
TextDirection get textDirection => TextDirection.ltr;
String get appName => "App Name";
String get title => "My Title";
String hello(String name) => "Hello $name";
}
class $zh_HK extends S {
const $zh_HK();
@override
TextDirection get textDirection => TextDirection.ltr;
@override
String get appName => "應用名";
@override
String get title => "我的標題";
@override
String hello(String name) => "妳好${name}";
}
class $en extends S {
const $en();
}
class $zh_CN extends S {
const $zh_CN();
@override
TextDirection get textDirection => TextDirection.ltr;
@override
String get appName => "应用名";
@override
String get title => "我的标题";
@override
String hello(String name) => "你好${name}";
}
class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
const GeneratedLocalizationsDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
Locale("zh", "HK"),
Locale("en", ""),
Locale("zh", "CN"),
];
}
LocaleListResolutionCallback listResolution({Locale fallback}) {
return (List<Locale> locales, Iterable<Locale> supported) {
if (locales == null || locales.isEmpty) {
return fallback ?? supported.first;
} else {
return _resolve(locales.first, fallback, supported);
}
};
}
LocaleResolutionCallback resolution({Locale fallback}) {
return (Locale locale, Iterable<Locale> supported) {
return _resolve(locale, fallback, supported);
};
}
Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported) {
if (locale == null || !isSupported(locale)) {
return fallback ?? supported.first;
}
final Locale languageLocale = Locale(locale.languageCode, "");
if (supported.contains(locale)) {
return locale;
} else if (supported.contains(languageLocale)) {
return languageLocale;
} else {
final Locale fallbackLocale = fallback ?? supported.first;
return fallbackLocale;
}
}
@override
Future<S> load(Locale locale) {
final String lang = getLang(locale);
if (lang != null) {
switch (lang) {
case "zh_HK":
return SynchronousFuture<S>(const $zh_HK());
case "en":
return SynchronousFuture<S>(const $en());
case "zh_CN":
return SynchronousFuture<S>(const $zh_CN());
default:
// NO-OP.
}
}
return SynchronousFuture<S>(const S());
}
@override
bool isSupported(Locale locale) =>
locale != null && supportedLocales.contains(locale);
@override
bool shouldReload(GeneratedLocalizationsDelegate old) => false;
}
String getLang(Locale l) => l == null
? null
: l.countryCode != null && l.countryCode.isEmpty
? l.languageCode
: l.toString();
再看下 strings_en.arb、strings_zh_CN.arb 文件内容:
// strings_en.arb
{
"appName": "App Name",
"hello": "Hello $name",
"title": "My Title"
}
// strings_zn_CN.arb
{
"appName": "应用名",
"hello": "你好${name}",
"title": "我的标题"
}
可以看到我们也可以通过 $ 符号来进行传递动态值。
flutter_i18n 插件库地址:https://pub.dev/packages/flutter_i18n。
首先我们需要引用这个库:
dependencies:
flutter_i18n: ^0.6.3
在使用的地方导入库:
import 'package:flutter_i18n/flutter_i18n.dart';
然后我们看下使用的方式:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_app/generated/i18n.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
class LocalizationsSamples extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return LocalizationsSamplesState();
}
}
class LocalizationsSamplesState extends State<LocalizationsSamples> {
Locale _locale = const Locale('zh', 'CN');
@override
void initState() {
super.initState();
localeChange = (locale) {
setState(() {
_locale = locale;
});
};
}
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
// 配置delegate
S.delegate,
],
supportedLocales: [
// 支持的语言
const Locale('en', ''), // English
const Locale('zh', 'CN'), // Chinese
const Locale("zh", "HK"),
// ... 其他语言支持
],
// 我们也可以指定一种默认语言
localeResolutionCallback:
S.delegate.resolution(fallback: const Locale('en', '')),
home: WelcomePage(),
);
}
}
class WelcomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return WelcomeState();
}
}
class WelcomeState extends State<WelcomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Localizations'),
primary: true,
),
body: Column(
children: <Widget>[
// 调用国际化后的属性资源
Text(
S.of(context).title,
)
],
),
);
}
}
主动切换语言:
FlutterI18n.refresh(context, Locale('en', ''));
当然还有其他方法:
FlutterI18n.translate(buildContext, "your.key")
FlutterI18n.plural(buildContext, "select", 0)
通过插件库实现国际化就大概这么多,大家可以自己实践下。