Complete client UI i18n migration
Adds client-side i18n support, language settings, translated UI copy, and locale parity coverage.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { confirmDialog } from "@/utils/confirm-dialog";
|
||||
import { i18n } from "@/i18n/i18next";
|
||||
|
||||
export interface WorktreeArchiveRisk {
|
||||
isDirty?: boolean | null;
|
||||
@@ -10,27 +11,61 @@ export interface WorktreeArchiveConfirmationInput extends WorktreeArchiveRisk {
|
||||
worktreeName: string;
|
||||
}
|
||||
|
||||
function pluralize(count: number, singular: string, plural = `${singular}s`): string {
|
||||
return count === 1 ? singular : plural;
|
||||
export interface WorktreeArchiveWarningLabels {
|
||||
title: (worktreeName: string) => string;
|
||||
confirm: string;
|
||||
cancel: string;
|
||||
uncommittedChanges: string;
|
||||
uncommittedChangesWithDiff: (diffStat: string) => string;
|
||||
addedLine: (count: number) => string;
|
||||
deletedLine: (count: number) => string;
|
||||
unpushedCommit: (count: number) => string;
|
||||
}
|
||||
|
||||
function formatDiffStat(diffStat: WorktreeArchiveRisk["diffStat"]): string | null {
|
||||
export const DEFAULT_WORKTREE_ARCHIVE_WARNING_LABELS: WorktreeArchiveWarningLabels = {
|
||||
title: (worktreeName) => i18n.t("workspace.git.actions.archiveWarning.title", { worktreeName }),
|
||||
confirm: i18n.t("workspace.git.actions.archiveWarning.confirm"),
|
||||
cancel: i18n.t("workspace.git.actions.archiveWarning.cancel"),
|
||||
uncommittedChanges: i18n.t("workspace.git.actions.archiveWarning.uncommittedChanges"),
|
||||
uncommittedChangesWithDiff: (diffStat) =>
|
||||
i18n.t("workspace.git.actions.archiveWarning.uncommittedChangesWithDiff", { diffStat }),
|
||||
addedLine: (count) =>
|
||||
count === 1
|
||||
? i18n.t("workspace.git.actions.archiveWarning.addedLine", { count })
|
||||
: i18n.t("workspace.git.actions.archiveWarning.addedLines", { count }),
|
||||
deletedLine: (count) =>
|
||||
count === 1
|
||||
? i18n.t("workspace.git.actions.archiveWarning.deletedLine", { count })
|
||||
: i18n.t("workspace.git.actions.archiveWarning.deletedLines", { count }),
|
||||
unpushedCommit: (count) =>
|
||||
count === 1
|
||||
? i18n.t("workspace.git.actions.archiveWarning.unpushedCommit", { count })
|
||||
: i18n.t("workspace.git.actions.archiveWarning.unpushedCommits", { count }),
|
||||
};
|
||||
|
||||
function formatDiffStat(
|
||||
diffStat: WorktreeArchiveRisk["diffStat"],
|
||||
labels: WorktreeArchiveWarningLabels,
|
||||
): string | null {
|
||||
if (!diffStat) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parts: string[] = [];
|
||||
if (diffStat.additions > 0) {
|
||||
parts.push(`${diffStat.additions} added ${pluralize(diffStat.additions, "line")}`);
|
||||
parts.push(labels.addedLine(diffStat.additions));
|
||||
}
|
||||
if (diffStat.deletions > 0) {
|
||||
parts.push(`${diffStat.deletions} deleted ${pluralize(diffStat.deletions, "line")}`);
|
||||
parts.push(labels.deletedLine(diffStat.deletions));
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join(", ") : null;
|
||||
}
|
||||
|
||||
export function buildWorktreeArchiveRiskReasons(input: WorktreeArchiveRisk): string[] {
|
||||
export function buildWorktreeArchiveRiskReasons(
|
||||
input: WorktreeArchiveRisk,
|
||||
labels: WorktreeArchiveWarningLabels = DEFAULT_WORKTREE_ARCHIVE_WARNING_LABELS,
|
||||
): string[] {
|
||||
const reasons: string[] = [];
|
||||
const diffStat = input.diffStat;
|
||||
const hasDiffStatChanges = diffStat ? diffStat.additions > 0 || diffStat.deletions > 0 : false;
|
||||
@@ -38,13 +73,15 @@ export function buildWorktreeArchiveRiskReasons(input: WorktreeArchiveRisk): str
|
||||
input.isDirty === true || (input.isDirty == null && hasDiffStatChanges);
|
||||
|
||||
if (hasUncommittedChanges) {
|
||||
const diffStatLabel = formatDiffStat(diffStat);
|
||||
reasons.push(diffStatLabel ? `Uncommitted changes (${diffStatLabel})` : "Uncommitted changes");
|
||||
const diffStatLabel = formatDiffStat(diffStat, labels);
|
||||
reasons.push(
|
||||
diffStatLabel ? labels.uncommittedChangesWithDiff(diffStatLabel) : labels.uncommittedChanges,
|
||||
);
|
||||
}
|
||||
|
||||
if ((input.aheadOfOrigin ?? 0) > 0) {
|
||||
const aheadOfOrigin = input.aheadOfOrigin ?? 0;
|
||||
reasons.push(`${aheadOfOrigin} unpushed ${pluralize(aheadOfOrigin, "commit")}`);
|
||||
reasons.push(labels.unpushedCommit(aheadOfOrigin));
|
||||
}
|
||||
|
||||
return reasons;
|
||||
@@ -52,8 +89,9 @@ export function buildWorktreeArchiveRiskReasons(input: WorktreeArchiveRisk): str
|
||||
|
||||
export function buildWorktreeArchiveConfirmationMessage(
|
||||
input: WorktreeArchiveConfirmationInput,
|
||||
labels: WorktreeArchiveWarningLabels = DEFAULT_WORKTREE_ARCHIVE_WARNING_LABELS,
|
||||
): string | null {
|
||||
const reasons = buildWorktreeArchiveRiskReasons(input);
|
||||
const reasons = buildWorktreeArchiveRiskReasons(input, labels);
|
||||
if (reasons.length === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -63,17 +101,18 @@ export function buildWorktreeArchiveConfirmationMessage(
|
||||
|
||||
export async function confirmRiskyWorktreeArchive(
|
||||
input: WorktreeArchiveConfirmationInput,
|
||||
labels: WorktreeArchiveWarningLabels = DEFAULT_WORKTREE_ARCHIVE_WARNING_LABELS,
|
||||
): Promise<boolean> {
|
||||
const message = buildWorktreeArchiveConfirmationMessage(input);
|
||||
const message = buildWorktreeArchiveConfirmationMessage(input, labels);
|
||||
if (!message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return await confirmDialog({
|
||||
title: `Archive "${input.worktreeName}"?`,
|
||||
title: labels.title(input.worktreeName),
|
||||
message,
|
||||
confirmLabel: "Archive",
|
||||
cancelLabel: "Cancel",
|
||||
confirmLabel: labels.confirm,
|
||||
cancelLabel: labels.cancel,
|
||||
destructive: true,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user