supervertaler 1.9.198__py3-none-any.whl → 1.9.203__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 +276 -179
- {supervertaler-1.9.198.dist-info → supervertaler-1.9.203.dist-info}/METADATA +1 -1
- {supervertaler-1.9.198.dist-info → supervertaler-1.9.203.dist-info}/RECORD +7 -7
- {supervertaler-1.9.198.dist-info → supervertaler-1.9.203.dist-info}/WHEEL +0 -0
- {supervertaler-1.9.198.dist-info → supervertaler-1.9.203.dist-info}/entry_points.txt +0 -0
- {supervertaler-1.9.198.dist-info → supervertaler-1.9.203.dist-info}/licenses/LICENSE +0 -0
- {supervertaler-1.9.198.dist-info → supervertaler-1.9.203.dist-info}/top_level.txt +0 -0
Supervertaler.py
CHANGED
|
@@ -32,9 +32,9 @@ License: MIT
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
# Version Information.
|
|
35
|
-
__version__ = "1.9.
|
|
35
|
+
__version__ = "1.9.203"
|
|
36
36
|
__phase__ = "0.9"
|
|
37
|
-
__release_date__ = "2026-02-
|
|
37
|
+
__release_date__ = "2026-02-03"
|
|
38
38
|
__edition__ = "Qt"
|
|
39
39
|
|
|
40
40
|
import sys
|
|
@@ -474,33 +474,6 @@ def strip_outer_wrapping_tags(text: str) -> tuple:
|
|
|
474
474
|
return (inner_content, tag_name)
|
|
475
475
|
|
|
476
476
|
|
|
477
|
-
def get_list_prefix_for_tag(tag_name: str, list_position: int = 1) -> str:
|
|
478
|
-
"""
|
|
479
|
-
Get visual prefix for list items in WYSIWYG mode.
|
|
480
|
-
|
|
481
|
-
Args:
|
|
482
|
-
tag_name: The outer wrapping tag name (e.g., 'li-o', 'li-b', 'li')
|
|
483
|
-
list_position: Position within the list (1-indexed) for ordered lists
|
|
484
|
-
|
|
485
|
-
Returns:
|
|
486
|
-
Visual prefix string: "1. ", "2. ", "• ", etc., or "" if not a list tag
|
|
487
|
-
"""
|
|
488
|
-
if not tag_name:
|
|
489
|
-
return ""
|
|
490
|
-
|
|
491
|
-
tag_lower = tag_name.lower()
|
|
492
|
-
|
|
493
|
-
# Ordered list items
|
|
494
|
-
if tag_lower == 'li-o':
|
|
495
|
-
return f"{list_position}. "
|
|
496
|
-
|
|
497
|
-
# Unordered/bullet list items
|
|
498
|
-
if tag_lower in ('li-b', 'li'):
|
|
499
|
-
return "• "
|
|
500
|
-
|
|
501
|
-
return ""
|
|
502
|
-
|
|
503
|
-
|
|
504
477
|
def has_formatting_tags(text: str) -> bool:
|
|
505
478
|
"""
|
|
506
479
|
Check if text contains any formatting tags.
|
|
@@ -8294,30 +8267,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
8294
8267
|
grid_font_family_menu.addAction(font_action)
|
|
8295
8268
|
|
|
8296
8269
|
view_menu.addSeparator()
|
|
8297
|
-
|
|
8298
|
-
# Translation Results Pane section
|
|
8299
|
-
results_zoom_menu = view_menu.addMenu("📋 Translation &Results Pane")
|
|
8300
|
-
|
|
8301
|
-
results_zoom_in_action = QAction("Results Zoom &In", self)
|
|
8302
|
-
results_zoom_in_action.setShortcut("Ctrl+Shift+=")
|
|
8303
|
-
results_zoom_in_action.triggered.connect(self.results_pane_zoom_in)
|
|
8304
|
-
results_zoom_menu.addAction(results_zoom_in_action)
|
|
8305
|
-
|
|
8306
|
-
results_zoom_out_action = QAction("Results Zoom &Out", self)
|
|
8307
|
-
results_zoom_out_action.setShortcut("Ctrl+Shift+-")
|
|
8308
|
-
results_zoom_out_action.triggered.connect(self.results_pane_zoom_out)
|
|
8309
|
-
results_zoom_menu.addAction(results_zoom_out_action)
|
|
8310
|
-
|
|
8311
|
-
results_zoom_reset_action = QAction("Results Zoom &Reset", self)
|
|
8312
|
-
results_zoom_reset_action.triggered.connect(self.results_pane_zoom_reset)
|
|
8313
|
-
results_zoom_menu.addAction(results_zoom_reset_action)
|
|
8314
|
-
|
|
8315
|
-
results_zoom_menu.addSeparator()
|
|
8316
|
-
|
|
8317
|
-
results_note = QAction("(Includes match list + compare boxes)", self)
|
|
8318
|
-
results_note.setEnabled(False)
|
|
8319
|
-
results_zoom_menu.addAction(results_note)
|
|
8320
|
-
|
|
8270
|
+
|
|
8321
8271
|
# Match Panel zoom section
|
|
8322
8272
|
match_panel_zoom_menu = view_menu.addMenu("🔍 &Match Panel")
|
|
8323
8273
|
|
|
@@ -17808,10 +17758,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
17808
17758
|
hide_wrapping_tags_check = CheckmarkCheckBox("Hide outer wrapping tags in grid (e.g. <li-o>...</li-o>)")
|
|
17809
17759
|
hide_wrapping_tags_check.setChecked(font_settings.get('hide_outer_wrapping_tags', False))
|
|
17810
17760
|
hide_wrapping_tags_check.setToolTip(
|
|
17811
|
-
"When enabled, structural tags that wrap the entire segment (like <li-o>, <p>, <td>) are hidden
|
|
17812
|
-
"
|
|
17813
|
-
"
|
|
17814
|
-
"
|
|
17761
|
+
"When enabled, structural tags that wrap the entire segment (like <li-o>, <p>, <td>) are hidden.\n"
|
|
17762
|
+
"Tags are automatically restored when saving. List items show visual prefixes (1. 2. or •).\n"
|
|
17763
|
+
"The segment type is shown in the Type column, so outer tags are redundant.\n"
|
|
17764
|
+
"Inner formatting tags like <b>bold</b> are still shown."
|
|
17815
17765
|
)
|
|
17816
17766
|
hide_wrapping_tags_layout.addWidget(hide_wrapping_tags_check)
|
|
17817
17767
|
hide_wrapping_tags_layout.addStretch()
|
|
@@ -20432,8 +20382,9 @@ class SupervertalerQt(QMainWindow):
|
|
|
20432
20382
|
|
|
20433
20383
|
# Add hide outer wrapping tags setting if provided
|
|
20434
20384
|
if hide_wrapping_tags_check is not None:
|
|
20435
|
-
|
|
20436
|
-
|
|
20385
|
+
hide_tags_value = hide_wrapping_tags_check.isChecked()
|
|
20386
|
+
general_settings['hide_outer_wrapping_tags'] = hide_tags_value
|
|
20387
|
+
self.hide_outer_wrapping_tags = hide_tags_value
|
|
20437
20388
|
|
|
20438
20389
|
# Add focus border settings if provided
|
|
20439
20390
|
if border_color_btn is not None:
|
|
@@ -21413,70 +21364,46 @@ class SupervertalerQt(QMainWindow):
|
|
|
21413
21364
|
|
|
21414
21365
|
# Add left side to main horizontal splitter
|
|
21415
21366
|
main_horizontal_splitter.addWidget(left_container)
|
|
21416
|
-
|
|
21417
|
-
# Right side: Translation Results panel with Preview tab
|
|
21418
|
-
from modules.translation_results_panel import TranslationResultsPanel
|
|
21419
|
-
|
|
21367
|
+
|
|
21420
21368
|
# Create tabbed container for right panel
|
|
21421
21369
|
right_tabs = QTabWidget()
|
|
21422
21370
|
right_tabs.tabBar().setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
|
21423
21371
|
right_tabs.tabBar().setDrawBase(False)
|
|
21424
21372
|
right_tabs.setStyleSheet("QTabBar::tab { outline: 0; } QTabBar::tab:focus { outline: none; } QTabBar::tab:selected { border-bottom: 1px solid #2196F3; background-color: rgba(33, 150, 243, 0.08); }")
|
|
21425
|
-
|
|
21426
|
-
#
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
# Connect signals for match selection/insertion
|
|
21430
|
-
self.translation_results_panel.match_selected.connect(self.on_match_selected)
|
|
21431
|
-
self.translation_results_panel.match_inserted.connect(self.on_match_inserted)
|
|
21432
|
-
|
|
21433
|
-
# Connect notes editing to save segment notes
|
|
21434
|
-
if hasattr(self.translation_results_panel, 'notes_edit') and self.translation_results_panel.notes_edit:
|
|
21435
|
-
self.translation_results_panel.notes_edit.textChanged.connect(self._on_results_panel_notes_changed)
|
|
21436
|
-
|
|
21437
|
-
# Register this panel so it receives updates when segments are selected
|
|
21373
|
+
|
|
21374
|
+
# NOTE: Translation Results panel is deprecated - MT/LLM is now only via QuickTrans (Ctrl+M)
|
|
21375
|
+
# Panel is no longer created to save resources. Set to None for compatibility checks.
|
|
21376
|
+
self.translation_results_panel = None
|
|
21438
21377
|
if not hasattr(self, 'results_panels'):
|
|
21439
21378
|
self.results_panels = []
|
|
21440
|
-
|
|
21441
|
-
|
|
21379
|
+
|
|
21442
21380
|
# Track tab indices for visibility-aware default selection
|
|
21443
21381
|
tab_index = 0
|
|
21444
|
-
results_tab_index = -1
|
|
21445
|
-
compare_tab_index = -1
|
|
21446
21382
|
preview_tab_index = -1
|
|
21447
|
-
|
|
21448
|
-
# Tab 1:
|
|
21449
|
-
if self.show_translation_results_pane:
|
|
21450
|
-
right_tabs.addTab(self.translation_results_panel, "🔍 Translation Results")
|
|
21451
|
-
results_tab_index = tab_index
|
|
21452
|
-
tab_index += 1
|
|
21453
|
-
else:
|
|
21454
|
-
# Hide the panel if not added as tab (it still exists as child widget)
|
|
21455
|
-
self.translation_results_panel.hide()
|
|
21456
|
-
|
|
21457
|
-
# Tab 2: Match Panel (Termview + TM Source/Target) - shown first by default
|
|
21383
|
+
|
|
21384
|
+
# Tab 1: Match Panel (Termview + TM Source/Target) - primary TM display
|
|
21458
21385
|
match_panel_widget = self._create_match_panel()
|
|
21459
21386
|
self.match_panel_widget = match_panel_widget # Store reference for mode detection
|
|
21460
21387
|
right_tabs.addTab(match_panel_widget, "🎯 Match Panel")
|
|
21461
21388
|
match_panel_tab_index = tab_index
|
|
21462
21389
|
tab_index += 1
|
|
21463
21390
|
|
|
21464
|
-
# Tab
|
|
21391
|
+
# Tab 2: Document Preview
|
|
21465
21392
|
preview_widget = self._create_preview_tab()
|
|
21466
21393
|
right_tabs.addTab(preview_widget, "📄 Preview")
|
|
21467
21394
|
preview_tab_index = tab_index
|
|
21468
21395
|
self._preview_tab_index = preview_tab_index # Store for visibility checks
|
|
21469
21396
|
tab_index += 1
|
|
21470
|
-
|
|
21471
|
-
# Tab
|
|
21397
|
+
|
|
21398
|
+
# Tab 3: Segment Note
|
|
21472
21399
|
right_tabs.addTab(self._notes_widget_for_right_panel, "📝 Segment note")
|
|
21473
21400
|
tab_index += 1
|
|
21474
|
-
|
|
21475
|
-
# Tab
|
|
21401
|
+
|
|
21402
|
+
# Tab 4: Session Log
|
|
21476
21403
|
right_tabs.addTab(self._session_log_widget_for_right_panel, "📋 Session Log")
|
|
21477
21404
|
tab_index += 1
|
|
21478
|
-
|
|
21479
|
-
# Tab
|
|
21405
|
+
|
|
21406
|
+
# Tab 5: Scratchpad (private translator notes for the whole project)
|
|
21480
21407
|
right_tabs.addTab(self._scratchpad_widget_for_right_panel, "📝 Scratchpad")
|
|
21481
21408
|
tab_index += 1
|
|
21482
21409
|
|
|
@@ -21623,7 +21550,13 @@ class SupervertalerQt(QMainWindow):
|
|
|
21623
21550
|
self.table.setRowHidden(row, not in_page)
|
|
21624
21551
|
finally:
|
|
21625
21552
|
self.table.setUpdatesEnabled(True)
|
|
21626
|
-
|
|
21553
|
+
|
|
21554
|
+
# Recalculate heights for visible rows to prevent layout corruption.
|
|
21555
|
+
# Call synchronously first, then schedule a deferred resize to catch any
|
|
21556
|
+
# layout issues that Qt processes asynchronously.
|
|
21557
|
+
self._resize_visible_rows()
|
|
21558
|
+
QTimer.singleShot(50, self._resize_visible_rows)
|
|
21559
|
+
|
|
21627
21560
|
# Update pagination UI
|
|
21628
21561
|
self._update_pagination_ui()
|
|
21629
21562
|
|
|
@@ -21915,7 +21848,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
21915
21848
|
header.setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch) # Target - stretch to fill space
|
|
21916
21849
|
header.setSectionResizeMode(4, QHeaderView.ResizeMode.Interactive) # Status - allow resizing
|
|
21917
21850
|
header.setStretchLastSection(False) # Don't auto-stretch last section (we use Stretch mode for Source/Target)
|
|
21918
|
-
|
|
21851
|
+
|
|
21852
|
+
# Recalculate row heights when columns are resized (text reflows to new width)
|
|
21853
|
+
header.sectionResized.connect(self._on_column_resized)
|
|
21854
|
+
|
|
21919
21855
|
# Set initial column widths - give Source and Target equal space
|
|
21920
21856
|
# ID column width will be auto-adjusted by _update_segment_column_width() after segments load
|
|
21921
21857
|
self.table.setColumnWidth(0, 35) # ID - temporary, auto-adjusts to fit content
|
|
@@ -23797,6 +23733,9 @@ class SupervertalerQt(QMainWindow):
|
|
|
23797
23733
|
- 'latest': Overwrites existing entries with same source (keeps only newest translation)
|
|
23798
23734
|
- 'all': Keeps all translations with different targets (default SQLite behavior)
|
|
23799
23735
|
|
|
23736
|
+
If hide_outer_wrapping_tags is enabled, strips structural tags (like <li-o>, <p>)
|
|
23737
|
+
before saving to TM for better matching leverage.
|
|
23738
|
+
|
|
23800
23739
|
Args:
|
|
23801
23740
|
source: Source text
|
|
23802
23741
|
target: Target text
|
|
@@ -23804,6 +23743,11 @@ class SupervertalerQt(QMainWindow):
|
|
|
23804
23743
|
if not self.current_project:
|
|
23805
23744
|
return
|
|
23806
23745
|
|
|
23746
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
23747
|
+
if self.hide_outer_wrapping_tags:
|
|
23748
|
+
source, _ = strip_outer_wrapping_tags(source)
|
|
23749
|
+
target, _ = strip_outer_wrapping_tags(target)
|
|
23750
|
+
|
|
23807
23751
|
if not hasattr(self.current_project, 'source_lang') or not hasattr(self.current_project, 'target_lang'):
|
|
23808
23752
|
return
|
|
23809
23753
|
|
|
@@ -29946,16 +29890,9 @@ class SupervertalerQt(QMainWindow):
|
|
|
29946
29890
|
# Strip outer wrapping tags if setting is enabled (display only, data unchanged)
|
|
29947
29891
|
source_for_display = segment.source
|
|
29948
29892
|
stripped_source_tag = None
|
|
29949
|
-
list_prefix = ""
|
|
29950
29893
|
if self.hide_outer_wrapping_tags:
|
|
29951
29894
|
stripped, stripped_source_tag = strip_outer_wrapping_tags(segment.source)
|
|
29952
29895
|
source_for_display = stripped
|
|
29953
|
-
# Add WYSIWYG list prefix (e.g., "1. " or "• ") for list items
|
|
29954
|
-
if stripped_source_tag:
|
|
29955
|
-
list_pos = list_numbers.get(row, 1)
|
|
29956
|
-
list_prefix = get_list_prefix_for_tag(stripped_source_tag, list_pos)
|
|
29957
|
-
if list_prefix:
|
|
29958
|
-
source_for_display = list_prefix + source_for_display
|
|
29959
29896
|
# Apply invisible character replacements for display only
|
|
29960
29897
|
source_display_text = self.apply_invisible_replacements(source_for_display)
|
|
29961
29898
|
source_editor = ReadOnlyGridTextEditor(source_display_text, self.table, row)
|
|
@@ -29979,26 +29916,19 @@ class SupervertalerQt(QMainWindow):
|
|
|
29979
29916
|
# Strip outer wrapping tags if enabled (will be auto-restored when saving)
|
|
29980
29917
|
target_for_display = segment.target
|
|
29981
29918
|
stripped_target_tag = None
|
|
29982
|
-
target_list_prefix = ""
|
|
29983
29919
|
if self.hide_outer_wrapping_tags:
|
|
29984
29920
|
stripped, stripped_target_tag = strip_outer_wrapping_tags(segment.target)
|
|
29985
29921
|
target_for_display = stripped
|
|
29986
29922
|
# Store stripped tag on segment for restore during save
|
|
29987
29923
|
# Use source tag as reference (target should match source structure)
|
|
29988
29924
|
segment._stripped_outer_tag = stripped_source_tag or stripped_target_tag
|
|
29989
|
-
# Add WYSIWYG list prefix for display (same as source)
|
|
29990
|
-
target_list_prefix = list_prefix # Reuse the prefix calculated for source
|
|
29991
29925
|
|
|
29992
29926
|
# Apply invisible character replacements for display (will be reversed when saving)
|
|
29993
29927
|
target_display_text = self.apply_invisible_replacements(target_for_display)
|
|
29994
|
-
# Add list prefix for display (stored separately so we can strip it on save)
|
|
29995
|
-
if target_list_prefix:
|
|
29996
|
-
target_display_text = target_list_prefix + target_display_text
|
|
29997
29928
|
target_editor = EditableGridTextEditor(target_display_text, self.table, row, self.table)
|
|
29998
29929
|
target_editor.setFont(font)
|
|
29999
|
-
# Store stripped tag
|
|
29930
|
+
# Store stripped tag on editor for auto-restore
|
|
30000
29931
|
target_editor._stripped_outer_tag = stripped_source_tag or stripped_target_tag
|
|
30001
|
-
target_editor._list_prefix = target_list_prefix
|
|
30002
29932
|
|
|
30003
29933
|
# Connect text changes to update segment
|
|
30004
29934
|
# Use a factory function to create a proper closure that captures the segment ID
|
|
@@ -30010,11 +29940,6 @@ class SupervertalerQt(QMainWindow):
|
|
|
30010
29940
|
nonlocal debounce_timer
|
|
30011
29941
|
new_text = editor_widget.toPlainText()
|
|
30012
29942
|
|
|
30013
|
-
# Strip WYSIWYG list prefix if present (display-only, not saved)
|
|
30014
|
-
list_prefix = getattr(editor_widget, '_list_prefix', '')
|
|
30015
|
-
if list_prefix and new_text.startswith(list_prefix):
|
|
30016
|
-
new_text = new_text[len(list_prefix):]
|
|
30017
|
-
|
|
30018
29943
|
# Reverse invisible character replacements before saving
|
|
30019
29944
|
new_text = self.reverse_invisible_replacements(new_text)
|
|
30020
29945
|
|
|
@@ -30155,10 +30080,15 @@ class SupervertalerQt(QMainWindow):
|
|
|
30155
30080
|
|
|
30156
30081
|
# Update progress stats in status bar
|
|
30157
30082
|
self.update_progress_stats()
|
|
30158
|
-
|
|
30083
|
+
|
|
30159
30084
|
# Refresh document preview
|
|
30160
30085
|
self.refresh_preview()
|
|
30161
30086
|
|
|
30087
|
+
# Deferred row resize: columns use Stretch mode, so their final widths
|
|
30088
|
+
# are only known after Qt processes the layout. Schedule a resize for
|
|
30089
|
+
# after all pending events are processed.
|
|
30090
|
+
QTimer.singleShot(0, self._resize_visible_rows)
|
|
30091
|
+
|
|
30162
30092
|
# =========================================================================
|
|
30163
30093
|
# COMPARE PANEL TAB
|
|
30164
30094
|
# =========================================================================
|
|
@@ -31111,14 +31041,15 @@ class SupervertalerQt(QMainWindow):
|
|
|
31111
31041
|
import re
|
|
31112
31042
|
list_numbers = {} # {segment_id: list_number}
|
|
31113
31043
|
list_counter = 0
|
|
31114
|
-
|
|
31044
|
+
prev_was_ordered_list = False
|
|
31045
|
+
|
|
31115
31046
|
for seg in self.current_project.segments:
|
|
31116
31047
|
source_text = seg.source.strip() if seg.source else ""
|
|
31117
|
-
|
|
31048
|
+
|
|
31118
31049
|
# Check for list tags (new <li-o>, <li-b> and legacy <li>)
|
|
31119
31050
|
has_li_ordered = '<li-o>' in source_text or ('<li>' in source_text and '<li-b>' not in source_text)
|
|
31120
31051
|
has_li_bullet = '<li-b>' in source_text
|
|
31121
|
-
|
|
31052
|
+
|
|
31122
31053
|
if has_li_ordered:
|
|
31123
31054
|
# Check for explicit number in text
|
|
31124
31055
|
num_match = re.match(r'^<li(?:-o)?>\s*(\d+)[.)\s]', source_text)
|
|
@@ -31126,8 +31057,15 @@ class SupervertalerQt(QMainWindow):
|
|
|
31126
31057
|
list_numbers[seg.id] = int(num_match.group(1))
|
|
31127
31058
|
list_counter = int(num_match.group(1))
|
|
31128
31059
|
else:
|
|
31060
|
+
# Reset counter if starting a new list (gap since last ordered item)
|
|
31061
|
+
if not prev_was_ordered_list:
|
|
31062
|
+
list_counter = 0
|
|
31129
31063
|
list_counter += 1
|
|
31130
31064
|
list_numbers[seg.id] = list_counter
|
|
31065
|
+
prev_was_ordered_list = True
|
|
31066
|
+
else:
|
|
31067
|
+
# Not an ordered list item - mark gap for potential new list
|
|
31068
|
+
prev_was_ordered_list = False
|
|
31131
31069
|
|
|
31132
31070
|
# Render all segments
|
|
31133
31071
|
current_segment_id = self._get_current_segment_id()
|
|
@@ -31142,12 +31080,20 @@ class SupervertalerQt(QMainWindow):
|
|
|
31142
31080
|
# Type field values: "para", "heading", "list_item", "table_cell", "Sub", "¶", etc.
|
|
31143
31081
|
seg_type_lower = seg_type.lower() if seg_type else 'para'
|
|
31144
31082
|
|
|
31083
|
+
# Check if this is a list item (need source_text first)
|
|
31084
|
+
source_text = seg.source.strip() if seg.source else ""
|
|
31085
|
+
|
|
31145
31086
|
# Check if this is a heading/subtitle
|
|
31146
|
-
|
|
31087
|
+
# Also detect bold-wrapped short text as section headings (e.g., <b>TECHNICAL FIELD</b>)
|
|
31088
|
+
is_heading = ('Heading' in style or 'Title' in style or 'Subtitle' in style
|
|
31147
31089
|
or seg_type_lower in ('heading', 'sub', 'subtitle', 'title'))
|
|
31148
|
-
|
|
31149
|
-
#
|
|
31150
|
-
|
|
31090
|
+
|
|
31091
|
+
# Detect bold section headings: <b>TEXT</b> where text is short and likely a heading
|
|
31092
|
+
if not is_heading and source_text.startswith('<b>') and source_text.endswith('</b>'):
|
|
31093
|
+
inner_text = source_text[3:-4].strip() # Remove <b> and </b>
|
|
31094
|
+
# Short text (under 80 chars) that's bold is likely a section heading
|
|
31095
|
+
if len(inner_text) < 80 and '<' not in inner_text:
|
|
31096
|
+
is_heading = True
|
|
31151
31097
|
is_list_item = ('<li-o>' in source_text or '<li-b>' in source_text or
|
|
31152
31098
|
'<li>' in source_text or seg_type_lower == 'list_item')
|
|
31153
31099
|
|
|
@@ -31166,9 +31112,15 @@ class SupervertalerQt(QMainWindow):
|
|
|
31166
31112
|
prev_type = getattr(prev_seg, 'type', 'para') or 'para'
|
|
31167
31113
|
prev_type_lower = prev_type.lower() if prev_type else 'para'
|
|
31168
31114
|
|
|
31169
|
-
prev_is_heading = ('Heading' in prev_style or 'Title' in prev_style or
|
|
31170
|
-
'Subtitle' in prev_style or
|
|
31115
|
+
prev_is_heading = ('Heading' in prev_style or 'Title' in prev_style or
|
|
31116
|
+
'Subtitle' in prev_style or
|
|
31171
31117
|
prev_type_lower in ('heading', 'sub', 'subtitle', 'title'))
|
|
31118
|
+
# Also detect bold section headings in previous segment
|
|
31119
|
+
prev_source = prev_seg.source.strip() if prev_seg.source else ""
|
|
31120
|
+
if not prev_is_heading and prev_source.startswith('<b>') and prev_source.endswith('</b>'):
|
|
31121
|
+
prev_inner = prev_source[3:-4].strip()
|
|
31122
|
+
if len(prev_inner) < 80 and '<' not in prev_inner:
|
|
31123
|
+
prev_is_heading = True
|
|
31172
31124
|
prev_is_paragraph = prev_type == '¶' or prev_type_lower == 'para'
|
|
31173
31125
|
prev_is_list = prev_type_lower == 'list_item' or '<li' in (prev_seg.source or '')
|
|
31174
31126
|
|
|
@@ -31190,9 +31142,15 @@ class SupervertalerQt(QMainWindow):
|
|
|
31190
31142
|
else:
|
|
31191
31143
|
# Same paragraph, running text
|
|
31192
31144
|
need_space = True
|
|
31193
|
-
# 4. List items:
|
|
31145
|
+
# 4. List items: ordered list items (like claims) get paragraph spacing
|
|
31194
31146
|
elif is_list_item or prev_is_list:
|
|
31195
|
-
|
|
31147
|
+
# Ordered list items (<li-o>) get double breaks for visual separation
|
|
31148
|
+
is_ordered_list = '<li-o>' in source_text or ('<li>' in source_text and '<li-b>' not in source_text)
|
|
31149
|
+
prev_is_ordered = '<li-o>' in (prev_seg.source or '') or ('<li>' in (prev_seg.source or '') and '<li-b>' not in (prev_seg.source or ''))
|
|
31150
|
+
if is_ordered_list or prev_is_ordered:
|
|
31151
|
+
need_double_break = True # Paragraph-like spacing for numbered items
|
|
31152
|
+
else:
|
|
31153
|
+
need_single_break = True # Bullet lists stay compact
|
|
31196
31154
|
# 5. Default: space (running text)
|
|
31197
31155
|
else:
|
|
31198
31156
|
need_space = True
|
|
@@ -31227,6 +31185,11 @@ class SupervertalerQt(QMainWindow):
|
|
|
31227
31185
|
char_format.setFontPointSize(13)
|
|
31228
31186
|
char_format.setFontWeight(QFont.Weight.Bold)
|
|
31229
31187
|
char_format.setForeground(QColor('#1f4068'))
|
|
31188
|
+
elif is_heading:
|
|
31189
|
+
# Bold section headings detected from <b>TEXT</b> pattern
|
|
31190
|
+
char_format.setFontPointSize(12)
|
|
31191
|
+
char_format.setFontWeight(QFont.Weight.Bold)
|
|
31192
|
+
char_format.setForeground(QColor('#1a365d'))
|
|
31230
31193
|
else:
|
|
31231
31194
|
char_format.setFontPointSize(11)
|
|
31232
31195
|
char_format.setForeground(QColor('#2d2d2d'))
|
|
@@ -31485,9 +31448,11 @@ class SupervertalerQt(QMainWindow):
|
|
|
31485
31448
|
def _refresh_segment_status(self, segment: Segment):
|
|
31486
31449
|
if not self.current_project:
|
|
31487
31450
|
return
|
|
31451
|
+
updated_row = None
|
|
31488
31452
|
for row, seg in enumerate(self.current_project.segments):
|
|
31489
31453
|
if seg.id == segment.id:
|
|
31490
31454
|
self._update_status_cell(row, seg)
|
|
31455
|
+
updated_row = row
|
|
31491
31456
|
break
|
|
31492
31457
|
|
|
31493
31458
|
# Update list view entry in place (if visible)
|
|
@@ -31509,7 +31474,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
31509
31474
|
status_tooltip += f"\nComment: {segment.notes.strip()}"
|
|
31510
31475
|
item.setToolTip(2, status_tooltip)
|
|
31511
31476
|
item.setBackground(2, QColor(status_def.color))
|
|
31512
|
-
|
|
31477
|
+
|
|
31478
|
+
# Recalculate row height after status cell update to prevent layout corruption
|
|
31479
|
+
if updated_row is not None:
|
|
31480
|
+
self._auto_resize_single_row(updated_row)
|
|
31513
31481
|
|
|
31514
31482
|
def _refresh_segment_status_by_id(self, segment_id: int):
|
|
31515
31483
|
"""Refresh the status display for a segment by its ID."""
|
|
@@ -31572,40 +31540,79 @@ class SupervertalerQt(QMainWindow):
|
|
|
31572
31540
|
return
|
|
31573
31541
|
if row < 0 or row >= self.table.rowCount():
|
|
31574
31542
|
return
|
|
31575
|
-
|
|
31543
|
+
|
|
31544
|
+
# Minimum column width threshold - if columns are narrower than this,
|
|
31545
|
+
# layout isn't ready yet and we should skip resizing
|
|
31546
|
+
MIN_COL_WIDTH = 100
|
|
31547
|
+
|
|
31576
31548
|
max_height = 1
|
|
31577
|
-
|
|
31549
|
+
|
|
31578
31550
|
# Check source cell (column 2)
|
|
31579
31551
|
source_widget = self.table.cellWidget(row, 2)
|
|
31580
31552
|
if source_widget and isinstance(source_widget, ReadOnlyGridTextEditor):
|
|
31581
31553
|
doc = source_widget.document()
|
|
31582
31554
|
if doc:
|
|
31583
31555
|
col_width = self.table.columnWidth(2) - width_reduction
|
|
31584
|
-
if col_width
|
|
31556
|
+
if col_width >= MIN_COL_WIDTH:
|
|
31585
31557
|
doc.setTextWidth(col_width)
|
|
31586
|
-
|
|
31587
|
-
|
|
31588
|
-
|
|
31589
|
-
|
|
31590
|
-
|
|
31558
|
+
# Use documentLayout().documentSize() to force fresh calculation
|
|
31559
|
+
layout = doc.documentLayout()
|
|
31560
|
+
if layout:
|
|
31561
|
+
size = layout.documentSize()
|
|
31562
|
+
if size.isValid():
|
|
31563
|
+
height = int(size.height())
|
|
31564
|
+
max_height = max(max_height, height)
|
|
31565
|
+
|
|
31591
31566
|
# Check target cell (column 3)
|
|
31592
31567
|
target_widget = self.table.cellWidget(row, 3)
|
|
31593
31568
|
if target_widget and isinstance(target_widget, EditableGridTextEditor):
|
|
31594
31569
|
doc = target_widget.document()
|
|
31595
31570
|
if doc:
|
|
31596
31571
|
col_width = self.table.columnWidth(3) - width_reduction
|
|
31597
|
-
if col_width
|
|
31572
|
+
if col_width >= MIN_COL_WIDTH:
|
|
31598
31573
|
doc.setTextWidth(col_width)
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
|
|
31602
|
-
|
|
31574
|
+
# Use documentLayout().documentSize() to force fresh calculation
|
|
31575
|
+
layout = doc.documentLayout()
|
|
31576
|
+
if layout:
|
|
31577
|
+
size = layout.documentSize()
|
|
31578
|
+
if size.isValid():
|
|
31579
|
+
height = int(size.height())
|
|
31580
|
+
max_height = max(max_height, height)
|
|
31603
31581
|
|
|
31604
31582
|
# Set row height with minimal padding
|
|
31605
31583
|
# Minimum 32px to accommodate status icons (16px) + match text + padding without any cutoff
|
|
31606
31584
|
compact_height = max(max_height + 2, 32)
|
|
31607
31585
|
self.table.setRowHeight(row, compact_height)
|
|
31608
|
-
|
|
31586
|
+
|
|
31587
|
+
def _resize_visible_rows(self):
|
|
31588
|
+
"""Resize only visible (non-hidden) rows for efficiency after pagination/filter changes."""
|
|
31589
|
+
if not hasattr(self, 'table') or not self.table:
|
|
31590
|
+
return
|
|
31591
|
+
row_count = self.table.rowCount()
|
|
31592
|
+
for row in range(row_count):
|
|
31593
|
+
if not self.table.isRowHidden(row):
|
|
31594
|
+
self._auto_resize_single_row(row)
|
|
31595
|
+
|
|
31596
|
+
def _on_column_resized(self, logical_index: int, old_size: int, new_size: int):
|
|
31597
|
+
"""Handle column resize - recalculate row heights for text reflow.
|
|
31598
|
+
|
|
31599
|
+
Only reacts to Source (2) or Target (3) column changes since those
|
|
31600
|
+
contain wrapped text. Uses debouncing to avoid excessive recalculations
|
|
31601
|
+
during continuous dragging.
|
|
31602
|
+
"""
|
|
31603
|
+
# Only care about Source or Target columns (they contain wrapped text)
|
|
31604
|
+
if logical_index not in (2, 3):
|
|
31605
|
+
return
|
|
31606
|
+
|
|
31607
|
+
# Debounce: cancel previous timer and start a new one
|
|
31608
|
+
if hasattr(self, '_column_resize_timer') and self._column_resize_timer is not None:
|
|
31609
|
+
self._column_resize_timer.stop()
|
|
31610
|
+
|
|
31611
|
+
self._column_resize_timer = QTimer()
|
|
31612
|
+
self._column_resize_timer.setSingleShot(True)
|
|
31613
|
+
self._column_resize_timer.timeout.connect(self._resize_visible_rows)
|
|
31614
|
+
self._column_resize_timer.start(150) # 150ms debounce
|
|
31615
|
+
|
|
31609
31616
|
def apply_font_to_grid(self):
|
|
31610
31617
|
"""Apply selected font to all grid cells"""
|
|
31611
31618
|
font = QFont(self.default_font_family, self.default_font_size)
|
|
@@ -34885,12 +34892,20 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
34885
34892
|
self.log("💡 Tip: Go to Resources tab to activate TMs and glossaries for this project")
|
|
34886
34893
|
|
|
34887
34894
|
def search_and_display_tm_matches(self, source_text: str):
|
|
34888
|
-
"""Search TM and Termbases and display matches with visual diff for fuzzy matches
|
|
34895
|
+
"""Search TM and Termbases and display matches with visual diff for fuzzy matches.
|
|
34896
|
+
|
|
34897
|
+
If hide_outer_wrapping_tags is enabled, strips structural tags (like <li-o>, <p>)
|
|
34898
|
+
from the query before searching for better TM matching.
|
|
34899
|
+
"""
|
|
34889
34900
|
self.log(f"🚨 search_and_display_tm_matches called with source_text: '{source_text[:50]}...'")
|
|
34890
34901
|
if not source_text or not source_text.strip():
|
|
34891
34902
|
if hasattr(self, 'tm_display'):
|
|
34892
34903
|
self.tm_display.clear()
|
|
34893
34904
|
return
|
|
34905
|
+
|
|
34906
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
34907
|
+
if self.hide_outer_wrapping_tags:
|
|
34908
|
+
source_text, _ = strip_outer_wrapping_tags(source_text)
|
|
34894
34909
|
|
|
34895
34910
|
# Initialize TM if not already done
|
|
34896
34911
|
if not self.tm_database and self.current_project:
|
|
@@ -34967,14 +34982,18 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
34967
34982
|
# Update grid (using cell widget)
|
|
34968
34983
|
target_widget = self.table.cellWidget(current_row, 3)
|
|
34969
34984
|
if target_widget and isinstance(target_widget, EditableGridTextEditor):
|
|
34970
|
-
|
|
34971
|
-
|
|
34985
|
+
# Strip outer wrapping tags if setting is enabled
|
|
34986
|
+
display_text = segment.target
|
|
34987
|
+
if self.hide_outer_wrapping_tags:
|
|
34988
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
34989
|
+
target_widget.setPlainText(display_text)
|
|
34990
|
+
|
|
34972
34991
|
# Update status column
|
|
34973
34992
|
status_item = self.table.item(current_row, 1)
|
|
34974
34993
|
if status_item:
|
|
34975
34994
|
status_item.setText("Translated")
|
|
34976
34995
|
status_item.setBackground(QColor("#d1fae5")) # Light green for translated
|
|
34977
|
-
|
|
34996
|
+
|
|
34978
34997
|
# Update project modified status (progress is handled by status bar)
|
|
34979
34998
|
self.log(f"✨ Auto-propagated 100% TM match for segment #{current_row + 1}")
|
|
34980
34999
|
|
|
@@ -36032,7 +36051,11 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
36032
36051
|
target_widget = self.table.cellWidget(row, 3)
|
|
36033
36052
|
if target_widget and hasattr(target_widget, 'setPlainText'):
|
|
36034
36053
|
target_widget.blockSignals(True)
|
|
36035
|
-
|
|
36054
|
+
# Strip outer wrapping tags if setting is enabled
|
|
36055
|
+
display_text = segment.target
|
|
36056
|
+
if self.hide_outer_wrapping_tags:
|
|
36057
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
36058
|
+
target_widget.setPlainText(display_text)
|
|
36036
36059
|
target_widget.blockSignals(False)
|
|
36037
36060
|
self.table.viewport().update()
|
|
36038
36061
|
|
|
@@ -36481,7 +36504,11 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
36481
36504
|
cell_widget = self.table.cellWidget(row, col)
|
|
36482
36505
|
if cell_widget and hasattr(cell_widget, 'setPlainText'):
|
|
36483
36506
|
cell_widget.blockSignals(True)
|
|
36484
|
-
|
|
36507
|
+
# Strip outer wrapping tags if setting is enabled
|
|
36508
|
+
display_text = new_text
|
|
36509
|
+
if self.hide_outer_wrapping_tags:
|
|
36510
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
36511
|
+
cell_widget.setPlainText(display_text)
|
|
36485
36512
|
cell_widget.blockSignals(False)
|
|
36486
36513
|
|
|
36487
36514
|
self.project_modified = True
|
|
@@ -37422,10 +37449,10 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
37422
37449
|
# Clear filtering flag to re-enable auto-center
|
|
37423
37450
|
self.filtering_active = False
|
|
37424
37451
|
|
|
37425
|
-
# Re-apply pagination after clearing filters
|
|
37452
|
+
# Re-apply pagination after clearing filters (also resizes visible rows)
|
|
37426
37453
|
if hasattr(self, '_apply_pagination_to_grid'):
|
|
37427
37454
|
self._apply_pagination_to_grid()
|
|
37428
|
-
|
|
37455
|
+
|
|
37429
37456
|
# Restore selection to the previously selected segment
|
|
37430
37457
|
if selected_segment_id is not None:
|
|
37431
37458
|
for row, segment in enumerate(self.current_project.segments):
|
|
@@ -38693,7 +38720,12 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
38693
38720
|
break
|
|
38694
38721
|
|
|
38695
38722
|
def _on_results_panel_notes_changed(self):
|
|
38696
|
-
"""Handle notes change in Translation Results panel - saves to current segment
|
|
38723
|
+
"""Handle notes change in Translation Results panel - saves to current segment.
|
|
38724
|
+
NOTE: This function is deprecated - Translation Results panel has been removed.
|
|
38725
|
+
"""
|
|
38726
|
+
# Translation Results panel is deprecated - this should never be called
|
|
38727
|
+
if not self.translation_results_panel:
|
|
38728
|
+
return
|
|
38697
38729
|
if not self.current_project:
|
|
38698
38730
|
return
|
|
38699
38731
|
|
|
@@ -39571,13 +39603,19 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
39571
39603
|
if target_widget and isinstance(target_widget, QTextEdit):
|
|
39572
39604
|
from PyQt6.QtGui import QTextCursor
|
|
39573
39605
|
|
|
39606
|
+
# Strip outer wrapping tags for display if setting is enabled
|
|
39607
|
+
display_text = new_text
|
|
39608
|
+
if self.hide_outer_wrapping_tags:
|
|
39609
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
39610
|
+
|
|
39574
39611
|
cursor = target_widget.textCursor()
|
|
39575
39612
|
cursor.beginEditBlock()
|
|
39576
39613
|
cursor.select(QTextCursor.SelectionType.Document)
|
|
39577
|
-
cursor.insertText(
|
|
39614
|
+
cursor.insertText(display_text)
|
|
39578
39615
|
cursor.endEditBlock()
|
|
39579
39616
|
|
|
39580
|
-
|
|
39617
|
+
# Store full text (with tags) in segment, but use displayed text if tags are hidden
|
|
39618
|
+
segment.target = new_text # Keep full text with tags
|
|
39581
39619
|
target_widget.setFocus()
|
|
39582
39620
|
target_widget.moveCursor(QTextCursor.MoveOperation.End)
|
|
39583
39621
|
|
|
@@ -39589,7 +39627,11 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
39589
39627
|
# Fallback: no editor widget, update data and (if possible) update cell
|
|
39590
39628
|
segment.target = new_text
|
|
39591
39629
|
if target_widget and hasattr(target_widget, 'setPlainText'):
|
|
39592
|
-
|
|
39630
|
+
# Strip outer wrapping tags if setting is enabled
|
|
39631
|
+
display_text = new_text
|
|
39632
|
+
if self.hide_outer_wrapping_tags:
|
|
39633
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
39634
|
+
target_widget.setPlainText(display_text)
|
|
39593
39635
|
label = f" ({source_label})" if source_label else ""
|
|
39594
39636
|
self.log(f"✓ Replaced target text in segment {segment_id}{label}")
|
|
39595
39637
|
self._play_sound_effect('match_inserted')
|
|
@@ -40552,24 +40594,37 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
40552
40594
|
"""Refresh all visible cells to reflect current tag view mode"""
|
|
40553
40595
|
if not hasattr(self, 'table') or not self.current_project:
|
|
40554
40596
|
return
|
|
40555
|
-
|
|
40597
|
+
|
|
40556
40598
|
# Get current visible rows
|
|
40557
40599
|
for row in range(self.table.rowCount()):
|
|
40558
40600
|
# Get segment for this row
|
|
40559
40601
|
if row >= len(self.current_project.segments):
|
|
40560
40602
|
continue
|
|
40561
|
-
|
|
40603
|
+
|
|
40562
40604
|
segment = self.current_project.segments[row]
|
|
40563
|
-
|
|
40605
|
+
|
|
40606
|
+
# Apply tag stripping if enabled
|
|
40607
|
+
source_for_display = segment.source
|
|
40608
|
+
target_for_display = segment.target
|
|
40609
|
+
|
|
40610
|
+
if self.hide_outer_wrapping_tags:
|
|
40611
|
+
# Strip outer wrapping tags from source
|
|
40612
|
+
stripped_source, _ = strip_outer_wrapping_tags(segment.source)
|
|
40613
|
+
source_for_display = stripped_source
|
|
40614
|
+
|
|
40615
|
+
# Strip outer wrapping tags from target
|
|
40616
|
+
stripped_target, _ = strip_outer_wrapping_tags(segment.target)
|
|
40617
|
+
target_for_display = stripped_target
|
|
40618
|
+
|
|
40564
40619
|
# Update source cell (column 2)
|
|
40565
40620
|
source_widget = self.table.cellWidget(row, 2)
|
|
40566
40621
|
if source_widget and hasattr(source_widget, 'update_display_mode'):
|
|
40567
|
-
source_widget.update_display_mode(
|
|
40568
|
-
|
|
40622
|
+
source_widget.update_display_mode(source_for_display, self.show_tags)
|
|
40623
|
+
|
|
40569
40624
|
# Update target cell (column 3)
|
|
40570
40625
|
target_widget = self.table.cellWidget(row, 3)
|
|
40571
40626
|
if target_widget and hasattr(target_widget, 'update_display_mode'):
|
|
40572
|
-
target_widget.update_display_mode(
|
|
40627
|
+
target_widget.update_display_mode(target_for_display, self.show_tags)
|
|
40573
40628
|
|
|
40574
40629
|
def update_tab_segment_editor(self, segment_id: int, source_text: str, target_text: str,
|
|
40575
40630
|
status: str = "untranslated", notes: str = ""):
|
|
@@ -42091,15 +42146,20 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
42091
42146
|
tm_ids = self.tm_metadata_mgr.get_active_tm_ids(project_id)
|
|
42092
42147
|
|
|
42093
42148
|
try:
|
|
42149
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
42150
|
+
search_source = segment.source
|
|
42151
|
+
if self.hide_outer_wrapping_tags:
|
|
42152
|
+
search_source, _ = strip_outer_wrapping_tags(search_source)
|
|
42153
|
+
|
|
42094
42154
|
if check_tm_exact_only:
|
|
42095
42155
|
# Use exact match only (faster - O(1) hash lookup)
|
|
42096
|
-
exact_match = self.tm_database.get_exact_match(
|
|
42156
|
+
exact_match = self.tm_database.get_exact_match(search_source, tm_ids=tm_ids)
|
|
42097
42157
|
if exact_match:
|
|
42098
42158
|
tm_match = exact_match.get('target_text', '')
|
|
42099
42159
|
self.log(f"✓ Found 100% TM match (exact) for segment #{segment.id}")
|
|
42100
42160
|
else:
|
|
42101
42161
|
# Use fuzzy search (includes 100% matches)
|
|
42102
|
-
matches = self.tm_database.search_all(
|
|
42162
|
+
matches = self.tm_database.search_all(search_source, tm_ids=tm_ids, enabled_only=False, max_matches=1)
|
|
42103
42163
|
if matches and matches[0].get('match_pct', 0) == 100:
|
|
42104
42164
|
tm_match = matches[0].get('target', '')
|
|
42105
42165
|
self.log(f"✓ Found 100% TM match for segment #{segment.id}")
|
|
@@ -43199,10 +43259,15 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43199
43259
|
|
|
43200
43260
|
try:
|
|
43201
43261
|
match = None
|
|
43202
|
-
|
|
43262
|
+
|
|
43263
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
43264
|
+
search_source = segment.source
|
|
43265
|
+
if self.hide_outer_wrapping_tags:
|
|
43266
|
+
search_source, _ = strip_outer_wrapping_tags(search_source)
|
|
43267
|
+
|
|
43203
43268
|
if tm_exact_only:
|
|
43204
43269
|
# Exact match only - use hash lookup (fast)
|
|
43205
|
-
match = self.tm_database.get_exact_match(
|
|
43270
|
+
match = self.tm_database.get_exact_match(search_source, tm_ids=tm_ids)
|
|
43206
43271
|
print(f"DEBUG get_exact_match returned: type={type(match)}, value={match}")
|
|
43207
43272
|
if match and isinstance(match, dict):
|
|
43208
43273
|
segment.target = match.get('target_text', '')
|
|
@@ -43214,7 +43279,7 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43214
43279
|
else:
|
|
43215
43280
|
# Fuzzy matching enabled - get best match ≥75%
|
|
43216
43281
|
matches = self.tm_database.search_all(
|
|
43217
|
-
|
|
43282
|
+
search_source,
|
|
43218
43283
|
tm_ids=tm_ids,
|
|
43219
43284
|
enabled_only=False,
|
|
43220
43285
|
max_matches=1
|
|
@@ -43236,7 +43301,11 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43236
43301
|
if segment.target:
|
|
43237
43302
|
target_widget = self.table.cellWidget(row_index, 3)
|
|
43238
43303
|
if target_widget and isinstance(target_widget, EditableGridTextEditor):
|
|
43239
|
-
|
|
43304
|
+
# Strip outer wrapping tags if setting is enabled
|
|
43305
|
+
display_text = segment.target
|
|
43306
|
+
if self.hide_outer_wrapping_tags:
|
|
43307
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
43308
|
+
target_widget.setPlainText(display_text)
|
|
43240
43309
|
self.update_status_icon(row_index, segment.status)
|
|
43241
43310
|
QApplication.processEvents()
|
|
43242
43311
|
|
|
@@ -43305,11 +43374,15 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43305
43374
|
if success and current <= len(segments_needing_translation):
|
|
43306
43375
|
row_index, segment = segments_needing_translation[current - 1]
|
|
43307
43376
|
if row_index < self.table.rowCount():
|
|
43377
|
+
# Strip outer wrapping tags if setting is enabled
|
|
43378
|
+
display_text = segment.target
|
|
43379
|
+
if self.hide_outer_wrapping_tags:
|
|
43380
|
+
display_text, _ = strip_outer_wrapping_tags(display_text)
|
|
43308
43381
|
target_widget = self.table.cellWidget(row_index, 3)
|
|
43309
43382
|
if target_widget and isinstance(target_widget, EditableGridTextEditor):
|
|
43310
|
-
target_widget.setPlainText(
|
|
43383
|
+
target_widget.setPlainText(display_text)
|
|
43311
43384
|
else:
|
|
43312
|
-
self.table.setItem(row_index, 3, QTableWidgetItem(
|
|
43385
|
+
self.table.setItem(row_index, 3, QTableWidgetItem(display_text))
|
|
43313
43386
|
self.update_status_icon(row_index, segment.status)
|
|
43314
43387
|
|
|
43315
43388
|
def handle_translation_complete(final_success_count, final_error_count):
|
|
@@ -43477,9 +43550,14 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43477
43550
|
# Check each segment against TM
|
|
43478
43551
|
for row_index, segment in segments_to_translate:
|
|
43479
43552
|
try:
|
|
43553
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
43554
|
+
search_source = segment.source
|
|
43555
|
+
if self.hide_outer_wrapping_tags:
|
|
43556
|
+
search_source, _ = strip_outer_wrapping_tags(search_source)
|
|
43557
|
+
|
|
43480
43558
|
if check_tm_exact_only:
|
|
43481
43559
|
# Use exact match only (faster - O(1) hash lookup)
|
|
43482
|
-
exact_match = self.tm_database.get_exact_match(
|
|
43560
|
+
exact_match = self.tm_database.get_exact_match(search_source, tm_ids=tm_ids)
|
|
43483
43561
|
if exact_match:
|
|
43484
43562
|
# Found 100% exact match - auto-insert it
|
|
43485
43563
|
tm_match = exact_match.get('target_text', '')
|
|
@@ -43503,7 +43581,7 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43503
43581
|
segments_needing_translation.append((row_index, segment))
|
|
43504
43582
|
else:
|
|
43505
43583
|
# Use fuzzy search (includes 100% matches)
|
|
43506
|
-
matches = self.tm_database.search_all(
|
|
43584
|
+
matches = self.tm_database.search_all(search_source, tm_ids=tm_ids, enabled_only=False, max_matches=1)
|
|
43507
43585
|
if matches and matches[0].get('match_pct', 0) == 100:
|
|
43508
43586
|
# Found 100% match - auto-insert it
|
|
43509
43587
|
tm_match = matches[0].get('target', '')
|
|
@@ -43582,8 +43660,13 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43582
43660
|
# Translate each segment from TM
|
|
43583
43661
|
for row_index, segment in segments_needing_translation:
|
|
43584
43662
|
try:
|
|
43663
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
43664
|
+
search_source = segment.source
|
|
43665
|
+
if self.hide_outer_wrapping_tags:
|
|
43666
|
+
search_source, _ = strip_outer_wrapping_tags(search_source)
|
|
43667
|
+
|
|
43585
43668
|
# Search TM for best match
|
|
43586
|
-
matches = self.tm_database.search_all(
|
|
43669
|
+
matches = self.tm_database.search_all(search_source, tm_ids=tm_ids, enabled_only=False, max_matches=1)
|
|
43587
43670
|
|
|
43588
43671
|
if matches and len(matches) > 0:
|
|
43589
43672
|
match = matches[0]
|
|
@@ -45389,8 +45472,12 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
45389
45472
|
if tm_ids is not None and isinstance(tm_ids, list) and len(tm_ids) == 0:
|
|
45390
45473
|
all_tm_matches = []
|
|
45391
45474
|
else:
|
|
45475
|
+
# Strip outer structural tags if setting is enabled (for cleaner TM matching)
|
|
45476
|
+
search_source = segment.source
|
|
45477
|
+
if self.hide_outer_wrapping_tags:
|
|
45478
|
+
search_source, _ = strip_outer_wrapping_tags(search_source)
|
|
45392
45479
|
# Search using TMDatabase (includes bidirectional + base language matching)
|
|
45393
|
-
all_tm_matches = self.tm_database.search_all(
|
|
45480
|
+
all_tm_matches = self.tm_database.search_all(search_source, tm_ids=tm_ids, enabled_only=False, max_matches=10)
|
|
45394
45481
|
|
|
45395
45482
|
# Single consolidated log message for TM search results
|
|
45396
45483
|
if all_tm_matches:
|
|
@@ -45576,9 +45663,19 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
45576
45663
|
self.log(f"⚠ Error auto-inserting TM match: {e}")
|
|
45577
45664
|
|
|
45578
45665
|
def _add_mt_and_llm_matches_progressive(self, segment, source_lang, target_lang, source_lang_code, target_lang_code):
|
|
45579
|
-
"""
|
|
45666
|
+
"""DEPRECATED: Translation Results panel has been removed.
|
|
45667
|
+
|
|
45668
|
+
MT/LLM translations are now only available via QuickTrans (Ctrl+M).
|
|
45669
|
+
This function is kept as a stub for backwards compatibility.
|
|
45670
|
+
"""
|
|
45671
|
+
pass # Translation Results panel removed - use QuickTrans (Ctrl+M) for MT/LLM
|
|
45672
|
+
|
|
45673
|
+
def _REMOVED_add_mt_and_llm_matches_progressive(self, segment, source_lang, target_lang, source_lang_code, target_lang_code):
|
|
45674
|
+
"""REMOVED - kept for reference only, will be deleted in future cleanup."""
|
|
45675
|
+
return # Dead code below - keeping temporarily for reference
|
|
45676
|
+
|
|
45580
45677
|
from modules.translation_results_panel import TranslationMatch
|
|
45581
|
-
|
|
45678
|
+
|
|
45582
45679
|
# MT matches (usually fast ~0.5s each)
|
|
45583
45680
|
if self.enable_mt_matching:
|
|
45584
45681
|
api_keys = self.load_api_keys()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: supervertaler
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.203
|
|
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=
|
|
1
|
+
Supervertaler.py,sha256=Mgq5ZnZJxaQ-_aSzVZoVQiVYz_kEaxeVq1nfq-6TkNE,2396397
|
|
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.
|
|
84
|
-
supervertaler-1.9.
|
|
85
|
-
supervertaler-1.9.
|
|
86
|
-
supervertaler-1.9.
|
|
87
|
-
supervertaler-1.9.
|
|
88
|
-
supervertaler-1.9.
|
|
83
|
+
supervertaler-1.9.203.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
|
|
84
|
+
supervertaler-1.9.203.dist-info/METADATA,sha256=Az4lNNikNxVVE-gTtXSSNrTM1F_aG4x1HhqjRtnbq0E,5725
|
|
85
|
+
supervertaler-1.9.203.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
86
|
+
supervertaler-1.9.203.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
|
|
87
|
+
supervertaler-1.9.203.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
|
|
88
|
+
supervertaler-1.9.203.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|