2016-03-16 14:58:43 -04:00
|
|
|
import 'package:build/build.dart';
|
|
|
|
import 'package:analyzer/src/generated/element.dart';
|
|
|
|
import 'src/transform/common/url_resolver.dart';
|
2016-01-06 17:13:44 -05:00
|
|
|
|
2016-03-16 14:58:43 -04:00
|
|
|
import 'dart:async';
|
|
|
|
import 'package:angular2/i18n.dart';
|
2016-01-06 17:13:44 -05:00
|
|
|
import 'package:angular2/src/compiler/expression_parser/parser.dart';
|
|
|
|
import 'package:angular2/src/compiler/expression_parser/lexer.dart';
|
2016-03-16 14:58:43 -04:00
|
|
|
import 'package:angular2/src/compiler/html_parser.dart';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An command-line utility extracting i18n messages from an application.
|
|
|
|
*
|
|
|
|
* For instance, the following command will extract all the messages from the 'my-app-package' package, where
|
|
|
|
* index.dart is the entry point, and will serialize them into i18n-messages.xml.
|
|
|
|
*
|
|
|
|
* pub run packages/angular2/extract_messages.dart 'my-app-package' 'web/src/index.dart' 'i18n-messages.xml'
|
|
|
|
*/
|
|
|
|
main(List<String> args) async {
|
|
|
|
final input = new InputSet(args[0], [args[1]]);
|
|
|
|
final output = new AssetId(args[0], args[2]);
|
|
|
|
|
|
|
|
await build(new PhaseGroup.singleAction(new I18nMessageExtractorBuilder(output), input));
|
|
|
|
}
|
|
|
|
|
|
|
|
class I18nMessageExtractorBuilder implements Builder {
|
|
|
|
final AssetId outputAssetId;
|
|
|
|
|
|
|
|
I18nMessageExtractorBuilder(this.outputAssetId);
|
|
|
|
|
|
|
|
Future build(BuildStep buildStep) async {
|
|
|
|
final resolver = await buildStep.resolve(buildStep.input.id);
|
|
|
|
final entryLib = resolver.getLibrary(buildStep.input.id);
|
|
|
|
|
|
|
|
final extractor = new I18nMessageExtractor((path) => buildStep.readAsString(path));
|
|
|
|
await extractor.processLibrary(entryLib);
|
|
|
|
resolver.release();
|
|
|
|
|
|
|
|
if (extractor.errors.length > 0) {
|
|
|
|
print("Errors:");
|
|
|
|
extractor.errors.forEach(print);
|
|
|
|
throw "Failed to extract messages";
|
|
|
|
|
|
|
|
} else {
|
|
|
|
await buildStep.writeAsString(new Asset(outputAssetId, extractor.output));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
List<AssetId> declareOutputs(AssetId inputId) => [outputAssetId];
|
|
|
|
}
|
|
|
|
|
|
|
|
class I18nMessageExtractor {
|
2016-01-06 17:13:44 -05:00
|
|
|
final urlResovler = createOfflineCompileUrlResolver();
|
2016-03-16 14:58:43 -04:00
|
|
|
final List<Message> messages = [];
|
|
|
|
final List errors = [];
|
|
|
|
final HtmlParser htmlParser = new HtmlParser();
|
2016-01-06 17:13:44 -05:00
|
|
|
final Parser parser = new Parser(new Lexer());
|
2016-03-16 14:58:43 -04:00
|
|
|
|
|
|
|
final Function readInput;
|
|
|
|
|
|
|
|
I18nMessageExtractor(this.readInput);
|
|
|
|
|
2016-03-27 14:12:48 -04:00
|
|
|
String get output => serializeXmb(removeDuplicates(messages));
|
2016-03-16 14:58:43 -04:00
|
|
|
|
|
|
|
Future processLibrary(LibraryElement el) async {
|
|
|
|
return Future.wait(el.units.map(processCompilationUnit));
|
|
|
|
}
|
|
|
|
|
|
|
|
Future processCompilationUnit(CompilationUnitElement el) async {
|
|
|
|
return Future.wait(el.types.map(processClass));
|
|
|
|
}
|
|
|
|
|
|
|
|
Future processClass(ClassElement el) async {
|
|
|
|
final baseUrl = (el.source as dynamic).assetId;
|
|
|
|
final filtered = el.metadata.where((m) {
|
|
|
|
if (m.element is ConstructorElement) {
|
|
|
|
final isComponent = m.element.enclosingElement.name == "Component" &&
|
|
|
|
m.element.library.displayName == "angular2.src.core.metadata";
|
|
|
|
|
|
|
|
final isView = m.element.enclosingElement.name == "View" &&
|
|
|
|
m.element.library.displayName == "angular2.src.core.metadata";
|
|
|
|
|
|
|
|
return isComponent || isView;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return Future.wait(filtered.map((m) => processAnnotation(el, m, baseUrl)));
|
|
|
|
}
|
|
|
|
|
|
|
|
Future processAnnotation(ClassElement el, ElementAnnotation m, baseUrl) async {
|
|
|
|
final fields = (m.constantValue as dynamic).fields["(super)"].fields;
|
|
|
|
final template = fields["template"];
|
|
|
|
final templateUrl = fields["templateUrl"];
|
|
|
|
|
|
|
|
if (template != null && !template.isNull) {
|
|
|
|
processTemplate(template.toStringValue(), baseUrl.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (templateUrl != null && !templateUrl.isNull) {
|
|
|
|
final value = templateUrl.toStringValue();
|
|
|
|
final resolvedPath = urlResovler.resolve(toAssetUri(baseUrl), value);
|
|
|
|
final template = await readInput(fromUri(resolvedPath));
|
|
|
|
processTemplate(template.toStringValue(), baseUrl.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void processTemplate(String template, String sourceUrl) {
|
|
|
|
final m = new MessageExtractor(htmlParser, parser);
|
|
|
|
final res = m.extract(template, sourceUrl);
|
|
|
|
if (res.errors.isNotEmpty) {
|
|
|
|
errors.addAll(res.errors);
|
|
|
|
} else {
|
|
|
|
messages.addAll(res.messages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|