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.

Files changed (85) hide show
  1. Supervertaler.py +47886 -0
  2. modules/__init__.py +10 -0
  3. modules/ai_actions.py +964 -0
  4. modules/ai_attachment_manager.py +343 -0
  5. modules/ai_file_viewer_dialog.py +210 -0
  6. modules/autofingers_engine.py +466 -0
  7. modules/cafetran_docx_handler.py +379 -0
  8. modules/config_manager.py +469 -0
  9. modules/database_manager.py +1878 -0
  10. modules/database_migrations.py +417 -0
  11. modules/dejavurtf_handler.py +779 -0
  12. modules/document_analyzer.py +427 -0
  13. modules/docx_handler.py +689 -0
  14. modules/encoding_repair.py +319 -0
  15. modules/encoding_repair_Qt.py +393 -0
  16. modules/encoding_repair_ui.py +481 -0
  17. modules/feature_manager.py +350 -0
  18. modules/figure_context_manager.py +340 -0
  19. modules/file_dialog_helper.py +148 -0
  20. modules/find_replace.py +164 -0
  21. modules/find_replace_qt.py +457 -0
  22. modules/glossary_manager.py +433 -0
  23. modules/image_extractor.py +188 -0
  24. modules/keyboard_shortcuts_widget.py +571 -0
  25. modules/llm_clients.py +1211 -0
  26. modules/llm_leaderboard.py +737 -0
  27. modules/llm_superbench_ui.py +1401 -0
  28. modules/local_llm_setup.py +1104 -0
  29. modules/model_update_dialog.py +381 -0
  30. modules/model_version_checker.py +373 -0
  31. modules/mqxliff_handler.py +638 -0
  32. modules/non_translatables_manager.py +743 -0
  33. modules/pdf_rescue_Qt.py +1822 -0
  34. modules/pdf_rescue_tkinter.py +909 -0
  35. modules/phrase_docx_handler.py +516 -0
  36. modules/project_home_panel.py +209 -0
  37. modules/prompt_assistant.py +357 -0
  38. modules/prompt_library.py +689 -0
  39. modules/prompt_library_migration.py +447 -0
  40. modules/quick_access_sidebar.py +282 -0
  41. modules/ribbon_widget.py +597 -0
  42. modules/sdlppx_handler.py +874 -0
  43. modules/setup_wizard.py +353 -0
  44. modules/shortcut_manager.py +932 -0
  45. modules/simple_segmenter.py +128 -0
  46. modules/spellcheck_manager.py +727 -0
  47. modules/statuses.py +207 -0
  48. modules/style_guide_manager.py +315 -0
  49. modules/superbench_ui.py +1319 -0
  50. modules/superbrowser.py +329 -0
  51. modules/supercleaner.py +600 -0
  52. modules/supercleaner_ui.py +444 -0
  53. modules/superdocs.py +19 -0
  54. modules/superdocs_viewer_qt.py +382 -0
  55. modules/superlookup.py +252 -0
  56. modules/tag_cleaner.py +260 -0
  57. modules/tag_manager.py +333 -0
  58. modules/term_extractor.py +270 -0
  59. modules/termbase_entry_editor.py +842 -0
  60. modules/termbase_import_export.py +488 -0
  61. modules/termbase_manager.py +1060 -0
  62. modules/termview_widget.py +1172 -0
  63. modules/theme_manager.py +499 -0
  64. modules/tm_editor_dialog.py +99 -0
  65. modules/tm_manager_qt.py +1280 -0
  66. modules/tm_metadata_manager.py +545 -0
  67. modules/tmx_editor.py +1461 -0
  68. modules/tmx_editor_qt.py +2784 -0
  69. modules/tmx_generator.py +284 -0
  70. modules/tracked_changes.py +900 -0
  71. modules/trados_docx_handler.py +430 -0
  72. modules/translation_memory.py +715 -0
  73. modules/translation_results_panel.py +2134 -0
  74. modules/translation_services.py +282 -0
  75. modules/unified_prompt_library.py +659 -0
  76. modules/unified_prompt_manager_qt.py +3951 -0
  77. modules/voice_commands.py +920 -0
  78. modules/voice_dictation.py +477 -0
  79. modules/voice_dictation_lite.py +249 -0
  80. supervertaler-1.9.153.dist-info/METADATA +896 -0
  81. supervertaler-1.9.153.dist-info/RECORD +85 -0
  82. supervertaler-1.9.153.dist-info/WHEEL +5 -0
  83. supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
  84. supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
  85. 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
@@ -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")