supervertaler 1.9.173__py3-none-any.whl → 1.9.190__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.
- Supervertaler.py +50498 -48981
- modules/database_manager.py +197 -70
- modules/database_migrations.py +54 -7
- modules/extract_tm.py +518 -0
- modules/mqxliff_handler.py +71 -2
- modules/project_tm.py +320 -0
- modules/superbrowser.py +22 -0
- modules/termbase_manager.py +105 -2
- modules/termview_widget.py +68 -32
- modules/theme_manager.py +41 -4
- modules/tm_metadata_manager.py +23 -18
- modules/translation_memory.py +3 -12
- modules/translation_results_panel.py +0 -7
- modules/unified_prompt_library.py +2 -2
- modules/unified_prompt_manager_qt.py +47 -18
- supervertaler-1.9.190.dist-info/METADATA +151 -0
- {supervertaler-1.9.173.dist-info → supervertaler-1.9.190.dist-info}/RECORD +21 -19
- supervertaler-1.9.173.dist-info/METADATA +0 -936
- {supervertaler-1.9.173.dist-info → supervertaler-1.9.190.dist-info}/WHEEL +0 -0
- {supervertaler-1.9.173.dist-info → supervertaler-1.9.190.dist-info}/entry_points.txt +0 -0
- {supervertaler-1.9.173.dist-info → supervertaler-1.9.190.dist-info}/licenses/LICENSE +0 -0
- {supervertaler-1.9.173.dist-info → supervertaler-1.9.190.dist-info}/top_level.txt +0 -0
modules/theme_manager.py
CHANGED
|
@@ -212,7 +212,10 @@ class ThemeManager:
|
|
|
212
212
|
self.themes_file = user_data_path / "themes.json"
|
|
213
213
|
self.current_theme: Theme = self.PREDEFINED_THEMES["Light (Default)"]
|
|
214
214
|
self.custom_themes: Dict[str, Theme] = {}
|
|
215
|
-
|
|
215
|
+
|
|
216
|
+
# Global UI font scale (50-200%, default 100%)
|
|
217
|
+
self.font_scale: int = 100
|
|
218
|
+
|
|
216
219
|
# Load custom themes
|
|
217
220
|
self.load_custom_themes()
|
|
218
221
|
|
|
@@ -289,14 +292,48 @@ class ThemeManager:
|
|
|
289
292
|
def apply_theme(self, app: QApplication):
|
|
290
293
|
"""
|
|
291
294
|
Apply current theme to application
|
|
292
|
-
|
|
295
|
+
|
|
293
296
|
Args:
|
|
294
297
|
app: QApplication instance
|
|
295
298
|
"""
|
|
296
299
|
theme = self.current_theme
|
|
297
|
-
|
|
300
|
+
|
|
301
|
+
# Calculate scaled font sizes based on font_scale (default 100%)
|
|
302
|
+
base_font_size = int(10 * self.font_scale / 100) # Base: 10pt at 100%
|
|
303
|
+
small_font_size = max(7, int(9 * self.font_scale / 100)) # Small text (status bar)
|
|
304
|
+
|
|
305
|
+
# Font scaling rules (only applied if scale != 100%)
|
|
306
|
+
font_rules = ""
|
|
307
|
+
if self.font_scale != 100:
|
|
308
|
+
font_rules = f"""
|
|
309
|
+
/* Global font scaling ({self.font_scale}%) */
|
|
310
|
+
QWidget {{ font-size: {base_font_size}pt; }}
|
|
311
|
+
QMenuBar {{ font-size: {base_font_size}pt; }}
|
|
312
|
+
QMenuBar::item {{ font-size: {base_font_size}pt; }}
|
|
313
|
+
QMenu {{ font-size: {base_font_size}pt; }}
|
|
314
|
+
QMenu::item {{ font-size: {base_font_size}pt; }}
|
|
315
|
+
QStatusBar {{ font-size: {small_font_size}pt; }}
|
|
316
|
+
QTabBar::tab {{ font-size: {base_font_size}pt; }}
|
|
317
|
+
QToolBar {{ font-size: {base_font_size}pt; }}
|
|
318
|
+
QLabel {{ font-size: {base_font_size}pt; }}
|
|
319
|
+
QCheckBox {{ font-size: {base_font_size}pt; }}
|
|
320
|
+
QRadioButton {{ font-size: {base_font_size}pt; }}
|
|
321
|
+
QComboBox {{ font-size: {base_font_size}pt; }}
|
|
322
|
+
QSpinBox {{ font-size: {base_font_size}pt; }}
|
|
323
|
+
QDoubleSpinBox {{ font-size: {base_font_size}pt; }}
|
|
324
|
+
QLineEdit {{ font-size: {base_font_size}pt; }}
|
|
325
|
+
QPushButton {{ font-size: {base_font_size}pt; }}
|
|
326
|
+
QGroupBox {{ font-size: {base_font_size}pt; }}
|
|
327
|
+
QGroupBox::title {{ font-size: {base_font_size}pt; }}
|
|
328
|
+
QTextEdit {{ font-size: {base_font_size}pt; }}
|
|
329
|
+
QPlainTextEdit {{ font-size: {base_font_size}pt; }}
|
|
330
|
+
QListWidget {{ font-size: {base_font_size}pt; }}
|
|
331
|
+
QTreeWidget {{ font-size: {base_font_size}pt; }}
|
|
332
|
+
QHeaderView::section {{ font-size: {base_font_size}pt; }}
|
|
333
|
+
"""
|
|
334
|
+
|
|
298
335
|
# Create and apply stylesheet - COLORS ONLY, preserves native sizes/spacing
|
|
299
|
-
stylesheet = f"""
|
|
336
|
+
stylesheet = font_rules + f"""
|
|
300
337
|
/* Main window background */
|
|
301
338
|
QMainWindow, QWidget {{
|
|
302
339
|
background-color: {theme.window_bg};
|
modules/tm_metadata_manager.py
CHANGED
|
@@ -344,19 +344,22 @@ class TMMetadataManager:
|
|
|
344
344
|
"""Check if a TM is active for a project (or global when project_id=0)"""
|
|
345
345
|
if project_id is None:
|
|
346
346
|
return False # If None (not 0), default to inactive
|
|
347
|
-
|
|
347
|
+
|
|
348
348
|
try:
|
|
349
349
|
cursor = self.db_manager.cursor
|
|
350
|
-
|
|
350
|
+
|
|
351
|
+
# Check if TM is active for this project OR globally (project_id=0)
|
|
351
352
|
cursor.execute("""
|
|
352
|
-
SELECT is_active FROM tm_activation
|
|
353
|
-
WHERE tm_id = ? AND project_id = ?
|
|
353
|
+
SELECT is_active FROM tm_activation
|
|
354
|
+
WHERE tm_id = ? AND (project_id = ? OR project_id = 0)
|
|
355
|
+
ORDER BY project_id DESC
|
|
354
356
|
""", (tm_db_id, project_id))
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
357
|
+
|
|
358
|
+
# Return True if any activation is active (project-specific takes priority due to ORDER BY)
|
|
359
|
+
for row in cursor.fetchall():
|
|
360
|
+
if bool(row[0]):
|
|
361
|
+
return True
|
|
362
|
+
|
|
360
363
|
# If no activation record exists, TM is inactive by default
|
|
361
364
|
return False
|
|
362
365
|
except Exception as e:
|
|
@@ -382,15 +385,16 @@ class TMMetadataManager:
|
|
|
382
385
|
|
|
383
386
|
try:
|
|
384
387
|
cursor = self.db_manager.cursor
|
|
385
|
-
|
|
388
|
+
|
|
386
389
|
# Only return TMs that have been explicitly activated (is_active = 1)
|
|
390
|
+
# Include both project-specific activations AND global activations (project_id=0)
|
|
387
391
|
cursor.execute("""
|
|
388
|
-
SELECT tm.tm_id
|
|
392
|
+
SELECT DISTINCT tm.tm_id
|
|
389
393
|
FROM translation_memories tm
|
|
390
394
|
INNER JOIN tm_activation ta ON tm.id = ta.tm_id
|
|
391
|
-
WHERE ta.project_id = ? AND ta.is_active = 1
|
|
395
|
+
WHERE (ta.project_id = ? OR ta.project_id = 0) AND ta.is_active = 1
|
|
392
396
|
""", (project_id,))
|
|
393
|
-
|
|
397
|
+
|
|
394
398
|
return [row[0] for row in cursor.fetchall()]
|
|
395
399
|
except Exception as e:
|
|
396
400
|
self.log(f"✗ Error fetching active tm_ids: {e}")
|
|
@@ -422,16 +426,17 @@ class TMMetadataManager:
|
|
|
422
426
|
|
|
423
427
|
try:
|
|
424
428
|
cursor = self.db_manager.cursor
|
|
425
|
-
|
|
429
|
+
|
|
426
430
|
# Return TMs where Write checkbox is enabled (read_only = 0)
|
|
427
|
-
# AND the TM has an activation record for this project
|
|
431
|
+
# AND the TM has an activation record for this project OR for global (project_id=0)
|
|
432
|
+
# This ensures TMs created when no project was loaded still work
|
|
428
433
|
cursor.execute("""
|
|
429
|
-
SELECT tm.tm_id
|
|
434
|
+
SELECT DISTINCT tm.tm_id
|
|
430
435
|
FROM translation_memories tm
|
|
431
436
|
INNER JOIN tm_activation ta ON tm.id = ta.tm_id
|
|
432
|
-
WHERE ta.project_id = ? AND tm.read_only = 0
|
|
437
|
+
WHERE (ta.project_id = ? OR ta.project_id = 0) AND tm.read_only = 0
|
|
433
438
|
""", (project_id,))
|
|
434
|
-
|
|
439
|
+
|
|
435
440
|
return [row[0] for row in cursor.fetchall()]
|
|
436
441
|
except Exception as e:
|
|
437
442
|
self.log(f"✗ Error fetching writable tm_ids: {e}")
|
modules/translation_memory.py
CHANGED
|
@@ -205,20 +205,14 @@ class TMDatabase:
|
|
|
205
205
|
Returns:
|
|
206
206
|
List of match dictionaries sorted by similarity
|
|
207
207
|
"""
|
|
208
|
-
print(f"[DEBUG] TMDatabase.search_all: source='{source[:50]}...', tm_ids={tm_ids}")
|
|
209
|
-
|
|
210
208
|
# Determine which TMs to search
|
|
211
209
|
# If tm_ids is None or empty, search ALL TMs (don't filter by tm_id)
|
|
212
210
|
if tm_ids is None and enabled_only:
|
|
213
211
|
tm_ids = [tm_id for tm_id, meta in self.tm_metadata.items() if meta.get('enabled', True)]
|
|
214
|
-
|
|
215
|
-
|
|
212
|
+
|
|
216
213
|
# If tm_ids is still empty, set to None to search ALL TMs
|
|
217
214
|
if tm_ids is not None and len(tm_ids) == 0:
|
|
218
215
|
tm_ids = None
|
|
219
|
-
print(f"[DEBUG] TMDatabase.search_all: Empty tm_ids, setting to None to search ALL")
|
|
220
|
-
|
|
221
|
-
print(f"[DEBUG] TMDatabase.search_all: Final tm_ids to search: {tm_ids}")
|
|
222
216
|
|
|
223
217
|
# First try exact match
|
|
224
218
|
exact_match = self.db.get_exact_match(
|
|
@@ -227,8 +221,7 @@ class TMDatabase:
|
|
|
227
221
|
source_lang=self.source_lang,
|
|
228
222
|
target_lang=self.target_lang
|
|
229
223
|
)
|
|
230
|
-
|
|
231
|
-
|
|
224
|
+
|
|
232
225
|
if exact_match:
|
|
233
226
|
# Format as match dictionary
|
|
234
227
|
return [{
|
|
@@ -241,7 +234,6 @@ class TMDatabase:
|
|
|
241
234
|
}]
|
|
242
235
|
|
|
243
236
|
# Try fuzzy matches
|
|
244
|
-
print(f"[DEBUG] TMDatabase.search_all: Calling fuzzy search with source_lang={self.source_lang}, target_lang={self.target_lang}")
|
|
245
237
|
fuzzy_matches = self.db.search_fuzzy_matches(
|
|
246
238
|
source=source,
|
|
247
239
|
tm_ids=tm_ids,
|
|
@@ -250,8 +242,7 @@ class TMDatabase:
|
|
|
250
242
|
source_lang=self.source_lang,
|
|
251
243
|
target_lang=self.target_lang
|
|
252
244
|
)
|
|
253
|
-
|
|
254
|
-
|
|
245
|
+
|
|
255
246
|
# Format matches for UI
|
|
256
247
|
formatted_matches = []
|
|
257
248
|
for match in fuzzy_matches:
|
|
@@ -1676,13 +1676,6 @@ class TranslationResultsPanel(QWidget):
|
|
|
1676
1676
|
Args:
|
|
1677
1677
|
matches_dict: Dict with keys like "NT", "MT", "TM", "Termbases"
|
|
1678
1678
|
"""
|
|
1679
|
-
print(f"🎯 TranslationResultsPanel.set_matches() called with matches_dict keys: {list(matches_dict.keys())}")
|
|
1680
|
-
for match_type, matches in matches_dict.items():
|
|
1681
|
-
print(f" {match_type}: {len(matches)} matches")
|
|
1682
|
-
if match_type == "Termbases" and matches:
|
|
1683
|
-
for i, match in enumerate(matches[:2]): # Show first 2 for debugging
|
|
1684
|
-
print(f" [{i}] {match.source} → {match.target}")
|
|
1685
|
-
|
|
1686
1679
|
# Ensure CompactMatchItem has current theme_manager
|
|
1687
1680
|
if self.theme_manager:
|
|
1688
1681
|
CompactMatchItem.theme_manager = self.theme_manager
|
|
@@ -367,7 +367,7 @@ class UnifiedPromptLibrary:
|
|
|
367
367
|
|
|
368
368
|
self.active_primary_prompt = self.prompts[relative_path]['content']
|
|
369
369
|
self.active_primary_prompt_path = relative_path
|
|
370
|
-
self.log(f"✓ Set
|
|
370
|
+
self.log(f"✓ Set custom prompt: {self.prompts[relative_path].get('name', relative_path)}")
|
|
371
371
|
return True
|
|
372
372
|
|
|
373
373
|
def set_external_primary_prompt(self, file_path: str) -> Tuple[bool, str]:
|
|
@@ -399,7 +399,7 @@ class UnifiedPromptLibrary:
|
|
|
399
399
|
self.active_primary_prompt = content
|
|
400
400
|
self.active_primary_prompt_path = f"[EXTERNAL] {file_path}"
|
|
401
401
|
|
|
402
|
-
self.log(f"✓ Set external
|
|
402
|
+
self.log(f"✓ Set external custom prompt: {display_name}")
|
|
403
403
|
return True, display_name
|
|
404
404
|
|
|
405
405
|
def attach_prompt(self, relative_path: str) -> bool:
|
|
@@ -1566,9 +1566,9 @@ class UnifiedPromptManagerQt:
|
|
|
1566
1566
|
|
|
1567
1567
|
layout.addWidget(mode_frame)
|
|
1568
1568
|
|
|
1569
|
-
#
|
|
1569
|
+
# Custom Prompt
|
|
1570
1570
|
primary_layout = QHBoxLayout()
|
|
1571
|
-
primary_label = QLabel("
|
|
1571
|
+
primary_label = QLabel("Custom Prompt ⭐:")
|
|
1572
1572
|
primary_label.setFont(QFont("Segoe UI", 9, QFont.Weight.Bold))
|
|
1573
1573
|
primary_layout.addWidget(primary_label)
|
|
1574
1574
|
|
|
@@ -2210,8 +2210,8 @@ class UnifiedPromptManagerQt:
|
|
|
2210
2210
|
if data['type'] == 'prompt':
|
|
2211
2211
|
path = data['path']
|
|
2212
2212
|
|
|
2213
|
-
# Set as
|
|
2214
|
-
action_primary = menu.addAction("⭐ Set as
|
|
2213
|
+
# Set as custom prompt
|
|
2214
|
+
action_primary = menu.addAction("⭐ Set as Custom Prompt")
|
|
2215
2215
|
action_primary.triggered.connect(lambda: self._set_primary_prompt(path))
|
|
2216
2216
|
|
|
2217
2217
|
# Attach/detach
|
|
@@ -2372,8 +2372,20 @@ class UnifiedPromptManagerQt:
|
|
|
2372
2372
|
else:
|
|
2373
2373
|
# Name unchanged, just update in place
|
|
2374
2374
|
if self.library.save_prompt(path, prompt_data):
|
|
2375
|
+
# Refresh active prompts if this prompt is currently active or attached
|
|
2376
|
+
# This ensures "Preview Combined" shows the updated content immediately
|
|
2377
|
+
if self.library.active_primary_prompt_path == path:
|
|
2378
|
+
# Update cached primary prompt content
|
|
2379
|
+
self.library.active_primary_prompt = self.library.prompts[path]['content']
|
|
2380
|
+
|
|
2381
|
+
if path in self.library.attached_prompt_paths:
|
|
2382
|
+
# Update cached attached prompt content
|
|
2383
|
+
idx = self.library.attached_prompt_paths.index(path)
|
|
2384
|
+
self.library.attached_prompts[idx] = self.library.prompts[path]['content']
|
|
2385
|
+
|
|
2375
2386
|
QMessageBox.information(self.main_widget, "Saved", "Prompt updated successfully!")
|
|
2376
2387
|
self._refresh_tree()
|
|
2388
|
+
self._update_attached_list() # Refresh attached list to show updated names
|
|
2377
2389
|
else:
|
|
2378
2390
|
QMessageBox.warning(self.main_widget, "Error", "Failed to save prompt")
|
|
2379
2391
|
else:
|
|
@@ -2493,7 +2505,7 @@ class UnifiedPromptManagerQt:
|
|
|
2493
2505
|
self.library.active_primary_prompt_path = None
|
|
2494
2506
|
self.primary_prompt_label.setText("[None selected]")
|
|
2495
2507
|
self.primary_prompt_label.setStyleSheet("color: #999;")
|
|
2496
|
-
self.log_message("✓ Cleared
|
|
2508
|
+
self.log_message("✓ Cleared custom prompt")
|
|
2497
2509
|
|
|
2498
2510
|
def _load_external_primary_prompt(self):
|
|
2499
2511
|
"""Load an external prompt file (not in library) as primary"""
|
|
@@ -2787,7 +2799,7 @@ class UnifiedPromptManagerQt:
|
|
|
2787
2799
|
composition_parts.append(f"📏 Total prompt length: {len(combined):,} characters")
|
|
2788
2800
|
|
|
2789
2801
|
if self.library.active_primary_prompt:
|
|
2790
|
-
composition_parts.append(f"✓
|
|
2802
|
+
composition_parts.append(f"✓ Custom prompt attached")
|
|
2791
2803
|
|
|
2792
2804
|
if self.library.attached_prompts:
|
|
2793
2805
|
composition_parts.append(f"✓ {len(self.library.attached_prompts)} additional prompt(s) attached")
|
|
@@ -2993,49 +3005,66 @@ If the text refers to figures (e.g., 'Figure 1A'), relevant images may be provid
|
|
|
2993
3005
|
|
|
2994
3006
|
# === Prompt Composition (for translation) ===
|
|
2995
3007
|
|
|
2996
|
-
def build_final_prompt(self, source_text: str, source_lang: str, target_lang: str,
|
|
3008
|
+
def build_final_prompt(self, source_text: str, source_lang: str, target_lang: str,
|
|
3009
|
+
mode: str = None, glossary_terms: list = None) -> str:
|
|
2997
3010
|
"""
|
|
2998
3011
|
Build final prompt for translation using 2-layer architecture:
|
|
2999
3012
|
1. System Prompt (auto-selected by mode)
|
|
3000
3013
|
2. Combined prompts from library (primary + attached)
|
|
3001
|
-
|
|
3014
|
+
3. Glossary terms (optional, injected before translation delimiter)
|
|
3015
|
+
|
|
3002
3016
|
Args:
|
|
3003
3017
|
source_text: Text to translate
|
|
3004
3018
|
source_lang: Source language
|
|
3005
3019
|
target_lang: Target language
|
|
3006
3020
|
mode: Override mode (if None, uses self.current_mode)
|
|
3007
|
-
|
|
3021
|
+
glossary_terms: Optional list of term dicts with 'source_term' and 'target_term' keys
|
|
3022
|
+
|
|
3008
3023
|
Returns:
|
|
3009
3024
|
Complete prompt ready for LLM
|
|
3010
3025
|
"""
|
|
3011
3026
|
if mode is None:
|
|
3012
3027
|
mode = self.current_mode
|
|
3013
|
-
|
|
3028
|
+
|
|
3014
3029
|
# Layer 1: System Prompt
|
|
3015
3030
|
system_template = self.get_system_template(mode)
|
|
3016
|
-
|
|
3031
|
+
|
|
3017
3032
|
# Replace placeholders in system prompt
|
|
3018
3033
|
system_template = system_template.replace("{{SOURCE_LANGUAGE}}", source_lang)
|
|
3019
3034
|
system_template = system_template.replace("{{TARGET_LANGUAGE}}", target_lang)
|
|
3020
3035
|
system_template = system_template.replace("{{SOURCE_TEXT}}", source_text)
|
|
3021
|
-
|
|
3036
|
+
|
|
3022
3037
|
# Layer 2: Library prompts (primary + attached)
|
|
3023
3038
|
library_prompts = ""
|
|
3024
|
-
|
|
3039
|
+
|
|
3025
3040
|
if self.library.active_primary_prompt:
|
|
3026
|
-
library_prompts += "\n\n#
|
|
3041
|
+
library_prompts += "\n\n# CUSTOM PROMPT\n\n"
|
|
3027
3042
|
library_prompts += self.library.active_primary_prompt
|
|
3028
|
-
|
|
3043
|
+
|
|
3029
3044
|
for attached_content in self.library.attached_prompts:
|
|
3030
3045
|
library_prompts += "\n\n# ADDITIONAL INSTRUCTIONS\n\n"
|
|
3031
3046
|
library_prompts += attached_content
|
|
3032
|
-
|
|
3047
|
+
|
|
3033
3048
|
# Combine
|
|
3034
3049
|
final_prompt = system_template + library_prompts
|
|
3035
|
-
|
|
3050
|
+
|
|
3051
|
+
# Glossary injection (if terms provided)
|
|
3052
|
+
if glossary_terms:
|
|
3053
|
+
final_prompt += "\n\n# GLOSSARY\n\n"
|
|
3054
|
+
final_prompt += "Use these approved terms in your translation:\n\n"
|
|
3055
|
+
for term in glossary_terms:
|
|
3056
|
+
source_term = term.get('source_term', '')
|
|
3057
|
+
target_term = term.get('target_term', '')
|
|
3058
|
+
if source_term and target_term:
|
|
3059
|
+
# Mark forbidden terms
|
|
3060
|
+
if term.get('forbidden'):
|
|
3061
|
+
final_prompt += f"- {source_term} → ⚠️ DO NOT USE: {target_term}\n"
|
|
3062
|
+
else:
|
|
3063
|
+
final_prompt += f"- {source_term} → {target_term}\n"
|
|
3064
|
+
|
|
3036
3065
|
# Add translation delimiter
|
|
3037
3066
|
final_prompt += "\n\n**YOUR TRANSLATION (provide ONLY the translated text, no numbering or labels):**\n"
|
|
3038
|
-
|
|
3067
|
+
|
|
3039
3068
|
return final_prompt
|
|
3040
3069
|
|
|
3041
3070
|
# ============================================================================
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: supervertaler
|
|
3
|
+
Version: 1.9.190
|
|
4
|
+
Summary: Professional AI-enhanced translation workbench with multi-LLM support, glossary system, TM, spellcheck, voice commands, and PyQt6 interface. Batteries included (core).
|
|
5
|
+
Home-page: https://supervertaler.com
|
|
6
|
+
Author: Michael Beijer
|
|
7
|
+
Author-email: Michael Beijer <info@michaelbeijer.co.uk>
|
|
8
|
+
Maintainer-email: Michael Beijer <info@michaelbeijer.co.uk>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Project-URL: Homepage, https://supervertaler.com
|
|
11
|
+
Project-URL: Repository, https://github.com/michaelbeijer/Supervertaler.git
|
|
12
|
+
Project-URL: Bug Tracker, https://github.com/michaelbeijer/Supervertaler/issues
|
|
13
|
+
Project-URL: Changelog, https://github.com/michaelbeijer/Supervertaler/blob/main/CHANGELOG.md
|
|
14
|
+
Project-URL: Documentation, https://github.com/michaelbeijer/Supervertaler/blob/main/AGENTS.md
|
|
15
|
+
Project-URL: Author Website, https://michaelbeijer.co.uk
|
|
16
|
+
Keywords: translation,CAT,CAT-tool,AI,LLM,GPT,Claude,Gemini,Ollama,glossary,termbase,translation-memory,TM,PyQt6,localization,memoQ,Trados,SDLPPX,XLIFF,voice-commands,spellcheck
|
|
17
|
+
Classifier: Development Status :: 4 - Beta
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Operating System :: OS Independent
|
|
23
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
24
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
25
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
26
|
+
Classifier: Topic :: Office/Business
|
|
27
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
28
|
+
Classifier: Environment :: X11 Applications :: Qt
|
|
29
|
+
Requires-Python: >=3.10
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: PyQt6>=6.5.0
|
|
33
|
+
Requires-Dist: PyQt6-WebEngine>=6.5.0
|
|
34
|
+
Requires-Dist: python-docx>=0.8.11
|
|
35
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
36
|
+
Requires-Dist: Pillow>=10.0.0
|
|
37
|
+
Requires-Dist: lxml>=4.9.0
|
|
38
|
+
Requires-Dist: openai>=1.0.0
|
|
39
|
+
Requires-Dist: anthropic>=0.7.0
|
|
40
|
+
Requires-Dist: google-generativeai>=0.3.0
|
|
41
|
+
Requires-Dist: requests>=2.28.0
|
|
42
|
+
Requires-Dist: markitdown>=0.0.1
|
|
43
|
+
Requires-Dist: sacrebleu>=2.3.1
|
|
44
|
+
Requires-Dist: pyperclip>=1.8.2
|
|
45
|
+
Requires-Dist: chardet>=5.0.0
|
|
46
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
47
|
+
Requires-Dist: markdown>=3.4.0
|
|
48
|
+
Requires-Dist: pyspellchecker>=0.7.0
|
|
49
|
+
Requires-Dist: sounddevice>=0.4.6
|
|
50
|
+
Requires-Dist: numpy>=1.24.0
|
|
51
|
+
Requires-Dist: PyMuPDF>=1.23.0
|
|
52
|
+
Requires-Dist: boto3>=1.28.0
|
|
53
|
+
Requires-Dist: deepl>=1.15.0
|
|
54
|
+
Requires-Dist: spylls>=0.1.7
|
|
55
|
+
Requires-Dist: keyboard>=0.13.5; platform_system == "Windows"
|
|
56
|
+
Requires-Dist: ahk>=1.0.0; platform_system == "Windows"
|
|
57
|
+
Requires-Dist: pyautogui>=0.9.54; platform_system == "Windows"
|
|
58
|
+
Requires-Dist: psutil>=5.9.0
|
|
59
|
+
Provides-Extra: local-whisper
|
|
60
|
+
Requires-Dist: openai-whisper>=20230314; extra == "local-whisper"
|
|
61
|
+
Provides-Extra: voice
|
|
62
|
+
Provides-Extra: web
|
|
63
|
+
Provides-Extra: pdf
|
|
64
|
+
Provides-Extra: mt
|
|
65
|
+
Provides-Extra: hunspell
|
|
66
|
+
Provides-Extra: windows
|
|
67
|
+
Provides-Extra: core
|
|
68
|
+
Provides-Extra: all
|
|
69
|
+
Dynamic: author
|
|
70
|
+
Dynamic: home-page
|
|
71
|
+
Dynamic: license-file
|
|
72
|
+
Dynamic: requires-python
|
|
73
|
+
|
|
74
|
+
# Supervertaler
|
|
75
|
+
|
|
76
|
+
[](https://pypi.org/project/Supervertaler/)
|
|
77
|
+
[](https://www.python.org/downloads/)
|
|
78
|
+
[](https://opensource.org/licenses/MIT)
|
|
79
|
+
|
|
80
|
+
**Professional AI-enhanced translation workbench** with multi-LLM support (GPT-4, Claude, Gemini, Ollama), translation memory, glossary management, and seamless CAT tool integration (memoQ, Trados, CafeTran, Phrase, Déjà Vu).
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install supervertaler
|
|
88
|
+
supervertaler
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or run from source:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
git clone https://github.com/michaelbeijer/Supervertaler.git
|
|
95
|
+
cd Supervertaler
|
|
96
|
+
pip install -r requirements.txt
|
|
97
|
+
python Supervertaler.py
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Key Features
|
|
103
|
+
|
|
104
|
+
- **Multi-LLM AI Translation** - OpenAI GPT-4/5, Anthropic Claude, Google Gemini, Local Ollama
|
|
105
|
+
- **Translation Memory** - Fuzzy matching TM with TMX import/export
|
|
106
|
+
- **Glossary System** - Priority-based term highlighting with forbidden term marking
|
|
107
|
+
- **Superlookup** - Unified concordance search across TM, glossaries, MT, and web resources
|
|
108
|
+
- **CAT Tool Integration** - memoQ XLIFF, Trados SDLPPX/SDLRPX, CafeTran, Phrase, Déjà Vu X3
|
|
109
|
+
- **Voice Commands** - Hands-free translation with OpenAI Whisper
|
|
110
|
+
- **Document Support** - DOCX, bilingual DOCX, PDF, Markdown, plain text
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Documentation
|
|
115
|
+
|
|
116
|
+
| Resource | Description |
|
|
117
|
+
|----------|-------------|
|
|
118
|
+
| [Online Manual](https://supervertaler.gitbook.io/superdocs/) | Quick start, guides, and troubleshooting |
|
|
119
|
+
| [Changelog](CHANGELOG.md) | Complete version history |
|
|
120
|
+
| [Keyboard Shortcuts](docs/guides/KEYBOARD_SHORTCUTS.md) | Shortcut reference |
|
|
121
|
+
| [FAQ](FAQ.md) | Common questions |
|
|
122
|
+
| [Website](https://supervertaler.com) | Project homepage |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Requirements
|
|
127
|
+
|
|
128
|
+
- Python 3.10+
|
|
129
|
+
- PyQt6
|
|
130
|
+
- Windows, macOS, or Linux
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Contributing
|
|
135
|
+
|
|
136
|
+
- [Report bugs](https://github.com/michaelbeijer/Supervertaler/issues)
|
|
137
|
+
- [Request features](https://github.com/michaelbeijer/Supervertaler/discussions)
|
|
138
|
+
- [Contributing guide](CONTRIBUTING.md)
|
|
139
|
+
- [Stargazers](https://github.com/michaelbeijer/Supervertaler/stargazers) - See who's starred the project
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## About
|
|
144
|
+
|
|
145
|
+
**Supervertaler** is maintained by [Michael Beijer](https://michaelbeijer.co.uk), a professional translator with 30 years of experience in technical and patent translation.
|
|
146
|
+
|
|
147
|
+
**License:** MIT - Free for personal and commercial use.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
**Current Version:** See [CHANGELOG.md](CHANGELOG.md) for the latest release notes.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Supervertaler.py,sha256=
|
|
1
|
+
Supervertaler.py,sha256=EjPOZoI4N8Z7kG1YGrvGrPoZIYf9mjx7Tz6gu4mZWag,2407002
|
|
2
2
|
modules/__init__.py,sha256=G58XleS-EJ2sX4Kehm-3N2m618_W2Es0Kg8CW_eBG7g,327
|
|
3
3
|
modules/ai_actions.py,sha256=i5MJcM-7Y6CAvKUwxmxrVHeoZAVtAP7aRDdWM5KLkO0,33877
|
|
4
4
|
modules/ai_attachment_manager.py,sha256=juZlrW3UPkIkcnj0SREgOQkQROLf0fcu3ShZcKXMxsI,11361
|
|
@@ -6,14 +6,15 @@ modules/ai_file_viewer_dialog.py,sha256=lKKqUUlOEVgHmmu6aRxqH7P6ds-7dRLk4ltDyjCw
|
|
|
6
6
|
modules/autofingers_engine.py,sha256=eJ7tBi7YJvTToe5hYTfnyGXB-qme_cHrOPZibaoR2Xw,17061
|
|
7
7
|
modules/cafetran_docx_handler.py,sha256=_F7Jh0WPVaDnMhdxEsVSXuD1fN9r-S_V6i0gr86Pdfc,14076
|
|
8
8
|
modules/config_manager.py,sha256=MkPY3xVFgFDkcwewLREg4BfyKueO0OJkT1cTLxehcjM,17894
|
|
9
|
-
modules/database_manager.py,sha256=
|
|
10
|
-
modules/database_migrations.py,sha256=
|
|
9
|
+
modules/database_manager.py,sha256=yNtaJNAKtICBBSc5iyhIufzDn25k7OqkOuFeojmWuM4,87319
|
|
10
|
+
modules/database_migrations.py,sha256=tndJ4wV_2JBfPggMgO1tQRwdfRFZ9zwvADllCZE9CCk,15663
|
|
11
11
|
modules/dejavurtf_handler.py,sha256=8NZPPYtHga40SZCypHjPoJPmZTvm9rD-eEUUab7mjtg,28156
|
|
12
12
|
modules/document_analyzer.py,sha256=t1rVvqLaTcpQTEja228C7zZnh8dXshK4wA9t1E9aGVk,19524
|
|
13
13
|
modules/docx_handler.py,sha256=jSlZs5tollJnsnIA80buEXLfZBunp_GQ9lCtFZPUnBs,34053
|
|
14
14
|
modules/encoding_repair.py,sha256=mrQSOMW-tvaowLja_gtXDJv3Qw4YhLCLsuZtBS0BVms,11900
|
|
15
15
|
modules/encoding_repair_Qt.py,sha256=UosUOpMnXf0gkgp_F_4wszd25Op_3X_4h9kWMomK0Sk,17533
|
|
16
16
|
modules/encoding_repair_ui.py,sha256=bRnyFg-5_Lz-bZoNT5eivM5LKsQgpoxbllsffYW3fAg,18619
|
|
17
|
+
modules/extract_tm.py,sha256=ix58ti1_Zkd2dxIx1PwN8rWxmYHGv2zsUPrT-VfcMwA,18228
|
|
17
18
|
modules/feature_manager.py,sha256=toQnOjeCPMTWp3sas-J88-ZAomFdoeRLKPc19C-6XFo,12416
|
|
18
19
|
modules/figure_context_manager.py,sha256=hy7h5PcvN8qr8nQtDS41YvTwr6gecWvmjzMBP43_LM8,12682
|
|
19
20
|
modules/file_dialog_helper.py,sha256=0dthKAFBkoQkdGm_zsYQMImn31Z9Bc9-VKNV5idZtkQ,3767
|
|
@@ -28,12 +29,13 @@ modules/llm_superbench_ui.py,sha256=lmzsL8lt0KzFw-z8De1zb49Emnv7f1dZv_DJmoQz0bQ,
|
|
|
28
29
|
modules/local_llm_setup.py,sha256=33y-D_LKzkn2w8ejyjeKaovf_An6xQ98mKISoqe-Qjc,42661
|
|
29
30
|
modules/model_update_dialog.py,sha256=kEg0FuO1N-uj6QY5ZIj-FqdiLQuPuAY48pbuwT0HUGI,13113
|
|
30
31
|
modules/model_version_checker.py,sha256=41g7gcWvyrKPYeobaOGCMZLwAHgQmFwVF8zokodKae8,12741
|
|
31
|
-
modules/mqxliff_handler.py,sha256
|
|
32
|
+
modules/mqxliff_handler.py,sha256=TVtrf7ieGoxfoLxy4v4S7by9YImKypw1EY0wFpZO3Lo,28792
|
|
32
33
|
modules/non_translatables_manager.py,sha256=izorabiX6rSQzuBIvnY67wmu5vd85SbzexXccbmwPs4,27465
|
|
33
34
|
modules/pdf_rescue_Qt.py,sha256=9W_M0Zms4miapQbrqm-viHNCpaW39GL9VaKKFCJxpnE,80479
|
|
34
35
|
modules/pdf_rescue_tkinter.py,sha256=a4R_OUnn7X5O_XMR1roybrdu1aXoGCwwO-mwYB2ZpOg,39606
|
|
35
36
|
modules/phrase_docx_handler.py,sha256=7vJNbvxxURzdcinZ3rkqyJ-7Y5O1NpVL4Lvu9NuGFjQ,18598
|
|
36
37
|
modules/project_home_panel.py,sha256=P0PgMnoPp6WEiGrfq8cNJNEdxO83aHQDdXzRLqF173w,6810
|
|
38
|
+
modules/project_tm.py,sha256=TQUc9ApZjfiKZlA4bc1PrHwtEtk_XM9XArCd53De_20,12327
|
|
37
39
|
modules/prompt_assistant.py,sha256=shkZqNTvyQKNDO_9aFEu1_gN0zQq0fR5krXkWfnTR2Y,13150
|
|
38
40
|
modules/prompt_library.py,sha256=t5w4cqB6_Sin4BQDVNALKpfB1EN_oaDeHFwlHxILLSY,26894
|
|
39
41
|
modules/prompt_library_migration.py,sha256=fv3RHhe2-EnH50XW5tyTWy0YP_KJ2EsESuTxR8klfmI,17639
|
|
@@ -47,7 +49,7 @@ modules/spellcheck_manager.py,sha256=jwduHJ66pOKv1MtzSAltxpP8LPgz11FvF6j8h7BiRZY
|
|
|
47
49
|
modules/statuses.py,sha256=t6TCA9pNZHDw3SbKTxT73uKezJhwWk9gFLr0NOgEufs,6911
|
|
48
50
|
modules/style_guide_manager.py,sha256=QBvbygF2E-cgRuwJPWmIatwQl37voU1FErYnycv8Sac,10809
|
|
49
51
|
modules/superbench_ui.py,sha256=O8pSb-R8Mul1Qz9-8gbBrFR1j7Z8b8_G0wSTSLSCf2U,55563
|
|
50
|
-
modules/superbrowser.py,sha256=
|
|
52
|
+
modules/superbrowser.py,sha256=jZw7jNOJyZdLTOVcbAdALH1MYiP43qi_4qGzNWxJ2Hs,13481
|
|
51
53
|
modules/supercleaner.py,sha256=uRJAEh03Eu4Qtujrf_jxuIyPBbcxKAZzryhV9Chi9ro,22939
|
|
52
54
|
modules/supercleaner_ui.py,sha256=sVTnYxlX9R20eWs52BKCeQ15iFVFOIQEl29L72lup1c,18812
|
|
53
55
|
modules/superdocs.py,sha256=vMYyUbHU8zDkS1YMSDF7niDkT8d8FJFDcfIxSktCyGc,522
|
|
@@ -58,28 +60,28 @@ modules/tag_manager.py,sha256=g66S0JSxdguN9AhWzZG3hsIz87Ul51wQ3c2wOCTZVSk,12789
|
|
|
58
60
|
modules/term_extractor.py,sha256=qPvKNCVXFTGEGwXNvvC0cfCmdb5c3WhzE38EOgKdKUI,11253
|
|
59
61
|
modules/termbase_entry_editor.py,sha256=iWO9CgLjMomGAqBXDsGAX7TFJvDOp2s_taS4gBL1rZY,35818
|
|
60
62
|
modules/termbase_import_export.py,sha256=16IAY04IS_rgt0GH5UOUzUI5NoqAli4JMfMquxmFBm0,23552
|
|
61
|
-
modules/termbase_manager.py,sha256
|
|
62
|
-
modules/termview_widget.py,sha256=
|
|
63
|
-
modules/theme_manager.py,sha256=
|
|
63
|
+
modules/termbase_manager.py,sha256=XAVrz-wt8jKcjoD6ocHoXewY5PN0A0GeqFEctsv0jS8,48697
|
|
64
|
+
modules/termview_widget.py,sha256=FsNnSWh86PCnmKcC3fFJS8MJNdVvRpgE5e8-u4jAosY,55742
|
|
65
|
+
modules/theme_manager.py,sha256=Qk_jfCmfm7fjdMAOyBHpD18w3MiRfWBZk0cHTw6yAAg,18639
|
|
64
66
|
modules/tm_editor_dialog.py,sha256=AzGwq4QW641uFJdF8DljLTRRp4FLoYX3Pe4rlTjQWNg,3517
|
|
65
67
|
modules/tm_manager_qt.py,sha256=h2bvXkRuboHf_RRz9-5FX35GVRlpXgRDWeXyj1QWtPs,54406
|
|
66
|
-
modules/tm_metadata_manager.py,sha256=
|
|
68
|
+
modules/tm_metadata_manager.py,sha256=NTsaI_YjQnVOpU_scAwK9uR1Tcl9pzKD1GwLVy7sx2g,23590
|
|
67
69
|
modules/tmx_editor.py,sha256=n0CtdZI8f1fPRWmCqz5Ysxbnp556Qj-6Y56a-YIz6pY,59239
|
|
68
70
|
modules/tmx_editor_qt.py,sha256=PxBIUw_06PHYTBHsd8hZzVJXW8T0A0ljfz1Wjjsa4yU,117022
|
|
69
71
|
modules/tmx_generator.py,sha256=pNkxwdMLvSRMMru0lkB1gvViIpg9BQy1EVhRbwoef3k,9426
|
|
70
72
|
modules/tracked_changes.py,sha256=S_BIEC6r7wVAwjG42aSy_RgH4KaMAC8GS5thEvqrYdE,39480
|
|
71
73
|
modules/trados_docx_handler.py,sha256=VPRAQ73cUHs_SEj6x81z1PmSxfjnwPBp9P4fXeK3KpQ,16363
|
|
72
|
-
modules/translation_memory.py,sha256=
|
|
73
|
-
modules/translation_results_panel.py,sha256=
|
|
74
|
+
modules/translation_memory.py,sha256=LnG8csZNL2GTHXT4zk0uecJEtvRc-MKwv7Pt7EX3s7s,28002
|
|
75
|
+
modules/translation_results_panel.py,sha256=OWqzV9xmJOi8NGCi3h42nq-qE7-v6WStjQWRsghCVbQ,92044
|
|
74
76
|
modules/translation_services.py,sha256=lyVpWuZK1wtVtYZMDMdLoq1DHBoSaeAnp-Yejb0TlVQ,10530
|
|
75
|
-
modules/unified_prompt_library.py,sha256=
|
|
76
|
-
modules/unified_prompt_manager_qt.py,sha256=
|
|
77
|
+
modules/unified_prompt_library.py,sha256=96u4WlMwnmmhD4uNJHZ-qVQj8v9_8dA2AVCWpBcwTrg,26006
|
|
78
|
+
modules/unified_prompt_manager_qt.py,sha256=HkGUnH0wlfxt-hVe-nKCeWLyProYdefuuq2slqv8hI4,172160
|
|
77
79
|
modules/voice_commands.py,sha256=iBb-gjWxRMLhFH7-InSRjYJz1EIDBNA2Pog8V7TtJaY,38516
|
|
78
80
|
modules/voice_dictation.py,sha256=QmitXfkG-vRt5hIQATjphHdhXfqmwhzcQcbXB6aRzIg,16386
|
|
79
81
|
modules/voice_dictation_lite.py,sha256=jorY0BmWE-8VczbtGrWwt1zbnOctMoSlWOsQrcufBcc,9423
|
|
80
|
-
supervertaler-1.9.
|
|
81
|
-
supervertaler-1.9.
|
|
82
|
-
supervertaler-1.9.
|
|
83
|
-
supervertaler-1.9.
|
|
84
|
-
supervertaler-1.9.
|
|
85
|
-
supervertaler-1.9.
|
|
82
|
+
supervertaler-1.9.190.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
|
|
83
|
+
supervertaler-1.9.190.dist-info/METADATA,sha256=Za8JXAiHISPtQVhgL3IVE8gCqJhWEiP-3todaiG_84M,5725
|
|
84
|
+
supervertaler-1.9.190.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
85
|
+
supervertaler-1.9.190.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
|
|
86
|
+
supervertaler-1.9.190.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
|
|
87
|
+
supervertaler-1.9.190.dist-info/RECORD,,
|