i18ntk 2.2.0 → 2.3.1
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 +83 -50
- package/main/i18ntk-backup-class.js +37 -41
- package/main/i18ntk-backup.js +28 -30
- package/main/i18ntk-doctor.js +7 -6
- package/main/i18ntk-init.js +44 -8
- package/main/i18ntk-sizing.js +7 -8
- package/main/i18ntk-usage.js +17 -5
- package/main/i18ntk-validate.js +72 -22
- package/main/manage/commands/AnalyzeCommand.js +12 -14
- package/main/manage/commands/CommandRouter.js +15 -12
- package/main/manage/commands/FixerCommand.js +92 -36
- package/main/manage/commands/ValidateCommand.js +78 -27
- package/main/manage/index.js +158 -148
- package/main/manage/managers/DebugMenu.js +6 -6
- package/main/manage/managers/InteractiveMenu.js +6 -6
- package/main/manage/managers/LanguageMenu.js +5 -4
- 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 +17 -5
- package/package.json +8 -7
- package/settings/settings-cli.js +2 -2
- package/settings/settings-manager.js +984 -968
- package/utils/config-helper.js +27 -16
- package/utils/config-manager.js +8 -7
- package/utils/i18n-helper.js +9 -13
- package/utils/init-helper.js +3 -2
- package/utils/json-output.js +11 -10
- 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
|
@@ -8,12 +8,14 @@ const path = require('path');
|
|
|
8
8
|
const configManager = require('../../../settings/settings-manager');
|
|
9
9
|
const { loadTranslations, t, refreshLanguageFromSettings } = require('../../../utils/i18n-helper');
|
|
10
10
|
|
|
11
|
-
const { createPrompt, isInteractive } = require('../../../utils/prompt-helper');
|
|
12
|
-
const { loadConfig, saveConfig, ensureConfigDefaults } = require('../../../utils/config');
|
|
13
|
-
const { getUnifiedConfig, ensureInitialized, validateSourceDir } = require('../../../utils/config-helper');
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
11
|
+
const { createPrompt, isInteractive } = require('../../../utils/prompt-helper');
|
|
12
|
+
const { loadConfig, saveConfig, ensureConfigDefaults } = require('../../../utils/config');
|
|
13
|
+
const { getUnifiedConfig, ensureInitialized, validateSourceDir } = require('../../../utils/config-helper');
|
|
14
|
+
const { checkInitialized } = require('../../../utils/init-helper');
|
|
15
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
16
|
+
const pkg = require('../../../package.json');
|
|
17
|
+
const SetupEnforcer = require('../../../utils/setup-enforcer');
|
|
18
|
+
const FrameworkDetectionService = require('./FrameworkDetectionService');
|
|
17
19
|
|
|
18
20
|
module.exports = class ConfigurationService {
|
|
19
21
|
constructor(config = {}) {
|
|
@@ -54,9 +56,8 @@ module.exports = class ConfigurationService {
|
|
|
54
56
|
loadTranslations(uiLanguage);
|
|
55
57
|
|
|
56
58
|
// Validate source directory exists
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
|
|
59
|
+
try {
|
|
60
|
+
validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
|
|
60
61
|
} catch (err) {
|
|
61
62
|
console.log(t('init.requiredTitle'));
|
|
62
63
|
console.log(t('init.requiredBody'));
|
|
@@ -102,23 +103,18 @@ module.exports = class ConfigurationService {
|
|
|
102
103
|
// Ensure setup is complete before running any operations
|
|
103
104
|
await SetupEnforcer.checkSetupCompleteAsync();
|
|
104
105
|
|
|
105
|
-
prompt = createPrompt({ noPrompt: args.noPrompt
|
|
106
|
-
const interactive = isInteractive({ noPrompt: args.noPrompt
|
|
106
|
+
prompt = createPrompt({ noPrompt: args.noPrompt });
|
|
107
|
+
const interactive = isInteractive({ noPrompt: args.noPrompt });
|
|
107
108
|
|
|
108
109
|
// Load settings and UI language
|
|
109
110
|
const settings = this.settings || (this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {}));
|
|
110
111
|
const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
|
|
111
112
|
loadTranslations(uiLanguage);
|
|
112
113
|
|
|
113
|
-
if (args.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (args.help) {
|
|
119
|
-
this.showHelp();
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
114
|
+
if (args.help) {
|
|
115
|
+
this.showHelp();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
122
118
|
|
|
123
119
|
let cfgAfterInitCheck = {};
|
|
124
120
|
if (interactive) {
|
|
@@ -155,11 +151,7 @@ module.exports = class ConfigurationService {
|
|
|
155
151
|
* @returns {Promise<Object>} Configuration object if initialized
|
|
156
152
|
*/
|
|
157
153
|
async ensureInitializedOrExit(prompt) {
|
|
158
|
-
const {
|
|
159
|
-
const cliHelper = require('../../../utils/cli-helper');
|
|
160
|
-
const pkg = require('../../../package.json');
|
|
161
|
-
|
|
162
|
-
const { initialized, config } = await checkInitialized();
|
|
154
|
+
const { initialized, config } = await checkInitialized();
|
|
163
155
|
|
|
164
156
|
if (!initialized) {
|
|
165
157
|
console.log('\nThis project is not yet initialized with i18ntk.');
|
|
@@ -224,8 +216,7 @@ module.exports = class ConfigurationService {
|
|
|
224
216
|
console.log('Framework detection prompt will be suppressed for this version.');
|
|
225
217
|
} else if (action === 'detect') {
|
|
226
218
|
// Run framework detection
|
|
227
|
-
const
|
|
228
|
-
const frameworkService = new FrameworkDetectionService();
|
|
219
|
+
const frameworkService = new FrameworkDetectionService();
|
|
229
220
|
frameworkService.initialize(this.configManager);
|
|
230
221
|
|
|
231
222
|
const { detectedLanguage, detectedFramework } = await frameworkService.detectEnvironmentAndFramework();
|
|
@@ -260,8 +251,7 @@ module.exports = class ConfigurationService {
|
|
|
260
251
|
* @returns {Promise<boolean>} True if frameworks detected, false otherwise
|
|
261
252
|
*/
|
|
262
253
|
async checkI18nDependencies() {
|
|
263
|
-
const
|
|
264
|
-
const frameworkService = new FrameworkDetectionService();
|
|
254
|
+
const frameworkService = new FrameworkDetectionService();
|
|
265
255
|
frameworkService.initialize(this.configManager);
|
|
266
256
|
return await frameworkService.checkI18nDependencies(null);
|
|
267
257
|
}
|
|
@@ -274,8 +264,7 @@ module.exports = class ConfigurationService {
|
|
|
274
264
|
* @returns {Promise<Object>} Updated configuration
|
|
275
265
|
*/
|
|
276
266
|
async maybePromptFramework(prompt, cfg, currentVersion) {
|
|
277
|
-
const
|
|
278
|
-
const frameworkService = new FrameworkDetectionService();
|
|
267
|
+
const frameworkService = new FrameworkDetectionService();
|
|
279
268
|
frameworkService.initialize(this.configManager);
|
|
280
269
|
return await frameworkService.maybePromptFramework(prompt, cfg, currentVersion);
|
|
281
270
|
}
|
|
@@ -392,8 +381,7 @@ module.exports = class ConfigurationService {
|
|
|
392
381
|
* @returns {Promise<string>} User input or empty string
|
|
393
382
|
*/
|
|
394
383
|
prompt(question) {
|
|
395
|
-
|
|
396
|
-
// If interactive not available, return empty string to avoid hangs
|
|
384
|
+
// If interactive not available, return empty string to avoid hangs
|
|
397
385
|
if (!process.stdin.isTTY || process.stdin.destroyed) {
|
|
398
386
|
console.log('\n⚠️ Interactive input not available, using default response.');
|
|
399
387
|
return Promise.resolve('');
|
|
@@ -446,4 +434,4 @@ module.exports = class ConfigurationService {
|
|
|
446
434
|
getUI() {
|
|
447
435
|
return this.ui;
|
|
448
436
|
}
|
|
449
|
-
};
|
|
437
|
+
};
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* @module services/FileManagementService
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const SecurityUtils = require('../../../utils/security');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const SecurityUtils = require('../../../utils/security');
|
|
10
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
10
11
|
|
|
11
12
|
module.exports = class FileManagementService {
|
|
12
13
|
constructor(config = {}) {
|
|
@@ -108,8 +109,7 @@ module.exports = class FileManagementService {
|
|
|
108
109
|
const authRequired = await this.isAuthRequiredForScript('deleteReports');
|
|
109
110
|
if (authRequired) {
|
|
110
111
|
console.log(`\n${ui ? ui.t('adminPin.protectedAccess') : 'Protected Access'}`);
|
|
111
|
-
const
|
|
112
|
-
const pin = await cliHelper.promptPin((ui ? ui.t('adminPin.enterPin') : 'Enter PIN: ') + ': ');
|
|
112
|
+
const pin = await cliHelper.promptPin((ui ? ui.t('adminPin.enterPin') : 'Enter PIN: ') + ': ');
|
|
113
113
|
const isValid = await this.verifyPin(pin);
|
|
114
114
|
if (!isValid) {
|
|
115
115
|
console.log(ui ? ui.t('adminPin.invalidPin') : 'Invalid PIN');
|
|
@@ -365,4 +365,4 @@ module.exports = class FileManagementService {
|
|
|
365
365
|
// For now, return true as a placeholder
|
|
366
366
|
return true;
|
|
367
367
|
}
|
|
368
|
-
};
|
|
368
|
+
};
|
|
@@ -681,7 +681,7 @@ class InitService {
|
|
|
681
681
|
}
|
|
682
682
|
|
|
683
683
|
// Interactive admin PIN setup
|
|
684
|
-
async promptAdminPinSetup() {
|
|
684
|
+
async promptAdminPinSetup() {
|
|
685
685
|
const { ask, askHidden, flushStdout } = require('../../../utils/cli');
|
|
686
686
|
|
|
687
687
|
console.log('\n' + t('init.adminPinSetupOptional'));
|
|
@@ -733,8 +733,31 @@ class InitService {
|
|
|
733
733
|
}
|
|
734
734
|
} else {
|
|
735
735
|
console.log(t('init.skippingAdminPinSetup'));
|
|
736
|
-
}
|
|
737
|
-
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
async promptBackupConfiguration(skipPrompt = false) {
|
|
740
|
+
const defaultBackupConfig = { enabled: false, maxBackups: 1, location: './i18ntk-backups' };
|
|
741
|
+
if (skipPrompt || !process.stdin.isTTY) {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const { ask } = require('../../../utils/cli');
|
|
746
|
+
console.log('\nBackup Settings');
|
|
747
|
+
console.log('Backups are disabled by default to avoid backup recursion and repo pollution.');
|
|
748
|
+
const enableAnswer = await ask('Enable automatic backups? (y/N): ');
|
|
749
|
+
const enabled = ['y', 'yes'].includes(String(enableAnswer || '').trim().toLowerCase());
|
|
750
|
+
|
|
751
|
+
if (!enabled) {
|
|
752
|
+
return defaultBackupConfig;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const keepAnswer = await ask('How many backups should be kept automatically (1-3, default 1): ');
|
|
756
|
+
const parsedKeep = parseInt(String(keepAnswer || '').trim(), 10);
|
|
757
|
+
const maxBackups = Number.isInteger(parsedKeep) ? Math.min(Math.max(parsedKeep, 1), 3) : 1;
|
|
758
|
+
|
|
759
|
+
return { enabled: true, maxBackups, location: './i18ntk-backups' };
|
|
760
|
+
}
|
|
738
761
|
|
|
739
762
|
// Interactive language selection
|
|
740
763
|
async selectLanguages(skipPrompt = false) {
|
|
@@ -845,11 +868,24 @@ class InitService {
|
|
|
845
868
|
// Prompt for admin PIN setup if not already configured
|
|
846
869
|
const securitySettings = configManager.getConfig().security || {};
|
|
847
870
|
|
|
848
|
-
if (!securitySettings.adminPinEnabled && securitySettings.adminPinPromptOnInit !== false && !args.noPrompt) {
|
|
849
|
-
const { flushStdout } = require('../../../utils/cli');
|
|
850
|
-
await flushStdout();
|
|
851
|
-
await this.promptAdminPinSetup();
|
|
852
|
-
}
|
|
871
|
+
if (!securitySettings.adminPinEnabled && securitySettings.adminPinPromptOnInit !== false && !args.noPrompt) {
|
|
872
|
+
const { flushStdout } = require('../../../utils/cli');
|
|
873
|
+
await flushStdout();
|
|
874
|
+
await this.promptAdminPinSetup();
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const backupSettings = await this.promptBackupConfiguration(args.noPrompt);
|
|
878
|
+
if (backupSettings) {
|
|
879
|
+
await configManager.updateConfig({
|
|
880
|
+
backup: {
|
|
881
|
+
...(this.config.backup || {}),
|
|
882
|
+
...backupSettings
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
this.config.backup = { ...(this.config.backup || {}), ...backupSettings };
|
|
886
|
+
} else if (!this.config.backup) {
|
|
887
|
+
this.config.backup = { enabled: false, maxBackups: 1, location: './i18ntk-backups' };
|
|
888
|
+
}
|
|
853
889
|
|
|
854
890
|
// Get target languages - use args.languages if provided
|
|
855
891
|
let targetLanguages = args.languages || await this.selectLanguages(args.noPrompt);
|
|
@@ -512,10 +512,22 @@ class UsageService {
|
|
|
512
512
|
return keys;
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
-
collectPlaceholderKeys(obj, prefix = '', language) {
|
|
516
|
-
const patterns = this.placeholderStyles[language] || [];
|
|
517
|
-
const regexes = patterns.
|
|
518
|
-
|
|
515
|
+
collectPlaceholderKeys(obj, prefix = '', language) {
|
|
516
|
+
const patterns = this.placeholderStyles[language] || [];
|
|
517
|
+
const regexes = patterns.reduce((compiled, pattern) => {
|
|
518
|
+
try {
|
|
519
|
+
compiled.push(new RegExp(pattern));
|
|
520
|
+
} catch (error) {
|
|
521
|
+
SecurityUtils.logSecurityEvent('Invalid placeholder regex pattern skipped', 'warn', {
|
|
522
|
+
component: 'i18ntk-usage',
|
|
523
|
+
language,
|
|
524
|
+
pattern,
|
|
525
|
+
error: error.message
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return compiled;
|
|
529
|
+
}, []);
|
|
530
|
+
if (typeof obj !== 'object' || obj === null) return;
|
|
519
531
|
|
|
520
532
|
for (const [key, value] of Object.entries(obj)) {
|
|
521
533
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
@@ -1499,4 +1511,4 @@ Analysis Features (v1.8.3):
|
|
|
1499
1511
|
}
|
|
1500
1512
|
}
|
|
1501
1513
|
|
|
1502
|
-
module.exports = UsageService;
|
|
1514
|
+
module.exports = UsageService;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18ntk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "🚀 The fastest
|
|
3
|
+
"version": "2.3.1",
|
|
4
|
+
"description": "🚀 The fastest internationalization 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",
|
|
7
7
|
"internationalization",
|
|
@@ -227,21 +227,22 @@
|
|
|
227
227
|
},
|
|
228
228
|
"preferGlobal": true,
|
|
229
229
|
"versionInfo": {
|
|
230
|
-
"version": "2.
|
|
230
|
+
"version": "2.3.1",
|
|
231
231
|
"releaseDate": "12/04/2026",
|
|
232
232
|
"lastUpdated": "12/04/2026",
|
|
233
|
-
"maintainer": "
|
|
233
|
+
"maintainer": "Vlad Noskov",
|
|
234
234
|
"changelog": "./CHANGELOG.md",
|
|
235
235
|
"documentation": "./README.md",
|
|
236
236
|
"apiReference": "./docs/api/API_REFERENCE.md",
|
|
237
237
|
"majorChanges": [
|
|
238
|
+
"HOTFIX: Removed deprecated package-path fallback that caused production build warnings for non-exported subpaths.",
|
|
238
239
|
"CRITICAL FIX: Resolved sizing and usage-analysis regressions in v2 command flow.",
|
|
239
240
|
"PACKAGING: Reduced publish footprint by removing internal development scripts and legacy fixed-file artifacts.",
|
|
240
|
-
"SECURITY: Hardened release checks and added explicit support guidance to update from pre-2.
|
|
241
|
+
"SECURITY: Hardened release checks and added explicit support guidance to update from pre-2.3.1 versions.",
|
|
241
242
|
"I18N: Completed internal UI locale parity and actionable untranslated-key cleanup across supported languages."
|
|
242
243
|
],
|
|
243
244
|
"breakingChanges": [],
|
|
244
|
-
"nextVersion": "2.
|
|
245
|
+
"nextVersion": "2.4.0",
|
|
245
246
|
"supportedNodeVersions": ">=16.0.0",
|
|
246
247
|
"supportedFrameworks": {
|
|
247
248
|
"react-i18next": ">=11.0.0",
|
|
@@ -263,7 +264,7 @@
|
|
|
263
264
|
"spring-boot": ">=2.5.0",
|
|
264
265
|
"laravel": ">=8.0.0"
|
|
265
266
|
},
|
|
266
|
-
"supportPolicy": "Versions earlier than 2.
|
|
267
|
+
"supportPolicy": "Versions earlier than 2.3.1 may be unstable or insecure. Upgrade to 2.3.1 or newer."
|
|
267
268
|
},
|
|
268
269
|
"_comment": "This package is zero-dependency and uses only native Node.js modules"
|
|
269
270
|
}
|
package/settings/settings-cli.js
CHANGED
|
@@ -567,8 +567,8 @@ class SettingsCLI {
|
|
|
567
567
|
'backupRetention': { min: 1, max: 30, type: 'int', unit: 'days' },
|
|
568
568
|
'logRetention': { min: 1, max: 90, type: 'int', unit: 'days' },
|
|
569
569
|
'retentionDays': { min: 1, max: 365, type: 'int', unit: 'days' },
|
|
570
|
-
'maxBackups': { min: 1, max:
|
|
571
|
-
};
|
|
570
|
+
'maxBackups': { min: 1, max: 3, type: 'int' }
|
|
571
|
+
};
|
|
572
572
|
|
|
573
573
|
for (const [field, rules] of Object.entries(validations)) {
|
|
574
574
|
if (key.includes(field)) {
|