资料
宁皓网 20 Flutter移动应用:国际化
Flutter实战*第二版 第十三章 国际化我的GIT仓库 / 分支: learn-localizations-ninghao
环境
pubspec.yaml
name: flutter_project
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
dio: ^4.0.4
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.17.0
dev_dependencies:
flutter_test:
sdk: flutter
intl_generator: ^0.1.0+0
# intl_translation: ^0.17.10
flutter:
uses-material-design: true
Flutter环境
Flutter 2.6.0-6.0.pre.6 • channel master • https://github.com/flutter/flutter.git
Framework • revision 0c5431d99c (5 months ago) • 2021-09-05 22:31:02 -0400
Engine • revision b9c633900e
Tools • Dart 2.15.0 (build 2.15.0-82.0.dev)
android studio
项目结构
设置国际化参数
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_project/localizations_const.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'i18n/i18n_demo.dart';
// 自动生成的
import 'i18n/intl/ninghao_demo_localizations.dart';
// 手动生成的
// import 'i18n/ninghao_demo_localizations.dart';
void main() => runApp(Home());
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
//locale: locale_enUs,
// 系统回调回来的语言设置
localeResolutionCallback: (
Locale? locale,
Iterable<Locale> supportedLocales,
) {
print('localeResolutionCallback locale = $locale');
print('localeResolutionCallback supportedLocales = $supportedLocales');
return locale_enUs;
},
localizationsDelegates: [
// 本地化的代理类
// 为Material组件库提供的本地化的字符串和其他值,它可以使Material组建支持多语言
GlobalMaterialLocalizations.delegate,
// 定义组件默认的文本方向,从左到右或从右到左,这是因为有些语言的阅读习惯不是从左到右
GlobalWidgetsLocalizations.delegate,
// 注册我们自己的Delegate
DemoLocalizationsDelegate(),
NinghaoDemoLocalizationDelegate(),
],
supportedLocales: mapLocals,
title: 'Woolha.com Flutter Tutorial',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyApp(),
debugShowCheckedModeBanner: false,
);
}
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
LOCAL_TYPE local_type = LOCAL_TYPE.CH;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(DemoLocalizations.of(context).title),
centerTitle: true,
backgroundColor: Colors.green,
brightness: Brightness.dark,
//automaticallyImplyLeading: false,
leading: Icon(Icons.add),
actions: [
IconButton(
onPressed: () {
setState(() {
//local_type = toggleLocal(local_type);
_showDatePicker1(context);
});
},
icon: Icon(Icons.toggle_on),
),
],
),
body: I18nDemo(),
// body: Center(
// child: Text(getLocalName(local_type)),
// ),
);
}
// 日历选择
Future<DateTime?> _showDatePicker1(BuildContext context) {
bool useIOS = true;
var date = DateTime.now();
return useIOS
? showDatePicker(
context: context,
initialDate: date,
firstDate: date,
lastDate: date.add(Duration(days: 30)),
)
: showCupertinoModalPopup(
context: context,
builder: (ctx) {
return Container(
height: 200,
color: Colors.white,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.dateAndTime,
minimumDate: date,
maximumDate: date.add(Duration(days: 30)),
onDateTimeChanged: (DateTime value) {
print(value);
},
maximumYear: date.year + 1,
),
);
},
);
}
}
设置固定的Locale
- 设置locale参数
MaterialApp(
locale: locale_enUs,
....
- 通过获取系统语言的回调监听设置
MaterialApp(
// 系统回调回来的语言设置
localeResolutionCallback: (
Locale? locale,
Iterable<Locale> supportedLocales,
) {
print('localeResolutionCallback locale = $locale');
print('localeResolutionCallback supportedLocales = $supportedLocales');
return locale_enUs; // 此时默认的是英文, 可以根据具体要求修改
},
...
);
其中:Locale? locale 返回系统修改的语言
Iterable supportedLocales 为APP中所支持的语言
- 设置国际化的代理lo
MaterialApp(
...
localizationsDelegates: [
// 本地化的代理类
// 为Material组件库提供的本地化的字符串和其他值,它可以使Material组建支持多语言
GlobalMaterialLocalizations.delegate,
// 定义组件默认的文本方向,从左到右或从右到左,这是因为有些语言的阅读习惯不是从左到右
GlobalWidgetsLocalizations.delegate,
// 注册我们自己的Delegate
DemoLocalizationsDelegate(),
NinghaoDemoLocalizationDelegate(),
],
...
);
设置代理, 所有的代理都继承LocalizationsDelegate, 可以使用系统的+自定义的
- 所有支持的语言
const locale_enUs = const Locale('en', 'US');
const locale_zhCN = const Locale('zh', 'CN');
const locale_zhHK = const Locale('zh', 'HK');
// 这里放所有能支持的语言
const List<Locale> mapLocals = [
locale_enUs, // 美国英语
locale_zhCN, // 中文简体
locale_zhHK,
];
MaterialApp(
...
supportedLocales: mapLocals,
...
)
手写国际化
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import '../localizations_const.dart';
// 手动版本
class NinghaoDemoLocalizations {
//传入要使用的Local
final Locale locale;
NinghaoDemoLocalizations(this.locale);
// 工具方法, 通过Localizations.of<T>(context, T) 来获取相对应的语言
static NinghaoDemoLocalizations? of(BuildContext context) {
return Localizations.of<NinghaoDemoLocalizations>(
context, NinghaoDemoLocalizations);
}
// 'en'就是语言的名字
// 每个map中, 都有具体要使用的字段
static Map<String, Map<String, String>> _localized = {
'en': {
'title': 'hello',
},
'zh': {
'title': '您好',
},
};
// 通过传入的locale.languageCode取出对应的字段
String? get title {
Map<String, String>? map = _localized[locale.languageCode];
return map == null ? null : map['title'];
}
}
class NinghaoDemoLocalizationDelegate
extends LocalizationsDelegate<NinghaoDemoLocalizations> {
NinghaoDemoLocalizationDelegate();
// 判断是否支持这个语言
@override
bool isSupported(Locale locale) {
return isContainsThisLanguage(locale);
}
// 载入对应的语言资源包
@override
Future<NinghaoDemoLocalizations> load(Locale locale) {
return SynchronousFuture<NinghaoDemoLocalizations>(
NinghaoDemoLocalizations(locale));
}
// 是否每次重新加载资源
@override
bool shouldReload(
covariant LocalizationsDelegate<NinghaoDemoLocalizations> old) {
return false; // 是否加载本地资源
}
}
使用
class I18nDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
Locale locale = Localizations.localeOf(context);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${locale.toString()}'),
Text(
'${Localizations.of(context, NinghaoDemoLocalizations).title}', // 使用手动创建title字段
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
}
自动生成国际化
在我本地的环境使用
dev_dependencies:
intl_generator: ^0.1.0+0
...
先创建一个类,作为要生成arb的模版
class NinghaoDemoLocalizations {
String get title =>
Intl.message(
'hello',
name: 'title',
desc: 'demo localizations.',
);
String greet(String name) =>
Intl.message(
'hello $name',
name: 'greet',
desc: 'greet someone.',
args: [name],
);
}
通过命令行生成arb文件
flutter pub run intl_generator:extract_to_arb --output-dir=lib/i18n/intl/ lib/i18n/intl/ninghao_demo_localizations.dart
分解:
flutter pub run intl_generator:extract_to_arb
--output-dir=lib/i18n/intl/ // 要生成arb文件的目录
lib/i18n/intl/ninghao_demo_localizations.dart // 生成arb文件使用的模板
会生成intl_messages.arb文件
{
"@@last_modified": "2022-02-09T15:56:37.677571",
"title": "hello",
"@title": {
"description": "demo localizations.",
"type": "text",
"placeholders": {}
},
"greet": "hello {name}",
"@greet": {
"description": "greet someone.",
"type": "text",
"placeholders": {
"name": {}
}
}
}
通过粘贴复制改名,创建多个语言的文件intl_en.arb和intl_zh.arb
intl_en.arb
{
"@@last_modified": "2022-02-09T15:56:37.677571",
"title": "hello",
"@title": {
"description": "demo localizations.",
"type": "text",
"placeholders": {}
},
"greet": "hello {name}",
"@greet": {
"description": "greet someone.",
"type": "text",
"placeholders": {
"name": {}
}
}
}
intl_zh.arb
{
"@@last_modified": "2022-02-09T15:56:37.677571",
"title": "您好",
"@title": {
"description": "演示本地化.",
"type": "text",
"placeholders": {}
},
"greet": "您好 {name}",
"@greet": {
"description": "问候某人.",
"type": "text",
"placeholders": {
"name": {}
}
}
}
通过命令,将所有的arb文件生成对应的dart文件,及dart管理文件
以下都是自动生成的
ninghao_demo_messages_all.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that looks up messages for specific locales by
// delegating to the appropriate library.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:implementation_imports, file_names
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
// ignore_for_file:argument_type_not_assignable, invalid_assignment
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
// ignore_for_file:comment_references
// ignore_for_file:avoid_catches_without_on_clauses
import 'dart:async';
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
import 'package:intl/src/intl_helpers.dart';
import 'ninghao_demo_messages_en.dart' as messages_en;
import 'ninghao_demo_messages_messages.dart' as messages_messages;
import 'ninghao_demo_messages_zh.dart' as messages_zh;
typedef Future<dynamic> LibraryLoader();
Map<String, LibraryLoader> _deferredLibraries = {
'en': () => Future.value(null),
'messages': () => Future.value(null),
'zh': () => Future.value(null),
};
MessageLookupByLibrary? _findExact(String localeName) {
switch (localeName) {
case 'en':
return messages_en.messages;
case 'messages':
return messages_messages.messages;
case 'zh':
return messages_zh.messages;
default:
return null;
}
}
/// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) async {
// 验证是否是有效的Local
final availableLocale = Intl.verifiedLocale(
localeName,
(locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null);
if (availableLocale == null) {
return Future.value(false);
}
final lib = _deferredLibraries[availableLocale];
await (lib == null ? Future.value(false) : lib());
initializeInternalMessageLookup(() => CompositeMessageLookup());
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
return Future.value(true);
}
bool _messagesExistFor(String locale) {
try {
return _findExact(locale) != null;
} catch (e) {
return false;
}
}
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
final actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
onFailure: (_) => null);
if (actualLocale == null) return null;
return _findExact(actualLocale);
}
ninghao_demo_messages_en.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a en locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, always_declare_return_types
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = MessageLookup();
typedef String MessageIfAbsent(String? messageStr, List<Object>? args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'en';
static m0(name) => "hello ${name}";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
"greet" : m0,
"title" : MessageLookupByLibrary.simpleMessage("hello")
};
}
ninghao_demo_messages_messages.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a messages locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, always_declare_return_types
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = MessageLookup();
typedef String MessageIfAbsent(String? messageStr, List<Object>? args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'messages';
static m0(name) => "hello ${name}";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
"greet" : m0,
"title" : MessageLookupByLibrary.simpleMessage("hello")
};
}
ninghao_demo_messages_zh.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a zh locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, always_declare_return_types
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = MessageLookup();
typedef String MessageIfAbsent(String? messageStr, List<Object>? args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'zh';
static m0(name) => "您好 ${name}";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
"greet" : m0,
"title" : MessageLookupByLibrary.simpleMessage("您好")
};
}
在ninghao_demo_localization.dart中使用自动生成的文件
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../localizations_const.dart';
import 'ninghao_demo_messages_all.dart';
// 自动版本
// 宁皓网教程
// flutter pub pub run intl_translation:extract_to_arb
// --output-dir=lib/demo/i18n/intl/
// lib/demo/i18n/intl/ninghao_demo_localizations.dart
// 自己摸索
// flutter pub run intl_generator:extract_to_arb --output-dir=lib/i18n/intl/ lib/i18n/intl/ninghao_demo_localizations.dart
class NinghaoDemoLocalizations {
// 通过Localizations.of<T>(context, T)使用国际化
static NinghaoDemoLocalizations? of(BuildContext context) {
return Localizations.of<NinghaoDemoLocalizations>(
context,
NinghaoDemoLocalizations
);
}
// 加载要使用的资源包
static Future<NinghaoDemoLocalizations> load(Locale locale) {
// 当前要使用的Local
final String name = locale.countryCode?.isEmpty == true
? locale.languageCode
: locale.toString();
// 带有格式化的local string localName = "en_US"
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((bool _) {
Intl.defaultLocale = localeName;
return NinghaoDemoLocalizations();
});
}
String get title =>
Intl.message(
'hello',
name: 'title',
desc: 'demo localizations.',
);
String greet(String name) =>
Intl.message(
'hello $name',
name: 'greet',
desc: 'greet someone.',
args: [name],
);
}
// 通过arb反过来生成dart文件
// flutter pub pub run intl_translation:generate_from_arb
// --generated-file-prefix=ninghao_demo_ --output-dir=lib/demo/i18n/intl
// --no-use-deferred-loading lib/demo/i18n/intl/ninghao_demo_localizations.dart
// lib/demo/i18n/int/intl_*.arb
// 自己摸索
//flutter pub run intl_generator:generate_from_arb --generated-file-prefix=ninghao_demo_ --output-dir=lib/i18n/intl/ --no-use-deferred-loading lib/i18n/intl/ninghao_demo_localizations.dart lib/i18n/intl/intl_*.arb
class NinghaoDemoLocalizationDelegate extends LocalizationsDelegate<NinghaoDemoLocalizations> {
NinghaoDemoLocalizationDelegate();
@override
bool isSupported(Locale locale) {
return isContainsThisLanguage(locale);
}
@override
Future<NinghaoDemoLocalizations> load(Locale locale) {
return NinghaoDemoLocalizations.load(locale); // 使用自动生成的国际化语言包
}
@override
bool shouldReload(
covariant LocalizationsDelegate<NinghaoDemoLocalizations> old) {
return false; // 是否每次重新加载本地资源
}
}
自动国际化如何实现的
- 通过load进行加载
Future<bool> initializeMessages(String localeName) async {
final availableLocale = Intl.verifiedLocale(
localeName,
(locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null);
if (availableLocale == null) {
return Future.value(false);
}
final lib = _deferredLibraries[availableLocale];
await (lib == null ? Future.value(false) : lib());
initializeInternalMessageLookup(() => CompositeMessageLookup());
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
return Future.value(true);
}
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
final actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
onFailure: (_) => null);
if (actualLocale == null) return null;
return _findExact(actualLocale);
}
MessageLookupByLibrary? _findExact(String localeName) {
switch (localeName) {
case 'en':
return messages_en.messages;
case 'messages':
return messages_messages.messages;
case 'zh':
return messages_zh.messages;
default:
return null;
}
}
// 是这么引用的
import 'ninghao_demo_messages_en.dart' as messages_en;
import 'ninghao_demo_messages_messages.dart' as messages_messages;
import 'ninghao_demo_messages_zh.dart' as messages_zh;
调用了就是自动生成的文件
-------一下是Old版本-------------------------------------------------------------
-------一下是Old版本-------------------------------------------------------------
-------一下是Old版本-------------------------------------------------------------
-------一下是Old版本-------------------------------------------------------------
-------一下是Old版本-------------------------------------------------------------
安装步骤
添加依赖
将intl package 添加到pubspec.yaml文件中
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.17.0 # Add this line
在pubspec.yaml中,启用generate标志
# The following section is specific to Flutter.
flutter:
generate: true # Add this line
在Flutter项目中的根目录中添加一个新的yaml文件,命名为l10n.yaml文件
arb-dir: lib/l10n # 文件存放的目录
template-arb-file: intl_en.arb # 依赖的模版文件
output-localization-file: app_localizations.dart # 要生成的文件(自动生成,不清楚)
在${FLUTTER_PROJECT}/lib/l10n中,添加intl_en.arb模版文件。如下:
{
"helloWorld": "Hello World!",
"@helloWorld": {
"description": "The conventional newborn programmer greeting"
}
}
在同一目录中,添加一个intl_es.arb文件,对同一条信息做西班牙语的翻译
{
"helloWorld": "Hola Mundo!"
}
自动生成generate目录
正常使用
引入依赖
import 'package:flutter_localizations/flutter_localizations.dart'; // 国际化
import 'package:flutter_helper/generated/l10n.dart';
添加代理
localizationsDelegates: [
S.delegate, // 这个是自动生成的
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate
],
添加多语言支持
supportedLocales: [
Locale('en', ''), //English, no country code
Locale('ko', ''), //Spanish, no country code
Locale('zh', ''), //Spanish, no country code
],
添加默认语言
locale: Locale('ko', ''),
总的添加Local
class LanguageApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// var localization = AppLocalizationDelegate();
// var support = localization.supportedLocales;
return const MaterialApp(
title: 'Localizations Sample App',
locale: Locale('ko', ''),
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate
],
supportedLocales: [
Locale('en', ''), //English, no country code
Locale('ko', ''), //Spanish, no country code
Locale('zh', ''), //Spanish, no country code
],
home: Scaffold(
body: _MyHome(),
),
);
}
}
获取字符串资源
Text(S.of(context).helloWorld) // helloWorld是自动生成的方法