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
@@ -1,380 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * i18ntk-java.js - Java Language I18n Management Command
5
- *
6
- * Supports:
7
- * - Spring Boot i18n
8
- * - Java ResourceBundle
9
- * - Android string resources
10
- * - Java internationalization patterns
11
- * - Properties files (.properties)
12
- * - XML resource files
13
- */
14
-
15
- const fs = require('fs');
16
- const path = require('path');
17
-
18
- const SecurityUtils = require(path.join(__dirname, '../utils/security'));
19
- const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper'));
20
- const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper'));
21
- const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
22
- const { program } = require('../utils/mini-commander');
23
-
24
- (async () => {
25
- try {
26
- await SetupEnforcer.checkSetupCompleteAsync();
27
- } catch (error) {
28
- console.error('Setup check failed:', error.message);
29
- process.exit(1);
30
- }
31
- })();
32
-
33
- class JavaI18nManager {
34
- constructor() {
35
- this.supportedPatterns = [
36
- 'getString(R.string.',
37
- 'getResources().getString(',
38
- 'messages.getString(',
39
- 'bundle.getString(',
40
- 'MessageSource.getMessage(',
41
- '@Value("${',
42
- '#{messages.',
43
- 'localeMessage.get(',
44
- 'i18n.get(',
45
- 'translate('
46
- ];
47
-
48
- this.fileExtensions = ['.java', '.kt', '.xml', '.properties'];
49
- this.resourceFormats = ['.properties', '.xml', '.json'];
50
- }
51
-
52
- async detectFramework(sourceDir) {
53
- // Check for Android
54
- const androidManifest = path.join(sourceDir, 'AndroidManifest.xml');
55
- if (SecurityUtils.safeExistsSync(androidManifest)) {
56
- return 'android';
57
- }
58
-
59
- // Check for Spring Boot
60
- const pomXml = path.join(sourceDir, 'pom.xml');
61
- const gradleFile = path.join(sourceDir, 'build.gradle');
62
-
63
- if (SecurityUtils.safeExistsSync(pomXml)) {
64
- const content = SecurityUtils.safeReadFileSync(pomXml, path.dirname(pomXml), 'utf8');
65
- if (content.includes('spring-boot')) {
66
- return 'spring-boot';
67
- }
68
- }
69
-
70
- if (SecurityUtils.safeExistsSync(gradleFile)) {
71
- const content = SecurityUtils.safeReadFileSync(gradleFile, path.dirname(gradleFile), 'utf8');
72
- if (content.includes('spring-boot')) {
73
- return 'spring-boot';
74
- }
75
- }
76
-
77
- // Check for standard Java
78
- const javaFiles = this.findFiles(sourceDir, '.java');
79
- if (javaFiles.length > 0) {
80
- return 'standard-java';
81
- }
82
-
83
- // Check for Kotlin
84
- const kotlinFiles = this.findFiles(sourceDir, '.kt');
85
- if (kotlinFiles.length > 0) {
86
- return 'kotlin';
87
- }
88
-
89
- return 'generic';
90
- }
91
-
92
- async extractTranslations(sourceDir, options = {}) {
93
- const framework = await this.detectFramework(sourceDir);
94
- const translations = new Set();
95
-
96
- // Process Java/Kotlin files
97
- const javaFiles = [...this.findFiles(sourceDir, '.java'), ...this.findFiles(sourceDir, '.kt')];
98
-
99
- for (const file of javaFiles) {
100
- const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
101
-
102
- // Extract Android string references
103
- const androidPatterns = [
104
- /R\.string\.([a-zA-Z0-9_]+)/g,
105
- /getString\(R\.string\.([a-zA-Z0-9_]+)\)/g
106
- ];
107
-
108
- // Extract Spring/JVM patterns
109
- const springPatterns = [
110
- /getResources\(\)\.getString\(R\.string\.([a-zA-Z0-9_]+)\)/g,
111
- /messages\.getString\("([^"]+)"\)/g,
112
- /bundle\.getString\("([^"]+)"\)/g,
113
- /MessageSource\.getMessage\("([^"]+)"/g,
114
- /@Value\("\$\{([^}]+)\}"\)/g,
115
- /localeMessage\.get\("([^"]+)"\)/g,
116
- /i18n\.get\("([^"]+)"\)/g,
117
- /translate\("([^"]+)"\)/g
118
- ];
119
-
120
- const patterns = framework === 'android' ? androidPatterns : [...androidPatterns, ...springPatterns];
121
-
122
- for (const pattern of patterns) {
123
- let match;
124
- while ((match = pattern.exec(content)) !== null) {
125
- translations.add(match[1]);
126
- }
127
- }
128
- }
129
-
130
- // Process XML files (Android)
131
- const xmlFiles = this.findFiles(sourceDir, '.xml');
132
- for (const file of xmlFiles) {
133
- if (file.includes('strings.xml')) {
134
- const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
135
- const stringPatterns = [
136
- /<string name="([^"]+)"/g,
137
- /<string-array name="([^"]+)"/g,
138
- /<plurals name="([^"]+)"/g
139
- ];
140
-
141
- for (const pattern of stringPatterns) {
142
- let match;
143
- while ((match = pattern.exec(content)) !== null) {
144
- translations.add(match[1]);
145
- }
146
- }
147
- }
148
- }
149
-
150
- // Process properties files
151
- const propertiesFiles = this.findFiles(sourceDir, '.properties');
152
- for (const file of propertiesFiles) {
153
- if (file.includes('messages') || file.includes('i18n')) {
154
- const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
155
- const lines = content.split('\n');
156
-
157
- for (const line of lines) {
158
- const trimmed = line.trim();
159
- if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {
160
- const key = trimmed.split('=')[0].trim();
161
- translations.add(key);
162
- }
163
- }
164
- }
165
- }
166
-
167
- return {
168
- framework,
169
- translations: Array.from(translations),
170
- files: javaFiles.length + xmlFiles.length + propertiesFiles.length,
171
- patterns: this.supportedPatterns
172
- };
173
- }
174
-
175
- async createLocaleStructure(outputDir, languages = ['en'], framework = 'standard-java') {
176
- const localesDir = path.join(outputDir, 'locales');
177
-
178
- for (const lang of languages) {
179
- const langDir = path.join(localesDir, lang);
180
- fs.mkdirSync(langDir, { recursive: true });
181
-
182
- if (framework === 'android') {
183
- // Android string resources
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
- );
198
-
199
- // JSON format
200
- SecurityUtils.safeWriteFileSync(path.join(langDir, 'messages.json'), JSON.stringify({
201
- app: { name: "My Application" },
202
- hello: { message: "Hello, World!" },
203
- items: { count: "{0} items" }
204
- }, null, 2));
205
- }
206
- }
207
-
208
- return localesDir;
209
- }
210
-
211
- findFiles(dir, extension) {
212
- const files = [];
213
-
214
- function traverse(currentDir) {
215
- if (!SecurityUtils.safeExistsSync(currentDir)) return;
216
-
217
- const items = fs.readdirSync(currentDir);
218
-
219
- for (const item of items) {
220
- const fullPath = path.join(currentDir, item);
221
- try {
222
- const stat = fs.statSync(fullPath);
223
-
224
- if (stat.isDirectory() && !item.startsWith('.') &&
225
- !['node_modules', 'build', 'target'].includes(item)) {
226
- traverse(fullPath);
227
- } else if (stat.isFile() && path.extname(item) === extension) {
228
- files.push(fullPath);
229
- }
230
- } catch (error) {
231
- // Skip files we can't access
232
- }
233
- }
234
- }
235
-
236
- traverse(dir);
237
- return files;
238
- }
239
-
240
- async generateReport(results, outputDir) {
241
- const report = {
242
- timestamp: new Date().toISOString(),
243
- framework: results.framework,
244
- totalTranslations: results.translations.length,
245
- translations: results.translations,
246
- filesProcessed: results.files,
247
- patterns: results.patterns,
248
- recommendations: this.getRecommendations(results)
249
- };
250
-
251
- const reportPath = path.join(outputDir, 'i18ntk-java-report.json');
252
- SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
253
-
254
- return reportPath;
255
- }
256
-
257
- getRecommendations(results) {
258
- const recommendations = [];
259
-
260
- if (results.translations.length === 0) {
261
- recommendations.push('No translations found. Check your Java i18n patterns');
262
- }
263
-
264
- if (results.files === 0) {
265
- recommendations.push('No Java/Kotlin files found in source directory');
266
- }
267
-
268
- if (results.framework === 'generic') {
269
- recommendations.push('Consider using Spring Boot for better i18n support');
270
- }
271
-
272
- if (results.framework === 'android') {
273
- recommendations.push('Use Android string resources for best compatibility');
274
- }
275
-
276
- return recommendations;
277
- }
278
- }
279
-
280
- // CLI Implementation
281
- program
282
- .name('i18ntk-java')
283
- .description('Java language i18n management tool')
284
- .version('1.10.1');
285
-
286
- program
287
- .command('init')
288
- .description('Initialize Java i18n structure')
289
- .option('-s, --source-dir <dir>', 'Source directory', './')
290
- .option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
291
- .option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
292
- .option('-f, --framework <framework>', 'Framework (spring-boot|android|standard)', 'standard-java')
293
- .action(async (options) => {
294
- try {
295
- const manager = new JavaI18nManager();
296
- const languages = options.languages.split(',');
297
-
298
- const localesDir = await manager.createLocaleStructure(options.outputDir, languages, options.framework);
299
-
300
- console.log(`✅ Java i18n structure initialized in: ${localesDir}`);
301
- console.log(`📊 Languages: ${languages.join(', ')}`);
302
- console.log(`🎯 Framework: ${options.framework}`);
303
-
304
- } catch (error) {
305
- console.error('❌ Error initializing Java i18n:', error.message);
306
- process.exit(1);
307
- }
308
- });
309
-
310
- program
311
- .command('analyze')
312
- .description('Analyze Java i18n usage')
313
- .option('-s, --source-dir <dir>', 'Source directory', './')
314
- .option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
315
- .option('--dry-run', 'Preview without making changes')
316
- .action(async (options) => {
317
- try {
318
- SecurityUtils.validatePath(options.sourceDir);
319
-
320
- const manager = new JavaI18nManager();
321
- const results = await manager.extractTranslations(options.sourceDir);
322
-
323
- console.log(`🔍 Framework detected: ${results.framework}`);
324
- console.log(`📊 Files processed: ${results.files}`);
325
- console.log(`📝 Translations found: ${results.translations.length}`);
326
-
327
- if (!options.dryRun) {
328
- const reportPath = await manager.generateReport(results, options.outputDir);
329
- console.log(`📄 Report saved: ${reportPath}`);
330
- }
331
-
332
- } catch (error) {
333
- console.error('❌ Error analyzing Java i18n:', error.message);
334
- process.exit(1);
335
- }
336
- });
337
-
338
- program
339
- .command('extract')
340
- .description('Extract Java translations')
341
- .option('-s, --source-dir <dir>', 'Source directory', './')
342
- .option('-o, --output-dir <dir>', 'Output directory', './locales')
343
- .option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
344
- .option('-f, --framework <framework>', 'Framework (spring-boot|android|standard)', 'standard-java')
345
- .action(async (options) => {
346
- try {
347
- SecurityUtils.validatePath(options.sourceDir);
348
-
349
- const manager = new JavaI18nManager();
350
- const results = await manager.extractTranslations(options.sourceDir);
351
-
352
- await manager.createLocaleStructure(options.outputDir, options.languages.split(','), options.framework);
353
-
354
- console.log(`✅ Extracted ${results.translations.length} translations`);
355
- console.log(`📁 Locale structure created in: ${options.outputDir}`);
356
- console.log(`🎯 Framework: ${options.framework}`);
357
-
358
- } catch (error) {
359
- console.error('❌ Error extracting Java translations:', error.message);
360
- process.exit(1);
361
- }
362
- });
363
-
364
- // Handle uncaught errors
365
- process.on('uncaughtException', (error) => {
366
- console.error('❌ Uncaught exception:', error.message);
367
- process.exit(1);
368
- });
369
-
370
- process.on('unhandledRejection', (reason) => {
371
- console.error('❌ Unhandled rejection:', reason);
372
- process.exit(1);
373
- });
374
-
375
- // Export for programmatic use
376
- module.exports = { JavaI18nManager };
377
-
378
- if (require.main === module) {
379
- program.parse();
380
- }