国际化 Flutter 应用
如果您的应用可能会部署到使用其他语言的用户,则需要对其进行国际化。这意味着您需要以一种方式编写应用,使其能够本地化应用支持的每种语言或区域设置的文本和布局等值。Flutter 提供了有助于国际化的组件和类,Flutter 库本身也已国际化。
此页面介绍了使用 MaterialApp
和 CupertinoApp
类本地化 Flutter 应用所需的概念和工作流,因为大多数应用都是这样编写的。但是,使用更低级别的 WidgetsApp
类编写的应用也可以使用相同的类和逻辑进行国际化。
Flutter 中本地化的简介
#本节提供了一个关于如何创建和国际化新的 Flutter 应用的教程,以及目标平台可能需要的任何其他设置。
您可以在 gen_l10n_example
中找到此示例的源代码。
设置国际化应用:Flutter_localizations 包
#默认情况下,Flutter 仅提供美式英语本地化。要添加对其他语言的支持,应用必须指定其他 MaterialApp
(或 CupertinoApp
)属性,并包含一个名为 flutter_localizations
的包。截至 2023 年 12 月,此包支持 115 种语言 和语言变体。
首先,使用 flutter create
命令在您选择的目录中创建一个新的 Flutter 应用。
flutter create <name_of_flutter_app>
要使用 flutter_localizations
,请将该包作为依赖项添加到您的 pubspec.yaml
文件中,以及 intl
包
flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any
这将创建一个包含以下条目的 pubspec.yml
文件
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
然后导入 flutter_localizations
库并为您的 MaterialApp
或 CupertinoApp
指定 localizationsDelegates
和 supportedLocales
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('es'), // Spanish
],
home: MyHomePage(),
);
引入 flutter_localizations
包并添加之前的代码后,Material
和 Cupertino
包现在应该在 115 个支持的区域设置之一中正确本地化。组件应适应本地化消息,以及正确的从左到右或从右到左布局。
尝试将目标平台的区域设置切换为西班牙语 (es
),消息应该会本地化。
基于 WidgetsApp
的应用类似,只是不需要 GlobalMaterialLocalizations.delegate
。
完整的 Locale.fromSubtags
构造函数是首选,因为它支持 scriptCode
,尽管 Locale
默认构造函数仍然完全有效。
localizationsDelegates
列表的元素是生成本地化值集合的工厂。GlobalMaterialLocalizations.delegate
为 Material Components 库提供本地化字符串和其他值。GlobalWidgetsLocalizations.delegate
为组件库定义默认文本方向,即从左到右或从右到左。
此页面介绍了有关这些应用属性、它们依赖的类型以及国际化 Flutter 应用的典型结构的更多信息。
覆盖区域设置
#Localizations.override
是 Localizations
组件的工厂构造函数,它允许(通常很少见)在应用的某个部分需要本地化为与设备配置的区域设置不同的区域设置的情况。
要观察此行为,请添加对 Localizations.override
和一个简单的 CalendarDatePicker
的调用
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Add the following code
Localizations.override(
context: context,
locale: const Locale('es'),
// Using a Builder to get the correct BuildContext.
// Alternatively, you can create a new widget and Localizations.override
// will pass the updated BuildContext to the new widget.
child: Builder(
builder: (context) {
// A toy example for an internationalized Material widget.
return CalendarDatePicker(
initialDate: DateTime.now(),
firstDate: DateTime(1900),
lastDate: DateTime(2100),
onDateChanged: (value) {},
);
},
),
),
],
),
),
);
}
热重载应用,CalendarDatePicker
组件应以西班牙语重新渲染。
添加您自己的本地化消息
#添加 flutter_localizations
包后,您可以配置本地化。要向应用添加本地化文本,请完成以下说明
添加
intl
包作为依赖项,并引入flutter_localizations
固定的版本flutter pub add intl:any
打开
pubspec.yaml
文件并启用generate
标志。此标志位于 pubspec 文件中的flutter
部分。yaml# The following section is specific to Flutter. flutter: generate: true # Add this line
向 Flutter 项目的根目录添加一个新的 yaml 文件。将此文件命名为
l10n.yaml
并包含以下内容yamlarb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart
此文件配置本地化工具。在此示例中,您已执行以下操作
- 将 应用资源包 (
.arb
) 输入文件放在${FLUTTER_PROJECT}/lib/l10n
中。.arb
提供应用的本地化资源。 - 将英语模板设置为
app_en.arb
。 - 告诉 Flutter 在
app_localizations.dart
文件中生成本地化。
- 将 应用资源包 (
在
${FLUTTER_PROJECT}/lib/l10n
中,添加app_en.arb
模板文件。例如json{ "helloWorld": "Hello World!", "@helloWorld": { "description": "The conventional newborn programmer greeting" } }
在同一目录中添加另一个名为
app_es.arb
的包文件。在此文件中,添加相同消息的西班牙语翻译。json{ "helloWorld": "¡Hola Mundo!" }
现在,运行
flutter pub get
或flutter run
,代码生成会自动进行。您应该在${FLUTTER_PROJECT}/.dart_tool/flutter_gen/gen_l10n
中找到生成的文件。或者,您也可以运行flutter gen-l10n
生成相同的文件,而无需运行应用。在对
MaterialApp
的构造函数调用中,添加app_localizations.dart
和AppLocalizations.delegate
的导入语句dartimport 'package:flutter_gen/gen_l10n/app_localizations.dart';
dartreturn const MaterialApp( title: 'Localizations Sample App', localizationsDelegates: [ AppLocalizations.delegate, // Add this line GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('en'), // English Locale('es'), // Spanish ], home: MyHomePage(), );
AppLocalizations
类还提供自动生成的localizationsDelegates
和supportedLocales
列表。您可以使用它们,而不是手动提供它们。dartconst MaterialApp( title: 'Localizations Sample App', localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, );
Material 应用启动后,您可以在应用的任何位置使用
AppLocalizations
dartappBar: AppBar( // The [AppBar] title text should update its message // according to the system locale of the target platform. // Switching between English and Spanish locales should // cause this text to update. title: Text(AppLocalizations.of(context)!.helloWorld), ),
此代码生成一个 Text
组件,如果目标设备的区域设置设置为英语,则显示“Hello World!”,如果目标设备的区域设置设置为西班牙语,则显示“¡Hola Mundo!”。在 arb
文件中,每个条目的键用作 getter 的方法名称,而该条目的值包含本地化消息。
gen_l10n_example
使用此工具。
要本地化设备应用说明,请将本地化字符串传递给 MaterialApp.onGenerateTitle
return MaterialApp(
onGenerateTitle: (context) => DemoLocalizations.of(context).title,
占位符、复数和选择
#您还可以使用特殊语法将应用值包含在消息中,该语法使用占位符生成方法而不是 getter。占位符(必须是有效的 Dart 标识符名称)成为 AppLocalizations
代码中生成方法的位置参数。通过以下方式用花括号括起来定义占位符名称
"{placeholderName}"
在应用的 .arb
文件中的 placeholders
对象中定义每个占位符。例如,要定义一个带 userName
参数的 hello 消息,请将以下内容添加到 lib/l10n/app_en.arb
"hello": "Hello {userName}",
"@hello": {
"description": "A message with a single parameter",
"placeholders": {
"userName": {
"type": "String",
"example": "Bob"
}
}
}
此代码片段向 AppLocalizations.of(context)
对象添加了 hello
方法调用,该方法接受类型为 String
的参数;hello
方法返回一个字符串。重新生成 AppLocalizations
文件。
将传递给 Builder
的代码替换为以下内容
// Examples of internationalized strings.
return Column(
children: <Widget>[
// Returns 'Hello John'
Text(AppLocalizations.of(context)!.hello('John')),
],
);
您还可以使用数字占位符来指定多个值。不同的语言有不同的单词复数化方式。语法还支持指定单词的复数化方式。复数化的消息必须包含一个 num
参数,指示在不同情况下如何复数化单词。例如,英语将“person”复数化为“people”,但这还不够。message0
复数可能是“no people”或“zero people”。messageFew
复数可能是“several people”、“some people”或“a few people”。messageMany
复数可能是“most people”或“many people”,或“a crowd”。仅需要更通用的 messageOther
字段。以下示例显示了可用的选项
"{countPlaceholder, plural, =0{message0} =1{message1} =2{message2} few{messageFew} many{messageMany} other{messageOther}}"
前面的表达式将替换为对应于 countPlaceholder
值的消息变体 (message0
、message1
等)。仅需要 messageOther
字段。
以下示例定义了一条消息,该消息对单词“wombat”进行复数化。
"nWombats": "{count, plural, =0{no wombats} =1{1 wombat} other{{count} wombats}}",
"@nWombats": {
"description": "A plural message",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
}
通过传入count
参数来使用复数方法。
// Examples of internationalized strings.
return Column(
children: <Widget>[
...
// Returns 'no wombats'
Text(AppLocalizations.of(context)!.nWombats(0)),
// Returns '1 wombat'
Text(AppLocalizations.of(context)!.nWombats(1)),
// Returns '5 wombats'
Text(AppLocalizations.of(context)!.nWombats(5)),
],
);
类似于复数,您还可以根据String
占位符选择一个值。这最常用于支持有性别的语言。语法如下所示:
"{selectPlaceholder, select, case{message} ... other{messageOther}}"
下一个示例定义了一条消息,该消息根据性别选择代词。
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
"description": "A gendered message",
"placeholders": {
"gender": {
"type": "String"
}
}
}
通过将性别字符串作为参数传递来使用此功能。
// Examples of internationalized strings.
return Column(
children: <Widget>[
...
// Returns 'he'
Text(AppLocalizations.of(context)!.pronoun('male')),
// Returns 'she'
Text(AppLocalizations.of(context)!.pronoun('female')),
// Returns 'they'
Text(AppLocalizations.of(context)!.pronoun('other')),
],
);
请记住,在使用select
语句时,参数和实际值之间的比较区分大小写。也就是说,AppLocalizations.of(context)!.pronoun("Male")
默认为“other”情况,并返回“they”。
转义语法
#有时,您必须将诸如{
和}
之类的标记用作普通字符。要忽略解析此类标记,请通过将以下内容添加到l10n.yaml
中启用use-escaping
标志:
use-escaping: true
解析器会忽略用一对单引号括起来的所有字符串。要使用普通的单引号字符,请使用一对连续的单引号。例如,以下文本将转换为Dart String
:
{
"helloWorld": "Hello! '{Isn''t}' this a wonderful day?"
}
生成的字符串如下所示:
"Hello! {Isn't} this a wonderful day?"
包含数字和货币的消息
#数字,包括表示货币值的数字,在不同的语言环境中显示方式大不相同。flutter_localizations
中的本地化生成工具使用intl
包中的NumberFormat
类根据语言环境和所需的格式来格式化数字。
int
、double
和number
类型可以使用以下任何NumberFormat
构造函数:
消息“format”值 | 1200000的输出 |
---|---|
compact | "1.2M" |
compactCurrency * | "$1.2M" |
compactSimpleCurrency * | "$1.2M" |
compactLong | "1.2 million" |
currency * | "USD1,200,000.00" |
decimalPattern | "1,200,000" |
decimalPatternDigits * | "1,200,000" |
decimalPercentPattern * | "120,000,000%" |
percentPattern | "120,000,000%" |
scientificPattern | "1E6" |
simpleCurrency * | "$1,200,000" |
表中带星号的NumberFormat
构造函数提供了可选的命名参数。这些参数可以指定为占位符的optionalParameters
对象的value。例如,要为compactCurrency
指定可选的decimalDigits
参数,请对lib/l10n/app_en.arg
文件进行以下更改:
"numberOfDataPoints": "Number of data points: {value}",
"@numberOfDataPoints": {
"description": "A message with a formatted int parameter",
"placeholders": {
"value": {
"type": "int",
"format": "compactCurrency",
"optionalParameters": {
"decimalDigits": 2
}
}
}
}
包含日期的消息
#日期字符串的格式多种多样,具体取决于语言环境和应用程序的需求。
类型为DateTime
的占位符值使用intl
包中的DateFormat
进行格式化。
有41种格式变体,由其DateFormat
工厂构造函数的名称标识。在以下示例中,出现在helloWorldOn
消息中的DateTime
值使用DateFormat.yMd
进行格式化。
"helloWorldOn": "Hello World on {date}",
"@helloWorldOn": {
"description": "A message with a date parameter",
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMd"
}
}
}
在一个语言环境为美国英语的应用程序中,以下表达式将生成“7/9/1959”。在俄语语言环境中,它将生成“9.07.1959”。
AppLocalizations.of(context).helloWorldOn(DateTime.utc(1959, 7, 9))
iOS 本地化:更新 iOS 应用包
#尽管本地化由Flutter处理,但您需要在Xcode项目中添加支持的语言。这可确保您在App Store中的条目正确显示支持的语言。
要配置您的应用程序支持的语言环境,请使用以下说明:
打开项目的
ios/Runner.xcodeproj
Xcode文件。在项目导航器中,选择项目下的
Runner
项目文件。在项目编辑器中选择信息选项卡。
在本地化部分,点击添加按钮(
+
)将支持的语言和区域添加到您的项目中。当系统提示您选择文件和参考语言时,只需选择完成即可。Xcode会自动创建空的
.strings
文件并更新ios/Runner.xcodeproj/project.pbxproj
文件。这些文件由App Store用于确定您的应用程序支持哪些语言和区域。
进一步自定义的高级主题
#本节介绍自定义本地化Flutter应用程序的其他方法。
高级区域设置定义
#一些具有多个变体的语言需要不止一个语言代码才能正确区分。
例如,要完全区分所有中文变体,需要指定语言代码、脚本代码和国家/地区代码。这是由于存在简体和繁体脚本,以及同一脚本类型中字符书写方式的区域差异。
为了完全表达国家/地区代码为CN
、TW
和HK
的所有中文变体,支持的语言环境列表应包括:
supportedLocales: [
Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hans',
countryCode: 'CN'), // 'zh_Hans_CN'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
countryCode: 'TW'), // 'zh_Hant_TW'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
countryCode: 'HK'), // 'zh_Hant_HK'
],
此明确的完整定义可确保您的应用程序能够区分并向所有这些国家/地区代码的组合提供完全细致入微的本地化内容。如果未指定用户的首选语言环境,Flutter会选择最接近的匹配项,该匹配项可能与用户期望的内容存在差异。Flutter仅解析supportedLocales
中定义的语言环境,并为常用语言提供脚本代码区分的本地化内容。有关如何解析支持的语言环境和首选语言环境的信息,请参阅Localizations
。
尽管中文是一个主要示例,但其他语言(如法语(fr_FR
、fr_CA
))也应得到充分区分,以实现更细致入微的本地化。
跟踪区域设置:Locale 类和 Localizations 组件
#Locale
类标识用户的语言。移动设备支持为所有应用程序设置语言环境,通常使用系统设置菜单。国际化应用程序通过显示特定于语言环境的值来响应。例如,如果用户将设备的语言环境从英语切换到法语,则最初显示“Hello World”的Text
小部件将使用“Bonjour le monde”重新构建。
Localizations
小部件为其子级定义语言环境及其子级依赖的本地化资源。WidgetsApp
小部件创建了一个Localizations
小部件,并在系统语言环境更改时对其进行重建。
您可以始终使用Localizations.localeOf()
查找应用程序的当前语言环境。
Locale myLocale = Localizations.localeOf(context);
指定应用支持的 Locales 参数
#尽管flutter_localizations
库目前支持115种语言和语言变体,但默认情况下仅提供英语翻译。开发人员需要决定要支持哪些语言。
MaterialApp
的supportedLocales
参数限制了语言环境的更改。当用户更改其设备上的语言环境设置时,应用程序的Localizations
小部件仅在新的语言环境是此列表的成员时才会随之更改。如果找不到设备语言环境的完全匹配项,则使用第一个具有匹配languageCode
的支持的语言环境。如果这失败,则使用supportedLocales
列表的第一个元素。
想要使用不同“语言环境解析”方法的应用程序可以提供localeResolutionCallback
。例如,要使您的应用程序无条件地接受用户选择的任何语言环境:
MaterialApp(
localeResolutionCallback: (
locale,
supportedLocales,
) {
return locale;
},
);
配置 l10n.yaml 文件
#l10n.yaml
文件允许您配置gen-l10n
工具以指定以下内容:
- 所有输入文件所在的目录。
- 所有输出文件应创建到的目录。
- 要为您的本地化委托提供的Dart类名。
有关选项的完整列表,请在命令行中运行flutter gen-l10n --help
或参考下表:
选项 | 描述 |
---|---|
arb-dir | 模板和翻译后的arb文件所在的目录。默认为lib/l10n 。 |
output-dir | 生成的本地化类写入到的目录。仅当您希望在Flutter项目的其他位置生成本地化代码时,此选项才相关。您还需要将synthetic-package 标志设置为false。应用程序必须从该目录导入 output-localization-file 选项中指定的文件。如果未指定,则默认为与arb-dir 中指定的输入目录相同的目录。 |
template-arb-file | 用作生成Dart本地化和消息文件的依据的模板arb文件。默认为app_en.arb 。 |
output-localization-file | 输出本地化和本地化委托类的文件名。默认为app_localizations.dart 。 |
untranslated-messages-file | 描述尚未翻译的本地化消息的文件的位置。使用此选项将在目标位置创建以下格式的JSON文件:"locale": ["message_1", "message_2" ... "message_n"] 如果未指定此选项,则会在命令行上打印尚未翻译的消息摘要。 |
output-class | 用于输出本地化和本地化委托类的Dart类名。默认为AppLocalizations 。 |
preferred-supported-locales | 应用程序的首选支持语言环境列表。默认情况下,该工具会按字母顺序生成支持的语言环境列表。使用此标志默认为不同的语言环境。 例如,传入 [ en_US ] 以在设备支持的情况下默认为美式英语。 |
header | 要添加到生成的Dart本地化文件开头的标头。此选项接收一个字符串。 例如,传入 "/// All localized files." 以将此字符串添加到生成的Dart文件的开头。或者,查看 header-file 选项以传入一个文本文件以获取更长的标头。 |
header-file | 要添加到生成的Dart本地化文件开头的标头。此选项的值是包含插入到每个生成的Dart文件顶部的标头文本的文件的名称。 或者,查看 header 选项以传入一个字符串以获取更简单的标头。此文件应放置在 arb-dir 中指定的目录中。 |
[no-]use-deferred-loading | 指定是否使用延迟导入的语言环境生成Dart本地化文件,从而允许在Flutter Web中延迟加载每个语言环境。 这可以通过减小JavaScript捆绑包的大小来减少Web应用程序的初始启动时间。当此标志设置为true时,Flutter应用程序仅在需要时才下载和加载特定语言环境的消息。对于具有大量不同语言环境和许多本地化字符串的项目,延迟加载可以提高性能。对于语言环境数量较少的项目,差异可以忽略不计,并且与捆绑应用程序其余部分的本地化相比,可能会减慢启动速度。 请注意,此标志不影响其他平台(如移动或桌面)。 |
gen-inputs-and-outputs-list | 指定时,该工具会生成一个包含工具的输入和输出的JSON文件,名为gen_l10n_inputs_and_outputs.json 。这对于跟踪在生成最新本地化集时使用了Flutter项目的哪些文件很有用。例如,Flutter工具的构建系统使用此文件来跟踪何时在热重载期间调用gen_l10n。 此选项的值是生成 JSON 文件的目录。如果为 null,则不会生成 JSON 文件。 |
synthetic-package | 确定生成的输出文件是作为合成包生成还是在 Flutter 项目中的指定目录中生成。此标志默认为 true 。当 synthetic-package 设置为 false 时,它默认在 arb-dir 指定的目录中生成本地化文件。如果指定了 output-dir ,则文件将生成在那里。 |
project-dir | 如果指定,则该工具将使用传递到此选项的路径作为根 Flutter 项目的目录。 如果为 null,则使用当前工作目录的相对路径。 |
[no-]required-resource-attributes | 要求所有资源 ID 都包含相应的资源属性。 默认情况下,简单的消息不需要元数据,但强烈建议使用,因为这为读者提供了消息含义的上下文。 复数消息仍然需要资源属性。 |
[no-]nullable-getter | 指定本地化类 getter 是否可为空。 默认情况下,此值为 true,以便 Localizations.of(context) 返回一个可为空的值以实现向后兼容性。如果此值为 false,则对 Localizations.of(context) 的返回值执行空检查,从而无需在用户代码中进行空检查。 |
[no-]format | 如果指定,则在生成本地化文件后运行 dart format 命令。 |
use-escaping | 指定是否启用使用单引号作为转义语法。 |
[no-]suppress-warnings | 如果指定,则抑制所有警告。 |
[no-]relax-syntax | 如果指定,则语法会变得宽松,以便如果特殊字符 "{" 后面没有有效的占位符,则将其视为字符串;如果 "}" 后面没有关闭任何之前被视为特殊字符的 "{",则将其视为字符串。 |
[no-]use-named-parameters | 是否为生成的本地化方法使用命名参数。 |
Flutter 中国际化工作原理
#本节介绍 Flutter 中本地化工作原理的技术细节。如果您计划支持自己的本地化消息集,以下内容将有所帮助。否则,您可以跳过本节。
加载和检索本地化值
#Localizations
小部件用于加载和查找包含本地化值集合的对象。应用程序使用 Localizations.of(context,type)
来引用这些对象。如果设备的语言环境发生更改,Localizations
小部件会自动加载新语言环境的值,然后重建使用它的部件。发生这种情况是因为 Localizations
的工作方式类似于 InheritedWidget
。当构建函数引用继承的小部件时,会创建对继承的小部件的隐式依赖关系。当继承的小部件发生更改(当 Localizations
小部件的语言环境发生更改时),其依赖的上下文将被重建。
本地化值由 Localizations
小部件的 LocalizationsDelegate
列表加载。每个委托必须定义一个异步的 load()
方法,该方法生成一个封装本地化值集合的对象。通常,这些对象为每个本地化值定义一个方法。
在一个大型应用程序中,不同的模块或包可能会捆绑他们自己的本地化。这就是 Localizations
小部件管理对象表的原因,每个 LocalizationsDelegate
一个。要检索由其中一个 LocalizationsDelegate
的 load
方法生成的对象,请指定一个 BuildContext
和对象的类型。
例如,Material 组件部件的本地化字符串由 MaterialLocalizations
类定义。此类的实例由 MaterialApp
类提供的 LocalizationDelegate
创建。可以使用 Localizations.of()
检索它们。
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
此特定的 Localizations.of()
表达式经常使用,因此 MaterialLocalizations
类提供了一个方便的简写。
static MaterialLocalizations of(BuildContext context) {
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}
/// References to the localized values defined by MaterialLocalizations
/// are typically written like this:
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
为应用的本地化资源定义一个类
#构建国际化 Flutter 应用程序通常从封装应用程序本地化值的类开始。以下示例是此类类的典型示例。
此应用程序的 intl_example
的完整源代码。
此示例基于 intl
包提供的 API 和工具。 应用程序本地化资源的替代类 部分描述了 一个示例,该示例不依赖于 intl
包。
DemoLocalizations
类(在以下代码片段中定义)包含应用程序的字符串(对于示例来说只有一个),并将其翻译成应用程序支持的语言环境。它使用 Dart 的 intl
包生成的 initializeMessages()
函数,Intl.message()
,来查找它们。
class DemoLocalizations {
DemoLocalizations(this.localeName);
static Future<DemoLocalizations> load(Locale locale) {
final String name =
locale.countryCode == null || locale.countryCode!.isEmpty
? locale.languageCode
: locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
return DemoLocalizations(localeName);
});
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
final String localeName;
String get title {
return Intl.message(
'Hello World',
name: 'title',
desc: 'Title for the Demo application',
locale: localeName,
);
}
}
基于 intl
包的类导入一个生成的的消息目录,该目录提供 initializeMessages()
函数和 Intl.message()
的每个语言环境的后备存储。消息目录由 intl
工具 生成,该工具分析包含 Intl.message()
调用的类的源代码。在这种情况下,它将只是 DemoLocalizations
类。
添加对新语言的支持
#需要支持 GlobalMaterialLocalizations
中未包含的语言的应用程序需要做一些额外的工作:它必须为单词或短语以及语言环境的日期模式和符号提供大约 70 个翻译(“本地化”)。
请参阅以下内容,了解如何添加对挪威新挪威语的支持。
新的 GlobalMaterialLocalizations
子类定义了 Material 库依赖的本地化。还必须定义一个新的 LocalizationsDelegate
子类,作为 GlobalMaterialLocalizations
子类的工厂。
以下是完整 add_language
示例的源代码,减去实际的新挪威语翻译。
特定于语言环境的 GlobalMaterialLocalizations
子类称为 NnMaterialLocalizations
,LocalizationsDelegate
子类称为 _NnMaterialLocalizationsDelegate
。NnMaterialLocalizations.delegate
的值是委托的实例,对于使用这些本地化的应用程序来说,这已足够了。
委托类包括基本的日期和数字格式本地化。所有其他本地化都由 NnMaterialLocalizations
中的 String
值属性 getter 定义,如下所示
@override
String get moreButtonTooltip => r'More';
@override
String get aboutListTileTitleRaw => r'About $applicationName';
@override
String get alertDialogLabel => r'Alert';
当然,这些是英文翻译。要完成工作,您需要将每个 getter 的返回值更改为适当的新挪威语字符串。
getter 返回带有 r
前缀的“原始” Dart 字符串,例如 r'About $applicationName'
,因为有时字符串包含带有 $
前缀的变量。变量由参数化本地化方法扩展。
@override
String get pageRowsInfoTitleRaw => r'$firstRow–$lastRow of $rowCount';
@override
String get pageRowsInfoTitleApproximateRaw =>
r'$firstRow–$lastRow of about $rowCount';
还需要指定语言环境的日期模式和符号,这些符号在源代码中定义如下
const nnLocaleDatePatterns = {
'd': 'd.',
'E': 'ccc',
'EEEE': 'cccc',
'LLL': 'LLL',
// ...
}
const nnDateSymbols = {
'NAME': 'nn',
'ERAS': <dynamic>[
'f.Kr.',
'e.Kr.',
],
// ...
}
需要修改这些值才能使语言环境使用正确的日期格式。不幸的是,由于 intl
库在数字格式方面没有相同的灵活性,因此必须在 _NnMaterialLocalizationsDelegate
中使用现有语言环境的格式作为替代。
class _NnMaterialLocalizationsDelegate
extends LocalizationsDelegate<MaterialLocalizations> {
const _NnMaterialLocalizationsDelegate();
@override
bool isSupported(Locale locale) => locale.languageCode == 'nn';
@override
Future<MaterialLocalizations> load(Locale locale) async {
final String localeName = intl.Intl.canonicalizedLocale(locale.toString());
// The locale (in this case `nn`) needs to be initialized into the custom
// date symbols and patterns setup that Flutter uses.
date_symbol_data_custom.initializeDateFormattingCustom(
locale: localeName,
patterns: nnLocaleDatePatterns,
symbols: intl.DateSymbols.deserializeFromMap(nnDateSymbols),
);
return SynchronousFuture<MaterialLocalizations>(
NnMaterialLocalizations(
localeName: localeName,
// The `intl` library's NumberFormat class is generated from CLDR data
// (see https://github.com/dart-lang/i18n/blob/main/pkgs/intl/lib/number_symbols_data.dart).
// Unfortunately, there is no way to use a locale that isn't defined in
// this map and the only way to work around this is to use a listed
// locale's NumberFormat symbols. So, here we use the number formats
// for 'en_US' instead.
decimalFormat: intl.NumberFormat('#,##0.###', 'en_US'),
twoDigitZeroPaddedFormat: intl.NumberFormat('00', 'en_US'),
// DateFormat here will use the symbols and patterns provided in the
// `date_symbol_data_custom.initializeDateFormattingCustom` call above.
// However, an alternative is to simply use a supported locale's
// DateFormat symbols, similar to NumberFormat above.
fullYearFormat: intl.DateFormat('y', localeName),
compactDateFormat: intl.DateFormat('yMd', localeName),
shortDateFormat: intl.DateFormat('yMMMd', localeName),
mediumDateFormat: intl.DateFormat('EEE, MMM d', localeName),
longDateFormat: intl.DateFormat('EEEE, MMMM d, y', localeName),
yearMonthFormat: intl.DateFormat('MMMM y', localeName),
shortMonthDayFormat: intl.DateFormat('MMM d'),
),
);
}
@override
bool shouldReload(_NnMaterialLocalizationsDelegate old) => false;
}
有关本地化字符串的更多信息,请查看 flutter_localizations 自述文件。
实现 GlobalMaterialLocalizations
和 LocalizationsDelegate
的特定于语言的子类后,您需要将语言和委托实例添加到您的应用程序中。以下代码将应用程序的语言设置为新挪威语,并将 NnMaterialLocalizations
委托实例添加到应用程序的 localizationsDelegates
列表中
const MaterialApp(
localizationsDelegates: [
GlobalWidgetsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
NnMaterialLocalizations.delegate, // Add the newly created delegate
],
supportedLocales: [
Locale('en', 'US'),
Locale('nn'),
],
home: Home(),
),
替代国际化工作流
#本节描述了国际化 Flutter 应用程序的不同方法。
应用本地化资源的替代类
#前面的示例是根据 Dart intl
包定义的。为了简单起见,或者为了与其他 i18n 框架集成,您可以选择自己的方法来管理本地化值。
minimal
应用程序的完整源代码。
在以下示例中,DemoLocalizations
类直接在其每个语言的 Maps 中包含所有翻译。
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
static const _localizedValues = <String, Map<String, String>>{
'en': {
'title': 'Hello World',
},
'es': {
'title': 'Hola Mundo',
},
};
static List<String> languages() => _localizedValues.keys.toList();
String get title {
return _localizedValues[locale.languageCode]!['title']!;
}
}
在 minimal 应用程序中,DemoLocalizationsDelegate
略有不同。它的 load
方法返回 SynchronousFuture
,因为不需要进行异步加载。
class DemoLocalizationsDelegate
extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
@override
bool isSupported(Locale locale) =>
DemoLocalizations.languages().contains(locale.languageCode);
@override
Future<DemoLocalizations> load(Locale locale) {
// Returning a SynchronousFuture here because an async "load" operation
// isn't needed to produce an instance of DemoLocalizations.
return SynchronousFuture<DemoLocalizations>(DemoLocalizations(locale));
}
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
}
使用 Dart intl 工具
#在使用 Dart intl
包构建 API 之前,请查看 intl
包的文档。以下列表总结了本地化依赖于 intl
包的应用程序的过程。
演示应用程序依赖于一个名为 l10n/messages_all.dart
的生成的源文件,该文件定义了应用程序使用的所有可本地化字符串。
重新构建 l10n/messages_all.dart
需要两个步骤。
以应用程序的根目录作为当前目录,从
lib/main.dart
生成l10n/intl_messages.arb
。dart run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart
intl_messages.arb
文件是一个 JSON 格式的映射,每个条目对应于main.dart
中定义的每个Intl.message()
函数。此文件用作英语和西班牙语翻译(intl_en.arb
和intl_es.arb
)的模板。这些翻译由您(开发人员)创建。以应用程序的根目录作为当前目录,为每个
intl_<locale>.arb
文件生成intl_messages_<locale>.dart
和intl_messages_all.dart
,后者导入所有消息文件。dart run intl_translation:generate_from_arb \ --output-dir=lib/l10n --no-use-deferred-loading \ lib/main.dart lib/l10n/intl_*.arb
Windows 不支持文件名通配符。 相反,列出由
intl_translation:extract_to_arb
命令生成的 .arb 文件。dart run intl_translation:generate_from_arb \ --output-dir=lib/l10n --no-use-deferred-loading \ lib/main.dart \ lib/l10n/intl_en.arb lib/l10n/intl_fr.arb lib/l10n/intl_messages.arb
DemoLocalizations
类使用生成的initializeMessages()
函数(在intl_messages_all.dart
中定义)加载本地化消息,并使用Intl.message()
查找它们。
更多信息
#如果您喜欢通过阅读代码来学习,请查看以下示例。
minimal
minimal
示例旨在尽可能简单。intl_example
使用intl
包提供的 API 和工具。
如果您不熟悉 Dart 的 intl
包,请查看 使用 Dart intl 工具。
除非另有说明,否则本网站上的文档反映了 Flutter 的最新稳定版本。页面上次更新于 2024-10-24。 查看源代码 或 报告问题。