#88 + #77: Fehlerursache-Übersetzung und vollständige Tooltip-Abdeckung

Aufgabe 1 (#88): AiFailureMessageTranslator auf public gesetzt, damit der
Verlauf-Tab die technischen Fehlermeldungen in benutzerfreundliche deutsche
Texte übersetzen kann.

Aufgabe 2 (#77): Vollständige Bestandsaufnahme aller interaktiven GUI-Elemente.
13 neue Konstanten in GuiTooltipTexts ergänzt (Provider-Felder, Verarbeitungs-
limits, optionale Pfade, Vorschau-Navigation, Prompt-Buttons, Dateiname-Textfeld).
Alle fehlenden Tooltips in GuiConfigurationEditorWorkspace, GuiPromptEditorTab,
PdfPreviewPane und FileNameEditorPane gesetzt. Hartcodierte Strings in
GuiPromptEditorTab durch Konstantenreferenzen ersetzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 13:25:56 +02:00
parent 6c2e2efe22
commit 0412874f08
7 changed files with 87 additions and 9 deletions
@@ -1573,10 +1573,12 @@ public final class GuiConfigurationEditorWorkspace {
TextField lockField = boundTextField(
editorState.values().runtimeLockFile(),
val -> updateValues(editorState.values().withRuntimeLockFile(val)));
applyTooltip(lockField, GuiTooltipTexts.PFADE_LOCK_DATEI);
TextField logDirField = boundTextField(
editorState.values().logDirectory(),
val -> updateValues(editorState.values().withLogDirectory(val)));
applyTooltip(logDirField, GuiTooltipTexts.PFADE_LOG_VERZEICHNIS);
VBox optionalContent = new VBox(4);
optionalContent.setPadding(new Insets(6, 0, 0, 0));
@@ -1858,6 +1860,7 @@ public final class GuiConfigurationEditorWorkspace {
val, pState2.model(), pState2.timeoutSeconds(), pState2.apiKey())));
Label baseUrlError = createFieldErrorLabel();
fieldErrorLabels.put(ns + "baseUrl", baseUrlError);
applyTooltip(baseUrlField, GuiTooltipTexts.PROVIDER_BASIS_URL);
HBox baseUrlBox = new HBox(4, baseUrlField);
HBox.setHgrow(baseUrlField, Priority.ALWAYS);
fieldGrid.add(new Label("Basis-URL:"), 0, gridRow);
@@ -1866,6 +1869,7 @@ public final class GuiConfigurationEditorWorkspace {
TextField timeoutField = boundTextField(pState.timeoutSeconds(),
val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState(
pState2.baseUrl(), pState2.model(), val, pState2.apiKey())));
applyTooltip(timeoutField, GuiTooltipTexts.PROVIDER_TIMEOUT);
Label timeoutError = createFieldErrorLabel();
fieldErrorLabels.put(ns + "timeoutSeconds", timeoutError);
fieldGrid.add(new Label("Timeout (Sek.):"), 2, gridRow);
@@ -1924,6 +1928,7 @@ public final class GuiConfigurationEditorWorkspace {
val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState(
pState2.baseUrl(), pState2.model(), pState2.timeoutSeconds(),
GuiProviderApiKeyState.unresolved(val))));
applyTooltip(apiKeyField, GuiTooltipTexts.PROVIDER_API_KEY);
Label apiKeyError = createFieldErrorLabel();
fieldErrorLabels.put(ns + "apiKey", apiKeyError);
Label apiKeyOriginLabel = createApiKeyOriginLabel();
@@ -2015,6 +2020,7 @@ public final class GuiConfigurationEditorWorkspace {
TextField maxRetriesField = boundTextField(
editorState.values().maxRetriesTransient(),
val -> updateValues(editorState.values().withMaxRetriesTransient(val)));
applyTooltip(maxRetriesField, GuiTooltipTexts.LIMITS_MAX_RETRIES);
grid.add(new Label("Max. Retries:"), 2, row);
grid.add(maxRetriesField, 3, row);
row++;
@@ -2023,6 +2029,7 @@ public final class GuiConfigurationEditorWorkspace {
TextField logLevelField = boundTextField(
editorState.values().logLevel(),
val -> updateValues(editorState.values().withLogLevel(val)));
applyTooltip(logLevelField, GuiTooltipTexts.LIMITS_LOG_LEVEL);
grid.add(new Label("Log-Level:"), 0, row);
grid.add(logLevelField, 1, row);
@@ -2032,6 +2039,7 @@ public final class GuiConfigurationEditorWorkspace {
sensitiveCheck.setSelected(sensitive);
sensitiveCheck.selectedProperty().addListener((obs, oldVal, newVal) ->
updateValues(editorState.values().withLogAiSensitive(Boolean.toString(newVal))));
applyTooltip(sensitiveCheck, GuiTooltipTexts.LIMITS_SENSIBLE_KI_AUSGABE);
grid.add(new Label(), 2, row);
grid.add(sensitiveCheck, 3, row);
@@ -8,7 +8,6 @@ import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts;
import de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingFailure;
import de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingSuccess;
import de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult;
@@ -245,14 +244,13 @@ public class GuiPromptEditorTab {
statusLabel.setStyle("-fx-text-fill: #555555;");
// Buttons verdrahten
saveButton.setTooltip(new Tooltip("Prompt-Datei speichern (atomar, UTF-8)."));
saveButton.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_SPEICHERN));
saveButton.setOnAction(e -> requestSave());
resetButton.setTooltip(new Tooltip("Textfeld mit dem Standard-Prompt-Inhalt befüllen, ohne zu speichern."));
resetButton.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_ZURUECKSETZEN));
resetButton.setOnAction(e -> resetToDefault());
createDefaultButton.setTooltip(new Tooltip(
"Standard-Prompt-Datei am konfigurierten Pfad anlegen."));
createDefaultButton.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_STANDARD_ANLEGEN));
createDefaultButton.setOnAction(e -> requestCreateDefault());
createDefaultButton.setVisible(false);
createDefaultButton.setManaged(false);
@@ -65,6 +65,14 @@ public final class GuiTooltipTexts {
public static final String PFADE_PROMPT =
"Externe Textdatei mit den KI-Anweisungen.";
/** Tooltip für das Eingabefeld „Lock-Datei". */
public static final String PFADE_LOCK_DATEI =
"Pfad zur Lock-Datei, die parallele Instanzen verhindert (optional).";
/** Tooltip für das Eingabefeld „Log-Verzeichnis". */
public static final String PFADE_LOG_VERZEICHNIS =
"Verzeichnis für Log-Dateien. Leer = Standardverzeichnis logs/ im Programmverzeichnis.";
// -------------------------------------------------------------------------
// Konfigurationstab Provider
// -------------------------------------------------------------------------
@@ -77,6 +85,18 @@ public final class GuiTooltipTexts {
public static final String PROVIDER_MODELL =
"Das konkrete Sprachmodell des gewählten Providers.";
/** Tooltip für das Eingabefeld „Basis-URL". */
public static final String PROVIDER_BASIS_URL =
"Basis-URL des KI-Dienstes (z.B. https://api.openai.com/v1).";
/** Tooltip für das Eingabefeld „Timeout". */
public static final String PROVIDER_TIMEOUT =
"Zeitlimit für KI-Anfragen in Sekunden.";
/** Tooltip für das Eingabefeld „API-Key". */
public static final String PROVIDER_API_KEY =
"API-Schlüssel für den konfigurierten KI-Dienst. Umgebungsvariable hat Vorrang.";
// -------------------------------------------------------------------------
// Konfigurationstab Verarbeitungslimits
// -------------------------------------------------------------------------
@@ -93,10 +113,26 @@ public final class GuiTooltipTexts {
public static final String LIMITS_MAX_TITLE_LENGTH =
"Maximale Länge des Dateinamens in Zeichen (ohne Datum und Erweiterung). Gültig: 10120.";
/** Tooltip für das Eingabefeld „max.retries.transient". */
public static final String LIMITS_MAX_RETRIES =
"Maximale Anzahl transienter Wiederholversuche je Dokument (Ganzzahl ≥ 1).";
/** Tooltip für das Eingabefeld „Log-Level". */
public static final String LIMITS_LOG_LEVEL =
"Log-Detailstufe (z. B. INFO, DEBUG, WARN). Leer = Standardwert INFO.";
/** Tooltip für die Checkbox „Sensible KI-Ausgabe". */
public static final String LIMITS_SENSIBLE_KI_AUSGABE =
"Vollständige KI-Antworten in die Log-Datei schreiben (nur für Diagnosezwecke empfohlen).";
// -------------------------------------------------------------------------
// Verarbeitungslauf-Tab Dateiname-Editor
// -------------------------------------------------------------------------
/** Tooltip für das Dateiname-Textfeld im Dateiname-Editor. */
public static final String DATEINAME_TEXTFELD =
"Dateiname bearbeiten. Format: JJJJ-MM-TT - Titel.pdf";
/** Tooltip für den Button „Dateiname übernehmen". */
public static final String DATEINAME_UEBERNEHMEN =
"Benennt die Zieldatei um und aktualisiert die Datenbank. Nicht rückgängig zu machen.";
@@ -133,6 +169,14 @@ public final class GuiTooltipTexts {
public static final String BATCHRUN_MESSAGE_AREA =
"Statusmeldungen und Fortschrittsinformationen des aktuellen Verarbeitungslaufs.";
/** Tooltip für den Navigations-Button „Vorherige Seite" in der PDF-Vorschau. */
public static final String PREVIEW_VORHERIGE_SEITE =
"Vorherige Seite der Vorschau anzeigen.";
/** Tooltip für den Navigations-Button „Nächste Seite" in der PDF-Vorschau. */
public static final String PREVIEW_NAECHSTE_SEITE =
"Nächste Seite der Vorschau anzeigen.";
/** Tooltip für Spalte „Status" in der Verarbeitungslauf-Tabelle. */
public static final String BATCHRUN_COL_STATUS =
"Verarbeitungsergebnis: Erfolg, Fehler oder übersprungen.";
@@ -233,6 +277,18 @@ public final class GuiTooltipTexts {
public static final String PROMPT_TEXTAREA =
"KI-Anweisungstext. Dieser Prompt wird bei jedem Verarbeitungsversuch an das Sprachmodell gesendet.";
/** Tooltip für den Button „Speichern" im Prompt-Editor-Tab. */
public static final String PROMPT_SPEICHERN =
"Prompt-Datei speichern (atomar, UTF-8).";
/** Tooltip für den Button „Auf Standard zurücksetzen" im Prompt-Editor-Tab. */
public static final String PROMPT_ZURUECKSETZEN =
"Textfeld mit dem Standard-Prompt-Inhalt befüllen, ohne zu speichern.";
/** Tooltip für den Button „Standard-Prompt erstellen" im Prompt-Editor-Tab. */
public static final String PROMPT_STANDARD_ANLEGEN =
"Standard-Prompt-Datei am konfigurierten Pfad anlegen.";
/** Nicht instanziierbar reine Konstantenklasse. */
private GuiTooltipTexts() {
throw new UnsupportedOperationException("Nicht instanziierbar");
@@ -2,7 +2,7 @@ package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun;
/**
* Übersetzt strukturierte Fehlermeldungen aus der Anwendungsschicht in
* benutzerfreundliche deutsche Texte für den Detailbereich des Verarbeitungslauf-Tabs.
* benutzerfreundliche deutsche Texte für die Darstellungsschicht der GUI.
* <p>
* Die Klasse wertet die englischsprachige Fehlermeldung aus dem Verarbeitungsversuch
* musterbasiert aus und liefert eine für den Endbenutzer lesbare Beschreibung des
@@ -12,8 +12,10 @@ package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun;
* Die Mustererkennung erfolgt ohne Berücksichtigung der Groß-/Kleinschreibung
* und prüft die definierten Schlüsselbegriffe in festgelegter Reihenfolge,
* damit spezifischere Muster vor allgemeineren greifen.
* <p>
* Die Klasse wird sowohl im Verarbeitungslauf-Tab als auch im Verlauf-Tab verwendet.
*/
final class AiFailureMessageTranslator {
public final class AiFailureMessageTranslator {
private AiFailureMessageTranslator() {
}
@@ -28,7 +30,7 @@ final class AiFailureMessageTranslator {
* @param technicalMessage die rohe technische Fehlermeldung; darf {@code null} sein
* @return eine nicht-leere deutsche Benutzerfehlermeldung ohne führendes Warnsymbol
*/
static String translate(String technicalMessage) {
public static String translate(String technicalMessage) {
if (technicalMessage == null || technicalMessage.isBlank()) {
return "Verarbeitung fehlgeschlagen. Bitte Konfiguration prüfen und ggf. erneut verarbeiten.";
}
@@ -76,6 +76,9 @@ public final class FileNameEditorPane {
sectionTitle.setStyle("-fx-font-weight: bold;");
textField.setId("filename-editor-text-field");
Tooltip textFieldTooltip = new Tooltip(GuiTooltipTexts.DATEINAME_TEXTFELD);
textFieldTooltip.setShowDelay(Duration.millis(300));
textField.setTooltip(textFieldTooltip);
HBox.setHgrow(textField, Priority.ALWAYS);
HBox inputRow = new HBox(4, textField);
@@ -22,10 +22,13 @@ import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ScrollEvent;
@@ -213,9 +216,15 @@ public final class PdfPreviewPane {
prevButton.setId("pdf-preview-prev-button");
prevButton.setOnAction(e -> navigateToPreviousPage());
Tooltip prevTooltip = new Tooltip(GuiTooltipTexts.PREVIEW_VORHERIGE_SEITE);
prevTooltip.setShowDelay(Duration.millis(300));
prevButton.setTooltip(prevTooltip);
nextButton.setId("pdf-preview-next-button");
nextButton.setOnAction(e -> navigateToNextPage());
Tooltip nextTooltip = new Tooltip(GuiTooltipTexts.PREVIEW_NAECHSTE_SEITE);
nextTooltip.setShowDelay(Duration.millis(300));
nextButton.setTooltip(nextTooltip);
pageLabel.setId("pdf-preview-page-label");
pageLabel.setStyle("-fx-text-fill: #555555;");
@@ -16,6 +16,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.AiFailureMessageTranslator;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.ProcessingStatusPresentation;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord;
import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingAttempt;
@@ -810,7 +811,8 @@ public final class GuiHistoryTab {
}
}
failureArea.setText(failureMessage != null ? failureMessage : "");
failureArea.setText(failureMessage != null
? AiFailureMessageTranslator.translate(failureMessage) : "");
failureArea.setPromptText("Keine Fehlerdetails gespeichert.");
}