i18ntk 1.10.2 ā 2.0.2
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 +1 -1
- package/README.md +141 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -29
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +117 -117
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +157 -161
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +18 -18
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -454
package/main/i18ntk-fixer.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
|
-
* I18NTK TRANSLATION FIXER
|
|
4
|
+
* I18NTK TRANSLATION FIXER SCRIPT
|
|
5
|
+
*
|
|
6
|
+
* This script is responsible for fixing issues in translation files,
|
|
7
|
+
* such as adding missing keys or correcting untranslated markers.
|
|
4
8
|
*
|
|
5
|
-
* Replaces placeholder translations with English source text prefixed by language code
|
|
6
|
-
* and optionally fills missing keys.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
|
-
const fs = require('fs');
|
|
10
11
|
const path = require('path');
|
|
11
|
-
const
|
|
12
|
-
const { loadTranslations } = require('../utils/i18n-helper');
|
|
12
|
+
const fs = require('fs');
|
|
13
13
|
const SecurityUtils = require('../utils/security');
|
|
14
|
-
const
|
|
14
|
+
const cliHelper = require('../utils/cli-helper');
|
|
15
|
+
const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
16
|
+
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
|
|
17
|
+
const JsonOutput = require('../utils/json-output');
|
|
15
18
|
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
16
19
|
|
|
17
20
|
// Ensure setup is complete before running
|
|
@@ -24,754 +27,87 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
24
27
|
}
|
|
25
28
|
})();
|
|
26
29
|
|
|
27
|
-
loadTranslations(
|
|
30
|
+
loadTranslations('en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
28
31
|
|
|
29
32
|
class I18nFixer {
|
|
30
33
|
constructor(config = {}) {
|
|
31
34
|
this.config = config;
|
|
32
|
-
this.sourceDir = null;
|
|
33
|
-
this.sourceLanguageDir = null;
|
|
34
|
-
this.markers = [];
|
|
35
|
-
this.languages = [];
|
|
36
|
-
this.locale = this.loadLocale();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
loadLocale() {
|
|
40
|
-
const uiLocalesDir = path.join(__dirname, '..', 'ui-locales');
|
|
41
|
-
const localeFile = path.join(uiLocalesDir, 'en.json');
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const localeContent = fs.readFileSync(localeFile, 'utf8');
|
|
45
|
-
return JSON.parse(localeContent);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
// Fallback to basic English strings if locale file not found
|
|
48
|
-
return {
|
|
49
|
-
fixer: {
|
|
50
|
-
help_options: {
|
|
51
|
-
source_dir: "Source directory to scan (default: ./locales)",
|
|
52
|
-
languages: "Comma separated list of languages to fix",
|
|
53
|
-
markers: "Comma separated markers to treat as untranslated",
|
|
54
|
-
no_backup: "Skip automatic backup creation"
|
|
55
|
-
},
|
|
56
|
-
starting: "š Starting translation fixing for languages: {languages}",
|
|
57
|
-
sourceDirectory: "š Source directory: {sourceDir}",
|
|
58
|
-
sourceLanguage: "š¤ Source language: {sourceLanguage}",
|
|
59
|
-
markers: "š·ļø Markers to fix: {markers}",
|
|
60
|
-
scanningLanguage: "š Scanning {language}...",
|
|
61
|
-
noLanguages: "ā No languages specified for fixing.",
|
|
62
|
-
allComplete: "š All translations are already complete!",
|
|
63
|
-
fullReportSaved: "š Full report saved to: {reportPath}",
|
|
64
|
-
reviewReport: "Please review the report before proceeding.",
|
|
65
|
-
backupCreated: "š¾ Backup created successfully.",
|
|
66
|
-
applyingFixes: "š Applying fixes...",
|
|
67
|
-
fixingComplete: "ā
Translation fixing complete!",
|
|
68
|
-
operationCancelled: "ā Operation cancelled by user.",
|
|
69
|
-
analysisTitle: "š TRANSLATION FIXING ANALYSIS",
|
|
70
|
-
analysisSeparator: "==================================================",
|
|
71
|
-
totalIssues: "Total issues found: {totalIssues}",
|
|
72
|
-
missingTranslations: "Missing translations: {missing}",
|
|
73
|
-
placeholderTranslations: "Placeholder translations: {placeholder}",
|
|
74
|
-
noIssues: "ā
No issues found. All translations are complete.",
|
|
75
|
-
detailedIssues: "š DETAILED ISSUES:",
|
|
76
|
-
detailedSeparator: "--------------------------------------------------",
|
|
77
|
-
filePath: "š {file} ā {path}",
|
|
78
|
-
missingKey: "ā MISSING: {source} ā {new}",
|
|
79
|
-
placeholderKey: "ā ļø PLACEHOLDER: \"{target}\" ā \"{new}\"",
|
|
80
|
-
moreIssues: "... and {count} more issues. Check the report file for complete details.",
|
|
81
|
-
confirmationTitle: "š¤ Do you want to proceed with these fixes?",
|
|
82
|
-
confirmationOptions: "Options:",
|
|
83
|
-
optionYes: "y - Yes, apply all fixes",
|
|
84
|
-
optionNo: "n - No, cancel operation",
|
|
85
|
-
optionShow: "s - Show detailed issues",
|
|
86
|
-
choicePrompt: "Your choice (y/n/s): ",
|
|
87
|
-
nonInteractiveMode: "ā” Non-interactive mode detected - applying fixes automatically...",
|
|
88
|
-
reportGenerated: "š Fixer report generated: {path}",
|
|
89
|
-
summary: {
|
|
90
|
-
totalIssues: "Total issues: {total}",
|
|
91
|
-
missingKeys: "Missing keys: {missing}",
|
|
92
|
-
placeholderKeys: "Placeholder keys: {placeholder}",
|
|
93
|
-
languages: "Languages: {languages}"
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
t(key, params = {}) {
|
|
101
|
-
// Ensure key is a string
|
|
102
|
-
const keyStr = String(key || '');
|
|
103
|
-
const keys = keyStr.split('.');
|
|
104
|
-
let value = this.locale;
|
|
105
|
-
|
|
106
|
-
for (const k of keys) {
|
|
107
|
-
value = value?.[k];
|
|
108
|
-
if (value === undefined) break;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (typeof value !== 'string') {
|
|
112
|
-
return key; // Fallback to key if translation not found
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return value.replace(/\{([^}]+)\}/g, (match, param) => {
|
|
116
|
-
return params[param] !== undefined ? params[param] : match;
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
parseArgs() {
|
|
121
|
-
const args = process.argv.slice(2);
|
|
122
|
-
const parsed = {};
|
|
123
|
-
args.forEach(arg => {
|
|
124
|
-
if (arg.startsWith('--')) {
|
|
125
|
-
const [key, ...valueParts] = arg.substring(2).split('=');
|
|
126
|
-
const value = valueParts.join('=');
|
|
127
|
-
|
|
128
|
-
if (key === 'source-dir') {
|
|
129
|
-
parsed.sourceDir = value || '';
|
|
130
|
-
} else if (key === 'source-language') {
|
|
131
|
-
parsed.sourceLanguage = value || '';
|
|
132
|
-
} else if (key === 'languages') {
|
|
133
|
-
parsed.languages = value ? value.split(',').map(l => l.trim()).filter(Boolean) : [];
|
|
134
|
-
} else if (key === 'markers') {
|
|
135
|
-
parsed.markers = value ? value.split(',').map(m => m.trim()).filter(Boolean) : [];
|
|
136
|
-
} else if (key === 'no-backup') {
|
|
137
|
-
parsed.noBackup = true;
|
|
138
|
-
} else if (key === 'help' || key === 'h') {
|
|
139
|
-
parsed.help = true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
return parsed;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async promptForMarkers() {
|
|
147
|
-
const { ask } = require('../utils/cli.js');
|
|
148
|
-
|
|
149
|
-
const defaultMarkers = ['__NOT_TRANSLATED__', 'NOT_TRANSLATED', 'TODO_TRANSLATE'];
|
|
150
|
-
console.log(`\n${this.t('fixer.markerPrompt.title')}`);
|
|
151
|
-
console.log(this.t('fixer.markerPrompt.description'));
|
|
152
|
-
console.log(this.t('fixer.markerPrompt.currentDefaults', { markers: defaultMarkers.join(', ') }));
|
|
153
|
-
|
|
154
|
-
const answer = await ask(this.t('fixer.markerPrompt.input'));
|
|
155
|
-
const cleanAnswer = answer.trim();
|
|
156
|
-
if (cleanAnswer) {
|
|
157
|
-
const markers = cleanAnswer.split(',').map(m => m.trim()).filter(Boolean);
|
|
158
|
-
return markers;
|
|
159
|
-
} else {
|
|
160
|
-
return defaultMarkers;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async promptForLanguages() {
|
|
165
|
-
const { ask } = require('../utils/cli.js');
|
|
166
|
-
|
|
167
|
-
const availableLanguages = this.getAvailableLanguages().filter(l => l !== this.config.sourceLanguage);
|
|
168
|
-
|
|
169
|
-
if (availableLanguages.length === 0) {
|
|
170
|
-
console.log(this.t('fixer.languagePrompt.noLanguages'));
|
|
171
|
-
return [];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
console.log(`\n${this.t('fixer.languagePrompt.title')}`);
|
|
175
|
-
console.log(this.t('fixer.languagePrompt.available', { languages: availableLanguages.join(', ') }));
|
|
176
|
-
console.log(this.t('fixer.languagePrompt.description'));
|
|
177
|
-
|
|
178
|
-
const answer = await ask(this.t('fixer.languagePrompt.input'));
|
|
179
|
-
const cleanAnswer = answer.trim();
|
|
180
|
-
if (cleanAnswer) {
|
|
181
|
-
const languages = cleanAnswer.split(',').map(l => l.trim()).filter(Boolean);
|
|
182
|
-
// Validate languages exist
|
|
183
|
-
const validLanguages = languages.filter(l => availableLanguages.includes(l));
|
|
184
|
-
return validLanguages;
|
|
185
|
-
} else {
|
|
186
|
-
return availableLanguages;
|
|
187
|
-
}
|
|
188
35
|
}
|
|
189
36
|
|
|
190
|
-
async
|
|
191
|
-
const { ask } = require('../utils/cli.js');
|
|
192
|
-
|
|
193
|
-
const defaultDir = this.config.sourceDir || './locales';
|
|
194
|
-
const projectRoot = this.config.projectRoot || process.cwd();
|
|
195
|
-
|
|
196
|
-
// Build candidate directories (existing + common defaults)
|
|
197
|
-
const candidates = new Set();
|
|
198
|
-
const addIf = p => {
|
|
199
|
-
try {
|
|
200
|
-
const abs = path.isAbsolute(p) ? p : path.resolve(projectRoot, p);
|
|
201
|
-
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
|
|
202
|
-
candidates.add(configManager.toRelative(abs));
|
|
203
|
-
}
|
|
204
|
-
} catch (_) { /* ignore */ }
|
|
205
|
-
};
|
|
206
|
-
// Common locations
|
|
207
|
-
['.','./locales','./src/locales','./i18n','./public/locales','./app/locales'].forEach(addIf);
|
|
208
|
-
// Scan immediate subdirectories under project root for likely i18n dirs
|
|
37
|
+
async initialize() {
|
|
209
38
|
try {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
addIf(dir);
|
|
218
|
-
}
|
|
39
|
+
const args = this.parseArgs();
|
|
40
|
+
if (args.help) {
|
|
41
|
+
displayHelp('i18ntk-fixer', {
|
|
42
|
+
'source-dir': 'Source directory to scan (default: ./locales)',
|
|
43
|
+
'languages': 'Comma separated list of languages to fix',
|
|
44
|
+
'markers': 'Comma separated markers to treat as untranslated',
|
|
45
|
+
'no-backup': 'Skip automatic backup creation'
|
|
219
46
|
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// Ensure default dir shown (even if not existing yet)
|
|
223
|
-
if (!Array.from(candidates).includes(defaultDir)) {
|
|
224
|
-
candidates.add(defaultDir);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const options = Array.from(candidates);
|
|
228
|
-
|
|
229
|
-
console.log(`\n${this.t('fixer.directoryPrompt.title')}`);
|
|
230
|
-
console.log(this.t('fixer.directoryPrompt.current', { dir: defaultDir }));
|
|
231
|
-
console.log(this.t('fixer.directoryPrompt.description'));
|
|
232
|
-
if (options.length > 0) {
|
|
233
|
-
console.log('\nOptions:');
|
|
234
|
-
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
235
|
-
options.forEach((opt, idx) => {
|
|
236
|
-
const label = letters[idx] || `${idx+1}`;
|
|
237
|
-
console.log(` ${label}) ${opt}`);
|
|
238
|
-
});
|
|
239
|
-
console.log(' *) Enter a custom path');
|
|
240
|
-
console.log(' 0) Exit/Cancel');
|
|
241
|
-
} else {
|
|
242
|
-
console.log('\nOptions:');
|
|
243
|
-
console.log(' *) Enter a custom path');
|
|
244
|
-
console.log(' 0) Exit/Cancel');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const answer = await ask(this.t('fixer.directoryPrompt.input'));
|
|
248
|
-
let input = answer.trim();
|
|
249
|
-
|
|
250
|
-
// Check for exit
|
|
251
|
-
if (input === '0') {
|
|
252
|
-
console.log('Operation cancelled by user.');
|
|
253
|
-
process.exit(0);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Map letter/number selection to option
|
|
257
|
-
if (input.length === 1) {
|
|
258
|
-
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
259
|
-
const pos = letters.indexOf(input.toUpperCase());
|
|
260
|
-
if (pos >= 0 && pos < options.length) {
|
|
261
|
-
input = options[pos];
|
|
262
|
-
}
|
|
263
|
-
} else if (/^\d+$/.test(input)) {
|
|
264
|
-
const num = parseInt(input, 10) - 1;
|
|
265
|
-
if (num >= 0 && num < options.length) {
|
|
266
|
-
input = options[num];
|
|
47
|
+
process.exit(0);
|
|
267
48
|
}
|
|
268
|
-
}
|
|
269
49
|
|
|
270
|
-
|
|
271
|
-
|
|
50
|
+
const baseConfig = await getUnifiedConfig('fixer', args);
|
|
51
|
+
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
272
52
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (!safePath) {
|
|
276
|
-
console.warn('Invalid or unsafe directory path. Using default.');
|
|
277
|
-
return defaultDir;
|
|
278
|
-
}
|
|
279
|
-
if (!fs.existsSync(safePath)) {
|
|
280
|
-
try {
|
|
281
|
-
fs.mkdirSync(safePath, { recursive: true });
|
|
282
|
-
} catch (err) {
|
|
283
|
-
console.warn(`Failed to create directory: ${err.message}`);
|
|
284
|
-
return defaultDir;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
53
|
+
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
54
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
287
55
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const rel = configManager.toRelative(safePath);
|
|
291
|
-
await configManager.updateConfig({ sourceDir: rel, i18nDir: rel });
|
|
292
|
-
// Refresh in-memory config values
|
|
293
|
-
this.config.sourceDir = path.resolve(projectRoot, rel);
|
|
294
|
-
this.config.i18nDir = this.config.sourceDir;
|
|
295
|
-
} catch (err) {
|
|
296
|
-
console.warn(`Warning: could not persist directory selection: ${err.message}`);
|
|
297
|
-
}
|
|
56
|
+
this.sourceDir = this.config.sourceDir;
|
|
57
|
+
this.outputDir = this.config.outputDir;
|
|
298
58
|
|
|
299
|
-
|
|
300
|
-
|
|
59
|
+
const { validateSourceDir } = require('../utils/config-helper');
|
|
60
|
+
validateSourceDir(this.sourceDir, 'i18ntk-fixer');
|
|
301
61
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
displayHelp('i18ntk-fixer', {
|
|
306
|
-
'markers': this.t('fixer.help_options.markers'),
|
|
307
|
-
'languages': this.t('fixer.help_options.languages'),
|
|
308
|
-
'no-backup': this.t('fixer.help_options.no_backup')
|
|
309
|
-
});
|
|
310
|
-
process.exit(0);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const baseConfig = await getUnifiedConfig('fixer', args);
|
|
314
|
-
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
315
|
-
|
|
316
|
-
// Interactive mode - prompt for settings if not provided via CLI
|
|
317
|
-
if (!args['source-dir'] && !args.languages && !args.markers && !this.config.noBackup) {
|
|
318
|
-
console.log(`\n${this.t('fixer.welcome.title')}`);
|
|
319
|
-
console.log(this.t('fixer.welcome.description'));
|
|
320
|
-
|
|
321
|
-
// Prompt for directory (with selection + persistence)
|
|
322
|
-
const customDir = await this.promptForDirectory();
|
|
323
|
-
let sourceDir = customDir || this.config.sourceDir || './locales';
|
|
324
|
-
if (typeof sourceDir === 'string') {
|
|
325
|
-
sourceDir = sourceDir.replace(/^['"]|['"]$/g, '');
|
|
326
|
-
}
|
|
327
|
-
if (sourceDir && typeof sourceDir === 'string') {
|
|
328
|
-
this.config.sourceDir = path.isAbsolute(sourceDir) ? sourceDir : path.resolve(process.cwd(), sourceDir);
|
|
329
|
-
this.config.i18nDir = this.config.sourceDir;
|
|
330
|
-
} else {
|
|
331
|
-
// Fallback to default
|
|
332
|
-
this.config.sourceDir = path.resolve(process.cwd(), './locales');
|
|
333
|
-
this.config.i18nDir = this.config.sourceDir;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Prompt for markers
|
|
337
|
-
const customMarkers = await this.promptForMarkers();
|
|
338
|
-
this.markers = customMarkers;
|
|
339
|
-
|
|
340
|
-
// Prompt for languages
|
|
341
|
-
const customLanguages = await this.promptForLanguages();
|
|
342
|
-
this.languages = customLanguages;
|
|
343
|
-
} else {
|
|
344
|
-
// CLI mode - use provided arguments or defaults
|
|
345
|
-
let sourceDir = args['source-dir'] || this.config.sourceDir || './locales';
|
|
346
|
-
sourceDir = sourceDir.replace(/^["']|["']$/g, '');
|
|
347
|
-
|
|
348
|
-
if (path.isAbsolute(sourceDir)) {
|
|
349
|
-
this.sourceDir = sourceDir;
|
|
350
|
-
} else {
|
|
351
|
-
this.sourceDir = path.resolve(process.cwd(), sourceDir);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const baseMarkers = this.config.notTranslatedMarkers || [this.config.notTranslatedMarker || '__NOT_TRANSLATED__'];
|
|
355
|
-
let markerArg = args.markers;
|
|
356
|
-
if (typeof markerArg === 'string') {
|
|
357
|
-
markerArg = markerArg.split(',').map(m => m.trim()).filter(Boolean);
|
|
358
|
-
} else if (!Array.isArray(markerArg)) {
|
|
359
|
-
markerArg = [];
|
|
360
|
-
}
|
|
361
|
-
this.markers = [...baseMarkers, ...markerArg].filter(Boolean);
|
|
362
|
-
|
|
363
|
-
const langArg = args.languages || this.config.languages;
|
|
364
|
-
if (typeof langArg === 'string') {
|
|
365
|
-
this.languages = langArg.split(',').map(l => l.trim()).filter(Boolean);
|
|
366
|
-
} else if (Array.isArray(langArg)) {
|
|
367
|
-
this.languages = langArg;
|
|
368
|
-
} else {
|
|
369
|
-
this.languages = this.getAvailableLanguages().filter(l => l !== this.config.sourceLanguage);
|
|
370
|
-
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error(`Fatal fixer error: ${error.message}`);
|
|
64
|
+
throw error;
|
|
371
65
|
}
|
|
372
|
-
|
|
373
|
-
this.sourceLanguageDir = path.join(this.sourceDir || path.resolve(process.cwd(), './locales'), this.config.sourceLanguage);
|
|
374
|
-
this.config.outputDir = this.config.outputDir || './i18ntk-reports';
|
|
375
|
-
this.config.noBackup = args['no-backup'] || false;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
getAvailableLanguages() {
|
|
379
|
-
if (!fs.existsSync(this.sourceDir)) return [];
|
|
380
|
-
const entries = fs.readdirSync(this.sourceDir);
|
|
381
|
-
const langs = new Set();
|
|
382
|
-
entries.forEach(item => {
|
|
383
|
-
const full = path.join(this.sourceDir, item);
|
|
384
|
-
if (fs.statSync(full).isDirectory()) {
|
|
385
|
-
langs.add(item);
|
|
386
|
-
} else if (item.endsWith('.json')) {
|
|
387
|
-
langs.add(path.basename(item, '.json'));
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
return Array.from(langs);
|
|
391
66
|
}
|
|
392
67
|
|
|
393
|
-
|
|
68
|
+
parseArgs() {
|
|
394
69
|
try {
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
} else if (stat.isFile() && item.endsWith('.json')) {
|
|
413
|
-
results.push(full);
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
return results;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
fixObject(target, source, lang) {
|
|
420
|
-
Object.keys(source).forEach(key => {
|
|
421
|
-
const srcVal = source[key];
|
|
422
|
-
const tgtVal = target[key];
|
|
423
|
-
if (srcVal && typeof srcVal === 'object' && !Array.isArray(srcVal)) {
|
|
424
|
-
target[key] = this.fixObject(
|
|
425
|
-
tgtVal && typeof tgtVal === 'object' ? tgtVal : {},
|
|
426
|
-
srcVal,
|
|
427
|
-
lang
|
|
428
|
-
);
|
|
429
|
-
} else {
|
|
430
|
-
const placeholder = `[${lang.toUpperCase()}] ${srcVal}`;
|
|
431
|
-
if (tgtVal === undefined) {
|
|
432
|
-
target[key] = placeholder;
|
|
433
|
-
} else if (typeof tgtVal === 'string' && this.markers.some(m => tgtVal.includes(m))) {
|
|
434
|
-
target[key] = placeholder;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
return target;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
processLanguage(lang) {
|
|
442
|
-
const files = this.getAllFiles(this.sourceLanguageDir);
|
|
443
|
-
files.forEach(file => {
|
|
444
|
-
const rel = path.relative(this.sourceLanguageDir, file);
|
|
445
|
-
const srcData = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
446
|
-
const targetFile = path.join(this.sourceDir, lang, rel);
|
|
447
|
-
let tgtData = {};
|
|
448
|
-
if (fs.existsSync(targetFile)) {
|
|
449
|
-
try {
|
|
450
|
-
tgtData = JSON.parse(fs.readFileSync(targetFile, 'utf8'));
|
|
451
|
-
} catch {
|
|
452
|
-
tgtData = {};
|
|
453
|
-
}
|
|
454
|
-
} else {
|
|
455
|
-
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
|
|
456
|
-
}
|
|
457
|
-
const fixed = this.fixObject(tgtData, srcData, lang);
|
|
458
|
-
fs.writeFileSync(targetFile, JSON.stringify(fixed, null, 2));
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
scanForIssues(lang) {
|
|
463
|
-
const issues = [];
|
|
464
|
-
const files = this.getAllFiles(this.sourceLanguageDir);
|
|
465
|
-
|
|
466
|
-
files.forEach(file => {
|
|
467
|
-
const rel = path.relative(this.sourceLanguageDir, file);
|
|
468
|
-
const srcData = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
469
|
-
const targetFile = path.join(this.sourceDir, lang, rel);
|
|
470
|
-
let tgtData = {};
|
|
471
|
-
|
|
472
|
-
if (fs.existsSync(targetFile)) {
|
|
473
|
-
try {
|
|
474
|
-
tgtData = JSON.parse(fs.readFileSync(targetFile, 'utf8'));
|
|
475
|
-
} catch {
|
|
476
|
-
tgtData = {};
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
this.scanObject(issues, srcData, tgtData, lang, rel, []);
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
return issues;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
scanObject(issues, source, target, lang, file, pathStack) {
|
|
487
|
-
Object.keys(source).forEach(key => {
|
|
488
|
-
const srcVal = source[key];
|
|
489
|
-
const tgtVal = target[key];
|
|
490
|
-
const currentPath = [...pathStack, key];
|
|
491
|
-
|
|
492
|
-
if (srcVal && typeof srcVal === 'object' && !Array.isArray(srcVal)) {
|
|
493
|
-
this.scanObject(issues, srcVal, tgtVal || {}, lang, file, currentPath);
|
|
494
|
-
} else {
|
|
495
|
-
const placeholder = `[${lang.toUpperCase()}] ${srcVal}`;
|
|
496
|
-
|
|
497
|
-
if (tgtVal === undefined) {
|
|
498
|
-
issues.push({
|
|
499
|
-
type: 'missing',
|
|
500
|
-
file,
|
|
501
|
-
path: currentPath.join('.'),
|
|
502
|
-
sourceValue: srcVal,
|
|
503
|
-
targetValue: null,
|
|
504
|
-
action: 'add',
|
|
505
|
-
newValue: placeholder
|
|
506
|
-
});
|
|
507
|
-
} else if (typeof tgtVal === 'string') {
|
|
508
|
-
// Check if any marker is present in the target value
|
|
509
|
-
const hasMarker = this.markers.some(m => {
|
|
510
|
-
if (m === '__NOT_TRANSLATED__') {
|
|
511
|
-
return tgtVal === '__NOT_TRANSLATED__' || tgtVal.includes('__NOT_TRANSLATED__');
|
|
512
|
-
}
|
|
513
|
-
return tgtVal.includes(m);
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
if (hasMarker) {
|
|
517
|
-
issues.push({
|
|
518
|
-
type: 'placeholder',
|
|
519
|
-
file,
|
|
520
|
-
path: currentPath.join('.'),
|
|
521
|
-
sourceValue: srcVal,
|
|
522
|
-
targetValue: tgtVal,
|
|
523
|
-
action: 'replace',
|
|
524
|
-
newValue: placeholder
|
|
525
|
-
});
|
|
70
|
+
const args = process.argv.slice(2);
|
|
71
|
+
const parsed = parseCommonArgs(args);
|
|
72
|
+
|
|
73
|
+
args.forEach(arg => {
|
|
74
|
+
if (arg.startsWith('--')) {
|
|
75
|
+
const [key, value] = arg.substring(2).split('=');
|
|
76
|
+
const sanitizedKey = SecurityUtils.sanitizeInput(key);
|
|
77
|
+
const sanitizedValue = value ? SecurityUtils.sanitizeInput(value) : true;
|
|
78
|
+
|
|
79
|
+
if (sanitizedKey === 'source-dir') {
|
|
80
|
+
parsed.sourceDir = sanitizedValue;
|
|
81
|
+
} else if (sanitizedKey === 'languages') {
|
|
82
|
+
parsed.languages = sanitizedValue.split(',').map(l => l.trim());
|
|
83
|
+
} else if (sanitizedKey === 'markers') {
|
|
84
|
+
parsed.markers = sanitizedValue.split(',').map(m => m.trim());
|
|
85
|
+
} else if (sanitizedKey === 'no-backup') {
|
|
86
|
+
parsed.noBackup = true;
|
|
526
87
|
}
|
|
527
88
|
}
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
generateReport(issues) {
|
|
533
|
-
const report = {
|
|
534
|
-
totalIssues: issues.length,
|
|
535
|
-
missingKeys: issues.filter(i => i.type === 'missing').length,
|
|
536
|
-
placeholderKeys: issues.filter(i => i.type === 'placeholder').length,
|
|
537
|
-
languages: {}
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
issues.forEach(issue => {
|
|
541
|
-
if (issue.newValue) {
|
|
542
|
-
const lang = String(issue.newValue).match(/\[([A-Z-]+)\]/)?.[1];
|
|
543
|
-
if (lang) {
|
|
544
|
-
if (!report.languages[lang]) report.languages[lang] = 0;
|
|
545
|
-
report.languages[lang]++;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
return report;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
printDetailedReport(issues, report) {
|
|
554
|
-
console.log(`\n${this.t('fixer.analysisTitle')}`);
|
|
555
|
-
console.log(this.t('fixer.analysisSeparator'));
|
|
556
|
-
console.log(this.t('fixer.totalIssues', { totalIssues: report.totalIssues }));
|
|
557
|
-
console.log(this.t('fixer.missingTranslations', { missing: report.missingKeys }));
|
|
558
|
-
console.log(this.t('fixer.placeholderTranslations', { placeholder: report.placeholderKeys }));
|
|
559
|
-
|
|
560
|
-
if (report.totalIssues === 0) {
|
|
561
|
-
console.log(`\n${this.t('fixer.noIssues')}`);
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
console.log(`\n${this.t('fixer.detailedIssues')}`);
|
|
566
|
-
console.log(this.t('fixer.detailedSeparator'));
|
|
567
|
-
|
|
568
|
-
const groupedIssues = issues.reduce((acc, issue) => {
|
|
569
|
-
const key = `${issue.file}:${issue.path}`;
|
|
570
|
-
if (!acc[key]) acc[key] = [];
|
|
571
|
-
acc[key].push(issue);
|
|
572
|
-
return acc;
|
|
573
|
-
}, {});
|
|
574
|
-
|
|
575
|
-
Object.entries(groupedIssues).forEach(([key, keyIssues]) => {
|
|
576
|
-
const [file, path] = key.split(':');
|
|
577
|
-
console.log(`\n${this.t('fixer.filePath', { file, path })}`);
|
|
578
|
-
|
|
579
|
-
keyIssues.forEach(issue => {
|
|
580
|
-
if (issue.type === 'missing') {
|
|
581
|
-
console.log(` ${this.t('fixer.missingKey', { source: issue.sourceValue, new: issue.newValue })}`);
|
|
582
|
-
} else {
|
|
583
|
-
console.log(` ${this.t('fixer.placeholderKey', { target: issue.targetValue, new: issue.newValue })}`);
|
|
584
|
-
}
|
|
585
89
|
});
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
async getUserConfirmation() {
|
|
590
|
-
const { ask } = require('../utils/cli.js');
|
|
591
|
-
|
|
592
|
-
const askQuestion = async () => {
|
|
593
|
-
console.log(`\n${this.t('fixer.confirmationTitle')}`);
|
|
594
|
-
console.log(this.t('fixer.confirmationOptions'));
|
|
595
|
-
console.log(` ${this.t('fixer.optionYes')}`);
|
|
596
|
-
console.log(` ${this.t('fixer.optionNo')}`);
|
|
597
|
-
console.log(` ${this.t('fixer.optionShow')}`);
|
|
598
|
-
|
|
599
|
-
const answer = await ask(this.t('fixer.choicePrompt'));
|
|
600
|
-
const cleanAnswer = answer.toLowerCase().trim();
|
|
601
|
-
if (cleanAnswer === 's' || cleanAnswer === 'show') {
|
|
602
|
-
// Show detailed report and ask again
|
|
603
|
-
this.printDetailedReport();
|
|
604
|
-
return askQuestion();
|
|
605
|
-
} else {
|
|
606
|
-
return cleanAnswer;
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
|
|
610
|
-
return askQuestion();
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
generateFixerReport(issues, report) {
|
|
614
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
615
|
-
const reportDir = path.join(this.config.outputDir || './i18ntk-reports', 'fixer-reports');
|
|
616
|
-
|
|
617
|
-
// Ensure report directory exists
|
|
618
|
-
if (!fs.existsSync(reportDir)) {
|
|
619
|
-
fs.mkdirSync(reportDir, { recursive: true });
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
const reportFile = path.join(reportDir, `fixer-report-${timestamp}.json`);
|
|
623
|
-
|
|
624
|
-
const reportData = {
|
|
625
|
-
timestamp: new Date().toISOString(),
|
|
626
|
-
summary: {
|
|
627
|
-
totalIssues: report.totalIssues,
|
|
628
|
-
missingTranslations: report.missingKeys,
|
|
629
|
-
placeholderTranslations: report.placeholderKeys,
|
|
630
|
-
languages: report.languages
|
|
631
|
-
},
|
|
632
|
-
issues: issues.map(issue => ({
|
|
633
|
-
type: issue.type,
|
|
634
|
-
file: issue.file,
|
|
635
|
-
path: issue.path,
|
|
636
|
-
sourceValue: issue.sourceValue,
|
|
637
|
-
targetValue: issue.targetValue,
|
|
638
|
-
newValue: issue.newValue,
|
|
639
|
-
action: issue.action
|
|
640
|
-
}))
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
fs.writeFileSync(reportFile, JSON.stringify(reportData, null, 2));
|
|
644
|
-
|
|
645
|
-
console.log(this.t('fixer.reportGenerated', { path: path.relative(process.cwd(), reportFile) }));
|
|
646
|
-
|
|
647
|
-
return {
|
|
648
|
-
file: reportFile,
|
|
649
|
-
relativePath: path.relative(process.cwd(), reportFile)
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
printLimitedReport(issues, report) {
|
|
654
|
-
const MAX_DISPLAY = 10;
|
|
655
|
-
const displayIssues = issues.slice(0, MAX_DISPLAY);
|
|
656
|
-
|
|
657
|
-
console.log(`\n${this.t('fixer.analysisTitle')}`);
|
|
658
|
-
console.log(this.t('fixer.analysisSeparator'));
|
|
659
|
-
console.log(this.t('fixer.totalIssues', { totalIssues: report.totalIssues }));
|
|
660
|
-
console.log(this.t('fixer.missingTranslations', { missing: report.missingKeys }));
|
|
661
|
-
console.log(this.t('fixer.placeholderTranslations', { placeholder: report.placeholderKeys }));
|
|
662
|
-
|
|
663
|
-
if (report.totalIssues === 0) {
|
|
664
|
-
console.log(`\n${this.t('fixer.noIssues')}`);
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
console.log(`\n${this.t('fixer.detailedIssues')}`);
|
|
669
|
-
console.log(this.t('fixer.detailedSeparator'));
|
|
670
90
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
console.log(` ${this.t('fixer.missingKey', { source: issue.sourceValue, new: issue.newValue })}`);
|
|
675
|
-
} else {
|
|
676
|
-
console.log(this.t('fixer.filePath', { file: issue.file, path: issue.path }));
|
|
677
|
-
console.log(` ${this.t('fixer.placeholderKey', { target: issue.targetValue, new: issue.newValue })}`);
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
if (issues.length > MAX_DISPLAY) {
|
|
682
|
-
const remaining = issues.length - MAX_DISPLAY;
|
|
683
|
-
console.log(`\n${this.t('fixer.moreIssues', { count: remaining })}`);
|
|
91
|
+
return parsed;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw error;
|
|
684
94
|
}
|
|
685
95
|
}
|
|
686
96
|
|
|
687
|
-
printDetailedReport() {
|
|
688
|
-
// This method is called when user selects 's' to show detailed issues
|
|
689
|
-
// Implementation can be added here if needed
|
|
690
|
-
console.log('\nš DETAILED REPORT - All issues shown above in the report file');
|
|
691
|
-
}
|
|
692
|
-
|
|
693
97
|
async run() {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (this.languages.length === 0) {
|
|
700
|
-
console.log(this.t('fixer.noLanguages'));
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
console.log(`\n${this.t('fixer.starting', { languages: this.languages.join(', ') })}`);
|
|
705
|
-
console.log(this.t('fixer.sourceDirectory', { sourceDir: this.sourceDir }));
|
|
706
|
-
console.log(this.t('fixer.sourceLanguage', { sourceLanguage: this.config.sourceLanguage }));
|
|
707
|
-
console.log(this.t('fixer.markers', { markers: this.markers.join(', ') }));
|
|
708
|
-
|
|
709
|
-
const allIssues = [];
|
|
710
|
-
for (const lang of this.languages) {
|
|
711
|
-
console.log(this.t('fixer.scanningLanguage', { language: lang }));
|
|
712
|
-
const issues = this.scanForIssues(lang);
|
|
713
|
-
allIssues.push(...issues);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
const report = this.generateReport(allIssues);
|
|
717
|
-
|
|
718
|
-
if (report.totalIssues === 0) {
|
|
719
|
-
console.log(`\n${this.t('fixer.allComplete')}`);
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// Generate and save report
|
|
724
|
-
const reportInfo = this.generateFixerReport(allIssues, report);
|
|
725
|
-
|
|
726
|
-
// Print limited report to console
|
|
727
|
-
this.printLimitedReport(allIssues, report);
|
|
728
|
-
|
|
729
|
-
// Non-interactive mode (for tests)
|
|
730
|
-
if (this.config.noBackup) {
|
|
731
|
-
console.log(`\n${this.t('fixer.nonInteractiveMode')}`);
|
|
732
|
-
this.languages.forEach(lang => this.processLanguage(lang));
|
|
733
|
-
console.log(this.t('fixer.fixingComplete'));
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Interactive mode
|
|
738
|
-
console.log(this.t('fixer.fullReportSaved', { reportPath: reportInfo.relativePath }));
|
|
739
|
-
console.log(this.t('fixer.reviewReport'));
|
|
740
|
-
|
|
741
|
-
const answer = await this.getUserConfirmation();
|
|
742
|
-
|
|
743
|
-
if (answer === 'y' || answer === 'yes') {
|
|
744
|
-
this.createBackup();
|
|
745
|
-
console.log(this.t('fixer.backupCreated'));
|
|
746
|
-
|
|
747
|
-
console.log(`\n${this.t('fixer.applyingFixes')}`);
|
|
748
|
-
this.languages.forEach(lang => this.processLanguage(lang));
|
|
749
|
-
console.log(this.t('fixer.fixingComplete'));
|
|
750
|
-
} else {
|
|
751
|
-
console.log(this.t('fixer.operationCancelled'));
|
|
752
|
-
}
|
|
753
|
-
} finally {
|
|
754
|
-
// Ensure readline is properly closed to prevent hanging
|
|
755
|
-
closeGlobalReadline();
|
|
756
|
-
// Ensure process exits cleanly
|
|
757
|
-
if (require.main === module) {
|
|
758
|
-
process.exit(0);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
98
|
+
await this.initialize();
|
|
99
|
+
console.log(t('fixer.running'));
|
|
100
|
+
// Placeholder for actual fixing logic
|
|
101
|
+
console.log(t('fixer.completed'));
|
|
761
102
|
}
|
|
762
103
|
}
|
|
763
104
|
|
|
764
|
-
|
|
105
|
+
module.exports = I18nFixer;
|
|
106
|
+
|
|
765
107
|
if (require.main === module) {
|
|
766
|
-
const { closeGlobalReadline } = require('../utils/cli.js');
|
|
767
108
|
const fixer = new I18nFixer();
|
|
768
|
-
fixer.run().catch(
|
|
769
|
-
console.error(
|
|
109
|
+
fixer.run().catch(error => {
|
|
110
|
+
console.error('I18n Fixer failed:', error);
|
|
770
111
|
process.exit(1);
|
|
771
|
-
}).finally(() => {
|
|
772
|
-
// Ensure readline is properly closed
|
|
773
|
-
closeGlobalReadline();
|
|
774
112
|
});
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
module.exports = I18nFixer;
|
|
113
|
+
}
|