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-go.js
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* i18ntk-go.js - Go Language I18n Management Command
|
|
5
|
-
*
|
|
6
|
-
* Supports:
|
|
7
|
-
* - Standard Go i18n patterns
|
|
8
|
-
* - go-i18n library
|
|
9
|
-
* - Custom Go i18n implementations
|
|
10
|
-
* - Resource file (.json, .toml) management
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
const SecurityUtils = require(path.join(__dirname, '../utils/security'));
|
|
16
|
-
const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper'));
|
|
17
|
-
const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper'));
|
|
18
|
-
const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
|
|
19
|
-
const { program } = require('../utils/mini-commander');
|
|
20
|
-
|
|
21
|
-
(async () => {
|
|
22
|
-
try {
|
|
23
|
-
await SetupEnforcer.checkSetupCompleteAsync();
|
|
24
|
-
} catch (error) {
|
|
25
|
-
console.error('Setup check failed:', error.message);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
})();
|
|
29
|
-
|
|
30
|
-
class GoI18nManager {
|
|
31
|
-
constructor() {
|
|
32
|
-
this.supportedPatterns = [
|
|
33
|
-
'T("key")',
|
|
34
|
-
'Localize("key")',
|
|
35
|
-
'i18n.T("key")',
|
|
36
|
-
'tr("key")',
|
|
37
|
-
'message.Printer.Printf',
|
|
38
|
-
'i18n.MustLocalize'
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
this.fileExtensions = ['.go', '.mod', '.sum'];
|
|
42
|
-
this.resourceFormats = ['.json', '.toml', '.yaml', '.yml'];
|
|
43
|
-
}
|
|
44
|
-
|
|
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';
|
|
52
|
-
|
|
53
|
-
return 'standard-go';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Check for Go files
|
|
57
|
-
const goFiles = this.findFiles(sourceDir, '.go');
|
|
58
|
-
if (goFiles.length > 0) {
|
|
59
|
-
return 'standard-go';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return 'generic';
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async extractTranslations(sourceDir, options = {}) {
|
|
66
|
-
const framework = await this.detectFramework(sourceDir);
|
|
67
|
-
const translations = new Set();
|
|
68
|
-
|
|
69
|
-
const goFiles = this.findFiles(sourceDir, '.go');
|
|
70
|
-
|
|
71
|
-
for (const file of goFiles) {
|
|
72
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
|
|
73
|
-
|
|
74
|
-
// Extract Go i18n patterns
|
|
75
|
-
const patterns = [
|
|
76
|
-
/T\("([^"]+)"\)/g,
|
|
77
|
-
/Localize\("([^"]+)"\)/g,
|
|
78
|
-
/i18n\.T\("([^"]+)"\)/g,
|
|
79
|
-
/tr\("([^"]+)"\)/g,
|
|
80
|
-
/message\.Printer\.Printf\("([^"]+)"/g,
|
|
81
|
-
/i18n\.MustLocalize\(&i18n\.LocalizeConfig\{MessageID:\s*"([^"]+)"/g
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
for (const pattern of patterns) {
|
|
85
|
-
let match;
|
|
86
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
87
|
-
translations.add(match[1]);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
framework,
|
|
94
|
-
translations: Array.from(translations),
|
|
95
|
-
files: goFiles.length,
|
|
96
|
-
patterns: this.supportedPatterns
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async createLocaleStructure(outputDir, languages = ['en']) {
|
|
101
|
-
const localesDir = path.join(outputDir, 'locales');
|
|
102
|
-
|
|
103
|
-
for (const lang of languages) {
|
|
104
|
-
const langDir = path.join(localesDir, lang);
|
|
105
|
-
fs.mkdirSync(langDir, { recursive: true });
|
|
106
|
-
|
|
107
|
-
// Create Go i18n format files
|
|
108
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.toml'), `# Go i18n translations for ${lang}
|
|
109
|
-
[hello]
|
|
110
|
-
other = "Hello, World!"
|
|
111
|
-
|
|
112
|
-
[items]
|
|
113
|
-
one = "{{.Count}} item"
|
|
114
|
-
other = "{{.Count}} items"
|
|
115
|
-
`);
|
|
116
|
-
|
|
117
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.json'), JSON.stringify({
|
|
118
|
-
hello: "Hello, World!",
|
|
119
|
-
items: {
|
|
120
|
-
one: "{{.Count}} item",
|
|
121
|
-
other: "{{.Count}} items"
|
|
122
|
-
}
|
|
123
|
-
}, null, 2));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return localesDir;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
findFiles(dir, extension) {
|
|
130
|
-
const files = [];
|
|
131
|
-
|
|
132
|
-
function traverse(currentDir) {
|
|
133
|
-
const items = fs.readdirSync(currentDir);
|
|
134
|
-
|
|
135
|
-
for (const item of items) {
|
|
136
|
-
const fullPath = path.join(currentDir, item);
|
|
137
|
-
const stat = fs.statSync(fullPath);
|
|
138
|
-
|
|
139
|
-
if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
|
|
140
|
-
traverse(fullPath);
|
|
141
|
-
} else if (stat.isFile() && path.extname(item) === extension) {
|
|
142
|
-
files.push(fullPath);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
traverse(dir);
|
|
148
|
-
return files;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async generateReport(results, outputDir) {
|
|
152
|
-
const report = {
|
|
153
|
-
timestamp: new Date().toISOString(),
|
|
154
|
-
framework: results.framework,
|
|
155
|
-
totalTranslations: results.translations.length,
|
|
156
|
-
translations: results.translations,
|
|
157
|
-
filesProcessed: results.files,
|
|
158
|
-
patterns: results.patterns,
|
|
159
|
-
recommendations: this.getRecommendations(results)
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const reportPath = path.join(outputDir, 'i18ntk-go-report.json');
|
|
163
|
-
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
164
|
-
|
|
165
|
-
return reportPath;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
getRecommendations(results) {
|
|
169
|
-
const recommendations = [];
|
|
170
|
-
|
|
171
|
-
if (results.translations.length === 0) {
|
|
172
|
-
recommendations.push('No translations found. Check your Go i18n patterns');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (results.files === 0) {
|
|
176
|
-
recommendations.push('No Go files found in source directory');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (results.framework === 'generic') {
|
|
180
|
-
recommendations.push('Consider using go-i18n library for better i18n support');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return recommendations;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// CLI Implementation
|
|
188
|
-
program
|
|
189
|
-
.name('i18ntk-go')
|
|
190
|
-
.description('Go language i18n management tool')
|
|
191
|
-
.version('1.10.1');
|
|
192
|
-
|
|
193
|
-
program
|
|
194
|
-
.command('init')
|
|
195
|
-
.description('Initialize Go i18n structure')
|
|
196
|
-
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
197
|
-
.option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
|
|
198
|
-
.option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
|
|
199
|
-
.action(async (options) => {
|
|
200
|
-
try {
|
|
201
|
-
const manager = new GoI18nManager();
|
|
202
|
-
const languages = options.languages.split(',');
|
|
203
|
-
|
|
204
|
-
const localesDir = await manager.createLocaleStructure(options.outputDir, languages);
|
|
205
|
-
|
|
206
|
-
console.log(`✅ Go i18n structure initialized in: ${localesDir}`);
|
|
207
|
-
console.log(`📊 Languages: ${languages.join(', ')}`);
|
|
208
|
-
|
|
209
|
-
} catch (error) {
|
|
210
|
-
console.error('❌ Error initializing Go i18n:', error.message);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
program
|
|
216
|
-
.command('analyze')
|
|
217
|
-
.description('Analyze Go i18n usage')
|
|
218
|
-
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
219
|
-
.option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
|
|
220
|
-
.option('--dry-run', 'Preview without making changes')
|
|
221
|
-
.action(async (options) => {
|
|
222
|
-
try {
|
|
223
|
-
SecurityUtils.validatePath(options.sourceDir);
|
|
224
|
-
|
|
225
|
-
const manager = new GoI18nManager();
|
|
226
|
-
const results = await manager.extractTranslations(options.sourceDir);
|
|
227
|
-
|
|
228
|
-
console.log(`🔍 Framework detected: ${results.framework}`);
|
|
229
|
-
console.log(`📊 Files processed: ${results.files}`);
|
|
230
|
-
console.log(`📝 Translations found: ${results.translations.length}`);
|
|
231
|
-
|
|
232
|
-
if (!options.dryRun) {
|
|
233
|
-
const reportPath = await manager.generateReport(results, options.outputDir);
|
|
234
|
-
console.log(`📄 Report saved: ${reportPath}`);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
} catch (error) {
|
|
238
|
-
console.error('❌ Error analyzing Go i18n:', error.message);
|
|
239
|
-
process.exit(1);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
program
|
|
244
|
-
.command('extract')
|
|
245
|
-
.description('Extract Go translations')
|
|
246
|
-
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
247
|
-
.option('-o, --output-dir <dir>', 'Output directory', './locales')
|
|
248
|
-
.option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
|
|
249
|
-
.action(async (options) => {
|
|
250
|
-
try {
|
|
251
|
-
SecurityUtils.validatePath(options.sourceDir);
|
|
252
|
-
|
|
253
|
-
const manager = new GoI18nManager();
|
|
254
|
-
const results = await manager.extractTranslations(options.sourceDir);
|
|
255
|
-
|
|
256
|
-
await manager.createLocaleStructure(options.outputDir, options.languages.split(','));
|
|
257
|
-
|
|
258
|
-
console.log(`✅ Extracted ${results.translations.length} translations`);
|
|
259
|
-
console.log(`📁 Locale structure created in: ${options.outputDir}`);
|
|
260
|
-
|
|
261
|
-
} catch (error) {
|
|
262
|
-
console.error('❌ Error extracting Go translations:', error.message);
|
|
263
|
-
process.exit(1);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// Handle uncaught errors
|
|
268
|
-
process.on('uncaughtException', (error) => {
|
|
269
|
-
console.error('❌ Uncaught exception:', error.message);
|
|
270
|
-
process.exit(1);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
process.on('unhandledRejection', (reason) => {
|
|
274
|
-
console.error('❌ Unhandled rejection:', reason);
|
|
275
|
-
process.exit(1);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
// Export for programmatic use
|
|
279
|
-
module.exports = { GoI18nManager };
|
|
280
|
-
|
|
281
|
-
if (require.main === module) {
|
|
282
|
-
program.parse();
|
|
283
|
-
}
|
package/main/i18ntk-java.js
DELETED
|
@@ -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
|
-
}
|