Unverified Commit 31418570 authored by Hans Muller's avatar Hans Muller Committed by GitHub

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
parent 30720bd1
......@@ -91,28 +91,139 @@ String generateString(String s) {
return output.toString();
}
String generateLocalizationsMap() {
String generateTranslationBundles() {
final StringBuffer output = new StringBuffer();
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);
}
// 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('''
/// 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>> {''');
// 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.
for (String locale in localeToResources.keys.toList()..sort()) {
output.writeln(" '$locale': const <String, String>{");
import \'dart:ui\' show Locale;
final Map<String, String> resources = localeToResources[locale];
for (String name in resources.keys) {
final String value = generateString(resources[name]);
output.writeln(" '$name': $value,");
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(' },');
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);
}''');
output.writeln('};');
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());
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment