文章目錄
- 1.實作應用國際化
- 2.使用插件庫實作應用國際化
國際化就是讓我們的應用支援多種語言,例如運作在國内的使用中文簡體、在港澳台的使用繁體字、美國的使用英文、日本的使用者顯示的是日文等等類似場景,也可以把國際化稱為本地化處理。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)