Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/).

## [Unreleased]

### Übersetzte Hilfetexte und gespeicherte Sprache (2026-10-01)
- 86 bislang fehlende Hilfetexte, Beschriftungen und Fehlermeldungen im Katalog ergänzt; Hilfetexte über die Übersetzungsfunktion angebunden (DE, EN, ES, ZH, JA, RU).
- Die Sprachauswahl erklärt in allen sechs Sprachen, dass Sprachänderungen beim nächsten Start übernommen werden.
- Sprachauswahl wird geladen und gespeichert; beim nächsten Start verwenden die angebundenen Texte und Qt-Standarddialoge die gespeicherte Sprache.
- Übersetzungsprüfung erkennt auch bereits angebundene `t(...)`-Aufrufe. Weitere fest deutsche Bedienelemente bleiben separat offen.

### Datei-/Ordner-Eigenschaften, Pfad-Kopier-Suite & Terminal-Integration (2026-10-03, TW-EP-11)
- **Datei- & Ordner-Eigenschaften Dialog (`src/gui/properties_dialog.py`)**:
- Neuer standardkonformer Eigenschafts-Dialog mit Tabs für "Allgemein" und "Prüfsummen" (bzw. "Inhalt" für Textdateien).
Expand Down
90 changes: 89 additions & 1 deletion locales/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2846,5 +2846,93 @@
"zh": "打开选定的文件",
"ja": "選択したファイルを開く",
"ru": "Открывает выбранный файл"
},
"Datei nicht gefunden": {
"de": "Datei nicht gefunden",
"en": "File not found",
"es": "Archivo no encontrado",
"zh": "找不到文件",
"ja": "ファイルが見つかりません",
"ru": "Файл не найден"
},
"Dateien umbenannt, aber Fehler aufgetreten:": {
"de": "Dateien umbenannt, aber Fehler aufgetreten:",
"en": "Files renamed, but errors occurred:",
"es": "Archivos renombrados, pero se produjeron errores:",
"zh": "文件已重命名,但发生了错误:",
"ja": "ファイルの名前を変更しましたが、エラーが発生しました:",
"ru": "Файлы переименованы, но возникли ошибки:"
},
"Dateien zurückgesetzt, Fehler:": {
"de": "Dateien zurückgesetzt, Fehler:",
"en": "Files restored; errors:",
"es": "Archivos restaurados; errores:",
"zh": "文件已还原;错误:",
"ja": "ファイルを復元しました。エラー:",
"ru": "Файлы восстановлены; ошибки:"
},
"Pfad ist keine Datei": {
"de": "Pfad ist keine Datei",
"en": "The path is not a file",
"es": "La ruta no es un archivo",
"zh": "该路径不是文件",
"ja": "パスはファイルではありません",
"ru": "Путь не указывает на файл"
},
"Ungültiger Pfad": {
"de": "Ungültiger Pfad",
"en": "Invalid path",
"es": "Ruta no válida",
"zh": "无效路径",
"ja": "無効なパス",
"ru": "Недопустимый путь"
},
"Sprachänderungen werden beim nächsten Start übernommen.": {
"de": "Sprachänderungen werden beim nächsten Start übernommen.",
"en": "Language changes take effect on the next start.",
"es": "Los cambios de idioma se aplican en el próximo inicio.",
"zh": "语言更改将在下次启动时生效。",
"ja": "言語の変更は次回起動時に適用されます。",
"ru": "Изменение языка вступит в силу при следующем запуске."
},
"Umbenennen": {
"de": "Umbenennen",
"en": "Rename",
"es": "Renombrar",
"zh": "重命名",
"ja": "名前を変更",
"ru": "Переименовать"
},
"Status": {
"de": "Status",
"en": "Status",
"es": "Estado",
"zh": "状态",
"ja": "状態",
"ru": "Статус"
},
"Erfolg": {
"de": "Erfolg",
"en": "Success",
"es": "Correcto",
"zh": "成功",
"ja": "成功",
"ru": "Успех"
},
"Inhalt": {
"de": "Inhalt",
"en": "Content",
"es": "Contenido",
"zh": "内容",
"ja": "内容",
"ru": "Содержимое"
},
"Diff kopieren": {
"de": "Diff kopieren",
"en": "Copy diff",
"es": "Copiar diferencias",
"zh": "复制差异",
"ja": "差分をコピー",
"ru": "Копировать различия"
}
}
}
17 changes: 16 additions & 1 deletion manage_translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

import argparse
import ast
import json
import os
import re
Expand Down Expand Up @@ -76,6 +77,20 @@ def find_german_strings(source_dir: str) -> Set[str]:
for match in pattern.findall(content):
if is_german(match):
german_strings.add(match.strip())
# Translated calls must remain visible to the auditor after
# setters are changed from setToolTip("...") to setToolTip(t("...")).
try:
tree = ast.parse(content)
except SyntaxError:
continue
for node in ast.walk(tree):
if (isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "t" and node.args
and isinstance(node.args[0], ast.Constant)
and isinstance(node.args[0].value, str)
and node.args[0].value.strip()):
german_strings.add(node.args[0].value.strip())
return german_strings


Expand Down Expand Up @@ -123,7 +138,7 @@ def manage_translations(source_dir: str = ".", check_mode: bool = False) -> int:
for lang, count in missing_by_lang.items():
print(f" - {lang}: {count} unvollständig")
else:
print("\n[ok] Alle Strings haben vollständige Übersetzungen in allen 6 Sprachen (DE, EN, ES, ZH, JA, RU).")
print("\n[ok] Alle Katalogeinträge haben vollständige Übersetzungen in allen 6 Sprachen (DE, EN, ES, ZH, JA, RU).")

print(f"\n[i] Gesamt: {len(translations)} Strings in {trans_file}")

Expand Down
30 changes: 15 additions & 15 deletions src/gui/batch_rename_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def _setup_ui(self):
self.search_edit = QLineEdit()
self.search_edit.setAccessibleName("Suchbegriff für Umbenennung")
self.search_edit.setAccessibleDescription("Zu suchender Text im ursprünglichen Dateinamen.")
self.search_edit.setToolTip("Zu ersetzender Teilstring im Dateinamen")
self.search_edit.setToolTip(t("Zu ersetzender Teilstring im Dateinamen"))
search_lbl.setBuddy(self.search_edit)
self.search_edit.textChanged.connect(self._update_preview)
search_grid.addWidget(self.search_edit, 0, 1)
Expand All @@ -97,23 +97,23 @@ def _setup_ui(self):
self.replace_edit = QLineEdit()
self.replace_edit.setAccessibleName("Ersetzungstext")
self.replace_edit.setAccessibleDescription("Neuer Text, der anstelle des Suchbegriffs eingesetzt wird.")
self.replace_edit.setToolTip("Neuer Text für gefundene Treffer")
self.replace_edit.setToolTip(t("Neuer Text für gefundene Treffer"))
replace_lbl.setBuddy(self.replace_edit)
self.replace_edit.textChanged.connect(self._update_preview)
search_grid.addWidget(self.replace_edit, 1, 1)

self.regex_cb = QCheckBox(t("Reguläre Ausdrücke (Regex)"))
self.regex_cb.setAccessibleName("Reguläre Ausdrücke")
self.regex_cb.setAccessibleDescription("Aktiviert die Auswertung regulärer Ausdrücke im Suchbegriff.")
self.regex_cb.setToolTip("Suchbegriff als regulären Ausdruck auswerten")
self.regex_cb.setToolTip(t("Suchbegriff als regulären Ausdruck auswerten"))
self.regex_cb.toggled.connect(self._update_preview)
search_grid.addWidget(self.regex_cb, 2, 0, 1, 2)

self.case_sensitive_cb = QCheckBox(t("Groß-/Kleinschreibung beachten"))
self.case_sensitive_cb.setChecked(True)
self.case_sensitive_cb.setAccessibleName("Groß- und Kleinschreibung beachten")
self.case_sensitive_cb.setAccessibleDescription("Unterscheidet zwischen Groß- und Kleinbuchstaben beim Suchen.")
self.case_sensitive_cb.setToolTip("Exakte Schreibweise beim Suchen berücksichtigen")
self.case_sensitive_cb.setToolTip(t("Exakte Schreibweise beim Suchen berücksichtigen"))
self.case_sensitive_cb.toggled.connect(self._update_preview)
search_grid.addWidget(self.case_sensitive_cb, 3, 0, 1, 2)

Expand All @@ -128,7 +128,7 @@ def _setup_ui(self):
self.prefix_edit = QLineEdit()
self.prefix_edit.setAccessibleName("Dateinamen-Präfix")
self.prefix_edit.setAccessibleDescription("Text, der vor jeden Dateinamen eingefügt wird.")
self.prefix_edit.setToolTip("Präfix vor den Dateinamen stellen")
self.prefix_edit.setToolTip(t("Präfix vor den Dateinamen stellen"))
prefix_lbl.setBuddy(self.prefix_edit)
self.prefix_edit.textChanged.connect(self._update_preview)
affix_grid.addWidget(self.prefix_edit, 0, 1)
Expand All @@ -138,7 +138,7 @@ def _setup_ui(self):
self.suffix_edit = QLineEdit()
self.suffix_edit.setAccessibleName("Dateinamen-Suffix")
self.suffix_edit.setAccessibleDescription("Text, der an das Ende des Dateinamens vor die Dateiendung gehängt wird.")
self.suffix_edit.setToolTip("Suffix an den Dateinamen anhängen")
self.suffix_edit.setToolTip(t("Suffix an den Dateinamen anhängen"))
suffix_lbl.setBuddy(self.suffix_edit)
self.suffix_edit.textChanged.connect(self._update_preview)
affix_grid.addWidget(self.suffix_edit, 1, 1)
Expand All @@ -153,7 +153,7 @@ def _setup_ui(self):
self.case_combo.addItem(t("Satzanfang groß"), "sentence")
self.case_combo.setAccessibleName("Schreibweisenumwandlung")
self.case_combo.setAccessibleDescription("Wandelt die Schreibweise aller Dateinamen einheitlich um.")
self.case_combo.setToolTip("Groß- und Kleinschreibung des Namens anpassen")
self.case_combo.setToolTip(t("Groß- und Kleinschreibung des Namens anpassen"))
case_lbl.setBuddy(self.case_combo)
self.case_combo.currentIndexChanged.connect(self._update_preview)
affix_grid.addWidget(self.case_combo, 2, 1)
Expand All @@ -178,7 +178,7 @@ def _setup_ui(self):
self.num_enabled_cb = QCheckBox(t("Nummerierung aktivieren"))
self.num_enabled_cb.setAccessibleName("Nummerierung aktivieren")
self.num_enabled_cb.setAccessibleDescription("Schaltet die sequentielle Dateinummerierung ein.")
self.num_enabled_cb.setToolTip("Dateien fortlaufend nummerieren")
self.num_enabled_cb.setToolTip(t("Dateien fortlaufend nummerieren"))
self.num_enabled_cb.toggled.connect(self._update_preview)
num_grid.addWidget(self.num_enabled_cb, 0, 0, 1, 2)

Expand All @@ -188,7 +188,7 @@ def _setup_ui(self):
self.start_spin.setRange(0, 999999)
self.start_spin.setValue(1)
self.start_spin.setAccessibleName("Startwert der Nummerierung")
self.start_spin.setToolTip("Startnummer der Zählung")
self.start_spin.setToolTip(t("Startnummer der Zählung"))
start_lbl.setBuddy(self.start_spin)
self.start_spin.valueChanged.connect(self._update_preview)
num_grid.addWidget(self.start_spin, 1, 1)
Expand All @@ -199,7 +199,7 @@ def _setup_ui(self):
self.step_spin.setRange(1, 1000)
self.step_spin.setValue(1)
self.step_spin.setAccessibleName("Schrittweite der Nummerierung")
self.step_spin.setToolTip("Inkrement pro Datei")
self.step_spin.setToolTip(t("Inkrement pro Datei"))
step_lbl.setBuddy(self.step_spin)
self.step_spin.valueChanged.connect(self._update_preview)
num_grid.addWidget(self.step_spin, 2, 1)
Expand All @@ -222,7 +222,7 @@ def _setup_ui(self):
self.num_pos_combo.addItem(t("Am Anfang (Präfix)"), "prefix")
self.num_pos_combo.addItem(t("Ersetzen"), "replace")
self.num_pos_combo.setAccessibleName("Position der Nummerierung")
self.num_pos_combo.setToolTip("Position der Nummerierung im Dateinamen")
self.num_pos_combo.setToolTip(t("Position der Nummerierung im Dateinamen"))
pos_lbl.setBuddy(self.num_pos_combo)
self.num_pos_combo.currentIndexChanged.connect(self._update_preview)
num_grid.addWidget(self.num_pos_combo, 4, 1)
Expand All @@ -246,7 +246,7 @@ def _setup_ui(self):
self.table.setAlternatingRowColors(True)
self.table.setAccessibleName("Umbenennungs-Vorschautabelle")
self.table.setAccessibleDescription("Zeigt Originalnamen, vorgeschlagene neue Namen und eventuelle Konflikte.")
self.table.setToolTip("Vorschautabelle aller zu ändernden Dateinamen")
self.table.setToolTip(t("Vorschautabelle aller zu ändernden Dateinamen"))
preview_layout.addWidget(self.table)

# Statusleiste / Zusammenfassung
Expand All @@ -261,7 +261,7 @@ def _setup_ui(self):

self.rollback_btn = QPushButton(t("Rückgängig"))
self.rollback_btn.setAccessibleName("Letzte Umbenennung rückgängig machen")
self.rollback_btn.setToolTip("Macht die vorherige Umbenennungs-Aktion rückgängig")
self.rollback_btn.setToolTip(t("Macht die vorherige Umbenennungs-Aktion rückgängig"))
self.rollback_btn.setEnabled(False)
self.rollback_btn.clicked.connect(self._do_rollback)
btn_layout.addWidget(self.rollback_btn)
Expand All @@ -270,14 +270,14 @@ def _setup_ui(self):

self.rename_btn = QPushButton(t("Umbenennen"))
self.rename_btn.setAccessibleName("Dateien jetzt umbenennen")
self.rename_btn.setToolTip("Führt die geplante Umbenennung für alle Dateien aus (Enter)")
self.rename_btn.setToolTip(t("Führt die geplante Umbenennung für alle Dateien aus (Enter)"))
self.rename_btn.setDefault(True)
self.rename_btn.clicked.connect(self._do_rename)
btn_layout.addWidget(self.rename_btn)

self.close_btn = QPushButton(t("Schließen"))
self.close_btn.setAccessibleName("Dialog schließen")
self.close_btn.setToolTip("Schließt das Mehrfachumbenennungs-Fenster (Esc)")
self.close_btn.setToolTip(t("Schließt das Mehrfachumbenennungs-Fenster (Esc)"))
self.close_btn.setShortcut("Escape")
self.close_btn.clicked.connect(self._on_close_clicked)
btn_layout.addWidget(self.close_btn)
Expand Down
9 changes: 5 additions & 4 deletions src/gui/checksum_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from PySide6.QtGui import QFont

from core.checksum_service import ChecksumWorker, verify_hash
from translator import t


class ChecksumDialog(QDialog):
Expand Down Expand Up @@ -104,7 +105,7 @@ def _setup_ui(self):
self.copy_all_btn = QPushButton("📋 Alle Prüfsummen kopieren")
self.copy_all_btn.setAccessibleName("Alle Prüfsummen kopieren")
self.copy_all_btn.setAccessibleDescription("Kopiert alle berechneten Hash-Werte strukturiert in die Zwischenablage.")
self.copy_all_btn.setToolTip("Alle berechneten Prüfsummen (MD5, SHA-1, SHA-256, SHA-512) in die Zwischenablage kopieren")
self.copy_all_btn.setToolTip(t("Alle berechneten Prüfsummen (MD5, SHA-1, SHA-256, SHA-512) in die Zwischenablage kopieren"))
self.copy_all_btn.clicked.connect(self._copy_all_hashes)
self.copy_all_btn.setEnabled(False)
hashes_layout.addRow("", self.copy_all_btn)
Expand All @@ -124,7 +125,7 @@ def _setup_ui(self):
self.verify_edit.setClearButtonEnabled(True)
self.verify_edit.setAccessibleName("Erwartete Prüfsumme zur Verifikation")
self.verify_edit.setAccessibleDescription("Eingabefeld für den Vergleichs-Hash. Erkennt automatisch den passenden Prüfsummen-Algorithmus.")
self.verify_edit.setToolTip("Geben Sie hier einen Hash ein, um die Integrität automatisch zu überprüfen")
self.verify_edit.setToolTip(t("Geben Sie hier einen Hash ein, um die Integrität automatisch zu überprüfen"))
self.verify_edit.textChanged.connect(self._on_verify_text_changed)
verify_layout.addWidget(self.verify_edit)

Expand All @@ -141,7 +142,7 @@ def _setup_ui(self):

self.close_btn = QPushButton("Schließen")
self.close_btn.setAccessibleName("Dialog schließen")
self.close_btn.setToolTip("Schließt das Prüfsummen-Fenster (Esc)")
self.close_btn.setToolTip(t("Schließt das Prüfsummen-Fenster (Esc)"))
self.close_btn.setShortcut("Escape")
self.close_btn.setDefault(True)
self.close_btn.clicked.connect(self.accept)
Expand All @@ -157,7 +158,7 @@ def _start_calculation(self):
return
if not os.path.isfile(self.filepath):
self.progress_bar.setVisible(False)
self.verify_result_label.setText("Fehler: Pfad ist ein Verzeichnis, keine Datei.")
self.verify_result_label.setText(t("Fehler: Pfad ist ein Verzeichnis, keine Datei."))
self.verify_result_label.setStyleSheet("color: red; font-weight: bold;")
return

Expand Down
10 changes: 5 additions & 5 deletions src/gui/diff_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def _setup_ui(self):
files_grid.addWidget(self.file1_edit, 0, 1)
browse1_btn = QPushButton(t("Durchsuchen..."))
browse1_btn.setAccessibleName("Datei 1 durchsuchen")
browse1_btn.setToolTip("Öffnet die Dateiauswahl für Datei 1 (Basis)")
browse1_btn.setToolTip(t("Öffnet die Dateiauswahl für Datei 1 (Basis)"))
browse1_btn.clicked.connect(self._browse_file1)
files_grid.addWidget(browse1_btn, 0, 2)

Expand All @@ -81,15 +81,15 @@ def _setup_ui(self):
files_grid.addWidget(self.file2_edit, 1, 1)
browse2_btn = QPushButton(t("Durchsuchen..."))
browse2_btn.setAccessibleName("Datei 2 durchsuchen")
browse2_btn.setToolTip("Öffnet die Dateiauswahl für Datei 2 (Vergleich)")
browse2_btn.setToolTip(t("Öffnet die Dateiauswahl für Datei 2 (Vergleich)"))
browse2_btn.clicked.connect(self._browse_file2)
files_grid.addWidget(browse2_btn, 1, 2)

self.compare_btn = QPushButton("🔍 " + t("Vergleichen"))
self.compare_btn.setStyleSheet("font-weight: bold; padding: 5px;")
self.compare_btn.setAccessibleName("Dateien vergleichen")
self.compare_btn.setAccessibleDescription("Startet die Differenzanalyse zwischen Datei 1 und Datei 2.")
self.compare_btn.setToolTip("Dateivergleich starten (Strg+Enter)")
self.compare_btn.setToolTip(t("Dateivergleich starten (Strg+Enter)"))
self.compare_btn.setShortcut("Ctrl+Return")
self.compare_btn.clicked.connect(self._do_compare)
files_grid.addWidget(self.compare_btn, 2, 1, 1, 2)
Expand Down Expand Up @@ -127,7 +127,7 @@ def _setup_ui(self):
self.table.verticalHeader().setVisible(False)
self.table.setAccessibleName("Diff-Ergebnistabelle")
self.table.setAccessibleDescription("Zeilenweiser Vergleich beider Dateien: Grün = hinzugefügt, Rot = entfernt.")
self.table.setToolTip("Zeilenweiser Vergleich der beiden Dateien")
self.table.setToolTip(t("Zeilenweiser Vergleich der beiden Dateien"))
main_layout.addWidget(self.table)

# 4. Buttons
Expand All @@ -144,7 +144,7 @@ def _setup_ui(self):

self.close_btn = QPushButton(t("Schließen"))
self.close_btn.setAccessibleName("Dialog schließen")
self.close_btn.setToolTip("Schließt den Vergleichs-Dialog (Esc)")
self.close_btn.setToolTip(t("Schließt den Vergleichs-Dialog (Esc)"))
self.close_btn.setShortcut("Escape")
self.close_btn.setDefault(True)
self.close_btn.clicked.connect(self.accept)
Expand Down
Loading
Loading