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-php.js
DELETED
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* i18ntk-php.js - PHP Language I18n Management Command
|
|
5
|
-
*
|
|
6
|
-
* Supports:
|
|
7
|
-
* - Laravel i18n
|
|
8
|
-
* - Symfony translations
|
|
9
|
-
* - WordPress i18n
|
|
10
|
-
* - Standard PHP gettext
|
|
11
|
-
* - PHP array translations
|
|
12
|
-
* - JSON translations
|
|
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 PhpI18nManager {
|
|
34
|
-
constructor() {
|
|
35
|
-
this.supportedPatterns = [
|
|
36
|
-
'__(',
|
|
37
|
-
'_e(',
|
|
38
|
-
'_n(',
|
|
39
|
-
'_x(',
|
|
40
|
-
'_ex(',
|
|
41
|
-
'_nx(',
|
|
42
|
-
'trans(',
|
|
43
|
-
'trans_choice(',
|
|
44
|
-
'Lang::get(',
|
|
45
|
-
'Lang::choice(',
|
|
46
|
-
'$this->trans(',
|
|
47
|
-
'Yii::t(',
|
|
48
|
-
'$this->t(',
|
|
49
|
-
'dgettext(',
|
|
50
|
-
'dngettext(',
|
|
51
|
-
'dcgettext(',
|
|
52
|
-
'ngettext(',
|
|
53
|
-
'gettext('
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
this.fileExtensions = ['.php', '.blade.php', '.twig', '.phtml', '.module', '.theme'];
|
|
57
|
-
this.resourceFormats = ['.php', '.json', '.yml', '.yaml', '.po', '.mo'];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async detectFramework(sourceDir) {
|
|
61
|
-
// Check for Laravel
|
|
62
|
-
const composerJson = path.join(sourceDir, 'composer.json');
|
|
63
|
-
if (SecurityUtils.safeExistsSync(composerJson, sourceDir)) {
|
|
64
|
-
const content = SecurityUtils.safeReadFileSync(composerJson, sourceDir, 'utf8');
|
|
65
|
-
const composer = SecurityUtils.safeParseJSON(content);
|
|
66
|
-
|
|
67
|
-
if (composer.require && composer.require['laravel/framework']) {
|
|
68
|
-
return 'laravel';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (composer.require && composer.require['symfony/framework-bundle']) {
|
|
72
|
-
return 'symfony';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (composer.require && composer.require['yiisoft/yii2']) {
|
|
76
|
-
return 'yii2';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (composer.require && composer.require['wordpress/wordpress']) {
|
|
80
|
-
return 'wordpress';
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Check for WordPress
|
|
85
|
-
const wpConfig = path.join(sourceDir, 'wp-config.php');
|
|
86
|
-
if (SecurityUtils.safeExistsSync(wpConfig, sourceDir)) {
|
|
87
|
-
return 'wordpress';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Check for standard PHP
|
|
91
|
-
const phpFiles = this.findFiles(sourceDir, '.php');
|
|
92
|
-
if (phpFiles.length > 0) {
|
|
93
|
-
return 'standard-php';
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return 'generic';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async extractTranslations(sourceDir, options = {}) {
|
|
100
|
-
const framework = await this.detectFramework(sourceDir);
|
|
101
|
-
const translations = new Set();
|
|
102
|
-
|
|
103
|
-
// Process PHP files
|
|
104
|
-
const phpFiles = [
|
|
105
|
-
...this.findFiles(sourceDir, '.php'),
|
|
106
|
-
...this.findFiles(sourceDir, '.blade.php'),
|
|
107
|
-
...this.findFiles(sourceDir, '.twig'),
|
|
108
|
-
...this.findFiles(sourceDir, '.phtml')
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
for (const file of phpFiles) {
|
|
112
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
113
|
-
|
|
114
|
-
// Extract PHP i18n patterns
|
|
115
|
-
const patterns = [
|
|
116
|
-
/__\(\s*["']([^"']+)["']/g,
|
|
117
|
-
/_e\(\s*["']([^"']+)["']/g,
|
|
118
|
-
/_n\(\s*["']([^"']+)["']/g,
|
|
119
|
-
/_x\(\s*["']([^"']+)["']/g,
|
|
120
|
-
/trans\(\s*["']([^"']+)["']/g,
|
|
121
|
-
/trans_choice\(\s*["']([^"']+)["']/g,
|
|
122
|
-
/Lang::get\(\s*["']([^"']+)["']/g,
|
|
123
|
-
/Lang::choice\(\s*["']([^"']+)["']/g,
|
|
124
|
-
/Yii::t\(\s*["']([^"']+)["']/g,
|
|
125
|
-
/gettext\(\s*["']([^"']+)["']/g,
|
|
126
|
-
/dgettext\(\s*["']([^"']+)["']/g,
|
|
127
|
-
/ngettext\(\s*["']([^"']+)["']/g,
|
|
128
|
-
/dngettext\(\s*["']([^"']+)["']/g
|
|
129
|
-
];
|
|
130
|
-
|
|
131
|
-
for (const pattern of patterns) {
|
|
132
|
-
let match;
|
|
133
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
134
|
-
translations.add(match[1]);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Blade template patterns
|
|
139
|
-
const bladePatterns = [
|
|
140
|
-
/@lang\(['"]([^'"]+)['"]\)/g,
|
|
141
|
-
/@choice\(['"]([^'"]+)['"]\)/g,
|
|
142
|
-
/{{\s*__\(['"]([^'"]+)['"]\)\s*}}/g,
|
|
143
|
-
/{{\s*trans\(['"]([^'"]+)['"]\)\s*}}/g
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
for (const pattern of bladePatterns) {
|
|
147
|
-
let match;
|
|
148
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
149
|
-
translations.add(match[1]);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Process translation files
|
|
155
|
-
const translationFiles = [
|
|
156
|
-
...this.findFiles(sourceDir, '.php'),
|
|
157
|
-
...this.findFiles(sourceDir, '.json'),
|
|
158
|
-
...this.findFiles(sourceDir, '.yml'),
|
|
159
|
-
...this.findFiles(sourceDir, '.yaml')
|
|
160
|
-
];
|
|
161
|
-
|
|
162
|
-
for (const file of translationFiles) {
|
|
163
|
-
if (file.includes('lang') || file.includes('translations') || file.includes('i18n')) {
|
|
164
|
-
try {
|
|
165
|
-
const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
|
|
166
|
-
|
|
167
|
-
if (file.endsWith('.php')) {
|
|
168
|
-
// PHP array translations
|
|
169
|
-
const arrayPattern = /'([^']+)'\s*=>/g;
|
|
170
|
-
let match;
|
|
171
|
-
while ((match = arrayPattern.exec(content)) !== null) {
|
|
172
|
-
translations.add(match[1]);
|
|
173
|
-
}
|
|
174
|
-
} else if (file.endsWith('.json')) {
|
|
175
|
-
// JSON translations
|
|
176
|
-
const data = SecurityUtils.safeParseJSON(content);
|
|
177
|
-
const keys = this.extractKeysFromObject(data);
|
|
178
|
-
keys.forEach(key => translations.add(key));
|
|
179
|
-
} else if (file.endsWith('.yml') || file.endsWith('.yaml')) {
|
|
180
|
-
// YAML translations (basic parsing)
|
|
181
|
-
const lines = content.split('\n');
|
|
182
|
-
for (const line of lines) {
|
|
183
|
-
const trimmed = line.trim();
|
|
184
|
-
if (trimmed && !trimmed.startsWith('#') && trimmed.includes(':')) {
|
|
185
|
-
const key = trimmed.split(':')[0].trim();
|
|
186
|
-
if (!key.startsWith(' ')) {
|
|
187
|
-
translations.add(key);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
} catch (error) {
|
|
193
|
-
// Skip files that can't be parsed
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
framework,
|
|
200
|
-
translations: Array.from(translations),
|
|
201
|
-
files: phpFiles.length + translationFiles.length,
|
|
202
|
-
patterns: this.supportedPatterns
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
extractKeysFromObject(obj, prefix = '') {
|
|
207
|
-
const keys = [];
|
|
208
|
-
|
|
209
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
210
|
-
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
211
|
-
keys.push(fullKey);
|
|
212
|
-
|
|
213
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
214
|
-
keys.push(...this.extractKeysFromObject(value, fullKey));
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return keys;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async createLocaleStructure(outputDir, languages = ['en'], framework = 'standard-php') {
|
|
222
|
-
const localesDir = path.join(outputDir, 'locales');
|
|
223
|
-
|
|
224
|
-
for (const lang of languages) {
|
|
225
|
-
const langDir = path.join(localesDir, lang);
|
|
226
|
-
fs.mkdirSync(langDir, { recursive: true });
|
|
227
|
-
|
|
228
|
-
if (framework === 'laravel') {
|
|
229
|
-
// Laravel structure
|
|
230
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'messages.php'), `<?php
|
|
231
|
-
// Laravel i18n for ${lang}
|
|
232
|
-
return [
|
|
233
|
-
'hello' => 'Hello, World!',
|
|
234
|
-
'items' => [
|
|
235
|
-
'one' => ':count item',
|
|
236
|
-
'other' => ':count items'
|
|
237
|
-
],
|
|
238
|
-
'welcome' => 'Welcome to our application'
|
|
239
|
-
];
|
|
240
|
-
`, path.dirname(langDir));
|
|
241
|
-
|
|
242
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'validation.php'), `<?php
|
|
243
|
-
// Laravel validation messages for ${lang}
|
|
244
|
-
return [
|
|
245
|
-
'required' => 'The :attribute field is required.',
|
|
246
|
-
'email' => 'The :attribute must be a valid email address.',
|
|
247
|
-
'unique' => 'The :attribute has already been taken.'
|
|
248
|
-
];
|
|
249
|
-
`, path.dirname(localesDir));
|
|
250
|
-
} else if (framework === 'symfony') {
|
|
251
|
-
// Symfony structure
|
|
252
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'messages.yml'), `# Symfony i18n for ${lang}
|
|
253
|
-
hello: "Hello, World!"
|
|
254
|
-
items:
|
|
255
|
-
one: "%count% item"
|
|
256
|
-
other: "%count% items"
|
|
257
|
-
welcome: "Welcome to our application"
|
|
258
|
-
`, path.dirname(localesDir));
|
|
259
|
-
|
|
260
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'validators.yml'), `# Symfony validation for ${lang}
|
|
261
|
-
required: "The {{ field }} field is required."
|
|
262
|
-
email: "The {{ field }} must be a valid email address."
|
|
263
|
-
unique: "The {{ field }} has already been taken."
|
|
264
|
-
`, path.dirname(localesDir));
|
|
265
|
-
} else {
|
|
266
|
-
// Standard PHP
|
|
267
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'messages.php'), `<?php
|
|
268
|
-
// PHP i18n for ${lang}
|
|
269
|
-
return [
|
|
270
|
-
'hello' => 'Hello, World!',
|
|
271
|
-
'items' => [
|
|
272
|
-
'one' => '{0} item',
|
|
273
|
-
'other' => '{0} items'
|
|
274
|
-
],
|
|
275
|
-
'welcome' => 'Welcome to our application'
|
|
276
|
-
];
|
|
277
|
-
`, path.dirname(langDir));
|
|
278
|
-
|
|
279
|
-
SecurityUtils.safeWriteFileSync(path.join(langDir, 'messages.json'), JSON.stringify({
|
|
280
|
-
hello: "Hello, World!",
|
|
281
|
-
items: {
|
|
282
|
-
one: "{0} item",
|
|
283
|
-
other: "{0} items"
|
|
284
|
-
},
|
|
285
|
-
welcome: "Welcome to our application"
|
|
286
|
-
}, null, 2), path.dirname(langDir));
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return localesDir;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
findFiles(dir, extension) {
|
|
294
|
-
const files = [];
|
|
295
|
-
|
|
296
|
-
function traverse(currentDir) {
|
|
297
|
-
if (!SecurityUtils.safeExistsSync(currentDir, path.dirname(currentDir))) return;
|
|
298
|
-
|
|
299
|
-
const items = fs.readdirSync(currentDir);
|
|
300
|
-
|
|
301
|
-
for (const item of items) {
|
|
302
|
-
const fullPath = path.join(currentDir, item);
|
|
303
|
-
try {
|
|
304
|
-
const stat = fs.statSync(fullPath);
|
|
305
|
-
|
|
306
|
-
if (stat.isDirectory() && !item.startsWith('.') &&
|
|
307
|
-
!['node_modules', 'vendor', 'cache', 'storage'].includes(item)) {
|
|
308
|
-
traverse(fullPath);
|
|
309
|
-
} else if (stat.isFile() && path.extname(item) === extension) {
|
|
310
|
-
files.push(fullPath);
|
|
311
|
-
}
|
|
312
|
-
} catch (error) {
|
|
313
|
-
// Skip files we can't access
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
traverse(dir);
|
|
319
|
-
return files;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async generateReport(results, outputDir) {
|
|
323
|
-
const report = {
|
|
324
|
-
timestamp: new Date().toISOString(),
|
|
325
|
-
framework: results.framework,
|
|
326
|
-
totalTranslations: results.translations.length,
|
|
327
|
-
translations: results.translations,
|
|
328
|
-
filesProcessed: results.files,
|
|
329
|
-
patterns: results.patterns,
|
|
330
|
-
recommendations: this.getRecommendations(results)
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
const reportPath = path.join(outputDir, 'i18ntk-php-report.json');
|
|
334
|
-
SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2), outputDir);
|
|
335
|
-
|
|
336
|
-
return reportPath;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
getRecommendations(results) {
|
|
340
|
-
const recommendations = [];
|
|
341
|
-
|
|
342
|
-
if (results.translations.length === 0) {
|
|
343
|
-
recommendations.push('No translations found. Check your PHP i18n patterns');
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (results.files === 0) {
|
|
347
|
-
recommendations.push('No PHP files found in source directory');
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (results.framework === 'generic') {
|
|
351
|
-
recommendations.push('Consider using Laravel or Symfony for better i18n support');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (results.framework === 'wordpress') {
|
|
355
|
-
recommendations.push('Use WordPress i18n functions (__(), _e(), _n()) for consistency');
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return recommendations;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// CLI Implementation
|
|
363
|
-
program
|
|
364
|
-
.name('i18ntk-php')
|
|
365
|
-
.description('PHP language i18n management tool')
|
|
366
|
-
.version('1.10.1');
|
|
367
|
-
|
|
368
|
-
program
|
|
369
|
-
.command('init')
|
|
370
|
-
.description('Initialize PHP i18n structure')
|
|
371
|
-
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
372
|
-
.option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
|
|
373
|
-
.option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
|
|
374
|
-
.option('-f, --framework <framework>', 'Framework (laravel|symfony|wordpress|standard)', 'standard-php')
|
|
375
|
-
.action(async (options) => {
|
|
376
|
-
try {
|
|
377
|
-
const manager = new PhpI18nManager();
|
|
378
|
-
const languages = options.languages.split(',');
|
|
379
|
-
|
|
380
|
-
const localesDir = await manager.createLocaleStructure(options.outputDir, languages, options.framework);
|
|
381
|
-
|
|
382
|
-
console.log(`✅ PHP i18n structure initialized in: ${localesDir}`);
|
|
383
|
-
console.log(`📊 Languages: ${languages.join(', ')}`);
|
|
384
|
-
console.log(`🎯 Framework: ${options.framework}`);
|
|
385
|
-
|
|
386
|
-
} catch (error) {
|
|
387
|
-
console.error('❌ Error initializing PHP i18n:', error.message);
|
|
388
|
-
process.exit(1);
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
program
|
|
393
|
-
.command('analyze')
|
|
394
|
-
.description('Analyze PHP i18n usage')
|
|
395
|
-
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
396
|
-
.option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
|
|
397
|
-
.option('--dry-run', 'Preview without making changes')
|
|
398
|
-
.action(async (options) => {
|
|
399
|
-
try {
|
|
400
|
-
SecurityUtils.validatePath(options.sourceDir);
|
|
401
|
-
|
|
402
|
-
const manager = new PhpI18nManager();
|
|
403
|
-
const results = await manager.extractTranslations(options.sourceDir);
|
|
404
|
-
|
|
405
|
-
console.log(`🔍 Framework detected: ${results.framework}`);
|
|
406
|
-
console.log(`📊 Files processed: ${results.files}`);
|
|
407
|
-
console.log(`📝 Translations found: ${results.translations.length}`);
|
|
408
|
-
|
|
409
|
-
if (!options.dryRun) {
|
|
410
|
-
const reportPath = await manager.generateReport(results, options.outputDir);
|
|
411
|
-
console.log(`📄 Report saved: ${reportPath}`);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
} catch (error) {
|
|
415
|
-
console.error('❌ Error analyzing PHP i18n:', error.message);
|
|
416
|
-
process.exit(1);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
program
|
|
421
|
-
.command('extract')
|
|
422
|
-
.description('Extract PHP translations')
|
|
423
|
-
.option('-s, --source-dir <dir>', 'Source directory', './')
|
|
424
|
-
.option('-o, --output-dir <dir>', 'Output directory', './locales')
|
|
425
|
-
.option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
|
|
426
|
-
.option('-f, --framework <framework>', 'Framework (laravel|symfony|wordpress|standard)', 'standard-php')
|
|
427
|
-
.action(async (options) => {
|
|
428
|
-
try {
|
|
429
|
-
SecurityUtils.validatePath(options.sourceDir);
|
|
430
|
-
|
|
431
|
-
const manager = new PhpI18nManager();
|
|
432
|
-
const results = await manager.extractTranslations(options.sourceDir);
|
|
433
|
-
|
|
434
|
-
await manager.createLocaleStructure(options.outputDir, options.languages.split(','), options.framework);
|
|
435
|
-
|
|
436
|
-
console.log(`✅ Extracted ${results.translations.length} translations`);
|
|
437
|
-
console.log(`📁 Locale structure created in: ${options.outputDir}`);
|
|
438
|
-
console.log(`🎯 Framework: ${options.framework}`);
|
|
439
|
-
|
|
440
|
-
} catch (error) {
|
|
441
|
-
console.error('❌ Error extracting PHP translations:', error.message);
|
|
442
|
-
process.exit(1);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
// Handle uncaught errors
|
|
447
|
-
process.on('uncaughtException', (error) => {
|
|
448
|
-
console.error('❌ Uncaught exception:', error.message);
|
|
449
|
-
process.exit(1);
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
process.on('unhandledRejection', (reason) => {
|
|
453
|
-
console.error('❌ Unhandled rejection:', reason);
|
|
454
|
-
process.exit(1);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Export for programmatic use
|
|
458
|
-
module.exports = { PhpI18nManager };
|
|
459
|
-
|
|
460
|
-
if (require.main === module) {
|
|
461
|
-
program.parse();
|
|
462
|
-
}
|