-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Adds i18n to widget layer #123620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds i18n to widget layer #123620
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,22 +634,30 @@ void main(List<String> rawArgs) { | |
| generateConstructor: generateCupertinoConstructor, | ||
| factoryName: cupertinoFactoryName, | ||
| factoryDeclaration: cupertinoFactoryDeclaration, | ||
| callsFactoryWithConst: false, | ||
| factoryArguments: cupertinoFactoryArguments, | ||
| supportedLanguagesConstant: cupertinoSupportedLanguagesConstant, | ||
| supportedLanguagesDocMacro: cupertinoSupportedLanguagesDocMacro, | ||
| ) | ||
| : 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); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this affect the generated code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The const keyword in https://github.com/flutter/flutter/blob/3173f275015cfd2d35790f1c97de3a32235f130f/packages/flutter_localizations/lib/src/l10n/generated_widgets_localizations.dart#L2693
The generated material localization can't use const here because they pass in parameter here