@xndrjs/i18n 0.3.3 → 0.5.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.
Files changed (61) hide show
  1. package/README.md +193 -44
  2. package/dist/codegen/index.d.ts +85 -0
  3. package/dist/codegen/index.js +181 -0
  4. package/dist/codegen/index.js.map +1 -0
  5. package/dist/index.d.ts +34 -18
  6. package/dist/index.js +174 -145
  7. package/dist/index.js.map +1 -1
  8. package/dist/validation/index.d.ts +86 -4
  9. package/dist/validation/index.js.map +1 -1
  10. package/package.json +6 -1
  11. package/src/IcuTranslationProviderMulti.test.ts +48 -1
  12. package/src/IcuTranslationProviderMulti.ts +41 -61
  13. package/src/IcuTranslationProviderSingle.test.ts +67 -0
  14. package/src/IcuTranslationProviderSingle.ts +27 -51
  15. package/src/audit/audit-dictionaries.ts +4 -1
  16. package/src/audit/run-audit.test.ts +35 -1
  17. package/src/audit/run-audit.ts +15 -7
  18. package/src/codegen/codegen-config-schema.ts +68 -1
  19. package/src/codegen/config.test.ts +174 -39
  20. package/src/codegen/config.ts +14 -5
  21. package/src/codegen/constants.ts +8 -0
  22. package/src/codegen/delivery-artifacts.test.ts +142 -0
  23. package/src/codegen/delivery-artifacts.ts +124 -0
  24. package/src/codegen/emit/dictionary-file.test.ts +190 -0
  25. package/src/codegen/emit/dictionary-file.ts +165 -2
  26. package/src/codegen/emit/dictionary-schema-file.ts +5 -0
  27. package/src/codegen/emit/instance-file.ts +119 -38
  28. package/src/codegen/emit/namespace-loaders-file.test.ts +176 -0
  29. package/src/codegen/emit/namespace-loaders-file.ts +118 -32
  30. package/src/codegen/emit/types-file.test.ts +114 -0
  31. package/src/codegen/emit/types-file.ts +48 -11
  32. package/src/codegen/generate-i18n-types.test.ts +765 -22
  33. package/src/codegen/generate-i18n-types.ts +111 -58
  34. package/src/codegen/icu-analysis.ts +9 -2
  35. package/src/codegen/locale-fallback.ts +19 -10
  36. package/src/codegen/locale-policy.ts +4 -0
  37. package/src/codegen/paths.ts +9 -5
  38. package/src/codegen/project-locales-set-namespace.test.ts +11 -9
  39. package/src/codegen/read-dictionary.test.ts +474 -2
  40. package/src/codegen/read-dictionary.ts +164 -15
  41. package/src/codegen/write-file-if-changed.test.ts +42 -0
  42. package/src/codegen/write-file-if-changed.ts +20 -0
  43. package/src/codegen-config/build-config.ts +34 -0
  44. package/src/codegen-config/codegen-config.test.ts +32 -0
  45. package/src/codegen-config/index.ts +21 -0
  46. package/src/codegen-config/write-config.ts +8 -0
  47. package/src/deep-freeze.test.ts +13 -0
  48. package/src/deep-freeze.ts +5 -0
  49. package/src/format-core.ts +91 -0
  50. package/src/index.ts +8 -5
  51. package/src/project-locales.test.ts +129 -10
  52. package/src/project-locales.ts +90 -3
  53. package/src/setup/setup-i18n.test.ts +1 -1
  54. package/src/setup/setup-i18n.ts +9 -32
  55. package/src/types.ts +19 -4
  56. package/src/validation/normalize.ts +4 -0
  57. package/dist/types-C1CpXVOJ.d.ts +0 -83
  58. package/src/ensure-namespace.integration.test.ts +0 -66
  59. package/src/ensure-namespace.test.ts +0 -125
  60. package/src/ensure-namespace.ts +0 -70
  61. /package/src/{setup → codegen-config}/type-names.ts +0 -0
@@ -1,6 +1,11 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { loadConfig, resolveLoadOnInit, resolveNamespaces } from "./config.js";
3
+ import {
4
+ loadConfig,
5
+ resolveDeliveryOutputDir,
6
+ resolveLoadOnInit,
7
+ resolveNamespaces,
8
+ } from "./config.js";
4
9
  import { formatDictionaryFile } from "./emit/dictionary-file.js";
5
10
  import {
6
11
  formatDictionarySchemaFile,
@@ -10,11 +15,18 @@ import { formatInstanceFile } from "./emit/instance-file.js";
10
15
  import { formatNamespaceLoadersFile } from "./emit/namespace-loaders-file.js";
11
16
  import { formatTypesFile } from "./emit/types-file.js";
12
17
  import { analyzeDictionaries } from "./icu-analysis.js";
13
- import { collectRequestLocales, validateCodegenLocaleFallback } from "./locale-fallback.js";
18
+ import { getDeliveryArtifactsIssues } from "./delivery-artifacts.js";
19
+ import { collectRequestLocales, getCodegenLocaleFallbackIssues } from "./locale-fallback.js";
14
20
  import { enrichLocaleFallback } from "./locale-policy.js";
15
- import { fail, resolveImportExtension, toModuleBasename } from "./paths.js";
21
+ import { reportCodegenIssues, resolveImportExtension, toModuleBasename } from "./paths.js";
16
22
  import { prepareDictionaryEntries } from "./read-dictionary.js";
23
+ import { writeFileIfChanged } from "./write-file-if-changed.js";
17
24
 
25
+ /**
26
+ * Codegen CLI entry point. Pipeline:
27
+ * config → analyze (ICU + types input) → validate locale/delivery policy →
28
+ * prepare artifacts (YAML→JSON, split) → resolve lazy/eager → emit generated .ts files.
29
+ */
18
30
  function main() {
19
31
  const configArgIndex = process.argv.indexOf("--config");
20
32
  const configPath = path.resolve(
@@ -24,47 +36,29 @@ function main() {
24
36
  const projectRoot = path.dirname(configPath);
25
37
 
26
38
  if (!fs.existsSync(configPath)) {
27
- fail(`[Codegen Error] Config file not found: ${configPath}`);
39
+ throw new Error(`[Codegen Error] Config file not found: ${configPath}`);
28
40
  }
29
41
 
30
42
  const config = loadConfig(configPath);
31
43
  const sourceEntries = resolveNamespaces(config);
32
- const generatedDirRelative = path.dirname(config.typesOutput);
33
- let resolvedEntries;
34
- let compiledFiles: string[];
35
-
36
- try {
37
- const prepared = prepareDictionaryEntries(projectRoot, sourceEntries, generatedDirRelative);
38
- resolvedEntries = prepared.resolvedEntries;
39
- compiledFiles = prepared.compiledFiles;
40
- } catch (error) {
41
- const message = error instanceof Error ? error.message : String(error);
42
- fail(message);
43
- }
44
-
45
- const entries = resolvedEntries;
44
+ const deliveryOutputRelative = resolveDeliveryOutputDir(config);
45
+ const delivery = config.delivery ?? "canonical";
46
46
  const isSingle = Boolean(config.dictionary);
47
- const { loadOnInitSet, lazyEntries, hasLazy } = resolveLoadOnInit(config, entries, isSingle);
48
-
49
- if (hasLazy && !config.dictionarySchemaOutput) {
50
- fail(
51
- '[Codegen Error] Lazy namespaces require "dictionarySchemaOutput" for validateExternalNamespace.'
52
- );
53
- }
54
-
55
- const typesOutputPath = path.resolve(projectRoot, config.typesOutput);
56
- const dictionaryOutputPath = path.resolve(projectRoot, config.dictionaryOutput);
57
- const instanceOutputPath = path.resolve(projectRoot, config.instanceOutput);
58
47
 
59
- const analysisResult = analyzeDictionaries(projectRoot, entries);
48
+ const analysisResult = analyzeDictionaries(projectRoot, sourceEntries);
60
49
  if (!analysisResult.ok) {
61
50
  process.exit(1);
62
51
  }
63
52
 
64
- const { paramsByNamespace, argsSpecByNamespace, locales } = analysisResult.analysis;
53
+ const { paramsByNamespace, argsSpecByNamespace, locales, dictionariesByNamespace } =
54
+ analysisResult.analysis;
65
55
 
66
- if (config.localeFallback && validateCodegenLocaleFallback(config.localeFallback, locales)) {
67
- process.exit(1);
56
+ if (config.localeFallback) {
57
+ const localeFallbackIssues = getCodegenLocaleFallbackIssues(config.localeFallback, locales);
58
+ if (localeFallbackIssues.length > 0) {
59
+ reportCodegenIssues(localeFallbackIssues);
60
+ process.exit(1);
61
+ }
68
62
  }
69
63
 
70
64
  const paramsTypeName = config.paramsTypeName;
@@ -74,14 +68,55 @@ function main() {
74
68
  const localeFallbackTypeName = `${localeTypeName}Fallback`;
75
69
  const factoryName = config.factoryName ?? "createI18n";
76
70
  const importExtension = resolveImportExtension(config);
77
- const typesModule = toModuleBasename(typesOutputPath);
78
71
 
79
72
  const requestLocales = collectRequestLocales(locales, config.localeFallback);
80
- const requestLocaleUnion = [...requestLocales]
73
+ const requestLocalesList = [...requestLocales].sort();
74
+
75
+ if (delivery === "custom" && config.deliveryArtifacts) {
76
+ const deliveryArtifactsIssues = getDeliveryArtifactsIssues(
77
+ config.deliveryArtifacts,
78
+ requestLocales
79
+ );
80
+ if (deliveryArtifactsIssues.length > 0) {
81
+ reportCodegenIssues(deliveryArtifactsIssues);
82
+ process.exit(1);
83
+ }
84
+ }
85
+
86
+ const {
87
+ resolvedEntries: entries,
88
+ splitPathsByNamespace,
89
+ compiledFiles,
90
+ } = prepareDictionaryEntries(projectRoot, sourceEntries, deliveryOutputRelative, {
91
+ dictionariesByNamespace,
92
+ delivery,
93
+ localeFallback: config.localeFallback,
94
+ requestLocales: delivery === "split-by-locale" ? requestLocalesList : undefined,
95
+ deliveryArtifacts: delivery === "custom" ? config.deliveryArtifacts : undefined,
96
+ });
97
+
98
+ const { loadOnInitSet, lazyEntries, hasLazy } = resolveLoadOnInit(config, entries, isSingle);
99
+
100
+ const typesOutputPath = path.resolve(projectRoot, config.typesOutput);
101
+ const dictionaryOutputPath = path.resolve(projectRoot, config.dictionaryOutput);
102
+ const instanceOutputPath = path.resolve(projectRoot, config.instanceOutput);
103
+ const typesModule = toModuleBasename(typesOutputPath);
104
+
105
+ const requestLocaleUnion = requestLocalesList
81
106
  .sort()
82
107
  .map((locale) => `'${locale}'`)
83
108
  .join(" | ");
84
109
 
110
+ const deliveryAreaNames =
111
+ delivery === "custom" && config.deliveryArtifacts
112
+ ? Object.keys(config.deliveryArtifacts).sort()
113
+ : undefined;
114
+ const deliveryAreaTypeName =
115
+ delivery === "custom" ? schemaTypeName.replace(/Schema$/, "DeliveryArea") : undefined;
116
+ const deliveryAreaUnion = deliveryAreaNames
117
+ ? deliveryAreaNames.map((area) => `'${area}'`).join(" | ")
118
+ : undefined;
119
+
85
120
  const eagerEntries = hasLazy
86
121
  ? entries.filter((entry) => loadOnInitSet.has(entry.namespace))
87
122
  : entries;
@@ -103,6 +138,15 @@ function main() {
103
138
  localeFallback: localeFallbackForEmit,
104
139
  paramsByNamespace,
105
140
  requestLocaleUnion,
141
+ ...(deliveryAreaTypeName && deliveryAreaUnion
142
+ ? {
143
+ deliveryAreaTypeName,
144
+ deliveryAreaUnion,
145
+ ...(delivery === "custom" && config.deliveryArtifacts
146
+ ? { deliveryArtifacts: config.deliveryArtifacts }
147
+ : {}),
148
+ }
149
+ : {}),
106
150
  hasLazy,
107
151
  loadOnInitSet,
108
152
  lazyEntries,
@@ -117,14 +161,20 @@ function main() {
117
161
  dictionaryOutputPath,
118
162
  typesOutputPath,
119
163
  schemaTypeName,
164
+ localeTypeName,
120
165
  importExtension,
166
+ delivery,
167
+ splitPathsByNamespace,
168
+ ...(delivery === "split-by-locale" ? { requestLocales: requestLocalesList } : {}),
169
+ ...(delivery === "custom" && deliveryAreaTypeName && deliveryAreaNames
170
+ ? { deliveryAreaTypeName, deliveryAreaNames }
171
+ : {}),
121
172
  });
122
173
 
123
174
  const instanceContent = formatInstanceFile({
124
175
  isSingle,
125
176
  hasLazy,
126
177
  typesOutputPath,
127
- dictionaryOutputPath,
128
178
  paramsTypeName,
129
179
  schemaTypeName,
130
180
  localeTypeName,
@@ -134,16 +184,12 @@ function main() {
134
184
  hasLocaleType: Boolean(requestLocaleUnion),
135
185
  namespaceNames: entries.map((entry) => entry.namespace),
136
186
  importExtension,
187
+ delivery,
137
188
  });
138
189
 
139
- fs.mkdirSync(path.dirname(typesOutputPath), { recursive: true });
140
- fs.writeFileSync(typesOutputPath, typesContent);
141
-
142
- fs.mkdirSync(path.dirname(dictionaryOutputPath), { recursive: true });
143
- fs.writeFileSync(dictionaryOutputPath, dictionaryContent);
144
-
145
- fs.mkdirSync(path.dirname(instanceOutputPath), { recursive: true });
146
- fs.writeFileSync(instanceOutputPath, instanceContent);
190
+ writeFileIfChanged(typesOutputPath, typesContent);
191
+ writeFileIfChanged(dictionaryOutputPath, dictionaryContent);
192
+ writeFileIfChanged(instanceOutputPath, instanceContent);
147
193
 
148
194
  const generatedFiles = [
149
195
  path.relative(projectRoot, typesOutputPath),
@@ -162,8 +208,7 @@ function main() {
162
208
  importExtension
163
209
  );
164
210
 
165
- fs.mkdirSync(path.dirname(dictionarySchemaOutputPath), { recursive: true });
166
- fs.writeFileSync(dictionarySchemaOutputPath, dictionarySchemaContent);
211
+ writeFileIfChanged(dictionarySchemaOutputPath, dictionarySchemaContent);
167
212
  generatedFiles.push(path.relative(projectRoot, dictionarySchemaOutputPath));
168
213
  }
169
214
 
@@ -173,24 +218,27 @@ function main() {
173
218
  config.namespaceLoadersOutput ??
174
219
  path.join(path.dirname(config.instanceOutput), "namespace-loaders.generated.ts")
175
220
  );
176
- const dictionarySchemaOutputPath = path.resolve(projectRoot, config.dictionarySchemaOutput!);
177
221
  const lazyEntriesWithPaths = lazyEntries.map((entry) => ({
178
222
  ...entry,
179
223
  absolutePath: path.resolve(projectRoot, entry.filePath),
180
224
  }));
181
- const namespaceLoadersContent = formatNamespaceLoadersFile(
182
- namespaceLoadersOutputPath,
183
- lazyEntriesWithPaths,
225
+ const namespaceLoadersContent = formatNamespaceLoadersFile({
226
+ loadersOutputPath: namespaceLoadersOutputPath,
227
+ lazyEntries: lazyEntriesWithPaths,
184
228
  schemaTypeName,
229
+ localeTypeName,
185
230
  typesModule,
186
- toModuleBasename(dictionarySchemaOutputPath),
187
- toModuleBasename(instanceOutputPath),
188
- factoryName,
189
- importExtension
190
- );
231
+ importExtension,
232
+ projectRoot,
233
+ delivery,
234
+ splitPathsByNamespace,
235
+ ...(delivery === "split-by-locale" ? { requestLocales: requestLocalesList } : {}),
236
+ ...(delivery === "custom" && deliveryAreaTypeName && deliveryAreaNames
237
+ ? { deliveryAreaTypeName, deliveryAreaNames }
238
+ : {}),
239
+ });
191
240
 
192
- fs.mkdirSync(path.dirname(namespaceLoadersOutputPath), { recursive: true });
193
- fs.writeFileSync(namespaceLoadersOutputPath, namespaceLoadersContent);
241
+ writeFileIfChanged(namespaceLoadersOutputPath, namespaceLoadersContent);
194
242
  generatedFiles.push(path.relative(projectRoot, namespaceLoadersOutputPath));
195
243
  }
196
244
 
@@ -200,4 +248,9 @@ function main() {
200
248
  }
201
249
  }
202
250
 
203
- main();
251
+ try {
252
+ main();
253
+ } catch (error) {
254
+ console.error(error instanceof Error ? error.message : String(error));
255
+ process.exit(1);
256
+ }
@@ -6,7 +6,7 @@ import {
6
6
  mergeVariableMetaAcrossLocales,
7
7
  type VariableSpec,
8
8
  } from "../icu/extract-variables.js";
9
- import type { NamespaceEntry } from "./types.js";
9
+ import type { DictionaryJson, NamespaceEntry } from "./types.js";
10
10
  import { readDictionaryFile } from "./read-dictionary.js";
11
11
 
12
12
  export function paramsTypeForVariables(variables: VariableSpec): string {
@@ -26,8 +26,13 @@ export interface DictionaryAnalysis {
26
26
  paramsByNamespace: Record<string, Record<string, string>>;
27
27
  argsSpecByNamespace: Record<string, Record<string, VariableSpec>>;
28
28
  locales: Set<string>;
29
+ dictionariesByNamespace: Record<string, DictionaryJson>;
29
30
  }
30
31
 
32
+ /**
33
+ * Phase 1 of codegen: read source dictionaries, validate ICU syntax, infer param types.
34
+ * Output feeds type emission, optional `DICTIONARY_SPEC`, and `prepareDictionaryEntries`.
35
+ */
31
36
  export function analyzeDictionaries(
32
37
  projectRoot: string,
33
38
  entries: NamespaceEntry[]
@@ -35,6 +40,7 @@ export function analyzeDictionaries(
35
40
  const paramsByNamespace: Record<string, Record<string, string>> = {};
36
41
  const argsSpecByNamespace: Record<string, Record<string, VariableSpec>> = {};
37
42
  const locales = new Set<string>();
43
+ const dictionariesByNamespace: Record<string, DictionaryJson> = {};
38
44
  let hasErrors = false;
39
45
 
40
46
  for (const entry of entries) {
@@ -49,6 +55,7 @@ export function analyzeDictionaries(
49
55
  }
50
56
 
51
57
  const dictionary = readDictionaryFile(absolutePath);
58
+ dictionariesByNamespace[entry.namespace] = dictionary;
52
59
  paramsByNamespace[entry.namespace] = {};
53
60
  argsSpecByNamespace[entry.namespace] = {};
54
61
 
@@ -98,6 +105,6 @@ export function analyzeDictionaries(
98
105
 
99
106
  return {
100
107
  ok: true,
101
- analysis: { paramsByNamespace, argsSpecByNamespace, locales },
108
+ analysis: { paramsByNamespace, argsSpecByNamespace, locales, dictionariesByNamespace },
102
109
  };
103
110
  }
@@ -1,5 +1,9 @@
1
1
  import { validateLocaleFallback } from "../resolve-locale.js";
2
2
 
3
+ /**
4
+ * Union of locales found in dictionaries plus every locale referenced by `localeFallback`.
5
+ * Drives split-by-locale partitioning and the generated locale union type.
6
+ */
3
7
  export function collectRequestLocales(
4
8
  dictionaryLocales: Set<string>,
5
9
  fallback?: Record<string, string | null>
@@ -19,30 +23,35 @@ export function collectRequestLocales(
19
23
  return all;
20
24
  }
21
25
 
22
- export function validateCodegenLocaleFallback(
26
+ export type LocaleFallbackIssue = {
27
+ path: (string | number)[];
28
+ message: string;
29
+ };
30
+
31
+ /** Pre-emit validation gate for `localeFallback` config against discovered dictionary locales. */
32
+ export function getCodegenLocaleFallbackIssues(
23
33
  fallback: Record<string, string | null>,
24
34
  dictionaryLocales: Set<string>
25
- ): boolean {
26
- let hasErrors = false;
35
+ ): LocaleFallbackIssue[] {
36
+ const issues: LocaleFallbackIssue[] = [];
27
37
 
28
38
  try {
29
39
  validateLocaleFallback(fallback);
30
40
  } catch (error) {
31
41
  const message = error instanceof Error ? error.message : String(error);
32
- console.error(`[Codegen Error] ${message}`);
33
- hasErrors = true;
42
+ issues.push({ path: ["localeFallback"], message });
34
43
  }
35
44
 
36
45
  for (const [locale, target] of Object.entries(fallback)) {
37
46
  if (target !== null && !(target in fallback) && !dictionaryLocales.has(target)) {
38
- console.error(
39
- `[Codegen Error] localeFallback: "${locale}" points to "${target}" which is not defined in the fallback map or dictionary locales`
40
- );
41
- hasErrors = true;
47
+ issues.push({
48
+ path: ["localeFallback", locale],
49
+ message: `localeFallback: "${locale}" points to "${target}" which is not defined in the fallback map or dictionary locales`,
50
+ });
42
51
  }
43
52
  }
44
53
 
45
- return hasErrors;
54
+ return issues;
46
55
  }
47
56
 
48
57
  export function formatLocaleFallbackBlock(
@@ -7,6 +7,10 @@ export function buildRequiredLocales(
7
7
  return [...collectRequestLocales(dictionaryLocales, configFallback)].sort();
8
8
  }
9
9
 
10
+ /**
11
+ * Completes `localeFallback` with explicit `null` entries for every request locale,
12
+ * producing a stable map emitted into `i18n-types.generated.ts`.
13
+ */
10
14
  export function enrichLocaleFallback(
11
15
  dictionaryLocales: Set<string>,
12
16
  configFallback: Record<string, string | null>
@@ -6,10 +6,10 @@ import { CodegenConfig } from "./codegen-config-schema.js";
6
6
  export const GENERATED_FILE_BANNER = "// Automatically generated code. Do not edit manually.\n";
7
7
  export const DEFAULT_IMPORT_EXTENSION: ImportExtension = "none";
8
8
 
9
- export function fail(message: string): never {
10
- console.error(message);
11
- process.exit(1);
12
- throw new Error(message);
9
+ export function reportCodegenIssues(issues: readonly { message: string }[]): void {
10
+ for (const issue of issues) {
11
+ console.error(`[Codegen Error] ${issue.message}`);
12
+ }
13
13
  }
14
14
 
15
15
  export function toImportPath(fromFile: string, toFile: string): string {
@@ -27,7 +27,7 @@ export function resolveImportExtension(
27
27
  ): ImportExtension {
28
28
  const extension = config.importExtension ?? DEFAULT_IMPORT_EXTENSION;
29
29
  if (!SUPPORTED_IMPORT_EXTENSIONS.includes(extension)) {
30
- fail(
30
+ throw new Error(
31
31
  `[Codegen Error] importExtension must be "none", ".ts", or ".js", got ${JSON.stringify(extension)}.`
32
32
  );
33
33
  }
@@ -52,3 +52,7 @@ export function toImportIdentifier(namespace: string): string {
52
52
  }
53
53
  return `${safe}Ns`;
54
54
  }
55
+
56
+ export function toLocaleObjectKey(locale: string): string {
57
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(locale) ? locale : JSON.stringify(locale);
58
+ }
@@ -95,7 +95,7 @@ function writeTscProject(tempDir: string) {
95
95
  );
96
96
  }
97
97
 
98
- describe("codegen projectLocales + setNamespace", () => {
98
+ describe("codegen projectDictionaryLocales + setNamespace", () => {
99
99
  describe("generated instance output", () => {
100
100
  let tempDir: string;
101
101
 
@@ -112,7 +112,7 @@ describe("codegen projectLocales + setNamespace", () => {
112
112
  const factory = readFileSync(join(tempDir, "src/i18n/instance.generated.ts"), "utf8");
113
113
  expect(factory).toContain('dictionary: AppSchema["billing"]');
114
114
  expect(factory).toContain(
115
- "projectNamespacesLocalesCore(dictionary, locales, LOCALE_FALLBACK)"
115
+ "projectDictionaryLocalesCore(dictionary, locales, LOCALE_FALLBACK)"
116
116
  );
117
117
  });
118
118
 
@@ -124,9 +124,10 @@ describe("codegen projectLocales + setNamespace", () => {
124
124
  join(tempDir, "src/hydrate-billing.ts"),
125
125
  [
126
126
  `import { createI18n, projectNamespaceLocales } from "./i18n/instance.generated.js";`,
127
+ `import { defaultDictionary } from "./i18n/dictionary.generated.js";`,
127
128
  `import billingDictionary from "./i18n/translations/billing.json";`,
128
129
  ``,
129
- `const i18n = createI18n();`,
130
+ `const i18n = createI18n(defaultDictionary);`,
130
131
  `i18n.setNamespace("billing", projectNamespaceLocales(billingDictionary, ["en"]));`,
131
132
  ].join("\n")
132
133
  );
@@ -137,25 +138,26 @@ describe("codegen projectLocales + setNamespace", () => {
137
138
  expect(tsc.status, `${tsc.stdout}\n${tsc.stderr}`).toBe(0);
138
139
  });
139
140
 
140
- it("passes tsc when replacing the full schema with projectLocales before setAll", () => {
141
+ it("passes tsc when replacing the full schema with projectDictionaryLocales before setAll", () => {
141
142
  tempDir = mkdtempSync(join(tmpdir(), "xndrjs-i18n-project-locales-"));
142
143
  setupMultiCodegenFixture(tempDir);
143
144
 
144
145
  writeFileSync(
145
146
  join(tempDir, "src/hydrate-all.ts"),
146
147
  [
147
- `import { createI18n, projectLocales } from "./i18n/instance.generated.js";`,
148
+ `import { createI18n, projectDictionaryLocales } from "./i18n/instance.generated.js";`,
149
+ `import { defaultDictionary } from "./i18n/dictionary.generated.js";`,
148
150
  `import type { AppSchema } from "./i18n/i18n-types.generated.js";`,
149
151
  `import billingDictionary from "./i18n/translations/billing.json";`,
150
- `import defaultDictionary from "./i18n/translations/default.json";`,
152
+ `import defaultNs from "./i18n/translations/default.json";`,
151
153
  ``,
152
154
  `const fullDictionary = {`,
153
- ` default: defaultDictionary,`,
155
+ ` default: defaultNs,`,
154
156
  ` billing: billingDictionary,`,
155
157
  `} satisfies AppSchema;`,
156
158
  ``,
157
- `const i18n = createI18n();`,
158
- `i18n.setAll(projectLocales(fullDictionary, ["en"]));`,
159
+ `const i18n = createI18n(defaultDictionary);`,
160
+ `i18n.setAll(projectDictionaryLocales(fullDictionary, ["en"]));`,
159
161
  ].join("\n")
160
162
  );
161
163