i18ntk 1.10.1 → 1.10.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/README.md +8 -2
- package/main/i18ntk-analyze.js +84 -49
- package/main/i18ntk-backup.js +1 -1
- package/main/i18ntk-init.js +1 -1
- package/main/i18ntk-manage.js +1 -1
- package/main/i18ntk-setup.js +10 -1
- package/main/i18ntk-sizing.js +40 -34
- package/main/i18ntk-usage.js +1 -1
- package/package.json +3 -4
- package/scripts/security-check.js +117 -109
- package/scripts/verify-deprecations.js +161 -149
- package/settings/i18ntk-config.json +2 -2
- package/settings/settings-manager.js +5 -2
- package/utils/config-helper.js +81 -1
- package/utils/config-manager.js +12 -5
- package/utils/env-manager.js +270 -0
- package/utils/logger.js +6 -2
- package/utils/plugin-loader.js +38 -31
- package/utils/security-check.js +6 -2
- package/utils/security.js +602 -438
- package/utils/terminal-icons.js +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://github.com/vladnoskv/i18ntk#features)
|
|
13
13
|
[](https://www.npmjs.com/package/i18ntk)
|
|
14
14
|
[](https://github.com/vladnoskv/i18ntk)
|
|
15
|
-
[](https://socket.dev/npm/package/i18ntk/overview/1.10.2)
|
|
16
16
|
|
|
17
17
|
[📦 Install Now](#-installation) • [⚡ Quick Start](#-quick-start) • [📚 Documentation](#-documentation) • [🎯 Features](#-why-choose-i18ntk)
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
**15.38ms** for 200k translation keys • **<2MB** memory usage • **97% faster** than traditional tools
|
|
24
24
|
|
|
25
|
-
**v1.10.
|
|
25
|
+
**v1.10.2** - Stable & Secure • **v2.0.0** - Coming Soon
|
|
26
26
|
|
|
27
27
|
</div>
|
|
28
28
|
|
|
@@ -58,10 +58,16 @@ i18ntk fixer --interactive
|
|
|
58
58
|
|
|
59
59
|
# 5. Validate everything
|
|
60
60
|
i18ntk validate
|
|
61
|
+
|
|
62
|
+
# 6. Mangage Mre
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
That's it! Your i18n infrastructure is ready. 🎉
|
|
64
66
|
|
|
67
|
+
> **🚨 v1.10.2 Update**: Fresh installs now work out-of-the-box! We've fixed the default `projectRoot` path from `"./"` to `"/"` when resetting settings. No more configuration issues on first run!
|
|
68
|
+
>
|
|
69
|
+
> **Migration Note**: If you're upgrading from v1.10.1 or earlier, your existing `projectRoot` settings will remain unchanged. Only new installations or manual resets will use the improved default.
|
|
70
|
+
|
|
65
71
|
## 🎯 Why Choose i18ntk?
|
|
66
72
|
|
|
67
73
|
| Feature | i18ntk v1.10.0 | Traditional Tools | Manual Process |
|
package/main/i18ntk-analyze.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* I18NTK TRANSLATION ANALYSIS SCRIPT
|
|
4
4
|
*
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const fs = require('fs');
|
|
11
10
|
const path = require('path');
|
|
12
11
|
const cliHelper = require('../utils/cli-helper');
|
|
13
12
|
const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
@@ -146,7 +145,12 @@ class I18nAnalyzer {
|
|
|
146
145
|
// Get all available languages
|
|
147
146
|
getAvailableLanguages() {
|
|
148
147
|
try {
|
|
149
|
-
const items =
|
|
148
|
+
const items = SecurityUtils.safeReaddirSync(this.sourceDir, process.cwd(), { withFileTypes: true });
|
|
149
|
+
if (!items) {
|
|
150
|
+
console.error('Error reading source directory: Unable to access directory');
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
150
154
|
const languages = [];
|
|
151
155
|
|
|
152
156
|
// Check for directory-based structure
|
|
@@ -173,15 +177,17 @@ class I18nAnalyzer {
|
|
|
173
177
|
for (const dir of directories) {
|
|
174
178
|
const dirPath = path.join(this.sourceDir, dir);
|
|
175
179
|
try {
|
|
176
|
-
const dirItems =
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
languages.
|
|
180
|
+
const dirItems = SecurityUtils.safeReaddirSync(dirPath, process.cwd(), { withFileTypes: true });
|
|
181
|
+
if (dirItems) {
|
|
182
|
+
const jsonFiles = dirItems
|
|
183
|
+
.filter(item => item.isFile() && item.name.endsWith('.json'))
|
|
184
|
+
.map(item => item.name.replace('.json', ''));
|
|
185
|
+
|
|
186
|
+
// If directory contains JSON files, it's likely a language directory
|
|
187
|
+
if (jsonFiles.length > 0) {
|
|
188
|
+
if (!languages.includes(dir)) {
|
|
189
|
+
languages.push(dir);
|
|
190
|
+
}
|
|
185
191
|
}
|
|
186
192
|
}
|
|
187
193
|
} catch (error) {
|
|
@@ -208,12 +214,14 @@ class I18nAnalyzer {
|
|
|
208
214
|
const files = [];
|
|
209
215
|
|
|
210
216
|
// Handle monolith file structure
|
|
211
|
-
|
|
217
|
+
const languageFileStat = SecurityUtils.safeStatSync(languageFile, this.sourceDir);
|
|
218
|
+
if (languageFileStat && languageFileStat.isFile()) {
|
|
212
219
|
return [path.basename(languageFile)];
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
// Handle directory-based structure
|
|
216
|
-
|
|
223
|
+
const languageDirStat = SecurityUtils.safeStatSync(languageDir, this.sourceDir);
|
|
224
|
+
if (languageDirStat && languageDirStat.isDirectory()) {
|
|
217
225
|
try {
|
|
218
226
|
// Ensure the path is within the source directory for security
|
|
219
227
|
const validatedPath = SecurityUtils.validatePath(languageDir, this.sourceDir);
|
|
@@ -224,7 +232,9 @@ class I18nAnalyzer {
|
|
|
224
232
|
|
|
225
233
|
const findJsonFiles = (dir) => {
|
|
226
234
|
const results = [];
|
|
227
|
-
const items =
|
|
235
|
+
const items = SecurityUtils.safeReaddirSync(dir, this.sourceDir, { withFileTypes: true });
|
|
236
|
+
|
|
237
|
+
if (!items) return results;
|
|
228
238
|
|
|
229
239
|
for (const item of items) {
|
|
230
240
|
const fullPath = path.join(dir, item.name);
|
|
@@ -267,16 +277,20 @@ class I18nAnalyzer {
|
|
|
267
277
|
const searchDir = this.sourceDir;
|
|
268
278
|
|
|
269
279
|
try {
|
|
270
|
-
|
|
271
|
-
|
|
280
|
+
const searchDirExists = SecurityUtils.safeExistsSync(searchDir, this.sourceDir);
|
|
281
|
+
if (searchDirExists) {
|
|
282
|
+
const items = SecurityUtils.safeReaddirSync(searchDir, this.sourceDir, { withFileTypes: true });
|
|
272
283
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
284
|
+
if (items) {
|
|
285
|
+
for (const item of items) {
|
|
286
|
+
if (item.isDirectory() && !item.name.startsWith('.') && item.name !== 'node_modules') {
|
|
287
|
+
const namespaceDir = path.join(searchDir, item.name);
|
|
288
|
+
const namespaceFile = path.join(namespaceDir, `${language}.json`);
|
|
289
|
+
|
|
290
|
+
const namespaceFileExists = SecurityUtils.safeExistsSync(namespaceFile, this.sourceDir);
|
|
291
|
+
if (namespaceFileExists) {
|
|
292
|
+
results.push(path.relative(path.join(this.sourceDir, item.name), namespaceFile));
|
|
293
|
+
}
|
|
280
294
|
}
|
|
281
295
|
}
|
|
282
296
|
}
|
|
@@ -468,15 +482,28 @@ class I18nAnalyzer {
|
|
|
468
482
|
const sourceFullPath = path.join(this.sourceDir, sourceFilePath);
|
|
469
483
|
const targetFullPath = path.join(this.sourceDir, targetFilePath);
|
|
470
484
|
|
|
471
|
-
|
|
485
|
+
const sourceExists = SecurityUtils.safeExistsSync(sourceFullPath, this.sourceDir);
|
|
486
|
+
if (!sourceExists) {
|
|
472
487
|
continue;
|
|
473
488
|
}
|
|
474
489
|
|
|
475
490
|
let sourceContent, targetContent;
|
|
476
491
|
|
|
477
492
|
try {
|
|
478
|
-
const sourceFileContent =
|
|
479
|
-
|
|
493
|
+
const sourceFileContent = SecurityUtils.safeReadFileSync(sourceFullPath, this.sourceDir, 'utf8');
|
|
494
|
+
if (!sourceFileContent) {
|
|
495
|
+
analysis.files[fileName] = {
|
|
496
|
+
error: `Failed to read source file: File not accessible or empty`
|
|
497
|
+
};
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
sourceContent = SecurityUtils.safeParseJSON(sourceFileContent);
|
|
501
|
+
if (!sourceContent) {
|
|
502
|
+
analysis.files[fileName] = {
|
|
503
|
+
error: `Failed to parse source file: Invalid JSON format`
|
|
504
|
+
};
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
480
507
|
} catch (error) {
|
|
481
508
|
analysis.files[fileName] = {
|
|
482
509
|
error: `Failed to parse source file: ${error.message}`
|
|
@@ -484,7 +511,8 @@ class I18nAnalyzer {
|
|
|
484
511
|
continue;
|
|
485
512
|
}
|
|
486
513
|
|
|
487
|
-
|
|
514
|
+
const targetExists = SecurityUtils.safeExistsSync(targetFullPath, this.sourceDir);
|
|
515
|
+
if (!targetExists) {
|
|
488
516
|
analysis.files[fileName] = {
|
|
489
517
|
status: 'missing',
|
|
490
518
|
sourceKeys: this.getAllKeys(sourceContent).size
|
|
@@ -493,9 +521,15 @@ class I18nAnalyzer {
|
|
|
493
521
|
}
|
|
494
522
|
|
|
495
523
|
try {
|
|
496
|
-
const targetFileContent =
|
|
497
|
-
|
|
498
|
-
|
|
524
|
+
const targetFileContent = SecurityUtils.safeReadFileSync(targetFullPath, this.sourceDir, 'utf8');
|
|
525
|
+
if (!targetFileContent) {
|
|
526
|
+
analysis.files[fileName] = {
|
|
527
|
+
error: `Failed to read target file: File not accessible or empty`
|
|
528
|
+
};
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const parsed = SecurityUtils.safeParseJSON(targetFileContent);
|
|
499
533
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
500
534
|
analysis.files[fileName] = {
|
|
501
535
|
error: `Invalid structure in target file: must be a plain object (not array/null/type)`
|
|
@@ -644,13 +678,10 @@ try {
|
|
|
644
678
|
}
|
|
645
679
|
|
|
646
680
|
// Ensure the output directory exists
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
console.error(`Failed to create output directory ${this.outputDir}:`, error.message);
|
|
652
|
-
return null;
|
|
653
|
-
}
|
|
681
|
+
const dirCreated = SecurityUtils.safeMkdirSync(this.outputDir, process.cwd(), { recursive: true });
|
|
682
|
+
if (!dirCreated) {
|
|
683
|
+
console.error(`Failed to create output directory ${this.outputDir}`);
|
|
684
|
+
return null;
|
|
654
685
|
}
|
|
655
686
|
|
|
656
687
|
// Validate the output directory is within the project
|
|
@@ -670,14 +701,10 @@ try {
|
|
|
670
701
|
return null;
|
|
671
702
|
}
|
|
672
703
|
|
|
673
|
-
// Use safeWriteFile
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
throw new Error(t('analyze.failedToWriteReportFile') || 'Failed to write report file securely');
|
|
678
|
-
}
|
|
679
|
-
} else {
|
|
680
|
-
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
|
|
704
|
+
// Use safeWriteFile for secure file writing
|
|
705
|
+
const success = await SecurityUtils.safeWriteFile(reportPath, JSON.stringify(report, null, 2), process.cwd(), 'utf8');
|
|
706
|
+
if (!success) {
|
|
707
|
+
throw new Error(t('analyze.failedToWriteReportFile') || 'Failed to write report file securely');
|
|
681
708
|
}
|
|
682
709
|
|
|
683
710
|
console.log(`Report saved to: ${reportPath}`);
|
|
@@ -709,8 +736,9 @@ try {
|
|
|
709
736
|
}
|
|
710
737
|
|
|
711
738
|
// Ensure output directory exists
|
|
712
|
-
|
|
713
|
-
|
|
739
|
+
const outputDirExists = SecurityUtils.safeExistsSync(this.outputDir, process.cwd());
|
|
740
|
+
if (!outputDirExists) {
|
|
741
|
+
SecurityUtils.safeMkdirSync(this.outputDir, process.cwd(), { recursive: true });
|
|
714
742
|
}
|
|
715
743
|
|
|
716
744
|
const languages = this.getAvailableLanguages();
|
|
@@ -1100,7 +1128,14 @@ async function analyzeTranslations(datasetPath) {
|
|
|
1100
1128
|
analyzer.outputDir = './reports';
|
|
1101
1129
|
|
|
1102
1130
|
// Load and analyze the dataset
|
|
1103
|
-
const
|
|
1131
|
+
const datasetContent = SecurityUtils.safeReadFileSync(datasetPath, process.cwd(), 'utf8');
|
|
1132
|
+
if (!datasetContent) {
|
|
1133
|
+
throw new Error('Failed to load dataset for benchmark');
|
|
1134
|
+
}
|
|
1135
|
+
const dataset = SecurityUtils.safeParseJSON(datasetContent);
|
|
1136
|
+
if (!dataset) {
|
|
1137
|
+
throw new Error('Failed to parse dataset JSON');
|
|
1138
|
+
}
|
|
1104
1139
|
|
|
1105
1140
|
// Simulate analysis processing
|
|
1106
1141
|
const languages = Object.keys(dataset).filter(lang => lang !== 'en');
|
package/main/i18ntk-backup.js
CHANGED
package/main/i18ntk-init.js
CHANGED
package/main/i18ntk-manage.js
CHANGED
package/main/i18ntk-setup.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* i18ntk-setup.js - Foundational Setup Script
|
|
@@ -334,6 +334,15 @@ class I18nSetupManager {
|
|
|
334
334
|
const SettingsManager = require('../settings/settings-manager');
|
|
335
335
|
const settingsManager = new SettingsManager();
|
|
336
336
|
|
|
337
|
+
console.log('🔧 About to update settings with:', {
|
|
338
|
+
setup: {
|
|
339
|
+
completed: true,
|
|
340
|
+
completedAt: new Date().toISOString(),
|
|
341
|
+
version: require('../package.json').version,
|
|
342
|
+
setupId: `setup_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
337
346
|
settingsManager.updateSettings({
|
|
338
347
|
'sourceDir': this.config.sourceDir,
|
|
339
348
|
'outputDir': this.config.outputDir,
|
package/main/i18ntk-sizing.js
CHANGED
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
|
|
34
34
|
const fs = require('fs');
|
|
35
35
|
const path = require('path');
|
|
36
|
-
const { performance } = require('perf_hooks');
|
|
37
36
|
const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
38
37
|
const configManager = require('../settings/settings-manager');
|
|
39
38
|
const SecurityUtils = require('../utils/security');
|
|
@@ -116,23 +115,24 @@ class I18nSizingAnalyzer {
|
|
|
116
115
|
throw new Error(t("sizing.invalidSourceDirectoryError", { sourceDir: this.sourceDir }));
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
if (!
|
|
118
|
+
if (!SecurityUtils.safeExistsSync(validatedSourceDir)) {
|
|
120
119
|
throw new Error(t("sizing.sourceDirectoryNotFoundError", { sourceDir: validatedSourceDir }));
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
const files = [];
|
|
124
|
-
const items =
|
|
123
|
+
const items = SecurityUtils.safeReaddirSync(validatedSourceDir);
|
|
125
124
|
|
|
126
125
|
// Check for nested language directories
|
|
127
126
|
for (const item of items) {
|
|
128
127
|
const itemPath = SecurityUtils.validatePath(path.join(validatedSourceDir, item), process.cwd());
|
|
129
128
|
if (!itemPath) continue;
|
|
130
129
|
|
|
131
|
-
const stat =
|
|
130
|
+
const stat = SecurityUtils.safeStatSync(itemPath);
|
|
131
|
+
if (!stat) continue;
|
|
132
132
|
|
|
133
133
|
if (stat.isDirectory()) {
|
|
134
134
|
// This is a language directory, combine all JSON files
|
|
135
|
-
const langFiles =
|
|
135
|
+
const langFiles = SecurityUtils.safeReaddirSync(itemPath)
|
|
136
136
|
.filter(file => file.endsWith('.json'))
|
|
137
137
|
.map(file => SecurityUtils.validatePath(path.join(itemPath, file), process.cwd()))
|
|
138
138
|
.filter(file => file !== null);
|
|
@@ -165,7 +165,7 @@ class I18nSizingAnalyzer {
|
|
|
165
165
|
|
|
166
166
|
// Analyze file sizes
|
|
167
167
|
analyzeFileSizes(files) {
|
|
168
|
-
|
|
168
|
+
SecurityUtils.debugLog('info', t("sizing.analyzing_file_sizes"));
|
|
169
169
|
|
|
170
170
|
files.forEach(({ language, file, path: filePath, files: langFiles }) => {
|
|
171
171
|
if (langFiles) {
|
|
@@ -176,7 +176,9 @@ class I18nSizingAnalyzer {
|
|
|
176
176
|
let lastModified = new Date(0);
|
|
177
177
|
|
|
178
178
|
langFiles.forEach(langFile => {
|
|
179
|
-
const stats =
|
|
179
|
+
const stats = SecurityUtils.safeStatSync(langFile, process.cwd());
|
|
180
|
+
if (!stats) return;
|
|
181
|
+
|
|
180
182
|
let content = SecurityUtils.safeReadFileSync(langFile, process.cwd());
|
|
181
183
|
if (typeof content !== "string") content = "";
|
|
182
184
|
totalSize += stats.size;
|
|
@@ -198,7 +200,9 @@ class I18nSizingAnalyzer {
|
|
|
198
200
|
};
|
|
199
201
|
} else {
|
|
200
202
|
// Handle single file structure
|
|
201
|
-
const stats =
|
|
203
|
+
const stats = SecurityUtils.safeStatSync(filePath, process.cwd());
|
|
204
|
+
if (!stats) return;
|
|
205
|
+
|
|
202
206
|
let content = SecurityUtils.safeReadFileSync(filePath, process.cwd());
|
|
203
207
|
if (typeof content !== "string") content = "";
|
|
204
208
|
this.stats.files[language] = {
|
|
@@ -216,7 +220,7 @@ class I18nSizingAnalyzer {
|
|
|
216
220
|
|
|
217
221
|
// Analyze translation content
|
|
218
222
|
analyzeTranslationContent(files) {
|
|
219
|
-
|
|
223
|
+
SecurityUtils.debugLog('info', t("sizing.analyzing_translation_content"));
|
|
220
224
|
|
|
221
225
|
files.forEach(({ language, path: filePath, files: langFiles }) => {
|
|
222
226
|
try {
|
|
@@ -262,7 +266,7 @@ class I18nSizingAnalyzer {
|
|
|
262
266
|
});
|
|
263
267
|
|
|
264
268
|
} catch (error) {
|
|
265
|
-
|
|
269
|
+
SecurityUtils.debugLog('error', t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
|
|
266
270
|
}
|
|
267
271
|
});
|
|
268
272
|
}
|
|
@@ -312,13 +316,13 @@ class I18nSizingAnalyzer {
|
|
|
312
316
|
|
|
313
317
|
// Generate size comparison analysis
|
|
314
318
|
generateSizeComparison() {
|
|
315
|
-
|
|
319
|
+
SecurityUtils.debugLog('info', t("sizing.generating_size_comparisons"));
|
|
316
320
|
|
|
317
321
|
const languages = Object.keys(this.stats.languages);
|
|
318
322
|
const baseLanguage = languages[0]; // Use first language as baseline
|
|
319
323
|
|
|
320
324
|
if (!baseLanguage) {
|
|
321
|
-
|
|
325
|
+
SecurityUtils.debugLog('warn', t("sizing.no_languages_found_for_comparison"));
|
|
322
326
|
return;
|
|
323
327
|
}
|
|
324
328
|
|
|
@@ -491,7 +495,7 @@ class I18nSizingAnalyzer {
|
|
|
491
495
|
async generateHumanReadableReport() {
|
|
492
496
|
if (!this.outputReport) return;
|
|
493
497
|
|
|
494
|
-
|
|
498
|
+
SecurityUtils.debugLog('info', t("sizing.generating_detailed_report"));
|
|
495
499
|
|
|
496
500
|
const validatedOutputDir = SecurityUtils.validatePath(this.outputDir, process.cwd());
|
|
497
501
|
if (!validatedOutputDir) {
|
|
@@ -499,8 +503,8 @@ class I18nSizingAnalyzer {
|
|
|
499
503
|
}
|
|
500
504
|
|
|
501
505
|
// Ensure output directory exists
|
|
502
|
-
if (!
|
|
503
|
-
|
|
506
|
+
if (!SecurityUtils.safeExistsSync(validatedOutputDir)) {
|
|
507
|
+
SecurityUtils.safeMkdirSync(validatedOutputDir, { recursive: true });
|
|
504
508
|
}
|
|
505
509
|
|
|
506
510
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
@@ -514,7 +518,7 @@ class I18nSizingAnalyzer {
|
|
|
514
518
|
let textReport = this.generateTextReport(timestamp);
|
|
515
519
|
const textSuccess = await SecurityUtils.safeWriteFile(textReportPath, textReport, process.cwd());
|
|
516
520
|
if (textSuccess) {
|
|
517
|
-
|
|
521
|
+
SecurityUtils.debugLog('info', t("sizing.human_report_saved", { reportPath: textReportPath }));
|
|
518
522
|
}
|
|
519
523
|
|
|
520
524
|
// Generate JSON for programmatic access
|
|
@@ -660,7 +664,7 @@ Generated: ${new Date().toISOString()}
|
|
|
660
664
|
|
|
661
665
|
const success = await SecurityUtils.safeWriteFile(csvPath, csvContent, process.cwd());
|
|
662
666
|
if (success) {
|
|
663
|
-
|
|
667
|
+
SecurityUtils.debugLog('info', t("sizing.csv_report_saved_to", { csvPath }));
|
|
664
668
|
SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
|
|
665
669
|
} else {
|
|
666
670
|
throw new Error(t("sizing.failedToSaveCsvError"));
|
|
@@ -669,20 +673,21 @@ Generated: ${new Date().toISOString()}
|
|
|
669
673
|
|
|
670
674
|
// Main analysis method
|
|
671
675
|
async analyze() {
|
|
672
|
-
const
|
|
676
|
+
const perfTimer = SecurityUtils.getPerformanceTimer();
|
|
677
|
+
const startTime = perfTimer.now();
|
|
673
678
|
|
|
674
679
|
try {
|
|
675
|
-
|
|
676
|
-
|
|
680
|
+
SecurityUtils.debugLog('info', t("sizing.starting_i18n_sizing_analysis"));
|
|
681
|
+
SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
677
682
|
|
|
678
683
|
const files = this.getLanguageFiles();
|
|
679
684
|
|
|
680
685
|
if (files.length === 0) {
|
|
681
|
-
|
|
686
|
+
SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
|
|
682
687
|
return;
|
|
683
688
|
}
|
|
684
689
|
|
|
685
|
-
|
|
690
|
+
SecurityUtils.debugLog('info', t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
|
|
686
691
|
|
|
687
692
|
this.analyzeFileSizes(files);
|
|
688
693
|
this.analyzeTranslationContent(files);
|
|
@@ -691,16 +696,16 @@ Generated: ${new Date().toISOString()}
|
|
|
691
696
|
if (this.format === 'table') {
|
|
692
697
|
this.displayFolderResults();
|
|
693
698
|
} else if (this.format === 'json') {
|
|
694
|
-
|
|
699
|
+
SecurityUtils.debugLog('info', t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
|
|
695
700
|
}
|
|
696
701
|
|
|
697
702
|
await this.generateHumanReadableReport();
|
|
698
703
|
|
|
699
|
-
const endTime =
|
|
700
|
-
|
|
704
|
+
const endTime = perfTimer.now();
|
|
705
|
+
SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
|
|
701
706
|
|
|
702
707
|
} catch (error) {
|
|
703
|
-
|
|
708
|
+
SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
704
709
|
process.exit(1);
|
|
705
710
|
}
|
|
706
711
|
}
|
|
@@ -901,19 +906,20 @@ Options:
|
|
|
901
906
|
// Main analysis method
|
|
902
907
|
async analyze() {
|
|
903
908
|
try {
|
|
904
|
-
|
|
905
|
-
|
|
909
|
+
SecurityUtils.debugLog('info', t("sizing.starting_analysis"));
|
|
910
|
+
SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
906
911
|
|
|
907
|
-
const
|
|
912
|
+
const perfTimer = SecurityUtils.getPerformanceTimer();
|
|
913
|
+
const startTime = perfTimer.now();
|
|
908
914
|
|
|
909
915
|
// Get language files
|
|
910
916
|
const files = this.getLanguageFiles();
|
|
911
917
|
if (files.length === 0) {
|
|
912
|
-
|
|
918
|
+
SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
|
|
913
919
|
return { success: false, error: "No translation files found" };
|
|
914
920
|
}
|
|
915
921
|
|
|
916
|
-
|
|
922
|
+
SecurityUtils.debugLog('info', t("sizing.found_files", { count: files.length }));
|
|
917
923
|
|
|
918
924
|
// Analyze file sizes
|
|
919
925
|
this.analyzeFileSizes(files);
|
|
@@ -930,15 +936,15 @@ Options:
|
|
|
930
936
|
// Generate reports if requested
|
|
931
937
|
await this.generateHumanReadableReport();
|
|
932
938
|
|
|
933
|
-
const endTime =
|
|
939
|
+
const endTime = perfTimer.now();
|
|
934
940
|
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
|
935
941
|
|
|
936
|
-
|
|
942
|
+
SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration }));
|
|
937
943
|
|
|
938
944
|
return { success: true, stats: this.stats };
|
|
939
945
|
|
|
940
946
|
} catch (error) {
|
|
941
|
-
|
|
947
|
+
SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
942
948
|
return { success: false, error: error.message };
|
|
943
949
|
}
|
|
944
950
|
}
|
package/main/i18ntk-usage.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18ntk",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.2",
|
|
4
4
|
"description": "🚀 The fastest i18n toolkit with 97% performance boost! Zero-dependency, enterprise-grade internationalization for React, Vue, Angular, Python, Java, PHP & more. Features PIN protection, auto framework detection, 7+ UI languages, and comprehensive translation management. Perfect for startups to enterprises.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -213,8 +213,8 @@
|
|
|
213
213
|
},
|
|
214
214
|
"preferGlobal": true,
|
|
215
215
|
"versionInfo": {
|
|
216
|
-
"version": "1.10.
|
|
217
|
-
"releaseDate": "
|
|
216
|
+
"version": "1.10.2",
|
|
217
|
+
"releaseDate": "23/08/2025",
|
|
218
218
|
"lastUpdated": "23/08/2025",
|
|
219
219
|
"maintainer": "Vladimir Noskov",
|
|
220
220
|
"changelog": "./CHANGELOG.md",
|
|
@@ -283,6 +283,5 @@
|
|
|
283
283
|
"laravel": ">=8.0.0"
|
|
284
284
|
}
|
|
285
285
|
},
|
|
286
|
-
"dependencies": {},
|
|
287
286
|
"_comment": "This package is zero-dependency and uses only native Node.js modules"
|
|
288
287
|
}
|