Flutter本地化数据支持插件common_locale_data的使用
Flutter本地化数据支持插件common_locale_data的使用
common_locale_data
插件提供了类型安全且可树摇的方法来访问翻译后的通用数据。这些翻译是从通用区域数据存储库(CLDR)提取出来的。
该插件还包括用于使用这些数据格式化单位、时区名称和区域名称的帮助函数。
可用数据
翻译包括:
- 测量单位的全称和缩写形式,包括单数/复数变化
- 语言名称
- 脚本、变体和Unicode扩展名称
- 国家和地区名称
- 国家子分区名称
- 货币名称,包括单数/复数变化
- 星期几、月份、纪元、一天中的时间段,全称和缩写形式
- 日历字段
- 相对时间字段
- 时区和时区示例城市(或类似物)
功能
翻译可以通过静态成员函数或动态映射访问。
格式化函数可用于:单位、货币、相对时间字段、时区和区域标识符。
区域标识符支持解析、规范化、添加和删除可能的子标签,并以多种格式进行格式化。
树摇优化
所有数据和翻译都作为代码中的字面字符串存储。API 设计为可树摇(机制移除未使用的数据)。最终应用程序的二进制文件不会包含未使用的语言的翻译,也不会包含未使用的数据类型。
如果您单独使用区域(例如不存储在集合中),树摇效果最佳:仅包括实际使用的每个区域的数据类型。
如果通过静态访问成员,编译器也可以减少到仅使用的字段(例如国家、货币、语言)。如果使用动态访问(通过 []
操作符),则会包括所有字段。
如果您将区域存储在集合中并动态选择集合成员(或遍历集合),编译器无法确定实际使用的区域,因此它会包含集合中的所有区域及其所有使用的数据类型。
编译
为了提高编译速度,当使用 [common_locale_data]
时,需要单独导入区域。
例如,对于英语:
import 'package:common_locale_data/en.dart';
或者对于法语:
import 'package:common_locale_data/fr.dart';
您可以导入所有区域,通过 [CommonLocaleDataAll]
。但是这将显著增加编译时间(比包括几个区域要多10倍)。
只要您不使用 [CommonLocaleDataAll.locales]
成员动态获取区域,编译后的文件大小不会受到影响。如果您确实使用了 [CommonLocaleDataAll.locales]
成员,所有区域都会被编译进来,文件大小会达到几十兆字节(取决于您使用的数据类型)。
数据源
主要的 CLDR 数据从以下仓库提取:https://github.com/unicode-org/cldr-json。
附加时区相关数据从以下仓库提取:https://github.com/unicode-org/cldr 和 https://github.com/unicode-org/icu。
- CLDR 版本 46.0.0,变体:现代
- Unicode 版本 16.0.0
- TZDB 版本 2024b
示例
// 导入特定区域而不是 common_locale_data_all.dart 以节省编译时间。
import 'package:common_locale_data/de.dart';
import 'package:common_locale_data/en.dart';
import 'package:common_locale_data/en_gb.dart';
import 'package:common_locale_data/es.dart';
import 'package:common_locale_data/fr.dart';
import 'package:common_locale_data/zh_hans.dart';
import 'package:intl/intl.dart';
void main() {
// 打印此包的基本版本信息。
print(
'CLDR version: ${CommonLocaleData.cldrVersion} - ${CommonLocaleData.cldrVariant} ');
print('Unicode version: ${CommonLocaleData.unicodeVersion}');
print('ICU version: ${CommonLocaleData.icuVersion}');
print('Timezone DB version: ${CommonLocaleData.tzdbVersion}');
// 为了获得最佳的树摇效果,你应该显式选择你想要使用的语言,例如 `var cld = CommonLocaleDataEnGB()`。
// 然而在实践中,你通常希望根据支持的语言动态选择用户的首选语言。
// 你可以使用 e.g. Platform.localeName 或者 Intl.defaultLocale。
// 为了说明目的,我们在这里固定一组所需的语言:
var desiredLocales = ['nl', 'en-IR'];
// 随意选择的一组支持的语言。
var supportedLocales = [
CommonLocaleDataDe(),
CommonLocaleDataEn(),
CommonLocaleDataEnGB(),
CommonLocaleDataEs(),
CommonLocaleDataFr(),
CommonLocaleDataZhHans(),
];
// 获取最佳匹配的语言。
var cld =
LocaleMatcher.getBestCommonLocaleData(desiredLocales, supportedLocales)!;
// 打印当前语言标识。
print('');
print(
'Current localeId: ${cld.locale}: ${cld.localeDisplayName.formatWithExtensions(LocaleId.parse(cld.locale))}');
// 单位
print('');
print(cld.units.lengthMeter); // meters
print(cld.units.lengthMeter.long(3.5)); // 3 meters
print(cld.units.lengthMeter.long(3)); // 3 meters
print(cld.units.lengthMeter.long(1)); // 1 meter
print(cld.units.areaSquareMeter.long(3.5)); // 3 square meters
print(cld.units.areaSquareMeter.short(3.5)); // 3 m²
print(cld.units.areaSquareMeter.narrow(3.5)); // 3m²
// 具有不同格式的单位
print('');
print(cld.units.areaSquareMeter.short(3541.53));
print(cld.units.areaSquareMeter.short(3541.53,
numberFormat: NumberFormat.decimalPatternDigits(decimalDigits: 2)));
print(cld.units.areaSquareMeter
.short(3541.53, numberFormat: NumberFormat.compact()));
print(cld.units.areaSquareMeter
.short(3541.53, numberFormat: NumberFormat.compactLong()));
// 复合单位
print('');
print(cld.units.per.long(cld.units.electricAmpere.long.displayName,
cld.units.durationSecond.long.displayName));
print(cld.units.per.short(cld.units.electricAmpere.short.displayName,
cld.units.durationSecond.short.displayName));
print(cld.units.per.narrow(cld.units.electricAmpere.narrow.displayName,
cld.units.durationSecond.narrow.displayName));
print(cld.units.times.long(cld.units.electricOhm.long.displayName,
cld.units.lengthMeter.long.displayName));
print(cld.units.times.short(cld.units.electricOhm.short.displayName,
cld.units.lengthMeter.short.displayName));
print(cld.units.times.narrow(cld.units.electricOhm.narrow.displayName,
cld.units.lengthMeter.narrow.displayName));
// 日期字段
print('');
print(cld.date.year.future.long(2)); // in 2 years
print(cld.date.year.past.long(2)); // 2 years ago
print(cld.date.year.next.long); // next year
print(cld.date.year.previous.long); // last year
// 领土
print('');
print(cld.territories.world); // world
print(cld.territories.africa); // Africa
print(cld.territories.$019); // Americas
print(cld.territories.us); // South Africa
// 使用动态访问的领土:这将导致所有领土数据被包含在二进制文件中
print(cld.territories.territories['019']); // Americas
print(cld.territories.territories['US']); // United States
// 语言名称
print('');
print(cld.languages.en.name); // English
print(cld.languages.zhHans.name); // Simplified Chinese
print(cld.languages.und.name); // Undefined language
// 使用动态访问的语言:这将导致所有领土数据被包含在二进制文件中
print(cld.languages['en']!.name); // English
// 货币
print('');
var currency = cld.currencies.usd;
print(currency.displayName);
print(currency.symbol);
print(currency.symbolNarrow);
print(currency.symbolVariant);
print(currency(1.51));
print(currency.short(1.51));
print(currency.narrow(1.51));
print(currency.variant(1.51));
print(currency.iso(1.51));
print(currency.hidden(1.51));
print(currency.full(0));
print(currency.full(0.51));
print(currency.full(1));
print(currency.full(1, decimalDigits: 1));
print(currency.full(1.51));
print(currency.full(2));
print(currency.full(2.51));
print(currency.full(3));
print(currency.full(10));
print(currency.short(1513));
print(currency.short(1513, compact: true));
print(currency.full(1513));
print(currency.full(1513, compact: true));
// 使用动态访问的货币:这将导致所有领土数据被包含在二进制文件中
print(cld.currencies['eur']!.displayName);
// 时区
print('');
var zone = cld.timeZones.get('europe/athens', dateTime: DateTime(2017))!;
print('code: ${zone.code}');
print('canonicalCode: ${zone.canonicalCode}');
print('ianaCode: ${zone.iana}');
print('shortCode: ${zone.short}');
print('exemplarCity: ${zone.exemplarCity}');
print('country: ${zone.country}');
print('metaZone code: ${zone.metaZone?.code}');
print('isPrimaryOrSingle: ${zone.isPrimaryOrSingle}');
print('dateRange: ${zone.dateRange}');
// 时区的不同格式
print('');
var otherCountry = 'US';
for (var style in TimeZoneStyle.values) {
// 偏移量手动获取,使用时区包来获取给定日期/时间的偏移量。
var zoneNameOwn = zone.format(style, Duration(hours: 2, minutes: 0));
// 也依赖于当前国家
var zoneNameOther = zone.format(style, Duration(hours: 2, minutes: 0),
country: otherCountry);
print(
'${style.name.padLeft(28)}: $zoneNameOwn${zoneNameOwn != zoneNameOther ? " (in $otherCountry: $zoneNameOther)".padLeft(50) : ""}');
}
// 展示不同时间的时区名称
print('');
print(cld.timeZones['America/Buenos_Aires']); // 使用当前 DateTime
print(cld.timeZones.get('America/Buenos_Aires', dateTime: DateTime(2008)));
print(cld.timeZones
.get('America/Buenos_Aires', dateTime: DateTime(2004, 6, 2)));
// 区域标识符操作
print('');
var localeId = LanguageId(lang: 'en', region: 'us');
print(' toString: $localeId');
print(' toBCP47: ${localeId.toBCP47()}');
print(' toUnicodeBCP47: ${localeId.toUnicodeBCP47()}');
print(' toUnicodeCLDR: ${localeId.toUnicodeCLDR()}');
print(' canonicalize: ${localeId.canonicalize()}');
print(' addLikelySubTags: ${localeId.addLikelySubTags()}');
print('removeLikelySubTags: ${localeId.removeLikelySubTags()}');
print(
' localeDisplayName: ${cld.localeDisplayName.formatWithExtensions(localeId)}');
print('');
localeId = LocaleId.parse(
'en_US_POSIX_fonipa_u_co_phonebk_kb_yes_x_private@calendar=japanese;timezone=europe/paris###');
print(' toString: $localeId');
print(' toBCP47: ${localeId.toBCP47()}');
print(' toUnicodeBCP47: ${localeId.toUnicodeBCP47()}');
print(' toUnicodeCLDR: ${localeId.toUnicodeCLDR()}');
print(' canonicalize: ${localeId.canonicalize()}');
print(' addLikelySubTags: ${localeId.addLikelySubTags()}');
print('removeLikelySubTags: ${localeId.removeLikelySubTags()}');
print(' localeDisplayName: ${cld.localeDisplayName.format(localeId)}');
print(
' with Extensions: ${cld.localeDisplayName.formatWithExtensions(localeId)}');
// 区域显示名称
print('');
print(cld.localeDisplayName.format(LanguageId.parse('en-GB')));
print(cld.localeDisplayName
.format(LanguageId.parse('en-GB'), preferComposition: true));
print(cld.localeDisplayName.format(LanguageId.parse('hi-Latn-GB')));
print(cld.localeDisplayName.format(LanguageId.parse('hi-Latn-GB'),
preferType: LocaleDisplayNameStyle.variant));
print(cld.localeDisplayName.format(LanguageId.parse('hi-Latn-GB'),
preferType: LocaleDisplayNameStyle.short));
print(cld.localeDisplayName.format(LanguageId.parse('hi-Latn-GB'),
preferType: LocaleDisplayNameStyle.menu));
// 不带扩展格式化
print(cld.localeDisplayName.format(LocaleId.parse(
'en-GB-scoUse-fonipa-abl1943-u-cu-eur-ms-uksystem-rg-gband-sd-gblnd')));
// 带扩展格式化(因此会拉取分区、货币和时区数据)
print(cld.localeDisplayName.formatWithExtensions(LocaleId.parse(
'en-GB-scoUse-fonipa-abl1943-u-cu-eur-ms-ussystem-rg-gband-sd-gblnd')));
}
更多关于Flutter本地化数据支持插件common_locale_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter本地化数据支持插件common_locale_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用Flutter本地化数据支持插件common_locale_data
的代码案例。这个插件可以帮助你管理和使用应用程序中的本地化数据。
首先,确保你已经在pubspec.yaml
文件中添加了common_locale_data
依赖:
dependencies:
flutter:
sdk: flutter
common_locale_data: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来获取依赖。
接下来,你可以按照以下步骤在你的Flutter应用程序中使用common_locale_data
插件:
- 定义本地化文件:
假设你有两个语言环境的本地化资源,英语(en
)和中文(zh
)。你可以创建两个JSON文件,比如en.json
和zh.json
,内容如下:
en.json
:
{
"greeting": "Hello",
"farewell": "Goodbye"
}
zh.json
:
{
"greeting": "你好",
"farewell": "再见"
}
- 加载本地化数据:
在你的Flutter应用中,你可以使用common_locale_data
来加载这些JSON文件。以下是一个简单的示例,展示了如何加载和使用本地化数据:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:common_locale_data/common_locale_data.dart';
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
localizationsDelegates: [
// 添加common_locale_data的LocalizationsDelegate
CommonLocaleData.delegate,
// 添加Flutter自带的localizations delegates
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: CommonLocaleData.supportedLocales,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Locale _currentLocale;
Map<String, String> _localizedStrings;
@override
void initState() {
super.initState();
_currentLocale = Localizations.localeOf(context);
loadLocalizedStrings(_currentLocale.languageCode);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Localization Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_localizedStrings['greeting'] ?? 'Loading...'),
ElevatedButton(
onPressed: () {
// 切换语言示例(这里只是简单地切换英语和中文)
setState(() {
if (_currentLocale.languageCode == 'en') {
_currentLocale = Locale('zh');
} else {
_currentLocale = Locale('en');
}
loadLocalizedStrings(_currentLocale.languageCode);
// 更新应用的语言环境
Localizations.overrideLocaleOf(context, _currentLocale);
});
},
child: Text('Switch Language'),
),
Text(_localizedStrings['farewell'] ?? 'Loading...'),
],
),
),
);
}
Future<void> loadLocalizedStrings(String languageCode) async {
String jsonString = '';
// 根据语言代码加载对应的JSON文件
if (languageCode == 'zh') {
final file = File('${(await getApplicationDocumentsDirectory()).path}/zh.json');
jsonString = await file.readAsString();
} else if (languageCode == 'en') {
final file = File('${(await getApplicationDocumentsDirectory()).path}/en.json');
jsonString = await file.readAsString();
}
// 解析JSON数据
final Map<String, dynamic> jsonMap = jsonDecode(jsonString);
setState(() {
_localizedStrings = jsonMap.map<String, String>((key, value) => MapEntry(key, value.toString()));
});
}
}
注意:上面的代码示例中,假设你的JSON文件已经被复制到应用程序的文档目录中。在实际应用中,你可能需要在应用启动时将这些文件从assets复制到文档目录,或者直接使用rootBundle
从assets加载它们。这里为了简化示例,直接假设文件已经存在。
使用rootBundle
从assets加载JSON文件的示例:
Future<void> loadLocalizedStringsFromAssets(String languageCode) async {
String jsonString;
if (languageCode == 'zh') {
jsonString = await rootBundle.loadString('assets/locales/zh.json');
} else if (languageCode == 'en') {
jsonString = await rootBundle.loadString('assets/locales/en.json');
}
final Map<String, dynamic> jsonMap = jsonDecode(jsonString);
setState(() {
_localizedStrings = jsonMap.map<String, String>((key, value) => MapEntry(key, value.toString()));
});
}
在pubspec.yaml
中添加assets:
flutter:
assets:
- assets/locales/en.json
- assets/locales/zh.json
然后调用loadLocalizedStringsFromAssets
而不是loadLocalizedStrings
即可。
这个示例展示了如何使用common_locale_data
插件来管理和加载本地化数据,并通过按钮切换语言环境。当然,根据实际需求,你可能需要进一步优化和扩展这个示例。