@angular/localize 21.0.0-next.0 → 21.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/_localize-chunk.mjs +257 -0
- package/fesm2022/_localize-chunk.mjs.map +1 -0
- package/fesm2022/init.mjs +3 -4
- package/fesm2022/init.mjs.map +1 -1
- package/fesm2022/localize.mjs +72 -171
- package/fesm2022/localize.mjs.map +1 -1
- package/package.json +7 -7
- package/schematics/ng-add/ng_add_bundle.cjs +1 -1
- package/types/init.d.ts +7 -0
- package/{index.d.ts → types/localize.d.ts} +2 -2
- package/fesm2022/localize2.mjs +0 -496
- package/fesm2022/localize2.mjs.map +0 -1
- package/init/index.d.ts +0 -7
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Angular v21.0.0-next.10
|
|
3
|
+
* (c) 2010-2025 Google LLC. https://angular.dev/
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const BLOCK_MARKER$1 = ':';
|
|
8
|
+
const MEANING_SEPARATOR = '|';
|
|
9
|
+
const ID_SEPARATOR = '@@';
|
|
10
|
+
const LEGACY_ID_INDICATOR = '\u241F';
|
|
11
|
+
|
|
12
|
+
let textEncoder;
|
|
13
|
+
function fingerprint(str) {
|
|
14
|
+
textEncoder ??= new TextEncoder();
|
|
15
|
+
const utf8 = textEncoder.encode(str);
|
|
16
|
+
const view = new DataView(utf8.buffer, utf8.byteOffset, utf8.byteLength);
|
|
17
|
+
let hi = hash32(view, utf8.length, 0);
|
|
18
|
+
let lo = hash32(view, utf8.length, 102072);
|
|
19
|
+
if (hi == 0 && (lo == 0 || lo == 1)) {
|
|
20
|
+
hi = hi ^ 0x130f9bef;
|
|
21
|
+
lo = lo ^ -0x6b5f56d8;
|
|
22
|
+
}
|
|
23
|
+
return BigInt.asUintN(32, BigInt(hi)) << BigInt(32) | BigInt.asUintN(32, BigInt(lo));
|
|
24
|
+
}
|
|
25
|
+
function computeMsgId(msg, meaning = '') {
|
|
26
|
+
let msgFingerprint = fingerprint(msg);
|
|
27
|
+
if (meaning) {
|
|
28
|
+
msgFingerprint = BigInt.asUintN(64, msgFingerprint << BigInt(1)) | msgFingerprint >> BigInt(63) & BigInt(1);
|
|
29
|
+
msgFingerprint += fingerprint(meaning);
|
|
30
|
+
}
|
|
31
|
+
return BigInt.asUintN(63, msgFingerprint).toString();
|
|
32
|
+
}
|
|
33
|
+
function hash32(view, length, c) {
|
|
34
|
+
let a = 0x9e3779b9,
|
|
35
|
+
b = 0x9e3779b9;
|
|
36
|
+
let index = 0;
|
|
37
|
+
const end = length - 12;
|
|
38
|
+
for (; index <= end; index += 12) {
|
|
39
|
+
a += view.getUint32(index, true);
|
|
40
|
+
b += view.getUint32(index + 4, true);
|
|
41
|
+
c += view.getUint32(index + 8, true);
|
|
42
|
+
const res = mix(a, b, c);
|
|
43
|
+
a = res[0], b = res[1], c = res[2];
|
|
44
|
+
}
|
|
45
|
+
const remainder = length - index;
|
|
46
|
+
c += length;
|
|
47
|
+
if (remainder >= 4) {
|
|
48
|
+
a += view.getUint32(index, true);
|
|
49
|
+
index += 4;
|
|
50
|
+
if (remainder >= 8) {
|
|
51
|
+
b += view.getUint32(index, true);
|
|
52
|
+
index += 4;
|
|
53
|
+
if (remainder >= 9) {
|
|
54
|
+
c += view.getUint8(index++) << 8;
|
|
55
|
+
}
|
|
56
|
+
if (remainder >= 10) {
|
|
57
|
+
c += view.getUint8(index++) << 16;
|
|
58
|
+
}
|
|
59
|
+
if (remainder === 11) {
|
|
60
|
+
c += view.getUint8(index++) << 24;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (remainder >= 5) {
|
|
64
|
+
b += view.getUint8(index++);
|
|
65
|
+
}
|
|
66
|
+
if (remainder >= 6) {
|
|
67
|
+
b += view.getUint8(index++) << 8;
|
|
68
|
+
}
|
|
69
|
+
if (remainder === 7) {
|
|
70
|
+
b += view.getUint8(index++) << 16;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
if (remainder >= 1) {
|
|
75
|
+
a += view.getUint8(index++);
|
|
76
|
+
}
|
|
77
|
+
if (remainder >= 2) {
|
|
78
|
+
a += view.getUint8(index++) << 8;
|
|
79
|
+
}
|
|
80
|
+
if (remainder === 3) {
|
|
81
|
+
a += view.getUint8(index++) << 16;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return mix(a, b, c)[2];
|
|
85
|
+
}
|
|
86
|
+
function mix(a, b, c) {
|
|
87
|
+
a -= b;
|
|
88
|
+
a -= c;
|
|
89
|
+
a ^= c >>> 13;
|
|
90
|
+
b -= c;
|
|
91
|
+
b -= a;
|
|
92
|
+
b ^= a << 8;
|
|
93
|
+
c -= a;
|
|
94
|
+
c -= b;
|
|
95
|
+
c ^= b >>> 13;
|
|
96
|
+
a -= b;
|
|
97
|
+
a -= c;
|
|
98
|
+
a ^= c >>> 12;
|
|
99
|
+
b -= c;
|
|
100
|
+
b -= a;
|
|
101
|
+
b ^= a << 16;
|
|
102
|
+
c -= a;
|
|
103
|
+
c -= b;
|
|
104
|
+
c ^= b >>> 5;
|
|
105
|
+
a -= b;
|
|
106
|
+
a -= c;
|
|
107
|
+
a ^= c >>> 3;
|
|
108
|
+
b -= c;
|
|
109
|
+
b -= a;
|
|
110
|
+
b ^= a << 10;
|
|
111
|
+
c -= a;
|
|
112
|
+
c -= b;
|
|
113
|
+
c ^= b >>> 15;
|
|
114
|
+
return [a, b, c];
|
|
115
|
+
}
|
|
116
|
+
var Endian;
|
|
117
|
+
(function (Endian) {
|
|
118
|
+
Endian[Endian["Little"] = 0] = "Little";
|
|
119
|
+
Endian[Endian["Big"] = 1] = "Big";
|
|
120
|
+
})(Endian || (Endian = {}));
|
|
121
|
+
|
|
122
|
+
function parseMessage(messageParts, expressions, location, messagePartLocations, expressionLocations = []) {
|
|
123
|
+
const substitutions = {};
|
|
124
|
+
const substitutionLocations = {};
|
|
125
|
+
const associatedMessageIds = {};
|
|
126
|
+
const metadata = parseMetadata(messageParts[0], messageParts.raw[0]);
|
|
127
|
+
const cleanedMessageParts = [metadata.text];
|
|
128
|
+
const placeholderNames = [];
|
|
129
|
+
let messageString = metadata.text;
|
|
130
|
+
for (let i = 1; i < messageParts.length; i++) {
|
|
131
|
+
const {
|
|
132
|
+
messagePart,
|
|
133
|
+
placeholderName = computePlaceholderName(i),
|
|
134
|
+
associatedMessageId
|
|
135
|
+
} = parsePlaceholder(messageParts[i], messageParts.raw[i]);
|
|
136
|
+
messageString += `{$${placeholderName}}${messagePart}`;
|
|
137
|
+
if (expressions !== undefined) {
|
|
138
|
+
substitutions[placeholderName] = expressions[i - 1];
|
|
139
|
+
substitutionLocations[placeholderName] = expressionLocations[i - 1];
|
|
140
|
+
}
|
|
141
|
+
placeholderNames.push(placeholderName);
|
|
142
|
+
if (associatedMessageId !== undefined) {
|
|
143
|
+
associatedMessageIds[placeholderName] = associatedMessageId;
|
|
144
|
+
}
|
|
145
|
+
cleanedMessageParts.push(messagePart);
|
|
146
|
+
}
|
|
147
|
+
const messageId = metadata.customId || computeMsgId(messageString, metadata.meaning || '');
|
|
148
|
+
const legacyIds = metadata.legacyIds ? metadata.legacyIds.filter(id => id !== messageId) : [];
|
|
149
|
+
return {
|
|
150
|
+
id: messageId,
|
|
151
|
+
legacyIds,
|
|
152
|
+
substitutions,
|
|
153
|
+
substitutionLocations,
|
|
154
|
+
text: messageString,
|
|
155
|
+
customId: metadata.customId,
|
|
156
|
+
meaning: metadata.meaning || '',
|
|
157
|
+
description: metadata.description || '',
|
|
158
|
+
messageParts: cleanedMessageParts,
|
|
159
|
+
messagePartLocations,
|
|
160
|
+
placeholderNames,
|
|
161
|
+
associatedMessageIds,
|
|
162
|
+
location
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function parseMetadata(cooked, raw) {
|
|
166
|
+
const {
|
|
167
|
+
text: messageString,
|
|
168
|
+
block
|
|
169
|
+
} = splitBlock(cooked, raw);
|
|
170
|
+
if (block === undefined) {
|
|
171
|
+
return {
|
|
172
|
+
text: messageString
|
|
173
|
+
};
|
|
174
|
+
} else {
|
|
175
|
+
const [meaningDescAndId, ...legacyIds] = block.split(LEGACY_ID_INDICATOR);
|
|
176
|
+
const [meaningAndDesc, customId] = meaningDescAndId.split(ID_SEPARATOR, 2);
|
|
177
|
+
let [meaning, description] = meaningAndDesc.split(MEANING_SEPARATOR, 2);
|
|
178
|
+
if (description === undefined) {
|
|
179
|
+
description = meaning;
|
|
180
|
+
meaning = undefined;
|
|
181
|
+
}
|
|
182
|
+
if (description === '') {
|
|
183
|
+
description = undefined;
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
text: messageString,
|
|
187
|
+
meaning,
|
|
188
|
+
description,
|
|
189
|
+
customId,
|
|
190
|
+
legacyIds
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function parsePlaceholder(cooked, raw) {
|
|
195
|
+
const {
|
|
196
|
+
text: messagePart,
|
|
197
|
+
block
|
|
198
|
+
} = splitBlock(cooked, raw);
|
|
199
|
+
if (block === undefined) {
|
|
200
|
+
return {
|
|
201
|
+
messagePart
|
|
202
|
+
};
|
|
203
|
+
} else {
|
|
204
|
+
const [placeholderName, associatedMessageId] = block.split(ID_SEPARATOR);
|
|
205
|
+
return {
|
|
206
|
+
messagePart,
|
|
207
|
+
placeholderName,
|
|
208
|
+
associatedMessageId
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function splitBlock(cooked, raw) {
|
|
213
|
+
if (raw.charAt(0) !== BLOCK_MARKER$1) {
|
|
214
|
+
return {
|
|
215
|
+
text: cooked
|
|
216
|
+
};
|
|
217
|
+
} else {
|
|
218
|
+
const endOfBlock = findEndOfBlock(cooked, raw);
|
|
219
|
+
return {
|
|
220
|
+
block: cooked.substring(1, endOfBlock),
|
|
221
|
+
text: cooked.substring(endOfBlock + 1)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function computePlaceholderName(index) {
|
|
226
|
+
return index === 1 ? 'PH' : `PH_${index - 1}`;
|
|
227
|
+
}
|
|
228
|
+
function findEndOfBlock(cooked, raw) {
|
|
229
|
+
for (let cookedIndex = 1, rawIndex = 1; cookedIndex < cooked.length; cookedIndex++, rawIndex++) {
|
|
230
|
+
if (raw[rawIndex] === '\\') {
|
|
231
|
+
rawIndex++;
|
|
232
|
+
} else if (cooked[cookedIndex] === BLOCK_MARKER$1) {
|
|
233
|
+
return cookedIndex;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
throw new Error(`Unterminated $localize metadata block in "${raw}".`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const $localize = function (messageParts, ...expressions) {
|
|
240
|
+
if ($localize.translate) {
|
|
241
|
+
const translation = $localize.translate(messageParts, expressions);
|
|
242
|
+
messageParts = translation[0];
|
|
243
|
+
expressions = translation[1];
|
|
244
|
+
}
|
|
245
|
+
let message = stripBlock(messageParts[0], messageParts.raw[0]);
|
|
246
|
+
for (let i = 1; i < messageParts.length; i++) {
|
|
247
|
+
message += expressions[i - 1] + stripBlock(messageParts[i], messageParts.raw[i]);
|
|
248
|
+
}
|
|
249
|
+
return message;
|
|
250
|
+
};
|
|
251
|
+
const BLOCK_MARKER = ':';
|
|
252
|
+
function stripBlock(messagePart, rawMessagePart) {
|
|
253
|
+
return rawMessagePart.charAt(0) === BLOCK_MARKER ? messagePart.substring(findEndOfBlock(messagePart, rawMessagePart) + 1) : messagePart;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export { $localize, BLOCK_MARKER$1 as BLOCK_MARKER, computeMsgId, findEndOfBlock, parseMessage, parseMetadata, splitBlock };
|
|
257
|
+
//# sourceMappingURL=_localize-chunk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_localize-chunk.mjs","sources":["../../../../../darwin_arm64-fastbuild-ST-199a4f3c4e20/bin/packages/compiler/src/i18n/digest.ts","../../../../../darwin_arm64-fastbuild-ST-199a4f3c4e20/bin/packages/localize/src/utils/src/messages.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Byte} from '../util';\n\nimport * as i18n from './i18n_ast';\n\n/**\n * A lazily created TextEncoder instance for converting strings into UTF-8 bytes\n */\nlet textEncoder: TextEncoder | undefined;\n\n/**\n * Return the message id or compute it using the XLIFF1 digest.\n */\nexport function digest(message: i18n.Message): string {\n return message.id || computeDigest(message);\n}\n\n/**\n * Compute the message id using the XLIFF1 digest.\n */\nexport function computeDigest(message: i18n.Message): string {\n return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);\n}\n\n/**\n * Return the message id or compute it using the XLIFF2/XMB/$localize digest.\n */\nexport function decimalDigest(message: i18n.Message): string {\n return message.id || computeDecimalDigest(message);\n}\n\n/**\n * Compute the message id using the XLIFF2/XMB/$localize digest.\n */\nexport function computeDecimalDigest(message: i18n.Message): string {\n const visitor = new _SerializerIgnoreIcuExpVisitor();\n const parts = message.nodes.map((a) => a.visit(visitor, null));\n return computeMsgId(parts.join(''), message.meaning);\n}\n\n/**\n * Serialize the i18n ast to something xml-like in order to generate an UID.\n *\n * The visitor is also used in the i18n parser tests\n *\n * @internal\n */\nclass _SerializerVisitor implements i18n.Visitor {\n visitText(text: i18n.Text, context: any): any {\n return text.value;\n }\n\n visitContainer(container: i18n.Container, context: any): any {\n return `[${container.children.map((child) => child.visit(this)).join(', ')}]`;\n }\n\n visitIcu(icu: i18n.Icu, context: any): any {\n const strCases = Object.keys(icu.cases).map(\n (k: string) => `${k} {${icu.cases[k].visit(this)}}`,\n );\n return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;\n }\n\n visitTagPlaceholder(ph: i18n.TagPlaceholder, context: any): any {\n return ph.isVoid\n ? `<ph tag name=\"${ph.startName}\"/>`\n : `<ph tag name=\"${ph.startName}\">${ph.children\n .map((child) => child.visit(this))\n .join(', ')}</ph name=\"${ph.closeName}\">`;\n }\n\n visitPlaceholder(ph: i18n.Placeholder, context: any): any {\n return ph.value ? `<ph name=\"${ph.name}\">${ph.value}</ph>` : `<ph name=\"${ph.name}\"/>`;\n }\n\n visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {\n return `<ph icu name=\"${ph.name}\">${ph.value.visit(this)}</ph>`;\n }\n\n visitBlockPlaceholder(ph: i18n.BlockPlaceholder, context: any): any {\n return `<ph block name=\"${ph.startName}\">${ph.children\n .map((child) => child.visit(this))\n .join(', ')}</ph name=\"${ph.closeName}\">`;\n }\n}\n\nconst serializerVisitor = new _SerializerVisitor();\n\nexport function serializeNodes(nodes: i18n.Node[]): string[] {\n return nodes.map((a) => a.visit(serializerVisitor, null));\n}\n\n/**\n * Serialize the i18n ast to something xml-like in order to generate an UID.\n *\n * Ignore the ICU expressions so that message IDs stays identical if only the expression changes.\n *\n * @internal\n */\nclass _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {\n override visitIcu(icu: i18n.Icu): string {\n let strCases = Object.keys(icu.cases).map((k: string) => `${k} {${icu.cases[k].visit(this)}}`);\n // Do not take the expression into account\n return `{${icu.type}, ${strCases.join(', ')}}`;\n }\n}\n\n/**\n * Compute the SHA1 of the given string\n *\n * see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf\n *\n * WARNING: this function has not been designed not tested with security in mind.\n * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT.\n */\nexport function sha1(str: string): string {\n textEncoder ??= new TextEncoder();\n const utf8 = [...textEncoder.encode(str)];\n const words32 = bytesToWords32(utf8, Endian.Big);\n const len = utf8.length * 8;\n\n const w = new Uint32Array(80);\n let a = 0x67452301,\n b = 0xefcdab89,\n c = 0x98badcfe,\n d = 0x10325476,\n e = 0xc3d2e1f0;\n\n words32[len >> 5] |= 0x80 << (24 - (len % 32));\n words32[(((len + 64) >> 9) << 4) + 15] = len;\n\n for (let i = 0; i < words32.length; i += 16) {\n const h0 = a,\n h1 = b,\n h2 = c,\n h3 = d,\n h4 = e;\n\n for (let j = 0; j < 80; j++) {\n if (j < 16) {\n w[j] = words32[i + j];\n } else {\n w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);\n }\n\n const fkVal = fk(j, b, c, d);\n const f = fkVal[0];\n const k = fkVal[1];\n const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);\n e = d;\n d = c;\n c = rol32(b, 30);\n b = a;\n a = temp;\n }\n a = add32(a, h0);\n b = add32(b, h1);\n c = add32(c, h2);\n d = add32(d, h3);\n e = add32(e, h4);\n }\n\n // Convert the output parts to a 160-bit hexadecimal string\n return toHexU32(a) + toHexU32(b) + toHexU32(c) + toHexU32(d) + toHexU32(e);\n}\n\n/**\n * Convert and format a number as a string representing a 32-bit unsigned hexadecimal number.\n * @param value The value to format as a string.\n * @returns A hexadecimal string representing the value.\n */\nfunction toHexU32(value: number): string {\n // unsigned right shift of zero ensures an unsigned 32-bit number\n return (value >>> 0).toString(16).padStart(8, '0');\n}\n\nfunction fk(index: number, b: number, c: number, d: number): [number, number] {\n if (index < 20) {\n return [(b & c) | (~b & d), 0x5a827999];\n }\n\n if (index < 40) {\n return [b ^ c ^ d, 0x6ed9eba1];\n }\n\n if (index < 60) {\n return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];\n }\n\n return [b ^ c ^ d, 0xca62c1d6];\n}\n\n/**\n * Compute the fingerprint of the given string\n *\n * The output is 64 bit number encoded as a decimal string\n *\n * based on:\n * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java\n */\nexport function fingerprint(str: string): bigint {\n textEncoder ??= new TextEncoder();\n const utf8 = textEncoder.encode(str);\n const view = new DataView(utf8.buffer, utf8.byteOffset, utf8.byteLength);\n\n let hi = hash32(view, utf8.length, 0);\n let lo = hash32(view, utf8.length, 102072);\n\n if (hi == 0 && (lo == 0 || lo == 1)) {\n hi = hi ^ 0x130f9bef;\n lo = lo ^ -0x6b5f56d8;\n }\n\n return (BigInt.asUintN(32, BigInt(hi)) << BigInt(32)) | BigInt.asUintN(32, BigInt(lo));\n}\n\nexport function computeMsgId(msg: string, meaning: string = ''): string {\n let msgFingerprint = fingerprint(msg);\n\n if (meaning) {\n // Rotate the 64-bit message fingerprint one bit to the left and then add the meaning\n // fingerprint.\n msgFingerprint =\n BigInt.asUintN(64, msgFingerprint << BigInt(1)) |\n ((msgFingerprint >> BigInt(63)) & BigInt(1));\n msgFingerprint += fingerprint(meaning);\n }\n\n return BigInt.asUintN(63, msgFingerprint).toString();\n}\n\nfunction hash32(view: DataView, length: number, c: number): number {\n let a = 0x9e3779b9,\n b = 0x9e3779b9;\n let index = 0;\n\n const end = length - 12;\n for (; index <= end; index += 12) {\n a += view.getUint32(index, true);\n b += view.getUint32(index + 4, true);\n c += view.getUint32(index + 8, true);\n const res = mix(a, b, c);\n (a = res[0]), (b = res[1]), (c = res[2]);\n }\n\n const remainder = length - index;\n\n // the first byte of c is reserved for the length\n c += length;\n\n if (remainder >= 4) {\n a += view.getUint32(index, true);\n index += 4;\n\n if (remainder >= 8) {\n b += view.getUint32(index, true);\n index += 4;\n\n // Partial 32-bit word for c\n if (remainder >= 9) {\n c += view.getUint8(index++) << 8;\n }\n if (remainder >= 10) {\n c += view.getUint8(index++) << 16;\n }\n if (remainder === 11) {\n c += view.getUint8(index++) << 24;\n }\n } else {\n // Partial 32-bit word for b\n if (remainder >= 5) {\n b += view.getUint8(index++);\n }\n if (remainder >= 6) {\n b += view.getUint8(index++) << 8;\n }\n if (remainder === 7) {\n b += view.getUint8(index++) << 16;\n }\n }\n } else {\n // Partial 32-bit word for a\n if (remainder >= 1) {\n a += view.getUint8(index++);\n }\n if (remainder >= 2) {\n a += view.getUint8(index++) << 8;\n }\n if (remainder === 3) {\n a += view.getUint8(index++) << 16;\n }\n }\n\n return mix(a, b, c)[2];\n}\n\nfunction mix(a: number, b: number, c: number): [number, number, number] {\n a -= b;\n a -= c;\n a ^= c >>> 13;\n b -= c;\n b -= a;\n b ^= a << 8;\n c -= a;\n c -= b;\n c ^= b >>> 13;\n a -= b;\n a -= c;\n a ^= c >>> 12;\n b -= c;\n b -= a;\n b ^= a << 16;\n c -= a;\n c -= b;\n c ^= b >>> 5;\n a -= b;\n a -= c;\n a ^= c >>> 3;\n b -= c;\n b -= a;\n b ^= a << 10;\n c -= a;\n c -= b;\n c ^= b >>> 15;\n return [a, b, c];\n}\n\n// Utils\n\nenum Endian {\n Little,\n Big,\n}\n\nfunction add32(a: number, b: number): number {\n return add32to64(a, b)[1];\n}\n\nfunction add32to64(a: number, b: number): [number, number] {\n const low = (a & 0xffff) + (b & 0xffff);\n const high = (a >>> 16) + (b >>> 16) + (low >>> 16);\n return [high >>> 16, (high << 16) | (low & 0xffff)];\n}\n\n// Rotate a 32b number left `count` position\nfunction rol32(a: number, count: number): number {\n return (a << count) | (a >>> (32 - count));\n}\n\nfunction bytesToWords32(bytes: Byte[], endian: Endian): number[] {\n const size = (bytes.length + 3) >>> 2;\n const words32 = [];\n\n for (let i = 0; i < size; i++) {\n words32[i] = wordAt(bytes, i * 4, endian);\n }\n\n return words32;\n}\n\nfunction byteAt(bytes: Byte[], index: number): Byte {\n return index >= bytes.length ? 0 : bytes[index];\n}\n\nfunction wordAt(bytes: Byte[], index: number, endian: Endian): number {\n let word = 0;\n if (endian === Endian.Big) {\n for (let i = 0; i < 4; i++) {\n word += byteAt(bytes, index + i) << (24 - 8 * i);\n }\n } else {\n for (let i = 0; i < 4; i++) {\n word += byteAt(bytes, index + i) << (8 * i);\n }\n }\n return word;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n// This module specifier is intentionally a relative path to allow bundling the code directly\n// into the package.\n// @ng_package: ignore-cross-repo-import\nimport {computeMsgId} from '../../../../compiler/src/i18n/digest';\n\nimport {BLOCK_MARKER, ID_SEPARATOR, LEGACY_ID_INDICATOR, MEANING_SEPARATOR} from './constants';\n\n/**\n * Re-export this helper function so that users of `@angular/localize` don't need to actively import\n * from `@angular/compiler`.\n */\nexport {computeMsgId};\n\n/**\n * A string containing a translation source message.\n *\n * I.E. the message that indicates what will be translated from.\n *\n * Uses `{$placeholder-name}` to indicate a placeholder.\n */\nexport type SourceMessage = string;\n\n/**\n * A string containing a translation target message.\n *\n * I.E. the message that indicates what will be translated to.\n *\n * Uses `{$placeholder-name}` to indicate a placeholder.\n *\n * @publicApi\n */\nexport type TargetMessage = string;\n\n/**\n * A string that uniquely identifies a message, to be used for matching translations.\n *\n * @publicApi\n */\nexport type MessageId = string;\n\n/**\n * Declares a copy of the `AbsoluteFsPath` branded type in `@angular/compiler-cli` to avoid an\n * import into `@angular/compiler-cli`. The compiler-cli's declaration files are not necessarily\n * compatible with web environments that use `@angular/localize`, and would inadvertently include\n * `typescript` declaration files in any compilation unit that uses `@angular/localize` (which\n * increases parsing time and memory usage during builds) using a default import that only\n * type-checks when `allowSyntheticDefaultImports` is enabled.\n *\n * @see https://github.com/angular/angular/issues/45179\n */\ntype AbsoluteFsPathLocalizeCopy = string & {_brand: 'AbsoluteFsPath'};\n\n/**\n * The location of the message in the source file.\n *\n * The `line` and `column` values for the `start` and `end` properties are zero-based.\n */\nexport interface SourceLocation {\n start: {line: number; column: number};\n end: {line: number; column: number};\n file: AbsoluteFsPathLocalizeCopy;\n text?: string;\n}\n\n/**\n * Additional information that can be associated with a message.\n */\nexport interface MessageMetadata {\n /**\n * A human readable rendering of the message\n */\n text: string;\n /**\n * Legacy message ids, if provided.\n *\n * In legacy message formats the message id can only be computed directly from the original\n * template source.\n *\n * Since this information is not available in `$localize` calls, the legacy message ids may be\n * attached by the compiler to the `$localize` metablock so it can be used if needed at the point\n * of translation if the translations are encoded using the legacy message id.\n */\n legacyIds?: string[];\n /**\n * The id of the `message` if a custom one was specified explicitly.\n *\n * This id overrides any computed or legacy ids.\n */\n customId?: string;\n /**\n * The meaning of the `message`, used to distinguish identical `messageString`s.\n */\n meaning?: string;\n /**\n * The description of the `message`, used to aid translation.\n */\n description?: string;\n /**\n * The location of the message in the source.\n */\n location?: SourceLocation;\n}\n\n/**\n * Information parsed from a `$localize` tagged string that is used to translate it.\n *\n * For example:\n *\n * ```ts\n * const name = 'Jo Bloggs';\n * $localize`Hello ${name}:title@@ID:!`;\n * ```\n *\n * May be parsed into:\n *\n * ```ts\n * {\n * id: '6998194507597730591',\n * substitutions: { title: 'Jo Bloggs' },\n * messageString: 'Hello {$title}!',\n * placeholderNames: ['title'],\n * associatedMessageIds: { title: 'ID' },\n * }\n * ```\n */\nexport interface ParsedMessage extends MessageMetadata {\n /**\n * The key used to look up the appropriate translation target.\n */\n id: MessageId;\n /**\n * A mapping of placeholder names to substitution values.\n */\n substitutions: Record<string, any>;\n /**\n * An optional mapping of placeholder names to associated MessageIds.\n * This can be used to match ICU placeholders to the message that contains the ICU.\n */\n associatedMessageIds?: Record<string, MessageId>;\n /**\n * An optional mapping of placeholder names to source locations\n */\n substitutionLocations?: Record<string, SourceLocation | undefined>;\n /**\n * The static parts of the message.\n */\n messageParts: string[];\n /**\n * An optional mapping of message parts to source locations\n */\n messagePartLocations?: (SourceLocation | undefined)[];\n /**\n * The names of the placeholders that will be replaced with substitutions.\n */\n placeholderNames: string[];\n}\n\n/**\n * Parse a `$localize` tagged string into a structure that can be used for translation or\n * extraction.\n *\n * See `ParsedMessage` for an example.\n */\nexport function parseMessage(\n messageParts: TemplateStringsArray,\n expressions?: readonly any[],\n location?: SourceLocation,\n messagePartLocations?: (SourceLocation | undefined)[],\n expressionLocations: (SourceLocation | undefined)[] = [],\n): ParsedMessage {\n const substitutions: {[placeholderName: string]: any} = {};\n const substitutionLocations: {[placeholderName: string]: SourceLocation | undefined} = {};\n const associatedMessageIds: {[placeholderName: string]: MessageId} = {};\n const metadata = parseMetadata(messageParts[0], messageParts.raw[0]);\n const cleanedMessageParts: string[] = [metadata.text];\n const placeholderNames: string[] = [];\n let messageString = metadata.text;\n for (let i = 1; i < messageParts.length; i++) {\n const {\n messagePart,\n placeholderName = computePlaceholderName(i),\n associatedMessageId,\n } = parsePlaceholder(messageParts[i], messageParts.raw[i]);\n messageString += `{$${placeholderName}}${messagePart}`;\n if (expressions !== undefined) {\n substitutions[placeholderName] = expressions[i - 1];\n substitutionLocations[placeholderName] = expressionLocations[i - 1];\n }\n placeholderNames.push(placeholderName);\n if (associatedMessageId !== undefined) {\n associatedMessageIds[placeholderName] = associatedMessageId;\n }\n cleanedMessageParts.push(messagePart);\n }\n const messageId = metadata.customId || computeMsgId(messageString, metadata.meaning || '');\n const legacyIds = metadata.legacyIds ? metadata.legacyIds.filter((id) => id !== messageId) : [];\n return {\n id: messageId,\n legacyIds,\n substitutions,\n substitutionLocations,\n text: messageString,\n customId: metadata.customId,\n meaning: metadata.meaning || '',\n description: metadata.description || '',\n messageParts: cleanedMessageParts,\n messagePartLocations,\n placeholderNames,\n associatedMessageIds,\n location,\n };\n}\n\n/**\n * Parse the given message part (`cooked` + `raw`) to extract the message metadata from the text.\n *\n * If the message part has a metadata block this function will extract the `meaning`,\n * `description`, `customId` and `legacyId` (if provided) from the block. These metadata properties\n * are serialized in the string delimited by `|`, `@@` and `␟` respectively.\n *\n * (Note that `␟` is the `LEGACY_ID_INDICATOR` - see `constants.ts`.)\n *\n * For example:\n *\n * ```ts\n * `:meaning|description@@custom-id:`\n * `:meaning|@@custom-id:`\n * `:meaning|description:`\n * `:description@@custom-id:`\n * `:meaning|:`\n * `:description:`\n * `:@@custom-id:`\n * `:meaning|description@@custom-id␟legacy-id-1␟legacy-id-2:`\n * ```\n *\n * @param cooked The cooked version of the message part to parse.\n * @param raw The raw version of the message part to parse.\n * @returns A object containing any metadata that was parsed from the message part.\n */\nexport function parseMetadata(cooked: string, raw: string): MessageMetadata {\n const {text: messageString, block} = splitBlock(cooked, raw);\n if (block === undefined) {\n return {text: messageString};\n } else {\n const [meaningDescAndId, ...legacyIds] = block.split(LEGACY_ID_INDICATOR);\n const [meaningAndDesc, customId] = meaningDescAndId.split(ID_SEPARATOR, 2);\n let [meaning, description]: (string | undefined)[] = meaningAndDesc.split(MEANING_SEPARATOR, 2);\n if (description === undefined) {\n description = meaning;\n meaning = undefined;\n }\n if (description === '') {\n description = undefined;\n }\n return {text: messageString, meaning, description, customId, legacyIds};\n }\n}\n\n/**\n * Parse the given message part (`cooked` + `raw`) to extract any placeholder metadata from the\n * text.\n *\n * If the message part has a metadata block this function will extract the `placeholderName` and\n * `associatedMessageId` (if provided) from the block.\n *\n * These metadata properties are serialized in the string delimited by `@@`.\n *\n * For example:\n *\n * ```ts\n * `:placeholder-name@@associated-id:`\n * ```\n *\n * @param cooked The cooked version of the message part to parse.\n * @param raw The raw version of the message part to parse.\n * @returns A object containing the metadata (`placeholderName` and `associatedMessageId`) of the\n * preceding placeholder, along with the static text that follows.\n */\nexport function parsePlaceholder(\n cooked: string,\n raw: string,\n): {messagePart: string; placeholderName?: string; associatedMessageId?: string} {\n const {text: messagePart, block} = splitBlock(cooked, raw);\n if (block === undefined) {\n return {messagePart};\n } else {\n const [placeholderName, associatedMessageId] = block.split(ID_SEPARATOR);\n return {messagePart, placeholderName, associatedMessageId};\n }\n}\n\n/**\n * Split a message part (`cooked` + `raw`) into an optional delimited \"block\" off the front and the\n * rest of the text of the message part.\n *\n * Blocks appear at the start of message parts. They are delimited by a colon `:` character at the\n * start and end of the block.\n *\n * If the block is in the first message part then it will be metadata about the whole message:\n * meaning, description, id. Otherwise it will be metadata about the immediately preceding\n * substitution: placeholder name.\n *\n * Since blocks are optional, it is possible that the content of a message block actually starts\n * with a block marker. In this case the marker must be escaped `\\:`.\n *\n * @param cooked The cooked version of the message part to parse.\n * @param raw The raw version of the message part to parse.\n * @returns An object containing the `text` of the message part and the text of the `block`, if it\n * exists.\n * @throws an error if the `block` is unterminated\n */\nexport function splitBlock(cooked: string, raw: string): {text: string; block?: string} {\n if (raw.charAt(0) !== BLOCK_MARKER) {\n return {text: cooked};\n } else {\n const endOfBlock = findEndOfBlock(cooked, raw);\n return {\n block: cooked.substring(1, endOfBlock),\n text: cooked.substring(endOfBlock + 1),\n };\n }\n}\n\nfunction computePlaceholderName(index: number) {\n return index === 1 ? 'PH' : `PH_${index - 1}`;\n}\n\n/**\n * Find the end of a \"marked block\" indicated by the first non-escaped colon.\n *\n * @param cooked The cooked string (where escaped chars have been processed)\n * @param raw The raw string (where escape sequences are still in place)\n *\n * @returns the index of the end of block marker\n * @throws an error if the block is unterminated\n */\nexport function findEndOfBlock(cooked: string, raw: string): number {\n for (let cookedIndex = 1, rawIndex = 1; cookedIndex < cooked.length; cookedIndex++, rawIndex++) {\n if (raw[rawIndex] === '\\\\') {\n rawIndex++;\n } else if (cooked[cookedIndex] === BLOCK_MARKER) {\n return cookedIndex;\n }\n }\n throw new Error(`Unterminated $localize metadata block in \"${raw}\".`);\n}\n"],"names":["hi","BigInt","asUintN","lo","computeMsgId","msg","meaning","fingerprint","msgFingerprint","hash32","view","length","c","b","end","a","getUint32","index","res","mix","remainder","getUint8","parseMessage","messageParts","expressions","location","messagePartLocations","expressionLocations","substitutions","i","messagePart","placeholderName","computePlaceholderName","associatedMessageId","parsePlaceholder","raw","messageString","undefined","substitutionLocations","placeholderNames","push"],"mappings":"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"}
|
package/fesm2022/init.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v21.0.0-next.
|
|
3
|
-
* (c) 2010-2025 Google LLC. https://angular.
|
|
2
|
+
* @license Angular v21.0.0-next.10
|
|
3
|
+
* (c) 2010-2025 Google LLC. https://angular.dev/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { $localize } from './
|
|
7
|
+
import { $localize } from './_localize-chunk.mjs';
|
|
8
8
|
|
|
9
|
-
// Attach $localize to the global context, as a side-effect of this module.
|
|
10
9
|
globalThis.$localize = $localize;
|
|
11
10
|
|
|
12
11
|
export { $localize };
|
package/fesm2022/init.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.mjs","sources":[
|
|
1
|
+
{"version":3,"file":"init.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
|
package/fesm2022/localize.mjs
CHANGED
|
@@ -1,201 +1,102 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v21.0.0-next.
|
|
3
|
-
* (c) 2010-2025 Google LLC. https://angular.
|
|
2
|
+
* @license Angular v21.0.0-next.10
|
|
3
|
+
* (c) 2010-2025 Google LLC. https://angular.dev/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { parseMessage, BLOCK_MARKER } from './
|
|
8
|
-
export { $localize as ɵ$localize, computeMsgId as ɵcomputeMsgId, findEndOfBlock as ɵfindEndOfBlock, parseMetadata as ɵparseMetadata, splitBlock as ɵsplitBlock } from './
|
|
7
|
+
import { parseMessage, BLOCK_MARKER } from './_localize-chunk.mjs';
|
|
8
|
+
export { $localize as ɵ$localize, computeMsgId as ɵcomputeMsgId, findEndOfBlock as ɵfindEndOfBlock, parseMetadata as ɵparseMetadata, splitBlock as ɵsplitBlock } from './_localize-chunk.mjs';
|
|
9
9
|
|
|
10
10
|
class MissingTranslationError extends Error {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
parsedMessage;
|
|
12
|
+
type = 'MissingTranslationError';
|
|
13
|
+
constructor(parsedMessage) {
|
|
14
|
+
super(`No translation found for ${describeMessage(parsedMessage)}.`);
|
|
15
|
+
this.parsedMessage = parsedMessage;
|
|
16
|
+
}
|
|
17
17
|
}
|
|
18
18
|
function isMissingTranslationError(e) {
|
|
19
|
-
|
|
19
|
+
return e.type === 'MissingTranslationError';
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Translate the text of the `$localize` tagged-string (i.e. `messageParts` and
|
|
23
|
-
* `substitutions`) using the given `translations`.
|
|
24
|
-
*
|
|
25
|
-
* The tagged-string is parsed to extract its `messageId` which is used to find an appropriate
|
|
26
|
-
* `ParsedTranslation`. If this doesn't match and there are legacy ids then try matching a
|
|
27
|
-
* translation using those.
|
|
28
|
-
*
|
|
29
|
-
* If one is found then it is used to translate the message into a new set of `messageParts` and
|
|
30
|
-
* `substitutions`.
|
|
31
|
-
* The translation may reorder (or remove) substitutions as appropriate.
|
|
32
|
-
*
|
|
33
|
-
* If there is no translation with a matching message id then an error is thrown.
|
|
34
|
-
* If a translation contains a placeholder that is not found in the message being translated then an
|
|
35
|
-
* error is thrown.
|
|
36
|
-
*/
|
|
37
21
|
function translate$1(translations, messageParts, substitutions) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
for (let i = 0; i < message.legacyIds.length && translation === undefined; i++) {
|
|
44
|
-
translation = translations[message.legacyIds[i]];
|
|
45
|
-
}
|
|
22
|
+
const message = parseMessage(messageParts, substitutions);
|
|
23
|
+
let translation = translations[message.id];
|
|
24
|
+
if (message.legacyIds !== undefined) {
|
|
25
|
+
for (let i = 0; i < message.legacyIds.length && translation === undefined; i++) {
|
|
26
|
+
translation = translations[message.legacyIds[i]];
|
|
46
27
|
}
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
}
|
|
29
|
+
if (translation === undefined) {
|
|
30
|
+
throw new MissingTranslationError(message);
|
|
31
|
+
}
|
|
32
|
+
return [translation.messageParts, translation.placeholderNames.map(placeholder => {
|
|
33
|
+
if (message.substitutions.hasOwnProperty(placeholder)) {
|
|
34
|
+
return message.substitutions[placeholder];
|
|
35
|
+
} else {
|
|
36
|
+
throw new Error(`There is a placeholder name mismatch with the translation provided for the message ${describeMessage(message)}.\n` + `The translation contains a placeholder with name ${placeholder}, which does not exist in the message.`);
|
|
49
37
|
}
|
|
50
|
-
|
|
51
|
-
translation.messageParts,
|
|
52
|
-
translation.placeholderNames.map((placeholder) => {
|
|
53
|
-
if (message.substitutions.hasOwnProperty(placeholder)) {
|
|
54
|
-
return message.substitutions[placeholder];
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
throw new Error(`There is a placeholder name mismatch with the translation provided for the message ${describeMessage(message)}.\n` +
|
|
58
|
-
`The translation contains a placeholder with name ${placeholder}, which does not exist in the message.`);
|
|
59
|
-
}
|
|
60
|
-
}),
|
|
61
|
-
];
|
|
38
|
+
})];
|
|
62
39
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Parse the `messageParts` and `placeholderNames` out of a target `message`.
|
|
65
|
-
*
|
|
66
|
-
* Used by `loadTranslations()` to convert target message strings into a structure that is more
|
|
67
|
-
* appropriate for doing translation.
|
|
68
|
-
*
|
|
69
|
-
* @param message the message to be parsed.
|
|
70
|
-
*/
|
|
71
40
|
function parseTranslation(messageString) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
41
|
+
const parts = messageString.split(/{\$([^}]*)}/);
|
|
42
|
+
const messageParts = [parts[0]];
|
|
43
|
+
const placeholderNames = [];
|
|
44
|
+
for (let i = 1; i < parts.length - 1; i += 2) {
|
|
45
|
+
placeholderNames.push(parts[i]);
|
|
46
|
+
messageParts.push(`${parts[i + 1]}`);
|
|
47
|
+
}
|
|
48
|
+
const rawMessageParts = messageParts.map(part => part.charAt(0) === BLOCK_MARKER ? '\\' + part : part);
|
|
49
|
+
return {
|
|
50
|
+
text: messageString,
|
|
51
|
+
messageParts: makeTemplateObject(messageParts, rawMessageParts),
|
|
52
|
+
placeholderNames
|
|
53
|
+
};
|
|
85
54
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Create a `ParsedTranslation` from a set of `messageParts` and `placeholderNames`.
|
|
88
|
-
*
|
|
89
|
-
* @param messageParts The message parts to appear in the ParsedTranslation.
|
|
90
|
-
* @param placeholderNames The names of the placeholders to intersperse between the `messageParts`.
|
|
91
|
-
*/
|
|
92
55
|
function makeParsedTranslation(messageParts, placeholderNames = []) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
56
|
+
let messageString = messageParts[0];
|
|
57
|
+
for (let i = 0; i < placeholderNames.length; i++) {
|
|
58
|
+
messageString += `{$${placeholderNames[i]}}${messageParts[i + 1]}`;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
text: messageString,
|
|
62
|
+
messageParts: makeTemplateObject(messageParts, messageParts),
|
|
63
|
+
placeholderNames
|
|
64
|
+
};
|
|
102
65
|
}
|
|
103
|
-
/**
|
|
104
|
-
* Create the specialized array that is passed to tagged-string tag functions.
|
|
105
|
-
*
|
|
106
|
-
* @param cooked The message parts with their escape codes processed.
|
|
107
|
-
* @param raw The message parts with their escaped codes as-is.
|
|
108
|
-
*/
|
|
109
66
|
function makeTemplateObject(cooked, raw) {
|
|
110
|
-
|
|
111
|
-
|
|
67
|
+
Object.defineProperty(cooked, 'raw', {
|
|
68
|
+
value: raw
|
|
69
|
+
});
|
|
70
|
+
return cooked;
|
|
112
71
|
}
|
|
113
72
|
function describeMessage(message) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
: '';
|
|
118
|
-
return `"${message.id}"${legacy} ("${message.text}"${meaningString})`;
|
|
73
|
+
const meaningString = message.meaning && ` - "${message.meaning}"`;
|
|
74
|
+
const legacy = message.legacyIds && message.legacyIds.length > 0 ? ` [${message.legacyIds.map(l => `"${l}"`).join(', ')}]` : '';
|
|
75
|
+
return `"${message.id}"${legacy} ("${message.text}"${meaningString})`;
|
|
119
76
|
}
|
|
120
77
|
|
|
121
|
-
/**
|
|
122
|
-
* Load translations for use by `$localize`, if doing runtime translation.
|
|
123
|
-
*
|
|
124
|
-
* If the `$localize` tagged strings are not going to be replaced at compiled time, it is possible
|
|
125
|
-
* to load a set of translations that will be applied to the `$localize` tagged strings at runtime,
|
|
126
|
-
* in the browser.
|
|
127
|
-
*
|
|
128
|
-
* Loading a new translation will overwrite a previous translation if it has the same `MessageId`.
|
|
129
|
-
*
|
|
130
|
-
* Note that `$localize` messages are only processed once, when the tagged string is first
|
|
131
|
-
* encountered, and does not provide dynamic language changing without refreshing the browser.
|
|
132
|
-
* Loading new translations later in the application life-cycle will not change the translated text
|
|
133
|
-
* of messages that have already been translated.
|
|
134
|
-
*
|
|
135
|
-
* The message IDs and translations are in the same format as that rendered to "simple JSON"
|
|
136
|
-
* translation files when extracting messages. In particular, placeholders in messages are rendered
|
|
137
|
-
* using the `{$PLACEHOLDER_NAME}` syntax. For example the message from the following template:
|
|
138
|
-
*
|
|
139
|
-
* ```html
|
|
140
|
-
* <div i18n>pre<span>inner-pre<b>bold</b>inner-post</span>post</div>
|
|
141
|
-
* ```
|
|
142
|
-
*
|
|
143
|
-
* would have the following form in the `translations` map:
|
|
144
|
-
*
|
|
145
|
-
* ```ts
|
|
146
|
-
* {
|
|
147
|
-
* "2932901491976224757":
|
|
148
|
-
* "pre{$START_TAG_SPAN}inner-pre{$START_BOLD_TEXT}bold{$CLOSE_BOLD_TEXT}inner-post{$CLOSE_TAG_SPAN}post"
|
|
149
|
-
* }
|
|
150
|
-
* ```
|
|
151
|
-
*
|
|
152
|
-
* @param translations A map from message ID to translated message.
|
|
153
|
-
*
|
|
154
|
-
* These messages are processed and added to a lookup based on their `MessageId`.
|
|
155
|
-
*
|
|
156
|
-
* @see {@link clearTranslations} for removing translations loaded using this function.
|
|
157
|
-
* @see {@link /api/localize/init/$localize $localize} for tagging messages as needing to be translated.
|
|
158
|
-
* @publicApi
|
|
159
|
-
*/
|
|
160
78
|
function loadTranslations(translations) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
79
|
+
if (!$localize.translate) {
|
|
80
|
+
$localize.translate = translate;
|
|
81
|
+
}
|
|
82
|
+
if (!$localize.TRANSLATIONS) {
|
|
83
|
+
$localize.TRANSLATIONS = {};
|
|
84
|
+
}
|
|
85
|
+
Object.keys(translations).forEach(key => {
|
|
86
|
+
$localize.TRANSLATIONS[key] = parseTranslation(translations[key]);
|
|
87
|
+
});
|
|
171
88
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Remove all translations for `$localize`, if doing runtime translation.
|
|
174
|
-
*
|
|
175
|
-
* All translations that had been loading into memory using `loadTranslations()` will be removed.
|
|
176
|
-
*
|
|
177
|
-
* @see {@link loadTranslations} for loading translations at runtime.
|
|
178
|
-
* @see {@link /api/localize/init/$localize $localize} for tagging messages as needing to be translated.
|
|
179
|
-
*
|
|
180
|
-
* @publicApi
|
|
181
|
-
*/
|
|
182
89
|
function clearTranslations() {
|
|
183
|
-
|
|
184
|
-
|
|
90
|
+
$localize.translate = undefined;
|
|
91
|
+
$localize.TRANSLATIONS = {};
|
|
185
92
|
}
|
|
186
|
-
/**
|
|
187
|
-
* Translate the text of the given message, using the loaded translations.
|
|
188
|
-
*
|
|
189
|
-
* This function may reorder (or remove) substitutions as indicated in the matching translation.
|
|
190
|
-
*/
|
|
191
93
|
function translate(messageParts, substitutions) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
94
|
+
try {
|
|
95
|
+
return translate$1($localize.TRANSLATIONS, messageParts, substitutions);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.warn(e.message);
|
|
98
|
+
return [messageParts, substitutions];
|
|
99
|
+
}
|
|
199
100
|
}
|
|
200
101
|
|
|
201
102
|
export { clearTranslations, loadTranslations, MissingTranslationError as ɵMissingTranslationError, isMissingTranslationError as ɵisMissingTranslationError, makeParsedTranslation as ɵmakeParsedTranslation, makeTemplateObject as ɵmakeTemplateObject, parseMessage as ɵparseMessage, parseTranslation as ɵparseTranslation, translate$1 as ɵtranslate };
|