angular2
Version:
Angular 2 - a web framework for modern web apps
167 lines (166 loc) • 5.97 kB
JavaScript
import { ParseError } from 'angular2/src/compiler/parse_util';
import { HtmlElementAst, HtmlTextAst, HtmlCommentAst, htmlVisitAll } from 'angular2/src/compiler/html_ast';
import { isPresent, isBlank, StringWrapper } from 'angular2/src/facade/lang';
import { Message } from './message';
export const I18N_ATTR = "i18n";
export const I18N_ATTR_PREFIX = "i18n-";
var CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*"([\s\S]*?)"[\s\S]*\)/g;
/**
* An i18n error.
*/
export class I18nError extends ParseError {
constructor(span, msg) {
super(span, msg);
}
}
// Man, this is so ugly!
export function partition(nodes, errors) {
let res = [];
for (let i = 0; i < nodes.length; ++i) {
let n = nodes[i];
let temp = [];
if (_isOpeningComment(n)) {
let i18n = n.value.substring(5).trim();
i++;
while (!_isClosingComment(nodes[i])) {
temp.push(nodes[i++]);
if (i === nodes.length) {
errors.push(new I18nError(n.sourceSpan, "Missing closing 'i18n' comment."));
break;
}
}
res.push(new Part(null, null, temp, i18n, true));
}
else if (n instanceof HtmlElementAst) {
let i18n = _findI18nAttr(n);
res.push(new Part(n, null, n.children, isPresent(i18n) ? i18n.value : null, isPresent(i18n)));
}
else if (n instanceof HtmlTextAst) {
res.push(new Part(null, n, null, null, false));
}
}
return res;
}
export class Part {
constructor(rootElement, rootTextNode, children, i18n, hasI18n) {
this.rootElement = rootElement;
this.rootTextNode = rootTextNode;
this.children = children;
this.i18n = i18n;
this.hasI18n = hasI18n;
}
get sourceSpan() {
if (isPresent(this.rootElement))
return this.rootElement.sourceSpan;
else if (isPresent(this.rootTextNode))
return this.rootTextNode.sourceSpan;
else
return this.children[0].sourceSpan;
}
createMessage(parser) {
return new Message(stringifyNodes(this.children, parser), meaning(this.i18n), description(this.i18n));
}
}
function _isOpeningComment(n) {
return n instanceof HtmlCommentAst && isPresent(n.value) && n.value.startsWith("i18n:");
}
function _isClosingComment(n) {
return n instanceof HtmlCommentAst && isPresent(n.value) && n.value == "/i18n";
}
function _findI18nAttr(p) {
let i18n = p.attrs.filter(a => a.name == I18N_ATTR);
return i18n.length == 0 ? null : i18n[0];
}
export function meaning(i18n) {
if (isBlank(i18n) || i18n == "")
return null;
return i18n.split("|")[0];
}
export function description(i18n) {
if (isBlank(i18n) || i18n == "")
return null;
let parts = i18n.split("|");
return parts.length > 1 ? parts[1] : null;
}
export function messageFromAttribute(parser, p, attr) {
let expectedName = attr.name.substring(5);
let matching = p.attrs.filter(a => a.name == expectedName);
if (matching.length > 0) {
let value = removeInterpolation(matching[0].value, matching[0].sourceSpan, parser);
return new Message(value, meaning(attr.value), description(attr.value));
}
else {
throw new I18nError(p.sourceSpan, `Missing attribute '${expectedName}'.`);
}
}
export function removeInterpolation(value, source, parser) {
try {
let parsed = parser.splitInterpolation(value, source.toString());
let usedNames = new Map();
if (isPresent(parsed)) {
let res = "";
for (let i = 0; i < parsed.strings.length; ++i) {
res += parsed.strings[i];
if (i != parsed.strings.length - 1) {
let customPhName = getPhNameFromBinding(parsed.expressions[i], i);
customPhName = dedupePhName(usedNames, customPhName);
res += `<ph name="${customPhName}"/>`;
}
}
return res;
}
else {
return value;
}
}
catch (e) {
return value;
}
}
export function getPhNameFromBinding(input, index) {
let customPhMatch = StringWrapper.split(input, CUSTOM_PH_EXP);
return customPhMatch.length > 1 ? customPhMatch[1] : `${index}`;
}
export function dedupePhName(usedNames, name) {
let duplicateNameCount = usedNames.get(name);
if (isPresent(duplicateNameCount)) {
usedNames.set(name, duplicateNameCount + 1);
return `${name}_${duplicateNameCount}`;
}
else {
usedNames.set(name, 1);
return name;
}
}
export function stringifyNodes(nodes, parser) {
let visitor = new _StringifyVisitor(parser);
return htmlVisitAll(visitor, nodes).join("");
}
class _StringifyVisitor {
constructor(_parser) {
this._parser = _parser;
this._index = 0;
}
visitElement(ast, context) {
let name = this._index++;
let children = this._join(htmlVisitAll(this, ast.children), "");
return `<ph name="e${name}">${children}</ph>`;
}
visitAttr(ast, context) { return null; }
visitText(ast, context) {
let index = this._index++;
let noInterpolation = removeInterpolation(ast.value, ast.sourceSpan, this._parser);
if (noInterpolation != ast.value) {
return `<ph name="t${index}">${noInterpolation}</ph>`;
}
else {
return ast.value;
}
}
visitComment(ast, context) { return ""; }
visitExpansion(ast, context) { return null; }
visitExpansionCase(ast, context) { return null; }
_join(strs, str) {
return strs.filter(s => s.length > 0).join(str);
}
}