i18ntk 1.0.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/CHANGELOG.md +401 -0
- package/LICENSE +21 -0
- package/README.md +507 -0
- package/dev/README.md +37 -0
- package/dev/debug/README.md +30 -0
- package/dev/debug/complete-console-translations.js +295 -0
- package/dev/debug/console-key-checker.js +408 -0
- package/dev/debug/console-translations.js +335 -0
- package/dev/debug/debugger.js +408 -0
- package/dev/debug/export-missing-keys.js +432 -0
- package/dev/debug/final-normalize.js +236 -0
- package/dev/debug/find-extra-keys.js +68 -0
- package/dev/debug/normalize-locales.js +153 -0
- package/dev/debug/refactor-locales.js +240 -0
- package/dev/debug/reorder-locales.js +85 -0
- package/dev/debug/replace-hardcoded-console.js +378 -0
- package/docs/INSTALLATION.md +449 -0
- package/docs/README.md +222 -0
- package/docs/TODO_ROADMAP.md +279 -0
- package/docs/api/API_REFERENCE.md +377 -0
- package/docs/api/COMPONENTS.md +492 -0
- package/docs/api/CONFIGURATION.md +651 -0
- package/docs/api/NPM_PUBLISHING_GUIDE.md +434 -0
- package/docs/debug/DEBUG_README.md +30 -0
- package/docs/debug/DEBUG_TOOLS.md +494 -0
- package/docs/development/AGENTS.md +351 -0
- package/docs/development/DEVELOPMENT_RULES.md +165 -0
- package/docs/development/DEV_README.md +37 -0
- package/docs/release-notes/RELEASE_NOTES_v1.0.0.md +173 -0
- package/docs/release-notes/RELEASE_NOTES_v1.6.0.md +141 -0
- package/docs/release-notes/RELEASE_NOTES_v1.6.1.md +185 -0
- package/docs/release-notes/RELEASE_NOTES_v1.6.3.md +199 -0
- package/docs/reports/ANALYSIS_README.md +17 -0
- package/docs/reports/CONSOLE_MISMATCH_BUG_REPORT_v1.5.0.md +181 -0
- package/docs/reports/SIZING_README.md +18 -0
- package/docs/reports/SUMMARY_README.md +18 -0
- package/docs/reports/TRANSLATION_BUG_REPORT_v1.5.0.md +129 -0
- package/docs/reports/USAGE_README.md +18 -0
- package/docs/reports/VALIDATION_README.md +18 -0
- package/locales/de/auth.json +3 -0
- package/locales/de/common.json +16 -0
- package/locales/de/pagination.json +6 -0
- package/locales/en/auth.json +3 -0
- package/locales/en/common.json +16 -0
- package/locales/en/pagination.json +6 -0
- package/locales/es/auth.json +3 -0
- package/locales/es/common.json +16 -0
- package/locales/es/pagination.json +6 -0
- package/locales/fr/auth.json +3 -0
- package/locales/fr/common.json +16 -0
- package/locales/fr/pagination.json +6 -0
- package/locales/ru/auth.json +3 -0
- package/locales/ru/common.json +16 -0
- package/locales/ru/pagination.json +6 -0
- package/main/i18ntk-analyze.js +625 -0
- package/main/i18ntk-autorun.js +461 -0
- package/main/i18ntk-complete.js +494 -0
- package/main/i18ntk-init.js +686 -0
- package/main/i18ntk-manage.js +848 -0
- package/main/i18ntk-sizing.js +557 -0
- package/main/i18ntk-summary.js +671 -0
- package/main/i18ntk-usage.js +1282 -0
- package/main/i18ntk-validate.js +762 -0
- package/main/ui-i18n.js +332 -0
- package/package.json +152 -0
- package/scripts/fix-missing-translation-keys.js +214 -0
- package/scripts/verify-package.js +168 -0
- package/ui-locales/de.json +637 -0
- package/ui-locales/en.json +688 -0
- package/ui-locales/es.json +637 -0
- package/ui-locales/fr.json +637 -0
- package/ui-locales/ja.json +637 -0
- package/ui-locales/ru.json +637 -0
- package/ui-locales/zh.json +637 -0
- package/utils/admin-auth.js +317 -0
- package/utils/admin-cli.js +353 -0
- package/utils/admin-pin.js +409 -0
- package/utils/detect-language-mismatches.js +454 -0
- package/utils/i18n-helper.js +128 -0
- package/utils/maintain-language-purity.js +433 -0
- package/utils/native-translations.js +478 -0
- package/utils/security.js +384 -0
- package/utils/test-complete-system.js +356 -0
- package/utils/test-console-i18n.js +402 -0
- package/utils/translate-mismatches.js +571 -0
- package/utils/validate-language-purity.js +531 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translation Helper Tool
|
|
5
|
+
*
|
|
6
|
+
* This script helps translate English content in foreign language files
|
|
7
|
+
* by providing translation suggestions and batch processing capabilities.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
class TranslationHelper {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.localesDir = path.join(__dirname, 'ui-locales');
|
|
16
|
+
this.translationMappings = {
|
|
17
|
+
de: {
|
|
18
|
+
// Common UI terms
|
|
19
|
+
'Debug Tools': 'Debug-Tools',
|
|
20
|
+
'Settings': 'Einstellungen',
|
|
21
|
+
'Configuration': 'Konfiguration',
|
|
22
|
+
'Options': 'Optionen',
|
|
23
|
+
'Menu': 'Menü',
|
|
24
|
+
'Back': 'Zurück',
|
|
25
|
+
'Main Menu': 'Hauptmenü',
|
|
26
|
+
'Back to Main Menu': 'Zurück zum Hauptmenü',
|
|
27
|
+
'Invalid choice': 'Ungültige Auswahl',
|
|
28
|
+
'Please select': 'Bitte wählen Sie',
|
|
29
|
+
'Error': 'Fehler',
|
|
30
|
+
'Warning': 'Warnung',
|
|
31
|
+
'Success': 'Erfolg',
|
|
32
|
+
'Failed': 'Fehlgeschlagen',
|
|
33
|
+
'Loading': 'Laden',
|
|
34
|
+
'Saving': 'Speichern',
|
|
35
|
+
'Delete': 'Löschen',
|
|
36
|
+
'Create': 'Erstellen',
|
|
37
|
+
'Update': 'Aktualisieren',
|
|
38
|
+
'Add': 'Hinzufügen',
|
|
39
|
+
'Remove': 'Entfernen',
|
|
40
|
+
'Edit': 'Bearbeiten',
|
|
41
|
+
'View': 'Anzeigen',
|
|
42
|
+
'Show': 'Zeigen',
|
|
43
|
+
'Hide': 'Verstecken',
|
|
44
|
+
'Open': 'Öffnen',
|
|
45
|
+
'Close': 'Schließen',
|
|
46
|
+
'Start': 'Starten',
|
|
47
|
+
'Stop': 'Stoppen',
|
|
48
|
+
'Run': 'Ausführen',
|
|
49
|
+
'Execute': 'Ausführen',
|
|
50
|
+
'Process': 'Verarbeiten',
|
|
51
|
+
'Generate': 'Generieren',
|
|
52
|
+
'Analysis': 'Analyse',
|
|
53
|
+
'Report': 'Bericht',
|
|
54
|
+
'Summary': 'Zusammenfassung',
|
|
55
|
+
'Details': 'Details',
|
|
56
|
+
'Tools': 'Werkzeuge',
|
|
57
|
+
'Debug': 'Debug',
|
|
58
|
+
'Test': 'Test',
|
|
59
|
+
'Validate': 'Validieren',
|
|
60
|
+
'Check': 'Prüfen',
|
|
61
|
+
'Verify': 'Überprüfen',
|
|
62
|
+
'Confirm': 'Bestätigen',
|
|
63
|
+
'Cancel': 'Abbrechen',
|
|
64
|
+
'Continue': 'Fortfahren',
|
|
65
|
+
'Finish': 'Beenden',
|
|
66
|
+
'Complete': 'Vollständig',
|
|
67
|
+
'File': 'Datei',
|
|
68
|
+
'Files': 'Dateien',
|
|
69
|
+
'Found': 'Gefunden',
|
|
70
|
+
'Not found': 'Nicht gefunden',
|
|
71
|
+
'Available': 'Verfügbar',
|
|
72
|
+
'Not available': 'Nicht verfügbar',
|
|
73
|
+
'Issues found': 'Probleme gefunden',
|
|
74
|
+
'No issues found': 'Keine Probleme gefunden',
|
|
75
|
+
'Warnings found': 'Warnungen gefunden',
|
|
76
|
+
|
|
77
|
+
// Debug-specific terms
|
|
78
|
+
'Full System Debug': 'Vollständiges System-Debug',
|
|
79
|
+
'Configuration Debug': 'Konfigurations-Debug',
|
|
80
|
+
'Translation Debug': 'Übersetzungs-Debug',
|
|
81
|
+
'Performance Debug': 'Leistungs-Debug',
|
|
82
|
+
|
|
83
|
+
// Operations
|
|
84
|
+
'INITIALIZING I18N': 'I18N INITIALISIEREN',
|
|
85
|
+
'ANALYZING TRANSLATIONS': 'ÜBERSETZUNGEN ANALYSIEREN',
|
|
86
|
+
'VALIDATING TRANSLATIONS': 'ÜBERSETZUNGEN VALIDIEREN',
|
|
87
|
+
'ANALYZING KEY USAGE': 'SCHLÜSSEL-VERWENDUNG ANALYSIEREN',
|
|
88
|
+
'COMPLETING TRANSLATIONS': 'ÜBERSETZUNGEN VERVOLLSTÄNDIGEN',
|
|
89
|
+
'ANALYZING TRANSLATION SIZES': 'ÜBERSETZUNGSGRÖSSEN ANALYSIEREN',
|
|
90
|
+
'RUNNING COMPREHENSIVE I18N WORKFLOW': 'UMFASSENDEN I18N-WORKFLOW AUSFÜHREN',
|
|
91
|
+
|
|
92
|
+
// Admin terms
|
|
93
|
+
'Admin PIN Setup': 'Admin-PIN-Einrichtung',
|
|
94
|
+
'Enter Admin PIN': 'Admin-PIN eingeben',
|
|
95
|
+
'Confirm Admin PIN': 'Admin-PIN bestätigen',
|
|
96
|
+
'PINs do not match': 'PINs stimmen nicht überein',
|
|
97
|
+
'Authentication failed': 'Authentifizierung fehlgeschlagen',
|
|
98
|
+
'Access denied': 'Zugriff verweigert',
|
|
99
|
+
'Session authenticated': 'Sitzung authentifiziert',
|
|
100
|
+
'Security log': 'Sicherheitsprotokoll',
|
|
101
|
+
|
|
102
|
+
// Common phrases
|
|
103
|
+
'Please try again': 'Bitte versuchen Sie es erneut',
|
|
104
|
+
'Would you like': 'Möchten Sie',
|
|
105
|
+
'Select option': 'Option auswählen',
|
|
106
|
+
'try again': 'erneut versuchen',
|
|
107
|
+
'choice': 'Auswahl',
|
|
108
|
+
'option': 'Option',
|
|
109
|
+
'issue(s)': 'Problem(e)',
|
|
110
|
+
'warning(s)': 'Warnung(en)',
|
|
111
|
+
'found': 'gefunden',
|
|
112
|
+
'not': 'nicht'
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
fr: {
|
|
116
|
+
// Common UI terms
|
|
117
|
+
'Debug Tools': 'Outils de débogage',
|
|
118
|
+
'Settings': 'Paramètres',
|
|
119
|
+
'Configuration': 'Configuration',
|
|
120
|
+
'Options': 'Options',
|
|
121
|
+
'Menu': 'Menu',
|
|
122
|
+
'Back': 'Retour',
|
|
123
|
+
'Main Menu': 'Menu principal',
|
|
124
|
+
'Back to Main Menu': 'Retour au menu principal',
|
|
125
|
+
'Invalid choice': 'Choix invalide',
|
|
126
|
+
'Please select': 'Veuillez sélectionner',
|
|
127
|
+
'Error': 'Erreur',
|
|
128
|
+
'Warning': 'Avertissement',
|
|
129
|
+
'Success': 'Succès',
|
|
130
|
+
'Failed': 'Échec',
|
|
131
|
+
'Loading': 'Chargement',
|
|
132
|
+
'Saving': 'Sauvegarde',
|
|
133
|
+
'Delete': 'Supprimer',
|
|
134
|
+
'Create': 'Créer',
|
|
135
|
+
'Update': 'Mettre à jour',
|
|
136
|
+
'Add': 'Ajouter',
|
|
137
|
+
'Remove': 'Supprimer',
|
|
138
|
+
'Edit': 'Modifier',
|
|
139
|
+
'View': 'Voir',
|
|
140
|
+
'Show': 'Afficher',
|
|
141
|
+
'Hide': 'Masquer',
|
|
142
|
+
'Open': 'Ouvrir',
|
|
143
|
+
'Close': 'Fermer',
|
|
144
|
+
'Start': 'Démarrer',
|
|
145
|
+
'Stop': 'Arrêter',
|
|
146
|
+
'Run': 'Exécuter',
|
|
147
|
+
'Execute': 'Exécuter',
|
|
148
|
+
'Process': 'Traiter',
|
|
149
|
+
'Generate': 'Générer',
|
|
150
|
+
'Analysis': 'Analyse',
|
|
151
|
+
'Report': 'Rapport',
|
|
152
|
+
'Summary': 'Résumé',
|
|
153
|
+
'Details': 'Détails',
|
|
154
|
+
'Tools': 'Outils',
|
|
155
|
+
'Debug': 'Débogage',
|
|
156
|
+
'Test': 'Test',
|
|
157
|
+
'Validate': 'Valider',
|
|
158
|
+
'Check': 'Vérifier',
|
|
159
|
+
'Verify': 'Vérifier',
|
|
160
|
+
'Confirm': 'Confirmer',
|
|
161
|
+
'Cancel': 'Annuler',
|
|
162
|
+
'Continue': 'Continuer',
|
|
163
|
+
'Finish': 'Terminer',
|
|
164
|
+
'Complete': 'Complet',
|
|
165
|
+
'File': 'Fichier',
|
|
166
|
+
'Files': 'Fichiers',
|
|
167
|
+
'Found': 'Trouvé',
|
|
168
|
+
'Not found': 'Non trouvé',
|
|
169
|
+
'Available': 'Disponible',
|
|
170
|
+
'Not available': 'Non disponible',
|
|
171
|
+
'Issues found': 'Problèmes trouvés',
|
|
172
|
+
'No issues found': 'Aucun problème trouvé',
|
|
173
|
+
'Warnings found': 'Avertissements trouvés',
|
|
174
|
+
|
|
175
|
+
// Debug-specific terms
|
|
176
|
+
'Full System Debug': 'Débogage système complet',
|
|
177
|
+
'Configuration Debug': 'Débogage de configuration',
|
|
178
|
+
'Translation Debug': 'Débogage de traduction',
|
|
179
|
+
'Performance Debug': 'Débogage de performance',
|
|
180
|
+
|
|
181
|
+
// Operations
|
|
182
|
+
'INITIALIZING I18N': 'INITIALISATION I18N',
|
|
183
|
+
'ANALYZING TRANSLATIONS': 'ANALYSE DES TRADUCTIONS',
|
|
184
|
+
'VALIDATING TRANSLATIONS': 'VALIDATION DES TRADUCTIONS',
|
|
185
|
+
'ANALYZING KEY USAGE': 'ANALYSE DE L\'UTILISATION DES CLÉS',
|
|
186
|
+
'COMPLETING TRANSLATIONS': 'FINALISATION DES TRADUCTIONS',
|
|
187
|
+
'ANALYZING TRANSLATION SIZES': 'ANALYSE DES TAILLES DE TRADUCTION',
|
|
188
|
+
'RUNNING COMPREHENSIVE I18N WORKFLOW': 'EXÉCUTION DU WORKFLOW I18N COMPLET',
|
|
189
|
+
|
|
190
|
+
// Admin terms
|
|
191
|
+
'Admin PIN Setup': 'Configuration du PIN administrateur',
|
|
192
|
+
'Enter Admin PIN': 'Entrer le PIN administrateur',
|
|
193
|
+
'Confirm Admin PIN': 'Confirmer le PIN administrateur',
|
|
194
|
+
'PINs do not match': 'Les PINs ne correspondent pas',
|
|
195
|
+
'Authentication failed': 'Échec de l\'authentification',
|
|
196
|
+
'Access denied': 'Accès refusé',
|
|
197
|
+
'Session authenticated': 'Session authentifiée',
|
|
198
|
+
'Security log': 'Journal de sécurité',
|
|
199
|
+
|
|
200
|
+
// Common phrases
|
|
201
|
+
'Please try again': 'Veuillez réessayer',
|
|
202
|
+
'Would you like': 'Souhaitez-vous',
|
|
203
|
+
'Select option': 'Sélectionner une option',
|
|
204
|
+
'try again': 'réessayer',
|
|
205
|
+
'choice': 'choix',
|
|
206
|
+
'option': 'option',
|
|
207
|
+
'issue(s)': 'problème(s)',
|
|
208
|
+
'warning(s)': 'avertissement(s)',
|
|
209
|
+
'found': 'trouvé',
|
|
210
|
+
'not': 'ne pas'
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
es: {
|
|
214
|
+
// Common UI terms
|
|
215
|
+
'Debug Tools': 'Herramientas de depuración',
|
|
216
|
+
'Settings': 'Configuración',
|
|
217
|
+
'Configuration': 'Configuración',
|
|
218
|
+
'Options': 'Opciones',
|
|
219
|
+
'Menu': 'Menú',
|
|
220
|
+
'Back': 'Atrás',
|
|
221
|
+
'Main Menu': 'Menú principal',
|
|
222
|
+
'Back to Main Menu': 'Volver al menú principal',
|
|
223
|
+
'Invalid choice': 'Opción inválida',
|
|
224
|
+
'Please select': 'Por favor seleccione',
|
|
225
|
+
'Error': 'Error',
|
|
226
|
+
'Warning': 'Advertencia',
|
|
227
|
+
'Success': 'Éxito',
|
|
228
|
+
'Failed': 'Falló',
|
|
229
|
+
'Loading': 'Cargando',
|
|
230
|
+
'Saving': 'Guardando',
|
|
231
|
+
'Delete': 'Eliminar',
|
|
232
|
+
'Create': 'Crear',
|
|
233
|
+
'Update': 'Actualizar',
|
|
234
|
+
'Add': 'Agregar',
|
|
235
|
+
'Remove': 'Quitar',
|
|
236
|
+
'Edit': 'Editar',
|
|
237
|
+
'View': 'Ver',
|
|
238
|
+
'Show': 'Mostrar',
|
|
239
|
+
'Hide': 'Ocultar',
|
|
240
|
+
'Open': 'Abrir',
|
|
241
|
+
'Close': 'Cerrar',
|
|
242
|
+
'Start': 'Iniciar',
|
|
243
|
+
'Stop': 'Detener',
|
|
244
|
+
'Run': 'Ejecutar',
|
|
245
|
+
'Execute': 'Ejecutar',
|
|
246
|
+
'Process': 'Procesar',
|
|
247
|
+
'Generate': 'Generar',
|
|
248
|
+
'Analysis': 'Análisis',
|
|
249
|
+
'Report': 'Informe',
|
|
250
|
+
'Summary': 'Resumen',
|
|
251
|
+
'Details': 'Detalles',
|
|
252
|
+
'Tools': 'Herramientas',
|
|
253
|
+
'Debug': 'Depurar',
|
|
254
|
+
'Test': 'Prueba',
|
|
255
|
+
'Validate': 'Validar',
|
|
256
|
+
'Check': 'Verificar',
|
|
257
|
+
'Verify': 'Verificar',
|
|
258
|
+
'Confirm': 'Confirmar',
|
|
259
|
+
'Cancel': 'Cancelar',
|
|
260
|
+
'Continue': 'Continuar',
|
|
261
|
+
'Finish': 'Finalizar',
|
|
262
|
+
'Complete': 'Completo',
|
|
263
|
+
'File': 'Archivo',
|
|
264
|
+
'Files': 'Archivos',
|
|
265
|
+
'Found': 'Encontrado',
|
|
266
|
+
'Not found': 'No encontrado',
|
|
267
|
+
'Available': 'Disponible',
|
|
268
|
+
'Not available': 'No disponible',
|
|
269
|
+
'Issues found': 'Problemas encontrados',
|
|
270
|
+
'No issues found': 'No se encontraron problemas',
|
|
271
|
+
'Warnings found': 'Advertencias encontradas',
|
|
272
|
+
|
|
273
|
+
// Debug-specific terms
|
|
274
|
+
'Full System Debug': 'Depuración completa del sistema',
|
|
275
|
+
'Configuration Debug': 'Depuración de configuración',
|
|
276
|
+
'Translation Debug': 'Depuración de traducción',
|
|
277
|
+
'Performance Debug': 'Depuración de rendimiento',
|
|
278
|
+
|
|
279
|
+
// Operations
|
|
280
|
+
'INITIALIZING I18N': 'INICIALIZANDO I18N',
|
|
281
|
+
'ANALYZING TRANSLATIONS': 'ANALIZANDO TRADUCCIONES',
|
|
282
|
+
'VALIDATING TRANSLATIONS': 'VALIDANDO TRADUCCIONES',
|
|
283
|
+
'ANALYZING KEY USAGE': 'ANALIZANDO USO DE CLAVES',
|
|
284
|
+
'COMPLETING TRANSLATIONS': 'COMPLETANDO TRADUCCIONES',
|
|
285
|
+
'ANALYZING TRANSLATION SIZES': 'ANALIZANDO TAMAÑOS DE TRADUCCIÓN',
|
|
286
|
+
'RUNNING COMPREHENSIVE I18N WORKFLOW': 'EJECUTANDO FLUJO DE TRABAJO I18N INTEGRAL',
|
|
287
|
+
|
|
288
|
+
// Admin terms
|
|
289
|
+
'Admin PIN Setup': 'Configuración de PIN de administrador',
|
|
290
|
+
'Enter Admin PIN': 'Ingrese PIN de administrador',
|
|
291
|
+
'Confirm Admin PIN': 'Confirme PIN de administrador',
|
|
292
|
+
'PINs do not match': 'Los PINs no coinciden',
|
|
293
|
+
'Authentication failed': 'Falló la autenticación',
|
|
294
|
+
'Access denied': 'Acceso denegado',
|
|
295
|
+
'Session authenticated': 'Sesión autenticada',
|
|
296
|
+
'Security log': 'Registro de seguridad',
|
|
297
|
+
|
|
298
|
+
// Common phrases
|
|
299
|
+
'Please try again': 'Por favor intente de nuevo',
|
|
300
|
+
'Would you like': '¿Le gustaría',
|
|
301
|
+
'Select option': 'Seleccionar opción',
|
|
302
|
+
'try again': 'intentar de nuevo',
|
|
303
|
+
'choice': 'opción',
|
|
304
|
+
'option': 'opción',
|
|
305
|
+
'issue(s)': 'problema(s)',
|
|
306
|
+
'warning(s)': 'advertencia(s)',
|
|
307
|
+
'found': 'encontrado',
|
|
308
|
+
'not': 'no'
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Translate English content in all locale files
|
|
315
|
+
*/
|
|
316
|
+
async translateAll(dryRun = true) {
|
|
317
|
+
console.log(`🌐 Translation Helper ${dryRun ? '(DRY RUN)' : '(LIVE)'}`);
|
|
318
|
+
console.log('=====================================\n');
|
|
319
|
+
|
|
320
|
+
const localeFiles = this.getLocaleFiles();
|
|
321
|
+
let totalTranslations = 0;
|
|
322
|
+
|
|
323
|
+
for (const file of localeFiles) {
|
|
324
|
+
if (file.language === 'en') {
|
|
325
|
+
continue; // Skip source language
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
console.log(`📄 Processing ${file.language}.json...`);
|
|
329
|
+
const translations = await this.translateFile(file, dryRun);
|
|
330
|
+
totalTranslations += translations;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log(`\n📊 Translation Summary:`);
|
|
334
|
+
console.log(` Total translations applied: ${totalTranslations}`);
|
|
335
|
+
|
|
336
|
+
if (dryRun && totalTranslations > 0) {
|
|
337
|
+
console.log('\n💡 Run with --apply to apply these translations\n');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Get all locale files
|
|
343
|
+
*/
|
|
344
|
+
getLocaleFiles() {
|
|
345
|
+
return fs.readdirSync(this.localesDir)
|
|
346
|
+
.filter(file => file.endsWith('.json'))
|
|
347
|
+
.map(file => ({
|
|
348
|
+
filename: file,
|
|
349
|
+
language: path.basename(file, '.json'),
|
|
350
|
+
path: path.join(this.localesDir, file)
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Translate a single file
|
|
356
|
+
*/
|
|
357
|
+
async translateFile(file, dryRun = true) {
|
|
358
|
+
try {
|
|
359
|
+
const content = fs.readFileSync(file.path, 'utf8');
|
|
360
|
+
let translations = JSON.parse(content);
|
|
361
|
+
let translationCount = 0;
|
|
362
|
+
|
|
363
|
+
const mappings = this.translationMappings[file.language];
|
|
364
|
+
if (!mappings) {
|
|
365
|
+
console.log(` ⚠️ No translation mappings available for ${file.language}`);
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Apply translations
|
|
370
|
+
this.translateObject(translations, '', mappings, (key, oldValue, newValue) => {
|
|
371
|
+
if (!dryRun) {
|
|
372
|
+
console.log(` ✅ ${key}: "${oldValue}" → "${newValue}"`);
|
|
373
|
+
}
|
|
374
|
+
translationCount++;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
if (translationCount > 0 && !dryRun) {
|
|
378
|
+
fs.writeFileSync(file.path, JSON.stringify(translations, null, 2));
|
|
379
|
+
console.log(` 💾 Saved ${translationCount} translations to ${file.filename}`);
|
|
380
|
+
} else if (translationCount > 0) {
|
|
381
|
+
console.log(` 🔍 Found ${translationCount} translatable items`);
|
|
382
|
+
} else {
|
|
383
|
+
console.log(` ✨ No automatic translations available`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return translationCount;
|
|
387
|
+
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error(` ❌ Error processing ${file.filename}: ${error.message}`);
|
|
390
|
+
return 0;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Recursively translate object properties
|
|
396
|
+
*/
|
|
397
|
+
translateObject(obj, keyPath, mappings, callback) {
|
|
398
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
399
|
+
const currentPath = keyPath ? `${keyPath}.${key}` : key;
|
|
400
|
+
|
|
401
|
+
if (typeof value === 'string') {
|
|
402
|
+
const translated = this.translateString(value, mappings);
|
|
403
|
+
if (translated !== value) {
|
|
404
|
+
obj[key] = translated;
|
|
405
|
+
callback(currentPath, value, translated);
|
|
406
|
+
}
|
|
407
|
+
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
408
|
+
this.translateObject(value, currentPath, mappings, callback);
|
|
409
|
+
} else if (Array.isArray(value)) {
|
|
410
|
+
value.forEach((item, index) => {
|
|
411
|
+
if (typeof item === 'string') {
|
|
412
|
+
const translated = this.translateString(item, mappings);
|
|
413
|
+
if (translated !== item) {
|
|
414
|
+
value[index] = translated;
|
|
415
|
+
callback(`${currentPath}[${index}]`, item, translated);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Translate a single string
|
|
425
|
+
*/
|
|
426
|
+
translateString(text, mappings) {
|
|
427
|
+
let translated = text;
|
|
428
|
+
|
|
429
|
+
// Remove [NOT TRANSLATED] markers
|
|
430
|
+
translated = translated.replace(/\[NOT TRANSLATED\]\s*/g, '');
|
|
431
|
+
|
|
432
|
+
// Apply direct mappings
|
|
433
|
+
for (const [english, foreign] of Object.entries(mappings)) {
|
|
434
|
+
// Case-insensitive replacement
|
|
435
|
+
const regex = new RegExp(english.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
436
|
+
translated = translated.replace(regex, foreign);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return translated;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Generate translation report
|
|
444
|
+
*/
|
|
445
|
+
async generateReport() {
|
|
446
|
+
console.log('📊 Translation Coverage Report');
|
|
447
|
+
console.log('===============================\n');
|
|
448
|
+
|
|
449
|
+
const localeFiles = this.getLocaleFiles();
|
|
450
|
+
|
|
451
|
+
for (const file of localeFiles) {
|
|
452
|
+
if (file.language === 'en') continue;
|
|
453
|
+
|
|
454
|
+
console.log(`📄 ${file.filename}:`);
|
|
455
|
+
|
|
456
|
+
const content = fs.readFileSync(file.path, 'utf8');
|
|
457
|
+
const translations = JSON.parse(content);
|
|
458
|
+
|
|
459
|
+
const stats = {
|
|
460
|
+
total: 0,
|
|
461
|
+
translated: 0,
|
|
462
|
+
untranslated: 0,
|
|
463
|
+
hasEnglish: 0
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
this.analyzeTranslations(translations, stats);
|
|
467
|
+
|
|
468
|
+
const coverage = ((stats.translated / stats.total) * 100).toFixed(1);
|
|
469
|
+
|
|
470
|
+
console.log(` Total keys: ${stats.total}`);
|
|
471
|
+
console.log(` Translated: ${stats.translated}`);
|
|
472
|
+
console.log(` Untranslated markers: ${stats.untranslated}`);
|
|
473
|
+
console.log(` Contains English: ${stats.hasEnglish}`);
|
|
474
|
+
console.log(` Coverage: ${coverage}%\n`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Analyze translation statistics
|
|
480
|
+
*/
|
|
481
|
+
analyzeTranslations(obj, stats) {
|
|
482
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
483
|
+
if (typeof value === 'string') {
|
|
484
|
+
stats.total++;
|
|
485
|
+
|
|
486
|
+
if (value.includes('[TRANSLATE]') || value.includes('[NOT TRANSLATED]')) {
|
|
487
|
+
stats.untranslated++;
|
|
488
|
+
} else if (this.containsEnglish(value)) {
|
|
489
|
+
stats.hasEnglish++;
|
|
490
|
+
} else {
|
|
491
|
+
stats.translated++;
|
|
492
|
+
}
|
|
493
|
+
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
494
|
+
this.analyzeTranslations(value, stats);
|
|
495
|
+
} else if (Array.isArray(value)) {
|
|
496
|
+
value.forEach(item => {
|
|
497
|
+
if (typeof item === 'string') {
|
|
498
|
+
stats.total++;
|
|
499
|
+
|
|
500
|
+
if (item.includes('[TRANSLATE]') || item.includes('[NOT TRANSLATED]')) {
|
|
501
|
+
stats.untranslated++;
|
|
502
|
+
} else if (this.containsEnglish(item)) {
|
|
503
|
+
stats.hasEnglish++;
|
|
504
|
+
} else {
|
|
505
|
+
stats.translated++;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Check if text contains English
|
|
515
|
+
*/
|
|
516
|
+
containsEnglish(text) {
|
|
517
|
+
const englishWords = [
|
|
518
|
+
'error', 'warning', 'success', 'failed', 'loading', 'saving',
|
|
519
|
+
'debug', 'tools', 'settings', 'configuration', 'options',
|
|
520
|
+
'menu', 'back', 'main', 'invalid', 'choice', 'select',
|
|
521
|
+
'please', 'try', 'again', 'found', 'not', 'available'
|
|
522
|
+
];
|
|
523
|
+
|
|
524
|
+
const lowerText = text.toLowerCase();
|
|
525
|
+
return englishWords.some(word => lowerText.includes(word));
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// CLI Interface
|
|
530
|
+
if (require.main === module) {
|
|
531
|
+
const args = process.argv.slice(2);
|
|
532
|
+
const helper = new TranslationHelper();
|
|
533
|
+
|
|
534
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
535
|
+
console.log(`
|
|
536
|
+
Translation Helper Tool
|
|
537
|
+
|
|
538
|
+
Usage:
|
|
539
|
+
node translate-mismatches.js [options]
|
|
540
|
+
|
|
541
|
+
Options:
|
|
542
|
+
--translate Show translation preview (dry run)
|
|
543
|
+
--apply Apply translations (live mode)
|
|
544
|
+
--report Generate translation coverage report
|
|
545
|
+
--help, -h Show this help message
|
|
546
|
+
|
|
547
|
+
Examples:
|
|
548
|
+
node translate-mismatches.js --report # Show coverage report
|
|
549
|
+
node translate-mismatches.js --translate # Preview translations
|
|
550
|
+
node translate-mismatches.js --apply # Apply translations
|
|
551
|
+
`);
|
|
552
|
+
process.exit(0);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (args.includes('--report')) {
|
|
556
|
+
helper.generateReport().catch(error => {
|
|
557
|
+
console.error('❌ Error:', error.message);
|
|
558
|
+
process.exit(1);
|
|
559
|
+
});
|
|
560
|
+
} else if (args.includes('--translate') || args.includes('--apply')) {
|
|
561
|
+
const dryRun = !args.includes('--apply');
|
|
562
|
+
helper.translateAll(dryRun).catch(error => {
|
|
563
|
+
console.error('❌ Error:', error.message);
|
|
564
|
+
process.exit(1);
|
|
565
|
+
});
|
|
566
|
+
} else {
|
|
567
|
+
console.log('Use --help for usage information');
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
module.exports = TranslationHelper;
|