i18ntk 4.0.0 → 4.2.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/CHANGELOG.md +116 -29
- package/README.md +83 -18
- package/SECURITY.md +13 -5
- package/main/i18ntk-analyze.js +10 -20
- package/main/i18ntk-backup.js +227 -111
- package/main/i18ntk-init.js +153 -157
- package/main/i18ntk-scanner.js +9 -7
- package/main/i18ntk-setup.js +36 -13
- package/main/i18ntk-sizing.js +18 -50
- package/main/i18ntk-translate.js +169 -21
- package/main/i18ntk-usage.js +298 -154
- package/main/i18ntk-validate.js +49 -37
- package/main/manage/commands/AnalyzeCommand.js +7 -17
- package/main/manage/commands/CommandRouter.js +6 -6
- package/main/manage/commands/TranslateCommand.js +65 -56
- package/main/manage/commands/ValidateCommand.js +34 -26
- package/main/manage/index.js +11 -42
- package/main/manage/managers/InteractiveMenu.js +11 -40
- package/main/manage/services/InitService.js +114 -118
- package/main/manage/services/UsageService.js +244 -85
- package/package.json +55 -4
- package/runtime/enhanced.d.ts +5 -5
- package/runtime/enhanced.js +49 -25
- package/runtime/i18ntk.d.ts +30 -7
- package/runtime/index.d.ts +48 -19
- package/runtime/index.js +188 -97
- package/settings/settings-cli.js +115 -38
- package/settings/settings-manager.js +24 -6
- package/ui-locales/de.json +192 -11
- package/ui-locales/en.json +182 -8
- package/ui-locales/es.json +193 -12
- package/ui-locales/fr.json +189 -8
- package/ui-locales/ja.json +190 -8
- package/ui-locales/ru.json +191 -9
- package/ui-locales/zh.json +194 -9
- package/utils/cli-helper.js +8 -12
- package/utils/config-helper.js +1 -1
- package/utils/config-manager.js +8 -6
- package/utils/localized-confirm.js +55 -0
- package/utils/menu-layout.js +41 -0
- package/utils/report-writer.js +110 -0
- package/utils/security.js +15 -22
- package/utils/translate/api.js +31 -3
- package/utils/translate/placeholder.js +42 -1
- package/utils/translate/protection.js +17 -12
- package/utils/translate/report.js +3 -2
- package/utils/translate/safe-network.js +24 -4
- package/utils/usage-insights.js +435 -0
- package/utils/usage-source.js +50 -0
- package/utils/watch-locales.js +13 -9
package/main/i18ntk-init.js
CHANGED
|
@@ -24,30 +24,33 @@ const { getFormatAdapter } = require('../utils/format-manager');
|
|
|
24
24
|
const UIi18n = require('./i18ntk-ui');
|
|
25
25
|
loadTranslations();
|
|
26
26
|
const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
|
|
27
|
-
const { showFrameworkWarningOnce } = require('../utils/cli-helper');
|
|
28
|
-
const { createPrompt, isInteractive } = require('../utils/prompt-helper');
|
|
27
|
+
const { showFrameworkWarningOnce } = require('../utils/cli-helper');
|
|
28
|
+
const { createPrompt, isInteractive } = require('../utils/prompt-helper');
|
|
29
|
+
const { parseConfirmation } = require('../utils/localized-confirm');
|
|
30
|
+
const { normalizeReportFormat, writeReportFile } = require('../utils/report-writer');
|
|
29
31
|
|
|
30
32
|
// Language configurations with native names
|
|
31
|
-
const LANGUAGE_CONFIG = {
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
33
|
+
const LANGUAGE_CONFIG = {
|
|
34
|
+
'en': { name: 'English', nativeName: 'English' },
|
|
35
|
+
'de': { name: 'German', nativeName: 'Deutsch' },
|
|
36
|
+
'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
|
|
37
|
+
'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
|
|
38
|
+
'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
|
|
39
|
+
'it': { name: 'Italian', nativeName: 'Italiano' },
|
|
40
|
+
'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
|
|
41
|
+
'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
|
|
42
|
+
'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
|
|
43
|
+
'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' },
|
|
44
|
+
'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
|
|
45
|
+
'nl': { name: 'Dutch', nativeName: 'Nederlands' },
|
|
46
|
+
'sv': { name: 'Swedish', nativeName: 'Svenska' },
|
|
47
|
+
'da': { name: 'Danish', nativeName: 'Dansk' },
|
|
48
|
+
'no': { name: 'Norwegian', nativeName: 'Norsk' },
|
|
49
|
+
'fi': { name: 'Finnish', nativeName: 'Suomi' },
|
|
50
|
+
'pl': { name: 'Polish', nativeName: 'Polski' },
|
|
51
|
+
'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
|
|
52
|
+
'hu': { name: 'Hungarian', nativeName: 'Magyar' },
|
|
53
|
+
'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' }
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
class I18nInitializer {
|
|
@@ -76,7 +79,7 @@ class I18nInitializer {
|
|
|
76
79
|
: path.join(this.sourceDir, this.config.sourceLanguage);
|
|
77
80
|
|
|
78
81
|
// Ensure defaultLanguages is properly initialized from config
|
|
79
|
-
this.config.defaultLanguages = this.config.defaultLanguages || ['de', 'es', 'fr', 'ru'];
|
|
82
|
+
this.config.defaultLanguages = this.config.defaultLanguages || ['en', 'de', 'es', 'fr', 'ru'];
|
|
80
83
|
|
|
81
84
|
// No longer create readline interface here - use CLI helpers
|
|
82
85
|
this.rl = null;
|
|
@@ -153,7 +156,7 @@ class I18nInitializer {
|
|
|
153
156
|
return true;
|
|
154
157
|
}
|
|
155
158
|
const answer = await this.prompt('\n' + t('init.continueWithoutI18nPrompt'));
|
|
156
|
-
return answer
|
|
159
|
+
return parseConfirmation(answer, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false });
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
// Add the missing prompt method
|
|
@@ -767,7 +770,7 @@ class I18nInitializer {
|
|
|
767
770
|
await flushStdout();
|
|
768
771
|
const enableProtection = await ask('\n' + t('adminPin.setup_prompt'));
|
|
769
772
|
|
|
770
|
-
if (enableProtection
|
|
773
|
+
if (parseConfirmation(enableProtection, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false })) {
|
|
771
774
|
try {
|
|
772
775
|
const adminAuth = new AdminAuth();
|
|
773
776
|
await adminAuth.initialize();
|
|
@@ -816,16 +819,16 @@ class I18nInitializer {
|
|
|
816
819
|
}
|
|
817
820
|
|
|
818
821
|
const { ask } = require('../utils/cli');
|
|
819
|
-
console.log('\
|
|
820
|
-
console.log('
|
|
821
|
-
const enableAnswer = await ask(
|
|
822
|
-
const enabled =
|
|
822
|
+
console.log('\n' + t('init.backup.title'));
|
|
823
|
+
console.log(t('init.backup.description'));
|
|
824
|
+
const enableAnswer = await ask(t('init.backup.enablePrompt'));
|
|
825
|
+
const enabled = parseConfirmation(enableAnswer, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false });
|
|
823
826
|
|
|
824
827
|
if (!enabled) {
|
|
825
828
|
return defaultBackupConfig;
|
|
826
829
|
}
|
|
827
830
|
|
|
828
|
-
const keepAnswer = await ask(
|
|
831
|
+
const keepAnswer = await ask(t('init.backup.keepPrompt'));
|
|
829
832
|
const parsedKeep = parseInt(String(keepAnswer || '').trim(), 10);
|
|
830
833
|
const maxBackups = Number.isInteger(parsedKeep) ? Math.min(Math.max(parsedKeep, 1), 3) : 1;
|
|
831
834
|
|
|
@@ -895,7 +898,7 @@ class I18nInitializer {
|
|
|
895
898
|
let perLanguage = [];
|
|
896
899
|
if (structure !== 'existing') {
|
|
897
900
|
const duplicateChoice = await ask('\n' + t('init.setup.apply_all_prompt'));
|
|
898
|
-
duplicateStructure = duplicateChoice
|
|
901
|
+
duplicateStructure = parseConfirmation(duplicateChoice, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: true });
|
|
899
902
|
if (!duplicateStructure) {
|
|
900
903
|
// Prompt for languages to include/exclude
|
|
901
904
|
console.log(t('init.setup.per_language_intro'));
|
|
@@ -1054,138 +1057,131 @@ class I18nInitializer {
|
|
|
1054
1057
|
console.log(t('init.nextStep3'));
|
|
1055
1058
|
}
|
|
1056
1059
|
|
|
1057
|
-
// Generate completion summary with proper error handling
|
|
1058
|
-
async generateCompletionSummary(results, targetLanguages) {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
console.log(t('init.initializationSummaryTitle'));
|
|
1062
|
-
console.log(t('common.separator'));
|
|
1063
|
-
|
|
1064
|
-
let totalChanges = 0;
|
|
1065
|
-
let languagesProcessed = 0;
|
|
1066
|
-
let missingKeysAdded = 0;
|
|
1067
|
-
|
|
1068
|
-
Object.entries(results || {}).forEach(([lang, data]) => {
|
|
1069
|
-
if (!data || typeof data !== 'object') return;
|
|
1070
|
-
|
|
1071
|
-
const langName = LANGUAGE_CONFIG[lang]?.name || 'Unknown';
|
|
1072
|
-
const stats = data.totalStats || { total: 0, translated: 0, percentage: 0, missing: 0 };
|
|
1073
|
-
|
|
1074
|
-
const statusIcon = stats.percentage === 100 ? '✅' : stats.percentage >= 80 ? '🟡' : '🔴';
|
|
1075
|
-
|
|
1076
|
-
console.log(
|
|
1077
|
-
t('init.languageSummary', {
|
|
1078
|
-
icon: statusIcon,
|
|
1079
|
-
name: langName,
|
|
1080
|
-
code: lang,
|
|
1081
|
-
percentage: stats.percentage || 0,
|
|
1082
|
-
})
|
|
1083
|
-
);
|
|
1084
|
-
|
|
1085
|
-
if (data.files && Array.isArray(data.files)) {
|
|
1086
|
-
console.log(t('init.languageFiles', { count: data.files.length }));
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
console.log(
|
|
1090
|
-
t('init.languageKeys', {
|
|
1091
|
-
translated: stats.translated || 0,
|
|
1092
|
-
total: stats.total || 0,
|
|
1093
|
-
})
|
|
1094
|
-
);
|
|
1095
|
-
|
|
1096
|
-
console.log(t('init.languageMissing', { count: stats.missing || 0 }));
|
|
1097
|
-
|
|
1098
|
-
totalChanges += (stats.translated || 0) + (stats.missing || 0);
|
|
1099
|
-
languagesProcessed += 1;
|
|
1100
|
-
missingKeysAdded += stats.missing || 0;
|
|
1101
|
-
});
|
|
1102
|
-
|
|
1103
|
-
console.log('\n📊 COMPLETION SUMMARY');
|
|
1104
|
-
console.log(t('common.separator'));
|
|
1105
|
-
console.log(`📝 Total changes: ${totalChanges}`);
|
|
1106
|
-
console.log(`🌍 Languages processed: ${languagesProcessed}`);
|
|
1107
|
-
console.log(`➕ Missing keys added: ${missingKeysAdded}`);
|
|
1108
|
-
|
|
1109
|
-
if (process.stdin.isTTY && !this.config?.noPrompt) {
|
|
1110
|
-
const { ask } = require('../utils/cli');
|
|
1111
|
-
const generateReport = await ask('\n🤖 Would you like a report generated? (Y/N): ');
|
|
1112
|
-
if (generateReport.toLowerCase() === 'y' || generateReport.toLowerCase() === 'yes') {
|
|
1113
|
-
await this.generateDetailedReport(results, targetLanguages);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
} catch (error) {
|
|
1117
|
-
console.error('\n❌ Error during completion:', error.message);
|
|
1118
|
-
console.log('📊 COMPLETION SUMMARY (Basic)');
|
|
1119
|
-
console.log(t('common.separator'));
|
|
1120
|
-
console.log(`🌍 Languages processed: ${Object.keys(results || {}).length}`);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1060
|
+
// Generate completion summary with proper error handling
|
|
1061
|
+
async generateCompletionSummary(results, targetLanguages) {
|
|
1062
|
+
return await this.generateLocalizedCompletionSummary(results, targetLanguages);
|
|
1063
|
+
}
|
|
1123
1064
|
|
|
1124
|
-
|
|
1065
|
+
async generateLocalizedCompletionSummary(results, targetLanguages) {
|
|
1066
|
+
try {
|
|
1067
|
+
console.log('\n' + '='.repeat(50));
|
|
1068
|
+
console.log(t('init.initializationSummaryTitle'));
|
|
1069
|
+
console.log(t('common.separator'));
|
|
1070
|
+
|
|
1071
|
+
let totalChanges = 0;
|
|
1072
|
+
let languagesProcessed = 0;
|
|
1073
|
+
let missingKeysAdded = 0;
|
|
1074
|
+
|
|
1075
|
+
Object.entries(results || {}).forEach(([lang, data]) => {
|
|
1076
|
+
if (!data || typeof data !== 'object') return;
|
|
1077
|
+
|
|
1078
|
+
const langName = LANGUAGE_CONFIG[lang]?.name || 'Unknown';
|
|
1079
|
+
const stats = data.totalStats || { total: 0, translated: 0, percentage: 0, missing: 0 };
|
|
1080
|
+
const statusIcon = stats.percentage === 100 ? '✅' : stats.percentage >= 80 ? '🟡' : '🔴';
|
|
1081
|
+
|
|
1082
|
+
console.log(t('init.languageSummary', {
|
|
1083
|
+
icon: statusIcon,
|
|
1084
|
+
name: langName,
|
|
1085
|
+
code: lang,
|
|
1086
|
+
percentage: stats.percentage || 0,
|
|
1087
|
+
}));
|
|
1088
|
+
|
|
1089
|
+
if (Array.isArray(data.files)) {
|
|
1090
|
+
console.log(t('init.languageFiles', { count: data.files.length }));
|
|
1091
|
+
}
|
|
1092
|
+
console.log(t('init.languageKeys', { translated: stats.translated || 0, total: stats.total || 0 }));
|
|
1093
|
+
console.log(t('init.languageMissing', { count: stats.missing || 0 }));
|
|
1094
|
+
|
|
1095
|
+
totalChanges += (stats.translated || 0) + (stats.missing || 0);
|
|
1096
|
+
languagesProcessed += 1;
|
|
1097
|
+
missingKeysAdded += stats.missing || 0;
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
console.log('\n' + t('init.completionSummaryTitle'));
|
|
1101
|
+
console.log(t('common.separator'));
|
|
1102
|
+
console.log(t('init.totalChanges', { count: totalChanges }));
|
|
1103
|
+
console.log(t('init.languagesProcessed', { count: languagesProcessed }));
|
|
1104
|
+
console.log(t('init.missingKeysAdded', { count: missingKeysAdded }));
|
|
1105
|
+
|
|
1106
|
+
if (process.stdin.isTTY && !this.config?.noPrompt) {
|
|
1107
|
+
const { ask } = require('../utils/cli');
|
|
1108
|
+
const generateReport = await ask('\n' + t('init.reportPrompt'));
|
|
1109
|
+
if (parseConfirmation(generateReport, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: true })) {
|
|
1110
|
+
await this.generateDetailedReport(results, targetLanguages);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
console.error('\n' + t('init.completionError', { error: error.message }));
|
|
1115
|
+
console.log(t('init.completionSummaryBasicTitle'));
|
|
1116
|
+
console.log(t('common.separator'));
|
|
1117
|
+
console.log(t('init.languagesProcessed', { count: Object.keys(results || {}).length }));
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Generate detailed report
|
|
1125
1122
|
async generateDetailedReport(results, targetLanguages) {
|
|
1126
1123
|
try {
|
|
1127
1124
|
const outputDir = this.config.outputDir || path.join(process.cwd(), 'i18ntk-reports');
|
|
1128
|
-
if (!SecurityUtils.safeExistsSync(outputDir)) {
|
|
1129
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
}
|
|
1125
|
+
if (!SecurityUtils.safeExistsSync(outputDir)) {
|
|
1126
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
const reportPayload = {
|
|
1130
|
+
timestamp: new Date().toISOString(),
|
|
1131
|
+
languages: targetLanguages,
|
|
1132
|
+
results,
|
|
1133
|
+
summary: {
|
|
1134
|
+
languagesProcessed: targetLanguages.length,
|
|
1135
|
+
totalFiles: Object.values(results).reduce((sum, data) => sum + (data.files?.length || 0), 0),
|
|
1136
|
+
totalKeys: Object.values(results).reduce((sum, data) => sum + (data.totalStats?.total || 0), 0),
|
|
1137
|
+
totalMissing: Object.values(results).reduce((sum, data) => sum + (data.totalStats?.missing || 0), 0)
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
const format = normalizeReportFormat(this.config.reports?.format || this.config.reportFormat || 'markdown');
|
|
1141
|
+
const writtenPath = await writeReportFile(outputDir, 'init-report', reportPayload, { format, title: 'I18NTK Init Report' });
|
|
1142
|
+
console.log(t('init.reportGenerated', { reportPath: writtenPath }));
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
console.error(t('init.reportFailed', { error: error.message }));
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1151
1147
|
|
|
1152
1148
|
// Offer interactive locale optimization after initialization
|
|
1153
|
-
async offerLocaleOptimization() {
|
|
1154
|
-
try {
|
|
1155
|
-
console.log('\n' + '='.repeat(60));
|
|
1156
|
-
console.log('
|
|
1157
|
-
console.log('='.repeat(60));
|
|
1158
|
-
|
|
1159
|
-
try {
|
|
1160
|
-
// Import locale optimizer directly
|
|
1161
|
-
const LocaleOptimizer = require('../utils/locale-optimizer');
|
|
1162
|
-
|
|
1163
|
-
// First run dry run to show current state
|
|
1164
|
-
console.log('\n
|
|
1165
|
-
const optimizer = new LocaleOptimizer();
|
|
1166
|
-
await optimizer.run({ dryRun: true });
|
|
1167
|
-
|
|
1168
|
-
console.log('\n
|
|
1169
|
-
|
|
1170
|
-
const answer = await this.prompt('\n
|
|
1171
|
-
|
|
1172
|
-
if (answer
|
|
1173
|
-
console.log('\n
|
|
1174
|
-
await optimizer.run({ interactive: true });
|
|
1175
|
-
console.log('\n
|
|
1176
|
-
} else {
|
|
1177
|
-
console.log('\n
|
|
1178
|
-
console.log(' node utils/locale-optimizer.js --interactive');
|
|
1179
|
-
}
|
|
1180
|
-
} catch (error) {
|
|
1181
|
-
console.log('\n
|
|
1182
|
-
console.log('\n
|
|
1183
|
-
console.log(' node utils/locale-optimizer.js --interactive');
|
|
1184
|
-
}
|
|
1185
|
-
} catch (error) {
|
|
1186
|
-
console.log('\n
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1149
|
+
async offerLocaleOptimization() {
|
|
1150
|
+
try {
|
|
1151
|
+
console.log('\n' + '='.repeat(60));
|
|
1152
|
+
console.log(t('init.optimize.title'));
|
|
1153
|
+
console.log('='.repeat(60));
|
|
1154
|
+
|
|
1155
|
+
try {
|
|
1156
|
+
// Import locale optimizer directly
|
|
1157
|
+
const LocaleOptimizer = require('../utils/locale-optimizer');
|
|
1158
|
+
|
|
1159
|
+
// First run dry run to show current state
|
|
1160
|
+
console.log('\n' + t('init.optimize.preview'));
|
|
1161
|
+
const optimizer = new LocaleOptimizer();
|
|
1162
|
+
await optimizer.run({ dryRun: true });
|
|
1163
|
+
|
|
1164
|
+
console.log('\n' + t('init.optimize.reduceTip'));
|
|
1165
|
+
|
|
1166
|
+
const answer = await this.prompt('\n' + t('init.optimize.prompt'));
|
|
1167
|
+
|
|
1168
|
+
if (parseConfirmation(answer, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false })) {
|
|
1169
|
+
console.log('\n' + t('init.optimize.starting'));
|
|
1170
|
+
await optimizer.run({ interactive: true });
|
|
1171
|
+
console.log('\n' + t('init.optimize.completed'));
|
|
1172
|
+
} else {
|
|
1173
|
+
console.log('\n' + t('init.optimize.later'));
|
|
1174
|
+
console.log(' node utils/locale-optimizer.js --interactive');
|
|
1175
|
+
}
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
console.log('\n' + t('init.optimize.unavailable', { error: error.message }));
|
|
1178
|
+
console.log('\n' + t('init.optimize.later'));
|
|
1179
|
+
console.log(' node utils/locale-optimizer.js --interactive');
|
|
1180
|
+
}
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
console.log('\n' + t('init.optimize.unavailable', { error: error.message }));
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1189
1185
|
|
|
1190
1186
|
// Run the initialization process with admin authentication
|
|
1191
1187
|
async run(options = {}) {
|
package/main/i18ntk-scanner.js
CHANGED
|
@@ -423,12 +423,11 @@ class I18nTextScanner {
|
|
|
423
423
|
for (const word of words) {
|
|
424
424
|
if (profile.stopwords.includes(word)) return true;
|
|
425
425
|
}
|
|
426
|
-
const validChars = trimmed.match(/[\p{L}\p{N}\s\-,.!?':"()\[\]{}]/gu) || [];
|
|
427
|
-
const validRatio = validChars.length / trimmed.length;
|
|
428
|
-
return validRatio >= 0.5;
|
|
429
426
|
}
|
|
430
427
|
|
|
431
|
-
|
|
428
|
+
const validChars = trimmed.match(/[\p{L}\p{N}\s\-,.!?':"()\[\]{}]/gu) || [];
|
|
429
|
+
const validRatio = validChars.length / trimmed.length;
|
|
430
|
+
return validRatio >= 0.5;
|
|
432
431
|
}
|
|
433
432
|
|
|
434
433
|
scanFile(filePath, patterns, minLength, maxLength) {
|
|
@@ -436,7 +435,7 @@ class I18nTextScanner {
|
|
|
436
435
|
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
437
436
|
const lines = content.split('\n');
|
|
438
437
|
const results = [];
|
|
439
|
-
const sourceLang = this.
|
|
438
|
+
const sourceLang = this.sourceLanguage || 'en';
|
|
440
439
|
|
|
441
440
|
patterns.forEach(pattern => {
|
|
442
441
|
let match;
|
|
@@ -475,7 +474,7 @@ class I18nTextScanner {
|
|
|
475
474
|
}
|
|
476
475
|
|
|
477
476
|
generateSuggestion(text) {
|
|
478
|
-
const sourceLang = this.
|
|
477
|
+
const sourceLang = this.sourceLanguage || 'en';
|
|
479
478
|
const transliterations = {
|
|
480
479
|
ja: { 'あ': 'a', 'い': 'i', 'う': 'u', 'え': 'e', 'お': 'o', 'か': 'ka', 'き': 'ki', 'く': 'ku', 'け': 'ke', 'こ': 'ko', 'さ': 'sa', 'し': 'shi', 'す': 'su', 'せ': 'se', 'そ': 'so', 'た': 'ta', 'ち': 'chi', 'つ': 'tsu', 'て': 'te', 'と': 'to', 'な': 'na', 'に': 'ni', 'ぬ': 'nu', 'ね': 'ne', 'の': 'no', 'は': 'ha', 'ひ': 'hi', 'ふ': 'fu', 'へ': 'he', 'ほ': 'ho', 'ま': 'ma', 'み': 'mi', 'む': 'mu', 'め': 'me', 'も': 'mo', 'や': 'ya', 'ゆ': 'yu', 'よ': 'yo', 'ら': 'ra', 'り': 'ri', 'る': 'ru', 'れ': 're', 'ろ': 'ro', 'わ': 'wa', 'を': 'wo', 'ん': 'n' },
|
|
481
480
|
ru: { 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'kh', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'sch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya' },
|
|
@@ -533,6 +532,9 @@ class I18nTextScanner {
|
|
|
533
532
|
gettext: `import gettext\ngettext.gettext('${text}')`,
|
|
534
533
|
underscore: `from gettext import gettext as _\n_('${text}')`,
|
|
535
534
|
lazy: `from gettext import gettext_lazy as _\n_('${text}')`
|
|
535
|
+
},
|
|
536
|
+
vanilla: {
|
|
537
|
+
generic: `t('ui.${text.toLowerCase().replace(/[^a-z0-9\s]/g, '').replace(/\s+/g, '_')}')`
|
|
536
538
|
}
|
|
537
539
|
};
|
|
538
540
|
|
|
@@ -696,7 +698,7 @@ class I18nTextScanner {
|
|
|
696
698
|
this.sourceDir = this.config.sourceDir || './src';
|
|
697
699
|
|
|
698
700
|
// Source language for multi-language detection
|
|
699
|
-
this.sourceLanguage = args
|
|
701
|
+
this.sourceLanguage = args.sourceLanguage || this.config.sourceLanguage || 'en';
|
|
700
702
|
|
|
701
703
|
// Resolve framework with precedence: CLI arg > config.framework.preference|string > auto-detect > fallback
|
|
702
704
|
const cliFramework = args.framework;
|
package/main/i18ntk-setup.js
CHANGED
|
@@ -15,7 +15,7 @@ const SecurityUtils = require('../utils/security');
|
|
|
15
15
|
const configManager = require('../utils/config-manager');
|
|
16
16
|
const SetupService = require('./manage/services/SetupService');
|
|
17
17
|
|
|
18
|
-
class I18nSetupManager {
|
|
18
|
+
class I18nSetupManager {
|
|
19
19
|
constructor() {
|
|
20
20
|
// Use the new SetupService for core business logic
|
|
21
21
|
this.setupService = new SetupService();
|
|
@@ -26,18 +26,41 @@ class I18nSetupManager {
|
|
|
26
26
|
return await this.setupService.setup();
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log([
|
|
34
|
+
'',
|
|
35
|
+
'i18ntk-setup - foundational i18n toolkit setup',
|
|
36
|
+
'',
|
|
37
|
+
'Usage:',
|
|
38
|
+
' i18ntk-setup [options]',
|
|
39
|
+
' node main/i18ntk-setup.js [options]',
|
|
40
|
+
'',
|
|
41
|
+
'Options:',
|
|
42
|
+
' -h, --help Show this help message',
|
|
43
|
+
'',
|
|
44
|
+
'The setup command detects the current project, writes i18ntk settings,',
|
|
45
|
+
'and generates i18ntk-setup-report.json in the current working directory.',
|
|
46
|
+
].join('\n'));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// CLI interface
|
|
50
|
+
if (require.main === module) {
|
|
51
|
+
if (process.argv.slice(2).some(arg => arg === '--help' || arg === '-h')) {
|
|
52
|
+
printHelp();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const setupManager = new I18nSetupManager();
|
|
57
|
+
setupManager.setup().catch(console.error);
|
|
58
|
+
}
|
|
37
59
|
|
|
38
60
|
// Export both the class and a run function for direct usage
|
|
39
61
|
module.exports = I18nSetupManager;
|
|
40
|
-
module.exports.run = async function() {
|
|
41
|
-
const setupManager = new I18nSetupManager();
|
|
42
|
-
return await setupManager.setup();
|
|
43
|
-
};
|
|
62
|
+
module.exports.run = async function() {
|
|
63
|
+
const setupManager = new I18nSetupManager();
|
|
64
|
+
return await setupManager.setup();
|
|
65
|
+
};
|
|
66
|
+
module.exports.printHelp = printHelp;
|
package/main/i18ntk-sizing.js
CHANGED
|
@@ -41,15 +41,17 @@ const { logger } = require('../utils/logger');
|
|
|
41
41
|
const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
|
|
42
42
|
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
43
43
|
|
|
44
|
-
// Ensure setup is complete before running
|
|
45
|
-
(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
44
|
+
// Ensure setup is complete before running (only when executed directly)
|
|
45
|
+
if (require.main === module) {
|
|
46
|
+
(async () => {
|
|
47
|
+
try {
|
|
48
|
+
await SetupEnforcer.checkSetupCompleteAsync();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Setup check failed:', error.message);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
}
|
|
53
55
|
|
|
54
56
|
loadTranslations();
|
|
55
57
|
|
|
@@ -1100,45 +1102,7 @@ Generated: ${new Date().toISOString()}
|
|
|
1100
1102
|
}
|
|
1101
1103
|
}
|
|
1102
1104
|
|
|
1103
|
-
//
|
|
1104
|
-
async analyze() {
|
|
1105
|
-
const startTime = Date.now();
|
|
1106
|
-
|
|
1107
|
-
try {
|
|
1108
|
-
logger.info(t("sizing.starting_i18n_sizing_analysis"));
|
|
1109
|
-
logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
1110
|
-
|
|
1111
|
-
const files = this.getLanguageFiles();
|
|
1112
|
-
|
|
1113
|
-
if (files.length === 0) {
|
|
1114
|
-
logger.warn(t("sizing.no_translation_files_found"));
|
|
1115
|
-
return;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
logger.info(t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
|
|
1119
|
-
|
|
1120
|
-
this.analyzeFileSizes(files);
|
|
1121
|
-
this.analyzeTranslationContent(files);
|
|
1122
|
-
this.generateSizeComparison();
|
|
1123
|
-
|
|
1124
|
-
if (this.format === 'table') {
|
|
1125
|
-
this.displayFolderResults();
|
|
1126
|
-
} else if (this.format === 'json') {
|
|
1127
|
-
logger.info(t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
await this.generateHumanReadableReport();
|
|
1131
|
-
|
|
1132
|
-
const endTime = Date.now();
|
|
1133
|
-
logger.info(t("sizing.analysis_completed", { duration: ((endTime - startTime) / 1000).toFixed(2) }));
|
|
1134
|
-
|
|
1135
|
-
} catch (error) {
|
|
1136
|
-
logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
1137
|
-
process.exit(1);
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// Parse command line arguments without yargs
|
|
1105
|
+
// Parse command line arguments without yargs
|
|
1142
1106
|
parseArgs() {
|
|
1143
1107
|
const args = process.argv.slice(2);
|
|
1144
1108
|
const options = {
|
|
@@ -1335,7 +1299,7 @@ Options:
|
|
|
1335
1299
|
|
|
1336
1300
|
const cliHelper = require('../utils/cli-helper');
|
|
1337
1301
|
const pin = await cliHelper.promptPin(t('adminCli.enterPin'));
|
|
1338
|
-
const isValid = await
|
|
1302
|
+
const isValid = await adminAuth.verifyPin(pin);
|
|
1339
1303
|
|
|
1340
1304
|
if (!isValid) {
|
|
1341
1305
|
console.log(t('adminCli.invalidPin'));
|
|
@@ -1377,7 +1341,11 @@ Options:
|
|
|
1377
1341
|
this.generateSizeComparison();
|
|
1378
1342
|
|
|
1379
1343
|
// Display results
|
|
1380
|
-
this.
|
|
1344
|
+
if (this.format === 'table') {
|
|
1345
|
+
this.displayFolderResults();
|
|
1346
|
+
} else if (this.format === 'json') {
|
|
1347
|
+
logger.info(t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
|
|
1348
|
+
}
|
|
1381
1349
|
|
|
1382
1350
|
// Generate reports if requested
|
|
1383
1351
|
await this.generateHumanReadableReport();
|