环境
Flutter 3.29 macOS Sequoia 15.4.1 Xcode 16.3
配置 安装依赖包 默认情况下,Flutter只提供美式英语的本地化,可以通过flutter_localizations这个package来实现国际化。
创建flutter工程后执行
1 2 ¥ flutter pub add flutter_localizations --sdk=flutter ¥ flutter pub add intl:any
执行后的pubspec.yaml
文件的效果
1 2 3 4 5 6 7 dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.8 flutter_localizations: sdk: flutter intl: any
安装对应的package
pubspec.yaml文件 在 Flutter 项目的根目录中添加一个新的 yaml 文件,命名为 l10n.yaml,其内容如下:
1 2 3 arb-dir: lib/common/localization/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart
将 应用资源包 (.arb) 的输入路径指定为 ${FLUTTER_PROJECT}/lib/common/localization/l10n。
将英文的语言模板设定为 app_en.arb。
指定 Flutter 生成本地化内容到 app_localizations.dart 文件。
要在pubspec.yaml 文件中,设置generate为true,执行命令时才会生成对应的app_localizations.dart文件
1 2 flutter: generate: true
指定这个是为了生成项目时能自动生成对应的.dart代码文件
在 ${FLUTTER_PROJECT}/lib/common/localization/l10n
添加 app_en.arb
模板文件
1 2 3 { "helloWorld" : "Hello World!" }
在同一目录下添加中文的模板文件 app_zh.arb
1 2 3 { "helloWorld" : "你好,世界" }
执行命令生成.dart文件
运行报错
1 2 3 4 5 6 7 Synthetic package output (package:flutter_gen) is deprecated: https://flutter.dev/to/flutter-gen-deprecation. In a future release, synthetic-package will default to `false ` and will later be removed entirely. Generating synthetic localizations package failed with 1 error: Error: Attempted to generate localizations code without having the flutter: generate flag turned on. Check pubspec.yaml and ensure that flutter: generate: true has been added and rebuild the project. Otherwise, the localizations source code will not be importable.
有两个问题,一个是使用flutter_gen
生成synthetic package 的方式已经过期,推荐使用在 Flutter 项目中指定的目录下生成国际化资源代码文件。该标志默认为 true,还有一个报错是因为pubspec.yaml
没有设置允许自动生成代码
解决方案: l10n.yaml文件中追加设置不使用Synthetic package的方式生成国际化资源文件
1 synthetic-package: false
解决方案: pubspec.yaml
设置允许自动生成代码
1 2 flutter: generate: true
重新运行 flutter run
报错消失,并在l10n
目录下生成对应的.dart代码文件
打开 app_localizations.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 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 'app_localizations_en.dart' ;import 'app_localizations_zh.dart' ;abstract class AppLocalizations { AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; static AppLocalizations? of(BuildContext context) { return Localizations.of<AppLocalizations>(context, AppLocalizations); } static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate(); 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 get helloWorld; } class _AppLocalizationsDelegate extends LocalizationsDelegate <AppLocalizations > { const _AppLocalizationsDelegate(); @override Future<AppLocalizations> load(Locale locale) { return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale)); } @override bool isSupported(Locale locale) => <String >['en' , 'zh' ].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false ; } AppLocalizations lookupAppLocalizations(Locale locale) { switch (locale.languageCode) { case 'en' : return AppLocalizationsEn(); case 'zh' : return AppLocalizationsZh(); } throw FlutterError( 'AppLocalizations.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.' ); }
使用 使用系统的组件,比如日历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @override Widget build(BuildContext context) { return MaterialApp( title: "localization示例" , localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: [Locale('en' ), Locale('zh' )], theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), ), home: MyHomePage(title: 'home page' ), locale: Locale("zh" ), ); }
1 2 3 4 5 6 7 8 9 10 ... children: <Widget>[ CalendarDatePicker( initialDate: DateTime .now(), firstDate: DateTime (2020 , 3 , 30 ), lastDate: DateTime (2025 , 6 , 31 ), onDateChanged: (value) => {logger.d(value.toString())}, ), ], ...
locale
参数传Locale("en")
再次运行
场景: 自定义 实现业务过程中相关的自定义的词条
1 2 3 4 5 6 7 ... return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(AppLocalizations.of(context)!.helloWorld), ), ...
效果同上,中文时显示你好,世界,英文时显示Hello World
场景: 切换语言 添加get
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @override Widget build(BuildContext context) { return GetMaterialApp( title: "localization示例" , localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: [Locale('en' ), Locale('zh' )], theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), ), home: MyHomePage(title: 'home page' ), locale: Locale("en" ), ); }
1 2 3 4 5 6 if (Get.locale?.languageCode == "zh" ) { Get.updateLocale(Locale('en' )); } else { Get.updateLocale(Locale('zh' )); }
场景: 占位符,复数和选项 当需要再词条中接收变量时可以使用占位符
1 2 3 4 5 6 7 8 9 10 "hello" : "Hello {userName}" "@hello" : { "description" : "A message with a single parameter" , "placeholders" : { "userName" : { "type" : "String" , "example" : "Bob" } } }
1 2 3 4 5 6 7 8 9 10 "hello" : "你好 {userName}" , "@hello" : { "description" : "A message with a single parameter" , "placeholders" : { "userName" : { "type" : "String" , "example" : "张三" } } }
1 2 3 ... Text(AppLocalizations.of(context)!.hello("Programmer" )), ...
你还可以使用数字占位符来指定多个值。不同的语言有不同的单词复数化形式。该语法还支持指定单词的复数化形式。
1 "{countPlaceholder, plural, =0{message0} =1{message1} =2{message2} few{messageFew} many{messageMany} other{messageOther}}"
countPlaceholder: 变量名 plural: 固定搭配 =0{message0} =1{message1}…: 参数0时显示message0,参数1时显示message1
app_en.arb
1 2 3 4 5 6 7 8 9 10 "people" : "{count, plural, =0{no people} =1{person} other{people} }" , "@people" : { "description" : "A plural people" , "placeholders" : { "count" : { "type" : "num" , "format" : "compact" } } }
app_zh.arb
1 2 3 4 5 6 7 8 9 10 "people" : "{count, plural, =0{没有人} =1{一个人} other{人群}}" , "@people" : { "description" : "A plural people" , "placeholders" : { "count" : { "type" : "num" , "format" : "compact" } } }
other的情况必须填写,不然会报
[app_en.arb:people] ICU Syntax Error: Plural expressions must have an “other” case. {count, plural, =0{no people} =1{person} few{some people} } ^ [app_zh.arb:people] ICU Syntax Error: Plural expressions must have an “other” case. {count, plural, =0{没有人} =1{一个人} few{一些人} } ^ Found syntax errors.
添加词条后可以在.arb文件中右键然后选择Generate Localizations
生成国际化的.dart文件
1 2 3 4 5 ... Text(AppLocalizations.of(context)!.people(0 )), Text(AppLocalizations.of(context)!.people(1 )), Text(AppLocalizations.of(context)!.people(2 )), ...
也可以使用String
占位符来表示选择一个值,类似词条使用case语句的感觉
1 "{selectPlaceholder, select, case{message} ... other{messageOther}}"
selectPlaceholder: 变量名 select: 固定搭配 case…other: 情况1,情况2,其它情况
1 2 3 4 5 6 7 8 9 "pronoun" : "{gender, select, male{he} other{she}}" , "@pronoun" : { "description" : "A gendered message" , "placeholders" : { "gender" : { "type" : "String" } } }
1 2 3 4 5 6 7 8 9 "pronoun" : "{gender, select, male{他} other{她}}" , "@pronoun" : { "description" : "A gendered message" , "placeholders" : { "gender" : { "type" : "String" } } }
1 2 Text(AppLocalizations.of(context)!.pronoun('male' )), Text(AppLocalizations.of(context)!.pronoun('female' )),
其它 避免语法解析 有时候符号(例如{
或}
)也可能是普通文本的部分,如果不想被解析为一种语法,可以在l10n.yaml
中设置use-escaping
启用后,解析器会忽略使用一对单引号包括的文字,如果在文字中又想使用单个单引号,需要使用成对的单引号进行转义。
配置l10n.yaml文件 通过l10n.yaml
文件,可以配置gen-l10n
工具,指定以下内容
所有输入文件的位置
所有输出文件的创建位置
为本地化代理设置自定义的类名
可以在命令行运行 flutter gen-10n --help
获取命令的具体使用
详见Flutter 应用里的国际化
参考
Flutter 应用里的国际化
Localized messages are generated into source, not a synthetic package.