supervertaler 1.9.196__py3-none-any.whl → 1.9.197__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.

Potentially problematic release.


This version of supervertaler might be problematic. Click here for more details.

Supervertaler.py CHANGED
@@ -32,7 +32,7 @@ License: MIT
32
32
  """
33
33
 
34
34
  # Version Information.
35
- __version__ = "1.9.196"
35
+ __version__ = "1.9.197"
36
36
  __phase__ = "0.9"
37
37
  __release_date__ = "2026-02-02"
38
38
  __edition__ = "Qt"
@@ -391,10 +391,10 @@ def runs_to_tagged_text(paragraphs) -> str:
391
391
  def strip_formatting_tags(text: str) -> str:
392
392
  """
393
393
  Remove HTML formatting tags from text, leaving plain text.
394
-
394
+
395
395
  Args:
396
396
  text: Text with HTML tags like <b>, </b>, <i>, </i>, <u>, </u>
397
-
397
+
398
398
  Returns:
399
399
  Plain text without tags
400
400
  """
@@ -403,6 +403,77 @@ def strip_formatting_tags(text: str) -> str:
403
403
  return re.sub(r'</?[biu]>', '', text)
404
404
 
405
405
 
406
+ def strip_outer_wrapping_tags(text: str) -> tuple:
407
+ """
408
+ Strip outer wrapping tags from text if the entire segment is wrapped in a single tag pair.
409
+
410
+ This handles structural tags like <li-o>...</li-o>, <p>...</p>, <td>...</td>, etc.
411
+ Inner formatting tags like <b>...</b> are preserved.
412
+
413
+ Args:
414
+ text: Text that may be wrapped in outer structural tags
415
+
416
+ Returns:
417
+ Tuple of (stripped_text, tag_name) where tag_name is the outer tag that was stripped,
418
+ or (original_text, None) if no outer wrapping tag was found
419
+
420
+ Examples:
421
+ "<li-o>Hello <b>world</b></li-o>" -> ("Hello <b>world</b>", "li-o")
422
+ "<p>Simple text</p>" -> ("Simple text", "p")
423
+ "No tags here" -> ("No tags here", None)
424
+ "<b>Bold text</b>" -> ("<b>Bold text</b>", None) # <b> is formatting, not structural
425
+ """
426
+ import re
427
+
428
+ if not text or not text.strip():
429
+ return (text, None)
430
+
431
+ text = text.strip()
432
+
433
+ # Structural tags that wrap entire segments (not inline formatting)
434
+ structural_tags = {
435
+ 'li-o', 'li-b', 'li', 'p', 'td', 'th', 'tr', 'div', 'span',
436
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'title', 'caption',
437
+ 'blockquote', 'pre', 'code', 'dt', 'dd', 'header', 'footer',
438
+ 'article', 'section', 'aside', 'nav', 'main', 'figure', 'figcaption'
439
+ }
440
+
441
+ # Pattern to match opening tag at start: <tag> or <tag attr="...">
442
+ opening_pattern = r'^<([a-zA-Z][a-zA-Z0-9-]*)(?:\s+[^>]*)?>(.*)$'
443
+ opening_match = re.match(opening_pattern, text, re.DOTALL)
444
+
445
+ if not opening_match:
446
+ return (text, None)
447
+
448
+ tag_name = opening_match.group(1).lower()
449
+ rest = opening_match.group(2)
450
+
451
+ # Only strip structural tags, not inline formatting like <b>, <i>, <u>, <em>, <strong>
452
+ if tag_name not in structural_tags:
453
+ return (text, None)
454
+
455
+ # Check if text ends with matching closing tag
456
+ closing_pattern = rf'^(.*)</{re.escape(tag_name)}>$'
457
+ closing_match = re.match(closing_pattern, rest, re.DOTALL | re.IGNORECASE)
458
+
459
+ if not closing_match:
460
+ return (text, None)
461
+
462
+ inner_content = closing_match.group(1)
463
+
464
+ # Verify this is truly a wrapping pair (no other occurrences of this tag inside)
465
+ # Count opening and closing tags of this type in the inner content
466
+ inner_opening_count = len(re.findall(rf'<{re.escape(tag_name)}(?:\s+[^>]*)?>',
467
+ inner_content, re.IGNORECASE))
468
+ inner_closing_count = len(re.findall(rf'</{re.escape(tag_name)}>', inner_content, re.IGNORECASE))
469
+
470
+ # If there are nested tags of the same type, don't strip
471
+ if inner_opening_count > 0 or inner_closing_count > 0:
472
+ return (text, None)
473
+
474
+ return (inner_content, tag_name)
475
+
476
+
406
477
  def has_formatting_tags(text: str) -> bool:
407
478
  """
408
479
  Check if text contains any formatting tags.
@@ -6222,7 +6293,10 @@ class SupervertalerQt(QMainWindow):
6222
6293
  self.enable_alternating_row_colors = True # Enable alternating row colors by default
6223
6294
  self.even_row_color = '#FFFFFF' # White for even rows
6224
6295
  self.odd_row_color = '#F0F0F0' # Light gray for odd rows
6225
-
6296
+
6297
+ # Hide outer wrapping tags in grid display (e.g. <li-o>...</li-o>)
6298
+ self.hide_outer_wrapping_tags = False # Disabled by default
6299
+
6226
6300
  # Termbase highlight style settings
6227
6301
  self.termbase_highlight_style = 'semibold' # 'background', 'dotted', or 'semibold'
6228
6302
  self.termbase_dotted_color = '#808080' # Medium gray for dotted underline (more visible)
@@ -17690,14 +17764,41 @@ class SupervertalerQt(QMainWindow):
17690
17764
 
17691
17765
  grid_group.setLayout(grid_layout)
17692
17766
  layout.addWidget(grid_group)
17693
-
17694
- # Translation Results Pane & Tag Coloring section
17695
- results_group = QGroupBox("📋 Translation Results Pane && Tag Colors")
17767
+
17768
+ # Grid Display Options section
17769
+ grid_display_group = QGroupBox("📊 Grid Display Options")
17770
+ grid_display_layout = QVBoxLayout()
17771
+
17772
+ grid_display_info = QLabel(
17773
+ "Configure how content is displayed in the translation grid."
17774
+ )
17775
+ grid_display_info.setStyleSheet("font-size: 8pt; padding: 8px; border-radius: 2px;")
17776
+ grid_display_info.setWordWrap(True)
17777
+ grid_display_layout.addWidget(grid_display_info)
17778
+
17779
+ # Hide wrapping tags checkbox
17780
+ hide_wrapping_tags_layout = QHBoxLayout()
17781
+ hide_wrapping_tags_check = CheckmarkCheckBox("Hide outer wrapping tags in grid (e.g. <li-o>...</li-o>)")
17782
+ hide_wrapping_tags_check.setChecked(font_settings.get('hide_outer_wrapping_tags', False))
17783
+ hide_wrapping_tags_check.setToolTip(
17784
+ "When enabled, structural tags that wrap the entire segment (like <li-o>, <p>, <td>) are hidden in the grid.\n"
17785
+ "The segment type is already shown in the Type column, so this reduces visual clutter.\n"
17786
+ "Inner formatting tags like <b>bold</b> are still shown.\n"
17787
+ "This affects the Source column display only - the Target column keeps tags for editing."
17788
+ )
17789
+ hide_wrapping_tags_layout.addWidget(hide_wrapping_tags_check)
17790
+ hide_wrapping_tags_layout.addStretch()
17791
+ grid_display_layout.addLayout(hide_wrapping_tags_layout)
17792
+
17793
+ grid_display_group.setLayout(grid_display_layout)
17794
+ layout.addWidget(grid_display_group)
17795
+
17796
+ # Match Panel & Tag Colors section
17797
+ results_group = QGroupBox("📋 Match Panel && Tag Colors")
17696
17798
  results_layout = QVBoxLayout()
17697
17799
 
17698
17800
  results_size_info = QLabel(
17699
- "Set the default font sizes for the translation results pane.\n"
17700
- "You can also adjust these using View menu → Translation Results Pane."
17801
+ "Set font sizes for the Match Panel (TM/termbase matches) and tag colors."
17701
17802
  )
17702
17803
  results_size_info.setStyleSheet("font-size: 8pt; padding: 8px; border-radius: 2px;")
17703
17804
  results_size_info.setWordWrap(True)
@@ -17737,7 +17838,7 @@ class SupervertalerQt(QMainWindow):
17737
17838
  show_tags_layout.addWidget(show_tags_check)
17738
17839
  show_tags_layout.addStretch()
17739
17840
  results_layout.addLayout(show_tags_layout)
17740
-
17841
+
17741
17842
  # Tag highlight color picker
17742
17843
  tag_color_layout = QHBoxLayout()
17743
17844
  tag_color_layout.addWidget(QLabel("Tag Highlight Color:"))
@@ -18295,7 +18396,8 @@ class SupervertalerQt(QMainWindow):
18295
18396
  grid_font_spin, match_font_spin, compare_font_spin, show_tags_check, tag_color_btn,
18296
18397
  alt_colors_check, even_color_btn, odd_color_btn, invisible_char_color_btn, grid_font_family_combo,
18297
18398
  termview_font_family_combo, termview_font_spin, termview_bold_check,
18298
- border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check
18399
+ border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check,
18400
+ hide_wrapping_tags_check
18299
18401
  )
18300
18402
 
18301
18403
  save_btn.clicked.connect(save_view_settings_with_scale)
@@ -20226,7 +20328,8 @@ class SupervertalerQt(QMainWindow):
20226
20328
  def _save_view_settings_from_ui(self, grid_spin, match_spin, compare_spin, show_tags_check=None, tag_color_btn=None,
20227
20329
  alt_colors_check=None, even_color_btn=None, odd_color_btn=None, invisible_char_color_btn=None,
20228
20330
  grid_font_family_combo=None, termview_font_family_combo=None, termview_font_spin=None, termview_bold_check=None,
20229
- border_color_btn=None, border_thickness_spin=None, badge_text_color_btn=None, tabs_above_check=None):
20331
+ border_color_btn=None, border_thickness_spin=None, badge_text_color_btn=None, tabs_above_check=None,
20332
+ hide_wrapping_tags_check=None):
20230
20333
  """Save view settings from UI"""
20231
20334
  # CRITICAL: Suppress TM saves during view settings update
20232
20335
  # Grid operations (setStyleSheet, rehighlight, etc.) can trigger textChanged events
@@ -20239,7 +20342,8 @@ class SupervertalerQt(QMainWindow):
20239
20342
  grid_spin, match_spin, compare_spin, show_tags_check, tag_color_btn,
20240
20343
  alt_colors_check, even_color_btn, odd_color_btn, invisible_char_color_btn,
20241
20344
  grid_font_family_combo, termview_font_family_combo, termview_font_spin, termview_bold_check,
20242
- border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check
20345
+ border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check,
20346
+ hide_wrapping_tags_check
20243
20347
  )
20244
20348
  finally:
20245
20349
  self._suppress_target_change_handlers = previous_suppression
@@ -20247,16 +20351,18 @@ class SupervertalerQt(QMainWindow):
20247
20351
  def _save_view_settings_from_ui_impl(self, grid_spin, match_spin, compare_spin, show_tags_check=None, tag_color_btn=None,
20248
20352
  alt_colors_check=None, even_color_btn=None, odd_color_btn=None, invisible_char_color_btn=None,
20249
20353
  grid_font_family_combo=None, termview_font_family_combo=None, termview_font_spin=None, termview_bold_check=None,
20250
- border_color_btn=None, border_thickness_spin=None, badge_text_color_btn=None, tabs_above_check=None):
20354
+ border_color_btn=None, border_thickness_spin=None, badge_text_color_btn=None, tabs_above_check=None,
20355
+ hide_wrapping_tags_check=None):
20251
20356
  """Implementation of save view settings (called with TM saves suppressed)"""
20252
- general_settings = {
20253
- 'restore_last_project': self.load_general_settings().get('restore_last_project', False),
20254
- 'auto_propagate_exact_matches': self.auto_propagate_exact_matches, # Keep existing value
20357
+ # Load existing settings first to preserve all values, then update with new ones
20358
+ general_settings = self.load_general_settings()
20359
+ general_settings.update({
20360
+ 'auto_propagate_exact_matches': self.auto_propagate_exact_matches,
20255
20361
  'grid_font_size': grid_spin.value(),
20256
20362
  'results_match_font_size': match_spin.value(),
20257
20363
  'results_compare_font_size': compare_spin.value(),
20258
- 'enable_tm_termbase_matching': self.enable_tm_matching # Save TM/termbase matching state
20259
- }
20364
+ 'enable_tm_termbase_matching': self.enable_tm_matching
20365
+ })
20260
20366
 
20261
20367
  # Add tabs above grid setting if provided
20262
20368
  if tabs_above_check is not None:
@@ -20296,7 +20402,12 @@ class SupervertalerQt(QMainWindow):
20296
20402
  if invisible_char_color:
20297
20403
  general_settings['invisible_char_color'] = invisible_char_color
20298
20404
  self.invisible_char_color = invisible_char_color
20299
-
20405
+
20406
+ # Add hide outer wrapping tags setting if provided
20407
+ if hide_wrapping_tags_check is not None:
20408
+ general_settings['hide_outer_wrapping_tags'] = hide_wrapping_tags_check.isChecked()
20409
+ self.hide_outer_wrapping_tags = hide_wrapping_tags_check.isChecked()
20410
+
20300
20411
  # Add focus border settings if provided
20301
20412
  if border_color_btn is not None:
20302
20413
  border_color = border_color_btn.property('selected_color')
@@ -20460,7 +20571,11 @@ class SupervertalerQt(QMainWindow):
20460
20571
  self.refresh_grid_tag_colors()
20461
20572
  # Also refresh row colors
20462
20573
  self.apply_alternating_row_colors()
20463
-
20574
+
20575
+ # Refresh source column if hide_outer_wrapping_tags setting changed
20576
+ if hide_wrapping_tags_check is not None and hasattr(self, 'table') and self.table is not None:
20577
+ self._refresh_source_column_display()
20578
+
20464
20579
  self.log("✓ View settings saved and applied")
20465
20580
  # Use explicit QMessageBox instance to ensure proper dialog closing
20466
20581
  msg = QMessageBox(self)
@@ -29801,8 +29916,13 @@ class SupervertalerQt(QMainWindow):
29801
29916
  self.table.setItem(row, 1, type_item)
29802
29917
 
29803
29918
  # Source - Use read-only QTextEdit widget for easy text selection
29919
+ # Strip outer wrapping tags if setting is enabled (display only, data unchanged)
29920
+ source_for_display = segment.source
29921
+ if self.hide_outer_wrapping_tags:
29922
+ stripped, _ = strip_outer_wrapping_tags(segment.source)
29923
+ source_for_display = stripped
29804
29924
  # Apply invisible character replacements for display only
29805
- source_display_text = self.apply_invisible_replacements(segment.source)
29925
+ source_display_text = self.apply_invisible_replacements(source_for_display)
29806
29926
  source_editor = ReadOnlyGridTextEditor(source_display_text, self.table, row)
29807
29927
 
29808
29928
  # Initialize empty termbase matches (will be populated lazily on segment selection or by background worker)
@@ -29821,6 +29941,7 @@ class SupervertalerQt(QMainWindow):
29821
29941
  self.table.setItem(row, 2, source_item)
29822
29942
 
29823
29943
  # Target - Use editable QTextEdit widget for easy text selection and editing
29944
+ # Note: We don't strip wrapping tags from target cells since users edit them directly
29824
29945
  # Apply invisible character replacements for display (will be reversed when saving)
29825
29946
  target_display_text = self.apply_invisible_replacements(segment.target)
29826
29947
  target_editor = EditableGridTextEditor(target_display_text, self.table, row, self.table)
@@ -31758,7 +31879,10 @@ class SupervertalerQt(QMainWindow):
31758
31879
  self.enable_alternating_row_colors = settings.get('enable_alternating_row_colors', True)
31759
31880
  self.even_row_color = settings.get('even_row_color', '#FFFFFF')
31760
31881
  self.odd_row_color = settings.get('odd_row_color', '#F0F0F0')
31761
-
31882
+
31883
+ # Load hide outer wrapping tags setting
31884
+ self.hide_outer_wrapping_tags = settings.get('hide_outer_wrapping_tags', False)
31885
+
31762
31886
  # Load termbase highlight style settings
31763
31887
  self.termbase_highlight_style = settings.get('termbase_highlight_style', 'semibold')
31764
31888
  self.termbase_dotted_color = settings.get('termbase_dotted_color', '#808080')
@@ -37405,6 +37529,34 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
37405
37529
  # during load_segments_to_grid when creating cell widgets
37406
37530
  self.load_segments_to_grid()
37407
37531
 
37532
+ def _refresh_source_column_display(self):
37533
+ """Refresh source column to reflect hide_outer_wrapping_tags setting"""
37534
+ if not hasattr(self, 'table') or not self.table:
37535
+ return
37536
+ if not hasattr(self, 'current_project') or not self.current_project:
37537
+ return
37538
+
37539
+ # Update each source cell with potentially stripped tags
37540
+ for row in range(self.table.rowCount()):
37541
+ if row >= len(self.current_project.segments):
37542
+ continue
37543
+
37544
+ segment = self.current_project.segments[row]
37545
+ source_widget = self.table.cellWidget(row, 2)
37546
+
37547
+ if source_widget and hasattr(source_widget, 'setPlainText'):
37548
+ # Calculate display text with or without outer wrapping tags
37549
+ source_for_display = segment.source
37550
+ if self.hide_outer_wrapping_tags:
37551
+ stripped, _ = strip_outer_wrapping_tags(segment.source)
37552
+ source_for_display = stripped
37553
+ source_display_text = self.apply_invisible_replacements(source_for_display)
37554
+ source_widget.setPlainText(source_display_text)
37555
+
37556
+ # Re-apply highlighting
37557
+ if hasattr(source_widget, 'highlighter'):
37558
+ source_widget.highlighter.rehighlight()
37559
+
37408
37560
  def apply_invisible_replacements(self, text):
37409
37561
  """Apply invisible character replacements to text based on settings"""
37410
37562
  if not hasattr(self, 'invisible_display_settings'):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervertaler
3
- Version: 1.9.196
3
+ Version: 1.9.197
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
@@ -1,4 +1,4 @@
1
- Supervertaler.py,sha256=OQinmBTFrHuVWv_Y_LDBgnpmYZNDNGUVgtIYvpgBZt0,2381042
1
+ Supervertaler.py,sha256=YdvLAgY__whrP8VBodeeVh70BXurnBlJ4PpFrFUkXdk,2387857
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
@@ -80,9 +80,9 @@ modules/unified_prompt_manager_qt.py,sha256=y7xAIM9X7f1afXGE1tOcAc5EY7BKFy53Rgqi
80
80
  modules/voice_commands.py,sha256=iBb-gjWxRMLhFH7-InSRjYJz1EIDBNA2Pog8V7TtJaY,38516
81
81
  modules/voice_dictation.py,sha256=QmitXfkG-vRt5hIQATjphHdhXfqmwhzcQcbXB6aRzIg,16386
82
82
  modules/voice_dictation_lite.py,sha256=jorY0BmWE-8VczbtGrWwt1zbnOctMoSlWOsQrcufBcc,9423
83
- supervertaler-1.9.196.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
84
- supervertaler-1.9.196.dist-info/METADATA,sha256=QaMp-9_npfNkQ0ptVGsulOnMQD73QlXB44mKdAIAAMk,5725
85
- supervertaler-1.9.196.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
86
- supervertaler-1.9.196.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
87
- supervertaler-1.9.196.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
88
- supervertaler-1.9.196.dist-info/RECORD,,
83
+ supervertaler-1.9.197.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
84
+ supervertaler-1.9.197.dist-info/METADATA,sha256=6_J_jdwGKZ-MCwIJODfkHTxXjhBHY7Wf5pK-xDMbQwU,5725
85
+ supervertaler-1.9.197.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
86
+ supervertaler-1.9.197.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
87
+ supervertaler-1.9.197.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
88
+ supervertaler-1.9.197.dist-info/RECORD,,