/
message_bundle.ts
120 lines (100 loc) Β· 4.44 KB
/
message_bundle.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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* @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 {InterpolationConfig} from '../ml_parser/defaults';
import {HtmlParser} from '../ml_parser/html_parser';
import {ParseError} from '../parse_util';
import {extractMessages} from './extractor_merger';
import * as i18n from './i18n_ast';
import {PlaceholderMapper, Serializer} from './serializers/serializer';
/**
* A container for message extracted from the templates.
*/
export class MessageBundle {
private _messages: i18n.Message[] = [];
constructor(
private _htmlParser: HtmlParser, private _implicitTags: string[],
private _implicitAttrs: {[k: string]: string[]}, private _locale: string|null = null) {}
updateFromTemplate(html: string, url: string, interpolationConfig: InterpolationConfig):
ParseError[] {
const htmlParserResult =
this._htmlParser.parse(html, url, {tokenizeExpansionForms: true, interpolationConfig});
if (htmlParserResult.errors.length) {
return htmlParserResult.errors;
}
const i18nParserResult = extractMessages(
htmlParserResult.rootNodes, interpolationConfig, this._implicitTags, this._implicitAttrs);
if (i18nParserResult.errors.length) {
return i18nParserResult.errors;
}
this._messages.push(...i18nParserResult.messages);
return [];
}
// Return the message in the internal format
// The public (serialized) format might be different, see the `write` method.
getMessages(): i18n.Message[] {
return this._messages;
}
write(serializer: Serializer, filterSources?: (path: string) => string): string {
const messages: {[id: string]: i18n.Message} = {};
const mapperVisitor = new MapPlaceholderNames();
// Deduplicate messages based on their ID
this._messages.forEach(message => {
const id = serializer.digest(message);
if (!messages.hasOwnProperty(id)) {
messages[id] = message;
} else {
messages[id].sources.push(...message.sources);
}
});
// Transform placeholder names using the serializer mapping
const msgList = Object.keys(messages).map(id => {
const mapper = serializer.createNameMapper(messages[id]);
const src = messages[id];
const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes;
let transformedMessage = new i18n.Message(nodes, {}, {}, src.meaning, src.description, id);
transformedMessage.sources = src.sources;
if (filterSources) {
transformedMessage.sources.forEach(
(source: i18n.MessageSpan) => source.filePath = filterSources(source.filePath));
}
return transformedMessage;
});
return serializer.write(msgList, this._locale);
}
}
// Transform an i18n AST by renaming the placeholder nodes with the given mapper
class MapPlaceholderNames extends i18n.CloneVisitor {
convert(nodes: i18n.Node[], mapper: PlaceholderMapper): i18n.Node[] {
return mapper ? nodes.map(n => n.visit(this, mapper)) : nodes;
}
override visitTagPlaceholder(ph: i18n.TagPlaceholder, mapper: PlaceholderMapper):
i18n.TagPlaceholder {
const startName = mapper.toPublicName(ph.startName)!;
const closeName = ph.closeName ? mapper.toPublicName(ph.closeName)! : ph.closeName;
const children = ph.children.map(n => n.visit(this, mapper));
return new i18n.TagPlaceholder(
ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan,
ph.startSourceSpan, ph.endSourceSpan);
}
override visitBlockPlaceholder(ph: i18n.BlockPlaceholder, mapper: PlaceholderMapper):
i18n.BlockPlaceholder {
const startName = mapper.toPublicName(ph.startName)!;
const closeName = ph.closeName ? mapper.toPublicName(ph.closeName)! : ph.closeName;
const children = ph.children.map(n => n.visit(this, mapper));
return new i18n.BlockPlaceholder(
ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan,
ph.endSourceSpan);
}
override visitPlaceholder(ph: i18n.Placeholder, mapper: PlaceholderMapper): i18n.Placeholder {
return new i18n.Placeholder(ph.value, mapper.toPublicName(ph.name)!, ph.sourceSpan);
}
override visitIcuPlaceholder(ph: i18n.IcuPlaceholder, mapper: PlaceholderMapper):
i18n.IcuPlaceholder {
return new i18n.IcuPlaceholder(ph.value, mapper.toPublicName(ph.name)!, ph.sourceSpan);
}
}