@xndrjs/i18n 0.6.0 → 0.7.0-alpha.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 -134
- package/dist/codegen/index.js.map +1 -1
- package/dist/index.d.ts +276 -61
- package/dist/index.js +687 -126
- 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 +115 -215
- package/src/IcuTranslationProviderMulti.ts +107 -96
- package/src/IcuTranslationProviderSingle.test.ts +36 -294
- package/src/IcuTranslationProviderSingle.ts +55 -72
- 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 +35 -10
- package/src/codegen/emit/namespace-loaders-file.ts +11 -61
- 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 +47 -7
- package/src/patch-key.test.ts +186 -0
- package/src/patch-key.ts +140 -0
- package/src/project-locales.test.ts +76 -0
- package/src/project-locales.ts +58 -1
- 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,153 +360,238 @@ function resolveAndFormat(options) {
|
|
|
143
360
|
}
|
|
144
361
|
}
|
|
145
362
|
|
|
146
|
-
// src/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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;
|
|
151
382
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
get(key, ...args) {
|
|
155
|
-
const params = args[0];
|
|
156
|
-
return this.provider.getWithLocale(String(key), this.locale, params);
|
|
383
|
+
if (left === "string" && right === "number" || left === "number" && right === "string") {
|
|
384
|
+
return "number";
|
|
157
385
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
}
|
|
169
414
|
}
|
|
170
|
-
|
|
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";
|
|
171
447
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return this.getWithLocale(String(key), locale, params);
|
|
448
|
+
if (hasPlural || hasSelectordinal || hasNumber) {
|
|
449
|
+
return "number";
|
|
175
450
|
}
|
|
176
|
-
|
|
177
|
-
return
|
|
178
|
-
localeByKey: this.dictionary[key],
|
|
179
|
-
locale,
|
|
180
|
-
params,
|
|
181
|
-
getCache: (resolvedLocale) => {
|
|
182
|
-
if (!this.compiledCache[resolvedLocale]) {
|
|
183
|
-
this.compiledCache[resolvedLocale] = {};
|
|
184
|
-
}
|
|
185
|
-
return this.compiledCache[resolvedLocale];
|
|
186
|
-
},
|
|
187
|
-
context: {
|
|
188
|
-
key,
|
|
189
|
-
locale,
|
|
190
|
-
localeFallback: this.localeFallback,
|
|
191
|
-
onMissing: this.onMissing
|
|
192
|
-
}
|
|
193
|
-
});
|
|
451
|
+
if (hasSelect || hasSimple) {
|
|
452
|
+
return "string";
|
|
194
453
|
}
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
};
|
|
197
463
|
}
|
|
198
|
-
|
|
199
|
-
|
|
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;
|
|
200
488
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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 };
|
|
204
499
|
}
|
|
205
|
-
}
|
|
500
|
+
}
|
|
206
501
|
|
|
207
|
-
// src/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
+
}
|
|
212
517
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
+
}
|
|
218
532
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.dictionary = structuredClone(dictionary);
|
|
228
|
-
this.loadedNamespaces = new Set(Object.keys(dictionary));
|
|
229
|
-
if (options?.localeFallback) {
|
|
230
|
-
validateLocaleFallback(options.localeFallback);
|
|
231
|
-
this.localeFallback = options.localeFallback;
|
|
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));
|
|
232
541
|
}
|
|
233
|
-
this.onMissing = options?.onMissing ?? "throw";
|
|
234
542
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
+
}
|
|
238
552
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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);
|
|
244
563
|
}
|
|
245
|
-
const localeByKey = this.dictionary[namespace]?.[key];
|
|
246
|
-
return resolveAndFormat({
|
|
247
|
-
localeByKey,
|
|
248
|
-
locale,
|
|
249
|
-
params,
|
|
250
|
-
getCache: (resolvedLocale) => {
|
|
251
|
-
if (!this.compiledCache[resolvedLocale]) {
|
|
252
|
-
this.compiledCache[resolvedLocale] = {};
|
|
253
|
-
}
|
|
254
|
-
if (!this.compiledCache[resolvedLocale][namespace]) {
|
|
255
|
-
this.compiledCache[resolvedLocale][namespace] = {};
|
|
256
|
-
}
|
|
257
|
-
return this.compiledCache[resolvedLocale][namespace];
|
|
258
|
-
},
|
|
259
|
-
context: {
|
|
260
|
-
namespace,
|
|
261
|
-
key,
|
|
262
|
-
locale,
|
|
263
|
-
localeFallback: this.localeFallback,
|
|
264
|
-
onMissing: this.onMissing
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
564
|
}
|
|
268
|
-
|
|
269
|
-
|
|
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})`);
|
|
270
570
|
}
|
|
271
|
-
|
|
272
|
-
|
|
571
|
+
const parsed = parseTemplate(template);
|
|
572
|
+
if (!parsed.ok) {
|
|
573
|
+
throw new Error(`[i18n] ICU syntax error on patch: ${parsed.message}`);
|
|
273
574
|
}
|
|
274
|
-
|
|
275
|
-
|
|
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}`);
|
|
276
579
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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})`);
|
|
281
584
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
[namespace]: structuredClone(values)
|
|
286
|
-
};
|
|
287
|
-
this.loadedNamespaces.add(namespace);
|
|
288
|
-
for (const locale of Object.keys(this.compiledCache)) {
|
|
289
|
-
delete this.compiledCache[locale]?.[namespace];
|
|
290
|
-
}
|
|
585
|
+
const parsed = parseTemplate(template);
|
|
586
|
+
if (!parsed.ok) {
|
|
587
|
+
throw new Error(`[i18n] ICU syntax error on patch: ${parsed.message}`);
|
|
291
588
|
}
|
|
292
|
-
};
|
|
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
|
+
}
|
|
293
595
|
|
|
294
596
|
// src/project-locales.ts
|
|
295
597
|
function classifyAreaLocales(areaLocales, localeFallback) {
|
|
@@ -380,7 +682,266 @@ function projectDictionaryLocalesCore(dictionary, locales, localeFallback) {
|
|
|
380
682
|
}
|
|
381
683
|
return result;
|
|
382
684
|
}
|
|
685
|
+
function mergeNamespaceLocalesCore(existing, incoming) {
|
|
686
|
+
const merged = structuredClone(existing);
|
|
687
|
+
for (const [key, incomingLocales] of Object.entries(incoming)) {
|
|
688
|
+
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
merged[key] = {
|
|
692
|
+
...merged[key] ?? {},
|
|
693
|
+
...incomingLocales
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
return merged;
|
|
697
|
+
}
|
|
698
|
+
function mergeDictionaryLocalesCore(existing, incoming) {
|
|
699
|
+
const merged = structuredClone(existing);
|
|
700
|
+
for (const namespace of Object.keys(incoming)) {
|
|
701
|
+
const incomingNamespace = incoming[namespace];
|
|
702
|
+
if (incomingNamespace === void 0) {
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
merged[namespace] = mergeNamespaceLocalesCore(
|
|
706
|
+
merged[namespace] ?? {},
|
|
707
|
+
incomingNamespace
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
return merged;
|
|
711
|
+
}
|
|
712
|
+
|
|
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;
|
|
730
|
+
this.locale = locale;
|
|
731
|
+
}
|
|
732
|
+
engine;
|
|
733
|
+
locale;
|
|
734
|
+
t = (key, ...args) => {
|
|
735
|
+
const params = args[0];
|
|
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
|
+
};
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
// src/IcuTranslationProviderSingle.ts
|
|
747
|
+
var IcuTranslationProviderSingle = class {
|
|
748
|
+
__i18nEngineMode = "single";
|
|
749
|
+
dictionary;
|
|
750
|
+
compiledCache = {};
|
|
751
|
+
preloadedKeys = /* @__PURE__ */ new Set();
|
|
752
|
+
builderLoadRegistry = new BuilderLoadRegistry();
|
|
753
|
+
localeFallback;
|
|
754
|
+
onMissing;
|
|
755
|
+
constructor(dictionary, options) {
|
|
756
|
+
this.dictionary = structuredClone(dictionary);
|
|
757
|
+
seedPreloadedKeysSingle(this.dictionary, this.preloadedKeys);
|
|
758
|
+
if (options?.localeFallback) {
|
|
759
|
+
validateLocaleFallback(options.localeFallback);
|
|
760
|
+
this.localeFallback = options.localeFallback;
|
|
761
|
+
}
|
|
762
|
+
this.onMissing = options?.onMissing ?? "throw";
|
|
763
|
+
}
|
|
764
|
+
getWithLocale(key, locale, params) {
|
|
765
|
+
return resolveAndFormat({
|
|
766
|
+
localeByKey: this.dictionary[key],
|
|
767
|
+
locale,
|
|
768
|
+
params,
|
|
769
|
+
getCache: (resolvedLocale) => {
|
|
770
|
+
if (!this.compiledCache[resolvedLocale]) {
|
|
771
|
+
this.compiledCache[resolvedLocale] = {};
|
|
772
|
+
}
|
|
773
|
+
return this.compiledCache[resolvedLocale];
|
|
774
|
+
},
|
|
775
|
+
context: {
|
|
776
|
+
key,
|
|
777
|
+
locale,
|
|
778
|
+
localeFallback: this.localeFallback,
|
|
779
|
+
onMissing: this.onMissing
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
toScope(options) {
|
|
784
|
+
if (options?.locale !== void 0) {
|
|
785
|
+
return new I18nScopeSingleForLocaleImpl(this, options.locale);
|
|
786
|
+
}
|
|
787
|
+
return new I18nScopeSingleImpl(this);
|
|
788
|
+
}
|
|
789
|
+
getAll() {
|
|
790
|
+
return cloneAndFreeze(this.dictionary);
|
|
791
|
+
}
|
|
792
|
+
hasBuilderResourceLoaded(partition) {
|
|
793
|
+
return this.builderLoadRegistry.has(SINGLE_BUILDER_RESOURCE, partition);
|
|
794
|
+
}
|
|
795
|
+
markBuilderResourceLoaded(partition) {
|
|
796
|
+
this.builderLoadRegistry.mark(SINGLE_BUILDER_RESOURCE, partition);
|
|
797
|
+
}
|
|
798
|
+
applyLoadMergeSingle(values) {
|
|
799
|
+
this.dictionary = mergeNamespaceLocalesCore(this.dictionary, values);
|
|
800
|
+
recordPreloadedKeysSingle(values, this.preloadedKeys);
|
|
801
|
+
for (const [key, incomingLocales] of Object.entries(values)) {
|
|
802
|
+
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
for (const locale of Object.keys(incomingLocales)) {
|
|
806
|
+
delete this.compiledCache[locale]?.[key];
|
|
807
|
+
}
|
|
808
|
+
}
|
|
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
|
+
}
|
|
817
|
+
};
|
|
818
|
+
|
|
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;
|
|
836
|
+
this.locale = locale;
|
|
837
|
+
}
|
|
838
|
+
engine;
|
|
839
|
+
locale;
|
|
840
|
+
t = (namespace, key, ...args) => {
|
|
841
|
+
const params = args[0];
|
|
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
|
+
};
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
// src/IcuTranslationProviderMulti.ts
|
|
853
|
+
var IcuTranslationProviderMulti = class {
|
|
854
|
+
__i18nEngineMode = "multi";
|
|
855
|
+
dictionary;
|
|
856
|
+
compiledCache = {};
|
|
857
|
+
preloadedKeys = /* @__PURE__ */ new Set();
|
|
858
|
+
builderLoadRegistry = new BuilderLoadRegistry();
|
|
859
|
+
localeFallback;
|
|
860
|
+
onMissing;
|
|
861
|
+
constructor(dictionary, options) {
|
|
862
|
+
this.dictionary = structuredClone(dictionary);
|
|
863
|
+
seedPreloadedKeysMulti(this.dictionary, this.preloadedKeys);
|
|
864
|
+
if (options?.localeFallback) {
|
|
865
|
+
validateLocaleFallback(options.localeFallback);
|
|
866
|
+
this.localeFallback = options.localeFallback;
|
|
867
|
+
}
|
|
868
|
+
this.onMissing = options?.onMissing ?? "throw";
|
|
869
|
+
}
|
|
870
|
+
getWithLocale(namespace, key, locale, params) {
|
|
871
|
+
const localeByKey = this.dictionary[namespace]?.[key];
|
|
872
|
+
return resolveAndFormat({
|
|
873
|
+
localeByKey,
|
|
874
|
+
locale,
|
|
875
|
+
params,
|
|
876
|
+
getCache: (resolvedLocale) => {
|
|
877
|
+
if (!this.compiledCache[resolvedLocale]) {
|
|
878
|
+
this.compiledCache[resolvedLocale] = {};
|
|
879
|
+
}
|
|
880
|
+
if (!this.compiledCache[resolvedLocale][namespace]) {
|
|
881
|
+
this.compiledCache[resolvedLocale][namespace] = {};
|
|
882
|
+
}
|
|
883
|
+
return this.compiledCache[resolvedLocale][namespace];
|
|
884
|
+
},
|
|
885
|
+
context: {
|
|
886
|
+
namespace,
|
|
887
|
+
key,
|
|
888
|
+
locale,
|
|
889
|
+
localeFallback: this.localeFallback,
|
|
890
|
+
onMissing: this.onMissing
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
toScope(options) {
|
|
895
|
+
if (options.locale !== void 0) {
|
|
896
|
+
return new I18nScopeMultiForLocaleImpl(this, options.locale);
|
|
897
|
+
}
|
|
898
|
+
return new I18nScopeMultiImpl(this);
|
|
899
|
+
}
|
|
900
|
+
getAll() {
|
|
901
|
+
return cloneAndFreeze(this.dictionary);
|
|
902
|
+
}
|
|
903
|
+
hasBuilderResourceLoaded(namespace, partition) {
|
|
904
|
+
return this.builderLoadRegistry.has(namespace, partition);
|
|
905
|
+
}
|
|
906
|
+
markBuilderResourceLoaded(namespace, partition) {
|
|
907
|
+
this.builderLoadRegistry.mark(namespace, partition);
|
|
908
|
+
}
|
|
909
|
+
applyLoadMergeNamespace(namespace, values) {
|
|
910
|
+
const existing = this.dictionary[namespace];
|
|
911
|
+
const merged = mergeNamespaceLocalesCore(existing ?? {}, values);
|
|
912
|
+
this.dictionary = {
|
|
913
|
+
...this.dictionary,
|
|
914
|
+
[namespace]: merged
|
|
915
|
+
};
|
|
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
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
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
|
+
}
|
|
932
|
+
}
|
|
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
|
+
);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
383
944
|
|
|
384
|
-
export { IcuTranslationProviderMulti, IcuTranslationProviderSingle, formatLocaleFallbackChain, projectDictionaryForDeliveryAreaCore, projectDictionaryLocalesCore, projectNamespaceForDeliveryAreaCore, projectNamespaceLocalesCore, resolveLocaleTemplate, validateLocaleFallback };
|
|
945
|
+
export { IcuTranslationProviderMulti, IcuTranslationProviderSingle, createI18nBuilder, createI18nMultiBuilder, createI18nSingleBuilder, formatLocaleFallbackChain, invokeNamespaceLoader, mergeDictionaryLocalesCore, mergeNamespaceLocalesCore, projectDictionaryForDeliveryAreaCore, projectDictionaryLocalesCore, projectNamespaceForDeliveryAreaCore, projectNamespaceLocalesCore, resolveLocaleTemplate, validateLocaleFallback };
|
|
385
946
|
//# sourceMappingURL=index.js.map
|
|
386
947
|
//# sourceMappingURL=index.js.map
|