Flutter国际化插件dynamic_intl的使用
Flutter国际化插件dynamic_intl的使用
基于原intl
和flutter_intl
自动生成插件,使原先固定的arb变为可动态化更新的模式。也可以不用flutter_intl
,默认的只要格式正确也是一样的。
支持通过版本变更来更新,以及arb增量更新。并且可以动态增加支持的语言并及时应用。
安装
在pubspec.yaml
文件中添加依赖:
dependencies:
dynamic_intl: ^0.1.0
导入插件:
import 'package:dynamic_intl/dynamic_intl.dart';
初始化以及配置
继承LanguageSetting
来配置对应的设置。首要就是设置对应的资源远程链接,还可以配置缓存的目录、文件名等。
import 'package:dynamic_intl/dynamic_intl.dart';
class TestSetting extends LanguageSetting {
[@override](/user/override)
Future<String> languageApi(String locale) async {
return 'https://jomin-web.web.app/language/intl_$locale.arb';
}
[@override](/user/override)
String get defaultLocale => 'en';
[@override](/user/override)
Map<String, LibraryLoader> get deferredLibraries => {
'zh': () => Future.value(null),
'en': () => Future.value(null),
};
}
defaultLocale
为默认的语言,最好跟flutter_intl
中设置的保持一致。会根据这个默认语言来决定各个文案的占位符,很重要。
deferredLibraries
是支持的语言列表,格式跟官方intl
中自动生成的一样,下边的checkAndDownload
可以放在这里,那么在加载对应语言时就可以触发检查下载。
还可以设置在远程资源失效或没有正常获得的时候,备用的本地语言包,格式也是跟官方的一致。
import 'package:lib/generated/intl/messages_zh.dart' as messages_zh;
import 'package:lib/generated/intl/messages_en.dart' as messages_en;
[@override](/user/override)
MessageLookupByLibrary? defaultLocaleMessages(String localeName) {
switch (localeName) {
case 'zh':
return messages_zh.messages;
case 'en':
return messages_en.messages;
default:
return null;
}
}
本地语言包示例
flutter_intl
的配置:
flutter_intl:
enabled: true
按照官方的来即可,也可以自己配置路径什么的(example中的配置了language路径下)。
自动生成的文件(generated
下的)不需要去手动修改,messages_all
已经不会用到。messages_en
等,可以作为本地备用资源,在远程资源失败时可做备用。l10n.dart
需要记住其中的类,后续需要注册到delegate
中,但不要用它自己里面的delegate
。
l10n
目录下的为语言包,即本地的,尽量跟远程同步,修改后及时更新到远程中或随版本更新。格式就简单的json
,根据文件名区分语言。
{
"test": "test title"
}
Arb转译器
目前arb的转译仅支持普通的Intl.message
,且最多十个占位符(各规则可参考ApplicationResourceBundleSpecification),如果有需要想支持plural
、gender
等,或者需要更多占位符,可以继承ArbTraslation
,并重写parseFile
。相应的写法可自行查看代码。然后设置到LanguageSetting
中。
[@override](/user/override)
ArbTranslation arbTranslation = NewArbTranslation();
开启arb增量更新
若是觉得语言包体积过于庞大,可以配合服务端做增量更新。即服务端把‘增量’返回,客户端会进行merge
并更新(不会移除旧的)。
默认为false
,需设置为true
。
[@override](/user/override)
bool get arbMergeEnable => true;
在runApp
前初始化完成
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LanguageUtil.instance.init(TestSetting());
runApp(const MyApp());
}
使用多语言,WidgetsFlutterBinding.ensureInitialized()
也是需要的。
注册到MaterialApp
locale
可自行控制,需要配置的主要是localizationsDelegates
和supportedLocales
。可继承BaseLocalizationsDynamicDelegate<T>
(T
即为flutter_intl
自动生成的类,默认是S
, 具体由你决定)来获取最简单的Delegate
。有自己想法的也可以自行编写,并重写supportedLocales
和在load
中使用initializeDynamicMessages
。
class AppLocalizationDynamicDelegate
extends BaseLocalizationsDynamicDelegate<S> {
const AppLocalizationDynamicDelegate();
static const AppLocalizationDynamicDelegate delegate =
AppLocalizationDynamicDelegate();
[@override](/user/override)
Future<S> loadS() {
final instance = S();
return Future.value(instance);
}
}
按照上边的写法即可。然后设置到MaterialApp
中
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
/// 当前语言
locale: MyApp.locale,
localizationsDelegates: const [
/// Delegate 注册
AppLocalizationDynamicDelegate.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
/// 支持的语言列表
supportedLocales:
AppLocalizationDynamicDelegate.delegate.supportedLocales,
home: MyHomePage(
title: 'Flutter Demo Home Page', updateLocale: updateLocale),
);
}
在自定支持语言列表时,这三个delegate
也是需要的,系统功能的多语言支持,记得也加上。
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
它们需要flutter_localizations
flutter_localizations: # Add this line
sdk: flutter
触发检查下载
通过Future checkAndDownload(String locale, [String? newVersion])
可下载指定语言指定版本的语言包(版本不同则会更新)。下载完成之后可刷新UI来展示最新文案。切换语言也应该调用该方法,已存在语言包则会跳过。
// 下载语言包,可以加版本, 无版本则每次都更新
LanguageUtil.instance
.checkAndDownload(MyApp.locale.languageCode, '1002')
.then((value) {
// 因为下载完成前会使用默认文案,下载完成后应刷新下UI
setState(() {});
});
若当前下载的不是默认语言,且默认语言包也没有下载保存过,那么会先下载默认语言包。
版本管理需要自行设计,这里仅根据传入的版本对比来更新。
增加支持的语言列表
可通过updateLanguageLibrary
动态新增支持的语言列表。
updateLanguageLibrary([const Locale('it'), const Locale('de')]);
若Setting
中没有,则可以先检查远程列表然后通过该方法新增,新增后才可以有效切换。
关闭远程仅使用本地
setLocalLanguage
设置为true
,则会无视远程语言包,直接使用本地的语言包,确认defaultLocaleMessages
正常配置。
LanguageUtil.instance.setLocalLanguage(true);
使用文案
即普通的S.of(context).text
,觉得需要context
太麻烦,可以在MaterialApp
下build
的时候注册一个全局context
(参考GetX的全局context),一样用。
Text(S.of(context).test)
/// 带占位符
S.of(context).textPlace('123')
切换语言
修改MaterialApp
的locale
,然后检查语言包下载,最后刷新UI即可。需要刷新到MaterialApp
这一层,可以看看example
中的示例。
void _changeLocale() async {
var locale = MyApp.locale.languageCode == 'en'
? const Locale('zh')
: const Locale('en');
LanguageUtil.instance
.checkAndDownload(locale.languageCode, '1002')
.then((_) {
// 因为下载前会使用默认文案,下载完成后应刷新下UI
widget.updateLocale(locale);
});
}
最后
翻译的文件有@开头的注释消息,也没有问题,只不过默认的ArbTranslation
不会处理。LanguageSetting
可以在你的管理类里面继承,建议S
也重新再套一层再使用,方便维护。检查下载和切换可以根据实际需求来,全部都一起下载也不是不可以,也可以配置在deferredLibraries
中随系统切换时自动同步检查下载。
example
中有个比较简单的可以直接运行的例子,可供参考。有什么问题的话可以直接提issue。
以下是完整的示例代码:
import 'package:dynamic_intl/dynamic_intl.dart';
import 'package:example/language/generated/l10n.dart';
import 'package:example/test_delegate.dart';
import 'package:example/test_setting.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LanguageUtil.instance.init(TestSetting());
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
static var locale = const Locale('zh');
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
[@override](/user/override)
void initState() {
super.initState();
// 可增加支持的语言列表
// updateLanguageLibrary([const Locale('it'), const Locale('de')]);
// 下载语言包,可以加版本, 无版本则每次都更新
LanguageUtil.instance
.checkAndDownload(MyApp.locale.languageCode, '1002')
.then((value) {
// 因为下载完成前会使用默认文案,下载完成后应刷新下UI
setState(() {});
});
// 可以设置是否使用本地语言包
// LanguageUtil.instance.setLocalLanguage(true);
// 检查是否使用本地语言包
LanguageUtil.instance.checkLocalLanguage();
}
void updateLocale(Locale locale) {
MyApp.locale = locale;
setState(() {});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
/// 当前语言
locale: MyApp.locale,
localizationsDelegates: const [
/// Delegate 注册
AppLocalizationDynamicDelegate.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
/// 支持的语言列表
supportedLocales:
AppLocalizationDynamicDelegate.delegate.supportedLocales,
home: MyHomePage(
title: 'Flutter Demo Home Page', updateLocale: updateLocale),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title, required this.updateLocale})
: super(key: key);
final String title;
final Function updateLocale;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _changeLocale() async {
var locale = MyApp.locale.languageCode == 'en'
? const Locale('zh')
: const Locale('en');
LanguageUtil.instance
.checkAndDownload(locale.languageCode, '1002')
.then((_) {
// 因为下载前会使用默认文案,下载完成后应刷新下UI
widget.updateLocale(locale);
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(S.of(context).test),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
S.of(context).test,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _changeLocale,
tooltip: MyApp.locale.languageCode,
child: const Icon(Icons.all_inclusive),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
更多关于Flutter国际化插件dynamic_intl的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter国际化插件dynamic_intl的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter项目中使用dynamic_intl
插件来实现国际化的代码案例。dynamic_intl
是一个强大的插件,允许你在运行时动态加载和切换语言。
第一步:添加依赖
首先,在你的pubspec.yaml
文件中添加dynamic_intl
的依赖:
dependencies:
flutter:
sdk: flutter
dynamic_intl: ^0.x.x # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
第二步:配置国际化资源
在你的项目根目录下创建assets/locales
文件夹,并在其中创建不同语言的JSON文件。例如:
assets/locales/en.json
assets/locales/zh.json
en.json
内容示例:
{
"greeting": "Hello",
"farewell": "Goodbye"
}
zh.json
内容示例:
{
"greeting": "你好",
"farewell": "再见"
}
第三步:加载和配置dynamic_intl
在你的main.dart
文件中,配置dynamic_intl
以加载这些资源文件。
import 'package:flutter/material.dart';
import 'package:dynamic_intl/dynamic_intl.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
// 初始化国际化配置
IntlDelegate delegate = IntlDelegate(
fallbackLocale: 'en', // 默认语言
supportedLocales: ['en', 'zh'], // 支持的语言列表
assetBase: 'assets/locales', // 资源文件的基础路径
assetName: '{locale}.json', // 资源文件的命名模式
);
runApp(
MaterialApp(
localizationsDelegates: [
delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: delegate.supportedLocales,
locale: delegate.fallbackLocale,
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Locale _currentLocale = Locale('en');
void changeLanguage(Locale newLocale) {
setState(() {
_currentLocale = newLocale;
// 通知IntlDelegate当前语言已经改变
IntlProvider.of(context).locale = newLocale;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(intl.lookup('greeting')), // 使用intl.lookup来获取国际化字符串
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(intl.lookup('greeting')),
Text(intl.lookup('farewell')),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => changeLanguage(Locale('zh')),
child: Text('切换到中文'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () => changeLanguage(Locale('en')),
child: Text('切换到英文'),
),
],
),
),
);
}
}
注意事项
-
IntlProvider:
IntlProvider
是dynamic_intl
提供的上下文提供者,用于在widget树中提供当前的国际化配置。在这个例子中,我们通过IntlProvider.of(context).locale = newLocale;
来更新当前的语言环境。 -
intl.lookup:
intl.lookup
方法用于获取当前语言环境下的字符串。注意,由于我们没有显式地在代码中创建Intl
实例,这里的intl
实际上是通过IntlProvider
隐式提供的。 -
Locale和SupportedLocales:确保在
IntlDelegate
中正确设置了fallbackLocale
和supportedLocales
。 -
热重载和热重启:在切换语言后,你可能需要热重启应用(而不是热重载)才能看到语言的变化。
这个示例展示了如何使用dynamic_intl
插件在Flutter应用中实现国际化。你可以根据需要扩展这个示例,以支持更多的语言和更复杂的应用场景。