@xndrjs/i18n 0.7.0 → 0.8.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 +67 -872
- package/dist/codegen/index.d.ts +117 -51
- package/dist/codegen/index.js +1585 -93
- package/dist/codegen/index.js.map +1 -1
- package/dist/index.d.ts +100 -234
- package/dist/index.js +74 -638
- package/dist/index.js.map +1 -1
- package/dist/validation/index.d.ts +5 -0
- package/dist/validation/index.js.map +1 -1
- package/package.json +2 -2
- package/src/IcuTranslationProviderMulti.test.ts +18 -40
- package/src/IcuTranslationProviderMulti.ts +27 -37
- package/src/audit/audit-dictionaries.ts +1 -1
- package/src/audit/run-audit.test.ts +6 -7
- package/src/builder-load-registry.ts +26 -5
- package/src/builder-loaders.ts +8 -11
- package/src/builder-types.test.ts +11 -42
- package/src/builder.test.ts +82 -243
- package/src/builder.ts +8 -110
- package/src/codegen/codegen-config-schema.ts +82 -77
- package/src/codegen/config.test.ts +55 -143
- package/src/codegen/config.ts +10 -67
- package/src/codegen/dictionary-spec-contract.test.ts +28 -0
- package/src/codegen/dictionary-spec-contract.ts +138 -0
- package/src/codegen/emit/dictionary-schema-file.ts +7 -43
- package/src/codegen/emit/instance-file.test.ts +35 -52
- package/src/codegen/emit/instance-file.ts +170 -268
- package/src/codegen/emit/namespace-loaders-file.test.ts +72 -74
- package/src/codegen/emit/namespace-loaders-file.ts +147 -90
- package/src/codegen/emit/types-file.test.ts +20 -41
- package/src/codegen/emit/types-file.ts +48 -74
- package/src/codegen/generate-i18n-types.test.ts +163 -492
- package/src/codegen/generate-i18n-types.ts +7 -269
- package/src/codegen/paths.ts +0 -14
- package/src/codegen/project-locales-set-namespace.test.ts +44 -86
- package/src/codegen/read-dictionary.test.ts +27 -9
- package/src/codegen/read-dictionary.ts +18 -40
- package/src/codegen/regenerate-namespaces.ts +180 -0
- package/src/codegen/run-codegen.test.ts +230 -0
- package/src/codegen/run-codegen.ts +252 -0
- package/src/codegen/types.ts +0 -6
- package/src/codegen-config/build-config.ts +4 -23
- package/src/codegen-config/codegen-config.test.ts +10 -6
- package/src/codegen-config/index.ts +18 -3
- package/src/engine.ts +3 -27
- package/src/fetch-artifact.ts +16 -0
- package/src/i18n-handle.ts +202 -0
- package/src/index.ts +15 -34
- package/src/project-locales.ts +2 -2
- package/src/scope-multi.ts +1 -20
- package/src/scope-types.ts +14 -6
- package/src/scope.test.ts +3 -312
- package/src/serialized-state.test.ts +114 -0
- package/src/serialized-state.ts +28 -0
- package/src/setup/setup-i18n.test.ts +22 -33
- package/src/setup/setup-i18n.ts +22 -27
- package/src/types.ts +3 -3
- package/src/validation/index.ts +4 -0
- package/src/IcuTranslationProviderSingle.test.ts +0 -177
- package/src/IcuTranslationProviderSingle.ts +0 -114
- package/src/builder-multi.ts +0 -481
- package/src/codegen/emit/dictionary-file.test.ts +0 -215
- package/src/codegen/emit/dictionary-file.ts +0 -244
- package/src/deep-freeze.test.ts +0 -40
- package/src/deep-freeze.ts +0 -22
- package/src/patch-key.test.ts +0 -186
- package/src/patch-key.ts +0 -140
- package/src/scope-single.ts +0 -85
- package/src/single-builder.ts +0 -153
package/dist/index.js
CHANGED
|
@@ -1,102 +1,57 @@
|
|
|
1
1
|
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
|
-
import { parse } from '@formatjs/icu-messageformat-parser';
|
|
3
2
|
|
|
4
3
|
// src/builder-loaders.ts
|
|
5
|
-
async function invokeNamespaceLoader(loader, partition) {
|
|
6
|
-
|
|
7
|
-
return await loader();
|
|
8
|
-
}
|
|
9
|
-
return await loader(partition);
|
|
4
|
+
async function invokeNamespaceLoader(loader, partition, context) {
|
|
5
|
+
return await loader(partition, context);
|
|
10
6
|
}
|
|
11
7
|
|
|
12
|
-
// src/
|
|
13
|
-
var
|
|
8
|
+
// src/i18n-handle.ts
|
|
9
|
+
var I18nHandleImpl = class {
|
|
14
10
|
constructor(engine, options) {
|
|
15
11
|
this.engine = engine;
|
|
16
12
|
this.options = options;
|
|
13
|
+
this.partitionForLocale = options?.partitionForLocale ?? ((locale) => locale);
|
|
17
14
|
}
|
|
18
15
|
engine;
|
|
19
16
|
options;
|
|
20
|
-
|
|
21
|
-
|
|
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;
|
|
17
|
+
partitionForLocale;
|
|
18
|
+
async load(input) {
|
|
19
|
+
await applyHandleLoad(this.engine, this.options, input, this.partitionForLocale);
|
|
20
|
+
return this.engine.toScope({ namespaces: input.namespaces }).forLocale(input.locale);
|
|
64
21
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return
|
|
70
|
-
...this.state,
|
|
71
|
-
namespaces
|
|
72
|
-
});
|
|
22
|
+
peek(input) {
|
|
23
|
+
if (!areHandleResourcesReady(this.engine, this.options, input, this.partitionForLocale)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return this.engine.toScope({ namespaces: input.namespaces }).forLocale(input.locale);
|
|
73
27
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return this.engine.toScope({ namespaces: this.state.namespaces });
|
|
28
|
+
serialize() {
|
|
29
|
+
return this.engine.serialize();
|
|
77
30
|
}
|
|
78
31
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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);
|
|
32
|
+
function createI18nHandle(engine, options) {
|
|
33
|
+
return new I18nHandleImpl(engine, options);
|
|
34
|
+
}
|
|
35
|
+
function areHandleResourcesReady(engine, options, input, partitionForLocale) {
|
|
36
|
+
if (input.namespaces.length === 0) {
|
|
37
|
+
return false;
|
|
91
38
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
39
|
+
const partition = partitionForLocale(input.locale);
|
|
40
|
+
return input.namespaces.every((namespace) => {
|
|
41
|
+
const loader = options?.namespaceLoaders?.[namespace];
|
|
42
|
+
if (loader === void 0) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return engine.hasBuilderResourceLoaded(namespace, partition);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function applyHandleLoad(engine, options, input, partitionForLocale) {
|
|
49
|
+
if (input.namespaces.length === 0) {
|
|
50
|
+
throw new Error("[i18n] load() requires a non-empty namespaces array.");
|
|
96
51
|
}
|
|
97
|
-
const partition =
|
|
52
|
+
const partition = partitionForLocale(input.locale);
|
|
98
53
|
await Promise.all(
|
|
99
|
-
|
|
54
|
+
input.namespaces.map(async (namespace) => {
|
|
100
55
|
const loader = options?.namespaceLoaders?.[namespace];
|
|
101
56
|
if (loader === void 0) {
|
|
102
57
|
return;
|
|
@@ -104,127 +59,25 @@ async function applyMultiBuilderLoad(engine, options, state) {
|
|
|
104
59
|
if (engine.hasBuilderResourceLoaded(namespace, partition)) {
|
|
105
60
|
return;
|
|
106
61
|
}
|
|
107
|
-
const data = await invokeNamespaceLoader(loader, partition);
|
|
62
|
+
const data = await invokeNamespaceLoader(loader, partition, { locale: input.locale });
|
|
108
63
|
engine.applyLoadMergeNamespace(namespace, data);
|
|
109
64
|
engine.markBuilderResourceLoaded(namespace, partition);
|
|
110
65
|
})
|
|
111
66
|
);
|
|
112
67
|
}
|
|
113
68
|
|
|
114
|
-
// src/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
}
|
|
204
|
-
|
|
205
|
-
// src/deep-freeze.ts
|
|
206
|
-
function deepFreeze(value) {
|
|
207
|
-
if (value === null || typeof value !== "object") {
|
|
208
|
-
return value;
|
|
209
|
-
}
|
|
210
|
-
if (Object.isFrozen(value)) {
|
|
211
|
-
return value;
|
|
212
|
-
}
|
|
213
|
-
Object.freeze(value);
|
|
214
|
-
for (const child of Object.values(value)) {
|
|
215
|
-
deepFreeze(child);
|
|
216
|
-
}
|
|
217
|
-
return value;
|
|
218
|
-
}
|
|
219
|
-
function cloneAndFreeze(value) {
|
|
220
|
-
return deepFreeze(structuredClone(value));
|
|
69
|
+
// src/serialized-state.ts
|
|
70
|
+
function normalizeI18nCreateInput(value) {
|
|
71
|
+
return {
|
|
72
|
+
dictionary: value?.dictionary ?? {},
|
|
73
|
+
resources: value?.resources ?? []
|
|
74
|
+
};
|
|
221
75
|
}
|
|
222
76
|
|
|
223
77
|
// src/builder-load-registry.ts
|
|
224
78
|
function formatBuilderResourceKey(namespace, partition) {
|
|
225
|
-
return `${namespace}\0${partition
|
|
79
|
+
return `${namespace}\0${partition}`;
|
|
226
80
|
}
|
|
227
|
-
var SINGLE_BUILDER_RESOURCE = "__dictionary__";
|
|
228
81
|
var BuilderLoadRegistry = class {
|
|
229
82
|
loaded = /* @__PURE__ */ new Set();
|
|
230
83
|
has(namespace, partition) {
|
|
@@ -233,6 +86,20 @@ var BuilderLoadRegistry = class {
|
|
|
233
86
|
mark(namespace, partition) {
|
|
234
87
|
this.loaded.add(formatBuilderResourceKey(namespace, partition));
|
|
235
88
|
}
|
|
89
|
+
/** Snapshot of loaded resources as `[namespace, partition]` tuples. */
|
|
90
|
+
entries() {
|
|
91
|
+
const out = [];
|
|
92
|
+
for (const key of this.loaded) {
|
|
93
|
+
const sep = key.indexOf("\0");
|
|
94
|
+
out.push([key.slice(0, sep), key.slice(sep + 1)]);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
seed(resources) {
|
|
99
|
+
for (const [namespace, partition] of resources) {
|
|
100
|
+
this.mark(namespace, partition);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
236
103
|
};
|
|
237
104
|
|
|
238
105
|
// src/resolve-locale.ts
|
|
@@ -360,328 +227,7 @@ function resolveAndFormat(options) {
|
|
|
360
227
|
}
|
|
361
228
|
}
|
|
362
229
|
|
|
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
|
-
|
|
596
230
|
// src/project-locales.ts
|
|
597
|
-
function classifyAreaLocales(areaLocales, localeFallback) {
|
|
598
|
-
const areaSet = new Set(areaLocales);
|
|
599
|
-
const full = [];
|
|
600
|
-
const preserve = [];
|
|
601
|
-
for (const locale of areaLocales) {
|
|
602
|
-
const fallback = localeFallback?.[locale];
|
|
603
|
-
if (fallback !== null && fallback !== void 0 && areaSet.has(fallback)) {
|
|
604
|
-
preserve.push(locale);
|
|
605
|
-
} else {
|
|
606
|
-
full.push(locale);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return { full, preserve };
|
|
610
|
-
}
|
|
611
|
-
function projectKeyDictionary(dictionary, locales, localeFallback) {
|
|
612
|
-
const targetLocales = [...new Set(locales)];
|
|
613
|
-
const result = {};
|
|
614
|
-
for (const [key, localesByKey] of Object.entries(dictionary)) {
|
|
615
|
-
const projected = {};
|
|
616
|
-
for (const locale of targetLocales) {
|
|
617
|
-
const resolved = resolveLocaleTemplate(localesByKey, locale, localeFallback);
|
|
618
|
-
if (resolved !== void 0) {
|
|
619
|
-
projected[locale] = resolved.template;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
if (Object.keys(projected).length > 0) {
|
|
623
|
-
result[key] = projected;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
return result;
|
|
627
|
-
}
|
|
628
|
-
function projectNamespaceLocalesCore(dictionary, locales, localeFallback) {
|
|
629
|
-
return projectKeyDictionary(dictionary, locales, localeFallback);
|
|
630
|
-
}
|
|
631
|
-
function projectNamespaceForDeliveryAreaCore(dictionary, areaLocales, localeFallback) {
|
|
632
|
-
const { full, preserve } = classifyAreaLocales(areaLocales, localeFallback);
|
|
633
|
-
const fullProjection = full.length > 0 ? projectKeyDictionary(dictionary, full, localeFallback) : void 0;
|
|
634
|
-
const result = {};
|
|
635
|
-
for (const [key, localesByKey] of Object.entries(dictionary)) {
|
|
636
|
-
const projected = {};
|
|
637
|
-
if (fullProjection !== void 0) {
|
|
638
|
-
const fullEntry = fullProjection[key];
|
|
639
|
-
if (fullEntry !== void 0) {
|
|
640
|
-
Object.assign(projected, fullEntry);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
for (const locale of preserve) {
|
|
644
|
-
const template = localesByKey[locale];
|
|
645
|
-
if (template !== void 0) {
|
|
646
|
-
projected[locale] = template;
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
if (Object.keys(projected).length > 0) {
|
|
650
|
-
result[key] = projected;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return result;
|
|
654
|
-
}
|
|
655
|
-
function projectDictionaryForDeliveryAreaCore(dictionary, areaLocales, localeFallback) {
|
|
656
|
-
const result = {};
|
|
657
|
-
for (const namespace of Object.keys(dictionary)) {
|
|
658
|
-
const namespaceDictionary = dictionary[namespace];
|
|
659
|
-
if (namespaceDictionary === void 0) {
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
result[namespace] = projectNamespaceForDeliveryAreaCore(
|
|
663
|
-
namespaceDictionary,
|
|
664
|
-
areaLocales,
|
|
665
|
-
localeFallback
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
return result;
|
|
669
|
-
}
|
|
670
|
-
function projectDictionaryLocalesCore(dictionary, locales, localeFallback) {
|
|
671
|
-
const result = {};
|
|
672
|
-
for (const namespace of Object.keys(dictionary)) {
|
|
673
|
-
const namespaceDictionary = dictionary[namespace];
|
|
674
|
-
if (namespaceDictionary === void 0) {
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
result[namespace] = projectKeyDictionary(
|
|
678
|
-
namespaceDictionary,
|
|
679
|
-
locales,
|
|
680
|
-
localeFallback
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
return result;
|
|
684
|
-
}
|
|
685
231
|
function mergeNamespaceLocalesCore(existing, incoming) {
|
|
686
232
|
const merged = structuredClone(existing);
|
|
687
233
|
for (const [key, incomingLocales] of Object.entries(incoming)) {
|
|
@@ -710,112 +256,6 @@ function mergeDictionaryLocalesCore(existing, incoming) {
|
|
|
710
256
|
return merged;
|
|
711
257
|
}
|
|
712
258
|
|
|
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
259
|
// src/scope-multi.ts
|
|
820
260
|
var I18nScopeMultiImpl = class {
|
|
821
261
|
constructor(engine) {
|
|
@@ -841,9 +281,6 @@ var I18nScopeMultiForLocaleImpl = class _I18nScopeMultiForLocaleImpl {
|
|
|
841
281
|
const params = args[0];
|
|
842
282
|
return this.engine.getWithLocale(String(namespace), String(key), this.locale, params);
|
|
843
283
|
};
|
|
844
|
-
set = (namespace, key, template) => {
|
|
845
|
-
this.engine.patchKeyMulti(String(namespace), String(key), this.locale, template);
|
|
846
|
-
};
|
|
847
284
|
forLocale = (locale) => {
|
|
848
285
|
return new _I18nScopeMultiForLocaleImpl(this.engine, locale);
|
|
849
286
|
};
|
|
@@ -852,15 +289,14 @@ var I18nScopeMultiForLocaleImpl = class _I18nScopeMultiForLocaleImpl {
|
|
|
852
289
|
// src/IcuTranslationProviderMulti.ts
|
|
853
290
|
var IcuTranslationProviderMulti = class {
|
|
854
291
|
__i18nEngineMode = "multi";
|
|
292
|
+
/** Loaded namespaces only — cold start is `{}`; never claim full Schema until loaded. */
|
|
855
293
|
dictionary;
|
|
856
294
|
compiledCache = {};
|
|
857
|
-
preloadedKeys = /* @__PURE__ */ new Set();
|
|
858
295
|
builderLoadRegistry = new BuilderLoadRegistry();
|
|
859
296
|
localeFallback;
|
|
860
297
|
onMissing;
|
|
861
|
-
constructor(dictionary, options) {
|
|
298
|
+
constructor(dictionary = {}, options) {
|
|
862
299
|
this.dictionary = structuredClone(dictionary);
|
|
863
|
-
seedPreloadedKeysMulti(this.dictionary, this.preloadedKeys);
|
|
864
300
|
if (options?.localeFallback) {
|
|
865
301
|
validateLocaleFallback(options.localeFallback);
|
|
866
302
|
this.localeFallback = options.localeFallback;
|
|
@@ -897,8 +333,9 @@ var IcuTranslationProviderMulti = class {
|
|
|
897
333
|
}
|
|
898
334
|
return new I18nScopeMultiImpl(this);
|
|
899
335
|
}
|
|
336
|
+
/** Live dictionary reference (loaded namespaces only). Do not mutate. */
|
|
900
337
|
getAll() {
|
|
901
|
-
return
|
|
338
|
+
return this.dictionary;
|
|
902
339
|
}
|
|
903
340
|
hasBuilderResourceLoaded(namespace, partition) {
|
|
904
341
|
return this.builderLoadRegistry.has(namespace, partition);
|
|
@@ -906,14 +343,22 @@ var IcuTranslationProviderMulti = class {
|
|
|
906
343
|
markBuilderResourceLoaded(namespace, partition) {
|
|
907
344
|
this.builderLoadRegistry.mark(namespace, partition);
|
|
908
345
|
}
|
|
346
|
+
seedBuilderResources(resources) {
|
|
347
|
+
this.builderLoadRegistry.seed(resources);
|
|
348
|
+
}
|
|
349
|
+
serialize() {
|
|
350
|
+
return {
|
|
351
|
+
dictionary: this.dictionary,
|
|
352
|
+
resources: this.builderLoadRegistry.entries()
|
|
353
|
+
};
|
|
354
|
+
}
|
|
909
355
|
applyLoadMergeNamespace(namespace, values) {
|
|
910
|
-
const existing = this.dictionary[namespace];
|
|
911
|
-
const merged = mergeNamespaceLocalesCore(existing
|
|
356
|
+
const existing = this.dictionary[namespace] ?? {};
|
|
357
|
+
const merged = mergeNamespaceLocalesCore(existing, values);
|
|
912
358
|
this.dictionary = {
|
|
913
359
|
...this.dictionary,
|
|
914
360
|
[namespace]: merged
|
|
915
361
|
};
|
|
916
|
-
recordPreloadedKeysMulti(namespace, values, this.preloadedKeys);
|
|
917
362
|
for (const [key, incomingLocales] of Object.entries(values)) {
|
|
918
363
|
if (incomingLocales === void 0 || typeof incomingLocales !== "object") {
|
|
919
364
|
continue;
|
|
@@ -931,17 +376,8 @@ var IcuTranslationProviderMulti = class {
|
|
|
931
376
|
}
|
|
932
377
|
}
|
|
933
378
|
}
|
|
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
379
|
};
|
|
944
380
|
|
|
945
|
-
export { IcuTranslationProviderMulti,
|
|
381
|
+
export { IcuTranslationProviderMulti, createI18nHandle, formatLocaleFallbackChain, invokeNamespaceLoader, mergeDictionaryLocalesCore, mergeNamespaceLocalesCore, normalizeI18nCreateInput, resolveLocaleTemplate, validateLocaleFallback };
|
|
946
382
|
//# sourceMappingURL=index.js.map
|
|
947
383
|
//# sourceMappingURL=index.js.map
|