i18nsmith 0.3.4 ā 0.4.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/build.mjs +1 -1
- package/dist/commands/detect.d.ts +3 -0
- package/dist/commands/detect.d.ts.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/rename.d.ts.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/transform.d.ts.map +1 -1
- package/dist/index.cjs +47471 -42714
- package/dist/index.d.ts.map +1 -1
- package/dist/utils/adapter-preflight.d.ts +10 -0
- package/dist/utils/adapter-preflight.d.ts.map +1 -0
- package/i18n.config.json +14 -0
- package/package.json +4 -2
- package/src/commands/detect.ts +342 -0
- package/src/commands/init.test.ts +208 -1
- package/src/commands/init.ts +472 -195
- package/src/commands/rename.ts +13 -0
- package/src/commands/review.ts +1 -1
- package/src/commands/scan.ts +4 -1
- package/src/commands/sync.ts +22 -2
- package/src/commands/transform.ts +54 -2
- package/src/e2e.test.ts +4 -4
- package/src/fixtures/suspicious-keys/locales/en.json +8 -8
- package/src/fixtures/suspicious-keys/locales/fr.json +8 -8
- package/src/fixtures/suspicious-keys/preview.json +419 -0
- package/src/fixtures/suspicious-keys/src/BadKeys.tsx.backup +19 -0
- package/src/index.ts +3 -1
- package/src/integration.test.ts +2 -6
- package/src/rename-suspicious.test.ts +3 -3
- package/src/utils/adapter-preflight.ts +53 -0
- package/test.vue +33 -0
package/src/commands/rename.ts
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { promises as fs } from 'node:fs';
|
|
5
5
|
import { loadConfig, KeyRenamer, type KeyRenameSummary, type KeyRenameBatchSummary, type KeyRenameMapping } from '@i18nsmith/core';
|
|
6
6
|
import { applyPreviewFile, writePreviewFile } from '../utils/preview.js';
|
|
7
|
+
import { runAdapterPreflight } from '../utils/adapter-preflight.js';
|
|
7
8
|
import { CliError, withErrorHandling } from '../utils/errors.js';
|
|
8
9
|
|
|
9
10
|
interface ScanOptions {
|
|
@@ -59,6 +60,12 @@ export function registerRename(program: Command): void {
|
|
|
59
60
|
|
|
60
61
|
try {
|
|
61
62
|
const config = await loadConfig(options.config);
|
|
63
|
+
|
|
64
|
+
// Run preflight checks for write operations
|
|
65
|
+
if (writeEnabled) {
|
|
66
|
+
await runAdapterPreflight();
|
|
67
|
+
}
|
|
68
|
+
|
|
62
69
|
const renamer = new KeyRenamer(config);
|
|
63
70
|
const summary = await renamer.rename(oldKey, newKey, {
|
|
64
71
|
write: options.write,
|
|
@@ -113,6 +120,12 @@ export function registerRename(program: Command): void {
|
|
|
113
120
|
|
|
114
121
|
try {
|
|
115
122
|
const config = await loadConfig(options.config);
|
|
123
|
+
|
|
124
|
+
// Run preflight checks for write operations
|
|
125
|
+
if (options.write) {
|
|
126
|
+
await runAdapterPreflight();
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
const mappings = await loadRenameMappings(options.map);
|
|
117
130
|
const renamer = new KeyRenamer(config);
|
|
118
131
|
const summary = await renamer.renameBatch(mappings, { write: options.write, diff: options.diff });
|
package/src/commands/review.ts
CHANGED
|
@@ -138,7 +138,7 @@ export function registerReview(program: Command) {
|
|
|
138
138
|
withErrorHandling(async (options: ReviewCommandOptions) => {
|
|
139
139
|
try {
|
|
140
140
|
const { config, projectRoot, configPath } = await loadConfigWithMeta(options.config);
|
|
141
|
-
const scanner =
|
|
141
|
+
const scanner = await Scanner.create(config, { workspaceRoot: projectRoot });
|
|
142
142
|
const summary = scanner.scan({ scanCalls: options.scanCalls }) as BucketedScanSummary;
|
|
143
143
|
const buckets = summary.buckets ?? {};
|
|
144
144
|
const needsReview = buckets.needsReview ?? [];
|
package/src/commands/scan.ts
CHANGED
|
@@ -75,7 +75,10 @@ export function registerScan(program: Command) {
|
|
|
75
75
|
if (options.exclude?.length) {
|
|
76
76
|
config.exclude = options.exclude;
|
|
77
77
|
}
|
|
78
|
-
|
|
78
|
+
// Use factory that registers framework adapters (React/Vue) so
|
|
79
|
+
// scans include files handled by adapters. Backwards compatible
|
|
80
|
+
// API: Scanner.create will return a scanner with adapters wired.
|
|
81
|
+
const scanner = await Scanner.create(config, { workspaceRoot: projectRoot });
|
|
79
82
|
const summary = scanner.scan();
|
|
80
83
|
|
|
81
84
|
if (options.report) {
|
package/src/commands/sync.ts
CHANGED
|
@@ -554,12 +554,15 @@ export function registerSync(program: Command) {
|
|
|
554
554
|
|
|
555
555
|
// Handle --auto-rename-suspicious
|
|
556
556
|
if (options.autoRenameSuspicious && summary.suspiciousKeys.length > 0) {
|
|
557
|
-
await handleAutoRenameSuspicious(
|
|
557
|
+
const renameDiffs = await handleAutoRenameSuspicious(
|
|
558
558
|
summary,
|
|
559
559
|
options,
|
|
560
560
|
config,
|
|
561
561
|
projectRoot
|
|
562
562
|
);
|
|
563
|
+
if (renameDiffs && renameDiffs.length > 0) {
|
|
564
|
+
summary.renameDiffs = renameDiffs;
|
|
565
|
+
}
|
|
563
566
|
}
|
|
564
567
|
|
|
565
568
|
// Handle --rewrite-shape
|
|
@@ -857,7 +860,7 @@ async function handleAutoRenameSuspicious(
|
|
|
857
860
|
options: SyncCommandOptions,
|
|
858
861
|
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
859
862
|
projectRoot: string
|
|
860
|
-
) {
|
|
863
|
+
): Promise<any[] | undefined> {
|
|
861
864
|
console.log(chalk.blue("\nš Auto-rename suspicious keys analysis:"));
|
|
862
865
|
|
|
863
866
|
// Get existing keys from locale data to check for conflicts
|
|
@@ -1008,6 +1011,23 @@ async function handleAutoRenameSuspicious(
|
|
|
1008
1011
|
chalk.gray(" Run with --write to apply safe proposals automatically.")
|
|
1009
1012
|
);
|
|
1010
1013
|
}
|
|
1014
|
+
|
|
1015
|
+
// Generate diffs for preview mode
|
|
1016
|
+
if (options.diff && report.safeProposals.length > 0) {
|
|
1017
|
+
const mappings = report.safeProposals.map((proposal) => ({
|
|
1018
|
+
from: proposal.originalKey,
|
|
1019
|
+
to: proposal.proposedKey,
|
|
1020
|
+
}));
|
|
1021
|
+
|
|
1022
|
+
const renamer = new KeyRenamer(config, { workspaceRoot: projectRoot });
|
|
1023
|
+
const diffSummary = await renamer.renameBatch(mappings, {
|
|
1024
|
+
write: false,
|
|
1025
|
+
diff: true,
|
|
1026
|
+
allowConflicts: true,
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
return diffSummary.diffs;
|
|
1030
|
+
}
|
|
1011
1031
|
}
|
|
1012
1032
|
|
|
1013
1033
|
async function handleRewriteShape(
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
3
5
|
import chalk from 'chalk';
|
|
4
6
|
import type { Command } from 'commander';
|
|
5
7
|
import { loadConfigWithMeta } from '@i18nsmith/core';
|
|
@@ -8,6 +10,8 @@ import type { TransformProgress, TransformSummary } from '@i18nsmith/transformer
|
|
|
8
10
|
import { printLocaleDiffs, writeLocaleDiffPatches } from '../utils/diff-utils.js';
|
|
9
11
|
import { applyPreviewFile, writePreviewFile } from '../utils/preview.js';
|
|
10
12
|
import { CliError, withErrorHandling } from '../utils/errors.js';
|
|
13
|
+
import inquirer from 'inquirer';
|
|
14
|
+
import { detectPackageManager, installDependencies } from '../utils/package-manager.js';
|
|
11
15
|
|
|
12
16
|
interface TransformOptions {
|
|
13
17
|
config?: string;
|
|
@@ -32,6 +36,7 @@ const collectTargetPatterns = (value: string | string[], previous: string[]) =>
|
|
|
32
36
|
return [...previous, ...tokens];
|
|
33
37
|
};
|
|
34
38
|
|
|
39
|
+
/* Re-enabled after framework migration stabilization */
|
|
35
40
|
function printTransformSummary(summary: TransformSummary) {
|
|
36
41
|
const counts = summary.candidates.reduce(
|
|
37
42
|
(acc, c) => {
|
|
@@ -68,7 +73,8 @@ function printTransformSummary(summary: TransformSummary) {
|
|
|
68
73
|
|
|
69
74
|
console.table(preview);
|
|
70
75
|
|
|
71
|
-
const pending = summary.
|
|
76
|
+
const pending = summary.candidateStats?.pending
|
|
77
|
+
?? summary.candidates.filter((candidate) => candidate.status === 'pending').length;
|
|
72
78
|
if (pending > 0) {
|
|
73
79
|
console.log(
|
|
74
80
|
chalk.yellow(
|
|
@@ -97,6 +103,13 @@ function printTransformSummary(summary: TransformSummary) {
|
|
|
97
103
|
console.log(chalk.yellow('Skipped items:'));
|
|
98
104
|
summary.skippedFiles.forEach((item) => console.log(` ⢠${item.filePath}: ${item.reason}`));
|
|
99
105
|
}
|
|
106
|
+
|
|
107
|
+
if (summary.skippedReasons && Object.keys(summary.skippedReasons).length) {
|
|
108
|
+
console.log(chalk.yellow('Skipped reasons:'));
|
|
109
|
+
(Object.entries(summary.skippedReasons) as [string, number][])
|
|
110
|
+
.sort((a, b) => b[1] - a[1])
|
|
111
|
+
.forEach(([reason, count]) => console.log(` ⢠${reason}: ${count}`));
|
|
112
|
+
}
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
function createProgressLogger() {
|
|
@@ -194,7 +207,7 @@ export function registerTransform(program: Command) {
|
|
|
194
207
|
.option('--apply-preview <path>', 'Apply a previously saved transform preview JSON file safely')
|
|
195
208
|
.action(
|
|
196
209
|
withErrorHandling(async (options: TransformOptions) => {
|
|
197
|
-
|
|
210
|
+
if (options.applyPreview) {
|
|
198
211
|
await applyPreviewFile('transform', options.applyPreview);
|
|
199
212
|
return;
|
|
200
213
|
}
|
|
@@ -225,6 +238,45 @@ export function registerTransform(program: Command) {
|
|
|
225
238
|
console.log(chalk.gray(`Config found at ${path.relative(cwd, configPath)}`));
|
|
226
239
|
console.log(chalk.gray(`Using project root: ${projectRoot}\n`));
|
|
227
240
|
}
|
|
241
|
+
|
|
242
|
+
// Proactively check if Vue files are targeted but the parser is missing
|
|
243
|
+
const includesVue = config.include?.some(pattern => pattern.includes('.vue')) ?? false;
|
|
244
|
+
let isVueParserAvailable = false;
|
|
245
|
+
try {
|
|
246
|
+
const require = createRequire(import.meta.url);
|
|
247
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
248
|
+
require.resolve('vue-eslint-parser', { paths: [projectRoot, moduleDir] });
|
|
249
|
+
isVueParserAvailable = true;
|
|
250
|
+
} catch {
|
|
251
|
+
isVueParserAvailable = false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (includesVue && !isVueParserAvailable) {
|
|
255
|
+
console.log(chalk.yellow('ā ļø Vue files detected but "vue-eslint-parser" is not installed.'));
|
|
256
|
+
console.log(chalk.yellow(' Verification of Vue templates might be incomplete or fail.'));
|
|
257
|
+
|
|
258
|
+
if (process.stdout.isTTY) {
|
|
259
|
+
const { install } = await inquirer.prompt<{ install: boolean }>([
|
|
260
|
+
{
|
|
261
|
+
type: 'confirm',
|
|
262
|
+
name: 'install',
|
|
263
|
+
message: 'Do you want to install "vue-eslint-parser" (dev dependency) now?',
|
|
264
|
+
default: true,
|
|
265
|
+
},
|
|
266
|
+
]);
|
|
267
|
+
|
|
268
|
+
if (install) {
|
|
269
|
+
const pm = await detectPackageManager(projectRoot);
|
|
270
|
+
const cmd = pm === 'npm' ? 'npm install --save-dev vue-eslint-parser' : `${pm} add -D vue-eslint-parser`;
|
|
271
|
+
console.log(chalk.gray(`> ${cmd}`));
|
|
272
|
+
// We use installDependencies helper but need to pass dev flag args if not flexible
|
|
273
|
+
// The helper seems simple: const args = manager === 'npm' ? ['install', ...deps] : ['add', ...deps];
|
|
274
|
+
// So for dev we need to include -D in deps
|
|
275
|
+
await installDependencies(pm, ['-D', 'vue-eslint-parser'], projectRoot);
|
|
276
|
+
console.log(chalk.green('ā Installed. Continuing...'));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
228
280
|
|
|
229
281
|
const transformer = new Transformer(config, { workspaceRoot: projectRoot });
|
|
230
282
|
const progressLogger = createProgressLogger();
|
package/src/e2e.test.ts
CHANGED
|
@@ -281,13 +281,13 @@ describe('E2E Fixture Tests', () => {
|
|
|
281
281
|
const renameMap = parseRenameMap(mapContents);
|
|
282
282
|
const helloKey = renameMap['Hello World'];
|
|
283
283
|
expect(helloKey).toBeDefined();
|
|
284
|
-
const
|
|
285
|
-
expect(
|
|
284
|
+
const saveKey = renameMap['Save'];
|
|
285
|
+
expect(saveKey).toBeDefined();
|
|
286
286
|
|
|
287
287
|
expect(enLocale).toHaveProperty(helloKey!);
|
|
288
288
|
expect(frLocale).toHaveProperty(helloKey!);
|
|
289
|
-
expect(enLocale).toHaveProperty(
|
|
290
|
-
expect(frLocale).toHaveProperty(
|
|
289
|
+
expect(enLocale).toHaveProperty(saveKey!);
|
|
290
|
+
expect(frLocale).toHaveProperty(saveKey!);
|
|
291
291
|
|
|
292
292
|
const sourceFile = await fs.readFile(path.join(fixtureDir, 'src', 'BadKeys.tsx'), 'utf8');
|
|
293
293
|
expect(sourceFile).toContain(`t('${helloKey!}')`);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
2
|
+
"common.badkeys.buttons-submit.3b94a6": "Submit",
|
|
3
|
+
"common.badkeys.click-here.788c1e": "Click Here",
|
|
4
|
+
"common.badkeys.hello-world.66bf53": "Hello World",
|
|
5
|
+
"common.badkeys.save.a495bf": "Save",
|
|
6
|
+
"common.badkeys.welcome-to-our-app.a53bf0": "Welcome to our app!",
|
|
7
|
+
"common.title": "Application Title",
|
|
8
8
|
"proper.namespaced.key": "This is a properly namespaced key",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"The Quick Brown Fox": "The Quick Brown Fox",
|
|
10
|
+
"When to use this feature:": "When to use this feature:"
|
|
11
11
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
2
|
+
"common.badkeys.buttons-submit.3b94a6": "Soumettre",
|
|
3
|
+
"common.badkeys.click-here.788c1e": "Cliquez ici",
|
|
4
|
+
"common.badkeys.hello-world.66bf53": "Bonjour le monde",
|
|
5
|
+
"common.badkeys.save.a495bf": "Sauvegarder",
|
|
6
|
+
"common.badkeys.welcome-to-our-app.a53bf0": "Bienvenue dans notre application!",
|
|
7
|
+
"common.title": "Titre de l'application",
|
|
8
8
|
"proper.namespaced.key": "Ceci est une clƩ correctement nommƩe",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"The Quick Brown Fox": "Le Renard Brun Rapide",
|
|
10
|
+
"When to use this feature:": "Quand utiliser cette fonctionnalitƩ:"
|
|
11
11
|
}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "sync-preview",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"command": "i18nsmith sync --diff --auto-rename-suspicious --preview-output preview.json",
|
|
5
|
+
"args": [
|
|
6
|
+
"sync",
|
|
7
|
+
"--diff",
|
|
8
|
+
"--auto-rename-suspicious",
|
|
9
|
+
"--preview-output",
|
|
10
|
+
"preview.json"
|
|
11
|
+
],
|
|
12
|
+
"timestamp": "2026-02-08T16:49:09.709Z",
|
|
13
|
+
"summary": {
|
|
14
|
+
"filesScanned": 1,
|
|
15
|
+
"references": [
|
|
16
|
+
{
|
|
17
|
+
"key": "Hello World",
|
|
18
|
+
"filePath": "src/BadKeys.tsx",
|
|
19
|
+
"position": {
|
|
20
|
+
"line": 9,
|
|
21
|
+
"column": 12
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"key": "Welcome to our app!",
|
|
26
|
+
"filePath": "src/BadKeys.tsx",
|
|
27
|
+
"position": {
|
|
28
|
+
"line": 10,
|
|
29
|
+
"column": 11
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"key": "Click Here",
|
|
34
|
+
"filePath": "src/BadKeys.tsx",
|
|
35
|
+
"position": {
|
|
36
|
+
"line": 11,
|
|
37
|
+
"column": 20
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"key": "Save",
|
|
42
|
+
"filePath": "src/BadKeys.tsx",
|
|
43
|
+
"position": {
|
|
44
|
+
"line": 12,
|
|
45
|
+
"column": 16
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"key": "proper.namespaced.key",
|
|
50
|
+
"filePath": "src/BadKeys.tsx",
|
|
51
|
+
"position": {
|
|
52
|
+
"line": 15,
|
|
53
|
+
"column": 11
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"key": "buttons.submit",
|
|
58
|
+
"filePath": "src/BadKeys.tsx",
|
|
59
|
+
"position": {
|
|
60
|
+
"line": 16,
|
|
61
|
+
"column": 16
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"missingKeys": [
|
|
66
|
+
{
|
|
67
|
+
"key": "Hello World",
|
|
68
|
+
"references": [
|
|
69
|
+
{
|
|
70
|
+
"key": "Hello World",
|
|
71
|
+
"filePath": "src/BadKeys.tsx",
|
|
72
|
+
"position": {
|
|
73
|
+
"line": 9,
|
|
74
|
+
"column": 12
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"suspicious": true
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"key": "Welcome to our app!",
|
|
82
|
+
"references": [
|
|
83
|
+
{
|
|
84
|
+
"key": "Welcome to our app!",
|
|
85
|
+
"filePath": "src/BadKeys.tsx",
|
|
86
|
+
"position": {
|
|
87
|
+
"line": 10,
|
|
88
|
+
"column": 11
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"suspicious": true
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"key": "Click Here",
|
|
96
|
+
"references": [
|
|
97
|
+
{
|
|
98
|
+
"key": "Click Here",
|
|
99
|
+
"filePath": "src/BadKeys.tsx",
|
|
100
|
+
"position": {
|
|
101
|
+
"line": 11,
|
|
102
|
+
"column": 20
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"suspicious": true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"key": "Save",
|
|
110
|
+
"references": [
|
|
111
|
+
{
|
|
112
|
+
"key": "Save",
|
|
113
|
+
"filePath": "src/BadKeys.tsx",
|
|
114
|
+
"position": {
|
|
115
|
+
"line": 12,
|
|
116
|
+
"column": 16
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"suspicious": true
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"key": "buttons.submit",
|
|
124
|
+
"references": [
|
|
125
|
+
{
|
|
126
|
+
"key": "buttons.submit",
|
|
127
|
+
"filePath": "src/BadKeys.tsx",
|
|
128
|
+
"position": {
|
|
129
|
+
"line": 16,
|
|
130
|
+
"column": 16
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"suspicious": false
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
"unusedKeys": [
|
|
138
|
+
{
|
|
139
|
+
"key": "common.badkeys.buttons-submit.3b94a6",
|
|
140
|
+
"locales": [
|
|
141
|
+
"en",
|
|
142
|
+
"fr"
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"key": "common.badkeys.click-here.788c1e",
|
|
147
|
+
"locales": [
|
|
148
|
+
"en",
|
|
149
|
+
"fr"
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"key": "common.badkeys.hello-world.66bf53",
|
|
154
|
+
"locales": [
|
|
155
|
+
"en",
|
|
156
|
+
"fr"
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"key": "common.badkeys.save.a495bf",
|
|
161
|
+
"locales": [
|
|
162
|
+
"en",
|
|
163
|
+
"fr"
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"key": "common.badkeys.welcome-to-our-app.a53bf0",
|
|
168
|
+
"locales": [
|
|
169
|
+
"en",
|
|
170
|
+
"fr"
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"key": "common.title",
|
|
175
|
+
"locales": [
|
|
176
|
+
"en",
|
|
177
|
+
"fr"
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"key": "The Quick Brown Fox",
|
|
182
|
+
"locales": [
|
|
183
|
+
"en",
|
|
184
|
+
"fr"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"key": "When to use this feature:",
|
|
189
|
+
"locales": [
|
|
190
|
+
"en",
|
|
191
|
+
"fr"
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"localeStats": [],
|
|
196
|
+
"localePreview": [],
|
|
197
|
+
"diffs": [],
|
|
198
|
+
"localeDiffs": [],
|
|
199
|
+
"placeholderIssues": [],
|
|
200
|
+
"emptyValueViolations": [],
|
|
201
|
+
"dynamicKeyWarnings": [],
|
|
202
|
+
"suspiciousKeys": [
|
|
203
|
+
{
|
|
204
|
+
"key": "Hello World",
|
|
205
|
+
"filePath": "src/BadKeys.tsx",
|
|
206
|
+
"position": {
|
|
207
|
+
"line": 9,
|
|
208
|
+
"column": 12
|
|
209
|
+
},
|
|
210
|
+
"reason": "contains-spaces"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"key": "Welcome to our app!",
|
|
214
|
+
"filePath": "src/BadKeys.tsx",
|
|
215
|
+
"position": {
|
|
216
|
+
"line": 10,
|
|
217
|
+
"column": 11
|
|
218
|
+
},
|
|
219
|
+
"reason": "contains-spaces"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"key": "Click Here",
|
|
223
|
+
"filePath": "src/BadKeys.tsx",
|
|
224
|
+
"position": {
|
|
225
|
+
"line": 11,
|
|
226
|
+
"column": 20
|
|
227
|
+
},
|
|
228
|
+
"reason": "contains-spaces"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"key": "Save",
|
|
232
|
+
"filePath": "src/BadKeys.tsx",
|
|
233
|
+
"position": {
|
|
234
|
+
"line": 12,
|
|
235
|
+
"column": 16
|
|
236
|
+
},
|
|
237
|
+
"reason": "single-word-no-namespace"
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"validation": {
|
|
241
|
+
"interpolations": false,
|
|
242
|
+
"emptyValuePolicy": "warn"
|
|
243
|
+
},
|
|
244
|
+
"assumedKeys": [],
|
|
245
|
+
"write": false,
|
|
246
|
+
"actionableItems": [
|
|
247
|
+
{
|
|
248
|
+
"kind": "suspicious-keys",
|
|
249
|
+
"severity": "warn",
|
|
250
|
+
"message": "4 suspicious keys detected (contains spaces or special chars) ā auto-insert skipped until keys are renamed.",
|
|
251
|
+
"details": {
|
|
252
|
+
"count": 4,
|
|
253
|
+
"policy": "skip",
|
|
254
|
+
"keys": [
|
|
255
|
+
"Hello World",
|
|
256
|
+
"Welcome to our app!",
|
|
257
|
+
"Click Here",
|
|
258
|
+
"Save"
|
|
259
|
+
],
|
|
260
|
+
"fallbackLiterals": []
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"kind": "missing-key",
|
|
265
|
+
"severity": "error",
|
|
266
|
+
"key": "Hello World",
|
|
267
|
+
"filePath": "src/BadKeys.tsx",
|
|
268
|
+
"message": "Key \"Hello World\" referenced 1 time but missing from source locale",
|
|
269
|
+
"details": {
|
|
270
|
+
"referenceCount": 1
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"kind": "missing-key",
|
|
275
|
+
"severity": "error",
|
|
276
|
+
"key": "Welcome to our app!",
|
|
277
|
+
"filePath": "src/BadKeys.tsx",
|
|
278
|
+
"message": "Key \"Welcome to our app!\" referenced 1 time but missing from source locale",
|
|
279
|
+
"details": {
|
|
280
|
+
"referenceCount": 1
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"kind": "missing-key",
|
|
285
|
+
"severity": "error",
|
|
286
|
+
"key": "Click Here",
|
|
287
|
+
"filePath": "src/BadKeys.tsx",
|
|
288
|
+
"message": "Key \"Click Here\" referenced 1 time but missing from source locale",
|
|
289
|
+
"details": {
|
|
290
|
+
"referenceCount": 1
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"kind": "missing-key",
|
|
295
|
+
"severity": "error",
|
|
296
|
+
"key": "Save",
|
|
297
|
+
"filePath": "src/BadKeys.tsx",
|
|
298
|
+
"message": "Key \"Save\" referenced 1 time but missing from source locale",
|
|
299
|
+
"details": {
|
|
300
|
+
"referenceCount": 1
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"kind": "missing-key",
|
|
305
|
+
"severity": "error",
|
|
306
|
+
"key": "buttons.submit",
|
|
307
|
+
"filePath": "src/BadKeys.tsx",
|
|
308
|
+
"message": "Key \"buttons.submit\" referenced 1 time but missing from source locale",
|
|
309
|
+
"details": {
|
|
310
|
+
"referenceCount": 1
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"kind": "unused-key",
|
|
315
|
+
"severity": "warn",
|
|
316
|
+
"key": "common.badkeys.buttons-submit.3b94a6",
|
|
317
|
+
"message": "Key \"common.badkeys.buttons-submit.3b94a6\" is present in locales (en, fr) but not referenced in code",
|
|
318
|
+
"details": {
|
|
319
|
+
"locales": [
|
|
320
|
+
"en",
|
|
321
|
+
"fr"
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"kind": "unused-key",
|
|
327
|
+
"severity": "warn",
|
|
328
|
+
"key": "common.badkeys.click-here.788c1e",
|
|
329
|
+
"message": "Key \"common.badkeys.click-here.788c1e\" is present in locales (en, fr) but not referenced in code",
|
|
330
|
+
"details": {
|
|
331
|
+
"locales": [
|
|
332
|
+
"en",
|
|
333
|
+
"fr"
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"kind": "unused-key",
|
|
339
|
+
"severity": "warn",
|
|
340
|
+
"key": "common.badkeys.hello-world.66bf53",
|
|
341
|
+
"message": "Key \"common.badkeys.hello-world.66bf53\" is present in locales (en, fr) but not referenced in code",
|
|
342
|
+
"details": {
|
|
343
|
+
"locales": [
|
|
344
|
+
"en",
|
|
345
|
+
"fr"
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"kind": "unused-key",
|
|
351
|
+
"severity": "warn",
|
|
352
|
+
"key": "common.badkeys.save.a495bf",
|
|
353
|
+
"message": "Key \"common.badkeys.save.a495bf\" is present in locales (en, fr) but not referenced in code",
|
|
354
|
+
"details": {
|
|
355
|
+
"locales": [
|
|
356
|
+
"en",
|
|
357
|
+
"fr"
|
|
358
|
+
]
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"kind": "unused-key",
|
|
363
|
+
"severity": "warn",
|
|
364
|
+
"key": "common.badkeys.welcome-to-our-app.a53bf0",
|
|
365
|
+
"message": "Key \"common.badkeys.welcome-to-our-app.a53bf0\" is present in locales (en, fr) but not referenced in code",
|
|
366
|
+
"details": {
|
|
367
|
+
"locales": [
|
|
368
|
+
"en",
|
|
369
|
+
"fr"
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"kind": "unused-key",
|
|
375
|
+
"severity": "warn",
|
|
376
|
+
"key": "common.title",
|
|
377
|
+
"message": "Key \"common.title\" is present in locales (en, fr) but not referenced in code",
|
|
378
|
+
"details": {
|
|
379
|
+
"locales": [
|
|
380
|
+
"en",
|
|
381
|
+
"fr"
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
"kind": "unused-key",
|
|
387
|
+
"severity": "warn",
|
|
388
|
+
"key": "The Quick Brown Fox",
|
|
389
|
+
"message": "Key \"The Quick Brown Fox\" is present in locales (en, fr) but not referenced in code",
|
|
390
|
+
"details": {
|
|
391
|
+
"locales": [
|
|
392
|
+
"en",
|
|
393
|
+
"fr"
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"kind": "unused-key",
|
|
399
|
+
"severity": "warn",
|
|
400
|
+
"key": "When to use this feature:",
|
|
401
|
+
"message": "Key \"When to use this feature:\" is present in locales (en, fr) but not referenced in code",
|
|
402
|
+
"details": {
|
|
403
|
+
"locales": [
|
|
404
|
+
"en",
|
|
405
|
+
"fr"
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
],
|
|
410
|
+
"renameDiffs": [
|
|
411
|
+
{
|
|
412
|
+
"path": "/Users/arturlavrov/Documents/GitHub/i18nsmith/packages/cli/src/fixtures/suspicious-keys/src/BadKeys.tsx",
|
|
413
|
+
"relativePath": "src/BadKeys.tsx",
|
|
414
|
+
"diff": "Index: src/BadKeys.tsx\n===================================================================\n--- src/BadKeys.tsx\n+++ src/BadKeys.tsx\n@@ -5,12 +5,12 @@\n \n return (\n <div>\n {/* These are bad keys - text as key */}\n- <h1>{t('Hello World')}</h1>\n- <p>{t('Welcome to our app!')}</p>\n- <a href=\"#\">{t('Click Here')}</a>\n- <button>{t('Save')}</button>\n+ <h1>{t('common.badkeys.hello-world.66bf53')}</h1>\n+ <p>{t('common.badkeys.welcome-to-our-app.a53bf0')}</p>\n+ <a href=\"#\">{t('common.badkeys.click-here.788c1e')}</a>\n+ <button>{t('common.badkeys.save.a495bf')}</button>\n \n {/* These are good keys */}\n <p>{t('proper.namespaced.key')}</p>\n <button>{t('buttons.submit')}</button>\n",
|
|
415
|
+
"changes": 8
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
}
|