Adds i18n to widget layer (#123620)

Adds i18n to widget layer
This commit is contained in:
chunhtai
2023-04-04 15:57:35 -07:00
committed by GitHub
parent c7bab2973e
commit 9e4b5fb7d9
124 changed files with 4171 additions and 66 deletions

View File

@@ -69,18 +69,23 @@ void _rewriteBundle(File file, Map<String, dynamic> bundle) {
}
void encodeKnArbFiles(Directory directory) {
final File widgetsArbFile = File(path.join(directory.path, 'widgets_kn.arb'));
final File materialArbFile = File(path.join(directory.path, 'material_kn.arb'));
final File cupertinoArbFile = File(path.join(directory.path, 'cupertino_kn.arb'));
final Map<String, dynamic> widgetsBundle = _loadBundle(widgetsArbFile);
final Map<String, dynamic> materialBundle = _loadBundle(materialArbFile);
final Map<String, dynamic> cupertinoBundle = _loadBundle(cupertinoArbFile);
_encodeBundleTranslations(widgetsBundle);
_encodeBundleTranslations(materialBundle);
_encodeBundleTranslations(cupertinoBundle);
_checkEncodedTranslations(widgetsBundle, _loadBundle(widgetsArbFile));
_checkEncodedTranslations(materialBundle, _loadBundle(materialArbFile));
_checkEncodedTranslations(cupertinoBundle, _loadBundle(cupertinoArbFile));
_rewriteBundle(widgetsArbFile, widgetsBundle);
_rewriteBundle(materialArbFile, materialBundle);
_rewriteBundle(cupertinoArbFile, cupertinoBundle);
}

View File

@@ -53,6 +53,7 @@ import 'package:path/path.dart' as path;
import '../gen_cupertino_localizations.dart';
import '../gen_material_localizations.dart';
import '../gen_widgets_localizations.dart';
import '../localizations_utils.dart';
import '../localizations_validator.dart';
import 'encode_kn_arb_files.dart';
@@ -65,8 +66,10 @@ String generateArbBasedLocalizationSubclasses({
required String baseClass,
required HeaderGenerator generateHeader,
required ConstructorGenerator generateConstructor,
ConstructorGenerator? generateConstructorForCountrySubClass,
required String factoryName,
required String factoryDeclaration,
required bool callsFactoryWithConst,
required String factoryArguments,
required String supportedLanguagesConstant,
required String supportedLanguagesDocMacro,
@@ -78,7 +81,7 @@ String generateArbBasedLocalizationSubclasses({
assert(factoryArguments.isNotEmpty);
assert(supportedLanguagesConstant.isNotEmpty);
assert(supportedLanguagesDocMacro.isNotEmpty);
generateConstructorForCountrySubClass ??= generateConstructor;
final StringBuffer output = StringBuffer();
output.writeln(generateHeader('dart dev/tools/localization/bin/gen_localizations.dart --overwrite'));
@@ -133,7 +136,6 @@ String generateArbBasedLocalizationSubclasses({
final LocaleInfo canonicalLocale = LocaleInfo.fromString('en');
for (final String languageName in languageCodes) {
final LocaleInfo languageLocale = LocaleInfo.fromString(languageName);
output.writeln(generateClassDeclaration(languageLocale, generatedClassPrefix, baseClass));
output.writeln(generateConstructor(languageLocale));
@@ -156,7 +158,7 @@ String generateArbBasedLocalizationSubclasses({
generatedClassPrefix,
'$generatedClassPrefix${languageLocale.camelCase()}',
));
output.writeln(generateConstructor(scriptBaseLocale));
output.writeln(generateConstructorForCountrySubClass(scriptBaseLocale));
final Map<String, String> scriptResources = localeToResources[scriptBaseLocale]!;
for (final String key in scriptResources.keys.toList()..sort()) {
if (languageResources[key] == scriptResources[key]) {
@@ -184,7 +186,7 @@ String generateArbBasedLocalizationSubclasses({
generatedClassPrefix,
'$generatedClassPrefix${scriptBaseLocale.camelCase()}',
));
output.writeln(generateConstructor(locale));
output.writeln(generateConstructorForCountrySubClass(locale));
final Map<String, String> localeResources = localeToResources[locale]!;
for (final String key in localeResources.keys) {
// When script fallback contains the key, we compare to it instead of language fallback.
@@ -212,7 +214,7 @@ String generateArbBasedLocalizationSubclasses({
generatedClassPrefix,
'$generatedClassPrefix${languageLocale.camelCase()}',
));
output.writeln(generateConstructor(locale));
output.writeln(generateConstructorForCountrySubClass(locale));
for (final String key in localeResources.keys) {
if (languageResources[key] == localeResources[key]) {
continue;
@@ -279,7 +281,7 @@ $factoryDeclaration
if (languageToLocales[language]!.length == 1) {
output.writeln('''
case '$language':
return $generatedClassPrefix${languageToLocales[language]![0].camelCase()}($factoryArguments);''');
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${languageToLocales[language]![0].camelCase()}($factoryArguments);''');
} else if (!languageToScriptCodes.containsKey(language)) { // Does not distinguish between scripts. Switch on countryCode directly.
output.writeln('''
case '$language': {
@@ -292,11 +294,11 @@ $factoryDeclaration
final String countryCode = locale.countryCode!;
output.writeln('''
case '$countryCode':
return $generatedClassPrefix${locale.camelCase()}($factoryArguments);''');
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${locale.camelCase()}($factoryArguments);''');
}
output.writeln('''
}
return $generatedClassPrefix${LocaleInfo.fromString(language).camelCase()}($factoryArguments);
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${LocaleInfo.fromString(language).camelCase()}($factoryArguments);
}''');
} else { // Language has scriptCode, add additional switch logic.
bool hasCountryCode = false;
@@ -325,7 +327,7 @@ $factoryDeclaration
final String countryCode = locale.countryCode!;
output.writeln('''
case '$countryCode':
return $generatedClassPrefix${locale.camelCase()}($factoryArguments);''');
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${locale.camelCase()}($factoryArguments);''');
}
}
// Return a fallback locale that matches scriptCode, but not countryCode.
@@ -337,7 +339,7 @@ $factoryDeclaration
}''');
}
output.writeln('''
return $generatedClassPrefix${scriptLocale.camelCase()}($factoryArguments);
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${scriptLocale.camelCase()}($factoryArguments);
}''');
} else {
// Not Explicitly defined, fallback to first locale with the same language and
@@ -351,7 +353,7 @@ $factoryDeclaration
}''');
}
output.writeln('''
return $generatedClassPrefix${scriptLocale.camelCase()}($factoryArguments);
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${scriptLocale.camelCase()}($factoryArguments);
}''');
break;
}
@@ -373,13 +375,13 @@ $factoryDeclaration
final String countryCode = locale.countryCode!;
output.writeln('''
case '$countryCode':
return $generatedClassPrefix${locale.camelCase()}($factoryArguments);''');
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${locale.camelCase()}($factoryArguments);''');
}
output.writeln('''
}''');
}
output.writeln('''
return $generatedClassPrefix${LocaleInfo.fromString(language).camelCase()}($factoryArguments);
return ${callsFactoryWithConst ? 'const ': ''}$generatedClassPrefix${LocaleInfo.fromString(language).camelCase()}($factoryArguments);
}''');
}
}
@@ -515,10 +517,12 @@ void main(List<String> rawArgs) {
// code. In most cases both codes are just two characters.
final Directory directory = Directory(path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n'));
final RegExp widgetsFilenameRE = RegExp(r'widgets_(\w+)\.arb$');
final RegExp materialFilenameRE = RegExp(r'material_(\w+)\.arb$');
final RegExp cupertinoFilenameRE = RegExp(r'cupertino_(\w+)\.arb$');
try {
validateEnglishLocalizations(File(path.join(directory.path, 'widgets_en.arb')));
validateEnglishLocalizations(File(path.join(directory.path, 'material_en.arb')));
validateEnglishLocalizations(File(path.join(directory.path, 'cupertino_en.arb')));
} on ValidationError catch (exception) {
@@ -537,17 +541,30 @@ void main(List<String> rawArgs) {
precacheLanguageAndRegionTags();
// Maps of locales to resource key/value pairs for Widgets ARBs.
final Map<LocaleInfo, Map<String, String>> widgetsLocaleToResources = <LocaleInfo, Map<String, String>>{};
// Maps of locales to resource key/attributes pairs for Widgets ARBs..
// https://github.com/googlei18n/app-resource-bundle/wiki/ApplicationResourceBundleSpecification#resource-attributes
final Map<LocaleInfo, Map<String, dynamic>> widgetsLocaleToResourceAttributes = <LocaleInfo, Map<String, dynamic>>{};
// Maps of locales to resource key/value pairs for Material ARBs.
final Map<LocaleInfo, Map<String, String>> materialLocaleToResources = <LocaleInfo, Map<String, String>>{};
// Maps of locales to resource key/attributes pairs for Material ARBs..
// https://github.com/googlei18n/app-resource-bundle/wiki/ApplicationResourceBundleSpecification#resource-attributes
final Map<LocaleInfo, Map<String, dynamic>> materialLocaleToResourceAttributes = <LocaleInfo, Map<String, dynamic>>{};
// Maps of locales to resource key/value pairs for Cupertino ARBs.
final Map<LocaleInfo, Map<String, String>> cupertinoLocaleToResources = <LocaleInfo, Map<String, String>>{};
// Maps of locales to resource key/attributes pairs for Cupertino ARBs..
// https://github.com/googlei18n/app-resource-bundle/wiki/ApplicationResourceBundleSpecification#resource-attributes
final Map<LocaleInfo, Map<String, dynamic>> cupertinoLocaleToResourceAttributes = <LocaleInfo, Map<String, dynamic>>{};
loadMatchingArbsIntoBundleMaps(
directory: directory,
filenamePattern: widgetsFilenameRE,
localeToResources: widgetsLocaleToResources,
localeToResourceAttributes: widgetsLocaleToResourceAttributes,
);
loadMatchingArbsIntoBundleMaps(
directory: directory,
filenamePattern: materialFilenameRE,
@@ -562,17 +579,35 @@ void main(List<String> rawArgs) {
);
try {
validateLocalizations(widgetsLocaleToResources, widgetsLocaleToResourceAttributes, removeUndefined: options.removeUndefined);
validateLocalizations(materialLocaleToResources, materialLocaleToResourceAttributes, removeUndefined: options.removeUndefined);
validateLocalizations(cupertinoLocaleToResources, cupertinoLocaleToResourceAttributes, removeUndefined: options.removeUndefined);
} on ValidationError catch (exception) {
exitWithError('$exception');
}
if (options.removeUndefined) {
removeUndefinedLocalizations(widgetsLocaleToResources);
removeUndefinedLocalizations(materialLocaleToResources);
removeUndefinedLocalizations(cupertinoLocaleToResources);
}
final String? widgetsLocalizations = options.writeToFile || !options.cupertinoOnly
? generateArbBasedLocalizationSubclasses(
localeToResources: widgetsLocaleToResources,
localeToResourceAttributes: widgetsLocaleToResourceAttributes,
generatedClassPrefix: 'WidgetsLocalization',
baseClass: 'GlobalWidgetsLocalizations',
generateHeader: generateWidgetsHeader,
generateConstructor: generateWidgetsConstructor,
generateConstructorForCountrySubClass: generateWidgetsConstructorForCountrySubclass,
factoryName: widgetsFactoryName,
factoryDeclaration: widgetsFactoryDeclaration,
callsFactoryWithConst: true,
factoryArguments: widgetsFactoryArguments,
supportedLanguagesConstant: widgetsSupportedLanguagesConstant,
supportedLanguagesDocMacro: widgetsSupportedLanguagesDocMacro,
)
: null;
final String? materialLocalizations = options.writeToFile || !options.cupertinoOnly
? generateArbBasedLocalizationSubclasses(
localeToResources: materialLocaleToResources,
@@ -583,6 +618,7 @@ void main(List<String> rawArgs) {
generateConstructor: generateMaterialConstructor,
factoryName: materialFactoryName,
factoryDeclaration: materialFactoryDeclaration,
callsFactoryWithConst: false,
factoryArguments: materialFactoryArguments,
supportedLanguagesConstant: materialSupportedLanguagesConstant,
supportedLanguagesDocMacro: materialSupportedLanguagesDocMacro,
@@ -598,6 +634,7 @@ void main(List<String> rawArgs) {
generateConstructor: generateCupertinoConstructor,
factoryName: cupertinoFactoryName,
factoryDeclaration: cupertinoFactoryDeclaration,
callsFactoryWithConst: false,
factoryArguments: cupertinoFactoryArguments,
supportedLanguagesConstant: cupertinoSupportedLanguagesConstant,
supportedLanguagesDocMacro: cupertinoSupportedLanguagesDocMacro,
@@ -605,15 +642,22 @@ void main(List<String> rawArgs) {
: null;
if (options.writeToFile) {
final File widgetsLocalizationsFile = File(path.join(directory.path, 'generated_widgets_localizations.dart'));
widgetsLocalizationsFile.writeAsStringSync(widgetsLocalizations!, flush: true);
final File materialLocalizationsFile = File(path.join(directory.path, 'generated_material_localizations.dart'));
materialLocalizationsFile.writeAsStringSync(materialLocalizations!, flush: true);
final File cupertinoLocalizationsFile = File(path.join(directory.path, 'generated_cupertino_localizations.dart'));
cupertinoLocalizationsFile.writeAsStringSync(cupertinoLocalizations!, flush: true);
} else {
if (!options.cupertinoOnly) {
if (options.cupertinoOnly) {
stdout.write(cupertinoLocalizations);
} else if (options.materialOnly) {
stdout.write(materialLocalizations);
} else if (options.widgetsOnly) {
stdout.write(widgetsLocalizations);
} else {
stdout.write(widgetsLocalizations);
stdout.write(materialLocalizations);
}
if (!options.materialOnly) {
stdout.write(cupertinoLocalizations);
}
}

View File

@@ -0,0 +1,74 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'localizations_utils.dart';
// See http://en.wikipedia.org/wiki/Right-to-left
const List<String> _rtlLanguages = <String>[
'ar', // Arabic
'fa', // Farsi
'he', // Hebrew
'ps', // Pashto
'ur', // Urdu
];
String generateWidgetsHeader(String regenerateInstructions) {
return '''
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file has been automatically generated. Please do not edit it manually.
// To regenerate the file, use:
// $regenerateInstructions
import 'dart:collection';
import 'dart:ui';
import '../widgets_localizations.dart';
// The classes defined here encode all of the translations found in the
// `flutter_localizations/lib/src/l10n/*.arb` files.
//
// These classes are constructed by the [getWidgetsTranslation] method at the
// bottom of this file, and used by the [_WidgetsLocalizationsDelegate.load]
// method defined in `flutter_localizations/lib/src/widgets_localizations.dart`.''';
}
/// Returns the source of the constructor for a GlobalWidgetsLocalizations
/// subclass.
String generateWidgetsConstructor(LocaleInfo locale) {
final String localeName = locale.originalString;
final String language = locale.languageCode.toLowerCase();
final String textDirection = _rtlLanguages.contains(language) ? 'TextDirection.rtl' : 'TextDirection.ltr';
return '''
/// Create an instance of the translation bundle for ${describeLocale(localeName)}.
///
/// For details on the meaning of the arguments, see [GlobalWidgetsLocalizations].
const WidgetsLocalization${locale.camelCase()}() : super($textDirection);''';
}
/// Returns the source of the constructor for a GlobalWidgetsLocalizations
/// subclass.
String generateWidgetsConstructorForCountrySubclass(LocaleInfo locale) {
final String localeName = locale.originalString;
return '''
/// Create an instance of the translation bundle for ${describeLocale(localeName)}.
///
/// For details on the meaning of the arguments, see [GlobalWidgetsLocalizations].
const WidgetsLocalization${locale.camelCase()}();''';
}
const String widgetsFactoryName = 'getWidgetsTranslation';
const String widgetsFactoryDeclaration = '''
GlobalWidgetsLocalizations? getWidgetsTranslation(
Locale locale,
) {''';
const String widgetsFactoryArguments = '';
const String widgetsSupportedLanguagesConstant = 'kWidgetsSupportedLanguages';
const String widgetsSupportedLanguagesDocMacro = 'flutter.localizations.widgets.languages';

View File

@@ -239,6 +239,10 @@ GeneratorOptions parseArgs(List<String> rawArgs) {
'remove-undefined',
help: 'Remove any localizations that are not defined in the canonical locale.',
)
..addFlag(
'widgets',
help: 'Whether to print the generated classes for the Widgets package only. Ignored when --overwrite is passed.',
)
..addFlag(
'material',
help: 'Whether to print the generated classes for the Material package only. Ignored when --overwrite is passed.',
@@ -254,6 +258,7 @@ GeneratorOptions parseArgs(List<String> rawArgs) {
}
final bool writeToFile = args['overwrite'] as bool;
final bool removeUndefined = args['remove-undefined'] as bool;
final bool widgetsOnly = args['widgets'] as bool;
final bool materialOnly = args['material'] as bool;
final bool cupertinoOnly = args['cupertino'] as bool;
@@ -261,6 +266,7 @@ GeneratorOptions parseArgs(List<String> rawArgs) {
writeToFile: writeToFile,
materialOnly: materialOnly,
cupertinoOnly: cupertinoOnly,
widgetsOnly: widgetsOnly,
removeUndefined: removeUndefined,
);
}
@@ -271,12 +277,14 @@ class GeneratorOptions {
required this.removeUndefined,
required this.materialOnly,
required this.cupertinoOnly,
required this.widgetsOnly,
});
final bool writeToFile;
final bool removeUndefined;
final bool materialOnly;
final bool cupertinoOnly;
final bool widgetsOnly;
}
// See also //master/tools/gen_locale.dart in the engine repo.