i18ntk 2.1.0 ā 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -50
- package/main/i18ntk-analyze.js +63 -63
- package/main/i18ntk-backup-class.js +37 -41
- package/main/i18ntk-backup.js +28 -30
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-doctor.js +7 -6
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +49 -13
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +36 -37
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +95 -96
- package/main/i18ntk-usage.js +31 -19
- package/main/i18ntk-validate.js +78 -27
- package/main/manage/commands/AnalyzeCommand.js +71 -73
- package/main/manage/commands/CommandRouter.js +15 -12
- package/main/manage/commands/FixerCommand.js +94 -38
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +87 -36
- package/main/manage/index.js +165 -152
- package/main/manage/managers/DebugMenu.js +6 -6
- package/main/manage/managers/InteractiveMenu.js +6 -6
- package/main/manage/managers/LanguageMenu.js +12 -6
- package/main/manage/managers/SettingsMenu.js +6 -6
- package/main/manage/services/AuthenticationService.js +5 -6
- package/main/manage/services/ConfigurationService.js +22 -34
- package/main/manage/services/FileManagementService.js +6 -6
- package/main/manage/services/InitService.js +44 -8
- package/main/manage/services/UsageService.js +24 -12
- package/package.json +21 -42
- package/settings/settings-cli.js +5 -5
- package/settings/settings-manager.js +984 -968
- package/ui-locales/de.json +12 -11
- package/ui-locales/en.json +12 -11
- package/ui-locales/es.json +12 -11
- package/ui-locales/fr.json +12 -11
- package/ui-locales/ja.json +12 -11
- package/ui-locales/ru.json +12 -11
- package/ui-locales/zh.json +12 -11
- package/utils/config-helper.js +27 -16
- package/utils/config-manager.js +8 -7
- package/utils/i18n-helper.js +161 -166
- package/utils/init-helper.js +3 -2
- package/utils/json-output.js +11 -10
- package/{scripts ā utils}/locale-optimizer.js +61 -60
- package/utils/logger.js +4 -4
- package/utils/safe-json.js +3 -3
- package/utils/secure-backup.js +8 -7
- package/utils/setup-enforcer.js +63 -98
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -236
- package/scripts/fix-and-purify-i18n.js +0 -233
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish-dev.js +0 -221
- package/scripts/prepublish.js +0 -362
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -195
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -609
package/main/i18ntk-py.js
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* i18ntk Python Command
|
|
5
|
-
* Specialized command for Python i18n management
|
|
6
|
-
*
|
|
7
|
-
* Usage: i18ntk-py [options]
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
|
|
13
|
-
const SecurityUtils = require(path.join(__dirname, '../utils/security'));
|
|
14
|
-
const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper'));
|
|
15
|
-
const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper'));
|
|
16
|
-
const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
|
|
17
|
-
const { program } = require('../utils/mini-commander');
|
|
18
|
-
|
|
19
|
-
(async () => {
|
|
20
|
-
try {
|
|
21
|
-
await SetupEnforcer.checkSetupCompleteAsync();
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.error('Setup check failed:', error.message);
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
})();
|
|
27
|
-
|
|
28
|
-
class I18ntkPythonCommand {
|
|
29
|
-
constructor() {
|
|
30
|
-
this.config = null;
|
|
31
|
-
this.sourceDir = './locales';
|
|
32
|
-
this.pythonPatterns = [
|
|
33
|
-
/_(?:gettext)?\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
|
|
34
|
-
/gettext\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
|
|
35
|
-
/gettext_lazy\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
|
|
36
|
-
/ngettext\s*\(\s*["'`]([^"'`]+)["'`]\s*,/g,
|
|
37
|
-
/lazy_gettext\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
|
|
38
|
-
/ugettext\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g
|
|
39
|
-
];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async init() {
|
|
43
|
-
console.log('š§ Initializing i18ntk Python command...');
|
|
44
|
-
|
|
45
|
-
program
|
|
46
|
-
.name('i18ntk-py')
|
|
47
|
-
.description('i18ntk specialized for Python applications')
|
|
48
|
-
.version('1.10.1')
|
|
49
|
-
.option('-s, --source-dir <dir>', 'Source directory to scan', './')
|
|
50
|
-
.option('-l, --locales-dir <dir>', 'Locales directory', './locales')
|
|
51
|
-
.option('--framework <type>', 'Python framework type', 'auto')
|
|
52
|
-
.option('--dry-run', 'Show what would be done without making changes')
|
|
53
|
-
.option('--debug', 'Enable debug output')
|
|
54
|
-
.option('--extract-only', 'Only extract translations, don\'t analyze')
|
|
55
|
-
.option('--django', 'Force Django mode')
|
|
56
|
-
.option('--flask', 'Force Flask mode')
|
|
57
|
-
.option('--generic', 'Force generic Python mode')
|
|
58
|
-
.parse();
|
|
59
|
-
|
|
60
|
-
this.options = program.opts();
|
|
61
|
-
this.sourceDir = path.resolve(this.options.sourceDir);
|
|
62
|
-
this.localesDir = path.resolve(this.options.localesDir);
|
|
63
|
-
|
|
64
|
-
await this.validateSourceDir();
|
|
65
|
-
await this.loadConfig();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async validateSourceDir() {
|
|
69
|
-
if (!SecurityUtils.safeExistsSync(this.sourceDir, path.dirname(this.sourceDir))) {
|
|
70
|
-
console.error(`ā Source directory not found: ${this.sourceDir}`);
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const stats = fs.statSync(this.sourceDir);
|
|
75
|
-
if (!stats.isDirectory()) {
|
|
76
|
-
console.error(`ā Source path is not a directory: ${this.sourceDir}`);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async loadConfig() {
|
|
82
|
-
try {
|
|
83
|
-
this.config = await getConfig();
|
|
84
|
-
this.config.python = this.config.python || {};
|
|
85
|
-
} catch (error) {
|
|
86
|
-
console.warn('ā ļø Could not load config, using defaults');
|
|
87
|
-
this.config = { python: {} };
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async detectFramework() {
|
|
92
|
-
if (this.options.django) return 'django';
|
|
93
|
-
if (this.options.flask) return 'flask';
|
|
94
|
-
if (this.options.generic) return 'generic';
|
|
95
|
-
|
|
96
|
-
console.log('š Detecting Python framework...');
|
|
97
|
-
|
|
98
|
-
// Check for Django
|
|
99
|
-
const djangoIndicators = [
|
|
100
|
-
'manage.py',
|
|
101
|
-
'settings.py',
|
|
102
|
-
'requirements.txt',
|
|
103
|
-
'django'
|
|
104
|
-
];
|
|
105
|
-
|
|
106
|
-
// Check for Flask
|
|
107
|
-
const flaskIndicators = [
|
|
108
|
-
'app.py',
|
|
109
|
-
'requirements.txt',
|
|
110
|
-
'flask'
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
let framework = 'generic';
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
// Check requirements.txt
|
|
117
|
-
const requirementsPath = path.join(this.sourceDir, 'requirements.txt');
|
|
118
|
-
if (SecurityUtils.safeExistsSync(requirementsPath, this.sourceDir)) {
|
|
119
|
-
const requirements = SecurityUtils.safeReadFileSync(requirementsPath, this.sourceDir, 'utf8');
|
|
120
|
-
if (requirements.includes('Django')) framework = 'django';
|
|
121
|
-
else if (requirements.includes('Flask')) framework = 'flask';
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Check for Django files
|
|
125
|
-
for (const indicator of djangoIndicators) {
|
|
126
|
-
if (this.findFiles(indicator).length > 0) {
|
|
127
|
-
framework = 'django';
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Check for Flask files
|
|
133
|
-
for (const indicator of flaskIndicators) {
|
|
134
|
-
if (this.findFiles(indicator).length > 0) {
|
|
135
|
-
framework = 'flask';
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
} catch (error) {
|
|
141
|
-
if (this.options.debug) {
|
|
142
|
-
console.error('Debug: Framework detection error:', error.message);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
console.log(`ā
Detected framework: ${framework}`);
|
|
147
|
-
return framework;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
findFiles(pattern) {
|
|
151
|
-
const results = [];
|
|
152
|
-
|
|
153
|
-
function scanDir(dir) {
|
|
154
|
-
if (!SecurityUtils.safeExistsSync(dir, path.dirname(dir))) return;
|
|
155
|
-
|
|
156
|
-
const items = fs.readdirSync(dir);
|
|
157
|
-
for (const item of items) {
|
|
158
|
-
const fullPath = path.join(dir, item);
|
|
159
|
-
const stat = fs.statSync(fullPath);
|
|
160
|
-
|
|
161
|
-
if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
|
|
162
|
-
scanDir(fullPath);
|
|
163
|
-
} else if (item.includes(pattern) || fullPath.includes(pattern)) {
|
|
164
|
-
results.push(fullPath);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
scanDir(this.sourceDir);
|
|
170
|
-
return results;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async extractTranslations() {
|
|
174
|
-
console.log('š¦ Extracting Python translations...');
|
|
175
|
-
|
|
176
|
-
const pythonFiles = this.findFiles('.py');
|
|
177
|
-
const translations = new Set();
|
|
178
|
-
|
|
179
|
-
for (const file of pythonFiles) {
|
|
180
|
-
try {
|
|
181
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
182
|
-
|
|
183
|
-
for (const pattern of this.pythonPatterns) {
|
|
184
|
-
let match;
|
|
185
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
186
|
-
translations.add(match[1]);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Reset regex state for next file
|
|
191
|
-
for (const pattern of this.pythonPatterns) {
|
|
192
|
-
pattern.lastIndex = 0;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
} catch (error) {
|
|
196
|
-
if (this.options.debug) {
|
|
197
|
-
console.error(`Error reading ${file}:`, error.message);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
console.log(`š Found ${translations.size} unique translation keys`);
|
|
203
|
-
return Array.from(translations);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async createLocaleStructure() {
|
|
207
|
-
if (!SecurityUtils.safeExistsSync(this.localesDir, path.dirname(this.localesDir))) {
|
|
208
|
-
if (this.options.dryRun) {
|
|
209
|
-
console.log(`š Would create directory: ${this.localesDir}`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
fs.mkdirSync(this.localesDir, { recursive: true });
|
|
214
|
-
console.log(`š Created locales directory: ${this.localesDir}`);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const languages = ['en', 'es', 'fr', 'de', 'ja', 'ru', 'zh'];
|
|
218
|
-
|
|
219
|
-
for (const lang of languages) {
|
|
220
|
-
const langDir = path.join(this.localesDir, lang);
|
|
221
|
-
if (!SecurityUtils.safeExistsSync(langDir, this.localesDir)) {
|
|
222
|
-
if (this.options.dryRun) {
|
|
223
|
-
console.log(`š Would create directory: ${langDir}`);
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
fs.mkdirSync(langDir, { recursive: true });
|
|
228
|
-
|
|
229
|
-
// Create basic translation files
|
|
230
|
-
const commonFile = path.join(langDir, 'common.json');
|
|
231
|
-
const initialContent = {
|
|
232
|
-
"python": {
|
|
233
|
-
"welcome": `Welcome to Python (${lang})`,
|
|
234
|
-
"framework_detected": "Framework detected: {framework}",
|
|
235
|
-
"files_processed": "Processed {count} files"
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
SecurityUtils.safeWriteFileSync(commonFile, JSON.stringify(initialContent, null, 2), this.localesDir);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async analyzeFramework(framework) {
|
|
245
|
-
console.log(`š Analyzing ${framework} project...`);
|
|
246
|
-
|
|
247
|
-
const analysis = {
|
|
248
|
-
framework,
|
|
249
|
-
files: {
|
|
250
|
-
total: 0,
|
|
251
|
-
python: 0,
|
|
252
|
-
templates: 0,
|
|
253
|
-
config: 0
|
|
254
|
-
},
|
|
255
|
-
patterns: {
|
|
256
|
-
gettext: 0,
|
|
257
|
-
gettext_lazy: 0,
|
|
258
|
-
ngettext: 0,
|
|
259
|
-
django: 0,
|
|
260
|
-
flask: 0
|
|
261
|
-
},
|
|
262
|
-
recommendations: []
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
// Count Python files
|
|
266
|
-
const pythonFiles = this.findFiles('.py');
|
|
267
|
-
analysis.files.python = pythonFiles.length;
|
|
268
|
-
analysis.files.total += pythonFiles.length;
|
|
269
|
-
|
|
270
|
-
// Count template files
|
|
271
|
-
const templateExtensions = ['.html', '.jinja', '.j2'];
|
|
272
|
-
let templateFiles = [];
|
|
273
|
-
for (const ext of templateExtensions) {
|
|
274
|
-
templateFiles = templateFiles.concat(this.findFiles(ext));
|
|
275
|
-
}
|
|
276
|
-
analysis.files.templates = templateFiles.length;
|
|
277
|
-
analysis.files.total += templateFiles.length;
|
|
278
|
-
|
|
279
|
-
// Analyze patterns
|
|
280
|
-
for (const file of pythonFiles) {
|
|
281
|
-
try {
|
|
282
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
283
|
-
|
|
284
|
-
if (content.includes('gettext(')) analysis.patterns.gettext++;
|
|
285
|
-
if (content.includes('gettext_lazy(')) analysis.patterns.gettext_lazy++;
|
|
286
|
-
if (content.includes('ngettext(')) analysis.patterns.ngettext++;
|
|
287
|
-
if (content.includes('django')) analysis.patterns.django++;
|
|
288
|
-
if (content.includes('flask')) analysis.patterns.flask++;
|
|
289
|
-
|
|
290
|
-
} catch (error) {
|
|
291
|
-
// Skip unreadable files
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Generate recommendations
|
|
296
|
-
if (framework === 'django' && analysis.patterns.gettext === 0) {
|
|
297
|
-
analysis.recommendations.push('Consider adding Django gettext for i18n support');
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (framework === 'flask' && analysis.patterns.gettext === 0) {
|
|
301
|
-
analysis.recommendations.push('Consider adding Flask-Babel for i18n support');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return analysis;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
async generateReport(analysis, translations) {
|
|
308
|
-
const report = {
|
|
309
|
-
timestamp: new Date().toISOString(),
|
|
310
|
-
framework: analysis.framework,
|
|
311
|
-
summary: {
|
|
312
|
-
totalFiles: analysis.files.total,
|
|
313
|
-
pythonFiles: analysis.files.python,
|
|
314
|
-
templateFiles: analysis.files.templates,
|
|
315
|
-
translationKeys: translations.length
|
|
316
|
-
},
|
|
317
|
-
patterns: analysis.patterns,
|
|
318
|
-
recommendations: analysis.recommendations,
|
|
319
|
-
files: {
|
|
320
|
-
python: this.findFiles('.py'),
|
|
321
|
-
templates: this.findFiles('.html').concat(this.findFiles('.jinja')).concat(this.findFiles('.j2'))
|
|
322
|
-
},
|
|
323
|
-
translations: translations.sort()
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
const reportPath = path.join(this.sourceDir, 'i18ntk-py-report.json');
|
|
327
|
-
|
|
328
|
-
if (this.options.dryRun) {
|
|
329
|
-
console.log(`š Would create report: ${reportPath}`);
|
|
330
|
-
console.log('š Report contents:', JSON.stringify(report, null, 2));
|
|
331
|
-
} else {
|
|
332
|
-
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2), this.sourceDir);
|
|
333
|
-
console.log(`š Report saved: ${reportPath}`);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return report;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async run() {
|
|
340
|
-
try {
|
|
341
|
-
console.log('š i18ntk Python Command v1.10.1');
|
|
342
|
-
console.log('='.repeat(50));
|
|
343
|
-
|
|
344
|
-
await this.init();
|
|
345
|
-
|
|
346
|
-
const framework = await this.detectFramework();
|
|
347
|
-
const translations = await this.extractTranslations();
|
|
348
|
-
|
|
349
|
-
if (!this.options.extractOnly) {
|
|
350
|
-
await this.createLocaleStructure();
|
|
351
|
-
const analysis = await this.analyzeFramework(framework);
|
|
352
|
-
const report = await this.generateReport(analysis, translations);
|
|
353
|
-
|
|
354
|
-
console.log('\nā
Analysis complete!');
|
|
355
|
-
console.log(`š Framework: ${framework}`);
|
|
356
|
-
console.log(`š Python files: ${analysis.files.python}`);
|
|
357
|
-
console.log(`šÆ Translation keys: ${translations.length}`);
|
|
358
|
-
console.log(`š Report: ${this.options.dryRun ? 'Not saved (dry-run)' : 'Saved to i18ntk-py-report.json'}`);
|
|
359
|
-
} else {
|
|
360
|
-
console.log(`š¦ Extracted ${translations.length} translation keys`);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
} catch (error) {
|
|
364
|
-
console.error('ā Error:', error.message);
|
|
365
|
-
if (this.options.debug) {
|
|
366
|
-
console.error(error.stack);
|
|
367
|
-
}
|
|
368
|
-
process.exit(1);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Run if called directly
|
|
374
|
-
if (require.main === module) {
|
|
375
|
-
const cmd = new I18ntkPythonCommand();
|
|
376
|
-
cmd.run();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
module.exports = I18ntkPythonCommand;
|
package/main/i18ntk-settings.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
3
|
-
const SettingsCLI = require('../settings/settings-cli');
|
|
4
|
-
|
|
5
|
-
(async () => {
|
|
6
|
-
try {
|
|
7
|
-
await SetupEnforcer.checkSetupCompleteAsync();
|
|
8
|
-
} catch (error) {
|
|
9
|
-
console.error('Setup check failed:', error.message);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
})();
|
|
13
|
-
|
|
14
|
-
async function run() {
|
|
15
|
-
const cli = new SettingsCLI();
|
|
16
|
-
await cli.run();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (require.main === module) {
|
|
20
|
-
run();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
module.exports = { run };
|