| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |