supervertaler 1.9.153__py3-none-any.whl

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.

Potentially problematic release.


This version of supervertaler might be problematic. Click here for more details.

Files changed (85) hide show
  1. Supervertaler.py +47886 -0
  2. modules/__init__.py +10 -0
  3. modules/ai_actions.py +964 -0
  4. modules/ai_attachment_manager.py +343 -0
  5. modules/ai_file_viewer_dialog.py +210 -0
  6. modules/autofingers_engine.py +466 -0
  7. modules/cafetran_docx_handler.py +379 -0
  8. modules/config_manager.py +469 -0
  9. modules/database_manager.py +1878 -0
  10. modules/database_migrations.py +417 -0
  11. modules/dejavurtf_handler.py +779 -0
  12. modules/document_analyzer.py +427 -0
  13. modules/docx_handler.py +689 -0
  14. modules/encoding_repair.py +319 -0
  15. modules/encoding_repair_Qt.py +393 -0
  16. modules/encoding_repair_ui.py +481 -0
  17. modules/feature_manager.py +350 -0
  18. modules/figure_context_manager.py +340 -0
  19. modules/file_dialog_helper.py +148 -0
  20. modules/find_replace.py +164 -0
  21. modules/find_replace_qt.py +457 -0
  22. modules/glossary_manager.py +433 -0
  23. modules/image_extractor.py +188 -0
  24. modules/keyboard_shortcuts_widget.py +571 -0
  25. modules/llm_clients.py +1211 -0
  26. modules/llm_leaderboard.py +737 -0
  27. modules/llm_superbench_ui.py +1401 -0
  28. modules/local_llm_setup.py +1104 -0
  29. modules/model_update_dialog.py +381 -0
  30. modules/model_version_checker.py +373 -0
  31. modules/mqxliff_handler.py +638 -0
  32. modules/non_translatables_manager.py +743 -0
  33. modules/pdf_rescue_Qt.py +1822 -0
  34. modules/pdf_rescue_tkinter.py +909 -0
  35. modules/phrase_docx_handler.py +516 -0
  36. modules/project_home_panel.py +209 -0
  37. modules/prompt_assistant.py +357 -0
  38. modules/prompt_library.py +689 -0
  39. modules/prompt_library_migration.py +447 -0
  40. modules/quick_access_sidebar.py +282 -0
  41. modules/ribbon_widget.py +597 -0
  42. modules/sdlppx_handler.py +874 -0
  43. modules/setup_wizard.py +353 -0
  44. modules/shortcut_manager.py +932 -0
  45. modules/simple_segmenter.py +128 -0
  46. modules/spellcheck_manager.py +727 -0
  47. modules/statuses.py +207 -0
  48. modules/style_guide_manager.py +315 -0
  49. modules/superbench_ui.py +1319 -0
  50. modules/superbrowser.py +329 -0
  51. modules/supercleaner.py +600 -0
  52. modules/supercleaner_ui.py +444 -0
  53. modules/superdocs.py +19 -0
  54. modules/superdocs_viewer_qt.py +382 -0
  55. modules/superlookup.py +252 -0
  56. modules/tag_cleaner.py +260 -0
  57. modules/tag_manager.py +333 -0
  58. modules/term_extractor.py +270 -0
  59. modules/termbase_entry_editor.py +842 -0
  60. modules/termbase_import_export.py +488 -0
  61. modules/termbase_manager.py +1060 -0
  62. modules/termview_widget.py +1172 -0
  63. modules/theme_manager.py +499 -0
  64. modules/tm_editor_dialog.py +99 -0
  65. modules/tm_manager_qt.py +1280 -0
  66. modules/tm_metadata_manager.py +545 -0
  67. modules/tmx_editor.py +1461 -0
  68. modules/tmx_editor_qt.py +2784 -0
  69. modules/tmx_generator.py +284 -0
  70. modules/tracked_changes.py +900 -0
  71. modules/trados_docx_handler.py +430 -0
  72. modules/translation_memory.py +715 -0
  73. modules/translation_results_panel.py +2134 -0
  74. modules/translation_services.py +282 -0
  75. modules/unified_prompt_library.py +659 -0
  76. modules/unified_prompt_manager_qt.py +3951 -0
  77. modules/voice_commands.py +920 -0
  78. modules/voice_dictation.py +477 -0
  79. modules/voice_dictation_lite.py +249 -0
  80. supervertaler-1.9.153.dist-info/METADATA +896 -0
  81. supervertaler-1.9.153.dist-info/RECORD +85 -0
  82. supervertaler-1.9.153.dist-info/WHEEL +5 -0
  83. supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
  84. supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
  85. supervertaler-1.9.153.dist-info/top_level.txt +2 -0
@@ -0,0 +1,393 @@
1
+ """
2
+ Encoding Repair Module - Qt Edition
3
+ Embeddable version of the text encoding repair tool for detecting and fixing mojibake/encoding corruption
4
+
5
+ This module can be embedded in the main Supervertaler Qt application as a tab.
6
+ Can also be run independently as a standalone application.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ # Fix import path for standalone mode - must be done before any module imports
14
+ _parent_dir = Path(__file__).parent.parent
15
+ if str(_parent_dir) not in sys.path:
16
+ sys.path.insert(0, str(_parent_dir))
17
+
18
+ from PyQt6.QtWidgets import (
19
+ QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFileDialog,
20
+ QMessageBox, QPlainTextEdit, QGroupBox, QFrame, QApplication
21
+ )
22
+ from PyQt6.QtCore import Qt, QThread, pyqtSignal
23
+ from PyQt6.QtGui import QFont, QTextOption
24
+
25
+ from modules.encoding_repair import EncodingRepair
26
+
27
+
28
+ class EncodingRepairQt:
29
+ """
30
+ Encoding Repair feature - detect and fix text encoding corruption (mojibake)
31
+ Can be embedded in any PyQt6 application as a tab or panel
32
+ """
33
+
34
+ def __init__(self, parent_app, standalone=False):
35
+ """
36
+ Initialize Encoding Repair module
37
+
38
+ Args:
39
+ parent_app: Reference to the main application (optional, for logging)
40
+ standalone: If True, running as standalone app. If False, embedded in Supervertaler
41
+ """
42
+ self.parent_app = parent_app
43
+ self.standalone = standalone
44
+
45
+ # Initialize logging
46
+ self.log = parent_app.log if hasattr(parent_app, 'log') else print
47
+
48
+ # State
49
+ self.selected_path = None
50
+ self.is_folder = False
51
+
52
+ def log_message(self, message: str):
53
+ """Log a message to the parent app's log if available"""
54
+ if hasattr(self.parent_app, 'log'):
55
+ self.parent_app.log(f"[Encoding Repair] {message}")
56
+ else:
57
+ print(f"[Encoding Repair] {message}")
58
+
59
+ def create_tab(self, parent: QWidget):
60
+ """
61
+ Create the Encoding Repair tab UI
62
+
63
+ Args:
64
+ parent: The QWidget container for the tab
65
+ """
66
+ # Main layout
67
+ main_layout = QVBoxLayout(parent)
68
+ main_layout.setContentsMargins(10, 10, 10, 10)
69
+ main_layout.setSpacing(5)
70
+
71
+ # ===== HEADER: Standard Template (matches PDF Rescue / TMX Editor style) =====
72
+ header = QLabel("šŸ”§ Encoding Repair")
73
+ header.setStyleSheet("font-size: 16pt; font-weight: bold; color: #1976D2;")
74
+ main_layout.addWidget(header, 0) # 0 = no stretch, stays compact
75
+
76
+ # Description box (matches Universal Lookup / PDF Rescue / TMX Editor style)
77
+ description = QLabel(
78
+ "Detect and fix text encoding corruption (mojibake) in translation files.\n"
79
+ "Automatically repairs UTF-8 text incorrectly decoded as Latin-1 or Windows-1252."
80
+ )
81
+ description.setWordWrap(True)
82
+ description.setStyleSheet("color: #666; padding: 5px; background-color: #E3F2FD; border-radius: 3px;")
83
+ main_layout.addWidget(description, 0) # 0 = no stretch, stays compact
84
+
85
+ # ===== FILE SELECTION SECTION =====
86
+ file_group = QGroupBox("šŸ“ Select File or Folder")
87
+ file_layout = QVBoxLayout(file_group)
88
+ file_layout.setSpacing(10)
89
+
90
+ # Selected path display
91
+ self.path_label = QLabel("No file or folder selected")
92
+ self.path_label.setWordWrap(True)
93
+ self.path_label.setStyleSheet("color: #666; padding: 5px; background-color: #F5F5F5; border-radius: 3px;")
94
+ file_layout.addWidget(self.path_label)
95
+
96
+ # Selection buttons
97
+ select_layout = QHBoxLayout()
98
+ select_layout.setSpacing(10)
99
+
100
+ select_file_btn = QPushButton("šŸ“„ Select File")
101
+ select_file_btn.clicked.connect(self._select_file)
102
+ select_file_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 6px 12px;")
103
+ select_layout.addWidget(select_file_btn)
104
+
105
+ select_folder_btn = QPushButton("šŸ“ Select Folder")
106
+ select_folder_btn.clicked.connect(self._select_folder)
107
+ select_folder_btn.setStyleSheet("background-color: #9C27B0; color: white; font-weight: bold; padding: 6px 12px;")
108
+ select_layout.addWidget(select_folder_btn)
109
+
110
+ select_layout.addStretch()
111
+ file_layout.addLayout(select_layout)
112
+
113
+ main_layout.addWidget(file_group, 0)
114
+
115
+ # ===== ACTIONS SECTION =====
116
+ action_group = QGroupBox("šŸ” Detect & Repair")
117
+ action_layout = QVBoxLayout(action_group)
118
+ action_layout.setSpacing(10)
119
+
120
+ # Action buttons
121
+ action_btn_layout = QHBoxLayout()
122
+ action_btn_layout.setSpacing(10)
123
+
124
+ scan_btn = QPushButton("šŸ” Scan for Corruption")
125
+ scan_btn.clicked.connect(self._scan_files)
126
+ scan_btn.setStyleSheet("background-color: #FF9800; color: white; font-weight: bold; padding: 6px 12px;")
127
+ action_btn_layout.addWidget(scan_btn)
128
+
129
+ repair_btn = QPushButton("āœ… Repair Files")
130
+ repair_btn.clicked.connect(self._repair_files)
131
+ repair_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 6px 12px;")
132
+ action_btn_layout.addWidget(repair_btn)
133
+
134
+ clear_btn = QPushButton("āŒ Clear")
135
+ clear_btn.clicked.connect(self._clear_results)
136
+ clear_btn.setStyleSheet("background-color: #9E9E9E; color: white; padding: 6px 12px;")
137
+ action_btn_layout.addWidget(clear_btn)
138
+
139
+ action_btn_layout.addStretch()
140
+ action_layout.addLayout(action_btn_layout)
141
+
142
+ main_layout.addWidget(action_group, 0)
143
+
144
+ # ===== RESULTS SECTION =====
145
+ results_group = QGroupBox("šŸ“Š Results")
146
+ results_layout = QVBoxLayout(results_group)
147
+ results_layout.setContentsMargins(5, 5, 5, 5)
148
+
149
+ self.results_text = QPlainTextEdit()
150
+ self.results_text.setReadOnly(True)
151
+ self.results_text.setFont(QFont("Consolas", 9))
152
+ self.results_text.setStyleSheet("""
153
+ QPlainTextEdit {
154
+ background-color: #F5F5F5;
155
+ border: 1px solid #DDD;
156
+ border-radius: 3px;
157
+ }
158
+ """)
159
+ results_layout.addWidget(self.results_text, 1) # 1 = stretch factor
160
+
161
+ main_layout.addWidget(results_group, 1) # 1 = stretch factor, expands to fill space
162
+
163
+ def _select_file(self):
164
+ """Select a single file for encoding repair"""
165
+ file_path, _ = QFileDialog.getOpenFileName(
166
+ None, "Select file to scan",
167
+ "", "Text files (*.txt);;All files (*.*)"
168
+ )
169
+ if file_path:
170
+ self.selected_path = file_path
171
+ self.is_folder = False
172
+ self.path_label.setText(f"File: {file_path}")
173
+ self.log_message(f"Selected file: {file_path}")
174
+
175
+ def _select_folder(self):
176
+ """Select a folder for encoding repair"""
177
+ folder_path = QFileDialog.getExistingDirectory(
178
+ None, "Select folder to scan"
179
+ )
180
+ if folder_path:
181
+ self.selected_path = folder_path
182
+ self.is_folder = True
183
+ self.path_label.setText(f"Folder: {folder_path}")
184
+ self.log_message(f"Selected folder: {folder_path}")
185
+
186
+ def _scan_files(self):
187
+ """Scan selected file(s) for encoding corruption"""
188
+ if not self.selected_path:
189
+ QMessageBox.warning(None, "No Selection", "Please select a file or folder first.")
190
+ return
191
+
192
+ # Clear results
193
+ self.results_text.clear()
194
+ self.results_text.appendPlainText("Scanning for encoding corruption...\n")
195
+ QApplication.processEvents()
196
+
197
+ try:
198
+ if self.is_folder:
199
+ # Scan all text files in folder
200
+ folder = Path(self.selected_path)
201
+ text_files = list(folder.rglob('*.txt'))
202
+
203
+ if not text_files:
204
+ self.results_text.appendPlainText("āš ļø No text files found in folder\n")
205
+ return
206
+
207
+ self.results_text.appendPlainText(f"Scanning {len(text_files)} file(s)...\n\n")
208
+ QApplication.processEvents()
209
+
210
+ total_corruptions = 0
211
+ files_with_corruption = 0
212
+
213
+ for filepath in text_files:
214
+ try:
215
+ with open(filepath, 'r', encoding='utf-8') as f:
216
+ content = f.read()
217
+
218
+ has_corruption, corruption_count, patterns = EncodingRepair.detect_corruption(content)
219
+
220
+ if has_corruption:
221
+ files_with_corruption += 1
222
+ total_corruptions += corruption_count
223
+
224
+ self.results_text.appendPlainText(f"šŸ“„ {filepath.name}\n")
225
+ self.results_text.appendPlainText(f" āœ“ Found {corruption_count} corruption(s)\n")
226
+ for pattern in patterns:
227
+ self.results_text.appendPlainText(f" • {pattern}\n")
228
+ self.results_text.appendPlainText("\n")
229
+ except Exception as e:
230
+ self.results_text.appendPlainText(f"āŒ Error reading {filepath.name}: {str(e)}\n")
231
+
232
+ QApplication.processEvents()
233
+
234
+ self.results_text.appendPlainText("\n--- Summary ---\n")
235
+ self.results_text.appendPlainText(f"Files with corruption: {files_with_corruption}\n")
236
+ self.results_text.appendPlainText(f"Total corruptions found: {total_corruptions}\n")
237
+
238
+ else:
239
+ # Scan single file
240
+ try:
241
+ with open(self.selected_path, 'r', encoding='utf-8') as f:
242
+ content = f.read()
243
+
244
+ has_corruption, corruption_count, patterns = EncodingRepair.detect_corruption(content)
245
+
246
+ filename = Path(self.selected_path).name
247
+
248
+ if has_corruption:
249
+ self.results_text.appendPlainText(f"šŸ“„ {filename}\n")
250
+ self.results_text.appendPlainText("āœ“ Encoding corruption detected!\n\n")
251
+ self.results_text.appendPlainText(f"Found {corruption_count} corruption pattern(s):\n\n")
252
+ for pattern in patterns:
253
+ self.results_text.appendPlainText(f" • {pattern}\n")
254
+ else:
255
+ self.results_text.appendPlainText(f"šŸ“„ {filename}\n")
256
+ self.results_text.appendPlainText("āœ“ No encoding corruption found\n")
257
+
258
+ except Exception as e:
259
+ self.results_text.appendPlainText(f"āŒ Error: {str(e)}\n")
260
+
261
+ self.log_message("Scan completed")
262
+
263
+ except Exception as e:
264
+ QMessageBox.critical(None, "Error", f"Failed to scan files:\n\n{str(e)}")
265
+ self.log_message(f"Scan error: {str(e)}")
266
+
267
+ def _repair_files(self):
268
+ """Fix encoding corruption in selected file(s)"""
269
+ if not self.selected_path:
270
+ QMessageBox.warning(None, "No Selection", "Please select a file or folder first.")
271
+ return
272
+
273
+ # Confirm before repair
274
+ if self.is_folder:
275
+ msg = f"Repair all text files in folder?\n\n{self.selected_path}\n\nA backup will be created for each file."
276
+ else:
277
+ msg = f"Repair file?\n\n{Path(self.selected_path).name}\n\nA backup will be created."
278
+
279
+ reply = QMessageBox.question(
280
+ None, "Confirm Repair", msg,
281
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
282
+ )
283
+
284
+ if reply != QMessageBox.StandardButton.Yes:
285
+ return
286
+
287
+ # Append to results
288
+ self.results_text.appendPlainText("\n--- Repair Process ---\n")
289
+ QApplication.processEvents()
290
+
291
+ try:
292
+ if self.is_folder:
293
+ # Repair all text files in folder
294
+ folder = Path(self.selected_path)
295
+ text_files = list(folder.rglob('*.txt'))
296
+
297
+ if not text_files:
298
+ self.results_text.appendPlainText("āš ļø No text files found\n")
299
+ return
300
+
301
+ repaired_count = 0
302
+ for filepath in text_files:
303
+ try:
304
+ # Create backup
305
+ backup_path = f"{filepath}.backup"
306
+ with open(filepath, 'rb') as f_src:
307
+ with open(backup_path, 'wb') as f_dst:
308
+ f_dst.write(f_src.read())
309
+
310
+ # Repair
311
+ success, message, stats = EncodingRepair.repair_file(str(filepath))
312
+
313
+ if success:
314
+ if stats.get('has_corruption', False):
315
+ repaired_count += 1
316
+ self.results_text.appendPlainText(f"āœ… {filepath.name}\n")
317
+ self.results_text.appendPlainText(f" Fixed {stats.get('corruption_count', 0)} corruption(s)\n")
318
+ else:
319
+ self.results_text.appendPlainText(f"ā„¹ļø {filepath.name} (no corruption found)\n")
320
+ else:
321
+ self.results_text.appendPlainText(f"āŒ {filepath.name}: {message}\n")
322
+
323
+ QApplication.processEvents()
324
+
325
+ except Exception as e:
326
+ self.results_text.appendPlainText(f"āŒ Error repairing {filepath.name}: {str(e)}\n")
327
+
328
+ self.results_text.appendPlainText(f"\nāœ… Repaired {repaired_count}/{len(text_files)} file(s)\n")
329
+ QMessageBox.information(None, "Repair Complete", f"Repaired {repaired_count} file(s) in folder.")
330
+
331
+ else:
332
+ # Repair single file
333
+ # Create backup
334
+ backup_path = f"{self.selected_path}.backup"
335
+ with open(self.selected_path, 'rb') as f_src:
336
+ with open(backup_path, 'wb') as f_dst:
337
+ f_dst.write(f_src.read())
338
+
339
+ # Repair
340
+ success, message, stats = EncodingRepair.repair_file(self.selected_path)
341
+
342
+ if success:
343
+ self.results_text.appendPlainText(message + "\n")
344
+ if 'corruption_count' in stats and stats['corruption_count'] > 0:
345
+ self.results_text.appendPlainText(f"File size: {stats.get('original_size', 0)} → {stats.get('repaired_size', 0)} bytes\n")
346
+ self.results_text.appendPlainText(f"Backup created: {backup_path}\n")
347
+ QMessageBox.information(None, "Success", f"File repaired successfully!\n\nBackup saved as: {backup_path}")
348
+ else:
349
+ self.results_text.appendPlainText(message + "\n")
350
+ QMessageBox.critical(None, "Error", f"Repair failed:\n\n{message}")
351
+
352
+ self.log_message("Repair completed")
353
+
354
+ except Exception as e:
355
+ QMessageBox.critical(None, "Error", f"Failed to repair files:\n\n{str(e)}")
356
+ self.log_message(f"Repair error: {str(e)}")
357
+
358
+ def _clear_results(self):
359
+ """Clear the results display"""
360
+ self.results_text.clear()
361
+
362
+
363
+ # === Standalone Application ===
364
+
365
+ if __name__ == "__main__":
366
+ """Run Encoding Repair as a standalone application"""
367
+ from PyQt6.QtWidgets import QMainWindow
368
+
369
+ class StandaloneApp(QMainWindow):
370
+ """Minimal parent app for standalone mode"""
371
+ def __init__(self):
372
+ super().__init__()
373
+ self.setWindowTitle("Encoding Repair Tool - Text Encoding Corruption Fixer")
374
+ self.setGeometry(100, 100, 900, 700)
375
+
376
+ # Create central widget
377
+ central_widget = QWidget()
378
+ self.setCentralWidget(central_widget)
379
+
380
+ # Create Encoding Repair widget
381
+ self.encoding_repair = EncodingRepairQt(self, standalone=True)
382
+ self.encoding_repair.create_tab(central_widget)
383
+
384
+ def log(self, message: str):
385
+ """Simple log method for standalone mode"""
386
+ print(message)
387
+
388
+ # Create and run application
389
+ app = QApplication(sys.argv)
390
+ window = StandaloneApp()
391
+ window.show()
392
+ sys.exit(app.exec())
393
+