diff --git a/lib/builder.dart b/lib/builder.dart index a79f2acc..e513f2da 100644 --- a/lib/builder.dart +++ b/lib/builder.dart @@ -23,24 +23,27 @@ class I18nBuilder implements Builder { bool _generated = false; - String get inputFilePattern => options.config['input_file_pattern'] ?? defaultInputFilePattern; - String get outputFilePattern => options.config['output_file_pattern'] ?? defaultOutputFilePattern; + String get inputFilePattern => + options.config['input_file_pattern'] ?? defaultInputFilePattern; + String get outputFilePattern => + options.config['output_file_pattern'] ?? defaultOutputFilePattern; @override FutureOr build(BuildStep buildStep) async { - final String baseLocale = Utils.normalize(options.config['base_locale'] ?? defaultBaseLocale); + final String baseLocale = + Utils.normalize(options.config['base_locale'] ?? defaultBaseLocale); final String inputDirectory = options.config['input_directory']; final String outputDirectory = options.config['output_directory']; - final String translateVar = options.config['output_translate_var'] ?? defaultTranslateVar; + final String translateVar = + options.config['output_translate_var'] ?? defaultTranslateVar; final String keyCase = options.config['key_case']; final List maps = options.config['maps']?.cast() ?? []; - if (inputDirectory != null && !buildStep.inputId.path.contains(inputDirectory)) - return; + if (inputDirectory != null && + !buildStep.inputId.path.contains(inputDirectory)) return; // only generate once - if (_generated) - return; + if (_generated) return; _generated = true; @@ -53,7 +56,8 @@ class I18nBuilder implements Builder { : Glob('**$inputFilePattern'); await buildStep.findAssets(findAssetsPattern).forEach((assetId) { - final fileNameNoExtension = assetId.pathSegments.last.replaceAll(inputFilePattern, ''); + final fileNameNoExtension = + assetId.pathSegments.last.replaceAll(inputFilePattern, ''); final match = Utils.localeRegex.firstMatch(fileNameNoExtension); if (match != null) { @@ -77,12 +81,11 @@ class I18nBuilder implements Builder { // build config which applies to all locales final config = I18nConfig( - baseName: baseName ?? defaultBaseName, - baseLocale: baseLocale, - maps: maps, - keyCase: keyCase, - translateVariable: translateVar - ); + baseName: baseName ?? defaultBaseName, + baseLocale: baseLocale, + maps: maps, + keyCase: keyCase, + translateVariable: translateVar); // map each assetId to I18nData final localesWithData = Map(); @@ -96,23 +99,24 @@ class I18nBuilder implements Builder { // generate final String output = generate( - config: config, - translations: localesWithData.values.toList()..sort((a, b) => a.locale.compareTo(b.locale)) - ); + config: config, + translations: localesWithData.values.toList() + ..sort((a, b) => a.locale.compareTo(b.locale))); // write only to main locale - final AssetId baseId = localesWithData.entries - .firstWhere((element) => element.value.base) - .key; + final AssetId baseId = + localesWithData.entries.firstWhere((element) => element.value.base).key; - final finalOutputDirectory = outputDirectory ?? (baseId.pathSegments..removeLast()).join('/'); - final String outFilePath = '$finalOutputDirectory/$baseName$outputFilePattern'; + final finalOutputDirectory = + outputDirectory ?? (baseId.pathSegments..removeLast()).join('/'); + final String outFilePath = + '$finalOutputDirectory/$baseName$outputFilePattern'; File(outFilePath).writeAsStringSync(output); } @override get buildExtensions => { - inputFilePattern: [outputFilePattern], - }; + inputFilePattern: [outputFilePattern], + }; } diff --git a/lib/fast_i18n.dart b/lib/fast_i18n.dart index d47df1a3..30170726 100644 --- a/lib/fast_i18n.dart +++ b/lib/fast_i18n.dart @@ -13,23 +13,27 @@ class FastI18n { /// Returns the candidate (or part of it) if it is supported. /// Fallbacks to base locale. - static String selectLocale(String candidate, List supported, String baseLocale) { + static String selectLocale( + String candidate, List supported, String baseLocale) { // normalize candidate = Utils.normalize(candidate); // 1st try: match exactly - String selected = supported.firstWhere((element) => element == candidate, orElse: () => null); + String selected = supported.firstWhere((element) => element == candidate, + orElse: () => null); if (selected != null) return selected; // 2nd try: match the first part (language) List deviceLocaleParts = candidate.split(_localePartsDelimiter); - selected = - supported.firstWhere((element) => element == deviceLocaleParts.first, orElse: () => null); + selected = supported.firstWhere( + (element) => element == deviceLocaleParts.first, + orElse: () => null); if (selected != null) return selected; // 3rd try: match the second part (region) - selected = - supported.firstWhere((element) => element == deviceLocaleParts.last, orElse: () => null); + selected = supported.firstWhere( + (element) => element == deviceLocaleParts.last, + orElse: () => null); if (selected != null) return selected; // fallback: default locale @@ -38,7 +42,8 @@ class FastI18n { /// Converts the passed locales from [String] to [Locale]. /// Puts the [baseLocale] into the the beginning of the list. - static List convertToLocales(List locales, String baseLocale) { + static List convertToLocales( + List locales, String baseLocale) { final rawSupportedLocales = [ baseLocale, ...locales.where((locale) => locale != baseLocale), @@ -46,10 +51,13 @@ class FastI18n { final supportedLocales = rawSupportedLocales.map((rawLocale) { if (rawLocale.contains(_localePartsDelimiter)) { - final localeParts = - rawLocale.split(_localePartsDelimiter).where((part) => part.isNotEmpty).toList(); + final localeParts = rawLocale + .split(_localePartsDelimiter) + .where((part) => part.isNotEmpty) + .toList(); if (localeParts.length == 2) { - return Locale.fromSubtags(languageCode: localeParts[0], countryCode: localeParts[1]); + return Locale.fromSubtags( + languageCode: localeParts[0], countryCode: localeParts[1]); } else if (localeParts.length == 3) { return Locale.fromSubtags( languageCode: localeParts[0], diff --git a/lib/src/generator.dart b/lib/src/generator.dart index c2716129..e1ae2227 100644 --- a/lib/src/generator.dart +++ b/lib/src/generator.dart @@ -36,7 +36,8 @@ String generate({I18nConfig config, List translations}) { /// generates the header of the .g.dart file /// contains the t function, LocaleSettings class and some global variables -void _generateHeader(StringBuffer buffer, I18nConfig config, List allLocales) { +void _generateHeader( + StringBuffer buffer, I18nConfig config, List allLocales) { // identifiers const String mapVar = '_strings'; const String baseLocaleVar = '_baseLocale'; @@ -79,7 +80,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln('///'); buffer.writeln( '/// Widgets using this method will not be updated when locale changes during runtime.'); - buffer.writeln('/// Translation happens during initialization of the widget (call of t).'); + buffer.writeln( + '/// Translation happens during initialization of the widget (call of t).'); buffer.writeln('///'); buffer.writeln('/// Usage:'); buffer.writeln('/// String translated = t.someKey.anotherKey;'); @@ -89,7 +91,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln(); buffer.writeln('/// Method B: Advanced'); buffer.writeln('///'); - buffer.writeln('/// All widgets using this method will trigger a rebuild when locale changes.'); + buffer.writeln( + '/// All widgets using this method will trigger a rebuild when locale changes.'); buffer.writeln( '/// Use this if you have e.g. a settings page where the user can select the locale during runtime.'); buffer.writeln('///'); @@ -100,8 +103,10 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln('/// );'); buffer.writeln('///'); buffer.writeln('/// Step 2:'); - buffer.writeln('/// final t = $translationsClass.of(context); // get t variable'); - buffer.writeln('/// String translated = t.someKey.anotherKey; // use t variable'); + buffer.writeln( + '/// final t = $translationsClass.of(context); // get t variable'); + buffer.writeln( + '/// String translated = t.someKey.anotherKey; // use t variable'); buffer.writeln('class $translationsClass {'); buffer.writeln('\t$translationsClass._(); // no constructor'); buffer.writeln(); @@ -133,7 +138,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln('\t\t$translateVar = $mapVar[$localeVar];'); buffer.writeln(); buffer.writeln('\t\tif ($translationProviderKey.currentState != null) {'); - buffer.writeln('\t\t\t$translationProviderKey.currentState.setLocale($localeVar);'); + buffer.writeln( + '\t\t\t$translationProviderKey.currentState.setLocale($localeVar);'); buffer.writeln('\t\t}'); buffer.writeln(); buffer.writeln('\t\treturn $localeVar;'); @@ -160,7 +166,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln(); buffer.writeln('\t/// Get supported locales with base locale sorted first.'); buffer.writeln('\tstatic List get supportedLocales {'); - buffer.writeln('\t\treturn FastI18n.convertToLocales($mapVar.keys.toList(), $baseLocaleVar);'); + buffer.writeln( + '\t\treturn FastI18n.convertToLocales($mapVar.keys.toList(), $baseLocaleVar);'); buffer.writeln('\t}'); buffer.writeln('}'); @@ -182,7 +189,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln('}'); buffer.writeln(); - buffer.writeln('class $translationProviderStateClass extends State<$translationProviderClass> {'); + buffer.writeln( + 'class $translationProviderStateClass extends State<$translationProviderClass> {'); buffer.writeln('\tString locale = $localeVar;'); buffer.writeln(); buffer.writeln('\tvoid setLocale(String newLocale) {'); @@ -204,7 +212,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL buffer.writeln(); buffer.writeln('class $inheritedClass extends InheritedWidget {'); buffer.writeln('\tfinal $baseClassName translations;'); - buffer.writeln('\t$inheritedClass({this.translations, Widget child}) : super(child: child);'); + buffer.writeln( + '\t$inheritedClass({this.translations, Widget child}) : super(child: child);'); buffer.writeln(); buffer.writeln('\t@override'); buffer.writeln('\tbool updateShouldNotify($inheritedClass oldWidget) {'); @@ -216,7 +225,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List allL /// generates all classes of one locale /// all non-default locales has a postfix of their locale code /// e.g. Strings, StringsDe, StringsFr -void _generateLocale(StringBuffer buffer, I18nConfig config, I18nData localeData) { +void _generateLocale( + StringBuffer buffer, I18nConfig config, I18nData localeData) { Queue queue = Queue(); queue.add(ClassTask( @@ -250,7 +260,8 @@ void _generateClass( String className, Map currMembers, ) { - String finalClassName = base ? className : className + locale.capitalize().replaceAll('-', ''); + String finalClassName = + base ? className : className + locale.capitalize().replaceAll('-', ''); buffer.writeln(); @@ -276,7 +287,8 @@ void _generateClass( if (value.params.isEmpty) { buffer.writeln('String $key = \'${value.content}\';'); } else { - buffer.writeln('String $key${_toParameterList(value.params)} => \'${value.content}\';'); + buffer.writeln( + 'String $key${_toParameterList(value.params)} => \'${value.content}\';'); } } else if (value is ListNode) { String type = value.plainStrings ? 'String' : 'dynamic'; @@ -288,15 +300,18 @@ void _generateClass( // inline map String type = value.plainStrings ? 'String' : 'dynamic'; buffer.write('Map get $key => '); - _generateMap(base, locale, buffer, queue, childClassName, value.entries, 0); + _generateMap( + base, locale, buffer, queue, childClassName, value.entries, 0); } else { // generate a class later on queue.add(ClassTask(childClassName, value.entries)); - String finalChildClassName = - base ? childClassName : childClassName + locale.capitalize().replaceAll('-', ''); + String finalChildClassName = base + ? childClassName + : childClassName + locale.capitalize().replaceAll('-', ''); - buffer.writeln('$finalChildClassName get $key => $finalChildClassName._instance;'); + buffer.writeln( + '$finalChildClassName get $key => $finalChildClassName._instance;'); } } }); @@ -323,23 +338,27 @@ void _generateMap( if (value.params.isEmpty) { buffer.writeln('\'$key\': \'${value.content}\','); } else { - buffer.writeln('\'$key\': ${_toParameterList(value.params)} => \'${value.content}\','); + buffer.writeln( + '\'$key\': ${_toParameterList(value.params)} => \'${value.content}\','); } } else if (value is ListNode) { buffer.write('\'$key\': '); - _generateList(base, locale, buffer, queue, className, value.entries, depth + 1); + _generateList( + base, locale, buffer, queue, className, value.entries, depth + 1); } else if (value is ObjectNode) { String childClassName = className + key.capitalize(); if (value.mapMode) { // inline map buffer.write('\'$key\': '); - _generateMap(base, locale, buffer, queue, childClassName, value.entries, depth + 1); + _generateMap(base, locale, buffer, queue, childClassName, value.entries, + depth + 1); } else { // generate a class later on queue.add(ClassTask(childClassName, value.entries)); - String finalChildClassName = - base ? childClassName : childClassName + locale.capitalize().replaceAll('-', ''); + String finalChildClassName = base + ? childClassName + : childClassName + locale.capitalize().replaceAll('-', ''); buffer.writeln('\'$key\': $finalChildClassName._instance,'); } @@ -376,10 +395,12 @@ void _generateList( if (value.params.isEmpty) { buffer.writeln('\'${value.content}\','); } else { - buffer.writeln('${_toParameterList(value.params)} => \'${value.content}\','); + buffer.writeln( + '${_toParameterList(value.params)} => \'${value.content}\','); } } else if (value is ListNode) { - _generateList(base, locale, buffer, queue, className, value.entries, depth + 1); + _generateList( + base, locale, buffer, queue, className, value.entries, depth + 1); } else if (value is ObjectNode) { String childClassName = className + depth.toString() + 'i' + i.toString(); queue.add(ClassTask(childClassName, value.entries)); diff --git a/lib/src/model.dart b/lib/src/model.dart index 95bcfcaa..06d2eeeb 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -8,7 +8,13 @@ class I18nConfig { final String keyCase; final String translateVariable; - I18nConfig({this.baseName, this.baseLocale, this.maps, this.keyCase, this.translateVariable}); + I18nConfig({ + this.baseName, + this.baseLocale, + this.maps, + this.keyCase, + this.translateVariable, + }); @override String toString() => '$baseLocale, maps: $maps'; @@ -80,8 +86,7 @@ class TextNode extends Value { /// 'my name is $name and I am $age years old' => ['name', 'age'] /// 'my name is ${name} and I am ${age} years old' => ['name', 'age'] List _findArguments(String content) { - return Utils - .argumentsRegex + return Utils.argumentsRegex .allMatches(content) .map((e) => e.group(2)) .toList(); diff --git a/lib/src/parser_json.dart b/lib/src/parser_json.dart index 5c97d455..b42a6192 100644 --- a/lib/src/parser_json.dart +++ b/lib/src/parser_json.dart @@ -4,7 +4,8 @@ import 'package:fast_i18n/src/model.dart'; /// parses a json of one locale /// returns an I18nData object -I18nData parseJSON(I18nConfig config, String baseName, String locale, String content) { +I18nData parseJSON( + I18nConfig config, String baseName, String locale, String content) { Map map = json.decode(content); Map destination = Map(); _parseJSONObject(config.maps, map, destination, []); @@ -12,11 +13,16 @@ I18nData parseJSON(I18nConfig config, String baseName, String locale, String con return I18nData( base: config.baseLocale == locale, locale: locale, - root: ObjectNode(destination, false) + root: ObjectNode(destination, false), ); } -void _parseJSONObject(List maps, Map curr, Map destination, List stack) { +void _parseJSONObject( + List maps, + Map curr, + Map destination, + List stack, +) { curr.forEach((key, value) { if (value is String) { // key: 'value' @@ -38,7 +44,12 @@ void _parseJSONObject(List maps, Map curr, Map maps, List curr, List destination, List stack) { +void _parseJSONArray( + List maps, + List curr, + List destination, + List stack, +) { for (dynamic value in curr) { if (value is String) { // key: 'value' diff --git a/lib/utils.dart b/lib/utils.dart index 2c6dfbb8..ef41da11 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -3,7 +3,8 @@ class Utils { /// finds the parts of the locale /// must start with an underscore - static RegExp localeRegex = RegExp(r'^((\w+)_)?([a-z]{2})([-_]([a-zA-Z]{2}))?$'); + static RegExp localeRegex = + RegExp(r'^((\w+)_)?([a-z]{2})([-_]([a-zA-Z]{2}))?$'); /// returns the locale with the following syntax: /// - all lowercase diff --git a/test/fast_i18n_test.dart b/test/fast_i18n_test.dart index 5bbed8d4..7a641af1 100644 --- a/test/fast_i18n_test.dart +++ b/test/fast_i18n_test.dart @@ -41,7 +41,8 @@ void testSelectLocale() { final localesAsStrings = ['en-us', 'ru-RU', 'de-de', 'zh-Hans-CN']; final baseLocaleString = 'ru-RU'; - final locales = FastI18n.convertToLocales(localesAsStrings, baseLocaleString); + final locales = + FastI18n.convertToLocales(localesAsStrings, baseLocaleString); expect(locales.length, 4); expect(locales[0].toLanguageTag(), 'ru-RU'); @@ -54,7 +55,8 @@ void testSelectLocale() { final localesAsStrings = ['en', 'ru-RU', 'de']; final baseLocaleString = 'de'; - final locales = FastI18n.convertToLocales(localesAsStrings, baseLocaleString); + final locales = + FastI18n.convertToLocales(localesAsStrings, baseLocaleString); expect(locales.length, 3); expect(locales[0].toLanguageTag(), 'de'); @@ -62,7 +64,9 @@ void testSelectLocale() { expect(locales[2].toLanguageTag(), 'ru-RU'); }); - test("throws Exception if a locale with '-' delimiter doesn't have 2+ non-empty parts", () { + test( + "throws Exception if a locale with '-' delimiter doesn't have 2+ non-empty parts", + () { final localesAsStrings = ['ru-', 'de']; final baseLocaleString = 'de'; @@ -84,7 +88,8 @@ void testSelectLocale() { ); }); - test('throws AssertionError if primary language subtag is not present in the provided locales', + test( + 'throws AssertionError if primary language subtag is not present in the provided locales', () { final localesAsStrings = ['en-us', '']; final baseLocaleString = 'en-US';