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.
- package/CHANGELOG.md +48 -2
- package/LICENSE +3 -1
- package/README.md +47 -46
- package/docs/README.md +40 -18
- package/docs/SCRIPT_DIRECTORY_GUIDE.md +224 -0
- package/docs/release-notes/v1.3.0.md +162 -0
- package/main/i18ntk-analyze.js +11 -6
- package/main/i18ntk-autorun.js +37 -36
- package/main/i18ntk-complete.js +9 -5
- package/main/i18ntk-init.js +16 -12
- package/main/i18ntk-manage.js +6 -4
- package/main/i18ntk-sizing.js +5 -1
- package/main/i18ntk-summary.js +6 -2
- package/main/i18ntk-usage.js +5 -5
- package/main/i18ntk-validate.js +9 -5
- package/package.json +12 -5
- package/scripts/copy-translations.js +90 -0
- package/settings/i18ntk-config.json +11 -1
- package/settings/settings-cli.js +136 -75
- package/settings/settings-manager.js +19 -0
- package/ui-locales/de/autorun.json +69 -64
- package/ui-locales/de/common.json +14 -1
- package/ui-locales/de/errors.json +11 -1
- package/ui-locales/de/menu.json +10 -1
- package/ui-locales/de/operations.json +11 -0
- package/ui-locales/de/security.json +2 -1
- package/ui-locales/de/settings.json +60 -9
- package/ui-locales/de/sizing.json +14 -1
- package/ui-locales/de/status.json +18 -1
- package/ui-locales/de/test-complete-system.json +13 -1
- package/ui-locales/en/autorun.json +68 -65
- package/ui-locales/en/common.json +15 -1
- package/ui-locales/en/init.json +20 -5
- package/ui-locales/en/menu.json +1 -0
- package/ui-locales/en/operations.json +11 -1
- package/ui-locales/en/settings.json +151 -34
- package/ui-locales/es/autorun.json +68 -65
- package/ui-locales/es/common.json +14 -1
- package/ui-locales/es/errors.json +11 -1
- package/ui-locales/es/menu.json +10 -1
- package/ui-locales/es/operations.json +11 -0
- package/ui-locales/es/security.json +2 -1
- package/ui-locales/es/settings.json +60 -9
- package/ui-locales/es/sizing.json +14 -1
- package/ui-locales/es/status.json +18 -1
- package/ui-locales/es/test-complete-system.json +13 -1
- package/ui-locales/fr/autorun.json +69 -64
- package/ui-locales/fr/common.json +14 -1
- package/ui-locales/fr/errors.json +11 -1
- package/ui-locales/fr/menu.json +10 -1
- package/ui-locales/fr/operations.json +11 -0
- package/ui-locales/fr/security.json +2 -1
- package/ui-locales/fr/settings.json +60 -9
- package/ui-locales/fr/sizing.json +14 -1
- package/ui-locales/fr/status.json +18 -1
- package/ui-locales/fr/test-complete-system.json +13 -1
- package/ui-locales/ja/autorun.json +69 -64
- package/ui-locales/ja/common.json +14 -1
- package/ui-locales/ja/errors.json +11 -1
- package/ui-locales/ja/menu.json +10 -1
- package/ui-locales/ja/operations.json +11 -0
- package/ui-locales/ja/security.json +2 -1
- package/ui-locales/ja/settings.json +60 -9
- package/ui-locales/ja/sizing.json +14 -1
- package/ui-locales/ja/status.json +18 -1
- package/ui-locales/ja/test-complete-system.json +13 -1
- package/ui-locales/pt/analyze.json +2 -1
- package/ui-locales/pt/autorun.json +69 -64
- package/ui-locales/pt/common.json +14 -1
- package/ui-locales/pt/errors.json +11 -1
- package/ui-locales/pt/init.json +5 -1
- package/ui-locales/pt/menu.json +10 -1
- package/ui-locales/pt/operations.json +11 -0
- package/ui-locales/pt/security.json +2 -1
- package/ui-locales/pt/settings.json +60 -9
- package/ui-locales/pt/sizing.json +14 -1
- package/ui-locales/pt/status.json +18 -1
- package/ui-locales/pt/test-complete-system.json +13 -1
- package/ui-locales/ru/autorun.json +69 -64
- package/ui-locales/ru/common.json +14 -1
- package/ui-locales/ru/errors.json +11 -1
- package/ui-locales/ru/menu.json +10 -1
- package/ui-locales/ru/operations.json +11 -0
- package/ui-locales/ru/security.json +2 -1
- package/ui-locales/ru/settings.json +60 -9
- package/ui-locales/ru/sizing.json +14 -1
- package/ui-locales/ru/status.json +18 -1
- package/ui-locales/ru/test-complete-system.json +13 -1
- package/ui-locales/zh/autorun.json +69 -64
- package/ui-locales/zh/common.json +14 -1
- package/ui-locales/zh/errors.json +11 -1
- package/ui-locales/zh/menu.json +10 -1
- package/ui-locales/zh/operations.json +11 -0
- package/ui-locales/zh/security.json +2 -1
- package/ui-locales/zh/settings.json +60 -9
- package/ui-locales/zh/sizing.json +13 -1
- package/ui-locales/zh/status.json +25 -8
- package/ui-locales/zh/test-complete-system.json +13 -1
- package/utils/test-complete-system.js +178 -162
- package/settings/backups/i18ntk-config-backup-2025-08-01T23-38-43-753Z.json +0 -117
- package/ui-locales/de-old.json +0 -705
- package/ui-locales/de.json +0 -15
- package/ui-locales/en-old.json +0 -709
- package/ui-locales/en.json +0 -15
- package/ui-locales/es-old.json +0 -654
- package/ui-locales/es.json +0 -15
- package/ui-locales/fr-old.json +0 -606
- package/ui-locales/fr.json +0 -15
- package/ui-locales/ja-old.json +0 -660
- package/ui-locales/ja.json +0 -15
- package/ui-locales/pt.json +0 -15
- package/ui-locales/ru-old.json +0 -655
- package/ui-locales/ru.json +0 -15
- package/ui-locales/zh-old.json +0 -647
- package/ui-locales/zh.json +0 -15
package/ui-locales/zh/menu.json
CHANGED
|
@@ -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
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
this.logError('No
|
|
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
|
|
160
|
-
if (!fs.existsSync(
|
|
161
|
-
this.logError('English
|
|
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
|
|
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
|
|
172
|
-
if (
|
|
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
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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 (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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 (
|
|
215
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
352
|
-
|
|
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
|
-
|
|
361
|
-
console.log(`ā ļø ${message}`);
|
|
362
|
-
this.results.warnings++;
|
|
382
|
+
return recommendations;
|
|
363
383
|
}
|
|
364
384
|
}
|
|
365
385
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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;
|