@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.
Files changed (51) hide show
  1. package/README.md +154 -141
  2. package/dist/codegen/index.js.map +1 -1
  3. package/dist/index.d.ts +268 -69
  4. package/dist/index.js +572 -65
  5. package/dist/index.js.map +1 -1
  6. package/dist/validation/index.d.ts +23 -7
  7. package/dist/validation/index.js +196 -89
  8. package/dist/validation/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/IcuTranslationProviderMulti.test.ts +80 -234
  11. package/src/IcuTranslationProviderMulti.ts +101 -112
  12. package/src/IcuTranslationProviderSingle.test.ts +30 -309
  13. package/src/IcuTranslationProviderSingle.ts +42 -75
  14. package/src/builder-load-registry.ts +18 -0
  15. package/src/builder-loaders.ts +18 -0
  16. package/src/builder-multi.ts +481 -0
  17. package/src/builder-types.test.ts +80 -0
  18. package/src/builder-types.ts +24 -0
  19. package/src/builder.test.ts +421 -0
  20. package/src/builder.ts +112 -0
  21. package/src/codegen/delivery-artifacts.test.ts +2 -1
  22. package/src/codegen/delivery-artifacts.ts +2 -1
  23. package/src/codegen/emit/dictionary-file.test.ts +0 -7
  24. package/src/codegen/emit/dictionary-schema-file.ts +55 -42
  25. package/src/codegen/emit/instance-file.test.ts +88 -0
  26. package/src/codegen/emit/instance-file.ts +164 -17
  27. package/src/codegen/emit/namespace-loaders-file.test.ts +4 -18
  28. package/src/codegen/emit/namespace-loaders-file.ts +4 -54
  29. package/src/codegen/emit/types-file.test.ts +0 -2
  30. package/src/codegen/generate-i18n-types.test.ts +8 -303
  31. package/src/codegen/generate-i18n-types.ts +16 -1
  32. package/src/codegen/project-locales-set-namespace.test.ts +25 -32
  33. package/src/engine.ts +71 -0
  34. package/src/index.ts +45 -7
  35. package/src/patch-key.test.ts +186 -0
  36. package/src/patch-key.ts +140 -0
  37. package/src/project-locales.test.ts +16 -6
  38. package/src/project-locales.ts +17 -6
  39. package/src/scope-multi.ts +124 -0
  40. package/src/scope-single.ts +85 -0
  41. package/src/scope-types.ts +27 -0
  42. package/src/scope.test.ts +481 -0
  43. package/src/single-builder.ts +153 -0
  44. package/src/types.ts +16 -0
  45. package/src/validation/create-normalized-schema.ts +1 -1
  46. package/src/validation/errors.ts +2 -0
  47. package/src/validation/index.ts +135 -27
  48. package/src/validation/normalize.ts +176 -46
  49. package/src/validation/types.ts +4 -0
  50. package/src/validation/validate-normalized.ts +43 -1
  51. package/src/validation/validation.test.ts +200 -7
@@ -1,5 +1,12 @@
1
1
  import { cloneAndFreeze } from "./deep-freeze.js";
2
+ import { BuilderLoadRegistry, SINGLE_BUILDER_RESOURCE } from "./builder-load-registry.js";
3
+ import type { I18nEngineSingle } from "./engine.js";
2
4
  import { resolveAndFormat } from "./format-core.js";
5
+ import {
6
+ assertPatchKeySingle,
7
+ recordPreloadedKeysSingle,
8
+ seedPreloadedKeysSingle,
9
+ } from "./patch-key.js";
3
10
  import { mergeNamespaceLocalesCore } from "./project-locales.js";
4
11
  import { validateLocaleFallback } from "./resolve-locale.js";
5
12
  import type {
@@ -9,77 +16,28 @@ import type {
9
16
  LocaleOfSingle,
10
17
  OnMissingTranslation,
11
18
  SingleCompiledCache,
19
+ PartialKeyDictionary,
12
20
  } from "./types.js";
13
-
14
- export interface TranslationProviderSingleForLocale<
15
- Schema extends KeyDictionary,
16
- Params extends { [K in keyof Schema]: unknown },
17
- Locale extends string,
18
- > {
19
- readonly locale: Locale;
20
- get<K extends keyof Schema & string>(
21
- key: K,
22
- ...params: Params[K] extends never ? [] : [params: Params[K]]
23
- ): string;
24
- }
25
-
26
- export interface TranslationProviderSingle<
27
- Schema extends KeyDictionary,
28
- Params extends { [K in keyof Schema]: unknown },
29
- RequestLocales extends string = LocaleOfSingle<Schema>,
30
- > {
31
- get<K extends keyof Schema & string>(
32
- key: K,
33
- locale: RequestLocales,
34
- ...params: Params[K] extends never ? [] : [params: Params[K]]
35
- ): string;
36
- forLocale<Locale extends RequestLocales>(
37
- locale: Locale
38
- ): TranslationProviderSingleForLocale<Schema, Params, Locale>;
39
- getAll(): Schema;
40
- setAll(values: Schema): void;
41
- mergeAll(values: Partial<Schema>): void;
42
- }
43
-
44
- export class IcuTranslationProviderSingleForLocale<
45
- Schema extends KeyDictionary,
46
- Params extends { [K in keyof Schema]: unknown },
47
- RequestLocales extends string,
48
- Locale extends RequestLocales,
49
- Fallback extends LocaleFallbackMap | undefined,
50
- > implements TranslationProviderSingleForLocale<Schema, Params, Locale> {
51
- constructor(
52
- private readonly provider: IcuTranslationProviderSingle<
53
- Schema,
54
- Params,
55
- RequestLocales,
56
- Fallback
57
- >,
58
- readonly locale: Locale
59
- ) {}
60
-
61
- get<K extends keyof Schema & string>(
62
- key: K,
63
- ...args: Params[K] extends never ? [] : [params: Params[K]]
64
- ): string {
65
- const params = args[0] as Record<string, unknown> | undefined;
66
- return this.provider.getWithLocale(String(key), this.locale, params);
67
- }
68
- }
21
+ import { I18nScopeSingleForLocaleImpl, I18nScopeSingleImpl } from "./scope-single.js";
69
22
 
70
23
  export class IcuTranslationProviderSingle<
71
24
  Schema extends KeyDictionary,
72
25
  Params extends { [K in keyof Schema]: unknown },
73
26
  RequestLocales extends string = LocaleOfSingle<Schema>,
74
27
  Fallback extends LocaleFallbackMap | undefined = undefined,
75
- > implements TranslationProviderSingle<Schema, Params, RequestLocales> {
28
+ > implements I18nEngineSingle<Schema, Params, RequestLocales> {
29
+ readonly __i18nEngineMode = "single" as const;
30
+
76
31
  private dictionary: Schema;
77
32
  private compiledCache: SingleCompiledCache = {};
33
+ private readonly preloadedKeys = new Set<string>();
34
+ private readonly builderLoadRegistry = new BuilderLoadRegistry();
78
35
  private readonly localeFallback?: Fallback;
79
36
  private readonly onMissing: OnMissingTranslation;
80
37
 
81
38
  constructor(dictionary: Schema, options?: IcuTranslationProviderOptions<Fallback>) {
82
39
  this.dictionary = structuredClone(dictionary);
40
+ seedPreloadedKeysSingle(this.dictionary, this.preloadedKeys);
83
41
  if (options?.localeFallback) {
84
42
  validateLocaleFallback(options.localeFallback);
85
43
  this.localeFallback = options.localeFallback;
@@ -87,15 +45,6 @@ export class IcuTranslationProviderSingle<
87
45
  this.onMissing = options?.onMissing ?? "throw";
88
46
  }
89
47
 
90
- get<K extends keyof Schema & string>(
91
- key: K,
92
- locale: RequestLocales,
93
- ...args: Params[K] extends never ? [] : [params: Params[K]]
94
- ): string {
95
- const params = args[0] as Record<string, unknown> | undefined;
96
- return this.getWithLocale(String(key), locale, params);
97
- }
98
-
99
48
  getWithLocale(key: string, locale: string, params?: Record<string, unknown>): string {
100
49
  return resolveAndFormat({
101
50
  localeByKey: this.dictionary[key],
@@ -116,23 +65,32 @@ export class IcuTranslationProviderSingle<
116
65
  });
117
66
  }
118
67
 
119
- forLocale<Locale extends RequestLocales>(
120
- locale: Locale
121
- ): IcuTranslationProviderSingleForLocale<Schema, Params, RequestLocales, Locale, Fallback> {
122
- return new IcuTranslationProviderSingleForLocale(this, locale);
68
+ toScope(): I18nScopeSingleImpl<Schema, Params, RequestLocales, Fallback>;
69
+ toScope<Locale extends RequestLocales>(options: {
70
+ locale: Locale;
71
+ }): I18nScopeSingleForLocaleImpl<Schema, Params, RequestLocales, Locale, Fallback>;
72
+ toScope<Locale extends RequestLocales>(options?: { locale?: Locale }) {
73
+ if (options?.locale !== undefined) {
74
+ return new I18nScopeSingleForLocaleImpl(this, options.locale);
75
+ }
76
+ return new I18nScopeSingleImpl(this);
123
77
  }
124
78
 
125
79
  getAll(): Schema {
126
80
  return cloneAndFreeze(this.dictionary);
127
81
  }
128
82
 
129
- setAll(values: Schema): void {
130
- this.dictionary = structuredClone(values);
131
- this.compiledCache = {};
83
+ hasBuilderResourceLoaded(partition: string | undefined): boolean {
84
+ return this.builderLoadRegistry.has(SINGLE_BUILDER_RESOURCE, partition);
85
+ }
86
+
87
+ markBuilderResourceLoaded(partition: string | undefined): void {
88
+ this.builderLoadRegistry.mark(SINGLE_BUILDER_RESOURCE, partition);
132
89
  }
133
90
 
134
- mergeAll(values: Partial<Schema>): void {
135
- this.dictionary = mergeNamespaceLocalesCore(this.dictionary, values as Schema);
91
+ applyLoadMergeSingle(values: PartialKeyDictionary<Schema, RequestLocales>): void {
92
+ this.dictionary = mergeNamespaceLocalesCore(this.dictionary, values);
93
+ recordPreloadedKeysSingle(values, this.preloadedKeys);
136
94
 
137
95
  for (const [key, incomingLocales] of Object.entries(values)) {
138
96
  if (incomingLocales === undefined || typeof incomingLocales !== "object") {
@@ -144,4 +102,13 @@ export class IcuTranslationProviderSingle<
144
102
  }
145
103
  }
146
104
  }
105
+
106
+ patchKey(key: string, locale: string, template: string): void {
107
+ assertPatchKeySingle(key, locale, template, this.preloadedKeys, this.dictionary[key]);
108
+
109
+ this.dictionary = mergeNamespaceLocalesCore(this.dictionary, {
110
+ [key]: { [locale]: template },
111
+ } as PartialKeyDictionary<Schema, RequestLocales>);
112
+ delete this.compiledCache[locale]?.[key];
113
+ }
147
114
  }
@@ -0,0 +1,18 @@
1
+ /** Stable key for a lazy-loaded builder resource (namespace + locale or delivery area). */
2
+ export function formatBuilderResourceKey(namespace: string, partition: string | undefined): string {
3
+ return `${namespace}\0${partition ?? ""}`;
4
+ }
5
+
6
+ export const SINGLE_BUILDER_RESOURCE = "__dictionary__";
7
+
8
+ export class BuilderLoadRegistry {
9
+ private readonly loaded = new Set<string>();
10
+
11
+ has(namespace: string, partition: string | undefined): boolean {
12
+ return this.loaded.has(formatBuilderResourceKey(namespace, partition));
13
+ }
14
+
15
+ mark(namespace: string, partition: string | undefined): void {
16
+ this.loaded.add(formatBuilderResourceKey(namespace, partition));
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ /** Canonical lazy loader — no locale or delivery-area partition. */
2
+ export type CanonicalLoader<T> = () => Promise<T> | T;
3
+
4
+ /** Split-by-locale or custom-area loader — partition key is locale or area name. */
5
+ export type PartitionedLoader<T> = (partition: string) => Promise<T> | T;
6
+
7
+ export type NamespaceLoader<T> = CanonicalLoader<T> | PartitionedLoader<T>;
8
+
9
+ export async function invokeNamespaceLoader<T>(
10
+ loader: NamespaceLoader<T>,
11
+ partition: string | undefined
12
+ ): Promise<T> {
13
+ if (partition === undefined) {
14
+ return await (loader as CanonicalLoader<T>)();
15
+ }
16
+
17
+ return await (loader as PartitionedLoader<T>)(partition);
18
+ }
@@ -0,0 +1,481 @@
1
+ import { invokeNamespaceLoader, type NamespaceLoader } from "./builder-loaders.js";
2
+ import type { DeliveryArtifactsMap, LocalesForDeliveryAreaOrAll } from "./builder-types.js";
3
+ import type { I18nEngineMultiImpl } from "./engine.js";
4
+ import type { LocaleOfMulti, MultiDictionary, PartialKeyDictionary } from "./types.js";
5
+ import type { I18nScopeMulti, I18nScopeMultiForLocale } from "./scope-multi.js";
6
+ import type { MultiParams, ParamsForNamespaces, SchemaForNamespaces } from "./scope-types.js";
7
+
8
+ export type I18nBuilderMultiOptions<
9
+ Schema extends MultiDictionary,
10
+ RequestLocales extends string = LocaleOfMulti<Schema>,
11
+ > = {
12
+ namespaceLoaders?: {
13
+ [NS in keyof Schema & string]?: NamespaceLoader<
14
+ PartialKeyDictionary<Schema[NS], RequestLocales> | Schema[NS]
15
+ >;
16
+ };
17
+ };
18
+
19
+ /** Non-empty namespace list — required before partition or load. */
20
+ type NonEmptyNamespaces<NS extends readonly string[]> = NS extends readonly [] ? never : NS;
21
+
22
+ type MultiBuilderState<
23
+ Schema extends MultiDictionary,
24
+ RequestLocales extends string = LocaleOfMulti<Schema>,
25
+ NsList extends readonly (keyof Schema & string)[] = readonly (keyof Schema & string)[],
26
+ > = {
27
+ namespaces: NsList;
28
+ locale?: RequestLocales;
29
+ deliveryArea?: string;
30
+ };
31
+
32
+ /** Builder returned by `createI18n({})` — only `withNamespaces` until namespaces are selected. */
33
+ export interface I18nBuilderMultiInitial<
34
+ Schema extends MultiDictionary,
35
+ Params extends MultiParams<Schema>,
36
+ RequestLocales extends string = LocaleOfMulti<Schema>,
37
+ ActiveLocales extends RequestLocales = RequestLocales,
38
+ DeliveryArea extends string = never,
39
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
40
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
41
+ > {
42
+ withNamespaces<const NS extends readonly (keyof Schema & string)[]>(
43
+ namespaces: NonEmptyNamespaces<NS>
44
+ ): I18nBuilderMultiReady<
45
+ Schema,
46
+ Params,
47
+ RequestLocales,
48
+ ActiveLocales,
49
+ NS,
50
+ DeliveryArea,
51
+ DeliveryArtifacts
52
+ >;
53
+ }
54
+
55
+ /** Builder after `withNamespaces([...])` — partition selection and `load()` are available. */
56
+ export interface I18nBuilderMultiReady<
57
+ Schema extends MultiDictionary,
58
+ Params extends MultiParams<Schema>,
59
+ RequestLocales extends string = LocaleOfMulti<Schema>,
60
+ ActiveLocales extends RequestLocales = RequestLocales,
61
+ NsList extends readonly (keyof Schema & string)[] = readonly (keyof Schema & string)[],
62
+ DeliveryArea extends string = never,
63
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
64
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
65
+ > {
66
+ withNamespaces<const NS extends readonly (keyof Schema & string)[]>(
67
+ namespaces: NonEmptyNamespaces<NS>
68
+ ): I18nBuilderMultiReady<
69
+ Schema,
70
+ Params,
71
+ RequestLocales,
72
+ ActiveLocales,
73
+ NS,
74
+ DeliveryArea,
75
+ DeliveryArtifacts
76
+ >;
77
+
78
+ withLocale<Locale extends ActiveLocales>(
79
+ locale: Locale
80
+ ): I18nBuilderMultiForLocale<Schema, Params, RequestLocales, ActiveLocales, NsList, Locale>;
81
+
82
+ withDeliveryArea<Area extends DeliveryArea>(
83
+ area: Area
84
+ ): I18nBuilderMultiPartitioned<
85
+ Schema,
86
+ Params,
87
+ RequestLocales,
88
+ LocalesForDeliveryAreaOrAll<RequestLocales, DeliveryArea, DeliveryArtifacts, Area>,
89
+ NsList,
90
+ DeliveryArea,
91
+ DeliveryArtifacts
92
+ >;
93
+
94
+ load(): Promise<
95
+ I18nScopeMulti<
96
+ SchemaForNamespaces<Schema, NsList>,
97
+ ParamsForNamespaces<Schema, Params, NsList>,
98
+ ActiveLocales
99
+ >
100
+ >;
101
+ }
102
+
103
+ /** @deprecated Alias for {@link I18nBuilderMultiInitial} — the multi builder before `withNamespaces`. */
104
+ export type I18nBuilderMulti<
105
+ Schema extends MultiDictionary,
106
+ Params extends MultiParams<Schema>,
107
+ RequestLocales extends string = LocaleOfMulti<Schema>,
108
+ ActiveLocales extends RequestLocales = RequestLocales,
109
+ NsList extends readonly (keyof Schema & string)[] = readonly [],
110
+ DeliveryArea extends string = never,
111
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
112
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
113
+ > = [NsList] extends [readonly []]
114
+ ? I18nBuilderMultiInitial<
115
+ Schema,
116
+ Params,
117
+ RequestLocales,
118
+ ActiveLocales,
119
+ DeliveryArea,
120
+ DeliveryArtifacts
121
+ >
122
+ : I18nBuilderMultiReady<
123
+ Schema,
124
+ Params,
125
+ RequestLocales,
126
+ ActiveLocales,
127
+ NsList,
128
+ DeliveryArea,
129
+ DeliveryArtifacts
130
+ >;
131
+
132
+ /** Builder after `withDeliveryArea` — partition is fixed; bind locale via `scope.forLocale` after `load()`. */
133
+ export interface I18nBuilderMultiPartitioned<
134
+ Schema extends MultiDictionary,
135
+ Params extends MultiParams<Schema>,
136
+ RequestLocales extends string,
137
+ ActiveLocales extends RequestLocales,
138
+ NsList extends readonly (keyof Schema & string)[],
139
+ DeliveryArea extends string = never,
140
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
141
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
142
+ > {
143
+ withNamespaces<const NS extends readonly (keyof Schema & string)[]>(
144
+ namespaces: NonEmptyNamespaces<NS>
145
+ ): I18nBuilderMultiPartitioned<
146
+ Schema,
147
+ Params,
148
+ RequestLocales,
149
+ ActiveLocales,
150
+ NS,
151
+ DeliveryArea,
152
+ DeliveryArtifacts
153
+ >;
154
+
155
+ load(): Promise<
156
+ I18nScopeMulti<
157
+ SchemaForNamespaces<Schema, NsList>,
158
+ ParamsForNamespaces<Schema, Params, NsList>,
159
+ ActiveLocales
160
+ >
161
+ >;
162
+ }
163
+
164
+ export interface I18nBuilderMultiForLocale<
165
+ Schema extends MultiDictionary,
166
+ Params extends MultiParams<Schema>,
167
+ RequestLocales extends string,
168
+ ActiveLocales extends RequestLocales,
169
+ NsList extends readonly (keyof Schema & string)[],
170
+ Locale extends ActiveLocales,
171
+ > {
172
+ load(): Promise<
173
+ I18nScopeMultiForLocale<
174
+ SchemaForNamespaces<Schema, NsList>,
175
+ ParamsForNamespaces<Schema, Params, NsList>,
176
+ Locale,
177
+ Locale
178
+ >
179
+ >;
180
+ }
181
+
182
+ export class I18nBuilderMultiInitialImpl<
183
+ Schema extends MultiDictionary,
184
+ Params extends MultiParams<Schema>,
185
+ RequestLocales extends string = LocaleOfMulti<Schema>,
186
+ ActiveLocales extends RequestLocales = RequestLocales,
187
+ DeliveryArea extends string = never,
188
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
189
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
190
+ > implements I18nBuilderMultiInitial<
191
+ Schema,
192
+ Params,
193
+ RequestLocales,
194
+ ActiveLocales,
195
+ DeliveryArea,
196
+ DeliveryArtifacts
197
+ > {
198
+ constructor(
199
+ private readonly engine: I18nEngineMultiImpl<Schema, Params, RequestLocales>,
200
+ private readonly options?: I18nBuilderMultiOptions<Schema, RequestLocales>
201
+ ) {}
202
+
203
+ withNamespaces<const NS extends readonly (keyof Schema & string)[]>(
204
+ namespaces: NonEmptyNamespaces<NS>
205
+ ): I18nBuilderMultiReadyImpl<
206
+ Schema,
207
+ Params,
208
+ RequestLocales,
209
+ ActiveLocales,
210
+ NS,
211
+ DeliveryArea,
212
+ DeliveryArtifacts
213
+ > {
214
+ return new I18nBuilderMultiReadyImpl<
215
+ Schema,
216
+ Params,
217
+ RequestLocales,
218
+ ActiveLocales,
219
+ NS,
220
+ DeliveryArea,
221
+ DeliveryArtifacts
222
+ >(this.engine, this.options, { namespaces });
223
+ }
224
+ }
225
+
226
+ export class I18nBuilderMultiReadyImpl<
227
+ Schema extends MultiDictionary,
228
+ Params extends MultiParams<Schema>,
229
+ RequestLocales extends string = LocaleOfMulti<Schema>,
230
+ ActiveLocales extends RequestLocales = RequestLocales,
231
+ NsList extends readonly (keyof Schema & string)[] = readonly (keyof Schema & string)[],
232
+ DeliveryArea extends string = never,
233
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
234
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
235
+ > implements I18nBuilderMultiReady<
236
+ Schema,
237
+ Params,
238
+ RequestLocales,
239
+ ActiveLocales,
240
+ NsList,
241
+ DeliveryArea,
242
+ DeliveryArtifacts
243
+ > {
244
+ constructor(
245
+ private readonly engine: I18nEngineMultiImpl<Schema, Params, RequestLocales>,
246
+ private readonly options: I18nBuilderMultiOptions<Schema, RequestLocales> | undefined,
247
+ private readonly state: MultiBuilderState<Schema, RequestLocales, NsList>
248
+ ) {}
249
+
250
+ private clone(): MultiBuilderState<Schema, RequestLocales, NsList> {
251
+ return {
252
+ namespaces: this.state.namespaces,
253
+ ...(this.state.locale !== undefined ? { locale: this.state.locale } : {}),
254
+ ...(this.state.deliveryArea !== undefined ? { deliveryArea: this.state.deliveryArea } : {}),
255
+ };
256
+ }
257
+
258
+ withNamespaces<const NS extends readonly (keyof Schema & string)[]>(
259
+ namespaces: NonEmptyNamespaces<NS>
260
+ ): I18nBuilderMultiReadyImpl<
261
+ Schema,
262
+ Params,
263
+ RequestLocales,
264
+ ActiveLocales,
265
+ NS,
266
+ DeliveryArea,
267
+ DeliveryArtifacts
268
+ > {
269
+ return new I18nBuilderMultiReadyImpl<
270
+ Schema,
271
+ Params,
272
+ RequestLocales,
273
+ ActiveLocales,
274
+ NS,
275
+ DeliveryArea,
276
+ DeliveryArtifacts
277
+ >(this.engine, this.options, {
278
+ ...this.clone(),
279
+ namespaces,
280
+ });
281
+ }
282
+
283
+ withLocale<Locale extends ActiveLocales>(
284
+ locale: Locale
285
+ ): I18nBuilderMultiForLocaleImpl<Schema, Params, RequestLocales, ActiveLocales, NsList, Locale> {
286
+ const { deliveryArea: _deliveryArea, ...rest } = this.clone();
287
+ return new I18nBuilderMultiForLocaleImpl<
288
+ Schema,
289
+ Params,
290
+ RequestLocales,
291
+ ActiveLocales,
292
+ NsList,
293
+ Locale
294
+ >(this.engine, this.options, { ...rest, locale });
295
+ }
296
+
297
+ withDeliveryArea<Area extends DeliveryArea>(
298
+ area: Area
299
+ ): I18nBuilderMultiPartitionedImpl<
300
+ Schema,
301
+ Params,
302
+ RequestLocales,
303
+ LocalesForDeliveryAreaOrAll<RequestLocales, DeliveryArea, DeliveryArtifacts, Area>,
304
+ NsList,
305
+ DeliveryArea,
306
+ DeliveryArtifacts
307
+ > {
308
+ const { locale: _locale, ...rest } = this.clone();
309
+ return new I18nBuilderMultiPartitionedImpl<
310
+ Schema,
311
+ Params,
312
+ RequestLocales,
313
+ LocalesForDeliveryAreaOrAll<RequestLocales, DeliveryArea, DeliveryArtifacts, Area>,
314
+ NsList,
315
+ DeliveryArea,
316
+ DeliveryArtifacts
317
+ >(this.engine, this.options, { ...rest, deliveryArea: area });
318
+ }
319
+
320
+ async load(): Promise<
321
+ I18nScopeMulti<
322
+ SchemaForNamespaces<Schema, NsList>,
323
+ ParamsForNamespaces<Schema, Params, NsList>,
324
+ ActiveLocales
325
+ >
326
+ > {
327
+ await applyMultiBuilderLoad(this.engine, this.options, this.state);
328
+ return this.engine.toScope({ namespaces: this.state.namespaces });
329
+ }
330
+ }
331
+
332
+ /** @deprecated Use {@link I18nBuilderMultiInitialImpl} or {@link I18nBuilderMultiReadyImpl}. */
333
+ export class I18nBuilderMultiImpl<
334
+ Schema extends MultiDictionary,
335
+ Params extends MultiParams<Schema>,
336
+ RequestLocales extends string = LocaleOfMulti<Schema>,
337
+ ActiveLocales extends RequestLocales = RequestLocales,
338
+ NsList extends readonly (keyof Schema & string)[] = readonly (keyof Schema & string)[],
339
+ DeliveryArea extends string = never,
340
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
341
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
342
+ > extends I18nBuilderMultiReadyImpl<
343
+ Schema,
344
+ Params,
345
+ RequestLocales,
346
+ ActiveLocales,
347
+ NsList,
348
+ DeliveryArea,
349
+ DeliveryArtifacts
350
+ > {}
351
+
352
+ export class I18nBuilderMultiPartitionedImpl<
353
+ Schema extends MultiDictionary,
354
+ Params extends MultiParams<Schema>,
355
+ RequestLocales extends string,
356
+ ActiveLocales extends RequestLocales,
357
+ NsList extends readonly (keyof Schema & string)[],
358
+ DeliveryArea extends string = never,
359
+ DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea> =
360
+ DeliveryArtifactsMap<RequestLocales, DeliveryArea>,
361
+ > implements I18nBuilderMultiPartitioned<
362
+ Schema,
363
+ Params,
364
+ RequestLocales,
365
+ ActiveLocales,
366
+ NsList,
367
+ DeliveryArea,
368
+ DeliveryArtifacts
369
+ > {
370
+ constructor(
371
+ private readonly engine: I18nEngineMultiImpl<Schema, Params, RequestLocales>,
372
+ private readonly options: I18nBuilderMultiOptions<Schema, RequestLocales> | undefined,
373
+ private readonly state: MultiBuilderState<Schema, RequestLocales, NsList> & {
374
+ deliveryArea: string;
375
+ }
376
+ ) {}
377
+
378
+ withNamespaces<const NS extends readonly (keyof Schema & string)[]>(
379
+ namespaces: NonEmptyNamespaces<NS>
380
+ ): I18nBuilderMultiPartitionedImpl<
381
+ Schema,
382
+ Params,
383
+ RequestLocales,
384
+ ActiveLocales,
385
+ NS,
386
+ DeliveryArea,
387
+ DeliveryArtifacts
388
+ > {
389
+ return new I18nBuilderMultiPartitionedImpl<
390
+ Schema,
391
+ Params,
392
+ RequestLocales,
393
+ ActiveLocales,
394
+ NS,
395
+ DeliveryArea,
396
+ DeliveryArtifacts
397
+ >(this.engine, this.options, {
398
+ ...this.state,
399
+ namespaces,
400
+ });
401
+ }
402
+
403
+ async load(): Promise<
404
+ I18nScopeMulti<
405
+ SchemaForNamespaces<Schema, NsList>,
406
+ ParamsForNamespaces<Schema, Params, NsList>,
407
+ ActiveLocales
408
+ >
409
+ > {
410
+ await applyMultiBuilderLoad(this.engine, this.options, this.state);
411
+ return this.engine.toScope({ namespaces: this.state.namespaces });
412
+ }
413
+ }
414
+
415
+ export class I18nBuilderMultiForLocaleImpl<
416
+ Schema extends MultiDictionary,
417
+ Params extends MultiParams<Schema>,
418
+ RequestLocales extends string,
419
+ ActiveLocales extends RequestLocales,
420
+ NsList extends readonly (keyof Schema & string)[],
421
+ Locale extends ActiveLocales,
422
+ > implements I18nBuilderMultiForLocale<
423
+ Schema,
424
+ Params,
425
+ RequestLocales,
426
+ ActiveLocales,
427
+ NsList,
428
+ Locale
429
+ > {
430
+ constructor(
431
+ private readonly engine: I18nEngineMultiImpl<Schema, Params, RequestLocales>,
432
+ private readonly options: I18nBuilderMultiOptions<Schema, RequestLocales> | undefined,
433
+ private readonly state: MultiBuilderState<Schema, RequestLocales, NsList> & { locale: Locale }
434
+ ) {}
435
+
436
+ async load(): Promise<
437
+ I18nScopeMultiForLocale<
438
+ SchemaForNamespaces<Schema, NsList>,
439
+ ParamsForNamespaces<Schema, Params, NsList>,
440
+ Locale,
441
+ Locale
442
+ >
443
+ > {
444
+ await applyMultiBuilderLoad(this.engine, this.options, this.state);
445
+ return this.engine.toScope({ namespaces: this.state.namespaces }).forLocale(this.state.locale);
446
+ }
447
+ }
448
+
449
+ async function applyMultiBuilderLoad<
450
+ Schema extends MultiDictionary,
451
+ Params extends MultiParams<Schema>,
452
+ RequestLocales extends string,
453
+ NsList extends readonly (keyof Schema & string)[],
454
+ >(
455
+ engine: I18nEngineMultiImpl<Schema, Params, RequestLocales>,
456
+ options: I18nBuilderMultiOptions<Schema, RequestLocales> | undefined,
457
+ state: MultiBuilderState<Schema, RequestLocales, NsList>
458
+ ): Promise<void> {
459
+ if (state.namespaces.length === 0) {
460
+ throw new Error("[i18n] withNamespaces(...) is required before load().");
461
+ }
462
+
463
+ const partition = state.locale ?? state.deliveryArea;
464
+
465
+ await Promise.all(
466
+ state.namespaces.map(async (namespace) => {
467
+ const loader = options?.namespaceLoaders?.[namespace];
468
+ if (loader === undefined) {
469
+ return;
470
+ }
471
+
472
+ if (engine.hasBuilderResourceLoaded(namespace, partition)) {
473
+ return;
474
+ }
475
+
476
+ const data = await invokeNamespaceLoader(loader, partition);
477
+ engine.applyLoadMergeNamespace(namespace, data);
478
+ engine.markBuilderResourceLoaded(namespace, partition);
479
+ })
480
+ );
481
+ }