i18ntk 2.3.8 → 2.5.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 +10 -6
- package/main/i18ntk-backup-class.js +35 -423
- package/main/manage/commands/BackupCommand.js +62 -62
- package/main/manage/commands/FixerCommand.js +97 -97
- package/main/manage/managers/DebugMenu.js +10 -9
- package/main/manage/services/SetupService.js +444 -462
- package/package.json +61 -32
- package/runtime/index.js +14 -8
- package/utils/admin-auth.js +594 -576
- package/utils/config-manager.js +72 -72
- package/utils/env-manager.js +117 -26
- package/utils/i18n-helper.js +50 -49
- package/utils/json-output.js +13 -12
- package/utils/logger.js +7 -6
- package/utils/npm-version-warning.js +12 -141
- package/utils/prompt-helper.js +44 -41
- package/utils/secure-errors.js +156 -154
- package/utils/security.js +235 -233
- package/utils/setup-enforcer.js +110 -109
- package/utils/terminal-icons.js +164 -163
- package/settings/i18ntk-config.json +0 -283
- package/utils/admin-pin.js +0 -520
- package/utils/arg-parser.js +0 -40
- package/utils/cli-args.js +0 -210
- package/utils/mini-commander.js +0 -179
- package/utils/missing-key-validator.js +0 -858
- package/utils/path-utils.js +0 -33
- package/utils/performance-optimizer.js +0 -246
- package/utils/prompt-new.js +0 -55
- package/utils/promptPin.js +0 -76
- package/utils/safe-json.js +0 -40
- package/utils/secure-backup.js +0 -340
- package/utils/security-check-improved.js +0 -393
- package/utils/security-config.js +0 -239
- package/utils/setup-validator.js +0 -717
- package/utils/ultra-performance-optimizer.js +0 -352
package/utils/config-manager.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const crypto = require('crypto');
|
|
5
|
-
const SecurityUtils = require('./security');
|
|
6
|
-
const { logger } = require('./logger');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const crypto = require('crypto');
|
|
5
|
+
const SecurityUtils = require('./security');
|
|
6
|
+
const { logger } = require('./logger');
|
|
7
|
+
const { envManager } = require('./env-manager');
|
|
7
8
|
|
|
8
9
|
// Determine package directory and user project root
|
|
9
10
|
const packageDir = path.resolve(__dirname, '..');
|
|
@@ -20,55 +21,55 @@ const CONFIG_LOCK_PATH = `${PROJECT_CONFIG_PATH}.lock`;
|
|
|
20
21
|
const CONFIG_LOCK_TIMEOUT_MS = 5000;
|
|
21
22
|
const CONFIG_LOCK_STALE_MS = 15000;
|
|
22
23
|
const CONFIG_LOCK_RETRY_MS = 50;
|
|
23
|
-
let autosaveDisabledWarned = false;
|
|
24
|
-
let defaultConfigNoticeShown = false;
|
|
25
|
-
let configFallbackNoticeShown = false;
|
|
26
|
-
|
|
27
|
-
function logInfo(message, details) {
|
|
28
|
-
logger.info(message, details);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function logWarn(message, details) {
|
|
32
|
-
logger.warn(message, details);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function logError(message, details) {
|
|
36
|
-
logger.error(message, details);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function notifyDefaultConfig(reason, error) {
|
|
40
|
-
if (defaultConfigNoticeShown) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
defaultConfigNoticeShown = true;
|
|
44
|
-
|
|
45
|
-
logger.info('Using default configuration (reason: configuration error)', { reason });
|
|
46
|
-
if (logger.isDebugMode() && error) {
|
|
47
|
-
logger.debug(`Default configuration reason details: ${error.message}`, {
|
|
48
|
-
stack: error.stack
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function notifyConfigFallback(error) {
|
|
54
|
-
if (!configFallbackNoticeShown) {
|
|
55
|
-
configFallbackNoticeShown = true;
|
|
56
|
-
logger.info('Using default configuration (reason: configuration error)', {
|
|
57
|
-
reason: 'configuration error'
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
logger.recordFirstError('config:load-fallback', {
|
|
62
|
-
message: error && error.message ? error.message : 'Unknown configuration error',
|
|
63
|
-
stack: error && error.stack ? error.stack : null
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
if (logger.isDebugMode() && error) {
|
|
67
|
-
logger.debug(`Configuration load fallback details: ${error.message}`, {
|
|
68
|
-
stack: error.stack
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
24
|
+
let autosaveDisabledWarned = false;
|
|
25
|
+
let defaultConfigNoticeShown = false;
|
|
26
|
+
let configFallbackNoticeShown = false;
|
|
27
|
+
|
|
28
|
+
function logInfo(message, details) {
|
|
29
|
+
logger.info(message, details);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function logWarn(message, details) {
|
|
33
|
+
logger.warn(message, details);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function logError(message, details) {
|
|
37
|
+
logger.error(message, details);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function notifyDefaultConfig(reason, error) {
|
|
41
|
+
if (defaultConfigNoticeShown) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
defaultConfigNoticeShown = true;
|
|
45
|
+
|
|
46
|
+
logger.info('Using default configuration (reason: configuration error)', { reason });
|
|
47
|
+
if (logger.isDebugMode() && error) {
|
|
48
|
+
logger.debug(`Default configuration reason details: ${error.message}`, {
|
|
49
|
+
stack: error.stack
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function notifyConfigFallback(error) {
|
|
55
|
+
if (!configFallbackNoticeShown) {
|
|
56
|
+
configFallbackNoticeShown = true;
|
|
57
|
+
logger.info('Using default configuration (reason: configuration error)', {
|
|
58
|
+
reason: 'configuration error'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
logger.recordFirstError('config:load-fallback', {
|
|
63
|
+
message: error && error.message ? error.message : 'Unknown configuration error',
|
|
64
|
+
stack: error && error.stack ? error.stack : null
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (logger.isDebugMode() && error) {
|
|
68
|
+
logger.debug(`Configuration load fallback details: ${error.message}`, {
|
|
69
|
+
stack: error.stack
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
72
73
|
|
|
73
74
|
// Setup tracking file
|
|
74
75
|
const SETUP_COMPLETED_FILE = path.join(PROJECT_SETTINGS_DIR, 'setup.json');
|
|
@@ -362,8 +363,7 @@ async function acquireConfigLock(timeoutMs = CONFIG_LOCK_TIMEOUT_MS) {
|
|
|
362
363
|
}
|
|
363
364
|
|
|
364
365
|
function isAutosaveDisabled() {
|
|
365
|
-
|
|
366
|
-
return flag === '1' || flag === 'true' || flag === 'yes';
|
|
366
|
+
return envManager.getBoolean('I18NTK_DISABLE_AUTOSAVE');
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
function clone(obj) {
|
|
@@ -530,8 +530,8 @@ function loadConfig() {
|
|
|
530
530
|
currentConfig = cfg;
|
|
531
531
|
return currentConfig;
|
|
532
532
|
} catch (error) {
|
|
533
|
-
logError('[i18ntk] Error in loadConfig', { error: error.message });
|
|
534
|
-
notifyConfigFallback(error);
|
|
533
|
+
logError('[i18ntk] Error in loadConfig', { error: error.message });
|
|
534
|
+
notifyConfigFallback(error);
|
|
535
535
|
currentConfig = clone(DEFAULT_CONFIG);
|
|
536
536
|
return currentConfig;
|
|
537
537
|
} finally {
|
|
@@ -590,7 +590,7 @@ async function saveConfig(cfg = currentConfig) {
|
|
|
590
590
|
currentConfig = cfg;
|
|
591
591
|
return true;
|
|
592
592
|
} catch (error) {
|
|
593
|
-
logError('[i18ntk] Error saving configuration', { error: error.message });
|
|
593
|
+
logError('[i18ntk] Error saving configuration', { error: error.message });
|
|
594
594
|
return false;
|
|
595
595
|
} finally {
|
|
596
596
|
if (releaseLock) {
|
|
@@ -643,7 +643,7 @@ function getConfig() {
|
|
|
643
643
|
|
|
644
644
|
// Check for legacy config for migration
|
|
645
645
|
if (SecurityUtils.safeExistsSync(LEGACY_CONFIG_PATH)) {
|
|
646
|
-
logInfo('Migrating legacy configuration');
|
|
646
|
+
logInfo('Migrating legacy configuration');
|
|
647
647
|
const legacyRaw = SecurityUtils.safeReadFileSync(LEGACY_CONFIG_PATH, path.dirname(LEGACY_CONFIG_PATH), 'utf8');
|
|
648
648
|
const legacyConfig = SecurityUtils.safeParseJSON(legacyRaw);
|
|
649
649
|
if (!legacyConfig || typeof legacyConfig !== 'object') {
|
|
@@ -651,7 +651,7 @@ function getConfig() {
|
|
|
651
651
|
}
|
|
652
652
|
const migratedConfig = { ...DEFAULT_CONFIG, ...legacyConfig };
|
|
653
653
|
saveConfig(migratedConfig).catch((err) => {
|
|
654
|
-
logWarn('[i18ntk] Warning: failed to persist migrated configuration', { error: err.message });
|
|
654
|
+
logWarn('[i18ntk] Warning: failed to persist migrated configuration', { error: err.message });
|
|
655
655
|
});
|
|
656
656
|
currentConfig = migratedConfig;
|
|
657
657
|
|
|
@@ -669,18 +669,18 @@ function getConfig() {
|
|
|
669
669
|
}
|
|
670
670
|
|
|
671
671
|
// Use package defaults for new installation
|
|
672
|
-
notifyDefaultConfig('default initialization');
|
|
673
|
-
saveConfig(DEFAULT_CONFIG).catch((err) => {
|
|
674
|
-
logWarn('[i18ntk] Warning: failed to persist default configuration', { error: err.message });
|
|
675
|
-
});
|
|
676
|
-
currentConfig = DEFAULT_CONFIG;
|
|
677
|
-
return resolvePaths(DEFAULT_CONFIG);
|
|
678
|
-
|
|
679
|
-
} catch (error) {
|
|
680
|
-
notifyConfigFallback(error);
|
|
681
|
-
currentConfig = DEFAULT_CONFIG;
|
|
682
|
-
return resolvePaths(DEFAULT_CONFIG);
|
|
683
|
-
}
|
|
672
|
+
notifyDefaultConfig('default initialization');
|
|
673
|
+
saveConfig(DEFAULT_CONFIG).catch((err) => {
|
|
674
|
+
logWarn('[i18ntk] Warning: failed to persist default configuration', { error: err.message });
|
|
675
|
+
});
|
|
676
|
+
currentConfig = DEFAULT_CONFIG;
|
|
677
|
+
return resolvePaths(DEFAULT_CONFIG);
|
|
678
|
+
|
|
679
|
+
} catch (error) {
|
|
680
|
+
notifyConfigFallback(error);
|
|
681
|
+
currentConfig = DEFAULT_CONFIG;
|
|
682
|
+
return resolvePaths(DEFAULT_CONFIG);
|
|
683
|
+
}
|
|
684
684
|
}
|
|
685
685
|
|
|
686
686
|
async function setConfig(cfg) {
|
package/utils/env-manager.js
CHANGED
|
@@ -13,6 +13,54 @@ const ALLOWED_ENV_VARS = {
|
|
|
13
13
|
validate: (value) => ['error', 'warn', 'info', 'debug', 'silent'].includes(value.toLowerCase()),
|
|
14
14
|
transform: (value) => value.toLowerCase()
|
|
15
15
|
},
|
|
16
|
+
|
|
17
|
+
'DEBUG_MODE': {
|
|
18
|
+
default: 'false',
|
|
19
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
20
|
+
transform: normalizeBooleanString
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
'JSON_LOG': {
|
|
24
|
+
default: 'false',
|
|
25
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
26
|
+
transform: normalizeBooleanString
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
'NODE_ENV': {
|
|
30
|
+
default: 'development',
|
|
31
|
+
validate: (value) => ['development', 'production', 'test'].includes(value.toLowerCase()),
|
|
32
|
+
transform: (value) => value.toLowerCase()
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
'CI': {
|
|
36
|
+
default: 'false',
|
|
37
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
38
|
+
transform: normalizeBooleanString
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
'CONTINUOUS_INTEGRATION': {
|
|
42
|
+
default: 'false',
|
|
43
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
44
|
+
transform: normalizeBooleanString
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
'NO_INTERACTIVE': {
|
|
48
|
+
default: 'false',
|
|
49
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
50
|
+
transform: normalizeBooleanString
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
'npm_lifecycle_event': {
|
|
54
|
+
default: '',
|
|
55
|
+
validate: (value) => typeof value === 'string',
|
|
56
|
+
transform: (value) => value.trim()
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
'npm_config_loglevel': {
|
|
60
|
+
default: '',
|
|
61
|
+
validate: (value) => typeof value === 'string',
|
|
62
|
+
transform: (value) => value.trim().toLowerCase()
|
|
63
|
+
},
|
|
16
64
|
|
|
17
65
|
'I18NTK_OUTDIR': {
|
|
18
66
|
default: './i18ntk-reports',
|
|
@@ -21,29 +69,23 @@ const ALLOWED_ENV_VARS = {
|
|
|
21
69
|
},
|
|
22
70
|
|
|
23
71
|
// UI and interaction
|
|
24
|
-
'I18NTK_UI_LANGUAGE': {
|
|
25
|
-
default: 'en',
|
|
26
|
-
validate: (value) => ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(value.toLowerCase()),
|
|
27
|
-
transform: (value) => value.toLowerCase()
|
|
28
|
-
},
|
|
72
|
+
'I18NTK_UI_LANGUAGE': {
|
|
73
|
+
default: 'en',
|
|
74
|
+
validate: (value) => ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(value.toLowerCase()),
|
|
75
|
+
transform: (value) => value.toLowerCase()
|
|
76
|
+
},
|
|
29
77
|
|
|
30
78
|
'I18NTK_SILENT': {
|
|
31
79
|
default: 'false',
|
|
32
80
|
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
33
|
-
transform:
|
|
34
|
-
const lower = value.toLowerCase();
|
|
35
|
-
return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false';
|
|
36
|
-
}
|
|
81
|
+
transform: normalizeBooleanString
|
|
37
82
|
},
|
|
38
83
|
|
|
39
84
|
// Debug and development
|
|
40
85
|
'I18NTK_DEBUG_LOCALES': {
|
|
41
|
-
default: '
|
|
86
|
+
default: 'false',
|
|
42
87
|
validate: (value) => ['0', '1', 'true', 'false'].includes(value.toLowerCase()),
|
|
43
|
-
transform:
|
|
44
|
-
const lower = value.toLowerCase();
|
|
45
|
-
return lower === '1' || lower === 'true' ? '1' : '0';
|
|
46
|
-
}
|
|
88
|
+
transform: normalizeBooleanString
|
|
47
89
|
},
|
|
48
90
|
|
|
49
91
|
// Runtime configuration
|
|
@@ -54,19 +96,19 @@ const ALLOWED_ENV_VARS = {
|
|
|
54
96
|
},
|
|
55
97
|
|
|
56
98
|
'I18NTK_I18N_DIR': {
|
|
57
|
-
default:
|
|
99
|
+
default: null,
|
|
58
100
|
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
59
101
|
transform: (value) => value.trim()
|
|
60
102
|
},
|
|
61
103
|
|
|
62
104
|
'I18NTK_SOURCE_DIR': {
|
|
63
|
-
default:
|
|
105
|
+
default: null,
|
|
64
106
|
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
65
107
|
transform: (value) => value.trim()
|
|
66
108
|
},
|
|
67
109
|
|
|
68
110
|
'I18NTK_PROJECT_ROOT': {
|
|
69
|
-
default:
|
|
111
|
+
default: null,
|
|
70
112
|
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
71
113
|
transform: (value) => value.trim()
|
|
72
114
|
},
|
|
@@ -87,13 +129,57 @@ const ALLOWED_ENV_VARS = {
|
|
|
87
129
|
'I18NTK_FRAMEWORK_DETECT': {
|
|
88
130
|
default: 'true',
|
|
89
131
|
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
90
|
-
transform:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
132
|
+
transform: normalizeBooleanString
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
'I18NTK_DISABLE_AUTOSAVE': {
|
|
136
|
+
default: 'false',
|
|
137
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
138
|
+
transform: normalizeBooleanString
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
'I18NTK_INTERNAL_PATH_PREFIXES': {
|
|
142
|
+
default: '',
|
|
143
|
+
validate: (value) => typeof value === 'string',
|
|
144
|
+
transform: (value) => value.trim()
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
'I18NTK_ENABLE_SECURITY_LOGS': {
|
|
148
|
+
default: 'false',
|
|
149
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
150
|
+
transform: normalizeBooleanString
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
'TERM_PROGRAM': {
|
|
154
|
+
default: '',
|
|
155
|
+
validate: (value) => typeof value === 'string',
|
|
156
|
+
transform: (value) => value.trim()
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
'WT_SESSION': {
|
|
160
|
+
default: '',
|
|
161
|
+
validate: (value) => typeof value === 'string',
|
|
162
|
+
transform: (value) => value.trim()
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
'PSModulePath': {
|
|
166
|
+
default: '',
|
|
167
|
+
validate: (value) => typeof value === 'string',
|
|
168
|
+
transform: (value) => value.trim()
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
'POWERSHELL_EDITION': {
|
|
172
|
+
default: '',
|
|
173
|
+
validate: (value) => typeof value === 'string',
|
|
174
|
+
transform: (value) => value.trim()
|
|
94
175
|
}
|
|
95
176
|
};
|
|
96
177
|
|
|
178
|
+
function normalizeBooleanString(value) {
|
|
179
|
+
const lower = String(value || '').toLowerCase();
|
|
180
|
+
return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false';
|
|
181
|
+
}
|
|
182
|
+
|
|
97
183
|
// Security: Block access to sensitive environment variables
|
|
98
184
|
const BLOCKED_PATTERNS = [
|
|
99
185
|
/^SECRET/i,
|
|
@@ -170,6 +256,11 @@ class EnvironmentManager {
|
|
|
170
256
|
}
|
|
171
257
|
}
|
|
172
258
|
|
|
259
|
+
getBoolean(name) {
|
|
260
|
+
const value = this.get(name);
|
|
261
|
+
return value === true || value === 'true' || value === '1';
|
|
262
|
+
}
|
|
263
|
+
|
|
173
264
|
/**
|
|
174
265
|
* Check if an environment variable is allowed
|
|
175
266
|
* @param {string} name - Environment variable name
|
|
@@ -229,7 +320,7 @@ class EnvironmentManager {
|
|
|
229
320
|
const descriptions = {
|
|
230
321
|
'I18NTK_LOG_LEVEL': 'Logging level (error, warn, info, debug, silent)',
|
|
231
322
|
'I18NTK_OUTDIR': 'Output directory for reports and generated files',
|
|
232
|
-
'I18NTK_UI_LANGUAGE': 'UI language (en, de, es, fr, ru, ja, zh)',
|
|
323
|
+
'I18NTK_UI_LANGUAGE': 'UI language (en, de, es, fr, ru, ja, zh)',
|
|
233
324
|
'I18NTK_SILENT': 'Run in silent mode without interactive prompts',
|
|
234
325
|
'I18NTK_DEBUG_LOCALES': 'Enable debug logging for locale loading',
|
|
235
326
|
'I18NTK_RUNTIME_DIR': 'Custom runtime directory path',
|
|
@@ -248,9 +339,9 @@ class EnvironmentManager {
|
|
|
248
339
|
// Create singleton instance
|
|
249
340
|
const envManager = new EnvironmentManager();
|
|
250
341
|
|
|
251
|
-
module.exports = {
|
|
252
|
-
EnvironmentManager,
|
|
253
|
-
envManager,
|
|
342
|
+
module.exports = {
|
|
343
|
+
EnvironmentManager,
|
|
344
|
+
envManager,
|
|
254
345
|
ALLOWED_ENV_VARS,
|
|
255
346
|
BLOCKED_PATTERNS
|
|
256
|
-
};
|
|
347
|
+
};
|
package/utils/i18n-helper.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// utils/i18n-helper.js
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const { logger } = require('./logger');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { logger } = require('./logger');
|
|
5
|
+
const { envManager } = require('./env-manager');
|
|
5
6
|
|
|
6
7
|
// Lazy load SecurityUtils to prevent circular dependencies
|
|
7
8
|
let securityUtils;
|
|
@@ -101,15 +102,15 @@ function pkgUiLocalesDirViaThisFile() {
|
|
|
101
102
|
return path.resolve(__dirname, '..', 'ui-locales');
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
function pkgUiLocalesDirViaResolve() {
|
|
105
|
-
try {
|
|
106
|
-
// Resolve using the current exported package path.
|
|
107
|
-
const enJson = require.resolve('i18ntk/ui-locales/en.json');
|
|
108
|
-
return path.dirname(enJson);
|
|
109
|
-
} catch {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
105
|
+
function pkgUiLocalesDirViaResolve() {
|
|
106
|
+
try {
|
|
107
|
+
// Resolve using the current exported package path.
|
|
108
|
+
const enJson = require.resolve('i18ntk/ui-locales/en.json');
|
|
109
|
+
return path.dirname(enJson);
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
113
114
|
|
|
114
115
|
// Removed legacyResourcesUiLocalesDir as resources/i18n/ui-locales is deprecated
|
|
115
116
|
|
|
@@ -162,7 +163,7 @@ function findLocaleFilesAllDirs(lang, preferredDir) {
|
|
|
162
163
|
const SecurityUtils = getSecurityUtils();
|
|
163
164
|
const dirs = resolveLocalesDirs(preferredDir);
|
|
164
165
|
|
|
165
|
-
if (
|
|
166
|
+
if (envManager.getBoolean('I18NTK_DEBUG_LOCALES')) {
|
|
166
167
|
console.log('🔎 i18ntk locale search dirs:', dirs);
|
|
167
168
|
}
|
|
168
169
|
|
|
@@ -194,28 +195,28 @@ function findLocaleFilesAllDirs(lang, preferredDir) {
|
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
if (
|
|
198
|
+
if (envManager.getBoolean('I18NTK_DEBUG_LOCALES') && errors.length > 0) {
|
|
198
199
|
console.warn(`⚠️ Locale resolution errors for ${lang}:`, errors);
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
return files;
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
let translations = {};
|
|
205
|
-
let currentLanguage = 'en';
|
|
206
|
-
let isInitialized = false;
|
|
207
|
-
const missingKeyCache = new Map();
|
|
208
|
-
const missingKeyTtlMs = 5 * 60 * 1000;
|
|
209
|
-
|
|
210
|
-
function shouldReportMissingKey(key) {
|
|
211
|
-
const now = Date.now();
|
|
212
|
-
const expiresAt = missingKeyCache.get(key) || 0;
|
|
213
|
-
if (expiresAt > now) {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
missingKeyCache.set(key, now + missingKeyTtlMs);
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
205
|
+
let translations = {};
|
|
206
|
+
let currentLanguage = 'en';
|
|
207
|
+
let isInitialized = false;
|
|
208
|
+
const missingKeyCache = new Map();
|
|
209
|
+
const missingKeyTtlMs = 5 * 60 * 1000;
|
|
210
|
+
|
|
211
|
+
function shouldReportMissingKey(key) {
|
|
212
|
+
const now = Date.now();
|
|
213
|
+
const expiresAt = missingKeyCache.get(key) || 0;
|
|
214
|
+
if (expiresAt > now) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
missingKeyCache.set(key, now + missingKeyTtlMs);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
219
220
|
|
|
220
221
|
function loadTranslations(language) {
|
|
221
222
|
const cfg = safeRequireConfig();
|
|
@@ -243,7 +244,7 @@ function loadTranslations(language) {
|
|
|
243
244
|
currentLanguage = lang;
|
|
244
245
|
isInitialized = true;
|
|
245
246
|
|
|
246
|
-
if (
|
|
247
|
+
if (envManager.getBoolean('I18NTK_DEBUG_LOCALES')) {
|
|
247
248
|
console.log(`🗂 Loaded UI locale → ${file} (${lang})`);
|
|
248
249
|
}
|
|
249
250
|
|
|
@@ -255,7 +256,7 @@ function loadTranslations(language) {
|
|
|
255
256
|
}
|
|
256
257
|
} catch (e) {
|
|
257
258
|
loadErrors.push({ file, error: e.message });
|
|
258
|
-
if (
|
|
259
|
+
if (envManager.getBoolean('I18NTK_DEBUG_LOCALES')) {
|
|
259
260
|
console.warn(`⚠️ Failed to parse ${file}: ${e.message}`);
|
|
260
261
|
}
|
|
261
262
|
}
|
|
@@ -263,7 +264,7 @@ function loadTranslations(language) {
|
|
|
263
264
|
}
|
|
264
265
|
|
|
265
266
|
// Log comprehensive error summary if debugging
|
|
266
|
-
if (
|
|
267
|
+
if (envManager.getBoolean('I18NTK_DEBUG_LOCALES') && loadErrors.length > 0) {
|
|
267
268
|
console.warn(`📊 Locale loading errors summary:`, {
|
|
268
269
|
requested: requested,
|
|
269
270
|
triedLanguages: tryOrder,
|
|
@@ -298,11 +299,11 @@ function loadTranslations(language) {
|
|
|
298
299
|
currentLanguage = 'en';
|
|
299
300
|
isInitialized = true;
|
|
300
301
|
|
|
301
|
-
if (loadErrors.length > 0) {
|
|
302
|
-
logger.warn('No valid UI locale files found. Using built-in English strings.', {
|
|
303
|
-
errorCount: loadErrors.length
|
|
304
|
-
});
|
|
305
|
-
}
|
|
302
|
+
if (loadErrors.length > 0) {
|
|
303
|
+
logger.warn('No valid UI locale files found. Using built-in English strings.', {
|
|
304
|
+
errorCount: loadErrors.length
|
|
305
|
+
});
|
|
306
|
+
}
|
|
306
307
|
|
|
307
308
|
return translations;
|
|
308
309
|
}
|
|
@@ -351,24 +352,24 @@ function t(key, params = {}) {
|
|
|
351
352
|
}
|
|
352
353
|
}
|
|
353
354
|
|
|
354
|
-
if (typeof value === 'undefined') {
|
|
355
|
-
if (shouldReportMissingKey(key)) {
|
|
356
|
-
logger.logMissingTranslationKey(key, 'Configuration error');
|
|
357
|
-
}
|
|
358
|
-
return key;
|
|
359
|
-
}
|
|
355
|
+
if (typeof value === 'undefined') {
|
|
356
|
+
if (shouldReportMissingKey(key)) {
|
|
357
|
+
logger.logMissingTranslationKey(key, 'Configuration error');
|
|
358
|
+
}
|
|
359
|
+
return key;
|
|
360
|
+
}
|
|
360
361
|
|
|
361
362
|
// If we found a string, interpolate parameters
|
|
362
363
|
if (typeof value === 'string') {
|
|
363
364
|
return interpolateParams(value, params);
|
|
364
365
|
}
|
|
365
366
|
|
|
366
|
-
// Return the key if the final value is not a string
|
|
367
|
-
if (shouldReportMissingKey(`${key}:non-string`)) {
|
|
368
|
-
logger.warn(`Translation key does not resolve to a string: ${key}`);
|
|
369
|
-
}
|
|
370
|
-
return key;
|
|
371
|
-
}
|
|
367
|
+
// Return the key if the final value is not a string
|
|
368
|
+
if (shouldReportMissingKey(`${key}:non-string`)) {
|
|
369
|
+
logger.warn(`Translation key does not resolve to a string: ${key}`);
|
|
370
|
+
}
|
|
371
|
+
return key;
|
|
372
|
+
}
|
|
372
373
|
|
|
373
374
|
/**
|
|
374
375
|
* Interpolate parameters into a translation string
|
package/utils/json-output.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
const packageJson = require('../package.json');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
1
|
+
const packageJson = require('../package.json');
|
|
2
|
+
const { envManager } = require('./env-manager');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* JSON Output Utility for i18ntk commands
|
|
6
|
+
* Provides consistent machine-readable output format for CI/CD integration
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
class JsonOutput {
|
|
@@ -23,11 +24,11 @@ class JsonOutput {
|
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
getPackageVersion() {
|
|
27
|
-
try {
|
|
28
|
-
return packageJson.version;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
return '1.8.3';
|
|
27
|
+
getPackageVersion() {
|
|
28
|
+
try {
|
|
29
|
+
return packageJson.version;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return '1.8.3';
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -75,7 +76,7 @@ class JsonOutput {
|
|
|
75
76
|
output() {
|
|
76
77
|
this.data.metadata.duration = Date.now() - this.startTime;
|
|
77
78
|
|
|
78
|
-
if (
|
|
79
|
+
if (envManager.get('NODE_ENV') !== 'test') {
|
|
79
80
|
console.log(JSON.stringify(this.data, null, 2));
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -97,4 +98,4 @@ class JsonOutput {
|
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
module.exports = JsonOutput;
|
|
101
|
+
module.exports = JsonOutput;
|
package/utils/logger.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const colors = require('./colors-new');
|
|
2
|
+
const { envManager } = require('./env-manager');
|
|
2
3
|
|
|
3
4
|
const LEVELS = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
4
5
|
const DEFAULT_INTERNAL_PREFIXES = ['[BUILD]', '[WORKERS]', '[I18N]', '[SECURITY]', '[SUCCESS]', '[WARN]', '[ERROR]', '[INFO]', '[DEBUG]'];
|
|
@@ -9,8 +10,8 @@ function asBoolean(value) {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
function resolveLevel() {
|
|
12
|
-
const debugMode =
|
|
13
|
-
const configured = String(
|
|
13
|
+
const debugMode = envManager.getBoolean('DEBUG_MODE');
|
|
14
|
+
const configured = String(envManager.get('I18NTK_LOG_LEVEL') || '').trim().toLowerCase();
|
|
14
15
|
if (configured && Object.prototype.hasOwnProperty.call(LEVELS, configured)) {
|
|
15
16
|
return configured;
|
|
16
17
|
}
|
|
@@ -20,7 +21,7 @@ function resolveLevel() {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
// Silent-by-default in production-like builds.
|
|
23
|
-
if (
|
|
24
|
+
if (envManager.get('NODE_ENV') === 'production' || envManager.getBoolean('CI')) {
|
|
24
25
|
return 'error';
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -43,7 +44,7 @@ function normalizePrefix(prefix, fallback) {
|
|
|
43
44
|
function write(level, message, options = {}) {
|
|
44
45
|
if (!shouldLog(level)) return;
|
|
45
46
|
|
|
46
|
-
const jsonMode =
|
|
47
|
+
const jsonMode = envManager.getBoolean('JSON_LOG');
|
|
47
48
|
const prefix = normalizePrefix(options.prefix, `[${level.toUpperCase()}]`);
|
|
48
49
|
const details = options.details && typeof options.details === 'object' ? options.details : undefined;
|
|
49
50
|
const text = String(message || '').trim();
|
|
@@ -116,7 +117,7 @@ function flushErrorContexts() {
|
|
|
116
117
|
function emitErrorContextSummary({ force = false } = {}) {
|
|
117
118
|
const contexts = Array.from(FIRST_ERROR_CONTEXT.entries());
|
|
118
119
|
if (!contexts.length) return;
|
|
119
|
-
if (!force && !
|
|
120
|
+
if (!force && !envManager.getBoolean('DEBUG_MODE')) return;
|
|
120
121
|
|
|
121
122
|
for (const [key, context] of contexts) {
|
|
122
123
|
write('debug', `First error context (${key})`, { prefix: '[ERROR]', details: context });
|
|
@@ -204,7 +205,7 @@ const logger = {
|
|
|
204
205
|
write('info', String(message), { prefix: '[BUILD]', details });
|
|
205
206
|
},
|
|
206
207
|
isDebugMode() {
|
|
207
|
-
return
|
|
208
|
+
return envManager.getBoolean('DEBUG_MODE') || resolveLevel() === 'debug';
|
|
208
209
|
},
|
|
209
210
|
shouldLog,
|
|
210
211
|
formatDuration,
|