| Index: pkg/intl/lib/src/intl_message.dart
|
| diff --git a/pkg/intl/lib/src/intl_message.dart b/pkg/intl/lib/src/intl_message.dart
|
| index ae3179876010680b4b27812bccdf12990b783417..5dfb9c882a57f2a43b3ee9b38a0ac584daa709f5 100644
|
| --- a/pkg/intl/lib/src/intl_message.dart
|
| +++ b/pkg/intl/lib/src/intl_message.dart
|
| @@ -33,6 +33,8 @@
|
| */
|
| library intl_message;
|
|
|
| +import 'package:analyzer_experimental/analyzer.dart';
|
| +
|
| /** A default function for the [Message.expanded] method. */
|
| _nullTransform(msg, chunk) => chunk;
|
|
|
| @@ -59,6 +61,39 @@ abstract class Message {
|
| */
|
| get arguments => parent == null ? const [] : parent.arguments;
|
|
|
| + String checkValidity(MethodInvocation node, List arguments,
|
| + String outerName, FormalParameterList outerArgs) {
|
| + var hasArgs = arguments.any(
|
| + (each) => each is NamedExpression && each.name.label.name == 'args');
|
| + var hasParameters = !outerArgs.parameters.isEmpty;
|
| + if (!hasArgs && hasParameters) {
|
| + return "The 'args' argument for Intl.message must be specified";
|
| + }
|
| +
|
| + var messageName = arguments.firstWhere(
|
| + (eachArg) => eachArg is NamedExpression &&
|
| + eachArg.name.label.name == 'name',
|
| + orElse: () => null);
|
| + if (messageName == null) {
|
| + return "The 'name' argument for Intl.message must be specified";
|
| + }
|
| + if ((messageName.expression is! SimpleStringLiteral)
|
| + || messageName.expression.value != outerName) {
|
| + return "The 'name' argument for Intl.message must be a simple string "
|
| + "literal and match the containing function name.";
|
| + }
|
| + var simpleArguments = arguments.where(
|
| + (each) => each is NamedExpression
|
| + && ["desc", "locale", "name"].contains(each.name.label.name));
|
| + var values = simpleArguments.map((each) => each.expression).toList();
|
| + for (var arg in values) {
|
| + if (arg is! SimpleStringLiteral) {
|
| + return "Intl.message argument '${arg.name.label.name}' must be "
|
| + "a simple string literal";
|
| + }
|
| + }
|
| + }
|
| +
|
| /**
|
| * Turn a value, typically read from a translation file or created out of an
|
| * AST for a source program, into the appropriate
|
| @@ -234,6 +269,16 @@ class MainMessage extends ComplexMessage {
|
| */
|
| List<Message> messagePieces = [];
|
|
|
| + /** Verify that this looks like a correct Intl.message invocation. */
|
| + String checkValidity(MethodInvocation node, List arguments,
|
| + String outerName, FormalParameterList outerArgs) {
|
| + if (arguments.first is! StringLiteral) {
|
| + return "Intl.message messages must be string literals";
|
| + }
|
| +
|
| + return super.checkValidity(node, arguments, outerName, outerArgs);
|
| + }
|
| +
|
| void addPieces(List<Message> messages) {
|
| for (var each in messages) {
|
| messagePieces.add(Message.from(each, this));
|
| @@ -384,6 +429,18 @@ abstract class SubMessage extends ComplexMessage {
|
| 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.elements;
|
| + 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.
|
| @@ -423,12 +480,12 @@ class Gender extends SubMessage {
|
|
|
| Gender();
|
| /**
|
| - * Create a new IntlGender providing [mainArgument] and the list of possible
|
| + * 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 IntlMessageSends or IntlVariableSubstitution.
|
| + * variable name and whose second element is either a [String] or
|
| + * a list of strings and [Message] or [VariableSubstitution].
|
| */
|
| - Gender.from(mainArgument, List clauses, parent) :
|
| + Gender.from(String mainArgument, List clauses, Message parent) :
|
| super.from(mainArgument, clauses, parent);
|
|
|
| Message female;
|
| @@ -467,7 +524,7 @@ class Gender extends SubMessage {
|
| class Plural extends SubMessage {
|
|
|
| Plural();
|
| - Plural.from(mainArgument, clauses, parent) :
|
| + Plural.from(String mainArgument, List clauses, Message parent) :
|
| super.from(mainArgument, clauses, parent);
|
|
|
| Message zero;
|
| @@ -519,3 +576,69 @@ class Plural extends SubMessage {
|
| }
|
| }
|
|
|
| +/**
|
| + * 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 []=(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) {
|
| + var casesArgument = node.argumentList.arguments.elements[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();
|
| + }
|
| +}
|
|
|