supervertaler 1.9.131__py3-none-any.whl → 1.9.172__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.
@@ -515,6 +515,9 @@ class TermviewWidget(QWidget):
515
515
  self.current_target_lang = None
516
516
  self.current_project_id = None # Store project ID for termbase priority lookup
517
517
 
518
+ # Debug mode - disable verbose tokenization logging by default (performance)
519
+ self.debug_tokenize = False
520
+
518
521
  # Default font settings (will be updated from main app settings)
519
522
  self.current_font_family = "Segoe UI"
520
523
  self.current_font_size = 10
@@ -750,7 +753,10 @@ class TermviewWidget(QWidget):
750
753
  if not source_term or not target_term:
751
754
  continue
752
755
 
753
- key = source_term.lower()
756
+ # Strip punctuation from key to match lookup normalization
757
+ # This ensures "ca." in glossary matches "ca." token stripped to "ca"
758
+ PUNCT_CHARS_FOR_KEY = '.,;:!?\"\'\u201C\u201D\u201E\u00AB\u00BB\u2018\u2019\u201A\u2039\u203A()[]'
759
+ key = source_term.lower().strip(PUNCT_CHARS_FOR_KEY)
754
760
  if key not in matches_dict:
755
761
  matches_dict[key] = []
756
762
 
@@ -803,7 +809,8 @@ class TermviewWidget(QWidget):
803
809
 
804
810
  # Comprehensive set of quote and punctuation characters to strip
805
811
  # Using Unicode escapes to avoid encoding issues
806
- PUNCT_CHARS = '.,;:!?\"\'\u201C\u201D\u201E\u00AB\u00BB\u2018\u2019\u201A\u2039\u203A'
812
+ # Include brackets for terms like "(typisch)" to match "typisch"
813
+ PUNCT_CHARS = '.,;:!?\"\'\u201C\u201D\u201E\u00AB\u00BB\u2018\u2019\u201A\u2039\u203A()[]'
807
814
 
808
815
  # Track which terms have already been assigned shortcuts (avoid duplicates)
809
816
  assigned_shortcuts = set()
@@ -816,7 +823,6 @@ class TermviewWidget(QWidget):
816
823
 
817
824
  # Check if this is a non-translatable
818
825
  if lookup_key in nt_dict:
819
- # Create NT block
820
826
  nt_block = NTBlock(token, nt_dict[lookup_key], self, theme_manager=self.theme_manager,
821
827
  font_size=self.current_font_size, font_family=self.current_font_family,
822
828
  font_bold=self.current_font_bold)
@@ -992,9 +998,9 @@ class TermviewWidget(QWidget):
992
998
  Returns:
993
999
  List of tokens (words/phrases/numbers), with multi-word terms kept together
994
1000
  """
995
- # DEBUG: Log multi-word terms we're looking for
1001
+ # DEBUG: Log multi-word terms we're looking for (only if debug_tokenize enabled)
996
1002
  multi_word_terms = [k for k in matches.keys() if ' ' in k]
997
- if multi_word_terms:
1003
+ if multi_word_terms and self.debug_tokenize:
998
1004
  self.log(f"🔍 Tokenize: Looking for {len(multi_word_terms)} multi-word terms:")
999
1005
  for term in sorted(multi_word_terms, key=len, reverse=True)[:3]:
1000
1006
  self.log(f" - '{term}'")
@@ -1019,11 +1025,12 @@ class TermviewWidget(QWidget):
1019
1025
  else:
1020
1026
  pattern = r'\b' + term_escaped + r'\b'
1021
1027
 
1022
- # DEBUG: Check if multi-word term is found
1028
+ # DEBUG: Check if multi-word term is found (only if debug_tokenize enabled)
1023
1029
  found = re.search(pattern, text_lower)
1024
- self.log(f"🔍 Tokenize: Pattern '{pattern}' for '{term}' → {'FOUND' if found else 'NOT FOUND'}")
1025
- if found:
1026
- self.log(f" Match at position {found.span()}: '{text[found.start():found.end()]}'")
1030
+ if self.debug_tokenize:
1031
+ self.log(f"🔍 Tokenize: Pattern '{pattern}' for '{term}' → {'FOUND' if found else 'NOT FOUND'}")
1032
+ if found:
1033
+ self.log(f" Match at position {found.span()}: '{text[found.start():found.end()]}'")
1027
1034
 
1028
1035
  # Find all matches using regex
1029
1036
  for match in re.finditer(pattern, text_lower):
@@ -1036,10 +1043,11 @@ class TermviewWidget(QWidget):
1036
1043
  original_term = text[pos:pos + len(term)]
1037
1044
  tokens_with_positions.append((pos, len(term), original_term))
1038
1045
  used_positions.update(term_positions)
1039
- self.log(f" ✅ Added multi-word token: '{original_term}' covering positions {pos}-{pos+len(term)}")
1046
+ if self.debug_tokenize:
1047
+ self.log(f" ✅ Added multi-word token: '{original_term}' covering positions {pos}-{pos+len(term)}")
1040
1048
 
1041
- # DEBUG: Log used_positions after first pass
1042
- if ' ' in sorted(matches.keys(), key=len, reverse=True)[0]:
1049
+ # DEBUG: Log used_positions after first pass (only if debug_tokenize enabled)
1050
+ if matches and ' ' in sorted(matches.keys(), key=len, reverse=True)[0] and self.debug_tokenize:
1043
1051
  self.log(f"🔍 After first pass: {len(used_positions)} positions marked as used")
1044
1052
  self.log(f" Used positions: {sorted(list(used_positions))[:20]}...")
1045
1053
 
@@ -396,6 +396,47 @@ class TMMetadataManager:
396
396
  self.log(f"✗ Error fetching active tm_ids: {e}")
397
397
  return []
398
398
 
399
+ def get_writable_tm_ids(self, project_id: Optional[int]) -> List[str]:
400
+ """
401
+ Get list of writable tm_id strings for a project.
402
+
403
+ Returns TMs where:
404
+ - The TM has an activation record for this project AND
405
+ - read_only = 0 (Write checkbox is enabled)
406
+
407
+ This is used for SAVING segments to TM, separate from get_active_tm_ids()
408
+ which is used for READING/matching from TM.
409
+
410
+ Returns:
411
+ List of tm_id strings that are writable for the project
412
+ """
413
+ if project_id is None:
414
+ # No project - return all writable TMs
415
+ try:
416
+ cursor = self.db_manager.cursor
417
+ cursor.execute("SELECT tm_id FROM translation_memories WHERE read_only = 0")
418
+ return [row[0] for row in cursor.fetchall()]
419
+ except Exception as e:
420
+ self.log(f"✗ Error fetching all writable tm_ids: {e}")
421
+ return []
422
+
423
+ try:
424
+ cursor = self.db_manager.cursor
425
+
426
+ # Return TMs where Write checkbox is enabled (read_only = 0)
427
+ # AND the TM has an activation record for this project
428
+ cursor.execute("""
429
+ SELECT tm.tm_id
430
+ FROM translation_memories tm
431
+ INNER JOIN tm_activation ta ON tm.id = ta.tm_id
432
+ WHERE ta.project_id = ? AND tm.read_only = 0
433
+ """, (project_id,))
434
+
435
+ return [row[0] for row in cursor.fetchall()]
436
+ except Exception as e:
437
+ self.log(f"✗ Error fetching writable tm_ids: {e}")
438
+ return []
439
+
399
440
  # ========================================================================
400
441
  # PROJECT TM MANAGEMENT (similar to termbases)
401
442
  # ========================================================================
modules/tmx_editor_qt.py CHANGED
@@ -2655,7 +2655,7 @@ if __name__ == "__main__":
2655
2655
  os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".supervertaler.local")
2656
2656
  )
2657
2657
  user_data_path = Path("user_data_private" if ENABLE_PRIVATE_FEATURES else "user_data")
2658
- db_path = user_data_path / "Translation_Resources" / "supervertaler.db"
2658
+ db_path = user_data_path / "resources" / "supervertaler.db"
2659
2659
 
2660
2660
  # Ensure database directory exists
2661
2661
  db_path.parent.mkdir(parents=True, exist_ok=True)
@@ -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
127
- self.fuzzy_threshold = 0.75
126
+ # Global fuzzy threshold (70% minimum similarity for fuzzy matches)
127
+ self.fuzzy_threshold = 0.7
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.
@@ -401,7 +401,7 @@ class TMDatabase:
401
401
 
402
402
  def load_tmx_file(self, filepath: str, src_lang: str, tgt_lang: str,
403
403
  tm_name: str = None, read_only: bool = False,
404
- strip_variants: bool = True) -> tuple[str, int]:
404
+ strip_variants: bool = True, progress_callback=None) -> tuple[str, int]:
405
405
  """
406
406
  Load TMX file into a new custom TM
407
407
 
@@ -412,6 +412,7 @@ class TMDatabase:
412
412
  tm_name: Custom name for TM (default: filename)
413
413
  read_only: Make TM read-only
414
414
  strip_variants: Match base languages ignoring regional variants (default: True)
415
+ progress_callback: Optional callback function(current, total, message) for progress updates
415
416
 
416
417
  Returns: (tm_id, entry_count)
417
418
  """
@@ -423,16 +424,18 @@ class TMDatabase:
423
424
  self.add_custom_tm(tm_name, tm_id, read_only=read_only)
424
425
 
425
426
  # Load TMX content
426
- loaded_count = self._load_tmx_into_db(filepath, src_lang, tgt_lang, tm_id, strip_variants=strip_variants)
427
+ loaded_count = self._load_tmx_into_db(filepath, src_lang, tgt_lang, tm_id,
428
+ strip_variants=strip_variants,
429
+ progress_callback=progress_callback)
427
430
 
428
431
  self.log(f"✓ Loaded {loaded_count} entries from {os.path.basename(filepath)}")
429
432
 
430
433
  return tm_id, loaded_count
431
434
 
432
435
  def _load_tmx_into_db(self, filepath: str, src_lang: str, tgt_lang: str, tm_id: str,
433
- strip_variants: bool = False) -> int:
436
+ strip_variants: bool = False, progress_callback=None) -> int:
434
437
  """
435
- Internal: Load TMX content into database
438
+ Internal: Load TMX content into database with chunked processing
436
439
 
437
440
  Args:
438
441
  filepath: Path to TMX file
@@ -440,12 +443,24 @@ class TMDatabase:
440
443
  tgt_lang: Target target language code
441
444
  tm_id: TM identifier
442
445
  strip_variants: If True, match base languages ignoring regional variants
446
+ progress_callback: Optional callback function(current, total, message) for progress updates
443
447
  """
444
448
  loaded_count = 0
449
+ chunk_size = 1000 # Process in chunks for responsiveness
450
+ chunk_buffer = []
445
451
 
446
452
  try:
453
+ # First pass: count total TUs for progress bar
454
+ if progress_callback:
455
+ progress_callback(0, 0, "Counting translation units...")
456
+
447
457
  tree = ET.parse(filepath)
448
458
  root = tree.getroot()
459
+ total_tus = len(root.findall('.//tu'))
460
+
461
+ if progress_callback:
462
+ progress_callback(0, total_tus, f"Processing 0 / {total_tus:,} entries...")
463
+
449
464
  xml_ns = "http://www.w3.org/XML/1998/namespace"
450
465
 
451
466
  # Normalize language codes
@@ -458,6 +473,7 @@ class TMDatabase:
458
473
  src_base = get_base_lang_code(src_lang_normalized)
459
474
  tgt_base = get_base_lang_code(tgt_lang_normalized)
460
475
 
476
+ processed = 0
461
477
  for tu in root.findall('.//tu'):
462
478
  src_text, tgt_text = None, None
463
479
 
@@ -488,14 +504,43 @@ class TMDatabase:
488
504
  tgt_text = text
489
505
 
490
506
  if src_text and tgt_text:
507
+ chunk_buffer.append((src_text, tgt_text))
508
+ loaded_count += 1
509
+
510
+ # Process chunk when buffer is full
511
+ if len(chunk_buffer) >= chunk_size:
512
+ for src, tgt in chunk_buffer:
513
+ self.db.add_translation_unit(
514
+ source=src,
515
+ target=tgt,
516
+ source_lang=src_lang_normalized,
517
+ target_lang=tgt_lang_normalized,
518
+ tm_id=tm_id
519
+ )
520
+ chunk_buffer.clear()
521
+
522
+ # Update progress
523
+ if progress_callback:
524
+ progress_callback(processed + 1, total_tus,
525
+ f"Processing {loaded_count:,} / {total_tus:,} entries...")
526
+
527
+ processed += 1
528
+
529
+ # Process remaining entries in buffer
530
+ if chunk_buffer:
531
+ for src, tgt in chunk_buffer:
491
532
  self.db.add_translation_unit(
492
- source=src_text,
493
- target=tgt_text,
533
+ source=src,
534
+ target=tgt,
494
535
  source_lang=src_lang_normalized,
495
536
  target_lang=tgt_lang_normalized,
496
537
  tm_id=tm_id
497
538
  )
498
- loaded_count += 1
539
+ chunk_buffer.clear()
540
+
541
+ # Final progress update
542
+ if progress_callback:
543
+ progress_callback(total_tus, total_tus, f"Completed: {loaded_count:,} entries imported")
499
544
 
500
545
  return loaded_count
501
546
  except Exception as e:
@@ -30,7 +30,7 @@ class UnifiedPromptLibrary:
30
30
  Initialize the Unified Prompt Library.
31
31
 
32
32
  Args:
33
- library_dir: Path to unified library directory (user_data/Prompt_Library/Library)
33
+ library_dir: Path to unified library directory (user_data/prompt_library)
34
34
  log_callback: Function to call for logging messages
35
35
  """
36
36
  self.library_dir = Path(library_dir) if library_dir else None
@@ -544,8 +544,8 @@ class UnifiedPromptManagerQt:
544
544
  self.log = parent_app.log if hasattr(parent_app, 'log') else print
545
545
 
546
546
  # Paths
547
- self.prompt_library_dir = self.user_data_path / "Prompt_Library"
548
- # Use Prompt_Library directly, not Prompt_Library/Library
547
+ self.prompt_library_dir = self.user_data_path / "prompt_library"
548
+ # Use prompt_library directly, not prompt_library/Library
549
549
  self.unified_library_dir = self.prompt_library_dir
550
550
 
551
551
  # Run migration if needed
@@ -579,7 +579,7 @@ class UnifiedPromptManagerQt:
579
579
  self._cached_document_markdown: Optional[str] = None # Cached markdown conversion of current document
580
580
 
581
581
  # Initialize Attachment Manager
582
- ai_assistant_dir = self.user_data_path / "AI_Assistant"
582
+ ai_assistant_dir = self.user_data_path / "ai_assistant"
583
583
  self.attachment_manager = AttachmentManager(
584
584
  base_dir=str(ai_assistant_dir),
585
585
  log_callback=self.log_message
@@ -694,7 +694,7 @@ class UnifiedPromptManagerQt:
694
694
  layout.setSpacing(5)
695
695
 
696
696
  # Title
697
- title = QLabel("🤖 Prompt Manager")
697
+ title = QLabel("📝 Prompt Manager")
698
698
  title.setStyleSheet("font-size: 16pt; font-weight: bold; color: #1976D2;")
699
699
  layout.addWidget(title, 0)
700
700
 
@@ -1546,7 +1546,7 @@ class UnifiedPromptManagerQt:
1546
1546
 
1547
1547
  def _create_active_config_panel(self) -> QGroupBox:
1548
1548
  """Create active prompt configuration panel"""
1549
- group = QGroupBox("Active Configuration")
1549
+ group = QGroupBox("Active Prompt")
1550
1550
  layout = QVBoxLayout()
1551
1551
 
1552
1552
  # Mode info (read-only, auto-selected)
@@ -1704,10 +1704,10 @@ class UnifiedPromptManagerQt:
1704
1704
  self.editor_quickmenu_label_input.setPlaceholderText("Label shown in QuickMenu")
1705
1705
  quickmenu_layout.addWidget(self.editor_quickmenu_label_input, 2)
1706
1706
 
1707
- self.editor_quickmenu_in_grid_cb = CheckmarkCheckBox("Show in Grid right-click QuickMenu")
1707
+ self.editor_quickmenu_in_grid_cb = CheckmarkCheckBox("Show in QuickMenu (in-app)")
1708
1708
  quickmenu_layout.addWidget(self.editor_quickmenu_in_grid_cb, 2)
1709
1709
 
1710
- self.editor_quickmenu_in_quickmenu_cb = CheckmarkCheckBox("Show in Supervertaler QuickMenu")
1710
+ self.editor_quickmenu_in_quickmenu_cb = CheckmarkCheckBox("Show in QuickMenu (global)")
1711
1711
  quickmenu_layout.addWidget(self.editor_quickmenu_in_quickmenu_cb, 1)
1712
1712
 
1713
1713
  layout.addLayout(quickmenu_layout)
@@ -1760,26 +1760,7 @@ class UnifiedPromptManagerQt:
1760
1760
  item.setFlags(item.flags() | Qt.ItemFlag.ItemIsDragEnabled)
1761
1761
  favorites_root.addChild(item)
1762
1762
 
1763
- # QuickMenu section (legacy kind name: quick_run)
1764
- quick_run_root = QTreeWidgetItem(["⚡ QuickMenu"])
1765
- # Special node: not draggable/droppable
1766
- quick_run_root.setData(0, Qt.ItemDataRole.UserRole, {'type': 'special', 'kind': 'quick_run'})
1767
- quick_run_root.setExpanded(False)
1768
- font = quick_run_root.font(0)
1769
- font.setBold(True)
1770
- quick_run_root.setFont(0, font)
1771
- self.tree_widget.addTopLevelItem(quick_run_root)
1772
-
1773
- quickmenu_items = self.library.get_quickmenu_prompts() if hasattr(self.library, 'get_quickmenu_prompts') else self.library.get_quick_run_prompts()
1774
- self.log_message(f"🔍 DEBUG: QuickMenu count: {len(quickmenu_items)}")
1775
- for path, label in quickmenu_items:
1776
- item = QTreeWidgetItem([label])
1777
- item.setData(0, Qt.ItemDataRole.UserRole, {'type': 'prompt', 'path': path})
1778
- # Quick Run entries are shortcuts, but allow dragging to move the actual prompt file.
1779
- item.setFlags(item.flags() | Qt.ItemFlag.ItemIsDragEnabled)
1780
- quick_run_root.addChild(item)
1781
-
1782
- # Library folders
1763
+ # Library folders (QuickMenu parent folder removed - folder hierarchy now defines menu structure)
1783
1764
  self.log_message(f"🔍 DEBUG: Building tree from {self.unified_library_dir}")
1784
1765
  self._build_tree_recursive(None, self.unified_library_dir, "")
1785
1766
 
@@ -3548,7 +3529,7 @@ Output complete ACTION."""
3548
3529
  """
3549
3530
  Save the markdown conversion of the current document.
3550
3531
 
3551
- Saves to: user_data_private/AI_Assistant/current_document/
3532
+ Saves to: user_data_private/ai_assistant/current_document/
3552
3533
 
3553
3534
  Args:
3554
3535
  original_path: Original document file path
@@ -3556,7 +3537,7 @@ Output complete ACTION."""
3556
3537
  """
3557
3538
  try:
3558
3539
  # Create directory for current document markdown
3559
- doc_dir = self.user_data_path / "AI_Assistant" / "current_document"
3540
+ doc_dir = self.user_data_path / "ai_assistant" / "current_document"
3560
3541
  doc_dir.mkdir(parents=True, exist_ok=True)
3561
3542
 
3562
3543
  # Create filename based on original
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervertaler
3
- Version: 1.9.131
4
- Summary: Professional AI-powered translation workbench with multi-LLM support, glossary system, TM, spellcheck, voice commands, and PyQt6 interface. Batteries included (core).
3
+ Version: 1.9.172
4
+ Summary: Professional AI-enhanced translation workbench with multi-LLM support, glossary system, TM, spellcheck, voice commands, and PyQt6 interface. Batteries included (core).
5
5
  Home-page: https://supervertaler.com
6
6
  Author: Michael Beijer
7
7
  Author-email: Michael Beijer <info@michaelbeijer.co.uk>
@@ -71,7 +71,7 @@ Dynamic: home-page
71
71
  Dynamic: license-file
72
72
  Dynamic: requires-python
73
73
 
74
- # 🚀 Supervertaler v1.9.128
74
+ # 🚀 Supervertaler v1.9.172
75
75
 
76
76
  [![PyPI version](https://badge.fury.io/py/supervertaler.svg)](https://pypi.org/project/Supervertaler/)
77
77
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
@@ -79,7 +79,99 @@ Dynamic: requires-python
79
79
 
80
80
  AI-enhanced CAT tool with multi-LLM support (GPT-4, Claude, Gemini, Ollama), innovative Superlookup concordance system offering access to multiple terminology sources (TMs, glossaries, web resources, etc.), and seamless CAT tool integration (memoQ, Trados, CafeTran, Phrase).
81
81
 
82
- **Current Version:** v1.9.131 (January 20, 2026)
82
+
83
+ **Current Version:** v1.9.172 (January 28, 2026)
84
+
85
+ ### FIXED in v1.9.172 - 🐛 Fresh Projects Start Clean
86
+
87
+ - **Fresh Projects Start Clean**: Fixed bug where TMs and glossaries remained activated from previous sessions when loading/creating new projects. Now all resources are properly deactivated, giving you a clean slate.
88
+
89
+ ### FIXED in v1.9.171 - 🐛 TM Target & Alt+0 Badge Regression
90
+
91
+ - **TM Target & Alt+0 Badge Restored**: Fixed regression where the TM Target and its blue "0" badge (Alt+0 shortcut) were missing from the Match Panel.
92
+
93
+ ### NEW in v1.9.170 - 📝 Scratchpad Tab, Cache Defaults, TM Target Shortcut Badge
94
+
95
+ - **Scratchpad Tab in Right Panel**: The Scratchpad is now available as a permanent tab in the right panel for easier access.
96
+ - **Location**: Right panel tabs → last tab after "Session Log"
97
+ - **Auto-Update**: Content automatically syncs with project's scratchpad notes
98
+ - **Dual Access**: Available both as popup dialog (`Ctrl+Shift+P`) and as permanent tab
99
+ - **Project-Aware**: Tab clears when creating new project, populates when loading project
100
+
101
+ - **Settings Improvement**: "Disable ALL caches" is now checked by default for new installs, ensuring maximum privacy and control for new users.
102
+
103
+ - **TM Target Shortcut Badge**: Added a blue "0" badge next to the TM Target in the Match Panel, indicating the Alt+0 shortcut for instant TM match insertion. Shortcut is documented and works out of the box.
104
+
105
+ ### Previously in v1.9.168 - 📝 Markdown File Import with Syntax Highlighting
106
+
107
+ Import Markdown files (`.md`) with full syntax highlighting! Headings, bold/italic markers, code blocks, links, images, blockquotes, and lists are all highlighted with distinctive colors. ([#127](https://github.com/michaelbeijer/Supervertaler/issues/127))
108
+
109
+ ### Previously in v1.9.167 - 🐛 Keyboard Shortcuts Panel Fix
110
+
111
+ Fixed bug where UI text (Action, Shortcut, Status columns) would disappear after changing a shortcut. ([#125](https://github.com/michaelbeijer/Supervertaler/issues/125))
112
+
113
+ ### Previously in v1.9.166 - 🐛 TM Write Checkbox Fix
114
+
115
+ Fixed critical bug where confirmed translations went to "project" TM instead of user-designated TM with Write enabled. ([#126](https://github.com/michaelbeijer/Supervertaler/issues/126))
116
+
117
+ ### Previously in v1.9.162 - ⚡ Cache Kill Switch & Performance Testing
118
+
119
+ Streamlined the right panel by replacing Compare Panel with Match Panel:
120
+ - **Match Panel** combines Termview + TM Source/Target in one tab
121
+ - Compare Panel removed (was redundant with Translation Results)
122
+ - TM matches display with green background for easy identification
123
+ - Zoom shortcuts (Ctrl+Alt+=/Ctrl+Alt+-) now work on Match Panel TM boxes
124
+ - Cleaner UI with less tab switching needed
125
+
126
+ ### v1.9.153 - 📝 Tab Layout Reorganization
127
+
128
+ **Lightning-Fast Term Addition:** Adding terms to glossaries now feels instant! When you add a term with Alt+Shift+Up/Down, it appears immediately in TermView AND the source highlighting updates instantly - no more 5-6 second delays.
129
+
130
+ **What Changed:** Instead of searching the entire segment again after adding a term (50+ database queries for long patent sentences), we now add the new term directly to the cache and update the display. The result: instant visual feedback that makes building glossaries during translation feel smooth and responsive.
131
+
132
+ ### v1.9.151 - 🔍 TM Pre-Translation Fixed
133
+
134
+ **Intuitive Language Filters:** "From: Dutch → To: English" now means what you'd expect - "Search FOR Dutch text and show me English translations"! Searches ALL TMs regardless of their stored direction (NL→EN or EN→NL) and automatically presents results in the correct order.
135
+
136
+ ### v1.9.148-beta - 📁 User-Choosable Data Folder
137
+
138
+ **Your Data, Your Location!** On first run, you choose where to store your data (API keys, TMs, glossaries, prompts). Default is a visible folder in your home directory:
139
+
140
+ | Platform | Default Location |
141
+ |----------|-----------------|
142
+ | **Windows** | `C:\Users\Username\Supervertaler\` |
143
+ | **macOS** | `~/Supervertaler/` |
144
+ | **Linux** | `~/Supervertaler/` |
145
+
146
+ **Features:**
147
+ - First-run dialog lets you choose your data folder
148
+ - Change location anytime in Settings → General
149
+ - Auto-recovery if config pointer is deleted
150
+ - Easy to backup - just copy the folder!
151
+
152
+ ### v1.9.147 - 📁 Persistent User Data Location
153
+
154
+ **No More Data Loss on Upgrade!** User data now stored outside the pip package directory, surviving `pip install --upgrade`.
155
+
156
+ ### FIXED in v1.9.146 - 🔑 Gemini/Google API Key Alias
157
+
158
+ **Bug Fix:** Fixed "Gemini API Key Missing" error when users had `google=...` instead of `gemini=...` in their api_keys.txt. Both names now work identically thanks to automatic normalization at load time.
159
+
160
+ ### FIXED in v1.9.140 - 🐛 Glossary Add No Longer Triggers TM Search
161
+
162
+ **Performance Fix ([#118](https://github.com/michaelbeijer/Supervertaler/issues/118)):** Adding a term to a glossary was unnecessarily triggering a full TM search. Now uses targeted refresh that only updates glossary display - TM results stay cached.
163
+
164
+ **Also:** Renamed "Voice OFF" → "Voice Commands OFF" and "Dictate" → "Dictation" for clarity.
165
+
166
+ ### FIXED in v1.9.138 - 🏷️ Termview Punctuated Terms & Auto-Sizing Columns
167
+
168
+ **Termview Fix:** Glossary terms with punctuation (like "ca." or "(typisch)") now correctly appear in the Termview pane. Previously they were found but not displayed due to a key normalization mismatch.
169
+
170
+ **Grid UX:** Segment number column now auto-sizes based on font size and segment count - no more truncated numbers!
171
+
172
+ ### FIXED in v1.9.137 - 🔧 Termview Race Condition
173
+
174
+ **Glossary terms now appear immediately:** Fixed timing bug where Termview showed "No glossary matches" until you pressed F5. Now updates correctly when navigating segments.
83
175
 
84
176
  ### ENHANCED in v1.9.128 - 📝 Placeholders Tab Layout Optimization
85
177
 
@@ -409,7 +501,7 @@ python Supervertaler.py
409
501
  - 📊 **Smart Status** - Manual edits reset status requiring confirmation
410
502
 
411
503
  **v1.4.0 - Supervoice Voice Dictation + Detachable Log:**
412
- - 🎤 **Supervoice Voice Dictation** - AI-powered hands-free translation input
504
+ - 🎤 **Supervoice Voice Dictation** - AI-enhanced hands-free translation input
413
505
  - 🌍 **100+ Languages** - OpenAI Whisper supports virtually any language
414
506
  - ⌨️ **F9 Global Hotkey** - Press-to-start, press-to-stop recording anywhere
415
507
  - 🎚️ **5 Model Sizes** - Tiny to Large (balance speed vs accuracy)
@@ -459,7 +551,7 @@ python Supervertaler.py
459
551
  - 🔍 **Superlookup** - System-wide search with global hotkey (Ctrl+Alt+L)
460
552
  - 📝 **TMX Editor** - Professional translation memory editor with database support
461
553
  - 🧹 **AutoFingers** - Automated translation pasting for memoQ with tag cleaning
462
- - 🔧 **PDF Rescue** - AI-powered OCR for poorly formatted PDFs
554
+ - 🔧 **PDF Rescue** - AI-enhanced OCR for poorly formatted PDFs
463
555
  - 🔧 **Encoding Repair Tool** - Detect and fix text encoding corruption (mojibake)
464
556
  - 💾 **Translation Memory** - Fuzzy matching with TMX import/export
465
557
  - 📚 **Multiple Termbases** - Glossary support per project
@@ -520,7 +612,7 @@ For comprehensive project information, see [PROJECT_CONTEXT.md](PROJECT_CONTEXT.
520
612
  - 🤖 **LLM Integration** - OpenAI GPT-4/5, Anthropic Claude, Google Gemini
521
613
  - 🎯 **Context-aware Translation** - Full document understanding
522
614
  - 📚 **Unified Prompt Library** - System Prompts + Custom Instructions
523
- - 🆘 **PDF Rescue** - AI-powered OCR for badly-formatted PDFs
615
+ - 🆘 **PDF Rescue** - AI-enhanced OCR for badly-formatted PDFs
524
616
  - ✅ **CAT Features** - Segment editing, grid pagination, dual selection
525
617
  - 📝 **TMX Editor** - Professional translation memory editor
526
618
  - 🔗 **CAT Tool Integration** - memoQ, CafeTran, Trados Studio
@@ -1,12 +1,12 @@
1
- Supervertaler.py,sha256=pnviFOGV-FylHZwS-hyKkToD7wQ1IY3CBI0ABV5DJsk,2143836
1
+ Supervertaler.py,sha256=QN11SrXGKNdKhXFRwgkumm6IRI6Q9vg_S2yj2Qbd3K4,2286283
2
2
  modules/__init__.py,sha256=G58XleS-EJ2sX4Kehm-3N2m618_W2Es0Kg8CW_eBG7g,327
3
3
  modules/ai_actions.py,sha256=i5MJcM-7Y6CAvKUwxmxrVHeoZAVtAP7aRDdWM5KLkO0,33877
4
- modules/ai_attachment_manager.py,sha256=mA5ISI22qN9mH3DQFF4gOTciDyBt5xVR7sHTkgkTIlw,11361
4
+ modules/ai_attachment_manager.py,sha256=juZlrW3UPkIkcnj0SREgOQkQROLf0fcu3ShZcKXMxsI,11361
5
5
  modules/ai_file_viewer_dialog.py,sha256=lKKqUUlOEVgHmmu6aRxqH7P6ds-7dRLk4ltDyjCwGxs,6246
6
6
  modules/autofingers_engine.py,sha256=eJ7tBi7YJvTToe5hYTfnyGXB-qme_cHrOPZibaoR2Xw,17061
7
7
  modules/cafetran_docx_handler.py,sha256=_F7Jh0WPVaDnMhdxEsVSXuD1fN9r-S_V6i0gr86Pdfc,14076
8
- modules/config_manager.py,sha256=kurQ56_o4IzxvKkIKB6YFg69nti01LWWoyVt_JlCWkU,17971
9
- modules/database_manager.py,sha256=qpTiNzahrt6kTLplxB2lE-dxBxdPeeY2k6VmdwYdDxU,72181
8
+ modules/config_manager.py,sha256=MkPY3xVFgFDkcwewLREg4BfyKueO0OJkT1cTLxehcjM,17894
9
+ modules/database_manager.py,sha256=ZdsiuwF67lh-FPKPdalWsW9t6IieX_FM0fA2Bca1xSQ,80221
10
10
  modules/database_migrations.py,sha256=Y1onFsLDV_6vzJLOpNy3WCZDohBZ2jc4prM-g2_RwLE,14085
11
11
  modules/dejavurtf_handler.py,sha256=8NZPPYtHga40SZCypHjPoJPmZTvm9rD-eEUUab7mjtg,28156
12
12
  modules/document_analyzer.py,sha256=t1rVvqLaTcpQTEja228C7zZnh8dXshK4wA9t1E9aGVk,19524
@@ -21,7 +21,7 @@ modules/find_replace.py,sha256=r9XU19hejjJhItrkzVtn-EKbe8z9Xq-wY77Gt61w468,6342
21
21
  modules/find_replace_qt.py,sha256=z06yyjOCmpYBrCzZcCv68VK-2o6pjfFCPtbr9o95XH8,18758
22
22
  modules/glossary_manager.py,sha256=JDxY9RAGcv-l6Nms4FH7CNDucZdY1TI4WTzyylAuj24,16437
23
23
  modules/image_extractor.py,sha256=HI6QHnpkjO35GHzTXbzazYdjoHZPFD44WAa4JGa9bAw,8332
24
- modules/keyboard_shortcuts_widget.py,sha256=LYIJjnwjYWUd6JdLWGsuPJAcmsR6gDmcuz7mDvqyFLI,23485
24
+ modules/keyboard_shortcuts_widget.py,sha256=wOw2Lxq4lqrJb_QebwYo-4LRkqy1GJ7JcP5DpusbgqU,23824
25
25
  modules/llm_clients.py,sha256=sdlc6CMFhPbAM5OEJow7LFsHCY8HOnU1jLXHVh5cJ50,48831
26
26
  modules/llm_leaderboard.py,sha256=MQ-RbjlE10-CdgVHwoVXzXlCuUfulZ839FaF6dKlt1M,29139
27
27
  modules/llm_superbench_ui.py,sha256=lmzsL8lt0KzFw-z8De1zb49Emnv7f1dZv_DJmoQz0bQ,60212
@@ -29,57 +29,57 @@ modules/local_llm_setup.py,sha256=33y-D_LKzkn2w8ejyjeKaovf_An6xQ98mKISoqe-Qjc,42
29
29
  modules/model_update_dialog.py,sha256=kEg0FuO1N-uj6QY5ZIj-FqdiLQuPuAY48pbuwT0HUGI,13113
30
30
  modules/model_version_checker.py,sha256=41g7gcWvyrKPYeobaOGCMZLwAHgQmFwVF8zokodKae8,12741
31
31
  modules/mqxliff_handler.py,sha256=-tUFubKsxkx6nW7aMDhuDaTBcVTPtVHV1HyzF0TbkVo,26205
32
- modules/non_translatables_manager.py,sha256=nA-EBPuaJu_tmSIZHaRrAoQ-rwE6AGAzmphu5o4XYNU,27477
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
35
35
  modules/phrase_docx_handler.py,sha256=7vJNbvxxURzdcinZ3rkqyJ-7Y5O1NpVL4Lvu9NuGFjQ,18598
36
36
  modules/project_home_panel.py,sha256=P0PgMnoPp6WEiGrfq8cNJNEdxO83aHQDdXzRLqF173w,6810
37
37
  modules/prompt_assistant.py,sha256=shkZqNTvyQKNDO_9aFEu1_gN0zQq0fR5krXkWfnTR2Y,13150
38
38
  modules/prompt_library.py,sha256=t5w4cqB6_Sin4BQDVNALKpfB1EN_oaDeHFwlHxILLSY,26894
39
- modules/prompt_library_migration.py,sha256=LEZnpj1Rc9aOtFil0L8rPaH760CGiipwtw6XN0MgZGk,17639
39
+ modules/prompt_library_migration.py,sha256=fv3RHhe2-EnH50XW5tyTWy0YP_KJ2EsESuTxR8klfmI,17639
40
40
  modules/quick_access_sidebar.py,sha256=RPn5ssvYXlitNMWFZN9Yxv7So8u_z5RGNpHN6N-SFDI,10151
41
41
  modules/ribbon_widget.py,sha256=QNGKxmit_oM5C5nJViadYYEzeRlIdIsla8Bzu_RNGO0,21990
42
42
  modules/sdlppx_handler.py,sha256=o6Rj_T0B94toiYlvDDwMYLSz4q6kANgegFaDK5i3yhs,33538
43
- modules/setup_wizard.py,sha256=1prK5GPrUU4U4CqdT3G1RA3iy8FG1Z4jgPmPPZXOOEA,13115
43
+ modules/setup_wizard.py,sha256=7apNkeTcmMyw8pzJOWAmTOncxFvt3klOtmg-kKjQNgQ,13091
44
44
  modules/shortcut_manager.py,sha256=Yf3ZlzJg8c0P0Z_GhfOYosLCAj5FWXWnmYe2DMXS8GU,31993
45
45
  modules/simple_segmenter.py,sha256=-V7L-tjajW1M3DADxvcYEgBu0VLJvmRQl6VB9OshiuM,4480
46
46
  modules/spellcheck_manager.py,sha256=jwduHJ66pOKv1MtzSAltxpP8LPgz11FvF6j8h7BiRZY,27592
47
47
  modules/statuses.py,sha256=t6TCA9pNZHDw3SbKTxT73uKezJhwWk9gFLr0NOgEufs,6911
48
48
  modules/style_guide_manager.py,sha256=QBvbygF2E-cgRuwJPWmIatwQl37voU1FErYnycv8Sac,10809
49
49
  modules/superbench_ui.py,sha256=O8pSb-R8Mul1Qz9-8gbBrFR1j7Z8b8_G0wSTSLSCf2U,55563
50
- modules/superbrowser.py,sha256=dzYYEcBeeRpqV7DLdKqQ7ubWvxFS93DrSFGf882D-d4,12243
50
+ modules/superbrowser.py,sha256=FAEaYTMiklXizYm3iPt83kp254bCGrFHRsGHduedKYI,12759
51
51
  modules/supercleaner.py,sha256=uRJAEh03Eu4Qtujrf_jxuIyPBbcxKAZzryhV9Chi9ro,22939
52
52
  modules/supercleaner_ui.py,sha256=sVTnYxlX9R20eWs52BKCeQ15iFVFOIQEl29L72lup1c,18812
53
53
  modules/superdocs.py,sha256=vMYyUbHU8zDkS1YMSDF7niDkT8d8FJFDcfIxSktCyGc,522
54
54
  modules/superdocs_viewer_qt.py,sha256=dBxKz71m7EH0jPoKfWLXByMXeAgEZa7zMRu13YsMBBI,14975
55
- modules/superlookup.py,sha256=gvtLotyD0TxWksFtPB2CVHpkDaG3pBEGdamXXIqDx3E,8434
55
+ modules/superlookup.py,sha256=Zc6StQppFRlh6S3h7A6Rjzwl3twe0JHAkTUiybAaBU0,8980
56
56
  modules/tag_cleaner.py,sha256=u7VOchIWzD4sAhFs3X1Vuo3hX6X72zESQ0AGZE83cYc,8703
57
- modules/tag_manager.py,sha256=Buc2yUD3vE5VM40nsyQQG_qw_oRlC0qjYT8JCQJmXe8,11910
57
+ 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
61
  modules/termbase_manager.py,sha256=-PlGF6fIA7KYCteoQ8FZ_0SQZNRRBFAtLimHPbmhQ6w,44544
62
- modules/termview_widget.py,sha256=KNzgQ7dEFW5ANcbCsUPHJ268MYwBmV5MQG-9GQJoobY,52745
62
+ modules/termview_widget.py,sha256=O3ah7g-4Lb_iUctxl9sMyxh8V3A5I5PFxmy9iIH2Kgk,53484
63
63
  modules/theme_manager.py,sha256=EOI_5pM2bXAadw08bbl92TLN-w28lbw4Zi1E8vQ-kM0,16694
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=mZrypIK3tTo4hXXJGrL4Ph7QbWD8htG2uiU6JK3FvVY,21503
66
+ modules/tm_metadata_manager.py,sha256=_drzJ80q-mr1y9La8t0Cb8MXh91n3I-lEGDwSmoVI_4,23161
67
67
  modules/tmx_editor.py,sha256=n0CtdZI8f1fPRWmCqz5Ysxbnp556Qj-6Y56a-YIz6pY,59239
68
- modules/tmx_editor_qt.py,sha256=D7cuICjFqjKHSWdVCiBzKrDvPC2_LSOrVtlPtejXgzM,117034
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=U-deNPybG2PKeEt2LSTZv0Ziu5VwxkH2BsuCCXbpalc,26453
72
+ modules/translation_memory.py,sha256=k0GtO6ANTqxI1XMcv3D5mdAoTgcWlDT5iVsYHizKNUM,28738
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=L3do_T-7NHnC5CxJ1TDq63iVM60XLkvovDJ9g4cJmZQ,26016
76
- modules/unified_prompt_manager_qt.py,sha256=kpR-Tk1xmVyJGHnZdHm6NbGQPTZ1jB8t4tI_Y93hXwY,171495
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
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.131.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
- supervertaler-1.9.131.dist-info/METADATA,sha256=biMnAy4CwZXST7aLuSI-pRKxQVDHn_CE0XRA30HoKJ0,42458
82
- supervertaler-1.9.131.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
- supervertaler-1.9.131.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
- supervertaler-1.9.131.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
- supervertaler-1.9.131.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5