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/main/i18ntk-go.js
CHANGED
|
@@ -16,7 +16,7 @@ const SecurityUtils = require(path.join(__dirname, '../utils/security'));
|
|
|
16
16
|
const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper'));
|
|
17
17
|
const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper'));
|
|
18
18
|
const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
|
|
19
|
-
const { program } = require('commander');
|
|
19
|
+
const { program } = require('../utils/mini-commander');
|
|
20
20
|
|
|
21
21
|
(async () => {
|
|
22
22
|
try {
|
|
@@ -42,14 +42,13 @@ class GoI18nManager {
|
|
|
42
42
|
this.resourceFormats = ['.json', '.toml', '.yaml', '.yml'];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
async detectFramework(sourceDir) {
|
|
46
|
-
const goModPath = path.join(sourceDir, 'go.mod');
|
|
47
|
-
if (
|
|
48
|
-
const content =
|
|
49
|
-
|
|
50
|
-
if (content.includes('go-i18n')) return 'go-i18n';
|
|
51
|
-
if (content
|
|
52
|
-
if (content.includes('golang.org/x/text')) return 'golang-text';
|
|
45
|
+
async detectFramework(sourceDir) {
|
|
46
|
+
const goModPath = path.join(sourceDir, 'go.mod');
|
|
47
|
+
if (SecurityUtils.safeExistsSync(goModPath)) {
|
|
48
|
+
const content = SecurityUtils.safeReadFileSync(goModPath, sourceDir, 'utf8') || '';
|
|
49
|
+
|
|
50
|
+
if (content.includes('go-i18n')) return 'go-i18n-v2';
|
|
51
|
+
if (/x\/text\b/.test(content)) return 'golang-text';
|
|
53
52
|
|
|
54
53
|
return 'standard-go';
|
|
55
54
|
}
|
|
@@ -69,8 +68,8 @@ class GoI18nManager {
|
|
|
69
68
|
|
|
70
69
|
const goFiles = this.findFiles(sourceDir, '.go');
|
|
71
70
|
|
|
72
|
-
for (const file of goFiles) {
|
|
73
|
-
const content =
|
|
71
|
+
for (const file of goFiles) {
|
|
72
|
+
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
|
|
74
73
|
|
|
75
74
|
// Extract Go i18n patterns
|
|
76
75
|
const patterns = [
|
|
@@ -106,7 +105,7 @@ class GoI18nManager {
|
|
|
106
105
|
fs.mkdirSync(langDir, { recursive: true });
|
|
107
106
|
|
|
108
107
|
// Create Go i18n format files
|
|
109
|
-
|
|
108
|
+
SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.toml'), `# Go i18n translations for ${lang}
|
|
110
109
|
[hello]
|
|
111
110
|
other = "Hello, World!"
|
|
112
111
|
|
|
@@ -115,7 +114,7 @@ one = "{{.Count}} item"
|
|
|
115
114
|
other = "{{.Count}} items"
|
|
116
115
|
`);
|
|
117
116
|
|
|
118
|
-
|
|
117
|
+
SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.json'), JSON.stringify({
|
|
119
118
|
hello: "Hello, World!",
|
|
120
119
|
items: {
|
|
121
120
|
one: "{{.Count}} item",
|
|
@@ -161,7 +160,7 @@ other = "{{.Count}} items"
|
|
|
161
160
|
};
|
|
162
161
|
|
|
163
162
|
const reportPath = path.join(outputDir, 'i18ntk-go-report.json');
|
|
164
|
-
|
|
163
|
+
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
165
164
|
|
|
166
165
|
return reportPath;
|
|
167
166
|
}
|
|
@@ -281,4 +280,4 @@ module.exports = { GoI18nManager };
|
|
|
281
280
|
|
|
282
281
|
if (require.main === module) {
|
|
283
282
|
program.parse();
|
|
284
|
-
}
|
|
283
|
+
}
|
package/main/i18ntk-init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* I18NTK INITIALIZATION SCRIPT
|
|
4
4
|
*
|
|
@@ -22,7 +22,7 @@ const { detectFramework } = require('../utils/framework-detector');
|
|
|
22
22
|
const { getFormatAdapter } = require('../utils/format-manager');
|
|
23
23
|
// Ensure UIi18n is available for this initializer class
|
|
24
24
|
const UIi18n = require('./i18ntk-ui');
|
|
25
|
-
loadTranslations(
|
|
25
|
+
loadTranslations();
|
|
26
26
|
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
|
|
27
27
|
const { showFrameworkWarningOnce } = require('../utils/cli-helper');
|
|
28
28
|
const { createPrompt, isInteractive } = require('../utils/prompt-helper');
|
|
@@ -89,13 +89,13 @@ class I18nInitializer {
|
|
|
89
89
|
async checkI18nDependencies(noPrompt = false) {
|
|
90
90
|
const packageJsonPath = path.resolve('./package.json');
|
|
91
91
|
|
|
92
|
-
if (!
|
|
92
|
+
if (!SecurityUtils.safeExistsSync(packageJsonPath)) {
|
|
93
93
|
console.log(t('errors.noPackageJson'));
|
|
94
94
|
return true; // Allow to continue without framework
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
try {
|
|
98
|
-
const packageJson = JSON.parse(
|
|
98
|
+
const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
|
|
99
99
|
// Include peerDependencies in the check
|
|
100
100
|
const dependencies = {
|
|
101
101
|
...packageJson.dependencies,
|
|
@@ -221,7 +221,7 @@ class I18nInitializer {
|
|
|
221
221
|
|
|
222
222
|
// Check for existing translation directories
|
|
223
223
|
for (const location of possibleLocations) {
|
|
224
|
-
if (
|
|
224
|
+
if (SecurityUtils.safeExistsSync(location)) {
|
|
225
225
|
try {
|
|
226
226
|
const items = fs.readdirSync(location);
|
|
227
227
|
const englishFormats = ['en', 'en-US', 'en-GB', 'english'];
|
|
@@ -229,7 +229,7 @@ class I18nInitializer {
|
|
|
229
229
|
// Check for English directories first
|
|
230
230
|
for (const format of englishFormats) {
|
|
231
231
|
const englishPath = path.join(location, format);
|
|
232
|
-
if (
|
|
232
|
+
if (SecurityUtils.safeExistsSync(englishPath) && fs.statSync(englishPath).isDirectory()) {
|
|
233
233
|
const englishFiles = fs.readdirSync(englishPath).filter(file => file.endsWith(this.format.extension));
|
|
234
234
|
if (englishFiles.length > 0) {
|
|
235
235
|
// Found English files, prioritize this
|
|
@@ -324,7 +324,7 @@ class I18nInitializer {
|
|
|
324
324
|
if (newDirName && newDirName.trim()) {
|
|
325
325
|
const newDirPath = path.resolve(newDirName.trim());
|
|
326
326
|
|
|
327
|
-
if (!
|
|
327
|
+
if (!SecurityUtils.safeExistsSync(newDirPath)) {
|
|
328
328
|
fs.mkdirSync(newDirPath, { recursive: true });
|
|
329
329
|
console.log(t('init.createdNewDirectory', { dir: newDirPath }));
|
|
330
330
|
} else {
|
|
@@ -332,7 +332,7 @@ class I18nInitializer {
|
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
const sourceLangDir = path.join(newDirPath, this.config.sourceLanguage);
|
|
335
|
-
if (!
|
|
335
|
+
if (!SecurityUtils.safeExistsSync(sourceLangDir)) {
|
|
336
336
|
fs.mkdirSync(sourceLangDir, { recursive: true });
|
|
337
337
|
console.log(t('init.createdSourceLanguageDirectory', { dir: sourceLangDir }));
|
|
338
338
|
await this.createSampleTranslationFile(sourceLangDir);
|
|
@@ -387,10 +387,10 @@ class I18nInitializer {
|
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// Create directories if they do not exist
|
|
390
|
-
if (!
|
|
390
|
+
if (!SecurityUtils.safeExistsSync(validatedSourceDir)) {
|
|
391
391
|
fs.mkdirSync(validatedSourceDir, { recursive: true });
|
|
392
392
|
}
|
|
393
|
-
if (this.config.structure !== 'single' && !
|
|
393
|
+
if (this.config.structure !== 'single' && !SecurityUtils.safeExistsSync(validatedSourceLanguageDir)) {
|
|
394
394
|
fs.mkdirSync(validatedSourceLanguageDir, { recursive: true });
|
|
395
395
|
}
|
|
396
396
|
|
|
@@ -445,7 +445,7 @@ class I18nInitializer {
|
|
|
445
445
|
const i18ntkCommonFilePath = path.join(validatedSourceLanguageDir, `i18ntk-common${this.format.extension}`);
|
|
446
446
|
|
|
447
447
|
let sampleFilePath;
|
|
448
|
-
if (!
|
|
448
|
+
if (!SecurityUtils.safeExistsSync(commonFilePath)) {
|
|
449
449
|
sampleFilePath = commonFilePath;
|
|
450
450
|
} else {
|
|
451
451
|
sampleFilePath = i18ntkCommonFilePath;
|
|
@@ -471,36 +471,80 @@ class I18nInitializer {
|
|
|
471
471
|
|
|
472
472
|
// Check if source directory and language exist
|
|
473
473
|
validateSource() {
|
|
474
|
-
if (!
|
|
474
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
|
|
475
475
|
throw new Error(t('validate.sourceLanguageDirectoryNotFound', { sourceDir: this.sourceDir }) || `Source directory not found: ${this.sourceDir}`);
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
if (!
|
|
478
|
+
if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir)) {
|
|
479
479
|
throw new Error(t('validate.sourceLanguageDirectoryNotFound', { sourceDir: this.sourceLanguageDir }) || `Source language directory not found: ${this.sourceLanguageDir}`);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
return true;
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
createBootstrapSourceFile(targetDir) {
|
|
486
|
+
try {
|
|
487
|
+
if (!SecurityUtils.safeExistsSync(targetDir)) {
|
|
488
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const sampleName = `common${this.format.extension}`;
|
|
492
|
+
const samplePath = path.join(targetDir, sampleName);
|
|
493
|
+
|
|
494
|
+
if (SecurityUtils.safeExistsSync(samplePath)) {
|
|
495
|
+
return sampleName;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const sampleContent = {
|
|
499
|
+
app: {
|
|
500
|
+
title: 'Application',
|
|
501
|
+
description: 'Application description'
|
|
502
|
+
},
|
|
503
|
+
common: {
|
|
504
|
+
yes: 'Yes',
|
|
505
|
+
no: 'No',
|
|
506
|
+
save: 'Save',
|
|
507
|
+
cancel: 'Cancel'
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const serializer = typeof this.format?.write === 'function'
|
|
512
|
+
? this.format.write.bind(this.format)
|
|
513
|
+
: (typeof this.format?.serialize === 'function'
|
|
514
|
+
? this.format.serialize.bind(this.format)
|
|
515
|
+
: (data) => JSON.stringify(data, null, 2));
|
|
516
|
+
|
|
517
|
+
const serialized = serializer(sampleContent);
|
|
518
|
+
SecurityUtils.safeWriteFileSync(samplePath, `${serialized}\n`, path.dirname(samplePath), 'utf8');
|
|
519
|
+
console.log(t('init.createdSampleTranslationFile', { file: samplePath }) || `Created sample translation file: ${samplePath}`);
|
|
520
|
+
|
|
521
|
+
return sampleName;
|
|
522
|
+
} catch {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
485
527
|
// Get all JSON files from source language directory (supports single/modular)
|
|
486
528
|
getSourceFiles() {
|
|
487
529
|
try {
|
|
488
530
|
if (this.config.structure === 'single') {
|
|
489
|
-
if (!
|
|
531
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
|
|
490
532
|
throw new Error(t('validate.sourceLanguageDirectoryNotFound', { sourceDir: this.sourceDir }) || `Source directory not found: ${this.sourceDir}`);
|
|
491
533
|
}
|
|
492
534
|
const files = fs.readdirSync(this.sourceDir)
|
|
493
535
|
.filter(file => file.endsWith(this.format.extension) && !this.config.excludeFiles.includes(file));
|
|
494
536
|
if (files.length === 0) {
|
|
537
|
+
const sampleFile = this.createBootstrapSourceFile(this.sourceDir);
|
|
538
|
+
if (sampleFile) return [sampleFile];
|
|
495
539
|
throw new Error(t('validate.noJsonFilesFound', { sourceDir: this.sourceDir }) || `No JSON files found in source directory: ${this.sourceDir}`);
|
|
496
540
|
}
|
|
497
541
|
return files;
|
|
498
542
|
}
|
|
499
543
|
|
|
500
|
-
if (!
|
|
544
|
+
if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir)) {
|
|
501
545
|
// Try to find English files in parent directory or subdirectories
|
|
502
546
|
const parentDir = path.dirname(this.sourceLanguageDir);
|
|
503
|
-
if (
|
|
547
|
+
if (SecurityUtils.safeExistsSync(parentDir)) {
|
|
504
548
|
const subdirs = fs.readdirSync(parentDir).filter(item => {
|
|
505
549
|
const fullPath = path.join(parentDir, item);
|
|
506
550
|
return fs.statSync(fullPath).isDirectory();
|
|
@@ -509,7 +553,7 @@ class I18nInitializer {
|
|
|
509
553
|
// Look for English files in any subdirectory
|
|
510
554
|
for (const subdir of subdirs) {
|
|
511
555
|
const englishDir = path.join(parentDir, subdir);
|
|
512
|
-
if (
|
|
556
|
+
if (SecurityUtils.safeExistsSync(englishDir)) {
|
|
513
557
|
const files = fs.readdirSync(englishDir);
|
|
514
558
|
const formatFiles = files.filter(file =>
|
|
515
559
|
file.endsWith(this.format.extension) &&
|
|
@@ -533,6 +577,8 @@ class I18nInitializer {
|
|
|
533
577
|
});
|
|
534
578
|
|
|
535
579
|
if (files.length === 0) {
|
|
580
|
+
const sampleFile = this.createBootstrapSourceFile(this.sourceLanguageDir);
|
|
581
|
+
if (sampleFile) return [sampleFile];
|
|
536
582
|
throw new Error(t('validate.noJsonFilesFound', { sourceDir: this.sourceLanguageDir }) || `No JSON files found in source directory: ${this.sourceLanguageDir}`);
|
|
537
583
|
}
|
|
538
584
|
|
|
@@ -586,7 +632,7 @@ class I18nInitializer {
|
|
|
586
632
|
} else {
|
|
587
633
|
// Modular: folder per language mirroring source file
|
|
588
634
|
const targetDir = path.join(this.sourceDir, targetLanguage);
|
|
589
|
-
if (!
|
|
635
|
+
if (!SecurityUtils.safeExistsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
590
636
|
targetFilePath = path.join(targetDir, sourceFile);
|
|
591
637
|
}
|
|
592
638
|
|
|
@@ -604,14 +650,14 @@ class I18nInitializer {
|
|
|
604
650
|
}
|
|
605
651
|
|
|
606
652
|
// Create target directory if it doesn't exist
|
|
607
|
-
if (!
|
|
653
|
+
if (!SecurityUtils.safeExistsSync(targetDir)) {
|
|
608
654
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
609
655
|
}
|
|
610
656
|
|
|
611
657
|
let targetContent;
|
|
612
658
|
|
|
613
659
|
// If target file exists, preserve existing translations
|
|
614
|
-
if (
|
|
660
|
+
if (SecurityUtils.safeExistsSync(validatedTargetPath)) {
|
|
615
661
|
try {
|
|
616
662
|
const existingContent = await SecurityUtils.safeReadFile(validatedTargetPath, process.cwd());
|
|
617
663
|
if (existingContent) {
|
|
@@ -844,6 +890,9 @@ class I18nInitializer {
|
|
|
844
890
|
async initialize(hasI18n = true, args = {}) {
|
|
845
891
|
console.log(t('init.initializingProject'));
|
|
846
892
|
|
|
893
|
+
// Preserve noPrompt setting in config
|
|
894
|
+
this.config.noPrompt = args.noPrompt || this.config.noPrompt;
|
|
895
|
+
|
|
847
896
|
if (!hasI18n) {
|
|
848
897
|
console.log(t('init.warningProceedingWithoutFramework'));
|
|
849
898
|
console.log(t('init.translationFilesCreatedWarning'));
|
|
@@ -1040,7 +1089,7 @@ class I18nInitializer {
|
|
|
1040
1089
|
async generateDetailedReport(results, targetLanguages) {
|
|
1041
1090
|
try {
|
|
1042
1091
|
const outputDir = this.config.outputDir || path.join(process.cwd(), 'i18ntk-reports');
|
|
1043
|
-
if (!
|
|
1092
|
+
if (!SecurityUtils.safeExistsSync(outputDir)) {
|
|
1044
1093
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
1045
1094
|
}
|
|
1046
1095
|
|
|
@@ -1107,11 +1156,12 @@ class I18nInitializer {
|
|
|
1107
1156
|
const fromMenu = options.fromMenu || false;
|
|
1108
1157
|
|
|
1109
1158
|
try {
|
|
1110
|
-
// Parse command line arguments
|
|
1111
|
-
const
|
|
1159
|
+
// Parse command line arguments and merge with options
|
|
1160
|
+
const cliArgs = this.parseArgs();
|
|
1161
|
+
const args = { ...cliArgs, ...options }; // options override cliArgs
|
|
1112
1162
|
|
|
1113
1163
|
// On first run, prompt user for preferred UI language
|
|
1114
|
-
if (!
|
|
1164
|
+
if (!SecurityUtils.safeExistsSync(configManager.CONFIG_PATH)) {
|
|
1115
1165
|
const { getGlobalReadline } = require('../utils/cli');
|
|
1116
1166
|
getGlobalReadline();
|
|
1117
1167
|
const selectedLang = await this.ui.selectLanguage();
|
|
@@ -1128,7 +1178,7 @@ class I18nInitializer {
|
|
|
1128
1178
|
// Load translations for UI messages
|
|
1129
1179
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
1130
1180
|
const { loadTranslations } = require('../utils/i18n-helper');
|
|
1131
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
1181
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
1132
1182
|
}
|
|
1133
1183
|
|
|
1134
1184
|
// Setup is now handled centrally by config manager
|
|
@@ -1211,7 +1261,7 @@ class I18nInitializer {
|
|
|
1211
1261
|
// Load translations for UI messages
|
|
1212
1262
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
1213
1263
|
const { loadTranslations } = require('../utils/i18n-helper');
|
|
1214
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
1264
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
1215
1265
|
|
|
1216
1266
|
// Skip i18n framework check in non-interactive mode
|
|
1217
1267
|
console.log('Running initialization in non-interactive mode...');
|
|
@@ -1230,6 +1280,7 @@ class I18nInitializer {
|
|
|
1230
1280
|
|
|
1231
1281
|
module.exports = I18nInitializer;
|
|
1232
1282
|
|
|
1283
|
+
|
|
1233
1284
|
// Run if called directly
|
|
1234
1285
|
if (require.main === module) {
|
|
1235
1286
|
async function main() {
|
package/main/i18ntk-java.js
CHANGED
|
@@ -19,7 +19,7 @@ const SecurityUtils = require(path.join(__dirname, '../utils/security'));
|
|
|
19
19
|
const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper'));
|
|
20
20
|
const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper'));
|
|
21
21
|
const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
|
|
22
|
-
const { program } = require('commander');
|
|
22
|
+
const { program } = require('../utils/mini-commander');
|
|
23
23
|
|
|
24
24
|
(async () => {
|
|
25
25
|
try {
|
|
@@ -52,7 +52,7 @@ class JavaI18nManager {
|
|
|
52
52
|
async detectFramework(sourceDir) {
|
|
53
53
|
// Check for Android
|
|
54
54
|
const androidManifest = path.join(sourceDir, 'AndroidManifest.xml');
|
|
55
|
-
if (
|
|
55
|
+
if (SecurityUtils.safeExistsSync(androidManifest)) {
|
|
56
56
|
return 'android';
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -60,15 +60,15 @@ class JavaI18nManager {
|
|
|
60
60
|
const pomXml = path.join(sourceDir, 'pom.xml');
|
|
61
61
|
const gradleFile = path.join(sourceDir, 'build.gradle');
|
|
62
62
|
|
|
63
|
-
if (
|
|
64
|
-
const content =
|
|
63
|
+
if (SecurityUtils.safeExistsSync(pomXml)) {
|
|
64
|
+
const content = SecurityUtils.safeReadFileSync(pomXml, path.dirname(pomXml), 'utf8');
|
|
65
65
|
if (content.includes('spring-boot')) {
|
|
66
66
|
return 'spring-boot';
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
if (
|
|
71
|
-
const content =
|
|
70
|
+
if (SecurityUtils.safeExistsSync(gradleFile)) {
|
|
71
|
+
const content = SecurityUtils.safeReadFileSync(gradleFile, path.dirname(gradleFile), 'utf8');
|
|
72
72
|
if (content.includes('spring-boot')) {
|
|
73
73
|
return 'spring-boot';
|
|
74
74
|
}
|
|
@@ -97,7 +97,7 @@ class JavaI18nManager {
|
|
|
97
97
|
const javaFiles = [...this.findFiles(sourceDir, '.java'), ...this.findFiles(sourceDir, '.kt')];
|
|
98
98
|
|
|
99
99
|
for (const file of javaFiles) {
|
|
100
|
-
const content =
|
|
100
|
+
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
101
101
|
|
|
102
102
|
// Extract Android string references
|
|
103
103
|
const androidPatterns = [
|
|
@@ -131,7 +131,7 @@ class JavaI18nManager {
|
|
|
131
131
|
const xmlFiles = this.findFiles(sourceDir, '.xml');
|
|
132
132
|
for (const file of xmlFiles) {
|
|
133
133
|
if (file.includes('strings.xml')) {
|
|
134
|
-
const content =
|
|
134
|
+
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
|
|
135
135
|
const stringPatterns = [
|
|
136
136
|
/<string name="([^"]+)"/g,
|
|
137
137
|
/<string-array name="([^"]+)"/g,
|
|
@@ -151,7 +151,7 @@ class JavaI18nManager {
|
|
|
151
151
|
const propertiesFiles = this.findFiles(sourceDir, '.properties');
|
|
152
152
|
for (const file of propertiesFiles) {
|
|
153
153
|
if (file.includes('messages') || file.includes('i18n')) {
|
|
154
|
-
const content =
|
|
154
|
+
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
|
|
155
155
|
const lines = content.split('\n');
|
|
156
156
|
|
|
157
157
|
for (const line of lines) {
|
|
@@ -181,28 +181,23 @@ class JavaI18nManager {
|
|
|
181
181
|
|
|
182
182
|
if (framework === 'android') {
|
|
183
183
|
// Android string resources
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
<string name="
|
|
189
|
-
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
<item quantity="
|
|
193
|
-
|
|
194
|
-
</
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
fs.writeFileSync(path.join(langDir, 'messages.properties'), `# Java i18n properties for ${lang}
|
|
199
|
-
app.name=My Application
|
|
200
|
-
hello.message=Hello, World!
|
|
201
|
-
items.count={0} items
|
|
202
|
-
`);
|
|
184
|
+
SecurityUtils.safeWriteFileSync(
|
|
185
|
+
path.join(langDir, 'strings.xml'),
|
|
186
|
+
`<?xml version="1.0" encoding="utf-8"?>
|
|
187
|
+
<resources>
|
|
188
|
+
<string name="app_name">My App</string>
|
|
189
|
+
<string name="hello">Hello, World!</string>
|
|
190
|
+
<string name="items_count">%d items</string>
|
|
191
|
+
<plurals name="items">
|
|
192
|
+
<item quantity="one">%d item</item>
|
|
193
|
+
<item quantity="other">%d items</item>
|
|
194
|
+
</plurals>
|
|
195
|
+
</resources>`,
|
|
196
|
+
path.dirname(path.join(langDir, 'strings.xml'))
|
|
197
|
+
);
|
|
203
198
|
|
|
204
199
|
// JSON format
|
|
205
|
-
|
|
200
|
+
SecurityUtils.safeWriteFileSync(path.join(langDir, 'messages.json'), JSON.stringify({
|
|
206
201
|
app: { name: "My Application" },
|
|
207
202
|
hello: { message: "Hello, World!" },
|
|
208
203
|
items: { count: "{0} items" }
|
|
@@ -217,7 +212,7 @@ items.count={0} items
|
|
|
217
212
|
const files = [];
|
|
218
213
|
|
|
219
214
|
function traverse(currentDir) {
|
|
220
|
-
if (!
|
|
215
|
+
if (!SecurityUtils.safeExistsSync(currentDir)) return;
|
|
221
216
|
|
|
222
217
|
const items = fs.readdirSync(currentDir);
|
|
223
218
|
|
|
@@ -254,7 +249,7 @@ items.count={0} items
|
|
|
254
249
|
};
|
|
255
250
|
|
|
256
251
|
const reportPath = path.join(outputDir, 'i18ntk-java-report.json');
|
|
257
|
-
|
|
252
|
+
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
258
253
|
|
|
259
254
|
return reportPath;
|
|
260
255
|
}
|
|
@@ -382,4 +377,4 @@ module.exports = { JavaI18nManager };
|
|
|
382
377
|
|
|
383
378
|
if (require.main === module) {
|
|
384
379
|
program.parse();
|
|
385
|
-
}
|
|
380
|
+
}
|