supervertaler 1.9.153__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 +47886 -0
- modules/__init__.py +10 -0
- modules/ai_actions.py +964 -0
- modules/ai_attachment_manager.py +343 -0
- modules/ai_file_viewer_dialog.py +210 -0
- modules/autofingers_engine.py +466 -0
- modules/cafetran_docx_handler.py +379 -0
- modules/config_manager.py +469 -0
- modules/database_manager.py +1878 -0
- modules/database_migrations.py +417 -0
- modules/dejavurtf_handler.py +779 -0
- modules/document_analyzer.py +427 -0
- modules/docx_handler.py +689 -0
- modules/encoding_repair.py +319 -0
- modules/encoding_repair_Qt.py +393 -0
- modules/encoding_repair_ui.py +481 -0
- modules/feature_manager.py +350 -0
- modules/figure_context_manager.py +340 -0
- modules/file_dialog_helper.py +148 -0
- modules/find_replace.py +164 -0
- modules/find_replace_qt.py +457 -0
- modules/glossary_manager.py +433 -0
- modules/image_extractor.py +188 -0
- modules/keyboard_shortcuts_widget.py +571 -0
- modules/llm_clients.py +1211 -0
- modules/llm_leaderboard.py +737 -0
- modules/llm_superbench_ui.py +1401 -0
- modules/local_llm_setup.py +1104 -0
- modules/model_update_dialog.py +381 -0
- modules/model_version_checker.py +373 -0
- modules/mqxliff_handler.py +638 -0
- modules/non_translatables_manager.py +743 -0
- modules/pdf_rescue_Qt.py +1822 -0
- modules/pdf_rescue_tkinter.py +909 -0
- modules/phrase_docx_handler.py +516 -0
- modules/project_home_panel.py +209 -0
- modules/prompt_assistant.py +357 -0
- modules/prompt_library.py +689 -0
- modules/prompt_library_migration.py +447 -0
- modules/quick_access_sidebar.py +282 -0
- modules/ribbon_widget.py +597 -0
- modules/sdlppx_handler.py +874 -0
- modules/setup_wizard.py +353 -0
- modules/shortcut_manager.py +932 -0
- modules/simple_segmenter.py +128 -0
- modules/spellcheck_manager.py +727 -0
- modules/statuses.py +207 -0
- modules/style_guide_manager.py +315 -0
- modules/superbench_ui.py +1319 -0
- modules/superbrowser.py +329 -0
- modules/supercleaner.py +600 -0
- modules/supercleaner_ui.py +444 -0
- modules/superdocs.py +19 -0
- modules/superdocs_viewer_qt.py +382 -0
- modules/superlookup.py +252 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +333 -0
- modules/term_extractor.py +270 -0
- modules/termbase_entry_editor.py +842 -0
- modules/termbase_import_export.py +488 -0
- modules/termbase_manager.py +1060 -0
- modules/termview_widget.py +1172 -0
- modules/theme_manager.py +499 -0
- modules/tm_editor_dialog.py +99 -0
- modules/tm_manager_qt.py +1280 -0
- modules/tm_metadata_manager.py +545 -0
- modules/tmx_editor.py +1461 -0
- modules/tmx_editor_qt.py +2784 -0
- modules/tmx_generator.py +284 -0
- modules/tracked_changes.py +900 -0
- modules/trados_docx_handler.py +430 -0
- modules/translation_memory.py +715 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +659 -0
- modules/unified_prompt_manager_qt.py +3951 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.153.dist-info/METADATA +896 -0
- supervertaler-1.9.153.dist-info/RECORD +85 -0
- supervertaler-1.9.153.dist-info/WHEEL +5 -0
- supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.153.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File Dialog Helper for Supervertaler
|
|
3
|
+
Wraps PyQt6 QFileDialog to remember last used directory across all dialogs.
|
|
4
|
+
|
|
5
|
+
Author: Michael Beijer
|
|
6
|
+
License: MIT
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from PyQt6.QtWidgets import QFileDialog, QWidget
|
|
10
|
+
from typing import Optional, Tuple, List
|
|
11
|
+
from modules.config_manager import get_config_manager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_open_file_name(
|
|
15
|
+
parent: Optional[QWidget] = None,
|
|
16
|
+
caption: str = "",
|
|
17
|
+
filter: str = "",
|
|
18
|
+
initial_filter: str = ""
|
|
19
|
+
) -> Tuple[str, str]:
|
|
20
|
+
"""
|
|
21
|
+
Show an open file dialog that remembers the last directory.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
parent: Parent widget
|
|
25
|
+
caption: Dialog title
|
|
26
|
+
filter: File type filters (e.g., "Text Files (*.txt);;All Files (*)")
|
|
27
|
+
initial_filter: Initially selected filter
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Tuple of (selected_file_path, selected_filter)
|
|
31
|
+
"""
|
|
32
|
+
config = get_config_manager()
|
|
33
|
+
start_dir = config.get_last_directory()
|
|
34
|
+
|
|
35
|
+
file_path, selected_filter = QFileDialog.getOpenFileName(
|
|
36
|
+
parent,
|
|
37
|
+
caption,
|
|
38
|
+
start_dir,
|
|
39
|
+
filter,
|
|
40
|
+
initial_filter
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if file_path:
|
|
44
|
+
config.update_last_directory_from_file(file_path)
|
|
45
|
+
|
|
46
|
+
return file_path, selected_filter
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_open_file_names(
|
|
50
|
+
parent: Optional[QWidget] = None,
|
|
51
|
+
caption: str = "",
|
|
52
|
+
filter: str = "",
|
|
53
|
+
initial_filter: str = ""
|
|
54
|
+
) -> Tuple[List[str], str]:
|
|
55
|
+
"""
|
|
56
|
+
Show an open multiple files dialog that remembers the last directory.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
parent: Parent widget
|
|
60
|
+
caption: Dialog title
|
|
61
|
+
filter: File type filters
|
|
62
|
+
initial_filter: Initially selected filter
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Tuple of (list_of_selected_files, selected_filter)
|
|
66
|
+
"""
|
|
67
|
+
config = get_config_manager()
|
|
68
|
+
start_dir = config.get_last_directory()
|
|
69
|
+
|
|
70
|
+
file_paths, selected_filter = QFileDialog.getOpenFileNames(
|
|
71
|
+
parent,
|
|
72
|
+
caption,
|
|
73
|
+
start_dir,
|
|
74
|
+
filter,
|
|
75
|
+
initial_filter
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if file_paths:
|
|
79
|
+
config.update_last_directory_from_file(file_paths[0])
|
|
80
|
+
|
|
81
|
+
return file_paths, selected_filter
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_save_file_name(
|
|
85
|
+
parent: Optional[QWidget] = None,
|
|
86
|
+
caption: str = "",
|
|
87
|
+
filter: str = "",
|
|
88
|
+
initial_filter: str = ""
|
|
89
|
+
) -> Tuple[str, str]:
|
|
90
|
+
"""
|
|
91
|
+
Show a save file dialog that remembers the last directory.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
parent: Parent widget
|
|
95
|
+
caption: Dialog title
|
|
96
|
+
filter: File type filters
|
|
97
|
+
initial_filter: Initially selected filter
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Tuple of (selected_file_path, selected_filter)
|
|
101
|
+
"""
|
|
102
|
+
config = get_config_manager()
|
|
103
|
+
start_dir = config.get_last_directory()
|
|
104
|
+
|
|
105
|
+
file_path, selected_filter = QFileDialog.getSaveFileName(
|
|
106
|
+
parent,
|
|
107
|
+
caption,
|
|
108
|
+
start_dir,
|
|
109
|
+
filter,
|
|
110
|
+
initial_filter
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if file_path:
|
|
114
|
+
config.update_last_directory_from_file(file_path)
|
|
115
|
+
|
|
116
|
+
return file_path, selected_filter
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_existing_directory(
|
|
120
|
+
parent: Optional[QWidget] = None,
|
|
121
|
+
caption: str = "",
|
|
122
|
+
options: QFileDialog.Option = QFileDialog.Option.ShowDirsOnly
|
|
123
|
+
) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Show a directory selection dialog that remembers the last directory.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
parent: Parent widget
|
|
129
|
+
caption: Dialog title
|
|
130
|
+
options: Dialog options
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Selected directory path
|
|
134
|
+
"""
|
|
135
|
+
config = get_config_manager()
|
|
136
|
+
start_dir = config.get_last_directory()
|
|
137
|
+
|
|
138
|
+
directory = QFileDialog.getExistingDirectory(
|
|
139
|
+
parent,
|
|
140
|
+
caption,
|
|
141
|
+
start_dir,
|
|
142
|
+
options
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if directory:
|
|
146
|
+
config.set_last_directory(directory)
|
|
147
|
+
|
|
148
|
+
return directory
|
modules/find_replace.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Find and Replace Dialog Module
|
|
3
|
+
|
|
4
|
+
This module provides find and replace functionality for the CAT editor.
|
|
5
|
+
|
|
6
|
+
Classes:
|
|
7
|
+
- FindReplaceDialog: Dialog window for searching and replacing text in segments
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import tkinter as tk
|
|
11
|
+
from tkinter import ttk
|
|
12
|
+
import re
|
|
13
|
+
from typing import List, Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FindReplaceDialog:
|
|
17
|
+
"""Find and replace dialog for searching and replacing text in segments"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, parent, app):
|
|
20
|
+
self.app = app
|
|
21
|
+
self.window = tk.Toplevel(parent)
|
|
22
|
+
self.window.title("Find and Replace")
|
|
23
|
+
self.window.geometry("500x250")
|
|
24
|
+
self.window.transient(parent)
|
|
25
|
+
|
|
26
|
+
# Find
|
|
27
|
+
tk.Label(self.window, text="Find:", font=('Segoe UI', 10, 'bold')).pack(anchor='w', padx=10, pady=(10,2))
|
|
28
|
+
self.find_var = tk.StringVar()
|
|
29
|
+
tk.Entry(self.window, textvariable=self.find_var, width=60).pack(padx=10, pady=2)
|
|
30
|
+
|
|
31
|
+
# Replace
|
|
32
|
+
tk.Label(self.window, text="Replace with:", font=('Segoe UI', 10, 'bold')).pack(anchor='w', padx=10, pady=(10,2))
|
|
33
|
+
self.replace_var = tk.StringVar()
|
|
34
|
+
tk.Entry(self.window, textvariable=self.replace_var, width=60).pack(padx=10, pady=2)
|
|
35
|
+
|
|
36
|
+
# Options
|
|
37
|
+
options_frame = tk.Frame(self.window)
|
|
38
|
+
options_frame.pack(pady=10)
|
|
39
|
+
|
|
40
|
+
self.match_case_var = tk.BooleanVar()
|
|
41
|
+
tk.Checkbutton(options_frame, text="Match case", variable=self.match_case_var).pack(side='left', padx=5)
|
|
42
|
+
|
|
43
|
+
self.search_in_var = tk.StringVar(value="target")
|
|
44
|
+
tk.Label(options_frame, text="Search in:").pack(side='left', padx=(20,5))
|
|
45
|
+
ttk.Combobox(options_frame, textvariable=self.search_in_var,
|
|
46
|
+
values=["source", "target", "both"], state='readonly', width=10).pack(side='left')
|
|
47
|
+
|
|
48
|
+
# Buttons
|
|
49
|
+
button_frame = tk.Frame(self.window)
|
|
50
|
+
button_frame.pack(pady=10)
|
|
51
|
+
|
|
52
|
+
tk.Button(button_frame, text="Find Next", command=self.find_next, width=12).pack(side='left', padx=5)
|
|
53
|
+
tk.Button(button_frame, text="Replace", command=self.replace_current, width=12).pack(side='left', padx=5)
|
|
54
|
+
tk.Button(button_frame, text="Replace All", command=self.replace_all, width=12,
|
|
55
|
+
bg='#FF9800', fg='white').pack(side='left', padx=5)
|
|
56
|
+
|
|
57
|
+
# Results
|
|
58
|
+
self.result_label = tk.Label(self.window, text="", fg='blue')
|
|
59
|
+
self.result_label.pack(pady=5)
|
|
60
|
+
|
|
61
|
+
self.current_match_index = -1
|
|
62
|
+
self.matches = []
|
|
63
|
+
|
|
64
|
+
def find_next(self):
|
|
65
|
+
"""Find next occurrence"""
|
|
66
|
+
query = self.find_var.get()
|
|
67
|
+
if not query:
|
|
68
|
+
self.result_label.config(text="Please enter search text")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
search_in = self.search_in_var.get()
|
|
72
|
+
case_sensitive = self.match_case_var.get()
|
|
73
|
+
|
|
74
|
+
# Search segments
|
|
75
|
+
self.matches = []
|
|
76
|
+
for seg in self.app.segments:
|
|
77
|
+
source = seg.source if case_sensitive else seg.source.lower()
|
|
78
|
+
target = seg.target if case_sensitive else seg.target.lower()
|
|
79
|
+
query_cmp = query if case_sensitive else query.lower()
|
|
80
|
+
|
|
81
|
+
found = False
|
|
82
|
+
if search_in in ['source', 'both'] and query_cmp in source:
|
|
83
|
+
found = True
|
|
84
|
+
if search_in in ['target', 'both'] and query_cmp in target:
|
|
85
|
+
found = True
|
|
86
|
+
|
|
87
|
+
if found:
|
|
88
|
+
self.matches.append(seg.id)
|
|
89
|
+
|
|
90
|
+
if not self.matches:
|
|
91
|
+
self.result_label.config(text="No matches found")
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
# Move to next match
|
|
95
|
+
self.current_match_index = (self.current_match_index + 1) % len(self.matches)
|
|
96
|
+
match_id = self.matches[self.current_match_index]
|
|
97
|
+
|
|
98
|
+
# Select in grid
|
|
99
|
+
for item in self.app.tree.get_children():
|
|
100
|
+
values = self.app.tree.item(item, 'values')
|
|
101
|
+
if int(values[0]) == match_id:
|
|
102
|
+
self.app.tree.selection_set(item)
|
|
103
|
+
self.app.tree.see(item)
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
self.result_label.config(
|
|
107
|
+
text=f"Match {self.current_match_index + 1} of {len(self.matches)}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def replace_current(self):
|
|
111
|
+
"""Replace current match"""
|
|
112
|
+
if not self.app.current_segment:
|
|
113
|
+
self.result_label.config(text="No segment selected")
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
find_text = self.find_var.get()
|
|
117
|
+
replace_text = self.replace_var.get()
|
|
118
|
+
|
|
119
|
+
if not find_text:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
# Replace in target
|
|
123
|
+
if self.match_case_var.get():
|
|
124
|
+
new_target = self.app.current_segment.target.replace(find_text, replace_text)
|
|
125
|
+
else:
|
|
126
|
+
new_target = re.sub(re.escape(find_text), replace_text,
|
|
127
|
+
self.app.current_segment.target, flags=re.IGNORECASE)
|
|
128
|
+
|
|
129
|
+
# Update
|
|
130
|
+
self.app.target_text.delete('1.0', 'end')
|
|
131
|
+
self.app.target_text.insert('1.0', new_target)
|
|
132
|
+
self.app.save_current_segment()
|
|
133
|
+
|
|
134
|
+
self.result_label.config(text="Replaced")
|
|
135
|
+
|
|
136
|
+
def replace_all(self):
|
|
137
|
+
"""Replace all occurrences"""
|
|
138
|
+
find_text = self.find_var.get()
|
|
139
|
+
replace_text = self.replace_var.get()
|
|
140
|
+
|
|
141
|
+
if not find_text:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
search_in = self.search_in_var.get()
|
|
145
|
+
count = 0
|
|
146
|
+
|
|
147
|
+
for seg in self.app.segments:
|
|
148
|
+
if search_in in ['target', 'both']:
|
|
149
|
+
if self.match_case_var.get():
|
|
150
|
+
if find_text in seg.target:
|
|
151
|
+
seg.target = seg.target.replace(find_text, replace_text)
|
|
152
|
+
count += 1
|
|
153
|
+
else:
|
|
154
|
+
if re.search(re.escape(find_text), seg.target, re.IGNORECASE):
|
|
155
|
+
seg.target = re.sub(re.escape(find_text), replace_text,
|
|
156
|
+
seg.target, flags=re.IGNORECASE)
|
|
157
|
+
count += 1
|
|
158
|
+
|
|
159
|
+
# Refresh grid
|
|
160
|
+
self.app.load_segments_to_grid()
|
|
161
|
+
self.app.modified = True
|
|
162
|
+
self.app.update_progress()
|
|
163
|
+
|
|
164
|
+
self.result_label.config(text=f"Replaced {count} occurrences")
|