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.
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}")
@@ -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
- print(f"[DEBUG] TMDatabase.search_all: No tm_ids provided, using from metadata: {tm_ids}")
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
- print(f"[DEBUG] TMDatabase.search_all: Exact match result: {exact_match}")
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
- print(f"[DEBUG] TMDatabase.search_all: Fuzzy search returned {len(fuzzy_matches)} matches")
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 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
@@ -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 primary prompt")
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"✓ Primary prompt attached")
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, mode: str = None) -> 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# PRIMARY INSTRUCTIONS\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
+ [![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=Hzzf8r4zCdvk8adLVgitzsdVxrOr406AppDlUc-HMJ4,2286978
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=XbOHaUWq8kdHnT6ZgqPEfA6I3fL5cYiaVXkhYRohi1E,81627
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
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=-tUFubKsxkx6nW7aMDhuDaTBcVTPtVHV1HyzF0TbkVo,26205
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=FAEaYTMiklXizYm3iPt83kp254bCGrFHRsGHduedKYI,12759
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=-PlGF6fIA7KYCteoQ8FZ_0SQZNRRBFAtLimHPbmhQ6w,44544
62
- modules/termview_widget.py,sha256=O3ah7g-4Lb_iUctxl9sMyxh8V3A5I5PFxmy9iIH2Kgk,53484
63
- modules/theme_manager.py,sha256=EOI_5pM2bXAadw08bbl92TLN-w28lbw4Zi1E8vQ-kM0,16694
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=_drzJ80q-mr1y9La8t0Cb8MXh91n3I-lEGDwSmoVI_4,23161
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=13PDK4_kgYrWTACWBIBypOh2DvoxY9cRT8U6ulilbh4,28739
73
- modules/translation_results_panel.py,sha256=DmEe0pZRSfcZFg2cWeEREK7H9vrTcPkgeuMW54Pgrys,92505
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=lzbevgjUz_qCiYSf141BB0mmuaDhSsevWju_a7welu0,26008
76
- modules/unified_prompt_manager_qt.py,sha256=fyF3_r0N8hnImT-CcWo1AuBOQ1Dn_ExeeUCkZxeSkqE,170422
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.173.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
- supervertaler-1.9.173.dist-info/METADATA,sha256=4y6XCqM8xqGT4T9dLHXLn4-KUDkYRAqJ_zsoZUHGW8k,48701
82
- supervertaler-1.9.173.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
- supervertaler-1.9.173.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
- supervertaler-1.9.173.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
- supervertaler-1.9.173.dist-info/RECORD,,
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,,