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,477 @@
1
+ """
2
+ Voice Dictation Module for Supervertaler
3
+ Uses OpenAI Whisper for multilingual speech recognition
4
+ Supports English, Dutch, and 90+ other languages
5
+ """
6
+
7
+ import sounddevice as sd
8
+ import numpy as np
9
+ import tempfile
10
+ import wave
11
+ from pathlib import Path
12
+ from PyQt6.QtWidgets import (
13
+ QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,
14
+ QComboBox, QTextEdit, QGroupBox, QMessageBox, QProgressBar
15
+ )
16
+ from PyQt6.QtCore import Qt, QThread, pyqtSignal
17
+ from PyQt6.QtGui import QFont, QShortcut, QKeySequence
18
+
19
+
20
+ class RecordingThread(QThread):
21
+ """Background thread for audio recording"""
22
+ finished = pyqtSignal(str) # Emits path to recorded file
23
+ error = pyqtSignal(str)
24
+
25
+ def __init__(self, duration=30, sample_rate=16000):
26
+ super().__init__()
27
+ self.duration = duration
28
+ self.sample_rate = sample_rate
29
+ self.is_recording = False
30
+
31
+ def run(self):
32
+ """Record audio in background"""
33
+ try:
34
+ self.is_recording = True
35
+
36
+ # Record audio
37
+ recording = sd.rec(
38
+ int(self.duration * self.sample_rate),
39
+ samplerate=self.sample_rate,
40
+ channels=1,
41
+ dtype='float32'
42
+ )
43
+
44
+ # Wait for recording to complete
45
+ sd.wait()
46
+
47
+ if not self.is_recording:
48
+ # Recording was stopped early
49
+ sd.stop()
50
+
51
+ # Convert to int16
52
+ audio_data = np.int16(recording * 32767)
53
+
54
+ # Save to temporary WAV file with explicit directory
55
+ import os
56
+ temp_dir = tempfile.gettempdir()
57
+ temp_path = os.path.join(temp_dir, f"voice_recording_{os.getpid()}.wav")
58
+
59
+ with wave.open(temp_path, 'wb') as wf:
60
+ wf.setnchannels(1)
61
+ wf.setsampwidth(2) # 16-bit
62
+ wf.setframerate(self.sample_rate)
63
+ wf.writeframes(audio_data.tobytes())
64
+
65
+ self.finished.emit(temp_path)
66
+
67
+ except Exception as e:
68
+ import traceback
69
+ self.error.emit(f"{str(e)}\n{traceback.format_exc()}")
70
+
71
+ def stop(self):
72
+ """Stop recording"""
73
+ self.is_recording = False
74
+ sd.stop()
75
+
76
+
77
+ class TranscriptionThread(QThread):
78
+ """Background thread for transcription"""
79
+ finished = pyqtSignal(str) # Emits transcribed text
80
+ error = pyqtSignal(str)
81
+ progress = pyqtSignal(str) # Progress updates
82
+
83
+ def __init__(self, audio_path, model_name="base", language=None):
84
+ super().__init__()
85
+ self.audio_path = audio_path
86
+ self.model_name = model_name
87
+ self.language = language
88
+
89
+ def run(self):
90
+ """Transcribe audio in background"""
91
+ try:
92
+ import os
93
+
94
+ try:
95
+ import whisper # Local Whisper (optional)
96
+ except ImportError:
97
+ self.error.emit(
98
+ "Local Whisper is not installed.\n\n"
99
+ "To use offline speech recognition, install:\n"
100
+ " pip install supervertaler[local-whisper]\n\n"
101
+ "Or switch to 'OpenAI Whisper API' in Supervoice settings."
102
+ )
103
+ return
104
+
105
+ # Verify file exists
106
+ if not os.path.exists(self.audio_path):
107
+ self.error.emit(f"Audio file not found: {self.audio_path}")
108
+ return
109
+
110
+ self.progress.emit("Loading Whisper model...")
111
+
112
+ # Load model
113
+ model = whisper.load_model(self.model_name)
114
+
115
+ self.progress.emit("Transcribing audio...")
116
+
117
+ # Transcribe
118
+ if self.language:
119
+ result = model.transcribe(self.audio_path, language=self.language)
120
+ else:
121
+ result = model.transcribe(self.audio_path)
122
+
123
+ # Clean up temp file
124
+ try:
125
+ Path(self.audio_path).unlink()
126
+ except:
127
+ pass
128
+
129
+ self.finished.emit(result["text"].strip())
130
+
131
+ except Exception as e:
132
+ import traceback
133
+ self.error.emit(f"{str(e)}\n\nFull error:\n{traceback.format_exc()}")
134
+
135
+
136
+ class VoiceDictationWidget(QWidget):
137
+ """
138
+ Voice Dictation Widget using Whisper
139
+
140
+ Features:
141
+ - Push-to-record button
142
+ - Multilingual support (100+ languages)
143
+ - Multiple model sizes (tiny, base, small, medium, large)
144
+ - Copy to clipboard functionality
145
+ """
146
+
147
+ MODELS = {
148
+ "tiny": "Tiny (fastest, ~1GB RAM)",
149
+ "base": "Base (good balance, ~1GB RAM)",
150
+ "small": "Small (better quality, ~2GB RAM)",
151
+ "medium": "Medium (high quality, ~5GB RAM)",
152
+ "large": "Large (best quality, ~10GB RAM)"
153
+ }
154
+
155
+ LANGUAGES = {
156
+ "auto": "Auto-detect",
157
+ "en": "English",
158
+ "nl": "Dutch",
159
+ "de": "German",
160
+ "fr": "French",
161
+ "es": "Spanish",
162
+ "it": "Italian",
163
+ "pt": "Portuguese",
164
+ "pl": "Polish",
165
+ "ru": "Russian",
166
+ "zh": "Chinese",
167
+ "ja": "Japanese",
168
+ "ko": "Korean",
169
+ # Add more as needed
170
+ }
171
+
172
+ def __init__(self, parent=None):
173
+ super().__init__(parent)
174
+ self.recording_thread = None
175
+ self.transcription_thread = None
176
+ self.init_ui()
177
+ self.setup_shortcuts()
178
+
179
+ def init_ui(self):
180
+ """Initialize the user interface"""
181
+ layout = QVBoxLayout(self)
182
+ layout.setContentsMargins(10, 10, 10, 10)
183
+
184
+ # Title
185
+ title = QLabel("🎤 Voice Dictation")
186
+ title.setFont(QFont("Segoe UI", 14, QFont.Weight.Bold))
187
+ title.setStyleSheet("color: #1976D2;")
188
+ layout.addWidget(title)
189
+
190
+ # Description
191
+ desc = QLabel(
192
+ "Record your voice and get instant transcription in 100+ languages.\n"
193
+ "Powered by OpenAI Whisper."
194
+ )
195
+ desc.setStyleSheet("color: #666; padding: 5px; background-color: #E3F2FD; border-radius: 3px;")
196
+ desc.setWordWrap(True)
197
+ layout.addWidget(desc)
198
+
199
+ # Settings group
200
+ settings_group = QGroupBox("Settings")
201
+ settings_layout = QHBoxLayout()
202
+
203
+ # Model selection
204
+ settings_layout.addWidget(QLabel("Model:"))
205
+ self.model_combo = QComboBox()
206
+ for key, label in self.MODELS.items():
207
+ self.model_combo.addItem(label, key)
208
+ self.model_combo.setCurrentIndex(1) # Default to "base"
209
+ settings_layout.addWidget(self.model_combo)
210
+
211
+ # Language selection
212
+ settings_layout.addWidget(QLabel("Language:"))
213
+ self.language_combo = QComboBox()
214
+ for key, label in self.LANGUAGES.items():
215
+ self.language_combo.addItem(label, key)
216
+ settings_layout.addWidget(self.language_combo)
217
+
218
+ settings_group.setLayout(settings_layout)
219
+ layout.addWidget(settings_group)
220
+
221
+ # Recording controls
222
+ controls_layout = QHBoxLayout()
223
+
224
+ self.record_btn = QPushButton("đŸŽ™ī¸ Start Recording")
225
+ self.record_btn.setStyleSheet("""
226
+ QPushButton {
227
+ background-color: #2196F3;
228
+ color: white;
229
+ border: none;
230
+ padding: 10px 20px;
231
+ font-size: 14px;
232
+ border-radius: 5px;
233
+ }
234
+ QPushButton:hover {
235
+ background-color: #1976D2;
236
+ }
237
+ QPushButton:pressed {
238
+ background-color: #0D47A1;
239
+ }
240
+ QPushButton:disabled {
241
+ background-color: #BDBDBD;
242
+ }
243
+ """)
244
+ self.record_btn.clicked.connect(self.toggle_recording)
245
+ controls_layout.addWidget(self.record_btn)
246
+
247
+ self.copy_btn = QPushButton("📋 Copy to Clipboard")
248
+ self.copy_btn.setEnabled(False)
249
+ self.copy_btn.clicked.connect(self.copy_to_clipboard)
250
+ controls_layout.addWidget(self.copy_btn)
251
+
252
+ self.clear_btn = QPushButton("đŸ—‘ī¸ Clear")
253
+ self.clear_btn.clicked.connect(self.clear_text)
254
+ controls_layout.addWidget(self.clear_btn)
255
+
256
+ layout.addLayout(controls_layout)
257
+
258
+ # Progress bar
259
+ self.progress_bar = QProgressBar()
260
+ self.progress_bar.setVisible(False)
261
+ self.progress_bar.setTextVisible(False)
262
+ layout.addWidget(self.progress_bar)
263
+
264
+ # Status label
265
+ self.status_label = QLabel("Ready")
266
+ self.status_label.setStyleSheet("color: #666; font-style: italic;")
267
+ layout.addWidget(self.status_label)
268
+
269
+ # Transcription output
270
+ output_group = QGroupBox("Transcription")
271
+ output_layout = QVBoxLayout()
272
+
273
+ self.transcription_text = QTextEdit()
274
+ self.transcription_text.setPlaceholderText("Transcribed text will appear here...")
275
+ self.transcription_text.setMinimumHeight(200)
276
+ output_layout.addWidget(self.transcription_text)
277
+
278
+ output_group.setLayout(output_layout)
279
+ layout.addWidget(output_group)
280
+
281
+ # Help text
282
+ help_text = QLabel(
283
+ "💡 Tip: Use 'base' model for quick transcription, 'medium' or 'large' for best quality. "
284
+ "First use downloads the model (~1-10GB depending on size)."
285
+ )
286
+ help_text.setWordWrap(True)
287
+ help_text.setStyleSheet("color: #666; font-size: 10px; padding: 5px;")
288
+ layout.addWidget(help_text)
289
+
290
+ # Keyboard shortcuts info
291
+ shortcuts_text = QLabel(
292
+ "âŒ¨ī¸ Shortcuts: F9 = Start/Stop Recording | Esc = Cancel | Ctrl+C = Copy"
293
+ )
294
+ shortcuts_text.setWordWrap(True)
295
+ shortcuts_text.setStyleSheet(
296
+ "color: #1976D2; font-size: 10px; font-weight: bold; "
297
+ "padding: 8px; background-color: #E3F2FD; border-radius: 3px; margin-top: 5px;"
298
+ )
299
+ layout.addWidget(shortcuts_text)
300
+
301
+ def setup_shortcuts(self):
302
+ """Setup keyboard shortcuts"""
303
+ # F9 - Start/Stop recording
304
+ self.shortcut_record = QShortcut(QKeySequence("F9"), self)
305
+ self.shortcut_record.activated.connect(self.toggle_recording)
306
+
307
+ # Esc - Cancel recording
308
+ self.shortcut_cancel = QShortcut(QKeySequence(Qt.Key.Key_Escape), self)
309
+ self.shortcut_cancel.activated.connect(self.cancel_recording)
310
+
311
+ # Ctrl+C - Copy to clipboard
312
+ self.shortcut_copy = QShortcut(QKeySequence("Ctrl+C"), self)
313
+ self.shortcut_copy.activated.connect(self.copy_to_clipboard)
314
+
315
+ def cancel_recording(self):
316
+ """Cancel ongoing recording"""
317
+ if self.recording_thread and self.recording_thread.is_recording:
318
+ self.stop_recording()
319
+ self.status_label.setText("âš ī¸ Recording cancelled")
320
+ self.status_label.setStyleSheet("color: #FF9800;")
321
+
322
+ def toggle_recording(self):
323
+ """Start or stop recording"""
324
+ if self.recording_thread and self.recording_thread.is_recording:
325
+ # Stop recording
326
+ self.stop_recording()
327
+ else:
328
+ # Start recording
329
+ self.start_recording()
330
+
331
+ def start_recording(self):
332
+ """Start recording audio"""
333
+ self.status_label.setText("🔴 Recording... (max 30 seconds)")
334
+ self.status_label.setStyleSheet("color: #D32F2F; font-weight: bold;")
335
+ self.record_btn.setText("âšī¸ Stop Recording")
336
+ self.record_btn.setStyleSheet("""
337
+ QPushButton {
338
+ background-color: #D32F2F;
339
+ color: white;
340
+ border: none;
341
+ padding: 10px 20px;
342
+ font-size: 14px;
343
+ border-radius: 5px;
344
+ }
345
+ QPushButton:hover {
346
+ background-color: #C62828;
347
+ }
348
+ """)
349
+
350
+ # Disable controls
351
+ self.model_combo.setEnabled(False)
352
+ self.language_combo.setEnabled(False)
353
+
354
+ # Start recording thread
355
+ self.recording_thread = RecordingThread()
356
+ self.recording_thread.finished.connect(self.on_recording_finished)
357
+ self.recording_thread.error.connect(self.on_recording_error)
358
+ self.recording_thread.start()
359
+
360
+ def stop_recording(self):
361
+ """Stop recording audio"""
362
+ if self.recording_thread:
363
+ self.recording_thread.stop()
364
+ self.status_label.setText("Processing...")
365
+ self.status_label.setStyleSheet("color: #FF9800; font-weight: bold;")
366
+
367
+ def on_recording_finished(self, audio_path):
368
+ """Handle recording completion"""
369
+ # Reset button
370
+ self.record_btn.setText("đŸŽ™ī¸ Start Recording")
371
+ self.record_btn.setStyleSheet("""
372
+ QPushButton {
373
+ background-color: #2196F3;
374
+ color: white;
375
+ border: none;
376
+ padding: 10px 20px;
377
+ font-size: 14px;
378
+ border-radius: 5px;
379
+ }
380
+ QPushButton:hover {
381
+ background-color: #1976D2;
382
+ }
383
+ """)
384
+
385
+ # Start transcription
386
+ self.transcribe_audio(audio_path)
387
+
388
+ def on_recording_error(self, error_msg):
389
+ """Handle recording error"""
390
+ self.status_label.setText(f"❌ Recording error: {error_msg}")
391
+ self.status_label.setStyleSheet("color: #D32F2F;")
392
+ self.record_btn.setEnabled(True)
393
+ self.model_combo.setEnabled(True)
394
+ self.language_combo.setEnabled(True)
395
+
396
+ def transcribe_audio(self, audio_path):
397
+ """Transcribe recorded audio"""
398
+ model_name = self.model_combo.currentData()
399
+ language = self.language_combo.currentData()
400
+ if language == "auto":
401
+ language = None
402
+
403
+ # Show progress
404
+ self.progress_bar.setVisible(True)
405
+ self.progress_bar.setRange(0, 0) # Indeterminate
406
+ self.record_btn.setEnabled(False)
407
+
408
+ # Start transcription thread
409
+ self.transcription_thread = TranscriptionThread(audio_path, model_name, language)
410
+ self.transcription_thread.finished.connect(self.on_transcription_finished)
411
+ self.transcription_thread.error.connect(self.on_transcription_error)
412
+ self.transcription_thread.progress.connect(self.on_transcription_progress)
413
+ self.transcription_thread.start()
414
+
415
+ def on_transcription_progress(self, message):
416
+ """Update progress message"""
417
+ self.status_label.setText(message)
418
+
419
+ def on_transcription_finished(self, text):
420
+ """Handle transcription completion"""
421
+ self.progress_bar.setVisible(False)
422
+ self.status_label.setText("✅ Transcription complete")
423
+ self.status_label.setStyleSheet("color: #388E3C; font-weight: bold;")
424
+
425
+ # Add text to output
426
+ current_text = self.transcription_text.toPlainText()
427
+ if current_text:
428
+ self.transcription_text.setPlainText(current_text + "\n\n" + text)
429
+ else:
430
+ self.transcription_text.setPlainText(text)
431
+
432
+ # Re-enable controls
433
+ self.record_btn.setEnabled(True)
434
+ self.model_combo.setEnabled(True)
435
+ self.language_combo.setEnabled(True)
436
+ self.copy_btn.setEnabled(True)
437
+
438
+ def on_transcription_error(self, error_msg):
439
+ """Handle transcription error"""
440
+ self.progress_bar.setVisible(False)
441
+ self.status_label.setText(f"❌ Transcription error: {error_msg}")
442
+ self.status_label.setStyleSheet("color: #D32F2F;")
443
+
444
+ QMessageBox.critical(self, "Error", f"Transcription failed:\n{error_msg}")
445
+
446
+ self.record_btn.setEnabled(True)
447
+ self.model_combo.setEnabled(True)
448
+ self.language_combo.setEnabled(True)
449
+
450
+ def copy_to_clipboard(self):
451
+ """Copy transcription to clipboard"""
452
+ from PyQt6.QtWidgets import QApplication
453
+ text = self.transcription_text.toPlainText()
454
+ if text:
455
+ QApplication.clipboard().setText(text)
456
+ self.status_label.setText("✅ Copied to clipboard")
457
+ self.status_label.setStyleSheet("color: #388E3C;")
458
+
459
+ def clear_text(self):
460
+ """Clear transcription text"""
461
+ self.transcription_text.clear()
462
+ self.copy_btn.setEnabled(False)
463
+ self.status_label.setText("Ready")
464
+ self.status_label.setStyleSheet("color: #666; font-style: italic;")
465
+
466
+
467
+ # Standalone test application
468
+ if __name__ == "__main__":
469
+ import sys
470
+ from PyQt6.QtWidgets import QApplication
471
+
472
+ app = QApplication(sys.argv)
473
+ window = VoiceDictationWidget()
474
+ window.setWindowTitle("Voice Dictation - Supervertaler")
475
+ window.resize(600, 700)
476
+ window.show()
477
+ sys.exit(app.exec())