Unverified Commit aab92447 authored by Gary Qian's avatar Gary Qian Committed by GitHub

scriptCode support in gen_localizations and strip scriptCodes for date l10n. (#23853)

parent b21fe0aa
......@@ -67,12 +67,20 @@ import '../material_localizations.dart';
''';
/// Maps locales to resource key/value pairs.
final Map<String, Map<String, String>> localeToResources = <String, Map<String, String>>{};
final Map<LocaleInfo, Map<String, String>> localeToResources = <LocaleInfo, Map<String, String>>{};
/// Maps locales to resource key/attributes pairs.
///
/// See also: <https://github.com/googlei18n/app-resource-bundle/wiki/ApplicationResourceBundleSpecification#resource-attributes>
final Map<String, Map<String, dynamic>> localeToResourceAttributes = <String, Map<String, dynamic>>{};
final Map<LocaleInfo, Map<String, dynamic>> localeToResourceAttributes = <LocaleInfo, Map<String, dynamic>>{};
/// Set that holds the locales that were assumed from the existing locales.
///
/// For example, when the data lacks data for zh_Hant, we will use the data of
/// the first Hant Chinese locale as a default by repeating the data. If an
/// explicit match is later found, we can reference this set to see if we should
/// overwrite the existing assumed data.
final Set<LocaleInfo> assumedLocales = Set<LocaleInfo>();
/// Return `s` as a Dart-parseable raw string in single or double quotes.
///
......@@ -112,13 +120,23 @@ String generateTranslationBundles() {
final StringBuffer output = StringBuffer();
final StringBuffer supportedLocales = StringBuffer();
final Map<String, List<String>> languageToLocales = <String, List<String>>{};
final Map<String, List<LocaleInfo>> languageToLocales = <String, List<LocaleInfo>>{};
final Map<String, Set<String>> languageToScriptCodes = <String, Set<String>>{};
// Used to calculate if there are any corresponding countries for a given language and script.
final Map<LocaleInfo, Set<String>> languageAndScriptToCountryCodes = <LocaleInfo, Set<String>>{};
final Set<String> allResourceIdentifiers = 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);
for (LocaleInfo locale in localeToResources.keys.toList()..sort()) {
if (locale.scriptCode != null) {
languageToScriptCodes[locale.languageCode] ??= Set<String>();
languageToScriptCodes[locale.languageCode].add(locale.scriptCode);
}
if (locale.countryCode != null && locale.scriptCode != null) {
final LocaleInfo key = LocaleInfo.fromString(locale.languageCode + '_' + locale.scriptCode);
languageAndScriptToCountryCodes[key] ??= Set<String>();
languageAndScriptToCountryCodes[key].add(locale.countryCode);
}
languageToLocales[locale.languageCode] ??= <LocaleInfo>[];
languageToLocales[locale.languageCode].add(locale);
allResourceIdentifiers.addAll(localeToResources[locale].keys);
}
......@@ -134,56 +152,107 @@ String generateTranslationBundles() {
// `MaterialLocalizationEn`). These implement everything that is needed by
// GlobalMaterialLocalizations.
// We also generate one subclass for each locale with a script code (e.g.
// `MaterialLocalizationZhHant`). Their superclasses are the aforementioned
// language classes for the same locale but without a script code (e.g.
// `MaterialLocalizationZh`).
// We also generate one subclass for each locale with a country code (e.g.
// `MaterialLocalizationEnGb`). Their superclasses are the aforementioned
// language classes for the same locale but without a country code (e.g.
// `MaterialLocalizationEn`). These classes only override getters that return
// a different value than their superclass.
// `MaterialLocalizationEn`).
// If scriptCodes for a language are defined, we expect a scriptCode to be
// defined for locales that contain a countryCode. The superclass becomes
// the script sublcass (e.g. `MaterialLocalizationZhHant`) and the generated
// subclass will also contain the script code (e.g. `MaterialLocalizationZhHantTW`).
// When scriptCodes are not defined for languages that use scriptCodes to distinguish
// between significantly differing scripts, we assume the scriptCodes in the
// [LocaleInfo.fromString] factory and add it to the [LocaleInfo]. We then generate
// the script classes based on the first locale that we assume to use the script.
final List<String> allKeys = allResourceIdentifiers.toList()..sort();
final List<String> languageCodes = languageToLocales.keys.toList()..sort();
final LocaleInfo canonicalLocale = LocaleInfo.fromString('en');
for (String languageName in languageCodes) {
final String camelCaseLanguage = camelCase(languageName);
final Map<String, String> languageResources = localeToResources[languageName];
final String languageClassName = 'MaterialLocalization$camelCaseLanguage';
final String constructor = generateConstructor(languageClassName, languageName);
output.writeln('');
output.writeln('/// The translations for ${describeLocale(languageName)} (`$languageName`).');
output.writeln('class $languageClassName extends GlobalMaterialLocalizations {');
output.writeln(constructor);
final LocaleInfo languageLocale = LocaleInfo.fromString(languageName);
writeClassHeader(output, languageLocale, 'GlobalMaterialLocalizations');
final Map<String, String> languageResources = localeToResources[languageLocale];
for (String key in allKeys) {
final Map<String, dynamic> attributes = localeToResourceAttributes['en'][key];
final Map<String, dynamic> attributes = localeToResourceAttributes[canonicalLocale][key];
output.writeln(generateGetter(key, languageResources[key], attributes));
}
output.writeln('}');
int countryCodeCount = 0;
final List<String> localeCodes = languageToLocales[languageName]..sort();
for (String localeName in localeCodes) {
if (localeName == languageName)
continue;
countryCodeCount += 1;
final String camelCaseLocaleName = camelCase(localeName);
final Map<String, String> localeResources = localeToResources[localeName];
final String localeClassName = 'MaterialLocalization$camelCaseLocaleName';
final String constructor = generateConstructor(localeClassName, localeName);
output.writeln('');
output.writeln('/// The translations for ${describeLocale(localeName)} (`$localeName`).');
output.writeln('class $localeClassName extends $languageClassName {');
output.writeln(constructor);
for (String key in localeResources.keys) {
if (languageResources[key] == localeResources[key])
int scriptCodeCount = 0;
if (languageToScriptCodes.containsKey(languageName)) {
scriptCodeCount = languageToScriptCodes[languageName].length;
// Language has scriptCodes, so we need to properly fallback countries to corresponding
// script default values before language default values.
for (String scriptCode in languageToScriptCodes[languageName]) {
final LocaleInfo scriptBaseLocale = LocaleInfo.fromString(languageName + '_' + scriptCode);
writeClassHeader(output, scriptBaseLocale, 'MaterialLocalization${camelCase(languageLocale)}');
final Map<String, String> scriptResources = localeToResources[scriptBaseLocale];
for (String key in scriptResources.keys) {
if (languageResources[key] == scriptResources[key])
continue;
final Map<String, dynamic> attributes = localeToResourceAttributes[canonicalLocale][key];
output.writeln(generateGetter(key, scriptResources[key], attributes));
}
output.writeln('}');
final List<LocaleInfo> localeCodes = languageToLocales[languageName]..sort();
for (LocaleInfo locale in localeCodes) {
if (locale.originalString == languageName)
continue;
if (locale.originalString == languageName + '_' + scriptCode)
continue;
if (locale.scriptCode != scriptCode)
continue;
countryCodeCount += 1;
writeClassHeader(output, locale, 'MaterialLocalization${camelCase(scriptBaseLocale)}');
final Map<String, String> localeResources = localeToResources[locale];
for (String key in localeResources.keys) {
// When script fallback contains the key, we compare to it instead of language fallback.
if (scriptResources.containsKey(key) ? scriptResources[key] == localeResources[key] : languageResources[key] == localeResources[key])
continue;
final Map<String, dynamic> attributes = localeToResourceAttributes[canonicalLocale][key];
output.writeln(generateGetter(key, localeResources[key], attributes));
}
output.writeln('}');
}
}
} else {
// No scriptCode. Here, we do not compare against script default (because it
// doesn't exist).
final List<LocaleInfo> localeCodes = languageToLocales[languageName]..sort();
for (LocaleInfo locale in localeCodes) {
if (locale.originalString == languageName)
continue;
final Map<String, dynamic> attributes = localeToResourceAttributes['en'][key];
output.writeln(generateGetter(key, localeResources[key], attributes));
countryCodeCount += 1;
final Map<String, String> localeResources = localeToResources[locale];
writeClassHeader(output, locale, 'MaterialLocalization${camelCase(languageLocale)}');
for (String key in localeResources.keys) {
if (languageResources[key] == localeResources[key])
continue;
final Map<String, dynamic> attributes = localeToResourceAttributes[canonicalLocale][key];
output.writeln(generateGetter(key, localeResources[key], attributes));
}
output.writeln('}');
}
output.writeln('}');
}
final String scriptCodeMessage = scriptCodeCount == 0 ? '' : ' and $scriptCodeCount script' + (scriptCodeCount == 1 ? '' : 's');
if (countryCodeCount == 0) {
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)}');
if (scriptCodeCount == 0)
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)}');
else
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)} (plus $scriptCodeCount script' + (scriptCodeCount == 1 ? '' : 's') + ')');
} else if (countryCodeCount == 1) {
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)} (plus one variant)');
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)} (plus one country variation$scriptCodeMessage)');
} else {
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)} (plus $countryCodeCount variants)');
supportedLocales.writeln('/// * `$languageName` - ${describeLocale(languageName)} (plus $countryCodeCount country variations$scriptCodeMessage)');
}
}
......@@ -232,26 +301,104 @@ GlobalMaterialLocalizations getTranslation(
switch (locale.languageCode) {''');
const String arguments = 'fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat';
for (String language in languageToLocales.keys) {
// Only one instance of the language.
if (languageToLocales[language].length == 1) {
output.writeln('''
case '$language':
return MaterialLocalization${camelCase(languageToLocales[language][0])}($arguments);''');
} else {
} else if (!languageToScriptCodes.containsKey(language)) { // Does not distinguish between scripts. Switch on countryCode directly.
output.writeln('''
case '$language': {
switch (locale.countryCode) {''');
for (String localeName in languageToLocales[language]) {
if (localeName == language)
for (LocaleInfo locale in languageToLocales[language]) {
if (locale.originalString == language)
continue;
assert(localeName.contains('_'));
final String countryCode = localeName.substring(localeName.indexOf('_') + 1);
assert(locale.length > 1);
final String countryCode = locale.countryCode;
output.writeln('''
case '$countryCode':
return MaterialLocalization${camelCase(localeName)}($arguments);''');
return MaterialLocalization${camelCase(locale)}($arguments);''');
}
output.writeln('''
}
return MaterialLocalization${camelCase(LocaleInfo.fromString(language))}($arguments);
}''');
} else { // Language has scriptCode, add additional switch logic.
bool hasCountryCode = false;
output.writeln('''
case '$language': {
switch (locale.scriptCode) {''');
for (String scriptCode in languageToScriptCodes[language]) {
final LocaleInfo scriptLocale = LocaleInfo.fromString(language + '_' + scriptCode);
output.writeln('''
case '$scriptCode': {''');
if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) {
output.writeln('''
switch (locale.countryCode) {''');
for (LocaleInfo locale in languageToLocales[language]) {
if (locale.countryCode == null)
continue;
else
hasCountryCode = true;
if (locale.originalString == language)
continue;
if (locale.scriptCode != scriptCode && locale.scriptCode != null)
continue;
final String countryCode = locale.countryCode;
output.writeln('''
case '$countryCode':
return MaterialLocalization${camelCase(locale)}($arguments);''');
}
}
// Return a fallback locale that matches scriptCode, but not countryCode.
//
// Explicitly defined scriptCode fallback:
if (languageToLocales[language].contains(scriptLocale)) {
if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) {
output.writeln('''
}''');
}
output.writeln('''
return MaterialLocalization${camelCase(scriptLocale)}($arguments);
}''');
} else {
// Not Explicitly defined, fallback to first locale with the same language and
// script:
for (LocaleInfo locale in languageToLocales[language]) {
if (locale.scriptCode != scriptCode)
continue;
if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) {
output.writeln('''
}''');
}
output.writeln('''
return MaterialLocalization${camelCase(scriptLocale)}($arguments);
}''');
break;
}
}
}
output.writeln('''
}''');
if (hasCountryCode) {
output.writeln('''
switch (locale.countryCode) {''');
for (LocaleInfo locale in languageToLocales[language]) {
if (locale.originalString == language)
continue;
assert(locale.length > 1);
if (locale.countryCode == null)
continue;
final String countryCode = locale.countryCode;
output.writeln('''
case '$countryCode':
return MaterialLocalization${camelCase(locale)}($arguments);''');
}
output.writeln('''
}''');
}
return MaterialLocalization${camelCase(language)}($arguments);
output.writeln('''
return MaterialLocalization${camelCase(LocaleInfo.fromString(language))}($arguments);
}''');
}
}
......@@ -264,6 +411,17 @@ GlobalMaterialLocalizations getTranslation(
return output.toString();
}
/// Writes the header of each class which corresponds to a locale.
void writeClassHeader(StringBuffer output, LocaleInfo locale, String superClass) {
final String camelCaseName = camelCase(locale);
final String className = 'MaterialLocalization$camelCaseName';
final String constructor = generateConstructor(className, locale);
output.writeln('');
output.writeln('/// The translations for ${describeLocale(locale.originalString)} (`${locale.originalString}`).');
output.writeln('class $className extends $superClass {');
output.writeln(constructor);
}
/// Returns the appropriate type for getters with the given attributes.
///
/// Typically "String", but some (e.g. "timeOfDayFormat") return enums.
......@@ -367,7 +525,8 @@ String generateGetter(String key, String value, Map<String, dynamic> attributes)
/// Returns the source of the constructor for a GlobalMaterialLocalizations
/// subclass.
String generateConstructor(String className, String localeName) {
String generateConstructor(String className, LocaleInfo locale) {
final String localeName = locale.originalString;
return '''
/// Create an instance of the translation bundle for ${describeLocale(localeName)}.
///
......@@ -393,19 +552,45 @@ String generateConstructor(String className, String localeName) {
/// Parse the data for a locale from a file, and store it in the [attributes]
/// and [resources] keys.
void processBundle(File file, { @required String locale }) {
assert(locale != null);
localeToResources[locale] ??= <String, String>{};
localeToResourceAttributes[locale] ??= <String, dynamic>{};
final Map<String, String> resources = localeToResources[locale];
final Map<String, dynamic> attributes = localeToResourceAttributes[locale];
final Map<String, dynamic> bundle = json.decode(file.readAsStringSync());
for (String key in bundle.keys) {
// The ARB file resource "attributes" for foo are called @foo.
if (key.startsWith('@'))
attributes[key.substring(1)] = bundle[key];
else
resources[key] = bundle[key];
void processBundle(File file, { @required String localeString }) {
assert(localeString != null);
// Helper method to fill the maps with the correct data from file.
void populateResources(LocaleInfo locale) {
final Map<String, String> resources = localeToResources[locale];
final Map<String, dynamic> attributes = localeToResourceAttributes[locale];
final Map<String, dynamic> bundle = json.decode(file.readAsStringSync());
for (String key in bundle.keys) {
// The ARB file resource "attributes" for foo are called @foo.
if (key.startsWith('@'))
attributes[key.substring(1)] = bundle[key];
else
resources[key] = bundle[key];
}
}
// Only pre-assume scriptCode if there is a country or script code to assume off of.
// When we assume scriptCode based on languageCode-only, we want this initial pass
// to use the un-assumed version as a base class.
LocaleInfo locale = LocaleInfo.fromString(localeString, assume: localeString.split('_').length > 1);
// Allow overwrite if the existing data is assumed.
if (assumedLocales.contains(locale)) {
localeToResources[locale] = <String, String>{};
localeToResourceAttributes[locale] = <String, dynamic>{};
assumedLocales.remove(locale);
} else {
localeToResources[locale] ??= <String, String>{};
localeToResourceAttributes[locale] ??= <String, dynamic>{};
}
populateResources(locale);
// Add an assumed locale to default to when there is no info on scriptOnly locales.
locale = LocaleInfo.fromString(localeString, assume: true);
if (locale.scriptCode != null) {
final LocaleInfo scriptLocale = LocaleInfo.fromString(locale.languageCode + '_' + locale.scriptCode);
if (!localeToResources.containsKey(scriptLocale)) {
assumedLocales.add(scriptLocale);
localeToResources[scriptLocale] ??= <String, String>{};
localeToResourceAttributes[scriptLocale] ??= <String, dynamic>{};
populateResources(scriptLocale);
}
}
}
......@@ -431,7 +616,7 @@ Future<void> main(List<String> rawArgs) async {
for (FileSystemEntity entity in directory.listSync()) {
final String entityPath = entity.path;
if (FileSystemEntity.isFileSync(entityPath) && filenameRE.hasMatch(entityPath)) {
processBundle(File(entityPath), locale: filenameRE.firstMatch(entityPath)[1]);
processBundle(File(entityPath), localeString: filenameRE.firstMatch(entityPath)[1]);
}
}
......
......@@ -9,6 +9,123 @@ import 'dart:io';
import 'package:args/args.dart' as argslib;
import 'package:meta/meta.dart';
/// Simple data class to hold parsed locale. Does not promise validity of any data.
class LocaleInfo implements Comparable<LocaleInfo> {
LocaleInfo({
this.languageCode,
this.scriptCode,
this.countryCode,
this.length,
this.originalString,
});
/// Simple parser. Expects the locale string to be in the form of 'language_script_COUNTRY'
/// where the langauge is 2 characters, script is 4 characters with the first uppercase,
/// and country is 2-3 characters and all uppercase.
///
/// 'language_COUNTRY' or 'language_script' are also valid. Missing fields will be null.
factory LocaleInfo.fromString(String locale, {bool assume = false}) {
final List<String> codes = locale.split('_'); // [language, script, country]
assert(codes.isNotEmpty && codes.length < 4);
final String languageCode = codes[0];
String scriptCode;
String countryCode;
int length = codes.length;
String originalString = locale;
if (codes.length == 2) {
scriptCode = codes[1].length >= 4 ? codes[1] : null;
countryCode = codes[1].length < 4 ? codes[1] : null;
} else if (codes.length == 3) {
scriptCode = codes[1].length > codes[2].length ? codes[1] : codes[2];
countryCode = codes[1].length < codes[2].length ? codes[1] : codes[2];
}
assert(codes[0] != null && codes[0].isNotEmpty);
assert(countryCode == null || countryCode.isNotEmpty);
assert(scriptCode == null || scriptCode.isNotEmpty);
/// Adds scriptCodes to locales where we are able to assume it to provide
/// finer granularity when resolving locales.
///
/// The basis of the assumptions here are based off of known usage of scripts
/// across various countries. For example, we know Taiwan uses traditional (Hant)
/// script, so it is safe to apply (Hant) to Taiwanese languages.
if (assume && scriptCode == null) {
switch (languageCode) {
case 'zh': {
if (countryCode == null) {
scriptCode = 'Hans';
}
switch (countryCode) {
case 'CN':
case 'SG':
scriptCode = 'Hans';
break;
case 'TW':
case 'HK':
case 'MO':
scriptCode = 'Hant';
break;
}
break;
}
case 'sr': {
if (countryCode == null) {
scriptCode = 'Cyrl';
}
break;
}
}
// Increment length if we were able to assume a scriptCode.
if (scriptCode != null) {
length += 1;
}
// Update the base string to reflect assumed scriptCodes.
originalString = languageCode;
if (scriptCode != null)
originalString += '_' + scriptCode;
if (countryCode != null)
originalString += '_' + countryCode;
}
return LocaleInfo(
languageCode: languageCode,
scriptCode: scriptCode,
countryCode: countryCode,
length: length,
originalString: originalString,
);
}
final String languageCode;
final String scriptCode;
final String countryCode;
final int length; // The number of fields. Ranges from 1-3.
final String originalString; // Original un-parsed locale string.
@override
bool operator ==(Object other) {
if (!(other is LocaleInfo))
return false;
final LocaleInfo otherLocale = other;
return originalString == otherLocale.originalString;
}
@override
int get hashCode {
return originalString.hashCode;
}
@override
String toString() {
return originalString;
}
@override
int compareTo(LocaleInfo other) {
return originalString.compareTo(other.originalString);
}
}
void exitWithError(String errorMessage) {
assert(errorMessage != null);
stderr.writeln('fatal: $errorMessage');
......@@ -26,8 +143,8 @@ void checkCwdIsRepoRoot(String commandName) {
}
}
String camelCase(String locale) {
return locale
String camelCase(LocaleInfo locale) {
return locale.originalString
.split('_')
.map<String>((String part) => part.substring(0, 1).toUpperCase() + part.substring(1).toLowerCase())
.join('');
......@@ -129,14 +246,21 @@ String describeLocale(String tag) {
assert(subtags.isNotEmpty);
assert(_languages.containsKey(subtags[0]));
final String language = _languages[subtags[0]];
if (subtags.length >= 2) {
final String region = _regions[subtags[1]];
final String script = _scripts[subtags[1]];
String output = '$language';
String region;
String script;
if (subtags.length == 2) {
region = _regions[subtags[1]];
script = _scripts[subtags[1]];
assert(region != null || script != null);
if (region != null)
return '$language, as used in $region';
if (script != null)
return '$language, using the $script script';
} else if (subtags.length >= 3) {
region = _regions[subtags[2]];
script = _scripts[subtags[1]];
assert(region != null && script != null);
}
return '$language';
if (region != null)
output += ', as used in $region';
if (script != null)
output += ', using the $script script';
return output;
}
\ No newline at end of file
......@@ -5,6 +5,8 @@
import 'dart:convert' show json;
import 'dart:io';
import 'localizations_utils.dart';
// The first suffix in kPluralSuffixes must be "Other". "Other" is special
// because it's the only one that is required.
const List<String> kPluralSuffixes = <String>['Other', 'Zero', 'One', 'Two', 'Few', 'Many'];
......@@ -96,14 +98,14 @@ void validateEnglishLocalizations(File file) {
///
/// If validation fails, throws an exception.
void validateLocalizations(
Map<String, Map<String, String>> localeToResources,
Map<String, Map<String, dynamic>> localeToAttributes,
Map<LocaleInfo, Map<String, String>> localeToResources,
Map<LocaleInfo, Map<String, dynamic>> localeToAttributes,
) {
final Map<String, String> canonicalLocalizations = localeToResources['en'];
final Map<String, String> canonicalLocalizations = localeToResources[LocaleInfo.fromString('en')];
final Set<String> canonicalKeys = Set<String>.from(canonicalLocalizations.keys);
final StringBuffer errorMessages = StringBuffer();
bool explainMissingKeys = false;
for (final String locale in localeToResources.keys) {
for (final LocaleInfo locale in localeToResources.keys) {
final Map<String, String> resources = localeToResources[locale];
// Whether `key` corresponds to one of the plural variations of a key with
......@@ -128,14 +130,12 @@ void validateLocalizations(
final Set<String> invalidKeys = keys.difference(canonicalKeys);
if (invalidKeys.isNotEmpty)
errorMessages.writeln('Locale "$locale" contains invalid resource keys: ${invalidKeys.join(', ')}');
// For language-level locales only, check that they have a complete list of
// keys, or opted out of using certain ones.
if (locale.length == 2) {
if (locale.length == 1) {
final Map<String, dynamic> attributes = localeToAttributes[locale];
final List<String> missingKeys = <String>[];
for (final String missingKey in canonicalKeys.difference(keys)) {
for (final String missingKey in canonicalKeys.difference(keys)) {
final dynamic attribute = attributes[missingKey];
final bool intentionallyOmitted = attribute is Map && attribute.containsKey('notUsed');
if (!intentionallyOmitted && !isPluralVariation(missingKey))
......
......@@ -6779,10 +6779,10 @@ const Map<String, dynamic> dateSymbols = <String, dynamic>{
r'''S'''
],
'SHORTQUARTERS': <dynamic>[
r'''1. cet.''',
r'''2. cet.''',
r'''3. cet.''',
r'''4. cet.'''
r'''1. cet.''',
r'''2. cet.''',
r'''3. cet.''',
r'''4. cet.'''
],
'QUARTERS': <dynamic>[
r'''1. ceturksnis''',
......@@ -13904,4 +13904,4 @@ const Map<String, Map<String, String>> datePatterns =
'zzzz': r'''zzzz''',
'ZZZZ': r'''ZZZZ''',
},
};
};
\ No newline at end of file
......@@ -9927,6 +9927,30 @@ class MaterialLocalizationSr extends GlobalMaterialLocalizations {
String get viewLicensesButtonLabel => r'ПРИКАЖИ ЛИЦЕНЦЕ';
}
/// The translations for Serbian, using the Cyrillic script (`sr_Cyrl`).
class MaterialLocalizationSrCyrl extends MaterialLocalizationSr {
/// Create an instance of the translation bundle for Serbian, using the Cyrillic script.
///
/// For details on the meaning of the arguments, see [GlobalMaterialLocalizations].
const MaterialLocalizationSrCyrl({
String localeName = 'sr_Cyrl',
@required intl.DateFormat fullYearFormat,
@required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat,
@required intl.DateFormat yearMonthFormat,
@required intl.NumberFormat decimalFormat,
@required intl.NumberFormat twoDigitZeroPaddedFormat,
}) : super(
localeName: localeName,
fullYearFormat: fullYearFormat,
mediumDateFormat: mediumDateFormat,
longDateFormat: longDateFormat,
yearMonthFormat: yearMonthFormat,
decimalFormat: decimalFormat,
twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat,
);
}
/// The translations for Serbian, using the Latin script (`sr_Latn`).
class MaterialLocalizationSrLatn extends MaterialLocalizationSr {
/// Create an instance of the translation bundle for Serbian, using the Latin script.
......@@ -11712,13 +11736,37 @@ class MaterialLocalizationZh extends GlobalMaterialLocalizations {
String get viewLicensesButtonLabel => r'查看许可';
}
/// The translations for Chinese, as used in Hong Kong (`zh_HK`).
class MaterialLocalizationZhHk extends MaterialLocalizationZh {
/// Create an instance of the translation bundle for Chinese, as used in Hong Kong.
/// The translations for Chinese, using the Han script (`zh_Hans`).
class MaterialLocalizationZhHans extends MaterialLocalizationZh {
/// Create an instance of the translation bundle for Chinese, using the Han script.
///
/// For details on the meaning of the arguments, see [GlobalMaterialLocalizations].
const MaterialLocalizationZhHans({
String localeName = 'zh_Hans',
@required intl.DateFormat fullYearFormat,
@required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat,
@required intl.DateFormat yearMonthFormat,
@required intl.NumberFormat decimalFormat,
@required intl.NumberFormat twoDigitZeroPaddedFormat,
}) : super(
localeName: localeName,
fullYearFormat: fullYearFormat,
mediumDateFormat: mediumDateFormat,
longDateFormat: longDateFormat,
yearMonthFormat: yearMonthFormat,
decimalFormat: decimalFormat,
twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat,
);
}
/// The translations for Chinese, using the Han script (`zh_Hant`).
class MaterialLocalizationZhHant extends MaterialLocalizationZh {
/// Create an instance of the translation bundle for Chinese, using the Han script.
///
/// For details on the meaning of the arguments, see [GlobalMaterialLocalizations].
const MaterialLocalizationZhHk({
String localeName = 'zh_HK',
const MaterialLocalizationZhHant({
String localeName = 'zh_Hant',
@required intl.DateFormat fullYearFormat,
@required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat,
......@@ -11871,13 +11919,13 @@ class MaterialLocalizationZhHk extends MaterialLocalizationZh {
String get remainingTextFieldCharacterCountOther => r'還可輸入 $remainingCount 個字元';
}
/// The translations for Chinese, as used in Taiwan (`zh_TW`).
class MaterialLocalizationZhTw extends MaterialLocalizationZh {
/// Create an instance of the translation bundle for Chinese, as used in Taiwan.
/// The translations for Chinese, as used in Hong Kong, using the Han script (`zh_Hant_HK`).
class MaterialLocalizationZhHantHk extends MaterialLocalizationZhHant {
/// Create an instance of the translation bundle for Chinese, as used in Hong Kong, using the Han script.
///
/// For details on the meaning of the arguments, see [GlobalMaterialLocalizations].
const MaterialLocalizationZhTw({
String localeName = 'zh_TW',
const MaterialLocalizationZhHantHk({
String localeName = 'zh_Hant_HK',
@required intl.DateFormat fullYearFormat,
@required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat,
......@@ -11893,141 +11941,30 @@ class MaterialLocalizationZhTw extends MaterialLocalizationZh {
decimalFormat: decimalFormat,
twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat,
);
}
@override
String get tabLabelRaw => r'第 $tabIndex 個分頁 (共 $tabCount 個)';
@override
String get showAccountsLabel => r'顯示帳戶';
@override
String get modalBarrierDismissLabel => r'關閉';
@override
String get hideAccountsLabel => r'隱藏帳戶';
@override
String get signedInLabel => r'已登入帳戶';
@override
String get openAppDrawerTooltip => r'開啟導覽選單';
@override
String get closeButtonTooltip => r'關閉';
@override
String get deleteButtonTooltip => r'刪除';
@override
String get nextMonthTooltip => r'下個月';
@override
String get previousMonthTooltip => r'上個月';
@override
String get nextPageTooltip => r'下一頁';
@override
String get previousPageTooltip => r'上一頁';
@override
String get showMenuTooltip => r'顯示選單';
@override
String get aboutListTileTitleRaw => r'關於「$applicationName」';
@override
String get licensesPageTitle => r'授權';
@override
String get pageRowsInfoTitleRaw => r'第 $firstRow - $lastRow 列 (總共 $rowCount 列)';
@override
String get pageRowsInfoTitleApproximateRaw => r'第 $firstRow - $lastRow 列 (總共約 $rowCount 列)';
@override
String get rowsPerPageTitle => r'每頁列數:';
@override
String get selectedRowCountTitleOne => r'已選取 1 個項目';
@override
String get selectedRowCountTitleOther => r'已選取 $selectedRowCount 個項目';
@override
String get closeButtonLabel => r'關閉';
@override
String get continueButtonLabel => r'繼續';
@override
String get copyButtonLabel => r'複製';
@override
String get cutButtonLabel => r'剪下';
@override
String get okButtonLabel => r'確定';
@override
String get pasteButtonLabel => r'貼上';
@override
String get selectAllButtonLabel => r'全選';
@override
String get viewLicensesButtonLabel => r'查看授權';
@override
String get timePickerHourModeAnnouncement => r'選取小時數';
@override
String get timePickerMinuteModeAnnouncement => r'選取分鐘數';
@override
String get drawerLabel => r'導覽選單';
@override
String get popupMenuLabel => r'彈出式選單';
@override
String get dialogLabel => r'對話方塊';
@override
String get alertDialogLabel => r'快訊';
@override
String get searchFieldLabel => r'搜尋';
@override
String get reorderItemToStart => r'移至開頭';
@override
String get reorderItemToEnd => r'移至結尾';
@override
String get reorderItemUp => r'向上移';
@override
String get reorderItemDown => r'向下移';
@override
String get reorderItemLeft => r'向左移';
@override
String get reorderItemRight => r'向右移';
@override
String get expandedIconTapHint => r'收合';
@override
String get collapsedIconTapHint => r'展開';
@override
String get remainingTextFieldCharacterCountOne => r'還可輸入 1 個字元';
@override
String get remainingTextFieldCharacterCountOther => r'還可輸入 $remainingCount 個字元';
/// The translations for Chinese, as used in Taiwan, using the Han script (`zh_Hant_TW`).
class MaterialLocalizationZhHantTw extends MaterialLocalizationZhHant {
/// Create an instance of the translation bundle for Chinese, as used in Taiwan, using the Han script.
///
/// For details on the meaning of the arguments, see [GlobalMaterialLocalizations].
const MaterialLocalizationZhHantTw({
String localeName = 'zh_Hant_TW',
@required intl.DateFormat fullYearFormat,
@required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat,
@required intl.DateFormat yearMonthFormat,
@required intl.NumberFormat decimalFormat,
@required intl.NumberFormat twoDigitZeroPaddedFormat,
}) : super(
localeName: localeName,
fullYearFormat: fullYearFormat,
mediumDateFormat: mediumDateFormat,
longDateFormat: longDateFormat,
yearMonthFormat: yearMonthFormat,
decimalFormat: decimalFormat,
twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat,
);
}
/// The set of supported languages, as language code strings.
......@@ -12107,15 +12044,15 @@ final Set<String> kSupportedLanguages = HashSet<String>.from(const <String>[
/// * `ca` - Catalan Valencian
/// * `cs` - Czech
/// * `da` - Danish
/// * `de` - German (plus one variant)
/// * `de` - German (plus one country variation)
/// * `el` - Modern Greek
/// * `en` - English (plus 7 variants)
/// * `es` - Spanish Castilian (plus 20 variants)
/// * `en` - English (plus 7 country variations)
/// * `es` - Spanish Castilian (plus 20 country variations)
/// * `et` - Estonian
/// * `fa` - Persian
/// * `fi` - Finnish
/// * `fil` - Filipino Pilipino
/// * `fr` - French (plus one variant)
/// * `fr` - French (plus one country variation)
/// * `gsw` - Swiss German Alemannic Alsatian
/// * `he` - Hebrew
/// * `hi` - Hindi
......@@ -12134,12 +12071,12 @@ final Set<String> kSupportedLanguages = HashSet<String>.from(const <String>[
/// * `nl` - Dutch Flemish
/// * `pl` - Polish
/// * `ps` - Pushto Pashto
/// * `pt` - Portuguese (plus one variant)
/// * `pt` - Portuguese (plus one country variation)
/// * `ro` - Romanian Moldavian Moldovan
/// * `ru` - Russian
/// * `sk` - Slovak
/// * `sl` - Slovenian
/// * `sr` - Serbian (plus one variant)
/// * `sr` - Serbian (plus 2 scripts)
/// * `sv` - Swedish
/// * `th` - Thai
/// * `tl` - Tagalog
......@@ -12147,7 +12084,7 @@ final Set<String> kSupportedLanguages = HashSet<String>.from(const <String>[
/// * `uk` - Ukrainian
/// * `ur` - Urdu
/// * `vi` - Vietnamese
/// * `zh` - Chinese (plus 2 variants)
/// * `zh` - Chinese (plus 2 country variations and 2 scripts)
/// {@endtemplate}
///
/// Generally speaking, this method is only intended to be used by
......@@ -12314,9 +12251,13 @@ GlobalMaterialLocalizations getTranslation(
case 'sl':
return MaterialLocalizationSl(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
case 'sr': {
switch (locale.countryCode) {
case 'Latn':
switch (locale.scriptCode) {
case 'Cyrl': {
return MaterialLocalizationSrCyrl(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
case 'Latn': {
return MaterialLocalizationSrLatn(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
}
return MaterialLocalizationSr(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
......@@ -12335,11 +12276,25 @@ GlobalMaterialLocalizations getTranslation(
case 'vi':
return MaterialLocalizationVi(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
case 'zh': {
switch (locale.scriptCode) {
case 'Hans': {
return MaterialLocalizationZhHans(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
case 'Hant': {
switch (locale.countryCode) {
case 'HK':
return MaterialLocalizationZhHantHk(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
case 'TW':
return MaterialLocalizationZhHantTw(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
return MaterialLocalizationZhHant(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
}
switch (locale.countryCode) {
case 'HK':
return MaterialLocalizationZhHk(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
return MaterialLocalizationZhHantHk(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
case 'TW':
return MaterialLocalizationZhTw(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
return MaterialLocalizationZhHantTw(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
return MaterialLocalizationZh(fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat);
}
......
......@@ -568,7 +568,27 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal
/// data. Subsequent invocations have no effect.
static void _loadDateIntlDataIfNotLoaded() {
if (!_dateIntlDataInitialized) {
// TODO(garyq): Add support for scriptCodes. Do not strip scriptCode from string.
// Keep track of initialzed locales, or will fail on attempted double init.
// This can only happen if a locale with a stripped scriptCode has already
// been initialzed. This should be removed when scriptCode stripping is removed.
final Set<String> initializedLocales = Set<String>();
date_localizations.dateSymbols.forEach((String locale, dynamic data) {
// Strip scriptCode from the locale, as we do not distinguish between scripts
// for dates.
final List<String> codes = locale.split('_');
String countryCode;
if (codes.length == 2) {
countryCode = codes[1].length < 4 ? codes[1] : null;
} else if (codes.length == 3) {
countryCode = codes[1].length < codes[2].length ? codes[1] : codes[2];
}
locale = codes[0] + (countryCode != null ? '_' + countryCode : '');
if (initializedLocales.contains(locale))
return;
initializedLocales.add(locale);
// Perform initialization.
assert(date_localizations.datePatterns.containsKey(locale));
final intl.DateSymbols symbols = intl.DateSymbols.deserializeFromMap(data);
date_symbol_data_custom.initializeDateFormattingCustom(
......
......@@ -153,4 +153,312 @@ void main() {
expect(localizations.formatMediumDate(DateTime(2015, 7, 23)), 'Do., 23. Juli');
expect(localizations.formatFullDate(DateTime(2015, 7, 23)), 'Donnerstag, 23. Juli 2015');
});
testWidgets('Chinese resolution', (WidgetTester tester) async {
Locale locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
MaterialLocalizations localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
locale = const Locale('zh', 'TW');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHantTw, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: null, countryCode: 'HK');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHantHk, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: null, countryCode: 'TW');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHantTw, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHans, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHant, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHans, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHans, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'TW');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHans, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'CN');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHant, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHant, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: null, countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Latn', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Latn', countryCode: 'TW');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: 'TW');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Cyrl', countryCode: 'RU');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: null, countryCode: 'RU');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Cyrl', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
});
testWidgets('Serbian resolution', (WidgetTester tester) async {
Locale locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
MaterialLocalizations localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSr, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSrCyrl, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSrLatn, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: null, countryCode: 'SR');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSr, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl', countryCode: 'SR');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSrCyrl, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn', countryCode: 'SR');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSrLatn, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSrCyrl, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSrLatn, true);
locale = const Locale.fromSubtags(languageCode: 'sr', scriptCode: null, countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationSr, true);
});
testWidgets('Misc resolution', (WidgetTester tester) async {
Locale locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
MaterialLocalizations localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Cyrl', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: 'AU');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEnAu, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: 'GB');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEnGb, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: 'SG');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEnSg, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: null, countryCode: 'MX');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Hant', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Hant', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Hans', countryCode: 'CN');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEn, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEs, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: null, countryCode: '419');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEs419, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: null, countryCode: 'MX');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEsMx, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: null, countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEsUs, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: null, countryCode: 'AR');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEsAr, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: null, countryCode: 'ES');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEs, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: 'Latn', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEs, true);
locale = const Locale.fromSubtags(languageCode: 'es', scriptCode: 'Latn', countryCode: 'US');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationEsUs, true);
locale = const Locale.fromSubtags(languageCode: 'fr', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationFr, true);
locale = const Locale.fromSubtags(languageCode: 'fr', scriptCode: null, countryCode: 'CA');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationFrCa, true);
locale = const Locale.fromSubtags(languageCode: 'de', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationDe, true);
locale = const Locale.fromSubtags(languageCode: 'de', scriptCode: null, countryCode: 'CH');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationDeCh, true);
locale = const Locale.fromSubtags(languageCode: 'th', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationTh, true);
locale = const Locale.fromSubtags(languageCode: 'ru', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationRu, true);
});
testWidgets('Chinese translations spot check', (WidgetTester tester) async {
Locale locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: null, countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
MaterialLocalizations localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZh, true);
expect(localizations.alertDialogLabel, '提醒');
expect(localizations.anteMeridiemAbbreviation, '上午');
expect(localizations.closeButtonLabel, '关闭');
expect(localizations.okButtonLabel, '确定');
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHans, true);
expect(localizations.alertDialogLabel, '提醒');
expect(localizations.anteMeridiemAbbreviation, '上午');
expect(localizations.closeButtonLabel, '关闭');
expect(localizations.okButtonLabel, '确定');
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: null);
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHant, true);
expect(localizations.alertDialogLabel, '快訊');
expect(localizations.anteMeridiemAbbreviation, '上午');
expect(localizations.closeButtonLabel, '關閉');
expect(localizations.okButtonLabel, '確定');
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHantTw, true);
expect(localizations.alertDialogLabel, '快訊');
expect(localizations.anteMeridiemAbbreviation, '上午');
expect(localizations.closeButtonLabel, '關閉');
expect(localizations.okButtonLabel, '確定');
locale = const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK');
expect(GlobalMaterialLocalizations.delegate.isSupported(locale), isTrue);
localizations = await GlobalMaterialLocalizations.delegate.load(locale);
expect(localizations is MaterialLocalizationZhHantHk, true);
expect(localizations.alertDialogLabel, '快訊');
expect(localizations.anteMeridiemAbbreviation, '上午');
expect(localizations.closeButtonLabel, '關閉');
expect(localizations.okButtonLabel, '確定');
});
}
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