diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart b/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart index 48c6553d9b..2c8d4f8608 100644 --- a/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart +++ b/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart @@ -136,23 +136,21 @@ class _StockStringsDelegate extends LocalizationsDelegate { StockStrings _lookupStockStrings(Locale locale) { - -// Lookup logic when language+country codes are specified. -switch (locale.languageCode) { - case 'en': { - switch (locale.countryCode) { - case 'US': return StockStringsEnUs(); + // Lookup logic when language+country codes are specified. + switch (locale.languageCode) { + case 'en': { + switch (locale.countryCode) { + case 'US': return StockStringsEnUs(); + } + break; + } } - break; -} -} -// Lookup logic when only language code is specified. -switch (locale.languageCode) { - case 'en': return StockStringsEn(); + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': return StockStringsEn(); case 'es': return StockStringsEs(); -} - + } throw FlutterError( 'StockStrings.delegate failed to load unsupported locale "$locale". This is likely ' diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart index 81feedc697..51f71e5962 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart @@ -346,6 +346,22 @@ String generateBaseClassMethod(Message message, LocaleInfo? templateArbLocale) { .replaceAll('@(name)', message.resourceId); } +// Add spaces to pad the start of each line. Skips the first line +// assuming that the padding is already present. +String _addSpaces(String message, {int spaces = 0}) { + bool isFirstLine = true; + return message + .split('\n') + .map((String value) { + if (isFirstLine) { + isFirstLine = false; + return value; + } + return value.padLeft(spaces); + }) + .join('\n'); +} + String _generateLookupByAllCodes( AppResourceBundleCollection allBundles, String Function(LocaleInfo) generateSwitchClauseTemplate, @@ -365,7 +381,7 @@ String _generateLookupByAllCodes( return allCodesLookupTemplate.replaceAll( '@(allCodesSwitchClauses)', - switchClauses.join('\n '), + switchClauses.join('\n '), ); } @@ -383,13 +399,20 @@ String _generateLookupByScriptCode( return null; } - return nestedSwitchTemplate + return _addSpaces(nestedSwitchTemplate .replaceAll('@(languageCode)', language) .replaceAll('@(code)', 'scriptCode') - .replaceAll('@(switchClauses)', localesWithScriptCodes.map((LocaleInfo locale) { - return generateSwitchClauseTemplate(locale) - .replaceAll('@(case)', locale.scriptCode!); - }).join('\n ')); + .replaceAll('@(switchClauses)', + _addSpaces( + localesWithScriptCodes.map((LocaleInfo locale) { + return generateSwitchClauseTemplate(locale) + .replaceAll('@(case)', locale.scriptCode!); + }).join('\n'), + spaces: 8, + ), + ), + spaces: 4, + ); }).whereType(); if (switchClauses.isEmpty) { @@ -398,7 +421,7 @@ String _generateLookupByScriptCode( return languageCodeSwitchTemplate .replaceAll('@(comment)', '// Lookup logic when language+script codes are specified.') - .replaceAll('@(switchClauses)', switchClauses.join('\n '), + .replaceAll('@(switchClauses)', switchClauses.join('\n '), ); } @@ -416,13 +439,18 @@ String _generateLookupByCountryCode( return null; } - return nestedSwitchTemplate - .replaceAll('@(languageCode)', language) - .replaceAll('@(code)', 'countryCode') - .replaceAll('@(switchClauses)', localesWithCountryCodes.map((LocaleInfo locale) { - return generateSwitchClauseTemplate(locale) - .replaceAll('@(case)', locale.countryCode!); - }).join('\n ')); + return _addSpaces( + nestedSwitchTemplate + .replaceAll('@(languageCode)', language) + .replaceAll('@(code)', 'countryCode') + .replaceAll('@(switchClauses)', _addSpaces( + localesWithCountryCodes.map((LocaleInfo locale) { + return generateSwitchClauseTemplate(locale).replaceAll('@(case)', locale.countryCode!); + }).join('\n'), + spaces: 4, + )), + spaces: 4, + ); }).whereType(); if (switchClauses.isEmpty) { @@ -451,7 +479,7 @@ String _generateLookupByLanguageCode( return localesWithLanguageCode.map((LocaleInfo locale) { return generateSwitchClauseTemplate(locale) .replaceAll('@(case)', locale.languageCode); - }).join('\n '); + }).join('\n '); }).whereType(); if (switchClauses.isEmpty) { @@ -1133,7 +1161,11 @@ class LocalizationsGenerator { .replaceAll('@(requiresFoundationImport)', useDeferredLoading ? '' : "import 'package:flutter/foundation.dart';") .replaceAll('@(requiresIntlImport)', _containsPluralMessage() ? "import 'package:intl/intl.dart' as intl;" : '') .replaceAll('@(canBeNullable)', usesNullableGetter ? '?' : '') - .replaceAll('@(needsNullCheck)', usesNullableGetter ? '' : '!'); + .replaceAll('@(needsNullCheck)', usesNullableGetter ? '' : '!') + // Removes all trailing whitespace from the generated file. + .split('\n').map((String line) => line.trimRight()).join('\n') + // Cleans out unnecessary newlines. + .replaceAll('\n\n\n', '\n\n'); } bool _containsPluralMessage() => _allMessages.any((Message message) => message.isPlural); diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart index 01b3d4789e..8da8122c8d 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart @@ -263,15 +263,15 @@ case '@(languageCode)': { }'''; const String languageCodeSwitchTemplate = ''' -@(comment) -switch (locale.languageCode) { - @(switchClauses) -} + @(comment) + switch (locale.languageCode) { + @(switchClauses) + } '''; const String allCodesLookupTemplate = ''' -// Lookup logic when language+script+country codes are specified. -switch (locale.toString()) { - @(allCodesSwitchClauses) -} + // Lookup logic when language+script+country codes are specified. + switch (locale.toString()) { + @(allCodesSwitchClauses) + } '''; diff --git a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart index 0f41fb77f4..3003e7770e 100644 --- a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart +++ b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart @@ -1809,6 +1809,42 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e expect(localizationsFile, contains(intlImportDartCode)); }); + testUsingContext('check indentation on generated files', () { + _standardFlutterDirectoryL10nSetup(fs); + + try { + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useDeferredLoading: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); + } on Exception catch (e) { + fail('Generating output files should not fail: $e'); + } + + final String localizationsFile = fs.file( + fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart'), + ).readAsStringSync(); + // Tests a few of the lines in the generated code. + // Localizations lookup code + expect(localizationsFile.contains(' switch (locale.languageCode) {'), true); + expect(localizationsFile.contains(" case 'en': return AppLocalizationsEn();"), true); + expect(localizationsFile.contains(" case 'es': return AppLocalizationsEs();"), true); + expect(localizationsFile.contains(' }'), true); + + // Supported locales list + expect(localizationsFile.contains(' static const List supportedLocales = ['), true); + expect(localizationsFile.contains(" Locale('en'),"), true); + expect(localizationsFile.contains(" Locale('es')"), true); + expect(localizationsFile.contains(' ];'), true); + }); + testUsingContext('foundation package import should be omitted from file template when deferred loading = true', () { fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true) ..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString)