Merge branch 'moe_writing_branch_from_43aa475dcacaa9399770cc5f12b491488f1feb95'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 321d5f3..799dbad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.14.0
+ * MAJOR BREAKING CHANGE! Remove message extraction and code generation into a separate
+ intl_translation package. This means packages with a runtime dependency on
+ intl don't also depend on analyzer, barback, and so forth.
+
## 0.13.1
* Update CLDR data to version 29.
* Add a toBeginningOfSentenceCase() method which converts the first character
diff --git a/README.md b/README.md
index 1a0f074..0b38b17 100644
--- a/README.md
+++ b/README.md
@@ -186,11 +186,12 @@
When your program contains messages that need translation, these must
be extracted from the program source, sent to human translators, and the
-results need to be incorporated.
+results need to be incorporated. The code for this is in the
+[Intl_translation][Intl_translation] package.
To extract messages, run the `extract_to_arb.dart` program.
- pub run intl:extract_to_arb --output-dir=target/directory
+ pub run intl_translation:extract_to_arb --output-dir=target/directory
my_program.dart more_of_my_program.dart
This will produce a file `intl_messages.arb` with the messages from
@@ -204,8 +205,10 @@
This expects to receive a series of files, one per
locale.
- pub run intl:generate_from_arb --generated_file_prefix=<prefix>
- <my_dart_files> <translated_ARB_files>
+```
+pub run intl_translation:generate_from_arb --generated_file_prefix=<prefix>
+ <my_dart_files> <translated_ARB_files>
+```
This will generate Dart libraries, one per locale, which contain the
translated versions. Your Dart libraries can import the primary file,
diff --git a/bin/extract_to_arb.dart b/bin/extract_to_arb.dart
deleted file mode 100644
index 7c643f9..0000000
--- a/bin/extract_to_arb.dart
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// This script uses the extract_messages.dart library to find the Intl.message
-/// calls in the target dart files and produces ARB format output. See
-/// https://code.google.com/p/arb/wiki/ApplicationResourceBundleSpecification
-library extract_to_arb;
-
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:args/args.dart';
-import 'package:path/path.dart' as path;
-
-import 'package:intl/extract_messages.dart';
-import 'package:intl/src/intl_message.dart';
-
-var outputFilename = 'intl_messages.arb';
-
-main(List<String> args) {
- var targetDir;
- bool transformer;
- var parser = new ArgParser();
- var extraction = new MessageExtraction();
- parser.addFlag("suppress-warnings",
- defaultsTo: false,
- callback: (x) => extraction.suppressWarnings = x,
- help: 'Suppress printing of warnings.');
- parser.addFlag("warnings-are-errors",
- defaultsTo: false,
- callback: (x) => extraction.warningsAreErrors = x,
- help: 'Treat all warnings as errors, stop processing ');
- parser.addFlag("embedded-plurals",
- defaultsTo: true,
- callback: (x) => extraction.allowEmbeddedPluralsAndGenders = x,
- help: 'Allow plurals and genders to be embedded as part of a larger '
- 'string, otherwise they must be at the top level.');
- parser.addFlag("transformer",
- defaultsTo: false,
- callback: (x) => transformer = x,
- help: "Assume that the transformer is in use, so name and args "
- "don't need to be specified for messages.");
-
- parser.addOption("output-dir",
- defaultsTo: '.',
- callback: (value) => targetDir = value,
- help: 'Specify the output directory.');
- parser.parse(args);
- if (args.length == 0) {
- print('Accepts Dart files and produces $outputFilename');
- print('Usage: extract_to_arb [options] [files.dart]');
- print(parser.usage);
- exit(0);
- }
- var allMessages = {};
- for (var arg in args.where((x) => x.contains(".dart"))) {
- var messages = extraction.parseFile(new File(arg), transformer);
- messages.forEach((k, v) => allMessages.addAll(toARB(v)));
- }
- var file = new File(path.join(targetDir, outputFilename));
- file.writeAsStringSync(JSON.encode(allMessages));
- if (extraction.hasWarnings && extraction.warningsAreErrors) {
- exit(1);
- }
-}
-
-/// This is a placeholder for transforming a parameter substitution from
-/// the translation file format into a Dart interpolation. In our case we
-/// store it to the file in Dart interpolation syntax, so the transformation
-/// is trivial.
-String leaveTheInterpolationsInDartForm(MainMessage msg, chunk) {
- if (chunk is String) return chunk;
- if (chunk is int) return "\$${msg.arguments[chunk]}";
- return chunk.toCode();
-}
-
-/// Convert the [MainMessage] to a trivial JSON format.
-Map toARB(MainMessage message) {
- if (message.messagePieces.isEmpty) return null;
- var out = {};
- out[message.name] = icuForm(message);
- out["@${message.name}"] = arbMetadata(message);
- return out;
-}
-
-Map arbMetadata(MainMessage message) {
- var out = {};
- var desc = message.description;
- if (desc != null) {
- out["description"] = desc;
- }
- out["type"] = "text";
- var placeholders = {};
- for (var arg in message.arguments) {
- addArgumentFor(message, arg, placeholders);
- }
- out["placeholders"] = placeholders;
- return out;
-}
-
-void addArgumentFor(MainMessage message, String arg, Map result) {
- var extraInfo = {};
- if (message.examples != null && message.examples[arg] != null) {
- extraInfo["example"] = message.examples[arg];
- }
- result[arg] = extraInfo;
-}
-
-/// Return a version of the message string with with ICU parameters "{variable}"
-/// rather than Dart interpolations "$variable".
-String icuForm(MainMessage message) =>
- message.expanded(turnInterpolationIntoICUForm);
-
-String turnInterpolationIntoICUForm(Message message, chunk,
- {bool shouldEscapeICU: false}) {
- if (chunk is String) {
- return shouldEscapeICU ? escape(chunk) : chunk;
- }
- if (chunk is int && chunk >= 0 && chunk < message.arguments.length) {
- return "{${message.arguments[chunk]}}";
- }
- if (chunk is SubMessage) {
- return chunk.expanded((message, chunk) =>
- turnInterpolationIntoICUForm(message, chunk, shouldEscapeICU: true));
- }
- if (chunk is Message) {
- return chunk.expanded((message, chunk) => turnInterpolationIntoICUForm(
- message, chunk,
- shouldEscapeICU: shouldEscapeICU));
- }
- throw new FormatException("Illegal interpolation: $chunk");
-}
-
-String escape(String s) {
- return s.replaceAll("'", "''").replaceAll("{", "'{'").replaceAll("}", "'}'");
-}
diff --git a/bin/generate_from_arb.dart b/bin/generate_from_arb.dart
deleted file mode 100644
index 13d3680..0000000
--- a/bin/generate_from_arb.dart
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// A main program that takes as input a source Dart file and a number
-/// of ARB files representing translations of messages from the corresponding
-/// Dart file. See extract_to_arb.dart and make_hardcoded_translation.dart.
-///
-/// If the ARB file has an @@locale or _locale value, that will be used as
-/// the locale. If not, we will try to figure out the locale from the end of
-/// the file name, e.g. foo_en_GB.arb will be assumed to be in en_GB locale.
-///
-/// This produces a series of files named
-/// "messages_<locale>.dart" containing messages for a particular locale
-/// and a main import file named "messages_all.dart" which has imports all of
-/// them and provides an initializeMessages function.
-
-library generate_from_arb;
-
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:args/args.dart';
-import 'package:path/path.dart' as path;
-
-import 'package:intl/extract_messages.dart';
-import 'package:intl/generate_localized.dart';
-import 'package:intl/src/intl_message.dart';
-import 'package:intl/src/icu_parser.dart';
-
-/// Keeps track of all the messages we have processed so far, keyed by message
-/// name.
-Map<String, List<MainMessage>> messages;
-
-main(List<String> args) {
- var targetDir;
- var parser = new ArgParser();
- var extraction = new MessageExtraction();
- var generation = new MessageGeneration();
- parser.addFlag("suppress-warnings",
- defaultsTo: false,
- callback: (x) => extraction.suppressWarnings = x,
- help: 'Suppress printing of warnings.');
- parser.addOption("output-dir",
- defaultsTo: '.',
- callback: (x) => targetDir = x,
- help: 'Specify the output directory.');
- parser.addOption("generated-file-prefix",
- defaultsTo: '',
- callback: (x) => generation.generatedFilePrefix = x,
- help: 'Specify a prefix to be used for the generated file names.');
- parser.addFlag("use-deferred-loading",
- defaultsTo: true,
- callback: (x) => generation.useDeferredLoading = x,
- help: 'Generate message code that must be loaded with deferred loading. '
- 'Otherwise, all messages are eagerly loaded.');
- parser.parse(args);
- var dartFiles = args.where((x) => x.endsWith("dart")).toList();
- var jsonFiles = args.where((x) => x.endsWith(".arb")).toList();
- if (dartFiles.length == 0 || jsonFiles.length == 0) {
- print('Usage: generate_from_arb [options]'
- ' file1.dart file2.dart ...'
- ' translation1_<languageTag>.arb translation2.arb ...');
- print(parser.usage);
- exit(0);
- }
-
- // TODO(alanknight): There is a possible regression here. If a project is
- // using the transformer and expecting it to provide names for messages with
- // parameters, we may report those names as missing. We now have two distinct
- // mechanisms for providing names: the transformer and just using the message
- // text if there are no parameters. Previously this was always acting as if
- // the transformer was in use, but that breaks the case of using the message
- // text. The intent is to deprecate the transformer, but if this is an issue
- // for real projects we could provide a command-line flag to indicate which
- // sort of automated name we're using.
- extraction.suppressWarnings = true;
- var allMessages =
- dartFiles.map((each) => extraction.parseFile(new File(each), false));
-
- messages = new Map();
- for (var eachMap in allMessages) {
- eachMap.forEach(
- (key, value) => messages.putIfAbsent(key, () => []).add(value));
- }
- for (var arg in jsonFiles) {
- var file = new File(arg);
- generateLocaleFile(file, targetDir, generation);
- }
-
- var mainImportFile = new File(path.join(
- targetDir, '${generation.generatedFilePrefix}messages_all.dart'));
- mainImportFile.writeAsStringSync(generation.generateMainImportFile());
-}
-
-/// Create the file of generated code for a particular locale. We read the ARB
-/// data and create [BasicTranslatedMessage] instances from everything,
-/// excluding only the special _locale attribute that we use to indicate the
-/// locale. If that attribute is missing, we try to get the locale from the last
-/// section of the file name.
-void generateLocaleFile(
- File file, String targetDir, MessageGeneration generation) {
- var src = file.readAsStringSync();
- var data = JSON.decode(src);
- data.forEach((k, v) => data[k] = recreateIntlObjects(k, v));
- var locale = data["@@locale"] ?? data["_locale"];
- if (locale != null) {
- locale = locale.translated.string;
- } else {
- // Get the locale from the end of the file name. This assumes that the file
- // name doesn't contain any underscores except to begin the language tag
- // and to separate language from country. Otherwise we can't tell if
- // my_file_fr.arb is locale "fr" or "file_fr".
- var name = path.basenameWithoutExtension(file.path);
- locale = name.split("_").skip(1).join("_");
- print("No @@locale or _locale field found in $name, "
- "assuming '$locale' based on the file name.");
- }
- generation.allLocales.add(locale);
-
- List<TranslatedMessage> translations = [];
- data.forEach((key, value) {
- if (value != null) {
- translations.add(value);
- }
- });
- generation.generateIndividualMessageFile(locale, translations, targetDir);
-}
-
-/// Regenerate the original IntlMessage objects from the given [data]. For
-/// things that are messages, we expect [id] not to start with "@" and
-/// [data] to be a String. For metadata we expect [id] to start with "@"
-/// and [data] to be a Map or null. For metadata we return null.
-BasicTranslatedMessage recreateIntlObjects(String id, data) {
- if (id.startsWith("@")) return null;
- if (data == null) return null;
- var parsed = pluralAndGenderParser.parse(data).value;
- if (parsed is LiteralString && parsed.string.isEmpty) {
- parsed = plainParser.parse(data).value;
- ;
- }
- return new BasicTranslatedMessage(id, parsed);
-}
-
-/// A TranslatedMessage that just uses the name as the id and knows how to look
-/// up its original messages in our [messages].
-class BasicTranslatedMessage extends TranslatedMessage {
- BasicTranslatedMessage(String name, translated) : super(name, translated);
-
- List<MainMessage> 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<MainMessage> _findOriginals() => originalMessages = messages[id];
-}
-
-final pluralAndGenderParser = new IcuParser().message;
-final plainParser = new IcuParser().nonIcuMessage;
diff --git a/bin/rewrite_intl_messages.dart b/bin/rewrite_intl_messages.dart
deleted file mode 100644
index 8fc0dd6..0000000
--- a/bin/rewrite_intl_messages.dart
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// A main program that imitates the action of the transformer, adding
-/// name and args parameters to Intl.message calls automatically.
-///
-/// This is mainly intended to test the transformer logic outside of barback.
-/// It takes as input a single source Dart file and rewrites any
-/// Intl.message or related calls to automatically include the name and args
-/// parameters and writes the result to stdout.
-///
-import 'dart:io';
-
-import 'package:args/args.dart';
-
-import 'package:intl/src/message_rewriter.dart';
-
-String outputFile = 'transformed_output.dart';
-
-main(List<String> args) {
- var parser = new ArgParser();
- parser.addOption('output',
- defaultsTo: 'transformed_output.dart',
- callback: (x) => outputFile = x,
- help: 'Specify the output file.');
- print(args);
- parser.parse(args);
- if (args.length == 0) {
- print('Accepts a single Dart file and adds "name" and "args" parameters '
- ' to Intl.message calls.');
- print('Primarily useful for exercising the transformer logic.');
- print('Usage: rewrite_intl_messages [options] [file.dart]');
- print(parser.usage);
- exit(0);
- }
- var dartFile = args.where((x) => x.endsWith(".dart")).last;
- var file = new File(dartFile);
- var content = file.readAsStringSync();
- var newSource = rewriteMessages(content, '$file');
- print('Writing new source to $outputFile');
- var out = new File(outputFile);
- out.writeAsStringSync(newSource);
-}
diff --git a/lib/extract_messages.dart b/lib/extract_messages.dart
deleted file mode 100644
index b9c1222..0000000
--- a/lib/extract_messages.dart
+++ /dev/null
@@ -1,550 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// This is for use in extracting messages from a Dart program
-/// using the Intl.message() mechanism and writing them to a file for
-/// translation. This provides only the stub of a mechanism, because it
-/// doesn't define how the file should be written. It provides an
-/// [IntlMessage] class that holds the extracted data and [parseString]
-/// and [parseFile] methods which
-/// can extract messages that conform to the expected pattern:
-/// (parameters) => Intl.message("Message $parameters", desc: ...);
-/// It uses the analyzer package to do the parsing, so may
-/// break if there are changes to the API that it provides.
-/// An example can be found in test/message_extraction/extract_to_json.dart
-///
-/// 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;
-
-import 'dart:io';
-
-import 'package:analyzer/analyzer.dart';
-import 'package:intl/src/intl_message.dart';
-import 'package:intl/src/intl_helpers.dart';
-
-/// A function that takes a message and does something useful with it.
-typedef void OnMessage(String message);
-
-/// A particular message extraction run.
-///
-/// This encapsulates all the state required for message extraction so that
-/// it can be run inside a persistent process.
-class MessageExtraction {
- /// What to do when a message is encountered, defaults to [print].
- OnMessage onMessage = print;
-
- /// If this is true, print warnings for skipped messages. Otherwise, warnings
- /// are suppressed.
- bool suppressWarnings = false;
-
- /// If this is true, then treat all warnings as errors.
- bool warningsAreErrors = false;
-
- /// This accumulates a list of all warnings/errors we have found. These are
- /// saved as strings right now, so all that can really be done is print and
- /// count them.
- List<String> warnings = [];
-
- /// Were there any warnings or errors in extracting messages.
- bool get hasWarnings => warnings.isNotEmpty;
-
- /// Are plural and gender expressions required to be at the top level
- /// of an expression, or are they allowed to be embedded in string literals.
- ///
- /// For example, the following expression
- /// 'There are ${Intl.plural(...)} items'.
- /// is legal if [allowEmbeddedPluralsAndGenders] is true, but illegal
- /// if [allowEmbeddedPluralsAndGenders] is false.
- bool allowEmbeddedPluralsAndGenders = true;
-
- /// Are examples required on all messages.
- bool examplesRequired = false;
-
- /// Parse the source of the Dart program file [file] and return a Map from
- /// message names to [IntlMessage] instances.
- ///
- /// If [transformer] is true, assume the transformer will supply any "name"
- /// and "args" parameters required in Intl.message calls.
- Map<String, MainMessage> parseFile(File file, [transformer = false]) {
- // Optimization to avoid parsing files we're sure don't contain any messages.
- String contents = file.readAsStringSync();
- origin = file.path;
- if (contents.contains("Intl.")) {
- root = _parseCompilationUnit(contents, origin);
- } else {
- return {};
- }
- var visitor = new MessageFindingVisitor(this);
- visitor.generateNameAndArgs = transformer;
- root.accept(visitor);
- return visitor.messages;
- }
-
- CompilationUnit _parseCompilationUnit(String contents, String origin) {
- var parsed;
- try {
- parsed = parseCompilationUnit(contents);
- } on AnalyzerErrorGroup catch (e) {
- print("Error in parsing $origin, no messages extracted.");
- print(" $e");
- }
- return parsed;
- }
-
- /// The root of the compilation unit, and the first node we visit. We hold
- /// on to this for error reporting, as it can give us line numbers of other
- /// nodes.
- CompilationUnit root;
-
- /// An arbitrary string describing where the source code came from. Most
- /// obviously, this could be a file path. We use this when reporting
- /// invalid messages.
- String origin;
-
- String _reportErrorLocation(AstNode node) {
- var result = new StringBuffer();
- if (origin != null) result.write(" from $origin");
- var info = root.lineInfo;
- if (info != null) {
- var line = info.getLocation(node.offset);
- result
- .write(" line: ${line.lineNumber}, column: ${line.columnNumber}");
- }
- return result.toString();
- }
-}
-
-/// This visits the program source nodes looking for Intl.message uses
-/// that conform to its pattern and then creating the corresponding
-/// IntlMessage objects. We have to find both the enclosing function, and
-/// the Intl.message invocation.
-class MessageFindingVisitor extends GeneralizingAstVisitor {
- MessageFindingVisitor(this.extraction);
-
- /// The message extraction in which we are running.
- final MessageExtraction extraction;
-
- /// Accumulates the messages we have found, keyed by name.
- final Map<String, MainMessage> messages = new Map<String, MainMessage>();
-
- /// Should we generate the name and arguments from the function definition,
- /// meaning we're running in the transformer.
- bool generateNameAndArgs = false;
-
- /// We keep track of the data from the last MethodDeclaration,
- /// FunctionDeclaration or FunctionExpression that we saw on the way down,
- /// as that will be the nearest parent of the Intl.message invocation.
- FormalParameterList parameters;
- String name;
-
- /// Return true if [node] matches the pattern we expect for Intl.message()
- bool looksLikeIntlMessage(MethodInvocation node) {
- const validNames = const ["message", "plural", "gender", "select"];
- if (!validNames.contains(node.methodName.name)) return false;
- if (!(node.target is SimpleIdentifier)) return false;
- SimpleIdentifier target = node.target;
- return target.token.toString() == "Intl";
- }
-
- Message _expectedInstance(String type) {
- switch (type) {
- case 'message':
- return new MainMessage();
- case 'plural':
- return new Plural();
- case 'gender':
- return new Gender();
- case 'select':
- return new Select();
- default:
- return null;
- }
- }
-
- /// Returns a String describing why the node is invalid, or null if no
- /// reason is found, so it's presumed valid.
- String checkValidity(MethodInvocation node) {
- // The containing function cannot have named parameters.
- if (parameters.parameters.any((each) => each.kind == ParameterKind.NAMED)) {
- return "Named parameters on message functions are not supported.";
- }
- var arguments = node.argumentList.arguments;
- var instance = _expectedInstance(node.methodName.name);
- return instance.checkValidity(node, arguments, name, parameters,
- nameAndArgsGenerated: generateNameAndArgs,
- examplesRequired: extraction.examplesRequired);
- }
-
- /// Record the parameters of the function or method declaration we last
- /// encountered before seeing the Intl.message call.
- void visitMethodDeclaration(MethodDeclaration node) {
- parameters = node.parameters;
- if (parameters == null) {
- parameters = new FormalParameterList(null, [], null, null, null);
- }
- name = node.name.name;
- super.visitMethodDeclaration(node);
- }
-
- /// Record the parameters of the function or method declaration we last
- /// encountered before seeing the Intl.message call.
- void visitFunctionDeclaration(FunctionDeclaration node) {
- parameters = node.functionExpression.parameters;
- if (parameters == null) {
- parameters = new FormalParameterList(null, [], null, null, null);
- }
- name = node.name.name;
- super.visitFunctionDeclaration(node);
- }
-
- /// Examine method invocations to see if they look like calls to Intl.message.
- /// If we've found one, stop recursing. This is important because we can have
- /// Intl.message(...Intl.plural...) and we don't want to treat the inner
- /// plural as if it was an outermost message.
- void visitMethodInvocation(MethodInvocation node) {
- if (!addIntlMessage(node)) {
- super.visitMethodInvocation(node);
- }
- }
-
- /// Check that the node looks like an Intl.message invocation, and create
- /// the [IntlMessage] object from it and store it in [messages]. Return true
- /// if we successfully extracted a message and should stop looking. Return
- /// false if we didn't, so should continue recursing.
- bool addIntlMessage(MethodInvocation node) {
- if (!looksLikeIntlMessage(node)) return false;
- var reason = checkValidity(node);
- if (reason != null) {
- if (!extraction.suppressWarnings) {
- var err = new 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);
- }
- // We found one, but it's not valid. Stop recursing.
- return true;
- }
- var message;
- if (node.methodName.name == "message") {
- message = messageFromIntlMessageCall(node);
- } else {
- message = messageFromDirectPluralOrGenderCall(node);
- }
- if (message != null) messages[message.name] = message;
- return true;
- }
-
- /// Create a MainMessage from [node] using the name and
- /// parameters of the last function/method declaration we encountered,
- /// and the values we get by calling [extract]. We set those values
- /// by calling [setAttribute]. This is the common parts between
- /// [messageFromIntlMessageCall] and [messageFromDirectPluralOrGenderCall].
- MainMessage _messageFromNode(
- MethodInvocation node,
- MainMessage extract(MainMessage message, List<AstNode> arguments),
- void setAttribute(
- MainMessage message, String fieldName, Object fieldValue)) {
- var message = new MainMessage();
- message.sourcePosition = node.offset;
- message.endPosition = node.end;
- message.arguments =
- parameters.parameters.map((x) => x.identifier.name).toList();
- var arguments = node.argumentList.arguments;
- var extractionResult = extract(message, arguments);
- if (extractionResult == null) return null;
-
- for (NamedExpression namedArgument
- in arguments.where((x) => x is NamedExpression)) {
- var name = namedArgument.name.label.name;
- var exp = namedArgument.expression;
- var evaluator = new ConstantEvaluator();
- var basicValue = exp.accept(evaluator);
- 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
- // string as the name and no arguments are necessary.
- if (!message.hasName) {
- if (generateNameAndArgs && message.arguments.isNotEmpty) {
- // Always try for class_method if this is a class method and
- // generating names/args.
- message.name = Message.classPlusMethodName(node, name) ?? name;
- } else if (arguments.first is SimpleStringLiteral ||
- arguments.first is AdjacentStrings) {
- // 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);
- }
- }
- return message;
- }
-
- /// Find the message pieces from a Dart interpolated string.
- List _extractFromIntlCallWithInterpolation(
- MainMessage message, List<AstNode> arguments) {
- var interpolation = new InterpolationVisitor(message, extraction);
- arguments.first.accept(interpolation);
- if (interpolation.pieces.any((x) => x is Plural || x is Gender) &&
- !extraction.allowEmbeddedPluralsAndGenders) {
- if (interpolation.pieces.any((x) => x is String && x.isNotEmpty)) {
- throw new IntlMessageExtractionException(
- "Plural and gender expressions must be at the top level, "
- "they cannot be embedded in larger string literals.\n");
- }
- }
- return interpolation.pieces;
- }
-
- /// Create a MainMessage from [node] using the name and
- /// parameters of the last function/method declaration we encountered
- /// and the parameters to the Intl.message call.
- MainMessage messageFromIntlMessageCall(MethodInvocation node) {
- MainMessage extractFromIntlCall(
- MainMessage message, List<AstNode> arguments) {
- try {
- // The pieces of the message, either literal strings, or integers
- // representing the index of the argument to be substituted.
- List extracted;
- extracted = _extractFromIntlCallWithInterpolation(message, arguments);
- message.addPieces(extracted);
- } on IntlMessageExtractionException catch (e) {
- message = null;
- var err = new StringBuffer()
- ..writeAll(["Error ", e, "\nProcessing <", node, ">\n"])
- ..write(extraction._reportErrorLocation(node));
- var errString = err.toString();
- extraction.onMessage(errString);
- extraction.warnings.add(errString);
- }
- return message;
- }
-
- void setValue(MainMessage message, String fieldName, Object fieldValue) {
- message[fieldName] = fieldValue;
- }
-
- return _messageFromNode(node, extractFromIntlCall, setValue);
- }
-
- /// Create a MainMessage from [node] using the name and
- /// parameters of the last function/method declaration we encountered
- /// and the parameters to the Intl.plural or Intl.gender call.
- MainMessage messageFromDirectPluralOrGenderCall(MethodInvocation node) {
- MainMessage extractFromPluralOrGender(MainMessage message, _) {
- var visitor = new PluralAndGenderVisitor(
- message.messagePieces, message, extraction);
- node.accept(visitor);
- return message;
- }
-
- void setAttribute(MainMessage msg, String fieldName, fieldValue) {
- if (msg.attributeNames.contains(fieldName)) {
- msg[fieldName] = fieldValue;
- }
- }
- return _messageFromNode(node, extractFromPluralOrGender, setAttribute);
- }
-}
-
-/// Given an interpolation, find all of its chunks, validate that they are only
-/// simple variable substitutions or else Intl.plural/gender calls,
-/// and keep track of the pieces of text so that other parts
-/// of the program can deal with the simple string sections and the generated
-/// parts separately. Note that this is a SimpleAstVisitor, so it only
-/// traverses one level of children rather than automatically recursing. If we
-/// find a plural or gender, which requires recursion, we do it with a separate
-/// special-purpose visitor.
-class InterpolationVisitor extends SimpleAstVisitor {
- final Message message;
-
- /// The message extraction in which we are running.
- final MessageExtraction extraction;
-
- InterpolationVisitor(this.message, this.extraction);
-
- List pieces = [];
- String get extractedMessage => pieces.join();
-
- void visitAdjacentStrings(AdjacentStrings node) {
- node.visitChildren(this);
- super.visitAdjacentStrings(node);
- }
-
- void visitStringInterpolation(StringInterpolation node) {
- node.visitChildren(this);
- super.visitStringInterpolation(node);
- }
-
- void visitSimpleStringLiteral(SimpleStringLiteral node) {
- pieces.add(node.value);
- super.visitSimpleStringLiteral(node);
- }
-
- void visitInterpolationString(InterpolationString node) {
- pieces.add(node.value);
- super.visitInterpolationString(node);
- }
-
- void visitInterpolationExpression(InterpolationExpression node) {
- if (node.expression is SimpleIdentifier) {
- return handleSimpleInterpolation(node);
- } else {
- return lookForPluralOrGender(node);
- }
- // Note that we never end up calling super.
- }
-
- lookForPluralOrGender(InterpolationExpression node) {
- var visitor = new PluralAndGenderVisitor(pieces, message, extraction);
- node.accept(visitor);
- if (!visitor.foundPluralOrGender) {
- throw new IntlMessageExtractionException(
- "Only simple identifiers and Intl.plural/gender/select expressions "
- "are allowed in message "
- "interpolation expressions.\nError at $node");
- }
- }
-
- void handleSimpleInterpolation(InterpolationExpression node) {
- var index = arguments.indexOf(node.expression.toString());
- if (index == -1) {
- throw new IntlMessageExtractionException(
- "Cannot find argument ${node.expression}");
- }
- pieces.add(index);
- }
-
- List get arguments => message.arguments;
-}
-
-/// A visitor to extract information from Intl.plural/gender sends. Note that
-/// this is a SimpleAstVisitor, so it doesn't automatically recurse. So this
-/// needs to be called where we expect a plural or gender immediately below.
-class PluralAndGenderVisitor extends SimpleAstVisitor {
- /// The message extraction in which we are running.
- final MessageExtraction extraction;
-
- /// A plural or gender always exists in the context of a parent message,
- /// which could in turn also be a plural or gender.
- final ComplexMessage parent;
-
- /// The pieces of the message. We are given an initial version of this
- /// from our parent and we add to it as we find additional information.
- List pieces;
-
- /// This will be set to true if we find a plural or gender.
- bool foundPluralOrGender = false;
-
- PluralAndGenderVisitor(this.pieces, this.parent, this.extraction) : super();
-
- visitInterpolationExpression(InterpolationExpression node) {
- // TODO(alanknight): Provide better errors for malformed expressions.
- if (!looksLikePluralOrGender(node.expression)) return;
- var reason = checkValidity(node.expression);
- if (reason != null) throw reason;
- var message = messageFromMethodInvocation(node.expression);
- foundPluralOrGender = true;
- pieces.add(message);
- super.visitInterpolationExpression(node);
- }
-
- visitMethodInvocation(MethodInvocation node) {
- pieces.add(messageFromMethodInvocation(node));
- super.visitMethodInvocation(node);
- }
-
- /// Return true if [node] matches the pattern for plural or gender message.
- bool looksLikePluralOrGender(MethodInvocation node) {
- if (!["plural", "gender", "select"].contains(node.methodName.name)) {
- return false;
- }
- if (!(node.target is SimpleIdentifier)) return false;
- SimpleIdentifier target = node.target;
- return target.token.toString() == "Intl";
- }
-
- /// Returns a String describing why the node is invalid, or null if no
- /// reason is found, so it's presumed valid.
- String checkValidity(MethodInvocation node) {
- // TODO(alanknight): Add reasonable validity checks.
- return null;
- }
-
- /// Create a MainMessage from [node] using the name and
- /// parameters of the last function/method declaration we encountered
- /// and the parameters to the Intl.message call.
- Message messageFromMethodInvocation(MethodInvocation node) {
- var message;
- switch (node.methodName.name) {
- case "gender":
- message = new Gender();
- break;
- case "plural":
- message = new Plural();
- break;
- case "select":
- message = new Select();
- break;
- default:
- throw new IntlMessageExtractionException(
- "Invalid plural/gender/select message ${node.methodName.name} "
- "in $node");
- }
- message.parent = parent;
-
- var arguments = message.argumentsOfInterestFor(node);
- arguments.forEach((key, value) {
- try {
- var interpolation = new InterpolationVisitor(message, extraction);
- value.accept(interpolation);
- message[key] = interpolation.pieces;
- } on IntlMessageExtractionException catch (e) {
- message = null;
- var err = new StringBuffer()
- ..writeAll(["Error ", e, "\nProcessing <", node, ">"])
- ..write(extraction._reportErrorLocation(node));
- var errString = err.toString();
- extraction.onMessage(errString);
- extraction.warnings.add(errString);
- }
- });
- var mainArg = node.argumentList.arguments
- .firstWhere((each) => each is! NamedExpression);
- if (mainArg is SimpleStringLiteral) {
- message.mainArgument = mainArg.toString();
- } else if (mainArg is SimpleIdentifier) {
- message.mainArgument = mainArg.name;
- } else {
- var err = new 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);
- }
- return message;
- }
-}
-
-/// Exception thrown when we cannot process a message properly.
-class IntlMessageExtractionException implements Exception {
- /// A message describing the error.
- final String message;
-
- /// Creates a new exception with an optional error [message].
- const IntlMessageExtractionException([this.message = ""]);
-
- String toString() => "IntlMessageExtractionException: $message";
-}
diff --git a/lib/generate_localized.dart b/lib/generate_localized.dart
deleted file mode 100644
index f443ad7..0000000
--- a/lib/generate_localized.dart
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// This provides utilities for generating localized versions of
-/// messages. It does not stand alone, but expects to be given
-/// TranslatedMessage objects and generate code for a particular locale
-/// based on them.
-///
-/// An example of usage can be found
-/// in test/message_extract/generate_from_json.dart
-library generate_localized;
-
-import 'intl.dart';
-import 'src/intl_message.dart';
-import 'dart:io';
-import 'package:path/path.dart' as path;
-
-class MessageGeneration {
- /// If the import path following package: is something else, modify the
- /// [intlImportPath] variable to change the import directives in the generated
- /// code.
- var intlImportPath = 'intl';
-
- /// If the path to the generated files is something other than the current
- /// directory, update the [generatedImportPath] variable to change the import
- /// directives in the generated code.
- var generatedImportPath = '';
-
- /// Given a base file, return the file prefixed with the path to import it.
- /// By default, that is in the current directory, but if [generatedImportPath]
- /// has been set, then use that as a prefix.
- String importForGeneratedFile(String file) =>
- generatedImportPath.isEmpty ? file : "$generatedImportPath/$file";
-
- /// A list of all the locales for which we have translations. Code that does
- /// the reading of translations should add to this.
- List<String> allLocales = [];
-
- /// If we have more than one set of messages to generate in a particular
- /// directory we may want to prefix some to distinguish them.
- String generatedFilePrefix = '';
-
- /// Should we use deferred loading for the generated libraries.
- bool useDeferredLoading = true;
-
- /// Generate a file <[generated_file_prefix]>_messages_<[locale]>.dart
- /// for the [translations] in [locale] and put it in [targetDir].
- void generateIndividualMessageFile(String basicLocale,
- Iterable<TranslatedMessage> translations, String targetDir) {
- var result = new StringBuffer();
- var locale = new MainMessage()
- .escapeAndValidateString(Intl.canonicalizedLocale(basicLocale));
- result.write(prologue(locale));
- // 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 && each.message != null)
- .toList();
- for (var each in usableTranslations) {
- for (var original in each.originalMessages) {
- original.addTranslation(locale, each.message);
- }
- }
- usableTranslations.sort((a, b) =>
- a.originalMessages.first.name.compareTo(b.originalMessages.first.name));
- 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((each) => _hasArguments(each));
- for (var original in messagesThatNeedMethods) {
- result
- ..write(" ")
- ..write(
- original.toCodeForLocale(locale, _methodNameFor(original.name)))
- ..write("\n\n");
- }
- }
- // Some gyrations to prevent parts of the deferred libraries from being
- // inlined into the main one, defeating the space savings. Issue 24356
- result.write("""
- final messages = _notInlinedMessages(_notInlinedMessages);
- static _notInlinedMessages(_) => {
-""");
- var entries = usableTranslations
- .expand((translation) => translation.originalMessages)
- .map((original) =>
- ' "${original.escapeAndValidateString(original.name)}" '
- ': ${_mapReference(original, locale)}');
- result..write(entries.join(",\n"))..write("\n };\n}\n");
-
- // To preserve compatibility, we don't use the canonical version of the
- // locale in the file name.
- var filename = path.join(
- targetDir, "${generatedFilePrefix}messages_$basicLocale.dart");
- new File(filename).writeAsStringSync(result.toString());
- }
-
- /// This returns the mostly constant string used in
- /// [generateIndividualMessageFile] for the beginning of the file,
- /// parameterized by [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
-// function name.
-
-import 'package:$intlImportPath/intl.dart';
-import 'package:$intlImportPath/message_lookup_by_library.dart';
-
-final messages = new MessageLookup();
-
-final _keepAnalysisHappy = Intl.defaultLocale;
-
-class MessageLookup extends MessageLookupByLibrary {
- get localeName => '$locale';
-
-""";
-
- /// This section generates the messages_all.dart file based on the list of
- /// [allLocales].
- String generateMainImportFile() {
- var output = new StringBuffer();
- output.write(mainPrologue);
- for (var locale in allLocales) {
- var baseFile = '${generatedFilePrefix}messages_$locale.dart';
- var file = importForGeneratedFile(baseFile);
- output.write("import '$file' ");
- if (useDeferredLoading) output.write("deferred ");
- output.write("as ${_libraryName(locale)};\n");
- }
- output.write("\n");
- output.write("Map<String, Function> _deferredLibraries = {\n");
- for (var rawLocale in allLocales) {
- var locale = Intl.canonicalizedLocale(rawLocale);
- var loadOperation = (useDeferredLoading)
- ? " '$locale': () => ${_libraryName(locale)}.loadLibrary(),\n"
- : " '$locale': () => new Future.value(null),\n";
- output.write(loadOperation);
- }
- output.write("};\n");
- output.write("\nMessageLookupByLibrary _findExact(localeName) {\n"
- " switch (localeName) {\n");
- for (var rawLocale in allLocales) {
- var locale = Intl.canonicalizedLocale(rawLocale);
- output.write(
- " case '$locale':\n return ${_libraryName(locale)}.messages;\n");
- }
- output.write(closing);
- return output.toString();
- }
-
- /// Constant string used in [generateMainImportFile] for the beginning of the
- /// file.
- 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.
-
-import 'dart:async';
-
-import 'package:$intlImportPath/intl.dart';
-import 'package:$intlImportPath/message_lookup_by_library.dart';
-import 'package:$intlImportPath/src/intl_helpers.dart';
-
-""";
-
- /// Constant string used in [generateMainImportFile] as the end of the file.
- static const closing = """
- default:\n return null;
- }
-}
-
-/// User programs should call this before using [localeName] for messages.
-Future initializeMessages(String localeName) {
- var lib = _deferredLibraries[Intl.canonicalizedLocale(localeName)];
- var load = lib == null ? new Future.value(false) : lib();
- return load.then((_) {
- initializeInternalMessageLookup(() => new CompositeMessageLookup());
- messageLookup.addLocale(localeName, _findGeneratedMessagesFor);
- });
-}
-
-bool _messagesExistFor(String locale) {
- var messages;
- try {
- messages = _findExact(locale);
- } catch (e) {}
- return messages != null;
-}
-
-MessageLookupByLibrary _findGeneratedMessagesFor(locale) {
- var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
- onFailure: (_) => null);
- if (actualLocale == null) return null;
- return _findExact(actualLocale);
-}
-""";
-}
-
-/// This represents a message and its translation. We assume that the
-/// translation has some identifier that allows us to figure out the original
-/// message it corresponds to, and that it may want to transform the translated
-/// text in some way, e.g. to turn whatever format the translation uses for
-/// variables into a Dart string interpolation. Specific translation mechanisms
-/// are expected to subclass this.
-abstract class TranslatedMessage {
- /// The identifier for this message. In the simplest case, this is the name
- /// parameter from the Intl.message call,
- /// but it can be any identifier that this program and the output of the
- /// translation can agree on as identifying a message.
- final String id;
-
- /// Our translated version of all the [originalMessages].
- final Message translated;
-
- /// The original messages that we are a translation of. There can
- /// be more than one original message for the same translation.
- List<MainMessage> _originalMessages;
-
- List<MainMessage> get originalMessages => _originalMessages;
- set originalMessages(List<MainMessage> x) {
- _originalMessages = x;
- }
-
- /// For backward compatibility, we still have the originalMessage API.
- MainMessage get originalMessage => originalMessages.first;
- set originalMessage(MainMessage m) {
- originalMessages = [m];
- }
-
- TranslatedMessage(this.id, this.translated);
-
- Message get message => translated;
-
- toString() => id.toString();
-}
-
-/// We can't use a hyphen in a Dart library name, so convert the locale
-/// separator to an underscore.
-String _libraryName(String x) => 'messages_' + x.replaceAll('-', '_');
-
-bool _hasArguments(MainMessage message) => message.arguments.length != 0;
-
-/// Simple messages are printed directly in the map of message names to
-/// functions as a call that returns a lambda. e.g.
-///
-/// "foo" : simpleMessage("This is foo"),
-///
-/// This is helpful for the compiler.
-/// */
-String _mapReference(MainMessage original, String locale) {
- if (!_hasArguments(original)) {
- // No parameters, can be printed simply.
- return 'MessageLookupByLibrary.simpleMessage("'
- '${original.translations[locale]}")';
- } else {
- return _methodNameFor(original.name);
- }
-}
-
-/// Generated method counter for use in [_methodNameFor].
-int _methodNameCounter = 0;
-
-/// A map from Intl message names to the generated method names
-/// for their translated versions.
-Map<String, String> _internalMethodNames = {};
-
-/// Generate a Dart method name of the form "m<number>".
-String _methodNameFor(String name) {
- return _internalMethodNames.putIfAbsent(
- name, () => "m${_methodNameCounter++}");
-}
diff --git a/lib/src/icu_parser.dart b/lib/src/icu_parser.dart
deleted file mode 100644
index 8603a3b..0000000
--- a/lib/src/icu_parser.dart
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// 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;
-
-import 'package:intl/src/intl_message.dart';
-import 'package:petitparser/petitparser.dart';
-
-/// This defines a grammar for ICU MessageFormat syntax. Usage is
-/// new IcuParser.message.parse(<string>).value;
-/// The "parse" method will return a Success or Failure object which responds
-/// to "value".
-class IcuParser {
- get openCurly => char("{");
-
- get closeCurly => char("}");
- get quotedCurly => (string("'{'") | string("'}'")).map((x) => x[1]);
-
- get icuEscapedText => quotedCurly | twoSingleQuotes;
- get curly => (openCurly | closeCurly);
- get notAllowedInIcuText => curly | char("<");
- get icuText => notAllowedInIcuText.neg();
- get notAllowedInNormalText => char("{");
- get normalText => notAllowedInNormalText.neg();
- get messageText => (icuEscapedText | icuText).plus().map((x) => x.join());
- get nonIcuMessageText => normalText.plus().map((x) => x.join());
- get twoSingleQuotes => string("''").map((x) => "'");
- get number => digit().plus().flatten().trim().map(int.parse);
- get id => (letter() & (word() | char("_")).star()).flatten().trim();
- get comma => char(",").trim();
-
- /// Given a list of possible keywords, return a rule that accepts any of them.
- /// e.g., given ["male", "female", "other"], accept any of them.
- asKeywords(list) => list.map(string).reduce((a, b) => a | b).flatten().trim();
-
- get pluralKeyword => asKeywords(
- ["=0", "=1", "=2", "zero", "one", "two", "few", "many", "other"]);
- get genderKeyword => asKeywords(["female", "male", "other"]);
-
- var interiorText = undefined();
-
- get preface => (openCurly & id & comma).map((values) => values[1]);
-
- get pluralLiteral => string("plural");
- get pluralClause => (pluralKeyword & openCurly & interiorText & closeCurly)
- .trim()
- .map((result) => [result[0], result[2]]);
- get plural =>
- preface & pluralLiteral & comma & pluralClause.plus() & closeCurly;
- get intlPlural =>
- plural.map((values) => new Plural.from(values.first, values[3], null));
-
- get selectLiteral => string("select");
- get genderClause => (genderKeyword & openCurly & interiorText & closeCurly)
- .trim()
- .map((result) => [result[0], result[2]]);
- get gender =>
- preface & selectLiteral & comma & genderClause.plus() & closeCurly;
- get intlGender =>
- gender.map((values) => new Gender.from(values.first, values[3], null));
- get selectClause =>
- (id & openCurly & interiorText & closeCurly).map((x) => [x.first, x[2]]);
- get generalSelect =>
- preface & selectLiteral & comma & selectClause.plus() & closeCurly;
- get intlSelect => generalSelect
- .map((values) => new Select.from(values.first, values[3], null));
-
- get pluralOrGenderOrSelect => intlPlural | intlGender | intlSelect;
-
- get contents => pluralOrGenderOrSelect | parameter | messageText;
- get simpleText => (nonIcuMessageText | parameter | openCurly).plus();
- get empty => epsilon().map((_) => '');
-
- get parameter => (openCurly & id & closeCurly)
- .map((param) => new VariableSubstitution.named(param[1], null));
-
- /// The primary entry point for parsing. Accepts a string and produces
- /// a parsed representation of it as a Message.
- get message => (pluralOrGenderOrSelect | empty)
- .map((chunk) => Message.from(chunk, null));
-
- /// Represents an ordinary message, i.e. not a plural/gender/select, although
- /// it may have parameters.
- get nonIcuMessage =>
- (simpleText | empty).map((chunk) => Message.from(chunk, null));
-
- get stuff => (pluralOrGenderOrSelect | empty)
- .map((chunk) => Message.from(chunk, null));
-
- IcuParser() {
- // There is a cycle here, so we need the explicit set to avoid
- // infinite recursion.
- interiorText.set(contents.plus() | empty);
- }
-}
diff --git a/lib/src/intl_helpers.dart b/lib/src/intl_helpers.dart
index 8e3c0f0..9d9cb5f 100644
--- a/lib/src/intl_helpers.dart
+++ b/lib/src/intl_helpers.dart
@@ -24,8 +24,8 @@
operator [](String key) =>
(key == 'en_US') ? fallbackData : _throwException();
- String lookupMessage(
- String message_str, String locale, String name, List args, String meaning,
+ String lookupMessage(String message_str, String locale, String name,
+ List args, String meaning,
{MessageIfAbsent ifAbsent}) =>
message_str;
@@ -78,10 +78,10 @@
}
}
- /// If a message is a string literal without interpolation, compute
- /// a name based on that and the meaning, if present.
- String computeMessageName(String name, String message_str, String meaning) {
- if (name != null && name != "") return name;
- var simpleName = message_str;
- return meaning == null ? simpleName : "${simpleName}_${meaning}";
- }
+/// If a message is a string literal without interpolation, compute
+/// a name based on that and the meaning, if present.
+// NOTE: THIS LOGIC IS DUPLICATED IN intl_translation AND THE TWO MUST MATCH.
+String computeMessageName(String name, String text, String meaning) {
+ if (name != null && name != "") return name;
+ return meaning == null ? text : "${text}_${meaning}";
+}
diff --git a/lib/src/intl_message.dart b/lib/src/intl_message.dart
deleted file mode 100644
index 45ac177..0000000
--- a/lib/src/intl_message.dart
+++ /dev/null
@@ -1,801 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// This provides classes to represent the internal structure of the
-/// arguments to `Intl.message`. It is used when parsing sources to extract
-/// messages or to generate code for message substitution. Normal programs
-/// using Intl would not import this library.
-///
-/// While it's written
-/// in a somewhat abstract way, it has some assumptions about ICU-style
-/// message syntax for parameter substitutions, choices, selects, etc.
-///
-/// For example, if we have the message
-/// plurals(num) => Intl.message("""${Intl.plural(num,
-/// zero : 'Is zero plural?',
-/// one : 'This is singular.',
-/// other : 'This is plural ($num).')
-/// }""",
-/// name: "plurals", args: [num], desc: "Basic plurals");
-/// That is represented as a MainMessage which has only one message component, a
-/// Plural, but also has a name, list of arguments, and a description.
-/// The Plural has three different clauses. The `zero` clause is
-/// a LiteralString containing 'Is zero plural?'. The `other` clause is a
-/// CompositeMessage containing three pieces, a LiteralString for
-/// 'This is plural (', a VariableSubstitution for `num`. amd a LiteralString
-/// for '.)'.
-///
-/// 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;
-
-import 'dart:convert';
-import 'package:analyzer/analyzer.dart';
-
-/// A default function for the [Message.expanded] method.
-_nullTransform(msg, chunk) => chunk;
-
-/// An abstract superclass for Intl.message/plural/gender calls in the
-/// program's source text. We
-/// assemble these into objects that can be used to write out some translation
-/// format and can also print themselves into code.
-abstract class Message {
- /// All [Message]s except a [MainMessage] are contained inside some parent,
- /// terminating at an Intl.message call which supplies the arguments we
- /// use for variable substitutions.
- Message parent;
-
- Message(this.parent);
-
- /// 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;
-
- /// 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;
-
- /// The name of the top-level [MainMessage].
- String get name => parent == null ? '<unnamed>' : parent.name;
-
- static final _evaluator = new ConstantEvaluator();
-
- String _evaluateAsString(expression) {
- var result = expression.accept(_evaluator);
- if (result == ConstantEvaluator.NOT_A_CONSTANT || result is! String) {
- return null;
- } else {
- return result;
- }
- }
-
- Map _evaluateAsMap(expression) {
- var result = expression.accept(_evaluator);
- if (result == ConstantEvaluator.NOT_A_CONSTANT || result is! Map) {
- return null;
- } else {
- return result;
- }
- }
-
- /// Verify that this looks like a correct
- /// Intl.message/plural/gender/... invocation.
- ///
- /// We expect an invocation like
- ///
- /// outerName(x) => Intl.message("foo \$x", ...)
- ///
- /// The [node] parameter is the Intl.message invocation node in the AST,
- /// [arguments] is the list of arguments to that node (also reachable as
- /// node.argumentList.arguments), [outerName] is the name of the containing
- /// function, e.g. "outerName" in this case and [outerArgs] is the list of
- /// arguments to that function. Of the optional parameters
- /// [nameAndArgsGenerated] indicates if we are generating names and arguments
- /// while rewriting the code in the transformer or a development-time rewrite,
- /// so we should not expect them to be present. The [examplesRequired]
- /// parameter indicates if we will fail if parameter examples are not provided
- /// for messages with parameters.
- String checkValidity(MethodInvocation node, List arguments, String outerName,
- FormalParameterList outerArgs,
- {bool nameAndArgsGenerated: false, bool examplesRequired: false}) {
- // If we have parameters, we must specify args and name.
- var hasArgs = arguments.any(
- (each) => each is NamedExpression && each.name.label.name == 'args');
- var hasParameters = !outerArgs.parameters.isEmpty;
- if (!nameAndArgsGenerated && !hasArgs && hasParameters) {
- return "The 'args' argument for Intl.message must be specified";
- }
-
- var messageNameArgument = arguments.firstWhere(
- (eachArg) =>
- eachArg is NamedExpression && eachArg.name.label.name == 'name',
- orElse: () => null);
- var nameExpression = messageNameArgument?.expression;
- String messageName;
- String givenName;
-
- //TODO(alanknight): If we generalize this to messages with parameters
- // this check will need to change.
- if (nameExpression == null) {
- if (!hasParameters) {
- // No name supplied, no parameters. Use the message as the name.
- messageName = _evaluateAsString(arguments[0]);
- outerName = messageName;
- } else {
- // We have no name and parameters, but the transformer generates the
- // name.
- if (nameAndArgsGenerated) {
- givenName = outerName;
- messageName = givenName;
- } else {
- return "The 'name' argument for Intl.message must be supplied for "
- "messages with parameters";
- }
- }
- } else {
- // Name argument is supplied, use it.
- givenName = _evaluateAsString(nameExpression);
- messageName = givenName;
- }
-
- if (messageName == null) {
- return "The 'name' argument for Intl.message must be a string literal";
- }
-
- var hasOuterName = outerName != null;
- var simpleMatch = outerName == givenName || givenName == null;
-
- var classPlusMethod = Message.classPlusMethodName(node, outerName);
- var classMatch = classPlusMethod != null && (givenName == classPlusMethod);
- if (!(hasOuterName && (simpleMatch || classMatch))) {
- return "The 'name' argument for Intl.message must match either "
- "the name of the containing function or <ClassName>_<methodName> ("
- "was '$givenName' but must be '$outerName' or '$classPlusMethod')";
- }
-
- var simpleArguments = arguments.where((each) =>
- each is NamedExpression &&
- ["desc", "name"].contains(each.name.label.name));
- var values = simpleArguments.map((each) => each.expression).toList();
- for (var arg in values) {
- if (_evaluateAsString(arg) == null) {
- return ("Intl.message arguments must be string literals: $arg");
- }
- }
-
- if (hasParameters && examplesRequired) {
- var exampleArg = arguments.where((each) =>
- each is NamedExpression && each.name.label.name == "examples");
- var examples = exampleArg.map((each) => each.expression).toList();
- if (examples.isEmpty) {
- return "Examples must be provided for messages with parameters";
- }
- var map = _evaluateAsMap(examples.first);
- if (map == null) {
- return "Examples must be a Map literal, preferably const";
- }
- }
-
- return null;
- }
-
- /// Return the name of the enclosing class (if any) plus method name, or null
- /// if there's no enclosing class.
- ///
- /// For a method foo in class Bar we allow either "foo" or "Bar_Foo" as the
- /// name.
- static String classPlusMethodName(MethodInvocation node, String outerName) {
- ClassDeclaration classNode(n) {
- if (n == null) return null;
- if (n is ClassDeclaration) return n;
- return classNode(n.parent);
- }
- var classDeclaration = classNode(node);
- return classDeclaration == null
- ? null
- : "${classDeclaration.name.token}_$outerName";
- }
-
- /// Turn a value, typically read from a translation file or created out of an
- /// AST for a source program, into the appropriate
- /// subclass. We expect to get literal Strings, variable substitutions
- /// represented by integers, things that are already MessageChunks and
- /// lists of the same.
- static Message from(Object value, Message parent) {
- if (value is String) return new LiteralString(value, parent);
- if (value is int) return new VariableSubstitution(value, parent);
- if (value is List) {
- if (value.length == 1) return Message.from(value[0], parent);
- var result = new CompositeMessage([], parent);
- var items = value.map((x) => from(x, result)).toList();
- result.pieces.addAll(items);
- return result;
- }
- // We assume this is already a Message.
- Message mustBeAMessage = value;
- mustBeAMessage.parent = parent;
- return mustBeAMessage;
- }
-
- /// Return a string representation of this message for use in generated Dart
- /// code.
- String toCode();
-
- /// Escape the string for use in generated Dart code.
- String escapeAndValidateString(String value) {
- const Map<String, String> escapes = const {
- r"\": r"\\",
- '"': r'\"',
- "\b": r"\b",
- "\f": r"\f",
- "\n": r"\n",
- "\r": r"\r",
- "\t": r"\t",
- "\v": r"\v",
- "'": r"\'",
- r"$": r"\$"
- };
-
- String _escape(String s) => escapes[s] ?? s;
-
- var escaped = value.splitMapJoin("", onNonMatch: _escape);
- return escaped;
- }
-
- /// Expand this string out into a printed form. The function [f] will be
- /// applied to any sub-messages, allowing this to be used to generate a form
- /// suitable for a wide variety of translation file formats.
- String expanded([Function f]);
-}
-
-/// Abstract class for messages with internal structure, representing the
-/// main Intl.message call, plurals, and genders.
-abstract class ComplexMessage extends Message {
- ComplexMessage(parent) : super(parent);
-
- /// 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);
-
- /// 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);
-
- List<String> get attributeNames;
-
- /// Return the name of the message type, as it will be generated into an
- /// ICU-type format. e.g. choice, select
- String get icuMessageName;
-
- /// Return the message name we would use for this when doing Dart code
- /// generation, e.g. "Intl.plural".
- String get dartMessageName;
-}
-
-/// This represents a message chunk that is a list of multiple sub-pieces,
-/// each of which is in turn a [Message].
-class CompositeMessage extends Message {
- List<Message> pieces;
-
- CompositeMessage.withParent(parent) : super(parent);
- CompositeMessage(this.pieces, ComplexMessage parent) : super(parent) {
- pieces.forEach((x) => x.parent = this);
- }
- toCode() => pieces.map((each) => each.toCode()).join('');
- toString() => "CompositeMessage(" + pieces.toString() + ")";
- String expanded([Function f = _nullTransform]) =>
- pieces.map((chunk) => f(this, chunk)).join("");
-}
-
-/// Represents a simple constant string with no dynamic elements.
-class LiteralString extends Message {
- String string;
- LiteralString(this.string, Message parent) : super(parent);
- toCode() => escapeAndValidateString(string);
- toString() => "Literal($string)";
- String expanded([Function f = _nullTransform]) => f(this, string);
-}
-
-/// Represents an interpolation of a variable value in a message. We expect
-/// this to be specified as an [index] into the list of variables, or else
-/// as the name of a variable that exists in [arguments] and we will
-/// compute the variable name or the index based on the value of the other.
-class VariableSubstitution extends Message {
- VariableSubstitution(this._index, Message parent) : super(parent);
-
- /// Create a substitution based on the name rather than the index. The name
- /// may have been used as all upper-case in the translation tool, so we
- /// save it separately and look it up case-insensitively once the parent
- /// (and its arguments) are definitely available.
- VariableSubstitution.named(String name, Message parent) : super(parent) {
- _variableNameUpper = name.toUpperCase();
- }
-
- /// The index in the list of parameters of the containing function.
- int _index;
- int get index {
- if (_index != null) return _index;
- if (arguments.isEmpty) return null;
- // We may have been given an all-uppercase version of the name, so compare
- // case-insensitive.
- _index = arguments
- .map((x) => x.toUpperCase())
- .toList()
- .indexOf(_variableNameUpper);
- if (_index == -1) {
- throw new ArgumentError(
- "Cannot find parameter named '$_variableNameUpper' in "
- "message named '$name'. Available "
- "parameters are $arguments");
- }
- return _index;
- }
-
- /// The variable name we get from parsing. This may be an all uppercase
- /// version of the Dart argument name.
- String _variableNameUpper;
-
- /// The name of the variable in the parameter list of the containing function.
- /// Used when generating code for the interpolation.
- String get variableName =>
- _variableName == null ? _variableName = arguments[index] : _variableName;
- String _variableName;
- // Although we only allow simple variable references, we always enclose them
- // in curly braces so that there's no possibility of ambiguity with
- // surrounding text.
- toCode() => "\${${variableName}}";
- toString() => "VariableSubstitution($index)";
- String expanded([Function f = _nullTransform]) => f(this, index);
-}
-
-class MainMessage extends ComplexMessage {
- MainMessage() : super(null);
-
- /// All the pieces of the message. When we go to print, these will
- /// all be expanded appropriately. The exact form depends on what we're
- /// printing it for See [expanded], [toCode].
- List<Message> messagePieces = [];
-
- /// The position in the source at which this message starts.
- int sourcePosition;
-
- /// The position in the source at which this message ends.
- int endPosition;
-
- /// Verify that this looks like a correct Intl.message invocation.
- String checkValidity(MethodInvocation node, List arguments, String outerName,
- FormalParameterList outerArgs,
- {bool nameAndArgsGenerated: false, bool examplesRequired: false}) {
- if (arguments.first is! StringLiteral) {
- return "Intl.message messages must be string literals";
- }
-
- return super.checkValidity(node, arguments, outerName, outerArgs,
- nameAndArgsGenerated: nameAndArgsGenerated,
- examplesRequired: examplesRequired);
- }
-
- void addPieces(List<Object> messages) {
- for (var each in messages) {
- messagePieces.add(Message.from(each, this));
- }
- }
-
- /// The description provided in the Intl.message call.
- String description;
-
- /// The examples from the Intl.message call
- Map<String, dynamic> examples;
-
- /// A field to disambiguate two messages that might have exactly the
- /// same text. The two messages will also need different names, but
- /// this can be used by machine translation tools to distinguish them.
- String meaning;
-
- /// The name, which may come from the function name, from the arguments
- /// to Intl.message, or we may just re-use the message.
- String _name;
-
- /// A placeholder for any other identifier that the translation format
- /// may want to use.
- String id;
-
- /// The arguments list from the Intl.message call.
- List<String> arguments;
-
- /// The locale argument from the Intl.message call
- String locale;
-
- /// When generating code, we store translations for each locale
- /// associated with the original message.
- Map<String, String> translations = new Map();
-
- /// If the message was not given a name, we use the entire message string as
- /// the name.
- String get name => _name ?? "";
- set name(String newName) {
- _name = newName;
- }
-
- /// Does this message have an assigned name.
- bool get hasName => _name != null;
-
- /// Return the full message, with any interpolation expressions transformed
- /// by [f] and all the results concatenated. The chunk argument to [f] may be
- /// either a String, an int or an object representing a more complex
- /// message entity.
- /// See [messagePieces].
- String expanded([Function f = _nullTransform]) =>
- messagePieces.map((chunk) => f(this, chunk)).join("");
-
- /// Record the translation for this message in the given locale, after
- /// suitably escaping it.
- void addTranslation(String locale, Message translated) {
- translated.parent = this;
- translations[locale] = translated.toCode();
- }
-
- toCode() =>
- throw new UnsupportedError("MainMessage.toCode requires a locale");
-
- /// Generate code for this message, expecting it to be part of a map
- /// keyed by name with values the function that calls Intl.message.
- String toCodeForLocale(String locale, String name) {
- var out = new StringBuffer()
- ..write('static $name(')
- ..write(arguments.join(", "))
- ..write(') => "')
- ..write(translations[locale])
- ..write('";');
- return out.toString();
- }
-
- turnInterpolationBackIntoStringForm(Message message, chunk) {
- if (chunk is String) return escapeAndValidateString(chunk);
- if (chunk is int) return r"${" + message.arguments[chunk] + "}";
- if (chunk is Message) return chunk.toCode();
- throw new ArgumentError.value(chunk, "Unexpected value in Intl.message");
- }
-
- String toOriginalCode() {
- var out = new StringBuffer()..write("Intl.message('");
- out.write(expanded(turnInterpolationBackIntoStringForm));
- out.write("', ");
- out.write("name: '$name', ");
- out.write(locale == null ? "" : "locale: '$locale', ");
- out.write(description == null
- ? ""
- : "desc: '${escapeAndValidateString(description)}', ");
- // json is already mostly-escaped, but we need to handle interpolations.
- var json = JSON.encode(examples).replaceAll(r"$", r"\$");
- out.write(examples == null ? "" : "examples: const ${json}, ");
- out.write(meaning == null
- ? ""
- : "meaning: '${escapeAndValidateString(meaning)}', ");
- out.write("args: [${arguments.join(', ')}]");
- out.write(")");
- return out.toString();
- }
-
- /// The AST node will have the attribute names as strings, so we translate
- /// between those and the fields of the class.
- void operator []=(String attributeName, value) {
- switch (attributeName) {
- case "desc":
- description = value;
- return;
- case "examples":
- examples = value as Map<String, dynamic>;
- return;
- case "name":
- name = value;
- return;
- // We use the actual args from the parser rather than what's given in the
- // arguments to Intl.message.
- case "args":
- return;
- case "meaning":
- meaning = value;
- return;
- case "locale":
- locale = value;
- return;
- default:
- return;
- }
- }
-
- /// The AST node will have the attribute names as strings, so we translate
- /// between those and the fields of the class.
- operator [](String attributeName) {
- switch (attributeName) {
- case "desc":
- return description;
- case "examples":
- return examples;
- case "name":
- return name;
- // We use the actual args from the parser rather than what's given in the
- // arguments to Intl.message.
- case "args":
- return [];
- case "meaning":
- return meaning;
- default:
- return null;
- }
- }
-
- // This is the top-level construct, so there's no meaningful ICU name.
- get icuMessageName => '';
-
- get dartMessageName => "message";
-
- /// The parameters that the Intl.message call may provide.
- get attributeNames => const ["name", "desc", "examples", "args", "meaning"];
-
- String toString() =>
- "Intl.message(${expanded()}, $name, $description, $examples, $arguments)";
-}
-
-/// An abstract class to represent sub-sections of a message, primarily
-/// plurals and genders.
-abstract class SubMessage extends ComplexMessage {
- SubMessage() : super(null);
-
- /// Creates the sub-message, given a list of [clauses] in the sort of form
- /// that we're likely to get them from parsing a translation file format,
- /// as a list of [key, value] where value may in turn be a list.
- SubMessage.from(this.mainArgument, List clauses, parent) : super(parent) {
- for (var clause in clauses) {
- this[clause.first] = (clause.last is List) ? clause.last : [clause.last];
- }
- }
-
- toString() => expanded();
-
- /// The name of the main argument, which is expected to have the value which
- /// is one of [attributeNames] and is used to decide which clause to use.
- String mainArgument;
-
- /// Return the arguments that affect this SubMessage as a map of
- /// argument names and values.
- Map argumentsOfInterestFor(MethodInvocation node) {
- var basicArguments = node.argumentList.arguments;
- var others = basicArguments.where((each) => each is NamedExpression);
- return new Map.fromIterable(others,
- key: (node) => node.name.label.token.value(),
- value: (node) => node.expression);
- }
-
- /// Return the list of attribute names to use when generating code. This
- /// may be different from [attributeNames] if there are multiple aliases
- /// that map to the same clause.
- List<String> get codeAttributeNames;
-
- String expanded([Function transform = _nullTransform]) {
- fullMessageForClause(String key) =>
- key + '{' + transform(parent, this[key]).toString() + '}';
- var clauses = attributeNames
- .where((key) => this[key] != null)
- .map(fullMessageForClause)
- .toList();
- return "{$mainArgument,$icuMessageName, ${clauses.join("")}}";
- }
-
- String toCode() {
- var out = new StringBuffer();
- out.write('\${');
- out.write(dartMessageName);
- out.write('(');
- out.write(mainArgument);
- var args = codeAttributeNames.where((attribute) => this[attribute] != null);
- args.fold(
- out, (buffer, arg) => buffer..write(", $arg: '${this[arg].toCode()}'"));
- out.write(")}");
- return out.toString();
- }
-}
-
-/// Represents a message send of [Intl.gender] inside a message that is to
-/// be internationalized. This corresponds to an ICU message syntax "select"
-/// with "male", "female", and "other" as the possible options.
-class Gender extends SubMessage {
- Gender();
-
- /// Create a new Gender providing [mainArgument] and the list of possible
- /// clauses. Each clause is expected to be a list whose first element is a
- /// variable name and whose second element is either a [String] or
- /// a list of strings and [Message] or [VariableSubstitution].
- Gender.from(String mainArgument, List clauses, Message parent)
- : super.from(mainArgument, clauses, parent);
-
- Message female;
- Message male;
- Message other;
-
- String get icuMessageName => "select";
- String get dartMessageName => 'Intl.gender';
-
- get attributeNames => ["female", "male", "other"];
- get codeAttributeNames => attributeNames;
-
- /// The node will have the attribute names as strings, so we translate
- /// between those and the fields of the class.
- void operator []=(String attributeName, rawValue) {
- var value = Message.from(rawValue, this);
- switch (attributeName) {
- case "female":
- female = value;
- return;
- case "male":
- male = value;
- return;
- case "other":
- other = value;
- return;
- default:
- return;
- }
- }
-
- Message operator [](String attributeName) {
- switch (attributeName) {
- case "female":
- return female;
- case "male":
- return male;
- case "other":
- return other;
- default:
- return other;
- }
- }
-}
-
-class Plural extends SubMessage {
- Plural();
- Plural.from(String mainArgument, List clauses, Message parent)
- : super.from(mainArgument, clauses, parent);
-
- Message zero;
- Message one;
- Message two;
- Message few;
- Message many;
- Message other;
-
- String get icuMessageName => "plural";
- String get dartMessageName => "Intl.plural";
-
- get attributeNames => ["=0", "=1", "=2", "few", "many", "other"];
- get codeAttributeNames => ["zero", "one", "two", "few", "many", "other"];
-
- /// The node will have the attribute names as strings, so we translate
- /// between those and the fields of the class.
- void operator []=(String attributeName, rawValue) {
- var value = Message.from(rawValue, this);
- switch (attributeName) {
- case "zero":
- zero = value;
- return;
- case "=0":
- zero = value;
- return;
- case "one":
- one = value;
- return;
- case "=1":
- one = value;
- return;
- case "two":
- two = value;
- return;
- case "=2":
- two = value;
- return;
- case "few":
- few = value;
- return;
- case "many":
- many = value;
- return;
- case "other":
- other = value;
- return;
- default:
- return;
- }
- }
-
- Message operator [](String attributeName) {
- switch (attributeName) {
- case "zero":
- return zero;
- case "=0":
- return zero;
- case "one":
- return one;
- case "=1":
- return one;
- case "two":
- return two;
- case "=2":
- return two;
- case "few":
- return few;
- case "many":
- return many;
- case "other":
- return other;
- default:
- return other;
- }
- }
-}
-
-/// Represents a message send of [Intl.select] inside a message that is to
-/// be internationalized. This corresponds to an ICU message syntax "select"
-/// with arbitrary options.
-class Select extends SubMessage {
- Select();
-
- /// Create a new [Select] providing [mainArgument] and the list of possible
- /// clauses. Each clause is expected to be a list whose first element is a
- /// variable name and whose second element is either a String or
- /// a list of strings and [Message]s or [VariableSubstitution]s.
- Select.from(String mainArgument, List clauses, Message parent)
- : super.from(mainArgument, clauses, parent);
-
- Map<String, Message> cases = new Map<String, Message>();
-
- String get icuMessageName => "select";
- String get dartMessageName => 'Intl.select';
-
- get attributeNames => cases.keys;
- get codeAttributeNames => attributeNames;
-
- void operator []=(String attributeName, rawValue) {
- var value = Message.from(rawValue, this);
- cases[attributeName] = value;
- }
-
- Message operator [](String attributeName) {
- var exact = cases[attributeName];
- return exact == null ? cases["other"] : exact;
- }
-
- /// Return the arguments that we care about for the select. In this
- /// case they will all be passed in as a Map rather than as the named
- /// arguments used in Plural/Gender.
- Map argumentsOfInterestFor(MethodInvocation node) {
- MapLiteral casesArgument = node.argumentList.arguments[1];
- return new Map.fromIterable(casesArgument.entries,
- key: (node) => node.key.value, value: (node) => node.value);
- }
-
- /// Write out the generated representation of this message. This differs
- /// from Plural/Gender in that it prints a literal map rather than
- /// named arguments.
- String toCode() {
- var out = new StringBuffer();
- out.write('\${');
- out.write(dartMessageName);
- out.write('(');
- out.write(mainArgument);
- var args = codeAttributeNames;
- out.write(", {");
- args.fold(out,
- (buffer, arg) => buffer..write("'$arg': '${this[arg].toCode()}', "));
- out.write("})}");
- return out.toString();
- }
-}
diff --git a/lib/src/message_rewriter.dart b/lib/src/message_rewriter.dart
deleted file mode 100644
index 0d512bd..0000000
--- a/lib/src/message_rewriter.dart
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// Code to rewrite Intl.message calls adding the name and args parameters
-/// automatically, primarily used by the transformer.
-import 'package:analyzer/analyzer.dart';
-
-import 'package:intl/extract_messages.dart';
-
-/// Rewrite all Intl.message/plural/etc. calls in [source], adding "name"
-/// and "args" parameters if they are not provided.
-///
-/// Return the modified source code. If there are errors parsing, list
-/// [sourceName] in the error message.
-String rewriteMessages(String source, String sourceName) {
- var messages = findMessages(source, sourceName);
- messages.sort((a, b) => a.sourcePosition.compareTo(b.sourcePosition));
-
- var start = 0;
- var newSource = new StringBuffer();
- for (var message in messages) {
- if (message.arguments.isNotEmpty) {
- newSource.write(source.substring(start, message.sourcePosition));
- // TODO(alanknight): We could generate more efficient code than the
- // original here, dispatching more directly to the MessageLookup.
- newSource.write(message.toOriginalCode());
- start = message.endPosition;
- }
- }
- newSource.write(source.substring(start));
- return newSource.toString();
-}
-
-/// Find all the messages in the [source] text.
-///
-/// Report errors as coming from [sourceName]
-List findMessages(String source, String sourceName) {
- var extraction = new MessageExtraction();
- try {
- extraction.root = parseCompilationUnit(source, name: sourceName);
- } on AnalyzerErrorGroup catch (e) {
- extraction.onMessage("Error in parsing $sourceName, no messages extracted.");
- extraction.onMessage(" $e");
- return [];
- }
- extraction.origin = sourceName;
- var visitor = new MessageFindingVisitor(extraction);
- visitor.generateNameAndArgs = true;
- extraction.root.accept(visitor);
- return visitor.messages.values.toList();
-}
diff --git a/lib/transformer.dart b/lib/transformer.dart
deleted file mode 100644
index 38dc8d0..0000000
--- a/lib/transformer.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// A transformer for Intl messages, supplying the name and arguments
-/// automatically.
-library intl_transformer;
-
-import 'package:barback/barback.dart';
-
-import 'src/message_rewriter.dart';
-
-/// Rewrites Intl.message calls to automatically insert the name and args
-/// parameters.
-class IntlMessageTransformer extends Transformer {
- IntlMessageTransformer.asPlugin();
-
- String get allowedExtensions => ".dart";
-
- apply(Transform transform) async {
- var content = await transform.primaryInput.readAsString();
- var id = transform.primaryInput.id;
- var newContent = rewriteMessages(content, '$id');
- transform.addOutput(new Asset.fromString(id, newContent));
- }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index d0f4687..0cff830 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: intl
-version: 0.13.1
+version: 0.14.0
author: Dart Team <misc@dartlang.org>
description: Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.
homepage: https://github.com/dart-lang/intl
@@ -7,11 +7,6 @@
sdk: '>=1.12.0 <2.0.0'
documentation: http://www.dartdocs.org/documentation/intl/latest
dependencies:
- analyzer: '>=0.13.2 <0.29.0'
- args: '>=0.12.1 <0.14.0'
- path: '>=0.9.0 <2.0.0'
- petitparser: '>=1.1.3 <2.0.0'
- barback: ^0.15.2
dev_dependencies:
fixnum: '>=0.9.0 <0.11.0'
unittest: '>=0.10.0 <0.12.0'
@@ -21,15 +16,4 @@
- test/date_time_format_file_even_test.dart
- test/date_time_format_file_odd_test.dart
- test/find_default_locale_standalone_test.dart
- - test/message_extraction/embedded_plural_text_after_test.dart
- - test/message_extraction/embedded_plural_text_before_test.dart
- - test/message_extraction/examples_parsing_test.dart
- - test/message_extraction/failed_extraction_test.dart
- - test/message_extraction/make_hardcoded_translation.dart
- - test/message_extraction/message_extraction_no_deferred_test.dart
- - test/message_extraction/message_extraction_test.dart
- - test/message_extraction/really_fail_extraction_test.dart
- test/intl_message_basic_example_test.dart # invalid import under pub's package-layout
-- intl:
- $include:
- - test/messages_with_transformer/transformer_test.dart
diff --git a/test/message_extraction/debug.sh b/test/message_extraction/debug.sh
deleted file mode 100755
index 82c8b87..0000000
--- a/test/message_extraction/debug.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-# The message_extraction_test.dart test uses a temporary directory and spawns
-# separate processes for each step. This can make it very painful to debug the
-# steps.
-# This script runs the steps individually, putting the files in the current
-# directory. You can run the script to run the test locally, or use this to
-# run individual steps or create them as launches in the editor.
-dart ../../bin/extract_to_arb.dart sample_with_messages.dart \
-part_of_sample_with_messages.dart
-dart make_hardcoded_translation.dart intl_messages.arb
-dart ../../bin/generate_from_arb.dart --generated-file-prefix=foo_ \
-sample_with_messages.dart part_of_sample_with_messages.dart \
-translation_fr.arb translation_de_DE.arb
diff --git a/test/message_extraction/embedded_plural_text_after.dart b/test/message_extraction/embedded_plural_text_after.dart
deleted file mode 100644
index cda5bae..0000000
--- a/test/message_extraction/embedded_plural_text_after.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// A test library that should fail because there is a plural with text
-/// following the plural expression.
-library embedded_plural_text_after;
-
-import "package:intl/intl.dart";
-
-embeddedPlural2(n) => Intl.message(
- "${Intl.plural(n, zero: 'none', one: 'one', other: 'some')} plus text.",
- name: 'embeddedPlural2', desc: 'An embedded plural', args: [n]);
diff --git a/test/message_extraction/embedded_plural_text_after_test.dart b/test/message_extraction/embedded_plural_text_after_test.dart
deleted file mode 100644
index ed9c16f..0000000
--- a/test/message_extraction/embedded_plural_text_after_test.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library embedded_plural_text_after_test;
-
-import "failed_extraction_test.dart";
-import "package:unittest/unittest.dart";
-
-main() {
- test("Expect failure because of embedded plural with text after it", () {
- List<String> specialFiles = ['embedded_plural_text_after.dart'];
- runTestWithWarnings(
- warningsAreErrors: true,
- expectedExitCode: 1,
- embeddedPlurals: false,
- sourceFiles: specialFiles);
- });
-}
diff --git a/test/message_extraction/embedded_plural_text_before.dart b/test/message_extraction/embedded_plural_text_before.dart
deleted file mode 100644
index 4843831..0000000
--- a/test/message_extraction/embedded_plural_text_before.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// A test library that should fail because there is a plural with text
-/// before the plural expression.
-library embedded_plural_text_before;
-
-import "package:intl/intl.dart";
-
-embeddedPlural(n) => Intl.message(
- "There are ${Intl.plural(n, zero: 'nothing', one: 'one', other: 'some')}.",
- name: 'embeddedPlural', desc: 'An embedded plural', args: [n]);
diff --git a/test/message_extraction/embedded_plural_text_before_test.dart b/test/message_extraction/embedded_plural_text_before_test.dart
deleted file mode 100644
index 75b411c..0000000
--- a/test/message_extraction/embedded_plural_text_before_test.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library embedded_plural_text_before_test;
-
-import "failed_extraction_test.dart";
-import "package:unittest/unittest.dart";
-
-main() {
- test("Expect failure because of embedded plural with text before it", () {
- List<String> files = ['embedded_plural_text_before.dart'];
- runTestWithWarnings(
- warningsAreErrors: true,
- expectedExitCode: 1,
- embeddedPlurals: false,
- sourceFiles: files);
- });
-}
diff --git a/test/message_extraction/examples_parsing_test.dart b/test/message_extraction/examples_parsing_test.dart
deleted file mode 100644
index f61add1..0000000
--- a/test/message_extraction/examples_parsing_test.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// Test for parsing the examples argument from an Intl.message call. Very
-/// minimal so far.
-import 'package:unittest/unittest.dart';
-import 'package:intl/extract_messages.dart';
-import '../data_directory.dart';
-import 'package:path/path.dart' as path;
-import 'dart:io';
-
-main() {
- test("Message examples are correctly extracted", () {
- var file = path.join(intlDirectory, 'test', 'message_extraction',
- 'sample_with_messages.dart');
- var extraction = new MessageExtraction();
- var messages = extraction.parseFile(new File(file));
- expect(messages['message2'].examples, {"x": 3});
- });
-}
diff --git a/test/message_extraction/failed_extraction_test.dart b/test/message_extraction/failed_extraction_test.dart
deleted file mode 100644
index b2758a7..0000000
--- a/test/message_extraction/failed_extraction_test.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-library failed_extraction_test;
-
-import "message_extraction_test.dart";
-import "dart:io";
-import "package:unittest/unittest.dart";
-
-main() {
- test("Expect warnings but successful extraction", () {
- runTestWithWarnings(warningsAreErrors: false, expectedExitCode: 0);
- });
-}
-
-const List<String> defaultFiles = const [
- "sample_with_messages.dart",
- "part_of_sample_with_messages.dart"
-];
-
-void runTestWithWarnings({bool warningsAreErrors, int expectedExitCode,
- bool embeddedPlurals: true, List<String> sourceFiles: defaultFiles}) {
- verify(ProcessResult result) {
- try {
- expect(result.exitCode, expectedExitCode);
- } finally {
- deleteGeneratedFiles();
- }
- }
-
- copyFilesToTempDirectory();
- var program = asTestDirPath("../../bin/extract_to_arb.dart");
- List<String> args = ["--output-dir=$tempDir"];
- if (warningsAreErrors) {
- args.add('--warnings-are-errors');
- }
- if (!embeddedPlurals) {
- args.add('--no-embedded-plurals');
- }
- var files = sourceFiles.map(asTempDirPath).toList();
- List<String> allArgs = [program]
- ..addAll(args)
- ..addAll(files);
- var callback = expectAsync(verify) as ThenArgument;
-
- run(null, allArgs).then(callback);
-}
-
-typedef dynamic ThenArgument(ProcessResult _);
diff --git a/test/message_extraction/foo_messages_all.dart b/test/message_extraction/foo_messages_all.dart
deleted file mode 100644
index 06b482e..0000000
--- a/test/message_extraction/foo_messages_all.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library keep_the_static_analysis_from_complaining;
-
-initializeMessages(_) => throw new UnimplementedError(
- "This entire file is only here to make the static"
- " analysis happy. It will be generated during actual tests.");
diff --git a/test/message_extraction/make_hardcoded_translation.dart b/test/message_extraction/make_hardcoded_translation.dart
deleted file mode 100644
index 0c3af44..0000000
--- a/test/message_extraction/make_hardcoded_translation.dart
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// This simulates a translation process, reading the messages generated from
-/// extract_message.dart for the files sample_with_messages.dart and
-/// part_of_sample_with_messages.dart and writing out hard-coded translations
-/// for German and French locales.
-
-import 'dart:convert';
-import 'dart:io';
-import 'package:path/path.dart' as path;
-import 'package:args/args.dart';
-
-/// A list of the French translations that we will produce.
-var french = {
- "types": r"{a}, {b}, {c}",
- "This string extends across multiple lines.":
- "Cette message prend plusiers lignes.",
- "message2": r"Un autre message avec un seul paramètre {x}",
- "alwaysTranslated": "Cette chaîne est toujours traduit",
- "message1": "Il s'agit d'un message",
- "\"So-called\"": "\"Soi-disant\"",
- "trickyInterpolation": r"L'interpolation est délicate "
- r"quand elle se termine une phrase comme {s}.",
- "message3": "Caractères qui doivent être échapper, par exemple barres \\ "
- "dollars \${ (les accolades sont ok), et xml/html réservés <& et "
- "des citations \" "
- "avec quelques paramètres ainsi {a}, {b}, et {c}",
- "YouveGotMessages_method": "Cela vient d'une méthode",
- "nonLambda": "Cette méthode n'est pas un lambda",
- "staticMessage": "Cela vient d'une méthode statique",
- "notAlwaysTranslated": "Ce manque certaines traductions",
- "thisNameIsNotInTheOriginal": "Could this lead to something malicious?",
- "Ancient Greek hangman characters: 𐅆𐅇.":
- "Anciens caractères grecs jeux du pendu: 𐅆𐅇.",
- "escapable": "Escapes: \n\r\f\b\t\v.",
- "sameContentsDifferentName": "Bonjour tout le monde",
- "differentNameSameContents": "Bonjour tout le monde",
- "rentToBePaid": "loyer",
- "rentAsVerb": "louer",
- "plurals": "{num,plural, =0{Est-ce que nulle est pluriel?}=1{C'est singulier}"
- "other{C'est pluriel ({num}).}}",
- "whereTheyWentMessage": "{gender,select, male{{name} est allé à sa {place}}"
- "female{{name} est allée à sa {place}}other{{name}"
- " est allé à sa {place}}}",
- // Gratuitously different translation for testing. Ignoring gender of place.
- "nestedMessage": "{combinedGender,select, "
- "other{"
- "{number,plural, "
- "=0{Personne n'avait allé à la {place}}"
- "=1{{names} était allé à la {place}}"
- "other{{names} étaient allés à la {place}}"
- "}"
- "}"
- "female{"
- "{number,plural, "
- "=1{{names} était allée à la {place}}"
- "other{{names} étaient allées à la {place}}"
- "}"
- "}"
- "}",
- "outerPlural": "{n,plural, =0{rien}=1{un}other{quelques-uns}}",
- "outerGender": "{g,select, male {homme} female {femme} other {autre}}",
- "pluralThatFailsParsing": "{noOfThings,plural, "
- "=1{1 chose:}other{{noOfThings} choses:}}",
- "nestedOuter": "{number,plural, other{"
- "{gen,select, male{{number} homme}other{{number} autre}}}}",
- "outerSelect": "{currency,select, CDN{{amount} dollars Canadiens}"
- "other{{amount} certaine devise ou autre.}}}",
- "nestedSelect": "{currency,select, CDN{{amount,plural, "
- "=1{{amount} dollar Canadien}"
- "other{{amount} dollars Canadiens}}}"
- "other{N'importe quoi}"
- "}}",
- "literalDollar": "Cinq sous est US\$0.05",
- r"'<>{}= +-_$()&^%$#@!~`'": r"interessant (fr): '<>{}= +-_$()&^%$#@!~`'"
-};
-
-/// A list of the German translations that we will produce.
-var german = {
- "types": r"{a}, {b}, {c}",
- "This string extends across multiple lines.":
- "Dieser String erstreckt sich über mehrere Zeilen erstrecken.",
- "message2": r"Eine weitere Meldung mit dem Parameter {x}",
- "alwaysTranslated": "Diese Zeichenkette wird immer übersetzt",
- "message1": "Dies ist eine Nachricht",
- "\"So-called\"": "\"Sogenannt\"",
- "trickyInterpolation": r"Interpolation ist schwierig, wenn es einen Satz "
- "wie dieser endet {s}.",
- "message3": "Zeichen, die Flucht benötigen, zB Schrägstriche \\ Dollar "
- "\${ (geschweiften Klammern sind ok) und xml reservierte Zeichen <& und "
- "Zitate \" Parameter {a}, {b} und {c}",
- "YouveGotMessages_method": "Dies ergibt sich aus einer Methode",
- "nonLambda": "Diese Methode ist nicht eine Lambda",
- "staticMessage": "Dies ergibt sich aus einer statischen Methode",
- "thisNameIsNotInTheOriginal": "Could this lead to something malicious?",
- "Ancient Greek hangman characters: 𐅆𐅇.":
- "Antike griechische Galgenmännchen Zeichen: 𐅆𐅇",
- "escapable": "Escapes: \n\r\f\b\t\v.",
- "sameContentsDifferentName": "Hallo Welt",
- "differentNameSameContents": "Hallo Welt",
- "rentToBePaid": "Miete",
- "rentAsVerb": "mieten",
- "plurals": "{num,plural, =0{Ist Null Plural?}=1{Dies ist einmalig}"
- "other{Dies ist Plural ({num}).}}",
- "whereTheyWentMessage": "{gender,select, male{{name} ging zu seinem {place}}"
- "female{{name} ging zu ihrem {place}}other{{name} ging zu seinem {place}}}",
- //Note that we're only using the gender of the people. The gender of the
- //place also matters, but we're not dealing with that here.
- "nestedMessage": "{combinedGender,select, "
- "other{"
- "{number,plural, "
- "=0{Niemand ging zu {place}}"
- "=1{{names} ging zum {place}}"
- "other{{names} gingen zum {place}}"
- "}"
- "}"
- "female{"
- "{number,plural, "
- "=1{{names} ging in dem {place}}"
- "other{{names} gingen zum {place}}"
- "}"
- "}"
- "}",
- "outerPlural": "{n,plural, =0{Null}=1{ein}other{einige}}",
- "outerGender": "{g,select, male{Mann}female{Frau}other{andere}}",
- "pluralThatFailsParsing": "{noOfThings,plural, "
- "=1{eins:}other{{noOfThings} Dinge:}}",
- "nestedOuter": "{number,plural, other{"
- "{gen,select, male{{number} Mann}other{{number} andere}}}}",
- "outerSelect": "{currency,select, CDN{{amount} Kanadischen dollar}"
- "other{{amount} einige Währung oder anderen.}}}",
- "nestedSelect": "{currency,select, CDN{{amount,plural, "
- "=1{{amount} Kanadischer dollar}"
- "other{{amount} Kanadischen dollar}}}"
- "other{whatever}"
- "}",
- "literalDollar": "Fünf Cent US \$ 0.05",
- r"'<>{}= +-_$()&^%$#@!~`'": r"interessant (de): '<>{}= +-_$()&^%$#@!~`'"
-};
-
-/// The output directory for translated files.
-String targetDir;
-
-/// Generate a translated json version from [originals] in [locale] looking
-/// up the translations in [translations].
-void translate(Map originals, String locale, Map translations) {
- var translated = {"_locale": locale};
- originals.forEach((name, text) {
- translated[name] = translations[name];
- });
- var file = new File(path.join(targetDir, 'translation_$locale.arb'));
- file.writeAsStringSync(JSON.encode(translated));
-}
-
-main(List<String> args) {
- if (args.length == 0) {
- print('Usage: make_hardcoded_translation [--output-dir=<dir>] '
- '[originalFile.arb]');
- exit(0);
- }
- var parser = new ArgParser();
- parser.addOption("output-dir",
- defaultsTo: '.', callback: (value) => targetDir = value);
- parser.parse(args);
-
- var fileArgs = args.where((x) => x.contains('.arb'));
-
- var messages = JSON.decode(new File(fileArgs.first).readAsStringSync());
- translate(messages, "fr", french);
- translate(messages, "de_DE", german);
-}
diff --git a/test/message_extraction/message_extraction_no_deferred_test.dart b/test/message_extraction/message_extraction_no_deferred_test.dart
deleted file mode 100644
index 68936f1..0000000
--- a/test/message_extraction/message_extraction_no_deferred_test.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-
-/// A test for message extraction and code generation not using deferred
-/// loading for the generated code.
-library message_extraction_no_deferred_test;
-
-import 'message_extraction_test.dart' as mainTest;
-
-main(arguments) {
- mainTest.useDeferredLoading = false;
- mainTest.main(arguments);
-}
diff --git a/test/message_extraction/message_extraction_test.dart b/test/message_extraction/message_extraction_test.dart
deleted file mode 100644
index d22e1e3..0000000
--- a/test/message_extraction/message_extraction_test.dart
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library message_extraction_test;
-
-import 'package:unittest/unittest.dart';
-import 'dart:io';
-import 'dart:async';
-import 'dart:convert';
-import 'package:path/path.dart' as path;
-import '../data_directory.dart';
-
-final dart = Platform.executable;
-
-/// Should we use deferred loading.
-bool useDeferredLoading = true;
-
-String get _deferredLoadPrefix => useDeferredLoading ? '' : 'no-';
-
-String get deferredLoadArg => '--${_deferredLoadPrefix}use-deferred-loading';
-
-/// The VM arguments we were given, most important package-root.
-final vmArgs = Platform.executableArguments;
-
-/// For testing we move the files into a temporary directory so as not to leave
-/// generated files around after a failed test. For debugging, we omit that
-/// step if [useLocalDirectory] is true. The place we move them to is saved as
-/// [tempDir].
-String get tempDir => _tempDir == null ? _tempDir = _createTempDir() : _tempDir;
-var _tempDir;
-_createTempDir() => useLocalDirectory
- ? '.'
- : Directory.systemTemp.createTempSync('message_extraction_test').path;
-
-var useLocalDirectory = false;
-
-/// Translate a relative file path into this test directory. This is
-/// applied to all the arguments of [run]. It will ignore a string that
-/// is an absolute path or begins with "--", because some of the arguments
-/// might be command-line options.
-String asTestDirPath([String s]) {
- if (s == null || s.startsWith("--") || path.isAbsolute(s)) return s;
- return path.join(intlDirectory, 'test', 'message_extraction', s);
-}
-
-/// Translate a relative file path into our temp directory. This is
-/// applied to all the arguments of [run]. It will ignore a string that
-/// is an absolute path or begins with "--", because some of the arguments
-/// might be command-line options.
-String asTempDirPath([String s]) {
- if (s == null || s.startsWith("--") || path.isAbsolute(s)) return s;
- return path.join(tempDir, s);
-}
-
-typedef ProcessResult ThenResult(ProcessResult _);
-main(arguments) {
- // If debugging, use --local to avoid copying everything to temporary
- // directories to make it even harder to debug. Note that this will also
- // not delete the generated files, so may require manual cleanup.
- if (arguments.contains("--local")) {
- print("Testing using local directory for generated files");
- useLocalDirectory = true;
- }
- setUp(copyFilesToTempDirectory);
- tearDown(deleteGeneratedFiles);
- test("Test round trip message extraction, translation, code generation, "
- "and printing", () {
- var makeSureWeVerify = expectAsync(runAndVerify) as ThenResult;
- return extractMessages(null).then((result) {
- return generateTranslationFiles(result);
- }).then((result) {
- return generateCodeFromTranslation(result);
- }).then(makeSureWeVerify).then(checkResult);
- });
-}
-
-void copyFilesToTempDirectory() {
- if (useLocalDirectory) return;
- var files = [
- asTestDirPath('sample_with_messages.dart'),
- asTestDirPath('part_of_sample_with_messages.dart'),
- asTestDirPath('verify_messages.dart'),
- asTestDirPath('run_and_verify.dart'),
- asTestDirPath('embedded_plural_text_before.dart'),
- asTestDirPath('embedded_plural_text_after.dart'),
- asTestDirPath('print_to_list.dart')
- ];
- for (var filename in files) {
- var file = new File(filename);
- file.copySync(path.join(tempDir, path.basename(filename)));
- }
-}
-
-void deleteGeneratedFiles() {
- if (useLocalDirectory) return;
- try {
- new Directory(tempDir).deleteSync(recursive: true);
- } on Error catch (e) {
- print("Failed to delete $tempDir");
- print("Exception:\n$e");
- }
-}
-
-/// Run the process with the given list of filenames, which we assume
-/// are in dir() and need to be qualified in case that's not our working
-/// directory.
-Future<ProcessResult> run(
- ProcessResult previousResult, List<String> filenames) {
- // If there's a failure in one of the sub-programs, print its output.
- checkResult(previousResult);
- var filesInTheRightDirectory = filenames
- .map((x) => asTempDirPath(x))
- .map((x) => path.normalize(x))
- .toList();
- // Inject the script argument --output-dir in between the script and its
- // arguments.
- List<String> args = []
- ..addAll(vmArgs)
- ..add(filesInTheRightDirectory.first)
- ..addAll(["--output-dir=$tempDir"])
- ..addAll(filesInTheRightDirectory.skip(1));
- var result =
- Process.run(dart, args, stdoutEncoding: UTF8, stderrEncoding: UTF8);
- return result;
-}
-
-checkResult(ProcessResult previousResult) {
- if (previousResult != null) {
- if (previousResult.exitCode != 0) {
- print("Error running sub-program:");
- }
- print(previousResult.stdout);
- print(previousResult.stderr);
- print("exitCode=${previousResult.exitCode}");
- // Fail the test.
- expect(previousResult.exitCode, 0);
- }
-}
-
-Future<ProcessResult> extractMessages(ProcessResult previousResult) => run(
- previousResult, [
- asTestDirPath('../../bin/extract_to_arb.dart'),
- '--suppress-warnings',
- 'sample_with_messages.dart',
- 'part_of_sample_with_messages.dart'
-]);
-
-Future<ProcessResult> generateTranslationFiles(ProcessResult previousResult) =>
- run(previousResult, [
- asTestDirPath('make_hardcoded_translation.dart'),
- 'intl_messages.arb'
-]);
-
-Future<ProcessResult> generateCodeFromTranslation(
- ProcessResult previousResult) => run(previousResult, [
- asTestDirPath('../../bin/generate_from_arb.dart'),
- deferredLoadArg,
- '--generated-file-prefix=foo_',
- 'sample_with_messages.dart',
- 'part_of_sample_with_messages.dart',
- 'translation_fr.arb',
- 'translation_de_DE.arb'
-]);
-
-Future<ProcessResult> runAndVerify(ProcessResult previousResult) =>
- run(previousResult, [asTempDirPath('run_and_verify.dart')]);
diff --git a/test/message_extraction/part_of_sample_with_messages.dart b/test/message_extraction/part_of_sample_with_messages.dart
deleted file mode 100644
index 31cddcc..0000000
--- a/test/message_extraction/part_of_sample_with_messages.dart
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.part of sample;
-
-part of sample;
-
-class Person {
- String name;
- String gender;
- Person(this.name, this.gender);
-}
-
-class YouveGotMessages {
-
- // A static message, rather than a standalone function.
- static staticMessage() =>
- Intl.message("This comes from a static method", name: 'staticMessage');
-
- // An instance method, rather than a standalone function.
- method() => Intl.message("This comes from a method",
- name: 'YouveGotMessages_method', desc: 'This is a method with a '
- 'long description which spans '
- 'multiple lines.');
-
- // A non-lambda, i.e. not using => syntax, and with an additional statement
- // before the Intl.message call.
- nonLambda() {
- var aTrueValue = true;
- var msg = Intl.message("This method is not a lambda", name: 'nonLambda');
- expect(aTrueValue, isTrue,
- reason: 'Parser should not fail with additional code.');
- return msg;
- }
-
- plurals(num) => Intl.message("""${Intl.plural(num,
- zero : 'Is zero plural?',
- one : 'This is singular.',
- other : 'This is plural ($num).')
- }""", name: "plurals", args: [num], desc: "Basic plurals");
-
- whereTheyWent(Person person, String place) =>
- whereTheyWentMessage(person.name, person.gender, place);
-
- whereTheyWentMessage(String name, String gender, String place) {
- return Intl.message("${Intl.gender(gender,
- male: '$name went to his $place',
- female: '$name went to her $place',
- other: '$name went to its $place')
- }",
- name: "whereTheyWentMessage",
- args: [name, gender, place],
- desc: 'A person went to some place that they own, e.g. their room');
- }
-
- // English doesn't do enough with genders, so this example is French.
- nested(List people, String place) {
- var names = people.map((x) => x.name).join(", ");
- var number = people.length;
- var combinedGender =
- people.every((x) => x.gender == "female") ? "female" : "other";
- if (number == 0) combinedGender = "other";
-
- nestedMessage(names, number, combinedGender, place) => Intl.message(
- '''${Intl.gender(combinedGender,
- other: '${Intl.plural(number,
- zero: "Personne n'est allé au $place",
- one: "${names} est allé au $place",
- other: "${names} sont allés au $place")}',
- female: '${Intl.plural(number,
- one: "$names est allée au $place",
- other: "$names sont allées au $place")}'
- )}''',
- name: "nestedMessage", args: [names, number, combinedGender, place]);
- return nestedMessage(names, number, combinedGender, place);
- }
-}
diff --git a/test/message_extraction/print_to_list.dart b/test/message_extraction/print_to_list.dart
deleted file mode 100644
index f5446d4..0000000
--- a/test/message_extraction/print_to_list.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-/// This provides a way for a test to print to an internal list so the
-/// results can be verified rather than writing to and reading a file.
-
-library print_to_list.dart;
-
-List<String> lines = [];
-
-void printOut(String s) {
- lines.add(s);
-}
diff --git a/test/message_extraction/really_fail_extraction_test.dart b/test/message_extraction/really_fail_extraction_test.dart
deleted file mode 100644
index 76f5251..0000000
--- a/test/message_extraction/really_fail_extraction_test.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library really_fail_extraction_test;
-
-import "failed_extraction_test.dart";
-import "package:unittest/unittest.dart";
-
-main() {
- test("Expect failure because warnings are errors", () {
- runTestWithWarnings(warningsAreErrors: true, expectedExitCode: 1);
- });
-}
diff --git a/test/message_extraction/run_and_verify.dart b/test/message_extraction/run_and_verify.dart
deleted file mode 100644
index ef9a1a0..0000000
--- a/test/message_extraction/run_and_verify.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-library verify_and_run;
-
-import 'sample_with_messages.dart' as sample;
-import 'verify_messages.dart';
-
-main() {
- sample.main().then(verifyResult);
-}
diff --git a/test/message_extraction/sample_with_messages.dart b/test/message_extraction/sample_with_messages.dart
deleted file mode 100644
index c22380f..0000000
--- a/test/message_extraction/sample_with_messages.dart
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// This is a program with various [Intl.message] messages. It just prints
-/// all of them, and is used for testing of message extraction, translation,
-/// and code generation.
-library sample;
-
-import "package:intl/intl.dart";
-import "foo_messages_all.dart";
-import "print_to_list.dart";
-import "dart:async";
-import "package:unittest/unittest.dart";
-
-part 'part_of_sample_with_messages.dart';
-
-message1() => Intl.message("This is a message", name: 'message1', desc: 'foo');
-
-message2(x) => Intl.message("Another message with parameter $x",
- name: 'mess' 'age2',
- desc: 'Description ' '2',
- args: [x],
- examples: {'x': 3});
-
-// A string with multiple adjacent strings concatenated together, verify
-// that the parser handles this properly.
-multiLine() => Intl.message("This "
- "string "
- "extends "
- "across "
- "multiple "
- "lines.");
-
-get interestingCharactersNoName =>
- Intl.message("'<>{}= +-_\$()&^%\$#@!~`'", desc: "interesting characters");
-
-// Have types on the enclosing function's arguments.
-types(int a, String b, List c) =>
- Intl.message("$a, $b, $c", name: 'types', args: [a, b, c]);
-
-// This string will be printed with a French locale, so it will always show
-// up in the French version, regardless of the current locale.
-alwaysTranslated() => Intl.message("This string is always translated",
- locale: 'fr', name: 'alwaysTranslated');
-
-// Test interpolation with curly braces around the expression, but it must
-// still be just a variable reference.
-trickyInterpolation(s) =>
- Intl.message("Interpolation is tricky when it ends a sentence like ${s}.",
- name: 'trickyInterpolation', args: [s]);
-
-get leadingQuotes => Intl.message("\"So-called\"");
-
-// A message with characters not in the basic multilingual plane.
-originalNotInBMP() => Intl.message("Ancient Greek hangman characters: 𐅆𐅇.");
-
-// A string for which we don't provide all translations.
-notAlwaysTranslated() => Intl.message("This is missing some translations",
- name: "notAlwaysTranslated");
-
-// This is invalid and should be recognized as such, because the message has
-// to be a literal. Otherwise, interpolations would be outside of the function
-// scope.
-var someString = "No, it has to be a literal string";
-noVariables() => Intl.message(someString, name: "noVariables");
-
-// This is unremarkable in English, but the translated versions will contain
-// characters that ought to be escaped during code generation.
-escapable() => Intl.message("Escapable characters here: ", name: "escapable");
-
-outerPlural(n) => Intl.plural(n,
- zero: 'none',
- one: 'one',
- other: 'some',
- name: 'outerPlural',
- desc: 'A plural with no enclosing message',
- args: [n]);
-
-outerGender(g) => Intl.gender(g,
- male: 'm',
- female: 'f',
- other: 'o',
- name: 'outerGender',
- desc: 'A gender with no enclosing message',
- args: [g]);
-
-pluralThatFailsParsing(noOfThings) => Intl.plural(noOfThings,
- one: "1 thing:",
- other: "$noOfThings things:",
- name: "pluralThatFailsParsing",
- args: [noOfThings],
- desc: "How many things are there?");
-
-// A standalone gender message where we don't provide name or args. This should
-// be rejected by validation code.
-invalidOuterGender(g) => Intl.gender(g, other: 'o');
-
-// A general select
-outerSelect(currency, amount) => Intl.select(
- currency,
- {
- "CDN": "$amount Canadian dollars",
- "other": "$amount some currency or other."
- },
- name: "outerSelect",
- args: [currency, amount]);
-
-// A select with a plural inside the expressions.
-nestedSelect(currency, amount) => Intl.select(
- currency,
- {
- "CDN": """${Intl.plural(amount, one: '$amount Canadian dollar',
- other: '$amount Canadian dollars')}""",
- "other": "Whatever",
- },
- name: "nestedSelect",
- args: [currency, amount]);
-
-// A trivial nested plural/gender where both are done directly rather than
-// in interpolations.
-nestedOuter(number, gen) => Intl.plural(number,
- other: Intl.gender(gen, male: "$number male", other: "$number other"),
- name: 'nestedOuter',
- args: [number, gen]);
-
-sameContentsDifferentName() => Intl.message("Hello World",
- name: "sameContentsDifferentName",
- desc: "One of two messages with the same contents, but different names");
-
-differentNameSameContents() => Intl.message("Hello World",
- name: "differentNameSameContents",
- desc: "One of two messages with the same contents, but different names");
-
-/// Distinguish two messages with identical text using the meaning parameter.
-rentToBePaid() => Intl.message("rent",
- name: "rentToBePaid",
- meaning: 'Money for rent',
- desc: "Money to be paid for rent");
-
-rentAsVerb() => Intl.message("rent",
- name: "rentAsVerb",
- meaning: 'rent as a verb',
- desc: "The action of renting, as in rent a car");
-
-literalDollar() => Intl.message("Five cents is US\$0.05",
- name: "literalDollar", desc: "Literal dollar sign with valid number");
-
-printStuff(Intl locale) {
- // Use a name that's not a literal so this will get skipped. Then we have
- // a name that's not in the original but we include it in the French
- // translation. Because it's not in the original it shouldn't get included
- // in the generated catalog and shouldn't get translated.
- if (locale.locale == 'fr') {
- var badName = "thisNameIsNotInTheOriginal";
- var notInOriginal = Intl.message("foo", name: badName);
- if (notInOriginal != "foo") {
- throw "You shouldn't be able to introduce a new message in a translation";
- }
- }
-
- // A function that is assigned to a variable. It's also nested
- // within another function definition.
- message3(a, b, c) => Intl.message(
- "Characters that need escaping, e.g slashes \\ dollars \${ (curly braces "
- "are ok) and xml reserved characters <& and quotes \" "
- "parameters $a, $b, and $c",
- name: 'message3',
- args: [a, b, c]);
- var messageVariable = message3;
-
- printOut("-------------------------------------------");
- printOut("Printing messages for ${locale.locale}");
- Intl.withLocale(locale.locale, () {
- printOut(message1());
- printOut(message2("hello"));
- printOut(messageVariable(1, 2, 3));
- printOut(multiLine());
- printOut(types(1, "b", ["c", "d"]));
- printOut(leadingQuotes);
- printOut(alwaysTranslated());
- printOut(trickyInterpolation("this"));
- var thing = new YouveGotMessages();
- printOut(thing.method());
- printOut(thing.nonLambda());
- printOut(YouveGotMessages.staticMessage());
- printOut(notAlwaysTranslated());
- printOut(originalNotInBMP());
- printOut(escapable());
-
- printOut(thing.plurals(0));
- printOut(thing.plurals(1));
- printOut(thing.plurals(2));
- printOut(thing.plurals(3));
- printOut(thing.plurals(4));
- printOut(thing.plurals(5));
- printOut(thing.plurals(6));
- printOut(thing.plurals(7));
- printOut(thing.plurals(8));
- printOut(thing.plurals(9));
- printOut(thing.plurals(10));
- printOut(thing.plurals(11));
- printOut(thing.plurals(20));
- printOut(thing.plurals(100));
- printOut(thing.plurals(101));
- printOut(thing.plurals(100000));
- var alice = new Person("Alice", "female");
- var bob = new Person("Bob", "male");
- var cat = new Person("cat", null);
- printOut(thing.whereTheyWent(alice, "house"));
- printOut(thing.whereTheyWent(bob, "house"));
- printOut(thing.whereTheyWent(cat, "litter box"));
- printOut(thing.nested([alice, bob], "magasin"));
- printOut(thing.nested([alice], "magasin"));
- printOut(thing.nested([], "magasin"));
- printOut(thing.nested([bob, bob], "magasin"));
- printOut(thing.nested([alice, alice], "magasin"));
-
- printOut(outerPlural(0));
- printOut(outerPlural(1));
- printOut(outerGender("male"));
- printOut(outerGender("female"));
- printOut(nestedOuter(7, "male"));
- printOut(outerSelect("CDN", 7));
- printOut(outerSelect("EUR", 5));
- printOut(nestedSelect("CDN", 1));
- printOut(nestedSelect("CDN", 2));
- printOut(pluralThatFailsParsing(1));
- printOut(pluralThatFailsParsing(2));
- printOut(differentNameSameContents());
- printOut(sameContentsDifferentName());
- printOut(rentAsVerb());
- printOut(rentToBePaid());
- printOut(literalDollar());
- printOut(interestingCharactersNoName);
- });
-}
-
-var localeToUse = 'en_US';
-
-main() {
- var fr = new Intl("fr");
- var english = new Intl("en_US");
- var de = new Intl("de_DE");
- // Throw in an initialize of a null locale to make sure it doesn't throw.
- initializeMessages(null);
-
- // Verify that a translated message isn't initially present.
- var messageInGerman = Intl.withLocale('de_DE', message1);
- expect(messageInGerman, "This is a message");
-
- var f1 = initializeMessages(fr.locale)
- // Since English has the one message which is always translated, we
- // can't print it until French is ready.
- .then((_) => printStuff(english))
- .then((_) => printStuff(fr));
- var f2 = initializeMessages('de-de').then((_) => printStuff(de));
- return Future.wait([f1, f2]);
-}
diff --git a/test/message_extraction/verify_messages.dart b/test/message_extraction/verify_messages.dart
deleted file mode 100644
index b68abcd..0000000
--- a/test/message_extraction/verify_messages.dart
+++ /dev/null
@@ -1,213 +0,0 @@
-library verify_messages;
-
-import "print_to_list.dart";
-import "package:unittest/unittest.dart";
-
-verifyResult(ignored) {
- test("Verify message translation output", actuallyVerifyResult);
-}
-
-actuallyVerifyResult() {
- var lineIterator;
- verify(String s) {
- lineIterator.moveNext();
- var value = lineIterator.current;
- expect(value, s);
- }
-
- var expanded = lines.expand((line) => line.split("\n")).toList();
- lineIterator = expanded.iterator..moveNext();
- verify("Printing messages for en_US");
- verify("This is a message");
- verify("Another message with parameter hello");
- verify("Characters that need escaping, e.g slashes \\ dollars \${ "
- "(curly braces are ok) and xml reserved characters <& and "
- "quotes \" parameters 1, 2, and 3");
- verify("This string extends across multiple lines.");
- verify("1, b, [c, d]");
- verify('"So-called"');
- verify("Cette chaîne est toujours traduit");
- verify("Interpolation is tricky when it ends a sentence like this.");
- verify("This comes from a method");
- verify("This method is not a lambda");
- verify("This comes from a static method");
- verify("This is missing some translations");
- verify("Ancient Greek hangman characters: 𐅆𐅇.");
- verify("Escapable characters here: ");
-
- verify('Is zero plural?');
- verify('This is singular.');
- verify('This is plural (2).');
- verify('This is plural (3).');
- verify('This is plural (4).');
- verify('This is plural (5).');
- verify('This is plural (6).');
- verify('This is plural (7).');
- verify('This is plural (8).');
- verify('This is plural (9).');
- verify('This is plural (10).');
- verify('This is plural (11).');
- verify('This is plural (20).');
- verify('This is plural (100).');
- verify('This is plural (101).');
- verify('This is plural (100000).');
- verify('Alice went to her house');
- verify('Bob went to his house');
- verify('cat went to its litter box');
- verify('Alice, Bob sont allés au magasin');
- verify('Alice est allée au magasin');
- verify('Personne n\'est allé au magasin');
- verify('Bob, Bob sont allés au magasin');
- verify('Alice, Alice sont allées au magasin');
- verify('none');
- verify('one');
- verify('m');
- verify('f');
- verify('7 male');
- verify('7 Canadian dollars');
- verify('5 some currency or other.');
- verify('1 Canadian dollar');
- verify('2 Canadian dollars');
- verify('1 thing:');
- verify('2 things:');
- verify('Hello World');
- verify('Hello World');
- verify('rent');
- verify('rent');
- verify('Five cents is US\$0.05');
- verify(r"'<>{}= +-_$()&^%$#@!~`'");
-
- var fr_lines =
- expanded.skip(1).skipWhile((line) => !line.contains('----')).toList();
- lineIterator = fr_lines.iterator..moveNext();
- verify("Printing messages for fr");
- verify("Il s'agit d'un message");
- verify("Un autre message avec un seul paramètre hello");
- verify("Caractères qui doivent être échapper, par exemple barres \\ "
- "dollars \${ (les accolades sont ok), et xml/html réservés <& et "
- "des citations \" "
- "avec quelques paramètres ainsi 1, 2, et 3");
- verify("Cette message prend plusiers lignes.");
- verify("1, b, [c, d]");
- verify('"Soi-disant"');
- verify("Cette chaîne est toujours traduit");
- verify("L'interpolation est délicate quand elle se termine une "
- "phrase comme this.");
- verify("Cela vient d'une méthode");
- verify("Cette méthode n'est pas un lambda");
- verify("Cela vient d'une méthode statique");
- verify("Ce manque certaines traductions");
- verify("Anciens caractères grecs jeux du pendu: 𐅆𐅇.");
- verify("Escapes: ");
- verify("\r\f\b\t\v.");
-
- verify('Est-ce que nulle est pluriel?');
- verify('C\'est singulier');
- verify('C\'est pluriel (2).');
- verify('C\'est pluriel (3).');
- verify('C\'est pluriel (4).');
- verify('C\'est pluriel (5).');
- verify('C\'est pluriel (6).');
- verify('C\'est pluriel (7).');
- verify('C\'est pluriel (8).');
- verify('C\'est pluriel (9).');
- verify('C\'est pluriel (10).');
- verify('C\'est pluriel (11).');
- verify('C\'est pluriel (20).');
- verify('C\'est pluriel (100).');
- verify('C\'est pluriel (101).');
- verify('C\'est pluriel (100000).');
- verify('Alice est allée à sa house');
- verify('Bob est allé à sa house');
- verify('cat est allé à sa litter box');
- verify('Alice, Bob étaient allés à la magasin');
- verify('Alice était allée à la magasin');
- verify('Personne n\'avait allé à la magasin');
- verify('Bob, Bob étaient allés à la magasin');
- verify('Alice, Alice étaient allées à la magasin');
- verify('rien');
- verify('un');
- verify('homme');
- verify('femme');
- verify('7 homme');
- verify('7 dollars Canadiens');
- verify('5 certaine devise ou autre.');
- verify('1 dollar Canadien');
- verify('2 dollars Canadiens');
- verify('1 chose:');
- verify('2 choses:');
- verify('Bonjour tout le monde');
- verify('Bonjour tout le monde');
- verify('louer');
- verify('loyer');
- // Using a non-French format for the currency to test interpolation.
- verify('Cinq sous est US\$0.05');
- verify(r"interessant (fr): '<>{}= +-_$()&^%$#@!~`'");
-
- var de_lines =
- fr_lines.skip(1).skipWhile((line) => !line.contains('----')).toList();
- lineIterator = de_lines.iterator..moveNext();
- verify("Printing messages for de_DE");
- verify("Dies ist eine Nachricht");
- verify("Eine weitere Meldung mit dem Parameter hello");
- verify("Zeichen, die Flucht benötigen, zB Schrägstriche \\ Dollar "
- "\${ (geschweiften Klammern sind ok) und xml reservierte Zeichen <& und "
- "Zitate \" Parameter 1, 2 und 3");
- verify("Dieser String erstreckt sich über mehrere "
- "Zeilen erstrecken.");
- verify("1, b, [c, d]");
- verify('"Sogenannt"');
- // This is correct, the message is forced to French, even in a German locale.
- verify("Cette chaîne est toujours traduit");
- verify(
- "Interpolation ist schwierig, wenn es einen Satz wie dieser endet this.");
- verify("Dies ergibt sich aus einer Methode");
- verify("Diese Methode ist nicht eine Lambda");
- verify("Dies ergibt sich aus einer statischen Methode");
- verify("This is missing some translations");
- verify("Antike griechische Galgenmännchen Zeichen: 𐅆𐅇");
- verify("Escapes: ");
- verify("\r\f\b\t\v.");
-
- verify('Ist Null Plural?');
- verify('Dies ist einmalig');
- verify('Dies ist Plural (2).');
- verify('Dies ist Plural (3).');
- verify('Dies ist Plural (4).');
- verify('Dies ist Plural (5).');
- verify('Dies ist Plural (6).');
- verify('Dies ist Plural (7).');
- verify('Dies ist Plural (8).');
- verify('Dies ist Plural (9).');
- verify('Dies ist Plural (10).');
- verify('Dies ist Plural (11).');
- verify('Dies ist Plural (20).');
- verify('Dies ist Plural (100).');
- verify('Dies ist Plural (101).');
- verify('Dies ist Plural (100000).');
- verify('Alice ging zu ihrem house');
- verify('Bob ging zu seinem house');
- verify('cat ging zu seinem litter box');
- verify('Alice, Bob gingen zum magasin');
- verify('Alice ging in dem magasin');
- verify('Niemand ging zu magasin');
- verify('Bob, Bob gingen zum magasin');
- verify('Alice, Alice gingen zum magasin');
- verify('Null');
- verify('ein');
- verify('Mann');
- verify('Frau');
- verify('7 Mann');
- verify('7 Kanadischen dollar');
- verify('5 einige Währung oder anderen.');
- verify('1 Kanadischer dollar');
- verify('2 Kanadischen dollar');
- verify('eins:');
- verify('2 Dinge:');
- verify('Hallo Welt');
- verify('Hallo Welt');
- verify('mieten');
- verify('Miete');
- verify('Fünf Cent US \$ 0.05');
- verify(r"interessant (de): '<>{}= +-_$()&^%$#@!~`'");
-}
diff --git a/test/messages_with_transformer/messages_all.dart b/test/messages_with_transformer/messages_all.dart
deleted file mode 100644
index b51ae4c..0000000
--- a/test/messages_with_transformer/messages_all.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-/// 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.
-
-import 'dart:async';
-import 'package:intl/message_lookup_by_library.dart';
-import 'package:intl/src/intl_helpers.dart';
-import 'package:intl/intl.dart';
-
-import 'messages_zz.dart' deferred as messages_zz;
-
-
-Map<String, Function> _deferredLibraries = {
- 'zz' : () => messages_zz.loadLibrary(),
-};
-
-MessageLookupByLibrary _findExact(localeName) {
- switch (localeName) {
- case 'zz' : return messages_zz.messages;
- default: return null;
- }
-}
-
-/// User programs should call this before using [localeName] for messages.
-Future initializeMessages(String localeName) {
- initializeInternalMessageLookup(() => new CompositeMessageLookup());
- var lib = _deferredLibraries[Intl.canonicalizedLocale(localeName)];
- var load = lib == null ? new Future.value(false) : lib();
- return load.then((_) =>
- messageLookup.addLocale(localeName, _findGeneratedMessagesFor));
-}
-
-MessageLookupByLibrary _findGeneratedMessagesFor(locale) {
- var actualLocale = Intl.verifiedLocale(locale, (x) => _findExact(x) != null,
- onFailure: (_) => null);
- if (actualLocale == null) return null;
- return _findExact(actualLocale);
-}
diff --git a/test/messages_with_transformer/messages_zz.dart b/test/messages_with_transformer/messages_zz.dart
deleted file mode 100644
index 1c2d054..0000000
--- a/test/messages_with_transformer/messages_zz.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-/// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
-/// This is a library that provides messages for a zz locale. All the
-/// messages from the main program should be duplicated here with the same
-/// function name.
-
-import 'package:intl/intl.dart';
-import 'package:intl/message_lookup_by_library.dart';
-
-final messages = new MessageLookup();
-
-final _keepAnalysisHappy = Intl.defaultLocale;
-
-class MessageLookup extends MessageLookupByLibrary {
-
- get localeName => 'zz';
- final messages = _notInlinedMessages(_notInlinedMessages);
- static _notInlinedMessages(_) => {
- "foo" : MessageLookupByLibrary.simpleMessage("bar")
- };
-}
\ No newline at end of file
diff --git a/test/messages_with_transformer/regenerate_translated_libraries.sh b/test/messages_with_transformer/regenerate_translated_libraries.sh
deleted file mode 100755
index 5f02a29..0000000
--- a/test/messages_with_transformer/regenerate_translated_libraries.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-# This test is just trying to verify that the transformer runs,
-# so we generate the translated messages once and just use them.
-# If the transformer successfully invokes them then we're good.
-#
-# To regenerate the code you can run the lines in this script,
-# although translation_zz.arb must be manually created.
-dart ../../bin/extract_to_arb.dart --transformer main.dart
-# manually edit to create translation_zz.arb
-dart ../../bin/generate_from_arb.dart translation_zz.arb transformer_test.dart
diff --git a/test/messages_with_transformer/transformer_test.dart b/test/messages_with_transformer/transformer_test.dart
deleted file mode 100644
index 08d902b..0000000
--- a/test/messages_with_transformer/transformer_test.dart
+++ /dev/null
@@ -1,16 +0,0 @@
-import 'package:unittest/unittest.dart';
-
-import 'package:intl/intl.dart';
-
-import 'messages_all.dart';
-
-foo() => Intl.message("foo");
-
-main() async {
- await initializeMessages("zz");
-
-test("Message without name/args", () {
- Intl.defaultLocale = "zz";
- expect(foo(), "bar");
- });
-}
\ No newline at end of file