i18ntk 2.4.0 → 2.5.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/CODE_OF_CONDUCT.md +133 -0
- package/CONTRIBUTING.md +41 -0
- package/FUNDING.md +5 -0
- package/README.md +62 -15
- package/SECURITY.md +52 -0
- package/main/manage/commands/FixerCommand.js +97 -97
- package/main/manage/managers/DebugMenu.js +10 -9
- package/package.json +59 -184
- package/runtime/index.js +14 -8
- package/utils/admin-auth.js +655 -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/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
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* @module managers/DebugMenu
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const { t } = require('../../../utils/i18n-helper');
|
|
9
|
-
const cliHelper = require('../../../utils/cli-helper');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
9
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
10
|
+
const SecurityUtils = require('../../../utils/security');
|
|
10
11
|
|
|
11
12
|
module.exports = class DebugMenu {
|
|
12
13
|
constructor(manager) {
|
|
@@ -22,7 +23,7 @@ module.exports = class DebugMenu {
|
|
|
22
23
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('debugMenu');
|
|
23
24
|
if (authRequired) {
|
|
24
25
|
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
25
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
26
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
26
27
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
27
28
|
|
|
28
29
|
if (!isValid) {
|
|
@@ -66,7 +67,7 @@ module.exports = class DebugMenu {
|
|
|
66
67
|
console.log(t('debug.runningDebugTool', { displayName }));
|
|
67
68
|
try {
|
|
68
69
|
const toolPath = path.join(__dirname, '..', '..', 'scripts', 'debug', toolName);
|
|
69
|
-
if (
|
|
70
|
+
if (SecurityUtils.safeExistsSync(toolPath, path.dirname(toolPath))) {
|
|
70
71
|
console.log(`Debug tool available: ${toolName}`);
|
|
71
72
|
console.log(`To run this tool manually: node "${toolPath}"`);
|
|
72
73
|
console.log(`Working directory: ${path.join(__dirname, '..', '..')}`);
|
|
@@ -90,7 +91,7 @@ module.exports = class DebugMenu {
|
|
|
90
91
|
|
|
91
92
|
try {
|
|
92
93
|
const logsDir = path.join(__dirname, '..', '..', 'scripts', 'debug', 'logs');
|
|
93
|
-
if (
|
|
94
|
+
if (SecurityUtils.safeExistsSync(logsDir, path.dirname(logsDir))) {
|
|
94
95
|
const files = fs.readdirSync(logsDir)
|
|
95
96
|
.filter(file => file.endsWith('.log') || file.endsWith('.txt'))
|
|
96
97
|
.sort((a, b) => {
|
|
@@ -111,7 +112,7 @@ module.exports = class DebugMenu {
|
|
|
111
112
|
const fileIndex = parseInt(choice) - 1;
|
|
112
113
|
|
|
113
114
|
if (fileIndex >= 0 && fileIndex < files.length) {
|
|
114
|
-
const logContent =
|
|
115
|
+
const logContent = SecurityUtils.safeReadFileSync(path.join(logsDir, files[fileIndex]), logsDir, 'utf8');
|
|
115
116
|
console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
|
|
116
117
|
console.log('============================================================');
|
|
117
118
|
console.log(logContent.slice(-2000)); // Show last 2000 characters
|
|
@@ -137,4 +138,4 @@ module.exports = class DebugMenu {
|
|
|
137
138
|
async show() {
|
|
138
139
|
return this.showDebugMenu();
|
|
139
140
|
}
|
|
140
|
-
};
|
|
141
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18ntk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.5.1",
|
|
4
|
+
"description": "Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, fixing, reporting, and runtime translation loading.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
7
7
|
"internationalization",
|
|
@@ -9,94 +9,22 @@
|
|
|
9
9
|
"translation",
|
|
10
10
|
"l10n",
|
|
11
11
|
"multilingual",
|
|
12
|
-
"globalization",
|
|
13
12
|
"i18next",
|
|
14
13
|
"react-i18next",
|
|
15
14
|
"vue-i18n",
|
|
16
15
|
"angular-i18n",
|
|
17
16
|
"next-i18next",
|
|
18
|
-
"nuxt-i18n",
|
|
19
|
-
"svelte-i18n",
|
|
20
|
-
"django",
|
|
21
|
-
"flask",
|
|
22
|
-
"fastapi",
|
|
23
|
-
"laravel",
|
|
24
|
-
"spring-boot",
|
|
25
17
|
"nodejs",
|
|
26
18
|
"javascript",
|
|
27
19
|
"typescript",
|
|
28
20
|
"cli",
|
|
29
21
|
"command-line",
|
|
30
|
-
"automation",
|
|
31
22
|
"developer-tools",
|
|
32
|
-
"productivity",
|
|
33
23
|
"translation-management",
|
|
34
|
-
"language-support",
|
|
35
24
|
"framework-detection",
|
|
36
25
|
"zero-dependency",
|
|
37
|
-
"lightweight",
|
|
38
|
-
"fast",
|
|
39
|
-
"performance",
|
|
40
26
|
"security",
|
|
41
|
-
"
|
|
42
|
-
"devops",
|
|
43
|
-
"ci-cd",
|
|
44
|
-
"json",
|
|
45
|
-
"yaml",
|
|
46
|
-
"csv",
|
|
47
|
-
"po",
|
|
48
|
-
"xliff",
|
|
49
|
-
"android",
|
|
50
|
-
"ios",
|
|
51
|
-
"react-native",
|
|
52
|
-
"flutter",
|
|
53
|
-
"expo",
|
|
54
|
-
"electron",
|
|
55
|
-
"desktop",
|
|
56
|
-
"mobile",
|
|
57
|
-
"web",
|
|
58
|
-
"full-stack",
|
|
59
|
-
"backend",
|
|
60
|
-
"frontend",
|
|
61
|
-
"api",
|
|
62
|
-
"microservices",
|
|
63
|
-
"serverless",
|
|
64
|
-
"docker",
|
|
65
|
-
"kubernetes",
|
|
66
|
-
"cloud",
|
|
67
|
-
"aws",
|
|
68
|
-
"azure",
|
|
69
|
-
"gcp",
|
|
70
|
-
"saas",
|
|
71
|
-
"paas",
|
|
72
|
-
"open-source",
|
|
73
|
-
"free",
|
|
74
|
-
"npm",
|
|
75
|
-
"package",
|
|
76
|
-
"tool",
|
|
77
|
-
"utility",
|
|
78
|
-
"workflow",
|
|
79
|
-
"efficiency",
|
|
80
|
-
"best-practices",
|
|
81
|
-
"standards",
|
|
82
|
-
"compliance",
|
|
83
|
-
"accessibility",
|
|
84
|
-
"a11y",
|
|
85
|
-
"seo",
|
|
86
|
-
"marketing",
|
|
87
|
-
"growth",
|
|
88
|
-
"scale",
|
|
89
|
-
"startup",
|
|
90
|
-
"business",
|
|
91
|
-
"professional",
|
|
92
|
-
"expert",
|
|
93
|
-
"advanced",
|
|
94
|
-
"comprehensive",
|
|
95
|
-
"complete",
|
|
96
|
-
"all-in-one",
|
|
97
|
-
"suite",
|
|
98
|
-
"platform",
|
|
99
|
-
"solution"
|
|
27
|
+
"json"
|
|
100
28
|
],
|
|
101
29
|
"homepage": "https://github.com/vladnoskv/i18ntk#readme",
|
|
102
30
|
"bugs": {
|
|
@@ -107,8 +35,8 @@
|
|
|
107
35
|
"url": "git+https://github.com/vladnoskv/i18ntk.git"
|
|
108
36
|
},
|
|
109
37
|
"funding": {
|
|
110
|
-
"type": "
|
|
111
|
-
"url": "https://
|
|
38
|
+
"type": "individual",
|
|
39
|
+
"url": "https://www.charitynavigator.org/"
|
|
112
40
|
},
|
|
113
41
|
"license": "MIT",
|
|
114
42
|
"author": {
|
|
@@ -131,9 +59,6 @@
|
|
|
131
59
|
"default": "./runtime/enhanced.js"
|
|
132
60
|
},
|
|
133
61
|
"./runtime/*": "./runtime/*",
|
|
134
|
-
"./main/*": "./main/*",
|
|
135
|
-
"./utils/*": "./utils/*",
|
|
136
|
-
"./settings/*": "./settings/*",
|
|
137
62
|
"./ui-locales/*": "./ui-locales/*",
|
|
138
63
|
"./package.json": "./package.json"
|
|
139
64
|
},
|
|
@@ -153,68 +78,66 @@
|
|
|
153
78
|
"i18ntk-scanner": "main/i18ntk-scanner.js",
|
|
154
79
|
"i18ntk-backup": "main/i18ntk-backup.js"
|
|
155
80
|
},
|
|
156
|
-
"directories": {
|
|
157
|
-
"doc": "docs",
|
|
158
|
-
"test": "tests"
|
|
159
|
-
},
|
|
160
81
|
"files": [
|
|
161
|
-
"main/",
|
|
82
|
+
"main/i18ntk-analyze.js",
|
|
83
|
+
"main/i18ntk-backup-class.js",
|
|
84
|
+
"main/i18ntk-backup.js",
|
|
85
|
+
"main/i18ntk-complete.js",
|
|
86
|
+
"main/i18ntk-doctor.js",
|
|
87
|
+
"main/i18ntk-fixer.js",
|
|
88
|
+
"main/i18ntk-init.js",
|
|
89
|
+
"main/i18ntk-scanner.js",
|
|
90
|
+
"main/i18ntk-setup.js",
|
|
91
|
+
"main/i18ntk-sizing.js",
|
|
92
|
+
"main/i18ntk-summary.js",
|
|
93
|
+
"main/i18ntk-ui.js",
|
|
94
|
+
"main/i18ntk-usage.js",
|
|
95
|
+
"main/i18ntk-validate.js",
|
|
96
|
+
"main/manage/",
|
|
162
97
|
"runtime/",
|
|
163
|
-
"
|
|
164
|
-
"settings/",
|
|
98
|
+
"settings/language-config.json",
|
|
99
|
+
"settings/settings-cli.js",
|
|
100
|
+
"settings/settings-manager.js",
|
|
165
101
|
"ui-locales/",
|
|
166
102
|
"!main/manage/index-fixed.js",
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"
|
|
103
|
+
"utils/admin-auth.js",
|
|
104
|
+
"utils/admin-cli.js",
|
|
105
|
+
"utils/cli-helper.js",
|
|
106
|
+
"utils/cli.js",
|
|
107
|
+
"utils/colors-new.js",
|
|
108
|
+
"utils/config-helper.js",
|
|
109
|
+
"utils/config-manager.js",
|
|
110
|
+
"utils/config.js",
|
|
111
|
+
"utils/env-manager.js",
|
|
112
|
+
"utils/exit-codes.js",
|
|
113
|
+
"utils/extractor-manager.js",
|
|
114
|
+
"utils/extractors/regex.js",
|
|
115
|
+
"utils/format-manager.js",
|
|
116
|
+
"utils/formats/json.js",
|
|
117
|
+
"utils/framework-detector.js",
|
|
118
|
+
"utils/i18n-helper.js",
|
|
119
|
+
"utils/init-helper.js",
|
|
120
|
+
"utils/json-output.js",
|
|
121
|
+
"utils/locale-optimizer.js",
|
|
122
|
+
"utils/logger.js",
|
|
123
|
+
"utils/npm-version-warning.js",
|
|
124
|
+
"utils/plugin-loader.js",
|
|
125
|
+
"utils/prompt-helper.js",
|
|
126
|
+
"utils/prompt.js",
|
|
127
|
+
"utils/secure-errors.js",
|
|
128
|
+
"utils/security.js",
|
|
129
|
+
"utils/setup-enforcer.js",
|
|
130
|
+
"utils/terminal-icons.js",
|
|
131
|
+
"utils/version-utils.js",
|
|
132
|
+
"utils/watch-locales.js",
|
|
175
133
|
"LICENSE",
|
|
176
|
-
"
|
|
177
|
-
"
|
|
134
|
+
"README.md",
|
|
135
|
+
"CODE_OF_CONDUCT.md",
|
|
136
|
+
"CONTRIBUTING.md",
|
|
137
|
+
"FUNDING.md",
|
|
138
|
+
"SECURITY.md"
|
|
178
139
|
],
|
|
179
140
|
"sideEffects": false,
|
|
180
|
-
"scripts": {
|
|
181
|
-
"i18ntk": "node main/manage/index.js",
|
|
182
|
-
"i18ntk-setup": "node main/i18ntk-setup.js",
|
|
183
|
-
"i18ntk-manage": "node main/manage/index.js",
|
|
184
|
-
"i18ntk-init": "node main/i18ntk-init.js",
|
|
185
|
-
"i18ntk-analyze": "node main/i18ntk-analyze.js",
|
|
186
|
-
"i18ntk-validate": "node main/i18ntk-validate.js",
|
|
187
|
-
"i18ntk-usage": "node main/i18ntk-usage.js",
|
|
188
|
-
"i18ntk-complete": "node main/i18ntk-complete.js",
|
|
189
|
-
"i18ntk-sizing": "node main/i18ntk-sizing.js",
|
|
190
|
-
"i18ntk-summary": "node main/i18ntk-summary.js",
|
|
191
|
-
"i18ntk-doctor": "node main/i18ntk-doctor.js",
|
|
192
|
-
"i18ntk-fixer": "node main/i18ntk-fixer.js",
|
|
193
|
-
"i18ntk-scanner": "node main/i18ntk-scanner.js",
|
|
194
|
-
"i18ntk-backup": "node main/i18ntk-backup.js",
|
|
195
|
-
"i18ntk-py": "node main/i18ntk-py.js",
|
|
196
|
-
"i18ntk-js": "node main/i18ntk-js.js",
|
|
197
|
-
"i18ntk-java": "node main/i18ntk-java.js",
|
|
198
|
-
"i18ntk-php": "node main/i18ntk-php.js",
|
|
199
|
-
"i18ntk-go": "node main/i18ntk-go.js",
|
|
200
|
-
"start": "node main/manage/index.js",
|
|
201
|
-
"security:check": "node utils/security-check-improved.js",
|
|
202
|
-
"security:test": "node --test tests/security.test.js",
|
|
203
|
-
"security:audit": "npm run security:check && npm run security:test",
|
|
204
|
-
"test": "npm run security:test",
|
|
205
|
-
"test:all": "npm run security:audit",
|
|
206
|
-
"prepublishOnly": "npm run security:audit",
|
|
207
|
-
"prepare": "npm run security:check",
|
|
208
|
-
"backup:create": "node main/i18ntk-backup.js create",
|
|
209
|
-
"backup:restore": "node main/i18ntk-backup.js restore",
|
|
210
|
-
"backup:list": "node main/i18ntk-backup.js list",
|
|
211
|
-
"backup:verify": "node main/i18ntk-backup.js verify",
|
|
212
|
-
"backup:cleanup": "node main/i18ntk-backup.js cleanup",
|
|
213
|
-
"languages:select": "node settings/settings-cli.js",
|
|
214
|
-
"languages:list": "node settings/settings-cli.js --list-languages",
|
|
215
|
-
"languages:status": "node settings/settings-cli.js --language-status",
|
|
216
|
-
"lint:locales": "node scripts/lint-locales.js"
|
|
217
|
-
},
|
|
218
141
|
"engines": {
|
|
219
142
|
"node": ">=16.0.0",
|
|
220
143
|
"npm": ">=8.0.0"
|
|
@@ -222,53 +145,5 @@
|
|
|
222
145
|
"publishConfig": {
|
|
223
146
|
"access": "public"
|
|
224
147
|
},
|
|
225
|
-
"preferGlobal": true
|
|
226
|
-
"versionInfo": {
|
|
227
|
-
"version": "2.4.0",
|
|
228
|
-
"releaseDate": "16/04/2026",
|
|
229
|
-
"lastUpdated": "16/04/2026",
|
|
230
|
-
"maintainer": "Vlad Noskov",
|
|
231
|
-
"changelog": "./CHANGELOG.md",
|
|
232
|
-
"documentation": "./README.md",
|
|
233
|
-
"apiReference": "./docs/api/API_REFERENCE.md",
|
|
234
|
-
"majorChanges": [
|
|
235
|
-
"LOGGING: Introduced centralized structured logger with silent-by-default production behavior and DEBUG_MODE/JSON_LOG toggles.",
|
|
236
|
-
"SECURITY: Added internal path whitelist detection to prevent false-positive traversal warnings for package/project internals.",
|
|
237
|
-
"I18N: Added missing-key warning TTL cache to eliminate repeated translation-key spam during builds.",
|
|
238
|
-
"HOTFIX: Removed deprecated package-path fallback that caused production build warnings for non-exported subpaths.",
|
|
239
|
-
"CRITICAL FIX: Resolved sizing and usage-analysis regressions in v2 command flow.",
|
|
240
|
-
"PACKAGING: Reduced publish footprint by removing internal development scripts and legacy fixed-file artifacts.",
|
|
241
|
-
"SECURITY: Hardened release checks and added explicit support guidance to update from pre-2.3.5 versions.",
|
|
242
|
-
"CONFIG: Added cross-process file locking for .i18ntk-config writes to prevent production rename races.",
|
|
243
|
-
"CONFIG: Made autosave runtime-safe with non-throwing save failures and I18NTK_DISABLE_AUTOSAVE support.",
|
|
244
|
-
"SECURITY: Hardened reset/backup path handling and disabled manager backup-route execution in current builds.",
|
|
245
|
-
"CLI: Removed npm registry startup update checks to eliminate outbound network calls in scanner-restricted environments.",
|
|
246
|
-
"I18N: Completed internal UI locale parity and actionable untranslated-key cleanup across supported languages."
|
|
247
|
-
],
|
|
248
|
-
"breakingChanges": [],
|
|
249
|
-
"nextVersion": "2.4.1",
|
|
250
|
-
"supportedNodeVersions": ">=16.0.0",
|
|
251
|
-
"supportedFrameworks": {
|
|
252
|
-
"react-i18next": ">=11.0.0",
|
|
253
|
-
"vue-i18n": ">=9.0.0",
|
|
254
|
-
"angular-i18n": ">=12.0.0",
|
|
255
|
-
"next-i18next": ">=13.0.0",
|
|
256
|
-
"nuxt-i18n": ">=8.0.0",
|
|
257
|
-
"svelte-i18n": ">=3.0.0",
|
|
258
|
-
"sveltekit-i18n": ">=2.0.0",
|
|
259
|
-
"react-native-localize": ">=2.0.0",
|
|
260
|
-
"expo-localization": ">=14.0.0",
|
|
261
|
-
"ionic-angular": ">=6.0.0",
|
|
262
|
-
"ember-intl": ">=5.0.0",
|
|
263
|
-
"formatjs": ">=2.0.0",
|
|
264
|
-
"i18next": ">=21.0.0",
|
|
265
|
-
"django": ">=3.0.0",
|
|
266
|
-
"flask-babel": ">=2.0.0",
|
|
267
|
-
"fastapi": ">=0.70.0",
|
|
268
|
-
"spring-boot": ">=2.5.0",
|
|
269
|
-
"laravel": ">=8.0.0"
|
|
270
|
-
},
|
|
271
|
-
"supportPolicy": "Versions earlier than 2.4.0 may be unstable or insecure. Upgrade to 2.4.0 or newer."
|
|
272
|
-
},
|
|
273
|
-
"_comment": "This package is zero-dependency and uses only native Node.js modules"
|
|
148
|
+
"preferGlobal": true
|
|
274
149
|
}
|
package/runtime/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const SecurityUtils = require('../utils/security');
|
|
9
|
+
const { envManager } = require('../utils/env-manager');
|
|
9
10
|
|
|
10
11
|
let configManager = null;
|
|
11
12
|
try { configManager = require('../utils/config-manager'); } catch (_) { /* optional */ }
|
|
@@ -54,15 +55,18 @@ function resolveBaseDir(explicitBaseDir) {
|
|
|
54
55
|
// 1) Highest priority: explicit option
|
|
55
56
|
if (explicitBaseDir) return path.resolve(explicitBaseDir);
|
|
56
57
|
// 2) Environment override for CI/explicit control
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
const runtimeDir = envManager.get('I18NTK_RUNTIME_DIR');
|
|
59
|
+
if (runtimeDir) {
|
|
60
|
+
return path.resolve(runtimeDir);
|
|
59
61
|
}
|
|
60
62
|
// 2b) Respect config-style env overrides, even without config-manager
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
const envI18nDir = envManager.get('I18NTK_I18N_DIR');
|
|
64
|
+
if (envI18nDir) {
|
|
65
|
+
return path.resolve(envI18nDir);
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
const envSourceDir = envManager.get('I18NTK_SOURCE_DIR');
|
|
68
|
+
if (envSourceDir) {
|
|
69
|
+
return path.resolve(envSourceDir);
|
|
66
70
|
}
|
|
67
71
|
// 3) Use config-manager if available (single source of truth: i18ntk-config.json)
|
|
68
72
|
try {
|
|
@@ -72,11 +76,13 @@ function resolveBaseDir(explicitBaseDir) {
|
|
|
72
76
|
// If config-manager resolved absolute paths, use as-is; otherwise resolve from project cwd
|
|
73
77
|
const isAbs = typeof base === 'string' && path.isAbsolute(base);
|
|
74
78
|
if (isAbs) return base;
|
|
75
|
-
const
|
|
79
|
+
const envProjectRoot = envManager.get('I18NTK_PROJECT_ROOT');
|
|
80
|
+
const root = envProjectRoot ? path.resolve(envProjectRoot) : process.cwd();
|
|
76
81
|
return path.resolve(root, base);
|
|
77
82
|
} catch (_) {
|
|
78
83
|
// 4) Fallback to conventional './locales' from project CWD
|
|
79
|
-
const
|
|
84
|
+
const envProjectRoot = envManager.get('I18NTK_PROJECT_ROOT');
|
|
85
|
+
const root = envProjectRoot ? path.resolve(envProjectRoot) : process.cwd();
|
|
80
86
|
return path.resolve(root, './locales');
|
|
81
87
|
}
|
|
82
88
|
}
|