深色模式
Flutter 国际化之 slang
slang是一个强大的Flutter国际化解决方案,它提供了比官方intl更灵活的功能和更简洁的API。slang专注于开发体验和性能,是构建多语言Flutter应用的优秀选择。
为什么选择slang
官方intl方案的主要缺点:
- 仅支持
.arb
格式,.arb
格式可读性差,甚至不能添加注释 - 不支持热重载,必须 shut down 再重新运行
slang解决了以上2个缺点。
再对比一下slang相比官方intl方案不足之处:
- 代码侵入性更强一点,要在
MaterialApp
外面多套1层(本质上就是把MaterialApp
放在InheritedWidget
中) - 生成代码的过程不如intl丝滑,intl修改
.arb
文件后会自动生成,slang则需要额外命令(dart run slang
)。
另外,slang的灵活性比intl更强。
安装与配置
添加依赖
在pubspec.yaml
文件中添加以下依赖:
yaml
dependencies:
flutter:
sdk: flutter
slang: <version>
slang_flutter: <version>
dev_dependencies:
build_runner: <version> # 如果要通过build_runner生成,需要添加此项
slang_build_runner: <version> # 如果要通过build_runner生成,需要添加此项
slang生成代码:
可以通过
build_runner
生成bashdart run build_runner build
当运行
build_runner
命令进,所有依赖build_runner
的工具都会生成,耗时比较长(几十秒)。而且,slang并未处理
build_runner
的watch
参数,所以修改了输入文件不会马上自动生成。也可以通过
slang
生成。bashdart run slang
所以,推荐使用
slang
命令生成,它只生成翻译代码,速度非常快(0.1秒)。
配置文件
- 如果不使用
build_runner
生成,配置可以写在单独的slang.yaml
中,也可以写在build.yaml
中; - 如果使用
build_runner
生成,配置必需写在build.yaml
中。
配置项都是一样的,这里使用build.yaml
:
yaml
targets:
$default:
builders:
slang_build_runner:
options:
base_locale: zh # 基础语言。【默认:en】
fallback_strategy: base_locale # 兜底策略。【默认:none】【可选值:none, base_locale, base_locale_empty_string】
input_directory: lib/inherited/lang/input # 输入目录。【默认:null】
input_file_pattern: .yaml # 输入文件格式。【默认:.i18n.json】
output_directory: lib/inherited/lang/output # 输出目录。【默认:null】
output_file_name: translations.g.dart # 输出文件名。【默认:translations.g.dart】
flat_map: false # 生成扁平化map。【默认:true】
yaml
targets:
$default:
builders:
slang_build_runner:
options:
base_locale: zh # 基础语言。【默认:en】
fallback_strategy: base_locale # 兜底策略。【默认:none】【可选值:none, base_locale, base_locale_empty_string】
input_directory: lib/inherited/lang/input # 输入目录。【默认:null】
input_file_pattern: .yaml # 输入文件格式。【默认:.i18n.json】
output_directory: lib/inherited/lang/output # 输出目录。【默认:null】
output_file_name: translations.g.dart # 输出文件名。【默认:translations.g.dart】
flat_map: false # 生成扁平化map。【默认:true】
# 以下默认⬇️
lazy: true # 懒加载副语言,只对Web平台生效。【默认:true】
locale_handling: true # 生成`LocaleSettings`, `t`等。【默认:true】
flutter_integration: true # 集成到 flutter 项目中。【默认:true】
namespaces: false # 拆分输入文件,以命名空间区分。【默认:false】
translate_var: t # 翻译对象变量的名称。【默认:t】
enum_name: AppLocale # 枚举类的名称。【默认:AppLocale】
class_name: Translations # 翻译类的名称。【默认:Translations】
translation_class_visibility: private # 翻译类的可见性。【默认:private】【可选值:public, private】
key_case: null # 键名转换。【默认:null】【可选值:null, camel, pascal, snake】
key_map_case: null # 键名映射转换。【默认:null】【可选值:null, camel, pascal, snake】
param_case: null # 参数转换。【默认:null】【可选值:null, camel, pascal, snake】
sanitization:
enabled: true # 启用清理,当key是保留关键字时,会添加前缀。【默认:true】
prefix: k # 前缀。【默认:k】
case: camel # 转换方式。【默认:camel】【可选值:camel, pascal, snake】
string_interpolation: dart # 字符串插值语法。【默认:dart】【可选值:dart, braces, double_braces】
translation_overrides: false # 翻译覆盖,不详。【默认:false】
timestamp: true # 记录生成的时间戳(文件顶部注释)。【默认:true】
statistics: true # 生成统计信息,语言数、字符串数(文件顶部注释)。【默认:true】
maps: [] # 指定map访问的key,大型项目用得上。【默认:[]】
pluralization: # 复数相关的配置
auto: cardinal
default_parameter: n
cardinal: []
ordinal: []
contexts: # 上下文相关(用于比如区分性别的称呼的场景),详见官方文档
obfuscation:
enabled: false # 混淆生成代码,防止简单查找,由于密钥在文件中,所以可被破解。【默认:false】
secret: null # 混淆密钥,null代表随机密钥。【默认:null】
format:
enabled: false # 对生成代码格式化,关闭以提高生成速度。【默认:false】
width: null # 最大宽度。【默认:null】
关于所有配置项的解释,可以去查看官方文档。
翻译文件
翻译文件支持多种格式(JSON
, YAML
, CSV
, ARB
),推荐使用YAML
,因为可读性更高。
创建2个文件:
lib/
i18n/
zh.i18n.yaml # 中文(基础语言)
en.i18n.yaml # 英文
内容:
yaml
app:
title: 我的应用
common:
hello: 你好
welcome: 欢迎使用 {appName}
itemCount:
zero: 没有项目
one: 1个项目
other: "{count}个项目"
gender:
male: 他
female: 她
other: 他们
home:
title: 首页
greeting: 欢迎回来,{name}!
lastLogin: 上次登录时间:{date}
settings:
title: 设置
language: 语言
theme: 主题
darkMode: 深色模式
yaml
app:
title: My App
common:
hello: Hello
welcome: Welcome to {appName}
itemCount:
zero: No items
one: 1 item
other: "{count} items"
gender:
male: He
female: She
other: They
home:
title: Home
greeting: Welcome back, {name}!
lastLogin: Last login: {date}
settings:
title: Settings
language: Language
theme: Theme
darkMode: Dark Mode
生成代码
可用的命令:
bash
dart run slang -d
dart run slang watch -d # 持续生成
dart run build_runner build -d
dart run build_runner watch -d # watch 不生效
在项目中使用
默认配置下,slang会生成LocaleSettings
、TranslationProvider
等相关代码,使用这些代码能满足大部分开发场景。
如果想要完全自己处理这些逻辑,可以参考官方文档。
下面介绍常规使用方式:
dart
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends HookConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return TranslationProvider(
child: MaterialApp(
// localization
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: AppLocaleUtils.supportedLocales,
locale: TranslationProvider.of(context).flutterLocale, // 使用设备当前语言
// 或:
// locale: ref.watch(localeProvider), // 全局状态指定语言,自己处理切换逻辑
// ...
),
);
}
}
dart
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取当前翻译实例
final t = context.t;
return Scaffold(
appBar: AppBar(
title: Text(t.app.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(t.common.hello),
Text(t.common.welcome('Flutter Demo')),
Text(t.common.itemCount(5)),
Text(t.home.greeting('John')),
Text(t.common.gender.male),
],
),
),
);
}
}
语言切换,只需要修改localeProvider
的状态,这里略过。
slang其它命令
dart run slang
: 生成翻译文件代码dart run slang analyze
: 分析未使用和缺失的项目dart run slang normalize
: 按base_locale
,调整其它输入文件的排序dart run slang configure
: 自动更新 iOS 的 CFBundleLocalizations 配置dart run slang edit move loginPage authPage
: 移动和重命名dart run slang migrate arb src.arb dest.json
: 从 arb 迁移到 json