@xndrjs/i18n 0.8.0 → 0.8.2-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 (94) hide show
  1. package/README.md +1 -2
  2. package/dist/cli/audit.js +739 -0
  3. package/dist/cli/audit.js.map +1 -0
  4. package/dist/cli/codegen.js +1471 -0
  5. package/dist/cli/codegen.js.map +1 -0
  6. package/dist/cli/setup.js +148 -0
  7. package/dist/cli/setup.js.map +1 -0
  8. package/dist/codegen/index.js +43 -40
  9. package/dist/codegen/index.js.map +1 -1
  10. package/dist/index.d.ts +20 -1
  11. package/dist/index.js +56 -10
  12. package/dist/index.js.map +1 -1
  13. package/package.json +6 -9
  14. package/bin/audit.mjs +0 -14
  15. package/bin/codegen.mjs +0 -17
  16. package/bin/setup.mjs +0 -14
  17. package/src/IcuTranslationProviderMulti.test.ts +0 -215
  18. package/src/IcuTranslationProviderMulti.ts +0 -176
  19. package/src/audit/audit-dictionaries.test.ts +0 -134
  20. package/src/audit/audit-dictionaries.ts +0 -178
  21. package/src/audit/run-audit.test.ts +0 -169
  22. package/src/audit/run-audit.ts +0 -94
  23. package/src/builder-load-registry.ts +0 -39
  24. package/src/builder-loaders.ts +0 -15
  25. package/src/builder-types.test.ts +0 -49
  26. package/src/builder-types.ts +0 -24
  27. package/src/builder.test.ts +0 -260
  28. package/src/builder.ts +0 -10
  29. package/src/codegen/codegen-config-schema.ts +0 -156
  30. package/src/codegen/config.test.ts +0 -231
  31. package/src/codegen/config.ts +0 -42
  32. package/src/codegen/constants.ts +0 -9
  33. package/src/codegen/delivery-artifacts.test.ts +0 -167
  34. package/src/codegen/delivery-artifacts.ts +0 -154
  35. package/src/codegen/dictionary-spec-contract.test.ts +0 -28
  36. package/src/codegen/dictionary-spec-contract.ts +0 -138
  37. package/src/codegen/emit/dictionary-schema-file.ts +0 -113
  38. package/src/codegen/emit/instance-file.test.ts +0 -71
  39. package/src/codegen/emit/instance-file.ts +0 -257
  40. package/src/codegen/emit/namespace-loaders-file.test.ts +0 -251
  41. package/src/codegen/emit/namespace-loaders-file.ts +0 -229
  42. package/src/codegen/emit/types-file.test.ts +0 -96
  43. package/src/codegen/emit/types-file.ts +0 -142
  44. package/src/codegen/fixtures/billing-namespace.json +0 -6
  45. package/src/codegen/generate-i18n-types.test.ts +0 -1137
  46. package/src/codegen/generate-i18n-types.ts +0 -20
  47. package/src/codegen/icu-analysis.ts +0 -110
  48. package/src/codegen/locale-fallback.ts +0 -73
  49. package/src/codegen/locale-policy.test.ts +0 -35
  50. package/src/codegen/locale-policy.ts +0 -30
  51. package/src/codegen/paths.ts +0 -44
  52. package/src/codegen/project-locales-set-namespace.test.ts +0 -134
  53. package/src/codegen/read-dictionary.test.ts +0 -594
  54. package/src/codegen/read-dictionary.ts +0 -260
  55. package/src/codegen/regenerate-namespaces.ts +0 -180
  56. package/src/codegen/run-codegen.test.ts +0 -230
  57. package/src/codegen/run-codegen.ts +0 -252
  58. package/src/codegen/types.ts +0 -10
  59. package/src/codegen/write-file-if-changed.test.ts +0 -42
  60. package/src/codegen/write-file-if-changed.ts +0 -20
  61. package/src/codegen-config/build-config.ts +0 -15
  62. package/src/codegen-config/codegen-config.test.ts +0 -36
  63. package/src/codegen-config/index.ts +0 -37
  64. package/src/codegen-config/type-names.ts +0 -20
  65. package/src/codegen-config/write-config.ts +0 -8
  66. package/src/engine.ts +0 -47
  67. package/src/fetch-artifact.ts +0 -16
  68. package/src/format-core.ts +0 -91
  69. package/src/i18n-handle.ts +0 -202
  70. package/src/icu/extract-variables.test.ts +0 -199
  71. package/src/icu/extract-variables.ts +0 -227
  72. package/src/icu/parse-template.ts +0 -31
  73. package/src/index.ts +0 -58
  74. package/src/project-locales.test.ts +0 -280
  75. package/src/project-locales.ts +0 -211
  76. package/src/resolve-locale.test.ts +0 -91
  77. package/src/resolve-locale.ts +0 -88
  78. package/src/scope-multi.ts +0 -105
  79. package/src/scope-types.ts +0 -35
  80. package/src/scope.test.ts +0 -172
  81. package/src/serialized-state.test.ts +0 -114
  82. package/src/serialized-state.ts +0 -28
  83. package/src/setup/setup-i18n.test.ts +0 -85
  84. package/src/setup/setup-i18n.ts +0 -156
  85. package/src/types.ts +0 -65
  86. package/src/validation/create-args-schema.ts +0 -76
  87. package/src/validation/create-normalized-schema.ts +0 -52
  88. package/src/validation/errors.ts +0 -43
  89. package/src/validation/index.ts +0 -202
  90. package/src/validation/normalize.ts +0 -299
  91. package/src/validation/to-dictionary.ts +0 -67
  92. package/src/validation/types.ts +0 -82
  93. package/src/validation/validate-normalized.ts +0 -133
  94. package/src/validation/validation.test.ts +0 -562
@@ -1,156 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { buildCodegenConfig } from "../codegen-config/build-config.js";
5
- import { inferProjectName } from "../codegen-config/type-names.js";
6
- import { writeCodegenConfig } from "../codegen-config/write-config.js";
7
- import { DEFAULT_IMPORT_EXTENSION, importExtensionSuffix } from "../codegen/paths.js";
8
-
9
- export { buildCodegenConfig } from "../codegen-config/build-config.js";
10
-
11
- export interface SetupOptions {
12
- targetDir: string;
13
- project?: string | undefined;
14
- force?: boolean | undefined;
15
- }
16
-
17
- export interface SetupResult {
18
- targetDir: string;
19
- project: string;
20
- created: string[];
21
- }
22
-
23
- const I18N_ROOT = "i18n";
24
- const CONFIG_FILE = `${I18N_ROOT}/i18n.codegen.json`;
25
- /** Paths in i18n.codegen.json are relative to the i18n/ folder */
26
- const GENERATED_DIR = "generated";
27
- const TRANSLATIONS_DIR = "translations";
28
-
29
- const DEFAULT_STARTER = {
30
- welcome: { en: "Welcome {name}!" },
31
- };
32
-
33
- const DEFAULT_IMPORT_SUFFIX = importExtensionSuffix(DEFAULT_IMPORT_EXTENSION);
34
-
35
- const INDEX_TS =
36
- `import { createI18n } from "./generated/instance.generated${DEFAULT_IMPORT_SUFFIX}";\n\n` +
37
- `export * from "./generated/instance.generated${DEFAULT_IMPORT_SUFFIX}";\n` +
38
- `export * from "./generated/i18n-types.generated${DEFAULT_IMPORT_SUFFIX}";\n` +
39
- `export * from "./generated/namespace-loaders.generated${DEFAULT_IMPORT_SUFFIX}";\n\n` +
40
- `export const i18n = createI18n();\n`;
41
-
42
- function writeJson(filePath: string, value: unknown): void {
43
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
44
- fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
45
- }
46
-
47
- function writeText(filePath: string, content: string): void {
48
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
49
- fs.writeFileSync(filePath, content);
50
- }
51
-
52
- function relative(fromRoot: string, filePath: string): string {
53
- return path.relative(fromRoot, filePath).replace(/\\/g, "/");
54
- }
55
-
56
- export function runSetup(options: SetupOptions): SetupResult {
57
- const targetDir = path.resolve(options.targetDir);
58
- const project =
59
- options.project ?? inferProjectName(path.basename(targetDir.replace(/[/\\]$/, "") || "app"));
60
-
61
- if (!/^[A-Z][a-zA-Z0-9]*$/.test(project)) {
62
- throw new Error(
63
- `[Setup Error] Invalid project name "${project}". Use PascalCase (e.g. MyApp) via --project.`
64
- );
65
- }
66
-
67
- const configPath = path.join(targetDir, CONFIG_FILE);
68
- if (fs.existsSync(configPath) && !options.force) {
69
- throw new Error(
70
- `[Setup Error] ${relative(process.cwd(), configPath)} already exists. Use --force to overwrite.`
71
- );
72
- }
73
-
74
- const created: string[] = [];
75
-
76
- writeCodegenConfig(configPath, buildCodegenConfig(project));
77
- created.push(relative(targetDir, configPath));
78
-
79
- const defaultPath = path.join(targetDir, I18N_ROOT, TRANSLATIONS_DIR, "default.json");
80
- writeJson(defaultPath, DEFAULT_STARTER);
81
- created.push(relative(targetDir, defaultPath));
82
-
83
- const indexPath = path.join(targetDir, I18N_ROOT, "index.ts");
84
- writeText(indexPath, INDEX_TS);
85
- created.push(relative(targetDir, indexPath));
86
-
87
- fs.mkdirSync(path.join(targetDir, I18N_ROOT, GENERATED_DIR), { recursive: true });
88
-
89
- return { targetDir, project, created };
90
- }
91
-
92
- export function parseSetupArgs(argv: string[]): SetupOptions {
93
- const first = argv[0];
94
-
95
- let targetDir = ".";
96
- let project: string | undefined;
97
- let force = false;
98
- let startIndex = 0;
99
-
100
- if (first === "multi") {
101
- startIndex = 1;
102
- } else if (first !== undefined && !first.startsWith("-")) {
103
- // positional targetDir without mode
104
- } else if (first === undefined || first.startsWith("-")) {
105
- // flags only / empty
106
- }
107
-
108
- for (let index = startIndex; index < argv.length; index++) {
109
- const arg = argv[index]!;
110
- if (arg === "--project") {
111
- project = argv[++index];
112
- if (!project) {
113
- throw new Error("[Setup Error] --project requires a value.");
114
- }
115
- continue;
116
- }
117
- if (arg === "--force") {
118
- force = true;
119
- continue;
120
- }
121
- if (arg.startsWith("-")) {
122
- throw new Error(`[Setup Error] Unknown flag: ${arg}`);
123
- }
124
- targetDir = arg;
125
- }
126
-
127
- return { targetDir, project, force };
128
- }
129
-
130
- function main() {
131
- try {
132
- const options = parseSetupArgs(process.argv.slice(2));
133
- const result = runSetup(options);
134
-
135
- console.log(`✅ Setup i18n in ${result.targetDir}`);
136
- console.log(
137
- ` Project types: ${result.project}Params, ${result.project}Schema, ${result.project}Locale`
138
- );
139
- console.log(` Created: ${result.created.join(", ")}`);
140
- const targetLabel = path.relative(process.cwd(), result.targetDir) || ".";
141
-
142
- console.log("");
143
- console.log(`Add to package.json scripts (from ${targetLabel}):`);
144
- console.log(` "i18n:codegen": "xndrjs-i18n-codegen --config ${CONFIG_FILE}"`);
145
- console.log("");
146
- console.log("Then run your package manager's run script (pnpm/npm/yarn).");
147
- } catch (error) {
148
- const message = error instanceof Error ? error.message : String(error);
149
- console.error(message);
150
- process.exit(1);
151
- }
152
- }
153
-
154
- if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
155
- main();
156
- }
package/src/types.ts DELETED
@@ -1,65 +0,0 @@
1
- import type { IntlMessageFormat } from "intl-messageformat";
2
-
3
- export type LocaleDictionary = Record<string, string>;
4
- export type KeyDictionary = Record<string, LocaleDictionary>;
5
- export type MultiDictionary = Record<string, KeyDictionary>;
6
-
7
- /** Locales present on any key of a flat (per-namespace) key dictionary. */
8
- export type LocaleOfKeys<Schema extends KeyDictionary> = {
9
- [K in keyof Schema]: keyof Schema[K];
10
- }[keyof Schema] &
11
- string;
12
-
13
- export type LocaleOfMulti<Schema extends MultiDictionary> = {
14
- [NS in keyof Schema]: {
15
- [K in keyof Schema[NS]]: keyof Schema[NS][K];
16
- }[keyof Schema[NS]];
17
- }[keyof Schema] &
18
- string;
19
-
20
- /** Subset of schema keys with any subset of project locales per key — valid input for merge operations. */
21
- export type PartialKeyDictionary<
22
- T extends KeyDictionary,
23
- Locales extends string = LocaleOfKeys<T>,
24
- > = {
25
- [K in keyof T]?: Partial<Record<Locales, string>>;
26
- };
27
-
28
- /** Subset of schema namespaces with partial keys/locales — valid input for multi merge operations. */
29
- export type PartialMultiDictionary<
30
- T extends MultiDictionary,
31
- Locales extends string = LocaleOfMulti<T>,
32
- > = {
33
- [NS in keyof T]?: PartialKeyDictionary<T[NS], Locales>;
34
- };
35
-
36
- export type LocaleFallbackMap = Record<string, string | null>;
37
-
38
- export type MissingTranslationContext = {
39
- /** Only set by the multi-namespace provider. */
40
- namespace?: string;
41
- key: string;
42
- locale: string;
43
- fallbackChain: string;
44
- };
45
-
46
- export type OnMissingTranslation =
47
- | "throw"
48
- | "key"
49
- | ((context: MissingTranslationContext) => string);
50
-
51
- export type IcuTranslationProviderOptions<
52
- Fallback extends LocaleFallbackMap | undefined = undefined,
53
- > = {
54
- localeFallback?: Fallback;
55
- /**
56
- * Strategy when no template resolves for a key/locale (after walking the
57
- * fallback chain). "throw" (default) raises an error, "key" returns the
58
- * translation key itself, a function receives the context and returns the
59
- * string to display. ICU syntax and formatting errors always throw.
60
- */
61
- onMissing?: OnMissingTranslation;
62
- };
63
-
64
- export type LocaleCache = Record<string, IntlMessageFormat>;
65
- export type MultiCompiledCache = Record<string, Record<string, LocaleCache>>;
@@ -1,76 +0,0 @@
1
- import { z } from "zod";
2
- import type { VariableSpec, VariableType } from "../icu/extract-variables.js";
3
-
4
- export const variableTypeSchema = z.enum(["string", "number", "date"]);
5
-
6
- function formatVariableMismatch(expected: VariableSpec, found: VariableSpec): string {
7
- const expectedKeys = Object.keys(expected).sort();
8
- const foundKeys = Object.keys(found).sort();
9
- return `Expected variables [${expectedKeys.join(", ")}], found [${foundKeys.join(", ")}]`;
10
- }
11
-
12
- export function createArgsSchema(expected: VariableSpec): z.ZodType<VariableSpec> {
13
- return z.record(z.string(), variableTypeSchema).superRefine((found, ctx) => {
14
- const expectedKeys = Object.keys(expected).sort();
15
- const foundKeys = Object.keys(found).sort();
16
-
17
- if (expectedKeys.join() !== foundKeys.join()) {
18
- ctx.addIssue({
19
- code: "custom",
20
- message: formatVariableMismatch(expected, found),
21
- });
22
- return;
23
- }
24
-
25
- for (const [name, type] of Object.entries(expected)) {
26
- if (found[name] !== type) {
27
- ctx.addIssue({
28
- code: "custom",
29
- message: `Variable "${name}": expected ${type}, found ${found[name]}`,
30
- });
31
- }
32
- }
33
- });
34
- }
35
-
36
- export function mapArgsSchemaError(
37
- expected: VariableSpec,
38
- found: VariableSpec,
39
- message: string,
40
- path: readonly string[]
41
- ) {
42
- const expectedKeys = Object.keys(expected).sort();
43
- const foundKeys = Object.keys(found).sort();
44
-
45
- if (expectedKeys.join() !== foundKeys.join()) {
46
- return {
47
- kind: "variable_mismatch" as const,
48
- path,
49
- expected,
50
- found,
51
- message,
52
- };
53
- }
54
-
55
- for (const [name, type] of Object.entries(expected)) {
56
- const foundType = found[name];
57
- if (foundType !== undefined && foundType !== type) {
58
- return {
59
- kind: "variable_type_mismatch" as const,
60
- path,
61
- variable: name,
62
- expected: type,
63
- found: foundType as VariableType,
64
- message,
65
- };
66
- }
67
- }
68
-
69
- return {
70
- kind: "variable_mismatch" as const,
71
- path,
72
- expected,
73
- found,
74
- message,
75
- };
76
- }
@@ -1,52 +0,0 @@
1
- import { z } from "zod";
2
- import type { VariableSpec } from "../icu/extract-variables.js";
3
- import { createArgsSchema, variableTypeSchema } from "./create-args-schema.js";
4
- import type { DictionarySpec, NormalizedDictionary, ParsedKeyEntry } from "./types.js";
5
-
6
- const parsedLocaleEntrySchema = z.object({
7
- template: z.string(),
8
- args: z.record(z.string(), variableTypeSchema),
9
- });
10
-
11
- const parsedKeyEntrySchema = z.object({
12
- locales: z.record(z.string(), parsedLocaleEntrySchema),
13
- mergedArgs: z.record(z.string(), variableTypeSchema),
14
- });
15
-
16
- export function createKeyDictionarySchema(
17
- argsByKey: Readonly<Record<string, VariableSpec>>
18
- ): z.ZodType<Record<string, ParsedKeyEntry>> {
19
- const keySchemas = Object.fromEntries(
20
- Object.entries(argsByKey).map(([key, expectedArgs]) => [
21
- key,
22
- parsedKeyEntrySchema.extend({
23
- mergedArgs: createArgsSchema(expectedArgs),
24
- }),
25
- ])
26
- );
27
-
28
- return z.object(keySchemas);
29
- }
30
-
31
- export function createNormalizedDictionarySchema(
32
- spec: DictionarySpec
33
- ): z.ZodType<NormalizedDictionary> {
34
- if (spec.mode === "single") {
35
- return z.object({
36
- mode: z.literal("single"),
37
- keys: createKeyDictionarySchema(spec.argsByKey),
38
- }) as z.ZodType<NormalizedDictionary>;
39
- }
40
-
41
- const namespaceSchemas = Object.fromEntries(
42
- Object.entries(spec.argsByKey).map(([namespace, argsByKey]) => [
43
- namespace,
44
- createKeyDictionarySchema(argsByKey),
45
- ])
46
- );
47
-
48
- return z.object({
49
- mode: z.literal("multi"),
50
- namespaces: z.object(namespaceSchemas),
51
- }) as z.ZodType<NormalizedDictionary>;
52
- }
@@ -1,43 +0,0 @@
1
- import type { ValidationIssue, ValidationResult } from "./types.js";
2
-
3
- function issueMessage(issue: ValidationIssue): string {
4
- switch (issue.kind) {
5
- case "missing_key":
6
- return `Missing required key at ${issue.path.join(".")}`;
7
- case "unknown_key":
8
- return `Unknown key at ${issue.path.join(".")}`;
9
- case "invalid_input":
10
- case "invalid_locale_value":
11
- case "icu_syntax_error":
12
- case "locale_args_mismatch":
13
- case "variable_mismatch":
14
- case "variable_type_mismatch":
15
- return issue.message;
16
- }
17
- }
18
-
19
- export function formatIssues(issues: readonly ValidationIssue[]): string {
20
- return issues
21
- .map((issue) => {
22
- if ("path" in issue && issue.path.length > 0) {
23
- return `[${issue.path.join(".")}] ${issue.kind}: ${issueMessage(issue)}`;
24
- }
25
- return `${issue.kind}: ${issueMessage(issue)}`;
26
- })
27
- .join("\n");
28
- }
29
-
30
- export class DictionaryValidationError extends Error {
31
- constructor(public readonly issues: readonly ValidationIssue[]) {
32
- super(formatIssues(issues));
33
- this.name = "DictionaryValidationError";
34
- }
35
- }
36
-
37
- export function assertValidDictionary<T>(
38
- result: ValidationResult<T>
39
- ): asserts result is { ok: true; data: T } {
40
- if (!result.ok) {
41
- throw new DictionaryValidationError(result.issues);
42
- }
43
- }
@@ -1,202 +0,0 @@
1
- /**
2
- * External payload validation for server-side codegen/ingest (CMS → authoring files).
3
- * Not a runtime patch API — validate before writing authoring, then `regenerateNamespaces` / `load()`.
4
- */
5
- import { normalizeDictionaryPartial, normalizeKeyDictionaryPartial } from "./normalize.js";
6
- import { toDictionary, toNamespaceDictionary } from "./to-dictionary.js";
7
- import type {
8
- DictionarySpec,
9
- NormalizedKeyDictionary,
10
- ValidationIssue,
11
- ValidationResult,
12
- } from "./types.js";
13
- import { validateNormalizedKeyDictionaryPartial } from "./validate-normalized.js";
14
-
15
- export function validateExternalDictionaryPartial<TSchema>(
16
- input: unknown,
17
- spec: DictionarySpec
18
- ): ValidationResult<Partial<TSchema>> {
19
- if (spec.mode === "single") {
20
- const step1 = normalizeKeyDictionaryPartial(input, spec.requiredKeys, spec.argsByKey, []);
21
- if (!step1.ok) {
22
- return step1;
23
- }
24
-
25
- const step2 = validateNormalizedKeyDictionaryPartial(step1.data, spec.argsByKey, spec, []);
26
- if (!step2.ok) {
27
- return step2;
28
- }
29
-
30
- return {
31
- ok: true,
32
- data: toDictionary({ mode: "single", keys: step2.data }) as Partial<TSchema>,
33
- };
34
- }
35
-
36
- const step1 = normalizeDictionaryPartial(input, spec);
37
- if (!step1.ok) {
38
- return step1;
39
- }
40
-
41
- if (step1.data.mode !== "multi") {
42
- return {
43
- ok: false,
44
- issues: [
45
- {
46
- kind: "invalid_input",
47
- message: "validateExternalDictionaryPartial requires multi-mode dictionary spec",
48
- },
49
- ],
50
- };
51
- }
52
-
53
- const issues: ValidationIssue[] = [];
54
- const namespaces: Record<string, NormalizedKeyDictionary> = {};
55
-
56
- for (const [namespace, keys] of Object.entries(step1.data.namespaces)) {
57
- const step2 = validateNormalizedKeyDictionaryPartial(
58
- keys,
59
- spec.argsByKey[namespace] ?? {},
60
- spec,
61
- [namespace]
62
- );
63
- if (!step2.ok) {
64
- issues.push(...step2.issues);
65
- } else {
66
- namespaces[namespace] = step2.data;
67
- }
68
- }
69
-
70
- if (issues.length > 0) {
71
- return { ok: false, issues };
72
- }
73
-
74
- return {
75
- ok: true,
76
- data: toDictionary({ mode: "multi", namespaces }) as Partial<TSchema>,
77
- };
78
- }
79
-
80
- export function validateExternalNamespacePartial<TNamespaceSchema>(
81
- namespace: string,
82
- input: unknown,
83
- spec: DictionarySpec
84
- ): ValidationResult<Partial<TNamespaceSchema>> {
85
- if (spec.mode !== "multi") {
86
- return {
87
- ok: false,
88
- issues: [
89
- {
90
- kind: "invalid_input",
91
- message: "validateExternalNamespacePartial requires multi-mode dictionary spec",
92
- },
93
- ],
94
- };
95
- }
96
-
97
- const knownKeys = spec.requiredKeys[namespace];
98
- if (!knownKeys) {
99
- return {
100
- ok: false,
101
- issues: [{ kind: "unknown_key", path: [namespace] }],
102
- };
103
- }
104
-
105
- const step1 = normalizeKeyDictionaryPartial(input, knownKeys, spec.argsByKey[namespace] ?? {}, [
106
- namespace,
107
- ]);
108
- if (!step1.ok) {
109
- return step1;
110
- }
111
-
112
- const step2 = validateNormalizedKeyDictionaryPartial(
113
- step1.data,
114
- spec.argsByKey[namespace] ?? {},
115
- spec,
116
- [namespace]
117
- );
118
- if (!step2.ok) {
119
- return step2;
120
- }
121
-
122
- return {
123
- ok: true,
124
- data: toNamespaceDictionary(
125
- { mode: "multi", namespaces: { [namespace]: step2.data } },
126
- namespace
127
- ) as Partial<TNamespaceSchema>,
128
- };
129
- }
130
-
131
- export function validateExternalKey<TKeySlice>(
132
- key: string,
133
- input: unknown,
134
- spec: Extract<DictionarySpec, { mode: "single" }>
135
- ): ValidationResult<TKeySlice>;
136
-
137
- export function validateExternalKey<TKeySlice>(
138
- namespace: string,
139
- key: string,
140
- input: unknown,
141
- spec: Extract<DictionarySpec, { mode: "multi" }>
142
- ): ValidationResult<TKeySlice>;
143
-
144
- export function validateExternalKey<TKeySlice>(
145
- namespaceOrKey: string,
146
- keyOrInput: string | unknown,
147
- inputOrSpec?: unknown,
148
- specMaybe?: DictionarySpec
149
- ): ValidationResult<TKeySlice> {
150
- if (specMaybe?.mode === "multi") {
151
- const namespace = namespaceOrKey;
152
- const key = keyOrInput as string;
153
- const input = inputOrSpec;
154
- const spec = specMaybe;
155
-
156
- return validateExternalNamespacePartial<TKeySlice>(
157
- namespace,
158
- { [key]: input },
159
- spec
160
- ) as ValidationResult<TKeySlice>;
161
- }
162
-
163
- const key = namespaceOrKey;
164
- const input = keyOrInput;
165
- const spec = inputOrSpec as Extract<DictionarySpec, { mode: "single" }>;
166
-
167
- return validateExternalDictionaryPartial<TKeySlice>(
168
- { [key]: input },
169
- spec
170
- ) as ValidationResult<TKeySlice>;
171
- }
172
-
173
- export {
174
- normalizeDictionaryPartial,
175
- normalizeKeyDictionaryPartial,
176
- toDictionary,
177
- toNamespaceDictionary,
178
- };
179
- export { validateNormalizedKeyDictionaryPartial } from "./validate-normalized.js";
180
- export { createArgsSchema } from "./create-args-schema.js";
181
- export { createNormalizedDictionarySchema } from "./create-normalized-schema.js";
182
- export { DictionaryValidationError, assertValidDictionary, formatIssues } from "./errors.js";
183
- export type {
184
- DictionarySpec,
185
- NormalizedDictionary,
186
- NormalizedKeyDictionary,
187
- NormalizedMultiDictionary,
188
- ParsedKeyEntry,
189
- ParsedLocaleEntry,
190
- ValidationIssue,
191
- ValidationResult,
192
- VariableSpec,
193
- VariableType,
194
- } from "./types.js";
195
-
196
- export type { ParseTemplateResult } from "../icu/parse-template.js";
197
- export { parseTemplate } from "../icu/parse-template.js";
198
- export {
199
- extractVariables,
200
- mergeVariableSpecs,
201
- variableSpecsEqual,
202
- } from "../icu/extract-variables.js";