ai-localize-cli 2.0.1 → 2.0.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # ai-localize-cli
2
2
 
3
+ ## 2.0.3
4
+
5
+ ### Minor Changes
6
+
7
+ - **`keyStyle` config option** — `extract` and `full-migrate` commands now respect `keyStyle: "screaming_snake"` in `ai-localize.config.json`; all scanned hardcoded strings are extracted with UPPER_SNAKE_CASE keys matching the text value (e.g. `"Save Changes"` -> key `SAVE_CHANGES`, value `"Save Changes"`) instead of the default hierarchical path-based key style
8
+ - **`staticKeys` config option** — `extract` and `full-migrate` commands now inject `staticKeys` entries from config into every generated locale file regardless of scanning results; useful for enum labels, status codes, and other constant strings not present as literals in source files
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+ - ai-localize-scanner@2.0.3
14
+ - ai-localize-config@2.0.3
15
+ - ai-localize-shared@2.0.3
16
+ - ai-localize-aws-cloudfront@2.0.3
17
+ - ai-localize-codemods@2.0.3
18
+ - ai-localize-framework-detectors@2.0.3
19
+ - ai-localize-locale-engine@2.0.3
20
+ - ai-localize-reporting@2.0.3
21
+ - ai-localize-validators@2.0.3
22
+
23
+ ## 2.0.2
24
+
25
+ ### Minor Changes
26
+
27
+ - **Flat locale layout in `extract` and `full-migrate` commands** — `--locale-structure flat` flag (or `localeStructure: "flat"` in config) now writes one `<lang>.json` per language with all namespace keys merged
28
+ - **Rich CLI report output** — `report` command now renders a structured terminal dashboard with coverage bars, top-file charts, missing-translation rankings, AST-context distribution, AI Insights, and actionable recommended next steps
29
+ - **HTML analytics dashboard** — `report --format html` now generates a fully self-contained interactive dashboard (light/dark theme, sortable tables, SVG donut chart, CSV/JSON export, print/PDF)
30
+
31
+ ### Patch Changes
32
+
33
+ - **Bug fix — locale key prefix** — resolved an issue where locale keys included ancestor path segments when `sourceRoot` was an absolute path
34
+ - **Bug fix — codemods config wired through** — `codemods` block in `ai-localize.config.json` is now applied by all codemod commands instead of being silently ignored
35
+ - Updated dependencies
36
+ - ai-localize-scanner@2.0.2
37
+ - ai-localize-config@2.0.2
38
+ - ai-localize-shared@2.0.2
39
+ - ai-localize-aws-cloudfront@2.0.2
40
+ - ai-localize-codemods@2.0.2
41
+ - ai-localize-framework-detectors@2.0.2
42
+ - ai-localize-locale-engine@2.0.2
43
+ - ai-localize-reporting@2.0.2
44
+ - ai-localize-validators@2.0.2
45
+
3
46
  ## 2.0.1
4
47
 
5
48
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -167,6 +167,13 @@ function extractCommand() {
167
167
  spinner.succeed("Configuration loaded");
168
168
  const structure = config.localeStructure ?? "nested";
169
169
  logger.info("Locale structure: " + import_chalk4.default.cyan(structure));
170
+ const staticKeys = config.staticKeys ?? {};
171
+ const staticKeyCount = Object.keys(staticKeys).length;
172
+ if (staticKeyCount > 0) {
173
+ logger.info(
174
+ "Static keys: " + import_chalk4.default.cyan(String(staticKeyCount)) + " (" + Object.keys(staticKeys).join(", ") + ")"
175
+ );
176
+ }
170
177
  const ss = createSpinner("Scanning for hardcoded text...").start();
171
178
  const scanner = new import_ai_localize_scanner2.ProjectScanner(config);
172
179
  const scanResult = await scanner.scan();
@@ -176,11 +183,14 @@ function extractCommand() {
176
183
  const extractor = new import_ai_localize_locale_engine.LocaleExtractor({
177
184
  defaultLanguage: config.defaultLanguage,
178
185
  targetLanguages: config.targetLanguages,
179
- namespaceSplitting: structure === "nested"
186
+ namespaceSplitting: structure === "nested",
180
187
  // flat mode does not need namespace splitting
188
+ staticKeys
181
189
  });
182
190
  const { localeFiles, keyCount, namespaces } = extractor.extract(uniqueTexts);
183
- logger.info("Keys generated: " + import_chalk4.default.green(String(keyCount)));
191
+ logger.info(
192
+ "Keys generated: " + import_chalk4.default.green(String(keyCount)) + (staticKeyCount > 0 ? import_chalk4.default.dim(" (+ " + staticKeyCount + " static)") : "")
193
+ );
184
194
  if (structure === "nested") {
185
195
  logger.info("Namespaces: " + import_chalk4.default.cyan(namespaces.join(", ")));
186
196
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-localize-cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "CLI for ai-localize-core: scan, extract, validate, migrate CDN",
5
5
  "bin": {
6
6
  "ai-localize": "./dist/cli.js"
@@ -11,15 +11,15 @@
11
11
  "chalk": "^5.3.0",
12
12
  "ora": "^8.0.1",
13
13
  "inquirer": "^9.2.12",
14
- "ai-localize-shared": "2.0.1",
15
- "ai-localize-framework-detectors": "2.0.1",
16
- "ai-localize-config": "2.0.1",
17
- "ai-localize-scanner": "2.0.1",
18
- "ai-localize-codemods": "2.0.1",
19
- "ai-localize-locale-engine": "2.0.1",
20
- "ai-localize-aws-cloudfront": "2.0.1",
21
- "ai-localize-validators": "2.0.1",
22
- "ai-localize-reporting": "2.0.1"
14
+ "ai-localize-framework-detectors": "2.0.3",
15
+ "ai-localize-shared": "2.0.3",
16
+ "ai-localize-config": "2.0.3",
17
+ "ai-localize-scanner": "2.0.3",
18
+ "ai-localize-aws-cloudfront": "2.0.3",
19
+ "ai-localize-validators": "2.0.3",
20
+ "ai-localize-locale-engine": "2.0.3",
21
+ "ai-localize-reporting": "2.0.3",
22
+ "ai-localize-codemods": "2.0.3"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/inquirer": "^9.0.7",
@@ -17,56 +17,76 @@ export function extractCommand(): Command {
17
17
  logger.header('ai-localize extract');
18
18
  const cwd = opts.cwd as string;
19
19
  const spinner = createSpinner('Loading configuration...').start();
20
- try {
20
+ try {
21
21
  const { config } = await loadConfig(cwd);
22
22
  spinner.succeed('Configuration loaded');
23
23
 
24
24
  // Log locale structure in use
25
- const structure = config.localeStructure ?? 'nested';
25
+ const structure = config.localeStructure ?? 'nested';
26
26
  logger.info('Locale structure: ' + chalk.cyan(structure));
27
27
 
28
- const ss = createSpinner('Scanning for hardcoded text...').start();
29
- const scanner = new ProjectScanner(config);
28
+ // Log static keys if configured
29
+ const staticKeys = config.staticKeys ?? {};
30
+ const staticKeyCount = Object.keys(staticKeys).length;
31
+ if (staticKeyCount > 0) {
32
+ logger.info(
33
+ 'Static keys: ' +
34
+ chalk.cyan(String(staticKeyCount)) +
35
+ ' (' +
36
+ Object.keys(staticKeys).join(', ') +
37
+ ')'
38
+ );
39
+ }
40
+
41
+ const ss = createSpinner('Scanning for hardcoded text...').start();
42
+ const scanner = new ProjectScanner(config);
30
43
  const scanResult = await scanner.scan();
31
- ss.succeed('Found ' + chalk.cyan(String(scanResult.detectedTexts.length)) + ' texts in ' + scanResult.scannedFiles + ' files');
44
+ ss.succeed('Found ' + chalk.cyan(String(scanResult.detectedTexts.length)) + ' texts in ' + scanResult.scannedFiles + ' files');
32
45
 
33
- const uniqueTexts = deduplicateTexts(scanResult.detectedTexts);
34
- logger.info('Unique texts: ' + chalk.cyan(String(uniqueTexts.length)));
46
+ const uniqueTexts = deduplicateTexts(scanResult.detectedTexts);
47
+ logger.info('Unique texts: ' + chalk.cyan(String(uniqueTexts.length)));
35
48
 
36
- const extractor = new LocaleExtractor({
37
- defaultLanguage: config.defaultLanguage,
38
- targetLanguages: config.targetLanguages,
39
- namespaceSplitting: structure === 'nested', // flat mode does not need namespace splitting
40
- });
49
+ const extractor = new LocaleExtractor({
50
+ defaultLanguage: config.defaultLanguage,
51
+ targetLanguages: config.targetLanguages,
52
+ namespaceSplitting: structure === 'nested', // flat mode does not need namespace splitting
53
+ staticKeys,
54
+ });
41
55
  const { localeFiles, keyCount, namespaces } = extractor.extract(uniqueTexts);
42
- logger.info('Keys generated: ' + chalk.green(String(keyCount)));
43
- if (structure === 'nested') {
44
- logger.info('Namespaces: ' + chalk.cyan(namespaces.join(', ')));
56
+ logger.info(
57
+ 'Keys generated: ' +
58
+ chalk.green(String(keyCount)) +
59
+ (staticKeyCount > 0
60
+ ? chalk.dim(' (+ ' + staticKeyCount + ' static)')
61
+ : '')
62
+ );
63
+ if (structure === 'nested') {
64
+ logger.info('Namespaces: ' + chalk.cyan(namespaces.join(', ')));
45
65
  }
46
66
 
47
- if (!opts.dryRun) {
67
+ if (!opts.dryRun) {
48
68
  const localesDir = path.resolve(cwd, config.localesDir);
49
- const writer = new LocaleWriter({
50
- localesDir,
51
- merge: opts.merge !== false,
52
- localeStructure: structure,
69
+ const writer = new LocaleWriter({
70
+ localesDir,
71
+ merge: opts.merge !== false,
72
+ localeStructure: structure,
53
73
  });
54
- const { written, created, merged } = writer.write(localeFiles);
74
+ const { written, created, merged } = writer.write(localeFiles);
55
75
  logger.success(
56
- 'Wrote ' + written.length + ' locale files (' + created.length + ' new, ' + merged.length + ' merged)'
57
- );
76
+ 'Wrote ' + written.length + ' locale files (' + created.length + ' new, ' + merged.length + ' merged)'
77
+ );
58
78
  written.forEach((f) => logger.info(' ' + chalk.gray(f)));
59
79
  } else {
60
80
  logger.info('Dry run — no files written');
61
81
  localeFiles.forEach((lf) => {
62
- const label = structure === 'flat'
63
- ? `${lf.language}.json`
64
- : `${lf.language}/${lf.namespace}.json`;
65
- logger.info(' ' + chalk.gray(label) + ' — ' + Object.keys(lf.entries).length + ' keys');
82
+ const label = structure === 'flat'
83
+ ? `${lf.language}.json`
84
+ : `${lf.language}/${lf.namespace}.json`;
85
+ logger.info(' ' + chalk.gray(label) + ' — ' + Object.keys(lf.entries).length + ' keys');
66
86
  });
67
87
  }
68
88
  } catch (err) {
69
- spinner.fail('Extraction failed');
89
+ spinner.fail('Extraction failed');
70
90
  logger.error(String(err));
71
91
  process.exit(1);
72
92
  }