Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: pkg/intl/lib/extract_messages.dart

Issue 771253002: Add a flag to prohibit plurals/genders that don't take up the whole string. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Review fixes Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/intl/bin/generate_from_arb.dart ('k') | pkg/intl/pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * This is for use in extracting messages from a Dart program 6 * This is for use in extracting messages from a Dart program
7 * using the Intl.message() mechanism and writing them to a file for 7 * using the Intl.message() mechanism and writing them to a file for
8 * translation. This provides only the stub of a mechanism, because it 8 * translation. This provides only the stub of a mechanism, because it
9 * doesn't define how the file should be written. It provides an 9 * doesn't define how the file should be written. It provides an
10 * [IntlMessage] class that holds the extracted data and [parseString] 10 * [IntlMessage] class that holds the extracted data and [parseString]
(...skipping 29 matching lines...) Expand all
40 /** 40 /**
41 * This accumulates a list of all warnings/errors we have found. These are 41 * This accumulates a list of all warnings/errors we have found. These are
42 * saved as strings right now, so all that can really be done is print and 42 * saved as strings right now, so all that can really be done is print and
43 * count them. 43 * count them.
44 */ 44 */
45 List<String> warnings = []; 45 List<String> warnings = [];
46 46
47 /** Were there any warnings or errors in extracting messages. */ 47 /** Were there any warnings or errors in extracting messages. */
48 bool get hasWarnings => warnings.isNotEmpty; 48 bool get hasWarnings => warnings.isNotEmpty;
49 49
50 /** Are plural and gender expressions required to be at the top level
51 * of an expression, or are they allowed to be embedded in string literals.
52 *
53 * For example, the following expression
54 * 'There are ${Intl.plural(...)} items'.
55 * is legal if [allowEmbeddedPluralsAndGenders] is true, but illegal
56 * if [allowEmbeddedPluralsAndGenders] is false.
57 */
58 bool allowEmbeddedPluralsAndGenders = true;
59
50 /** 60 /**
51 * Parse the source of the Dart program file [file] and return a Map from 61 * Parse the source of the Dart program file [file] and return a Map from
52 * message names to [IntlMessage] instances. 62 * message names to [IntlMessage] instances.
53 */ 63 */
54 Map<String, MainMessage> parseFile(File file) { 64 Map<String, MainMessage> parseFile(File file) {
55 try { 65 try {
56 _root = parseDartFile(file.path); 66 _root = parseDartFile(file.path);
57 } on AnalyzerErrorGroup catch (e) { 67 } on AnalyzerErrorGroup catch (e) {
58 print("Error in parsing ${file.path}, no messages extracted."); 68 print("Error in parsing ${file.path}, no messages extracted.");
59 print(" $e"); 69 print(" $e");
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 * by calling [setAttribute]. This is the common parts between 231 * by calling [setAttribute]. This is the common parts between
222 * [messageFromIntlMessageCall] and [messageFromDirectPluralOrGenderCall]. 232 * [messageFromIntlMessageCall] and [messageFromDirectPluralOrGenderCall].
223 */ 233 */
224 MainMessage _messageFromNode(MethodInvocation node, Function extract, 234 MainMessage _messageFromNode(MethodInvocation node, Function extract,
225 Function setAttribute) { 235 Function setAttribute) {
226 var message = new MainMessage(); 236 var message = new MainMessage();
227 message.name = name; 237 message.name = name;
228 message.arguments = parameters.parameters.map( 238 message.arguments = parameters.parameters.map(
229 (x) => x.identifier.name).toList(); 239 (x) => x.identifier.name).toList();
230 var arguments = node.argumentList.arguments; 240 var arguments = node.argumentList.arguments;
231 extract(message, arguments); 241 var extractionResult = extract(message, arguments);
242 if (extractionResult == null) return null;
232 243
233 for (var namedArgument in arguments.where((x) => x is NamedExpression)) { 244 for (var namedArgument in arguments.where((x) => x is NamedExpression)) {
234 var name = namedArgument.name.label.name; 245 var name = namedArgument.name.label.name;
235 var exp = namedArgument.expression; 246 var exp = namedArgument.expression;
236 var evaluator = new ConstantEvaluator(); 247 var evaluator = new ConstantEvaluator();
237 var basicValue = exp.accept(evaluator); 248 var basicValue = exp.accept(evaluator);
238 var value = basicValue == ConstantEvaluator.NOT_A_CONSTANT ? 249 var value = basicValue == ConstantEvaluator.NOT_A_CONSTANT ?
239 exp.toString() : basicValue; 250 exp.toString() : basicValue;
240 setAttribute(message, name, value); 251 setAttribute(message, name, value);
241 } 252 }
242 return message; 253 return message;
243 } 254 }
244 255
245 /** 256 /**
246 * Create a MainMessage from [node] using the name and 257 * Create a MainMessage from [node] using the name and
247 * parameters of the last function/method declaration we encountered 258 * parameters of the last function/method declaration we encountered
248 * and the parameters to the Intl.message call. 259 * and the parameters to the Intl.message call.
249 */ 260 */
250 MainMessage messageFromIntlMessageCall(MethodInvocation node) { 261 MainMessage messageFromIntlMessageCall(MethodInvocation node) {
251 262
252 void extractFromIntlCall(MainMessage message, List arguments) { 263 MainMessage extractFromIntlCall(MainMessage message, List arguments) {
253 try { 264 try {
254 var interpolation = new InterpolationVisitor(message); 265 var interpolation = new InterpolationVisitor(message);
255 arguments.first.accept(interpolation); 266 arguments.first.accept(interpolation);
267 if (interpolation.pieces.any((x) => x is Plural || x is Gender) &&
268 !allowEmbeddedPluralsAndGenders) {
269 if (interpolation.pieces.any((x) => x is String && x.isNotEmpty)) {
270 throw new IntlMessageExtractionException(
271 "Plural and gender expressions must be at the top level, "
272 "they cannot be embedded in larger string literals.\n"
273 "Error at $node");
274 }
275 }
256 message.messagePieces.addAll(interpolation.pieces); 276 message.messagePieces.addAll(interpolation.pieces);
257 } on IntlMessageExtractionException catch (e) { 277 } on IntlMessageExtractionException catch (e) {
258 message = null; 278 message = null;
259 var err = new StringBuffer() 279 var err = new StringBuffer()
260 ..writeAll(["Error ", e, "\nProcessing <", node, ">\n"]) 280 ..writeAll(["Error ", e, "\nProcessing <", node, ">\n"])
261 ..write(_reportErrorLocation(node)); 281 ..write(_reportErrorLocation(node));
262 print(err); 282 print(err);
263 warnings.add(err); 283 warnings.add(err.toString());
264 } 284 }
285 return message; // Because we may have set it to null on an error.
265 } 286 }
266 287
267 void setValue(MainMessage message, String fieldName, Object fieldValue) { 288 void setValue(MainMessage message, String fieldName, Object fieldValue) {
268 message[fieldName] = fieldValue; 289 message[fieldName] = fieldValue;
269 } 290 }
270 291
271 return _messageFromNode(node, extractFromIntlCall, setValue); 292 return _messageFromNode(node, extractFromIntlCall, setValue);
272 } 293 }
273 294
274 /** 295 /**
275 * Create a MainMessage from [node] using the name and 296 * Create a MainMessage from [node] using the name and
276 * parameters of the last function/method declaration we encountered 297 * parameters of the last function/method declaration we encountered
277 * and the parameters to the Intl.plural or Intl.gender call. 298 * and the parameters to the Intl.plural or Intl.gender call.
278 */ 299 */
279 MainMessage messageFromDirectPluralOrGenderCall(MethodInvocation node) { 300 MainMessage messageFromDirectPluralOrGenderCall(MethodInvocation node) {
280 var pluralOrGender; 301 var pluralOrGender;
281 302
282 void extractFromPluralOrGender(MainMessage message, _) { 303 MainMessage extractFromPluralOrGender(MainMessage message, _) {
283 var visitor = new PluralAndGenderVisitor(message.messagePieces, message); 304 var visitor = new PluralAndGenderVisitor(message.messagePieces, message);
284 node.accept(visitor); 305 node.accept(visitor);
285 pluralOrGender = message.messagePieces.last; 306 pluralOrGender = message.messagePieces.last;
307 return message;
286 } 308 }
287 309
288 void setAttribute(MainMessage msg, String fieldName, String fieldValue) { 310 void setAttribute(MainMessage msg, String fieldName, String fieldValue) {
289 if (msg.attributeNames.contains(fieldName)) { 311 if (msg.attributeNames.contains(fieldName)) {
290 msg[fieldName] = fieldValue; 312 msg[fieldName] = fieldValue;
291 } 313 }
292 } 314 }
293 return _messageFromNode(node, extractFromPluralOrGender, setAttribute); 315 return _messageFromNode(node, extractFromPluralOrGender, setAttribute);
294 } 316 }
295 } 317 }
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 * Returns a String describing why the node is invalid, or null if no 439 * Returns a String describing why the node is invalid, or null if no
418 * reason is found, so it's presumed valid. 440 * reason is found, so it's presumed valid.
419 */ 441 */
420 String checkValidity(MethodInvocation node) { 442 String checkValidity(MethodInvocation node) {
421 // TODO(alanknight): Add reasonable validity checks. 443 // TODO(alanknight): Add reasonable validity checks.
422 return null; 444 return null;
423 } 445 }
424 446
425 /** 447 /**
426 * Create a MainMessage from [node] using the name and 448 * Create a MainMessage from [node] using the name and
427 * parameters of the last function/method declaration we encountered e 449 * parameters of the last function/method declaration we encountered
428 * and the parameters to the Intl.message call. 450 * and the parameters to the Intl.message call.
429 */ 451 */
430 Message messageFromMethodInvocation(MethodInvocation node) { 452 Message messageFromMethodInvocation(MethodInvocation node) {
431 var message; 453 var message;
432 switch(node.methodName.name) { 454 switch(node.methodName.name) {
433 case "gender" : message = new Gender(); break; 455 case "gender" : message = new Gender(); break;
434 case "plural" : message = new Plural(); break; 456 case "plural" : message = new Plural(); break;
435 case "select" : message = new Select(); break; 457 case "select" : message = new Select(); break;
436 default: throw new IntlMessageExtractionException( 458 default: throw new IntlMessageExtractionException(
437 "Invalid plural/gender/select message"); 459 "Invalid plural/gender/select message");
438 } 460 }
439 message.parent = parent; 461 message.parent = parent;
440 462
441 var arguments = message.argumentsOfInterestFor(node); 463 var arguments = message.argumentsOfInterestFor(node);
442 arguments.forEach((key, value) { 464 arguments.forEach((key, value) {
443 try { 465 try {
444 var interpolation = new InterpolationVisitor(message); 466 var interpolation = new InterpolationVisitor(message);
445 value.accept(interpolation); 467 value.accept(interpolation);
446 message[key] = interpolation.pieces; 468 message[key] = interpolation.pieces;
447 } on IntlMessageExtractionException catch (e) { 469 } on IntlMessageExtractionException catch (e) {
448 message = null; 470 message = null;
449 var err = new StringBuffer() 471 var err = new StringBuffer()
450 ..writeAll(["Error ", e, "\nProcessing <", node, ">"]) 472 ..writeAll(["Error ", e, "\nProcessing <", node, ">"])
451 ..write(_reportErrorLocation(node)); 473 ..write(_reportErrorLocation(node));
452 print(err); 474 print(err);
453 warnings.add(err); 475 warnings.add(err.toString());
454 } 476 }
455 }); 477 });
456 var mainArg = node.argumentList.arguments.firstWhere( 478 var mainArg = node.argumentList.arguments.firstWhere(
457 (each) => each is! NamedExpression); 479 (each) => each is! NamedExpression);
458 if (mainArg is SimpleStringLiteral) { 480 if (mainArg is SimpleStringLiteral) {
459 message.mainArgument = mainArg.toString(); 481 message.mainArgument = mainArg.toString();
460 } else { 482 } else {
461 message.mainArgument = mainArg.name; 483 message.mainArgument = mainArg.name;
462 } 484 }
463 return message; 485 return message;
464 } 486 }
465 } 487 }
466 488
467 /** 489 /**
468 * Exception thrown when we cannot process a message properly. 490 * Exception thrown when we cannot process a message properly.
469 */ 491 */
470 class IntlMessageExtractionException implements Exception { 492 class IntlMessageExtractionException implements Exception {
471 /** 493 /**
472 * A message describing the error. 494 * A message describing the error.
473 */ 495 */
474 final String message; 496 final String message;
475 497
476 /** 498 /**
477 * Creates a new exception with an optional error [message]. 499 * Creates a new exception with an optional error [message].
478 */ 500 */
479 const IntlMessageExtractionException([this.message = ""]); 501 const IntlMessageExtractionException([this.message = ""]);
480 502
481 String toString() => "IntlMessageExtractionException: $message"; 503 String toString() => "IntlMessageExtractionException: $message";
482 } 504 }
OLDNEW
« no previous file with comments | « pkg/intl/bin/generate_from_arb.dart ('k') | pkg/intl/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698