flutter中常用的国际化是Intl,通常会生成这样的目录结构
然后在代码中的使用方式为
MaterialApp(
localizationsDelegates: [S.delegate],
),
S.of(context).test;
看到of
想必大家能联想起InheritedWidget
,其实国际化本质就是依托于InheritedWidget
下面我们从后往前推,先看下S.of(context)
static S of(BuildContext context) {
final instance = S.maybeOf(context);
assert(instance != null,
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
return instance!;
}
static S? maybeOf(BuildContext context) {
return Localizations.of<S>(context, S);
}
这里出现了Localizations
static T? of<T>(BuildContext context, Type type) {
assert(context != null);
assert(type != null);
final _LocalizationsScope? scope = context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>();
return scope?.localizationsState.resourcesFor<T?>(type);
}
然后是_LocalizationsScope
,他是我们要找的InheritedWidget
所以,可想而知,我们的国家化的内容用该是通过这里获取
然后我们看看
_LocalizationsScope
是在哪里创建的最终
WidgetsApp
就是在MaterialApp
中创建的通过上边的源代码发现,我们最初传入的
[S.delegate]
给到了Localizations
的delegates
属性,而Localizations
的state
,是赋值给了_LocalizationsScope
的localizationsState
属性所以此时思路应该清晰,Localizations负责管理所有的国际化,而
_LocalizationsScope
是负责国际化上下文的管理这个时候我们返回头再看
Localizations.of<S>(context, S)
干了什么继续寻找
_typeToResources
的来源这里可以看到Localizations
的delegates
最终转化成了_LocalizationsState
的_typeToResources
,而_typeToResources
是以国际化的类型与国际化的实例作为键值对的形式存储了起来
到此项目中使用国际化的详细流程就结束了
这里我们可以优化的一点是:如果是如果我们不想在使用的时候依托于上下文,来使用国际化,我们可以寻找一个恰当的时机来保存一个_LocalizationsScope
创建之后的一个BuildContext
,因为这个BuildContext
足以通过上下文获取到国际化的内容,这就有很多种方式了,这列举一种:onGenerateTitle
中的context
因为通过上下文
这里的title最终会到
_LocalizationsScope
下游,所以我们可以保存onGenerateTitle
中的BuildContext
MaterialApp(
onGenerateTitle: (BuildContext context) {
globalContext = context;
return "title";
},
localizationsDelegates: [S.delegate],
)
T getLanguage<T>() {
final instance = Localizations.of<T>(globalContext, T);
assert(instance != null, '没有找到${T}');
return instance!;
}
在使用的时候,直接提供Type就可以了,实现了国际化与BuildContext
树的解耦
getLanguage<S>().test;