From d6696c33172c9ac093b801279429a84276e51d95 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Thu, 10 Sep 2026 13:57:33 +0200 Subject: [PATCH] fix(localization): de.json-Konsistenz-Pass und restliche harte EN-Strings Queue/Task(s)/Gate in deutschem Fliesstext auf Warteschlange/Aufgabe(n)/ Limit-Stopp vereinheitlicht, columnTask-Keys auf AUFGABE angeglichen, Kill-Tonalitaet (closeSession/killSessionTip) ehrlich-destruktiv gemacht. RepoImportModalViewModel.CreateButtonText und alle Installer-Check-Messages (Git/GitIdentity/WriteAccess/ClaudeCli/ClaudeVersion/ClaudeAuth/ PermissionModeAuto/Ports) ueber Locale-Keys geroutet statt hartem Englisch, mit dem bestehenden TrExtension.Localizer-Fallback-Muster. --- .../Checks/ClaudeAuthCheck.cs | 12 ++-- .../Checks/ClaudeCliCheck.cs | 9 ++- .../Checks/ClaudeVersionCheck.cs | 10 +-- src/ClaudeDo.Installer/Checks/GitCheck.cs | 11 +++- .../Checks/GitIdentityCheck.cs | 8 ++- .../Checks/PermissionModeAutoCheck.cs | 10 +-- src/ClaudeDo.Installer/Checks/PortCheck.cs | 12 +++- .../Checks/WriteAccessCheck.cs | 7 +- src/ClaudeDo.Localization/locales/de.json | 66 +++++++++++++------ src/ClaudeDo.Localization/locales/en.json | 44 ++++++++++--- .../Modals/RepoImportModalViewModel.cs | 2 +- 11 files changed, 135 insertions(+), 56 deletions(-) diff --git a/src/ClaudeDo.Installer/Checks/ClaudeAuthCheck.cs b/src/ClaudeDo.Installer/Checks/ClaudeAuthCheck.cs index 9206f920..24bd4822 100644 --- a/src/ClaudeDo.Installer/Checks/ClaudeAuthCheck.cs +++ b/src/ClaudeDo.Installer/Checks/ClaudeAuthCheck.cs @@ -1,5 +1,6 @@ using System.Text.Json; using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -23,11 +24,12 @@ public sealed class ClaudeAuthCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var lookup = await _lookup.ResolveAsync(ctx, ct); if (lookup.Resolved is null) { return CheckResult.Unknown(Id, Severity, "checks.claudeAuth.title", - "The claude CLI was not found — login could not be checked."); + loc?["checks.claudeAuth.notFound"] ?? "The claude CLI was not found — login could not be checked."); } var (fileName, arguments) = ProcessCommand.For(lookup.Resolved, "auth", "status", "--json"); @@ -36,19 +38,19 @@ public sealed class ClaudeAuthCheck : IEnvironmentCheck if (exitCode != 0) { return CheckResult.Unknown(Id, Severity, "checks.claudeAuth.title", - "Login status could not be determined.", output); + loc?["checks.claudeAuth.undetermined"] ?? "Login status could not be determined.", output); } var loggedIn = TryParseLoggedIn(output); if (loggedIn is null) { return CheckResult.Unknown(Id, Severity, "checks.claudeAuth.title", - "Login status could not be determined.", output); + loc?["checks.claudeAuth.undetermined"] ?? "Login status could not be determined.", output); } return loggedIn.Value - ? CheckResult.Ok(Id, Severity, "checks.claudeAuth.title", "Logged in.") - : CheckResult.Fail(Id, Severity, "checks.claudeAuth.title", "Not logged in.", "checks.claudeAuth.hint"); + ? CheckResult.Ok(Id, Severity, "checks.claudeAuth.title", loc?["checks.claudeAuth.loggedIn"] ?? "Logged in.") + : CheckResult.Fail(Id, Severity, "checks.claudeAuth.title", loc?["checks.claudeAuth.notLoggedIn"] ?? "Not logged in.", "checks.claudeAuth.hint"); } private static bool? TryParseLoggedIn(string output) diff --git a/src/ClaudeDo.Installer/Checks/ClaudeCliCheck.cs b/src/ClaudeDo.Installer/Checks/ClaudeCliCheck.cs index 9ba8f5a8..a2a40ef5 100644 --- a/src/ClaudeDo.Installer/Checks/ClaudeCliCheck.cs +++ b/src/ClaudeDo.Installer/Checks/ClaudeCliCheck.cs @@ -1,6 +1,7 @@ using System.IO; using ClaudeDo.Data.Environment; using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -21,20 +22,21 @@ public sealed class ClaudeCliCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var lookup = await _lookup.ResolveAsync(ctx, ct); if (lookup.Resolved is null) { var searched = string.Join(", ", SearchedLocations()); return CheckResult.Fail(Id, Severity, "checks.claudeCli.title", - $"'{ctx.ClaudeBin}' was not found on PATH. Searched: {searched}.", + loc?.Get("checks.claudeCli.notFound", ctx.ClaudeBin, searched) ?? $"'{ctx.ClaudeBin}' was not found on PATH. Searched: {searched}.", "checks.claudeCli.hint"); } if (lookup.ExitCode != 0) { return CheckResult.Fail(Id, Severity, "checks.claudeCli.title", - $"'{lookup.Resolved.Path}' exited with code {lookup.ExitCode}.", + loc?.Get("checks.claudeCli.exitCode", lookup.Resolved.Path, lookup.ExitCode) ?? $"'{lookup.Resolved.Path}' exited with code {lookup.ExitCode}.", "checks.claudeCli.hint", detail: lookup.Output); } @@ -42,7 +44,8 @@ public sealed class ClaudeCliCheck : IEnvironmentCheck var detail = lookup.Resolved.IsShim ? "Resolved via a shim (.cmd/.bat/.ps1), not a native .exe." : null; - return CheckResult.Ok(Id, Severity, "checks.claudeCli.title", $"{lookup.Resolved.Path} — {version}", detail); + return CheckResult.Ok(Id, Severity, "checks.claudeCli.title", + loc?.Get("checks.claudeCli.resolvedFormat", lookup.Resolved.Path, version) ?? $"{lookup.Resolved.Path} — {version}", detail); } private IEnumerable SearchedLocations() diff --git a/src/ClaudeDo.Installer/Checks/ClaudeVersionCheck.cs b/src/ClaudeDo.Installer/Checks/ClaudeVersionCheck.cs index 1d595dc8..77038510 100644 --- a/src/ClaudeDo.Installer/Checks/ClaudeVersionCheck.cs +++ b/src/ClaudeDo.Installer/Checks/ClaudeVersionCheck.cs @@ -1,4 +1,5 @@ using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -27,28 +28,29 @@ public sealed class ClaudeVersionCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var lookup = await _lookup.ResolveAsync(ctx, ct); if (lookup.Resolved is null) { return CheckResult.Unknown(Id, Severity, "checks.claudeVersion.title", - "The claude CLI was not found — version could not be checked."); + loc?["checks.claudeVersion.notFound"] ?? "The claude CLI was not found — version could not be checked."); } if (lookup.ExitCode != 0 || lookup.ParsedVersion is null) { return CheckResult.Unknown(Id, Severity, "checks.claudeVersion.title", - $"Could not parse a version from '{lookup.Output.Trim()}'."); + loc?.Get("checks.claudeVersion.parseFailed", lookup.Output.Trim()) ?? $"Could not parse a version from '{lookup.Output.Trim()}'."); } if (lookup.ParsedVersion < MinimumVersion) { return CheckResult.Fail(Id, Severity, "checks.claudeVersion.title", - $"claude {lookup.ParsedVersion} is older than the required {MinimumVersion}.", + loc?.Get("checks.claudeVersion.tooOld", lookup.ParsedVersion, MinimumVersion) ?? $"claude {lookup.ParsedVersion} is older than the required {MinimumVersion}.", "checks.claudeVersion.hint"); } return CheckResult.Ok(Id, Severity, "checks.claudeVersion.title", - $"claude {lookup.ParsedVersion} meets the minimum ({MinimumVersion})."); + loc?.Get("checks.claudeVersion.ok", lookup.ParsedVersion, MinimumVersion) ?? $"claude {lookup.ParsedVersion} meets the minimum ({MinimumVersion})."); } } diff --git a/src/ClaudeDo.Installer/Checks/GitCheck.cs b/src/ClaudeDo.Installer/Checks/GitCheck.cs index 71aa3994..a7093deb 100644 --- a/src/ClaudeDo.Installer/Checks/GitCheck.cs +++ b/src/ClaudeDo.Installer/Checks/GitCheck.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using ClaudeDo.Data.Environment; using ClaudeDo.Installer.Core; using ClaudeDo.Installer.Core.Interfaces; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -27,10 +28,12 @@ public sealed class GitCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var resolved = ExecutableResolver.Resolve("git", _pathOverride, _pathExtOverride); if (resolved is null) { - return CheckResult.Fail(Id, Severity, "checks.git.title", "git was not found on PATH.", + return CheckResult.Fail(Id, Severity, "checks.git.title", + loc?["checks.git.notFound"] ?? "git was not found on PATH.", "checks.git.hint", "https://git-scm.com/downloads"); } @@ -39,11 +42,13 @@ public sealed class GitCheck : IEnvironmentCheck if (exitCode != 0) { - return CheckResult.Fail(Id, Severity, "checks.git.title", $"'{resolved.Path}' exited with code {exitCode}.", + return CheckResult.Fail(Id, Severity, "checks.git.title", + loc?.Get("checks.git.exitCode", resolved.Path, exitCode) ?? $"'{resolved.Path}' exited with code {exitCode}.", "checks.git.hint", "https://git-scm.com/downloads", output); } var version = VersionPattern.Match(output) is { Success: true } match ? match.Value : output.Trim(); - return CheckResult.Ok(Id, Severity, "checks.git.title", $"{resolved.Path} — {version}"); + return CheckResult.Ok(Id, Severity, "checks.git.title", + loc?.Get("checks.git.resolvedFormat", resolved.Path, version) ?? $"{resolved.Path} — {version}"); } } diff --git a/src/ClaudeDo.Installer/Checks/GitIdentityCheck.cs b/src/ClaudeDo.Installer/Checks/GitIdentityCheck.cs index 8a4a5a07..c62d64bc 100644 --- a/src/ClaudeDo.Installer/Checks/GitIdentityCheck.cs +++ b/src/ClaudeDo.Installer/Checks/GitIdentityCheck.cs @@ -1,6 +1,7 @@ using ClaudeDo.Data.Environment; using ClaudeDo.Installer.Core; using ClaudeDo.Installer.Core.Interfaces; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -25,10 +26,12 @@ public sealed class GitIdentityCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var resolved = ExecutableResolver.Resolve("git", _pathOverride, _pathExtOverride); if (resolved is null) { - return CheckResult.Unknown(Id, Severity, "checks.gitIdentity.title", "git was not found — identity could not be checked."); + return CheckResult.Unknown(Id, Severity, "checks.gitIdentity.title", + loc?["checks.gitIdentity.notFound"] ?? "git was not found — identity could not be checked."); } var name = await ReadConfigAsync(resolved, "user.name", ct); @@ -48,7 +51,8 @@ public sealed class GitIdentityCheck : IEnvironmentCheck _ => "user.email", }; - return CheckResult.Fail(Id, Severity, "checks.gitIdentity.title", $"Missing git identity: {missing}.", "checks.gitIdentity.hint"); + return CheckResult.Fail(Id, Severity, "checks.gitIdentity.title", + loc?.Get("checks.gitIdentity.missing", missing) ?? $"Missing git identity: {missing}.", "checks.gitIdentity.hint"); } private async Task ReadConfigAsync(ResolvedExecutable resolved, string key, CancellationToken ct) diff --git a/src/ClaudeDo.Installer/Checks/PermissionModeAutoCheck.cs b/src/ClaudeDo.Installer/Checks/PermissionModeAutoCheck.cs index 72f2d5e7..559bbf1c 100644 --- a/src/ClaudeDo.Installer/Checks/PermissionModeAutoCheck.cs +++ b/src/ClaudeDo.Installer/Checks/PermissionModeAutoCheck.cs @@ -1,5 +1,6 @@ using System.Text.RegularExpressions; using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -28,11 +29,12 @@ public sealed class PermissionModeAutoCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var lookup = await _lookup.ResolveAsync(ctx, ct); if (lookup.Resolved is null) { return CheckResult.Unknown(Id, Severity, "checks.permissionModeAuto.title", - "The claude CLI was not found — permission-mode support could not be checked."); + loc?["checks.permissionModeAuto.notFound"] ?? "The claude CLI was not found — permission-mode support could not be checked."); } var (fileName, arguments) = ProcessCommand.For(lookup.Resolved, "--help"); @@ -41,17 +43,17 @@ public sealed class PermissionModeAutoCheck : IEnvironmentCheck if (exitCode != 0 || string.IsNullOrWhiteSpace(output)) { return CheckResult.Unknown(Id, Severity, "checks.permissionModeAuto.title", - "Could not determine whether --permission-mode auto is supported."); + loc?["checks.permissionModeAuto.undetermined"] ?? "Could not determine whether --permission-mode auto is supported."); } if (AutoIsListed(output)) { return CheckResult.Ok(Id, Severity, "checks.permissionModeAuto.title", - "--permission-mode auto is recognized by this CLI."); + loc?["checks.permissionModeAuto.ok"] ?? "--permission-mode auto is recognized by this CLI."); } return CheckResult.Fail(Id, Severity, "checks.permissionModeAuto.title", - "This claude CLI version does not list 'auto' as a --permission-mode choice.", + loc?["checks.permissionModeAuto.notListed"] ?? "This claude CLI version does not list 'auto' as a --permission-mode choice.", "checks.permissionModeAuto.hint"); } diff --git a/src/ClaudeDo.Installer/Checks/PortCheck.cs b/src/ClaudeDo.Installer/Checks/PortCheck.cs index 07f1ade1..932f0fb0 100644 --- a/src/ClaudeDo.Installer/Checks/PortCheck.cs +++ b/src/ClaudeDo.Installer/Checks/PortCheck.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Sockets; using ClaudeDo.Installer.Checks.Interfaces; using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -19,6 +20,7 @@ public sealed class PortCheck : IEnvironmentCheck public async Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; var ports = new (string Label, int Port)[] { ("SignalR", ctx.SignalRPort), @@ -39,7 +41,9 @@ public sealed class PortCheck : IEnvironmentCheck continue; } - blocked.Add(owner is null ? $"{label} port {port} is in use." : $"{label} port {port} is in use by '{owner}'."); + blocked.Add(owner is null + ? loc?.Get("checks.ports.inUse", label, port) ?? $"{label} port {port} is in use." + : loc?.Get("checks.ports.inUseBy", label, port, owner) ?? $"{label} port {port} is in use by '{owner}'."); } if (blocked.Count > 0) @@ -49,10 +53,12 @@ public sealed class PortCheck : IEnvironmentCheck if (ownWorker.Count > 0) { - return CheckResult.Ok(Id, Severity, "checks.ports.title", $"In use by the running ClaudeDo Worker: {string.Join(", ", ownWorker)}."); + var ownWorkerList = string.Join(", ", ownWorker); + return CheckResult.Ok(Id, Severity, "checks.ports.title", + loc?.Get("checks.ports.inUseByOwnWorker", ownWorkerList) ?? $"In use by the running ClaudeDo Worker: {ownWorkerList}."); } - return CheckResult.Ok(Id, Severity, "checks.ports.title", "Ports are available."); + return CheckResult.Ok(Id, Severity, "checks.ports.title", loc?["checks.ports.available"] ?? "Ports are available."); } private static bool IsFree(int port) diff --git a/src/ClaudeDo.Installer/Checks/WriteAccessCheck.cs b/src/ClaudeDo.Installer/Checks/WriteAccessCheck.cs index fe396c2e..9480233b 100644 --- a/src/ClaudeDo.Installer/Checks/WriteAccessCheck.cs +++ b/src/ClaudeDo.Installer/Checks/WriteAccessCheck.cs @@ -1,6 +1,7 @@ using System.IO; using ClaudeDo.Data; using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Localization; namespace ClaudeDo.Installer.Checks; @@ -14,17 +15,19 @@ public sealed class WriteAccessCheck : IEnvironmentCheck public Task RunAsync(InstallContext ctx, CancellationToken ct) { + var loc = TrExtension.Localizer; foreach (var target in new[] { ctx.InstallDirectory, Paths.AppDataRoot() }) { var error = TryWrite(target); if (error is not null) { return Task.FromResult(CheckResult.Fail(Id, Severity, "checks.writeAccess.title", - $"Cannot write to '{target}': {error}", "checks.writeAccess.hint")); + loc?.Get("checks.writeAccess.cannotWrite", target, error) ?? $"Cannot write to '{target}': {error}", "checks.writeAccess.hint")); } } - return Task.FromResult(CheckResult.Ok(Id, Severity, "checks.writeAccess.title", "Install directory and data directory are writable.")); + return Task.FromResult(CheckResult.Ok(Id, Severity, "checks.writeAccess.title", + loc?["checks.writeAccess.writable"] ?? "Install directory and data directory are writable.")); } private static string? TryWrite(string path) diff --git a/src/ClaudeDo.Localization/locales/de.json b/src/ClaudeDo.Localization/locales/de.json index 2d079d85..ff291c64 100644 --- a/src/ClaudeDo.Localization/locales/de.json +++ b/src/ClaudeDo.Localization/locales/de.json @@ -38,11 +38,11 @@ "maxParallelExecutions": "Max. parallele Ausführungen", "maxParallelExecutionsHint": "Wie viele Aufgaben aus der Warteschlange der Worker gleichzeitig ausführt.", "usageGateHeading": "Usage-Limit-Stopp", - "usageGateHint": "Ab dieser Auslastung holt die Queue keine neuen Tasks mehr; laufende Tasks laufen zu Ende. 0 schaltet den Stopp ab.", + "usageGateHint": "Ab dieser Auslastung holt die Warteschlange keine neuen Aufgaben mehr; laufende Aufgaben laufen zu Ende. 0 schaltet den Stopp ab.", "usageGateFiveHourPct": "5-Stunden-Fenster (%)", "usageGateSevenDayPct": "7-Tage-Fenster (%)", "autoContinueOnUsageLimit": "Nach Limit-Reset fortsetzen", - "autoContinueOnUsageLimitHint": "Ist ein Run am Nutzungslimit abgebrochen, wird er nach Ablauf des 5-Stunden-Fensters automatisch fortgesetzt. Weckt außerdem die pausierte Queue genau zum Reset-Zeitpunkt statt erst beim nächsten Backstop-Check.", + "autoContinueOnUsageLimitHint": "Ist ein Run am Nutzungslimit abgebrochen, wird er nach Ablauf des 5-Stunden-Fensters automatisch fortgesetzt. Weckt außerdem die pausierte Warteschlange genau zum Reset-Zeitpunkt statt erst beim nächsten Backstop-Check.", "listHandlerMaxHandoffs": "Handoff-Budget des List-Handlers", "listHandlerMaxHandoffsHint": "Wie viele Wait/Merge-Handoffs ein „Let Claude handle it\"-Run verketten darf. 4 entspricht zwei Merge-Runden. Für Läufe, die stundenlang durchlaufen sollen, höher setzen; der Wert begrenzt eine entlaufene Kette und darf daher nicht 0 sein.", "powerUseHeading": "PowerUse", @@ -54,7 +54,7 @@ "powerUseWindowHint": "Beide Zeiten leer = rund um die Uhr. Das Fenster wird bei jedem Start einer Handler-Phase geprüft — eine Kette, die über das Ende hinausläuft, macht ohne PowerUse weiter.", "powerUseWindowInvalid": "PowerUse-Zeiten müssen im Format HH:mm sein oder leer bleiben.", "throttleStagesHeading": "Throttle-Stufen", - "throttleStagesHint": "Die Parallelität wird vor dem harten Gate begrenzt: soft auf 2 Slots, hard auf 1. Eingestellt wird das durch Ziehen der Marker an den Gauges im Usage-Monitor. Der Monitor schließt diese Einstellungen — vorher speichern, wenn du etwas geändert hast.", + "throttleStagesHint": "Die Parallelität wird vor dem harten Limit-Stopp begrenzt: soft auf 2 Slots, hard auf 1. Eingestellt wird das durch Ziehen der Marker an den Gauges im Usage-Monitor. Der Monitor schließt diese Einstellungen — vorher speichern, wenn du etwas geändert hast.", "throttleStagesValues": "5h: soft {0}% / hard {1}% · 7d: soft {2}% / hard {3}%", "openUsageMonitor": "Usage-Monitor öffnen...", "reportExcludedPaths": "Bericht: ausgeschlossene Pfade (einer pro Zeile)", @@ -236,7 +236,7 @@ "reasonIsPlanningParent": "Teil eines Plans", "reasonIsChildTask": "Teilaufgaben haben keine eigene Planungssitzung", "reasonPlanAlreadyFinalized": "Plan ist bereits finalisiert", - "reasonNoWorktree": "Kein Worktree für diesen Task", + "reasonNoWorktree": "Kein Worktree für diese Aufgabe", "cancel": "Abbrechen", "removeFromQueueTip": "Aus Warteschlange entfernen", "toggleSubtasksTip": "Unteraufgaben ein-/ausklappen", @@ -268,13 +268,13 @@ "contextRemoveFromFolder": "Aus Ordner herausnehmen", "newList": "Neue Liste", "addReposTip": "Repos als Listen hinzufügen", - "noRepoBannerText": "Noch kein Repo verknüpft. ClaudeDo führt Tasks in einem Git-Repo aus.", + "noRepoBannerText": "Noch kein Repo verknüpft. ClaudeDo führt Aufgaben in einem Git-Repo aus.", "noRepoBannerButton": "Repo importieren", "importTicketsResult": "{0} Tickets geprüft, {1} neue Tasks." }, "details": { "deleteTaskTip": "Aufgabe löschen", - "killSessionTip": "Laufende Sitzung beenden", + "killSessionTip": "Laufende Sitzung hart beenden", "sendToQueue": "In Warteschlange einreihen", "sendToQueueTip": "Diese Aufgabe in die Warteschlange einreihen, damit Claude sie beim nächsten Lauf übernimmt", "copyTaskIdTip": "Aufgaben-ID kopieren", @@ -379,7 +379,7 @@ "browseForFolder": "Ordner wählen…", "focusMode": "Fokus", "overviewMode": "Übersicht", - "closeSession": "Sitzung beenden", + "closeSession": "Sitzung hart beenden", "conptyLaunchFailed": "Interaktive Sitzung konnte nicht geöffnet werden: {0}", "conptyStarting": "Sitzung wird gestartet…", "conptyExited": "Sitzung beendet (Code {0}).", @@ -402,7 +402,7 @@ "queueRefreshFailed": "Warteschlange konnte nicht aktualisiert werden: {0}", "abandonRun": "Lauf abbrechen", "abandonRunTip": "Die noch offenen Tasks dieses Laufs abbrechen und die Sitzung schließen", - "abandonRunConfirm": "{0} offene(n) Task(s) abbrechen und diese Sitzung schließen? Tasks im Review oder bereits erledigt bleiben unangetastet.", + "abandonRunConfirm": "{0} offene Aufgabe(n) abbrechen und diese Sitzung schließen? Aufgaben im Review oder bereits erledigt bleiben unangetastet.", "abandonRunFailed": "Offene Tasks konnten nicht abgebrochen werden: {0}", "question": { "title": "Claude fragt nach", @@ -578,14 +578,14 @@ "noGauges": "Noch keine Nutzungslimits gemeldet.", "refreshHint": "Jetzt aktualisieren — Abruf alle 5 Min., während ein Task läuft, sonst alle 15 Min.", "staleFormat": "Werte veraltet (Stand {0})", - "staleGateHint": "Das Gate greift in diesem Zustand nicht.", + "staleGateHint": "Der Limit-Stopp greift in diesem Zustand nicht.", "gateBlockedFormat": "Warteschlange pausiert — {0}", "throttleFormat": "Warteschlange gedrosselt: {0}/{1} Slots ({2})", "resetIn": "Reset in {0} ({1})", "dragHint": "Marker ziehen oder Wert unten eintragen.", "legendSoft": "Soft · 2 Slots", "legendHard": "Hard · 1 Slot", - "legendGate": "Gate · Pause", + "legendGate": "Limit-Stopp · Pause", "gaugeSession": "Session (5 Std.)", "gaugeWeeklyAll": "Woche (alle Modelle)", "gaugeWeeklyScopedFormat": "Woche ({0})", @@ -605,7 +605,7 @@ "columnOtherOut": "ÜBRIGES OUT", "columnOtherCache": "ÜBRIGES CACHE", "columnShare": "ANTEIL", - "columnTask": "TASK", + "columnTask": "AUFGABE", "columnList": "LISTE", "columnModelShort": "MODELL", "columnRuns": "RUNS", @@ -709,35 +709,61 @@ "checks": { "git": { "title": "Git", - "hint": "Installiere Git von https://git-scm.com/downloads und starte den Installer neu." + "hint": "Installiere Git von https://git-scm.com/downloads und starte den Installer neu.", + "notFound": "git wurde nicht im PATH gefunden.", + "exitCode": "'{0}' wurde mit Exitcode {1} beendet.", + "resolvedFormat": "{0} — {1}" }, "gitIdentity": { "title": "Git-Identität", - "hint": "Führe aus: git config --global user.name \"Dein Name\" und git config --global user.email \"du@example.com\"" + "hint": "Führe aus: git config --global user.name \"Dein Name\" und git config --global user.email \"du@example.com\"", + "notFound": "git wurde nicht gefunden — Identität konnte nicht geprüft werden.", + "missing": "Fehlende Git-Identität: {0}." }, "ports": { "title": "Ports", - "hint": "Ändere die SignalR-/MCP-Ports auf der Dienste-Seite, oder beende den blockierenden Prozess." + "hint": "Ändere die SignalR-/MCP-Ports auf der Dienste-Seite, oder beende den blockierenden Prozess.", + "inUse": "{0}-Port {1} ist belegt.", + "inUseBy": "{0}-Port {1} ist belegt von '{2}'.", + "inUseByOwnWorker": "Belegt vom laufenden ClaudeDo Worker: {0}.", + "available": "Ports sind verfügbar." }, "writeAccess": { "title": "Schreibzugriff", - "hint": "Wähle ein anderes Installationsverzeichnis oder starte den Installer mit ausreichenden Rechten (z. B. als Administrator)." + "hint": "Wähle ein anderes Installationsverzeichnis oder starte den Installer mit ausreichenden Rechten (z. B. als Administrator).", + "cannotWrite": "Kein Schreibzugriff auf '{0}': {1}", + "writable": "Installations- und Datenverzeichnis sind beschreibbar." }, "claudeCli": { "title": "Claude CLI", - "hint": "Installiere die Claude CLI (npm install -g @anthropic-ai/claude-code) und starte danach dein Terminal/deine Anmeldesitzung neu, damit der aktualisierte PATH greift." + "hint": "Installiere die Claude CLI (npm install -g @anthropic-ai/claude-code) und starte danach dein Terminal/deine Anmeldesitzung neu, damit der aktualisierte PATH greift.", + "notFound": "'{0}' wurde nicht im PATH gefunden. Durchsucht: {1}.", + "exitCode": "'{0}' wurde mit Exitcode {1} beendet.", + "resolvedFormat": "{0} — {1}" }, "claudeVersion": { "title": "Claude-CLI-Version", - "hint": "Aktualisiere die CLI: npm update -g @anthropic-ai/claude-code" + "hint": "Aktualisiere die CLI: npm update -g @anthropic-ai/claude-code", + "notFound": "Die claude-CLI wurde nicht gefunden — Version konnte nicht geprüft werden.", + "parseFailed": "Konnte keine Version aus '{0}' auslesen.", + "tooOld": "claude {0} ist älter als die erforderliche Version {1}.", + "ok": "claude {0} erfüllt die Mindestanforderung ({1})." }, "claudeAuth": { "title": "Claude-CLI-Login", - "hint": "Starte 'claude' einmal und schließe den Login-Vorgang ab." + "hint": "Starte 'claude' einmal und schließe den Login-Vorgang ab.", + "notFound": "Die claude-CLI wurde nicht gefunden — Login konnte nicht geprüft werden.", + "undetermined": "Login-Status konnte nicht ermittelt werden.", + "notLoggedIn": "Nicht angemeldet.", + "loggedIn": "Angemeldet." }, "permissionModeAuto": { "title": "Berechtigungsmodus", - "hint": "Aktualisiere die CLI: npm update -g @anthropic-ai/claude-code. ClaudeDo funktioniert auch im Modus acceptEdits/default, dann mit mehr Rückfragen." + "hint": "Aktualisiere die CLI: npm update -g @anthropic-ai/claude-code. ClaudeDo funktioniert auch im Modus acceptEdits/default, dann mit mehr Rückfragen.", + "notFound": "Die claude-CLI wurde nicht gefunden — Unterstützung für permission-mode konnte nicht geprüft werden.", + "undetermined": "Konnte nicht ermitteln, ob --permission-mode auto unterstützt wird.", + "ok": "--permission-mode auto wird von dieser CLI erkannt.", + "notListed": "Diese claude-CLI-Version listet 'auto' nicht als --permission-mode-Option." } }, "planning": { @@ -846,7 +872,7 @@ "mergeSection": { "openWorktreeFailed": "Worktree konnte nicht geöffnet werden: {0}" }, "about": { "openPathFailed": "Pfad konnte nicht geöffnet werden: {0}" }, "lists": { "localSuffix": "{0} / lokal", "smartMyDay": "Mein Tag", "smartImportant": "Wichtig", "smartPlanned": "Geplant", "smartNotes": "Notizen", "virtualQueue": "Warteschlange", "virtualRunning": "Läuft", "virtualReview": "Prüfung", "newList": "Neue Liste", "findingsNotFound": "Für diese Liste gibt es noch keine Findings.", "findingsOpenFailed": "Findings-Ordner konnte nicht geöffnet werden: {0}", "openInExplorerFailed": "Pfad konnte nicht geöffnet werden: {0}" }, - "repoImport": { "loadFailed": "Gespeicherte Ordner konnten nicht geladen werden: {0}", "saveFailed": "Ordner konnten nicht gespeichert werden: {0}" } + "repoImport": { "loadFailed": "Gespeicherte Ordner konnten nicht geladen werden: {0}", "saveFailed": "Ordner konnten nicht gespeichert werden: {0}", "createButton": "{0} Liste(n) erstellen" } }, "ops": { "stalledHint": "Läuft weiter — Worker antwortet noch", diff --git a/src/ClaudeDo.Localization/locales/en.json b/src/ClaudeDo.Localization/locales/en.json index 8f71e486..a3882fb3 100644 --- a/src/ClaudeDo.Localization/locales/en.json +++ b/src/ClaudeDo.Localization/locales/en.json @@ -709,35 +709,61 @@ "checks": { "git": { "title": "Git", - "hint": "Install Git from https://git-scm.com/downloads, then restart the installer." + "hint": "Install Git from https://git-scm.com/downloads, then restart the installer.", + "notFound": "git was not found on PATH.", + "exitCode": "'{0}' exited with code {1}.", + "resolvedFormat": "{0} — {1}" }, "gitIdentity": { "title": "Git Identity", - "hint": "Run: git config --global user.name \"Your Name\" and git config --global user.email \"you@example.com\"" + "hint": "Run: git config --global user.name \"Your Name\" and git config --global user.email \"you@example.com\"", + "notFound": "git was not found — identity could not be checked.", + "missing": "Missing git identity: {0}." }, "ports": { "title": "Ports", - "hint": "Change the SignalR/MCP ports on the Service page, or stop the process using them." + "hint": "Change the SignalR/MCP ports on the Service page, or stop the process using them.", + "inUse": "{0} port {1} is in use.", + "inUseBy": "{0} port {1} is in use by '{2}'.", + "inUseByOwnWorker": "In use by the running ClaudeDo Worker: {0}.", + "available": "Ports are available." }, "writeAccess": { "title": "Write Access", - "hint": "Choose a different install directory, or run the installer with sufficient permissions (e.g. as administrator)." + "hint": "Choose a different install directory, or run the installer with sufficient permissions (e.g. as administrator).", + "cannotWrite": "Cannot write to '{0}': {1}", + "writable": "Install directory and data directory are writable." }, "claudeCli": { "title": "Claude CLI", - "hint": "Install the Claude CLI (npm install -g @anthropic-ai/claude-code), then restart your terminal/login session so the updated PATH takes effect." + "hint": "Install the Claude CLI (npm install -g @anthropic-ai/claude-code), then restart your terminal/login session so the updated PATH takes effect.", + "notFound": "'{0}' was not found on PATH. Searched: {1}.", + "exitCode": "'{0}' exited with code {1}.", + "resolvedFormat": "{0} — {1}" }, "claudeVersion": { "title": "Claude CLI Version", - "hint": "Update the CLI: npm update -g @anthropic-ai/claude-code" + "hint": "Update the CLI: npm update -g @anthropic-ai/claude-code", + "notFound": "The claude CLI was not found — version could not be checked.", + "parseFailed": "Could not parse a version from '{0}'.", + "tooOld": "claude {0} is older than the required {1}.", + "ok": "claude {0} meets the minimum ({1})." }, "claudeAuth": { "title": "Claude CLI Login", - "hint": "Run 'claude' once and complete the login flow." + "hint": "Run 'claude' once and complete the login flow.", + "notFound": "The claude CLI was not found — login could not be checked.", + "undetermined": "Login status could not be determined.", + "notLoggedIn": "Not logged in.", + "loggedIn": "Logged in." }, "permissionModeAuto": { "title": "Permission Mode", - "hint": "Update the CLI: npm update -g @anthropic-ai/claude-code. ClaudeDo still works in acceptEdits/default mode, with more prompts." + "hint": "Update the CLI: npm update -g @anthropic-ai/claude-code. ClaudeDo still works in acceptEdits/default mode, with more prompts.", + "notFound": "The claude CLI was not found — permission-mode support could not be checked.", + "undetermined": "Could not determine whether --permission-mode auto is supported.", + "ok": "--permission-mode auto is recognized by this CLI.", + "notListed": "This claude CLI version does not list 'auto' as a --permission-mode choice." } }, "planning": { @@ -846,7 +872,7 @@ "mergeSection": { "openWorktreeFailed": "Couldn't open worktree: {0}" }, "about": { "openPathFailed": "Couldn't open path: {0}" }, "lists": { "localSuffix": "{0} / local", "smartMyDay": "My Day", "smartImportant": "Important", "smartPlanned": "Planned", "smartNotes": "Notes", "virtualQueue": "Queue", "virtualRunning": "Running", "virtualReview": "Review", "newList": "New list", "findingsNotFound": "No findings yet for this list.", "findingsOpenFailed": "Couldn't open findings folder: {0}", "openInExplorerFailed": "Couldn't open path: {0}" }, - "repoImport": { "loadFailed": "Couldn't load remembered folders: {0}", "saveFailed": "Couldn't save folders: {0}" } + "repoImport": { "loadFailed": "Couldn't load remembered folders: {0}", "saveFailed": "Couldn't save folders: {0}", "createButton": "Create {0} list(s)" } }, "ops": { "stalledHint": "Still running — worker hasn't responded in a while", diff --git a/src/ClaudeDo.Ui/ViewModels/Modals/RepoImportModalViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Modals/RepoImportModalViewModel.cs index c674c8b1..15e3ca79 100644 --- a/src/ClaudeDo.Ui/ViewModels/Modals/RepoImportModalViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Modals/RepoImportModalViewModel.cs @@ -44,7 +44,7 @@ public sealed partial class RepoImportModalViewModel : ViewModelBase public int CreateCount => Repos.Count(r => r.IsChecked && !r.AlreadyAdded); public bool CanCreate => CreateCount > 0; - public string CreateButtonText => $"Create {CreateCount} list(s)"; + public string CreateButtonText => Loc.T("vm.repoImport.createButton", CreateCount); public bool HasFolders => _folders.Count > 0; public RepoImportModalViewModel(IDbContextFactory dbFactory, IWorkerClient workerClient)