@saidksi/localizer-core 0.1.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/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/ai/anthropic.d.ts +17 -0
- package/dist/ai/anthropic.d.ts.map +1 -0
- package/dist/ai/anthropic.js +58 -0
- package/dist/ai/anthropic.js.map +1 -0
- package/dist/ai/dedup.d.ts +19 -0
- package/dist/ai/dedup.d.ts.map +1 -0
- package/dist/ai/dedup.js +119 -0
- package/dist/ai/dedup.js.map +1 -0
- package/dist/ai/index.d.ts +65 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +464 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/openai.d.ts +11 -0
- package/dist/ai/openai.d.ts.map +1 -0
- package/dist/ai/openai.js +62 -0
- package/dist/ai/openai.js.map +1 -0
- package/dist/ai/prompts.d.ts +20 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +151 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/cache/index.d.ts +69 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +129 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/rewriter/index.d.ts +31 -0
- package/dist/rewriter/index.d.ts.map +1 -0
- package/dist/rewriter/index.js +128 -0
- package/dist/rewriter/index.js.map +1 -0
- package/dist/rewriter/transforms.d.ts +38 -0
- package/dist/rewriter/transforms.d.ts.map +1 -0
- package/dist/rewriter/transforms.js +189 -0
- package/dist/rewriter/transforms.js.map +1 -0
- package/dist/rewriter/ts-morph.d.ts +19 -0
- package/dist/rewriter/ts-morph.d.ts.map +1 -0
- package/dist/rewriter/ts-morph.js +121 -0
- package/dist/rewriter/ts-morph.js.map +1 -0
- package/dist/scanner/babel.d.ts +3 -0
- package/dist/scanner/babel.d.ts.map +1 -0
- package/dist/scanner/babel.js +504 -0
- package/dist/scanner/babel.js.map +1 -0
- package/dist/scanner/filters.d.ts +38 -0
- package/dist/scanner/filters.d.ts.map +1 -0
- package/dist/scanner/filters.js +133 -0
- package/dist/scanner/filters.js.map +1 -0
- package/dist/scanner/index.d.ts +22 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +82 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/typescript.d.ts +3 -0
- package/dist/scanner/typescript.d.ts.map +1 -0
- package/dist/scanner/typescript.js +542 -0
- package/dist/scanner/typescript.js.map +1 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validator/index.d.ts +65 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +237 -0
- package/dist/validator/index.js.map +1 -0
- package/package.json +65 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
export type AIProvider = "anthropic" | "openai";
|
|
2
|
+
export type KeyStyle = "dot.notation" | "snake_case";
|
|
3
|
+
export type I18nLibrary = "react-i18next" | "next-intl" | "react-intl" | "vue-i18n" | "i18next";
|
|
4
|
+
/**
|
|
5
|
+
* Shape of .localize.config.json in the user's project root.
|
|
6
|
+
* Loaded via cosmiconfig from `.localize.config.json` or the
|
|
7
|
+
* `localize` key in package.json.
|
|
8
|
+
*/
|
|
9
|
+
export interface LocalizeConfig {
|
|
10
|
+
/** ISO 639-1 code. Source of truth for key extraction. */
|
|
11
|
+
defaultLanguage: string;
|
|
12
|
+
/** Target translation languages, e.g. ["fr", "ar", "es"]. */
|
|
13
|
+
languages: string[];
|
|
14
|
+
/** Directory where messages/[lang]/*.json files are written. */
|
|
15
|
+
messagesDir: string;
|
|
16
|
+
/** Directories to scan, e.g. ["./src", "./app"]. */
|
|
17
|
+
include: string[];
|
|
18
|
+
/** Glob patterns to skip, e.g. ["node_modules", "dist", "**\/*.test.*"]. */
|
|
19
|
+
exclude: string[];
|
|
20
|
+
/** Which AI provider to use. */
|
|
21
|
+
aiProvider: AIProvider;
|
|
22
|
+
/** Specific model string, e.g. "claude-sonnet-4-6". */
|
|
23
|
+
aiModel: string;
|
|
24
|
+
/** Format for generated key names. */
|
|
25
|
+
keyStyle: KeyStyle;
|
|
26
|
+
/** Target i18n library — affects rewrite output. */
|
|
27
|
+
i18nLibrary: I18nLibrary;
|
|
28
|
+
/** If true, re-translate keys that already exist in target JSONs. */
|
|
29
|
+
overwriteExisting: boolean;
|
|
30
|
+
/** If true, `validate` exits with code 1 on any untranslated string. */
|
|
31
|
+
strictMode: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Enforced terminology per language.
|
|
34
|
+
* e.g. { "fr": { "Settings": "Paramètres" } }
|
|
35
|
+
*/
|
|
36
|
+
glossary: Record<string, Record<string, string>>;
|
|
37
|
+
/** Additional glob patterns for strings to ignore during scanning. */
|
|
38
|
+
ignorePatterns?: string[];
|
|
39
|
+
/** File glob patterns to skip entirely, e.g. ["**\/*.stories.*"]. */
|
|
40
|
+
ignoreFiles?: string[];
|
|
41
|
+
}
|
|
42
|
+
export type NodeType = "JSXText" | "JSXAttribute" | "TemplateLiteral" | "StringLiteral" | "JSXInterpolation";
|
|
43
|
+
/**
|
|
44
|
+
* A variable embedded in a JSXInterpolation string.
|
|
45
|
+
* e.g. "You have {{taskCount}} pending tasks" → { placeholder: "taskCount", expression: "taskCount" }
|
|
46
|
+
*/
|
|
47
|
+
export interface InterpolationVar {
|
|
48
|
+
/** The placeholder name used inside {{...}} in the template */
|
|
49
|
+
placeholder: string;
|
|
50
|
+
/** The raw JSX expression source: "taskCount", "user.name", etc. */
|
|
51
|
+
expression: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A single hardcoded string detected by the scanner.
|
|
55
|
+
* Carries enough context for the rewriter to locate and replace it.
|
|
56
|
+
*/
|
|
57
|
+
export interface ScanResult {
|
|
58
|
+
/** Absolute path to the source file. */
|
|
59
|
+
file: string;
|
|
60
|
+
/** 1-based line number of the string in the source file. */
|
|
61
|
+
line: number;
|
|
62
|
+
/** 0-based column of the string start. */
|
|
63
|
+
column: number;
|
|
64
|
+
/** The raw string value, e.g. "Welcome back". */
|
|
65
|
+
value: string;
|
|
66
|
+
/** AST node type — determines which transform the rewriter applies. */
|
|
67
|
+
nodeType: NodeType;
|
|
68
|
+
/** Human-readable context, e.g. "JSXText inside <h1>". */
|
|
69
|
+
context: string;
|
|
70
|
+
/** Up to 5 lines of surrounding source code for AI prompt context. */
|
|
71
|
+
surroundingCode: string;
|
|
72
|
+
/** True if this string is already wrapped in a t() / i18n.t() call. */
|
|
73
|
+
alreadyTranslated: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* True if this string is at module level (outside any function/component).
|
|
76
|
+
* Module-level strings cannot use t() — the hook is not available there.
|
|
77
|
+
* The rewriter skips these; they appear in scan/translate output only.
|
|
78
|
+
*/
|
|
79
|
+
isModuleLevel: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Resolved i18n key after the AI step.
|
|
82
|
+
* null until translate has run.
|
|
83
|
+
*/
|
|
84
|
+
resolvedKey: string | null;
|
|
85
|
+
/**
|
|
86
|
+
* For JSXInterpolation nodes — the variable substitutions in the template.
|
|
87
|
+
* e.g. "You have {{taskCount}} pending tasks" → [{ placeholder: "taskCount", expression: "taskCount" }]
|
|
88
|
+
*/
|
|
89
|
+
interpolations?: InterpolationVar[];
|
|
90
|
+
/**
|
|
91
|
+
* @internal Raw source text of all JSX children spanning this interpolation.
|
|
92
|
+
* Used by the rewriter to locate and replace the full span. Not shown in reports.
|
|
93
|
+
*/
|
|
94
|
+
rawSpan?: string;
|
|
95
|
+
/**
|
|
96
|
+
* @internal For object property values, the name of the parent object variable.
|
|
97
|
+
* e.g. "statusConfig" for the "online" property in { online: "..." }
|
|
98
|
+
* Used to group related object properties as siblings.
|
|
99
|
+
*/
|
|
100
|
+
objectKey?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Full output of a scan command run. */
|
|
103
|
+
export interface ScanReport {
|
|
104
|
+
generatedAt: string;
|
|
105
|
+
file?: string;
|
|
106
|
+
dir?: string;
|
|
107
|
+
results: ScanResult[];
|
|
108
|
+
}
|
|
109
|
+
/** Payload sent to an AI provider for a single unique string. */
|
|
110
|
+
export interface AIRequest {
|
|
111
|
+
file: string;
|
|
112
|
+
componentContext: string;
|
|
113
|
+
element: string;
|
|
114
|
+
surroundingCode: string;
|
|
115
|
+
value: string;
|
|
116
|
+
keyStyle: KeyStyle;
|
|
117
|
+
/** Glossary entries relevant to this string, keyed by language. */
|
|
118
|
+
glossary: Record<string, string>;
|
|
119
|
+
targetLanguages: string[];
|
|
120
|
+
/**
|
|
121
|
+
* Other strings from the same component context (siblings).
|
|
122
|
+
* Used to hint at consistent key naming.
|
|
123
|
+
* e.g. ["Total views", "Total clicks", "Conversion rate"]
|
|
124
|
+
*/
|
|
125
|
+
relatedStrings?: string[];
|
|
126
|
+
/**
|
|
127
|
+
* @internal Grouping key for finding siblings: "file:componentName"
|
|
128
|
+
* Not sent to AI, used internally for post-processing.
|
|
129
|
+
*/
|
|
130
|
+
contextKey?: string;
|
|
131
|
+
}
|
|
132
|
+
/** Parsed response from an AI provider. */
|
|
133
|
+
export interface AIResponse {
|
|
134
|
+
key: string;
|
|
135
|
+
/** Maps language code → translated string, e.g. { fr: "Se connecter" }. */
|
|
136
|
+
translations: Record<string, string>;
|
|
137
|
+
}
|
|
138
|
+
/** Outcome of rewriting a single source file. */
|
|
139
|
+
export interface RewriteResult {
|
|
140
|
+
file: string;
|
|
141
|
+
/** Whether the user confirmed and the file was actually written. */
|
|
142
|
+
applied: boolean;
|
|
143
|
+
/** Number of strings replaced. */
|
|
144
|
+
changesCount: number;
|
|
145
|
+
/** Unified diff string shown to the user before confirmation. */
|
|
146
|
+
diff: string;
|
|
147
|
+
/** The full modified source — caller writes this to disk after confirmation. */
|
|
148
|
+
modifiedSource: string;
|
|
149
|
+
}
|
|
150
|
+
/** Coverage report for a single target language. */
|
|
151
|
+
export interface ValidationResult {
|
|
152
|
+
language: string;
|
|
153
|
+
totalKeys: number;
|
|
154
|
+
presentKeys: number;
|
|
155
|
+
missingKeys: string[];
|
|
156
|
+
coveragePercent: number;
|
|
157
|
+
}
|
|
158
|
+
/** Single file record stored in .localize/cache.json. */
|
|
159
|
+
export interface CacheEntry {
|
|
160
|
+
/** SHA-256 hex digest of the file contents at processing time. */
|
|
161
|
+
hash: string;
|
|
162
|
+
processedAt: string;
|
|
163
|
+
stringCount: number;
|
|
164
|
+
}
|
|
165
|
+
/** Full shape of .localize/cache.json. */
|
|
166
|
+
export interface CacheStore {
|
|
167
|
+
version: 1;
|
|
168
|
+
/** Keyed by file path relative to the user's project root. */
|
|
169
|
+
entries: Record<string, CacheEntry>;
|
|
170
|
+
}
|
|
171
|
+
/** Options shared across all CLI commands. */
|
|
172
|
+
export interface RunOptions {
|
|
173
|
+
file?: string;
|
|
174
|
+
dir?: string;
|
|
175
|
+
/** Target languages override, e.g. ["fr", "ar"]. */
|
|
176
|
+
lang?: string[];
|
|
177
|
+
dryRun?: boolean;
|
|
178
|
+
/** Skip confirmation prompts (apply all changes automatically). */
|
|
179
|
+
yes?: boolean;
|
|
180
|
+
/** Ignore cache and re-process all files. */
|
|
181
|
+
force?: boolean;
|
|
182
|
+
skipRewrite?: boolean;
|
|
183
|
+
skipValidate?: boolean;
|
|
184
|
+
/** Machine-readable mode — exits with code 1 on any failure. */
|
|
185
|
+
ci?: boolean;
|
|
186
|
+
/** Path to write a JSON report file. */
|
|
187
|
+
output?: string;
|
|
188
|
+
}
|
|
189
|
+
/** Summary returned after a full `localize run` pipeline. */
|
|
190
|
+
export interface PipelineResult {
|
|
191
|
+
file?: string;
|
|
192
|
+
dir?: string;
|
|
193
|
+
/** Total strings detected by scanner. */
|
|
194
|
+
scanned: number;
|
|
195
|
+
/** Strings sent to AI and written to messages JSON. */
|
|
196
|
+
translated: number;
|
|
197
|
+
/** Source files rewritten. */
|
|
198
|
+
rewritten: number;
|
|
199
|
+
/** Whether the final validate step passed. */
|
|
200
|
+
validated: boolean;
|
|
201
|
+
durationMs: number;
|
|
202
|
+
/** Estimated USD cost of AI calls made during this run. */
|
|
203
|
+
aiCostUsd: number;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gFAAgF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ValidationResult, LocalizeConfig, ScanResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Flatten a nested JSON object into dot-notation key paths.
|
|
4
|
+
*
|
|
5
|
+
* { auth: { sign_in: "Sign in", title: "Login" } }
|
|
6
|
+
* → ["auth.sign_in", "auth.title"]
|
|
7
|
+
*
|
|
8
|
+
* Exported for unit testing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function flattenKeys(obj: Record<string, unknown>, prefix?: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Read all keys for a language by scanning every .json page file
|
|
13
|
+
* in `messagesDir/{lang}/`.
|
|
14
|
+
*
|
|
15
|
+
* Returns a Map of pageName → Set<dotNotationKey>.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readLanguageKeys(messagesDir: string, lang: string): Promise<Map<string, Set<string>>>;
|
|
18
|
+
export interface ValidateOptions {
|
|
19
|
+
/** Check only this language instead of all target languages */
|
|
20
|
+
lang?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Restrict validation to a single page file derived from the source filename.
|
|
23
|
+
*
|
|
24
|
+
* Source file → page name → JSON file (all languages):
|
|
25
|
+
* src/pages/Login.tsx → "login" → messages/{lang}/login.json
|
|
26
|
+
* app/checkout/page.tsx → "page" → messages/{lang}/page.json
|
|
27
|
+
*
|
|
28
|
+
* Pass the page name (no extension), not the full source path.
|
|
29
|
+
* The CLI derives it via: basename(file, extname(file)).toLowerCase()
|
|
30
|
+
*/
|
|
31
|
+
page?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate key coverage across all target languages.
|
|
35
|
+
*
|
|
36
|
+
* Reads all messages/{lang}/*.json files and computes:
|
|
37
|
+
* - Which keys are present in the default language
|
|
38
|
+
* - Which of those keys are missing in each target language
|
|
39
|
+
* - Coverage percentage per language
|
|
40
|
+
*
|
|
41
|
+
* Always includes the default language in the result (always 100%).
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateCoverage(config: LocalizeConfig, options?: ValidateOptions): Promise<ValidationResult[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Returns true if all target languages have 100% key coverage.
|
|
46
|
+
* Convenience wrapper used by `localize run` to decide the final status line.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isFullyCovered(config: LocalizeConfig): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Populate `resolvedKey` on each ScanResult by looking up the string value
|
|
51
|
+
* in the default language messages JSON for that page.
|
|
52
|
+
*
|
|
53
|
+
* Called by `localize rewrite` when run standalone (after `localize translate`
|
|
54
|
+
* has already written the messages JSON).
|
|
55
|
+
*
|
|
56
|
+
* Results whose value cannot be found in the JSON are returned unchanged
|
|
57
|
+
* (resolvedKey stays null — they will be skipped by the rewriter).
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveKeysFromMessages(results: ScanResult[], config: LocalizeConfig): Promise<ScanResult[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Compute a diff of missing keys for a single language relative to the default.
|
|
62
|
+
* Used by `localize diff --lang ar`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function getMissingKeys(config: LocalizeConfig, lang: string, page?: string): Promise<string[]>;
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validator/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIhF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,SAAK,GACV,MAAM,EAAE,CAeV;AAgCD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAenC;AA8CD,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAqD7B;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAiCD;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,UAAU,EAAE,EACrB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,UAAU,EAAE,CAAC,CAmCvB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { readFile, readdir } from "fs/promises";
|
|
2
|
+
import { resolve, join, basename, extname } from "path";
|
|
3
|
+
// ─── Key flattening ───────────────────────────────────────────────────────────
|
|
4
|
+
/**
|
|
5
|
+
* Flatten a nested JSON object into dot-notation key paths.
|
|
6
|
+
*
|
|
7
|
+
* { auth: { sign_in: "Sign in", title: "Login" } }
|
|
8
|
+
* → ["auth.sign_in", "auth.title"]
|
|
9
|
+
*
|
|
10
|
+
* Exported for unit testing.
|
|
11
|
+
*/
|
|
12
|
+
export function flattenKeys(obj, prefix = "") {
|
|
13
|
+
const keys = [];
|
|
14
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
15
|
+
const fullKey = prefix ? `${prefix}.${k}` : k;
|
|
16
|
+
if (typeof v === "object" &&
|
|
17
|
+
v !== null &&
|
|
18
|
+
!Array.isArray(v)) {
|
|
19
|
+
keys.push(...flattenKeys(v, fullKey));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
keys.push(fullKey);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return keys;
|
|
26
|
+
}
|
|
27
|
+
// ─── Messages file reading ────────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Read and parse a single messages JSON file.
|
|
30
|
+
* Returns an empty object if the file doesn't exist or is invalid JSON.
|
|
31
|
+
*/
|
|
32
|
+
async function readMessagesFile(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
const content = await readFile(filePath, "utf-8");
|
|
35
|
+
return JSON.parse(content);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* List all .json filenames inside a language directory.
|
|
43
|
+
* Returns [] if the directory doesn't exist.
|
|
44
|
+
*/
|
|
45
|
+
async function listPageFiles(langDir) {
|
|
46
|
+
try {
|
|
47
|
+
const entries = await readdir(langDir);
|
|
48
|
+
return entries.filter((f) => f.endsWith(".json"));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Read all keys for a language by scanning every .json page file
|
|
56
|
+
* in `messagesDir/{lang}/`.
|
|
57
|
+
*
|
|
58
|
+
* Returns a Map of pageName → Set<dotNotationKey>.
|
|
59
|
+
*/
|
|
60
|
+
export async function readLanguageKeys(messagesDir, lang) {
|
|
61
|
+
const langDir = join(resolve(messagesDir), lang);
|
|
62
|
+
const pageFiles = await listPageFiles(langDir);
|
|
63
|
+
const result = new Map();
|
|
64
|
+
await Promise.all(pageFiles.map(async (filename) => {
|
|
65
|
+
const pageName = basename(filename, extname(filename));
|
|
66
|
+
const filePath = join(langDir, filename);
|
|
67
|
+
const json = await readMessagesFile(filePath);
|
|
68
|
+
result.set(pageName, new Set(flattenKeys(json)));
|
|
69
|
+
}));
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Merge all per-page key sets into a single flat Set<string>.
|
|
74
|
+
* Used when validating total coverage without caring about pages.
|
|
75
|
+
*/
|
|
76
|
+
function mergeKeyMaps(pageKeys) {
|
|
77
|
+
const all = new Set();
|
|
78
|
+
for (const keys of pageKeys.values()) {
|
|
79
|
+
for (const k of keys)
|
|
80
|
+
all.add(k);
|
|
81
|
+
}
|
|
82
|
+
return all;
|
|
83
|
+
}
|
|
84
|
+
// ─── Validation logic ─────────────────────────────────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* Compute validation result for a single target language.
|
|
87
|
+
* Compares its keys against the default language's keys.
|
|
88
|
+
*/
|
|
89
|
+
function computeValidationResult(lang, defaultKeys, langKeys) {
|
|
90
|
+
const missingKeys = [];
|
|
91
|
+
for (const key of defaultKeys) {
|
|
92
|
+
if (!langKeys.has(key))
|
|
93
|
+
missingKeys.push(key);
|
|
94
|
+
}
|
|
95
|
+
const totalKeys = defaultKeys.size;
|
|
96
|
+
const presentKeys = totalKeys - missingKeys.length;
|
|
97
|
+
const coveragePercent = totalKeys === 0 ? 100 : Math.round((presentKeys / totalKeys) * 100);
|
|
98
|
+
return {
|
|
99
|
+
language: lang,
|
|
100
|
+
totalKeys,
|
|
101
|
+
presentKeys,
|
|
102
|
+
missingKeys,
|
|
103
|
+
coveragePercent,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate key coverage across all target languages.
|
|
108
|
+
*
|
|
109
|
+
* Reads all messages/{lang}/*.json files and computes:
|
|
110
|
+
* - Which keys are present in the default language
|
|
111
|
+
* - Which of those keys are missing in each target language
|
|
112
|
+
* - Coverage percentage per language
|
|
113
|
+
*
|
|
114
|
+
* Always includes the default language in the result (always 100%).
|
|
115
|
+
*/
|
|
116
|
+
export async function validateCoverage(config, options = {}) {
|
|
117
|
+
const { messagesDir, defaultLanguage, languages } = config;
|
|
118
|
+
// Read default language keys
|
|
119
|
+
const defaultPageKeys = await readLanguageKeys(messagesDir, defaultLanguage);
|
|
120
|
+
// If scoped to a specific page, narrow both sides
|
|
121
|
+
const effectiveDefaultKeys = options.page
|
|
122
|
+
? (defaultPageKeys.get(options.page) ?? new Set())
|
|
123
|
+
: mergeKeyMaps(defaultPageKeys);
|
|
124
|
+
// Determine which languages to check
|
|
125
|
+
const targetLanguages = options.lang
|
|
126
|
+
? languages.filter((l) => l === options.lang)
|
|
127
|
+
: languages;
|
|
128
|
+
const results = [];
|
|
129
|
+
// Default language is always 100% — include it first
|
|
130
|
+
results.push({
|
|
131
|
+
language: defaultLanguage,
|
|
132
|
+
totalKeys: effectiveDefaultKeys.size,
|
|
133
|
+
presentKeys: effectiveDefaultKeys.size,
|
|
134
|
+
missingKeys: [],
|
|
135
|
+
coveragePercent: 100,
|
|
136
|
+
});
|
|
137
|
+
// Check each target language
|
|
138
|
+
await Promise.all(targetLanguages.map(async (lang) => {
|
|
139
|
+
if (lang === defaultLanguage)
|
|
140
|
+
return; // already added above
|
|
141
|
+
const langPageKeys = await readLanguageKeys(messagesDir, lang);
|
|
142
|
+
const effectiveLangKeys = options.page
|
|
143
|
+
? (langPageKeys.get(options.page) ?? new Set())
|
|
144
|
+
: mergeKeyMaps(langPageKeys);
|
|
145
|
+
results.push(computeValidationResult(lang, effectiveDefaultKeys, effectiveLangKeys));
|
|
146
|
+
}));
|
|
147
|
+
// Sort: default language first, then alphabetical
|
|
148
|
+
return results.sort((a, b) => {
|
|
149
|
+
if (a.language === defaultLanguage)
|
|
150
|
+
return -1;
|
|
151
|
+
if (b.language === defaultLanguage)
|
|
152
|
+
return 1;
|
|
153
|
+
return a.language.localeCompare(b.language);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Returns true if all target languages have 100% key coverage.
|
|
158
|
+
* Convenience wrapper used by `localize run` to decide the final status line.
|
|
159
|
+
*/
|
|
160
|
+
export async function isFullyCovered(config) {
|
|
161
|
+
const results = await validateCoverage(config);
|
|
162
|
+
return results.every((r) => r.coveragePercent === 100);
|
|
163
|
+
}
|
|
164
|
+
// ─── Key resolution (used by rewrite command) ─────────────────────────────────
|
|
165
|
+
/**
|
|
166
|
+
* Flatten a nested JSON object to a value→key reverse-lookup map.
|
|
167
|
+
* Used to recover resolvedKey from messages JSON when rewrite runs standalone.
|
|
168
|
+
*
|
|
169
|
+
* { auth: { sign_in: "Sign in" } } → Map { "Sign in" → "auth.sign_in" }
|
|
170
|
+
*
|
|
171
|
+
* Note: if multiple keys have the same value, the last one wins.
|
|
172
|
+
*/
|
|
173
|
+
function buildValueToKeyMap(obj, prefix = "") {
|
|
174
|
+
const map = new Map();
|
|
175
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
176
|
+
const fullKey = prefix ? `${prefix}.${k}` : k;
|
|
177
|
+
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
178
|
+
for (const [val, key] of buildValueToKeyMap(v, fullKey)) {
|
|
179
|
+
map.set(val, key);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else if (typeof v === "string") {
|
|
183
|
+
map.set(v, fullKey);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return map;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Populate `resolvedKey` on each ScanResult by looking up the string value
|
|
190
|
+
* in the default language messages JSON for that page.
|
|
191
|
+
*
|
|
192
|
+
* Called by `localize rewrite` when run standalone (after `localize translate`
|
|
193
|
+
* has already written the messages JSON).
|
|
194
|
+
*
|
|
195
|
+
* Results whose value cannot be found in the JSON are returned unchanged
|
|
196
|
+
* (resolvedKey stays null — they will be skipped by the rewriter).
|
|
197
|
+
*/
|
|
198
|
+
export async function resolveKeysFromMessages(results, config) {
|
|
199
|
+
// Build a cache of page → value→key map to avoid re-reading files
|
|
200
|
+
const pageCache = new Map();
|
|
201
|
+
async function getValueToKey(pageName) {
|
|
202
|
+
if (pageCache.has(pageName))
|
|
203
|
+
return pageCache.get(pageName);
|
|
204
|
+
const filePath = join(resolve(config.messagesDir), config.defaultLanguage, `${pageName}.json`);
|
|
205
|
+
try {
|
|
206
|
+
const content = await readFile(filePath, "utf-8");
|
|
207
|
+
const map = buildValueToKeyMap(JSON.parse(content));
|
|
208
|
+
pageCache.set(pageName, map);
|
|
209
|
+
return map;
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
const empty = new Map();
|
|
213
|
+
pageCache.set(pageName, empty);
|
|
214
|
+
return empty;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return Promise.all(results.map(async (r) => {
|
|
218
|
+
if (r.resolvedKey !== null)
|
|
219
|
+
return r; // already resolved
|
|
220
|
+
const pageName = basename(r.file, extname(r.file)).toLowerCase();
|
|
221
|
+
const valueToKey = await getValueToKey(pageName);
|
|
222
|
+
const key = valueToKey.get(r.value) ?? null;
|
|
223
|
+
return { ...r, resolvedKey: key };
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Compute a diff of missing keys for a single language relative to the default.
|
|
228
|
+
* Used by `localize diff --lang ar`.
|
|
229
|
+
*/
|
|
230
|
+
export async function getMissingKeys(config, lang, page) {
|
|
231
|
+
const options = { lang };
|
|
232
|
+
if (page)
|
|
233
|
+
options.page = page;
|
|
234
|
+
const results = await validateCoverage(config, options);
|
|
235
|
+
return results.find((r) => r.language === lang)?.missingKeys ?? [];
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validator/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@saidksi/localizer-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core i18n automation library for localizer",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./dist/index.js",
|
|
8
|
+
"./types": "./dist/types.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"files": ["dist"],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"test:coverage": "vitest run --coverage",
|
|
19
|
+
"lint": "tsc --noEmit",
|
|
20
|
+
"clean": "rm -rf dist"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"i18n",
|
|
24
|
+
"internationalization",
|
|
25
|
+
"localization",
|
|
26
|
+
"ast",
|
|
27
|
+
"code-generation",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "SaidKSI",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"packageManager": "pnpm@10.33.0",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/SaidKSI/localize-core.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/SaidKSI/localize-core/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/SaidKSI/localize-core#readme",
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@anthropic-ai/sdk": "^0.32.0",
|
|
46
|
+
"@babel/parser": "^7.25.0",
|
|
47
|
+
"@babel/traverse": "^7.25.0",
|
|
48
|
+
"@babel/types": "^7.25.0",
|
|
49
|
+
"fast-glob": "^3.3.2",
|
|
50
|
+
"openai": "^4.65.0",
|
|
51
|
+
"p-limit": "^6.1.0",
|
|
52
|
+
"ts-morph": "^23.0.0",
|
|
53
|
+
"typescript": "^5.5.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/babel__traverse": "^7.20.6",
|
|
57
|
+
"@types/node": "^22.0.0",
|
|
58
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
59
|
+
"vitest": "^2.1.0"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=20.0.0",
|
|
63
|
+
"pnpm": ">=8.0.0"
|
|
64
|
+
}
|
|
65
|
+
}
|