feat(i18n): localize ViewModel-built strings via ambient Loc accessor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ClaudeDo.Data.Git;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
|
||||
namespace ClaudeDo.Ui.ViewModels.Modals;
|
||||
|
||||
@@ -91,13 +92,13 @@ public sealed partial class DiffModalViewModel : ViewModelBase
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"Failed to load diff: {ex.Message}";
|
||||
StatusMessage = Loc.T("vm.diff.loadFailed", ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
{
|
||||
StatusMessage = "No changes to show.";
|
||||
StatusMessage = Loc.T("vm.diff.noChanges");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,7 +170,7 @@ public sealed partial class DiffModalViewModel : ViewModelBase
|
||||
}
|
||||
|
||||
SelectedFile = Files.Count > 0 ? Files[0] : null;
|
||||
if (Files.Count == 0) StatusMessage = "No changes to show.";
|
||||
if (Files.Count == 0) StatusMessage = Loc.T("vm.diff.noChanges");
|
||||
}
|
||||
|
||||
private static void ParseHunkHeader(string header, out int oldStart, out int newStart)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.ObjectModel;
|
||||
using ClaudeDo.Data;
|
||||
using ClaudeDo.Data.Models;
|
||||
using ClaudeDo.Data.Repositories;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -80,7 +81,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
|
||||
await _worker.UpdateListAsync(new UpdateListDto(
|
||||
ListId,
|
||||
string.IsNullOrWhiteSpace(Name) ? "Untitled" : Name,
|
||||
string.IsNullOrWhiteSpace(Name) ? Loc.T("vm.listSettings.untitled") : Name,
|
||||
string.IsNullOrWhiteSpace(WorkingDir) ? null : WorkingDir,
|
||||
DefaultCommitType));
|
||||
|
||||
@@ -93,7 +94,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
[RelayCommand]
|
||||
private async Task DeleteAsync()
|
||||
{
|
||||
var displayName = string.IsNullOrWhiteSpace(Name) ? "Untitled" : Name;
|
||||
var displayName = string.IsNullOrWhiteSpace(Name) ? Loc.T("vm.listSettings.untitled") : Name;
|
||||
if (ConfirmAsync is not null)
|
||||
{
|
||||
var ok = await ConfirmAsync($"Delete list \"{displayName}\" and all its tasks? This cannot be undone.");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -36,7 +37,7 @@ public sealed partial class MergeModalViewModel : ViewModelBase
|
||||
{
|
||||
TaskId = taskId;
|
||||
TaskTitle = taskTitle;
|
||||
CommitMessage = $"Merge task: {taskTitle}";
|
||||
CommitMessage = Loc.T("vm.merge.commitMessage", taskTitle);
|
||||
|
||||
IsBusy = true;
|
||||
try
|
||||
@@ -45,7 +46,7 @@ public sealed partial class MergeModalViewModel : ViewModelBase
|
||||
Branches.Clear();
|
||||
if (targets is null)
|
||||
{
|
||||
ErrorMessage = "Worker offline — cannot list branches.";
|
||||
ErrorMessage = Loc.T("vm.merge.workerOfflineBranches");
|
||||
return;
|
||||
}
|
||||
foreach (var b in targets.LocalBranches) Branches.Add(b);
|
||||
@@ -55,7 +56,7 @@ public sealed partial class MergeModalViewModel : ViewModelBase
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Failed to load branches: {ex.Message}";
|
||||
ErrorMessage = Loc.T("vm.merge.loadBranchesFailed", ex.Message);
|
||||
}
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
@@ -81,7 +82,7 @@ public sealed partial class MergeModalViewModel : ViewModelBase
|
||||
case "merged":
|
||||
SuccessMessage = result.ErrorMessage is not null
|
||||
? $"Merged with warning: {result.ErrorMessage}"
|
||||
: "Merged.";
|
||||
: Loc.T("vm.merge.merged");
|
||||
// Auto-close after a short delay.
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
@@ -92,19 +93,19 @@ public sealed partial class MergeModalViewModel : ViewModelBase
|
||||
case "conflict":
|
||||
HasConflict = true;
|
||||
ConflictFiles = result.ConflictFiles;
|
||||
ErrorMessage = "Merge conflict — target branch restored. Resolve manually or via Continue, then retry.";
|
||||
ErrorMessage = Loc.T("vm.merge.conflict");
|
||||
break;
|
||||
case "blocked":
|
||||
ErrorMessage = $"Blocked: {result.ErrorMessage}";
|
||||
ErrorMessage = Loc.T("vm.merge.blocked", result.ErrorMessage ?? "");
|
||||
break;
|
||||
default:
|
||||
ErrorMessage = $"Unknown status: {result.Status}";
|
||||
ErrorMessage = Loc.T("vm.merge.unknownStatus", result.Status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Merge failed: {ex.Message}";
|
||||
ErrorMessage = Loc.T("vm.merge.mergeFailed", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using ClaudeDo.Data;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -26,13 +27,13 @@ public sealed partial class FilesSettingsTabViewModel : ViewModelBase
|
||||
try
|
||||
{
|
||||
var r = await _worker.RestoreDefaultAgentsAsync();
|
||||
if (r is null) StatusMessage = "Worker offline.";
|
||||
else if (r.Copied == 0 && r.Skipped == 0) StatusMessage = "No default agents bundled.";
|
||||
else if (r.Copied == 0) StatusMessage = "All default agents already present.";
|
||||
else StatusMessage = $"Restored {r.Copied} default agent(s).";
|
||||
if (r is null) StatusMessage = Loc.T("vm.filesTab.workerOffline");
|
||||
else if (r.Copied == 0 && r.Skipped == 0) StatusMessage = Loc.T("vm.filesTab.noneBundled");
|
||||
else if (r.Copied == 0) StatusMessage = Loc.T("vm.filesTab.allPresent");
|
||||
else StatusMessage = Loc.T("vm.filesTab.restored", r.Copied);
|
||||
await _worker.RefreshAgentsAsync();
|
||||
}
|
||||
catch (Exception ex) { StatusMessage = $"Restore failed: {ex.Message}"; }
|
||||
catch (Exception ex) { StatusMessage = Loc.T("vm.filesTab.restoreFailed", ex.Message); }
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
|
||||
@@ -46,6 +47,6 @@ public sealed partial class FilesSettingsTabViewModel : ViewModelBase
|
||||
var path = PromptFiles.PathFor(kind);
|
||||
Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex) { StatusMessage = $"Open failed: {ex.Message}"; }
|
||||
catch (Exception ex) { StatusMessage = Loc.T("vm.filesTab.openFailed", ex.Message); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -43,7 +44,7 @@ public sealed partial class WorktreesSettingsTabViewModel : ViewModelBase
|
||||
try
|
||||
{
|
||||
var r = await _worker.CleanupFinishedWorktreesAsync();
|
||||
StatusMessage = r is null ? "Worker offline." : $"Removed {r.Removed} worktree(s).";
|
||||
StatusMessage = r is null ? Loc.T("vm.worktreesTab.workerOffline") : Loc.T("vm.worktreesTab.removed", r.Removed);
|
||||
}
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
@@ -58,9 +59,9 @@ public sealed partial class WorktreesSettingsTabViewModel : ViewModelBase
|
||||
try
|
||||
{
|
||||
var r = await _worker.ResetAllWorktreesAsync();
|
||||
if (r is null) StatusMessage = "Worker offline.";
|
||||
else if (r.Blocked) StatusMessage = $"Cannot force-remove: {r.RunningTasks} task(s) still running. Cancel them first.";
|
||||
else StatusMessage = $"Removed {r.Removed} worktree(s) from {r.TasksAffected} task(s).";
|
||||
if (r is null) StatusMessage = Loc.T("vm.worktreesTab.workerOffline");
|
||||
else if (r.Blocked) StatusMessage = Loc.T("vm.worktreesTab.blocked", r.RunningTasks);
|
||||
else StatusMessage = Loc.T("vm.worktreesTab.removedFrom", r.Removed, r.TasksAffected);
|
||||
}
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using ClaudeDo.Data;
|
||||
using ClaudeDo.Localization;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using ClaudeDo.Ui.ViewModels.Modals.Settings;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
@@ -60,7 +61,7 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new());
|
||||
General.StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday;
|
||||
}
|
||||
else StatusMessage = "Worker offline — settings read-only.";
|
||||
else StatusMessage = Loc.T("vm.settingsModal.workerOffline");
|
||||
|
||||
await Prime.LoadAsync();
|
||||
}
|
||||
@@ -95,7 +96,7 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
await Prime.SaveAsync();
|
||||
CloseAction?.Invoke();
|
||||
}
|
||||
catch (Exception ex) { StatusMessage = $"Save failed: {ex.Message}"; }
|
||||
catch (Exception ex) { StatusMessage = Loc.T("vm.settingsModal.saveFailed", ex.Message); }
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -72,16 +73,16 @@ public sealed partial class WeeklyReportModalViewModel : ViewModelBase
|
||||
[RelayCommand(CanExecute = nameof(CanGenerate))]
|
||||
private async Task Generate()
|
||||
{
|
||||
if (!RangeValid) { StatusMessage = "Invalid date range."; return; }
|
||||
if (!RangeValid) { StatusMessage = Loc.T("vm.weeklyReport.invalidRange"); return; }
|
||||
IsBusy = true;
|
||||
StatusMessage = "Generating report…";
|
||||
StatusMessage = Loc.T("vm.weeklyReport.generating");
|
||||
try
|
||||
{
|
||||
ReportMarkdown = await _worker.GenerateWeekReportAsync(
|
||||
DateOnly.FromDateTime(StartDate!.Value), DateOnly.FromDateTime(EndDate!.Value));
|
||||
StatusMessage = "";
|
||||
}
|
||||
catch (Exception ex) { StatusMessage = $"Error: {ex.Message}"; }
|
||||
catch (Exception ex) { StatusMessage = Loc.T("vm.weeklyReport.error", ex.Message); }
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input.Platform;
|
||||
using ClaudeDo.Data.Models;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -86,7 +87,9 @@ public sealed partial class WorktreesOverviewModalViewModel : ViewModelBase
|
||||
{
|
||||
ListIdFilter = listId;
|
||||
IsGlobal = listId is null;
|
||||
Title = listId is null ? "Worktrees" : $"Worktrees — {listName ?? "list"}";
|
||||
Title = listId is null
|
||||
? Loc.T("vm.worktreesOverview.titleAll")
|
||||
: Loc.T("vm.worktreesOverview.titleList", listName ?? Loc.T("vm.worktreesOverview.listFallback"));
|
||||
}
|
||||
|
||||
public async Task LoadAsync(CancellationToken ct = default)
|
||||
@@ -138,7 +141,7 @@ public sealed partial class WorktreesOverviewModalViewModel : ViewModelBase
|
||||
try
|
||||
{
|
||||
var result = await _worker.CleanupFinishedWorktreesAsync(ListIdFilter);
|
||||
StatusMessage = result is null ? "Cleanup failed." : $"Removed {result.Removed} worktree(s).";
|
||||
StatusMessage = result is null ? Loc.T("vm.worktreesOverview.cleanupFailed") : Loc.T("vm.worktreesOverview.removed", result.Removed);
|
||||
await LoadAsync();
|
||||
}
|
||||
finally { IsBusy = false; }
|
||||
@@ -190,7 +193,7 @@ public sealed partial class WorktreesOverviewModalViewModel : ViewModelBase
|
||||
if (row is null || row.State != WorktreeState.Active) return;
|
||||
var (ok, err) = await _worker.SetWorktreeStateAsync(row.TaskId, WorktreeState.Discarded);
|
||||
if (ok) row.State = WorktreeState.Discarded;
|
||||
else StatusMessage = err ?? "Failed to discard worktree.";
|
||||
else StatusMessage = err ?? Loc.T("vm.worktreesOverview.discardFailed");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -199,20 +202,20 @@ public sealed partial class WorktreesOverviewModalViewModel : ViewModelBase
|
||||
if (row is null || row.State != WorktreeState.Active) return;
|
||||
var (ok, err) = await _worker.SetWorktreeStateAsync(row.TaskId, WorktreeState.Kept);
|
||||
if (ok) row.State = WorktreeState.Kept;
|
||||
else StatusMessage = err ?? "Failed to keep worktree.";
|
||||
else StatusMessage = err ?? Loc.T("vm.worktreesOverview.keepFailed");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ForceRemove(WorktreeOverviewRowViewModel? row)
|
||||
{
|
||||
if (row is null) return;
|
||||
if (row.IsRunning) { StatusMessage = "Cannot force-remove a running task."; return; }
|
||||
if (row.IsRunning) { StatusMessage = Loc.T("vm.worktreesOverview.cannotForceRunning"); return; }
|
||||
if (ConfirmAction is not null && !await ConfirmAction($"Force remove worktree for '{row.TaskTitle}'? This deletes the directory and branch.")) return;
|
||||
|
||||
var result = await _worker.ForceRemoveWorktreeAsync(row.TaskId);
|
||||
if (result is null || !result.Removed)
|
||||
{
|
||||
StatusMessage = result?.Reason ?? "Force remove failed.";
|
||||
StatusMessage = result?.Reason ?? Loc.T("vm.worktreesOverview.forceRemoveFailed");
|
||||
return;
|
||||
}
|
||||
if (IsGlobal)
|
||||
|
||||
Reference in New Issue
Block a user