i18ntk 1.10.2 → 2.0.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/LICENSE +1 -1
- package/README.md +141 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -29
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +117 -117
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +157 -161
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +18 -18
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -454
package/utils/i18n-helper.js
CHANGED
|
@@ -2,11 +2,47 @@
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
|
|
5
|
+
// Lazy load SecurityUtils to prevent circular dependencies
|
|
6
|
+
let securityUtils;
|
|
7
|
+
function getSecurityUtils() {
|
|
8
|
+
if (!securityUtils) {
|
|
9
|
+
try {
|
|
10
|
+
securityUtils = require('./security');
|
|
11
|
+
} catch (error) {
|
|
12
|
+
// Fallback: use basic fs operations if SecurityUtils is not available
|
|
13
|
+
return {
|
|
14
|
+
safeExistsSync: (path) => {
|
|
15
|
+
try {
|
|
16
|
+
return require('fs').existsSync(path);
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
safeWriteFileSync: (path, data, encoding) => {
|
|
22
|
+
try {
|
|
23
|
+
return require('fs').writeFileSync(path, data, encoding);
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
safeReadFileSync: (path, encoding) => {
|
|
29
|
+
try {
|
|
30
|
+
return require('fs').readFileSync(path, encoding);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return securityUtils;
|
|
39
|
+
}
|
|
40
|
+
|
|
5
41
|
// Helper functions for OS-agnostic path handling
|
|
6
42
|
function toPosix(p) { return String(p).replace(/\\/g, '/'); }
|
|
7
|
-
function isBundledPath(p) {
|
|
8
|
-
const s = toPosix(p);
|
|
9
|
-
return s.includes('/node_modules/i18ntk/') || s.includes('/i18ntk/ui-locales/');
|
|
43
|
+
function isBundledPath(p) {
|
|
44
|
+
const s = toPosix(p);
|
|
45
|
+
return s.includes('/node_modules/i18ntk/') || s.includes('/i18ntk/ui-locales/');
|
|
10
46
|
}
|
|
11
47
|
|
|
12
48
|
function safeRequireConfig() {
|
|
@@ -21,7 +57,8 @@ function stripBOMAndComments(s) {
|
|
|
21
57
|
}
|
|
22
58
|
|
|
23
59
|
function readJsonSafe(file) {
|
|
24
|
-
const
|
|
60
|
+
const SecurityUtils = getSecurityUtils();
|
|
61
|
+
const raw = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
25
62
|
return JSON.parse(stripBOMAndComments(raw));
|
|
26
63
|
}
|
|
27
64
|
|
|
@@ -43,7 +80,8 @@ function resolveLocalesDirs() {
|
|
|
43
80
|
try {
|
|
44
81
|
const normalized = path.normalize(path.resolve(dir.trim()));
|
|
45
82
|
|
|
46
|
-
|
|
83
|
+
const SecurityUtils = getSecurityUtils();
|
|
84
|
+
if (SecurityUtils.safeExistsSync(normalized) && fs.statSync(normalized).isDirectory()) {
|
|
47
85
|
dirs.push(normalized);
|
|
48
86
|
}
|
|
49
87
|
} catch {
|
|
@@ -54,7 +92,7 @@ function resolveLocalesDirs() {
|
|
|
54
92
|
|
|
55
93
|
const pkgA = pkgUiLocalesDirViaThisFile();
|
|
56
94
|
addDir(pkgA);
|
|
57
|
-
|
|
95
|
+
|
|
58
96
|
const pkgB = pkgUiLocalesDirViaResolve();
|
|
59
97
|
if (pkgB && pkgB !== pkgA) {
|
|
60
98
|
addDir(pkgB);
|
|
@@ -78,28 +116,33 @@ function candidatesForLang(dir, lang) {
|
|
|
78
116
|
|
|
79
117
|
function findLocaleFilesAllDirs(lang) {
|
|
80
118
|
const dirs = resolveLocalesDirs();
|
|
81
|
-
|
|
119
|
+
|
|
82
120
|
if (process.env.I18NTK_DEBUG_LOCALES === '1') {
|
|
83
121
|
console.log('🔎 i18ntk locale search dirs:', dirs);
|
|
84
122
|
}
|
|
85
|
-
|
|
123
|
+
|
|
86
124
|
const files = [];
|
|
87
125
|
const errors = [];
|
|
88
|
-
|
|
126
|
+
|
|
89
127
|
for (const dir of dirs) {
|
|
90
128
|
for (const candidate of candidatesForLang(dir, lang)) {
|
|
91
129
|
try {
|
|
92
|
-
|
|
130
|
+
const SecurityUtils = getSecurityUtils();
|
|
131
|
+
if (SecurityUtils.safeExistsSync(candidate)) {
|
|
93
132
|
const stats = fs.statSync(candidate);
|
|
94
133
|
if (stats.isFile() && stats.size > 0) {
|
|
95
134
|
// Validate file is readable and parseable
|
|
96
135
|
fs.accessSync(candidate, fs.constants.R_OK);
|
|
97
136
|
// Quick JSON validation
|
|
98
|
-
const content =
|
|
99
|
-
if (content
|
|
100
|
-
|
|
137
|
+
const content = SecurityUtils.safeReadFileSync(candidate, path.dirname(candidate), 'utf8');
|
|
138
|
+
if (content) {
|
|
139
|
+
if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
|
|
140
|
+
files.push(candidate);
|
|
141
|
+
} else {
|
|
142
|
+
errors.push({ file: candidate, error: 'Invalid JSON format' });
|
|
143
|
+
}
|
|
101
144
|
} else {
|
|
102
|
-
errors.push({ file: candidate, error: '
|
|
145
|
+
errors.push({ file: candidate, error: 'Empty or unreadable file' });
|
|
103
146
|
}
|
|
104
147
|
}
|
|
105
148
|
}
|
|
@@ -108,11 +151,11 @@ function findLocaleFilesAllDirs(lang) {
|
|
|
108
151
|
}
|
|
109
152
|
}
|
|
110
153
|
}
|
|
111
|
-
|
|
154
|
+
|
|
112
155
|
if (process.env.I18NTK_DEBUG_LOCALES === '1' && errors.length > 0) {
|
|
113
156
|
console.warn(`⚠️ Locale resolution errors for ${lang}:`, errors);
|
|
114
157
|
}
|
|
115
|
-
|
|
158
|
+
|
|
116
159
|
return files;
|
|
117
160
|
}
|
|
118
161
|
|
|
@@ -121,34 +164,34 @@ let currentLanguage = 'en';
|
|
|
121
164
|
let isInitialized = false;
|
|
122
165
|
const missingWarned = new Set();
|
|
123
166
|
|
|
124
|
-
function loadTranslations(language) {
|
|
125
|
-
const cfg = safeRequireConfig();
|
|
126
|
-
const settings = cfg?.getConfig?.() || {};
|
|
127
|
-
const configuredLanguage = settings.uiLanguage || settings.language
|
|
128
|
-
|
|
129
|
-
// Prioritize
|
|
130
|
-
const requested = (
|
|
167
|
+
function loadTranslations(language) {
|
|
168
|
+
const cfg = safeRequireConfig();
|
|
169
|
+
const settings = cfg?.getConfig?.() || {};
|
|
170
|
+
const configuredLanguage = settings.uiLanguage || settings.language;
|
|
171
|
+
|
|
172
|
+
// Prioritize CLI argument, then UI language, then language fallback
|
|
173
|
+
const requested = (language || configuredLanguage || 'en').toString();
|
|
131
174
|
const short = requested.split('-')[0].toLowerCase();
|
|
132
175
|
const tryOrder = [requested, short, 'en'];
|
|
133
176
|
|
|
134
177
|
const loadErrors = [];
|
|
135
|
-
|
|
178
|
+
|
|
136
179
|
for (const lang of tryOrder) {
|
|
137
180
|
const files = findLocaleFilesAllDirs(lang);
|
|
138
|
-
|
|
181
|
+
|
|
139
182
|
// Prioritize bundled locales over project ones
|
|
140
183
|
const prioritizedFiles = files.sort((a, b) => Number(isBundledPath(b)) - Number(isBundledPath(a)));
|
|
141
|
-
|
|
184
|
+
|
|
142
185
|
for (const file of prioritizedFiles) {
|
|
143
186
|
try {
|
|
144
187
|
translations = readJsonSafe(file);
|
|
145
188
|
currentLanguage = lang;
|
|
146
189
|
isInitialized = true;
|
|
147
|
-
|
|
190
|
+
|
|
148
191
|
if (process.env.I18NTK_DEBUG_LOCALES === '1') {
|
|
149
192
|
console.log(`🗂 Loaded UI locale → ${file} (${lang})`);
|
|
150
193
|
}
|
|
151
|
-
|
|
194
|
+
|
|
152
195
|
// Validate translations object
|
|
153
196
|
if (typeof translations === 'object' && translations !== null) {
|
|
154
197
|
return translations;
|
|
@@ -199,11 +242,11 @@ function loadTranslations(language) {
|
|
|
199
242
|
};
|
|
200
243
|
currentLanguage = 'en';
|
|
201
244
|
isInitialized = true;
|
|
202
|
-
|
|
245
|
+
|
|
203
246
|
if (loadErrors.length > 0) {
|
|
204
247
|
console.warn(`⚠️ No valid UI locale files found. Using built-in English strings.`);
|
|
205
248
|
}
|
|
206
|
-
|
|
249
|
+
|
|
207
250
|
return translations;
|
|
208
251
|
}
|
|
209
252
|
|
|
@@ -219,7 +262,7 @@ function t(key, params = {}) {
|
|
|
219
262
|
loadTranslations();
|
|
220
263
|
isInitialized = true;
|
|
221
264
|
}
|
|
222
|
-
|
|
265
|
+
|
|
223
266
|
// Split the key into parts (e.g., 'module.subkey' -> ['module', 'subkey'])
|
|
224
267
|
const keyParts = key.split('.');
|
|
225
268
|
let value = translations;
|
|
@@ -258,12 +301,12 @@ function t(key, params = {}) {
|
|
|
258
301
|
}
|
|
259
302
|
return key;
|
|
260
303
|
}
|
|
261
|
-
|
|
304
|
+
|
|
262
305
|
// If we found a string, interpolate parameters
|
|
263
306
|
if (typeof value === 'string') {
|
|
264
307
|
return interpolateParams(value, params);
|
|
265
308
|
}
|
|
266
|
-
|
|
309
|
+
|
|
267
310
|
// Return the key if the final value is not a string
|
|
268
311
|
console.warn(`Translation key does not resolve to a string: ${key}`);
|
|
269
312
|
return key;
|
|
@@ -309,12 +352,13 @@ function getAvailableLanguages() {
|
|
|
309
352
|
const langs = new Set();
|
|
310
353
|
for (const d of dirs) {
|
|
311
354
|
try {
|
|
312
|
-
|
|
355
|
+
const SecurityUtils = getSecurityUtils();
|
|
356
|
+
if (!SecurityUtils.safeExistsSync(d)) continue;
|
|
313
357
|
for (const f of fs.readdirSync(d)) {
|
|
314
358
|
if (f.endsWith('.json')) langs.add(path.basename(f, '.json'));
|
|
315
359
|
}
|
|
316
360
|
for (const f of fs.readdirSync(d, { withFileTypes: true })) {
|
|
317
|
-
if (f.isDirectory() &&
|
|
361
|
+
if (f.isDirectory() && SecurityUtils.safeExistsSync(path.join(d, f.name, `${f.name}.json`))) {
|
|
318
362
|
langs.add(f.name);
|
|
319
363
|
}
|
|
320
364
|
}
|
|
@@ -347,11 +391,11 @@ function deepMerge(target, source) {
|
|
|
347
391
|
* Refresh language from settings manager
|
|
348
392
|
* This ensures translations stay in sync with settings changes
|
|
349
393
|
*/
|
|
350
|
-
function refreshLanguageFromSettings() {
|
|
351
|
-
const cfg = safeRequireConfig();
|
|
352
|
-
const settings = cfg?.getConfig?.() || {};
|
|
353
|
-
const configuredLanguage = settings.
|
|
354
|
-
|
|
394
|
+
function refreshLanguageFromSettings() {
|
|
395
|
+
const cfg = safeRequireConfig();
|
|
396
|
+
const settings = cfg?.getConfig?.() || {};
|
|
397
|
+
const configuredLanguage = settings.uiLanguage || settings.language || 'en';
|
|
398
|
+
|
|
355
399
|
if (configuredLanguage !== currentLanguage) {
|
|
356
400
|
isInitialized = false;
|
|
357
401
|
loadTranslations(configuredLanguage);
|
|
@@ -373,4 +417,4 @@ module.exports = {
|
|
|
373
417
|
deepMerge,
|
|
374
418
|
refreshTranslations,
|
|
375
419
|
refreshLanguageFromSettings
|
|
376
|
-
};
|
|
420
|
+
};
|
package/utils/init-helper.js
CHANGED
|
@@ -1,81 +1,121 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const configManager = require('./config-
|
|
4
|
-
const
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const configManager = require('./config-manager');
|
|
4
|
+
const SecurityUtils = require('./security');
|
|
5
|
+
|
|
6
|
+
function ensureDirectory(dirPath) {
|
|
7
|
+
if (!dirPath || typeof dirPath !== 'string') return;
|
|
8
|
+
if (!SecurityUtils.safeExistsSync(dirPath)) {
|
|
9
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function readJsonSafe(filePath) {
|
|
14
|
+
try {
|
|
15
|
+
if (!SecurityUtils.safeExistsSync(filePath)) return null;
|
|
16
|
+
const raw = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
17
|
+
if (!raw) return null;
|
|
18
|
+
return JSON.parse(raw);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasSourceLanguageFiles(sourceDir, sourceLanguage) {
|
|
25
|
+
const baseSourceDir = path.resolve(sourceDir);
|
|
26
|
+
const modularLanguageDir = path.join(baseSourceDir, sourceLanguage);
|
|
27
|
+
const singleLanguageFile = path.join(baseSourceDir, `${sourceLanguage}.json`);
|
|
28
|
+
|
|
29
|
+
if (SecurityUtils.safeExistsSync(modularLanguageDir)) {
|
|
30
|
+
try {
|
|
31
|
+
if (fs.statSync(modularLanguageDir).isDirectory()) {
|
|
32
|
+
return fs.readdirSync(modularLanguageDir).some(file => file.endsWith('.json'));
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// Continue with single-file fallback.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return SecurityUtils.safeExistsSync(singleLanguageFile);
|
|
40
|
+
}
|
|
5
41
|
|
|
6
42
|
/**
|
|
7
43
|
* Check if the project is properly initialized
|
|
8
44
|
* @param {Object} options - Options for initialization check
|
|
9
45
|
* @returns {Promise<Object>} Object containing initialization status and config
|
|
10
46
|
*/
|
|
11
|
-
async function checkInitialized(options = {}) {
|
|
12
|
-
const settings = configManager.
|
|
13
|
-
const currentVersion = require('../package.json').version;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
47
|
+
async function checkInitialized(options = {}) {
|
|
48
|
+
const settings = configManager.getConfig ? configManager.getConfig() : {};
|
|
49
|
+
const currentVersion = require('../package.json').version;
|
|
50
|
+
const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config');
|
|
51
|
+
const configDir = path.dirname(projectConfigPath);
|
|
52
|
+
|
|
53
|
+
// Ensure config directory exists
|
|
54
|
+
ensureDirectory(configDir);
|
|
55
|
+
|
|
19
56
|
const defaultConfig = {
|
|
20
57
|
sourceDir: settings.sourceDir || './locales',
|
|
21
58
|
sourceLanguage: settings.sourceLanguage || 'en',
|
|
22
59
|
projectRoot: path.resolve('.'),
|
|
23
60
|
framework: settings.framework || { detected: false, prompt: 'always' },
|
|
24
|
-
configDir: configDir
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
configDir: configDir
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Primary source of truth in v2: project-level .i18ntk-config
|
|
65
|
+
const projectConfig = readJsonSafe(projectConfigPath);
|
|
66
|
+
if (projectConfig?.setup?.completed === true) {
|
|
67
|
+
return {
|
|
68
|
+
initialized: true,
|
|
69
|
+
config: {
|
|
70
|
+
...defaultConfig,
|
|
71
|
+
...projectConfig,
|
|
72
|
+
framework: projectConfig.framework || defaultConfig.framework
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Backward compatibility: legacy initialization marker file.
|
|
78
|
+
const initFilePath = path.join(configDir, 'initialization.json');
|
|
79
|
+
const initStatus = readJsonSafe(initFilePath);
|
|
80
|
+
const isLegacyInitialized = Boolean(initStatus?.initialized) && (
|
|
81
|
+
!initStatus.version ||
|
|
82
|
+
initStatus.version.split('.')[0] === currentVersion.split('.')[0]
|
|
83
|
+
);
|
|
84
|
+
if (isLegacyInitialized) {
|
|
85
|
+
return {
|
|
86
|
+
initialized: true,
|
|
87
|
+
config: {
|
|
88
|
+
...defaultConfig,
|
|
89
|
+
...initStatus,
|
|
90
|
+
framework: initStatus.framework || defaultConfig.framework
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Final fallback: detect existing source language files and mark initialized.
|
|
96
|
+
const sourceDir = options.sourceDir || defaultConfig.sourceDir;
|
|
97
|
+
const sourceLanguage = options.sourceLanguage || defaultConfig.sourceLanguage;
|
|
98
|
+
const hasLanguageFiles = hasSourceLanguageFiles(sourceDir, sourceLanguage);
|
|
99
|
+
|
|
100
|
+
// If language files exist but no init file, create one
|
|
101
|
+
if (hasLanguageFiles) {
|
|
102
|
+
const initData = {
|
|
103
|
+
initialized: true,
|
|
104
|
+
version: currentVersion,
|
|
105
|
+
timestamp: new Date().toISOString(),
|
|
106
|
+
sourceDir,
|
|
107
|
+
sourceLanguage,
|
|
108
|
+
detectedLanguage: defaultConfig.detectedLanguage,
|
|
109
|
+
detectedFramework: defaultConfig.detectedFramework,
|
|
110
|
+
lastUpdated: new Date().toISOString()
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
ensureDirectory(path.dirname(initFilePath));
|
|
114
|
+
SecurityUtils.safeWriteFileSync(initFilePath, JSON.stringify(initData, null, 2), path.dirname(initFilePath), 'utf8');
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
initialized: true,
|
|
118
|
+
config: { ...defaultConfig, ...initData }
|
|
79
119
|
};
|
|
80
120
|
}
|
|
81
121
|
|
|
@@ -90,33 +130,51 @@ async function checkInitialized(options = {}) {
|
|
|
90
130
|
* @param {Object} config - Configuration to save
|
|
91
131
|
* @returns {Promise<void>}
|
|
92
132
|
*/
|
|
93
|
-
async function markAsInitialized(config) {
|
|
94
|
-
const settings = configManager.
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
133
|
+
async function markAsInitialized(config) {
|
|
134
|
+
const settings = configManager.getConfig ? configManager.getConfig() : {};
|
|
135
|
+
const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config');
|
|
136
|
+
const configDir = path.dirname(projectConfigPath);
|
|
137
|
+
const initFilePath = path.join(configDir, 'initialization.json');
|
|
138
|
+
const currentVersion = require('../package.json').version;
|
|
139
|
+
const now = new Date().toISOString();
|
|
140
|
+
const sourceDir = config?.sourceDir || settings.sourceDir || './locales';
|
|
141
|
+
const sourceLanguage = config?.sourceLanguage || settings.sourceLanguage || 'en';
|
|
142
|
+
|
|
143
|
+
const initData = {
|
|
144
|
+
initialized: true,
|
|
145
|
+
version: currentVersion,
|
|
146
|
+
timestamp: now,
|
|
147
|
+
sourceDir,
|
|
148
|
+
sourceLanguage,
|
|
149
|
+
detectedLanguage: config?.detectedLanguage || settings.detectedLanguage,
|
|
150
|
+
detectedFramework: config?.detectedFramework || settings.detectedFramework,
|
|
151
|
+
lastUpdated: now
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
ensureDirectory(path.dirname(initFilePath));
|
|
155
|
+
SecurityUtils.safeWriteFileSync(initFilePath, JSON.stringify(initData, null, 2), path.dirname(initFilePath), 'utf8');
|
|
156
|
+
|
|
157
|
+
const mergedConfig = {
|
|
158
|
+
...settings,
|
|
159
|
+
...(config || {}),
|
|
160
|
+
sourceDir,
|
|
161
|
+
sourceLanguage,
|
|
162
|
+
version: currentVersion,
|
|
163
|
+
setup: {
|
|
164
|
+
...(settings.setup || {}),
|
|
165
|
+
completed: true,
|
|
166
|
+
completedAt: now,
|
|
167
|
+
version: currentVersion,
|
|
168
|
+
setupId: settings.setup?.setupId || `setup_${Date.now()}`
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
if (configManager.saveConfig) {
|
|
173
|
+
await configManager.saveConfig(mergedConfig);
|
|
174
|
+
} else {
|
|
175
|
+
SecurityUtils.safeWriteFileSync(projectConfigPath, JSON.stringify(mergedConfig, null, 2), path.dirname(projectConfigPath), 'utf8');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
120
178
|
|
|
121
179
|
module.exports = {
|
|
122
180
|
checkInitialized,
|