i18ntk 2.1.0 ā 2.3.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/README.md +87 -50
- package/main/i18ntk-analyze.js +63 -63
- package/main/i18ntk-backup-class.js +37 -41
- package/main/i18ntk-backup.js +28 -30
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-doctor.js +7 -6
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +49 -13
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +36 -37
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +95 -96
- package/main/i18ntk-usage.js +31 -19
- package/main/i18ntk-validate.js +78 -27
- package/main/manage/commands/AnalyzeCommand.js +71 -73
- package/main/manage/commands/CommandRouter.js +15 -12
- package/main/manage/commands/FixerCommand.js +94 -38
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +87 -36
- package/main/manage/index.js +165 -152
- package/main/manage/managers/DebugMenu.js +6 -6
- package/main/manage/managers/InteractiveMenu.js +6 -6
- package/main/manage/managers/LanguageMenu.js +12 -6
- package/main/manage/managers/SettingsMenu.js +6 -6
- package/main/manage/services/AuthenticationService.js +5 -6
- package/main/manage/services/ConfigurationService.js +22 -34
- package/main/manage/services/FileManagementService.js +6 -6
- package/main/manage/services/InitService.js +44 -8
- package/main/manage/services/UsageService.js +24 -12
- package/package.json +21 -42
- package/settings/settings-cli.js +5 -5
- package/settings/settings-manager.js +984 -968
- package/ui-locales/de.json +12 -11
- package/ui-locales/en.json +12 -11
- package/ui-locales/es.json +12 -11
- package/ui-locales/fr.json +12 -11
- package/ui-locales/ja.json +12 -11
- package/ui-locales/ru.json +12 -11
- package/ui-locales/zh.json +12 -11
- package/utils/config-helper.js +27 -16
- package/utils/config-manager.js +8 -7
- package/utils/i18n-helper.js +161 -166
- package/utils/init-helper.js +3 -2
- package/utils/json-output.js +11 -10
- package/{scripts ā utils}/locale-optimizer.js +61 -60
- package/utils/logger.js +4 -4
- package/utils/safe-json.js +3 -3
- package/utils/secure-backup.js +8 -7
- package/utils/setup-enforcer.js +63 -98
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -236
- package/scripts/fix-and-purify-i18n.js +0 -233
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish-dev.js +0 -221
- package/scripts/prepublish.js +0 -362
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -195
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -609
package/main/i18ntk-js.js
DELETED
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* i18ntk JavaScript/TypeScript Command
|
|
5
|
-
* Specialized command for JavaScript and TypeScript i18n management
|
|
6
|
-
*
|
|
7
|
-
* Usage: i18ntk-js [options]
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const SecurityUtils = require(path.join(__dirname, '../utils/security.js'));
|
|
13
|
-
const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper.js'));
|
|
14
|
-
const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper.js'));
|
|
15
|
-
const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
|
|
16
|
-
const { program } = require('../utils/mini-commander');
|
|
17
|
-
|
|
18
|
-
(async () => {
|
|
19
|
-
try {
|
|
20
|
-
await SetupEnforcer.checkSetupCompleteAsync();
|
|
21
|
-
} catch (error) {
|
|
22
|
-
console.error('Setup check failed:', error.message);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
})();
|
|
26
|
-
|
|
27
|
-
class I18ntkJavaScriptCommand {
|
|
28
|
-
constructor() {
|
|
29
|
-
this.config = null;
|
|
30
|
-
this.sourceDir = './src';
|
|
31
|
-
this.jsPatterns = [
|
|
32
|
-
// JavaScript/TypeScript patterns
|
|
33
|
-
/i18n\.t\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
34
|
-
/t\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
35
|
-
/translate\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
36
|
-
/formatMessage\s*\(\s*{[^}]*id:\s*["'`]([^"'`]+)["'`]/g,
|
|
37
|
-
/intl\.formatMessage\s*\(\s*{[^}]*id:\s*["'`]([^"'`]+)["'`]/g,
|
|
38
|
-
/useTranslations?\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
39
|
-
/getTranslations?\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
40
|
-
|
|
41
|
-
// React patterns
|
|
42
|
-
/FormattedMessage\s+id=["'`]([^"'`]+)["'`]/g,
|
|
43
|
-
/<FormattedMessage[^>]*id={["'`]([^"'`]+)["'`]}/g,
|
|
44
|
-
/useTranslation\(\)\s*\.t\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
45
|
-
|
|
46
|
-
// Angular patterns
|
|
47
|
-
/translate\s*\|\s*translate/g,
|
|
48
|
-
/\{\{\s*["'`]([^"'`]+)["'`]\s*\|\s*translate\s*}}/g,
|
|
49
|
-
|
|
50
|
-
// Vue patterns
|
|
51
|
-
/\$t\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
52
|
-
/this\.\$t\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
53
|
-
|
|
54
|
-
// Node.js patterns
|
|
55
|
-
/__\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
56
|
-
/gettext\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
57
|
-
|
|
58
|
-
// Template literal patterns
|
|
59
|
-
/i18n\.t\s*\(\s*`([^`]+)`/g,
|
|
60
|
-
/t\s*\(\s*`([^`]+)`/g
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
this.frameworks = {
|
|
64
|
-
react: {
|
|
65
|
-
indicators: ['package.json', 'react', 'react-dom'],
|
|
66
|
-
patterns: ['useTranslation', 'FormattedMessage', 'react-intl']
|
|
67
|
-
},
|
|
68
|
-
vue: {
|
|
69
|
-
indicators: ['package.json', 'vue', 'vue-i18n'],
|
|
70
|
-
patterns: ['$t', 'vue-i18n', 'i18n']
|
|
71
|
-
},
|
|
72
|
-
angular: {
|
|
73
|
-
indicators: ['package.json', '@angular/core', '@ngx-translate'],
|
|
74
|
-
patterns: ['translate', '@ngx-translate/core']
|
|
75
|
-
},
|
|
76
|
-
node: {
|
|
77
|
-
indicators: ['package.json', 'express', 'i18n'],
|
|
78
|
-
patterns: ['__', 'gettext', 'i18n']
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async init() {
|
|
84
|
-
console.log('š§ Initializing i18ntk JavaScript/TypeScript command...');
|
|
85
|
-
|
|
86
|
-
program
|
|
87
|
-
.name('i18ntk-js')
|
|
88
|
-
.description('i18ntk specialized for JavaScript and TypeScript applications')
|
|
89
|
-
.version('1.10.1')
|
|
90
|
-
.option('-s, --source-dir <dir>', 'Source directory to scan', './src')
|
|
91
|
-
.option('-l, --locales-dir <dir>', 'Locales directory', './locales')
|
|
92
|
-
.option('--framework <type>', 'JavaScript framework type', 'auto')
|
|
93
|
-
.option('--typescript', 'Include TypeScript files')
|
|
94
|
-
.option('--react', 'Force React mode')
|
|
95
|
-
.option('--vue', 'Force Vue mode')
|
|
96
|
-
.option('--angular', 'Force Angular mode')
|
|
97
|
-
.option('--node', 'Force Node.js mode')
|
|
98
|
-
.option('--dry-run', 'Show what would be done without making changes')
|
|
99
|
-
.option('--debug', 'Enable debug output')
|
|
100
|
-
.option('--extract-only', 'Only extract translations, don\'t analyze')
|
|
101
|
-
.option('--include-tests', 'Include test files in analysis')
|
|
102
|
-
.parse();
|
|
103
|
-
|
|
104
|
-
this.options = program.opts();
|
|
105
|
-
this.sourceDir = path.resolve(this.options.sourceDir);
|
|
106
|
-
this.localesDir = path.resolve(this.options.localesDir);
|
|
107
|
-
|
|
108
|
-
await this.validateSourceDir();
|
|
109
|
-
await this.loadConfig();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async validateSourceDir() {
|
|
113
|
-
if (!SecurityUtils.safeExistsSync(this.sourceDir, path.dirname(this.sourceDir))) {
|
|
114
|
-
console.error(`ā Source directory not found: ${this.sourceDir}`);
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const stats = fs.statSync(this.sourceDir);
|
|
119
|
-
if (!stats.isDirectory()) {
|
|
120
|
-
console.error(`ā Source path is not a directory: ${this.sourceDir}`);
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async loadConfig() {
|
|
126
|
-
try {
|
|
127
|
-
this.config = await getConfig();
|
|
128
|
-
this.config.javascript = this.config.javascript || {};
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.warn('ā ļø Could not load config, using defaults');
|
|
131
|
-
this.config = { javascript: {} };
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async detectFramework() {
|
|
136
|
-
if (this.options.react) return 'react';
|
|
137
|
-
if (this.options.vue) return 'vue';
|
|
138
|
-
if (this.options.angular) return 'angular';
|
|
139
|
-
if (this.options.node) return 'node';
|
|
140
|
-
|
|
141
|
-
console.log('š Detecting JavaScript framework...');
|
|
142
|
-
|
|
143
|
-
let framework = 'generic';
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const packageJsonPath = path.join(this.sourceDir, 'package.json');
|
|
147
|
-
if (SecurityUtils.safeExistsSync(packageJsonPath, this.sourceDir)) {
|
|
148
|
-
const packageJsonContent = SecurityUtils.safeReadFileSync(packageJsonPath, this.sourceDir, 'utf8');
|
|
149
|
-
const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
|
|
150
|
-
|
|
151
|
-
const dependencies = {
|
|
152
|
-
...packageJson.dependencies || {},
|
|
153
|
-
...packageJson.devDependencies || {}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// Check for React
|
|
157
|
-
if (dependencies.react && dependencies['react-dom']) {
|
|
158
|
-
framework = 'react';
|
|
159
|
-
}
|
|
160
|
-
// Check for Vue
|
|
161
|
-
else if (dependencies.vue && dependencies['vue-i18n']) {
|
|
162
|
-
framework = 'vue';
|
|
163
|
-
}
|
|
164
|
-
// Check for Angular
|
|
165
|
-
else if (dependencies['@angular/core'] || dependencies['@ngx-translate/core']) {
|
|
166
|
-
framework = 'angular';
|
|
167
|
-
}
|
|
168
|
-
// Check for Node.js
|
|
169
|
-
else if (dependencies.express || dependencies.i18n || dependencies['i18next']) {
|
|
170
|
-
framework = 'node';
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
} catch (error) {
|
|
175
|
-
if (this.options.debug) {
|
|
176
|
-
console.error('Debug: Framework detection error:', error.message);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
console.log(`ā
Detected framework: ${framework}`);
|
|
181
|
-
return framework;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
findFiles(pattern, extensions = []) {
|
|
185
|
-
const results = [];
|
|
186
|
-
const validExtensions = extensions.length > 0 ? extensions : ['.js', '.jsx', '.ts', '.tsx'];
|
|
187
|
-
|
|
188
|
-
function scanDir(dir) {
|
|
189
|
-
if (!SecurityUtils.safeExistsSync(dir, path.dirname(dir))) return;
|
|
190
|
-
|
|
191
|
-
const items = fs.readdirSync(dir);
|
|
192
|
-
for (const item of items) {
|
|
193
|
-
const fullPath = path.join(dir, item);
|
|
194
|
-
const stat = fs.statSync(fullPath);
|
|
195
|
-
|
|
196
|
-
if (stat.isDirectory()) {
|
|
197
|
-
// Skip node_modules and hidden directories
|
|
198
|
-
if (item === 'node_modules' || item.startsWith('.')) continue;
|
|
199
|
-
|
|
200
|
-
// Skip test directories unless explicitly included
|
|
201
|
-
if (!this.options.includeTests &&
|
|
202
|
-
(item === '__tests__' || item === 'test' || item === 'tests' || item.includes('.test.'))) {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
scanDir.call(this, fullPath);
|
|
207
|
-
} else {
|
|
208
|
-
// Check file extension
|
|
209
|
-
const ext = path.extname(item);
|
|
210
|
-
if (validExtensions.includes(ext)) {
|
|
211
|
-
// Skip test files unless explicitly included
|
|
212
|
-
if (!this.options.includeTests &&
|
|
213
|
-
(item.includes('.test.') || item.includes('.spec.') || item.includes('__test__'))) {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
results.push(fullPath);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
scanDir.call(this, this.sourceDir);
|
|
224
|
-
return results;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async extractTranslations() {
|
|
228
|
-
console.log('š¦ Extracting JavaScript/TypeScript translations...');
|
|
229
|
-
|
|
230
|
-
const extensions = this.options.typescript
|
|
231
|
-
? ['.js', '.jsx', '.ts', '.tsx']
|
|
232
|
-
: ['.js', '.jsx'];
|
|
233
|
-
|
|
234
|
-
const jsFiles = this.findFiles('', extensions);
|
|
235
|
-
const translations = new Set();
|
|
236
|
-
|
|
237
|
-
for (const file of jsFiles) {
|
|
238
|
-
try {
|
|
239
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
240
|
-
|
|
241
|
-
for (const pattern of this.jsPatterns) {
|
|
242
|
-
let match;
|
|
243
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
244
|
-
translations.add(match[1]);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Reset regex state for next file
|
|
249
|
-
for (const pattern of this.jsPatterns) {
|
|
250
|
-
pattern.lastIndex = 0;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
} catch (error) {
|
|
254
|
-
if (this.options.debug) {
|
|
255
|
-
console.error(`Error reading ${file}:`, error.message);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
console.log(`š Found ${translations.size} unique translation keys`);
|
|
261
|
-
return Array.from(translations);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async createLocaleStructure() {
|
|
265
|
-
if (!SecurityUtils.safeExistsSync(this.localesDir, path.dirname(this.localesDir))) {
|
|
266
|
-
if (this.options.dryRun) {
|
|
267
|
-
console.log(`š Would create directory: ${this.localesDir}`);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
fs.mkdirSync(this.localesDir, { recursive: true });
|
|
272
|
-
console.log(`š Created locales directory: ${this.localesDir}`);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const languages = ['en', 'es', 'fr', 'de', 'ja', 'ru', 'zh'];
|
|
276
|
-
|
|
277
|
-
for (const lang of languages) {
|
|
278
|
-
const langDir = path.join(this.localesDir, lang);
|
|
279
|
-
if (!SecurityUtils.safeExistsSync(langDir, this.localesDir)) {
|
|
280
|
-
if (this.options.dryRun) {
|
|
281
|
-
console.log(`š Would create directory: ${langDir}`);
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
fs.mkdirSync(langDir, { recursive: true });
|
|
286
|
-
|
|
287
|
-
// Create framework-specific translation files
|
|
288
|
-
const commonFile = path.join(langDir, 'common.json');
|
|
289
|
-
const initialContent = {
|
|
290
|
-
"javascript": {
|
|
291
|
-
"welcome": `Welcome to JavaScript (${lang})`,
|
|
292
|
-
"framework_detected": "Framework detected: {framework}",
|
|
293
|
-
"files_processed": "Processed {count} files",
|
|
294
|
-
"keys_found": "Found {count} translation keys"
|
|
295
|
-
},
|
|
296
|
-
"components": {
|
|
297
|
-
"button": {
|
|
298
|
-
"submit": "Submit",
|
|
299
|
-
"cancel": "Cancel",
|
|
300
|
-
"save": "Save"
|
|
301
|
-
},
|
|
302
|
-
"form": {
|
|
303
|
-
"validation": {
|
|
304
|
-
"required": "This field is required",
|
|
305
|
-
"invalid": "Invalid format"
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
SecurityUtils.safeWriteFileSync(commonFile, JSON.stringify(initialContent, null, 2), this.localesDir);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
async analyzeFramework(framework) {
|
|
317
|
-
console.log(`š Analyzing ${framework} project...`);
|
|
318
|
-
|
|
319
|
-
const extensions = this.options.typescript
|
|
320
|
-
? ['.js', '.jsx', '.ts', '.tsx']
|
|
321
|
-
: ['.js', '.jsx'];
|
|
322
|
-
|
|
323
|
-
const jsFiles = this.findFiles('', extensions);
|
|
324
|
-
|
|
325
|
-
const analysis = {
|
|
326
|
-
framework,
|
|
327
|
-
typescript: this.options.typescript || false,
|
|
328
|
-
files: {
|
|
329
|
-
total: jsFiles.length,
|
|
330
|
-
javascript: 0,
|
|
331
|
-
typescript: 0,
|
|
332
|
-
jsx: 0,
|
|
333
|
-
tsx: 0,
|
|
334
|
-
test: 0
|
|
335
|
-
},
|
|
336
|
-
patterns: {
|
|
337
|
-
i18n_t: 0,
|
|
338
|
-
t_function: 0,
|
|
339
|
-
useTranslation: 0,
|
|
340
|
-
FormattedMessage: 0,
|
|
341
|
-
translate: 0,
|
|
342
|
-
$t: 0,
|
|
343
|
-
gettext: 0
|
|
344
|
-
},
|
|
345
|
-
frameworks: {
|
|
346
|
-
react: false,
|
|
347
|
-
vue: false,
|
|
348
|
-
angular: false,
|
|
349
|
-
node: false
|
|
350
|
-
},
|
|
351
|
-
recommendations: [],
|
|
352
|
-
dependencies: []
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
// Analyze files
|
|
356
|
-
for (const file of jsFiles) {
|
|
357
|
-
const ext = path.extname(file);
|
|
358
|
-
if (ext === '.js') analysis.files.javascript++;
|
|
359
|
-
else if (ext === '.ts') analysis.files.typescript++;
|
|
360
|
-
else if (ext === '.jsx') analysis.files.jsx++;
|
|
361
|
-
else if (ext === '.tsx') analysis.files.tsx++;
|
|
362
|
-
|
|
363
|
-
if (file.includes('.test.') || file.includes('.spec.')) {
|
|
364
|
-
analysis.files.test++;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
try {
|
|
368
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
369
|
-
|
|
370
|
-
// Check patterns
|
|
371
|
-
if (content.includes('i18n.t(')) analysis.patterns.i18n_t++;
|
|
372
|
-
if (content.includes('t(')) analysis.patterns.t_function++;
|
|
373
|
-
if (content.includes('useTranslation')) analysis.patterns.useTranslation++;
|
|
374
|
-
if (content.includes('FormattedMessage')) analysis.patterns.FormattedMessage++;
|
|
375
|
-
if (content.includes('translate')) analysis.patterns.translate++;
|
|
376
|
-
if (content.includes('$t(')) analysis.patterns.$t++;
|
|
377
|
-
if (content.includes('gettext(')) analysis.patterns.gettext++;
|
|
378
|
-
|
|
379
|
-
// Detect frameworks
|
|
380
|
-
if (content.includes('import React') || content.includes('require("react")')) {
|
|
381
|
-
analysis.frameworks.react = true;
|
|
382
|
-
}
|
|
383
|
-
if (content.includes('Vue') || content.includes('vue-i18n')) {
|
|
384
|
-
analysis.frameworks.vue = true;
|
|
385
|
-
}
|
|
386
|
-
if (content.includes('@angular') || content.includes('@ngx-translate')) {
|
|
387
|
-
analysis.frameworks.angular = true;
|
|
388
|
-
}
|
|
389
|
-
if (content.includes('express') || content.includes('node')) {
|
|
390
|
-
analysis.frameworks.node = true;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
} catch (error) {
|
|
394
|
-
// Skip unreadable files
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Generate recommendations
|
|
399
|
-
if (analysis.files.total > 0 && analysis.patterns.i18n_t === 0) {
|
|
400
|
-
analysis.recommendations.push('Consider adding i18n support to your JavaScript/TypeScript files');
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (analysis.frameworks.react && analysis.patterns.FormattedMessage === 0) {
|
|
404
|
-
analysis.recommendations.push('Consider using react-intl or react-i18next for React i18n');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (analysis.frameworks.vue && analysis.patterns.$t === 0) {
|
|
408
|
-
analysis.recommendations.push('Consider using vue-i18n for Vue.js i18n');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Check package.json for dependencies
|
|
412
|
-
try {
|
|
413
|
-
const packageJsonPath = path.join(this.sourceDir, 'package.json');
|
|
414
|
-
if (SecurityUtils.safeExistsSync(packageJsonPath, this.sourceDir)) {
|
|
415
|
-
const packageJsonContent = SecurityUtils.safeReadFileSync(packageJsonPath, this.sourceDir, 'utf8');
|
|
416
|
-
const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
|
|
417
|
-
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
418
|
-
|
|
419
|
-
for (const [pkg, version] of Object.entries(deps)) {
|
|
420
|
-
if (pkg.includes('i18n') || pkg.includes('intl') || pkg.includes('translate')) {
|
|
421
|
-
analysis.dependencies.push(`${pkg}@${version}`);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
} catch (error) {
|
|
426
|
-
// Skip package.json analysis errors
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return analysis;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
async generateReport(analysis, translations) {
|
|
433
|
-
const report = {
|
|
434
|
-
timestamp: new Date().toISOString(),
|
|
435
|
-
framework: analysis.framework,
|
|
436
|
-
typescript: analysis.typescript,
|
|
437
|
-
summary: {
|
|
438
|
-
totalFiles: analysis.files.total,
|
|
439
|
-
javascriptFiles: analysis.files.javascript,
|
|
440
|
-
typescriptFiles: analysis.files.typescript,
|
|
441
|
-
jsxFiles: analysis.files.jsx,
|
|
442
|
-
tsxFiles: analysis.files.tsx,
|
|
443
|
-
testFiles: analysis.files.test,
|
|
444
|
-
translationKeys: translations.length
|
|
445
|
-
},
|
|
446
|
-
patterns: analysis.patterns,
|
|
447
|
-
frameworks: analysis.frameworks,
|
|
448
|
-
dependencies: analysis.dependencies,
|
|
449
|
-
recommendations: analysis.recommendations,
|
|
450
|
-
files: {
|
|
451
|
-
javascript: this.findFiles('', ['.js']),
|
|
452
|
-
typescript: this.findFiles('', ['.ts']),
|
|
453
|
-
jsx: this.findFiles('', ['.jsx']),
|
|
454
|
-
tsx: this.findFiles('', ['.tsx'])
|
|
455
|
-
},
|
|
456
|
-
translations: translations.sort()
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
const reportPath = path.join(this.sourceDir, 'i18ntk-js-report.json');
|
|
460
|
-
|
|
461
|
-
if (this.options.dryRun) {
|
|
462
|
-
console.log(`š Would create report: ${reportPath}`);
|
|
463
|
-
console.log('š Report contents:', JSON.stringify(report, null, 2));
|
|
464
|
-
} else {
|
|
465
|
-
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2), this.sourceDir);
|
|
466
|
-
console.log(`š Report saved: ${reportPath}`);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
return report;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
async run() {
|
|
473
|
-
try {
|
|
474
|
-
console.log('š i18ntk JavaScript/TypeScript Command v1.10.1');
|
|
475
|
-
console.log('='.repeat(60));
|
|
476
|
-
|
|
477
|
-
await this.init();
|
|
478
|
-
|
|
479
|
-
const framework = await this.detectFramework();
|
|
480
|
-
const translations = await this.extractTranslations();
|
|
481
|
-
|
|
482
|
-
if (!this.options.extractOnly) {
|
|
483
|
-
await this.createLocaleStructure();
|
|
484
|
-
const analysis = await this.analyzeFramework(framework);
|
|
485
|
-
const report = await this.generateReport(analysis, translations);
|
|
486
|
-
|
|
487
|
-
console.log('\nā
Analysis complete!');
|
|
488
|
-
console.log(`š Framework: ${framework}`);
|
|
489
|
-
console.log(`š Total files: ${analysis.files.total}`);
|
|
490
|
-
console.log(`šÆ Translation keys: ${translations.length}`);
|
|
491
|
-
console.log(`š Report: ${this.options.dryRun ? 'Not saved (dry-run)' : 'Saved to i18ntk-js-report.json'}`);
|
|
492
|
-
} else {
|
|
493
|
-
console.log(`š¦ Extracted ${translations.length} translation keys`);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
} catch (error) {
|
|
497
|
-
console.error('ā Error:', error.message);
|
|
498
|
-
if (this.options.debug) {
|
|
499
|
-
console.error(error.stack);
|
|
500
|
-
}
|
|
501
|
-
process.exit(1);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Run if called directly
|
|
507
|
-
if (require.main === module) {
|
|
508
|
-
const cmd = new I18ntkJavaScriptCommand();
|
|
509
|
-
cmd.run();
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
module.exports = I18ntkJavaScriptCommand;
|