官方流程
简单来讲,流程图如下
简单讲一下整个流程
- 我们先写一个
AppStrings.dart
,这个文件是整个多语言的核心,生成和使用都需要这个文件。在这个文件里可以定义获得文本的api,比如
其中,方法名和String order_list_waiting_for_review() => Intl.message( 'Waiting for the review', name: "order_list_waiting_for_review", locale: _localeName );
name
必须保持一致(不要问我为何会有这种坑爹要求)。'Waiting for the review'
是默认文案,locale
是当前的locale。
这样定义的话,可以用下面的方式获得文本
很好理解,String text = AppStrings.of(context).order_list_waiting_for_review(); // text == "Waiting for the review"
AppStrings.of(context)
就是通过context
获得对应的locale
的AppStrings
,进而获得对应的文案。 - 在上面以那种格式定义后,我们可以执行下面这个命令把
AppStrings.dart
生成intl_messages.arb
flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/app_strings.dart
*.arb
是个中间产物,其内就是JSON文本。其实会有更多附加信息,描述这个文本,不过在此省略。{ "order_list_waiting_for_review": "Waiting for the review", }
- 生成的
arb
文件只是一种语言的,我们可以把它拷贝成N种语言对应的文件,比如intl_en.arb
,intl_es.arb
等,并把内容的value都替换成对应语言的文案。 - 然后再执行下面这个命令,生成
messages_*.dart
可以看到,作为入参的是flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n \ --no-use-deferred-loading lib/app_strings.dart lib/l10n/intl_*.arb
lib/app_strings.dart
和lib/l10n/intl_*.arb
,flutter工具党确保lib/app_strings.dart
中的方法名、方法中的name参数,和lib/l10n/intl_*.arb
中的json的key一致的时候,才会在生成的messages_*.dart
文件中加入对应的文案,缺一不可。
我们再回过头来看这段代码
String order_list_waiting_for_review() => Intl.message(
'Waiting for the review',
name: "order_list_waiting_for_review",
locale: _localeName
);
Intl.message()
方法实际上是根据locale
获取对应的message_*.dart
文件,然后再通过name
找到对应的文案返回。
- 当然,还需要对
MaterialApp
定义localizationsDelegates本地化代理
和supportedLocales支持的多语言
,这部分在文章最开始的文档里写的很详细了,就不再赘述了。
官方方案的问题
官方的方案虽然很长很繁琐,但还是可以实现多语言的功能的,只是对于我们来讲,它存在以下几个问题:
- 需要在
AppStrings.dart
中定义文本获得的方法,一个文本就需要定义一个方法,并且方法名和name
必须一致才能和arb
文件一起生成最后的dart
文件。一个文案对应一个方法,代码写起来很冗余,并且不利于文案的枚举使用(比如文案的key之间有关系)。和native开发之前的习惯也不同。使用和生成的逻辑放到一起,倘若有几千个文案,AppStrings.dart
估计要爆表。 - 流程太长,不利于自动化下载语言的脚本的实现。
我们的流程
起点的i18n.py是我们自己写的python脚本,一共有两个作用
从美杜莎(阿里国际化文案管理平台)上获得多语言文案,并重命名成
arb
文件。前面说到,arb文件其实是json格式的,美杜莎支持以json格式导出文本,所以我们做的只是重命名,不需要对内容进行更改。
在
arb
文件处理好后,会拿默认的(英语)文本去生成一个dart文件,这个dart文件只用来作为中间产物,不会被其他dart使用的。
其中只包含如下内容
```javascript
import 'package:intl/intl.dart';
class MessagesIndex {
String order_list_waiting_for_review() => Intl.message(
'Waiting for the review',
name: "order_list_waiting_for_review",
);
// ......
}
那么,我们继续调用生成最终产物的命令行
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n \
--no-use-deferred-loading lib/messagesindex.dart lib/l10n/intl*.arb
发现文件以及其内的文案已经被正确生成了。
调用方式
-----
我之所以这样更改流程,其实只有两个目的:
1. 可以像读map一样的读取多语言文案,而不是像调用方法那样去读取。
2. 拉取、生成多语言文案的过程可以自动化。
通过上面一顿操作,生成文案的工作都被自动化搞定了,那我们只需要在`AppStrings.dart`中添加一个方法,传入name,传出想要的多语言文案即可:
```javascript
class AppStrings {
final String _localeName;
// ......
static AppStrings of(BuildContext context) {
return Localizations.of<AppStrings>(context, AppStrings);
}
// AppStrings.of(context).str(stringKey)
String str(String name) {
return Intl.message(name, name: name, locale: _localeName);
}
// 重写操作符,减少代码量
// AppStrings.of(context)[stringKey]
operator [](String messageName) => str(messageName);
}