@xndrjs/i18n 0.6.1 → 0.7.0
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/README.md +154 -141
- package/dist/codegen/index.js.map +1 -1
- package/dist/index.d.ts +268 -69
- package/dist/index.js +572 -65
- package/dist/index.js.map +1 -1
- package/dist/validation/index.d.ts +23 -7
- package/dist/validation/index.js +196 -89
- package/dist/validation/index.js.map +1 -1
- package/package.json +1 -1
- package/src/IcuTranslationProviderMulti.test.ts +80 -234
- package/src/IcuTranslationProviderMulti.ts +101 -112
- package/src/IcuTranslationProviderSingle.test.ts +30 -309
- package/src/IcuTranslationProviderSingle.ts +42 -75
- package/src/builder-load-registry.ts +18 -0
- package/src/builder-loaders.ts +18 -0
- package/src/builder-multi.ts +481 -0
- package/src/builder-types.test.ts +80 -0
- package/src/builder-types.ts +24 -0
- package/src/builder.test.ts +421 -0
- package/src/builder.ts +112 -0
- package/src/codegen/delivery-artifacts.test.ts +2 -1
- package/src/codegen/delivery-artifacts.ts +2 -1
- package/src/codegen/emit/dictionary-file.test.ts +0 -7
- package/src/codegen/emit/dictionary-schema-file.ts +55 -42
- package/src/codegen/emit/instance-file.test.ts +88 -0
- package/src/codegen/emit/instance-file.ts +164 -17
- package/src/codegen/emit/namespace-loaders-file.test.ts +4 -18
- package/src/codegen/emit/namespace-loaders-file.ts +4 -54
- package/src/codegen/emit/types-file.test.ts +0 -2
- package/src/codegen/generate-i18n-types.test.ts +8 -303
- package/src/codegen/generate-i18n-types.ts +16 -1
- package/src/codegen/project-locales-set-namespace.test.ts +25 -32
- package/src/engine.ts +71 -0
- package/src/index.ts +45 -7
- package/src/patch-key.test.ts +186 -0
- package/src/patch-key.ts +140 -0
- package/src/project-locales.test.ts +16 -6
- package/src/project-locales.ts +17 -6
- package/src/scope-multi.ts +124 -0
- package/src/scope-single.ts +85 -0
- package/src/scope-types.ts +27 -0
- package/src/scope.test.ts +481 -0
- package/src/single-builder.ts +153 -0
- package/src/types.ts +16 -0
- package/src/validation/create-normalized-schema.ts +1 -1
- package/src/validation/errors.ts +2 -0
- package/src/validation/index.ts +135 -27
- package/src/validation/normalize.ts +176 -46
- package/src/validation/types.ts +4 -0
- package/src/validation/validate-normalized.ts +43 -1
- package/src/validation/validation.test.ts +200 -7
package/dist/index.js
CHANGED
|
@@ -1,4 +1,206 @@
|
|
|
1
1
|
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
|
+
import { parse } from '@formatjs/icu-messageformat-parser';
|
|
3
|
+
|
|
4
|
+
// src/builder-loaders.ts
|
|
5
|
+
async function invokeNamespaceLoader(loader, partition) {
|
|
6
|
+
if (partition === void 0) {
|
|
7
|
+
return await loader();
|
|
8
|
+
}
|
|
9
|
+
return await loader(partition);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/builder-multi.ts
|
|
13
|
+
var I18nBuilderMultiInitialImpl = class {
|
|
14
|
+
constructor(engine, options) {
|
|
15
|
+
this.engine = engine;
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
engine;
|
|
19
|
+
options;
|
|
20
|
+
withNamespaces(namespaces) {
|
|
21
|
+
return new I18nBuilderMultiReadyImpl(this.engine, this.options, { namespaces });
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var I18nBuilderMultiReadyImpl = class _I18nBuilderMultiReadyImpl {
|
|
25
|
+
constructor(engine, options, state) {
|
|
26
|
+
this.engine = engine;
|
|
27
|
+
this.options = options;
|
|
28
|
+
this.state = state;
|
|
29
|
+
}
|
|
30
|
+
engine;
|
|
31
|
+
options;
|
|
32
|
+
state;
|
|
33
|
+
clone() {
|
|
34
|
+
return {
|
|
35
|
+
namespaces: this.state.namespaces,
|
|
36
|
+
...this.state.locale !== void 0 ? { locale: this.state.locale } : {},
|
|
37
|
+
...this.state.deliveryArea !== void 0 ? { deliveryArea: this.state.deliveryArea } : {}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
withNamespaces(namespaces) {
|
|
41
|
+
return new _I18nBuilderMultiReadyImpl(this.engine, this.options, {
|
|
42
|
+
...this.clone(),
|
|
43
|
+
namespaces
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
withLocale(locale) {
|
|
47
|
+
const { deliveryArea: _deliveryArea, ...rest } = this.clone();
|
|
48
|
+
return new I18nBuilderMultiForLocaleImpl(this.engine, this.options, { ...rest, locale });
|
|
49
|
+
}
|
|
50
|
+
withDeliveryArea(area) {
|
|
51
|
+
const { locale: _locale, ...rest } = this.clone();
|
|
52
|
+
return new I18nBuilderMultiPartitionedImpl(this.engine, this.options, { ...rest, deliveryArea: area });
|
|
53
|
+
}
|
|
54
|
+
async load() {
|
|
55
|
+
await applyMultiBuilderLoad(this.engine, this.options, this.state);
|
|
56
|
+
return this.engine.toScope({ namespaces: this.state.namespaces });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var I18nBuilderMultiPartitionedImpl = class _I18nBuilderMultiPartitionedImpl {
|
|
60
|
+
constructor(engine, options, state) {
|
|
61
|
+
this.engine = engine;
|
|
62
|
+
this.options = options;
|
|
63
|
+
this.state = state;
|
|
64
|
+
}
|
|
65
|
+
engine;
|
|
66
|
+
options;
|
|
67
|
+
state;
|
|
68
|
+
withNamespaces(namespaces) {
|
|
69
|
+
return new _I18nBuilderMultiPartitionedImpl(this.engine, this.options, {
|
|
70
|
+
...this.state,
|
|
71
|
+
namespaces
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async load() {
|
|
75
|
+
await applyMultiBuilderLoad(this.engine, this.options, this.state);
|
|
76
|
+
return this.engine.toScope({ namespaces: this.state.namespaces });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var I18nBuilderMultiForLocaleImpl = class {
|
|
80
|
+
constructor(engine, options, state) {
|
|
81
|
+
this.engine = engine;
|
|
82
|
+
this.options = options;
|
|
83
|
+
this.state = state;
|
|
84
|
+
}
|
|
85
|
+
engine;
|
|
86
|
+
options;
|
|
87
|
+
state;
|
|
88
|
+
async load() {
|
|
89
|
+
await applyMultiBuilderLoad(this.engine, this.options, this.state);
|
|
90
|
+
return this.engine.toScope({ namespaces: this.state.namespaces }).forLocale(this.state.locale);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
async function applyMultiBuilderLoad(engine, options, state) {
|
|
94
|
+
if (state.namespaces.length === 0) {
|
|
95
|
+
throw new Error("[i18n] withNamespaces(...) is required before load().");
|
|
96
|
+
}
|
|
97
|
+
const partition = state.locale ?? state.deliveryArea;
|
|
98
|
+
await Promise.all(
|
|
99
|
+
state.namespaces.map(async (namespace) => {
|
|
100
|
+
const loader = options?.namespaceLoaders?.[namespace];
|
|
101
|
+
if (loader === void 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (engine.hasBuilderResourceLoaded(namespace, partition)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const data = await invokeNamespaceLoader(loader, partition);
|
|
108
|
+
engine.applyLoadMergeNamespace(namespace, data);
|
|
109
|
+
engine.markBuilderResourceLoaded(namespace, partition);
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/single-builder.ts
|
|
115
|
+
var I18nBuilderSingleImpl = class _I18nBuilderSingleImpl {
|
|
116
|
+
constructor(engine, options, state) {
|
|
117
|
+
this.engine = engine;
|
|
118
|
+
this.options = options;
|
|
119
|
+
if (state !== void 0) {
|
|
120
|
+
this.state = {
|
|
121
|
+
...state.locale !== void 0 ? { locale: state.locale } : {},
|
|
122
|
+
...state.deliveryArea !== void 0 ? { deliveryArea: state.deliveryArea } : {}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
engine;
|
|
127
|
+
options;
|
|
128
|
+
state = {};
|
|
129
|
+
clone() {
|
|
130
|
+
return {
|
|
131
|
+
...this.state.locale !== void 0 ? { locale: this.state.locale } : {},
|
|
132
|
+
...this.state.deliveryArea !== void 0 ? { deliveryArea: this.state.deliveryArea } : {}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
withLocale(locale) {
|
|
136
|
+
const { deliveryArea: _deliveryArea, ...rest } = this.clone();
|
|
137
|
+
return new I18nBuilderSingleForLocaleImpl(
|
|
138
|
+
this.engine,
|
|
139
|
+
this.options,
|
|
140
|
+
{ ...rest, locale }
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
withDeliveryArea(_area) {
|
|
144
|
+
const { locale: _locale, ...rest } = this.clone();
|
|
145
|
+
return new _I18nBuilderSingleImpl(this.engine, this.options, {
|
|
146
|
+
...rest,
|
|
147
|
+
deliveryArea: _area
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async load() {
|
|
151
|
+
await applySingleBuilderLoad(this.engine, this.options, this.state);
|
|
152
|
+
return this.engine.toScope();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var I18nBuilderSingleForLocaleImpl = class {
|
|
156
|
+
constructor(engine, options, state) {
|
|
157
|
+
this.engine = engine;
|
|
158
|
+
this.options = options;
|
|
159
|
+
this.state = state;
|
|
160
|
+
}
|
|
161
|
+
engine;
|
|
162
|
+
options;
|
|
163
|
+
state;
|
|
164
|
+
async load() {
|
|
165
|
+
await applySingleBuilderLoad(this.engine, this.options, this.state);
|
|
166
|
+
return this.engine.toScope({ locale: this.state.locale });
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
async function applySingleBuilderLoad(engine, options, state) {
|
|
170
|
+
const partition = state.locale ?? state.deliveryArea;
|
|
171
|
+
const loader = options?.dictionaryLoader;
|
|
172
|
+
if (loader !== void 0) {
|
|
173
|
+
if (engine.hasBuilderResourceLoaded(partition)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const data = await invokeNamespaceLoader(loader, partition);
|
|
177
|
+
engine.applyLoadMergeSingle(data);
|
|
178
|
+
engine.markBuilderResourceLoaded(partition);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/builder.ts
|
|
183
|
+
function isMultiEngine(engine) {
|
|
184
|
+
return engine.__i18nEngineMode === "multi";
|
|
185
|
+
}
|
|
186
|
+
function createI18nSingleBuilder(engine, options) {
|
|
187
|
+
return new I18nBuilderSingleImpl(engine, options);
|
|
188
|
+
}
|
|
189
|
+
function createI18nMultiBuilder(engine, options) {
|
|
190
|
+
return new I18nBuilderMultiInitialImpl(engine, options);
|
|
191
|
+
}
|
|
192
|
+
function createI18nBuilder(engine, options) {
|
|
193
|
+
if (isMultiEngine(engine)) {
|
|
194
|
+
return createI18nMultiBuilder(
|
|
195
|
+
engine,
|
|
196
|
+
options
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return createI18nSingleBuilder(
|
|
200
|
+
engine,
|
|
201
|
+
options
|
|
202
|
+
);
|
|
203
|
+
}
|
|
2
204
|
|
|
3
205
|
// src/deep-freeze.ts
|
|
4
206
|
function deepFreeze(value) {
|
|
@@ -18,6 +220,21 @@ function cloneAndFreeze(value) {
|
|
|
18
220
|
return deepFreeze(structuredClone(value));
|
|
19
221
|
}
|
|
20
222
|
|
|
223
|
+
// src/builder-load-registry.ts
|
|
224
|
+
function formatBuilderResourceKey(namespace, partition) {
|
|
225
|
+
return `${namespace}\0${partition ?? ""}`;
|
|
226
|
+
}
|
|
227
|
+
var SINGLE_BUILDER_RESOURCE = "__dictionary__";
|
|
228
|
+
var BuilderLoadRegistry = class {
|
|
229
|
+
loaded = /* @__PURE__ */ new Set();
|
|
230
|
+
has(namespace, partition) {
|
|
231
|
+
return this.loaded.has(formatBuilderResourceKey(namespace, partition));
|
|
232
|
+
}
|
|
233
|
+
mark(namespace, partition) {
|
|
234
|
+
this.loaded.add(formatBuilderResourceKey(namespace, partition));
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
21
238
|
// src/resolve-locale.ts
|
|
22
239
|
function validateLocaleFallback(map) {
|
|
23
240
|
for (const startLocale of Object.keys(map)) {
|
|
@@ -143,6 +360,239 @@ function resolveAndFormat(options) {
|
|
|
143
360
|
}
|
|
144
361
|
}
|
|
145
362
|
|
|
363
|
+
// src/icu/extract-variables.ts
|
|
364
|
+
function isPluralOrOrdinalNode(node) {
|
|
365
|
+
return node.type === 6 && "pluralType" in node && Boolean(node.pluralType);
|
|
366
|
+
}
|
|
367
|
+
function hasVariableName(node) {
|
|
368
|
+
return "value" in node && typeof node.value === "string";
|
|
369
|
+
}
|
|
370
|
+
function addVariableMeta(variables, name, type, role) {
|
|
371
|
+
const existing = variables[name];
|
|
372
|
+
if (!existing) {
|
|
373
|
+
variables[name] = { type, roles: /* @__PURE__ */ new Set([role]) };
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
existing.roles.add(role);
|
|
377
|
+
existing.type = mergeVariableTypes(existing.type, type);
|
|
378
|
+
}
|
|
379
|
+
function mergeVariableTypes(left, right) {
|
|
380
|
+
if (left === right) {
|
|
381
|
+
return left;
|
|
382
|
+
}
|
|
383
|
+
if (left === "string" && right === "number" || left === "number" && right === "string") {
|
|
384
|
+
return "number";
|
|
385
|
+
}
|
|
386
|
+
return right;
|
|
387
|
+
}
|
|
388
|
+
function extractVariableMeta(nodes) {
|
|
389
|
+
const variables = {};
|
|
390
|
+
const walk = (walkNodes) => {
|
|
391
|
+
for (const node of walkNodes) {
|
|
392
|
+
if (node.type === 1 && hasVariableName(node)) {
|
|
393
|
+
addVariableMeta(variables, node.value, "string", "simple");
|
|
394
|
+
} else if (node.type === 2 && hasVariableName(node)) {
|
|
395
|
+
addVariableMeta(variables, node.value, "number", "number");
|
|
396
|
+
} else if (isPluralOrOrdinalNode(node) && hasVariableName(node)) {
|
|
397
|
+
const role = node.pluralType === "ordinal" ? "selectordinal" : "plural";
|
|
398
|
+
addVariableMeta(variables, node.value, "number", role);
|
|
399
|
+
} else if (node.type === 3 && hasVariableName(node)) {
|
|
400
|
+
addVariableMeta(variables, node.value, "date", "date");
|
|
401
|
+
} else if (node.type === 4 && hasVariableName(node)) {
|
|
402
|
+
addVariableMeta(variables, node.value, "date", "time");
|
|
403
|
+
} else if (node.type === 5 && hasVariableName(node)) {
|
|
404
|
+
addVariableMeta(variables, node.value, "string", "select");
|
|
405
|
+
}
|
|
406
|
+
if ("options" in node && node.options) {
|
|
407
|
+
for (const option of Object.values(node.options)) {
|
|
408
|
+
walk(option.value);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if ("children" in node && Array.isArray(node.children)) {
|
|
412
|
+
walk(node.children);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
walk(nodes);
|
|
417
|
+
return variables;
|
|
418
|
+
}
|
|
419
|
+
function variableMetaToSpec(meta) {
|
|
420
|
+
return Object.fromEntries(Object.entries(meta).map(([name, entry]) => [name, entry.type]));
|
|
421
|
+
}
|
|
422
|
+
function inferMergedVariableType(roles) {
|
|
423
|
+
const hasPlural = roles.has("plural");
|
|
424
|
+
const hasSelectordinal = roles.has("selectordinal");
|
|
425
|
+
const hasSelect = roles.has("select");
|
|
426
|
+
const hasSimple = roles.has("simple");
|
|
427
|
+
const hasNumber = roles.has("number");
|
|
428
|
+
const hasDate = roles.has("date");
|
|
429
|
+
const hasTime = roles.has("time");
|
|
430
|
+
if (hasPlural && hasSelectordinal) {
|
|
431
|
+
return "CONFLICT";
|
|
432
|
+
}
|
|
433
|
+
if (hasSelect && (hasPlural || hasSelectordinal || hasNumber)) {
|
|
434
|
+
return "CONFLICT";
|
|
435
|
+
}
|
|
436
|
+
if (hasPlural && hasSelect) {
|
|
437
|
+
return "CONFLICT";
|
|
438
|
+
}
|
|
439
|
+
if (hasSelectordinal && hasSelect) {
|
|
440
|
+
return "CONFLICT";
|
|
441
|
+
}
|
|
442
|
+
if (hasDate || hasTime) {
|
|
443
|
+
if (hasPlural || hasSelect || hasSelectordinal || hasSimple || hasNumber) {
|
|
444
|
+
return "CONFLICT";
|
|
445
|
+
}
|
|
446
|
+
return "date";
|
|
447
|
+
}
|
|
448
|
+
if (hasPlural || hasSelectordinal || hasNumber) {
|
|
449
|
+
return "number";
|
|
450
|
+
}
|
|
451
|
+
if (hasSelect || hasSimple) {
|
|
452
|
+
return "string";
|
|
453
|
+
}
|
|
454
|
+
return "string";
|
|
455
|
+
}
|
|
456
|
+
function mergeVariableMetaAcrossLocales(localeMetas) {
|
|
457
|
+
const keySignatures = localeMetas.map((meta) => Object.keys(meta).sort().join(","));
|
|
458
|
+
if (new Set(keySignatures).size > 1) {
|
|
459
|
+
return {
|
|
460
|
+
ok: false,
|
|
461
|
+
message: `Inconsistent ICU variable names across locales (keys: ${keySignatures.join(" vs ")})`
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const merged = {};
|
|
465
|
+
const variableNames = new Set(localeMetas.flatMap((meta) => Object.keys(meta)));
|
|
466
|
+
for (const variableName of variableNames) {
|
|
467
|
+
const combinedRoles = /* @__PURE__ */ new Set();
|
|
468
|
+
for (const meta of localeMetas) {
|
|
469
|
+
for (const role of meta[variableName]?.roles ?? []) {
|
|
470
|
+
combinedRoles.add(role);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const mergedType = inferMergedVariableType(combinedRoles);
|
|
474
|
+
if (mergedType === "CONFLICT") {
|
|
475
|
+
const rolesByLocale = localeMetas.map((meta) => {
|
|
476
|
+
const entry = meta[variableName];
|
|
477
|
+
if (!entry) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
return [...entry.roles].sort().join("|");
|
|
481
|
+
}).filter((value) => value !== null);
|
|
482
|
+
return {
|
|
483
|
+
ok: false,
|
|
484
|
+
message: `Incompatible ICU variable "${variableName}" across locales (roles: ${rolesByLocale.join(" vs ")})`
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
merged[variableName] = mergedType;
|
|
488
|
+
}
|
|
489
|
+
return { ok: true, merged };
|
|
490
|
+
}
|
|
491
|
+
function parseTemplate(template) {
|
|
492
|
+
try {
|
|
493
|
+
const ast = parse(template);
|
|
494
|
+
const meta = extractVariableMeta(ast);
|
|
495
|
+
return { ok: true, args: variableMetaToSpec(meta), meta };
|
|
496
|
+
} catch (error) {
|
|
497
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
498
|
+
return { ok: false, message };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/patch-key.ts
|
|
503
|
+
function preloadKeySingle(key, locale) {
|
|
504
|
+
return `${key}:${locale}`;
|
|
505
|
+
}
|
|
506
|
+
function preloadKeyMulti(namespace, key, locale) {
|
|
507
|
+
return `${namespace}:${key}:${locale}`;
|
|
508
|
+
}
|
|
509
|
+
function seedPreloadedKeysSingle(dictionary, preloadedKeys) {
|
|
510
|
+
for (const [key, localeByKey] of Object.entries(dictionary)) {
|
|
511
|
+
if (localeByKey === void 0 || typeof localeByKey !== "object") {
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
for (const locale of Object.keys(localeByKey)) {
|
|
515
|
+
preloadedKeys.add(preloadKeySingle(key, locale));
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function seedPreloadedKeysMulti(dictionary, preloadedKeys) {
|
|
520
|
+
for (const [namespace, keyDictionary] of Object.entries(dictionary)) {
|
|
521
|
+
if (keyDictionary === void 0 || typeof keyDictionary !== "object") {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
for (const [key, localeByKey] of Object.entries(keyDictionary)) {
|
|
525
|
+
if (localeByKey === void 0 || typeof localeByKey !== "object") {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
for (const locale of Object.keys(localeByKey)) {
|
|
529
|
+
preloadedKeys.add(preloadKeyMulti(namespace, key, locale));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function recordPreloadedKeysSingle(partial, preloadedKeys) {
|
|
535
|
+
for (const [key, incomingLocales] of Object.entries(partial)) {
|
|
536
|
+
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
for (const locale of Object.keys(incomingLocales)) {
|
|
540
|
+
preloadedKeys.add(preloadKeySingle(key, locale));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
function recordPreloadedKeysMulti(namespace, partial, preloadedKeys) {
|
|
545
|
+
for (const [key, incomingLocales] of Object.entries(partial)) {
|
|
546
|
+
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
for (const locale of Object.keys(incomingLocales)) {
|
|
550
|
+
preloadedKeys.add(preloadKeyMulti(namespace, key, locale));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function collectLocaleMetasForKey(localeByKey, patchedLocale, patchedMeta) {
|
|
555
|
+
const localeMetas = [patchedMeta];
|
|
556
|
+
for (const [locale, template] of Object.entries(localeByKey)) {
|
|
557
|
+
if (locale === patchedLocale) {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
const parsed = parseTemplate(template);
|
|
561
|
+
if (parsed.ok) {
|
|
562
|
+
localeMetas.push(parsed.meta);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return localeMetas;
|
|
566
|
+
}
|
|
567
|
+
function assertPatchKeySingle(key, locale, template, preloadedKeys, localeByKey) {
|
|
568
|
+
if (!preloadedKeys.has(preloadKeySingle(key, locale))) {
|
|
569
|
+
throw new Error(`[i18n] Key not preloaded: ${key} (${locale})`);
|
|
570
|
+
}
|
|
571
|
+
const parsed = parseTemplate(template);
|
|
572
|
+
if (!parsed.ok) {
|
|
573
|
+
throw new Error(`[i18n] ICU syntax error on patch: ${parsed.message}`);
|
|
574
|
+
}
|
|
575
|
+
const localeMetas = collectLocaleMetasForKey(localeByKey ?? {}, locale, parsed.meta);
|
|
576
|
+
const merged = mergeVariableMetaAcrossLocales(localeMetas);
|
|
577
|
+
if (!merged.ok) {
|
|
578
|
+
throw new Error(`[i18n] ICU args mismatch on patch: ${merged.message}`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function assertPatchKeyMulti(namespace, key, locale, template, preloadedKeys, localeByKey) {
|
|
582
|
+
if (!preloadedKeys.has(preloadKeyMulti(namespace, key, locale))) {
|
|
583
|
+
throw new Error(`[i18n] Key not preloaded: ${namespace}.${key} (${locale})`);
|
|
584
|
+
}
|
|
585
|
+
const parsed = parseTemplate(template);
|
|
586
|
+
if (!parsed.ok) {
|
|
587
|
+
throw new Error(`[i18n] ICU syntax error on patch: ${parsed.message}`);
|
|
588
|
+
}
|
|
589
|
+
const localeMetas = collectLocaleMetasForKey(localeByKey ?? {}, locale, parsed.meta);
|
|
590
|
+
const merged = mergeVariableMetaAcrossLocales(localeMetas);
|
|
591
|
+
if (!merged.ok) {
|
|
592
|
+
throw new Error(`[i18n] ICU args mismatch on patch: ${merged.message}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
146
596
|
// src/project-locales.ts
|
|
147
597
|
function classifyAreaLocales(areaLocales, localeFallback) {
|
|
148
598
|
const areaSet = new Set(areaLocales);
|
|
@@ -260,36 +710,57 @@ function mergeDictionaryLocalesCore(existing, incoming) {
|
|
|
260
710
|
return merged;
|
|
261
711
|
}
|
|
262
712
|
|
|
263
|
-
// src/
|
|
264
|
-
var
|
|
265
|
-
constructor(
|
|
266
|
-
this.
|
|
713
|
+
// src/scope-single.ts
|
|
714
|
+
var I18nScopeSingleImpl = class {
|
|
715
|
+
constructor(engine) {
|
|
716
|
+
this.engine = engine;
|
|
717
|
+
}
|
|
718
|
+
engine;
|
|
719
|
+
t = (key, locale, ...args) => {
|
|
720
|
+
const params = args[0];
|
|
721
|
+
return this.engine.getWithLocale(String(key), locale, params);
|
|
722
|
+
};
|
|
723
|
+
forLocale = (locale) => {
|
|
724
|
+
return new I18nScopeSingleForLocaleImpl(this.engine, locale);
|
|
725
|
+
};
|
|
726
|
+
};
|
|
727
|
+
var I18nScopeSingleForLocaleImpl = class _I18nScopeSingleForLocaleImpl {
|
|
728
|
+
constructor(engine, locale) {
|
|
729
|
+
this.engine = engine;
|
|
267
730
|
this.locale = locale;
|
|
268
731
|
}
|
|
269
|
-
|
|
732
|
+
engine;
|
|
270
733
|
locale;
|
|
271
|
-
|
|
734
|
+
t = (key, ...args) => {
|
|
272
735
|
const params = args[0];
|
|
273
|
-
return this.
|
|
274
|
-
}
|
|
736
|
+
return this.engine.getWithLocale(String(key), this.locale, params);
|
|
737
|
+
};
|
|
738
|
+
set = (key, template) => {
|
|
739
|
+
this.engine.patchKey(String(key), this.locale, template);
|
|
740
|
+
};
|
|
741
|
+
forLocale = (locale) => {
|
|
742
|
+
return new _I18nScopeSingleForLocaleImpl(this.engine, locale);
|
|
743
|
+
};
|
|
275
744
|
};
|
|
745
|
+
|
|
746
|
+
// src/IcuTranslationProviderSingle.ts
|
|
276
747
|
var IcuTranslationProviderSingle = class {
|
|
748
|
+
__i18nEngineMode = "single";
|
|
277
749
|
dictionary;
|
|
278
750
|
compiledCache = {};
|
|
751
|
+
preloadedKeys = /* @__PURE__ */ new Set();
|
|
752
|
+
builderLoadRegistry = new BuilderLoadRegistry();
|
|
279
753
|
localeFallback;
|
|
280
754
|
onMissing;
|
|
281
755
|
constructor(dictionary, options) {
|
|
282
756
|
this.dictionary = structuredClone(dictionary);
|
|
757
|
+
seedPreloadedKeysSingle(this.dictionary, this.preloadedKeys);
|
|
283
758
|
if (options?.localeFallback) {
|
|
284
759
|
validateLocaleFallback(options.localeFallback);
|
|
285
760
|
this.localeFallback = options.localeFallback;
|
|
286
761
|
}
|
|
287
762
|
this.onMissing = options?.onMissing ?? "throw";
|
|
288
763
|
}
|
|
289
|
-
get(key, locale, ...args) {
|
|
290
|
-
const params = args[0];
|
|
291
|
-
return this.getWithLocale(String(key), locale, params);
|
|
292
|
-
}
|
|
293
764
|
getWithLocale(key, locale, params) {
|
|
294
765
|
return resolveAndFormat({
|
|
295
766
|
localeByKey: this.dictionary[key],
|
|
@@ -309,18 +780,24 @@ var IcuTranslationProviderSingle = class {
|
|
|
309
780
|
}
|
|
310
781
|
});
|
|
311
782
|
}
|
|
312
|
-
|
|
313
|
-
|
|
783
|
+
toScope(options) {
|
|
784
|
+
if (options?.locale !== void 0) {
|
|
785
|
+
return new I18nScopeSingleForLocaleImpl(this, options.locale);
|
|
786
|
+
}
|
|
787
|
+
return new I18nScopeSingleImpl(this);
|
|
314
788
|
}
|
|
315
789
|
getAll() {
|
|
316
790
|
return cloneAndFreeze(this.dictionary);
|
|
317
791
|
}
|
|
318
|
-
|
|
319
|
-
this.
|
|
320
|
-
|
|
792
|
+
hasBuilderResourceLoaded(partition) {
|
|
793
|
+
return this.builderLoadRegistry.has(SINGLE_BUILDER_RESOURCE, partition);
|
|
794
|
+
}
|
|
795
|
+
markBuilderResourceLoaded(partition) {
|
|
796
|
+
this.builderLoadRegistry.mark(SINGLE_BUILDER_RESOURCE, partition);
|
|
321
797
|
}
|
|
322
|
-
|
|
798
|
+
applyLoadMergeSingle(values) {
|
|
323
799
|
this.dictionary = mergeNamespaceLocalesCore(this.dictionary, values);
|
|
800
|
+
recordPreloadedKeysSingle(values, this.preloadedKeys);
|
|
324
801
|
for (const [key, incomingLocales] of Object.entries(values)) {
|
|
325
802
|
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
326
803
|
continue;
|
|
@@ -330,46 +807,67 @@ var IcuTranslationProviderSingle = class {
|
|
|
330
807
|
}
|
|
331
808
|
}
|
|
332
809
|
}
|
|
810
|
+
patchKey(key, locale, template) {
|
|
811
|
+
assertPatchKeySingle(key, locale, template, this.preloadedKeys, this.dictionary[key]);
|
|
812
|
+
this.dictionary = mergeNamespaceLocalesCore(this.dictionary, {
|
|
813
|
+
[key]: { [locale]: template }
|
|
814
|
+
});
|
|
815
|
+
delete this.compiledCache[locale]?.[key];
|
|
816
|
+
}
|
|
333
817
|
};
|
|
334
818
|
|
|
335
|
-
// src/
|
|
336
|
-
var
|
|
337
|
-
constructor(
|
|
338
|
-
this.
|
|
819
|
+
// src/scope-multi.ts
|
|
820
|
+
var I18nScopeMultiImpl = class {
|
|
821
|
+
constructor(engine) {
|
|
822
|
+
this.engine = engine;
|
|
823
|
+
}
|
|
824
|
+
engine;
|
|
825
|
+
t = (namespace, key, locale, ...args) => {
|
|
826
|
+
const params = args[0];
|
|
827
|
+
return this.engine.getWithLocale(String(namespace), String(key), locale, params);
|
|
828
|
+
};
|
|
829
|
+
forLocale = (locale) => {
|
|
830
|
+
return new I18nScopeMultiForLocaleImpl(this.engine, locale);
|
|
831
|
+
};
|
|
832
|
+
};
|
|
833
|
+
var I18nScopeMultiForLocaleImpl = class _I18nScopeMultiForLocaleImpl {
|
|
834
|
+
constructor(engine, locale) {
|
|
835
|
+
this.engine = engine;
|
|
339
836
|
this.locale = locale;
|
|
340
837
|
}
|
|
341
|
-
|
|
838
|
+
engine;
|
|
342
839
|
locale;
|
|
343
|
-
|
|
840
|
+
t = (namespace, key, ...args) => {
|
|
344
841
|
const params = args[0];
|
|
345
|
-
return this.
|
|
346
|
-
}
|
|
842
|
+
return this.engine.getWithLocale(String(namespace), String(key), this.locale, params);
|
|
843
|
+
};
|
|
844
|
+
set = (namespace, key, template) => {
|
|
845
|
+
this.engine.patchKeyMulti(String(namespace), String(key), this.locale, template);
|
|
846
|
+
};
|
|
847
|
+
forLocale = (locale) => {
|
|
848
|
+
return new _I18nScopeMultiForLocaleImpl(this.engine, locale);
|
|
849
|
+
};
|
|
347
850
|
};
|
|
851
|
+
|
|
852
|
+
// src/IcuTranslationProviderMulti.ts
|
|
348
853
|
var IcuTranslationProviderMulti = class {
|
|
854
|
+
__i18nEngineMode = "multi";
|
|
349
855
|
dictionary;
|
|
350
856
|
compiledCache = {};
|
|
857
|
+
preloadedKeys = /* @__PURE__ */ new Set();
|
|
858
|
+
builderLoadRegistry = new BuilderLoadRegistry();
|
|
351
859
|
localeFallback;
|
|
352
860
|
onMissing;
|
|
353
|
-
loadedNamespaces;
|
|
354
861
|
constructor(dictionary, options) {
|
|
355
862
|
this.dictionary = structuredClone(dictionary);
|
|
356
|
-
this.
|
|
863
|
+
seedPreloadedKeysMulti(this.dictionary, this.preloadedKeys);
|
|
357
864
|
if (options?.localeFallback) {
|
|
358
865
|
validateLocaleFallback(options.localeFallback);
|
|
359
866
|
this.localeFallback = options.localeFallback;
|
|
360
867
|
}
|
|
361
868
|
this.onMissing = options?.onMissing ?? "throw";
|
|
362
869
|
}
|
|
363
|
-
get(namespace, key, locale, ...args) {
|
|
364
|
-
const params = args[0];
|
|
365
|
-
return this.getWithLocale(String(namespace), String(key), locale, params);
|
|
366
|
-
}
|
|
367
870
|
getWithLocale(namespace, key, locale, params) {
|
|
368
|
-
if (!this.loadedNamespaces.has(namespace)) {
|
|
369
|
-
throw new Error(
|
|
370
|
-
`[i18n] Namespace not loaded: "${namespace}". Register it with setNamespace() before calling .get().`
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
871
|
const localeByKey = this.dictionary[namespace]?.[key];
|
|
374
872
|
return resolveAndFormat({
|
|
375
873
|
localeByKey,
|
|
@@ -393,48 +891,57 @@ var IcuTranslationProviderMulti = class {
|
|
|
393
891
|
}
|
|
394
892
|
});
|
|
395
893
|
}
|
|
396
|
-
|
|
397
|
-
|
|
894
|
+
toScope(options) {
|
|
895
|
+
if (options.locale !== void 0) {
|
|
896
|
+
return new I18nScopeMultiForLocaleImpl(this, options.locale);
|
|
897
|
+
}
|
|
898
|
+
return new I18nScopeMultiImpl(this);
|
|
398
899
|
}
|
|
399
900
|
getAll() {
|
|
400
901
|
return cloneAndFreeze(this.dictionary);
|
|
401
902
|
}
|
|
402
|
-
|
|
403
|
-
return this.
|
|
903
|
+
hasBuilderResourceLoaded(namespace, partition) {
|
|
904
|
+
return this.builderLoadRegistry.has(namespace, partition);
|
|
404
905
|
}
|
|
405
|
-
|
|
406
|
-
this.
|
|
407
|
-
this.compiledCache = {};
|
|
408
|
-
this.loadedNamespaces = new Set(Object.keys(values));
|
|
906
|
+
markBuilderResourceLoaded(namespace, partition) {
|
|
907
|
+
this.builderLoadRegistry.mark(namespace, partition);
|
|
409
908
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (incoming !== void 0) {
|
|
414
|
-
this.mergeNamespace(namespace, incoming);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
setNamespace(namespace, values) {
|
|
909
|
+
applyLoadMergeNamespace(namespace, values) {
|
|
910
|
+
const existing = this.dictionary[namespace];
|
|
911
|
+
const merged = mergeNamespaceLocalesCore(existing ?? {}, values);
|
|
419
912
|
this.dictionary = {
|
|
420
913
|
...this.dictionary,
|
|
421
|
-
[namespace]:
|
|
914
|
+
[namespace]: merged
|
|
422
915
|
};
|
|
423
|
-
this.
|
|
424
|
-
for (const
|
|
425
|
-
|
|
916
|
+
recordPreloadedKeysMulti(namespace, values, this.preloadedKeys);
|
|
917
|
+
for (const [key, incomingLocales] of Object.entries(values)) {
|
|
918
|
+
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
for (const locale of Object.keys(incomingLocales)) {
|
|
922
|
+
delete this.compiledCache[locale]?.[namespace]?.[key];
|
|
923
|
+
}
|
|
426
924
|
}
|
|
427
925
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
926
|
+
applyLoadMergeAll(values) {
|
|
927
|
+
for (const namespace of Object.keys(values)) {
|
|
928
|
+
const incoming = values[namespace];
|
|
929
|
+
if (incoming !== void 0) {
|
|
930
|
+
this.applyLoadMergeNamespace(namespace, incoming);
|
|
931
|
+
}
|
|
432
932
|
}
|
|
433
|
-
|
|
434
|
-
|
|
933
|
+
}
|
|
934
|
+
patchKeyMulti(namespace, key, locale, template) {
|
|
935
|
+
const namespaceDictionary = this.dictionary[namespace];
|
|
936
|
+
const localeByKey = namespaceDictionary?.[key];
|
|
937
|
+
assertPatchKeyMulti(namespace, key, locale, template, this.preloadedKeys, localeByKey);
|
|
938
|
+
this.applyLoadMergeNamespace(
|
|
939
|
+
namespace,
|
|
940
|
+
{ [key]: { [locale]: template } }
|
|
941
|
+
);
|
|
435
942
|
}
|
|
436
943
|
};
|
|
437
944
|
|
|
438
|
-
export { IcuTranslationProviderMulti, IcuTranslationProviderSingle, formatLocaleFallbackChain, mergeDictionaryLocalesCore, mergeNamespaceLocalesCore, projectDictionaryForDeliveryAreaCore, projectDictionaryLocalesCore, projectNamespaceForDeliveryAreaCore, projectNamespaceLocalesCore, resolveLocaleTemplate, validateLocaleFallback };
|
|
945
|
+
export { IcuTranslationProviderMulti, IcuTranslationProviderSingle, createI18nBuilder, createI18nMultiBuilder, createI18nSingleBuilder, formatLocaleFallbackChain, invokeNamespaceLoader, mergeDictionaryLocalesCore, mergeNamespaceLocalesCore, projectDictionaryForDeliveryAreaCore, projectDictionaryLocalesCore, projectNamespaceForDeliveryAreaCore, projectNamespaceLocalesCore, resolveLocaleTemplate, validateLocaleFallback };
|
|
439
946
|
//# sourceMappingURL=index.js.map
|
|
440
947
|
//# sourceMappingURL=index.js.map
|