/
message_serializer.ts
96 lines (83 loc) 路 3.49 KB
/
message_serializer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* @license
* Copyright Google LLC 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 {Element, Expansion, ExpansionCase, Node, Text, visitAll} from '@angular/compiler';
import {BaseVisitor} from '../base_visitor';
import {TranslationParseError} from '../translation_parsers/translation_parse_error';
import {getAttribute, getAttrOrThrow} from '../translation_parsers/translation_utils';
import {MessageRenderer} from './message_renderer';
export interface MessageSerializerConfig {
inlineElements: string[];
placeholder?: {elementName: string; nameAttribute: string; bodyAttribute?: string;};
placeholderContainer?: {elementName: string; startAttribute: string; endAttribute: string;};
}
/**
* This visitor will walk over a set of XML nodes, which represent an i18n message, and serialize
* them into a message object of type `T`.
* The type of the serialized message is controlled by the
*/
export class MessageSerializer<T> extends BaseVisitor {
constructor(private renderer: MessageRenderer<T>, private config: MessageSerializerConfig) {
super();
}
serialize(nodes: Node[]): T {
this.renderer.startRender();
visitAll(this, nodes);
this.renderer.endRender();
return this.renderer.message;
}
visitElement(element: Element): void {
if (this.config.placeholder && element.name === this.config.placeholder.elementName) {
const name = getAttrOrThrow(element, this.config.placeholder.nameAttribute);
const body = this.config.placeholder.bodyAttribute &&
getAttribute(element, this.config.placeholder.bodyAttribute);
this.visitPlaceholder(name, body);
} else if (
this.config.placeholderContainer &&
element.name === this.config.placeholderContainer.elementName) {
const start = getAttrOrThrow(element, this.config.placeholderContainer.startAttribute);
const end = getAttrOrThrow(element, this.config.placeholderContainer.endAttribute);
this.visitPlaceholderContainer(start, element.children, end);
} else if (this.config.inlineElements.indexOf(element.name) !== -1) {
visitAll(this, element.children);
} else {
throw new TranslationParseError(element.sourceSpan, `Invalid element found in message.`);
}
}
visitText(text: Text): void {
this.renderer.text(text.value);
}
visitExpansion(expansion: Expansion): void {
this.renderer.startIcu();
this.renderer.text(`${expansion.switchValue}, ${expansion.type},`);
visitAll(this, expansion.cases);
this.renderer.endIcu();
}
visitExpansionCase(expansionCase: ExpansionCase): void {
this.renderer.text(` ${expansionCase.value} {`);
this.renderer.startContainer();
visitAll(this, expansionCase.expression);
this.renderer.closeContainer();
this.renderer.text(`}`);
}
visitContainedNodes(nodes: Node[]): void {
this.renderer.startContainer();
visitAll(this, nodes);
this.renderer.closeContainer();
}
visitPlaceholder(name: string, body: string|undefined): void {
this.renderer.placeholder(name, body);
}
visitPlaceholderContainer(startName: string, children: Node[], closeName: string): void {
this.renderer.startPlaceholder(startName);
this.visitContainedNodes(children);
this.renderer.closePlaceholder(closeName);
}
private isPlaceholderContainer(node: Node): boolean {
return node instanceof Element && node.name === this.config.placeholderContainer!.elementName;
}
}