supervertaler 1.9.177b0__py3-none-any.whl → 1.9.178b0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Supervertaler.py CHANGED
@@ -10622,7 +10622,11 @@ class SupervertalerQt(QMainWindow):
10622
10622
  button_layout = QHBoxLayout()
10623
10623
 
10624
10624
  create_btn = QPushButton("+ Create New TM")
10625
- create_btn.clicked.connect(lambda: self._show_create_tm_dialog(tm_metadata_mgr, refresh_tm_list, project_id))
10625
+ # Get project_id dynamically - use 0 (global) when no project is loaded
10626
+ create_btn.clicked.connect(lambda: self._show_create_tm_dialog(
10627
+ tm_metadata_mgr, refresh_tm_list,
10628
+ self.current_project.id if (hasattr(self, 'current_project') and self.current_project and hasattr(self.current_project, 'id')) else 0
10629
+ ))
10626
10630
  button_layout.addWidget(create_btn)
10627
10631
 
10628
10632
  import_btn = QPushButton("📥 Import TMX")
@@ -14974,8 +14978,8 @@ class SupervertalerQt(QMainWindow):
14974
14978
  )
14975
14979
 
14976
14980
  if result:
14977
- # Auto-activate for current project
14978
- if project_id:
14981
+ # Auto-activate for current project (or global=0 if no project loaded)
14982
+ if project_id is not None:
14979
14983
  tm_metadata_mgr.activate_tm(result, project_id)
14980
14984
 
14981
14985
  QMessageBox.information(self, "Success", f"Translation Memory '{name}' created successfully!")
@@ -26694,24 +26698,32 @@ class SupervertalerQt(QMainWindow):
26694
26698
  )
26695
26699
  return
26696
26700
 
26697
- # Extract segments
26698
- mqxliff_segments = handler.extract_source_segments()
26699
-
26701
+ # Extract segments (including targets for pretranslated files)
26702
+ mqxliff_segments = handler.extract_bilingual_segments()
26703
+
26700
26704
  if not mqxliff_segments:
26701
26705
  QMessageBox.warning(
26702
26706
  self, "No Segments",
26703
26707
  "No segments found in the memoQ XLIFF file."
26704
26708
  )
26705
26709
  return
26706
-
26710
+
26711
+ # Count pretranslated segments
26712
+ pretranslated_count = sum(1 for s in mqxliff_segments if s.get('target', '').strip())
26713
+
26707
26714
  # Convert to internal Segment format
26708
26715
  segments = []
26709
26716
  for i, mq_seg in enumerate(mqxliff_segments):
26717
+ # Map status from mqxliff
26718
+ status = mq_seg.get('status', 'not_started')
26719
+ if status not in ['not_started', 'pre_translated', 'translated', 'confirmed', 'locked']:
26720
+ status = 'not_started'
26721
+
26710
26722
  segment = Segment(
26711
26723
  id=i + 1,
26712
- source=mq_seg.plain_text,
26713
- target="",
26714
- status=DEFAULT_STATUS.key,
26724
+ source=mq_seg.get('source', ''),
26725
+ target=mq_seg.get('target', ''),
26726
+ status=status,
26715
26727
  notes="",
26716
26728
  )
26717
26729
  segments.append(segment)
@@ -26755,11 +26767,17 @@ class SupervertalerQt(QMainWindow):
26755
26767
  # Log success
26756
26768
  self.log(f"✓ Imported {len(segments)} segments from memoQ XLIFF: {Path(file_path).name}")
26757
26769
  self.log(f" Source: {source_lang}, Target: {target_lang}")
26758
-
26770
+ if pretranslated_count > 0:
26771
+ self.log(f" Pretranslated: {pretranslated_count} segments with target text")
26772
+
26773
+ # Build message with pretranslation info
26774
+ msg = f"Successfully imported {len(segments)} segment(s) from memoQ XLIFF.\n\nLanguages: {source_lang} → {target_lang}"
26775
+ if pretranslated_count > 0:
26776
+ msg += f"\n\nPretranslated: {pretranslated_count} segment(s) with target text loaded."
26777
+
26759
26778
  QMessageBox.information(
26760
26779
  self, "Import Successful",
26761
- f"Successfully imported {len(segments)} segment(s) from memoQ XLIFF.\n\n"
26762
- f"Languages: {source_lang} → {target_lang}"
26780
+ msg
26763
26781
  )
26764
26782
  except Exception as e:
26765
26783
  self.log(f"❌ Error importing memoQ XLIFF: {e}")
@@ -27168,7 +27186,19 @@ class SupervertalerQt(QMainWindow):
27168
27186
  source_row = QHBoxLayout()
27169
27187
  source_row.addWidget(QLabel("Source Language:"))
27170
27188
  source_combo = QComboBox()
27171
- source_combo.addItems(["English", "Dutch", "German", "French", "Spanish", "Italian", "Portuguese", "Polish", "Chinese", "Japanese", "Korean", "Russian"])
27189
+ # Full language list (same as New Project dialog)
27190
+ available_languages = [
27191
+ "Afrikaans", "Albanian", "Arabic", "Armenian", "Basque", "Bengali",
27192
+ "Bulgarian", "Catalan", "Chinese (Simplified)", "Chinese (Traditional)",
27193
+ "Croatian", "Czech", "Danish", "Dutch", "English", "Estonian",
27194
+ "Finnish", "French", "Galician", "Georgian", "German", "Greek",
27195
+ "Hebrew", "Hindi", "Hungarian", "Icelandic", "Indonesian", "Irish",
27196
+ "Italian", "Japanese", "Korean", "Latvian", "Lithuanian", "Macedonian",
27197
+ "Malay", "Norwegian", "Persian", "Polish", "Portuguese", "Romanian",
27198
+ "Russian", "Serbian", "Slovak", "Slovenian", "Spanish", "Swahili",
27199
+ "Swedish", "Thai", "Turkish", "Ukrainian", "Urdu", "Vietnamese", "Welsh"
27200
+ ]
27201
+ source_combo.addItems(available_languages)
27172
27202
  # Try to match current UI selection
27173
27203
  current_source = self.source_lang_combo.currentText() if hasattr(self, 'source_lang_combo') else "English"
27174
27204
  source_idx = source_combo.findText(current_source)
@@ -27176,12 +27206,12 @@ class SupervertalerQt(QMainWindow):
27176
27206
  source_combo.setCurrentIndex(source_idx)
27177
27207
  source_row.addWidget(source_combo)
27178
27208
  lang_group_layout.addLayout(source_row)
27179
-
27209
+
27180
27210
  # Target language
27181
27211
  target_row = QHBoxLayout()
27182
27212
  target_row.addWidget(QLabel("Target Language:"))
27183
27213
  target_combo = QComboBox()
27184
- target_combo.addItems(["English", "Dutch", "German", "French", "Spanish", "Italian", "Portuguese", "Polish", "Chinese", "Japanese", "Korean", "Russian"])
27214
+ target_combo.addItems(available_languages)
27185
27215
  # Try to match current UI selection
27186
27216
  current_target = self.target_lang_combo.currentText() if hasattr(self, 'target_lang_combo') else "Dutch"
27187
27217
  target_idx = target_combo.findText(current_target)
@@ -27189,15 +27219,15 @@ class SupervertalerQt(QMainWindow):
27189
27219
  target_combo.setCurrentIndex(target_idx)
27190
27220
  target_row.addWidget(target_combo)
27191
27221
  lang_group_layout.addLayout(target_row)
27192
-
27222
+
27193
27223
  lang_layout.addWidget(lang_group)
27194
-
27224
+
27195
27225
  # Buttons
27196
27226
  lang_buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
27197
27227
  lang_buttons.accepted.connect(lang_dialog.accept)
27198
27228
  lang_buttons.rejected.connect(lang_dialog.reject)
27199
27229
  lang_layout.addWidget(lang_buttons)
27200
-
27230
+
27201
27231
  if lang_dialog.exec() != QDialog.DialogCode.Accepted:
27202
27232
  self.log("✗ User cancelled Trados import during language selection")
27203
27233
  return
@@ -27848,7 +27878,19 @@ class SupervertalerQt(QMainWindow):
27848
27878
  source_row = QHBoxLayout()
27849
27879
  source_row.addWidget(QLabel("Source Language:"))
27850
27880
  source_combo = QComboBox()
27851
- source_combo.addItems(["English", "Dutch", "German", "French", "Spanish", "Italian", "Portuguese", "Polish", "Chinese", "Japanese", "Korean", "Russian"])
27881
+ # Full language list (same as New Project dialog)
27882
+ available_languages = [
27883
+ "Afrikaans", "Albanian", "Arabic", "Armenian", "Basque", "Bengali",
27884
+ "Bulgarian", "Catalan", "Chinese (Simplified)", "Chinese (Traditional)",
27885
+ "Croatian", "Czech", "Danish", "Dutch", "English", "Estonian",
27886
+ "Finnish", "French", "Galician", "Georgian", "German", "Greek",
27887
+ "Hebrew", "Hindi", "Hungarian", "Icelandic", "Indonesian", "Irish",
27888
+ "Italian", "Japanese", "Korean", "Latvian", "Lithuanian", "Macedonian",
27889
+ "Malay", "Norwegian", "Persian", "Polish", "Portuguese", "Romanian",
27890
+ "Russian", "Serbian", "Slovak", "Slovenian", "Spanish", "Swahili",
27891
+ "Swedish", "Thai", "Turkish", "Ukrainian", "Urdu", "Vietnamese", "Welsh"
27892
+ ]
27893
+ source_combo.addItems(available_languages)
27852
27894
  # Try to match current UI selection
27853
27895
  current_source = self.source_lang_combo.currentText() if hasattr(self, 'source_lang_combo') else "English"
27854
27896
  source_idx = source_combo.findText(current_source)
@@ -27861,7 +27903,7 @@ class SupervertalerQt(QMainWindow):
27861
27903
  target_row = QHBoxLayout()
27862
27904
  target_row.addWidget(QLabel("Target Language:"))
27863
27905
  target_combo = QComboBox()
27864
- target_combo.addItems(["English", "Dutch", "German", "French", "Spanish", "Italian", "Portuguese", "Polish", "Chinese", "Japanese", "Korean", "Russian"])
27906
+ target_combo.addItems(available_languages)
27865
27907
  # Try to match current UI selection
27866
27908
  current_target = self.target_lang_combo.currentText() if hasattr(self, 'target_lang_combo') else "Dutch"
27867
27909
  target_idx = target_combo.findText(current_target)
@@ -159,9 +159,78 @@ class MQXLIFFHandler:
159
159
 
160
160
  segment = FormattedSegment(trans_unit_id, plain_text, formatted_xml)
161
161
  segments.append(segment)
162
-
162
+
163
163
  return segments
164
-
164
+
165
+ def extract_bilingual_segments(self) -> List[Dict]:
166
+ """
167
+ Extract all source AND target segments from the MQXLIFF file.
168
+ Used for importing pretranslated mqxliff files.
169
+
170
+ Returns:
171
+ List of dicts with 'id', 'source', 'target', 'status' keys
172
+ """
173
+ segments = []
174
+
175
+ if self.body_element is None:
176
+ return segments
177
+
178
+ # Find all trans-unit elements (with or without namespace)
179
+ trans_units = self.body_element.findall('.//xliff:trans-unit', self.NAMESPACES)
180
+ if not trans_units:
181
+ trans_units = self.body_element.findall('.//trans-unit')
182
+
183
+ for trans_unit in trans_units:
184
+ trans_unit_id = trans_unit.get('id', 'unknown')
185
+
186
+ # Skip auxiliary segments (like hyperlink URLs with mq:nosplitjoin="true")
187
+ nosplitjoin = trans_unit.get('{MQXliff}nosplitjoin', 'false')
188
+ if nosplitjoin == 'true':
189
+ continue
190
+
191
+ # Find source element
192
+ source_elem = trans_unit.find('xliff:source', self.NAMESPACES)
193
+ if source_elem is None:
194
+ source_elem = trans_unit.find('source')
195
+
196
+ # Find target element
197
+ target_elem = trans_unit.find('xliff:target', self.NAMESPACES)
198
+ if target_elem is None:
199
+ target_elem = trans_unit.find('target')
200
+
201
+ source_text = ""
202
+ target_text = ""
203
+
204
+ if source_elem is not None:
205
+ source_text = self._extract_plain_text(source_elem)
206
+
207
+ if target_elem is not None:
208
+ target_text = self._extract_plain_text(target_elem)
209
+
210
+ # Get memoQ status if available
211
+ mq_status = trans_unit.get('{MQXliff}status', '')
212
+
213
+ # Map memoQ status to internal status
214
+ # memoQ statuses: "NotStarted", "Editing", "Confirmed", "Reviewed", "Rejected", etc.
215
+ status = 'not_started'
216
+ if mq_status in ['Confirmed', 'ProofRead', 'Reviewed']:
217
+ status = 'confirmed'
218
+ elif mq_status == 'Editing':
219
+ status = 'translated'
220
+ elif target_text.strip():
221
+ # Has target but unknown status - mark as pre-translated
222
+ status = 'pre_translated'
223
+
224
+ segments.append({
225
+ 'id': trans_unit_id,
226
+ 'source': source_text,
227
+ 'target': target_text,
228
+ 'status': status,
229
+ 'mq_status': mq_status
230
+ })
231
+
232
+ return segments
233
+
165
234
  def _extract_plain_text(self, element: ET.Element) -> str:
166
235
  """
167
236
  Recursively extract plain text from an XML element, stripping all tags.
@@ -344,19 +344,22 @@ class TMMetadataManager:
344
344
  """Check if a TM is active for a project (or global when project_id=0)"""
345
345
  if project_id is None:
346
346
  return False # If None (not 0), default to inactive
347
-
347
+
348
348
  try:
349
349
  cursor = self.db_manager.cursor
350
-
350
+
351
+ # Check if TM is active for this project OR globally (project_id=0)
351
352
  cursor.execute("""
352
- SELECT is_active FROM tm_activation
353
- WHERE tm_id = ? AND project_id = ?
353
+ SELECT is_active FROM tm_activation
354
+ WHERE tm_id = ? AND (project_id = ? OR project_id = 0)
355
+ ORDER BY project_id DESC
354
356
  """, (tm_db_id, project_id))
355
-
356
- row = cursor.fetchone()
357
- if row:
358
- return bool(row[0])
359
-
357
+
358
+ # Return True if any activation is active (project-specific takes priority due to ORDER BY)
359
+ for row in cursor.fetchall():
360
+ if bool(row[0]):
361
+ return True
362
+
360
363
  # If no activation record exists, TM is inactive by default
361
364
  return False
362
365
  except Exception as e:
@@ -382,15 +385,16 @@ class TMMetadataManager:
382
385
 
383
386
  try:
384
387
  cursor = self.db_manager.cursor
385
-
388
+
386
389
  # Only return TMs that have been explicitly activated (is_active = 1)
390
+ # Include both project-specific activations AND global activations (project_id=0)
387
391
  cursor.execute("""
388
- SELECT tm.tm_id
392
+ SELECT DISTINCT tm.tm_id
389
393
  FROM translation_memories tm
390
394
  INNER JOIN tm_activation ta ON tm.id = ta.tm_id
391
- WHERE ta.project_id = ? AND ta.is_active = 1
395
+ WHERE (ta.project_id = ? OR ta.project_id = 0) AND ta.is_active = 1
392
396
  """, (project_id,))
393
-
397
+
394
398
  return [row[0] for row in cursor.fetchall()]
395
399
  except Exception as e:
396
400
  self.log(f"✗ Error fetching active tm_ids: {e}")
@@ -422,16 +426,17 @@ class TMMetadataManager:
422
426
 
423
427
  try:
424
428
  cursor = self.db_manager.cursor
425
-
429
+
426
430
  # Return TMs where Write checkbox is enabled (read_only = 0)
427
- # AND the TM has an activation record for this project
431
+ # AND the TM has an activation record for this project OR for global (project_id=0)
432
+ # This ensures TMs created when no project was loaded still work
428
433
  cursor.execute("""
429
- SELECT tm.tm_id
434
+ SELECT DISTINCT tm.tm_id
430
435
  FROM translation_memories tm
431
436
  INNER JOIN tm_activation ta ON tm.id = ta.tm_id
432
- WHERE ta.project_id = ? AND tm.read_only = 0
437
+ WHERE (ta.project_id = ? OR ta.project_id = 0) AND tm.read_only = 0
433
438
  """, (project_id,))
434
-
439
+
435
440
  return [row[0] for row in cursor.fetchall()]
436
441
  except Exception as e:
437
442
  self.log(f"✗ Error fetching writable tm_ids: {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervertaler
3
- Version: 1.9.177b0
3
+ Version: 1.9.178b0
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=4cGlqNtBnj95ts-qLw6W-aNSJqjqkeFDTCbBAWkx5cI,2322643
1
+ Supervertaler.py,sha256=y-lO1pVkbcQW2BjOGPA0CME_DfuPkAUKzGcsIk-OPcM,2324936
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
@@ -28,7 +28,7 @@ modules/llm_superbench_ui.py,sha256=lmzsL8lt0KzFw-z8De1zb49Emnv7f1dZv_DJmoQz0bQ,
28
28
  modules/local_llm_setup.py,sha256=33y-D_LKzkn2w8ejyjeKaovf_An6xQ98mKISoqe-Qjc,42661
29
29
  modules/model_update_dialog.py,sha256=kEg0FuO1N-uj6QY5ZIj-FqdiLQuPuAY48pbuwT0HUGI,13113
30
30
  modules/model_version_checker.py,sha256=41g7gcWvyrKPYeobaOGCMZLwAHgQmFwVF8zokodKae8,12741
31
- modules/mqxliff_handler.py,sha256=-tUFubKsxkx6nW7aMDhuDaTBcVTPtVHV1HyzF0TbkVo,26205
31
+ modules/mqxliff_handler.py,sha256=TVtrf7ieGoxfoLxy4v4S7by9YImKypw1EY0wFpZO3Lo,28792
32
32
  modules/non_translatables_manager.py,sha256=izorabiX6rSQzuBIvnY67wmu5vd85SbzexXccbmwPs4,27465
33
33
  modules/pdf_rescue_Qt.py,sha256=9W_M0Zms4miapQbrqm-viHNCpaW39GL9VaKKFCJxpnE,80479
34
34
  modules/pdf_rescue_tkinter.py,sha256=a4R_OUnn7X5O_XMR1roybrdu1aXoGCwwO-mwYB2ZpOg,39606
@@ -63,7 +63,7 @@ modules/termview_widget.py,sha256=O3ah7g-4Lb_iUctxl9sMyxh8V3A5I5PFxmy9iIH2Kgk,53
63
63
  modules/theme_manager.py,sha256=EOI_5pM2bXAadw08bbl92TLN-w28lbw4Zi1E8vQ-kM0,16694
64
64
  modules/tm_editor_dialog.py,sha256=AzGwq4QW641uFJdF8DljLTRRp4FLoYX3Pe4rlTjQWNg,3517
65
65
  modules/tm_manager_qt.py,sha256=h2bvXkRuboHf_RRz9-5FX35GVRlpXgRDWeXyj1QWtPs,54406
66
- modules/tm_metadata_manager.py,sha256=_drzJ80q-mr1y9La8t0Cb8MXh91n3I-lEGDwSmoVI_4,23161
66
+ modules/tm_metadata_manager.py,sha256=NTsaI_YjQnVOpU_scAwK9uR1Tcl9pzKD1GwLVy7sx2g,23590
67
67
  modules/tmx_editor.py,sha256=n0CtdZI8f1fPRWmCqz5Ysxbnp556Qj-6Y56a-YIz6pY,59239
68
68
  modules/tmx_editor_qt.py,sha256=PxBIUw_06PHYTBHsd8hZzVJXW8T0A0ljfz1Wjjsa4yU,117022
69
69
  modules/tmx_generator.py,sha256=pNkxwdMLvSRMMru0lkB1gvViIpg9BQy1EVhRbwoef3k,9426
@@ -77,9 +77,9 @@ modules/unified_prompt_manager_qt.py,sha256=U89UFGG-M7BLetoaLAlma0x-n8SIyx682DhS
77
77
  modules/voice_commands.py,sha256=iBb-gjWxRMLhFH7-InSRjYJz1EIDBNA2Pog8V7TtJaY,38516
78
78
  modules/voice_dictation.py,sha256=QmitXfkG-vRt5hIQATjphHdhXfqmwhzcQcbXB6aRzIg,16386
79
79
  modules/voice_dictation_lite.py,sha256=jorY0BmWE-8VczbtGrWwt1zbnOctMoSlWOsQrcufBcc,9423
80
- supervertaler-1.9.177b0.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
- supervertaler-1.9.177b0.dist-info/METADATA,sha256=KeUutsoVb7QmekOgaHFTiDxTdrgrJLs0yLcThPa0lAQ,5727
82
- supervertaler-1.9.177b0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
- supervertaler-1.9.177b0.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
- supervertaler-1.9.177b0.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
- supervertaler-1.9.177b0.dist-info/RECORD,,
80
+ supervertaler-1.9.178b0.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
+ supervertaler-1.9.178b0.dist-info/METADATA,sha256=dc88Ptk60UHxgiJ7EsKQfQut93tobFVz3F18LKC0WjQ,5727
82
+ supervertaler-1.9.178b0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
+ supervertaler-1.9.178b0.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
+ supervertaler-1.9.178b0.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
+ supervertaler-1.9.178b0.dist-info/RECORD,,