supervertaler 1.9.164__py3-none-any.whl → 1.9.166__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Supervertaler.py CHANGED
@@ -34,9 +34,9 @@ License: MIT
34
34
  """
35
35
 
36
36
  # Version Information.
37
- __version__ = "1.9.164"
37
+ __version__ = "1.9.166"
38
38
  __phase__ = "0.9"
39
- __release_date__ = "2026-01-26"
39
+ __release_date__ = "2026-01-27"
40
40
  __edition__ = "Qt"
41
41
 
42
42
  import sys
@@ -14858,6 +14858,13 @@ class SupervertalerQt(QMainWindow):
14858
14858
 
14859
14859
  layout.addWidget(settings_tabs)
14860
14860
 
14861
+ # Apply saved UI font scale on startup
14862
+ saved_scale = self._get_settings_ui_font_scale()
14863
+ if saved_scale != 100:
14864
+ # Defer application to ensure widgets are fully created
14865
+ from PyQt6.QtCore import QTimer
14866
+ QTimer.singleShot(100, lambda: self._apply_settings_ui_font_scale(saved_scale))
14867
+
14861
14868
  return tab
14862
14869
 
14863
14870
  def _wrap_in_scroll(self, widget):
@@ -17019,6 +17026,58 @@ class SupervertalerQt(QMainWindow):
17019
17026
  termview_group.setLayout(termview_layout)
17020
17027
  layout.addWidget(termview_group)
17021
17028
 
17029
+ # ===== UI Font Scale (for Settings panels) =====
17030
+ ui_scale_group = QGroupBox("🖥️ Settings Panel Font Size")
17031
+ ui_scale_layout = QVBoxLayout()
17032
+
17033
+ ui_scale_info = QLabel(
17034
+ "Adjust the font size for all Settings panel text. Useful for high-DPI/4K displays.\n"
17035
+ "Changes apply immediately. Default is 100%."
17036
+ )
17037
+ ui_scale_info.setWordWrap(True)
17038
+ ui_scale_layout.addWidget(ui_scale_info)
17039
+
17040
+ ui_scale_row = QHBoxLayout()
17041
+ ui_scale_row.addWidget(QLabel("UI Font Scale:"))
17042
+ ui_scale_spin = QSpinBox()
17043
+ ui_scale_spin.setMinimum(80)
17044
+ ui_scale_spin.setMaximum(200)
17045
+ ui_scale_spin.setValue(font_settings.get('settings_ui_font_scale', 100))
17046
+ ui_scale_spin.setSuffix("%")
17047
+ ui_scale_spin.setSingleStep(10)
17048
+ ui_scale_spin.setToolTip("Scale Settings panel text (80%-200%)")
17049
+ ui_scale_spin.setMinimumHeight(28)
17050
+ ui_scale_spin.setMinimumWidth(90)
17051
+ ui_scale_spin.setStyleSheet("""
17052
+ QSpinBox {
17053
+ padding-right: 20px;
17054
+ }
17055
+ QSpinBox::up-button {
17056
+ width: 20px;
17057
+ height: 14px;
17058
+ }
17059
+ QSpinBox::down-button {
17060
+ width: 20px;
17061
+ height: 14px;
17062
+ }
17063
+ """)
17064
+ ui_scale_row.addWidget(ui_scale_spin)
17065
+
17066
+ # Apply button for immediate feedback
17067
+ apply_scale_btn = QPushButton("Apply")
17068
+ apply_scale_btn.setToolTip("Apply font scale immediately")
17069
+ apply_scale_btn.clicked.connect(lambda: self._apply_settings_ui_font_scale(ui_scale_spin.value()))
17070
+ ui_scale_row.addWidget(apply_scale_btn)
17071
+
17072
+ ui_scale_row.addStretch()
17073
+ ui_scale_layout.addLayout(ui_scale_row)
17074
+
17075
+ # Store reference for saving
17076
+ self._ui_scale_spin = ui_scale_spin
17077
+
17078
+ ui_scale_group.setLayout(ui_scale_layout)
17079
+ layout.addWidget(ui_scale_group)
17080
+
17022
17081
  # Quick Reference section
17023
17082
  reference_group = QGroupBox("⌨️ Font Size Quick Reference")
17024
17083
  reference_layout = QVBoxLayout()
@@ -17043,12 +17102,20 @@ class SupervertalerQt(QMainWindow):
17043
17102
  # Save button
17044
17103
  save_btn = QPushButton("💾 Save View Settings")
17045
17104
  save_btn.setStyleSheet("font-weight: bold; padding: 8px;")
17046
- save_btn.clicked.connect(lambda: self._save_view_settings_from_ui(
17047
- grid_font_spin, match_font_spin, compare_font_spin, show_tags_check, tag_color_btn,
17048
- alt_colors_check, even_color_btn, odd_color_btn, invisible_char_color_btn, grid_font_family_combo,
17049
- termview_font_family_combo, termview_font_spin, termview_bold_check,
17050
- border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check
17051
- ))
17105
+
17106
+ def save_view_settings_with_scale():
17107
+ # Save the UI scale setting first
17108
+ if hasattr(self, '_ui_scale_spin'):
17109
+ self._apply_settings_ui_font_scale(self._ui_scale_spin.value())
17110
+ # Then save other view settings
17111
+ self._save_view_settings_from_ui(
17112
+ grid_font_spin, match_font_spin, compare_font_spin, show_tags_check, tag_color_btn,
17113
+ alt_colors_check, even_color_btn, odd_color_btn, invisible_char_color_btn, grid_font_family_combo,
17114
+ termview_font_family_combo, termview_font_spin, termview_bold_check,
17115
+ border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check
17116
+ )
17117
+
17118
+ save_btn.clicked.connect(save_view_settings_with_scale)
17052
17119
  layout.addWidget(save_btn)
17053
17120
 
17054
17121
  layout.addStretch()
@@ -19167,6 +19234,73 @@ class SupervertalerQt(QMainWindow):
19167
19234
  msg.setStandardButtons(QMessageBox.StandardButton.Ok)
19168
19235
  msg.exec()
19169
19236
 
19237
+ def _apply_settings_ui_font_scale(self, scale_percent: int):
19238
+ """Apply font scale to all Settings panels for better readability on high-DPI displays"""
19239
+ # Save the setting
19240
+ general_settings = self.load_general_settings()
19241
+ general_settings['settings_ui_font_scale'] = scale_percent
19242
+ self.save_general_settings(general_settings)
19243
+
19244
+ # Calculate base font size (default system font is typically 9-10pt)
19245
+ base_size = 10 # Base font size in points
19246
+ scaled_size = int(base_size * scale_percent / 100)
19247
+
19248
+ # Create stylesheet for Settings panels
19249
+ settings_stylesheet = f"""
19250
+ QGroupBox {{
19251
+ font-size: {scaled_size + 1}pt;
19252
+ font-weight: bold;
19253
+ }}
19254
+ QGroupBox QLabel {{
19255
+ font-size: {scaled_size}pt;
19256
+ }}
19257
+ QGroupBox QCheckBox {{
19258
+ font-size: {scaled_size}pt;
19259
+ }}
19260
+ QGroupBox QRadioButton {{
19261
+ font-size: {scaled_size}pt;
19262
+ }}
19263
+ QGroupBox QComboBox {{
19264
+ font-size: {scaled_size}pt;
19265
+ }}
19266
+ QGroupBox QSpinBox {{
19267
+ font-size: {scaled_size}pt;
19268
+ }}
19269
+ QGroupBox QLineEdit {{
19270
+ font-size: {scaled_size}pt;
19271
+ }}
19272
+ QGroupBox QPushButton {{
19273
+ font-size: {scaled_size}pt;
19274
+ }}
19275
+ QGroupBox QTextEdit {{
19276
+ font-size: {scaled_size}pt;
19277
+ }}
19278
+ QGroupBox QPlainTextEdit {{
19279
+ font-size: {scaled_size}pt;
19280
+ }}
19281
+ """
19282
+
19283
+ # Apply to settings_tabs if it exists
19284
+ if hasattr(self, 'settings_tabs') and self.settings_tabs is not None:
19285
+ self.settings_tabs.setStyleSheet(
19286
+ "QTabBar::tab { outline: 0; font-size: " + str(scaled_size) + "pt; } "
19287
+ "QTabBar::tab:focus { outline: none; } "
19288
+ "QTabBar::tab:selected { border-bottom: 1px solid #2196F3; background-color: rgba(33, 150, 243, 0.08); }"
19289
+ )
19290
+
19291
+ # Apply to each tab's content
19292
+ for i in range(self.settings_tabs.count()):
19293
+ widget = self.settings_tabs.widget(i)
19294
+ if widget:
19295
+ widget.setStyleSheet(settings_stylesheet)
19296
+
19297
+ self.log(f"✓ Settings UI font scale set to {scale_percent}% (base: {scaled_size}pt)")
19298
+
19299
+ def _get_settings_ui_font_scale(self) -> int:
19300
+ """Get the current Settings UI font scale percentage"""
19301
+ general_settings = self.load_general_settings()
19302
+ return general_settings.get('settings_ui_font_scale', 100)
19303
+
19170
19304
  def create_grid_view_widget(self):
19171
19305
  """Create the Grid View widget (existing grid functionality)"""
19172
19306
  widget = QWidget()
@@ -21933,7 +22067,10 @@ class SupervertalerQt(QMainWindow):
21933
22067
 
21934
22068
  def save_segment_to_activated_tms(self, source: str, target: str):
21935
22069
  """
21936
- Save segment to all activated TMs for current project.
22070
+ Save segment to all writable TMs for current project.
22071
+
22072
+ Note: Uses get_writable_tm_ids() which checks the Write checkbox (read_only=0),
22073
+ NOT get_active_tm_ids() which checks the Read checkbox (is_active=1).
21937
22074
 
21938
22075
  Args:
21939
22076
  source: Source text
@@ -21945,7 +22082,7 @@ class SupervertalerQt(QMainWindow):
21945
22082
  if not hasattr(self.current_project, 'source_lang') or not hasattr(self.current_project, 'target_lang'):
21946
22083
  return
21947
22084
 
21948
- # Get activated TM IDs for this project
22085
+ # Get WRITABLE TM IDs for this project (Write checkbox enabled)
21949
22086
  tm_ids = []
21950
22087
 
21951
22088
  if hasattr(self, 'tm_metadata_mgr') and self.tm_metadata_mgr:
@@ -21953,7 +22090,8 @@ class SupervertalerQt(QMainWindow):
21953
22090
  project_id = self.current_project.id if hasattr(self.current_project, 'id') else None
21954
22091
 
21955
22092
  if project_id:
21956
- tm_ids = self.tm_metadata_mgr.get_active_tm_ids(project_id)
22093
+ # Use get_writable_tm_ids() to find TMs with Write enabled
22094
+ tm_ids = self.tm_metadata_mgr.get_writable_tm_ids(project_id)
21957
22095
  else:
21958
22096
  self.log(f"⚠️ Cannot save to TM: project has no 'id' attribute!")
21959
22097
  else:
@@ -21961,23 +22099,16 @@ class SupervertalerQt(QMainWindow):
21961
22099
  else:
21962
22100
  self.log(f"⚠️ Cannot save to TM: TM metadata manager not available!")
21963
22101
 
21964
- # If no TMs activated, skip saving (user must activate TMs explicitly)
22102
+ # If no TMs have Write enabled, skip saving
21965
22103
  if not tm_ids:
21966
- self.log("⚠️ No TMs activated - segment not saved to TM. Please activate at least one TM in Resources.")
21967
- self.log(f" - To fix: Go to Resources > Translation Memories > TM List and check the Active checkbox")
22104
+ self.log("⚠️ No TMs with Write enabled - segment not saved to TM.")
22105
+ self.log(f" - To fix: Go to Resources > Translation Memories > TM List and enable the Write checkbox")
21968
22106
  return
21969
22107
 
21970
- # Save to each activated TM (skip read-only ones)
22108
+ # Save to each writable TM
21971
22109
  saved_count = 0
21972
- skipped_readonly = 0
21973
22110
  for tm_id in tm_ids:
21974
22111
  try:
21975
- # Check if TM is read-only
21976
- tm_info = self.tm_metadata_mgr.get_tm_by_tm_id(tm_id)
21977
- if tm_info and tm_info.get('read_only'):
21978
- skipped_readonly += 1
21979
- continue
21980
-
21981
22112
  self.db_manager.add_translation_unit(
21982
22113
  source=source,
21983
22114
  target=target,
@@ -21991,13 +22122,9 @@ class SupervertalerQt(QMainWindow):
21991
22122
 
21992
22123
  if saved_count > 0:
21993
22124
  msg = f"💾 Saved segment to {saved_count} TM(s)"
21994
- if skipped_readonly > 0:
21995
- msg += f" (skipped {skipped_readonly} read-only)"
21996
22125
  self._queue_tm_save_log(msg)
21997
22126
  # Invalidate cache so prefetched segments get fresh TM matches
21998
22127
  self.invalidate_translation_cache()
21999
- elif skipped_readonly > 0:
22000
- self.log(f"ℹ️ Segment not saved - {skipped_readonly} activated TM(s) are read-only")
22001
22128
 
22002
22129
  def invalidate_translation_cache(self, smart_invalidation=True):
22003
22130
  """
@@ -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
  # ========================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervertaler
3
- Version: 1.9.164
3
+ Version: 1.9.166
4
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
@@ -71,7 +71,7 @@ Dynamic: home-page
71
71
  Dynamic: license-file
72
72
  Dynamic: requires-python
73
73
 
74
- # 🚀 Supervertaler v1.9.164
74
+ # 🚀 Supervertaler v1.9.166
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,18 +79,13 @@ 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.164 (January 26, 2026)
82
+ **Current Version:** v1.9.166 (January 27, 2026)
83
83
 
84
- ### NEW in v1.9.162 - Cache Kill Switch & Performance Testing
84
+ ### NEW in v1.9.166 - 🐛 TM Write Checkbox Fix
85
85
 
86
- Added experimental performance toggle to test grid responsiveness:
87
- - **New Setting:** Settings → Experimental Performance → "Disable all caching systems"
88
- - **What it does:** Bypasses termbase cache, TM/MT cache, and prefetch workers
89
- - **Why:** Testing showed direct database lookups may be faster than cache overhead for some workflows
90
- - **Proactive Highlighting:** Glossary terms now highlighted in upcoming segments while you're still editing the current one
91
- - **Improved TM Matching:** Fixed fuzzy match issues with multi-TM projects
86
+ 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))
92
87
 
93
- ### Previously in v1.9.154 - 🎯 Match Panel Consolidation
88
+ ### Previously in v1.9.162 - Cache Kill Switch & Performance Testing
94
89
 
95
90
  Streamlined the right panel by replacing Compare Panel with Match Panel:
96
91
  - **Match Panel** combines Termview + TM Source/Target in one tab
@@ -1,4 +1,4 @@
1
- Supervertaler.py,sha256=gh-67gpR65ZbNF8grWDpJJR9pUXr5wQ1p37nTRtsj3w,2264715
1
+ Supervertaler.py,sha256=p_l79UMwMh9S3PDezaF6DJ4Hk0XkjEGtwQNuC7_JBtw,2269768
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
@@ -63,7 +63,7 @@ modules/termview_widget.py,sha256=O3ah7g-4Lb_iUctxl9sMyxh8V3A5I5PFxmy9iIH2Kgk,53
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
68
  modules/tmx_editor_qt.py,sha256=PxBIUw_06PHYTBHsd8hZzVJXW8T0A0ljfz1Wjjsa4yU,117022
69
69
  modules/tmx_generator.py,sha256=pNkxwdMLvSRMMru0lkB1gvViIpg9BQy1EVhRbwoef3k,9426
@@ -77,9 +77,9 @@ modules/unified_prompt_manager_qt.py,sha256=fyF3_r0N8hnImT-CcWo1AuBOQ1Dn_ExeeUCk
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.164.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
- supervertaler-1.9.164.dist-info/METADATA,sha256=5YYkounxUPZNOqsTIUmZbqpOZYhoJebihJuKCIXycI4,46629
82
- supervertaler-1.9.164.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
- supervertaler-1.9.164.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
- supervertaler-1.9.164.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
- supervertaler-1.9.164.dist-info/RECORD,,
80
+ supervertaler-1.9.166.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
+ supervertaler-1.9.166.dist-info/METADATA,sha256=JYcWsy2FQHGEDjcC-YPE6OPRI508CdCqAnyycXK82tU,46266
82
+ supervertaler-1.9.166.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
+ supervertaler-1.9.166.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
+ supervertaler-1.9.166.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
+ supervertaler-1.9.166.dist-info/RECORD,,