Add option for deferred loading to gen_l10n (#53824)
This commit is contained in:
parent
0d459f2326
commit
4451ffca23
@ -81,6 +81,24 @@ void main(List<String> arguments) {
|
||||
'Alternatively, see the `header` option to pass in a string '
|
||||
'for a simpler header.'
|
||||
);
|
||||
parser.addFlag(
|
||||
'use-deferred-loading',
|
||||
defaultsTo: false,
|
||||
help: 'Whether to generate the Dart localization file with locales imported'
|
||||
' as deferred, allowing for lazy loading of each locale in Flutter web.\n'
|
||||
'\n'
|
||||
'This can reduce a web app’s initial startup time by decreasing the '
|
||||
'size of the JavaScript bundle. When this flag is set to true, the '
|
||||
'messages for a particular locale are only downloaded and loaded by the '
|
||||
'Flutter app as they are needed. For projects with a lot of different '
|
||||
'locales and many localization strings, it can be an performance '
|
||||
'improvement to have deferred loading. For projects with a small number '
|
||||
'of locales, the difference is negligible, and might slow down the start '
|
||||
'up compared to bundling the localizations with the rest of the '
|
||||
'application.\n\n'
|
||||
'Note that this flag does not affect other platforms such as mobile or '
|
||||
'desktop.',
|
||||
);
|
||||
|
||||
final argslib.ArgResults results = parser.parse(arguments);
|
||||
if (results['help'] == true) {
|
||||
@ -98,6 +116,7 @@ void main(List<String> arguments) {
|
||||
final String preferredSupportedLocaleString = results['preferred-supported-locales'] as String;
|
||||
final String headerString = results['header'] as String;
|
||||
final String headerFile = results['header-file'] as String;
|
||||
final bool useDeferredLoading = results['use-deferred-loading'] as bool;
|
||||
|
||||
const local.LocalFileSystem fs = local.LocalFileSystem();
|
||||
final LocalizationsGenerator localizationsGenerator = LocalizationsGenerator(fs);
|
||||
@ -112,6 +131,7 @@ void main(List<String> arguments) {
|
||||
preferredSupportedLocaleString: preferredSupportedLocaleString,
|
||||
headerString: headerString,
|
||||
headerFile: headerFile,
|
||||
useDeferredLoading: useDeferredLoading,
|
||||
)
|
||||
..loadResources()
|
||||
..writeOutputFile()
|
||||
|
@ -206,7 +206,10 @@ String generateBaseClassMethod(Message message) {
|
||||
.replaceAll('@(name)', message.resourceId);
|
||||
}
|
||||
|
||||
String _generateLookupByAllCodes(AppResourceBundleCollection allBundles, String className) {
|
||||
String _generateLookupByAllCodes(
|
||||
AppResourceBundleCollection allBundles,
|
||||
String Function(LocaleInfo) generateSwitchClauseTemplate,
|
||||
) {
|
||||
final Iterable<LocaleInfo> localesWithAllCodes = allBundles.locales.where((LocaleInfo locale) {
|
||||
return locale.scriptCode != null && locale.countryCode != null;
|
||||
});
|
||||
@ -216,9 +219,8 @@ String _generateLookupByAllCodes(AppResourceBundleCollection allBundles, String
|
||||
}
|
||||
|
||||
final Iterable<String> switchClauses = localesWithAllCodes.map<String>((LocaleInfo locale) {
|
||||
return switchClauseTemplate
|
||||
.replaceAll('@(case)', locale.toString())
|
||||
.replaceAll('@(class)', '$className${locale.camelCase()}');
|
||||
return generateSwitchClauseTemplate(locale)
|
||||
.replaceAll('@(case)', locale.toString());
|
||||
});
|
||||
|
||||
return allCodesLookupTemplate.replaceAll(
|
||||
@ -227,7 +229,10 @@ String _generateLookupByAllCodes(AppResourceBundleCollection allBundles, String
|
||||
);
|
||||
}
|
||||
|
||||
String _generateLookupByScriptCode(AppResourceBundleCollection allBundles, String className) {
|
||||
String _generateLookupByScriptCode(
|
||||
AppResourceBundleCollection allBundles,
|
||||
String Function(LocaleInfo) generateSwitchClauseTemplate,
|
||||
) {
|
||||
final Iterable<String> switchClauses = allBundles.languages.map((String language) {
|
||||
final Iterable<LocaleInfo> locales = allBundles.localesForLanguage(language);
|
||||
final Iterable<LocaleInfo> localesWithScriptCodes = locales.where((LocaleInfo locale) {
|
||||
@ -240,11 +245,9 @@ String _generateLookupByScriptCode(AppResourceBundleCollection allBundles, Strin
|
||||
return nestedSwitchTemplate
|
||||
.replaceAll('@(languageCode)', language)
|
||||
.replaceAll('@(code)', 'scriptCode')
|
||||
.replaceAll('@(class)', '$className${LocaleInfo.fromString(language).camelCase()}')
|
||||
.replaceAll('@(switchClauses)', localesWithScriptCodes.map((LocaleInfo locale) {
|
||||
return switchClauseTemplate
|
||||
.replaceAll('@(case)', locale.scriptCode)
|
||||
.replaceAll('@(class)', '$className${locale.camelCase()}');
|
||||
return generateSwitchClauseTemplate(locale)
|
||||
.replaceAll('@(case)', locale.scriptCode);
|
||||
}).join('\n '));
|
||||
}).where((String switchClause) => switchClause != null);
|
||||
|
||||
@ -258,7 +261,10 @@ String _generateLookupByScriptCode(AppResourceBundleCollection allBundles, Strin
|
||||
);
|
||||
}
|
||||
|
||||
String _generateLookupByCountryCode(AppResourceBundleCollection allBundles, String className) {
|
||||
String _generateLookupByCountryCode(
|
||||
AppResourceBundleCollection allBundles,
|
||||
String Function(LocaleInfo) generateSwitchClauseTemplate,
|
||||
) {
|
||||
final Iterable<String> switchClauses = allBundles.languages.map((String language) {
|
||||
final Iterable<LocaleInfo> locales = allBundles.localesForLanguage(language);
|
||||
final Iterable<LocaleInfo> localesWithCountryCodes = locales.where((LocaleInfo locale) {
|
||||
@ -271,11 +277,9 @@ String _generateLookupByCountryCode(AppResourceBundleCollection allBundles, Stri
|
||||
return nestedSwitchTemplate
|
||||
.replaceAll('@(languageCode)', language)
|
||||
.replaceAll('@(code)', 'countryCode')
|
||||
.replaceAll('@(class)', '$className${LocaleInfo.fromString(language).camelCase()}')
|
||||
.replaceAll('@(switchClauses)', localesWithCountryCodes.map((LocaleInfo locale) {
|
||||
return switchClauseTemplate
|
||||
.replaceAll('@(case)', locale.countryCode)
|
||||
.replaceAll('@(class)', '$className${locale.camelCase()}');
|
||||
return generateSwitchClauseTemplate(locale)
|
||||
.replaceAll('@(case)', locale.countryCode);
|
||||
}).join('\n '));
|
||||
}).where((String switchClause) => switchClause != null);
|
||||
|
||||
@ -288,7 +292,10 @@ String _generateLookupByCountryCode(AppResourceBundleCollection allBundles, Stri
|
||||
.replaceAll('@(switchClauses)', switchClauses.join('\n '));
|
||||
}
|
||||
|
||||
String _generateLookupByLanguageCode(AppResourceBundleCollection allBundles, String className) {
|
||||
String _generateLookupByLanguageCode(
|
||||
AppResourceBundleCollection allBundles,
|
||||
String Function(LocaleInfo) generateSwitchClauseTemplate,
|
||||
) {
|
||||
final Iterable<String> switchClauses = allBundles.languages.map((String language) {
|
||||
final Iterable<LocaleInfo> locales = allBundles.localesForLanguage(language);
|
||||
final Iterable<LocaleInfo> localesWithLanguageCode = locales.where((LocaleInfo locale) {
|
||||
@ -299,9 +306,8 @@ String _generateLookupByLanguageCode(AppResourceBundleCollection allBundles, Str
|
||||
return null;
|
||||
|
||||
return localesWithLanguageCode.map((LocaleInfo locale) {
|
||||
return switchClauseTemplate
|
||||
.replaceAll('@(case)', locale.languageCode)
|
||||
.replaceAll('@(class)', '$className${locale.camelCase()}');
|
||||
return generateSwitchClauseTemplate(locale)
|
||||
.replaceAll('@(case)', locale.languageCode);
|
||||
}).join('\n ');
|
||||
}).where((String switchClause) => switchClause != null);
|
||||
|
||||
@ -314,12 +320,67 @@ String _generateLookupByLanguageCode(AppResourceBundleCollection allBundles, Str
|
||||
.replaceAll('@(switchClauses)', switchClauses.join('\n '));
|
||||
}
|
||||
|
||||
String _generateLookupBody(AppResourceBundleCollection allBundles, String className) {
|
||||
String _generateLookupBody(
|
||||
AppResourceBundleCollection allBundles,
|
||||
String className,
|
||||
bool useDeferredLoading,
|
||||
String fileName,
|
||||
) {
|
||||
final String Function(LocaleInfo) generateSwitchClauseTemplate = (LocaleInfo locale) {
|
||||
return (useDeferredLoading ?
|
||||
switchClauseDeferredLoadingTemplate : switchClauseTemplate)
|
||||
.replaceAll('@(localeClass)', '$className${locale.camelCase()}')
|
||||
.replaceAll('@(appClass)', className)
|
||||
.replaceAll('@(library)', '${fileName}_${locale.languageCode}');
|
||||
};
|
||||
return lookupBodyTemplate
|
||||
.replaceAll('@(lookupAllCodesSpecified)', _generateLookupByAllCodes(allBundles, className))
|
||||
.replaceAll('@(lookupScriptCodeSpecified)', _generateLookupByScriptCode(allBundles, className))
|
||||
.replaceAll('@(lookupCountryCodeSpecified)', _generateLookupByCountryCode(allBundles, className))
|
||||
.replaceAll('@(lookupLanguageCodeSpecified)', _generateLookupByLanguageCode(allBundles, className));
|
||||
.replaceAll('@(lookupAllCodesSpecified)', _generateLookupByAllCodes(
|
||||
allBundles,
|
||||
generateSwitchClauseTemplate,
|
||||
))
|
||||
.replaceAll('@(lookupScriptCodeSpecified)', _generateLookupByScriptCode(
|
||||
allBundles,
|
||||
generateSwitchClauseTemplate,
|
||||
))
|
||||
.replaceAll('@(lookupCountryCodeSpecified)', _generateLookupByCountryCode(
|
||||
allBundles,
|
||||
generateSwitchClauseTemplate,
|
||||
))
|
||||
.replaceAll('@(lookupLanguageCodeSpecified)', _generateLookupByLanguageCode(
|
||||
allBundles,
|
||||
generateSwitchClauseTemplate,
|
||||
));
|
||||
}
|
||||
|
||||
String _generateDelegateClass({
|
||||
AppResourceBundleCollection allBundles,
|
||||
String className,
|
||||
Set<String> supportedLanguageCodes,
|
||||
bool useDeferredLoading,
|
||||
String fileName,
|
||||
}) {
|
||||
|
||||
final String lookupBody = _generateLookupBody(
|
||||
allBundles,
|
||||
className,
|
||||
useDeferredLoading,
|
||||
fileName,
|
||||
);
|
||||
final String loadBody = (
|
||||
useDeferredLoading ? loadBodyDeferredLoadingTemplate : loadBodyTemplate
|
||||
)
|
||||
.replaceAll('@(class)', className)
|
||||
.replaceAll('@(lookupName)', '_lookup$className');
|
||||
final String lookupFunction = (useDeferredLoading ?
|
||||
lookupFunctionDeferredLoadingTemplate : lookupFunctionTemplate)
|
||||
.replaceAll('@(class)', className)
|
||||
.replaceAll('@(lookupName)', '_lookup$className')
|
||||
.replaceAll('@(lookupBody)', lookupBody);
|
||||
return delegateClassTemplate
|
||||
.replaceAll('@(class)', className)
|
||||
.replaceAll('@(loadBody)', loadBody)
|
||||
.replaceAll('@(supportedLanguageCodes)', supportedLanguageCodes.join(', '))
|
||||
.replaceAll('@(lookupFunction)', lookupFunction);
|
||||
}
|
||||
|
||||
class LocalizationsGenerator {
|
||||
@ -397,6 +458,23 @@ class LocalizationsGenerator {
|
||||
|
||||
final Map<LocaleInfo, List<String>> _unimplementedMessages = <LocaleInfo, List<String>>{};
|
||||
|
||||
/// Whether to generate the Dart localization file with locales imported as
|
||||
/// deferred, allowing for lazy loading of each locale in Flutter web.
|
||||
///
|
||||
/// This can reduce a web app’s initial startup time by decreasing the size of
|
||||
/// the JavaScript bundle. When [_useDeferredLoading] is set to true, the
|
||||
/// messages for a particular locale are only downloaded and loaded by the
|
||||
/// Flutter app as they are needed. For projects with a lot of different
|
||||
/// locales and many localization strings, it can be an performance
|
||||
/// improvement to have deferred loading. For projects with a small number of
|
||||
/// locales, the difference is negligible, and might slow down the start up
|
||||
/// compared to bundling the localizations with the rest of the application.
|
||||
///
|
||||
/// Note that this flag does not affect other platforms such as mobile or
|
||||
/// desktop.
|
||||
bool get useDeferredLoading => _useDeferredLoading;
|
||||
bool _useDeferredLoading;
|
||||
|
||||
/// Initializes [l10nDirectory], [templateArbFile], [outputFile] and [className].
|
||||
///
|
||||
/// Throws an [L10nException] when a provided configuration is not allowed
|
||||
@ -412,12 +490,14 @@ class LocalizationsGenerator {
|
||||
String preferredSupportedLocaleString,
|
||||
String headerString,
|
||||
String headerFile,
|
||||
bool useDeferredLoading = false,
|
||||
}) {
|
||||
setL10nDirectory(l10nDirectoryPath);
|
||||
setTemplateArbFile(templateArbFileName);
|
||||
setOutputFile(outputFileString);
|
||||
setPreferredSupportedLocales(preferredSupportedLocaleString);
|
||||
_setHeader(headerString, headerFile);
|
||||
_setUseDeferredLoading(useDeferredLoading);
|
||||
className = classNameString;
|
||||
}
|
||||
|
||||
@ -550,6 +630,13 @@ class LocalizationsGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
void _setUseDeferredLoading(bool useDeferredLoading) {
|
||||
if (useDeferredLoading == null) {
|
||||
throw L10nException('useDeferredLoading argument cannot be null.');
|
||||
}
|
||||
_useDeferredLoading = useDeferredLoading;
|
||||
}
|
||||
|
||||
static bool _isValidGetterAndMethodName(String name) {
|
||||
// Public Dart method name must not start with an underscore
|
||||
if (name[0] == '_')
|
||||
@ -746,13 +833,26 @@ class LocalizationsGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
final Iterable<String> localeImports = supportedLocales
|
||||
final List<String> sortedClassImports = supportedLocales
|
||||
.where((LocaleInfo locale) => isBaseClassLocale(locale, locale.languageCode))
|
||||
.map((LocaleInfo locale) {
|
||||
return "import '${fileName}_${locale.toString()}.dart';";
|
||||
});
|
||||
final String library = '${fileName}_${locale.toString()}';
|
||||
if (useDeferredLoading) {
|
||||
return "import '$library.dart' deferred as $library;";
|
||||
} else {
|
||||
return "import '$library.dart';";
|
||||
}
|
||||
})
|
||||
.toList()
|
||||
..sort();
|
||||
|
||||
final String lookupBody = _generateLookupBody(_allBundles, className);
|
||||
final String delegateClass = _generateDelegateClass(
|
||||
allBundles: _allBundles,
|
||||
className: className,
|
||||
supportedLanguageCodes: supportedLanguageCodes,
|
||||
useDeferredLoading: useDeferredLoading,
|
||||
fileName: fileName,
|
||||
);
|
||||
|
||||
return fileTemplate
|
||||
.replaceAll('@(header)', header)
|
||||
@ -761,9 +861,8 @@ class LocalizationsGenerator {
|
||||
.replaceAll('@(importFile)', '$directory/$outputFileName')
|
||||
.replaceAll('@(supportedLocales)', supportedLocalesCode.join(',\n '))
|
||||
.replaceAll('@(supportedLanguageCodes)', supportedLanguageCodes.join(', '))
|
||||
.replaceAll('@(messageClassImports)', localeImports.join('\n'))
|
||||
.replaceAll('@(lookupName)', '_lookup$className')
|
||||
.replaceAll('@(lookupBody)', lookupBody);
|
||||
.replaceAll('@(messageClassImports)', sortedClassImports.join('\n'))
|
||||
.replaceAll('@(delegateClass)', delegateClass);
|
||||
}
|
||||
|
||||
void writeOutputFile() {
|
||||
|
@ -6,6 +6,7 @@ const String fileTemplate = '''
|
||||
@(header)
|
||||
import 'dart:async';
|
||||
|
||||
// ignore: unused_import
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
@ -104,26 +105,7 @@ abstract class @(class) {
|
||||
|
||||
@(methods)}
|
||||
|
||||
class _@(class)Delegate extends LocalizationsDelegate<@(class)> {
|
||||
const _@(class)Delegate();
|
||||
|
||||
@override
|
||||
Future<@(class)> load(Locale locale) {
|
||||
return SynchronousFuture<@(class)>(@(lookupName)(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) => <String>[@(supportedLanguageCodes)].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_@(class)Delegate old) => false;
|
||||
}
|
||||
|
||||
@(class) @(lookupName)(Locale locale) {
|
||||
@(lookupBody)
|
||||
assert(false, '@(class).delegate failed to load unsupported locale "\$locale"');
|
||||
return null;
|
||||
}
|
||||
@(delegateClass)
|
||||
''';
|
||||
|
||||
const String numberFormatTemplate = '''
|
||||
@ -205,14 +187,67 @@ const String baseClassMethodTemplate = '''
|
||||
String @(name)(@(parameters));
|
||||
''';
|
||||
|
||||
// DELEGATE CLASS TEMPLATES
|
||||
|
||||
const String delegateClassTemplate = '''
|
||||
class _@(class)Delegate extends LocalizationsDelegate<@(class)> {
|
||||
const _@(class)Delegate();
|
||||
|
||||
@override
|
||||
Future<@(class)> load(Locale locale) {
|
||||
@(loadBody)
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) => <String>[@(supportedLanguageCodes)].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_@(class)Delegate old) => false;
|
||||
}
|
||||
|
||||
@(lookupFunction)''';
|
||||
|
||||
const String loadBodyTemplate = '''return SynchronousFuture<@(class)>(@(lookupName)(locale));''';
|
||||
|
||||
const String loadBodyDeferredLoadingTemplate = '''return @(lookupName)(locale);''';
|
||||
|
||||
// DELEGATE LOOKUP TEMPLATES
|
||||
|
||||
const String lookupFunctionTemplate = '''
|
||||
@(class) @(lookupName)(Locale locale) {
|
||||
@(lookupBody)
|
||||
assert(false, '@(class).delegate failed to load unsupported locale "\$locale"');
|
||||
return null;
|
||||
}''';
|
||||
|
||||
const String lookupFunctionDeferredLoadingTemplate = '''
|
||||
/// Lazy load the library for web, on other platforms we return the
|
||||
/// localizations synchronously.
|
||||
Future<@(class)> _loadLibraryForWeb(
|
||||
Future<dynamic> Function() loadLibrary,
|
||||
@(class) Function() localizationClosure,
|
||||
) {
|
||||
if (kIsWeb) {
|
||||
return loadLibrary().then((dynamic _) => localizationClosure());
|
||||
} else {
|
||||
return SynchronousFuture<@(class)>(localizationClosure());
|
||||
}
|
||||
}
|
||||
|
||||
Future<@(class)> @(lookupName)(Locale locale) {
|
||||
@(lookupBody)
|
||||
assert(false, '@(class).delegate failed to load unsupported locale "\$locale"');
|
||||
return null;
|
||||
}''';
|
||||
|
||||
const String lookupBodyTemplate = '''@(lookupAllCodesSpecified)
|
||||
@(lookupScriptCodeSpecified)
|
||||
@(lookupCountryCodeSpecified)
|
||||
@(lookupLanguageCodeSpecified)''';
|
||||
|
||||
const String switchClauseTemplate = '''case '@(case)': return @(class)();''';
|
||||
const String switchClauseTemplate = '''case '@(case)': return @(localeClass)();''';
|
||||
|
||||
const String switchClauseDeferredLoadingTemplate = '''case '@(case)': return _loadLibraryForWeb(@(library).loadLibrary, () => @(library).@(localeClass)());''';
|
||||
|
||||
const String nestedSwitchTemplate = '''case '@(languageCode)': {
|
||||
switch (locale.@(code)) {
|
||||
|
@ -380,6 +380,28 @@ void main() {
|
||||
fail('Setting headerFile that does not exist should fail');
|
||||
});
|
||||
|
||||
test('setting useDefferedLoading to null should fail', () {
|
||||
_standardFlutterDirectoryL10nSetup(fs);
|
||||
|
||||
LocalizationsGenerator generator;
|
||||
try {
|
||||
generator = LocalizationsGenerator(fs);
|
||||
generator.initialize(
|
||||
l10nDirectoryPath: defaultArbPathString,
|
||||
templateArbFileName: defaultTemplateArbFileName,
|
||||
outputFileString: defaultOutputFileString,
|
||||
classNameString: defaultClassNameString,
|
||||
headerString: '/// Sample header',
|
||||
useDeferredLoading: null,
|
||||
);
|
||||
} on L10nException catch (e) {
|
||||
expect(e.message, contains('useDeferredLoading argument cannot be null.'));
|
||||
return;
|
||||
}
|
||||
|
||||
fail('Setting useDefferedLoading to null should fail');
|
||||
});
|
||||
|
||||
group('loadResources', () {
|
||||
test('correctly initializes supportedLocales and supportedLanguageCodes properties', () {
|
||||
_standardFlutterDirectoryL10nSetup(fs);
|
||||
@ -792,6 +814,67 @@ void main() {
|
||||
expect(englishLocalizationsFile, contains('class AppLocalizationsEn extends AppLocalizations'));
|
||||
});
|
||||
|
||||
test('language imports are sorted when preferredSupportedLocaleString is given', () {
|
||||
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true)
|
||||
..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString)
|
||||
..childFile('app_zh.arb').writeAsStringSync(singleZhMessageArbFileString)
|
||||
..childFile('app_es.arb').writeAsStringSync(singleEsMessageArbFileString);
|
||||
|
||||
const String preferredSupportedLocaleString = '["zh"]';
|
||||
final LocalizationsGenerator generator = LocalizationsGenerator(fs);
|
||||
try {
|
||||
generator.initialize(
|
||||
l10nDirectoryPath: defaultArbPathString,
|
||||
templateArbFileName: defaultTemplateArbFileName,
|
||||
outputFileString: defaultOutputFileString,
|
||||
classNameString: defaultClassNameString,
|
||||
preferredSupportedLocaleString: preferredSupportedLocaleString,
|
||||
);
|
||||
generator.loadResources();
|
||||
generator.writeOutputFile();
|
||||
} on Exception catch (e) {
|
||||
fail('Generating output files should not fail: $e');
|
||||
}
|
||||
|
||||
final String localizationsFile = fs.file(
|
||||
path.join('lib', 'l10n', defaultOutputFileString),
|
||||
).readAsStringSync();
|
||||
expect(localizationsFile, contains(
|
||||
'''
|
||||
import '${defaultOutputFileString}_en.dart';
|
||||
import '${defaultOutputFileString}_es.dart';
|
||||
import '${defaultOutputFileString}_zh.dart';
|
||||
'''));
|
||||
});
|
||||
|
||||
test('imports are deferred when useDeferredImports are set', () {
|
||||
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true)
|
||||
..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString);
|
||||
|
||||
final LocalizationsGenerator generator = LocalizationsGenerator(fs);
|
||||
try {
|
||||
generator.initialize(
|
||||
l10nDirectoryPath: defaultArbPathString,
|
||||
templateArbFileName: defaultTemplateArbFileName,
|
||||
outputFileString: defaultOutputFileString,
|
||||
classNameString: defaultClassNameString,
|
||||
useDeferredLoading: true,
|
||||
);
|
||||
generator.loadResources();
|
||||
generator.writeOutputFile();
|
||||
} on Exception catch (e) {
|
||||
fail('Generating output files should not fail: $e');
|
||||
}
|
||||
|
||||
final String localizationsFile = fs.file(
|
||||
path.join('lib', 'l10n', defaultOutputFileString),
|
||||
).readAsStringSync();
|
||||
expect(localizationsFile, contains(
|
||||
'''
|
||||
import '${defaultOutputFileString}_en.dart' deferred as ${defaultOutputFileString}_en;
|
||||
'''));
|
||||
});
|
||||
|
||||
group('DateTime tests', () {
|
||||
test('throws an exception when improperly formatted date is passed in', () {
|
||||
const String singleDateMessageArbFileString = '''
|
||||
|
@ -48,7 +48,7 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
test('generated l10n classes produce expected localized strings', () async {
|
||||
void setUpAndRunGenL10n({List<String> args}) {
|
||||
// Get the intl packages before running gen_l10n.
|
||||
final String flutterBin = globals.platform.isWindows ? 'flutter.bat' : 'flutter';
|
||||
final String flutterPath = globals.fs.path.join(getFlutterRoot(), 'bin', flutterBin);
|
||||
@ -58,8 +58,10 @@ void main() {
|
||||
final String genL10nPath = globals.fs.path.join(getFlutterRoot(), 'dev', 'tools', 'localization', 'bin', 'gen_l10n.dart');
|
||||
final String dartBin = globals.platform.isWindows ? 'dart.exe' : 'dart';
|
||||
final String dartPath = globals.fs.path.join(getFlutterRoot(), 'bin', 'cache', 'dart-sdk', 'bin', dartBin);
|
||||
runCommand(<String>[dartPath, genL10nPath]);
|
||||
runCommand(<String>[dartPath, genL10nPath, args?.join(' ')]);
|
||||
}
|
||||
|
||||
Future<StringBuffer> runApp() async {
|
||||
// Run the app defined in GenL10nProject.main and wait for it to
|
||||
// send '#l10n END' to its stdout.
|
||||
final Completer<void> l10nEnd = Completer<void>();
|
||||
@ -75,6 +77,10 @@ void main() {
|
||||
await _flutter.run();
|
||||
await l10nEnd.future;
|
||||
await subscription.cancel();
|
||||
return stdout;
|
||||
}
|
||||
|
||||
void expectOutput(StringBuffer stdout) {
|
||||
expect(stdout.toString(),
|
||||
'#l10n 0 (--- supportedLocales tests ---)\n'
|
||||
'#l10n 1 (supportedLocales[0]: languageCode: en, countryCode: null, scriptCode: null)\n'
|
||||
@ -133,5 +139,17 @@ void main() {
|
||||
'#l10n 54 (Flutter is "amazing", times 2!)\n'
|
||||
'#l10n END\n'
|
||||
);
|
||||
}
|
||||
|
||||
test('generated l10n classes produce expected localized strings', () async {
|
||||
setUpAndRunGenL10n();
|
||||
final StringBuffer stdout = await runApp();
|
||||
expectOutput(stdout);
|
||||
});
|
||||
|
||||
test('generated l10n classes produce expected localized strings with deferred loading', () async {
|
||||
setUpAndRunGenL10n(args: <String>['--use-deferred-loading']);
|
||||
final StringBuffer stdout = await runApp();
|
||||
expectOutput(stdout);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user