i18ntk 2.0.4 → 2.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.
Files changed (57) hide show
  1. package/README.md +38 -60
  2. package/main/i18ntk-analyze.js +49 -44
  3. package/main/i18ntk-complete.js +75 -74
  4. package/main/i18ntk-fixer.js +3 -3
  5. package/main/i18ntk-init.js +5 -5
  6. package/main/i18ntk-scanner.js +2 -2
  7. package/main/i18ntk-sizing.js +35 -35
  8. package/main/i18ntk-summary.js +4 -4
  9. package/main/i18ntk-ui.js +54 -8
  10. package/main/i18ntk-usage.js +14 -14
  11. package/main/i18ntk-validate.js +6 -5
  12. package/main/manage/commands/AnalyzeCommand.js +40 -35
  13. package/main/manage/commands/FixerCommand.js +2 -2
  14. package/main/manage/commands/ScannerCommand.js +2 -2
  15. package/main/manage/commands/ValidateCommand.js +9 -9
  16. package/main/manage/index.js +147 -75
  17. package/main/manage/managers/LanguageMenu.js +7 -2
  18. package/main/manage/services/UsageService.js +7 -7
  19. package/package.json +269 -290
  20. package/settings/settings-cli.js +3 -3
  21. package/ui-locales/de.json +161 -166
  22. package/ui-locales/en.json +13 -18
  23. package/ui-locales/es.json +171 -184
  24. package/ui-locales/fr.json +155 -161
  25. package/ui-locales/ja.json +192 -243
  26. package/ui-locales/ru.json +145 -196
  27. package/ui-locales/zh.json +179 -185
  28. package/utils/cli-helper.js +26 -98
  29. package/utils/extractors/regex.js +39 -12
  30. package/utils/i18n-helper.js +88 -40
  31. package/{scripts → utils}/locale-optimizer.js +61 -60
  32. package/utils/security-check-improved.js +16 -13
  33. package/utils/security.js +6 -4
  34. package/main/i18ntk-go.js +0 -283
  35. package/main/i18ntk-java.js +0 -380
  36. package/main/i18ntk-js.js +0 -512
  37. package/main/i18ntk-manage.js +0 -1694
  38. package/main/i18ntk-php.js +0 -462
  39. package/main/i18ntk-py.js +0 -379
  40. package/main/i18ntk-settings.js +0 -23
  41. package/main/manage/index-fixed.js +0 -1447
  42. package/main/manage/services/ConfigurationService-fixed.js +0 -449
  43. package/scripts/build-lite.js +0 -279
  44. package/scripts/deprecate-versions.js +0 -317
  45. package/scripts/export-translations.js +0 -84
  46. package/scripts/fix-all-i18n.js +0 -215
  47. package/scripts/fix-and-purify-i18n.js +0 -213
  48. package/scripts/fix-locale-control-chars.js +0 -110
  49. package/scripts/lint-locales.js +0 -80
  50. package/scripts/prepublish.js +0 -348
  51. package/scripts/security-check.js +0 -117
  52. package/scripts/sync-translations.js +0 -151
  53. package/scripts/sync-ui-locales.js +0 -20
  54. package/scripts/validate-all-translations.js +0 -139
  55. package/scripts/verify-deprecations.js +0 -157
  56. package/scripts/verify-translations.js +0 -63
  57. package/utils/security-fixed.js +0 -607
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;
@@ -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 };