supervertaler 1.9.179__py3-none-any.whl → 1.9.181__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Supervertaler.py CHANGED
@@ -32,9 +32,9 @@ License: MIT
32
32
  """
33
33
 
34
34
  # Version Information.
35
- __version__ = "1.9.179"
35
+ __version__ = "1.9.181"
36
36
  __phase__ = "0.9"
37
- __release_date__ = "2026-01-28"
37
+ __release_date__ = "2026-01-30"
38
38
  __edition__ = "Qt"
39
39
 
40
40
  import sys
@@ -6357,6 +6357,9 @@ class SupervertalerQt(QMainWindow):
6357
6357
 
6358
6358
  # Initialize theme manager and apply theme
6359
6359
  self.theme_manager = ThemeManager(self.user_data_path)
6360
+ # Apply saved global UI font scale
6361
+ saved_font_scale = self._get_global_ui_font_scale()
6362
+ self.theme_manager.font_scale = saved_font_scale
6360
6363
  self.theme_manager.apply_theme(QApplication.instance())
6361
6364
 
6362
6365
  # Update widgets that were created before theme_manager existed
@@ -6596,6 +6599,9 @@ class SupervertalerQt(QMainWindow):
6596
6599
  # Update theme manager
6597
6600
  from modules.theme_manager import ThemeManager
6598
6601
  self.theme_manager = ThemeManager(self.user_data_path)
6602
+ # Apply saved global UI font scale
6603
+ saved_font_scale = self._get_global_ui_font_scale()
6604
+ self.theme_manager.font_scale = saved_font_scale
6599
6605
  self.theme_manager.apply_theme(QApplication.instance())
6600
6606
 
6601
6607
  # Update recent projects file path
@@ -12196,27 +12202,90 @@ class SupervertalerQt(QMainWindow):
12196
12202
  f"Error testing segmentation:\n\n{e}"
12197
12203
  )
12198
12204
 
12199
- def _update_both_termviews(self, source_text, termbase_list, nt_matches):
12205
+ def _update_both_termviews(self, source_text, termbase_list, nt_matches, status_hint=None):
12200
12206
  """Update all three Termview instances with the same data.
12201
-
12207
+
12202
12208
  Termview locations:
12203
12209
  1. Under grid (collapsible via View menu)
12204
12210
  2. Match Panel tab (top section)
12211
+
12212
+ Args:
12213
+ source_text: The source text for the current segment
12214
+ termbase_list: List of termbase match dictionaries
12215
+ nt_matches: List of NT (Never Translate) matches
12216
+ status_hint: Optional hint for display when no matches:
12217
+ 'no_termbases_activated' - no glossaries activated for project
12218
+ 'wrong_language' - activated glossaries don't match project language
12205
12219
  """
12206
12220
  # Update left Termview (under grid)
12207
12221
  if hasattr(self, 'termview_widget') and self.termview_widget:
12208
12222
  try:
12209
- self.termview_widget.update_with_matches(source_text, termbase_list, nt_matches)
12223
+ self.termview_widget.update_with_matches(source_text, termbase_list, nt_matches, status_hint)
12210
12224
  except Exception as e:
12211
12225
  self.log(f"Error updating left termview: {e}")
12212
-
12226
+
12213
12227
  # Update Match Panel Termview
12214
12228
  if hasattr(self, 'termview_widget_match') and self.termview_widget_match:
12215
12229
  try:
12216
- self.termview_widget_match.update_with_matches(source_text, termbase_list, nt_matches)
12230
+ self.termview_widget_match.update_with_matches(source_text, termbase_list, nt_matches, status_hint)
12217
12231
  except Exception as e:
12218
12232
  self.log(f"Error updating Match Panel termview: {e}")
12219
-
12233
+
12234
+ def _get_termbase_status_hint(self) -> str:
12235
+ """Check termbase activation status and return appropriate hint.
12236
+
12237
+ Returns:
12238
+ 'no_termbases_activated' - if no glossaries are activated for this project
12239
+ 'wrong_language' - if activated glossaries don't match project language pair
12240
+ None - if everything is correctly configured
12241
+ """
12242
+ if not self.current_project:
12243
+ return None
12244
+
12245
+ project_id = self.current_project.id if hasattr(self.current_project, 'id') else None
12246
+ if not project_id:
12247
+ return None
12248
+
12249
+ # Check if termbase manager is available
12250
+ if not hasattr(self, 'termbase_mgr') or not self.termbase_mgr:
12251
+ return None
12252
+
12253
+ try:
12254
+ # Get active termbase IDs for this project
12255
+ active_tb_ids = self.termbase_mgr.get_active_termbase_ids(project_id)
12256
+
12257
+ # Check if no termbases are activated
12258
+ if not active_tb_ids or len(active_tb_ids) == 0:
12259
+ return 'no_termbases_activated'
12260
+
12261
+ # Check if any activated termbases match the project's language pair
12262
+ project_source = (self.current_project.source_lang or '').lower()
12263
+ project_target = (self.current_project.target_lang or '').lower()
12264
+
12265
+ # Get all termbases and check language pairs
12266
+ all_termbases = self.termbase_mgr.list_termbases()
12267
+ has_matching_language = False
12268
+
12269
+ for tb in all_termbases:
12270
+ if tb['id'] in active_tb_ids:
12271
+ tb_source = (tb.get('source_lang') or '').lower()
12272
+ tb_target = (tb.get('target_lang') or '').lower()
12273
+ # Match if: no language set, or languages match (bidirectional)
12274
+ if (not tb_source and not tb_target) or \
12275
+ (tb_source == project_source and tb_target == project_target) or \
12276
+ (tb_source == project_target and tb_target == project_source):
12277
+ has_matching_language = True
12278
+ break
12279
+
12280
+ if not has_matching_language:
12281
+ return 'wrong_language'
12282
+
12283
+ return None # All good
12284
+
12285
+ except Exception as e:
12286
+ self.log(f"Error checking termbase status: {e}")
12287
+ return None
12288
+
12220
12289
  def _refresh_termbase_display_for_current_segment(self):
12221
12290
  """Refresh only termbase/glossary display for the current segment.
12222
12291
 
@@ -12276,9 +12345,12 @@ class SupervertalerQt(QMainWindow):
12276
12345
 
12277
12346
  # Get NT matches
12278
12347
  nt_matches = self.find_nt_matches_in_source(segment.source)
12279
-
12348
+
12349
+ # Get status hint for termbase activation
12350
+ status_hint = self._get_termbase_status_hint()
12351
+
12280
12352
  # Update both Termview widgets (left and right)
12281
- self._update_both_termviews(segment.source, termbase_list, nt_matches)
12353
+ self._update_both_termviews(segment.source, termbase_list, nt_matches, status_hint)
12282
12354
  except Exception as e:
12283
12355
  self.log(f"Error updating termview: {e}")
12284
12356
 
@@ -12770,9 +12842,12 @@ class SupervertalerQt(QMainWindow):
12770
12842
 
12771
12843
  # Get NT matches
12772
12844
  nt_matches = self.find_nt_matches_in_source(segment.source)
12773
-
12845
+
12846
+ # Get status hint (although after adding a term, it should be fine)
12847
+ status_hint = self._get_termbase_status_hint()
12848
+
12774
12849
  # Update both Termview widgets (left and right)
12775
- self._update_both_termviews(segment.source, termbase_list, nt_matches)
12850
+ self._update_both_termviews(segment.source, termbase_list, nt_matches, status_hint)
12776
12851
  self.log(f"✅ Both TermView widgets updated instantly with new term")
12777
12852
 
12778
12853
  # Update source cell highlighting with updated cache
@@ -15551,11 +15626,11 @@ class SupervertalerQt(QMainWindow):
15551
15626
  layout.addWidget(settings_tabs)
15552
15627
 
15553
15628
  # Apply saved UI font scale on startup
15554
- saved_scale = self._get_settings_ui_font_scale()
15629
+ saved_scale = self._get_global_ui_font_scale()
15555
15630
  if saved_scale != 100:
15556
15631
  # Defer application to ensure widgets are fully created
15557
15632
  from PyQt6.QtCore import QTimer
15558
- QTimer.singleShot(100, lambda: self._apply_settings_ui_font_scale(saved_scale))
15633
+ QTimer.singleShot(100, lambda: self._apply_global_ui_font_scale(saved_scale))
15559
15634
 
15560
15635
  return tab
15561
15636
 
@@ -17717,26 +17792,27 @@ class SupervertalerQt(QMainWindow):
17717
17792
  termview_group.setLayout(termview_layout)
17718
17793
  layout.addWidget(termview_group)
17719
17794
 
17720
- # ===== UI Font Scale (for Settings panels) =====
17721
- ui_scale_group = QGroupBox("🖥️ Settings Panel Font Size")
17795
+ # ===== Global UI Font Scale =====
17796
+ ui_scale_group = QGroupBox("🖥️ Global UI Font Scale")
17722
17797
  ui_scale_layout = QVBoxLayout()
17723
-
17798
+
17724
17799
  ui_scale_info = QLabel(
17725
- "Adjust the font size for all Settings panel text. Useful for high-DPI/4K displays.\n"
17800
+ "Adjust the font size for the entire application UI. Useful for Linux/macOS users where\n"
17801
+ "Qt applications may render with smaller fonts, or for high-DPI displays.\n"
17726
17802
  "Changes apply immediately. Default is 100%."
17727
17803
  )
17728
17804
  ui_scale_info.setWordWrap(True)
17729
17805
  ui_scale_layout.addWidget(ui_scale_info)
17730
-
17806
+
17731
17807
  ui_scale_row = QHBoxLayout()
17732
17808
  ui_scale_row.addWidget(QLabel("UI Font Scale:"))
17733
17809
  ui_scale_spin = QSpinBox()
17734
- ui_scale_spin.setMinimum(80)
17810
+ ui_scale_spin.setMinimum(50)
17735
17811
  ui_scale_spin.setMaximum(200)
17736
- ui_scale_spin.setValue(font_settings.get('settings_ui_font_scale', 100))
17812
+ ui_scale_spin.setValue(font_settings.get('global_ui_font_scale', font_settings.get('settings_ui_font_scale', 100)))
17737
17813
  ui_scale_spin.setSuffix("%")
17738
17814
  ui_scale_spin.setSingleStep(10)
17739
- ui_scale_spin.setToolTip("Scale Settings panel text (80%-200%)")
17815
+ ui_scale_spin.setToolTip("Scale entire application UI text (50%-200%)")
17740
17816
  ui_scale_spin.setMinimumHeight(28)
17741
17817
  ui_scale_spin.setMinimumWidth(90)
17742
17818
  ui_scale_spin.setStyleSheet("""
@@ -17753,11 +17829,11 @@ class SupervertalerQt(QMainWindow):
17753
17829
  }
17754
17830
  """)
17755
17831
  ui_scale_row.addWidget(ui_scale_spin)
17756
-
17832
+
17757
17833
  # Apply button for immediate feedback
17758
17834
  apply_scale_btn = QPushButton("Apply")
17759
17835
  apply_scale_btn.setToolTip("Apply font scale immediately")
17760
- apply_scale_btn.clicked.connect(lambda: self._apply_settings_ui_font_scale(ui_scale_spin.value()))
17836
+ apply_scale_btn.clicked.connect(lambda: self._apply_global_ui_font_scale(ui_scale_spin.value()))
17761
17837
  ui_scale_row.addWidget(apply_scale_btn)
17762
17838
 
17763
17839
  ui_scale_row.addStretch()
@@ -17797,7 +17873,7 @@ class SupervertalerQt(QMainWindow):
17797
17873
  def save_view_settings_with_scale():
17798
17874
  # Save the UI scale setting first
17799
17875
  if hasattr(self, '_ui_scale_spin'):
17800
- self._apply_settings_ui_font_scale(self._ui_scale_spin.value())
17876
+ self._apply_global_ui_font_scale(self._ui_scale_spin.value())
17801
17877
  # Then save other view settings
17802
17878
  self._save_view_settings_from_ui(
17803
17879
  grid_font_spin, match_font_spin, compare_font_spin, show_tags_check, tag_color_btn,
@@ -19925,72 +20001,51 @@ class SupervertalerQt(QMainWindow):
19925
20001
  msg.setStandardButtons(QMessageBox.StandardButton.Ok)
19926
20002
  msg.exec()
19927
20003
 
19928
- def _apply_settings_ui_font_scale(self, scale_percent: int):
19929
- """Apply font scale to all Settings panels for better readability on high-DPI displays"""
19930
- # Save the setting
20004
+ def _apply_global_ui_font_scale(self, scale_percent: int):
20005
+ """Apply font scale to the entire application UI"""
19931
20006
  general_settings = self.load_general_settings()
19932
- general_settings['settings_ui_font_scale'] = scale_percent
20007
+ general_settings['global_ui_font_scale'] = scale_percent
20008
+ # Remove old key if present (migration)
20009
+ if 'settings_ui_font_scale' in general_settings:
20010
+ del general_settings['settings_ui_font_scale']
19933
20011
  self.save_general_settings(general_settings)
19934
-
19935
- # Calculate base font size (default system font is typically 9-10pt)
19936
- base_size = 10 # Base font size in points
19937
- scaled_size = int(base_size * scale_percent / 100)
19938
-
19939
- # Create stylesheet for Settings panels
19940
- settings_stylesheet = f"""
19941
- QGroupBox {{
19942
- font-size: {scaled_size + 1}pt;
19943
- font-weight: bold;
19944
- }}
19945
- QGroupBox QLabel {{
19946
- font-size: {scaled_size}pt;
19947
- }}
19948
- QGroupBox QCheckBox {{
19949
- font-size: {scaled_size}pt;
19950
- }}
19951
- QGroupBox QRadioButton {{
19952
- font-size: {scaled_size}pt;
19953
- }}
19954
- QGroupBox QComboBox {{
19955
- font-size: {scaled_size}pt;
19956
- }}
19957
- QGroupBox QSpinBox {{
19958
- font-size: {scaled_size}pt;
19959
- }}
19960
- QGroupBox QLineEdit {{
19961
- font-size: {scaled_size}pt;
19962
- }}
19963
- QGroupBox QPushButton {{
19964
- font-size: {scaled_size}pt;
19965
- }}
19966
- QGroupBox QTextEdit {{
19967
- font-size: {scaled_size}pt;
19968
- }}
19969
- QGroupBox QPlainTextEdit {{
19970
- font-size: {scaled_size}pt;
19971
- }}
19972
- """
19973
-
19974
- # Apply to settings_tabs if it exists
19975
- if hasattr(self, 'settings_tabs') and self.settings_tabs is not None:
19976
- self.settings_tabs.setStyleSheet(
19977
- "QTabBar::tab { outline: 0; font-size: " + str(scaled_size) + "pt; } "
19978
- "QTabBar::tab:focus { outline: none; } "
19979
- "QTabBar::tab:selected { border-bottom: 1px solid #2196F3; background-color: rgba(33, 150, 243, 0.08); }"
19980
- )
19981
-
19982
- # Apply to each tab's content
19983
- for i in range(self.settings_tabs.count()):
19984
- widget = self.settings_tabs.widget(i)
19985
- if widget:
19986
- widget.setStyleSheet(settings_stylesheet)
19987
-
19988
- self.log(f"✓ Settings UI font scale set to {scale_percent}% (base: {scaled_size}pt)")
19989
-
19990
- def _get_settings_ui_font_scale(self) -> int:
19991
- """Get the current Settings UI font scale percentage"""
20012
+
20013
+ # Update ThemeManager and reapply theme
20014
+ if hasattr(self, 'theme_manager') and self.theme_manager is not None:
20015
+ self.theme_manager.font_scale = scale_percent
20016
+ self.theme_manager.apply_theme(QApplication.instance())
20017
+
20018
+ # Update status bar and main tabs fonts
20019
+ self._update_status_bar_fonts(scale_percent)
20020
+ self._update_main_tabs_fonts(scale_percent)
20021
+ self.log(f"✓ Global UI font scale set to {scale_percent}%")
20022
+
20023
+ def _update_status_bar_fonts(self, scale_percent: int):
20024
+ """Update status bar label fonts based on scale percentage"""
20025
+ base_size = int(9 * scale_percent / 100)
20026
+ small_size = max(7, base_size)
20027
+ style = f"font-size: {small_size}pt;"
20028
+
20029
+ # Update all status bar labels if they exist
20030
+ for attr_name in ['segment_count_label', 'file_label', 'tm_status_label',
20031
+ 'termbase_status_label', 'source_lang_label', 'target_lang_label']:
20032
+ if hasattr(self, attr_name):
20033
+ label = getattr(self, attr_name)
20034
+ if label is not None:
20035
+ label.setStyleSheet(style)
20036
+
20037
+ def _update_main_tabs_fonts(self, scale_percent: int):
20038
+ """Update main tab bar fonts based on scale percentage"""
20039
+ base_size = int(10 * scale_percent / 100)
20040
+ if hasattr(self, 'main_tabs') and self.main_tabs is not None:
20041
+ self.main_tabs.tabBar().setStyleSheet(f"font-size: {base_size}pt;")
20042
+
20043
+ def _get_global_ui_font_scale(self) -> int:
20044
+ """Get the current global UI font scale percentage"""
19992
20045
  general_settings = self.load_general_settings()
19993
- return general_settings.get('settings_ui_font_scale', 100)
20046
+ # Check new key first, fall back to old key for migration
20047
+ return general_settings.get('global_ui_font_scale',
20048
+ general_settings.get('settings_ui_font_scale', 100))
19994
20049
 
19995
20050
  def create_grid_view_widget(self):
19996
20051
  """Create the Grid View widget (existing grid functionality)"""
@@ -21572,36 +21627,35 @@ class SupervertalerQt(QMainWindow):
21572
21627
 
21573
21628
  # Source language
21574
21629
  source_lang_combo = QComboBox()
21575
- common_langs = [
21576
- ("English", "en"),
21577
- ("Dutch", "nl"),
21578
- ("German", "de"),
21579
- ("French", "fr"),
21580
- ("Spanish", "es"),
21581
- ("Italian", "it"),
21582
- ("Portuguese", "pt"),
21583
- ("Russian", "ru"),
21584
- ("Chinese", "zh"),
21585
- ("Japanese", "ja"),
21630
+ # Full language list matching Settings → Language Pair (with ISO 639-1 codes)
21631
+ available_langs = [
21632
+ ("Afrikaans", "af"), ("Albanian", "sq"), ("Arabic", "ar"), ("Armenian", "hy"),
21633
+ ("Basque", "eu"), ("Bengali", "bn"), ("Bulgarian", "bg"), ("Catalan", "ca"),
21634
+ ("Chinese (Simplified)", "zh-CN"), ("Chinese (Traditional)", "zh-TW"),
21635
+ ("Croatian", "hr"), ("Czech", "cs"), ("Danish", "da"), ("Dutch", "nl"),
21636
+ ("English", "en"), ("Estonian", "et"), ("Finnish", "fi"), ("French", "fr"),
21637
+ ("Galician", "gl"), ("Georgian", "ka"), ("German", "de"), ("Greek", "el"),
21638
+ ("Hebrew", "he"), ("Hindi", "hi"), ("Hungarian", "hu"), ("Icelandic", "is"),
21639
+ ("Indonesian", "id"), ("Irish", "ga"), ("Italian", "it"), ("Japanese", "ja"),
21640
+ ("Korean", "ko"), ("Latvian", "lv"), ("Lithuanian", "lt"), ("Macedonian", "mk"),
21641
+ ("Malay", "ms"), ("Norwegian", "no"), ("Persian", "fa"), ("Polish", "pl"),
21642
+ ("Portuguese", "pt"), ("Romanian", "ro"), ("Russian", "ru"), ("Serbian", "sr"),
21643
+ ("Slovak", "sk"), ("Slovenian", "sl"), ("Spanish", "es"), ("Swahili", "sw"),
21644
+ ("Swedish", "sv"), ("Thai", "th"), ("Turkish", "tr"), ("Ukrainian", "uk"),
21645
+ ("Urdu", "ur"), ("Vietnamese", "vi"), ("Welsh", "cy"),
21586
21646
  ]
21587
- for lang_name, lang_code in common_langs:
21647
+ for lang_name, lang_code in available_langs:
21588
21648
  source_lang_combo.addItem(lang_name, lang_code)
21589
21649
  settings_layout.addRow("Source Language:", source_lang_combo)
21590
-
21650
+
21591
21651
  # Target language
21592
21652
  target_lang_combo = QComboBox()
21593
- for lang_name, lang_code in common_langs:
21653
+ for lang_name, lang_code in available_langs:
21594
21654
  target_lang_combo.addItem(lang_name, lang_code)
21595
-
21596
- # Set defaults based on global language settings (if in common_langs)
21597
- try:
21598
- for lang_name, lang_code in common_langs:
21599
- if lang_name == self.source_language:
21600
- source_lang_combo.setCurrentText(lang_name)
21601
- if lang_name == self.target_language:
21602
- target_lang_combo.setCurrentText(lang_name)
21603
- except:
21604
- target_lang_combo.setCurrentIndex(1) # Fallback to Dutch
21655
+
21656
+ # Set defaults based on global language settings
21657
+ source_lang_combo.setCurrentText(self.source_language)
21658
+ target_lang_combo.setCurrentText(self.target_language)
21605
21659
 
21606
21660
  settings_layout.addRow("Target Language:", target_lang_combo)
21607
21661
 
@@ -21723,7 +21777,11 @@ class SupervertalerQt(QMainWindow):
21723
21777
  target_lang=target_lang,
21724
21778
  segments=[]
21725
21779
  )
21726
-
21780
+
21781
+ # Sync global language settings with new project languages
21782
+ self.source_language = source_lang
21783
+ self.target_language = target_lang
21784
+
21727
21785
  # Process source text if provided
21728
21786
  source_text = text_input.toPlainText().strip()
21729
21787
  if source_text:
@@ -21833,7 +21891,13 @@ class SupervertalerQt(QMainWindow):
21833
21891
  self.current_project = Project.from_dict(data)
21834
21892
  self.project_file_path = file_path
21835
21893
  self.project_modified = False
21836
-
21894
+
21895
+ # Sync global language settings with project languages
21896
+ if self.current_project.source_lang:
21897
+ self.source_language = self.current_project.source_lang
21898
+ if self.current_project.target_lang:
21899
+ self.target_language = self.current_project.target_lang
21900
+
21837
21901
  # Restore prompt settings if they exist (unified library)
21838
21902
  if hasattr(self.current_project, 'prompt_settings') and self.current_project.prompt_settings:
21839
21903
  prompt_settings = self.current_project.prompt_settings
@@ -23748,9 +23812,9 @@ class SupervertalerQt(QMainWindow):
23748
23812
  # Initialize TM for this project
23749
23813
  self.initialize_tm_database()
23750
23814
 
23751
- # Deactivate all resources for new project (user explicitly activates what they need)
23815
+ # Deactivate all resources for new project, then auto-activate language-matching ones
23752
23816
  self._deactivate_all_resources_for_new_project()
23753
-
23817
+
23754
23818
  # Auto-resize rows for better initial display
23755
23819
  self.auto_resize_rows()
23756
23820
 
@@ -26224,7 +26288,11 @@ class SupervertalerQt(QMainWindow):
26224
26288
 
26225
26289
  # Store memoQ source path in project for persistence across saves
26226
26290
  self.current_project.memoq_source_path = file_path
26227
-
26291
+
26292
+ # Sync global language settings with imported project languages
26293
+ self.source_language = source_lang
26294
+ self.target_language = target_lang
26295
+
26228
26296
  # Create segments with simple sequential IDs
26229
26297
  for idx, source_text in enumerate(source_segments):
26230
26298
  existing_target = target_segments[idx] if idx < len(target_segments) else ""
@@ -26248,15 +26316,15 @@ class SupervertalerQt(QMainWindow):
26248
26316
  self.load_segments_to_grid()
26249
26317
  self.initialize_tm_database()
26250
26318
 
26251
- # Deactivate all resources for new project (user explicitly activates what they need)
26319
+ # Deactivate all resources for new project, then auto-activate language-matching ones
26252
26320
  self._deactivate_all_resources_for_new_project()
26253
-
26321
+
26254
26322
  # Auto-resize rows for better initial display
26255
26323
  self.auto_resize_rows()
26256
-
26324
+
26257
26325
  # Initialize spellcheck for target language
26258
26326
  self._initialize_spellcheck_for_target_language(target_lang)
26259
-
26327
+
26260
26328
  # If smart formatting was used, auto-enable Tags view so user sees the tags
26261
26329
  if self.memoq_smart_formatting:
26262
26330
  self._enable_tag_view_after_import()
@@ -26745,7 +26813,11 @@ class SupervertalerQt(QMainWindow):
26745
26813
 
26746
26814
  # Store memoQ XLIFF source path in project for persistence across saves
26747
26815
  self.current_project.mqxliff_source_path = file_path
26748
-
26816
+
26817
+ # Sync global language settings with imported project languages
26818
+ self.source_language = source_lang
26819
+ self.target_language = target_lang
26820
+
26749
26821
  # Update UI
26750
26822
  self.project_file_path = None
26751
26823
  self.project_modified = True
@@ -26753,15 +26825,15 @@ class SupervertalerQt(QMainWindow):
26753
26825
  self.load_segments_to_grid()
26754
26826
  self.initialize_tm_database()
26755
26827
 
26756
- # Deactivate all resources for new project (user explicitly activates what they need)
26828
+ # Deactivate all resources for new project, then auto-activate language-matching ones
26757
26829
  self._deactivate_all_resources_for_new_project()
26758
-
26830
+
26759
26831
  # Auto-resize rows for better initial display
26760
26832
  self.auto_resize_rows()
26761
-
26833
+
26762
26834
  # Initialize spellcheck for target language
26763
26835
  self._initialize_spellcheck_for_target_language(target_lang)
26764
-
26836
+
26765
26837
  # Log success
26766
26838
  self.log(f"✓ Imported {len(segments)} segments from memoQ XLIFF: {Path(file_path).name}")
26767
26839
  self.log(f" Source: {source_lang}, Target: {target_lang}")
@@ -27020,7 +27092,11 @@ class SupervertalerQt(QMainWindow):
27020
27092
 
27021
27093
  # Store CafeTran source path in project for persistence across saves
27022
27094
  self.current_project.cafetran_source_path = file_path
27023
-
27095
+
27096
+ # Sync global language settings with imported project languages
27097
+ self.source_language = self.current_project.source_lang
27098
+ self.target_language = self.current_project.target_lang
27099
+
27024
27100
  # Update UI
27025
27101
  self.project_file_path = None
27026
27102
  self.project_modified = True
@@ -27028,16 +27104,16 @@ class SupervertalerQt(QMainWindow):
27028
27104
  self.load_segments_to_grid()
27029
27105
  self.initialize_tm_database()
27030
27106
 
27031
- # Deactivate all resources for new project (user explicitly activates what they need)
27107
+ # Deactivate all resources for new project, then auto-activate language-matching ones
27032
27108
  self._deactivate_all_resources_for_new_project()
27033
-
27109
+
27034
27110
  # Auto-resize rows for better initial display
27035
27111
  self.auto_resize_rows()
27036
-
27112
+
27037
27113
  # Initialize spellcheck for target language
27038
27114
  target_lang = self.current_project.target_lang if self.current_project else 'nl'
27039
27115
  self._initialize_spellcheck_for_target_language(target_lang)
27040
-
27116
+
27041
27117
  # Log success
27042
27118
  self.log(f"✓ Imported {len(segments)} segments from CafeTran bilingual DOCX: {Path(file_path).name}")
27043
27119
 
@@ -27244,7 +27320,11 @@ class SupervertalerQt(QMainWindow):
27244
27320
 
27245
27321
  # Store Trados source path in project for persistence across saves
27246
27322
  self.current_project.trados_source_path = file_path
27247
-
27323
+
27324
+ # Sync global language settings with imported project languages
27325
+ self.source_language = source_lang
27326
+ self.target_language = target_lang
27327
+
27248
27328
  # Update UI
27249
27329
  self.project_file_path = None
27250
27330
  self.project_modified = True
@@ -27252,15 +27332,15 @@ class SupervertalerQt(QMainWindow):
27252
27332
  self.load_segments_to_grid()
27253
27333
  self.initialize_tm_database()
27254
27334
 
27255
- # Deactivate all resources for new project (user explicitly activates what they need)
27335
+ # Deactivate all resources for new project, then auto-activate language-matching ones
27256
27336
  self._deactivate_all_resources_for_new_project()
27257
-
27337
+
27258
27338
  # Auto-resize rows for better initial display
27259
27339
  self.auto_resize_rows()
27260
-
27340
+
27261
27341
  # Initialize spellcheck for target language
27262
27342
  self._initialize_spellcheck_for_target_language(target_lang)
27263
-
27343
+
27264
27344
  # Count segments with tags
27265
27345
  tagged_count = sum(1 for s in trados_segments if s.source_tags)
27266
27346
 
@@ -27604,7 +27684,11 @@ class SupervertalerQt(QMainWindow):
27604
27684
  self.sdlppx_handler = handler
27605
27685
  self.sdlppx_source_file = file_path
27606
27686
  self.current_project.sdlppx_source_path = file_path
27607
-
27687
+
27688
+ # Sync global language settings with imported project languages
27689
+ self.source_language = source_lang
27690
+ self.target_language = target_lang
27691
+
27608
27692
  # Update UI
27609
27693
  self.project_file_path = None
27610
27694
  self.project_modified = True
@@ -27937,6 +28021,10 @@ class SupervertalerQt(QMainWindow):
27937
28021
  # Store Phrase source path in project for persistence across saves
27938
28022
  self.current_project.phrase_source_path = file_path
27939
28023
 
28024
+ # Sync global language settings with imported project languages
28025
+ self.source_language = source_lang
28026
+ self.target_language = target_lang
28027
+
27940
28028
  # Update UI
27941
28029
  self.project_file_path = None
27942
28030
  self.project_modified = True
@@ -28217,7 +28305,11 @@ class SupervertalerQt(QMainWindow):
28217
28305
 
28218
28306
  # Store Déjà Vu source path in project for persistence
28219
28307
  self.current_project.dejavu_source_path = file_path
28220
-
28308
+
28309
+ # Sync global language settings with imported project languages
28310
+ self.source_language = source_lang
28311
+ self.target_language = target_lang
28312
+
28221
28313
  # Create segments
28222
28314
  for idx, seg_data in enumerate(segments_data):
28223
28315
  segment = Segment(
@@ -31549,9 +31641,12 @@ class SupervertalerQt(QMainWindow):
31549
31641
  ]
31550
31642
  # Also get NT matches (fresh, not cached - they may have changed)
31551
31643
  nt_matches = self.find_nt_matches_in_source(segment.source)
31552
-
31644
+
31645
+ # Get status hint for termbase activation
31646
+ status_hint = self._get_termbase_status_hint()
31647
+
31553
31648
  # Update both Termview widgets (left and right)
31554
- self._update_both_termviews(segment.source, termbase_matches, nt_matches)
31649
+ self._update_both_termviews(segment.source, termbase_matches, nt_matches, status_hint)
31555
31650
  except Exception as e:
31556
31651
  self.log(f"Error updating termview from cache: {e}")
31557
31652
 
@@ -31635,9 +31730,12 @@ class SupervertalerQt(QMainWindow):
31635
31730
  ] if stored_matches else []
31636
31731
  # Also get NT matches
31637
31732
  nt_matches = self.find_nt_matches_in_source(segment.source)
31638
-
31733
+
31734
+ # Get status hint for termbase activation
31735
+ status_hint = self._get_termbase_status_hint()
31736
+
31639
31737
  # Update both Termview widgets (left and right)
31640
- self._update_both_termviews(segment.source, termbase_matches, nt_matches)
31738
+ self._update_both_termviews(segment.source, termbase_matches, nt_matches, status_hint)
31641
31739
  except Exception as e:
31642
31740
  self.log(f"Error refreshing termview: {e}")
31643
31741
 
@@ -33779,7 +33877,8 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
33779
33877
  self.nt_manager.set_list_active(list_name, False)
33780
33878
 
33781
33879
  self.log("📋 New project: All TMs, glossaries, and NT lists deactivated (start clean)")
33782
-
33880
+ self.log("💡 Tip: Go to Resources tab to activate TMs and glossaries for this project")
33881
+
33783
33882
  def search_and_display_tm_matches(self, source_text: str):
33784
33883
  """Search TM and Termbases and display matches with visual diff for fuzzy matches"""
33785
33884
  self.log(f"🚨 search_and_display_tm_matches called with source_text: '{source_text[:50]}...'")
@@ -33802,11 +33901,34 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
33802
33901
  try:
33803
33902
  # Get activated TM IDs for current project
33804
33903
  tm_ids = None
33904
+ no_tms_activated = False
33905
+ tms_wrong_language = False
33805
33906
  if hasattr(self, 'tm_metadata_mgr') and self.tm_metadata_mgr and self.current_project:
33806
33907
  project_id = self.current_project.id if hasattr(self.current_project, 'id') else None
33807
33908
  if project_id:
33808
33909
  tm_ids = self.tm_metadata_mgr.get_active_tm_ids(project_id)
33809
-
33910
+ # Check if no TMs are activated for this project
33911
+ if tm_ids is not None and len(tm_ids) == 0:
33912
+ no_tms_activated = True
33913
+ elif tm_ids and len(tm_ids) > 0:
33914
+ # Check if any activated TMs match the project's language pair
33915
+ project_source = (self.current_project.source_lang or '').lower()
33916
+ project_target = (self.current_project.target_lang or '').lower()
33917
+ all_tms = self.tm_metadata_mgr.get_all_tms()
33918
+ has_matching_language = False
33919
+ for tm in all_tms:
33920
+ if tm['id'] in tm_ids:
33921
+ tm_source = (tm.get('source_lang') or '').lower()
33922
+ tm_target = (tm.get('target_lang') or '').lower()
33923
+ # Match if languages align (bidirectional) or TM has no language set
33924
+ if (not tm_source and not tm_target) or \
33925
+ (tm_source == project_source and tm_target == project_target) or \
33926
+ (tm_source == project_target and tm_target == project_source):
33927
+ has_matching_language = True
33928
+ break
33929
+ if not has_matching_language:
33930
+ tms_wrong_language = True
33931
+
33810
33932
  # Search for matches (using activated TMs if available)
33811
33933
  matches = self.tm_database.search_all(source_text, tm_ids=tm_ids, max_matches=5)
33812
33934
 
@@ -33853,10 +33975,26 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
33853
33975
 
33854
33976
  if not matches:
33855
33977
  if hasattr(self, 'tm_display'):
33856
- self.tm_display.setHtml(
33857
- f"<p style='color: #666;'><b>Source:</b> {source_text}</p>"
33858
- f"<p style='color: #999;'><i>No translation memory matches found</i></p>"
33859
- )
33978
+ if no_tms_activated:
33979
+ # Show helpful message when no TMs are activated
33980
+ self.tm_display.setHtml(
33981
+ f"<p style='color: #666;'><b>Source:</b> {source_text}</p>"
33982
+ f"<p style='color: #E65100;'><i>No TMs activated for this project.</i></p>"
33983
+ f"<p style='color: #999; font-size: 9pt;'>Go to <b>Resources → TM</b> to activate translation memories.</p>"
33984
+ )
33985
+ elif tms_wrong_language:
33986
+ # Show message when TMs are activated but don't match project language pair
33987
+ project_lang_pair = f"{self.current_project.source_lang} → {self.current_project.target_lang}" if self.current_project else ""
33988
+ self.tm_display.setHtml(
33989
+ f"<p style='color: #666;'><b>Source:</b> {source_text}</p>"
33990
+ f"<p style='color: #E65100;'><i>Activated TMs don't match project language ({project_lang_pair}).</i></p>"
33991
+ f"<p style='color: #999; font-size: 9pt;'>Go to <b>Resources → TM</b> to activate TMs for this language pair.</p>"
33992
+ )
33993
+ else:
33994
+ self.tm_display.setHtml(
33995
+ f"<p style='color: #666;'><b>Source:</b> {source_text}</p>"
33996
+ f"<p style='color: #999;'><i>No translation memory matches found</i></p>"
33997
+ )
33860
33998
  return
33861
33999
 
33862
34000
  # If using TranslationResultsPanel, populate it with TM and Termbase results
@@ -35508,8 +35646,11 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
35508
35646
  'termbase_id': match_info.get('termbase_id'),
35509
35647
  'notes': match_info.get('notes', '')
35510
35648
  })
35649
+ # Get status hint for termbase activation
35650
+ status_hint = self._get_termbase_status_hint()
35651
+
35511
35652
  # Update both Termview widgets
35512
- self._update_both_termviews(segment.source, tb_list, nt_matches)
35653
+ self._update_both_termviews(segment.source, tb_list, nt_matches, status_hint)
35513
35654
  self.log(" ✓ TermView updated")
35514
35655
  except Exception as e:
35515
35656
  self.log(f" ⚠️ TermView update error: {e}")
@@ -694,27 +694,28 @@ class TermviewWidget(QWidget):
694
694
  font.setBold(self.current_font_bold)
695
695
  block.source_label.setFont(font)
696
696
 
697
- def update_with_matches(self, source_text: str, termbase_matches: List[Dict], nt_matches: List[Dict] = None):
697
+ def update_with_matches(self, source_text: str, termbase_matches: List[Dict], nt_matches: List[Dict] = None, status_hint: str = None):
698
698
  """
699
699
  Update the termview display with pre-computed termbase and NT matches
700
-
700
+
701
701
  RYS-STYLE DISPLAY: Show source text as tokens with translations underneath
702
-
702
+
703
703
  Args:
704
704
  source_text: Source segment text
705
705
  termbase_matches: List of termbase match dicts from Translation Results
706
706
  nt_matches: Optional list of NT match dicts with 'text', 'start', 'end', 'list_name' keys
707
+ status_hint: Optional hint about why there might be no matches (e.g., 'no_termbases_activated', 'wrong_language')
707
708
  """
708
709
  self.current_source = source_text
709
-
710
+
710
711
  # Clear existing blocks and shortcut mappings
711
712
  self.clear_terms()
712
713
  self.shortcut_terms = {} # Reset shortcut mappings
713
-
714
+
714
715
  if not source_text or not source_text.strip():
715
716
  self.info_label.setText("No segment selected")
716
717
  return
717
-
718
+
718
719
  # Strip HTML/XML tags from source text for display in TermView
719
720
  # This handles CAT tool tags like <b>, </b>, <i>, </i>, <u>, </u>, <bi>, <sub>, <sup>, <li-o>, <li-b>
720
721
  # as well as memoQ tags {1}, [2}, {3], Trados tags <1>, </1>, and Déjà Vu tags {00001}
@@ -725,16 +726,22 @@ class TermviewWidget(QWidget):
725
726
  display_text = re.sub(r'\[[^\[\]]*\}', '', display_text) # Opening: [anything}
726
727
  display_text = re.sub(r'\{[^\{\}]*\]', '', display_text) # Closing: {anything]
727
728
  display_text = display_text.strip()
728
-
729
+
729
730
  # If stripping tags leaves nothing, fall back to original
730
731
  if not display_text:
731
732
  display_text = source_text
732
-
733
+
733
734
  has_termbase = termbase_matches and len(termbase_matches) > 0
734
735
  has_nt = nt_matches and len(nt_matches) > 0
735
-
736
+
736
737
  if not has_termbase and not has_nt:
737
- self.info_label.setText("No terminology or NT matches for this segment")
738
+ # Show appropriate message based on status hint
739
+ if status_hint == 'no_termbases_activated':
740
+ self.info_label.setText("No glossaries activated. Go to Resources → Glossary to activate.")
741
+ elif status_hint == 'wrong_language':
742
+ self.info_label.setText("Activated glossaries don't match project language pair.")
743
+ else:
744
+ self.info_label.setText("No terminology or NT matches for this segment")
738
745
  return
739
746
 
740
747
  # Convert termbase matches to dict for easy lookup: {source_term.lower(): [translations]}
modules/theme_manager.py CHANGED
@@ -212,7 +212,10 @@ class ThemeManager:
212
212
  self.themes_file = user_data_path / "themes.json"
213
213
  self.current_theme: Theme = self.PREDEFINED_THEMES["Light (Default)"]
214
214
  self.custom_themes: Dict[str, Theme] = {}
215
-
215
+
216
+ # Global UI font scale (50-200%, default 100%)
217
+ self.font_scale: int = 100
218
+
216
219
  # Load custom themes
217
220
  self.load_custom_themes()
218
221
 
@@ -289,14 +292,48 @@ class ThemeManager:
289
292
  def apply_theme(self, app: QApplication):
290
293
  """
291
294
  Apply current theme to application
292
-
295
+
293
296
  Args:
294
297
  app: QApplication instance
295
298
  """
296
299
  theme = self.current_theme
297
-
300
+
301
+ # Calculate scaled font sizes based on font_scale (default 100%)
302
+ base_font_size = int(10 * self.font_scale / 100) # Base: 10pt at 100%
303
+ small_font_size = max(7, int(9 * self.font_scale / 100)) # Small text (status bar)
304
+
305
+ # Font scaling rules (only applied if scale != 100%)
306
+ font_rules = ""
307
+ if self.font_scale != 100:
308
+ font_rules = f"""
309
+ /* Global font scaling ({self.font_scale}%) */
310
+ QWidget {{ font-size: {base_font_size}pt; }}
311
+ QMenuBar {{ font-size: {base_font_size}pt; }}
312
+ QMenuBar::item {{ font-size: {base_font_size}pt; }}
313
+ QMenu {{ font-size: {base_font_size}pt; }}
314
+ QMenu::item {{ font-size: {base_font_size}pt; }}
315
+ QStatusBar {{ font-size: {small_font_size}pt; }}
316
+ QTabBar::tab {{ font-size: {base_font_size}pt; }}
317
+ QToolBar {{ font-size: {base_font_size}pt; }}
318
+ QLabel {{ font-size: {base_font_size}pt; }}
319
+ QCheckBox {{ font-size: {base_font_size}pt; }}
320
+ QRadioButton {{ font-size: {base_font_size}pt; }}
321
+ QComboBox {{ font-size: {base_font_size}pt; }}
322
+ QSpinBox {{ font-size: {base_font_size}pt; }}
323
+ QDoubleSpinBox {{ font-size: {base_font_size}pt; }}
324
+ QLineEdit {{ font-size: {base_font_size}pt; }}
325
+ QPushButton {{ font-size: {base_font_size}pt; }}
326
+ QGroupBox {{ font-size: {base_font_size}pt; }}
327
+ QGroupBox::title {{ font-size: {base_font_size}pt; }}
328
+ QTextEdit {{ font-size: {base_font_size}pt; }}
329
+ QPlainTextEdit {{ font-size: {base_font_size}pt; }}
330
+ QListWidget {{ font-size: {base_font_size}pt; }}
331
+ QTreeWidget {{ font-size: {base_font_size}pt; }}
332
+ QHeaderView::section {{ font-size: {base_font_size}pt; }}
333
+ """
334
+
298
335
  # Create and apply stylesheet - COLORS ONLY, preserves native sizes/spacing
299
- stylesheet = f"""
336
+ stylesheet = font_rules + f"""
300
337
  /* Main window background */
301
338
  QMainWindow, QWidget {{
302
339
  background-color: {theme.window_bg};
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervertaler
3
- Version: 1.9.179
3
+ Version: 1.9.181
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=UtCfJez0AGX9-XdOMzjFzbKtooT6sYfdJognk_kQkK4,2324855
1
+ Supervertaler.py,sha256=8dvcmMQ-U7MpzzXQVLAsoB0O-yF_HMl142HfIiaMprE,2333467
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
@@ -59,8 +59,8 @@ modules/term_extractor.py,sha256=qPvKNCVXFTGEGwXNvvC0cfCmdb5c3WhzE38EOgKdKUI,112
59
59
  modules/termbase_entry_editor.py,sha256=iWO9CgLjMomGAqBXDsGAX7TFJvDOp2s_taS4gBL1rZY,35818
60
60
  modules/termbase_import_export.py,sha256=16IAY04IS_rgt0GH5UOUzUI5NoqAli4JMfMquxmFBm0,23552
61
61
  modules/termbase_manager.py,sha256=7KXEFab6y0o1EmFZwHs3ADklC95udVenxvrmN4XUoj0,48808
62
- modules/termview_widget.py,sha256=O3ah7g-4Lb_iUctxl9sMyxh8V3A5I5PFxmy9iIH2Kgk,53484
63
- modules/theme_manager.py,sha256=EOI_5pM2bXAadw08bbl92TLN-w28lbw4Zi1E8vQ-kM0,16694
62
+ modules/termview_widget.py,sha256=PibDY55CjBY4BSb6aMGEp4Y2JHNB67QWOQFTDNyeYy8,53968
63
+ modules/theme_manager.py,sha256=Qk_jfCmfm7fjdMAOyBHpD18w3MiRfWBZk0cHTw6yAAg,18639
64
64
  modules/tm_editor_dialog.py,sha256=AzGwq4QW641uFJdF8DljLTRRp4FLoYX3Pe4rlTjQWNg,3517
65
65
  modules/tm_manager_qt.py,sha256=h2bvXkRuboHf_RRz9-5FX35GVRlpXgRDWeXyj1QWtPs,54406
66
66
  modules/tm_metadata_manager.py,sha256=NTsaI_YjQnVOpU_scAwK9uR1Tcl9pzKD1GwLVy7sx2g,23590
@@ -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.179.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
- supervertaler-1.9.179.dist-info/METADATA,sha256=WMOJz651RKiVVDStAsVxI5AHtNN6PHl6IothPf1-_Po,5725
82
- supervertaler-1.9.179.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
- supervertaler-1.9.179.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
- supervertaler-1.9.179.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
- supervertaler-1.9.179.dist-info/RECORD,,
80
+ supervertaler-1.9.181.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
81
+ supervertaler-1.9.181.dist-info/METADATA,sha256=OQ1LAlPRGRyBLRAy3jX7II9VFMlrIMqy-s3o1L_NELg,5725
82
+ supervertaler-1.9.181.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
83
+ supervertaler-1.9.181.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
84
+ supervertaler-1.9.181.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
85
+ supervertaler-1.9.181.dist-info/RECORD,,