diff --git a/CHANGELOG.md b/CHANGELOG.md index 45b4e9d..1591074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to the "flutter-intl" extension will be documented in this f The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 2.8.17 - Unreleased + +- Fix language overrides ([#19](https://github.com/localizely/intl_utils/issues/19)) + ## 2.8.16 - 2026-06-11 - Update `analyzer` dependency @@ -86,7 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 2.8.0 - 2022-11-15 -- Update `analyzer` and `lints` dependencies +- Update `analyzer` and `lints` dependencies ([#91](https://github.com/localizely/intl_utils/issues/91)) ## 2.7.0 - 2022-07-07 diff --git a/bin/generate.dart b/bin/generate.dart index 53a2c84..aac467b 100644 --- a/bin/generate.dart +++ b/bin/generate.dart @@ -1,4 +1,4 @@ -library intl_utils; +library; import 'package:intl_utils/intl_utils.dart'; import 'package:intl_utils/src/generator/generator_exception.dart'; diff --git a/bin/localizely_download.dart b/bin/localizely_download.dart index 4b6dcbb..0a36d29 100644 --- a/bin/localizely_download.dart +++ b/bin/localizely_download.dart @@ -1,4 +1,4 @@ -library intl_utils; +library; import 'dart:io'; diff --git a/bin/localizely_upload_main.dart b/bin/localizely_upload_main.dart index f0055b2..289695a 100644 --- a/bin/localizely_upload_main.dart +++ b/bin/localizely_upload_main.dart @@ -1,4 +1,4 @@ -library intl_utils; +library; import 'dart:io'; diff --git a/lib/intl_utils.dart b/lib/intl_utils.dart index d77fba8..0e6909d 100644 --- a/lib/intl_utils.dart +++ b/lib/intl_utils.dart @@ -1,3 +1,3 @@ -library intl_utils; +library; export 'src/generator/generator.dart'; diff --git a/lib/src/config/credentials_config.dart b/lib/src/config/credentials_config.dart index 0530d17..31f7858 100644 --- a/lib/src/config/credentials_config.dart +++ b/lib/src/config/credentials_config.dart @@ -20,10 +20,9 @@ class CredentialsConfig { ); } - _apiToken = - credentialsYaml['api_token'] is String - ? credentialsYaml['api_token'] - : null; + _apiToken = credentialsYaml['api_token'] is String + ? credentialsYaml['api_token'] + : null; } String? get apiToken => _apiToken; diff --git a/lib/src/config/pubspec_config.dart b/lib/src/config/pubspec_config.dart index 261f799..7b5f096 100644 --- a/lib/src/config/pubspec_config.dart +++ b/lib/src/config/pubspec_config.dart @@ -32,30 +32,24 @@ class PubspecConfig { return; } - _enabled = - flutterIntlConfig['enabled'] is bool - ? flutterIntlConfig['enabled'] - : null; - _className = - flutterIntlConfig['class_name'] is String - ? flutterIntlConfig['class_name'] - : null; - _mainLocale = - flutterIntlConfig['main_locale'] is String - ? flutterIntlConfig['main_locale'] - : null; - _arbDir = - flutterIntlConfig['arb_dir'] is String - ? flutterIntlConfig['arb_dir'] - : null; - _outputDir = - flutterIntlConfig['output_dir'] is String - ? flutterIntlConfig['output_dir'] - : null; - _useDeferredLoading = - flutterIntlConfig['use_deferred_loading'] is bool - ? flutterIntlConfig['use_deferred_loading'] - : null; + _enabled = flutterIntlConfig['enabled'] is bool + ? flutterIntlConfig['enabled'] + : null; + _className = flutterIntlConfig['class_name'] is String + ? flutterIntlConfig['class_name'] + : null; + _mainLocale = flutterIntlConfig['main_locale'] is String + ? flutterIntlConfig['main_locale'] + : null; + _arbDir = flutterIntlConfig['arb_dir'] is String + ? flutterIntlConfig['arb_dir'] + : null; + _outputDir = flutterIntlConfig['output_dir'] is String + ? flutterIntlConfig['output_dir'] + : null; + _useDeferredLoading = flutterIntlConfig['use_deferred_loading'] is bool + ? flutterIntlConfig['use_deferred_loading'] + : null; _localizelyConfig = LocalizelyConfig.fromConfig( flutterIntlConfig['localizely'], ); @@ -94,50 +88,41 @@ class LocalizelyConfig { return; } - _projectId = - localizelyConfig['project_id'] is String - ? localizelyConfig['project_id'] - : null; - _branch = - localizelyConfig['branch'] is String - ? localizelyConfig['branch'] - : null; - _uploadAsReviewed = - localizelyConfig['upload_as_reviewed'] is bool - ? localizelyConfig['upload_as_reviewed'] - : null; - _uploadOverwrite = - localizelyConfig['upload_overwrite'] is bool - ? localizelyConfig['upload_overwrite'] - : null; - _uploadTagAdded = - localizelyConfig['upload_tag_added'] is yaml.YamlList - ? List.from(localizelyConfig['upload_tag_added']) - : null; - _uploadTagUpdated = - localizelyConfig['upload_tag_updated'] is yaml.YamlList - ? List.from(localizelyConfig['upload_tag_updated']) - : null; - _uploadTagRemoved = - localizelyConfig['upload_tag_removed'] is yaml.YamlList - ? List.from(localizelyConfig['upload_tag_removed']) - : null; - _downloadEmptyAs = - localizelyConfig['download_empty_as'] is String - ? localizelyConfig['download_empty_as'] - : null; + _projectId = localizelyConfig['project_id'] is String + ? localizelyConfig['project_id'] + : null; + _branch = localizelyConfig['branch'] is String + ? localizelyConfig['branch'] + : null; + _uploadAsReviewed = localizelyConfig['upload_as_reviewed'] is bool + ? localizelyConfig['upload_as_reviewed'] + : null; + _uploadOverwrite = localizelyConfig['upload_overwrite'] is bool + ? localizelyConfig['upload_overwrite'] + : null; + _uploadTagAdded = localizelyConfig['upload_tag_added'] is yaml.YamlList + ? List.from(localizelyConfig['upload_tag_added']) + : null; + _uploadTagUpdated = localizelyConfig['upload_tag_updated'] is yaml.YamlList + ? List.from(localizelyConfig['upload_tag_updated']) + : null; + _uploadTagRemoved = localizelyConfig['upload_tag_removed'] is yaml.YamlList + ? List.from(localizelyConfig['upload_tag_removed']) + : null; + _downloadEmptyAs = localizelyConfig['download_empty_as'] is String + ? localizelyConfig['download_empty_as'] + : null; _downloadIncludeTags = localizelyConfig['download_include_tags'] is yaml.YamlList - ? List.from(localizelyConfig['download_include_tags']) - : null; + ? List.from(localizelyConfig['download_include_tags']) + : null; _downloadExcludeTags = localizelyConfig['download_exclude_tags'] is yaml.YamlList - ? List.from(localizelyConfig['download_exclude_tags']) - : null; - _otaEnabled = - localizelyConfig['ota_enabled'] is bool - ? localizelyConfig['ota_enabled'] - : null; + ? List.from(localizelyConfig['download_exclude_tags']) + : null; + _otaEnabled = localizelyConfig['ota_enabled'] is bool + ? localizelyConfig['ota_enabled'] + : null; } String? get projectId => _projectId; diff --git a/lib/src/generator/generator.dart b/lib/src/generator/generator.dart index c402927..4d9d2a3 100644 --- a/lib/src/generator/generator.dart +++ b/lib/src/generator/generator.dart @@ -119,35 +119,35 @@ class Generator { var content = mainArbFile.readAsStringSync(); var decodedContent = json.decode(content) as Map; - var labels = - decodedContent.keys.where((key) => !key.startsWith('@')).map((key) { - var name = key; - var content = decodedContent[key]; - - var meta = decodedContent['@$key'] ?? {}; - var type = meta['type']; - var description = meta['description']; - var placeholders = - meta['placeholders'] != null - ? (meta['placeholders'] as Map).keys - .map( - (placeholder) => Placeholder( - key, - placeholder, - meta['placeholders'][placeholder], - ), - ) - .toList() - : null; - - return Label( - name, - content, - type: type, - description: description, - placeholders: placeholders, - ); - }).toList(); + var labels = decodedContent.keys.where((key) => !key.startsWith('@')).map(( + key, + ) { + var name = key; + var content = decodedContent[key]; + + var meta = decodedContent['@$key'] ?? {}; + var type = meta['type']; + var description = meta['description']; + var placeholders = meta['placeholders'] != null + ? (meta['placeholders'] as Map).keys + .map( + (placeholder) => Placeholder( + key, + placeholder, + meta['placeholders'][placeholder], + ), + ) + .toList() + : null; + + return Label( + name, + content, + type: type, + description: description, + placeholders: placeholders, + ); + }).toList(); return labels; } @@ -156,10 +156,10 @@ class Generator { var index = locales.indexOf(_mainLocale); return index != -1 ? [ - locales.elementAt(index), - ...locales.sublist(0, index), - ...locales.sublist(index + 1), - ] + locales.elementAt(index), + ...locales.sublist(0, index), + ...locales.sublist(index + 1), + ] : locales; } diff --git a/lib/src/generator/intl_translation_helper.dart b/lib/src/generator/intl_translation_helper.dart index c276f47..c816ee6 100644 --- a/lib/src/generator/intl_translation_helper.dart +++ b/lib/src/generator/intl_translation_helper.dart @@ -147,10 +147,9 @@ class BasicTranslatedMessage extends TranslatedMessage { BasicTranslatedMessage(super.name, super.translated, this.messages); @override - List? get originalMessages => - (super.originalMessages == null) - ? _findOriginals() - : super.originalMessages; + List? get originalMessages => (super.originalMessages == null) + ? _findOriginals() + : super.originalMessages; // We know that our [id] is the name of the message, which is used as the key in [messages]. List? _findOriginals() => originalMessages = messages[id]; diff --git a/lib/src/generator/label.dart b/lib/src/generator/label.dart index 53d7ae6..5ad882d 100644 --- a/lib/src/generator/label.dart +++ b/lib/src/generator/label.dart @@ -336,6 +336,7 @@ class Label { ' name: \'$name\',', ' desc: \'$description\',', ' args: [],', + ' locale: localeName,', ' );', ' }', ].join('\n'); @@ -352,6 +353,7 @@ class Label { ' name: \'$name\',', ' desc: \'$description\',', ' args: [${_generateDartMethodArgs(args)}],', + ' locale: localeName,', ' );', ' }', ].join('\n'); @@ -370,6 +372,7 @@ class Label { ' name: \'$name\',', ' desc: \'$description\',', ' args: [${_generateDartMethodArgs(args)}],', + ' locale: localeName,', ' );', ' }', ].join('\n'); @@ -388,16 +391,16 @@ class Label { ' name: \'$name\',', ' desc: \'$description\',', ' args: [${_generateDartMethodArgs(args)}],', + ' locale: localeName,', ' );', ' }', ].join('\n'); } case ContentType.select: { - var choiceArg = - args - .firstWhere((arg) => arg.isSelectArg()) - .name; // Note: The first argument in [args] must correspond to the [choice] Object. + var choiceArg = args + .firstWhere((arg) => arg.isSelectArg()) + .name; // Note: The first argument in [args] must correspond to the [choice] Object. return [ _generateDartDoc(), @@ -409,6 +412,7 @@ class Label { ' name: \'$name\',', ' desc: \'$description\',', ' args: [${_generateDartMethodArgs(args)}],', + ' locale: localeName,', ' );', ' }', ].join('\n'); @@ -595,12 +599,11 @@ class Label { List? placeholders, List data, ) { - var args = - placeholders != null - ? placeholders - .map((placeholder) => Argument.fromPlaceholder(placeholder)) - .toList() - : []; + var args = placeholders != null + ? placeholders + .map((placeholder) => Argument.fromPlaceholder(placeholder)) + .toList() + : []; data .where( @@ -782,20 +785,18 @@ class Label { bool _isLiteral(List data) => (data.isNotEmpty && - data - .map((BaseElement item) => item.type == ElementType.literal) - .reduce((bool acc, bool curr) => acc && curr)); + data + .map((BaseElement item) => item.type == ElementType.literal) + .reduce((bool acc, bool curr) => acc && curr)); bool _isArgument(List data) => (data.isNotEmpty && - data - .map( - (item) => [ - ElementType.argument, - ElementType.literal, - ].contains(item.type), - ) - .reduce((bool acc, bool curr) => acc && curr)); + data + .map( + (item) => + [ElementType.argument, ElementType.literal].contains(item.type), + ) + .reduce((bool acc, bool curr) => acc && curr)); bool _isPlural(List data) => (data.length == 1 && data[0].type == ElementType.plural); @@ -807,56 +808,52 @@ class Label { (data.length == 1 && data[0].type == ElementType.select); String _generateCompoundContent(List data, List args) { - var content = - data - .asMap() - .map((index, item) { - switch (item.type) { - case ElementType.literal: - { - return MapEntry(index, item.value); - } - case ElementType.argument: - { - var formattedArg = - args - .singleWhere( - (element) => element.name == item.value, - ) - .formattedName; - - return MapEntry( - index, - _isArgumentBracingRequired(data, index) - ? '\${$formattedArg}' - : '\$$formattedArg', - ); - } - case ElementType.plural: - { - return MapEntry( - index, - '\${${_generatePluralMessage(item as PluralElement, args)}}', - ); - } - case ElementType.gender: - { - return MapEntry( - index, - '\${${_generateGenderMessage(item as GenderElement, args)}}', - ); - } - case ElementType.select: - { - return MapEntry( - index, - '\${${_generateSelectMessage(item as SelectElement, args)}}', - ); - } + var content = data + .asMap() + .map((index, item) { + switch (item.type) { + case ElementType.literal: + { + return MapEntry(index, item.value); + } + case ElementType.argument: + { + var formattedArg = args + .singleWhere((element) => element.name == item.value) + .formattedName; + + return MapEntry( + index, + _isArgumentBracingRequired(data, index) + ? '\${$formattedArg}' + : '\$$formattedArg', + ); + } + case ElementType.plural: + { + return MapEntry( + index, + '\${${_generatePluralMessage(item as PluralElement, args)}}', + ); + } + case ElementType.gender: + { + return MapEntry( + index, + '\${${_generateGenderMessage(item as GenderElement, args)}}', + ); } - }) - .values - .join(); + case ElementType.select: + { + return MapEntry( + index, + '\${${_generateSelectMessage(item as SelectElement, args)}}', + ); + } + } + }) + .values + .join(); return content; } @@ -935,13 +932,12 @@ class Label { uniqueKeys.remove('=2'); } - var sanitized = - uniqueKeys - .map( - (uniqueKey) => - options.firstWhere((option) => option.name == uniqueKey), - ) - .toList(); + var sanitized = uniqueKeys + .map( + (uniqueKey) => + options.firstWhere((option) => option.name == uniqueKey), + ) + .toList(); if (sanitized.length != options.length) { warning("Detected plural irregularity for the '$name' key."); } else if (!uniqueKeys.contains('other')) { @@ -997,13 +993,12 @@ class Label { var keys = options.map((option) => option.name); var uniqueKeys = LinkedHashSet.from(keys); - var sanitized = - uniqueKeys - .map( - (uniqueKey) => - options.firstWhere((option) => option.name == uniqueKey), - ) - .toList(); + var sanitized = uniqueKeys + .map( + (uniqueKey) => + options.firstWhere((option) => option.name == uniqueKey), + ) + .toList(); if (sanitized.length != options.length) { warning("Detected gender irregularity for the '$name' key."); } else if (!uniqueKeys.contains('other')) { @@ -1042,13 +1037,12 @@ class Label { var keys = options.map((option) => option.name); var uniqueKeys = LinkedHashSet.from(keys); - var sanitized = - uniqueKeys - .map( - (uniqueKey) => - options.firstWhere((option) => option.name == uniqueKey), - ) - .toList(); + var sanitized = uniqueKeys + .map( + (uniqueKey) => + options.firstWhere((option) => option.name == uniqueKey), + ) + .toList(); if (sanitized.length != options.length) { warning("Detected select irregularity for the '$name' key."); } else if (!uniqueKeys.contains('other')) { @@ -1164,37 +1158,34 @@ class Label { return isValid ? data - .asMap() - .map((index, item) { - switch (item.type) { - case ElementType.literal: - { - return MapEntry(index, item.value); - } - case ElementType.argument: - { - var formattedArg = - args - .singleWhere( - (element) => element.name == item.value, - ) - .formattedName; - - return MapEntry( - index, - _isArgumentBracingRequired(data, index) - ? '\${$formattedArg}' - : '\$$formattedArg', - ); - } - default: - { - return MapEntry(index, ''); - } - } - }) - .values - .join() + .asMap() + .map((index, item) { + switch (item.type) { + case ElementType.literal: + { + return MapEntry(index, item.value); + } + case ElementType.argument: + { + var formattedArg = args + .singleWhere((element) => element.name == item.value) + .formattedName; + + return MapEntry( + index, + _isArgumentBracingRequired(data, index) + ? '\${$formattedArg}' + : '\$$formattedArg', + ); + } + default: + { + return MapEntry(index, ''); + } + } + }) + .values + .join() : _getRawPluralOrSelectOption(option); } diff --git a/lib/src/generator/templates.dart b/lib/src/generator/templates.dart index b18e944..81f827b 100644 --- a/lib/src/generator/templates.dart +++ b/lib/src/generator/templates.dart @@ -9,6 +9,7 @@ String generateL10nDartFileContent( ]) { return """ // GENERATED CODE - DO NOT MODIFY BY HAND +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart';${otaEnabled ? '\n${_generateLocalizelySdkImport()}' : ''} import 'intl/messages_all.dart'; @@ -23,10 +24,12 @@ import 'intl/messages_all.dart'; // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes class $className { - $className(); + $className(this.localeName); static $className? _current; + /// It is strongly discouraged to use this instance since it can easily + /// lead to non-determinism. Use [of] or [maybeOf] instead. static $className get current { assert(_current != null, 'No instance of $className was loaded. Try to initialize the $className delegate before accessing $className.current.'); return _current!; @@ -39,11 +42,9 @@ class $className { final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString(); final localeName = Intl.canonicalizedLocale(name);${otaEnabled ? '\n${_generateMetadataSetter()}' : ''} return initializeMessages(localeName).then((_) { - Intl.defaultLocale = localeName; - final instance = $className(); + final instance = $className(localeName); $className._current = instance; - - return instance; + return SynchronousFuture<$className>(instance); }); } @@ -56,6 +57,8 @@ class $className { static $className? maybeOf(BuildContext context) { return Localizations.of<$className>(context, $className); } + + final String localeName; ${otaEnabled ? '\n${_generateMetadata(labels)}\n' : ''} ${labels.map((label) => label.generateDartGetter()).join("\n\n")} } @@ -85,7 +88,8 @@ ${locales.map((locale) => _generateLocale(locale)).join("\n")} return false; } } -""".trim(); +""" + .trim(); } String _generateLocale(String locale) { diff --git a/lib/src/intl_translation/extract_messages.dart b/lib/src/intl_translation/extract_messages.dart index ba73dcd..d54d849 100644 --- a/lib/src/intl_translation/extract_messages.dart +++ b/lib/src/intl_translation/extract_messages.dart @@ -51,7 +51,7 @@ /// Note that this does not understand how to follow part directives, so it /// has to explicitly be given all the files that it needs. A typical use case /// is to run it on all .dart files in a directory. -library extract_messages; +library; // ignore_for_file: implementation_imports @@ -366,13 +366,12 @@ class MessageFindingVisitor extends GeneralizingAstVisitor { if (reason != null) { if (!extraction.suppressWarnings) { - var err = - StringBuffer() - ..write('Skipping invalid Intl.message invocation\n <$node>\n') - ..writeAll([ - ' reason: $reason\n', - extraction._reportErrorLocation(node), - ]); + var err = StringBuffer() + ..write('Skipping invalid Intl.message invocation\n <$node>\n') + ..writeAll([ + ' reason: $reason\n', + extraction._reportErrorLocation(node), + ]); var errString = err.toString(); extraction.warnings.add(errString); extraction.onMessage(errString); @@ -453,12 +452,11 @@ class MessageFindingVisitor extends GeneralizingAstVisitor { var message = MainMessage(); message.sourcePosition = node.offset; message.endPosition = node.end; - message.arguments = - parameters - ?.map((x) => x.name?.lexeme) - .where((x) => x != null) - .cast() - .toList(); + message.arguments = parameters + ?.map((x) => x.name?.lexeme) + .where((x) => x != null) + .cast() + .toList(); if (documentation != null) { message.documentation.addAll( documentation!.tokens.map((token) => token.toString()), @@ -473,10 +471,9 @@ class MessageFindingVisitor extends GeneralizingAstVisitor { var exp = namedArgument.argumentExpression; var evaluator = ConstantEvaluator(); var basicValue = exp.accept(evaluator); - var value = - basicValue == ConstantEvaluator.NOT_A_CONSTANT - ? exp.toString() - : basicValue; + var value = basicValue == ConstantEvaluator.NOT_A_CONSTANT + ? exp.toString() + : basicValue; setAttribute(message, name, value); } // We only rewrite messages with parameters, otherwise we use the literal @@ -493,8 +490,11 @@ class MessageFindingVisitor extends GeneralizingAstVisitor { // If there's no name, and the message text is a simple string, compute // a name based on that plus meaning, if present. var simpleName = (arguments.first as StringLiteral).stringValue; - message.name = - computeMessageName(message.name, simpleName, message.meaning)!; + message.name = computeMessageName( + message.name, + simpleName, + message.meaning, + )!; } } return message; @@ -535,10 +535,9 @@ class MessageFindingVisitor extends GeneralizingAstVisitor { message.addPieces(List.from(extracted)); } on IntlMessageExtractionException catch (e) { message = null; - var err = - StringBuffer() - ..writeAll(['Error ', e, '\nProcessing <', node, '>\n']) - ..write(extraction._reportErrorLocation(node)); + var err = StringBuffer() + ..writeAll(['Error ', e, '\nProcessing <', node, '>\n']) + ..write(extraction._reportErrorLocation(node)); var errString = err.toString(); extraction.onMessage(errString); extraction.warnings.add(errString); @@ -761,10 +760,9 @@ class PluralAndGenderVisitor extends SimpleAstVisitor { } } on IntlMessageExtractionException catch (e) { message = null; - var err = - StringBuffer() - ..writeAll(['Error ', e, '\nProcessing <', node, '>']) - ..write(extraction._reportErrorLocation(node)); + var err = StringBuffer() + ..writeAll(['Error ', e, '\nProcessing <', node, '>']) + ..write(extraction._reportErrorLocation(node)); var errString = err.toString(); extraction.onMessage(errString); extraction.warnings.add(errString); @@ -778,14 +776,13 @@ class PluralAndGenderVisitor extends SimpleAstVisitor { } else if (mainArg is SimpleIdentifier) { message.mainArgument = mainArg.name; } else { - var err = - StringBuffer() - ..write( - 'Error (Invalid argument to plural/gender/select, ' - 'must be simple variable reference) ' - '\nProcessing <$node>', - ) - ..write(extraction._reportErrorLocation(node)); + var err = StringBuffer() + ..write( + 'Error (Invalid argument to plural/gender/select, ' + 'must be simple variable reference) ' + '\nProcessing <$node>', + ) + ..write(extraction._reportErrorLocation(node)); var errString = err.toString(); extraction.onMessage(errString); extraction.warnings.add(errString); diff --git a/lib/src/intl_translation/generate_localized.dart b/lib/src/intl_translation/generate_localized.dart index ed62d6e..ceaf3bf 100644 --- a/lib/src/intl_translation/generate_localized.dart +++ b/lib/src/intl_translation/generate_localized.dart @@ -43,7 +43,7 @@ /// /// An example of usage can be found /// in test/message_extract/generate_from_json.dart -library generate_localized; +library; import 'package:intl/intl.dart'; import 'dart:convert'; @@ -135,8 +135,9 @@ class MessageGeneration { // Exclude messages with no translation and translations with no matching // original message (e.g. if we're using some messages from a larger // catalog) - var usableTranslations = - translations.where((each) => each.originalMessages != null).toList(); + var usableTranslations = translations + .where((each) => each.originalMessages != null) + .toList(); for (var each in usableTranslations) { for (var original in each.originalMessages!) { original.addTranslation(locale, each.message); @@ -161,8 +162,10 @@ class MessageGeneration { for (var translation in usableTranslations) { // Some messages we generate as methods in this class. Simpler ones // we inline in the map from names to messages. - var messagesThatNeedMethods = - translation.originalMessages!.where(_hasArguments).toSet().toList(); + var messagesThatNeedMethods = translation.originalMessages! + .where(_hasArguments) + .toSet() + .toList(); for (var original in messagesThatNeedMethods) { output ..write(' ') @@ -176,16 +179,17 @@ class MessageGeneration { // Now write the map of names to either the direct translation or to a // method. - var entries = (usableTranslations - .expand((translation) => translation.originalMessages!) - .toSet() - .toList() - ..sort((a, b) => a.name.compareTo(b.name))) - .map( - (original) => - ' "${original.escapeAndValidateString(original.name)}" ' - ': ${_mapReference(original, locale)}', - ); + var entries = + (usableTranslations + .expand((translation) => translation.originalMessages!) + .toSet() + .toList() + ..sort((a, b) => a.name.compareTo(b.name))) + .map( + (original) => + ' "${original.escapeAndValidateString(original.name)}" ' + ': ${_mapReference(original, locale)}', + ); output ..write(entries.join(',\n')) ..write('\n };\n}\n'); @@ -195,17 +199,18 @@ class MessageGeneration { String get extraImports => ''; String get messagesDeclaration => - // Includes some gyrations to prevent parts of the deferred libraries from - // being inlined into the main one, defeating the space savings. Issue - // 24356 - ''' + // Includes some gyrations to prevent parts of the deferred libraries from + // being inlined into the main one, defeating the space savings. Issue + // 24356 + ''' final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { '''; /// [generateIndividualMessageFile] for the beginning of the file, /// parameterized by [locale]. - String prologue(String locale) => """ + String prologue(String locale) => + """ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a $locale locale. All the // messages from the main program should be duplicated here with the same @@ -269,10 +274,9 @@ ${releaseMode ? overrideLookup : ''}"""; output.write('Map _deferredLibraries = {\n'); for (var rawLocale in allLocales) { var locale = Intl.canonicalizedLocale(rawLocale); - var loadOperation = - (useDeferredLoading) - ? " '$locale': ${libraryName(locale)}.loadLibrary,\n" - : " '$locale': () => new SynchronousFuture(null),\n"; + var loadOperation = (useDeferredLoading) + ? " '$locale': ${libraryName(locale)}.loadLibrary,\n" + : " '$locale': () => new SynchronousFuture(null),\n"; output.write(loadOperation); } output.write('};\n'); @@ -292,7 +296,8 @@ ${releaseMode ? overrideLookup : ''}"""; /// Constant string used in [generateMainImportFile] for the beginning of the /// file. - String get mainPrologue => """ + String get mainPrologue => + """ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that looks up messages for specific locales by // delegating to the appropriate library. @@ -313,7 +318,8 @@ import 'package:$intlImportPath/src/intl_helpers.dart'; """; /// Constant string used in [generateMainImportFile] as the end of the file. - String get closing => ''' + String get closing => + ''' default:\n return null; } } @@ -355,7 +361,8 @@ class JsonMessageGeneration extends MessageGeneration { /// We import the main file so as to get the shared code to evaluate /// the JSON data. @override - String get extraImports => ''' + String get extraImports => + ''' import 'dart:convert'; import '${generatedFilePrefix}messages_all.dart' show evaluateJsonTemplate; '''; diff --git a/lib/src/intl_translation/src/icu_parser.dart b/lib/src/intl_translation/src/icu_parser.dart index 0493dc1..9b79272 100644 --- a/lib/src/intl_translation/src/icu_parser.dart +++ b/lib/src/intl_translation/src/icu_parser.dart @@ -38,7 +38,7 @@ /// Contains a parser for ICU format plural/gender/select format for localized /// messages. See extract_to_arb.dart and make_hardcoded_translation.dart. -library icu_parser; +library; import 'package:petitparser/petitparser.dart'; @@ -116,12 +116,15 @@ class IcuParser { Parser get intlSelect => generalSelect.map((values) => Select.from(values.first, values[3], null)); - Parser get compound => (((parameter | nonIcuMessageText).plus() & - pluralOrGenderOrSelect & - (pluralOrGenderOrSelect | parameter | nonIcuMessageText).star()) | - (pluralOrGenderOrSelect & - (pluralOrGenderOrSelect | parameter | nonIcuMessageText).plus())) - .map((result) => result.expand((x) => x is List ? x : [x]).toList()); + Parser get compound => + (((parameter | nonIcuMessageText).plus() & + pluralOrGenderOrSelect & + (pluralOrGenderOrSelect | parameter | nonIcuMessageText) + .star()) | + (pluralOrGenderOrSelect & + (pluralOrGenderOrSelect | parameter | nonIcuMessageText) + .plus())) + .map((result) => result.expand((x) => x is List ? x : [x]).toList()); Parser get pluralOrGenderOrSelect => intlPlural | intlGender | intlSelect; diff --git a/lib/src/intl_translation/src/intl_message.dart b/lib/src/intl_translation/src/intl_message.dart index d314f6e..53896cd 100644 --- a/lib/src/intl_translation/src/intl_message.dart +++ b/lib/src/intl_translation/src/intl_message.dart @@ -63,7 +63,7 @@ /// This representation isn't used at runtime. Rather, we read some format /// from a translation file, parse it into these objects, and they are then /// used to generate the code representation above. -library intl_message; +library; // ignore_for_file: implementation_imports @@ -73,7 +73,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/constant_evaluator.dart'; /// A default function for the [Message.expanded] method. -dynamic _nullTransform(msg, chunk) => chunk; +dynamic _nullTransform(dynamic msg, dynamic chunk) => chunk; const jsonEncoder = JsonCodec(); @@ -92,19 +92,19 @@ abstract class Message { /// We find the arguments from the top-level [MainMessage] and use those to /// do variable substitutions. [MainMessage] overrides this to return /// the actual arguments. - get arguments => parent == null ? const [] : parent!.arguments; + dynamic get arguments => parent == null ? const [] : parent!.arguments; /// We find the examples from the top-level [MainMessage] and use those /// when writing out variables. [MainMessage] overrides this to return /// the actual examples. - get examples => parent == null ? const [] : parent!.examples; + dynamic get examples => parent == null ? const [] : parent!.examples; /// The name of the top-level [MainMessage]. String get name => parent == null ? '' : parent!.name; static final _evaluator = ConstantEvaluator(); - String? _evaluateAsString(expression) { + String? _evaluateAsString(dynamic expression) { var result = expression.accept(_evaluator); if (result == ConstantEvaluator.NOT_A_CONSTANT || result is! String) { return null; @@ -113,7 +113,7 @@ abstract class Message { } } - Map? _evaluateAsMap(expression) { + Map? _evaluateAsMap(dynamic expression) { var result = expression.accept(_evaluator); if (result == ConstantEvaluator.NOT_A_CONSTANT || result is! Map) { return null; @@ -379,13 +379,13 @@ abstract class ComplexMessage extends Message { /// and set their attributes by string names, so we override the indexing /// operators so that they behave like maps with respect to those attribute /// names. - operator [](String x); + dynamic operator [](String x); /// When we create these from strings or from AST nodes, we want to look up /// and set their attributes by string names, so we override the indexing /// operators so that they behave like maps with respect to those attribute /// names. - operator []=(String x, y); + void operator []=(String x, y); List get attributeNames; @@ -1084,7 +1084,7 @@ class Select extends SubMessage { // something else, in which case we convert it to a string // and take the portion after the period, if present. // This is to handle enums as select keys. - String _keyForm(key) { + String _keyForm(dynamic key) { return (key is SimpleStringLiteral) ? key.value : '$key'.split('.').last; } diff --git a/lib/src/localizely/api/api.dart b/lib/src/localizely/api/api.dart index 5124b86..515cc17 100644 --- a/lib/src/localizely/api/api.dart +++ b/lib/src/localizely/api/api.dart @@ -24,38 +24,36 @@ class LocalizelyApi { List? tagUpdated, List? tagRemoved, ]) async { - var queryParams = - [ - '?lang_code=$langCode', - '&overwrite=$overwrite', - '&reviewed=$reviewed', - branch != null ? '&branch=$branch' : '', - tagAdded != null - ? tagAdded.map((tag) => '&tag_added=$tag').toList().join() - : '', - tagUpdated != null - ? tagUpdated.map((tag) => '&tag_updated=$tag').toList().join() - : '', - tagRemoved != null - ? tagRemoved.map((tag) => '&tag_removed=$tag').toList().join() - : '', - ].join(); + var queryParams = [ + '?lang_code=$langCode', + '&overwrite=$overwrite', + '&reviewed=$reviewed', + branch != null ? '&branch=$branch' : '', + tagAdded != null + ? tagAdded.map((tag) => '&tag_added=$tag').toList().join() + : '', + tagUpdated != null + ? tagUpdated.map((tag) => '&tag_updated=$tag').toList().join() + : '', + tagRemoved != null + ? tagRemoved.map((tag) => '&tag_removed=$tag').toList().join() + : '', + ].join(); var uri = Uri.parse( '$_baseUrl/v1/projects/$projectId/files/upload$queryParams', ); var headers = {'X-Api-Token': apiToken}; - var request = - http.MultipartRequest('POST', uri) - ..headers.addAll(headers) - ..files.add( - http.MultipartFile.fromBytes( - 'file', - file.readAsBytesSync(), - filename: path.basename(file.path), - ), - ); + var request = http.MultipartRequest('POST', uri) + ..headers.addAll(headers) + ..files.add( + http.MultipartFile.fromBytes( + 'file', + file.readAsBytesSync(), + filename: path.basename(file.path), + ), + ); var response = await request.send(); @@ -79,18 +77,17 @@ class LocalizelyApi { List? includeTags, List? excludeTags, ]) async { - var queryParams = - [ - '?type=flutter_arb', - branch != null ? '&branch=$branch' : '', - exportEmptyAs != null ? '&export_empty_as=$exportEmptyAs' : '', - includeTags != null - ? includeTags.map((tag) => '&include_tags=$tag').toList().join() - : '', - excludeTags != null - ? excludeTags.map((tag) => '&exclude_tags=$tag').toList().join() - : '', - ].join(); + var queryParams = [ + '?type=flutter_arb', + branch != null ? '&branch=$branch' : '', + exportEmptyAs != null ? '&export_empty_as=$exportEmptyAs' : '', + includeTags != null + ? includeTags.map((tag) => '&include_tags=$tag').toList().join() + : '', + excludeTags != null + ? excludeTags.map((tag) => '&exclude_tags=$tag').toList().join() + : '', + ].join(); var uri = Uri.parse( '$_baseUrl/v1/projects/$projectId/files/download$queryParams', diff --git a/lib/src/parser/icu_parser.dart b/lib/src/parser/icu_parser.dart index b8577b7..82bbdae 100644 --- a/lib/src/parser/icu_parser.dart +++ b/lib/src/parser/icu_parser.dart @@ -138,12 +138,15 @@ class IcuParser { (result) => SelectElement(result[0], List