内置组件本地化
-
添加依赖
Flutter默认组件的本地化只需要添加
flutter_localizations: sdk: flutter
使用命令行添加就是
flutter pub add flutter_localizations --sdk=flutter
-
修改入口组件 main.dart
import 'package:flutter_localizations/flutter_localizations.dart';
return const MaterialApp( title: 'Localizations Sample App', localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('en'), // English Locale('zh'), // Chinese ], home: MyHomePage(), );
这样Flutter内置组件所显示的语言就能和系统语言保持一致,如果想要手动修改,只需要添加参数
return const MaterialApp( ... locale: Locale("zh"), );
如果想要修改指定的组件,需要使用到
Localizations.override
Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Localizations.override( context: context, locale: const Locale('zh'), child: Builder( builder: (context) { return CalendarDatePicker( initialDate: DateTime.now(), firstDate: DateTime(1900), lastDate: DateTime(2100), onDateChanged: (value) {}, ); }, ), ), ], ), ), ); }
自定义本地化
根据官方文档,可以利用 flutter_localozations
和 intl
来实现。但是如果根据官方文档,那可不是一般的麻烦,而是相当麻烦,操作步骤包括但不限于
-
修改
pubsepc.yaml
-
添加
l10n.yaml
-
添加
.arb
文件
当然,这一切也不能说是很麻烦,毕竟只要第一次改好,后续只用修改 .arb
文件就能自动运行,但根据这个流程生成的多语言本地化,我觉得有几个问题:
-
自动生成的
.dart
文件是保存在{项目目录}/.dart_tool/flutter_gen/gen_l10n
目录下的,这会导致项目默认的代码没有多语言相关,只有一些不知所谓的.arb
文件,毕竟与多语言相关的代码都是自动生成的 -
也是最重要的一点, 我为什么要在默认语言下重复定义一个不能带空格,只允许定义符合 dart方法名称 的关键字以供调用,比如一个简单的字符串
Text("Hello World")
我必须在默认语言定义一个
// app_es.arb "helloWorld": "Hello World"
然后在其它语言定义
// app_zh.arb "helloWorld": "你好 世界"
最后再修改默认的调用
Text(AppLocations.of(context).helloWorld)
我为什么不能直接使用原有的字符串呢,这样就不用再为默认的语言添加额外的翻译,比如
Text(AppLocations.of(context).tr("Hello World"))
所以,我仔细研究了一下,大抵不用如此麻烦
自定义本地化(非代码自动生成)
-
首先添加 l10n.dart
import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:intl/intl.dart' as intl; import 'l10n_en.dart'; import 'l10n_zh.dart'; abstract class L10n { L10n(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; static L10n? of(BuildContext context) { return Localizations.of<L10n>(context, L10n); } static const LocalizationsDelegate<L10n> delegate = _L10nDelegate(); static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[ delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ]; static const List<Locale> supportedLocales = <Locale>[ Locale('en'), Locale('zh') ]; String tr(String key); } class _L10nDelegate extends LocalizationsDelegate<L10n> { const _L10nDelegate(); @override Future<L10n> load(Locale locale) { return SynchronousFuture<L10n>(lookupL10n(locale)); } @override bool isSupported(Locale locale) => <String>['en', 'zh'].contains(locale.languageCode); @override bool shouldReload(_L10nDelegate old) => false; } L10n lookupL10n(Locale locale) { // Lookup logic when only language code is specified. switch (locale.languageCode) { case 'en': return L10nEn(); case 'zh': return L10nZh(); } throw FlutterError( 'L10n.delegate failed to load unsupported locale "$locale". This is likely ' 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' 'that was used.'); }
上述代码其实是由根据官方文档自动生成的
.dart
文件转化而来 -
添加默认语言的翻译
// l10n_en.dart import 'l10n.dart'; class L10nEn extends L10n { L10nEn([String locale = 'en']) : super(locale); @override String tr(String key) { return translations[key] ?? key; } } const translations = {};
对的,你没有看错,
translations
甚至可以是空的,这样就不用重复定义默认语言的翻译 -
添加其它语言的翻译
// l10n_zh.dart import 'l10n.dart'; class L10nZh extends L10n { L10nZh([String locale = 'zh']) : super(locale); @override String tr(String key) { return translations[key] ?? key; } } const translations = { "Settings": "设置", "Basic Settings": "基础设置", "Theme": "主题", "Language": "语言", "About": "关于", "Help": "帮助", };
-
修改 main.dart 入口组件
import 'app/l10n/l10n.dart'; return const MaterialApp( title: 'Localizations Sample App', localizationsDelegates: L10n.localizationsDelegates, supportedLocales: L10n.supportedLocales, home: MyHomePage(), );
这样就能使用自定义的翻译了
本地化多语言的使用
最常用的是在 Text
组件里
Text(L10n.of(context)!.tr("Hello World"))
但是所有的字符都要添加 L10n.of(context)!
未免有些麻烦,所以我增加了自定义扩展
-
BuildContext
extension L10nContext on BuildContext { String tr(String key) { final t = L10n.of(this); if (t == null) { return key; } return t.tr(key); } }
使用方式
Text(context.tr("Hello World"))
-
String
extension L10nString on String { String tr(BuildContext context) { final t = L10n.of(context); if (t == null) { return this; } return t.tr(this); } }
使用方式
Text("Hello World".tr(context))
-
Text
extension L10nText on Text { Text tr(BuildContext context) { final t = L10n.of(context); if (t == null || data == null) { return this; } return Text(t.tr(data ?? ''), key: key, style: style, strutStyle: strutStyle, textAlign: textAlign, textDirection: textDirection, locale: locale, softWrap: softWrap, overflow: overflow, textScaler: textScaler, maxLines: maxLines, semanticsLabel: semanticsLabel, textWidthBasis: textWidthBasis); } }
使用方式
Text("Hello World").tr(context)
如此,就能最大限度的较少对原有代码的侵略性修改
优化多语言选择
我这里选用的是 riverpod 进行状态管理,首先定义一个本地语言的状态
final localeProvider = StateProvider<String>((ref) { return "zh"; });
接着修改 main.dart
class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final localeCode = ref.watch(localeProvider); return MaterialApp( locale: L10n.delegate.isSupported(localeCode) ? Locale(localeCode) : null, ... ); } }
这样就能很方便地修改应用的显示语言
ref.read(localeProvider.notifier).state = "zh";