supervertaler 1.9.172__py3-none-any.whl → 1.9.180__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.
@@ -409,7 +409,111 @@ class TermbaseManager:
409
409
  except Exception as e:
410
410
  self.log(f"✗ Error setting termbase read_only: {e}")
411
411
  return False
412
-
412
+
413
+ def get_termbase_ai_inject(self, termbase_id: int) -> bool:
414
+ """Get whether termbase terms should be injected into LLM prompts"""
415
+ try:
416
+ cursor = self.db_manager.cursor
417
+ cursor.execute("SELECT ai_inject FROM termbases WHERE id = ?", (termbase_id,))
418
+ result = cursor.fetchone()
419
+ return bool(result[0]) if result and result[0] else False
420
+ except Exception as e:
421
+ self.log(f"✗ Error getting termbase ai_inject: {e}")
422
+ return False
423
+
424
+ def set_termbase_ai_inject(self, termbase_id: int, ai_inject: bool) -> bool:
425
+ """Set whether termbase terms should be injected into LLM prompts"""
426
+ try:
427
+ cursor = self.db_manager.cursor
428
+ cursor.execute("""
429
+ UPDATE termbases SET ai_inject = ? WHERE id = ?
430
+ """, (1 if ai_inject else 0, termbase_id))
431
+ self.db_manager.connection.commit()
432
+ status = "enabled" if ai_inject else "disabled"
433
+ self.log(f"✓ AI injection {status} for termbase {termbase_id}")
434
+ return True
435
+ except Exception as e:
436
+ self.log(f"✗ Error setting termbase ai_inject: {e}")
437
+ return False
438
+
439
+ def get_ai_inject_termbases(self, project_id: Optional[int] = None) -> List[Dict]:
440
+ """
441
+ Get all termbases with ai_inject enabled that are active for the given project.
442
+
443
+ Args:
444
+ project_id: Project ID (0 or None for global)
445
+
446
+ Returns:
447
+ List of termbase dictionaries with all terms
448
+ """
449
+ try:
450
+ cursor = self.db_manager.cursor
451
+ proj_id = project_id if project_id else 0
452
+
453
+ cursor.execute("""
454
+ SELECT t.id, t.name, t.source_lang, t.target_lang
455
+ FROM termbases t
456
+ LEFT JOIN termbase_activation ta ON t.id = ta.termbase_id AND ta.project_id = ?
457
+ WHERE t.ai_inject = 1
458
+ AND (ta.is_active = 1 OR (t.is_global = 1 AND ta.is_active IS NULL))
459
+ ORDER BY ta.priority ASC, t.name ASC
460
+ """, (proj_id,))
461
+
462
+ termbases = []
463
+ for row in cursor.fetchall():
464
+ termbases.append({
465
+ 'id': row[0],
466
+ 'name': row[1],
467
+ 'source_lang': row[2],
468
+ 'target_lang': row[3]
469
+ })
470
+ return termbases
471
+ except Exception as e:
472
+ self.log(f"✗ Error getting AI inject termbases: {e}")
473
+ return []
474
+
475
+ def get_ai_inject_terms(self, project_id: Optional[int] = None) -> List[Dict]:
476
+ """
477
+ Get all terms from AI-inject-enabled termbases for the given project.
478
+
479
+ Args:
480
+ project_id: Project ID (0 or None for global)
481
+
482
+ Returns:
483
+ List of term dictionaries with source_term, target_term, forbidden, termbase_name
484
+ """
485
+ try:
486
+ # First get all AI-inject termbases
487
+ ai_termbases = self.get_ai_inject_termbases(project_id)
488
+ if not ai_termbases:
489
+ return []
490
+
491
+ all_terms = []
492
+ cursor = self.db_manager.cursor
493
+
494
+ for tb in ai_termbases:
495
+ cursor.execute("""
496
+ SELECT source_term, target_term, forbidden, priority
497
+ FROM termbase_terms
498
+ WHERE termbase_id = ?
499
+ ORDER BY priority ASC, source_term ASC
500
+ """, (tb['id'],))
501
+
502
+ for row in cursor.fetchall():
503
+ all_terms.append({
504
+ 'source_term': row[0],
505
+ 'target_term': row[1],
506
+ 'forbidden': bool(row[2]) if row[2] else False,
507
+ 'priority': row[3] or 99,
508
+ 'termbase_name': tb['name']
509
+ })
510
+
511
+ self.log(f"📚 Retrieved {len(all_terms)} terms from {len(ai_termbases)} AI-inject glossar{'y' if len(ai_termbases) == 1 else 'ies'}")
512
+ return all_terms
513
+ except Exception as e:
514
+ self.log(f"✗ Error getting AI inject terms: {e}")
515
+ return []
516
+
413
517
  def set_termbase_priority(self, termbase_id: int, project_id: int, priority: int) -> bool:
414
518
  """
415
519
  Set manual priority for a termbase in a specific project.
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};
@@ -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
- row = cursor.fetchone()
357
- if row:
358
- return bool(row[0])
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}")
@@ -123,8 +123,8 @@ class TMDatabase:
123
123
  if source_lang and target_lang:
124
124
  self.set_tm_languages(source_lang, target_lang)
125
125
 
126
- # Global fuzzy threshold (70% minimum similarity for fuzzy matches)
127
- self.fuzzy_threshold = 0.7
126
+ # Global fuzzy threshold (75% minimum similarity for fuzzy matches)
127
+ self.fuzzy_threshold = 0.75
128
128
 
129
129
  # TM metadata cache (populated from database as needed)
130
130
  # Note: Legacy 'project' and 'big_mama' TMs are no longer used.
@@ -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 primary prompt: {self.prompts[relative_path].get('name', relative_path)}")
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 primary prompt: {display_name}")
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
- # Primary Prompt
1569
+ # Custom Prompt
1570
1570
  primary_layout = QHBoxLayout()
1571
- primary_label = QLabel("Primary Prompt ⭐:")
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 primary
2214
- action_primary = menu.addAction("⭐ Set as Primary Prompt")
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
@@ -2493,7 +2493,7 @@ class UnifiedPromptManagerQt:
2493
2493
  self.library.active_primary_prompt_path = None
2494
2494
  self.primary_prompt_label.setText("[None selected]")
2495
2495
  self.primary_prompt_label.setStyleSheet("color: #999;")
2496
- self.log_message("✓ Cleared primary prompt")
2496
+ self.log_message("✓ Cleared custom prompt")
2497
2497
 
2498
2498
  def _load_external_primary_prompt(self):
2499
2499
  """Load an external prompt file (not in library) as primary"""
@@ -2787,7 +2787,7 @@ class UnifiedPromptManagerQt:
2787
2787
  composition_parts.append(f"📏 Total prompt length: {len(combined):,} characters")
2788
2788
 
2789
2789
  if self.library.active_primary_prompt:
2790
- composition_parts.append(f"✓ Primary prompt attached")
2790
+ composition_parts.append(f"✓ Custom prompt attached")
2791
2791
 
2792
2792
  if self.library.attached_prompts:
2793
2793
  composition_parts.append(f"✓ {len(self.library.attached_prompts)} additional prompt(s) attached")
@@ -2993,49 +2993,66 @@ If the text refers to figures (e.g., 'Figure 1A'), relevant images may be provid
2993
2993
 
2994
2994
  # === Prompt Composition (for translation) ===
2995
2995
 
2996
- def build_final_prompt(self, source_text: str, source_lang: str, target_lang: str, mode: str = None) -> str:
2996
+ def build_final_prompt(self, source_text: str, source_lang: str, target_lang: str,
2997
+ mode: str = None, glossary_terms: list = None) -> str:
2997
2998
  """
2998
2999
  Build final prompt for translation using 2-layer architecture:
2999
3000
  1. System Prompt (auto-selected by mode)
3000
3001
  2. Combined prompts from library (primary + attached)
3001
-
3002
+ 3. Glossary terms (optional, injected before translation delimiter)
3003
+
3002
3004
  Args:
3003
3005
  source_text: Text to translate
3004
3006
  source_lang: Source language
3005
3007
  target_lang: Target language
3006
3008
  mode: Override mode (if None, uses self.current_mode)
3007
-
3009
+ glossary_terms: Optional list of term dicts with 'source_term' and 'target_term' keys
3010
+
3008
3011
  Returns:
3009
3012
  Complete prompt ready for LLM
3010
3013
  """
3011
3014
  if mode is None:
3012
3015
  mode = self.current_mode
3013
-
3016
+
3014
3017
  # Layer 1: System Prompt
3015
3018
  system_template = self.get_system_template(mode)
3016
-
3019
+
3017
3020
  # Replace placeholders in system prompt
3018
3021
  system_template = system_template.replace("{{SOURCE_LANGUAGE}}", source_lang)
3019
3022
  system_template = system_template.replace("{{TARGET_LANGUAGE}}", target_lang)
3020
3023
  system_template = system_template.replace("{{SOURCE_TEXT}}", source_text)
3021
-
3024
+
3022
3025
  # Layer 2: Library prompts (primary + attached)
3023
3026
  library_prompts = ""
3024
-
3027
+
3025
3028
  if self.library.active_primary_prompt:
3026
- library_prompts += "\n\n# PRIMARY INSTRUCTIONS\n\n"
3029
+ library_prompts += "\n\n# CUSTOM PROMPT\n\n"
3027
3030
  library_prompts += self.library.active_primary_prompt
3028
-
3031
+
3029
3032
  for attached_content in self.library.attached_prompts:
3030
3033
  library_prompts += "\n\n# ADDITIONAL INSTRUCTIONS\n\n"
3031
3034
  library_prompts += attached_content
3032
-
3035
+
3033
3036
  # Combine
3034
3037
  final_prompt = system_template + library_prompts
3035
-
3038
+
3039
+ # Glossary injection (if terms provided)
3040
+ if glossary_terms:
3041
+ final_prompt += "\n\n# GLOSSARY\n\n"
3042
+ final_prompt += "Use these approved terms in your translation:\n\n"
3043
+ for term in glossary_terms:
3044
+ source_term = term.get('source_term', '')
3045
+ target_term = term.get('target_term', '')
3046
+ if source_term and target_term:
3047
+ # Mark forbidden terms
3048
+ if term.get('forbidden'):
3049
+ final_prompt += f"- {source_term} → ⚠️ DO NOT USE: {target_term}\n"
3050
+ else:
3051
+ final_prompt += f"- {source_term} → {target_term}\n"
3052
+
3036
3053
  # Add translation delimiter
3037
3054
  final_prompt += "\n\n**YOUR TRANSLATION (provide ONLY the translated text, no numbering or labels):**\n"
3038
-
3055
+
3039
3056
  return final_prompt
3040
3057
 
3041
3058
  # ============================================================================
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: supervertaler
3
+ Version: 1.9.180
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
+ [![PyPI version](https://badge.fury.io/py/supervertaler.svg)](https://pypi.org/project/Supervertaler/)
77
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
78
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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=QN11SrXGKNdKhXFRwgkumm6IRI6Q9vg_S2yj2Qbd3K4,2286283
1
+ Supervertaler.py,sha256=D49WGFvjGTFaQ2Tmgl5jInj3qIUwzHdxPCJRv4iTjvY,2324894
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,8 +6,8 @@ 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=ZdsiuwF67lh-FPKPdalWsW9t6IieX_FM0fA2Bca1xSQ,80221
10
- modules/database_migrations.py,sha256=Y1onFsLDV_6vzJLOpNy3WCZDohBZ2jc4prM-g2_RwLE,14085
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
@@ -28,7 +28,7 @@ modules/llm_superbench_ui.py,sha256=lmzsL8lt0KzFw-z8De1zb49Emnv7f1dZv_DJmoQz0bQ,
28
28
  modules/local_llm_setup.py,sha256=33y-D_LKzkn2w8ejyjeKaovf_An6xQ98mKISoqe-Qjc,42661
29
29
  modules/model_update_dialog.py,sha256=kEg0FuO1N-uj6QY5ZIj-FqdiLQuPuAY48pbuwT0HUGI,13113
30
30
  modules/model_version_checker.py,sha256=41g7gcWvyrKPYeobaOGCMZLwAHgQmFwVF8zokodKae8,12741
31
- modules/mqxliff_handler.py,sha256=-tUFubKsxkx6nW7aMDhuDaTBcVTPtVHV1HyzF0TbkVo,26205
31
+ modules/mqxliff_handler.py,sha256=TVtrf7ieGoxfoLxy4v4S7by9YImKypw1EY0wFpZO3Lo,28792
32
32
  modules/non_translatables_manager.py,sha256=izorabiX6rSQzuBIvnY67wmu5vd85SbzexXccbmwPs4,27465
33
33
  modules/pdf_rescue_Qt.py,sha256=9W_M0Zms4miapQbrqm-viHNCpaW39GL9VaKKFCJxpnE,80479
34
34
  modules/pdf_rescue_tkinter.py,sha256=a4R_OUnn7X5O_XMR1roybrdu1aXoGCwwO-mwYB2ZpOg,39606
@@ -58,28 +58,28 @@ modules/tag_manager.py,sha256=g66S0JSxdguN9AhWzZG3hsIz87Ul51wQ3c2wOCTZVSk,12789
58
58
  modules/term_extractor.py,sha256=qPvKNCVXFTGEGwXNvvC0cfCmdb5c3WhzE38EOgKdKUI,11253
59
59
  modules/termbase_entry_editor.py,sha256=iWO9CgLjMomGAqBXDsGAX7TFJvDOp2s_taS4gBL1rZY,35818
60
60
  modules/termbase_import_export.py,sha256=16IAY04IS_rgt0GH5UOUzUI5NoqAli4JMfMquxmFBm0,23552
61
- modules/termbase_manager.py,sha256=-PlGF6fIA7KYCteoQ8FZ_0SQZNRRBFAtLimHPbmhQ6w,44544
61
+ modules/termbase_manager.py,sha256=7KXEFab6y0o1EmFZwHs3ADklC95udVenxvrmN4XUoj0,48808
62
62
  modules/termview_widget.py,sha256=O3ah7g-4Lb_iUctxl9sMyxh8V3A5I5PFxmy9iIH2Kgk,53484
63
- modules/theme_manager.py,sha256=EOI_5pM2bXAadw08bbl92TLN-w28lbw4Zi1E8vQ-kM0,16694
63
+ modules/theme_manager.py,sha256=Qk_jfCmfm7fjdMAOyBHpD18w3MiRfWBZk0cHTw6yAAg,18639
64
64
  modules/tm_editor_dialog.py,sha256=AzGwq4QW641uFJdF8DljLTRRp4FLoYX3Pe4rlTjQWNg,3517
65
65
  modules/tm_manager_qt.py,sha256=h2bvXkRuboHf_RRz9-5FX35GVRlpXgRDWeXyj1QWtPs,54406
66
- modules/tm_metadata_manager.py,sha256=_drzJ80q-mr1y9La8t0Cb8MXh91n3I-lEGDwSmoVI_4,23161
66
+ modules/tm_metadata_manager.py,sha256=NTsaI_YjQnVOpU_scAwK9uR1Tcl9pzKD1GwLVy7sx2g,23590
67
67
  modules/tmx_editor.py,sha256=n0CtdZI8f1fPRWmCqz5Ysxbnp556Qj-6Y56a-YIz6pY,59239
68
68
  modules/tmx_editor_qt.py,sha256=PxBIUw_06PHYTBHsd8hZzVJXW8T0A0ljfz1Wjjsa4yU,117022
69
69
  modules/tmx_generator.py,sha256=pNkxwdMLvSRMMru0lkB1gvViIpg9BQy1EVhRbwoef3k,9426
70
70
  modules/tracked_changes.py,sha256=S_BIEC6r7wVAwjG42aSy_RgH4KaMAC8GS5thEvqrYdE,39480
71
71
  modules/trados_docx_handler.py,sha256=VPRAQ73cUHs_SEj6x81z1PmSxfjnwPBp9P4fXeK3KpQ,16363
72
- modules/translation_memory.py,sha256=k0GtO6ANTqxI1XMcv3D5mdAoTgcWlDT5iVsYHizKNUM,28738
72
+ modules/translation_memory.py,sha256=13PDK4_kgYrWTACWBIBypOh2DvoxY9cRT8U6ulilbh4,28739
73
73
  modules/translation_results_panel.py,sha256=DmEe0pZRSfcZFg2cWeEREK7H9vrTcPkgeuMW54Pgrys,92505
74
74
  modules/translation_services.py,sha256=lyVpWuZK1wtVtYZMDMdLoq1DHBoSaeAnp-Yejb0TlVQ,10530
75
- modules/unified_prompt_library.py,sha256=lzbevgjUz_qCiYSf141BB0mmuaDhSsevWju_a7welu0,26008
76
- modules/unified_prompt_manager_qt.py,sha256=fyF3_r0N8hnImT-CcWo1AuBOQ1Dn_ExeeUCkZxeSkqE,170422
75
+ modules/unified_prompt_library.py,sha256=96u4WlMwnmmhD4uNJHZ-qVQj8v9_8dA2AVCWpBcwTrg,26006
76
+ modules/unified_prompt_manager_qt.py,sha256=U89UFGG-M7BLetoaLAlma0x-n8SIyx682DhSvaRnzJs,171285
77
77
  modules/voice_commands.py,sha256=iBb-gjWxRMLhFH7-InSRjYJz1EIDBNA2Pog8V7TtJaY,38516
78
78
  modules/voice_dictation.py,sha256=QmitXfkG-vRt5hIQATjphHdhXfqmwhzcQcbXB6aRzIg,16386
79
79
  modules/voice_dictation_lite.py,sha256=jorY0BmWE-8VczbtGrWwt1zbnOctMoSlWOsQrcufBcc,9423
80
- supervertaler-1.9.172.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
- supervertaler-1.9.172.dist-info/METADATA,sha256=cDxLhwPym42J-CQfuc07BvtDUoj231w1Lyfz2_iIN6Y,48267
82
- supervertaler-1.9.172.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
- supervertaler-1.9.172.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
- supervertaler-1.9.172.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
- supervertaler-1.9.172.dist-info/RECORD,,
80
+ supervertaler-1.9.180.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
+ supervertaler-1.9.180.dist-info/METADATA,sha256=_RNcZZvz-EN-Kay2HuiR1hNQhRMVjPHZwNAjvnZ5t20,5725
82
+ supervertaler-1.9.180.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
+ supervertaler-1.9.180.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
+ supervertaler-1.9.180.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
+ supervertaler-1.9.180.dist-info/RECORD,,