@blamejs/core 0.6.37 → 0.6.59
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/CHANGELOG.md +35 -0
- package/README.md +2 -2
- package/lib/audit.js +1 -0
- package/lib/bundler.js +166 -31
- package/lib/http-client.js +12 -7
- package/lib/http2-teardown.js +34 -0
- package/lib/i18n-messageformat.js +398 -0
- package/lib/i18n.js +17 -0
- package/lib/log-stream-otlp-grpc.js +404 -0
- package/lib/log-stream.js +8 -0
- package/lib/mail.js +18 -3
- package/lib/mtls-ca.js +155 -0
- package/lib/mtls-engine-default.js +40 -0
- package/lib/object-store/sigv4-bucket-ops.js +639 -39
- package/lib/object-store/sigv4.js +10 -3
- package/lib/protobuf-encoder.js +184 -0
- package/lib/queue-sqs.js +314 -0
- package/lib/queue.js +2 -2
- package/lib/safe-buffer.js +15 -2
- package/lib/totp.js +11 -3
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* i18n-messageformat — ICU MessageFormat parser + evaluator.
|
|
4
|
+
*
|
|
5
|
+
* The framework's `b.i18n` translation file format is JSON-shaped with
|
|
6
|
+
* CLDR plural keys at the JSON level (one / few / many / other under a
|
|
7
|
+
* key like `inbox.unread`). That covers the simple plural case but not
|
|
8
|
+
* the nested / inline patterns common in real-world translations:
|
|
9
|
+
* gendered selects, plural forms with embedded variables, ordinals,
|
|
10
|
+
* arguments inside cases.
|
|
11
|
+
*
|
|
12
|
+
* ICU MessageFormat is the standard syntax for those cases:
|
|
13
|
+
*
|
|
14
|
+
* You have {count, plural, =0 {no messages} =1 {# message} other {# messages}}.
|
|
15
|
+
* {gender, select, female {She} male {He} other {They}} liked your post.
|
|
16
|
+
* {n, plural, one {# day} other {# days}} until {event}.
|
|
17
|
+
*
|
|
18
|
+
* This module is a minimal-but-correct subset:
|
|
19
|
+
*
|
|
20
|
+
* - `{argName}` — simple replacement
|
|
21
|
+
* - `{argName, plural, =N {...} cat {...} other {...}}` (with optional
|
|
22
|
+
* `offset:N` and `#` placeholder for the plural arg minus offset)
|
|
23
|
+
* - `{argName, selectordinal, =N {...} ... other {...}}` (CLDR ordinal)
|
|
24
|
+
* - `{argName, select, caseA {...} caseB {...} other {...}}`
|
|
25
|
+
* - Nested arguments inside any case body
|
|
26
|
+
* - `'{'` / `'}'` literal escapes (ICU spec — single-quote pair is the
|
|
27
|
+
* escape mechanism; `''` renders as a literal apostrophe)
|
|
28
|
+
* - CLDR cardinal categories via `Intl.PluralRules` for `plural`
|
|
29
|
+
* - CLDR ordinal categories via `Intl.PluralRules({ type: "ordinal" })`
|
|
30
|
+
*
|
|
31
|
+
* Out of scope (operators wanting these reach for the full
|
|
32
|
+
* `messageformat` package and pre-format strings before storage):
|
|
33
|
+
*
|
|
34
|
+
* - Inline `number` / `date` / `time` formatters (use `formatNumber` /
|
|
35
|
+
* `formatDate` from b.i18n separately and inline the result)
|
|
36
|
+
* - Choice-format (deprecated by ICU in favor of plural)
|
|
37
|
+
* - Custom user-defined argument types
|
|
38
|
+
*
|
|
39
|
+
* Operator API:
|
|
40
|
+
*
|
|
41
|
+
* var msg = b.i18n.messageFormat.format(template, vars, locale?);
|
|
42
|
+
*
|
|
43
|
+
* Used by `b.i18n.t(key, vars, { messageFormat: true })` when the
|
|
44
|
+
* translation entry contains MessageFormat syntax. Existing plural-
|
|
45
|
+
* shaped JSON entries continue to work unchanged.
|
|
46
|
+
*/
|
|
47
|
+
var lazyRequire = require("./lazy-require");
|
|
48
|
+
var { defineClass } = require("./framework-error");
|
|
49
|
+
|
|
50
|
+
var I18nMessageFormatError = defineClass("I18nMessageFormatError",
|
|
51
|
+
{ alwaysPermanent: true });
|
|
52
|
+
|
|
53
|
+
// ---- Tokenizer ----
|
|
54
|
+
//
|
|
55
|
+
// ICU MessageFormat is a small enough grammar that hand-rolling a
|
|
56
|
+
// recursive-descent parser is cleaner than threading a tokenizer +
|
|
57
|
+
// parser layer. We do scan apostrophe-escapes up-front though — the
|
|
58
|
+
// `'{'` and `'}'` patterns flip the next character to literal,
|
|
59
|
+
// `''` renders as a literal apostrophe.
|
|
60
|
+
|
|
61
|
+
function _err(code, message) {
|
|
62
|
+
return new I18nMessageFormatError(code, message, true);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---- Parser ----
|
|
66
|
+
//
|
|
67
|
+
// AST node shapes:
|
|
68
|
+
// { type: "literal", value: string }
|
|
69
|
+
// { type: "argument", name: string } // {name}
|
|
70
|
+
// { type: "plural", name, offset, cases: { key: nodes[] } } // {n, plural, ...}
|
|
71
|
+
// { type: "select", name, cases: { key: nodes[] } }
|
|
72
|
+
// { type: "ordinal", name, offset, cases: { key: nodes[] } } // {n, selectordinal, ...}
|
|
73
|
+
// { type: "hash" } // # inside plural body
|
|
74
|
+
|
|
75
|
+
function parse(template) {
|
|
76
|
+
if (typeof template !== "string") {
|
|
77
|
+
throw _err("BAD_TEMPLATE",
|
|
78
|
+
"messageFormat.parse: template must be a string, got " + typeof template);
|
|
79
|
+
}
|
|
80
|
+
var state = { src: template, pos: 0 };
|
|
81
|
+
var nodes = _parseSequence(state, /* topLevel */ true);
|
|
82
|
+
if (state.pos < state.src.length) {
|
|
83
|
+
throw _err("BAD_TEMPLATE",
|
|
84
|
+
"messageFormat.parse: unexpected '" + state.src[state.pos] +
|
|
85
|
+
"' at position " + state.pos);
|
|
86
|
+
}
|
|
87
|
+
return nodes;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function _parseSequence(state, topLevel) {
|
|
91
|
+
var nodes = [];
|
|
92
|
+
var lit = "";
|
|
93
|
+
while (state.pos < state.src.length) {
|
|
94
|
+
var ch = state.src[state.pos];
|
|
95
|
+
if (ch === "}" && !topLevel) {
|
|
96
|
+
// Caller will consume the closing brace.
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (ch === "{") {
|
|
100
|
+
if (lit.length > 0) { nodes.push({ type: "literal", value: lit }); lit = ""; }
|
|
101
|
+
nodes.push(_parseArgument(state));
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (ch === "#" && !topLevel) {
|
|
105
|
+
if (lit.length > 0) { nodes.push({ type: "literal", value: lit }); lit = ""; }
|
|
106
|
+
nodes.push({ type: "hash" });
|
|
107
|
+
state.pos += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (ch === "'") {
|
|
111
|
+
// ICU-spec apostrophe handling:
|
|
112
|
+
// '' → literal "'"
|
|
113
|
+
// '{' → literal "{"
|
|
114
|
+
// '}' → literal "}"
|
|
115
|
+
// '#' → literal "#" (only inside a plural body)
|
|
116
|
+
// 'X' → literal "'X" (X not a special char) — this is
|
|
117
|
+
// the `quoting` rule. We also handle '{...'
|
|
118
|
+
// sequences that quote an entire run.
|
|
119
|
+
state.pos += 1;
|
|
120
|
+
if (state.pos >= state.src.length) { lit += "'"; break; }
|
|
121
|
+
var next = state.src[state.pos];
|
|
122
|
+
if (next === "'") {
|
|
123
|
+
lit += "'";
|
|
124
|
+
state.pos += 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// If the next char is a special metachar, the quote runs to the
|
|
128
|
+
// next single-quote (or end of string).
|
|
129
|
+
if (next === "{" || next === "}" || next === "#" || next === "|") {
|
|
130
|
+
var endQuote = state.src.indexOf("'", state.pos);
|
|
131
|
+
if (endQuote === -1) {
|
|
132
|
+
lit += state.src.slice(state.pos);
|
|
133
|
+
state.pos = state.src.length;
|
|
134
|
+
} else {
|
|
135
|
+
lit += state.src.slice(state.pos, endQuote);
|
|
136
|
+
state.pos = endQuote + 1;
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// Lone apostrophe — render literally.
|
|
141
|
+
lit += "'";
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
lit += ch;
|
|
145
|
+
state.pos += 1;
|
|
146
|
+
}
|
|
147
|
+
if (lit.length > 0) nodes.push({ type: "literal", value: lit });
|
|
148
|
+
return nodes;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function _parseArgument(state) {
|
|
152
|
+
// Consume opening '{'.
|
|
153
|
+
if (state.src[state.pos] !== "{") {
|
|
154
|
+
throw _err("BAD_TEMPLATE", "expected '{' at " + state.pos);
|
|
155
|
+
}
|
|
156
|
+
state.pos += 1;
|
|
157
|
+
_skipWs(state);
|
|
158
|
+
var name = _parseIdentifier(state);
|
|
159
|
+
if (!name) {
|
|
160
|
+
throw _err("BAD_TEMPLATE",
|
|
161
|
+
"missing argument name at position " + state.pos);
|
|
162
|
+
}
|
|
163
|
+
_skipWs(state);
|
|
164
|
+
var ch = state.src[state.pos];
|
|
165
|
+
if (ch === "}") {
|
|
166
|
+
state.pos += 1;
|
|
167
|
+
return { type: "argument", name: name };
|
|
168
|
+
}
|
|
169
|
+
if (ch !== ",") {
|
|
170
|
+
throw _err("BAD_TEMPLATE",
|
|
171
|
+
"expected ',' or '}' after argument name '" + name +
|
|
172
|
+
"' at position " + state.pos);
|
|
173
|
+
}
|
|
174
|
+
state.pos += 1;
|
|
175
|
+
_skipWs(state);
|
|
176
|
+
var typeName = _parseIdentifier(state);
|
|
177
|
+
if (!typeName) {
|
|
178
|
+
throw _err("BAD_TEMPLATE",
|
|
179
|
+
"missing argument type after ',' for '" + name + "'");
|
|
180
|
+
}
|
|
181
|
+
_skipWs(state);
|
|
182
|
+
if (typeName === "plural" || typeName === "selectordinal") {
|
|
183
|
+
return _parsePluralLike(state, name, typeName === "selectordinal" ? "ordinal" : "plural");
|
|
184
|
+
}
|
|
185
|
+
if (typeName === "select") {
|
|
186
|
+
return _parseSelect(state, name);
|
|
187
|
+
}
|
|
188
|
+
throw _err("BAD_TEMPLATE",
|
|
189
|
+
"unsupported argument type '" + typeName + "' (supported: plural, " +
|
|
190
|
+
"selectordinal, select)");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function _parsePluralLike(state, name, kind) {
|
|
194
|
+
// Optional ',' before the cases — the spec allows both `plural,
|
|
195
|
+
// offset:0 ...` and `plural, =0 ...` immediately after the type.
|
|
196
|
+
if (state.src[state.pos] === ",") { state.pos += 1; _skipWs(state); }
|
|
197
|
+
var offset = 0;
|
|
198
|
+
if (state.src.slice(state.pos, state.pos + 7) === "offset:") {
|
|
199
|
+
state.pos += 7;
|
|
200
|
+
offset = _parseInteger(state);
|
|
201
|
+
_skipWs(state);
|
|
202
|
+
}
|
|
203
|
+
var cases = {};
|
|
204
|
+
while (state.pos < state.src.length && state.src[state.pos] !== "}") {
|
|
205
|
+
var caseKey = _parseCaseKey(state);
|
|
206
|
+
_skipWs(state);
|
|
207
|
+
if (state.src[state.pos] !== "{") {
|
|
208
|
+
throw _err("BAD_TEMPLATE",
|
|
209
|
+
"expected '{' after plural case '" + caseKey +
|
|
210
|
+
"' at position " + state.pos);
|
|
211
|
+
}
|
|
212
|
+
state.pos += 1;
|
|
213
|
+
var body = _parseSequence(state, false);
|
|
214
|
+
if (state.src[state.pos] !== "}") {
|
|
215
|
+
throw _err("BAD_TEMPLATE",
|
|
216
|
+
"unclosed plural case body at position " + state.pos);
|
|
217
|
+
}
|
|
218
|
+
state.pos += 1;
|
|
219
|
+
cases[caseKey] = body;
|
|
220
|
+
_skipWs(state);
|
|
221
|
+
}
|
|
222
|
+
if (state.src[state.pos] !== "}") {
|
|
223
|
+
throw _err("BAD_TEMPLATE",
|
|
224
|
+
"unclosed plural argument for '" + name + "'");
|
|
225
|
+
}
|
|
226
|
+
if (!cases.other) {
|
|
227
|
+
throw _err("BAD_TEMPLATE",
|
|
228
|
+
"plural argument '" + name + "' missing required 'other' case");
|
|
229
|
+
}
|
|
230
|
+
state.pos += 1;
|
|
231
|
+
return { type: kind, name: name, offset: offset, cases: cases };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function _parseSelect(state, name) {
|
|
235
|
+
if (state.src[state.pos] === ",") { state.pos += 1; _skipWs(state); }
|
|
236
|
+
var cases = {};
|
|
237
|
+
while (state.pos < state.src.length && state.src[state.pos] !== "}") {
|
|
238
|
+
var caseKey = _parseIdentifier(state);
|
|
239
|
+
if (!caseKey) {
|
|
240
|
+
throw _err("BAD_TEMPLATE",
|
|
241
|
+
"expected select case identifier at position " + state.pos);
|
|
242
|
+
}
|
|
243
|
+
_skipWs(state);
|
|
244
|
+
if (state.src[state.pos] !== "{") {
|
|
245
|
+
throw _err("BAD_TEMPLATE",
|
|
246
|
+
"expected '{' after select case '" + caseKey +
|
|
247
|
+
"' at position " + state.pos);
|
|
248
|
+
}
|
|
249
|
+
state.pos += 1;
|
|
250
|
+
var body = _parseSequence(state, false);
|
|
251
|
+
if (state.src[state.pos] !== "}") {
|
|
252
|
+
throw _err("BAD_TEMPLATE",
|
|
253
|
+
"unclosed select case body at position " + state.pos);
|
|
254
|
+
}
|
|
255
|
+
state.pos += 1;
|
|
256
|
+
cases[caseKey] = body;
|
|
257
|
+
_skipWs(state);
|
|
258
|
+
}
|
|
259
|
+
if (state.src[state.pos] !== "}") {
|
|
260
|
+
throw _err("BAD_TEMPLATE",
|
|
261
|
+
"unclosed select argument for '" + name + "'");
|
|
262
|
+
}
|
|
263
|
+
if (!cases.other) {
|
|
264
|
+
throw _err("BAD_TEMPLATE",
|
|
265
|
+
"select argument '" + name + "' missing required 'other' case");
|
|
266
|
+
}
|
|
267
|
+
state.pos += 1;
|
|
268
|
+
return { type: "select", name: name, cases: cases };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function _parseIdentifier(state) {
|
|
272
|
+
var start = state.pos;
|
|
273
|
+
while (state.pos < state.src.length) {
|
|
274
|
+
var ch = state.src[state.pos];
|
|
275
|
+
// Identifiers per ICU: anything not whitespace or special char.
|
|
276
|
+
if (ch === "{" || ch === "}" || ch === "," || ch === "#" || ch === "'") break;
|
|
277
|
+
if (/\s/.test(ch)) break;
|
|
278
|
+
state.pos += 1;
|
|
279
|
+
}
|
|
280
|
+
return state.src.slice(start, state.pos);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function _parseCaseKey(state) {
|
|
284
|
+
// CLDR plural keys: zero / one / two / few / many / other, OR
|
|
285
|
+
// explicit `=N` literal-match keys.
|
|
286
|
+
if (state.src[state.pos] === "=") {
|
|
287
|
+
state.pos += 1;
|
|
288
|
+
var n = _parseInteger(state);
|
|
289
|
+
return "=" + n;
|
|
290
|
+
}
|
|
291
|
+
return _parseIdentifier(state);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function _parseInteger(state) {
|
|
295
|
+
var start = state.pos;
|
|
296
|
+
if (state.src[state.pos] === "-") state.pos += 1;
|
|
297
|
+
while (state.pos < state.src.length && /[0-9]/.test(state.src[state.pos])) {
|
|
298
|
+
state.pos += 1;
|
|
299
|
+
}
|
|
300
|
+
if (state.pos === start) {
|
|
301
|
+
throw _err("BAD_TEMPLATE", "expected integer at position " + state.pos);
|
|
302
|
+
}
|
|
303
|
+
return parseInt(state.src.slice(start, state.pos), 10);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function _skipWs(state) {
|
|
307
|
+
while (state.pos < state.src.length && /\s/.test(state.src[state.pos])) {
|
|
308
|
+
state.pos += 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ---- Evaluator ----
|
|
313
|
+
|
|
314
|
+
var _pluralRulesCache = new Map();
|
|
315
|
+
function _pluralRules(locale, type) {
|
|
316
|
+
var key = locale + "\x1f" + type;
|
|
317
|
+
var pr = _pluralRulesCache.get(key);
|
|
318
|
+
if (!pr) {
|
|
319
|
+
pr = new Intl.PluralRules(locale, { type: type });
|
|
320
|
+
_pluralRulesCache.set(key, pr);
|
|
321
|
+
}
|
|
322
|
+
return pr;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function format(template, vars, locale) {
|
|
326
|
+
var nodes = parse(template);
|
|
327
|
+
return _renderSequence(nodes, vars || {}, locale || "en", null);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function _renderSequence(nodes, vars, locale, hashContext) {
|
|
331
|
+
var out = "";
|
|
332
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
333
|
+
out += _renderNode(nodes[i], vars, locale, hashContext);
|
|
334
|
+
}
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function _renderNode(node, vars, locale, hashContext) {
|
|
339
|
+
if (node.type === "literal") return node.value;
|
|
340
|
+
if (node.type === "hash") {
|
|
341
|
+
return hashContext != null ? String(hashContext) : "#";
|
|
342
|
+
}
|
|
343
|
+
if (node.type === "argument") {
|
|
344
|
+
var v = vars[node.name];
|
|
345
|
+
return v === undefined ? "" : (v === null ? "" : String(v));
|
|
346
|
+
}
|
|
347
|
+
if (node.type === "plural" || node.type === "ordinal") {
|
|
348
|
+
var raw = vars[node.name];
|
|
349
|
+
var n = Number(raw);
|
|
350
|
+
if (!Number.isFinite(n)) {
|
|
351
|
+
throw _err("BAD_VAR",
|
|
352
|
+
"plural arg '" + node.name + "' must be a number, got " +
|
|
353
|
+
typeof raw + " " + JSON.stringify(raw));
|
|
354
|
+
}
|
|
355
|
+
var adjusted = n - (node.offset || 0);
|
|
356
|
+
var exact = "=" + n;
|
|
357
|
+
var caseBody = node.cases[exact];
|
|
358
|
+
if (!caseBody) {
|
|
359
|
+
var pr = _pluralRules(locale, node.type === "ordinal" ? "ordinal" : "cardinal");
|
|
360
|
+
var category = pr.select(adjusted);
|
|
361
|
+
caseBody = node.cases[category] || node.cases.other;
|
|
362
|
+
}
|
|
363
|
+
return _renderSequence(caseBody, vars, locale, adjusted);
|
|
364
|
+
}
|
|
365
|
+
if (node.type === "select") {
|
|
366
|
+
var sv = vars[node.name];
|
|
367
|
+
var key = (sv === undefined || sv === null) ? "other" : String(sv);
|
|
368
|
+
var body = node.cases[key] || node.cases.other;
|
|
369
|
+
return _renderSequence(body, vars, locale, hashContext);
|
|
370
|
+
}
|
|
371
|
+
return "";
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---- Detection helper for b.i18n.t() integration ----
|
|
375
|
+
//
|
|
376
|
+
// A string contains MessageFormat syntax if it has a `{...,...}` shape.
|
|
377
|
+
// Plain `{var}` interpolation is forwarded to the existing simple path
|
|
378
|
+
// (no plural / select / nested cases), matching backward compat. Used
|
|
379
|
+
// by `b.i18n.t(key, vars, { messageFormat: true })` to pick the
|
|
380
|
+
// renderer based on the entry.
|
|
381
|
+
function looksLikeMessageFormat(template) {
|
|
382
|
+
if (typeof template !== "string") return false;
|
|
383
|
+
// Cheap structural check — full-syntax detection comes from parse()
|
|
384
|
+
// throwing if it isn't valid MessageFormat.
|
|
385
|
+
return /\{[^}]+,\s*(plural|select|selectordinal)\b/.test(template);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
module.exports = {
|
|
389
|
+
parse: parse,
|
|
390
|
+
format: format,
|
|
391
|
+
looksLikeMessageFormat: looksLikeMessageFormat,
|
|
392
|
+
I18nMessageFormatError: I18nMessageFormatError,
|
|
393
|
+
// Test-only — clear plural-rules cache between locale rotations.
|
|
394
|
+
_resetCacheForTest: function () { _pluralRulesCache.clear(); },
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// Reserved for future expansion — keeps the require() side-effect-free.
|
|
398
|
+
void lazyRequire;
|
package/lib/i18n.js
CHANGED
|
@@ -617,6 +617,16 @@ function create(opts) {
|
|
|
617
617
|
return key;
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
+
// ICU MessageFormat path — when the operator opts in via
|
|
621
|
+
// `messageFormat: true` OR the entry contains a `{name, plural,
|
|
622
|
+
// ...}` / `{name, select, ...}` / `{name, selectordinal, ...}`
|
|
623
|
+
// shape, evaluate via the parser. Otherwise fall back to the
|
|
624
|
+
// simple `{var}` interpolator (existing behaviour, unchanged).
|
|
625
|
+
var useMf = callerOpts.messageFormat === true ||
|
|
626
|
+
messageFormat.looksLikeMessageFormat(raw);
|
|
627
|
+
if (useMf) {
|
|
628
|
+
return messageFormat.format(raw, vars, found.foundIn);
|
|
629
|
+
}
|
|
620
630
|
return _interpolate(raw, vars, interpolation);
|
|
621
631
|
}
|
|
622
632
|
|
|
@@ -862,8 +872,15 @@ function create(opts) {
|
|
|
862
872
|
};
|
|
863
873
|
}
|
|
864
874
|
|
|
875
|
+
// ICU MessageFormat companion — top-level namespace so operators can
|
|
876
|
+
// pre-format strings outside the i18n instance (build pipeline, audit
|
|
877
|
+
// formatters, etc.). The instance returned by `create()` plumbs it
|
|
878
|
+
// through `t(key, vars, { messageFormat: true })`.
|
|
879
|
+
var messageFormat = require("./i18n-messageformat");
|
|
880
|
+
|
|
865
881
|
module.exports = {
|
|
866
882
|
create: create,
|
|
883
|
+
messageFormat: messageFormat,
|
|
867
884
|
I18nError: I18nError,
|
|
868
885
|
DEFAULTS: DEFAULTS,
|
|
869
886
|
RTL_LANGUAGES: RTL_LANGUAGES,
|