i18ntk 1.2.2 → 1.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +48 -2
  2. package/LICENSE +3 -1
  3. package/README.md +47 -46
  4. package/docs/README.md +40 -18
  5. package/docs/SCRIPT_DIRECTORY_GUIDE.md +224 -0
  6. package/docs/release-notes/v1.3.0.md +162 -0
  7. package/main/i18ntk-analyze.js +11 -6
  8. package/main/i18ntk-autorun.js +37 -36
  9. package/main/i18ntk-complete.js +9 -5
  10. package/main/i18ntk-init.js +16 -12
  11. package/main/i18ntk-manage.js +6 -4
  12. package/main/i18ntk-sizing.js +5 -1
  13. package/main/i18ntk-summary.js +6 -2
  14. package/main/i18ntk-usage.js +5 -5
  15. package/main/i18ntk-validate.js +9 -5
  16. package/package.json +12 -5
  17. package/scripts/copy-translations.js +90 -0
  18. package/settings/i18ntk-config.json +11 -1
  19. package/settings/settings-cli.js +136 -75
  20. package/settings/settings-manager.js +19 -0
  21. package/ui-locales/de/autorun.json +69 -64
  22. package/ui-locales/de/common.json +14 -1
  23. package/ui-locales/de/errors.json +11 -1
  24. package/ui-locales/de/menu.json +10 -1
  25. package/ui-locales/de/operations.json +11 -0
  26. package/ui-locales/de/security.json +2 -1
  27. package/ui-locales/de/settings.json +60 -9
  28. package/ui-locales/de/sizing.json +14 -1
  29. package/ui-locales/de/status.json +18 -1
  30. package/ui-locales/de/test-complete-system.json +13 -1
  31. package/ui-locales/en/autorun.json +68 -65
  32. package/ui-locales/en/common.json +15 -1
  33. package/ui-locales/en/init.json +20 -5
  34. package/ui-locales/en/menu.json +1 -0
  35. package/ui-locales/en/operations.json +11 -1
  36. package/ui-locales/en/settings.json +151 -34
  37. package/ui-locales/es/autorun.json +68 -65
  38. package/ui-locales/es/common.json +14 -1
  39. package/ui-locales/es/errors.json +11 -1
  40. package/ui-locales/es/menu.json +10 -1
  41. package/ui-locales/es/operations.json +11 -0
  42. package/ui-locales/es/security.json +2 -1
  43. package/ui-locales/es/settings.json +60 -9
  44. package/ui-locales/es/sizing.json +14 -1
  45. package/ui-locales/es/status.json +18 -1
  46. package/ui-locales/es/test-complete-system.json +13 -1
  47. package/ui-locales/fr/autorun.json +69 -64
  48. package/ui-locales/fr/common.json +14 -1
  49. package/ui-locales/fr/errors.json +11 -1
  50. package/ui-locales/fr/menu.json +10 -1
  51. package/ui-locales/fr/operations.json +11 -0
  52. package/ui-locales/fr/security.json +2 -1
  53. package/ui-locales/fr/settings.json +60 -9
  54. package/ui-locales/fr/sizing.json +14 -1
  55. package/ui-locales/fr/status.json +18 -1
  56. package/ui-locales/fr/test-complete-system.json +13 -1
  57. package/ui-locales/ja/autorun.json +69 -64
  58. package/ui-locales/ja/common.json +14 -1
  59. package/ui-locales/ja/errors.json +11 -1
  60. package/ui-locales/ja/menu.json +10 -1
  61. package/ui-locales/ja/operations.json +11 -0
  62. package/ui-locales/ja/security.json +2 -1
  63. package/ui-locales/ja/settings.json +60 -9
  64. package/ui-locales/ja/sizing.json +14 -1
  65. package/ui-locales/ja/status.json +18 -1
  66. package/ui-locales/ja/test-complete-system.json +13 -1
  67. package/ui-locales/pt/analyze.json +2 -1
  68. package/ui-locales/pt/autorun.json +69 -64
  69. package/ui-locales/pt/common.json +14 -1
  70. package/ui-locales/pt/errors.json +11 -1
  71. package/ui-locales/pt/init.json +5 -1
  72. package/ui-locales/pt/menu.json +10 -1
  73. package/ui-locales/pt/operations.json +11 -0
  74. package/ui-locales/pt/security.json +2 -1
  75. package/ui-locales/pt/settings.json +60 -9
  76. package/ui-locales/pt/sizing.json +14 -1
  77. package/ui-locales/pt/status.json +18 -1
  78. package/ui-locales/pt/test-complete-system.json +13 -1
  79. package/ui-locales/ru/autorun.json +69 -64
  80. package/ui-locales/ru/common.json +14 -1
  81. package/ui-locales/ru/errors.json +11 -1
  82. package/ui-locales/ru/menu.json +10 -1
  83. package/ui-locales/ru/operations.json +11 -0
  84. package/ui-locales/ru/security.json +2 -1
  85. package/ui-locales/ru/settings.json +60 -9
  86. package/ui-locales/ru/sizing.json +14 -1
  87. package/ui-locales/ru/status.json +18 -1
  88. package/ui-locales/ru/test-complete-system.json +13 -1
  89. package/ui-locales/zh/autorun.json +69 -64
  90. package/ui-locales/zh/common.json +14 -1
  91. package/ui-locales/zh/errors.json +11 -1
  92. package/ui-locales/zh/menu.json +10 -1
  93. package/ui-locales/zh/operations.json +11 -0
  94. package/ui-locales/zh/security.json +2 -1
  95. package/ui-locales/zh/settings.json +60 -9
  96. package/ui-locales/zh/sizing.json +13 -1
  97. package/ui-locales/zh/status.json +25 -8
  98. package/ui-locales/zh/test-complete-system.json +13 -1
  99. package/utils/test-complete-system.js +178 -162
  100. package/settings/backups/i18ntk-config-backup-2025-08-01T23-38-43-753Z.json +0 -117
  101. package/ui-locales/de-old.json +0 -705
  102. package/ui-locales/de.json +0 -15
  103. package/ui-locales/en-old.json +0 -709
  104. package/ui-locales/en.json +0 -15
  105. package/ui-locales/es-old.json +0 -654
  106. package/ui-locales/es.json +0 -15
  107. package/ui-locales/fr-old.json +0 -606
  108. package/ui-locales/fr.json +0 -15
  109. package/ui-locales/ja-old.json +0 -660
  110. package/ui-locales/ja.json +0 -15
  111. package/ui-locales/pt.json +0 -15
  112. package/ui-locales/ru-old.json +0 -655
  113. package/ui-locales/ru.json +0 -15
  114. package/ui-locales/zh-old.json +0 -647
  115. package/ui-locales/zh.json +0 -15
@@ -32,5 +32,14 @@
32
32
  "autoDetectedI18nDirectory": "č‡ŖåŠØę£€ęµ‹åˆ°å›½é™…åŒ–ē›®å½•ļ¼š{path}",
33
33
  "executingCommand": "ę‰§č”Œå‘½ä»¤ļ¼š{command}",
34
34
  "unknownCommand": "ęœŖēŸ„å‘½ä»¤ļ¼š{command}",
35
- "languageOption": " {{index}}. {{displayName}}{{current}}"
35
+ "languageOption": " {{index}}. {{displayName}}{{current}}",
36
+ "update": "šŸ“¦ ę›“ę–°åŒ…",
37
+ "debugTools": {
38
+ "1": "ę—„åæ—ę–‡ä»¶åˆ†ęž",
39
+ "2": "ēæ»čÆ‘é”®ä½æē”Øåˆ†ęž",
40
+ "3": "ēæ»čÆ‘å®Œę•“ę€§åˆ†ęž",
41
+ "4": "ēæ»čÆ‘ę ¼å¼åˆ†ęž",
42
+ "5": "ēæ»čÆ‘ē±»åž‹åˆ†ęž",
43
+ "6": "ēæ»čÆ‘ē»“ęž„åˆ†ęž"
44
+ }
36
45
  }
@@ -64,5 +64,16 @@
64
64
  "allKeysAvailable": "šŸŽ‰ ę‰€ęœ‰ēæ»čÆ‘é”®ēŽ°åœØéƒ½å·²åÆē”Øļ¼",
65
65
  "runWithoutDryRun": "šŸ’” čæč”Œäøåø¦ --dry-run ēš„å‘½ä»¤ä»„åŗ”ē”Øę›“ę”¹",
66
66
  "pressEnterToContinue": "ęŒ‰å›žč½¦é”®ē»§ē»­..."
67
+ },
68
+ "cancelReportOption": "å–ę¶ˆ / čæ”å›žčœå•",
69
+ "updatePackage": {
70
+ "title": "šŸ“¦ ꛓꖰ i18n-toolkit 包",
71
+ "description": "čæ™å°†ęŠŠ i18n-toolkit åŒ…ę›“ę–°åˆ° npm äøŠåÆē”Øēš„ęœ€ę–°ē‰ˆęœ¬ć€‚",
72
+ "command": "č¦čæč”Œēš„å‘½ä»¤",
73
+ "confirm": "ę‚Øē”®å®šč¦ē»§ē»­ę›“ę–°å—ļ¼Ÿ(y/N): ",
74
+ "updating": "ę­£åœØę›“ę–°åŒ…...čæ™åÆčƒ½éœ€č¦äø€äŗ›ę—¶é—“ć€‚",
75
+ "success": "āœ… i18n-toolkit ę›“ę–°ęˆåŠŸļ¼",
76
+ "error": "āŒ ꛓꖰ i18n-toolkit 失蓄",
77
+ "cancelled": "āŒ ę›“ę–°å·²å–ę¶ˆć€‚"
67
78
  }
68
79
  }
@@ -20,5 +20,6 @@
20
20
  "inputDisallowedCharacters": "å®‰å…Øļ¼šč¾“å…„åŒ…å«äøå…č®øēš„å­—ē¬¦",
21
21
  "unknownCommandArg": "ęœŖēŸ„å‘½ä»¤å‚ę•°",
22
22
  "logMessage": "[安全{level}] {timestamp}: {event}",
23
- "alertMessage": "[å®‰å…Øč­¦ęŠ„] {timestamp}: {event}"
23
+ "alertMessage": "[å®‰å…Øč­¦ęŠ„] {timestamp}: {event}",
24
+ "securityWarning": "[ZH] [SECURITY WARNING] {timestamp}: {event}"
24
25
  }
@@ -25,7 +25,15 @@
25
25
  "helpDesc": "ę˜¾ē¤ŗčÆ¦ē»†åø®åŠ©äæ”ęÆ",
26
26
  "quit": "退出",
27
27
  "quitDesc": "é€€å‡ŗč®¾ē½®ļ¼ˆå¦‚ęœ‰éœ€č¦ä¼šęē¤ŗäæå­˜ļ¼‰",
28
- "selectOption": "é€‰ę‹©äø€äøŖé€‰é”¹ļ¼š"
28
+ "selectOption": "é€‰ę‹©äø€äøŖé€‰é”¹ļ¼š",
29
+ "updatePackage": "[ZH] Update Package",
30
+ "updatePackageDesc": "[ZH] Update the i18n-toolkit package via npm",
31
+ "unsavedChangesWarning": "[ZH] āš ļø You have unsaved changes",
32
+ "saveChangesBeforeQuit": "[ZH] Do you want to save changes before quitting? (y/N): ",
33
+ "goodbyeMessage": "[ZH] Thank you for using the i18n settings manager!",
34
+ "selectSettingPrompt": "[ZH] Select a setting to edit (or 'b' to go back): ",
35
+ "enterNewValue": "[ZH] Enter new value: ",
36
+ "updatedSuccessfully": "[ZH] {{setting}} updated successfully!"
29
37
  },
30
38
  "categories": {
31
39
  "uiSettings": "ē•Œé¢č®¾ē½®",
@@ -132,7 +140,8 @@
132
140
  },
133
141
  "themes": {
134
142
  "light": "浅色",
135
- "dark": "深色"
143
+ "dark": "深色",
144
+ "system": "[ZH] System"
136
145
  },
137
146
  "languages": {
138
147
  "en": "英语",
@@ -154,13 +163,19 @@
154
163
  "title": "ęŸ„ēœ‹ę‰€ęœ‰č®¾ē½®"
155
164
  },
156
165
  "importExport": {
157
- "title": "导兄/导出设置",
158
- "export": "导出设置",
159
- "import": "导兄设置",
160
- "backup": "åˆ›å»ŗå¤‡ä»½"
166
+ "enterFilename": "[ZH] Enter filename (or press Enter for default): ",
167
+ "exportSuccess": "[ZH] Settings exported to {{file}}",
168
+ "exportFailed": "[ZH] Failed to export settings: {{error}}",
169
+ "importFilename": "[ZH] Enter filename to import: ",
170
+ "fileNotFound": "[ZH] File not found.",
171
+ "invalidFormat": "[ZH] Invalid settings file format.",
172
+ "confirmImport": "[ZH] This will replace all current settings. Continue? (y/N): ",
173
+ "importSuccess": "[ZH] Settings imported successfully.",
174
+ "importFailed": "[ZH] Failed to import settings: {{error}}",
175
+ "backupSuccess": "[ZH] Backup created: {{file}}",
176
+ "backupFailed": "[ZH] Failed to create backup: {{error}}"
161
177
  },
162
178
  "pressEnter": "ꌉEnter键继续...",
163
- "selectOption": "é€‰ę‹©äø€äøŖé€‰é”¹ļ¼š",
164
179
  "current": "当前",
165
180
  "back": "čæ”å›žäø»čœå•",
166
181
  "selectSettingPrompt": "é€‰ę‹©č¦ē¼–č¾‘ēš„č®¾ē½®ļ¼ˆęˆ–bčæ”å›žļ¼‰ļ¼š",
@@ -190,6 +205,42 @@
190
205
  "benefitReset": "• é‡ē½®č®¾ē½®",
191
206
  "setupCancelled": "ā­ļø ē®”ē†å‘˜PINč®¾ē½®å·²å–ę¶ˆć€‚",
192
207
  "operationCancelled": "āŒ ę“ä½œå·²å–ę¶ˆć€‚",
193
- "startingSettings": "šŸŽ›ļø 正在启动设置CLI..."
194
- }
208
+ "startingSettings": "šŸŽ›ļø 正在启动设置CLI...",
209
+ "selectOption": "[ZH] Select option: ",
210
+ "updateSuccess": "[ZH] Admin PIN updated successfully!",
211
+ "updateFailed": "[ZH] Failed to update admin PIN.",
212
+ "protectionRemoved": "[ZH] Admin PIN protection removed.",
213
+ "configSuccess": "[ZH] Admin PIN configured successfully!",
214
+ "configFailed": "[ZH] Failed to configure admin PIN.",
215
+ "setupPinPrompt": "[ZH] Would you like to set up an admin PIN? (y/N): ",
216
+ "pinUpdatedSuccess": "[ZH] Admin PIN updated successfully!",
217
+ "pinUpdatedFailed": "[ZH] Failed to update admin PIN.",
218
+ "pinRemoved": "[ZH] Admin PIN protection removed.",
219
+ "pinConfiguredSuccess": "[ZH] Admin PIN configured successfully!",
220
+ "pinConfiguredFailed": "[ZH] Failed to configure admin PIN.",
221
+ "authRequired": "[ZH] šŸ”’ Admin authentication required for: {{label}}",
222
+ "pinSetupTitle": "[ZH] Admin PIN Setup",
223
+ "browserOpenFailed": "[ZH] Could not automatically open browser.",
224
+ "manualVisit": "[ZH] Please manually visit: {{url}}",
225
+ "browserOpened": "[ZH] āœ… Browser opened successfully!"
226
+ },
227
+ "goodbyeMessage": "[ZH] Thank you for using the i18ntk settings manager!",
228
+ "updatePackage": {
229
+ "title": "[ZH] Update Package",
230
+ "description": "[ZH] Update the i18n-toolkit package via npm",
231
+ "command": "[ZH] npm update i18ntk -g",
232
+ "success": "[ZH] Update completed successfully.",
233
+ "failed": "[ZH] Update failed.",
234
+ "prompt": "[ZH] Do you want to update the package now? (y/N): ",
235
+ "promptDesc": "[ZH] Note: This will update the i18ntk package to the latest version.",
236
+ "confirm": "[ZH] y",
237
+ "cancelled": "[ZH] Update cancelled."
238
+ },
239
+ "resetConfirm": "[ZH] Are you sure you want to continue? (y/N): ",
240
+ "resetSuccess": "[ZH] Settings reset to defaults successfully.",
241
+ "resetFailed": "[ZH] Failed to reset settings: {error}",
242
+ "saveSuccess": "[ZH] Settings saved successfully.",
243
+ "saveFailed": "[ZH] Failed to save settings.",
244
+ "initFailed": "[ZH] Failed to initialize settings: {error}",
245
+ "invalidValueFormat": "[ZH] Invalid value format."
195
246
  }
@@ -30,5 +30,17 @@
30
30
  "no_translation_files_found": "ęœŖę‰¾åˆ°ēæ»čÆ‘ę–‡ä»¶",
31
31
  "analysis_completed": "åˆ†ęžåœØ{{duration}}ęÆ«ē§’å†…å®Œęˆ",
32
32
  "analysis_failed": "åˆ†ęžå¤±č“„: {{errorMessage}}",
33
- "failed_to_parse_language_error": "ę— ę³•č§£ęž{{language}}翻译: {{errorMessage}}"
33
+ "failed_to_parse_language_error": "ę— ę³•č§£ęž{{language}}翻译: {{errorMessage}}",
34
+ "longTranslationsDetected": "[ZH] ${lang} has ${data.longKeys} translations longer than 100 characters - consider breaking them down",
35
+ "considerReviewingTranslations": "[ZH] Consider reviewing ${lang} translations - they are ${data.percentageDifference}% longer than baseline",
36
+ "recommendations": {
37
+ "keys_have_significant_size_variations": "[ZH] keys have significant size variations",
38
+ "consider_reviewing_translations": "[ZH] Consider reviewing {{lang}} translations - they are {{percentageDifference}}% {{comparison}} than baseline",
39
+ "long_translations_detected": "[ZH] {{lang}} has {{longKeys}} translations longer than {{threshold}} characters - consider breaking them down"
40
+ },
41
+ "hardcodedTexts": {
42
+ "keysHaveSignificantSizeVariations": "[ZH] keys have significant size variations",
43
+ "considerReviewingTranslations": "[ZH] Consider reviewing {{lang}} translations - they are {{percentageDifference}}% {{comparison}} than baseline",
44
+ "longTranslationsDetected": "[ZH] {{lang}} has {{longKeys}} translations longer than {{threshold}} characters - consider breaking them down"
45
+ }
34
46
  }
@@ -1,10 +1,27 @@
1
1
  {
2
- "title": "ēŠ¶ę€",
3
- "nextStep1": "1. ę£€ęŸ„ä½æē”Øęƒ…å†µåˆ†ęžļ¼š",
4
- "nextStep2": "2. éŖŒčÆēæ»čÆ‘ļ¼š",
5
- "nextStep3": "3. åˆ†ęžå®Œę•“ę€§ļ¼š",
6
- "nextStep4": "4. å®Œęˆē¼ŗå¤±ēš„ēæ»čÆ‘ļ¼š",
7
- "nextStep5": "5. ē”Ÿęˆę‘˜č¦ęŠ„å‘Šļ¼š",
8
- "nextStep6": "6. å®”ę øå¹¶å®Œęˆēæ»čÆ‘ļ¼š",
9
- "completed": "7. 成功完成!"
2
+ "title": "ēŠ¶ę€",
3
+ "nextStep1": "1. ę£€ęŸ„ä½æē”Øęƒ…å†µåˆ†ęžļ¼š",
4
+ "nextStep2": "2. éŖŒčÆēæ»čÆ‘ļ¼š",
5
+ "nextStep3": "3. åˆ†ęžå®Œę•“ę€§ļ¼š",
6
+ "nextStep4": "4. å®Œęˆē¼ŗå¤±ēš„ēæ»čÆ‘ļ¼š",
7
+ "nextStep5": "5. ē”Ÿęˆę‘˜č¦ęŠ„å‘Šļ¼š",
8
+ "nextStep6": "6. å®”ę øå¹¶å®Œęˆēæ»čÆ‘ļ¼š",
9
+ "completed": "7. 成功完成!",
10
+ "generating": "[ZH] Generating summary report...",
11
+ "review": "[ZH] Reviewing translations...",
12
+ "finalizing": "[ZH] Finalizing translations...",
13
+ "finalized": "[ZH] Finalized successfully!",
14
+ "errors": "[ZH] Errors:",
15
+ "warnings": "[ZH] Warnings:",
16
+ "info": "[ZH] Info:",
17
+ "progress": "[ZH] Progress:",
18
+ "remaining": "[ZH] Remaining:",
19
+ "total": "[ZH] Total:",
20
+ "completedKeys": "[ZH] Completed keys:",
21
+ "missingKeys": "[ZH] Missing keys:",
22
+ "partialKeys": "[ZH] Partial keys:",
23
+ "sameAsSource": "[ZH] Same as source:",
24
+ "formatMismatch": "[ZH] Format mismatch:",
25
+ "typeMismatch": "[ZH] Type mismatch:",
26
+ "structureMismatch": "[ZH] Structure mismatch:"
10
27
  }
@@ -23,6 +23,18 @@
23
23
  "failure_message": "āŒ {message}",
24
24
  "failure_error_message": " {errorMessage}",
25
25
  "warning_message": "āš ļø {message}",
26
- "test_runner_failed": "āŒ ęµ‹čÆ•čæč”Œå™Øå¤±č“„ļ¼š"
26
+ "test_runner_failed": "āŒ ęµ‹čÆ•čæč”Œå™Øå¤±č“„ļ¼š",
27
+ "recommendations": {
28
+ "add_missing_translation_keys": "[ZH] Add missing translation keys to maintain consistency",
29
+ "fix_failing_scripts": "[ZH] Fix failing scripts before deployment",
30
+ "review_warning_messages": "[ZH] Review and address warning messages",
31
+ "system_ready_for_deployment": "[ZH] System is ready for deployment"
32
+ },
33
+ "hardcodedTexts": {
34
+ "addMissingTranslationKeys": "[ZH] Add missing translation keys to maintain consistency",
35
+ "fixFailingScripts": "[ZH] Fix failing scripts before deployment",
36
+ "reviewWarningMessages": "[ZH] Review and address warning messages",
37
+ "systemReadyForDeployment": "[ZH] System is ready for deployment"
38
+ }
27
39
  }
28
40
  }
@@ -147,86 +147,192 @@ class SystemTester {
147
147
  console.log('\n🌐 Checking Translation Consistency...');
148
148
 
149
149
  try {
150
- const localesDir = './ui-locales';
151
- const files = fs.readdirSync(localesDir).filter(f => f.endsWith('.json'));
152
-
153
- if (files.length === 0) {
154
- this.logError('No translation files found');
150
+ const baseLocalesDir = './ui-locales';
151
+ const languages = fs.readdirSync(baseLocalesDir).filter(f => fs.statSync(path.join(baseLocalesDir, f)).isDirectory());
152
+
153
+ if (languages.length === 0) {
154
+ this.logError('No language directories found in ui-locales');
155
155
  return;
156
156
  }
157
-
157
+
158
158
  // Load English as reference
159
- const enPath = path.join(localesDir, 'en.json');
160
- if (!fs.existsSync(enPath)) {
161
- this.logError('English translation file not found');
159
+ const enDir = path.join(baseLocalesDir, 'en');
160
+ if (!fs.existsSync(enDir)) {
161
+ this.logError('English language directory not found');
162
162
  return;
163
163
  }
164
-
165
- const enData = JSON.parse(fs.readFileSync(enPath, 'utf8'));
164
+
165
+ const enFiles = fs.readdirSync(enDir).filter(f => f.endsWith('.json'));
166
+ let enData = {};
167
+ for (const file of enFiles) {
168
+ const filePath = path.join(enDir, file);
169
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
170
+ enData = { ...enData, [file.replace('.json', '')]: content };
171
+ }
166
172
  const enKeys = this.getAllKeys(enData);
167
-
173
+
168
174
  this.logSuccess(`Found ${enKeys.length} keys in English translations`);
169
-
175
+
170
176
  // Check other languages
171
- for (const file of files) {
172
- if (file === 'en.json') continue;
173
-
174
- const filePath = path.join(localesDir, file);
175
- const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
176
- const keys = this.getAllKeys(data);
177
-
178
- // Get critical keys that must be present in all languages
179
- const criticalKeys = [
180
- 'language.title',
181
- 'menu.title',
182
- 'menu.options.exit',
183
- 'common.success',
184
- 'common.error'
185
- ];
177
+ for (const lang of languages) {
178
+ if (lang === 'en') continue;
186
179
 
187
- const missingCritical = criticalKeys.filter(key => !keys.includes(key));
188
- const nonCriticalMissing = enKeys.filter(key => !criticalKeys.includes(key) && !keys.includes(key));
189
- const extra = keys.filter(key => !enKeys.includes(key));
190
-
191
- this.translationConsistency[file] = {
192
- criticalKeysPresent: missingCritical.length === 0,
193
- missingCriticalKeys: missingCritical,
194
- nonCriticalMissingKeys: nonCriticalMissing,
195
- extraKeys: extra
196
- };
197
-
198
- if (missingCritical.length > 0) {
199
- this.logWarning(`${file}: ${missingCritical.length} missing critical keys`);
200
- missingCritical.forEach(key => {
201
- this.missingTranslations.push(`${file}:${key}`);
202
- });
203
- } else {
204
- this.logSuccess(`${file}: All critical keys present`);
180
+ const langDir = path.join(baseLocalesDir, lang);
181
+ const langFiles = fs.readdirSync(langDir).filter(f => f.endsWith('.json'));
182
+ let langData = {};
183
+ for (const file of langFiles) {
184
+ const filePath = path.join(langDir, file);
185
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
186
+ langData = { ...langData, [file.replace('.json', '')]: content };
205
187
  }
188
+ const langKeys = this.getAllKeys(langData);
189
+
190
+ // Compare keys
191
+ const missingInLang = enKeys.filter(key => !langKeys.includes(key));
192
+ const extraInLang = langKeys.filter(key => !enKeys.includes(key));
206
193
 
207
- if (nonCriticalMissing.length > 0) {
208
- console.log(`ā„¹ļø ${file}: ${nonCriticalMissing.length} non-critical missing keys`);
209
- nonCriticalMissing.slice(0, 5).forEach(key => {
210
- this.missingTranslations.push(`${file}:${key}`);
211
- });
194
+ if (missingInLang.length > 0) {
195
+ this.logError(`Missing keys in ${lang} translations: ${missingInLang.join(', ')}`);
196
+ this.results.failed++;
197
+ this.translationConsistency[lang] = { status: 'failed', missing: missingInLang, extra: extraInLang };
198
+ }
199
+ if (extraInLang.length > 0) {
200
+ this.logError(`Extra keys in ${lang} translations: ${extraInLang.join(', ')}`);
201
+ this.results.failed++;
202
+ this.translationConsistency[lang] = { status: 'failed', missing: missingInLang, extra: extraInLang };
212
203
  }
213
204
 
214
- if (extra.length > 0) {
215
- console.log(`ā„¹ļø ${file}: ${extra.length} extra keys`);
205
+ if (missingInLang.length === 0 && extraInLang.length === 0) {
206
+ this.logSuccess(`Translation consistency OK for ${lang}`);
207
+ } else {
208
+ this.logError(`Translation consistency check failed for ${lang}`);
216
209
  }
217
210
  }
218
-
211
+
212
+ if (Object.keys(this.translationConsistency).length === 0 || Object.values(this.translationConsistency).every(status => status.status === 'ok')) {
213
+ this.logSuccess('All translations are consistent');
214
+ } else {
215
+ this.logError('Translation consistency check failed');
216
+ this.results.failed++;
217
+ }
218
+
219
219
  } catch (error) {
220
- this.logError('Translation consistency check failed', error);
220
+ this.logError(`Translation consistency check failed: ${error.message}`);
221
+ this.results.failed++;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Get all keys from a nested object
227
+ * @param {object} obj
228
+ * @param {string} prefix
229
+ * @returns {string[]}
230
+ */
231
+ getAllKeys(obj, prefix = '') {
232
+ let keys = [];
233
+ for (const key in obj) {
234
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
235
+ keys = keys.concat(this.getAllKeys(obj[key], prefix ? `${prefix}.${key}` : key));
236
+ } else {
237
+ keys.push(prefix ? `${prefix}.${key}` : key);
238
+ }
239
+ }
240
+ return keys;
241
+ }
242
+
243
+ /**
244
+ * Log a success message
245
+ * @param {string} message
246
+ */
247
+ logSuccess(message) {
248
+ console.log(`āœ… ${message}`);
249
+ this.results.passed++;
250
+ }
251
+
252
+ /**
253
+ * Log an error message
254
+ * @param {string} message
255
+ */
256
+ logError(message) {
257
+ console.log(`āŒ ${message}`);
258
+ this.results.errors.push(message);
259
+ }
260
+
261
+ /**
262
+ * Log a warning message
263
+ * @param {string} message
264
+ */
265
+ logWarning(message) {
266
+ console.log(`āš ļø ${message}`);
267
+ this.results.warnings++;
268
+ }
269
+
270
+ /**
271
+ * Run a shell command and return its output
272
+ * @param {string} command
273
+ * @returns {string}
274
+ */
275
+ runCommand(command) {
276
+ try {
277
+ return execSync(command, { encoding: 'utf8', stdio: 'pipe' });
278
+ } catch (error) {
279
+ this.logError(`Command failed: ${command}\n${error.stderr}`);
280
+ throw error;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Run the complete system test
286
+ */
287
+ async runTests() {
288
+ console.log('\n🧪 Starting Complete System Test\n');
289
+ console.log('============================================================\n');
290
+
291
+ // Test UI Translations
292
+ console.log('šŸ“ Testing UI Translations...');
293
+ this.testUITranslations();
294
+
295
+ // Test Settings Manager
296
+ console.log('\nāš™ļø Testing Settings Manager...');
297
+ this.testSettingsManager();
298
+
299
+ // Test Main Scripts
300
+ console.log('\nšŸ”§ Testing Main Scripts...');
301
+ this.testAllScripts();
302
+
303
+ // Check Translation Consistency
304
+ await this.checkTranslationConsistency();
305
+
306
+ // Generate Report
307
+ console.log('\nšŸ“Š Generating Report...');
308
+ this.generateReport();
309
+
310
+ console.log('\n============================================================');
311
+ console.log('šŸ“‹ FINAL TEST REPORT');
312
+ console.log('============================================================');
313
+ console.log(`āœ… Passed: ${this.results.passed}`);
314
+ console.log(`āŒ Failed: ${this.results.failed}`);
315
+ console.log(`āš ļø Warnings: ${this.results.warnings}`);
316
+
317
+ if (this.results.errors.length > 0) {
318
+ console.log('\nāŒ Errors:');
319
+ this.results.errors.forEach(error => console.log(` - ${error}`));
320
+ }
321
+
322
+ if (this.results.failed === 0) {
323
+ console.log('\nšŸ“Š Overall Status: 🟢 ALL TESTS PASSED');
324
+ } else {
325
+ console.log('\nšŸ“Š Overall Status: šŸ”“ NEEDS FIXES');
221
326
  }
327
+ console.log('============================================================');
222
328
  }
223
329
 
224
330
  /**
225
331
  * Generate comprehensive report
226
332
  */
227
- async generateReport() {
333
+ generateReport() {
228
334
  console.log('\nšŸ“Š Generating Report...');
229
-
335
+
230
336
  const report = {
231
337
  timestamp: new Date().toISOString(),
232
338
  summary: {
@@ -239,13 +345,13 @@ class SystemTester {
239
345
  errors: this.results.errors,
240
346
  recommendations: this.generateRecommendations()
241
347
  };
242
-
348
+
243
349
  // Ensure the reports directory exists
244
350
  const reportsDir = path.join(__dirname, '..', 'dev', 'debug', 'reports');
245
351
  if (!fs.existsSync(reportsDir)) {
246
352
  fs.mkdirSync(reportsDir, { recursive: true });
247
353
  }
248
-
354
+
249
355
  const reportPath = path.join(reportsDir, 'test-report.json');
250
356
  fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
251
357
  this.logSuccess(`Report saved to ${reportPath}`);
@@ -256,120 +362,30 @@ class SystemTester {
256
362
  */
257
363
  generateRecommendations() {
258
364
  const recommendations = [];
259
-
365
+
260
366
  if (this.missingTranslations.length > 0) {
261
- recommendations.push(this.ui.t('hardcodedTexts.addMissingTranslationKeys'));
367
+ recommendations.push('Add missing translation keys');
262
368
  }
263
-
264
- if (this.results.failed > 0) {
265
- recommendations.push(this.ui.t('hardcodedTexts.fixFailingScripts'));
266
- }
267
-
268
- if (this.results.warnings > 5) {
269
- recommendations.push(this.ui.t('hardcodedTexts.reviewWarningMessages'));
270
- }
271
-
272
- if (recommendations.length === 0) {
273
- recommendations.push(this.ui.t('hardcodedTexts.systemReadyForDeployment'));
274
- }
275
-
276
- return recommendations;
277
- }
278
369
 
279
- /**
280
- * Get all keys from nested object
281
- */
282
- getAllKeys(obj, prefix = '') {
283
- let keys = [];
284
-
285
- for (const key in obj) {
286
- const fullKey = prefix ? `${prefix}.${key}` : key;
287
-
288
- if (typeof obj[key] === 'object' && obj[key] !== null) {
289
- if (Array.isArray(obj[key])) {
290
- // Handle array values
291
- keys.push(fullKey);
292
-
293
- // If array contains objects, extract their keys too
294
- obj[key].forEach((item, index) => {
295
- if (typeof item === 'object' && item !== null) {
296
- keys = keys.concat(this.getAllKeys(item, `${fullKey}[${index}]`));
297
- }
298
- });
299
- } else {
300
- // Handle nested objects
301
- keys = keys.concat(this.getAllKeys(obj[key], fullKey));
302
- }
303
- } else {
304
- keys.push(fullKey);
305
- }
370
+ if (this.results.failed > 0) {
371
+ recommendations.push('Fix failing scripts');
306
372
  }
307
-
308
- return keys;
309
- }
310
373
 
311
- /**
312
- * Print final report
313
- */
314
- printFinalReport() {
315
- console.log('\n' + '=' .repeat(60));
316
- console.log('šŸ“‹ FINAL TEST REPORT');
317
- console.log('=' .repeat(60));
318
-
319
- console.log(`āœ… Passed: ${this.results.passed}`);
320
- console.log(`āŒ Failed: ${this.results.failed}`);
321
- console.log(`āš ļø Warnings: ${this.results.warnings}`);
322
-
323
- if (this.missingTranslations.length > 0) {
324
- console.log(`\nšŸ” Missing Translations (${this.missingTranslations.length}):`);
325
- this.missingTranslations.slice(0, 10).forEach(key => {
326
- console.log(` - ${key}`);
327
- });
328
- if (this.missingTranslations.length > 10) {
329
- console.log(` ... and ${this.missingTranslations.length - 10} more`);
330
- }
331
- }
332
-
333
- if (this.results.errors.length > 0) {
334
- console.log(`\nāŒ Errors:`);
335
- this.results.errors.forEach(error => {
336
- console.log(` - ${error}`);
337
- });
374
+ if (this.results.warnings > 5) {
375
+ recommendations.push('Review warning messages');
338
376
  }
339
-
340
- const status = this.results.failed === 0 ? '🟢 READY' : 'šŸ”“ NEEDS FIXES';
341
- console.log(`\nšŸ“Š Overall Status: ${status}`);
342
- console.log('=' .repeat(60));
343
- }
344
-
345
- // Logging methods
346
- logSuccess(message) {
347
- console.log(`āœ… ${message}`);
348
- this.results.passed++;
349
- }
350
377
 
351
- logError(message, error = null) {
352
- console.log(`āŒ ${message}`);
353
- if (error) {
354
- console.log(` ${error.message}`);
378
+ if (recommendations.length === 0) {
379
+ recommendations.push('System ready for deployment');
355
380
  }
356
- this.results.failed++;
357
- this.results.errors.push(message);
358
- }
359
381
 
360
- logWarning(message) {
361
- console.log(`āš ļø ${message}`);
362
- this.results.warnings++;
382
+ return recommendations;
363
383
  }
364
384
  }
365
385
 
366
- // Run tests if called directly
367
- if (require.main === module) {
368
- const tester = new SystemTester();
369
- tester.runAllTests().catch(error => {
370
- console.error('āŒ Test runner failed:', error.message);
371
- process.exit(1);
372
- });
373
- }
386
+
387
+ // Run the tests
388
+ const tester = new SystemTester();
389
+ tester.runTests();
374
390
 
375
391
  module.exports = SystemTester;