115 lines
3.8 KiB
TypeScript
Raw Normal View History

2016-07-15 09:42:33 -07:00
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {HtmlParser} from '../ml_parser/html_parser';
import {InterpolationConfig} from '../ml_parser/interpolation_config';
2016-07-21 13:56:58 -07:00
import {ParseError} from '../parse_util';
2016-07-15 09:42:33 -07:00
import * as extractor from './extractor_merger';
2016-07-21 13:56:58 -07:00
import * as i18n from './i18n_ast';
2016-07-15 09:42:33 -07:00
import {Serializer} from './serializers/serializer';
2016-07-21 13:56:58 -07:00
/**
* A container for message extracted from the templates.
*/
export class MessageBundle {
private _messageMap: {[id: string]: i18n.Message} = {};
2016-07-15 09:42:33 -07:00
constructor(
private _htmlParser: HtmlParser, private _implicitTags: string[],
private _implicitAttrs: {[k: string]: string[]}) {}
2016-07-21 13:56:58 -07:00
updateFromTemplate(html: string, url: string, interpolationConfig: InterpolationConfig):
ParseError[] {
2016-07-15 09:42:33 -07:00
const htmlParserResult = this._htmlParser.parse(html, url, true, interpolationConfig);
if (htmlParserResult.errors.length) {
2016-07-21 13:56:58 -07:00
return htmlParserResult.errors;
2016-07-15 09:42:33 -07:00
}
const i18nParserResult = extractor.extractMessages(
2016-07-15 09:42:33 -07:00
htmlParserResult.rootNodes, interpolationConfig, this._implicitTags, this._implicitAttrs);
if (i18nParserResult.errors.length) {
return i18nParserResult.errors;
}
i18nParserResult.messages.forEach((message) => {
2016-08-01 18:10:34 -07:00
this._messageMap[digestMessage(message.nodes, message.meaning)] = message;
2016-07-15 09:42:33 -07:00
});
}
2016-07-21 13:56:58 -07:00
write(serializer: Serializer): string { return serializer.write(this._messageMap); }
2016-07-15 09:42:33 -07:00
}
2016-08-01 18:10:34 -07:00
export function digestMessage(nodes: i18n.Node[], meaning: string): string {
2016-08-01 14:43:20 -07:00
return strHash(serializeNodes(nodes).join('') + `[${meaning}]`);
}
2016-07-15 09:42:33 -07:00
/**
* String hash function similar to java.lang.String.hashCode().
* The hash code for a string is computed as
* s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
* where s[i] is the ith character of the string and n is the length of
* the string. We mod the result to make it between 0 (inclusive) and 2^32 (exclusive).
*
* Based on goog.string.hashCode from the Google Closure library
* https://github.com/google/closure-library/
*
* @internal
*/
// TODO(vicb): better algo (less collisions) ?
export function strHash(str: string): string {
let result: number = 0;
for (var i = 0; i < str.length; ++i) {
// Normalize to 4 byte range, 0 ... 2^32.
result = (31 * result + str.charCodeAt(i)) >>> 0;
}
return result.toString(16);
}
/**
* Serialize the i18n ast to something xml-like in order to generate an UID.
*
* The visitor is also used in the i18n parser tests
*
* @internal
*/
2016-07-21 13:56:58 -07:00
class _SerializerVisitor implements i18n.Visitor {
visitText(text: i18n.Text, context: any): any { return text.value; }
2016-07-15 09:42:33 -07:00
2016-07-21 13:56:58 -07:00
visitContainer(container: i18n.Container, context: any): any {
2016-07-15 09:42:33 -07:00
return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
}
2016-07-21 13:56:58 -07:00
visitIcu(icu: i18n.Icu, context: any): any {
2016-07-15 09:42:33 -07:00
let strCases = Object.keys(icu.cases).map((k: string) => `${k} {${icu.cases[k].visit(this)}}`);
return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
}
2016-07-21 13:56:58 -07:00
visitTagPlaceholder(ph: i18n.TagPlaceholder, context: any): any {
2016-07-15 09:42:33 -07:00
return ph.isVoid ?
`<ph tag name="${ph.startName}"/>` :
`<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
}
2016-07-21 13:56:58 -07:00
visitPlaceholder(ph: i18n.Placeholder, context: any): any {
2016-07-15 09:42:33 -07:00
return `<ph name="${ph.name}">${ph.value}</ph>`;
}
2016-07-21 13:56:58 -07:00
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
2016-07-15 09:42:33 -07:00
return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
}
}
const serializerVisitor = new _SerializerVisitor();
2016-08-01 14:43:20 -07:00
export function serializeNodes(nodes: i18n.Node[]): string[] {
return nodes.map(a => a.visit(serializerVisitor, null));
2016-07-15 09:42:33 -07:00
}