Gen localization classes intead of a big map (#13653)
* Gen localization classes intead of a big map * tighten up the newlines so that std output matches file output * restore locale sorting
This commit is contained in:
parent
30720bd148
commit
31418570a2
@ -91,28 +91,139 @@ String generateString(String s) {
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
String generateLocalizationsMap() {
|
||||
String generateTranslationBundles() {
|
||||
final StringBuffer output = new StringBuffer();
|
||||
|
||||
output.writeln('''
|
||||
/// Maps from [Locale.languageCode] to a map that contains the localized strings
|
||||
/// for that locale.
|
||||
///
|
||||
/// This variable is used by [MaterialLocalizations].
|
||||
const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {''');
|
||||
|
||||
for (String locale in localeToResources.keys.toList()..sort()) {
|
||||
output.writeln(" '$locale': const <String, String>{");
|
||||
|
||||
final Map<String, String> resources = localeToResources[locale];
|
||||
for (String name in resources.keys) {
|
||||
final String value = generateString(resources[name]);
|
||||
output.writeln(" '$name': $value,");
|
||||
}
|
||||
output.writeln(' },');
|
||||
final Map<String, List<String>> languageToLocales = <String, List<String>>{};
|
||||
final Set<String> allResourceIdentifiers = new Set<String>();
|
||||
for(String locale in localeToResources.keys.toList()..sort()) {
|
||||
final List<String> codes = locale.split('_'); // [language, country]
|
||||
assert(codes.length == 1 || codes.length == 2);
|
||||
languageToLocales[codes[0]] ??= <String>[];
|
||||
languageToLocales[codes[0]].add(locale);
|
||||
allResourceIdentifiers.addAll(localeToResources[locale].keys);
|
||||
}
|
||||
|
||||
output.writeln('};');
|
||||
// Generate the TranslationsBundle base class. It contains one getter
|
||||
// per resource identifier found in any of the .arb files.
|
||||
//
|
||||
// class TranslationsBundle {
|
||||
// const TranslationsBundle(this.parent);
|
||||
// final TranslationsBundle parent;
|
||||
// String get scriptCategory => parent?.scriptCategory;
|
||||
// ...
|
||||
// }
|
||||
output.writeln('''
|
||||
// The TranslationBundle subclasses defined here encode all of the translations
|
||||
// found in the flutter_localizations/lib/src/l10n/*.arb files.
|
||||
//
|
||||
// The [MaterialLocalizations] class uses the (generated)
|
||||
// translationBundleForLocale() function to look up a const TranslationBundle
|
||||
// instance for a locale.
|
||||
|
||||
import \'dart:ui\' show Locale;
|
||||
|
||||
class TranslationBundle {
|
||||
const TranslationBundle(this.parent);
|
||||
final TranslationBundle parent;''');
|
||||
for (String key in allResourceIdentifiers)
|
||||
output.writeln(' String get $key => parent?.$key;');
|
||||
output.writeln('''
|
||||
}''');
|
||||
|
||||
// Generate one private TranslationBundle subclass per supported
|
||||
// language. Each of these classes overrides every resource identifier
|
||||
// getter. For example:
|
||||
//
|
||||
// class _Bundle_en extends TranslationBundle {
|
||||
// const _Bundle_en() : super(null);
|
||||
// @override String get scriptCategory => r'English-like';
|
||||
// ...
|
||||
// }
|
||||
for(String language in languageToLocales.keys) {
|
||||
final Map<String, String> resources = localeToResources[language];
|
||||
output.writeln('''
|
||||
|
||||
// ignore: camel_case_types
|
||||
class _Bundle_$language extends TranslationBundle {
|
||||
const _Bundle_$language() : super(null);''');
|
||||
for (String key in resources.keys) {
|
||||
final String value = generateString(resources[key]);
|
||||
output.writeln('''
|
||||
@override String get $key => $value;''');
|
||||
}
|
||||
output.writeln('''
|
||||
}''');
|
||||
}
|
||||
|
||||
// Generate one private TranslationBundle subclass for each locale
|
||||
// with a country code. The parent of these subclasses is a const
|
||||
// instance of a translation bundle for the same locale, but without
|
||||
// a country code. These subclasses only override getters that
|
||||
// return different value than the parent class, or a resource identifier
|
||||
// that's not defined in the parent class. For example:
|
||||
//
|
||||
// class _Bundle_en_CA extends TranslationBundle {
|
||||
// const _Bundle_en_CA() : super(const _Bundle_en());
|
||||
// @override String get licensesPageTitle => r'Licences';
|
||||
// ...
|
||||
// }
|
||||
for(String language in languageToLocales.keys) {
|
||||
final Map<String, String> languageResources = localeToResources[language];
|
||||
for(String localeName in languageToLocales[language]) {
|
||||
if (localeName == language)
|
||||
continue;
|
||||
final Map<String, String> localeResources = localeToResources[localeName];
|
||||
output.writeln('''
|
||||
|
||||
// ignore: camel_case_types
|
||||
class _Bundle_$localeName extends TranslationBundle {
|
||||
const _Bundle_$localeName() : super(const _Bundle_$language());''');
|
||||
for (String key in localeResources.keys) {
|
||||
if (languageResources[key] == localeResources[key])
|
||||
continue;
|
||||
final String value = generateString(localeResources[key]);
|
||||
output.writeln('''
|
||||
@override String get $key => $value;''');
|
||||
}
|
||||
output.writeln('''
|
||||
}''');
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the translationBundleForLocale function. Given a Locale
|
||||
// it returns the corresponding const TranslationBundle.
|
||||
output.writeln('''
|
||||
|
||||
TranslationBundle translationBundleForLocale(Locale locale) {
|
||||
switch(locale.languageCode) {''');
|
||||
for(String language in languageToLocales.keys) {
|
||||
if (languageToLocales[language].length == 1) {
|
||||
output.writeln('''
|
||||
case \'$language\':
|
||||
return const _Bundle_${languageToLocales[language][0]}();''');
|
||||
} else {
|
||||
output.writeln('''
|
||||
case \'$language\': {
|
||||
switch(locale.toString()) {''');
|
||||
for(String localeName in languageToLocales[language]) {
|
||||
if (localeName == language)
|
||||
continue;
|
||||
output.writeln('''
|
||||
case \'$localeName\':
|
||||
return const _Bundle_$localeName();''');
|
||||
}
|
||||
output.writeln('''
|
||||
}
|
||||
return const _Bundle_$language();
|
||||
}''');
|
||||
}
|
||||
}
|
||||
output.writeln('''
|
||||
}
|
||||
return const TranslationBundle(null);
|
||||
}''');
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@ -161,12 +272,12 @@ void main(List<String> rawArgs) {
|
||||
final String regenerate = 'dart dev/tools/gen_localizations.dart --overwrite';
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
buffer.writeln(outputHeader.replaceFirst('@(regenerate)', regenerate));
|
||||
buffer.writeln(generateLocalizationsMap());
|
||||
buffer.write(generateTranslationBundles());
|
||||
|
||||
if (options.writeToFile) {
|
||||
final File localizationsFile = new File(pathlib.join(directory.path, 'localizations.dart'));
|
||||
localizationsFile.writeAsStringSync('$buffer');
|
||||
localizationsFile.writeAsStringSync(buffer.toString());
|
||||
} else {
|
||||
print(buffer);
|
||||
stdout.write(buffer.toString());
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ import 'package:intl/date_symbols.dart' as intl;
|
||||
import 'package:intl/date_symbol_data_custom.dart' as date_symbol_data_custom;
|
||||
import 'l10n/date_localizations.dart' as date_localizations;
|
||||
|
||||
import 'l10n/localizations.dart';
|
||||
import 'l10n/localizations.dart' show TranslationBundle, translationBundleForLocale;
|
||||
import 'widgets_localizations.dart';
|
||||
|
||||
/// Localized strings for the material widgets.
|
||||
@ -68,13 +68,11 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
/// function, rather than constructing this class directly.
|
||||
GlobalMaterialLocalizations(this.locale)
|
||||
: assert(locale != null),
|
||||
this._localeName = _computeLocaleName(locale) {
|
||||
_localeName = _computeLocaleName(locale) {
|
||||
_loadDateIntlDataIfNotLoaded();
|
||||
|
||||
if (localizations.containsKey(locale.languageCode))
|
||||
_nameToValue.addAll(localizations[locale.languageCode]);
|
||||
if (localizations.containsKey(_localeName))
|
||||
_nameToValue.addAll(localizations[_localeName]);
|
||||
_translationBundle = translationBundleForLocale(locale);
|
||||
assert(_translationBundle != null);
|
||||
|
||||
const String kMediumDatePattern = 'E, MMM\u00a0d';
|
||||
if (intl.DateFormat.localeExists(_localeName)) {
|
||||
@ -113,7 +111,7 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
|
||||
final String _localeName;
|
||||
|
||||
final Map<String, String> _nameToValue = <String, String>{};
|
||||
TranslationBundle _translationBundle;
|
||||
|
||||
intl.NumberFormat _decimalFormat;
|
||||
|
||||
@ -132,24 +130,6 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
return intl.Intl.canonicalizedLocale(localeName);
|
||||
}
|
||||
|
||||
// TODO(hmuller): the rules for mapping from an integer value to
|
||||
// "one" or "two" etc. are locale specific and an additional "few" category
|
||||
// is needed. See http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
String _nameToPluralValue(int count, String key) {
|
||||
String text;
|
||||
if (count == 0)
|
||||
text = _nameToValue['${key}Zero'];
|
||||
else if (count == 1)
|
||||
text = _nameToValue['${key}One'];
|
||||
else if (count == 2)
|
||||
text = _nameToValue['${key}Two'];
|
||||
else if (count > 2)
|
||||
text = _nameToValue['${key}Many'];
|
||||
text ??= _nameToValue['${key}Other'];
|
||||
assert(text != null);
|
||||
return text;
|
||||
}
|
||||
|
||||
@override
|
||||
String formatHour(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat: false }) {
|
||||
switch (hourFormat(of: timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat))) {
|
||||
@ -242,45 +222,45 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get openAppDrawerTooltip => _nameToValue['openAppDrawerTooltip'];
|
||||
String get openAppDrawerTooltip => _translationBundle.openAppDrawerTooltip;
|
||||
|
||||
@override
|
||||
String get backButtonTooltip => _nameToValue['backButtonTooltip'];
|
||||
String get backButtonTooltip => _translationBundle.backButtonTooltip;
|
||||
|
||||
@override
|
||||
String get closeButtonTooltip => _nameToValue['closeButtonTooltip'];
|
||||
String get closeButtonTooltip => _translationBundle.closeButtonTooltip;
|
||||
|
||||
@override
|
||||
String get deleteButtonTooltip => _nameToValue['deleteButtonTooltip'];
|
||||
String get deleteButtonTooltip => _translationBundle.deleteButtonTooltip;
|
||||
|
||||
@override
|
||||
String get nextMonthTooltip => _nameToValue['nextMonthTooltip'];
|
||||
String get nextMonthTooltip => _translationBundle.nextMonthTooltip;
|
||||
|
||||
@override
|
||||
String get previousMonthTooltip => _nameToValue['previousMonthTooltip'];
|
||||
String get previousMonthTooltip => _translationBundle.previousMonthTooltip;
|
||||
|
||||
@override
|
||||
String get nextPageTooltip => _nameToValue['nextPageTooltip'];
|
||||
String get nextPageTooltip => _translationBundle.nextPageTooltip;
|
||||
|
||||
@override
|
||||
String get previousPageTooltip => _nameToValue['previousPageTooltip'];
|
||||
String get previousPageTooltip => _translationBundle.previousPageTooltip;
|
||||
|
||||
@override
|
||||
String get showMenuTooltip => _nameToValue['showMenuTooltip'];
|
||||
String get showMenuTooltip => _translationBundle.showMenuTooltip;
|
||||
|
||||
@override
|
||||
String aboutListTileTitle(String applicationName) {
|
||||
final String text = _nameToValue['aboutListTileTitle'];
|
||||
final String text = _translationBundle.aboutListTileTitle;
|
||||
return text.replaceFirst(r'$applicationName', applicationName);
|
||||
}
|
||||
|
||||
@override
|
||||
String get licensesPageTitle => _nameToValue['licensesPageTitle'];
|
||||
String get licensesPageTitle => _translationBundle.licensesPageTitle;
|
||||
|
||||
@override
|
||||
String pageRowsInfoTitle(int firstRow, int lastRow, int rowCount, bool rowCountIsApproximate) {
|
||||
String text = rowCountIsApproximate ? _nameToValue['pageRowsInfoTitleApproximate'] : null;
|
||||
text ??= _nameToValue['pageRowsInfoTitle'];
|
||||
String text = rowCountIsApproximate ? _translationBundle.pageRowsInfoTitleApproximate : null;
|
||||
text ??= _translationBundle.pageRowsInfoTitle;
|
||||
assert(text != null, 'A $locale localization was not found for pageRowsInfoTitle or pageRowsInfoTitleApproximate');
|
||||
// TODO(hansmuller): this could be more efficient.
|
||||
return text
|
||||
@ -290,55 +270,69 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get rowsPerPageTitle => _nameToValue['rowsPerPageTitle'];
|
||||
String get rowsPerPageTitle => _translationBundle.rowsPerPageTitle;
|
||||
|
||||
@override
|
||||
String selectedRowCountTitle(int selectedRowCount) {
|
||||
return _nameToPluralValue(selectedRowCount, 'selectedRowCountTitle') // asserts on no match
|
||||
.replaceFirst(r'$selectedRowCount', formatDecimal(selectedRowCount));
|
||||
// TODO(hmuller): the rules for mapping from an integer value to
|
||||
// "one" or "two" etc. are locale specific and an additional "few" category
|
||||
// is needed. See http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
String text;
|
||||
if (selectedRowCount == 0)
|
||||
text = _translationBundle.selectedRowCountTitleZero;
|
||||
else if (selectedRowCount == 1)
|
||||
text = _translationBundle.selectedRowCountTitleOne;
|
||||
else if (selectedRowCount == 2)
|
||||
text = _translationBundle.selectedRowCountTitleTwo;
|
||||
else if (selectedRowCount > 2)
|
||||
text = _translationBundle.selectedRowCountTitleMany;
|
||||
text ??= _translationBundle.selectedRowCountTitleOther;
|
||||
assert(text != null);
|
||||
|
||||
return text.replaceFirst(r'$selectedRowCount', formatDecimal(selectedRowCount));
|
||||
}
|
||||
|
||||
@override
|
||||
String get cancelButtonLabel => _nameToValue['cancelButtonLabel'];
|
||||
String get cancelButtonLabel => _translationBundle.cancelButtonLabel;
|
||||
|
||||
@override
|
||||
String get closeButtonLabel => _nameToValue['closeButtonLabel'];
|
||||
String get closeButtonLabel => _translationBundle.closeButtonLabel;
|
||||
|
||||
@override
|
||||
String get continueButtonLabel => _nameToValue['continueButtonLabel'];
|
||||
String get continueButtonLabel => _translationBundle.continueButtonLabel;
|
||||
|
||||
@override
|
||||
String get copyButtonLabel => _nameToValue['copyButtonLabel'];
|
||||
String get copyButtonLabel => _translationBundle.copyButtonLabel;
|
||||
|
||||
@override
|
||||
String get cutButtonLabel => _nameToValue['cutButtonLabel'];
|
||||
String get cutButtonLabel => _translationBundle.cutButtonLabel;
|
||||
|
||||
@override
|
||||
String get okButtonLabel => _nameToValue['okButtonLabel'];
|
||||
String get okButtonLabel => _translationBundle.okButtonLabel;
|
||||
|
||||
@override
|
||||
String get pasteButtonLabel => _nameToValue['pasteButtonLabel'];
|
||||
String get pasteButtonLabel => _translationBundle.pasteButtonLabel;
|
||||
|
||||
@override
|
||||
String get selectAllButtonLabel => _nameToValue['selectAllButtonLabel'];
|
||||
String get selectAllButtonLabel => _translationBundle.selectAllButtonLabel;
|
||||
|
||||
@override
|
||||
String get viewLicensesButtonLabel => _nameToValue['viewLicensesButtonLabel'];
|
||||
String get viewLicensesButtonLabel => _translationBundle.viewLicensesButtonLabel;
|
||||
|
||||
@override
|
||||
String get anteMeridiemAbbreviation => _nameToValue['anteMeridiemAbbreviation'];
|
||||
String get anteMeridiemAbbreviation => _translationBundle.anteMeridiemAbbreviation;
|
||||
|
||||
@override
|
||||
String get postMeridiemAbbreviation => _nameToValue['postMeridiemAbbreviation'];
|
||||
String get postMeridiemAbbreviation => _translationBundle.postMeridiemAbbreviation;
|
||||
|
||||
@override
|
||||
String get timePickerHourModeAnnouncement => _nameToValue['timePickerHourModeAnnouncement'];
|
||||
String get timePickerHourModeAnnouncement => _translationBundle.timePickerHourModeAnnouncement;
|
||||
|
||||
@override
|
||||
String get timePickerMinuteModeAnnouncement => _nameToValue['timePickerMinuteModeAnnouncement'];
|
||||
String get timePickerMinuteModeAnnouncement => _translationBundle.timePickerMinuteModeAnnouncement;
|
||||
|
||||
@override
|
||||
String get modalBarrierDismissLabel => _nameToValue['modalBarrierDismissLabel'];
|
||||
String get modalBarrierDismissLabel => _translationBundle.modalBarrierDismissLabel;
|
||||
|
||||
/// The [TimeOfDayFormat] corresponding to one of the following supported
|
||||
/// patterns:
|
||||
@ -358,7 +352,7 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
/// short time pattern used in locale en_US
|
||||
@override
|
||||
TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat: false }) {
|
||||
final String icuShortTimePattern = _nameToValue['timeOfDayFormat'];
|
||||
final String icuShortTimePattern = _translationBundle.timeOfDayFormat;
|
||||
|
||||
assert(() {
|
||||
if (!_icuTimeOfDayToEnum.containsKey(icuShortTimePattern)) {
|
||||
@ -382,7 +376,7 @@ class GlobalMaterialLocalizations implements MaterialLocalizations {
|
||||
|
||||
/// Looks up text geometry defined in [MaterialTextGeometry].
|
||||
@override
|
||||
TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(_nameToValue['scriptCategory']);
|
||||
TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(_translationBundle.scriptCategory);
|
||||
|
||||
/// Creates an object that provides localized resource values for the
|
||||
/// for the widgets of the material library.
|
||||
|
Loading…
x
Reference in New Issue
Block a user