@@ -246,8 +247,9 @@ export function CommitsRepoGroup({
// Each repo has its own "latest unpushed commit" — revert/amend in this
// group must target THIS repo's newest, not the merged-list newest.
const firstUnpushedInGroup = groupCommits.findIndex((c) => c.pushed !== true);
+ const { t } = useTranslation();
const stop = (e: React.MouseEvent) => e.stopPropagation();
- const label = displayName || repositoryName || "Repository";
+ const label = displayName || repositoryName || t("common:repository");
// Bug 10 acknowledged trade-off: `existingPrUrl` is sourced from a
// workspace-scoped `prByRepo` map keyed only by "" today — the kandev task
// model has one PR per task, not one PR per repo. As a result every per-
diff --git a/apps/web/components/task/chat/messages/permission-request-message.tsx b/apps/web/components/task/chat/messages/permission-request-message.tsx
index 75a761dc7a..bb76fd7d36 100644
--- a/apps/web/components/task/chat/messages/permission-request-message.tsx
+++ b/apps/web/components/task/chat/messages/permission-request-message.tsx
@@ -54,7 +54,7 @@ export function PermissionRequestMessage({ comment }: PermissionRequestMessagePr
});
const statusBadge = getPermissionStatusBadge(permissionStatus);
- const titleText = comment.content || "Permission Required";
+ const titleText = comment.content || t("task:permissionRequired");
const detailSummary = summarizePermissionAction(permissionMetadata?.action_details, titleText);
return (
diff --git a/apps/web/components/task/chat/messages/status-message.tsx b/apps/web/components/task/chat/messages/status-message.tsx
index 8d634f9c04..909a151ad7 100644
--- a/apps/web/components/task/chat/messages/status-message.tsx
+++ b/apps/web/components/task/chat/messages/status-message.tsx
@@ -134,7 +134,7 @@ function parseStatusMetadata(comment: Message) {
const progress =
typeof metadata?.progress === "number" ? Math.min(Math.max(metadata.progress, 0), 100) : null;
const statusLine = metadata?.stage || metadata?.status;
- const message = metadata?.message || comment.content || statusLine || "Status update";
+ const message = metadata?.message || comment.content || statusLine || t("task:statusUpdate");
const isError = comment.type === "error" || metadata?.variant === "error";
const isWarning = metadata?.variant === "warning" || metadata?.cancelled === true;
return { metadata, progress, statusLine, message, isError, isWarning };
diff --git a/apps/web/components/task/chat/messages/tool-call-message.tsx b/apps/web/components/task/chat/messages/tool-call-message.tsx
index bac85ac5cd..8c87930eaa 100644
--- a/apps/web/components/task/chat/messages/tool-call-message.tsx
+++ b/apps/web/components/task/chat/messages/tool-call-message.tsx
@@ -20,6 +20,7 @@ import { PermissionActionRow } from "./permission-action-row";
import { ExpandableRow } from "./expandable-row";
import { useExpandState } from "./use-expand-state";
import { parsePermission, usePermissionResponseHandlers } from "./use-permission-handlers";
+import { t } from "@/lib/i18n";
const TOOL_ICON_RULES: Array<{ keywords: string[]; Icon: typeof IconCode }> = [
{ keywords: ["edit", "replace", "write", "save"], Icon: IconEdit },
@@ -191,7 +192,7 @@ export const ToolCallMessage = memo(function ToolCallMessage({
const { isExpanded, handleToggle } = useExpandState(status, autoExpanded);
const metadata = comment.metadata as ToolCallMetadata | undefined;
- const rawTitle = metadata?.title ?? comment.content ?? "Tool call";
+ const rawTitle = metadata?.title ?? comment.content ?? t("task:toolCall");
const title = transformPathsInText(prettifyToolTitle(rawTitle), worktreePath);
const formattedOutput = hasOutput && !inlineOutput ? formatToolOutput(output) : null;
diff --git a/apps/web/components/task/chat/messages/tool-subagent-message.tsx b/apps/web/components/task/chat/messages/tool-subagent-message.tsx
index b6e2c02cd2..adc19f28e6 100644
--- a/apps/web/components/task/chat/messages/tool-subagent-message.tsx
+++ b/apps/web/components/task/chat/messages/tool-subagent-message.tsx
@@ -277,13 +277,14 @@ export const ToolSubagentMessage = memo(function ToolSubagentMessage({
onOpenFile,
renderChild,
}: ToolSubagentMessageProps) {
+ const { t } = useTranslation();
const metadata = comment.metadata as ToolCallMetadata | undefined;
const childCount = childMessages.length;
const { subagentTask, isActive, resultText, prompt, hasExpandableContent } =
deriveSubagentDisplay(metadata, childCount, isContainingTurnActive);
const resultSummary = firstResultLine(resultText);
- const description = subagentTask?.description || comment.content || "Subagent";
- const subagentType = subagentTask?.subagent_type || "Task";
+ const description = subagentTask?.description || comment.content || t("task:subagent");
+ const subagentType = subagentTask?.subagent_type || t("common:task");
// Track manual override state - null means "use auto behavior"
const [manualExpandState, setManualExpandState] = useState
(null);
diff --git a/apps/web/components/task/chat/session-search-hits.tsx b/apps/web/components/task/chat/session-search-hits.tsx
index eb1c4d4365..cd7af97150 100644
--- a/apps/web/components/task/chat/session-search-hits.tsx
+++ b/apps/web/components/task/chat/session-search-hits.tsx
@@ -89,7 +89,7 @@ function HitAuthor({
);
}
- const label = agentLabel?.trim() || "Agent";
+ const label = agentLabel?.trim() || t("common:agent");
return (
{agentName && }
diff --git a/apps/web/components/task/chat/task-mention-items.ts b/apps/web/components/task/chat/task-mention-items.ts
index f1916ba2ee..250a1ed3f4 100644
--- a/apps/web/components/task/chat/task-mention-items.ts
+++ b/apps/web/components/task/chat/task-mention-items.ts
@@ -1,5 +1,6 @@
import type { MentionItem } from "@/hooks/use-inline-mention";
import type { AppState } from "@/lib/state/store";
+import { t } from "@/lib/i18n";
type TaskLike = AppState["kanban"]["tasks"][number];
@@ -57,8 +58,8 @@ export function buildTaskMentionItems(
const addTask = (task: TaskLike, workflowId: string) => {
if (task.id === currentTaskId || seen.has(task.id)) return;
seen.add(task.id);
- const workflowName = workflowNameById.get(workflowId) ?? "Workflow";
- const stepTitle = stepTitleById.get(task.workflowStepId) ?? "Step";
+ const workflowName = workflowNameById.get(workflowId) ?? t("common:workflow");
+ const stepTitle = stepTitleById.get(task.workflowStepId) ?? t("common:step");
items.push(toMentionItem(task, workflowId, workflowName, stepTitle));
};
diff --git a/apps/web/components/task/chat/tiptap-entity-reference-extension.tsx b/apps/web/components/task/chat/tiptap-entity-reference-extension.tsx
index 15259a5e2a..9e820077f4 100644
--- a/apps/web/components/task/chat/tiptap-entity-reference-extension.tsx
+++ b/apps/web/components/task/chat/tiptap-entity-reference-extension.tsx
@@ -3,6 +3,7 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { NodeViewWrapper, ReactNodeViewRenderer, type ReactNodeViewProps } from "@tiptap/react";
import { IconHash } from "@tabler/icons-react";
+import { t } from "@/lib/i18n";
export const EntityReferenceNode = Node.create({
name: "entityReference",
@@ -54,7 +55,7 @@ function EntityReferenceChipView({ node }: ReactNodeViewProps) {
kind?: string | null;
id?: string | null;
};
- const label = key || title || id || "Reference";
+ const label = key || title || id || t("common:reference");
const source = [provider, kind].filter(Boolean).join(" ");
const description = source ? `${source}: ${title || label}` : title || label;
return (
diff --git a/apps/web/components/task/dockview-header-actions.tsx b/apps/web/components/task/dockview-header-actions.tsx
index eae7bf16a1..aa382901f1 100644
--- a/apps/web/components/task/dockview-header-actions.tsx
+++ b/apps/web/components/task/dockview-header-actions.tsx
@@ -156,7 +156,7 @@ export function LeftHeaderActions(props: IDockviewHeaderActionsProps) {
if (!environmentId) return;
try {
const result = await createUserShell(environmentId, { scriptId });
- const title = result.label ?? "Script";
+ const title = result.label ?? t("common:script");
addTerminalPanel(result.terminalId, group.id, environmentId, undefined, title);
} catch (error) {
console.error("Failed to run script:", error);
@@ -491,7 +491,7 @@ function TerminalScriptsDropdown({
rightBottomGroupId ?? undefined,
environmentId,
undefined,
- result.label ?? "Script",
+ result.label ?? t("common:script"),
);
} catch (error) {
console.error("Failed to run script:", error);
@@ -574,7 +574,7 @@ function TerminalDevPreviewButton({
}
try {
const shell = await createUserShell(environmentId, { taskId: taskID ?? undefined });
- const title = shell.displayName ?? shell.label ?? "Terminal";
+ const title = shell.displayName ?? shell.label ?? t("common:terminal");
addTerminalPanel(
shell.terminalId,
rightBottomGroupId ?? undefined,
diff --git a/apps/web/components/task/dockview-watermark.tsx b/apps/web/components/task/dockview-watermark.tsx
index 5795e54efc..2f0c92ac8f 100644
--- a/apps/web/components/task/dockview-watermark.tsx
+++ b/apps/web/components/task/dockview-watermark.tsx
@@ -155,7 +155,7 @@ function useWatermarkHandlers(
groupId,
environmentId,
undefined,
- result.label ?? "Script",
+ result.label ?? t("common:script"),
);
} catch (error) {
console.error("Failed to run script:", error);
diff --git a/apps/web/components/task/file-browser.tsx b/apps/web/components/task/file-browser.tsx
index 6aad0609a1..e5eee7e518 100644
--- a/apps/web/components/task/file-browser.tsx
+++ b/apps/web/components/task/file-browser.tsx
@@ -19,6 +19,7 @@ import { useOpenSessionFolder } from "@/hooks/use-open-session-folder";
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import { useToast } from "@/components/toast-provider";
import { useTranslation } from "react-i18next";
+import type { TFunction } from "i18next";
import { useMultiSelect } from "@/hooks/use-multi-select";
import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint";
import { useContextFilesStore } from "@/lib/state/context-files-store";
@@ -156,7 +157,11 @@ type MoveFilesParams = {
onRenameFile: (oldPath: string, newPath: string) => Promise;
};
-function executeMoveFiles(params: MoveFilesParams, toast: ReturnType["toast"]) {
+function executeMoveFiles(
+ params: MoveFilesParams,
+ toast: ReturnType["toast"],
+ t: TFunction,
+) {
const { sources, targetPath, treeState, setSelectedPaths, onRenameFile } = params;
const snapshot = treeState.tree;
@@ -172,8 +177,8 @@ function executeMoveFiles(params: MoveFilesParams, toast: ReturnType !ok)) {
treeState.setTree(snapshot);
toast({
- title: "Move failed",
- description: "Some files could not be moved",
+ title: t("task:moveFailed"),
+ description: t("task:moveFailedFiles"),
variant: "error",
});
}
@@ -181,8 +186,8 @@ function executeMoveFiles(params: MoveFilesParams, toast: ReturnType {
treeState.setTree(snapshot);
toast({
- title: "Move failed",
- description: "An error occurred while moving files",
+ title: t("task:moveFailed"),
+ description: t("task:moveFailedUnexpected"),
variant: "error",
});
});
@@ -194,6 +199,7 @@ function useDragAndDrop(
setSelectedPaths: (paths: Set) => void,
onRenameFile?: (oldPath: string, newPath: string) => Promise,
) {
+ const { t } = useTranslation();
const { toast } = useToast();
const [isDragging, setIsDragging] = useState(false);
const [dragOverPath, setDragOverPath] = useState(null);
@@ -257,9 +263,13 @@ function useDragAndDrop(
if (!onRenameFile) return;
const sources = dragPathsRef.current;
if (sources.length === 0 || isDropInvalid(sources, targetPath)) return;
- executeMoveFiles({ sources, targetPath, treeState, setSelectedPaths, onRenameFile }, toast);
+ executeMoveFiles(
+ { sources, targetPath, treeState, setSelectedPaths, onRenameFile },
+ toast,
+ t,
+ );
},
- [onRenameFile, treeState, setSelectedPaths, toast],
+ [onRenameFile, treeState, setSelectedPaths, t, toast],
);
return {
diff --git a/apps/web/components/task/mode-selector.tsx b/apps/web/components/task/mode-selector.tsx
index 26ab5d37fb..ba90a6cd71 100644
--- a/apps/web/components/task/mode-selector.tsx
+++ b/apps/web/components/task/mode-selector.tsx
@@ -170,7 +170,7 @@ export const ModeSelector = memo(function ModeSelector({
}
const currentMode = modeState.availableModes.find((m) => m.id === modeState.currentModeId);
- const displayName = currentMode?.name || modeState.currentModeId || "Mode";
+ const displayName = currentMode?.name || modeState.currentModeId || t("common:mode");
return (
diff --git a/apps/web/components/task/new-session-dialog.tsx b/apps/web/components/task/new-session-dialog.tsx
index ab38d25532..2cb3aff5df 100644
--- a/apps/web/components/task/new-session-dialog.tsx
+++ b/apps/web/components/task/new-session-dialog.tsx
@@ -49,6 +49,7 @@ function agentProfileDisplayLabel(profile: AgentProfileOption): string {
}
function useNewSessionDialogState(taskId: string) {
+ const { t } = useTranslation();
const resolvedWorkspaceId = useAppStore((state) =>
resolveComposerWorkspaceId({
sessionId: null,
@@ -61,8 +62,8 @@ function useNewSessionDialogState(taskId: string) {
}),
);
const taskTitle = useAppStore((state) => {
- const task = state.kanban.tasks.find((t: { id: string }) => t.id === taskId);
- return task?.title ?? "Task";
+ const task = state.kanban.tasks.find((entry: { id: string }) => entry.id === taskId);
+ return task?.title ?? t("common:task");
});
const agentProfiles = useAppStore((state) => state.agentProfiles.items);
const activeSessionId = useAppStore((state) => state.tasks.activeSessionId);
diff --git a/apps/web/components/task/recent-task-switcher-hooks.ts b/apps/web/components/task/recent-task-switcher-hooks.ts
index 48c7fa32de..9de306705e 100644
--- a/apps/web/components/task/recent-task-switcher-hooks.ts
+++ b/apps/web/components/task/recent-task-switcher-hooks.ts
@@ -10,6 +10,7 @@ import {
type MutableRefObject,
} from "react";
import { useTranslation } from "react-i18next";
+import { searchKeywords } from "@/lib/commands/search-keywords";
import { useRouter } from "@/lib/routing/client-router";
import { useAppStore } from "@/components/state-provider";
import { useCommandPanelOpen } from "@/lib/commands/command-registry";
@@ -419,25 +420,16 @@ function useSwitcherActions({
function useRecentTaskSwitcherCommand(shortcut: KeyboardShortcut, openSwitcher: () => void) {
// `group` is the command palette's Map key AND its rendered heading, so it has
// to be translated in lockstep with every other producer or the heading splits
- // in two under a non-English locale. `label`/`keywords` below stay English:
- // they belong to the components/task migration, not this one.
+ // in two under a non-English locale.
const { t } = useTranslation();
const commands = useMemo(
() => [
{
id: "open-recent-task-switcher",
- label: "Open Recent Task Switcher",
+ label: t("common:commandOpenRecentTaskSwitcher"),
group: t("common:commandGroupNavigation"),
shortcut,
- keywords: [
- "recent",
- "task",
- "switcher",
- "history",
- "recent tasks",
- "switch task",
- "switch recent tasks",
- ],
+ keywords: searchKeywords(t, "common:commandOpenRecentTaskSwitcherKeywords"),
action: openSwitcher,
},
],
diff --git a/apps/web/components/task/sidebar-filter/sidebar-filter-bar.tsx b/apps/web/components/task/sidebar-filter/sidebar-filter-bar.tsx
index 966f36c948..26b0b5aaf9 100644
--- a/apps/web/components/task/sidebar-filter/sidebar-filter-bar.tsx
+++ b/apps/web/components/task/sidebar-filter/sidebar-filter-bar.tsx
@@ -13,6 +13,7 @@ import { SidebarViewChips } from "./sidebar-view-chips";
import { SidebarFilterPopover } from "./sidebar-filter-popover";
import { useSidebarViewPopover } from "./use-sidebar-view-popover";
import { useTranslation } from "react-i18next";
+import { sidebarViewName } from "@/lib/state/slices/ui/sidebar-view-builtins";
// `group` is the command-palette bucket shared with `components/session-commands.tsx`;
// it is grouping taxonomy rather than this surface's copy, so it migrates with
@@ -36,9 +37,9 @@ function useSidebarCommands(
for (const view of views) {
list.push({
id: `sidebar-switch-view-${view.id}`,
- label: t("task:switchSidebarView", { name: view.name }),
+ label: t("task:switchSidebarView", { name: sidebarViewName(view, t) }),
group: "Sidebar",
- keywords: ["view", "switch", "sidebar", view.name.toLowerCase()],
+ keywords: ["view", "switch", "sidebar", sidebarViewName(view, t).toLowerCase()],
action: () => setActiveView(view.id),
});
}
diff --git a/apps/web/components/task/sidebar-filter/sidebar-view-chips.tsx b/apps/web/components/task/sidebar-filter/sidebar-view-chips.tsx
index de8be7467d..90c4ed88f5 100644
--- a/apps/web/components/task/sidebar-filter/sidebar-view-chips.tsx
+++ b/apps/web/components/task/sidebar-filter/sidebar-view-chips.tsx
@@ -15,6 +15,8 @@ import { CSS } from "@dnd-kit/utilities";
import { useAppStore } from "@/components/state-provider";
import type { SidebarView } from "@/lib/state/slices/ui/sidebar-view-types";
import { cn } from "@/lib/utils";
+import { sidebarViewName } from "@/lib/state/slices/ui/sidebar-view-builtins";
+import { useTranslation } from "react-i18next";
const DRAG_ACTIVATION_DISTANCE = 8;
const TOUCH_DRAG_DELAY_MS = 250;
@@ -80,6 +82,7 @@ function SidebarViewChip({
active: boolean;
onSelect: () => void;
}) {
+ const { t } = useTranslation();
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: view.id,
});
@@ -150,10 +153,10 @@ function SidebarViewChip({
: "border-transparent text-muted-foreground hover:text-foreground",
isDragging && "z-50 cursor-grabbing",
)}
- title={view.name}
+ title={sidebarViewName(view, t)}
onClick={handleClick}
>
- {view.name}
+ {sidebarViewName(view, t)}
);
}
diff --git a/apps/web/components/task/sidebar-filter/view-manager.tsx b/apps/web/components/task/sidebar-filter/view-manager.tsx
index 0253bfb0b1..807c52a05c 100644
--- a/apps/web/components/task/sidebar-filter/view-manager.tsx
+++ b/apps/web/components/task/sidebar-filter/view-manager.tsx
@@ -5,6 +5,7 @@ import { Input } from "@kandev/ui/input";
import { Button } from "@kandev/ui/button";
import type { SidebarView } from "@/lib/state/slices/ui/sidebar-view-types";
import { useTranslation } from "react-i18next";
+import { sidebarViewName } from "@/lib/state/slices/ui/sidebar-view-builtins";
type HeaderMode = "view" | "rename" | "saveAs";
@@ -115,7 +116,7 @@ function NameDisplay({
return (
<>
- {activeView?.name ?? "—"}
+ {activeView ? sidebarViewName(activeView, t) : "—"}
{hasDraft && (
setPreviewRevision(null)}
onRestore={() => {
@@ -307,9 +309,9 @@ function RevisionsDialogStack({
/** Display label for an author. User revisions always render as "You";
* agent revisions use the stored author_name (the agent profile display name). */
-function authorLabel(revision: TaskPlanRevision): string {
- if (revision.author_kind === "user") return USER_DISPLAY_NAME;
- return revision.author_name || "Agent";
+function authorLabel(revision: TaskPlanRevision, t: TFunction): string {
+ if (revision.author_kind === "user") return t(USER_DISPLAY_NAME_KEY);
+ return revision.author_name || t("common:agent");
}
/** Resolve the active session's agent backend name (e.g. "claude", "codex") so
@@ -370,6 +372,7 @@ function RevisionAuthor({
revision: TaskPlanRevision;
agentName: string | null;
}) {
+ const { t } = useTranslation();
if (revision.author_kind === "agent") {
return (
<>
@@ -384,7 +387,7 @@ function RevisionAuthor({
<>
- {USER_DISPLAY_NAME}
+ {t(USER_DISPLAY_NAME_KEY)}
>
);
diff --git a/apps/web/components/task/task-session-sidebar-archived-item.ts b/apps/web/components/task/task-session-sidebar-archived-item.ts
index 10388fd192..1170efac96 100644
--- a/apps/web/components/task/task-session-sidebar-archived-item.ts
+++ b/apps/web/components/task/task-session-sidebar-archived-item.ts
@@ -1,12 +1,13 @@
import type { TaskSwitcherItem } from "./task-switcher";
import type { useArchivedTaskState } from "./task-archived-context";
+import { t } from "@/lib/i18n";
export function buildArchivedSidebarItem(
s: ReturnType,
): TaskSwitcherItem {
return {
id: s.archivedTaskId!,
- title: s.archivedTaskTitle ?? "Archived task",
+ title: s.archivedTaskTitle ?? t("task:archivedTask"),
state: undefined,
sessionState: undefined,
description: undefined,
diff --git a/apps/web/components/task/terminal-tab.tsx b/apps/web/components/task/terminal-tab.tsx
index 0ec82917fb..03aa822cee 100644
--- a/apps/web/components/task/terminal-tab.tsx
+++ b/apps/web/components/task/terminal-tab.tsx
@@ -138,13 +138,14 @@ function useTerminalTabClose({
}
export function TerminalTab(props: IDockviewPanelHeaderProps) {
+ const { t } = useTranslation();
const { terminalId, taskID: stampedTaskID, environmentId: stampedEnv } = extractParams(props);
const activeTaskID = useAppStore((s) => s.tasks?.activeTaskId ?? null);
const taskID = stampedTaskID ?? activeTaskID ?? null;
const { shell, isOrdinary, seq, showBadge, displayName, closable } = useTerminalTabState(
stampedEnv,
terminalId,
- props.api.title ?? "Terminal",
+ props.api.title ?? t("common:terminal"),
);
// DockviewDefaultTab reads the title from `api.title` directly and
diff --git a/apps/web/components/task/use-tunnel-actions.ts b/apps/web/components/task/use-tunnel-actions.ts
index d085c4d479..ef238b91e4 100644
--- a/apps/web/components/task/use-tunnel-actions.ts
+++ b/apps/web/components/task/use-tunnel-actions.ts
@@ -3,6 +3,7 @@
import { useState, useCallback } from "react";
import { startTunnel, stopTunnel } from "@/lib/api/domains/port-api";
import { toast } from "@/lib/toast/sonner";
+import { t } from "@/lib/i18n";
export function useTunnelActions(
sessionId: string,
@@ -16,10 +17,12 @@ export function useTunnelActions(
try {
const tunnelPort = await startTunnel(sessionId, port, requestedPort);
setActiveTunnels((prev) => new Map(prev).set(port, tunnelPort));
- toast.success(`Tunnel started on port ${tunnelPort}`);
+ toast.success(t("task:tunnelStartedOnPort", { port: tunnelPort }));
} catch (err) {
toast.error(
- `Failed to start tunnel: ${err instanceof Error ? err.message : "unknown error"}`,
+ t("task:failedToStartTunnel", {
+ error: err instanceof Error ? err.message : t("task:unknownError"),
+ }),
);
} finally {
setPendingTunnels((prev) => {
@@ -42,10 +45,12 @@ export function useTunnelActions(
next.delete(port);
return next;
});
- toast.success("Tunnel stopped");
+ toast.success(t("task:tunnelStopped"));
} catch (err) {
toast.error(
- `Failed to stop tunnel: ${err instanceof Error ? err.message : "unknown error"}`,
+ t("task:failedToStopTunnel", {
+ error: err instanceof Error ? err.message : t("task:unknownError"),
+ }),
);
} finally {
setPendingTunnels((prev) => {
diff --git a/apps/web/components/vcs-split-button.tsx b/apps/web/components/vcs-split-button.tsx
index 95ebb8495f..a780aa5d7d 100644
--- a/apps/web/components/vcs-split-button.tsx
+++ b/apps/web/components/vcs-split-button.tsx
@@ -28,7 +28,7 @@ import {
import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip";
import { cn } from "@kandev/ui/lib/utils";
import { useSessionGit } from "@/hooks/domains/session/use-session-git";
-import { useGitWithFeedback } from "@/hooks/use-git-with-feedback";
+import { gitOperationLabel, useGitWithFeedback } from "@/hooks/use-git-with-feedback";
import { useVcsDialogs } from "@/components/vcs/vcs-dialogs";
import { useActiveTaskPR } from "@/hooks/domains/github/use-task-pr";
import { useRepoDisplayName } from "@/hooks/domains/session/use-repo-display-name";
@@ -310,47 +310,45 @@ type VcsSplitButtonProps = {
className?: string;
};
-// The `label` values below stay English on purpose. They are operation *names*
-// consumed by `useGitWithFeedback`, which concatenates them into its own English
-// toast titles (`${operationName} failed`). `hooks/use-git-with-feedback.ts` is
-// not migrated yet, so translating only this half would ship mixed-language
-// toasts. They move when that hook's messages do.
function useGitActions(git: ReturnType, baseBranch?: string) {
+ const { t } = useTranslation();
const gitWithFeedback = useGitWithFeedback();
const handlePull = useCallback(
(repo?: string) => {
- const label = repo ? `Pull (${repo})` : "Pull";
- gitWithFeedback(() => git.pull(false, repo), label);
+ gitWithFeedback(() => git.pull(false, repo), gitOperationLabel(t, "common:gitOpPull", repo));
},
- [gitWithFeedback, git],
+ [gitWithFeedback, git, t],
);
const handlePush = useCallback(
(force = false, repo?: string) => {
- const baseLabel = force ? "Force Push" : "Push";
- const label = repo ? `${baseLabel} (${repo})` : baseLabel;
- gitWithFeedback(() => git.push({ force }, repo), label);
+ const operationKey = force ? "common:gitOpForcePush" : "common:gitOpPush";
+ gitWithFeedback(() => git.push({ force }, repo), gitOperationLabel(t, operationKey, repo));
},
- [gitWithFeedback, git],
+ [gitWithFeedback, git, t],
);
const handleRebase = useCallback(
(repo?: string) => {
const targetBranch = baseBranch?.replace(/^origin\//, "") || "main";
- const label = repo ? `Rebase (${repo})` : "Rebase";
- gitWithFeedback(() => git.rebase(targetBranch, repo), label);
+ gitWithFeedback(
+ () => git.rebase(targetBranch, repo),
+ gitOperationLabel(t, "common:gitOpRebase", repo),
+ );
},
- [gitWithFeedback, git, baseBranch],
+ [gitWithFeedback, git, baseBranch, t],
);
const handleMerge = useCallback(
(repo?: string) => {
const targetBranch = baseBranch?.replace(/^origin\//, "") || "main";
- const label = repo ? `Merge (${repo})` : "Merge";
- gitWithFeedback(() => git.merge(targetBranch, repo), label);
+ gitWithFeedback(
+ () => git.merge(targetBranch, repo),
+ gitOperationLabel(t, "common:gitOpMerge", repo),
+ );
},
- [gitWithFeedback, git, baseBranch],
+ [gitWithFeedback, git, baseBranch, t],
);
return { handlePull, handlePush, handleRebase, handleMerge };
diff --git a/apps/web/components/vcs/vcs-dialogs.tsx b/apps/web/components/vcs/vcs-dialogs.tsx
index 8034138bcf..cf17b41d80 100644
--- a/apps/web/components/vcs/vcs-dialogs.tsx
+++ b/apps/web/components/vcs/vcs-dialogs.tsx
@@ -29,7 +29,7 @@ import {
useGitOperations,
} from "@/hooks/use-git-operations";
import { useAppStore } from "@/components/state-provider";
-import { useGitWithFeedback } from "@/hooks/use-git-with-feedback";
+import { gitOperationLabel, useGitWithFeedback } from "@/hooks/use-git-with-feedback";
import { useUtilityAgentGenerator } from "@/hooks/use-utility-agent-generator";
import { useIsUtilityConfigured } from "@/hooks/use-is-utility-configured";
import { useToast } from "@/components/toast-provider";
@@ -451,6 +451,7 @@ function useVcsDialogsState(
taskTitle: string | undefined,
baseBranch: string | undefined,
) {
+ const { t } = useTranslation();
const cs = useCommitDialogState();
const ps = usePRDialogState();
const { toast } = useToast();
@@ -477,16 +478,12 @@ function useVcsDialogsState(
const title = cs.message.trim();
const body = cs.body.trim();
const fullMessage = body ? `${title}\n\n${body}` : title;
- // Deliberately English: `useGitWithFeedback` composes this into
- // "...", " successful" and " failed" itself, and that
- // hook is not migrated. Translating only the label would produce a
- // half-translated toast.
- const label = cs.repo ? `Commit (${cs.repo})` : "Commit";
+ const label = gitOperationLabel(t, "common:gitOpCommit", cs.repo);
await gitWithFeedback(() => commit(fullMessage, cs.stageAll, false, cs.repo), label);
cs.setMessage("");
cs.setBody("");
cs.setRepo(undefined);
- }, [cs, gitWithFeedback, commit]);
+ }, [cs, gitWithFeedback, commit, t]);
const handleCreatePR = useCreatePRHandler(
ps,
baseBranch,
diff --git a/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts b/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts
index c778dee23b..c492b30f79 100644
--- a/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts
+++ b/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts
@@ -195,34 +195,18 @@ const SCREENS: Screen[] = [
// run between `cc6eb4dd5` and this commit. Re-check these against
// `BUILT_IN_LAYOUT_PROFILES` whenever a built-in name or description moves.
//
- // Both groups are display strings that are also PERSISTED, so translating
- // them in place would write locale-dependent values into a user's saved
- // layouts and leave them there after a locale switch:
- // - Built-in profile names/descriptions (lib/layout/layout-profiles.ts).
- // `upsertBuiltInLayoutOverride` copies `builtIn.name` into the saved
- // record the first time a built-in is customized.
- // - Dockview panel titles (lib/state/layout-manager/constants.ts), which
- // `toSerializedDockview` writes into the stored layout JSON. That path
- // is already in `EXCLUDED` in scripts/externalize-strings.mjs.
- // Localizing either needs a key/persisted-value split in those modules.
- allow: [
- "Default",
- "Plan Mode",
- "Preview Mode",
- "VS Code",
- "Agent with Files, Changes, and Terminal",
- "Agent and Plan side by side",
- "Agent and Browser side by side",
- "Agent and VS Code side by side",
- "Agent",
- "Plan",
- "Changes",
- "Files",
- "Browser",
- "Terminal",
- "PR Details",
- "Merge Request",
- ],
+ // Both groups WERE display strings that are also persisted, and both have
+ // had the key/persisted-value split this note used to ask for:
+ // - Built-in profile names/descriptions (lib/layout/layout-profiles.ts)
+ // carry `nameKey`/`descriptionKey`; `name` stays canonical English
+ // because `upsertBuiltInLayoutOverride` copies it into the saved record.
+ // - Dockview panel titles (lib/state/layout-manager/constants.ts) carry
+ // `titleKey`; `toSerializedDockview` writes the canonical English
+ // `title` into the stored layout JSON, and `normalizePanel` re-resolves
+ // the display title on restore. `panel-titles.test.ts` covers both
+ // directions.
+ // Only the two product names are still expected to render as English.
+ allow: ["VS Code", "Agent and VS Code side by side"],
},
{
name: "settings — keyboard shortcuts",
@@ -298,6 +282,23 @@ const SCREENS: Screen[] = [
// `test-base` state and belongs with whoever owns that fixture — not an
// allowlist entry and not a renamed screen, either of which would keep the
// false claim alive in a new form.
+ // NOT YET: "kanban board" (`/`, anchor `[data-testid=kanban-board]`). Probed
+ // under pseudo against this fixture. The board's own chrome is clean, and the
+ // one real finding it surfaced — the built-in sidebar view's "All tasks",
+ // which `sidebar-view-builtins.ts` persists as a user-editable name — is fixed
+ // and now resolves through `sidebar:viewAllTasks`.
+ //
+ // What is left is entirely the fixture's own records: the workflow name
+ // ("E2E Workflow") and its four step names ("Backlog", "In Progress",
+ // "Review", "Done"). Those are user data on exactly the footing as the
+ // workspace and executor-profile names described above, so this entry needs
+ // the same change — `findUnlocalizedText` learning to skip user data — and not
+ // an `allow` list of fixture values, which would fix this fixture and leave
+ // every developer instance red under different workflow names.
+ //
+ // `/tasks` was probed with it and cannot be added for a different reason:
+ // `[data-testid=tasks-list]` never renders in this fixture, so there is no
+ // anchor that proves the route mounted. It needs a seeded task list first.
{
name: "office — inbox",
url: "/office/inbox",
diff --git a/apps/web/eslint.i18n.options.mjs b/apps/web/eslint.i18n.options.mjs
index 0baaed0eea..6158c4f37d 100644
--- a/apps/web/eslint.i18n.options.mjs
+++ b/apps/web/eslint.i18n.options.mjs
@@ -1824,4 +1824,94 @@ export const i18nGuardFiles = [
// chips, routing cards and activity rows all resolve through, so the same
// vocabulary cannot drift apart across surfaces.
"app/office/**/*.{ts,tsx}",
+ // The command palette's two un-migrated producers. `global-commands.tsx` was
+ // already listed and already resolved everything through the catalog, so ⌘K
+ // rendered a translated Navigation group above English Git/Panels rows; these
+ // two are what made the same list half-English. Their labels are `label:`
+ // object properties rather than JSX, which is why the guard reported them
+ // clean the whole time — see the note on `mode: "jsx-only"` above.
+ "components/homepage-commands.tsx",
+ "components/session-commands.tsx",
+ // `use-git-with-feedback.ts` composed its own English around the operation
+ // name ("Push failed"), which is why `vcs-split-button.tsx`, `vcs-dialogs.tsx`
+ // and the palette all passed it a deliberately English label. It interpolates
+ // catalog messages now, so those three no longer have to.
+ "hooks/use-git-with-feedback.ts",
+ // Toast and feedback copy. None of it is JSX — it is `title:`/`description:`
+ // object properties and `toast.error("…")` arguments — so `mode: "jsx-only"`
+ // never inspected any of these files and they read as clean for the whole
+ // migration. Listing them records that they are done; only the pseudo-locale
+ // can prove they stay that way.
+ //
+ // `changes-panel-hooks.ts` carried the multi-repo half of the same
+ // concatenated-English problem as `use-git-with-feedback.ts`
+ // (`${operationName} partially succeeded`), including two per-repo counts that
+ // now use `count` with `_one`/`_other` instead of a bare "repos".
+ //
+ // `unarchive-feedback.ts` inflected its own sentence
+ // (`${plural ? "Branches" : "Branch"} … no longer ${plural ? "exist" : "exists"}`),
+ // which is the inline-plural shape docs/i18n.md rules out; the plural rule
+ // lives in the catalog now. `check-inline-plurals.mjs` does not see this
+ // form — it is two independent ternaries, not a `+ "s"`.
+ "app/tasks/tasks-page-client.tsx",
+ "components/azure-devops/azure-devops-task-launcher.tsx",
+ "components/github/use-pr-scoped-review-request.ts",
+ "components/task/changes-panel-hooks.ts",
+ "components/task/task-center-panel-restoration.ts",
+ "components/task/use-tunnel-actions.ts",
+ "hooks/domains/comments/use-markdown-preview-comments.ts",
+ "hooks/domains/session/use-task-environment.ts",
+ "hooks/use-file-save-delete.ts",
+ "hooks/use-utility-agent-generator.ts",
+ "lib/tasks/unarchive-feedback.ts",
+ // Dockview panel titles. These are display copy that is ALSO persisted —
+ // `toSerializedDockview` writes the title into the stored layout JSON — so
+ // they now carry a `titleKey` beside a canonical English `title`, the split
+ // the note on the layouts screen in `e2e/tests/i18n/pseudo-coverage.spec.ts`
+ // said they needed. `constants.ts` deliberately still holds English `title:`
+ // values; they are storage, not copy, and `panel-titles.test.ts` asserts the
+ // two stay apart.
+ "lib/state/dockview-panel-actions.ts",
+ "lib/state/layout-manager/constants.ts",
+ "lib/state/layout-manager/serializer.ts",
+ // Built-in layout profile names and descriptions, the other half of the same
+ // problem: `upsertBuiltInLayoutOverride` copies `name` into a saved override,
+ // so `name`/`description` stay canonical English and the settings list renders
+ // `nameKey`/`descriptionKey`.
+ "lib/layout/layout-profiles.ts",
+ // English `??` / `||` fallbacks — "Agent", "Terminal", "Repository",
+ // "Untitled task", "An error occurred". They render whenever the real value
+ // is absent, which is a normal path rather than an edge case, and none of
+ // them is a JSX literal, so the guard never saw them. Values that are
+ // PERSISTED or sent to an agent verbatim ("New Repository", the quick-chat
+ // session name, `use-plan-actions`' default prompt) are deliberately still
+ // English and stay off this list.
+ "components/review/review-diff-list-groups.tsx",
+ "components/review/review-dialog.tsx",
+ "components/task/changes-git-credential-display.ts",
+ "components/task/task-session-sidebar-archived-item.ts",
+ "hooks/domains/session/use-session-resumption.ts",
+ "hooks/domains/session/use-terminals.ts",
+ "hooks/domains/session/use-user-shells.ts",
+ "hooks/use-editor-keybinds.ts",
+ "hooks/use-summarize-session.ts",
+ "hooks/use-update-available-toast.ts",
+ "lib/agent-runtime-update.ts",
+ "lib/api/domains/plan-api.ts",
+ "lib/capability-warning.ts",
+ "lib/github-auth.ts",
+ "lib/recent-tasks.ts",
+ "lib/state/slices/comments/format.ts",
+ "lib/utils/file-diff.ts",
+ // The built-in sidebar view's name, the third persisted-and-displayed string
+ // in this change. `SidebarView.name` is user-editable and synced, so the
+ // built-in keeps a canonical English `name` and every surface resolves
+ // `sidebarViewName()` instead. Found by the pseudo oracle, not by lint: it is
+ // a `.ts` constant, which `mode: "jsx-only"` never inspects.
+ "lib/state/slices/ui/sidebar-view-builtins.ts",
+ // The last English row in the palette. Its `group` was already translated —
+ // it has to be, since the palette groups by the resolved value — and a
+ // comment deferred the `label`/`keywords` to "the components/task migration".
+ // Both are palette copy and belong with the rest of it.
+ "components/task/recent-task-switcher-hooks.ts",
];
diff --git a/apps/web/hooks/domains/comments/use-markdown-preview-comments.ts b/apps/web/hooks/domains/comments/use-markdown-preview-comments.ts
index 6dd0210219..751fc7efd5 100644
--- a/apps/web/hooks/domains/comments/use-markdown-preview-comments.ts
+++ b/apps/web/hooks/domains/comments/use-markdown-preview-comments.ts
@@ -9,6 +9,7 @@ import {
type RefObject,
type SetStateAction,
} from "react";
+import { useTranslation } from "react-i18next";
import { useToast } from "@/components/toast-provider";
import { useCommentsStore, type DiffComment } from "@/lib/state/slices/comments";
import {
@@ -169,6 +170,7 @@ function useMarkdownCommentSubmitters({
textSelection: MarkdownPreviewSelection | null;
clearTextSelection: () => void;
}) {
+ const { t } = useTranslation();
const addComment = useCommentsStore((s) => s.addComment);
const { runComment } = useRunComment({ sessionId: sessionId ?? null, taskId: taskId ?? null });
const { toast } = useToast();
@@ -193,24 +195,27 @@ function useMarkdownCommentSubmitters({
[addComment, clearTextSelection, path, repositoryId, sessionId, textSelection],
);
+ // Same `editors:` copy as the CodeMirror and Monaco comment flows in
+ // `components/editors/**`, which say exactly this. The markdown preview is
+ // the third surface of the same feature and was the one left in English.
const submitComment = useCallback(
(text: string) => {
const comment = createComment(text);
if (!comment) return;
toast({
- title: "Comment added",
- description: "Your comment will be sent with your next message.",
+ title: t("editors:commentAdded"),
+ description: t("editors:commentWillBeSentWithNextMessage"),
});
},
- [createComment, toast],
+ [createComment, t, toast],
);
const submitAndRunComment = useCallback(
async (text: string) => {
if (!canRunComment) {
toast({
- title: "Failed to send comment",
- description: "Open a task session before sending to the agent.",
+ title: t("editors:failedToSendComment"),
+ description: t("editors:openTaskSessionBeforeSending"),
variant: "error",
});
return;
@@ -220,18 +225,18 @@ function useMarkdownCommentSubmitters({
try {
const { queued } = await runComment(comment);
toast({
- title: "Comment sent",
- description: queued ? "Queued for the agent." : "Sent to the agent.",
+ title: t("editors:commentSent"),
+ description: queued ? t("editors:queuedForTheAgent") : t("editors:sentToTheAgent"),
});
} catch {
toast({
- title: "Failed to send comment",
- description: "Please try again.",
+ title: t("editors:failedToSendComment"),
+ description: t("editors:pleaseTryAgain"),
variant: "error",
});
}
},
- [canRunComment, createComment, runComment, toast],
+ [canRunComment, createComment, runComment, t, toast],
);
return { submitComment, submitAndRunComment: canRunComment ? submitAndRunComment : undefined };
@@ -278,6 +283,7 @@ function useVisibleMarkdownCommentActions({
}: {
setCommentView: Dispatch>;
}) {
+ const { t } = useTranslation();
const removeComment = useCommentsStore((s) => s.removeComment);
const updateComment = useCommentsStore((s) => s.updateComment);
const { toast } = useToast();
@@ -289,9 +295,9 @@ function useVisibleMarkdownCommentActions({
const nextComments = view.comments.filter((comment) => comment.id !== commentId);
return nextComments.length > 0 ? { ...view, comments: nextComments } : null;
});
- toast({ title: "Comment deleted" });
+ toast({ title: t("editors:commentDeleted") });
},
- [removeComment, setCommentView, toast],
+ [removeComment, setCommentView, t, toast],
);
const updateVisibleComment = useCallback(
(commentId: string, text: string) => {
@@ -305,9 +311,9 @@ function useVisibleMarkdownCommentActions({
),
};
});
- toast({ title: "Comment updated" });
+ toast({ title: t("editors:commentUpdated") });
},
- [setCommentView, toast, updateComment],
+ [setCommentView, t, toast, updateComment],
);
return { removeVisibleComment, updateVisibleComment };
diff --git a/apps/web/hooks/domains/session/use-session-resumption.ts b/apps/web/hooks/domains/session/use-session-resumption.ts
index a81e054e9c..723faf4424 100644
--- a/apps/web/hooks/domains/session/use-session-resumption.ts
+++ b/apps/web/hooks/domains/session/use-session-resumption.ts
@@ -13,6 +13,7 @@ import {
type TaskId,
type TaskSessionState,
} from "@/lib/types/http";
+import { t } from "@/lib/i18n";
export type SessionStatus = {
session_id: string;
@@ -95,7 +96,7 @@ function applyResumeResponse(
return true;
}
setters.setResumptionState("error");
- setters.setError(resp.error ?? "Failed to resume session");
+ setters.setError(resp.error ?? t("task:failedToResumeSession"));
return false;
}
diff --git a/apps/web/hooks/domains/session/use-task-environment.ts b/apps/web/hooks/domains/session/use-task-environment.ts
index b3d372ee92..89e7345f21 100644
--- a/apps/web/hooks/domains/session/use-task-environment.ts
+++ b/apps/web/hooks/domains/session/use-task-environment.ts
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "@/lib/toast/sonner";
+import { t } from "@/lib/i18n";
import {
fetchTaskEnvironmentLive,
@@ -101,15 +102,15 @@ export function useTaskEnvironment(taskId: string | null | undefined, active: bo
setIsResetting(true);
try {
await resetTaskEnvironment(taskId, { push_branch: pushBranch });
- toast.success("Environment reset");
+ toast.success(t("task:environmentReset"));
lastStatusRef.current = getEnvironmentStatusSnapshot(null, null);
setEnv(null);
setContainer(null);
setSsh(null);
return true;
} catch (err) {
- const msg = err instanceof Error ? err.message : "Unknown error";
- toast.error(`Reset failed: ${msg}`);
+ const msg = err instanceof Error ? err.message : t("task:unknownError");
+ toast.error(t("task:environmentResetFailed", { error: msg }));
return false;
} finally {
setIsResetting(false);
@@ -132,10 +133,10 @@ function maybeNotifyEnvironmentStatus(
) {
if (!prev || prev.key === next.key) return;
if (prev.tone === "running" && next.tone !== "running") {
- toast.error("Executor environment stopped", {
- description: `Current state: ${next.label}`,
+ toast.error(t("task:executorEnvironmentStopped"), {
+ description: t("task:environmentStateCurrent", { state: next.label }),
});
} else if (prev.tone !== "running" && next.tone === "running") {
- toast.success("Executor environment running");
+ toast.success(t("task:executorEnvironmentRunning"));
}
}
diff --git a/apps/web/hooks/domains/session/use-terminals.ts b/apps/web/hooks/domains/session/use-terminals.ts
index f729b64bda..2566545f10 100644
--- a/apps/web/hooks/domains/session/use-terminals.ts
+++ b/apps/web/hooks/domains/session/use-terminals.ts
@@ -23,6 +23,7 @@ import type { UserShellInfo } from "@/lib/state/slices/session-runtime/types";
import type { RepositoryScript } from "@/lib/types/http";
import type { Dispatch, SetStateAction, MouseEvent } from "react";
import type { PreviewStage } from "@/lib/state/slices";
+import { t } from "@/lib/i18n";
export type { Terminal, TerminalType };
@@ -168,7 +169,7 @@ function useAddTerminal({
const newTerm: Terminal = {
id: result.terminalId,
type: "shell",
- label: result.displayName ?? result.label ?? "Terminal",
+ label: result.displayName ?? result.label ?? t("common:terminal"),
closable: true,
kind: ordinary ? "ordinary" : result.kind,
seq: result.seq,
@@ -440,7 +441,7 @@ function useTerminalActions({
const newTerm: Terminal = {
id: result.terminalId,
type: "script",
- label: result.label ?? script.name ?? "Script",
+ label: result.label ?? script.name ?? t("common:script"),
closable: true,
kind: "script",
};
diff --git a/apps/web/hooks/domains/session/use-user-shells.ts b/apps/web/hooks/domains/session/use-user-shells.ts
index 73790ce211..dae8a5bc3b 100644
--- a/apps/web/hooks/domains/session/use-user-shells.ts
+++ b/apps/web/hooks/domains/session/use-user-shells.ts
@@ -7,6 +7,7 @@ import type {
UserShellState,
UserShellPTYStatus,
} from "@/lib/state/slices";
+import { t } from "@/lib/i18n";
interface UseUserShellsReturn {
shells: UserShellInfo[];
@@ -179,7 +180,7 @@ function mapListItemToShell(s: ListResponseItem): UserShellInfo {
ptyStatus: s.pty_status,
processId: s.process_id,
running: s.running ?? s.pty_status === "running",
- label: s.label || s.display_name || "Terminal",
+ label: s.label || s.display_name || t("common:terminal"),
closable: s.closable ?? true,
initialCommand: s.initial_command,
};
diff --git a/apps/web/hooks/use-editor-keybinds.ts b/apps/web/hooks/use-editor-keybinds.ts
index e6e96d5cd8..ed03dbcd53 100644
--- a/apps/web/hooks/use-editor-keybinds.ts
+++ b/apps/web/hooks/use-editor-keybinds.ts
@@ -8,6 +8,7 @@ import { createUserShell } from "@/lib/api/domains/user-shell-api";
import { matchesShortcut } from "@/lib/keyboard/utils";
import { getShortcut, type StoredShortcutOverrides } from "@/lib/keyboard/shortcut-overrides";
import type { DockviewApi } from "dockview-react";
+import { t } from "@/lib/i18n";
function handleTabNavigation(e: KeyboardEvent, api: DockviewApi) {
const activePanel = api.activePanel;
@@ -66,7 +67,7 @@ function handleTerminalToggle(
createUserShell(environmentId, { taskId: taskID ?? undefined })
.then((result) => {
- const title = result.displayName ?? result.label ?? "Terminal";
+ const title = result.displayName ?? result.label ?? t("common:terminal");
useDockviewStore
.getState()
.addTerminalPanel(result.terminalId, undefined, environmentId, taskID ?? undefined, title);
diff --git a/apps/web/hooks/use-file-save-delete.ts b/apps/web/hooks/use-file-save-delete.ts
index 0480248f0a..b29eccf4b3 100644
--- a/apps/web/hooks/use-file-save-delete.ts
+++ b/apps/web/hooks/use-file-save-delete.ts
@@ -8,6 +8,7 @@ import { generateUnifiedDiff, calculateHash } from "@/lib/utils/file-diff";
import type { useToast } from "@/components/toast-provider";
import { buildRepoScopedItemId, PREVIEW_FILE_EDITOR_ID } from "@/lib/state/dockview-panel-actions";
import { lspClientManager } from "@/lib/lsp/lsp-client-manager";
+import { t } from "@/lib/i18n";
/** Read openFiles from the store without subscribing to changes. */
function getOpenFiles() {
@@ -96,23 +97,22 @@ async function performSaveFile(path: string, repo: string | undefined, params: S
if (stillClean) updatePanelAfterSave(file.path, file.name, file.repo);
if (response.resolution === "overwritten") {
params.toast({
- title: "File saved (overwritten)",
- description: "The file was modified externally. Your version was saved.",
+ title: t("editors:fileSavedOverwritten"),
+ description: t("editors:fileSavedOverwrittenDescription"),
variant: "default",
});
}
} else {
params.toast({
- title: "Save failed",
- description: response.error || "Failed to save file",
+ title: t("editors:saveFailed"),
+ description: response.error || t("editors:failedToSaveFile"),
variant: "error",
});
}
} catch (error) {
params.toast({
- title: "Save failed",
- description:
- error instanceof Error ? error.message : "An error occurred while saving the file",
+ title: t("editors:saveFailed"),
+ description: error instanceof Error ? error.message : t("editors:errorWhileSavingFile"),
variant: "error",
});
} finally {
@@ -143,17 +143,16 @@ export function useSaveDeleteActions(params: SaveDeleteParams) {
const response = await deleteFile(client, currentSessionId, path, fileRepo);
if (!response.success) {
toast({
- title: "Delete failed",
- description: response.error || "Failed to delete file",
+ title: t("editors:deleteFailed"),
+ description: response.error || t("editors:failedToDeleteFile"),
variant: "error",
});
return;
}
} catch (error) {
toast({
- title: "Delete failed",
- description:
- error instanceof Error ? error.message : "An error occurred while deleting the file",
+ title: t("editors:deleteFailed"),
+ description: error instanceof Error ? error.message : t("editors:errorWhileDeletingFile"),
variant: "error",
});
return;
diff --git a/apps/web/hooks/use-git-with-feedback.ts b/apps/web/hooks/use-git-with-feedback.ts
index 21727d3684..b74edfd9bf 100644
--- a/apps/web/hooks/use-git-with-feedback.ts
+++ b/apps/web/hooks/use-git-with-feedback.ts
@@ -1,44 +1,71 @@
import { useCallback } from "react";
+import { useTranslation } from "react-i18next";
import { useToast } from "@/components/toast-provider";
type GitOperationResult = { success: boolean; output: string; error?: string };
+/**
+ * Resolve an operation name for `useGitWithFeedback`, optionally scoped to one
+ * repository in a multi-repo task. The parenthesised repo form is a catalog
+ * entry rather than a template literal so a locale can move or re-punctuate it.
+ */
+export function gitOperationLabel(
+ // Deliberately not `TFunction`: callers include plain helpers that use the
+ // module-level `t` from `@/lib/i18n`, whose signature is narrower.
+ translate: (key: string, values?: Record) => string,
+ operationKey: string,
+ repo?: string,
+): string {
+ const operation = translate(operationKey);
+ return repo ? translate("common:gitOperationScoped", { operation, repo }) : operation;
+}
+
/**
* Wraps a git operation with toast feedback (loading → success/error).
+ *
+ * `operationName` is the ALREADY-TRANSLATED name of the operation ("Push",
+ * "Rebase (kandev)"), which this hook interpolates into its own catalog
+ * messages. Callers resolve it through `gitOperationLabel` below rather than
+ * passing a literal: the three call sites used to pass English on purpose,
+ * because this hook concatenated English around it and translating only one
+ * half would have shipped a mixed-language toast.
*/
export function useGitWithFeedback() {
+ const { t } = useTranslation();
const { toast, updateToast } = useToast();
const run = useCallback(
async (operation: () => Promise, operationName: string) => {
const toastId = toast({
- title: `${operationName}...`,
+ title: t("common:gitOperationRunning", { operation: operationName }),
variant: "loading",
});
try {
const result = await operation();
if (result.success) {
updateToast(toastId, {
- title: `${operationName} successful`,
- description: result.output.slice(0, 200) || `${operationName} completed`,
+ title: t("common:gitOperationSucceeded", { operation: operationName }),
+ description:
+ result.output.slice(0, 200) ||
+ t("common:gitOperationCompleted", { operation: operationName }),
variant: "success",
});
} else {
updateToast(toastId, {
- title: `${operationName} failed`,
- description: result.error || "An error occurred",
+ title: t("common:gitOperationFailed", { operation: operationName }),
+ description: result.error || t("common:anErrorOccurred"),
variant: "error",
});
}
} catch (e) {
updateToast(toastId, {
- title: `${operationName} failed`,
- description: e instanceof Error ? e.message : "An unexpected error occurred",
+ title: t("common:gitOperationFailed", { operation: operationName }),
+ description: e instanceof Error ? e.message : t("common:anUnexpectedErrorOccurred"),
variant: "error",
});
}
},
- [toast, updateToast],
+ [t, toast, updateToast],
);
return run;
diff --git a/apps/web/hooks/use-summarize-session.ts b/apps/web/hooks/use-summarize-session.ts
index d8f7846487..c5ad7977a3 100644
--- a/apps/web/hooks/use-summarize-session.ts
+++ b/apps/web/hooks/use-summarize-session.ts
@@ -2,6 +2,7 @@ import { useCallback, useState } from "react";
import { executeUtilityPrompt } from "@/lib/api/domains/utility-api";
import { listTaskSessionMessages } from "@/lib/api/domains/session-api";
import type { Message } from "@/lib/types/http";
+import { t } from "@/lib/i18n";
export type SummarizeSessionResult = {
summary: string | null;
@@ -39,7 +40,7 @@ export function useSummarizeSession() {
conversation_history: transcript,
});
if (!result.success) {
- return { summary: null, error: result.error || "Summarize utility returned no result" };
+ return { summary: null, error: result.error || t("task:summarizeReturnedNoResult") };
}
return { summary: result.response ?? null };
} catch (error) {
diff --git a/apps/web/hooks/use-update-available-toast.ts b/apps/web/hooks/use-update-available-toast.ts
index 3db6691e86..b1489dd66e 100644
--- a/apps/web/hooks/use-update-available-toast.ts
+++ b/apps/web/hooks/use-update-available-toast.ts
@@ -4,6 +4,7 @@ import { useEffect, useRef } from "react";
import { useAppStore } from "@/components/state-provider";
import { useToast } from "@/components/toast-provider";
import { nativeNotifications } from "@/lib/desktop/native-notification-client";
+import { t } from "@/lib/i18n";
export function useUpdateAvailableToast() {
const notification = useAppStore((s) => s.updateAvailableNotification);
@@ -19,7 +20,7 @@ export function useUpdateAvailableToast() {
}
shownRef.current.add(notification.version);
- const title = notification.title || "Kandev update available";
+ const title = notification.title || t("task:kandevUpdateAvailable");
const body =
notification.body ||
`Kandev ${notification.version} is available. Open Settings > System > Updates to review it.`;
diff --git a/apps/web/hooks/use-utility-agent-generator.ts b/apps/web/hooks/use-utility-agent-generator.ts
index 980b311ad8..d643d8e80d 100644
--- a/apps/web/hooks/use-utility-agent-generator.ts
+++ b/apps/web/hooks/use-utility-agent-generator.ts
@@ -1,6 +1,7 @@
"use client";
import { useState, useCallback } from "react";
+import { useTranslation } from "react-i18next";
import { executeUtilityPrompt, type ExecutePromptRequest } from "@/lib/api/domains/utility-api";
import { useToast } from "@/components/toast-provider";
import { useSessionGitStatus } from "@/hooks/domains/session/use-session-git-status";
@@ -52,6 +53,7 @@ export function useUtilityAgentGenerator({
taskDescription,
}: UseUtilityAgentGeneratorOptions) {
const [generating, setGenerating] = useState>(new Set());
+ const { t } = useTranslation();
const { toast } = useToast();
const gitStatus = useSessionGitStatus(sessionId);
@@ -103,8 +105,8 @@ export function useUtilityAgentGenerator({
// Other generators need git context from an active session.
if (!sessionId && type !== ENHANCE_PROMPT) {
toast({
- title: "No active session",
- description: "Start a session first to use AI generation",
+ title: t("task:noActiveSession"),
+ description: t("task:noActiveSessionDescription"),
variant: "error",
});
return;
@@ -115,8 +117,8 @@ export function useUtilityAgentGenerator({
const resp = await executeUtilityPrompt(buildRequest(type, options));
if (!resp.success || !resp.response) {
toast({
- title: "Generation failed",
- description: resp.error || "Failed to generate content",
+ title: t("task:generationFailed"),
+ description: resp.error || t("task:failedToGenerateContent"),
variant: "error",
});
return;
@@ -128,15 +130,15 @@ export function useUtilityAgentGenerator({
});
} catch (error) {
toast({
- title: "Generation failed",
- description: error instanceof Error ? error.message : "Unknown error",
+ title: t("task:generationFailed"),
+ description: error instanceof Error ? error.message : t("task:unknownError"),
variant: "error",
});
} finally {
clearType(type);
}
},
- [sessionId, buildRequest, clearType, toast],
+ [sessionId, buildRequest, clearType, t, toast],
);
return useGeneratorCallbacks(generate, generating);
diff --git a/apps/web/lib/agent-runtime-update.test.ts b/apps/web/lib/agent-runtime-update.test.ts
index 7d97628c7d..aef375c17c 100644
--- a/apps/web/lib/agent-runtime-update.test.ts
+++ b/apps/web/lib/agent-runtime-update.test.ts
@@ -1,4 +1,5 @@
-import { describe, expect, it } from "vitest";
+import { afterEach, describe, expect, it } from "vitest";
+import i18n from "i18next";
import type { AgentUpdateJob, AgentUpdatePreview } from "@/lib/api";
import { canApproveAgentRuntimeUpdate, resolveRuntimeVersionPair } from "./agent-runtime-update";
@@ -27,12 +28,18 @@ describe("resolveRuntimeVersionPair", () => {
preview(),
job({ current_version: "0.63.0", target_version: "0.63.0" }),
),
- ).toEqual({ currentVersion: "0.63.0", targetVersion: "0.63.0", versionsMatch: true });
+ ).toEqual({
+ currentVersion: "0.63.0",
+ hasCurrentVersion: true,
+ targetVersion: "0.63.0",
+ versionsMatch: true,
+ });
});
it("falls back to preview versions before a job exists", () => {
expect(resolveRuntimeVersionPair(preview())).toEqual({
currentVersion: "0.62.0",
+ hasCurrentVersion: true,
targetVersion: "0.63.0",
versionsMatch: false,
});
@@ -69,3 +76,50 @@ describe("canApproveAgentRuntimeUpdate", () => {
expect(canApproveAgentRuntimeUpdate(state)).toBe(expected);
});
});
+
+/**
+ * The missing-version predicate used to be `currentVersion !== "Unknown"`, a
+ * comparison against the English placeholder. Once `currentVersion` became
+ * localized, that test was true in every other locale, so a missing version read
+ * as known and the approval gate opened. These pin the state to a flag and the
+ * string to display only.
+ */
+describe("missing current version under a non-English locale", () => {
+ afterEach(async () => {
+ await i18n.changeLanguage("en");
+ });
+
+ const missing = {
+ preview: preview({ current_version: "" }),
+ previewError: null,
+ loading: false,
+ updateInFlight: false,
+ starting: false,
+ installInFlight: false,
+ };
+
+ it.each(["en", "pt-pt", "zh-cn", "pseudo"])("stays un-approvable in %s", async (locale) => {
+ await i18n.changeLanguage(locale);
+
+ const pair = resolveRuntimeVersionPair(preview({ current_version: "" }));
+ expect(pair.hasCurrentVersion).toBe(false);
+ expect(canApproveAgentRuntimeUpdate(missing)).toBe(false);
+ });
+
+ it("still shows a localized placeholder to the user", async () => {
+ await i18n.changeLanguage("pseudo");
+ const pair = resolveRuntimeVersionPair(preview({ current_version: "" }));
+
+ expect(pair.currentVersion).not.toBe("Unknown");
+ expect(pair.currentVersion).toMatch(/[^\x20-\x7E]/);
+ });
+
+ it("does not treat a localized placeholder as matching a target version", async () => {
+ await i18n.changeLanguage("pt-pt");
+ const pair = resolveRuntimeVersionPair(
+ preview({ current_version: "", target_version: "0.63.0" }),
+ );
+
+ expect(pair.versionsMatch).toBe(false);
+ });
+});
diff --git a/apps/web/lib/agent-runtime-update.ts b/apps/web/lib/agent-runtime-update.ts
index 210cd44fcc..a0b083c734 100644
--- a/apps/web/lib/agent-runtime-update.ts
+++ b/apps/web/lib/agent-runtime-update.ts
@@ -1,7 +1,18 @@
import type { AgentUpdateJob, AgentUpdatePreview } from "@/lib/api";
+import { t } from "@/lib/i18n";
export type RuntimeVersionPair = {
+ /** Display copy — localized, and therefore never compared. */
currentVersion: string;
+ /**
+ * Whether a real version was reported. The predicate this replaced was
+ * `currentVersion !== "Unknown"`, which compared against the English
+ * placeholder; once `currentVersion` became localized that test was true in
+ * every other locale, so a missing version read as known and the update
+ * gate opened. docs/i18n.md calls this out under "Do not translate" — the
+ * flag is the state, the string is only for the user.
+ */
+ hasCurrentVersion: boolean;
targetVersion?: string;
versionsMatch: boolean;
};
@@ -10,12 +21,11 @@ export function resolveRuntimeVersionPair(
preview: AgentUpdatePreview | null,
job?: AgentUpdateJob,
): RuntimeVersionPair {
- const currentVersion = job?.current_version || preview?.current_version || "Unknown";
+ const reported = job?.current_version || preview?.current_version;
+ const currentVersion = reported || t("common:unknown");
const targetVersion = job?.target_version || preview?.target_version;
- const versionsMatch = Boolean(
- targetVersion && currentVersion !== "Unknown" && currentVersion === targetVersion,
- );
- return { currentVersion, targetVersion, versionsMatch };
+ const versionsMatch = Boolean(targetVersion && reported && reported === targetVersion);
+ return { currentVersion, hasCurrentVersion: Boolean(reported), targetVersion, versionsMatch };
}
export function canApproveAgentRuntimeUpdate({
@@ -35,11 +45,12 @@ export function canApproveAgentRuntimeUpdate({
starting: boolean;
installInFlight: boolean;
}): boolean {
- const { currentVersion, targetVersion } = resolveRuntimeVersionPair(preview, job);
+ const { currentVersion, hasCurrentVersion, targetVersion } = resolveRuntimeVersionPair(
+ preview,
+ job,
+ );
return (
- Boolean(
- preview && currentVersion !== "Unknown" && targetVersion && currentVersion !== targetVersion,
- ) &&
+ Boolean(preview && hasCurrentVersion && targetVersion && currentVersion !== targetVersion) &&
!previewError &&
!loading &&
!updateInFlight &&
diff --git a/apps/web/lib/api/domains/plan-api.ts b/apps/web/lib/api/domains/plan-api.ts
index 488e1bf055..29c050e4b3 100644
--- a/apps/web/lib/api/domains/plan-api.ts
+++ b/apps/web/lib/api/domains/plan-api.ts
@@ -35,6 +35,9 @@ export async function createTaskPlan(
}
const response = await client.request("task.plan.create", {
task_id: taskId,
+ // Canonical English on purpose: the backend persists this into
+ // `task_plans.title` / `task_plan_revisions.title`, so a localized default
+ // would freeze whichever locale created the plan into the stored record.
title: title || "Plan",
content,
created_by: "user",
diff --git a/apps/web/lib/capability-warning.ts b/apps/web/lib/capability-warning.ts
index 5f88ab55a8..16ff41f9ed 100644
--- a/apps/web/lib/capability-warning.ts
+++ b/apps/web/lib/capability-warning.ts
@@ -1,5 +1,6 @@
import { IconAlertTriangle, IconLock } from "@tabler/icons-react";
import type { CapabilityStatus } from "@/lib/types/http";
+import { t } from "@/lib/i18n";
export type CapabilityWarning = {
Icon: typeof IconLock;
@@ -16,19 +17,19 @@ export function getCapabilityWarning(
return {
Icon: IconLock,
color: "text-amber-600 dark:text-amber-400",
- title: error || "Authentication required",
+ title: error || t("agents:authenticationRequired"),
};
case "not_installed":
return {
Icon: IconAlertTriangle,
color: "text-muted-foreground",
- title: error || "Agent CLI not installed",
+ title: error || t("task:agentCliNotInstalled"),
};
case "failed":
return {
Icon: IconAlertTriangle,
color: "text-red-600 dark:text-red-400",
- title: error || "Agent probe failed",
+ title: error || t("task:agentProbeFailed"),
};
default:
return null;
diff --git a/apps/web/lib/github-auth.ts b/apps/web/lib/github-auth.ts
index 901dae4400..8dc01544d7 100644
--- a/apps/web/lib/github-auth.ts
+++ b/apps/web/lib/github-auth.ts
@@ -1,4 +1,5 @@
import type { GitHubStatus } from "@/lib/types/github";
+import { t } from "@/lib/i18n";
export function hasGitHubPersonalActor(status: GitHubStatus | null): boolean {
const actor = status?.effective_personal_actor;
@@ -18,7 +19,7 @@ export function getGitHubPersonalIdentityState(status: GitHubStatus): GitHubPers
active && principal?.source === "github_app_user" && status.personal?.status === "active",
);
let actor = "Unavailable";
- if (active) actor = principal?.login ?? "Unavailable";
+ if (active) actor = principal?.login ?? t("common:unavailable");
else if (status.automation?.source === "github_app_installation") actor = "Not connected";
return { active, actor, personalOAuthActive };
}
diff --git a/apps/web/lib/layout/layout-profiles.ts b/apps/web/lib/layout/layout-profiles.ts
index 27e9c81491..660172e13e 100644
--- a/apps/web/lib/layout/layout-profiles.ts
+++ b/apps/web/lib/layout/layout-profiles.ts
@@ -16,34 +16,69 @@ export type BuiltInLayoutProfileId = Exclude;
export type BuiltInLayoutProfileDescriptor = {
id: BuiltInLayoutProfileId;
+ /** Canonical English; persisted into a saved override. Not for display. */
name: string;
+ /** Catalog key the settings list renders. Absent for product names. */
+ nameKey?: string;
description: string;
+ descriptionKey?: string;
};
+/**
+ * Display name for a built-in profile, in the active locale.
+ *
+ * `descriptor.name` is the canonical English value `upsertBuiltInLayoutOverride`
+ * persists; every surface that shows the name to a user goes through here so the
+ * two cannot drift.
+ */
+export function builtInLayoutProfileName(
+ descriptor: Pick,
+ translate: (key: string) => string,
+): string {
+ return descriptor.nameKey ? translate(descriptor.nameKey) : descriptor.name;
+}
+
export type BuiltInLayoutProfile = BuiltInLayoutProfileDescriptor & {
layout: LayoutState;
};
+/**
+ * `name` is BOTH display copy and persisted data: `upsertBuiltInLayoutOverride`
+ * copies it into the saved record the first time a built-in is customized. It
+ * therefore stays canonical English, and `nameKey`/`descriptionKey` are what the
+ * settings list renders — the same split the dockview panel registry uses. A
+ * translated `name` here would write the current locale into a user's saved
+ * layouts and leave it there after a switch.
+ *
+ * "VS Code" is a product name and so has no `nameKey`.
+ */
export const BUILT_IN_LAYOUT_PROFILES: readonly BuiltInLayoutProfileDescriptor[] = [
{
id: "default",
name: "Default",
+ nameKey: "settings:layoutProfileDefault",
description: "Agent with Files, Changes, and Terminal",
+ descriptionKey: "settings:layoutProfileDefaultDescription",
},
{
id: "plan",
name: "Plan Mode",
+ nameKey: "settings:layoutProfilePlanMode",
description: "Agent and Plan side by side",
+ descriptionKey: "settings:layoutProfilePlanModeDescription",
},
{
id: "preview",
name: "Preview Mode",
+ nameKey: "settings:layoutProfilePreviewMode",
description: "Agent and Browser side by side",
+ descriptionKey: "settings:layoutProfilePreviewModeDescription",
},
{
id: "vscode",
name: "VS Code",
description: "Agent and VS Code side by side",
+ descriptionKey: "settings:layoutProfileVsCodeDescription",
},
];
diff --git a/apps/web/lib/recent-tasks.ts b/apps/web/lib/recent-tasks.ts
index b595c84862..14b2492614 100644
--- a/apps/web/lib/recent-tasks.ts
+++ b/apps/web/lib/recent-tasks.ts
@@ -1,4 +1,5 @@
import type { TaskSessionState, TaskState } from "@/lib/types/http";
+import { t } from "@/lib/i18n";
export const RECENT_TASKS_STORAGE_KEY = "kandev.recentTasks.v1";
export const RECENT_TASKS_CHANGED_EVENT = "kandev:recent-tasks-changed";
@@ -33,7 +34,7 @@ function normalizeEntry(value: unknown): RecentTaskEntry | null {
if (!isRecord(value)) return null;
const taskId = optionalString(value.taskId);
if (!taskId) return null;
- const title = optionalString(value.title) ?? "Untitled task";
+ const title = optionalString(value.title) ?? t("common:untitledTask");
const visitedAt = optionalString(value.visitedAt) ?? new Date(0).toISOString();
return {
diff --git a/apps/web/lib/state/dockview-panel-actions.ts b/apps/web/lib/state/dockview-panel-actions.ts
index 7bdca38312..1dd1f47718 100644
--- a/apps/web/lib/state/dockview-panel-actions.ts
+++ b/apps/web/lib/state/dockview-panel-actions.ts
@@ -2,6 +2,8 @@ import type { DockviewApi, DockviewGroupPanel } from "dockview-react";
import type { CommitDetailTarget } from "@/components/task/changes-diff-target";
import { t } from "@/lib/i18n";
import { focusOrAddPanel } from "./dockview-layout-builders";
+import { TERMINAL_DEFAULT_ID } from "./layout-manager/constants";
+import { panelTitle } from "./layout-manager/panel-title";
import {
parsePluginPanelId,
pluginPanelId,
@@ -365,7 +367,7 @@ function buildFileDiffAction(get: StoreGet) {
api,
type: "file-diff",
itemId,
- title: `Diff [${getFileName(path)}]`,
+ title: t("task:panelFileDiff", { file: getFileName(path) }),
params: {
kind: "file",
path,
@@ -439,7 +441,7 @@ export function buildPanelActions(set: StoreSet, get: StoreGet) {
id: "chat",
component: "chat",
tabComponent: "permanentTab",
- title: "Agent",
+ title: panelTitle("chat"),
position: { referenceGroup: centerGroupId },
});
},
@@ -449,7 +451,7 @@ export function buildPanelActions(set: StoreSet, get: StoreGet) {
addSimplePanel(api, groupId ?? rightTopGroupId, {
id: "changes",
component: "changes",
- title: "Changes",
+ title: panelTitle("changes"),
tabComponent: "changesTab",
});
},
@@ -459,7 +461,7 @@ export function buildPanelActions(set: StoreSet, get: StoreGet) {
addSimplePanel(api, groupId ?? rightTopGroupId, {
id: "files",
component: "files",
- title: "Files",
+ title: panelTitle("files"),
});
},
addDiffViewerPanel: (path?: string, content?: string, groupId?: string) => {
@@ -469,7 +471,7 @@ export function buildPanelActions(set: StoreSet, get: StoreGet) {
addSimplePanel(api, groupId ?? centerGroupId, {
id: "diff-viewer",
component: "diff-viewer",
- title: "Diff Viewer",
+ title: t("task:panelDiffViewer"),
params: { kind: "all" },
});
},
@@ -483,7 +485,7 @@ export function buildPanelActions(set: StoreSet, get: StoreGet) {
addSimplePanel(api, groupId ?? centerGroupId, {
id: browserId,
component: "browser",
- title: t("task:browser"),
+ title: panelTitle("browser"),
params: { url: url ?? "" },
});
},
@@ -549,7 +551,7 @@ function buildReviewPanelActions(get: StoreGet) {
focusOrAddPanel(api, {
id,
component: "pr-detail",
- title: prKey ? "Pull Request" : "PR Details",
+ title: prKey ? t("task:panelPullRequest") : panelTitle("pr-detail"),
position: { referenceGroup: targetGroupId },
params: prKey ? { prKey } : undefined,
});
@@ -572,7 +574,7 @@ function buildReviewPanelActions(get: StoreGet) {
focusOrAddPanel(api, {
id,
component: "mr-detail",
- title: "Merge Request",
+ title: panelTitle("mr-detail"),
position: { referenceGroup: canonical?.group.id ?? centerGroupId },
params: { mrKey },
});
@@ -588,7 +590,7 @@ export function buildExtraPanelActions(get: StoreGet) {
focusOrAddPanel(api, {
id: "vscode",
component: "vscode",
- title: "VS Code",
+ title: panelTitle("vscode"),
position: { referenceGroup: centerGroupId },
});
},
@@ -603,7 +605,7 @@ export function buildExtraPanelActions(get: StoreGet) {
focusOrAddPanel(api, {
id: "vscode",
component: "vscode",
- title: "VS Code",
+ title: panelTitle("vscode"),
position: { referenceGroup: centerGroupId },
});
},
@@ -613,7 +615,7 @@ export function buildExtraPanelActions(get: StoreGet) {
addSidePanel(
api,
centerGroupId,
- { id: "plan", component: "plan", title: "Plan", tabComponent: "planTab" },
+ { id: "plan", component: "plan", title: panelTitle("plan"), tabComponent: "planTab" },
opts,
);
},
@@ -646,7 +648,7 @@ export function buildExtraPanelActions(get: StoreGet) {
addSidePanel(
api,
centerGroupId,
- { id: "todos", component: "todos", title: t("common:todos") },
+ { id: "todos", component: "todos", title: panelTitle("todos") },
opts,
);
},
@@ -673,7 +675,7 @@ export function buildExtraPanelActions(get: StoreGet) {
// when there's more than one ordinary terminal in the task and
// exposes a context menu for rename / park / destroy.
tabComponent: "terminalTab",
- title: title ?? "Terminal",
+ title: title ?? panelTitle(TERMINAL_DEFAULT_ID),
params: { terminalId: id, environmentId, taskID },
});
},
diff --git a/apps/web/lib/state/layout-manager/constants.ts b/apps/web/lib/state/layout-manager/constants.ts
index 4824d59782..3d4a2c522e 100644
--- a/apps/web/lib/state/layout-manager/constants.ts
+++ b/apps/web/lib/state/layout-manager/constants.ts
@@ -64,13 +64,48 @@ export const STRUCTURAL_COMPONENTS = new Set([
"plugin-panel",
]);
-/** Default panel configurations for known panels. */
-export const PANEL_REGISTRY: Record> = {
- chat: { component: "chat", title: "Agent", tabComponent: "permanentTab" },
- plan: { component: "plan", title: "Plan", tabComponent: "planTab" },
- changes: { component: "changes", title: "Changes", tabComponent: "changesTab" },
- files: { component: "files", title: "Files" },
- browser: { component: "browser", title: "Browser", params: { url: "" } },
+/**
+ * Default panel configurations for known panels.
+ *
+ * `title` and `titleKey` are deliberately two different things:
+ *
+ * - `title` is the CANONICAL, always-English value. `toSerializedDockview`
+ * writes it into the stored layout JSON, so it has to be locale-independent
+ * — otherwise the locale a layout happened to be saved in is frozen into it
+ * and survives every later switch.
+ * - `titleKey` is what the user reads. `panelTitle()` resolves it whenever a
+ * panel is built or a saved layout is restored, so the tab always shows the
+ * ACTIVE locale regardless of which one wrote the layout.
+ *
+ * This is the key/persisted-value split docs/i18n.md prescribes for a display
+ * string that is also stored. Before it, two of these titles were translated at
+ * creation (`t("task:browser")`, `t("common:todos")`) and the rest were not, so
+ * the tab bar was half-translated AND persisted whichever locale created it.
+ *
+ * `vscode` has no `titleKey` on purpose: "VS Code" is a product name, and
+ * `panelTitle()` falls back to `title` when a key is absent.
+ */
+export const PANEL_REGISTRY: Record & { titleKey?: string }> = {
+ chat: {
+ component: "chat",
+ title: "Agent",
+ titleKey: "task:panelAgent",
+ tabComponent: "permanentTab",
+ },
+ plan: { component: "plan", title: "Plan", titleKey: "task:panelPlan", tabComponent: "planTab" },
+ changes: {
+ component: "changes",
+ title: "Changes",
+ titleKey: "task:panelChanges",
+ tabComponent: "changesTab",
+ },
+ files: { component: "files", title: "Files", titleKey: "task:panelFiles" },
+ browser: {
+ component: "browser",
+ title: "Browser",
+ titleKey: "task:browser",
+ params: { url: "" },
+ },
vscode: { component: "vscode", title: "VS Code" },
[TERMINAL_DEFAULT_ID]: {
component: "terminal",
@@ -81,18 +116,58 @@ export const PANEL_REGISTRY: Record> = {
// session-page mount so the badge logic has a seq to display.
tabComponent: "terminalTab",
title: "Terminal",
+ titleKey: "task:panelTerminal",
params: { terminalId: "shell-default" },
},
- "pr-detail": { component: "pr-detail", title: "PR Details" },
- "mr-detail": { component: "mr-detail", title: "Merge Request" },
- todos: { component: "todos", title: "Todos" },
+ "pr-detail": { component: "pr-detail", title: "PR Details", titleKey: "task:panelPrDetails" },
+ "mr-detail": {
+ component: "mr-detail",
+ title: "Merge Request",
+ titleKey: "task:panelMergeRequest",
+ },
+ todos: { component: "todos", title: "Todos", titleKey: "common:todos" },
};
-/** Create a LayoutPanel from the registry by ID. */
+/**
+ * `panelTitle()` — the locale-resolved counterpart of `canonicalPanelTitle()` —
+ * lives in `./panel-title.ts`. It needs `@/lib/i18n`, and this module must stay
+ * loadable from plain Node because e2e specs import constants from it; see that
+ * file's comment.
+ */
+
+/**
+ * Registry entry for a panel.
+ *
+ * Falls back to the COMPONENT when the id is not an exact key, because several
+ * panel families mint dynamic ids from their component — `browser:`,
+ * `pr-detail|`, `mr-detail|`. Matching on the id alone left those
+ * outside the canonical/localized split entirely: they were created with a
+ * localized title, captured verbatim into the saved layout, and then could not
+ * be re-localized on restore, so the layout showed whichever locale created it.
+ */
+export function panelRegistryEntry(
+ id: string,
+ component?: string,
+): (typeof PANEL_REGISTRY)[string] | undefined {
+ return PANEL_REGISTRY[id] ?? (component ? PANEL_REGISTRY[component] : undefined);
+}
+
+/** The canonical English title stored in a saved layout, if the panel is known. */
+export function canonicalPanelTitle(id: string, component?: string): string | undefined {
+ return panelRegistryEntry(id, component)?.title;
+}
+
+/**
+ * Create a LayoutPanel from the registry by ID.
+ *
+ * The title is canonical English: a `LayoutPanel` is the shape that gets
+ * persisted, and `toSerializedDockview` localizes it when it reaches dockview.
+ */
export function panel(id: string): LayoutPanel {
const config = PANEL_REGISTRY[id];
if (!config) throw new Error(`Unknown panel: ${id}`);
- return { id, ...config };
+ const { titleKey: _titleKey, ...rest } = config;
+ return { id, ...rest };
}
/** Generate panel config for a session tab. */
diff --git a/apps/web/lib/state/layout-manager/panel-title.ts b/apps/web/lib/state/layout-manager/panel-title.ts
new file mode 100644
index 0000000000..0e9a3e6f64
--- /dev/null
+++ b/apps/web/lib/state/layout-manager/panel-title.ts
@@ -0,0 +1,30 @@
+import { t } from "@/lib/i18n";
+
+import { panelRegistryEntry } from "./constants";
+
+/**
+ * The display title for a registry panel, in the active locale.
+ *
+ * Deliberately NOT in `constants.ts`. That module is imported by e2e specs
+ * (`e2e/tests/layout/pane-resize-right.spec.ts` takes `TERMINAL_DEFAULT_ID` from
+ * it), and Playwright loads spec files through Node rather than Vite. Pulling
+ * `@/lib/i18n` into that graph makes the whole file fail to load with
+ * `SyntaxError: Cannot use 'import.meta' outside a module` — and one unloadable
+ * spec aborts the entire run, so it takes every shard down with it, not just
+ * the layout tests. Keeping the i18n dependency in a separate module leaves
+ * `constants.ts` importable from plain Node.
+ *
+ * Resolved at call time, never at module load — the registry stores the key.
+ * Unknown ids (file diffs, plugin panels, per-session chat tabs) carry their own
+ * title and are returned unchanged.
+ *
+ * This is the RENDER direction only. `LayoutState` — what gets saved — always
+ * holds the canonical English title, so the localized form exists solely inside
+ * dockview. `toSerializedDockview` localizes on the way in and
+ * `panelFromDockviewPanel` canonicalizes on the way back out.
+ */
+export function panelTitle(id: string, fallback?: string, component?: string): string {
+ const config = panelRegistryEntry(id, component);
+ if (!config) return fallback ?? id;
+ return config.titleKey ? t(config.titleKey) : config.title;
+}
diff --git a/apps/web/lib/state/layout-manager/panel-titles.test.ts b/apps/web/lib/state/layout-manager/panel-titles.test.ts
new file mode 100644
index 0000000000..c01d8b1931
--- /dev/null
+++ b/apps/web/lib/state/layout-manager/panel-titles.test.ts
@@ -0,0 +1,113 @@
+import { afterEach, describe, expect, it } from "vitest";
+import i18n from "i18next";
+
+import { canonicalPanelTitle, panel, PANEL_REGISTRY } from "./constants";
+import { panelTitle } from "./panel-title";
+import { toSerializedDockview } from "./serializer";
+import type { LayoutState } from "./types";
+
+/**
+ * Panel titles are BOTH display copy and persisted data: `toSerializedDockview`
+ * writes them into the stored layout JSON. That is why the registry keeps the
+ * two apart — `title` is canonical English for storage, `titleKey` resolves at
+ * render for the tab bar.
+ *
+ * Before the split, two titles were translated at creation and the rest were
+ * not, so a saved layout captured whichever locale created it and kept showing
+ * it after a switch.
+ */
+afterEach(async () => {
+ await i18n.changeLanguage("en");
+});
+
+function layoutWith(panelIds: string[]): LayoutState {
+ return {
+ columns: [{ id: "col", groups: [{ id: "group", panels: panelIds.map(panel) }] }],
+ } as unknown as LayoutState;
+}
+
+describe("panelTitle", () => {
+ it("resolves a registry panel through the catalog", async () => {
+ expect(panelTitle("changes")).toBe("Changes");
+
+ await i18n.changeLanguage("pseudo");
+ const pseudo = panelTitle("changes");
+
+ expect(pseudo).not.toBe("Changes");
+ expect(pseudo).toMatch(/[^\x20-\x7E]/);
+ });
+
+ it("falls back to the given title for a panel the registry does not know", () => {
+ expect(panelTitle("diff:src/app.ts", "Diff [app.ts]")).toBe("Diff [app.ts]");
+ });
+
+ const BROWSER_PANEL_ID = "browser:https://example.com";
+
+ it("resolves a dynamic id through its component", async () => {
+ // `browser:`, `pr-detail|` and `mr-detail|` are minted per
+ // instance, so an exact-id lookup misses them. Without the component
+ // fallback they were created localized, captured verbatim into the saved
+ // layout, and could not be re-localized on restore — the layout then showed
+ // whichever locale had created it.
+ expect(panelTitle(BROWSER_PANEL_ID, undefined, "browser")).toBe("Browser");
+ expect(canonicalPanelTitle(BROWSER_PANEL_ID, "browser")).toBe("Browser");
+ expect(canonicalPanelTitle("mr-detail|kandev!7", "mr-detail")).toBe("Merge Request");
+
+ await i18n.changeLanguage("pseudo");
+ expect(panelTitle(BROWSER_PANEL_ID, undefined, "browser")).not.toBe("Browser");
+ // Storage stays canonical whatever the locale.
+ expect(canonicalPanelTitle(BROWSER_PANEL_ID, "browser")).toBe("Browser");
+ });
+
+ it("leaves a product name untranslated", async () => {
+ await i18n.changeLanguage("pseudo");
+ expect(panelTitle("vscode")).toBe("VS Code");
+ });
+
+ it("gives every registry panel a title that is not the raw key", () => {
+ const leaked = Object.keys(PANEL_REGISTRY).filter((id) => panelTitle(id).includes(":"));
+ expect(leaked).toEqual([]);
+ });
+});
+
+describe("layout round trip", () => {
+ const titlesOf = (state: LayoutState) => {
+ const serialized = toSerializedDockview(state, 1000, 800, new Map());
+ return (serialized as unknown as { panels: Record }).panels;
+ };
+
+ it("localizes on the way into dockview", async () => {
+ // `toSerializedDockview` feeds `api.fromJSON`, so this is what the tab bar
+ // and the layout editor's preview render — it must follow the locale. The
+ // first version of this split had the two directions the wrong way round,
+ // which the pseudo-coverage oracle caught as English tab titles on the
+ // layouts screen while every unit test passed.
+ await i18n.changeLanguage("pseudo");
+ const stored = titlesOf(layoutWith(["chat", "changes", "browser"]));
+
+ for (const title of ["chat", "changes", "browser"].map((id) => stored[id].title)) {
+ expect(title).toMatch(/[^\x20-\x7E]/);
+ }
+ });
+
+ it("keeps a stored LayoutState in canonical English", () => {
+ // `panel()` builds the shape that gets persisted, so it must NOT localize —
+ // otherwise the locale that created a layout is frozen into it.
+ const built = layoutWith(["chat", "changes", "browser"]).columns[0].groups[0].panels;
+ expect(built.map((p) => p.title)).toEqual(["Agent", "Changes", "Browser"]);
+ });
+
+ it("builds the same canonical LayoutState in any locale", async () => {
+ const english = layoutWith(["chat", "changes", "browser"]);
+ await i18n.changeLanguage("pseudo");
+ const pseudo = layoutWith(["chat", "changes", "browser"]);
+
+ expect(pseudo).toEqual(english);
+ });
+
+ it("keeps the canonical title for every registry panel", () => {
+ for (const id of Object.keys(PANEL_REGISTRY)) {
+ expect(canonicalPanelTitle(id)).toBe(PANEL_REGISTRY[id].title);
+ }
+ });
+});
diff --git a/apps/web/lib/state/layout-manager/serializer.ts b/apps/web/lib/state/layout-manager/serializer.ts
index cc8f2be543..ad23c262b9 100644
--- a/apps/web/lib/state/layout-manager/serializer.ts
+++ b/apps/web/lib/state/layout-manager/serializer.ts
@@ -1,7 +1,17 @@
import type { DockviewApi, SerializedDockview } from "dockview-react";
import type { LayoutState, LayoutColumn, LayoutGroup, LayoutPanel, LayoutNode } from "./types";
import { computeColumnWidths, computeGroupHeights } from "./sizing";
-import { SIDEBAR_LOCK, KNOWN_PANEL_IDS, STRUCTURAL_COMPONENTS } from "./constants";
+import {
+ SIDEBAR_LOCK,
+ KNOWN_PANEL_IDS,
+ STRUCTURAL_COMPONENTS,
+ TERMINAL_DEFAULT_ID,
+ canonicalPanelTitle,
+} from "./constants";
+import { panelTitle } from "./panel-title";
+
+/** Canonical English, because `normalizePanel` produces a stored LayoutState. */
+const TERMINAL_CANONICAL_TITLE = canonicalPanelTitle(TERMINAL_DEFAULT_ID) ?? "Terminal";
// Dockview serialized grid node types (internal format)
type SerializedLeafNode = {
@@ -137,7 +147,12 @@ function serializePanels(state: LayoutState): Record<
panels[panel.id] = {
id: panel.id,
contentComponent: panel.component,
- title: panel.title,
+ // RENDER direction: this feeds `api.fromJSON`, so the tab the user
+ // reads is resolved here, in the active locale. `LayoutState` itself
+ // keeps the canonical English title — see `panelFromDockviewPanel`,
+ // which canonicalizes again on capture so a translated title can
+ // never reach storage.
+ title: panelTitle(panel.id, panel.title, panel.component),
...(panel.tabComponent ? { tabComponent: panel.tabComponent } : {}),
...(panel.params ? { params: panel.params } : {}),
};
@@ -184,7 +199,9 @@ function panelFromDockviewPanel(panel: any): LayoutPanel {
return {
id: panel.id,
component: panel.view?.contentComponent ?? panel.id,
- title: panel.title ?? panel.id,
+ // CAPTURE direction: dockview holds the localized title, and this result is
+ // what gets persisted, so a registry panel goes back to canonical English.
+ title: canonicalPanelTitle(panel.id, panel.view?.contentComponent) ?? panel.title ?? panel.id,
...(panel.view?.tabComponent ? { tabComponent: panel.view.tabComponent } : {}),
...(panel.params ? { params: panel.params as Record } : {}),
};
@@ -331,7 +348,7 @@ function normalizePanel(
// terminals (shell-) — preserve id + params so the live PTY
// and store record match on restore. Coerce presentation in case
// the saved layout predates the custom tab + plain title.
- return { ...p, tabComponent: "terminalTab", title: "Terminal" };
+ return { ...p, tabComponent: "terminalTab", title: TERMINAL_CANONICAL_TITLE };
}
// Legacy ephemeral terminal id (e.g. `terminal-saved-1` from older
// layouts) — mint a fresh id so it reconnects to a new PTY.
@@ -341,16 +358,20 @@ function normalizePanel(
id,
params: { terminalId: id },
tabComponent: "terminalTab",
- title: "Terminal",
+ title: TERMINAL_CANONICAL_TITLE,
};
}
- if (KNOWN_PANEL_IDS.has(p.id)) return p;
+ // A layout saved before this split (or by an older build) can carry a
+ // translated title; normalise it back to canonical English so the render
+ // direction is the only place a locale is applied.
+ if (KNOWN_PANEL_IDS.has(p.id))
+ return { ...p, title: canonicalPanelTitle(p.id, p.component) ?? p.title };
if (p.component === "browser") {
const url = (p.params?.url as string) ?? "";
const id = url ? `browser:${url}` : `browser-saved-${++counters.browser}`;
- return { ...p, id, params: { url } };
+ return { ...p, id, params: { url }, title: canonicalPanelTitle(id, p.component) ?? p.title };
}
return p;
}
diff --git a/apps/web/lib/state/slices/comments/format.ts b/apps/web/lib/state/slices/comments/format.ts
index 4f0eb7e06c..1aae13876a 100644
--- a/apps/web/lib/state/slices/comments/format.ts
+++ b/apps/web/lib/state/slices/comments/format.ts
@@ -13,6 +13,7 @@ import {
isPRFeedbackComment,
isWalkthroughComment,
} from "./types";
+import { t } from "@/lib/i18n";
/**
* Format diff review comments as human-readable markdown for sending to agent.
@@ -109,7 +110,7 @@ export function formatWalkthroughCommentsAsMarkdown(comments: WalkthroughComment
for (const c of comments) {
const lineRange = c.startLine === c.endLine ? `${c.startLine}` : `${c.startLine}-${c.endLine}`;
const repoPrefix = c.repo ? `${c.repo}/` : "";
- const title = c.walkthroughTitle || "Walkthrough";
+ const title = c.walkthroughTitle || t("common:walkthrough");
lines.push(`**${title} · Step ${c.stepIndex + 1} / ${c.stepCount}**`);
lines.push(`**${repoPrefix}${c.filePath}:${lineRange}**`);
diff --git a/apps/web/lib/state/slices/ui/sidebar-view-builtins.ts b/apps/web/lib/state/slices/ui/sidebar-view-builtins.ts
index 8196ade42c..2d974d4aa4 100644
--- a/apps/web/lib/state/slices/ui/sidebar-view-builtins.ts
+++ b/apps/web/lib/state/slices/ui/sidebar-view-builtins.ts
@@ -14,6 +14,32 @@ export function createDefaultSidebarView(id: string, name: string): SidebarView
};
}
+/**
+ * Catalog key for the built-in view's name.
+ *
+ * `SidebarView.name` is persisted and user-editable (the view manager renames
+ * it in place), so the built-in's name stays canonical English in state and the
+ * surfaces that display it resolve this key instead — the same key/persisted-
+ * value split the layout profiles and dockview panel titles use. Translating
+ * `name` here would write the current locale into the user's synced views.
+ */
+export const DEFAULT_VIEW_NAME_KEY = "sidebar:viewAllTasks";
+
export const DEFAULT_VIEW: SidebarView = createDefaultSidebarView(DEFAULT_VIEW_ID, "All tasks");
+/**
+ * Display name for a view.
+ *
+ * Only the built-in that still carries its canonical name resolves through the
+ * catalog. The built-in is renameable like any other view, so keying off the id
+ * alone would show the translated default over a name the user chose.
+ */
+export function sidebarViewName(
+ view: Pick,
+ translate: (key: string) => string,
+): string {
+ const isUnrenamedBuiltIn = view.id === DEFAULT_VIEW_ID && view.name === DEFAULT_VIEW.name;
+ return isUnrenamedBuiltIn ? translate(DEFAULT_VIEW_NAME_KEY) : view.name;
+}
+
export const DEFAULT_ACTIVE_VIEW_ID = DEFAULT_VIEW_ID;
diff --git a/apps/web/lib/state/slices/ui/sidebar-view-name.test.ts b/apps/web/lib/state/slices/ui/sidebar-view-name.test.ts
new file mode 100644
index 0000000000..0e42146cfa
--- /dev/null
+++ b/apps/web/lib/state/slices/ui/sidebar-view-name.test.ts
@@ -0,0 +1,34 @@
+import { describe, expect, it } from "vitest";
+
+import { DEFAULT_VIEW, createDefaultSidebarView, sidebarViewName } from "./sidebar-view-builtins";
+
+const translate = (key: string) => `«${key}»`;
+
+/**
+ * `SidebarView.name` is persisted AND user-editable, so the built-in's display
+ * name resolves through the catalog while the stored value stays canonical
+ * English. Keying only off the id would have shown the translated default over
+ * a name the user typed.
+ */
+describe("sidebarViewName", () => {
+ it("localizes the built-in view while it keeps its canonical name", () => {
+ expect(sidebarViewName(DEFAULT_VIEW, translate)).toBe("«sidebar:viewAllTasks»");
+ });
+
+ it("keeps a renamed built-in view showing the user's name", () => {
+ const renamed = { ...DEFAULT_VIEW, name: "My stuff" };
+
+ expect(sidebarViewName(renamed, translate)).toBe("My stuff");
+ });
+
+ it("never localizes a user-created view", () => {
+ const custom = createDefaultSidebarView("view-abc", "Blocked work");
+
+ expect(sidebarViewName(custom, translate)).toBe("Blocked work");
+ });
+
+ it("keeps the stored default name canonical English", () => {
+ // What sync writes to the server; translating it would freeze a locale in.
+ expect(DEFAULT_VIEW.name).toBe("All tasks");
+ });
+});
diff --git a/apps/web/lib/tasks/unarchive-feedback.test.ts b/apps/web/lib/tasks/unarchive-feedback.test.ts
index acefa3d4a1..0409815df8 100644
--- a/apps/web/lib/tasks/unarchive-feedback.test.ts
+++ b/apps/web/lib/tasks/unarchive-feedback.test.ts
@@ -39,6 +39,15 @@ describe("unarchiveToastPayload", () => {
expect(payload.description).not.toContain("feat/ok");
});
+ it("uses the singular form for exactly one unrecoverable branch", () => {
+ const payload = unarchiveToastPayload(
+ response([{ task_id: "t1", repository_id: "r1", branch: "feat/gone", status: "missing" }]),
+ );
+ // Singular and plural now come from `_one`/`_other` rather than from two
+ // ternaries at the call site, so both forms have to keep resolving.
+ expect(payload.description).toContain("Branch feat/gone no longer exists");
+ });
+
it("pluralizes when multiple branches are unrecoverable", () => {
const payload = unarchiveToastPayload(
response([
diff --git a/apps/web/lib/tasks/unarchive-feedback.ts b/apps/web/lib/tasks/unarchive-feedback.ts
index 432131d9ba..33abe88045 100644
--- a/apps/web/lib/tasks/unarchive-feedback.ts
+++ b/apps/web/lib/tasks/unarchive-feedback.ts
@@ -1,4 +1,5 @@
import type { UnarchiveTaskResponse } from "@/lib/api/domains/kanban-api";
+import { t } from "@/lib/i18n";
export type UnarchiveToastPayload = {
title: string;
@@ -10,19 +11,26 @@ export type UnarchiveToastPayload = {
// branch no longer exists anywhere (never pushed, local copy deleted by
// archive), the prior work is unrecoverable and the user must be told the
// next session starts fresh.
+//
+// The missing-branch sentence used to inflect itself
+// (`${plural ? "Branches" : "Branch"} … no longer ${plural ? "exist" : "exists"}`),
+// which puts the plural rule at the call site and cannot be translated into a
+// language whose rules differ from English. `count` plus `_one`/`_other` moves
+// it into the catalog, where each locale supplies as many forms as it needs.
export function unarchiveToastPayload(result: UnarchiveTaskResponse): UnarchiveToastPayload {
const missing = (result.recovery ?? []).filter((r) => r.status === "missing");
if (missing.length > 0) {
- const branches = missing.map((r) => r.branch).join(", ");
- const plural = missing.length > 1;
return {
- title: "Task unarchived",
- description: `${plural ? "Branches" : "Branch"} ${branches} no longer ${plural ? "exist" : "exists"} locally or on the remote — the next session starts fresh from the base branch.`,
+ title: t("tasks:taskUnarchived"),
+ description: t("tasks:unarchivedBranchesMissing", {
+ count: missing.length,
+ branches: missing.map((r) => r.branch).join(", "),
+ }),
};
}
return {
- title: "Task unarchived",
- description: "The task has been restored.",
+ title: t("tasks:taskUnarchived"),
+ description: t("tasks:unarchivedTaskRestored"),
variant: "success",
};
}
diff --git a/apps/web/lib/utils/file-diff.ts b/apps/web/lib/utils/file-diff.ts
index 8153680012..ad4499d9f1 100644
--- a/apps/web/lib/utils/file-diff.ts
+++ b/apps/web/lib/utils/file-diff.ts
@@ -1,6 +1,7 @@
import { createTwoFilesPatch } from "diff";
import { djb2Hash } from "./hash";
+import { t } from "@/lib/i18n";
/**
* Generate a unified diff between original and modified content
@@ -65,5 +66,5 @@ export function formatDiffStats(additions: number, deletions: number): string {
if (deletions > 0) {
parts.push(`-${deletions}`);
}
- return parts.join(" ") || "No changes";
+ return parts.join(" ") || t("task:noChanges");
}
diff --git a/apps/web/src/locales/en/agents.json b/apps/web/src/locales/en/agents.json
index 166c1c6552..f4f1319c03 100644
--- a/apps/web/src/locales/en/agents.json
+++ b/apps/web/src/locales/en/agents.json
@@ -99,6 +99,7 @@
"failedToFetchCapabilities": "Failed to fetch capabilities",
"modelOptionsAuthenticationRequired": "Sign in to this agent to load model options.",
"modelOptionsResolutionFailed": "Model options could not be loaded.",
+ "modelSettings": "Model settings",
"resolvingModelOptions": "Loading model options…",
"retryModelOptions": "Retry",
"failedToLoadCommandPreview": "Failed to load command preview",
@@ -186,6 +187,7 @@
"searchModes": "Search modes...",
"selectAModel": "Select a model...",
"selectAModelShort": "Select a model",
+ "selectModel": "Select model...",
"sessionEnded": "Session ended.",
"sessionEndedWithCode": "Session ended (exit {{code}}).",
"sessionUnavailable": "Session is no longer available.",
diff --git a/apps/web/src/locales/en/azuredevops.json b/apps/web/src/locales/en/azuredevops.json
index 828d88c090..8fcf011d78 100644
--- a/apps/web/src/locales/en/azuredevops.json
+++ b/apps/web/src/locales/en/azuredevops.json
@@ -65,6 +65,9 @@
"enabled": "Enabled",
"enterValidOrganizationUrl": "Enter a valid organization URL to open its token settings.",
"executorProfileId": "Executor profile ID",
+ "failedToLinkPullRequest": "Failed to link Azure DevOps pull request.",
+ "failedToLinkWorkItem": "Failed to link Azure DevOps work item.",
+ "failedToLinkWorkItemNoProject": "Failed to link Azure DevOps work item: project is missing.",
"failedToLoadConfig": "Failed to load Azure DevOps config: {{error}}",
"failedToLoadQuickActions": "Failed to load quick actions.",
"failedToSaveDefaultQueries": "Failed to save default queries",
diff --git a/apps/web/src/locales/en/common.json b/apps/web/src/locales/en/common.json
index ff31a0335d..9696824a25 100644
--- a/apps/web/src/locales/en/common.json
+++ b/apps/web/src/locales/en/common.json
@@ -4,6 +4,7 @@
"activeSubagents_other": "{{count}} subagents running",
"activeTasks": "Active Tasks",
"aDirectLocalExecutorProfileIsRequired": "A direct local executor profile is required to create and use an empty repository.",
+ "agent": "Agent",
"agentDashboard": "agent dashboard",
"agentRun": "agent run",
"agentRuns": "agent runs",
@@ -13,6 +14,9 @@
"andMoreFiles_one": "+{{count}} more",
"andMoreFiles_other": "+{{count}} more",
"anEnhancedPromptIsAvailable": "An enhanced prompt is available.",
+ "anErrorOccurred": "An error occurred",
+ "anUnexpectedErrorOccurred": "An unexpected error occurred",
+ "anUnknownErrorOccurred": "An unknown error occurred",
"apply": "Apply",
"autoApproveAllPermissions": "Auto-approve all permissions",
"automations": "Automations",
@@ -36,9 +40,30 @@
"cliPassthrough": "CLI passthrough",
"close": "Close",
"closeEsc": "Close (Esc)",
+ "commandAddBrowserPanel": "Add Browser Panel",
+ "commandAddBrowserPanelKeywords": "browser, preview, web, open browser preview, web preview, app preview",
+ "commandAddChangesPanel": "Add Changes Panel",
+ "commandAddChangesPanelKeywords": "changes, diff, git changes, changed files, source control, git diff, review changes",
+ "commandAddPlanPanel": "Add Plan Panel",
+ "commandAddPlanPanelKeywords": "plan, document, task plan, implementation plan, plan details",
+ "commandAddTerminalPanel": "Add Terminal Panel",
+ "commandAddTerminalPanelKeywords": "terminal, shell, console, new terminal, open terminal, command line",
"commandAgentsSettingsKeywords": "agents, agent settings, agent profiles, installed agents, claude",
"commandArchiveTaskKeywords": "archive, archive task, archive current task, close task, finish task, put away",
+ "commandCancelTurn": "Cancel Turn",
+ "commandCancelTurnKeywords": "cancel, stop, turn, cancel agent, stop agent, interrupt agent",
+ "commandCommitChanges": "Commit Changes",
+ "commandCommitChangesKeywords": "commit, git, save changes, git commit",
"commandConfigChatKeywords": "config chat, config mode, configure kandev, workflow configuration, mcp configuration",
+ "commandCreateFile": "Create File",
+ "commandCreateFileKeywords": "create, new, file, add",
+ "commandCreateFilePlaceholder": "File path relative to workspace root...",
+ "commandCreateNewTask": "Create New Task",
+ "commandCreateNewTaskKeywords": "new, create, task, add",
+ "commandCreatePr": "Create PR",
+ "commandCreatePrKeywords": "pull request, pr, open pull request, submit pull request, git",
+ "commandCreateSubtask": "Create Subtask",
+ "commandCreateSubtaskKeywords": "subtask, create, new subtask, new sub-task, child task",
"commandExecutorsSettingsKeywords": "executors, executor profiles, execution environment, environment variables, runtime, compute",
"commandGoToAllTasks": "Go to All Tasks",
"commandGoToAllTasksKeywords": "tasks, list, all",
@@ -51,11 +76,21 @@
"commandGoToStats": "Go to Stats",
"commandGoToStatsKeywords": "stats, statistics, analytics, metrics",
"commandGroupActions": "Actions",
+ "commandGroupAgent": "Agent",
"commandGroupCommands": "Commands",
+ "commandGroupGit": "Git",
"commandGroupNavigation": "Navigation",
+ "commandGroupPanels": "Panels",
"commandGroupPreferences": "Preferences",
"commandGroupSettings": "Settings",
"commandGroupTasks": "Tasks",
+ "commandGroupView": "View",
+ "commandGroupWorkspace": "Workspace",
+ "commandMergeKeywords": "merge, git, branch",
+ "commandNewAgent": "New Agent",
+ "commandNewAgentKeywords": "new, agent, session, start agent, new session",
+ "commandOpenRecentTaskSwitcher": "Open Recent Task Switcher",
+ "commandOpenRecentTaskSwitcherKeywords": "recent, task, switcher, history, recent tasks, switch task, switch recent tasks",
"commandPaletteMode": "Command palette mode",
"commandPreviewAddTerminalPanel": "Add Terminal Panel",
"commandPreviewCommitChanges": "Commit Changes",
@@ -63,10 +98,19 @@
"commandPreviewPull": "Pull",
"commandPreviewSearchTasks": "Search Tasks",
"commandPromptsSettingsKeywords": "prompts, prompt settings, custom prompts, prompt snippets, prompt templates",
+ "commandPullKeywords": "pull, git, pull changes, download changes",
+ "commandPushKeywords": "push, git, push changes, push to remote, upload changes",
"commandQuickChat": "Quick Chat",
"commandQuickChatKeywords": "quick chat, new quick chat, quick question, ask agent",
+ "commandRebaseKeywords": "rebase, git, branch",
"commandSwitchToDarkMode": "Switch to Dark Mode",
+ "commandSwitchToKanbanView": "Switch to Kanban View",
+ "commandSwitchToKanbanViewKeywords": "kanban, board, view",
"commandSwitchToLightMode": "Switch to Light Mode",
+ "commandSwitchToListView": "Switch to List View",
+ "commandSwitchToListViewKeywords": "list, table, view",
+ "commandSwitchToPipelineView": "Switch to Pipeline View",
+ "commandSwitchToPipelineViewKeywords": "pipeline, graph, view",
"commandThemeKeywords": "theme, color theme, appearance",
"commandWorkspaceSettingsKeywords": "workspace, workspaces",
"commitsAndPushesToTheCurrent": "Commits and pushes to the current branch.",
@@ -126,6 +170,29 @@
"forwardStdinStdoutStraightToThe": "Forward stdin/stdout straight to the CLI subprocess. Disables ACP.",
"getStarted": "Get Started",
"githubCliNotAuthenticated": "GitHub CLI not authenticated",
+ "gitOpAmendCommit": "Amend commit",
+ "gitOpCommit": "Commit",
+ "gitOperationFailedInRepos_one": "Failed in {{count}} repo — {{summary}}",
+ "gitOperationFailedInRepos_other": "Failed in {{count}} repos — {{summary}}",
+ "gitOperationPartialDescription_one": "{{operation}} succeeded in {{succeeded}} of {{count}} repo ({{repos}}); failed in {{summary}}",
+ "gitOperationPartialDescription_other": "{{operation}} succeeded in {{succeeded}} of {{count}} repos ({{repos}}); failed in {{summary}}",
+ "gitOperationPartiallySucceeded": "{{operation}} partially succeeded",
+ "gitOperationRepoFailure": "{{repo}}: {{error}}",
+ "gitOperationSucceededInRepos_one": "{{operation}} succeeded in {{count}} repo: {{repos}}",
+ "gitOperationSucceededInRepos_other": "{{operation}} succeeded in {{count}} repos: {{repos}}",
+ "gitOpForcePush": "Force Push",
+ "gitOpHardReset": "Hard reset",
+ "gitOpMerge": "Merge",
+ "gitOpPull": "Pull",
+ "gitOpPush": "Push",
+ "gitOpRebase": "Rebase",
+ "gitOperationCompleted": "{{operation}} completed",
+ "gitOperationFailed": "{{operation}} failed",
+ "gitOperationRunning": "{{operation}}...",
+ "gitOperationScoped": "{{operation}} ({{repo}})",
+ "gitOperationSucceeded": "{{operation}} successful",
+ "gitOpRevertCommit": "Revert commit",
+ "gitOpSoftReset": "Soft reset",
"gitScopeSkipped": "Skipped {{scope}} because a child repository operation failed ({{failedScopes}}).",
"goToFirstPage": "Go to first page",
"goToLastPage": "Go to last page",
@@ -234,6 +301,7 @@
"probing": "Probing",
"profileName": "Profile name",
"prompts": "Prompts",
+ "reference": "Reference",
"refreshAriaLabel": "Refresh {{label}}",
"refreshBranchesGitFetch": "Refresh branches (git fetch)",
"refreshingLabel": "Refreshing {{label}}...",
@@ -275,6 +343,7 @@
"scopeFiles": "Files",
"scopeTitleWithShortcut": "{{label}} ({{shortcut}})",
"scratchWorkspace": "scratch workspace",
+ "script": "Script",
"searchEllipsis": "Search…",
"searchFailedEditTheQueryOr": "Search failed. Edit the query or reopen search to retry.",
"searchForFiles": "Search for files...",
@@ -302,9 +371,12 @@
"skip": "Skip",
"stagesAndCommitsChangesUsingConventional": "Stages and commits changes using Conventional Commits.",
"status": "Status",
+ "step": "Step",
"subtaskTitle": "Subtask title",
"switchMode": "Switch mode",
"system": "System",
+ "task": "Task",
+ "terminal": "Terminal",
"todos": "Todos",
"taskLoading": "Loading task...",
"tasks": "Tasks",
@@ -333,19 +405,25 @@
"typeACommand": "Type a command...",
"typeToSearchFiles": "Type to search files...",
"typeToSearchTaskContents": "Type to search task contents…",
+ "unavailable": "Unavailable",
+ "unknown": "Unknown",
+ "unknownError": "Unknown error",
"unknownPanel": "Unknown panel: {{component}}",
"unsavedChanges": "Unsaved changes",
+ "untitledTask": "Untitled task",
"usefulCommandsAgentSkills": "Useful commands & agent skills",
"useScratchInstead": "Use scratch instead",
"useThisFolder": "Use this folder",
"viewFullReleaseOnGithub": "View full release on GitHub",
"waitsForCiAndAutomatedReviews": "Waits for CI and automated reviews, then fixes failures and addresses comments.",
+ "walkthrough": "Walkthrough",
"whatSNew": "What's New",
"whenItsDoneTheAgentOpensAPr": "When it's done, the agent opens a pull request to <2>{{repo}}2> for the maintainers to review, saving them time and shipping the improvement to everyone.",
"workflow": "Workflow",
"workflowsControlTheStepsAutomationAnd": "Workflows control the steps, automation, and agent behavior for your tasks. You can add more workflows from Settings.",
"workspace": "Workspace",
"workspaces": "Workspaces",
+ "you": "You",
"youCanUseTheNavigationOr": "You can use the navigation or reload to try again.",
"yourAgentImplementsItInThe": "Your agent implements it in the kandev repository, with tests",
"youVerifyAndTestTheChange": "You verify and test the change in a second kandev instance"
diff --git a/apps/web/src/locales/en/editors.json b/apps/web/src/locales/en/editors.json
index fc923ebcfc..677fbcb662 100644
--- a/apps/web/src/locales/en/editors.json
+++ b/apps/web/src/locales/en/editors.json
@@ -19,10 +19,17 @@
"copyLine": "Copy line {{lineNumber}}",
"copyPath": "Copy path",
"default": "default",
+ "deleteFailed": "Delete failed",
"deleteFile": "Delete file",
"disableWordWrap": "Disable word wrap",
"enableWordWrap": "Enable word wrap",
+ "errorWhileDeletingFile": "An error occurred while deleting the file",
+ "errorWhileSavingFile": "An error occurred while saving the file",
+ "failedToDeleteFile": "Failed to delete file",
+ "failedToSaveFile": "Failed to save file",
"failedToSendComment": "Failed to send comment",
+ "fileSavedOverwritten": "File saved (overwritten)",
+ "fileSavedOverwrittenDescription": "The file was modified externally. Your version was saved.",
"foldUnchangedLines": "Fold unchanged lines",
"hideDiffIndicators": "Hide diff indicators",
"highlight": "Highlight",
@@ -35,6 +42,7 @@
"noDiffAvailable": "No diff available",
"noEditorsConfigured": "No editors configured",
"openFolder": "Open folder",
+ "openTaskSessionBeforeSending": "Open a task session before sending to the agent.",
"openWith": "Open with...",
"pasteLink": "Paste link...",
"pleaseTryAgain": "Please try again.",
@@ -43,6 +51,7 @@
"reload": "Reload",
"revertAllChanges": "Revert all changes",
"revertChanges": "Revert changes",
+ "saveFailed": "Save failed",
"saving": "Saving...",
"sentToTheAgent": "Sent to the agent.",
"showAllLines": "Show all lines",
diff --git a/apps/web/src/locales/en/github.json b/apps/web/src/locales/en/github.json
index 2c80b0cb75..ec2148f93a 100644
--- a/apps/web/src/locales/en/github.json
+++ b/apps/web/src/locales/en/github.json
@@ -180,6 +180,7 @@
"failedToDeleteReviewWatch": "Failed to delete review watch",
"failedToLoadGithubWorkspaceSettings": "Failed to load GitHub workspace settings",
"failedToMerge": "Failed to merge",
+ "failedToReRequestReview": "Failed to re-request review",
"failedToResetIssueWatch": "Failed to reset issue watch",
"failedToResetReviewWatch": "Failed to reset review watch",
"failedToSaveDefaultQueries": "Failed to save default queries",
@@ -445,6 +446,7 @@
"reviewCount_other": "{{count}} reviews",
"reviewFollowUp": "Review follow-up",
"reviewPermissions": "Review permissions",
+ "reviewReRequestedFrom": "Review re-requested from {{reviewer}}",
"reviewWatch": "review watch",
"reviewWatchCreated": "Review watch created",
"reviewWatchDeleted": "Review watch deleted",
@@ -468,6 +470,7 @@
"selectWorkflow": "Select workflow",
"selectWorkspace": "Select workspace",
"selectedRepositories": "Selected repositories",
+ "selectWorkspaceBeforeRequestingReview": "Select a workspace before requesting review.",
"setTheseExactUrlsCreateAClientSecret": "Set these exact URLs, create a client secret and webhook secret, and download a private key. This one-time setup expires {{expiresAt}}.",
"showGithubApiLimits": "Show GitHub API limits",
"showLess": "Show less",
diff --git a/apps/web/src/locales/en/gitlab.json b/apps/web/src/locales/en/gitlab.json
index 84f1272e52..bf8206b629 100644
--- a/apps/web/src/locales/en/gitlab.json
+++ b/apps/web/src/locales/en/gitlab.json
@@ -316,7 +316,6 @@
"mrAutomationExplainAutoFixRounds": "Explain auto-fix rounds",
"mrAutomationAutoFixPromptSaveFailed": "Failed to save auto-fix prompt.",
"mrAutomationAutoFixPromptResetFailed": "Failed to reset auto-fix prompt.",
- "mrPopoverOpenDetail": "Open merge request detail",
"mrPopoverOpenInGitlab": "Open !{{mriid}} in GitLab",
"mrPopoverUnlink": "Unlink !{{mriid}}",
"mrPopoverPassRate": "Pass rate",
diff --git a/apps/web/src/locales/en/kanban.json b/apps/web/src/locales/en/kanban.json
index 9443fa3993..049e4ee27c 100644
--- a/apps/web/src/locales/en/kanban.json
+++ b/apps/web/src/locales/en/kanban.json
@@ -4,6 +4,7 @@
"allRepositories": "All repositories",
"allWorkflows": "All workflows",
"allWorkflowsTitleCase": "All Workflows",
+ "allWorkspaces": "All workspaces",
"approvalRequired": "Approval Required",
"archive": "Archive",
"archiveCount_one": "Archive {{count}}",
diff --git a/apps/web/src/locales/en/office.json b/apps/web/src/locales/en/office.json
index 052dbec17c..cfc0b57192 100644
--- a/apps/web/src/locales/en/office.json
+++ b/apps/web/src/locales/en/office.json
@@ -101,10 +101,15 @@
"botTokenChatId": "{\"bot_token\": \"...\", \"chat_id\": \"...\"}",
"branch": "Branch",
"browseSkillsSh": "Browse skills.sh",
+ "budgetActionBlockNewTasks": "block new tasks",
+ "budgetActionNotifyOnly": "notify only",
+ "budgetActionPauseAgent": "pause agent",
"budgetAlert": "Budget Alert",
"budgetAmount": "Budget: {{amount}}",
"budgetCapConcurrencyAndExecutionEnvironment": "Budget cap, concurrency, and execution environment.",
"budgetLimitsConcurrencyAndExecutionEnvironment": "Budget limits, concurrency, and execution environment.",
+ "budgetPeriodMonthly": "monthly",
+ "budgetPeriodTotal": "total",
"budgetPolicies": "Budget Policies",
"budgetPolicyCreated": "Budget policy created",
"budgetPolicyDeleted": "Budget policy deleted",
diff --git a/apps/web/src/locales/en/settings.json b/apps/web/src/locales/en/settings.json
index 9814df87c7..1f868db4c2 100644
--- a/apps/web/src/locales/en/settings.json
+++ b/apps/web/src/locales/en/settings.json
@@ -347,10 +347,17 @@
"layoutEditorForProfile": "{{name}} editor",
"layoutProfile": "layout profile",
"layoutProfileCopyName": "{{name}} copy",
+ "layoutProfileDefault": "Default",
+ "layoutProfileDefaultDescription": "Agent with Files, Changes, and Terminal",
"layoutProfileName": "Layout profile name",
"layoutProfileNamesMustNotBe": "Layout profile names must not be empty",
+ "layoutProfilePlanMode": "Plan Mode",
+ "layoutProfilePlanModeDescription": "Agent and Plan side by side",
+ "layoutProfilePreviewMode": "Preview Mode",
+ "layoutProfilePreviewModeDescription": "Agent and Browser side by side",
"layoutProfiles": "Layout profiles",
"layoutProfilesWereNotSaved": "Layout profiles were not saved",
+ "layoutProfileVsCodeDescription": "Agent and VS Code side by side",
"layouts": "Layouts",
"ligatures": "Ligatures",
"light": "Light",
diff --git a/apps/web/src/locales/en/sidebar.json b/apps/web/src/locales/en/sidebar.json
index 5f05b592cf..3d78401ed5 100644
--- a/apps/web/src/locales/en/sidebar.json
+++ b/apps/web/src/locales/en/sidebar.json
@@ -101,6 +101,7 @@
"toggleTheme": "Toggle theme",
"unsavedFilterChanges": "Unsaved filter changes",
"viewAll": "All",
+ "viewAllTasks": "All tasks",
"viewLabel": "View: {{name}}",
"whatsNew": "What's new",
"work": "Work",
diff --git a/apps/web/src/locales/en/task.json b/apps/web/src/locales/en/task.json
index d42407be09..f980266469 100644
--- a/apps/web/src/locales/en/task.json
+++ b/apps/web/src/locales/en/task.json
@@ -1,5 +1,8 @@
{
+ "agentCliNotInstalled": "Agent CLI not installed",
+ "agentProbeFailed": "Agent probe failed",
"aPullRequestAlreadyExistsFor": "A pull request already exists for this task",
+ "archivedTask": "Archived task",
"aSharedDocumentForYouAnd": "A shared document for you and the agent to collaborate on the approach",
"aboutContextWindowSource": "About context window source",
"activeBadge": "ACTIVE",
@@ -269,6 +272,7 @@
"couldNotAddSources": "Could not add sources. Your entries are still here to retry.",
"couldNotLoadRepositories": "Could not load repositories: {{message}}",
"couldNotResolveRemoteRepository": "Could not resolve remote repository: {{message}}",
+ "couldNotSaveStatusBarOrder": "Could not save status bar order",
"couldnTStartASession": "Couldn't start a session",
"create": "Create",
"createANewBranch": "Create a new branch",
@@ -290,6 +294,7 @@
"creatingEllipsis": "Creating...",
"creatingFrom": "Creating {{shortName}} from <4>{{displayBranch}}4>",
"creatingFromTo": "Creating {{shortName}} from <4>{{displayBranch}}4> to <7>{{baseBranch}}7>",
+ "credentialsAvailableInExecutor": "Credentials available in the selected executor",
"ctrl": "Ctrl",
"current": "current",
"current2": "Current",
@@ -401,14 +406,19 @@
"environmentPrepared": "Environment prepared",
"environmentPreparedOnFreshSandbox": "Environment prepared on a fresh sandbox",
"environmentPreparedWithWarnings": "Environment prepared with warnings",
+ "environmentReset": "Environment reset",
+ "environmentResetFailed": "Reset failed: {{error}}",
"environmentSetupFailed": "Environment setup failed",
"environmentSetupFinishedWithErrors": "Environment setup finished with errors",
+ "environmentStateCurrent": "Current state: {{state}}",
"error": "Error",
"errorData": "Error Data",
"eventCount_one": "{{count}} event",
"eventCount_other": "{{count}} events",
"executor2": "Executor",
"executorEnvironmentIsUnavailable": "Executor environment is unavailable.",
+ "executorEnvironmentRunning": "Executor environment running",
+ "executorEnvironmentStopped": "Executor environment stopped",
"executorHintDocker": "A Docker container will be created from the selected base branch and checked out on a task branch.",
"executorHintLocal": "The agent will run directly on the repository.",
"executorHintWorktreeMulti": "A git worktree will be created for each repository in a parent folder. The agent runs in that parent folder so it can see every worktree side by side.",
@@ -439,6 +449,8 @@
"failedToChangeModel": "Failed to change model",
"failedToDeleteDocument": "Failed to delete document",
"failedToDeleteLayout": "Failed to delete layout",
+ "failedToDiscardChanges": "Failed to discard changes",
+ "failedToGenerateContent": "Failed to generate content",
"failedToLinkGithubIssue": "Failed to link GitHub issue.",
"failedToLinkGithubPullRequest": "Failed to link GitHub pull request.",
"failedToLinkTarget": "Failed to link {{target}}.",
@@ -454,13 +466,16 @@
"failedToRenameItem": "Failed to rename item",
"failedToRenameTask": "Failed to rename task",
"failedToRestorePlan": "Failed to restore plan",
+ "failedToResumeSession": "Failed to resume session",
"failedToRevokeShare": "Failed to revoke share.",
"failedToSaveDocument": "Failed to save document",
"failedToSaveLayout": "Failed to save layout",
"failedToSendComment": "Failed to send comment",
"failedToSendComments": "Failed to send comments",
"failedToSendMessage": "Failed to send message",
+ "failedToStartTunnel": "Failed to start tunnel: {{error}}",
"failedToStartVsCode": "Failed to start VS Code",
+ "failedToStopTunnel": "Failed to stop tunnel: {{error}}",
"failedToUnarchiveTask": "Failed to unarchive task",
"failedToUnlinkGithubIssue": "Failed to unlink GitHub issue.",
"failedToUpload": "Failed to upload {{name}}",
@@ -519,6 +534,7 @@
"fullImpactDetails": "Full impact details",
"fullSize": "Full size {{alt}}",
"generatingSummary": "Generating summary...",
+ "generationFailed": "Generation failed",
"gitActions": "Git actions",
"gitPollMode": "Git poll: {{mode}} ({{label}})",
"githubIssue": "GitHub Issue",
@@ -599,6 +615,7 @@
"kandevListWorkspaces": "Kandev: List Workspaces",
"kandevMessageTask": "Kandev: Message Task",
"kandevMoveTask": "Kandev: Move Task",
+ "kandevUpdateAvailable": "Kandev update available",
"kandevUpdateTask": "Kandev: Update Task",
"kandevUpdateTaskPlan": "Kandev: Update Task Plan",
"kandevWriteTaskDocument": "Kandev: Write Task Document",
@@ -703,6 +720,9 @@
"moreNotShown_one": "+ {{count}} more not shown",
"moreNotShown_other": "+ {{count}} more not shown",
"moreToolbarActions": "More toolbar actions",
+ "moveFailed": "Move failed",
+ "moveFailedFiles": "Some files could not be moved",
+ "moveFailedUnexpected": "An error occurred while moving files",
"moveHeadToThisCommitKeep": "Move HEAD to this commit. Keep all changes staged.",
"moveHere": "Move here",
"moveTaskToTheNextWorkflow": "Move task to the next workflow step",
@@ -733,6 +753,7 @@
"nextMessage": "Next message",
"nextQuestion": "Next question",
"noActiveSession": "No active session",
+ "noActiveSessionDescription": "Start a session first to use AI generation",
"noActiveTask": "No active task",
"noActivityYet": "No activity yet",
"noAgentFound": "No agent found.",
@@ -796,6 +817,7 @@
"noSavedLayouts": "No saved layouts",
"noSessionsYetLaunchOneTo": "No sessions yet. Launch one to get started.",
"noSteps": "No steps",
+ "noStepsConfigured": "No steps configured",
"noStepsFound": "No steps found.",
"noSubIssues": "No sub-issues",
"noTaskSelected": "No task selected",
@@ -836,9 +858,20 @@
"openWorkspaceSettings": "Open workspace settings",
"option": "option {{index}}",
"orTypeACustomAnswer": "Or type a custom answer…",
+ "otherChanges": "Other changes",
"output": "Output",
"outputTokens": "Output tokens",
"outputTruncated": "Output truncated",
+ "panelAgent": "Agent",
+ "panelChanges": "Changes",
+ "panelDiffViewer": "Diff Viewer",
+ "panelFileDiff": "Diff [{{file}}]",
+ "panelFiles": "Files",
+ "panelMergeRequest": "Merge Request",
+ "panelPlan": "Plan",
+ "panelPrDetails": "PR Details",
+ "panelPullRequest": "Pull Request",
+ "panelTerminal": "Terminal",
"parent": "Parent",
"parkedCount": "Parked ({{parked}})",
"parkedTerminals": "Parked terminals",
@@ -850,6 +883,7 @@
"pendingReviewCommentsHelp_one": "{{count}} pending review comment. Click to expand the list — you can edit each comment, click the file path to jump to the source, or remove a comment with the trash icon.",
"pendingReviewCommentsHelp_other": "{{count}} pending review comments. Click to expand the list — you can edit each comment, click the file path to jump to the source, or remove a comment with the trash icon.",
"permissionRequested": "Permission requested",
+ "permissionRequired": "Permission Required",
"pickADifferentRepoLocalFolder": "Pick a different repo, local folder, or remote URL",
"pickAStartingFolderOptional": "pick a starting folder (optional)",
"pickOrPasteARepo": "Pick or paste a repo",
@@ -1219,6 +1253,7 @@
"statusInProgress": "In Progress",
"statusInReview": "In Review",
"statusTodo": "Todo",
+ "statusUpdate": "Status update",
"stepCount_one": "{{count}} step",
"stepCount_other": "{{count}} steps",
"stepFieldLabel": "step",
@@ -1233,6 +1268,7 @@
"stopVoiceInput": "Stop voice input",
"stoppedWithAnError": "stopped with an error",
"stopping": "Stopping…",
+ "subagent": "Subagent",
"subIssues": "Sub-issues",
"subTasks": "Sub-tasks",
"submit": "Submit",
@@ -1244,6 +1280,7 @@
"subtasksStayActiveUnlessYouTick": "Subtasks stay active unless you tick this. They may still be in progress.",
"successful": "{{operationName}} successful",
"summarize": "Summarize",
+ "summarizeReturnedNoResult": "Summarize utility returned no result",
"summarizeSession": "Summarize session",
"summarizeSessionNamed": "Summarize {{label}}",
"summarizing": "Summarizing...",
@@ -1337,6 +1374,7 @@
"tokenCount_other": "{{formatted}} tokens",
"tokens": "tokens",
"tool": "tool",
+ "toolCall": "Tool call",
"toolCallCount_one": "{{count}} tool call",
"toolCallCount_other": "{{count}} tool calls",
"toolCount_one": "{{count}} tool",
@@ -1353,6 +1391,8 @@
"truncatedParen": "(truncated)",
"tryAgain": "Try again",
"tunnel": "Tunnel",
+ "tunnelStartedOnPort": "Tunnel started on port {{port}}",
+ "tunnelStopped": "Tunnel stopped",
"turnForDuration": "for {{duration}}",
"turnMessageCount_one": "· {{count}} message",
"turnMessageCount_other": "· {{count}} messages",
diff --git a/apps/web/src/locales/en/tasks.json b/apps/web/src/locales/en/tasks.json
index ed3490deee..594155e678 100644
--- a/apps/web/src/locales/en/tasks.json
+++ b/apps/web/src/locales/en/tasks.json
@@ -10,6 +10,10 @@
"columnWorkflow": "Workflow",
"delete": "Delete",
"deleteTask": "Delete task",
+ "failedToArchiveTask": "Failed to archive task",
+ "failedToDeleteTask": "Failed to delete task",
+ "failedToLoadTasks": "Failed to load tasks",
+ "failedToUnarchiveTask": "Failed to unarchive task",
"goToFirstPage": "Go to first page",
"goToLastPage": "Go to last page",
"goToNextPage": "Go to next page",
@@ -39,6 +43,12 @@
"sortTitleZA": "Title Z-A",
"sortUpdatedNewest": "Updated newest",
"sortUpdatedOldest": "Updated oldest",
+ "taskArchived": "Task archived",
+ "taskArchivedDescription": "The task has been archived successfully.",
+ "taskUnarchived": "Task unarchived",
"unarchive": "Unarchive",
+ "unarchivedBranchesMissing_one": "Branch {{branches}} no longer exists locally or on the remote — the next session starts fresh from the base branch.",
+ "unarchivedBranchesMissing_other": "Branches {{branches}} no longer exist locally or on the remote — the next session starts fresh from the base branch.",
+ "unarchivedTaskRestored": "The task has been restored.",
"unarchiveTask": "Unarchive task"
}
diff --git a/apps/web/src/locales/pseudo/agents.json b/apps/web/src/locales/pseudo/agents.json
index 50d8fd169e..752b9025d5 100644
--- a/apps/web/src/locales/pseudo/agents.json
+++ b/apps/web/src/locales/pseudo/agents.json
@@ -55,7 +55,7 @@
"conflictTierMappingsTitle": "Ŵōŕķśƥàćē ţĩēŕ ḿàƥƥĩńĝś:",
"conflictWatchersTitle": "Ŵàţćĥēŕś (ŵĩĺĺ ƀē ďĩśàƀĺēď):",
"conflictUtilityAgentsTitle": "Ũţĩĺĩţŷ àĝēńţś (ŵĩĺĺ ķēēƥ śţàĺē ƀĩńďĩńĝś):",
- "disableProfileUtilityWarning": "Ũţĩĺĩţŷ àĝēńţś ũśē ţĥĩś ƥŕōƒĩĺē. Ďĩśàƀĺĩńĝ ĩţ śţōƥś ńēŵ ũţĩĺĩţŷ ćàĺĺś ũńţĩĺ ēàćĥ ƀĩńďĩńĝ ĩś ƒĩχēď.",
+ "disableProfileUtilityWarning": "Ũţĩĺĩţŷ àĝēńţś ũśē ţĥĩś ƥŕōƒĩĺē. Ďĩśàƀĺĩńĝ ĩţ ŵĩĺĺ śţōƥ ńēŵ ũţĩĺĩţŷ ćàĺĺś ũńţĩĺ ēàćĥ ƀĩńďĩńĝ ĩś ŕēƥàĩŕēď.",
"disableProfileAnyway": "Ďĩśàƀĺē ƥŕōƒĩĺē",
"copied": "Ćōƥĩēď",
"copyInstallCommand": "Ćōƥŷ ĩńśţàĺĺ ćōḿḿàńď",
@@ -99,6 +99,7 @@
"failedToFetchCapabilities": "Ƒàĩĺēď ţō ƒēţćĥ ćàƥàƀĩĺĩţĩēś",
"modelOptionsAuthenticationRequired": "Śĩĝń ĩń ţō ţĥĩś àĝēńţ ţō ĺōàď ḿōďēĺ ōƥţĩōńś.",
"modelOptionsResolutionFailed": "Ḿōďēĺ ōƥţĩōńś ćōũĺď ńōţ ƀē ĺōàďēď.",
+ "modelSettings": "Ḿōďēĺ śēţţĩńĝś",
"resolvingModelOptions": "Ĺōàďĩńĝ ḿōďēĺ ōƥţĩōńś…",
"retryModelOptions": "Ŕēţŕŷ",
"failedToLoadCommandPreview": "Ƒàĩĺēď ţō ĺōàď ćōḿḿàńď ƥŕēvĩēŵ",
@@ -186,6 +187,7 @@
"searchModes": "Śēàŕćĥ ḿōďēś...",
"selectAModel": "Śēĺēćţ à ḿōďēĺ...",
"selectAModelShort": "Śēĺēćţ à ḿōďēĺ",
+ "selectModel": "Śēĺēćţ ḿōďēĺ...",
"sessionEnded": "Śēśśĩōń ēńďēď.",
"sessionEndedWithCode": "Śēśśĩōń ēńďēď (ēxĩţ {{code}}).",
"sessionUnavailable": "Śēśśĩōń ĩś ńō ĺōńĝēŕ àvàĩĺàƀĺē.",
diff --git a/apps/web/src/locales/pseudo/azuredevops.json b/apps/web/src/locales/pseudo/azuredevops.json
index 7b67a23d08..c577d22fef 100644
--- a/apps/web/src/locales/pseudo/azuredevops.json
+++ b/apps/web/src/locales/pseudo/azuredevops.json
@@ -65,6 +65,9 @@
"enabled": "Ēńàƀĺēď",
"enterValidOrganizationUrl": "Ēńţēŕ à vàĺĩď ōŕĝàńĩźàţĩōń ŨŔĹ ţō ōƥēń ĩţś ţōķēń śēţţĩńĝś.",
"executorProfileId": "Ēxēćũţōŕ ƥŕōƒĩĺē ĨĎ",
+ "failedToLinkPullRequest": "Ƒàĩĺēď ţō ĺĩńķ Àźũŕē ĎēvŌƥś ƥũĺĺ ŕēqũēśţ.",
+ "failedToLinkWorkItem": "Ƒàĩĺēď ţō ĺĩńķ Àźũŕē ĎēvŌƥś ŵōŕķ ĩţēḿ.",
+ "failedToLinkWorkItemNoProject": "Ƒàĩĺēď ţō ĺĩńķ Àźũŕē ĎēvŌƥś ŵōŕķ ĩţēḿ: ƥŕōĵēćţ ĩś ḿĩśśĩńĝ.",
"failedToLoadConfig": "Ƒàĩĺēď ţō ĺōàď Àźũŕē ĎēvŌƥś ćōńƒĩĝ: {{error}}",
"failedToLoadQuickActions": "Ƒàĩĺēď ţō ĺōàď qũĩćķ àćţĩōńś.",
"failedToSaveDefaultQueries": "Ƒàĩĺēď ţō śàvē ďēƒàũĺţ qũēŕĩēś",
diff --git a/apps/web/src/locales/pseudo/common.json b/apps/web/src/locales/pseudo/common.json
index f41dad6dc4..0708ac5f5c 100644
--- a/apps/web/src/locales/pseudo/common.json
+++ b/apps/web/src/locales/pseudo/common.json
@@ -4,6 +4,7 @@
"activeSubagents_other": "{{count}} śũƀàĝēńţś ŕũńńĩńĝ",
"activeTasks": "Àćţĩvē Ţàśķś",
"aDirectLocalExecutorProfileIsRequired": "À ďĩŕēćţ ĺōćàĺ ēxēćũţōŕ ƥŕōƒĩĺē ĩś ŕēqũĩŕēď ţō ćŕēàţē àńď ũśē àń ēḿƥţŷ ŕēƥōśĩţōŕŷ.",
+ "agent": "Àĝēńţ",
"agentDashboard": "àĝēńţ ďàśĥƀōàŕď",
"agentRun": "àĝēńţ ŕũń",
"agentRuns": "àĝēńţ ŕũńś",
@@ -13,6 +14,9 @@
"andMoreFiles_one": "+{{count}} ḿōŕē",
"andMoreFiles_other": "+{{count}} ḿōŕē",
"anEnhancedPromptIsAvailable": "Àń ēńĥàńćēď ƥŕōḿƥţ ĩś àvàĩĺàƀĺē.",
+ "anErrorOccurred": "Àń ēŕŕōŕ ōććũŕŕēď",
+ "anUnexpectedErrorOccurred": "Àń ũńēxƥēćţēď ēŕŕōŕ ōććũŕŕēď",
+ "anUnknownErrorOccurred": "Àń ũńķńōŵń ēŕŕōŕ ōććũŕŕēď",
"apply": "Àƥƥĺŷ",
"autoApproveAllPermissions": "Àũţō-àƥƥŕōvē àĺĺ ƥēŕḿĩśśĩōńś",
"automations": "Àũţōḿàţĩōńś",
@@ -36,9 +40,30 @@
"cliPassthrough": "ĆĹĨ ƥàśśţĥŕōũĝĥ",
"close": "Ćĺōśē",
"closeEsc": "Ćĺōśē (Ēść)",
+ "commandAddBrowserPanel": "Àďď Ɓŕōŵśēŕ Ƥàńēĺ",
+ "commandAddBrowserPanelKeywords": "ƀŕōŵśēŕ, ƥŕēvĩēŵ, ŵēƀ, ōƥēń ƀŕōŵśēŕ ƥŕēvĩēŵ, ŵēƀ ƥŕēvĩēŵ, àƥƥ ƥŕēvĩēŵ",
+ "commandAddChangesPanel": "Àďď Ćĥàńĝēś Ƥàńēĺ",
+ "commandAddChangesPanelKeywords": "ćĥàńĝēś, ďĩƒƒ, ĝĩţ ćĥàńĝēś, ćĥàńĝēď ƒĩĺēś, śōũŕćē ćōńţŕōĺ, ĝĩţ ďĩƒƒ, ŕēvĩēŵ ćĥàńĝēś",
+ "commandAddPlanPanel": "Àďď Ƥĺàń Ƥàńēĺ",
+ "commandAddPlanPanelKeywords": "ƥĺàń, ďōćũḿēńţ, ţàśķ ƥĺàń, ĩḿƥĺēḿēńţàţĩōń ƥĺàń, ƥĺàń ďēţàĩĺś",
+ "commandAddTerminalPanel": "Àďď Ţēŕḿĩńàĺ Ƥàńēĺ",
+ "commandAddTerminalPanelKeywords": "ţēŕḿĩńàĺ, śĥēĺĺ, ćōńśōĺē, ńēŵ ţēŕḿĩńàĺ, ōƥēń ţēŕḿĩńàĺ, ćōḿḿàńď ĺĩńē",
"commandAgentsSettingsKeywords": "àĝēńţś, àĝēńţ śēţţĩńĝś, àĝēńţ ƥŕōƒĩĺēś, ĩńśţàĺĺēď àĝēńţś, ćĺàũďē",
"commandArchiveTaskKeywords": "àŕćĥĩvē, àŕćĥĩvē ţàśķ, àŕćĥĩvē ćũŕŕēńţ ţàśķ, ćĺōśē ţàśķ, ƒĩńĩśĥ ţàśķ, ƥũţ àŵàŷ",
+ "commandCancelTurn": "Ćàńćēĺ Ţũŕń",
+ "commandCancelTurnKeywords": "ćàńćēĺ, śţōƥ, ţũŕń, ćàńćēĺ àĝēńţ, śţōƥ àĝēńţ, ĩńţēŕŕũƥţ àĝēńţ",
+ "commandCommitChanges": "Ćōḿḿĩţ Ćĥàńĝēś",
+ "commandCommitChangesKeywords": "ćōḿḿĩţ, ĝĩţ, śàvē ćĥàńĝēś, ĝĩţ ćōḿḿĩţ",
"commandConfigChatKeywords": "ćōńƒĩĝ ćĥàţ, ćōńƒĩĝ ḿōďē, ćōńƒĩĝũŕē ķàńďēv, ŵōŕķƒĺōŵ ćōńƒĩĝũŕàţĩōń, ḿćƥ ćōńƒĩĝũŕàţĩōń",
+ "commandCreateFile": "Ćŕēàţē Ƒĩĺē",
+ "commandCreateFileKeywords": "ćŕēàţē, ńēŵ, ƒĩĺē, àďď",
+ "commandCreateFilePlaceholder": "Ƒĩĺē ƥàţĥ ŕēĺàţĩvē ţō ŵōŕķśƥàćē ŕōōţ...",
+ "commandCreateNewTask": "Ćŕēàţē Ńēŵ Ţàśķ",
+ "commandCreateNewTaskKeywords": "ńēŵ, ćŕēàţē, ţàśķ, àďď",
+ "commandCreatePr": "Ćŕēàţē ƤŔ",
+ "commandCreatePrKeywords": "ƥũĺĺ ŕēqũēśţ, ƥŕ, ōƥēń ƥũĺĺ ŕēqũēśţ, śũƀḿĩţ ƥũĺĺ ŕēqũēśţ, ĝĩţ",
+ "commandCreateSubtask": "Ćŕēàţē Śũƀţàśķ",
+ "commandCreateSubtaskKeywords": "śũƀţàśķ, ćŕēàţē, ńēŵ śũƀţàśķ, ńēŵ śũƀ-ţàśķ, ćĥĩĺď ţàśķ",
"commandExecutorsSettingsKeywords": "ēxēćũţōŕś, ēxēćũţōŕ ƥŕōƒĩĺēś, ēxēćũţĩōń ēńvĩŕōńḿēńţ, ēńvĩŕōńḿēńţ vàŕĩàƀĺēś, ŕũńţĩḿē, ćōḿƥũţē",
"commandGoToAllTasks": "Ĝō ţō Àĺĺ Ţàśķś",
"commandGoToAllTasksKeywords": "ţàśķś, ĺĩśţ, àĺĺ",
@@ -51,11 +76,21 @@
"commandGoToStats": "Ĝō ţō Śţàţś",
"commandGoToStatsKeywords": "śţàţś, śţàţĩśţĩćś, àńàĺŷţĩćś, ḿēţŕĩćś",
"commandGroupActions": "Àćţĩōńś",
+ "commandGroupAgent": "Àĝēńţ",
"commandGroupCommands": "Ćōḿḿàńďś",
+ "commandGroupGit": "Ĝĩţ",
"commandGroupNavigation": "Ńàvĩĝàţĩōń",
+ "commandGroupPanels": "Ƥàńēĺś",
"commandGroupPreferences": "Ƥŕēƒēŕēńćēś",
"commandGroupSettings": "Śēţţĩńĝś",
"commandGroupTasks": "Ţàśķś",
+ "commandGroupView": "Vĩēŵ",
+ "commandGroupWorkspace": "Ŵōŕķśƥàćē",
+ "commandMergeKeywords": "ḿēŕĝē, ĝĩţ, ƀŕàńćĥ",
+ "commandNewAgent": "Ńēŵ Àĝēńţ",
+ "commandNewAgentKeywords": "ńēŵ, àĝēńţ, śēśśĩōń, śţàŕţ àĝēńţ, ńēŵ śēśśĩōń",
+ "commandOpenRecentTaskSwitcher": "Ōƥēń Ŕēćēńţ Ţàśķ Śŵĩţćĥēŕ",
+ "commandOpenRecentTaskSwitcherKeywords": "ŕēćēńţ, ţàśķ, śŵĩţćĥēŕ, ĥĩśţōŕŷ, ŕēćēńţ ţàśķś, śŵĩţćĥ ţàśķ, śŵĩţćĥ ŕēćēńţ ţàśķś",
"commandPaletteMode": "Ćōḿḿàńď ƥàĺēţţē ḿōďē",
"commandPreviewAddTerminalPanel": "Àďď Ţēŕḿĩńàĺ Ƥàńēĺ",
"commandPreviewCommitChanges": "Ćōḿḿĩţ Ćĥàńĝēś",
@@ -63,10 +98,19 @@
"commandPreviewPull": "Ƥũĺĺ",
"commandPreviewSearchTasks": "Śēàŕćĥ Ţàśķś",
"commandPromptsSettingsKeywords": "ƥŕōḿƥţś, ƥŕōḿƥţ śēţţĩńĝś, ćũśţōḿ ƥŕōḿƥţś, ƥŕōḿƥţ śńĩƥƥēţś, ƥŕōḿƥţ ţēḿƥĺàţēś",
+ "commandPullKeywords": "ƥũĺĺ, ĝĩţ, ƥũĺĺ ćĥàńĝēś, ďōŵńĺōàď ćĥàńĝēś",
+ "commandPushKeywords": "ƥũśĥ, ĝĩţ, ƥũśĥ ćĥàńĝēś, ƥũśĥ ţō ŕēḿōţē, ũƥĺōàď ćĥàńĝēś",
"commandQuickChat": "Qũĩćķ Ćĥàţ",
"commandQuickChatKeywords": "qũĩćķ ćĥàţ, ńēŵ qũĩćķ ćĥàţ, qũĩćķ qũēśţĩōń, àśķ àĝēńţ",
+ "commandRebaseKeywords": "ŕēƀàśē, ĝĩţ, ƀŕàńćĥ",
"commandSwitchToDarkMode": "Śŵĩţćĥ ţō Ďàŕķ Ḿōďē",
+ "commandSwitchToKanbanView": "Śŵĩţćĥ ţō Ķàńƀàń Vĩēŵ",
+ "commandSwitchToKanbanViewKeywords": "ķàńƀàń, ƀōàŕď, vĩēŵ",
"commandSwitchToLightMode": "Śŵĩţćĥ ţō Ĺĩĝĥţ Ḿōďē",
+ "commandSwitchToListView": "Śŵĩţćĥ ţō Ĺĩśţ Vĩēŵ",
+ "commandSwitchToListViewKeywords": "ĺĩśţ, ţàƀĺē, vĩēŵ",
+ "commandSwitchToPipelineView": "Śŵĩţćĥ ţō Ƥĩƥēĺĩńē Vĩēŵ",
+ "commandSwitchToPipelineViewKeywords": "ƥĩƥēĺĩńē, ĝŕàƥĥ, vĩēŵ",
"commandThemeKeywords": "ţĥēḿē, ćōĺōŕ ţĥēḿē, àƥƥēàŕàńćē",
"commandWorkspaceSettingsKeywords": "ŵōŕķśƥàćē, ŵōŕķśƥàćēś",
"commitsAndPushesToTheCurrent": "Ćōḿḿĩţś àńď ƥũśĥēś ţō ţĥē ćũŕŕēńţ ƀŕàńćĥ.",
@@ -126,6 +170,29 @@
"forwardStdinStdoutStraightToThe": "Ƒōŕŵàŕď śţďĩń/śţďōũţ śţŕàĩĝĥţ ţō ţĥē ĆĹĨ śũƀƥŕōćēśś. Ďĩśàƀĺēś ÀĆƤ.",
"getStarted": "Ĝēţ Śţàŕţēď",
"githubCliNotAuthenticated": "ĜĩţĤũƀ ĆĹĨ ńōţ àũţĥēńţĩćàţēď",
+ "gitOpAmendCommit": "Àḿēńď ćōḿḿĩţ",
+ "gitOpCommit": "Ćōḿḿĩţ",
+ "gitOperationFailedInRepos_one": "Ƒàĩĺēď ĩń {{count}} ŕēƥō — {{summary}}",
+ "gitOperationFailedInRepos_other": "Ƒàĩĺēď ĩń {{count}} ŕēƥōś — {{summary}}",
+ "gitOperationPartialDescription_one": "{{operation}} śũććēēďēď ĩń {{succeeded}} ōƒ {{count}} ŕēƥō ({{repos}}); ƒàĩĺēď ĩń {{summary}}",
+ "gitOperationPartialDescription_other": "{{operation}} śũććēēďēď ĩń {{succeeded}} ōƒ {{count}} ŕēƥōś ({{repos}}); ƒàĩĺēď ĩń {{summary}}",
+ "gitOperationPartiallySucceeded": "{{operation}} ƥàŕţĩàĺĺŷ śũććēēďēď",
+ "gitOperationRepoFailure": "{{repo}}: {{error}}",
+ "gitOperationSucceededInRepos_one": "{{operation}} śũććēēďēď ĩń {{count}} ŕēƥō: {{repos}}",
+ "gitOperationSucceededInRepos_other": "{{operation}} śũććēēďēď ĩń {{count}} ŕēƥōś: {{repos}}",
+ "gitOpForcePush": "Ƒōŕćē Ƥũśĥ",
+ "gitOpHardReset": "Ĥàŕď ŕēśēţ",
+ "gitOpMerge": "Ḿēŕĝē",
+ "gitOpPull": "Ƥũĺĺ",
+ "gitOpPush": "Ƥũśĥ",
+ "gitOpRebase": "Ŕēƀàśē",
+ "gitOperationCompleted": "{{operation}} ćōḿƥĺēţēď",
+ "gitOperationFailed": "{{operation}} ƒàĩĺēď",
+ "gitOperationRunning": "{{operation}}...",
+ "gitOperationScoped": "{{operation}} ({{repo}})",
+ "gitOperationSucceeded": "{{operation}} śũććēśśƒũĺ",
+ "gitOpRevertCommit": "Ŕēvēŕţ ćōḿḿĩţ",
+ "gitOpSoftReset": "Śōƒţ ŕēśēţ",
"gitScopeSkipped": "Śķĩƥƥēď {{scope}} ƀēćàũśē à ćĥĩĺď ŕēƥōśĩţōŕŷ ōƥēŕàţĩōń ƒàĩĺēď ({{failedScopes}}).",
"goToFirstPage": "Ĝō ţō ƒĩŕśţ ƥàĝē",
"goToLastPage": "Ĝō ţō ĺàśţ ƥàĝē",
@@ -234,6 +301,7 @@
"probing": "Ƥŕōƀĩńĝ",
"profileName": "Ƥŕōƒĩĺē ńàḿē",
"prompts": "Ƥŕōḿƥţś",
+ "reference": "Ŕēƒēŕēńćē",
"refreshAriaLabel": "Ŕēƒŕēśĥ {{label}}",
"refreshBranchesGitFetch": "Ŕēƒŕēśĥ ƀŕàńćĥēś (ĝĩţ ƒēţćĥ)",
"refreshingLabel": "Ŕēƒŕēśĥĩńĝ {{label}}...",
@@ -275,6 +343,7 @@
"scopeFiles": "Ƒĩĺēś",
"scopeTitleWithShortcut": "{{label}} ({{shortcut}})",
"scratchWorkspace": "śćŕàţćĥ ŵōŕķśƥàćē",
+ "script": "Śćŕĩƥţ",
"searchEllipsis": "Śēàŕćĥ…",
"searchFailedEditTheQueryOr": "Śēàŕćĥ ƒàĩĺēď. Ēďĩţ ţĥē qũēŕŷ ōŕ ŕēōƥēń śēàŕćĥ ţō ŕēţŕŷ.",
"searchForFiles": "Śēàŕćĥ ƒōŕ ƒĩĺēś...",
@@ -302,9 +371,12 @@
"skip": "Śķĩƥ",
"stagesAndCommitsChangesUsingConventional": "Śţàĝēś àńď ćōḿḿĩţś ćĥàńĝēś ũśĩńĝ Ćōńvēńţĩōńàĺ Ćōḿḿĩţś.",
"status": "Śţàţũś",
+ "step": "Śţēƥ",
"subtaskTitle": "Śũƀţàśķ ţĩţĺē",
"switchMode": "Śŵĩţćĥ ḿōďē",
"system": "Śŷśţēḿ",
+ "task": "Ţàśķ",
+ "terminal": "Ţēŕḿĩńàĺ",
"todos": "Ţōďōś",
"taskLoading": "Ĺōàďĩńĝ ţàśķ...",
"tasks": "Ţàśķś",
@@ -333,19 +405,25 @@
"typeACommand": "Ţŷƥē à ćōḿḿàńď...",
"typeToSearchFiles": "Ţŷƥē ţō śēàŕćĥ ƒĩĺēś...",
"typeToSearchTaskContents": "Ţŷƥē ţō śēàŕćĥ ţàśķ ćōńţēńţś…",
+ "unavailable": "Ũńàvàĩĺàƀĺē",
+ "unknown": "Ũńķńōŵń",
+ "unknownError": "Ũńķńōŵń ēŕŕōŕ",
"unknownPanel": "Ũńķńōŵń ƥàńēĺ: {{component}}",
"unsavedChanges": "Ũńśàvēď ćĥàńĝēś",
+ "untitledTask": "Ũńţĩţĺēď ţàśķ",
"usefulCommandsAgentSkills": "Ũśēƒũĺ ćōḿḿàńďś & àĝēńţ śķĩĺĺś",
"useScratchInstead": "Ũśē śćŕàţćĥ ĩńśţēàď",
"useThisFolder": "Ũśē ţĥĩś ƒōĺďēŕ",
"viewFullReleaseOnGithub": "Vĩēŵ ƒũĺĺ ŕēĺēàśē ōń ĜĩţĤũƀ",
"waitsForCiAndAutomatedReviews": "Ŵàĩţś ƒōŕ ĆĨ àńď àũţōḿàţēď ŕēvĩēŵś, ţĥēń ƒĩxēś ƒàĩĺũŕēś àńď àďďŕēśśēś ćōḿḿēńţś.",
+ "walkthrough": "Ŵàĺķţĥŕōũĝĥ",
"whatSNew": "Ŵĥàţ'ś Ńēŵ",
"whenItsDoneTheAgentOpensAPr": "Ŵĥēń ĩţ'ś ďōńē, ţĥē àĝēńţ ōƥēńś à ƥũĺĺ ŕēqũēśţ ţō <2>{{repo}}2> ƒōŕ ţĥē ḿàĩńţàĩńēŕś ţō ŕēvĩēŵ, śàvĩńĝ ţĥēḿ ţĩḿē àńď śĥĩƥƥĩńĝ ţĥē ĩḿƥŕōvēḿēńţ ţō ēvēŕŷōńē.",
"workflow": "Ŵōŕķƒĺōŵ",
"workflowsControlTheStepsAutomationAnd": "Ŵōŕķƒĺōŵś ćōńţŕōĺ ţĥē śţēƥś, àũţōḿàţĩōń, àńď àĝēńţ ƀēĥàvĩōŕ ƒōŕ ŷōũŕ ţàśķś. Ŷōũ ćàń àďď ḿōŕē ŵōŕķƒĺōŵś ƒŕōḿ Śēţţĩńĝś.",
"workspace": "Ŵōŕķśƥàćē",
"workspaces": "Ŵōŕķśƥàćēś",
+ "you": "Ŷōũ",
"youCanUseTheNavigationOr": "Ŷōũ ćàń ũśē ţĥē ńàvĩĝàţĩōń ōŕ ŕēĺōàď ţō ţŕŷ àĝàĩń.",
"yourAgentImplementsItInThe": "Ŷōũŕ àĝēńţ ĩḿƥĺēḿēńţś ĩţ ĩń ţĥē ķàńďēv ŕēƥōśĩţōŕŷ, ŵĩţĥ ţēśţś",
"youVerifyAndTestTheChange": "Ŷōũ vēŕĩƒŷ àńď ţēśţ ţĥē ćĥàńĝē ĩń à śēćōńď ķàńďēv ĩńśţàńćē"
diff --git a/apps/web/src/locales/pseudo/editors.json b/apps/web/src/locales/pseudo/editors.json
index c15d5a74cd..8c42240321 100644
--- a/apps/web/src/locales/pseudo/editors.json
+++ b/apps/web/src/locales/pseudo/editors.json
@@ -19,10 +19,17 @@
"copyLine": "Ćōƥŷ ĺĩńē {{lineNumber}}",
"copyPath": "Ćōƥŷ ƥàţĥ",
"default": "ďēƒàũĺţ",
+ "deleteFailed": "Ďēĺēţē ƒàĩĺēď",
"deleteFile": "Ďēĺēţē ƒĩĺē",
"disableWordWrap": "Ďĩśàƀĺē ŵōŕď ŵŕàƥ",
"enableWordWrap": "Ēńàƀĺē ŵōŕď ŵŕàƥ",
+ "errorWhileDeletingFile": "Àń ēŕŕōŕ ōććũŕŕēď ŵĥĩĺē ďēĺēţĩńĝ ţĥē ƒĩĺē",
+ "errorWhileSavingFile": "Àń ēŕŕōŕ ōććũŕŕēď ŵĥĩĺē śàvĩńĝ ţĥē ƒĩĺē",
+ "failedToDeleteFile": "Ƒàĩĺēď ţō ďēĺēţē ƒĩĺē",
+ "failedToSaveFile": "Ƒàĩĺēď ţō śàvē ƒĩĺē",
"failedToSendComment": "Ƒàĩĺēď ţō śēńď ćōḿḿēńţ",
+ "fileSavedOverwritten": "Ƒĩĺē śàvēď (ōvēŕŵŕĩţţēń)",
+ "fileSavedOverwrittenDescription": "Ţĥē ƒĩĺē ŵàś ḿōďĩƒĩēď ēxţēŕńàĺĺŷ. Ŷōũŕ vēŕśĩōń ŵàś śàvēď.",
"foldUnchangedLines": "Ƒōĺď ũńćĥàńĝēď ĺĩńēś",
"hideDiffIndicators": "Ĥĩďē ďĩƒƒ ĩńďĩćàţōŕś",
"highlight": "Ĥĩĝĥĺĩĝĥţ",
@@ -35,6 +42,7 @@
"noDiffAvailable": "Ńō ďĩƒƒ àvàĩĺàƀĺē",
"noEditorsConfigured": "Ńō ēďĩţōŕś ćōńƒĩĝũŕēď",
"openFolder": "Ōƥēń ƒōĺďēŕ",
+ "openTaskSessionBeforeSending": "Ōƥēń à ţàśķ śēśśĩōń ƀēƒōŕē śēńďĩńĝ ţō ţĥē àĝēńţ.",
"openWith": "Ōƥēń ŵĩţĥ...",
"pasteLink": "Ƥàśţē ĺĩńķ...",
"pleaseTryAgain": "Ƥĺēàśē ţŕŷ àĝàĩń.",
@@ -43,6 +51,7 @@
"reload": "Ŕēĺōàď",
"revertAllChanges": "Ŕēvēŕţ àĺĺ ćĥàńĝēś",
"revertChanges": "Ŕēvēŕţ ćĥàńĝēś",
+ "saveFailed": "Śàvē ƒàĩĺēď",
"saving": "Śàvĩńĝ...",
"sentToTheAgent": "Śēńţ ţō ţĥē àĝēńţ.",
"showAllLines": "Śĥōŵ àĺĺ ĺĩńēś",
diff --git a/apps/web/src/locales/pseudo/github.json b/apps/web/src/locales/pseudo/github.json
index 7cd6c4df1b..41a53a5bad 100644
--- a/apps/web/src/locales/pseudo/github.json
+++ b/apps/web/src/locales/pseudo/github.json
@@ -180,6 +180,7 @@
"failedToDeleteReviewWatch": "Ƒàĩĺēď ţō ďēĺēţē ŕēvĩēŵ ŵàţćĥ",
"failedToLoadGithubWorkspaceSettings": "Ƒàĩĺēď ţō ĺōàď ĜĩţĤũƀ ŵōŕķśƥàćē śēţţĩńĝś",
"failedToMerge": "Ƒàĩĺēď ţō ḿēŕĝē",
+ "failedToReRequestReview": "Ƒàĩĺēď ţō ŕē-ŕēqũēśţ ŕēvĩēŵ",
"failedToResetIssueWatch": "Ƒàĩĺēď ţō ŕēśēţ ĩśśũē ŵàţćĥ",
"failedToResetReviewWatch": "Ƒàĩĺēď ţō ŕēśēţ ŕēvĩēŵ ŵàţćĥ",
"failedToSaveDefaultQueries": "Ƒàĩĺēď ţō śàvē ďēƒàũĺţ qũēŕĩēś",
@@ -445,6 +446,7 @@
"reviewCount_other": "{{count}} ŕēvĩēŵś",
"reviewFollowUp": "Ŕēvĩēŵ ƒōĺĺōŵ-ũƥ",
"reviewPermissions": "Ŕēvĩēŵ ƥēŕḿĩśśĩōńś",
+ "reviewReRequestedFrom": "Ŕēvĩēŵ ŕē-ŕēqũēśţēď ƒŕōḿ {{reviewer}}",
"reviewWatch": "ŕēvĩēŵ ŵàţćĥ",
"reviewWatchCreated": "Ŕēvĩēŵ ŵàţćĥ ćŕēàţēď",
"reviewWatchDeleted": "Ŕēvĩēŵ ŵàţćĥ ďēĺēţēď",
@@ -468,6 +470,7 @@
"selectWorkflow": "Śēĺēćţ ŵōŕķƒĺōŵ",
"selectWorkspace": "Śēĺēćţ ŵōŕķśƥàćē",
"selectedRepositories": "Śēĺēćţēď ŕēƥōśĩţōŕĩēś",
+ "selectWorkspaceBeforeRequestingReview": "Śēĺēćţ à ŵōŕķśƥàćē ƀēƒōŕē ŕēqũēśţĩńĝ ŕēvĩēŵ.",
"setTheseExactUrlsCreateAClientSecret": "Śēţ ţĥēśē ēxàćţ ŨŔĹś, ćŕēàţē à ćĺĩēńţ śēćŕēţ àńď ŵēƀĥōōķ śēćŕēţ, àńď ďōŵńĺōàď à ƥŕĩvàţē ķēŷ. Ţĥĩś ōńē-ţĩḿē śēţũƥ ēxƥĩŕēś {{expiresAt}}.",
"showGithubApiLimits": "Śĥōŵ ĜĩţĤũƀ ÀƤĨ ĺĩḿĩţś",
"showLess": "Śĥōŵ ĺēśś",
diff --git a/apps/web/src/locales/pseudo/gitlab.json b/apps/web/src/locales/pseudo/gitlab.json
index ef5c4e6d75..7573f03f62 100644
--- a/apps/web/src/locales/pseudo/gitlab.json
+++ b/apps/web/src/locales/pseudo/gitlab.json
@@ -316,7 +316,6 @@
"mrAutomationExplainAutoFixRounds": "Ēxƥĺàĩń àũţō-ƒĩx ŕōũńďś",
"mrAutomationAutoFixPromptSaveFailed": "Ƒàĩĺēď ţō śàvē àũţō-ƒĩx ƥŕōḿƥţ.",
"mrAutomationAutoFixPromptResetFailed": "Ƒàĩĺēď ţō ŕēśēţ àũţō-ƒĩx ƥŕōḿƥţ.",
- "mrPopoverOpenDetail": "Ōƥēń ḿēŕĝē ŕēqũēśţ ďēţàĩĺ",
"mrPopoverOpenInGitlab": "Ōƥēń !{{mriid}} ĩń ĜĩţĹàƀ",
"mrPopoverUnlink": "Ũńĺĩńķ !{{mriid}}",
"mrPopoverPassRate": "Ƥàśś ŕàţē",
diff --git a/apps/web/src/locales/pseudo/kanban.json b/apps/web/src/locales/pseudo/kanban.json
index 24643153a3..440b6d2440 100644
--- a/apps/web/src/locales/pseudo/kanban.json
+++ b/apps/web/src/locales/pseudo/kanban.json
@@ -4,6 +4,7 @@
"allRepositories": "Àĺĺ ŕēƥōśĩţōŕĩēś",
"allWorkflows": "Àĺĺ ŵōŕķƒĺōŵś",
"allWorkflowsTitleCase": "Àĺĺ Ŵōŕķƒĺōŵś",
+ "allWorkspaces": "Àĺĺ ŵōŕķśƥàćēś",
"approvalRequired": "Àƥƥŕōvàĺ Ŕēqũĩŕēď",
"archive": "Àŕćĥĩvē",
"archiveCount_one": "Àŕćĥĩvē {{count}}",
diff --git a/apps/web/src/locales/pseudo/office.json b/apps/web/src/locales/pseudo/office.json
index eb5375bce0..9bf69b1557 100644
--- a/apps/web/src/locales/pseudo/office.json
+++ b/apps/web/src/locales/pseudo/office.json
@@ -101,10 +101,15 @@
"botTokenChatId": "{\"ƀōţ_ţōķēń\": \"...\", \"ćĥàţ_ĩď\": \"...\"}",
"branch": "Ɓŕàńćĥ",
"browseSkillsSh": "Ɓŕōŵśē śķĩĺĺś.śĥ",
+ "budgetActionBlockNewTasks": "ƀĺōćķ ńēŵ ţàśķś",
+ "budgetActionNotifyOnly": "ńōţĩƒŷ ōńĺŷ",
+ "budgetActionPauseAgent": "ƥàũśē àĝēńţ",
"budgetAlert": "Ɓũďĝēţ Àĺēŕţ",
"budgetAmount": "Ɓũďĝēţ: {{amount}}",
"budgetCapConcurrencyAndExecutionEnvironment": "Ɓũďĝēţ ćàƥ, ćōńćũŕŕēńćŷ, àńď ēxēćũţĩōń ēńvĩŕōńḿēńţ.",
"budgetLimitsConcurrencyAndExecutionEnvironment": "Ɓũďĝēţ ĺĩḿĩţś, ćōńćũŕŕēńćŷ, àńď ēxēćũţĩōń ēńvĩŕōńḿēńţ.",
+ "budgetPeriodMonthly": "ḿōńţĥĺŷ",
+ "budgetPeriodTotal": "ţōţàĺ",
"budgetPolicies": "Ɓũďĝēţ Ƥōĺĩćĩēś",
"budgetPolicyCreated": "Ɓũďĝēţ ƥōĺĩćŷ ćŕēàţēď",
"budgetPolicyDeleted": "Ɓũďĝēţ ƥōĺĩćŷ ďēĺēţēď",
diff --git a/apps/web/src/locales/pseudo/settings.json b/apps/web/src/locales/pseudo/settings.json
index 6a4f600be0..42fccdd685 100644
--- a/apps/web/src/locales/pseudo/settings.json
+++ b/apps/web/src/locales/pseudo/settings.json
@@ -347,10 +347,17 @@
"layoutEditorForProfile": "{{name}} ēďĩţōŕ",
"layoutProfile": "ĺàŷōũţ ƥŕōƒĩĺē",
"layoutProfileCopyName": "{{name}} ćōƥŷ",
+ "layoutProfileDefault": "Ďēƒàũĺţ",
+ "layoutProfileDefaultDescription": "Àĝēńţ ŵĩţĥ Ƒĩĺēś, Ćĥàńĝēś, àńď Ţēŕḿĩńàĺ",
"layoutProfileName": "Ĺàŷōũţ ƥŕōƒĩĺē ńàḿē",
"layoutProfileNamesMustNotBe": "Ĺàŷōũţ ƥŕōƒĩĺē ńàḿēś ḿũśţ ńōţ ƀē ēḿƥţŷ",
+ "layoutProfilePlanMode": "Ƥĺàń Ḿōďē",
+ "layoutProfilePlanModeDescription": "Àĝēńţ àńď Ƥĺàń śĩďē ƀŷ śĩďē",
+ "layoutProfilePreviewMode": "Ƥŕēvĩēŵ Ḿōďē",
+ "layoutProfilePreviewModeDescription": "Àĝēńţ àńď Ɓŕōŵśēŕ śĩďē ƀŷ śĩďē",
"layoutProfiles": "Ĺàŷōũţ ƥŕōƒĩĺēś",
"layoutProfilesWereNotSaved": "Ĺàŷōũţ ƥŕōƒĩĺēś ŵēŕē ńōţ śàvēď",
+ "layoutProfileVsCodeDescription": "Àĝēńţ àńď VŚ Ćōďē śĩďē ƀŷ śĩďē",
"layouts": "Ĺàŷōũţś",
"ligatures": "Ĺĩĝàţũŕēś",
"light": "Ĺĩĝĥţ",
diff --git a/apps/web/src/locales/pseudo/sidebar.json b/apps/web/src/locales/pseudo/sidebar.json
index 73f46b2263..9a6d8cd477 100644
--- a/apps/web/src/locales/pseudo/sidebar.json
+++ b/apps/web/src/locales/pseudo/sidebar.json
@@ -101,6 +101,7 @@
"toggleTheme": "Ţōĝĝĺē ţĥēḿē",
"unsavedFilterChanges": "Ũńśàvēď ƒĩĺţēŕ ćĥàńĝēś",
"viewAll": "Àĺĺ",
+ "viewAllTasks": "Àĺĺ ţàśķś",
"viewLabel": "Vĩēŵ: {{name}}",
"whatsNew": "Ŵĥàţ'ś ńēŵ",
"work": "Ŵōŕķ",
diff --git a/apps/web/src/locales/pseudo/task.json b/apps/web/src/locales/pseudo/task.json
index 0687af307e..6055446927 100644
--- a/apps/web/src/locales/pseudo/task.json
+++ b/apps/web/src/locales/pseudo/task.json
@@ -1,5 +1,8 @@
{
+ "agentCliNotInstalled": "Àĝēńţ ĆĹĨ ńōţ ĩńśţàĺĺēď",
+ "agentProbeFailed": "Àĝēńţ ƥŕōƀē ƒàĩĺēď",
"aPullRequestAlreadyExistsFor": "À ƥũĺĺ ŕēqũēśţ àĺŕēàďŷ ēxĩśţś ƒōŕ ţĥĩś ţàśķ",
+ "archivedTask": "Àŕćĥĩvēď ţàśķ",
"aSharedDocumentForYouAnd": "À śĥàŕēď ďōćũḿēńţ ƒōŕ ŷōũ àńď ţĥē àĝēńţ ţō ćōĺĺàƀōŕàţē ōń ţĥē àƥƥŕōàćĥ",
"aboutContextWindowSource": "Àƀōũţ ćōńţēxţ ŵĩńďōŵ śōũŕćē",
"activeBadge": "ÀĆŢĨVĒ",
@@ -269,6 +272,7 @@
"couldNotAddSources": "Ćōũĺď ńōţ àďď śōũŕćēś. Ŷōũŕ ēńţŕĩēś àŕē śţĩĺĺ ĥēŕē ţō ŕēţŕŷ.",
"couldNotLoadRepositories": "Ćōũĺď ńōţ ĺōàď ŕēƥōśĩţōŕĩēś: {{message}}",
"couldNotResolveRemoteRepository": "Ćōũĺď ńōţ ŕēśōĺvē ŕēḿōţē ŕēƥōśĩţōŕŷ: {{message}}",
+ "couldNotSaveStatusBarOrder": "Ćōũĺď ńōţ śàvē śţàţũś ƀàŕ ōŕďēŕ",
"couldnTStartASession": "Ćōũĺďń'ţ śţàŕţ à śēśśĩōń",
"create": "Ćŕēàţē",
"createANewBranch": "Ćŕēàţē à ńēŵ ƀŕàńćĥ",
@@ -290,6 +294,7 @@
"creatingEllipsis": "Ćŕēàţĩńĝ...",
"creatingFrom": "Ćŕēàţĩńĝ {{shortName}} ƒŕōḿ <4>{{displayBranch}}4>",
"creatingFromTo": "Ćŕēàţĩńĝ {{shortName}} ƒŕōḿ <4>{{displayBranch}}4> ţō <7>{{baseBranch}}7>",
+ "credentialsAvailableInExecutor": "Ćŕēďēńţĩàĺś àvàĩĺàƀĺē ĩń ţĥē śēĺēćţēď ēxēćũţōŕ",
"ctrl": "Ćţŕĺ",
"current": "ćũŕŕēńţ",
"current2": "Ćũŕŕēńţ",
@@ -401,14 +406,19 @@
"environmentPrepared": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕēď",
"environmentPreparedOnFreshSandbox": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕēď ōń à ƒŕēśĥ śàńďƀōx",
"environmentPreparedWithWarnings": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕēď ŵĩţĥ ŵàŕńĩńĝś",
+ "environmentReset": "Ēńvĩŕōńḿēńţ ŕēśēţ",
+ "environmentResetFailed": "Ŕēśēţ ƒàĩĺēď: {{error}}",
"environmentSetupFailed": "Ēńvĩŕōńḿēńţ śēţũƥ ƒàĩĺēď",
"environmentSetupFinishedWithErrors": "Ēńvĩŕōńḿēńţ śēţũƥ ƒĩńĩśĥēď ŵĩţĥ ēŕŕōŕś",
+ "environmentStateCurrent": "Ćũŕŕēńţ śţàţē: {{state}}",
"error": "Ēŕŕōŕ",
"errorData": "Ēŕŕōŕ Ďàţà",
"eventCount_one": "{{count}} ēvēńţ",
"eventCount_other": "{{count}} ēvēńţś",
"executor2": "Ēxēćũţōŕ",
"executorEnvironmentIsUnavailable": "Ēxēćũţōŕ ēńvĩŕōńḿēńţ ĩś ũńàvàĩĺàƀĺē.",
+ "executorEnvironmentRunning": "Ēxēćũţōŕ ēńvĩŕōńḿēńţ ŕũńńĩńĝ",
+ "executorEnvironmentStopped": "Ēxēćũţōŕ ēńvĩŕōńḿēńţ śţōƥƥēď",
"executorHintDocker": "À Ďōćķēŕ ćōńţàĩńēŕ ŵĩĺĺ ƀē ćŕēàţēď ƒŕōḿ ţĥē śēĺēćţēď ƀàśē ƀŕàńćĥ àńď ćĥēćķēď ōũţ ōń à ţàśķ ƀŕàńćĥ.",
"executorHintLocal": "Ţĥē àĝēńţ ŵĩĺĺ ŕũń ďĩŕēćţĺŷ ōń ţĥē ŕēƥōśĩţōŕŷ.",
"executorHintWorktreeMulti": "À ĝĩţ ŵōŕķţŕēē ŵĩĺĺ ƀē ćŕēàţēď ƒōŕ ēàćĥ ŕēƥōśĩţōŕŷ ĩń à ƥàŕēńţ ƒōĺďēŕ. Ţĥē àĝēńţ ŕũńś ĩń ţĥàţ ƥàŕēńţ ƒōĺďēŕ śō ĩţ ćàń śēē ēvēŕŷ ŵōŕķţŕēē śĩďē ƀŷ śĩďē.",
@@ -439,6 +449,8 @@
"failedToChangeModel": "Ƒàĩĺēď ţō ćĥàńĝē ḿōďēĺ",
"failedToDeleteDocument": "Ƒàĩĺēď ţō ďēĺēţē ďōćũḿēńţ",
"failedToDeleteLayout": "Ƒàĩĺēď ţō ďēĺēţē ĺàŷōũţ",
+ "failedToDiscardChanges": "Ƒàĩĺēď ţō ďĩśćàŕď ćĥàńĝēś",
+ "failedToGenerateContent": "Ƒàĩĺēď ţō ĝēńēŕàţē ćōńţēńţ",
"failedToLinkGithubIssue": "Ƒàĩĺēď ţō ĺĩńķ ĜĩţĤũƀ ĩśśũē.",
"failedToLinkGithubPullRequest": "Ƒàĩĺēď ţō ĺĩńķ ĜĩţĤũƀ ƥũĺĺ ŕēqũēśţ.",
"failedToLinkTarget": "Ƒàĩĺēď ţō ĺĩńķ {{target}}.",
@@ -454,13 +466,16 @@
"failedToRenameItem": "Ƒàĩĺēď ţō ŕēńàḿē ĩţēḿ",
"failedToRenameTask": "Ƒàĩĺēď ţō ŕēńàḿē ţàśķ",
"failedToRestorePlan": "Ƒàĩĺēď ţō ŕēśţōŕē ƥĺàń",
+ "failedToResumeSession": "Ƒàĩĺēď ţō ŕēśũḿē śēśśĩōń",
"failedToRevokeShare": "Ƒàĩĺēď ţō ŕēvōķē śĥàŕē.",
"failedToSaveDocument": "Ƒàĩĺēď ţō śàvē ďōćũḿēńţ",
"failedToSaveLayout": "Ƒàĩĺēď ţō śàvē ĺàŷōũţ",
"failedToSendComment": "Ƒàĩĺēď ţō śēńď ćōḿḿēńţ",
"failedToSendComments": "Ƒàĩĺēď ţō śēńď ćōḿḿēńţś",
"failedToSendMessage": "Ƒàĩĺēď ţō śēńď ḿēśśàĝē",
+ "failedToStartTunnel": "Ƒàĩĺēď ţō śţàŕţ ţũńńēĺ: {{error}}",
"failedToStartVsCode": "Ƒàĩĺēď ţō śţàŕţ VŚ Ćōďē",
+ "failedToStopTunnel": "Ƒàĩĺēď ţō śţōƥ ţũńńēĺ: {{error}}",
"failedToUnarchiveTask": "Ƒàĩĺēď ţō ũńàŕćĥĩvē ţàśķ",
"failedToUnlinkGithubIssue": "Ƒàĩĺēď ţō ũńĺĩńķ ĜĩţĤũƀ ĩśśũē.",
"failedToUpload": "Ƒàĩĺēď ţō ũƥĺōàď {{name}}",
@@ -519,6 +534,7 @@
"fullImpactDetails": "Ƒũĺĺ ĩḿƥàćţ ďēţàĩĺś",
"fullSize": "Ƒũĺĺ śĩźē {{alt}}",
"generatingSummary": "Ĝēńēŕàţĩńĝ śũḿḿàŕŷ...",
+ "generationFailed": "Ĝēńēŕàţĩōń ƒàĩĺēď",
"gitActions": "Ĝĩţ àćţĩōńś",
"gitPollMode": "Ĝĩţ ƥōĺĺ: {{mode}} ({{label}})",
"githubIssue": "ĜĩţĤũƀ Ĩśśũē",
@@ -599,6 +615,7 @@
"kandevListWorkspaces": "Ķàńďēv: Ĺĩśţ Ŵōŕķśƥàćēś",
"kandevMessageTask": "Ķàńďēv: Ḿēśśàĝē Ţàśķ",
"kandevMoveTask": "Ķàńďēv: Ḿōvē Ţàśķ",
+ "kandevUpdateAvailable": "Ķàńďēv ũƥďàţē àvàĩĺàƀĺē",
"kandevUpdateTask": "Ķàńďēv: Ũƥďàţē Ţàśķ",
"kandevUpdateTaskPlan": "Ķàńďēv: Ũƥďàţē Ţàśķ Ƥĺàń",
"kandevWriteTaskDocument": "Ķàńďēv: Ŵŕĩţē Ţàśķ Ďōćũḿēńţ",
@@ -703,6 +720,9 @@
"moreNotShown_one": "+ {{count}} ḿōŕē ńōţ śĥōŵń",
"moreNotShown_other": "+ {{count}} ḿōŕē ńōţ śĥōŵń",
"moreToolbarActions": "Ḿōŕē ţōōĺƀàŕ àćţĩōńś",
+ "moveFailed": "Ḿōvē ƒàĩĺēď",
+ "moveFailedFiles": "Śōḿē ƒĩĺēś ćōũĺď ńōţ ƀē ḿōvēď",
+ "moveFailedUnexpected": "Àń ēŕŕōŕ ōććũŕŕēď ŵĥĩĺē ḿōvĩńĝ ƒĩĺēś",
"moveHeadToThisCommitKeep": "Ḿōvē ĤĒÀĎ ţō ţĥĩś ćōḿḿĩţ. Ķēēƥ àĺĺ ćĥàńĝēś śţàĝēď.",
"moveHere": "Ḿōvē ĥēŕē",
"moveTaskToTheNextWorkflow": "Ḿōvē ţàśķ ţō ţĥē ńēxţ ŵōŕķƒĺōŵ śţēƥ",
@@ -733,6 +753,7 @@
"nextMessage": "Ńēxţ ḿēśśàĝē",
"nextQuestion": "Ńēxţ qũēśţĩōń",
"noActiveSession": "Ńō àćţĩvē śēśśĩōń",
+ "noActiveSessionDescription": "Śţàŕţ à śēśśĩōń ƒĩŕśţ ţō ũśē ÀĨ ĝēńēŕàţĩōń",
"noActiveTask": "Ńō àćţĩvē ţàśķ",
"noActivityYet": "Ńō àćţĩvĩţŷ ŷēţ",
"noAgentFound": "Ńō àĝēńţ ƒōũńď.",
@@ -796,6 +817,7 @@
"noSavedLayouts": "Ńō śàvēď ĺàŷōũţś",
"noSessionsYetLaunchOneTo": "Ńō śēśśĩōńś ŷēţ. Ĺàũńćĥ ōńē ţō ĝēţ śţàŕţēď.",
"noSteps": "Ńō śţēƥś",
+ "noStepsConfigured": "Ńō śţēƥś ćōńƒĩĝũŕēď",
"noStepsFound": "Ńō śţēƥś ƒōũńď.",
"noSubIssues": "Ńō śũƀ-ĩśśũēś",
"noTaskSelected": "Ńō ţàśķ śēĺēćţēď",
@@ -836,9 +858,20 @@
"openWorkspaceSettings": "Ōƥēń ŵōŕķśƥàćē śēţţĩńĝś",
"option": "ōƥţĩōń {{index}}",
"orTypeACustomAnswer": "Ōŕ ţŷƥē à ćũśţōḿ àńśŵēŕ…",
+ "otherChanges": "Ōţĥēŕ ćĥàńĝēś",
"output": "Ōũţƥũţ",
"outputTokens": "Ōũţƥũţ ţōķēńś",
"outputTruncated": "Ōũţƥũţ ţŕũńćàţēď",
+ "panelAgent": "Àĝēńţ",
+ "panelChanges": "Ćĥàńĝēś",
+ "panelDiffViewer": "Ďĩƒƒ Vĩēŵēŕ",
+ "panelFileDiff": "Ďĩƒƒ [{{file}}]",
+ "panelFiles": "Ƒĩĺēś",
+ "panelMergeRequest": "Ḿēŕĝē Ŕēqũēśţ",
+ "panelPlan": "Ƥĺàń",
+ "panelPrDetails": "ƤŔ Ďēţàĩĺś",
+ "panelPullRequest": "Ƥũĺĺ Ŕēqũēśţ",
+ "panelTerminal": "Ţēŕḿĩńàĺ",
"parent": "Ƥàŕēńţ",
"parkedCount": "Ƥàŕķēď ({{parked}})",
"parkedTerminals": "Ƥàŕķēď ţēŕḿĩńàĺś",
@@ -850,6 +883,7 @@
"pendingReviewCommentsHelp_one": "{{count}} ƥēńďĩńĝ ŕēvĩēŵ ćōḿḿēńţ. Ćĺĩćķ ţō ēxƥàńď ţĥē ĺĩśţ — ŷōũ ćàń ēďĩţ ēàćĥ ćōḿḿēńţ, ćĺĩćķ ţĥē ƒĩĺē ƥàţĥ ţō ĵũḿƥ ţō ţĥē śōũŕćē, ōŕ ŕēḿōvē à ćōḿḿēńţ ŵĩţĥ ţĥē ţŕàśĥ ĩćōń.",
"pendingReviewCommentsHelp_other": "{{count}} ƥēńďĩńĝ ŕēvĩēŵ ćōḿḿēńţś. Ćĺĩćķ ţō ēxƥàńď ţĥē ĺĩśţ — ŷōũ ćàń ēďĩţ ēàćĥ ćōḿḿēńţ, ćĺĩćķ ţĥē ƒĩĺē ƥàţĥ ţō ĵũḿƥ ţō ţĥē śōũŕćē, ōŕ ŕēḿōvē à ćōḿḿēńţ ŵĩţĥ ţĥē ţŕàśĥ ĩćōń.",
"permissionRequested": "Ƥēŕḿĩśśĩōń ŕēqũēśţēď",
+ "permissionRequired": "Ƥēŕḿĩśśĩōń Ŕēqũĩŕēď",
"pickADifferentRepoLocalFolder": "Ƥĩćķ à ďĩƒƒēŕēńţ ŕēƥō, ĺōćàĺ ƒōĺďēŕ, ōŕ ŕēḿōţē ŨŔĹ",
"pickAStartingFolderOptional": "ƥĩćķ à śţàŕţĩńĝ ƒōĺďēŕ (ōƥţĩōńàĺ)",
"pickOrPasteARepo": "Ƥĩćķ ōŕ ƥàśţē à ŕēƥō",
@@ -1219,6 +1253,7 @@
"statusInProgress": "Ĩń Ƥŕōĝŕēśś",
"statusInReview": "Ĩń Ŕēvĩēŵ",
"statusTodo": "Ţōďō",
+ "statusUpdate": "Śţàţũś ũƥďàţē",
"stepCount_one": "{{count}} śţēƥ",
"stepCount_other": "{{count}} śţēƥś",
"stepFieldLabel": "śţēƥ",
@@ -1233,6 +1268,7 @@
"stopVoiceInput": "Śţōƥ vōĩćē ĩńƥũţ",
"stoppedWithAnError": "śţōƥƥēď ŵĩţĥ àń ēŕŕōŕ",
"stopping": "Śţōƥƥĩńĝ…",
+ "subagent": "Śũƀàĝēńţ",
"subIssues": "Śũƀ-ĩśśũēś",
"subTasks": "Śũƀ-ţàśķś",
"submit": "Śũƀḿĩţ",
@@ -1244,6 +1280,7 @@
"subtasksStayActiveUnlessYouTick": "Śũƀţàśķś śţàŷ àćţĩvē ũńĺēśś ŷōũ ţĩćķ ţĥĩś. Ţĥēŷ ḿàŷ śţĩĺĺ ƀē ĩń ƥŕōĝŕēśś.",
"successful": "{{operationName}} śũććēśśƒũĺ",
"summarize": "Śũḿḿàŕĩźē",
+ "summarizeReturnedNoResult": "Śũḿḿàŕĩźē ũţĩĺĩţŷ ŕēţũŕńēď ńō ŕēśũĺţ",
"summarizeSession": "Śũḿḿàŕĩźē śēśśĩōń",
"summarizeSessionNamed": "Śũḿḿàŕĩźē {{label}}",
"summarizing": "Śũḿḿàŕĩźĩńĝ...",
@@ -1337,6 +1374,7 @@
"tokenCount_other": "{{formatted}} ţōķēńś",
"tokens": "ţōķēńś",
"tool": "ţōōĺ",
+ "toolCall": "Ţōōĺ ćàĺĺ",
"toolCallCount_one": "{{count}} ţōōĺ ćàĺĺ",
"toolCallCount_other": "{{count}} ţōōĺ ćàĺĺś",
"toolCount_one": "{{count}} ţōōĺ",
@@ -1353,6 +1391,8 @@
"truncatedParen": "(ţŕũńćàţēď)",
"tryAgain": "Ţŕŷ àĝàĩń",
"tunnel": "Ţũńńēĺ",
+ "tunnelStartedOnPort": "Ţũńńēĺ śţàŕţēď ōń ƥōŕţ {{port}}",
+ "tunnelStopped": "Ţũńńēĺ śţōƥƥēď",
"turnForDuration": "ƒōŕ {{duration}}",
"turnMessageCount_one": "· {{count}} ḿēśśàĝē",
"turnMessageCount_other": "· {{count}} ḿēśśàĝēś",
@@ -1466,7 +1506,7 @@
"yourReviewIsRequestedOnThis": "Ŷōũŕ ŕēvĩēŵ ĩś ŕēqũēśţēď ōń ţĥĩś ţàśķ.",
"autopilot": "Àũţōƥĩĺōţ",
"autopilotDescription": "Ţĥē àĝēńţ ŵōŕķś ĩńďēƥēńďēńţĺŷ àńď àśķś ţĥē ƥàŕēńţ ţàśķ ōńĺŷ ŵĥēń à ćŕĩţĩćàĺ ďēćĩśĩōń ƀĺōćķś ƥŕōĝŕēśś. Ĩţ ţĥēń ŵàĩţś ƒōŕ ţĥē àńśŵēŕ ƀēƒōŕē ćōńţĩńũĩńĝ.",
- "autopilotInfoLabel": "Mōŕē ĩńƒōŕḿàţĩōń àƀōũţ àũţōƥĩĺōţ",
+ "autopilotInfoLabel": "Ḿōŕē ĩńƒōŕḿàţĩōń àƀōũţ àũţōƥĩĺōţ",
"autopilotSidebarDescription": "Àũţōƥĩĺōţ ţàśķ",
"autopilotChatDescription": "Àũţōƥĩĺōţ ĩś ēńàƀĺēď. Ţĥē àĝēńţ àśķś ĩţś ƥàŕēńţ ōńĺŷ ƒōŕ ćŕĩţĩćàĺ ďēćĩśĩōńś."
}
diff --git a/apps/web/src/locales/pseudo/tasks.json b/apps/web/src/locales/pseudo/tasks.json
index 5d7820e9d1..b6fce2004d 100644
--- a/apps/web/src/locales/pseudo/tasks.json
+++ b/apps/web/src/locales/pseudo/tasks.json
@@ -10,6 +10,10 @@
"columnWorkflow": "Ŵōŕķƒĺōŵ",
"delete": "Ďēĺēţē",
"deleteTask": "Ďēĺēţē ţàśķ",
+ "failedToArchiveTask": "Ƒàĩĺēď ţō àŕćĥĩvē ţàśķ",
+ "failedToDeleteTask": "Ƒàĩĺēď ţō ďēĺēţē ţàśķ",
+ "failedToLoadTasks": "Ƒàĩĺēď ţō ĺōàď ţàśķś",
+ "failedToUnarchiveTask": "Ƒàĩĺēď ţō ũńàŕćĥĩvē ţàśķ",
"goToFirstPage": "Ĝō ţō ƒĩŕśţ ƥàĝē",
"goToLastPage": "Ĝō ţō ĺàśţ ƥàĝē",
"goToNextPage": "Ĝō ţō ńēxţ ƥàĝē",
@@ -39,6 +43,12 @@
"sortTitleZA": "Ţĩţĺē Ź-À",
"sortUpdatedNewest": "Ũƥďàţēď ńēŵēśţ",
"sortUpdatedOldest": "Ũƥďàţēď ōĺďēśţ",
+ "taskArchived": "Ţàśķ àŕćĥĩvēď",
+ "taskArchivedDescription": "Ţĥē ţàśķ ĥàś ƀēēń àŕćĥĩvēď śũććēśśƒũĺĺŷ.",
+ "taskUnarchived": "Ţàśķ ũńàŕćĥĩvēď",
"unarchive": "Ũńàŕćĥĩvē",
+ "unarchivedBranchesMissing_one": "Ɓŕàńćĥ {{branches}} ńō ĺōńĝēŕ ēxĩśţś ĺōćàĺĺŷ ōŕ ōń ţĥē ŕēḿōţē — ţĥē ńēxţ śēśśĩōń śţàŕţś ƒŕēśĥ ƒŕōḿ ţĥē ƀàśē ƀŕàńćĥ.",
+ "unarchivedBranchesMissing_other": "Ɓŕàńćĥēś {{branches}} ńō ĺōńĝēŕ ēxĩśţ ĺōćàĺĺŷ ōŕ ōń ţĥē ŕēḿōţē — ţĥē ńēxţ śēśśĩōń śţàŕţś ƒŕēśĥ ƒŕōḿ ţĥē ƀàśē ƀŕàńćĥ.",
+ "unarchivedTaskRestored": "Ţĥē ţàśķ ĥàś ƀēēń ŕēśţōŕēď.",
"unarchiveTask": "Ũńàŕćĥĩvē ţàśķ"
}
diff --git a/apps/web/src/locales/pt-pt/agents.json b/apps/web/src/locales/pt-pt/agents.json
index e421e8ddf8..1c2d6aa3aa 100644
--- a/apps/web/src/locales/pt-pt/agents.json
+++ b/apps/web/src/locales/pt-pt/agents.json
@@ -99,6 +99,7 @@
"failedToFetchCapabilities": "Não foi possível obter as capacidades",
"modelOptionsAuthenticationRequired": "Inicie sessão neste agente para carregar as opções do modelo.",
"modelOptionsResolutionFailed": "Não foi possível carregar as opções do modelo.",
+ "modelSettings": "Definições do modelo",
"resolvingModelOptions": "A carregar as opções do modelo…",
"retryModelOptions": "Tentar novamente",
"failedToLoadCommandPreview": "Não foi possível carregar a pré-visualização do comando",
@@ -186,6 +187,7 @@
"searchModes": "Pesquisar modos...",
"selectAModel": "Selecione um modelo...",
"selectAModelShort": "Selecionar um modelo",
+ "selectModel": "Selecionar modelo...",
"sessionEnded": "A sessão terminou.",
"sessionEndedWithCode": "A sessão terminou (saída {{code}}).",
"sessionUnavailable": "A sessão já não está disponível.",
diff --git a/apps/web/src/locales/pt-pt/azuredevops.json b/apps/web/src/locales/pt-pt/azuredevops.json
index 786d32a25a..5c4f65f8d9 100644
--- a/apps/web/src/locales/pt-pt/azuredevops.json
+++ b/apps/web/src/locales/pt-pt/azuredevops.json
@@ -65,6 +65,9 @@
"enabled": "Ativada",
"enterValidOrganizationUrl": "Introduza um URL de organização válido para abrir as respetivas definições de token.",
"executorProfileId": "ID do perfil de executor",
+ "failedToLinkPullRequest": "Não foi possível associar o pull request do Azure DevOps.",
+ "failedToLinkWorkItem": "Não foi possível associar o work item do Azure DevOps.",
+ "failedToLinkWorkItemNoProject": "Não foi possível associar o work item do Azure DevOps: falta o projeto.",
"failedToLoadConfig": "Não foi possível carregar a configuração do Azure DevOps: {{error}}",
"failedToLoadQuickActions": "Não foi possível carregar as ações rápidas.",
"failedToSaveDefaultQueries": "Não foi possível guardar as consultas pré-definidas",
diff --git a/apps/web/src/locales/pt-pt/chat.json b/apps/web/src/locales/pt-pt/chat.json
index 73271d9d62..534a2251eb 100644
--- a/apps/web/src/locales/pt-pt/chat.json
+++ b/apps/web/src/locales/pt-pt/chat.json
@@ -43,6 +43,7 @@
"failedToDeleteQuickChat": "Não foi possível eliminar o chat rápido",
"failedToMergeQueuedMessages": "Não foi possível unir as mensagens em fila.",
"failedToRemoveQueuedMessage": "Não foi possível remover a mensagem em fila.",
+ "failedToReorderQueuedMessages": "Não foi possível reordenar as mensagens em fila.",
"failedToRunQueuedMessage": "Não foi possível executar a mensagem em fila.",
"failedToStartQuickChat": "Não foi possível iniciar o chat rápido",
"fileTreeActions": "Ações de ficheiro",
@@ -84,6 +85,7 @@
"renameChat": "Mudar o nome do chat",
"renameSavedOnThisDeviceOnly": "Nome guardado apenas neste dispositivo",
"renameSyncFailedDescription": "Não foi possível sincronizar o novo nome com os seus outros dispositivos.",
+ "reorderQueuedMessage": "Reordenar mensagem em fila",
"repositories": "Repositórios",
"repositoriesHelp": "Adicione contexto de repositório para se focar em código e branches específicos.",
"repositoryContextHelp": "O Kandev usa um worktree isolado a partir do branch selecionado. As alterações locais sem commit não são incluídas.",
@@ -95,6 +97,7 @@
"runNextQueuedMessage": "Executar a próxima mensagem em fila",
"selectAgent": "Selecionar agente",
"selectARepository": "Selecione um repositório...",
+ "sortable": "ordenável",
"startChat": "Iniciar chat",
"startChatAction": "Iniciar chat",
"starting": "A iniciar...",
diff --git a/apps/web/src/locales/pt-pt/common.json b/apps/web/src/locales/pt-pt/common.json
index 2cf5b56270..77c188ea90 100644
--- a/apps/web/src/locales/pt-pt/common.json
+++ b/apps/web/src/locales/pt-pt/common.json
@@ -4,6 +4,7 @@
"activeSubagents_other": "{{count}} subagentes em execução",
"activeTasks": "Tarefas ativas",
"aDirectLocalExecutorProfileIsRequired": "É necessário um perfil de executor local direto para criar e utilizar um repositório vazio.",
+ "agent": "Agente",
"agentDashboard": "painel do agente",
"agentRun": "execução do agente",
"agentRuns": "execuções do agente",
@@ -13,6 +14,9 @@
"andMoreFiles_one": "+{{count}} mais",
"andMoreFiles_other": "+{{count}} mais",
"anEnhancedPromptIsAvailable": "Está disponível um prompt melhorado.",
+ "anErrorOccurred": "Ocorreu um erro",
+ "anUnexpectedErrorOccurred": "Ocorreu um erro inesperado",
+ "anUnknownErrorOccurred": "Ocorreu um erro desconhecido",
"apply": "Aplicar",
"autoApproveAllPermissions": "Aprovar automaticamente todas as permissões",
"automations": "Automatizações",
@@ -36,9 +40,30 @@
"cliPassthrough": "Passagem direta para a CLI",
"close": "Fechar",
"closeEsc": "Fechar (Esc)",
+ "commandAddBrowserPanel": "Adicionar painel do navegador",
+ "commandAddBrowserPanelKeywords": "navegador, pré-visualização, web, abrir pré-visualização do navegador, pré-visualização web, pré-visualização da aplicação",
+ "commandAddChangesPanel": "Adicionar painel de alterações",
+ "commandAddChangesPanelKeywords": "alterações, diff, alterações git, ficheiros alterados, controlo de versões, git diff, rever alterações",
+ "commandAddPlanPanel": "Adicionar painel do plano",
+ "commandAddPlanPanelKeywords": "plano, documento, plano da tarefa, plano de implementação, detalhes do plano",
+ "commandAddTerminalPanel": "Adicionar painel do terminal",
+ "commandAddTerminalPanelKeywords": "terminal, shell, consola, novo terminal, abrir terminal, linha de comandos",
"commandAgentsSettingsKeywords": "agentes, definições de agentes, perfis de agente, agentes instalados, claude",
"commandArchiveTaskKeywords": "arquivar, arquivar tarefa, arquivar a tarefa atual, fechar tarefa, terminar tarefa, arrumar",
+ "commandCancelTurn": "Cancelar turno",
+ "commandCancelTurnKeywords": "cancelar, parar, turno, cancelar agente, parar agente, interromper agente",
+ "commandCommitChanges": "Submeter alterações",
+ "commandCommitChangesKeywords": "commit, git, guardar alterações, git commit, submeter",
"commandConfigChatKeywords": "chat de configuração, modo de configuração, configurar kandev, configuração de fluxo de trabalho, configuração mcp",
+ "commandCreateFile": "Criar ficheiro",
+ "commandCreateFileKeywords": "criar, novo, ficheiro, adicionar",
+ "commandCreateFilePlaceholder": "Caminho do ficheiro relativo à raiz do workspace...",
+ "commandCreateNewTask": "Criar nova tarefa",
+ "commandCreateNewTaskKeywords": "nova, criar, tarefa, adicionar",
+ "commandCreatePr": "Criar PR",
+ "commandCreatePrKeywords": "pull request, pr, abrir pull request, submeter pull request, git",
+ "commandCreateSubtask": "Criar subtarefa",
+ "commandCreateSubtaskKeywords": "subtarefa, criar, nova subtarefa, tarefa filha",
"commandExecutorsSettingsKeywords": "executores, perfis de executor, ambiente de execução, variáveis de ambiente, runtime, computação",
"commandGoToAllTasks": "Ir para Todas as tarefas",
"commandGoToAllTasksKeywords": "tarefas, lista, todas",
@@ -51,11 +76,21 @@
"commandGoToStats": "Ir para as Estatísticas",
"commandGoToStatsKeywords": "estatísticas, análises, métricas",
"commandGroupActions": "Ações",
+ "commandGroupAgent": "Agente",
"commandGroupCommands": "Comandos",
+ "commandGroupGit": "Git",
"commandGroupNavigation": "Navegação",
+ "commandGroupPanels": "Painéis",
"commandGroupPreferences": "Preferências",
"commandGroupSettings": "Definições",
"commandGroupTasks": "Tarefas",
+ "commandGroupView": "Vista",
+ "commandGroupWorkspace": "Workspace",
+ "commandMergeKeywords": "merge, integrar, git, ramo",
+ "commandNewAgent": "Novo agente",
+ "commandNewAgentKeywords": "novo, agente, sessão, iniciar agente, nova sessão",
+ "commandOpenRecentTaskSwitcher": "Abrir o seletor de tarefas recentes",
+ "commandOpenRecentTaskSwitcherKeywords": "recentes, tarefa, seletor, histórico, tarefas recentes, mudar de tarefa, alternar entre tarefas recentes",
"commandPaletteMode": "Modo do painel de comandos",
"commandPreviewAddTerminalPanel": "Adicionar painel de terminal",
"commandPreviewCommitChanges": "Fazer commit das alterações",
@@ -63,10 +98,19 @@
"commandPreviewPull": "Pull",
"commandPreviewSearchTasks": "Pesquisar tarefas",
"commandPromptsSettingsKeywords": "prompts, definições de prompts, prompts personalizados, excertos de prompt, modelos de prompt",
+ "commandPullKeywords": "pull, git, obter alterações, transferir alterações",
+ "commandPushKeywords": "push, git, enviar alterações, enviar para o remoto, carregar alterações",
"commandQuickChat": "Chat rápido",
"commandQuickChatKeywords": "chat rápido, novo chat rápido, pergunta rápida, perguntar ao agente",
+ "commandRebaseKeywords": "rebase, git, ramo",
"commandSwitchToDarkMode": "Mudar para o modo escuro",
+ "commandSwitchToKanbanView": "Mudar para a vista Kanban",
+ "commandSwitchToKanbanViewKeywords": "kanban, quadro, vista",
"commandSwitchToLightMode": "Mudar para o modo claro",
+ "commandSwitchToListView": "Mudar para a vista de lista",
+ "commandSwitchToListViewKeywords": "lista, tabela, vista",
+ "commandSwitchToPipelineView": "Mudar para a vista de pipeline",
+ "commandSwitchToPipelineViewKeywords": "pipeline, grafo, vista",
"commandThemeKeywords": "tema, tema de cores, aspeto",
"commandWorkspaceSettingsKeywords": "área de trabalho, áreas de trabalho",
"commitsAndPushesToTheCurrent": "Faz commit e push para o branch atual.",
@@ -126,6 +170,29 @@
"forwardStdinStdoutStraightToThe": "Encaminha o stdin/stdout diretamente para o subprocesso da CLI. Desativa o ACP.",
"getStarted": "Começar",
"githubCliNotAuthenticated": "A CLI do GitHub não está autenticada",
+ "gitOpAmendCommit": "Corrigir commit",
+ "gitOpCommit": "Commit",
+ "gitOpForcePush": "Push forçado",
+ "gitOpHardReset": "Reposição hard",
+ "gitOpMerge": "Merge",
+ "gitOpPull": "Pull",
+ "gitOpPush": "Push",
+ "gitOpRebase": "Rebase",
+ "gitOpRevertCommit": "Reverter commit",
+ "gitOpSoftReset": "Reposição soft",
+ "gitOperationCompleted": "{{operation}} concluído",
+ "gitOperationFailed": "{{operation}} falhou",
+ "gitOperationFailedInRepos_one": "Falhou em {{count}} repositório — {{summary}}",
+ "gitOperationFailedInRepos_other": "Falhou em {{count}} repositórios — {{summary}}",
+ "gitOperationPartialDescription_one": "{{operation}} teve êxito em {{succeeded}} de {{count}} repositório ({{repos}}); falhou em {{summary}}",
+ "gitOperationPartialDescription_other": "{{operation}} teve êxito em {{succeeded}} de {{count}} repositórios ({{repos}}); falhou em {{summary}}",
+ "gitOperationPartiallySucceeded": "{{operation}} concluído parcialmente",
+ "gitOperationRepoFailure": "{{repo}}: {{error}}",
+ "gitOperationRunning": "{{operation}}...",
+ "gitOperationScoped": "{{operation}} ({{repo}})",
+ "gitOperationSucceeded": "{{operation}} com êxito",
+ "gitOperationSucceededInRepos_one": "{{operation}} teve êxito em {{count}} repositório: {{repos}}",
+ "gitOperationSucceededInRepos_other": "{{operation}} teve êxito em {{count}} repositórios: {{repos}}",
"gitScopeSkipped": "{{scope}} foi ignorado porque uma operação num repositório filho falhou ({{failedScopes}}).",
"goToFirstPage": "Ir para a primeira página",
"goToLastPage": "Ir para a última página",
@@ -234,6 +301,7 @@
"probing": "A sondar",
"profileName": "Nome do perfil",
"prompts": "Prompts",
+ "reference": "Referência",
"refreshAriaLabel": "Atualizar {{label}}",
"refreshBranchesGitFetch": "Atualizar os branches (git fetch)",
"refreshingLabel": "A atualizar {{label}}...",
@@ -275,6 +343,7 @@
"scopeFiles": "Ficheiros",
"scopeTitleWithShortcut": "{{label}} ({{shortcut}})",
"scratchWorkspace": "área de trabalho temporária",
+ "script": "Script",
"searchEllipsis": "Pesquisar…",
"searchFailedEditTheQueryOr": "A pesquisa falhou. Edite a consulta ou reabra a pesquisa para tentar novamente.",
"searchForFiles": "Pesquisar ficheiros...",
@@ -302,9 +371,12 @@
"skip": "Ignorar",
"stagesAndCommitsChangesUsingConventional": "Prepara e faz commit das alterações usando Conventional Commits.",
"status": "Estado",
+ "step": "Passo",
"subtaskTitle": "Título da subtarefa",
"switchMode": "Mudar de modo",
"system": "Sistema",
+ "task": "Tarefa",
+ "terminal": "Terminal",
"todos": "Tarefas por fazer",
"taskLoading": "A carregar a tarefa...",
"tasks": "Tarefas",
@@ -333,19 +405,25 @@
"typeACommand": "Escreva um comando...",
"typeToSearchFiles": "Escreva para pesquisar ficheiros...",
"typeToSearchTaskContents": "Escreva para pesquisar o conteúdo da tarefa…",
+ "unavailable": "Indisponível",
+ "unknown": "Desconhecido",
+ "unknownError": "Erro desconhecido",
"unknownPanel": "Painel desconhecido: {{component}}",
"unsavedChanges": "Alterações por guardar",
+ "untitledTask": "Tarefa sem título",
"usefulCommandsAgentSkills": "Comandos úteis e competências do agente",
"useScratchInstead": "Usar antes uma área temporária",
"useThisFolder": "Usar esta pasta",
"viewFullReleaseOnGithub": "Ver a versão completa no GitHub",
"waitsForCiAndAutomatedReviews": "Aguarda pelo CI e pelas revisões automáticas e depois corrige as falhas e responde aos comentários.",
+ "walkthrough": "Percurso",
"whatSNew": "Novidades",
"whenItsDoneTheAgentOpensAPr": "Quando terminar, o agente abre um pull request para <2>{{repo}}2> para os responsáveis reverem, poupando-lhes tempo e levando a melhoria a toda a gente.",
"workflow": "Fluxo de trabalho",
"workflowsControlTheStepsAutomationAnd": "Os fluxos de trabalho controlam os passos, a automatização e o comportamento do agente nas suas tarefas. Pode adicionar mais fluxos de trabalho nas Definições.",
"workspace": "Área de trabalho",
"workspaces": "Áreas de trabalho",
+ "you": "Você",
"youCanUseTheNavigationOr": "Pode usar a navegação ou recarregar para tentar novamente.",
"yourAgentImplementsItInThe": "O seu agente implementa-a no repositório do kandev, com testes",
"youVerifyAndTestTheChange": "Verifica e testa a alteração numa segunda instância do kandev"
diff --git a/apps/web/src/locales/pt-pt/editors.json b/apps/web/src/locales/pt-pt/editors.json
index b7ba7e6748..41f1cee91e 100644
--- a/apps/web/src/locales/pt-pt/editors.json
+++ b/apps/web/src/locales/pt-pt/editors.json
@@ -19,10 +19,17 @@
"copyLine": "Copiar a linha {{lineNumber}}",
"copyPath": "Copiar o caminho",
"default": "pré-definido",
+ "deleteFailed": "Falha ao eliminar",
"deleteFile": "Eliminar ficheiro",
"disableWordWrap": "Desativar a quebra de linha",
"enableWordWrap": "Ativar a quebra de linha",
+ "errorWhileDeletingFile": "Ocorreu um erro ao eliminar o ficheiro",
+ "errorWhileSavingFile": "Ocorreu um erro ao guardar o ficheiro",
+ "failedToDeleteFile": "Não foi possível eliminar o ficheiro",
+ "failedToSaveFile": "Não foi possível guardar o ficheiro",
"failedToSendComment": "Não foi possível enviar o comentário",
+ "fileSavedOverwritten": "Ficheiro guardado (substituído)",
+ "fileSavedOverwrittenDescription": "O ficheiro foi modificado externamente. A sua versão foi guardada.",
"foldUnchangedLines": "Dobrar as linhas inalteradas",
"hideDiffIndicators": "Ocultar os indicadores de diff",
"highlight": "Destacar",
@@ -35,6 +42,7 @@
"noDiffAvailable": "Nenhum diff disponível",
"noEditorsConfigured": "Nenhum editor configurado",
"openFolder": "Abrir a pasta",
+ "openTaskSessionBeforeSending": "Abra uma sessão da tarefa antes de enviar para o agente.",
"openWith": "Abrir com...",
"pasteLink": "Colar a ligação...",
"pleaseTryAgain": "Tente novamente.",
@@ -43,6 +51,7 @@
"reload": "Recarregar",
"revertAllChanges": "Reverter todas as alterações",
"revertChanges": "Reverter alterações",
+ "saveFailed": "Falha ao guardar",
"saving": "A guardar...",
"sentToTheAgent": "Enviado ao agente.",
"showAllLines": "Mostrar todas as linhas",
diff --git a/apps/web/src/locales/pt-pt/github.json b/apps/web/src/locales/pt-pt/github.json
index b1fef304ca..a10e05a03d 100644
--- a/apps/web/src/locales/pt-pt/github.json
+++ b/apps/web/src/locales/pt-pt/github.json
@@ -180,6 +180,7 @@
"failedToDeleteReviewWatch": "Não foi possível eliminar a vigilância de revisões",
"failedToLoadGithubWorkspaceSettings": "Não foi possível carregar as definições do GitHub da área de trabalho",
"failedToMerge": "Não foi possível fazer merge",
+ "failedToReRequestReview": "Não foi possível voltar a pedir a revisão",
"failedToResetIssueWatch": "Não foi possível repor a vigilância de issues",
"failedToResetReviewWatch": "Não foi possível repor a vigilância de revisões",
"failedToSaveDefaultQueries": "Não foi possível guardar as consultas pré-definidas",
@@ -445,6 +446,7 @@
"reviewCount_other": "{{count}} revisões",
"reviewFollowUp": "Seguimento da revisão",
"reviewPermissions": "Rever as permissões",
+ "reviewReRequestedFrom": "Revisão pedida novamente a {{reviewer}}",
"reviewWatch": "vigilância de revisões",
"reviewWatchCreated": "Vigilância de revisões criada",
"reviewWatchDeleted": "Vigilância de revisões eliminada",
@@ -468,6 +470,7 @@
"selectWorkflow": "Selecionar fluxo de trabalho",
"selectWorkspace": "Selecionar área de trabalho",
"selectedRepositories": "Repositórios selecionados",
+ "selectWorkspaceBeforeRequestingReview": "Selecione um workspace antes de pedir a revisão.",
"setTheseExactUrlsCreateAClientSecret": "Defina exatamente estes URLs, crie um segredo de cliente e um segredo de webhook e transfira uma chave privada. Esta configuração única expira {{expiresAt}}.",
"showGithubApiLimits": "Mostrar os limites da API do GitHub",
"showLess": "Mostrar menos",
diff --git a/apps/web/src/locales/pt-pt/gitlab.json b/apps/web/src/locales/pt-pt/gitlab.json
index 209d498292..b6592288bc 100644
--- a/apps/web/src/locales/pt-pt/gitlab.json
+++ b/apps/web/src/locales/pt-pt/gitlab.json
@@ -316,7 +316,6 @@
"mrAutomationExplainAutoFixRounds": "Explicar as rondas de correção automática",
"mrAutomationAutoFixPromptSaveFailed": "Não foi possível guardar o prompt de correção automática.",
"mrAutomationAutoFixPromptResetFailed": "Não foi possível repor o prompt de correção automática.",
- "mrPopoverOpenDetail": "Abrir os detalhes do merge request",
"mrPopoverOpenInGitlab": "Abrir !{{mriid}} no GitLab",
"mrPopoverUnlink": "Desassociar !{{mriid}}",
"mrPopoverPassRate": "Taxa de sucesso",
diff --git a/apps/web/src/locales/pt-pt/kanban.json b/apps/web/src/locales/pt-pt/kanban.json
index ffac4e2bbb..94efbf2ec6 100644
--- a/apps/web/src/locales/pt-pt/kanban.json
+++ b/apps/web/src/locales/pt-pt/kanban.json
@@ -4,6 +4,7 @@
"allRepositories": "Todos os repositórios",
"allWorkflows": "Todos os fluxos de trabalho",
"allWorkflowsTitleCase": "Todos os fluxos de trabalho",
+ "allWorkspaces": "Todos os workspaces",
"approvalRequired": "Aprovação necessária",
"archive": "Arquivar",
"archiveCount_one": "Arquivar {{count}}",
diff --git a/apps/web/src/locales/pt-pt/office.json b/apps/web/src/locales/pt-pt/office.json
index f2bf249b34..d4097064a0 100644
--- a/apps/web/src/locales/pt-pt/office.json
+++ b/apps/web/src/locales/pt-pt/office.json
@@ -101,10 +101,15 @@
"botTokenChatId": "{\"bot_token\": \"...\", \"chat_id\": \"...\"}",
"branch": "Branch",
"browseSkillsSh": "Explorar skills.sh",
+ "budgetActionBlockNewTasks": "bloquear novas tarefas",
+ "budgetActionNotifyOnly": "apenas notificar",
+ "budgetActionPauseAgent": "pausar o agente",
"budgetAlert": "Alerta de orçamento",
"budgetAmount": "Orçamento: {{amount}}",
"budgetCapConcurrencyAndExecutionEnvironment": "Limite de orçamento, concorrência e ambiente de execução.",
"budgetLimitsConcurrencyAndExecutionEnvironment": "Limites de orçamento, concorrência e ambiente de execução.",
+ "budgetPeriodMonthly": "mensal",
+ "budgetPeriodTotal": "total",
"budgetPolicies": "Políticas de orçamento",
"budgetPolicyCreated": "Política de orçamento criada",
"budgetPolicyDeleted": "Política de orçamento eliminada",
diff --git a/apps/web/src/locales/pt-pt/settings.json b/apps/web/src/locales/pt-pt/settings.json
index 1f32b2b88d..911c882e1a 100644
--- a/apps/web/src/locales/pt-pt/settings.json
+++ b/apps/web/src/locales/pt-pt/settings.json
@@ -288,6 +288,8 @@
"externalMcpToolsSummary": "{{tools}} em {{categories}} — expanda para pré-visualizar o que os agentes externos podem fazer.",
"externalProviders": "Fornecedores externos",
"externalProvidersDescription": "Configure fornecedores externos para as notificações remotas.",
+ "fallbackModelUnavailable": "O modelo alternativo já não está disponível — selecione outro",
+ "fallbackNoteLive": "Esta sessão começou com um modelo alternativo porque o modelo configurado não estava disponível. O marcador (alternativo) reflete a sessão em curso e deixa de ser apresentado depois de recarregar.",
"fallbackSettings": "Definições de fallback",
"fallbackSettingsSummaryAutomatic": "Fallback automático ativado",
"fallbackSettingsSummaryExplicit": "Fallback explícito: {{model}}",
@@ -345,10 +347,17 @@
"layoutEditorForProfile": "Editor de {{name}}",
"layoutProfile": "perfil de esquema",
"layoutProfileCopyName": "Cópia de {{name}}",
+ "layoutProfileDefault": "Predefinido",
+ "layoutProfileDefaultDescription": "Agente com Ficheiros, Alterações e Terminal",
"layoutProfileName": "Nome do perfil de esquema",
"layoutProfileNamesMustNotBe": "Os nomes dos perfis de esquema não podem estar vazios",
+ "layoutProfilePlanMode": "Modo de plano",
+ "layoutProfilePlanModeDescription": "Agente e Plano lado a lado",
+ "layoutProfilePreviewMode": "Modo de pré-visualização",
+ "layoutProfilePreviewModeDescription": "Agente e Navegador lado a lado",
"layoutProfiles": "Perfis de esquema",
"layoutProfilesWereNotSaved": "Os perfis de esquema não foram guardados",
+ "layoutProfileVsCodeDescription": "Agente e VS Code lado a lado",
"layouts": "Esquemas",
"ligatures": "Ligaduras",
"light": "Claro",
@@ -371,7 +380,14 @@
"makesAgentctlValuesAvailableToPlugins": "Disponibiliza os valores do agentctl a plugins e outros consumidores. A barra de estado incorporada continua limitada ao anfitrião.",
"manageApiKeysAndCredentialsSecrets": "Faça a gestão das chaves de API e das credenciais. Os segredos são guardados encriptados e injetados nos ambientes dos agentes através das variáveis de ambiente do perfil de executor.",
"globalScope": "Global",
+ "modelFallbackSuffix": "(alternativo)",
+ "mode": "Modo",
"modelWithValue": "Modelo: {{model}}",
+ "profileFallbackWillBeUsed": "O modelo inicial {{model}} já não está disponível — será usado o alternativo {{fallback}}.",
+ "profileStartModelUnavailable": "O modelo inicial {{model}} já não está disponível — altere-o no perfil do agente.",
+ "selectAModel": "Selecionar um modelo...",
+ "startModelUnavailable": "Já não está disponível — selecione outro modelo",
+ "startModelAria": "Definições do modelo inicial do perfil",
"temperatureWithValue": "Temperatura: {{temperature}}",
"workspaceScope": "Área de trabalho",
"workspaceSecretsDescription": "Faça a gestão dos segredos disponíveis para os repositórios desta área de trabalho. Os perfis de agente e de executor partilhados só podem usar segredos Globais.",
diff --git a/apps/web/src/locales/pt-pt/sidebar.json b/apps/web/src/locales/pt-pt/sidebar.json
index 9d119559ae..34ac2afc08 100644
--- a/apps/web/src/locales/pt-pt/sidebar.json
+++ b/apps/web/src/locales/pt-pt/sidebar.json
@@ -101,6 +101,7 @@
"toggleTheme": "Alternar o tema",
"unsavedFilterChanges": "Alterações de filtro por guardar",
"viewAll": "Todas",
+ "viewAllTasks": "Todas as tarefas",
"viewLabel": "Vista: {{name}}",
"whatsNew": "Novidades",
"work": "Trabalho",
diff --git a/apps/web/src/locales/pt-pt/task.json b/apps/web/src/locales/pt-pt/task.json
index 3a850a3eb8..87778eeb77 100644
--- a/apps/web/src/locales/pt-pt/task.json
+++ b/apps/web/src/locales/pt-pt/task.json
@@ -1,5 +1,8 @@
{
+ "agentCliNotInstalled": "CLI do agente não instalado",
+ "agentProbeFailed": "A sondagem do agente falhou",
"aPullRequestAlreadyExistsFor": "Já existe um pull request para esta tarefa",
+ "archivedTask": "Tarefa arquivada",
"aSharedDocumentForYouAnd": "Um documento partilhado onde você e o agente colaboram na abordagem",
"aboutContextWindowSource": "Sobre a origem da janela de contexto",
"activeBadge": "ATIVA",
@@ -269,6 +272,7 @@
"couldNotAddSources": "Não foi possível adicionar as fontes. As suas entradas continuam aqui para tentar novamente.",
"couldNotLoadRepositories": "Não foi possível carregar os repositórios: {{message}}",
"couldNotResolveRemoteRepository": "Não foi possível resolver o repositório remoto: {{message}}",
+ "couldNotSaveStatusBarOrder": "Não foi possível guardar a ordem da barra de estado",
"couldnTStartASession": "Não foi possível iniciar uma sessão",
"create": "Criar",
"createANewBranch": "Criar um novo branch",
@@ -290,6 +294,7 @@
"creatingEllipsis": "A criar...",
"creatingFrom": "A criar o {{shortName}} a partir de <4>{{displayBranch}}4>",
"creatingFromTo": "A criar o {{shortName}} a partir de <4>{{displayBranch}}4> para <7>{{baseBranch}}7>",
+ "credentialsAvailableInExecutor": "Credenciais disponíveis no executor selecionado",
"ctrl": "Ctrl",
"current": "atual",
"current2": "Atual",
@@ -401,14 +406,19 @@
"environmentPrepared": "Ambiente preparado",
"environmentPreparedOnFreshSandbox": "Ambiente preparado numa sandbox nova",
"environmentPreparedWithWarnings": "Ambiente preparado com avisos",
+ "environmentReset": "Ambiente reposto",
+ "environmentResetFailed": "A reposição falhou: {{error}}",
"environmentSetupFailed": "A configuração do ambiente falhou",
"environmentSetupFinishedWithErrors": "A configuração do ambiente terminou com erros",
+ "environmentStateCurrent": "Estado atual: {{state}}",
"error": "Erro",
"errorData": "Dados do erro",
"eventCount_one": "{{count}} evento",
"eventCount_other": "{{count}} eventos",
"executor2": "Executor",
"executorEnvironmentIsUnavailable": "O ambiente do executor não está disponível.",
+ "executorEnvironmentRunning": "Ambiente do executor em execução",
+ "executorEnvironmentStopped": "Ambiente do executor parado",
"executorHintDocker": "É criado um contentor Docker a partir do branch de base selecionado e é feito checkout de um branch da tarefa.",
"executorHintLocal": "O agente é executado diretamente no repositório.",
"executorHintWorktreeMulti": "É criada uma worktree do Git para cada repositório dentro de uma pasta-mãe. O agente é executado nessa pasta-mãe, para poder ver todas as worktrees lado a lado.",
@@ -439,6 +449,8 @@
"failedToChangeModel": "Não foi possível alterar o modelo",
"failedToDeleteDocument": "Não foi possível eliminar o documento",
"failedToDeleteLayout": "Não foi possível eliminar o esquema",
+ "failedToDiscardChanges": "Não foi possível descartar as alterações",
+ "failedToGenerateContent": "Não foi possível gerar o conteúdo",
"failedToLinkGithubIssue": "Não foi possível associar a issue do GitHub.",
"failedToLinkGithubPullRequest": "Não foi possível associar o pull request do GitHub.",
"failedToLinkTarget": "Não foi possível associar {{target}}.",
@@ -454,13 +466,16 @@
"failedToRenameItem": "Não foi possível mudar o nome do item",
"failedToRenameTask": "Não foi possível mudar o nome da tarefa",
"failedToRestorePlan": "Não foi possível restaurar o plano",
+ "failedToResumeSession": "Não foi possível retomar a sessão",
"failedToRevokeShare": "Não foi possível revogar a partilha.",
"failedToSaveDocument": "Não foi possível guardar o documento",
"failedToSaveLayout": "Não foi possível guardar o esquema",
"failedToSendComment": "Não foi possível enviar o comentário",
"failedToSendComments": "Não foi possível enviar os comentários",
"failedToSendMessage": "Não foi possível enviar a mensagem",
+ "failedToStartTunnel": "Não foi possível iniciar o túnel: {{error}}",
"failedToStartVsCode": "Não foi possível iniciar o VS Code",
+ "failedToStopTunnel": "Não foi possível parar o túnel: {{error}}",
"failedToUnarchiveTask": "Não foi possível desarquivar a tarefa",
"failedToUnlinkGithubIssue": "Não foi possível desassociar a issue do GitHub.",
"failedToUpload": "Não foi possível carregar {{name}}",
@@ -519,6 +534,7 @@
"fullImpactDetails": "Detalhes completos do impacto",
"fullSize": "Tamanho real {{alt}}",
"generatingSummary": "A gerar o resumo...",
+ "generationFailed": "A geração falhou",
"gitActions": "Ações do Git",
"gitPollMode": "Sondagem do Git: {{mode}} ({{label}})",
"githubIssue": "Issue do GitHub",
@@ -599,6 +615,7 @@
"kandevListWorkspaces": "Kandev: Listar áreas de trabalho",
"kandevMessageTask": "Kandev: Enviar mensagem à tarefa",
"kandevMoveTask": "Kandev: Mover tarefa",
+ "kandevUpdateAvailable": "Atualização do Kandev disponível",
"kandevUpdateTask": "Kandev: Atualizar tarefa",
"kandevUpdateTaskPlan": "Kandev: Atualizar plano da tarefa",
"kandevWriteTaskDocument": "Kandev: Escrever documento da tarefa",
@@ -703,6 +720,9 @@
"moreNotShown_one": "+ mais {{count}} não mostrado",
"moreNotShown_other": "+ mais {{count}} não mostrados",
"moreToolbarActions": "Mais ações da barra de ferramentas",
+ "moveFailed": "Falha ao mover",
+ "moveFailedFiles": "Não foi possível mover alguns ficheiros",
+ "moveFailedUnexpected": "Ocorreu um erro ao mover os ficheiros",
"moveHeadToThisCommitKeep": "Move a HEAD para este commit. Mantém todas as alterações preparadas.",
"moveHere": "Mover para aqui",
"moveTaskToTheNextWorkflow": "Mover a tarefa para o passo seguinte do fluxo de trabalho",
@@ -733,6 +753,7 @@
"nextMessage": "Mensagem seguinte",
"nextQuestion": "Pergunta seguinte",
"noActiveSession": "Nenhuma sessão ativa",
+ "noActiveSessionDescription": "Inicie uma sessão para poder usar a geração com IA",
"noActiveTask": "Nenhuma tarefa ativa",
"noActivityYet": "Ainda não há atividade",
"noAgentFound": "Nenhum agente encontrado.",
@@ -796,6 +817,7 @@
"noSavedLayouts": "Nenhum esquema guardado",
"noSessionsYetLaunchOneTo": "Ainda não há sessões. Lance uma para começar.",
"noSteps": "Nenhum passo",
+ "noStepsConfigured": "Nenhum passo configurado",
"noStepsFound": "Nenhum passo encontrado.",
"noSubIssues": "Sem subissues",
"noTaskSelected": "Nenhuma tarefa selecionada",
@@ -836,9 +858,20 @@
"openWorkspaceSettings": "Abrir as definições da área de trabalho",
"option": "opção {{index}}",
"orTypeACustomAnswer": "Ou escreva uma resposta personalizada…",
+ "otherChanges": "Outras alterações",
"output": "Saída",
"outputTokens": "Tokens de saída",
"outputTruncated": "Saída truncada",
+ "panelAgent": "Agente",
+ "panelChanges": "Alterações",
+ "panelDiffViewer": "Visualizador de diferenças",
+ "panelFileDiff": "Diff [{{file}}]",
+ "panelFiles": "Ficheiros",
+ "panelMergeRequest": "Merge request",
+ "panelPlan": "Plano",
+ "panelPrDetails": "Detalhes do PR",
+ "panelPullRequest": "Pull request",
+ "panelTerminal": "Terminal",
"parent": "Tarefa-mãe",
"parkedCount": "Estacionados ({{parked}})",
"parkedTerminals": "Terminais estacionados",
@@ -850,6 +883,7 @@
"pendingReviewCommentsHelp_one": "{{count}} comentário de revisão pendente. Clique para expandir a lista — pode editar cada comentário, clicar no caminho do ficheiro para saltar para a origem ou remover um comentário com o ícone do caixote do lixo.",
"pendingReviewCommentsHelp_other": "{{count}} comentários de revisão pendentes. Clique para expandir a lista — pode editar cada comentário, clicar no caminho do ficheiro para saltar para a origem ou remover um comentário com o ícone do caixote do lixo.",
"permissionRequested": "Permissão pedida",
+ "permissionRequired": "Permissão necessária",
"pickADifferentRepoLocalFolder": "Escolha outro repositório, pasta local ou URL remoto",
"pickAStartingFolderOptional": "escolha uma pasta inicial (opcional)",
"pickOrPasteARepo": "Escolha ou cole um repositório",
@@ -1219,6 +1253,7 @@
"statusInProgress": "Em curso",
"statusInReview": "Em revisão",
"statusTodo": "Por fazer",
+ "statusUpdate": "Atualização de estado",
"stepCount_one": "{{count}} passo",
"stepCount_other": "{{count}} passos",
"stepFieldLabel": "passo",
@@ -1233,6 +1268,7 @@
"stopVoiceInput": "Parar a entrada de voz",
"stoppedWithAnError": "parou com um erro",
"stopping": "A parar…",
+ "subagent": "Subagente",
"subIssues": "Subissues",
"subTasks": "Subtarefas",
"submit": "Submeter",
@@ -1244,6 +1280,7 @@
"subtasksStayActiveUnlessYouTick": "As subtarefas mantêm-se ativas se não assinalar esta opção. Podem ainda estar em curso.",
"successful": "{{operationName}} bem-sucedida",
"summarize": "Resumir",
+ "summarizeReturnedNoResult": "O utilitário de resumo não devolveu qualquer resultado",
"summarizeSession": "Resumir a sessão",
"summarizeSessionNamed": "Resumir {{label}}",
"summarizing": "A resumir...",
@@ -1283,6 +1320,7 @@
"terminalNotAvailableThisTaskIs": "Terminal indisponível — esta tarefa está arquivada",
"terminalNumbered": "terminal n.º {{seq}}",
"terminalPromptInputAreDisabledUntil": "O terminal e a entrada de prompt estão desativados até começar a próxima execução.",
+ "terminalSessionEnded": "Esta sessão terminou — não há terminal disponível para ela.",
"terminalUnavailableNoActiveSession": "Terminal indisponível — não há sessão ativa.",
"terminalWithSeq": "Terminal {{seq}}",
"terminals": "Terminais",
@@ -1336,6 +1374,7 @@
"tokenCount_other": "{{formatted}} tokens",
"tokens": "tokens",
"tool": "ferramenta",
+ "toolCall": "Chamada de ferramenta",
"toolCallCount_one": "{{count}} chamada de ferramenta",
"toolCallCount_other": "{{count}} chamadas de ferramentas",
"toolCount_one": "{{count}} ferramenta",
@@ -1352,6 +1391,8 @@
"truncatedParen": "(truncado)",
"tryAgain": "Tente novamente",
"tunnel": "Túnel",
+ "tunnelStartedOnPort": "Túnel iniciado na porta {{port}}",
+ "tunnelStopped": "Túnel parado",
"turnForDuration": "durante {{duration}}",
"turnMessageCount_one": "· {{count}} mensagem",
"turnMessageCount_other": "· {{count}} mensagens",
diff --git a/apps/web/src/locales/pt-pt/tasks.json b/apps/web/src/locales/pt-pt/tasks.json
index cdb7293b80..529108ec88 100644
--- a/apps/web/src/locales/pt-pt/tasks.json
+++ b/apps/web/src/locales/pt-pt/tasks.json
@@ -10,6 +10,10 @@
"columnWorkflow": "Fluxo de trabalho",
"delete": "Eliminar",
"deleteTask": "Eliminar tarefa",
+ "failedToArchiveTask": "Não foi possível arquivar a tarefa",
+ "failedToDeleteTask": "Não foi possível eliminar a tarefa",
+ "failedToLoadTasks": "Não foi possível carregar as tarefas",
+ "failedToUnarchiveTask": "Não foi possível desarquivar a tarefa",
"goToFirstPage": "Ir para a primeira página",
"goToLastPage": "Ir para a última página",
"goToNextPage": "Ir para a página seguinte",
@@ -39,6 +43,12 @@
"sortTitleZA": "Título Z-A",
"sortUpdatedNewest": "Atualizadas mais recentes",
"sortUpdatedOldest": "Atualizadas mais antigas",
+ "taskArchived": "Tarefa arquivada",
+ "taskArchivedDescription": "A tarefa foi arquivada com êxito.",
+ "taskUnarchived": "Tarefa desarquivada",
"unarchive": "Desarquivar",
+ "unarchivedBranchesMissing_one": "O ramo {{branches}} já não existe localmente nem no remoto — a próxima sessão começa de novo a partir do ramo base.",
+ "unarchivedBranchesMissing_other": "Os ramos {{branches}} já não existem localmente nem no remoto — a próxima sessão começa de novo a partir do ramo base.",
+ "unarchivedTaskRestored": "A tarefa foi restaurada.",
"unarchiveTask": "Desarquivar tarefa"
}
diff --git a/apps/web/src/locales/pt-pt/workflows.json b/apps/web/src/locales/pt-pt/workflows.json
index d51078da07..a9d34a3a85 100644
--- a/apps/web/src/locales/pt-pt/workflows.json
+++ b/apps/web/src/locales/pt-pt/workflows.json
@@ -184,6 +184,8 @@
"startStepHelp": "As novas tarefas começam neste passo. Só um passo por fluxo de trabalho pode ser o passo inicial.",
"stepIsPartOfReplayCycle": "{{stepName}} faz parte de um ciclo de repetição",
"stepNamePlaceholder": "Nome do passo",
+ "workflowDescription": "Descrição",
+ "workflowDescriptionPlaceholder": "Descrição opcional",
"workflowPrompt": "Prompt do fluxo de trabalho",
"workflowPromptHelp": "Instruções opcionais incluídas na entrada de cada passo, antes do prompt do passo. Suporta referências {task_id} e @prompt-guardado (a mesma expansão dos prompts de passo). {{taskPrompt}} só existe nos passos. Deixe vazio para desativar. Visível para o agente e mostrado recolhido no chat.",
"workflowPromptPlaceholder": "Se o PR estiver integrado ou fechado, mova a tarefa para Concluída.\nSe precisar de criar vários pull requests, prefira orquestrar subtarefas.",
diff --git a/apps/web/src/locales/zh-cn/agents.json b/apps/web/src/locales/zh-cn/agents.json
index 2c257da46e..de3302f683 100644
--- a/apps/web/src/locales/zh-cn/agents.json
+++ b/apps/web/src/locales/zh-cn/agents.json
@@ -99,6 +99,7 @@
"failedToFetchCapabilities": "获取能力失败",
"modelOptionsAuthenticationRequired": "请登录此代理以加载模型选项。",
"modelOptionsResolutionFailed": "无法加载模型选项。",
+ "modelSettings": "模型设置",
"resolvingModelOptions": "正在加载模型选项…",
"retryModelOptions": "重试",
"failedToLoadCommandPreview": "加载命令预览失败",
@@ -186,6 +187,7 @@
"searchModes": "搜索模式…",
"selectAModel": "选择模型…",
"selectAModelShort": "选择模型",
+ "selectModel": "选择模型...",
"sessionEnded": "会话已结束。",
"sessionEndedWithCode": "会话已结束(退出码 {{code}})。",
"sessionUnavailable": "会话已不再可用。",
diff --git a/apps/web/src/locales/zh-cn/azuredevops.json b/apps/web/src/locales/zh-cn/azuredevops.json
index 7540de16db..ea4ba575f5 100644
--- a/apps/web/src/locales/zh-cn/azuredevops.json
+++ b/apps/web/src/locales/zh-cn/azuredevops.json
@@ -65,6 +65,9 @@
"enabled": "已启用",
"enterValidOrganizationUrl": "请输入有效的组织 URL,以打开其令牌设置。",
"executorProfileId": "执行器配置 ID",
+ "failedToLinkPullRequest": "无法关联 Azure DevOps 拉取请求。",
+ "failedToLinkWorkItem": "无法关联 Azure DevOps 工作项。",
+ "failedToLinkWorkItemNoProject": "无法关联 Azure DevOps 工作项:缺少项目。",
"failedToLoadConfig": "加载 Azure DevOps 配置失败:{{error}}",
"failedToLoadQuickActions": "加载快捷操作失败。",
"failedToSaveDefaultQueries": "保存默认查询失败",
diff --git a/apps/web/src/locales/zh-cn/chat.json b/apps/web/src/locales/zh-cn/chat.json
index 5001f21ded..3a1144ba5e 100644
--- a/apps/web/src/locales/zh-cn/chat.json
+++ b/apps/web/src/locales/zh-cn/chat.json
@@ -43,6 +43,7 @@
"failedToDeleteQuickChat": "删除快速聊天失败",
"failedToMergeQueuedMessages": "合并排队中的消息失败。",
"failedToRemoveQueuedMessage": "移除排队中的消息失败。",
+ "failedToReorderQueuedMessages": "无法重新排序队列中的消息。",
"failedToRunQueuedMessage": "运行排队中的消息失败。",
"failedToStartQuickChat": "启动快速聊天失败",
"fileTreeActions": "文件操作",
@@ -52,6 +53,8 @@
"mergeWithAbove": "与上方消息合并",
"noTodosYet": "暂无待办。",
"removeQueuedMessage": "移除排队中的消息",
+ "reorderQueuedMessage": "重新排序队列消息",
+ "sortable": "可排序",
"technicalDetails": "技术详情",
"providerQuotaTitle": "{{provider}} 使用额度已达上限",
"newChatTab": "新建聊天",
diff --git a/apps/web/src/locales/zh-cn/common.json b/apps/web/src/locales/zh-cn/common.json
index af47ee8795..9b315f5f12 100644
--- a/apps/web/src/locales/zh-cn/common.json
+++ b/apps/web/src/locales/zh-cn/common.json
@@ -4,6 +4,7 @@
"activeSubagents_other": "{{count}} 个子代理运行中",
"activeTasks": "进行中的任务",
"aDirectLocalExecutorProfileIsRequired": "创建并使用空仓库需要一个直接本地执行器配置。",
+ "agent": "代理",
"agentDashboard": "代理仪表盘",
"agentRun": "代理运行",
"agentRuns": "代理运行记录",
@@ -13,6 +14,9 @@
"andMoreFiles_one": "+{{count}} 个",
"andMoreFiles_other": "+{{count}} 个",
"anEnhancedPromptIsAvailable": "可用增强提示词。",
+ "anErrorOccurred": "发生错误",
+ "anUnexpectedErrorOccurred": "发生意外错误",
+ "anUnknownErrorOccurred": "发生未知错误",
"apply": "应用",
"autoApproveAllPermissions": "自动批准所有权限",
"automations": "自动化",
@@ -36,9 +40,30 @@
"cliPassthrough": "CLI 透传",
"close": "关闭",
"closeEsc": "关闭(Esc)",
+ "commandAddBrowserPanel": "添加浏览器面板",
+ "commandAddBrowserPanelKeywords": "浏览器, 预览, 网页, 打开浏览器预览, 网页预览, 应用预览, browser",
+ "commandAddChangesPanel": "添加更改面板",
+ "commandAddChangesPanelKeywords": "更改, 差异, git 更改, 已更改文件, 版本控制, git diff, 审查更改, changes",
+ "commandAddPlanPanel": "添加计划面板",
+ "commandAddPlanPanelKeywords": "计划, 文档, 任务计划, 实现计划, 计划详情, plan",
+ "commandAddTerminalPanel": "添加终端面板",
+ "commandAddTerminalPanelKeywords": "终端, shell, 控制台, 新终端, 打开终端, 命令行, terminal",
"commandAgentsSettingsKeywords": "代理, 代理设置, 代理配置, 已安装代理, claude",
"commandArchiveTaskKeywords": "归档, 归档任务, 归档当前任务, 关闭任务, 完成任务, 收起",
+ "commandCancelTurn": "取消本轮",
+ "commandCancelTurnKeywords": "取消, 停止, 轮次, 取消代理, 停止代理, 中断代理, cancel",
+ "commandCommitChanges": "提交更改",
+ "commandCommitChangesKeywords": "提交, git, 保存更改, git commit, commit",
"commandConfigChatKeywords": "配置聊天, 配置模式, 配置 kandev, 工作流配置, mcp 配置",
+ "commandCreateFile": "创建文件",
+ "commandCreateFileKeywords": "创建, 新建, 文件, 添加, file",
+ "commandCreateFilePlaceholder": "相对于工作区根目录的文件路径...",
+ "commandCreateNewTask": "创建新任务",
+ "commandCreateNewTaskKeywords": "新建, 创建, 任务, 添加, task",
+ "commandCreatePr": "创建 PR",
+ "commandCreatePrKeywords": "拉取请求, pr, 打开拉取请求, 提交拉取请求, git",
+ "commandCreateSubtask": "创建子任务",
+ "commandCreateSubtaskKeywords": "子任务, 创建, 新建子任务, 子级任务, subtask",
"commandExecutorsSettingsKeywords": "执行器, 执行器配置, 执行环境, 环境变量, 运行时, 计算",
"commandGoToAllTasks": "前往全部任务",
"commandGoToAllTasksKeywords": "任务, 列表, 全部",
@@ -51,11 +76,21 @@
"commandGoToStats": "前往统计",
"commandGoToStatsKeywords": "统计, 数据分析, 分析, 指标",
"commandGroupActions": "操作",
+ "commandGroupAgent": "代理",
"commandGroupCommands": "命令",
+ "commandGroupGit": "Git",
"commandGroupNavigation": "导航",
+ "commandGroupPanels": "面板",
"commandGroupPreferences": "偏好",
"commandGroupSettings": "设置",
"commandGroupTasks": "任务",
+ "commandGroupView": "视图",
+ "commandGroupWorkspace": "工作区",
+ "commandMergeKeywords": "合并, git, 分支, merge",
+ "commandNewAgent": "新建代理",
+ "commandNewAgentKeywords": "新建, 代理, 会话, 启动代理, 新建会话, agent",
+ "commandOpenRecentTaskSwitcher": "打开最近任务切换器",
+ "commandOpenRecentTaskSwitcherKeywords": "最近, 任务, 切换器, 历史, 最近任务, 切换任务, 切换最近任务, recent",
"commandPaletteMode": "命令面板模式",
"commandPreviewAddTerminalPanel": "添加终端面板",
"commandPreviewCommitChanges": "提交更改",
@@ -63,10 +98,19 @@
"commandPreviewPull": "拉取",
"commandPreviewSearchTasks": "搜索任务",
"commandPromptsSettingsKeywords": "提示词, 提示词设置, 自定义提示词, 提示词片段, 提示词模板",
+ "commandPullKeywords": "拉取, git, 拉取更改, 下载更改, pull",
+ "commandPushKeywords": "推送, git, 推送更改, 推送到远程, 上传更改, push",
"commandQuickChat": "快速聊天",
"commandQuickChatKeywords": "快速聊天, 新建快速聊天, 快速提问, 询问代理",
+ "commandRebaseKeywords": "变基, git, 分支, rebase",
"commandSwitchToDarkMode": "切换到深色模式",
+ "commandSwitchToKanbanView": "切换到看板视图",
+ "commandSwitchToKanbanViewKeywords": "看板, 面板, 视图, kanban",
"commandSwitchToLightMode": "切换到浅色模式",
+ "commandSwitchToListView": "切换到列表视图",
+ "commandSwitchToListViewKeywords": "列表, 表格, 视图, list",
+ "commandSwitchToPipelineView": "切换到流水线视图",
+ "commandSwitchToPipelineViewKeywords": "流水线, 图, 视图, pipeline",
"commandThemeKeywords": "主题, 颜色主题, 外观",
"commandWorkspaceSettingsKeywords": "工作区, 工作区",
"commitsAndPushesToTheCurrent": "提交并推送到当前分支。",
@@ -126,6 +170,29 @@
"forwardStdinStdoutStraightToThe": "将 stdin/stdout 直接转发到 CLI 子进程。会禁用 ACP。",
"getStarted": "开始使用",
"githubCliNotAuthenticated": "GitHub CLI 未通过身份验证",
+ "gitOpAmendCommit": "修补提交",
+ "gitOpCommit": "提交",
+ "gitOpForcePush": "强制推送",
+ "gitOpHardReset": "硬重置",
+ "gitOpMerge": "合并",
+ "gitOpPull": "拉取",
+ "gitOpPush": "推送",
+ "gitOpRebase": "变基",
+ "gitOpRevertCommit": "还原提交",
+ "gitOpSoftReset": "软重置",
+ "gitOperationCompleted": "{{operation}}已完成",
+ "gitOperationFailed": "{{operation}}失败",
+ "gitOperationFailedInRepos_one": "在 {{count}} 个仓库中失败 — {{summary}}",
+ "gitOperationFailedInRepos_other": "在 {{count}} 个仓库中失败 — {{summary}}",
+ "gitOperationPartialDescription_one": "{{operation}}在 {{count}} 个仓库中的 {{succeeded}} 个成功({{repos}});失败:{{summary}}",
+ "gitOperationPartialDescription_other": "{{operation}}在 {{count}} 个仓库中的 {{succeeded}} 个成功({{repos}});失败:{{summary}}",
+ "gitOperationPartiallySucceeded": "{{operation}}部分成功",
+ "gitOperationRepoFailure": "{{repo}}:{{error}}",
+ "gitOperationRunning": "{{operation}}...",
+ "gitOperationScoped": "{{operation}}({{repo}})",
+ "gitOperationSucceeded": "{{operation}}成功",
+ "gitOperationSucceededInRepos_one": "{{operation}}在 {{count}} 个仓库中成功:{{repos}}",
+ "gitOperationSucceededInRepos_other": "{{operation}}在 {{count}} 个仓库中成功:{{repos}}",
"gitScopeSkipped": "已跳过 {{scope}},因为子仓库操作失败({{failedScopes}})。",
"goToFirstPage": "转到第一页",
"goToLastPage": "转到最后一页",
@@ -183,6 +250,12 @@
"noFilesFound": "未找到文件。",
"noFoldersHerePickThisOne": "此处没有文件夹 — 选择此项或返回上级",
"noResults": "无结果。",
+ "notificationClarificationBody": "代理提出了一个问题。",
+ "notificationClarificationTitle": "代理需要您的回答",
+ "notificationInboxItemBody": "有新条目需要您处理。",
+ "notificationInboxItemTitle": "新的收件箱条目",
+ "notificationTurnFinishedBody": "代理完成了一轮。",
+ "notificationTurnFinishedTitle": "代理本轮已完成",
"notInstalled": "未安装",
"notYetInstalled": "尚未安装",
"officeIsNotEnabledForThis": "此运行时未启用办公室。",
@@ -228,6 +301,7 @@
"probing": "正在探测",
"profileName": "配置名称",
"prompts": "提示词",
+ "reference": "引用",
"refreshAriaLabel": "刷新 {{label}}",
"refreshBranchesGitFetch": "刷新分支(git fetch)",
"refreshingLabel": "正在刷新 {{label}}...",
@@ -269,6 +343,7 @@
"scopeFiles": "文件",
"scopeTitleWithShortcut": "{{label}}({{shortcut}})",
"scratchWorkspace": "临时工作区",
+ "script": "脚本",
"searchEllipsis": "搜索…",
"searchFailedEditTheQueryOr": "搜索失败。请修改查询或重新打开搜索后重试。",
"searchForFiles": "搜索文件...",
@@ -277,6 +352,14 @@
"searchQueriesAreLimitedTo200": "搜索查询限制为 200 个字符。",
"searchTaskContents": "搜索任务内容…",
"select": "选择",
+ "sessionStateCancelled": "已取消",
+ "sessionStateCompleted": "已完成",
+ "sessionStateCreated": "已创建",
+ "sessionStateFailed": "失败",
+ "sessionStateIdle": "空闲",
+ "sessionStateRunning": "运行中",
+ "sessionStateStarting": "启动中",
+ "sessionStateWaitingForInput": "等待输入",
"settings": "设置",
"shell": "命令解释器",
"shellCommandPlusSlashCommandSkills": "可在次要实例中运行的 Shell 命令,以及工作流期间可让代理运行的斜杠命令 <2>skills2>。",
@@ -288,9 +371,23 @@
"skip": "跳过",
"stagesAndCommitsChangesUsingConventional": "暂存并用 Conventional Commits 提交更改。",
"status": "状态",
+ "step": "步骤",
"subtaskTitle": "子任务标题",
"switchMode": "切换模式",
"system": "系统",
+ "task": "任务",
+ "taskStateBlocked": "已阻塞",
+ "taskStateCancelled": "已取消",
+ "taskStateCompleted": "已完成",
+ "taskStateCreated": "已创建",
+ "taskStateFailed": "失败",
+ "taskStateInProgress": "进行中",
+ "taskStateNotStarted": "未开始",
+ "taskStateReview": "审查",
+ "taskStateScheduling": "调度中",
+ "taskStateTodo": "待办",
+ "taskStateWaitingForInput": "等待输入",
+ "terminal": "终端",
"todos": "待办",
"taskLoading": "正在加载任务…",
"tasks": "任务",
@@ -308,19 +405,25 @@
"typeACommand": "输入命令...",
"typeToSearchFiles": "输入以搜索文件...",
"typeToSearchTaskContents": "输入以搜索任务内容…",
+ "unavailable": "不可用",
+ "unknown": "未知",
+ "unknownError": "未知错误",
"unknownPanel": "未知面板:{{component}}",
"unsavedChanges": "未保存的更改",
+ "untitledTask": "未命名任务",
"usefulCommandsAgentSkills": "实用命令与代理技能",
"useScratchInstead": "改用临时工作区",
"useThisFolder": "使用此文件夹",
"viewFullReleaseOnGithub": "在 GitHub 上查看完整发行说明",
"waitsForCiAndAutomatedReviews": "等待 CI 与自动审阅,然后修复失败并处理评论。",
+ "walkthrough": "导览",
"whatSNew": "新功能",
"whenItsDoneTheAgentOpensAPr": "完成后,代理会向 <2>{{repo}}2> 打开拉取请求供维护者审阅,节省他们的时间,并把改进带给所有人。",
"workflow": "工作流",
"workflowsControlTheStepsAutomationAnd": "工作流控制任务的步骤、自动化与代理行为。可在设置中添加更多工作流。",
"workspace": "工作区",
"workspaces": "工作区",
+ "you": "您",
"youCanUseTheNavigationOr": "你可以使用导航或重新加载以重试。",
"yourAgentImplementsItInThe": "你的代理在 kandev 仓库中实现它,并附带测试",
"youVerifyAndTestTheChange": "你在第二个 kandev 实例中验证并测试该改动"
diff --git a/apps/web/src/locales/zh-cn/editors.json b/apps/web/src/locales/zh-cn/editors.json
index e9f2913f82..31a7b5dba9 100644
--- a/apps/web/src/locales/zh-cn/editors.json
+++ b/apps/web/src/locales/zh-cn/editors.json
@@ -19,10 +19,17 @@
"copyLine": "复制第 {{lineNumber}} 行",
"copyPath": "复制路径",
"default": "默认",
+ "deleteFailed": "删除失败",
"deleteFile": "删除文件",
"disableWordWrap": "关闭自动换行",
"enableWordWrap": "开启自动换行",
+ "errorWhileDeletingFile": "删除文件时发生错误",
+ "errorWhileSavingFile": "保存文件时发生错误",
+ "failedToDeleteFile": "无法删除文件",
+ "failedToSaveFile": "无法保存文件",
"failedToSendComment": "发送评论失败",
+ "fileSavedOverwritten": "文件已保存(已覆盖)",
+ "fileSavedOverwrittenDescription": "该文件已被外部修改。已保存您的版本。",
"foldUnchangedLines": "折叠未更改的行",
"hideDiffIndicators": "隐藏 diff 指示器",
"highlight": "高亮",
@@ -35,6 +42,7 @@
"noDiffAvailable": "无可用 diff",
"noEditorsConfigured": "未配置编辑器",
"openFolder": "打开文件夹",
+ "openTaskSessionBeforeSending": "发送给代理前请先打开任务会话。",
"openWith": "打开方式…",
"pasteLink": "粘贴链接…",
"pleaseTryAgain": "请重试。",
@@ -43,6 +51,7 @@
"reload": "重新加载",
"revertAllChanges": "还原全部更改",
"revertChanges": "还原更改",
+ "saveFailed": "保存失败",
"saving": "正在保存…",
"sentToTheAgent": "已发送给代理。",
"showAllLines": "显示全部行",
diff --git a/apps/web/src/locales/zh-cn/github.json b/apps/web/src/locales/zh-cn/github.json
index 178c4c4a03..b32e229eca 100644
--- a/apps/web/src/locales/zh-cn/github.json
+++ b/apps/web/src/locales/zh-cn/github.json
@@ -180,6 +180,7 @@
"failedToDeleteReviewWatch": "删除审阅监控失败",
"failedToLoadGithubWorkspaceSettings": "加载 GitHub 工作区设置失败",
"failedToMerge": "合并失败",
+ "failedToReRequestReview": "无法重新请求审查",
"failedToResetIssueWatch": "重置议题监控失败",
"failedToResetReviewWatch": "重置审阅监控失败",
"failedToSaveDefaultQueries": "保存默认查询失败",
@@ -445,6 +446,7 @@
"reviewCount_other": "{{count}} 条审阅",
"reviewFollowUp": "审阅跟进",
"reviewPermissions": "审阅权限",
+ "reviewReRequestedFrom": "已重新向 {{reviewer}} 请求审查",
"reviewWatch": "审阅监控",
"reviewWatchCreated": "审阅监控已创建",
"reviewWatchDeleted": "审阅监控已删除",
@@ -468,6 +470,7 @@
"selectWorkflow": "选择工作流",
"selectWorkspace": "选择工作区",
"selectedRepositories": "已选仓库",
+ "selectWorkspaceBeforeRequestingReview": "请求审查前请先选择工作区。",
"setTheseExactUrlsCreateAClientSecret": "设置这些准确的 URL,创建客户端密钥和网络钩子密钥,并下载私钥。此一次性设置于 {{expiresAt}} 过期。",
"showGithubApiLimits": "显示 GitHub API 限制",
"showLess": "收起",
diff --git a/apps/web/src/locales/zh-cn/gitlab.json b/apps/web/src/locales/zh-cn/gitlab.json
index 6fdba6e058..170df6bdd1 100644
--- a/apps/web/src/locales/zh-cn/gitlab.json
+++ b/apps/web/src/locales/zh-cn/gitlab.json
@@ -158,8 +158,46 @@
"mergeRequestsAndIssues": "{{host}} · 合并请求与议题",
"mergeThisMergeRequest": "合并此合并请求?",
"mergeability": "可合并性",
+ "merging": "合并中…",
"mockTest": "模拟(测试)",
+ "mrAutoFixCiAndAddressComments": "自动修复 CI 并处理评论",
+ "mrAutoFixInsertMrFeedback": "插入 MR 反馈",
+ "mrAutoFixPrompt": "自动修复提示词",
+ "mrAutoFixPromptDescription": "此提示词仅用于本任务。留空则使用默认提示词。若希望 Kandev 附带其 MR 反馈快照,请加入 <0>{{placeholder}}0>。",
+ "mrAutoFixRoundExplanation": "自动修复已为此 MR 使用 {{max}} 轮中的 {{current}} 轮。每当 Kandev 发送或排入一条 MR 自动修复消息时计为一轮。Kandev 会等待流水线结束后再开始新的自动修复轮次,以便代理同时获得最终失败的作业和当前的讨论评论。更新已排队的自动修复消息不会消耗额外轮次。当达到 {{max}}/{{max}} 且没有待更新的自动修复消息时,Kandev 会暂停此 MR 的自动修复,以免无限循环。人工审查后先停用再重新启用自动修复即可重新开始。",
+ "mrAutoMergeReadyExplanation": "一旦此 MR 完全就绪,Kandev 会立即自动合并:处于开启状态且非草稿、流水线已成功、没有未解决的讨论,并且 GitLab 自身的合并就绪检查也通过。如果您的项目需要审批,该条件已包含在 GitLab 的检查中——MR 获得批准且满足其他所有条件后,Kandev 会立即合并,不再另行确认。",
+ "mrAutoMergeWhenReady": "就绪时自动合并",
+ "mrAutomation": "自动化",
+ "mrAutomationAutoFixPromptResetFailed": "无法重置自动修复提示词。",
+ "mrAutomationAutoFixPromptSaveFailed": "无法保存自动修复提示词。",
+ "mrAutomationEditAutoFixPromptForThis": "编辑此任务的自动修复提示词",
+ "mrAutomationEditDefaultPrompt": "编辑默认提示词",
+ "mrAutomationExplainAutoFixRounds": "说明自动修复轮次",
+ "mrAutomationExplainAutoMerge": "说明自动合并就绪条件",
+ "mrAutomationExplainOptions": "说明自动化选项",
+ "mrAutomationOmitPlaceholderExplanation": "如果您希望代理自行拉取或获取分支并直接查看 GitLab,而不是接收 Kandev 的快照,请省略该占位符。",
+ "mrAutomationPlaceholderExplanation": "该占位符会插入当前 MR 标识符、新增或变更的失败流水线作业及其链接,以及新增或变更的未解决讨论评论(含文件、行号和正文)。",
+ "mrAutomationSavePrompt": "保存提示词",
+ "mrAutomationTaskAutoFixPrompt": "任务自动修复提示词",
+ "mrAutomationUseDefault": "使用默认值",
+ "mrAutomationWatchesLinkedMergeRequest": "在 1 分钟同步循环中监视此任务关联的合并请求。自动修复会为新观察到的流水线失败或讨论评论排入任务提示词,自动合并会等待 MR 就绪,而通知开关会在工作区已连接的 GitLab 账号被请求审查或 MR 进入终态时唤醒该任务的代理。审查请求会先静默记录该账号的当前请求状态,之后再有请求时才通知。",
+ "mrPopoverApproved": "已批准",
+ "mrPopoverAwaitingCount_one": "{{count}} 项待处理",
+ "mrPopoverAwaitingCount_other": "{{count}} 项待处理",
+ "mrPopoverAwaitingReview": "等待审查",
+ "mrPopoverNoPipeline": "尚未启动流水线",
+ "mrPopoverOpenInGitlab": "在 GitLab 中打开 !{{mriid}}",
+ "mrPopoverPassRate": "通过率",
+ "mrPopoverUnlink": "取消关联 !{{mriid}}",
+ "mrPopoverUnresolvedComments_one": "{{count}} 条未解决的评论",
+ "mrPopoverUnresolvedComments_other": "{{count}} 条未解决的评论",
+ "mrPopoverUpdatedAgo": "{{elapsed}}前更新",
+ "mrPopoverUpdatedJustNow": "刚刚更新",
"mrs": "MR",
+ "mrTooltipPipeline": "流水线:{{state}}",
+ "mrTooltipReadyToMerge": "可以合并",
+ "mrTooltipReview": "审查:{{state}}",
+ "mrTooltipState": "状态:{{state}}",
"name": "名称",
"never": "从不",
"neverDelete": "永不删除",
diff --git a/apps/web/src/locales/zh-cn/kanban.json b/apps/web/src/locales/zh-cn/kanban.json
index 5d52263c9a..e38cbfdbe8 100644
--- a/apps/web/src/locales/zh-cn/kanban.json
+++ b/apps/web/src/locales/zh-cn/kanban.json
@@ -4,6 +4,7 @@
"allRepositories": "全部仓库",
"allWorkflows": "全部工作流",
"allWorkflowsTitleCase": "全部工作流",
+ "allWorkspaces": "所有工作区",
"approvalRequired": "需要审批",
"archive": "归档",
"archiveCount_one": "归档 {{count}} 项",
diff --git a/apps/web/src/locales/zh-cn/office.json b/apps/web/src/locales/zh-cn/office.json
index e6d21cf928..1e0440bae0 100644
--- a/apps/web/src/locales/zh-cn/office.json
+++ b/apps/web/src/locales/zh-cn/office.json
@@ -101,10 +101,15 @@
"botTokenChatId": "{\"bot_token\": \"...\", \"chat_id\": \"...\"}",
"branch": "分支",
"browseSkillsSh": "浏览 skills.sh",
+ "budgetActionBlockNewTasks": "阻止新任务",
+ "budgetActionNotifyOnly": "仅通知",
+ "budgetActionPauseAgent": "暂停代理",
"budgetAlert": "预算告警",
"budgetAmount": "预算:{{amount}}",
"budgetCapConcurrencyAndExecutionEnvironment": "预算上限、并发与执行环境。",
"budgetLimitsConcurrencyAndExecutionEnvironment": "预算限制、并发与执行环境。",
+ "budgetPeriodMonthly": "每月",
+ "budgetPeriodTotal": "总计",
"budgetPolicies": "预算策略",
"budgetPolicyCreated": "预算策略已创建",
"budgetPolicyDeleted": "预算策略已删除",
diff --git a/apps/web/src/locales/zh-cn/settings.json b/apps/web/src/locales/zh-cn/settings.json
index 835b4c01c4..43cc1cf643 100644
--- a/apps/web/src/locales/zh-cn/settings.json
+++ b/apps/web/src/locales/zh-cn/settings.json
@@ -288,6 +288,8 @@
"externalMcpToolsSummary": "{{tools}},共 {{categories}} — 展开可预览外部代理能做什么。",
"externalProviders": "外部提供商",
"externalProvidersDescription": "配置用于远程通知的外部提供商。",
+ "fallbackModelUnavailable": "备用模型已不可用——请另选一个",
+ "fallbackNoteLive": "此会话因配置的模型不可用而以备用模型启动。(备用)标记仅反映当前会话,重新加载后不再显示。",
"fallbackSettings": "回退设置",
"fallbackSettingsSummaryAutomatic": "已启用自动回退",
"fallbackSettingsSummaryExplicit": "显式回退:{{model}}",
@@ -345,10 +347,17 @@
"layoutEditorForProfile": "{{name}} 编辑器",
"layoutProfile": "布局配置",
"layoutProfileCopyName": "{{name}} 副本",
+ "layoutProfileDefault": "默认",
+ "layoutProfileDefaultDescription": "代理,并附带文件、更改和终端",
"layoutProfileName": "布局配置名称",
"layoutProfileNamesMustNotBe": "布局配置名称不能为空",
+ "layoutProfilePlanMode": "计划模式",
+ "layoutProfilePlanModeDescription": "代理与计划并排显示",
+ "layoutProfilePreviewMode": "预览模式",
+ "layoutProfilePreviewModeDescription": "代理与浏览器并排显示",
"layoutProfiles": "布局配置",
"layoutProfilesWereNotSaved": "布局配置未保存",
+ "layoutProfileVsCodeDescription": "代理与 VS Code 并排显示",
"layouts": "布局",
"ligatures": "连字",
"light": "浅色",
@@ -371,7 +380,14 @@
"makesAgentctlValuesAvailableToPlugins": "使 agentctl 的值可供插件和其他使用方使用。内置状态栏仍仅由宿主使用。",
"manageApiKeysAndCredentialsSecrets": "管理 API 密钥和凭据。密钥会在静态存储时加密,并通过执行器配置文件的环境变量注入代理环境。",
"globalScope": "全局",
+ "modelFallbackSuffix": "(备用)",
+ "mode": "模式",
"modelWithValue": "模型:{{model}}",
+ "profileFallbackWillBeUsed": "起始模型 {{model}} 已不可用——将使用备用模型 {{fallback}}。",
+ "profileStartModelUnavailable": "起始模型 {{model}} 已不可用——请在代理配置中更改。",
+ "selectAModel": "选择模型...",
+ "startModelUnavailable": "已不可用——请另选模型",
+ "startModelAria": "配置起始模型设置",
"temperatureWithValue": "温度:{{temperature}}",
"workspaceScope": "工作区",
"workspaceSecretsDescription": "管理工作区中仓库可用的密钥。共享代理与执行器配置文件只能使用全局密钥。",
diff --git a/apps/web/src/locales/zh-cn/sidebar.json b/apps/web/src/locales/zh-cn/sidebar.json
index aaaaa10290..23b9cccf24 100644
--- a/apps/web/src/locales/zh-cn/sidebar.json
+++ b/apps/web/src/locales/zh-cn/sidebar.json
@@ -1,6 +1,9 @@
{
"account": "账户",
"archivedLoadFailed": "无法加载已归档任务",
+ "groupAll": "全部",
+ "groupMultiRepo": "多仓库",
+ "groupUnassigned": "未分配",
"retry": "重试",
"activeWorkspaceBadge": "活动",
"activity": "活动",
@@ -98,6 +101,7 @@
"toggleTheme": "切换主题",
"unsavedFilterChanges": "未保存的筛选更改",
"viewAll": "全部",
+ "viewAllTasks": "所有任务",
"viewLabel": "视图:{{name}}",
"whatsNew": "新功能",
"work": "工作",
diff --git a/apps/web/src/locales/zh-cn/task.json b/apps/web/src/locales/zh-cn/task.json
index 3e60857a4d..adaa18fc5c 100644
--- a/apps/web/src/locales/zh-cn/task.json
+++ b/apps/web/src/locales/zh-cn/task.json
@@ -1,5 +1,8 @@
{
+ "agentCliNotInstalled": "未安装代理 CLI",
+ "agentProbeFailed": "代理探测失败",
"aPullRequestAlreadyExistsFor": "此任务已存在拉取请求",
+ "archivedTask": "已归档任务",
"aSharedDocumentForYouAnd": "供你与代理协作制定方案的共享文档",
"aboutContextWindowSource": "关于上下文窗口来源",
"activeBadge": "活跃",
@@ -175,7 +178,6 @@
"chooseRepositoriesOrFoldersToMake": "选择要在此任务中可用的仓库或文件夹。",
"cleanup": "清理",
"clearAllAnnotations": "清除全部标注",
- "cliModeYourPromptWillBe": "CLI 模式 — 你的提示词将自动注入到终端",
"clickAnywhereToStartWriting": "点击任意位置开始编写",
"clickRefreshToDetectListeningPorts": "点击刷新以检测监听端口。",
"cloneFromAProviderOrGit": "从提供商或 Git URL 克隆。",
@@ -198,6 +200,13 @@
"collapseSubtasks": "折叠子任务",
"collapseToolbar": "折叠工具栏",
"color": "颜色",
+ "colorBlue": "蓝色",
+ "colorGreen": "绿色",
+ "colorOrange": "橙色",
+ "colorPink": "粉色",
+ "colorPurple": "紫色",
+ "colorRed": "红色",
+ "colorYellow": "黄色",
"command": "命令",
"commandCopied": "命令已复制",
"commandFailed": "命令失败",
@@ -235,7 +244,12 @@
"context": "上下文",
"contextFieldLabel": "上下文",
"contextWindow": "上下文窗口",
+ "contextWindowNotMeasured": "上下文窗口:未测量使用量",
+ "contextWindowUsagePendingExplain": "使用数据将在第一轮完成后显示。",
+ "contextWindowUsagePendingPercent": "—%",
+ "contextWindowUsagePendingTokens": "— / {{size}} 个 token",
"contextWindowUsed": "上下文窗口:已使用 {{percent}}%",
+ "contextWindowUsedTokens": "{{used}} / {{size}} 个 token",
"control": "Control",
"controlC": "Control C",
"controlD": "Control D",
@@ -258,6 +272,7 @@
"couldNotAddSources": "无法添加来源。你的条目仍保留在此以便重试。",
"couldNotLoadRepositories": "无法加载仓库:{{message}}",
"couldNotResolveRemoteRepository": "无法解析远程仓库:{{message}}",
+ "couldNotSaveStatusBarOrder": "无法保存状态栏顺序",
"couldnTStartASession": "无法启动会话",
"create": "创建",
"createANewBranch": "创建新分支",
@@ -279,6 +294,7 @@
"creatingEllipsis": "正在创建…",
"creatingFrom": "正在从 <4>{{displayBranch}}4> 创建 {{shortName}}",
"creatingFromTo": "正在从 <4>{{displayBranch}}4> 到 <7>{{baseBranch}}7> 创建 {{shortName}}",
+ "credentialsAvailableInExecutor": "所选执行器中已提供凭据",
"ctrl": "Ctrl",
"current": "当前",
"current2": "当前",
@@ -390,14 +406,19 @@
"environmentPrepared": "环境已准备",
"environmentPreparedOnFreshSandbox": "已在全新沙箱上准备环境",
"environmentPreparedWithWarnings": "环境已准备(有警告)",
+ "environmentReset": "环境已重置",
+ "environmentResetFailed": "重置失败:{{error}}",
"environmentSetupFailed": "环境设置失败",
"environmentSetupFinishedWithErrors": "环境设置完成但有错误",
+ "environmentStateCurrent": "当前状态:{{state}}",
"error": "错误",
"errorData": "错误数据",
"eventCount_one": "{{count}} 个事件",
"eventCount_other": "{{count}} 个事件",
"executor2": "执行器",
"executorEnvironmentIsUnavailable": "执行器环境不可用。",
+ "executorEnvironmentRunning": "执行器环境运行中",
+ "executorEnvironmentStopped": "执行器环境已停止",
"executorHintDocker": "将从所选基础分支创建 Docker 容器,并在任务分支上检出。",
"executorHintLocal": "代理将直接在仓库上运行。",
"executorHintWorktreeMulti": "将在父文件夹中为每个仓库创建 git 工作树。代理在该父文件夹中运行,以便并排查看所有工作树。",
@@ -428,6 +449,8 @@
"failedToChangeModel": "更改模型失败",
"failedToDeleteDocument": "删除文档失败",
"failedToDeleteLayout": "删除布局失败",
+ "failedToDiscardChanges": "无法放弃更改",
+ "failedToGenerateContent": "无法生成内容",
"failedToLinkGithubIssue": "关联 GitHub 议题失败。",
"failedToLinkGithubPullRequest": "关联 GitHub 拉取请求失败。",
"failedToLinkTarget": "关联 {{target}} 失败。",
@@ -443,13 +466,16 @@
"failedToRenameItem": "重命名项目失败",
"failedToRenameTask": "重命名任务失败",
"failedToRestorePlan": "恢复计划失败",
+ "failedToResumeSession": "无法恢复会话",
"failedToRevokeShare": "撤销分享失败。",
"failedToSaveDocument": "保存文档失败",
"failedToSaveLayout": "保存布局失败",
"failedToSendComment": "发送评论失败",
"failedToSendComments": "发送评论失败",
"failedToSendMessage": "发送消息失败",
+ "failedToStartTunnel": "无法启动隧道:{{error}}",
"failedToStartVsCode": "启动 VS Code 失败",
+ "failedToStopTunnel": "无法停止隧道:{{error}}",
"failedToUnarchiveTask": "取消归档任务失败",
"failedToUnlinkGithubIssue": "取消关联 GitHub 议题失败。",
"failedToUpload": "上传 {{name}} 失败",
@@ -508,6 +534,7 @@
"fullImpactDetails": "完整影响详情",
"fullSize": "全尺寸 {{alt}}",
"generatingSummary": "正在生成摘要…",
+ "generationFailed": "生成失败",
"gitActions": "Git 操作",
"gitPollMode": "Git 轮询:{{mode}}({{label}})",
"githubIssue": "GitHub 议题",
@@ -588,6 +615,7 @@
"kandevListWorkspaces": "Kandev:列出工作区",
"kandevMessageTask": "Kandev:向任务发送消息",
"kandevMoveTask": "Kandev:移动任务",
+ "kandevUpdateAvailable": "Kandev 有可用更新",
"kandevUpdateTask": "Kandev:更新任务",
"kandevUpdateTaskPlan": "Kandev:更新任务计划",
"kandevWriteTaskDocument": "Kandev:写入任务文档",
@@ -692,6 +720,9 @@
"moreNotShown_one": "+ 另有 {{count}} 项未显示",
"moreNotShown_other": "+ 另有 {{count}} 项未显示",
"moreToolbarActions": "更多工具栏操作",
+ "moveFailed": "移动失败",
+ "moveFailedFiles": "部分文件无法移动",
+ "moveFailedUnexpected": "移动文件时发生错误",
"moveHeadToThisCommitKeep": "将 HEAD 移至此提交。保留所有已暂存更改。",
"moveHere": "移到此处",
"moveTaskToTheNextWorkflow": "将任务移至下一工作流步骤",
@@ -722,6 +753,7 @@
"nextMessage": "下一条消息",
"nextQuestion": "下一个问题",
"noActiveSession": "无活动会话",
+ "noActiveSessionDescription": "请先启动会话再使用 AI 生成",
"noActiveTask": "无活动任务",
"noActivityYet": "暂无动态",
"noAgentFound": "未找到代理。",
@@ -785,6 +817,7 @@
"noSavedLayouts": "无已保存布局",
"noSessionsYetLaunchOneTo": "暂无会话。启动一个以开始。",
"noSteps": "无步骤",
+ "noStepsConfigured": "未配置步骤",
"noStepsFound": "未找到步骤。",
"noSubIssues": "无子议题",
"noTaskSelected": "未选择任务",
@@ -825,9 +858,20 @@
"openWorkspaceSettings": "打开工作区设置",
"option": "选项 {{index}}",
"orTypeACustomAnswer": "或输入自定义回答…",
+ "otherChanges": "其他更改",
"output": "输出",
"outputTokens": "输出 token",
"outputTruncated": "输出已截断",
+ "panelAgent": "代理",
+ "panelChanges": "更改",
+ "panelDiffViewer": "差异查看器",
+ "panelFileDiff": "差异 [{{file}}]",
+ "panelFiles": "文件",
+ "panelMergeRequest": "合并请求",
+ "panelPlan": "计划",
+ "panelPrDetails": "PR 详情",
+ "panelPullRequest": "拉取请求",
+ "panelTerminal": "终端",
"parent": "父项",
"parkedCount": "已停放({{parked}})",
"parkedTerminals": "已停放的终端",
@@ -839,6 +883,7 @@
"pendingReviewCommentsHelp_one": "{{count}} 条待发送的审阅评论。点击展开列表 — 可编辑每条评论、点击文件路径跳转到源码,或用垃圾桶图标移除评论。",
"pendingReviewCommentsHelp_other": "{{count}} 条待发送的审阅评论。点击展开列表 — 可编辑每条评论、点击文件路径跳转到源码,或用垃圾桶图标移除评论。",
"permissionRequested": "已请求权限",
+ "permissionRequired": "需要权限",
"pickADifferentRepoLocalFolder": "选择其他仓库、本地文件夹或远程 URL",
"pickAStartingFolderOptional": "选择起始文件夹(可选)",
"pickOrPasteARepo": "选择或粘贴仓库",
@@ -856,6 +901,7 @@
"pluginTopBarActions": "插件顶栏操作",
"portAlreadyAdded": "端口已添加",
"portForwarding": "端口转发",
+ "portForwardingPreferenceUpdateFailed": "无法更新端口转发可见性",
"portForwardingTunnelActive_one": "端口转发({{count}} 个隧道活动)",
"portForwardingTunnelActive_other": "端口转发({{count}} 个隧道活动)",
"portNumber": "端口号",
@@ -1207,6 +1253,7 @@
"statusInProgress": "进行中",
"statusInReview": "审阅中",
"statusTodo": "待办",
+ "statusUpdate": "状态更新",
"stepCount_one": "{{count}} 个步骤",
"stepCount_other": "{{count}} 个步骤",
"stepFieldLabel": "步骤",
@@ -1221,6 +1268,7 @@
"stopVoiceInput": "停止语音输入",
"stoppedWithAnError": "因错误而停止",
"stopping": "正在停止…",
+ "subagent": "子代理",
"subIssues": "子议题",
"subTasks": "子任务",
"submit": "提交",
@@ -1232,6 +1280,7 @@
"subtasksStayActiveUnlessYouTick": "除非勾选此项,否则子任务保持活动。它们可能仍在进行中。",
"successful": "{{operationName}} 成功",
"summarize": "总结",
+ "summarizeReturnedNoResult": "摘要工具未返回任何结果",
"summarizeSession": "总结会话",
"summarizeSessionNamed": "总结 {{label}}",
"summarizing": "正在总结…",
@@ -1271,6 +1320,7 @@
"terminalNotAvailableThisTaskIs": "终端不可用 — 此任务已归档",
"terminalNumbered": "终端 #{{seq}}",
"terminalPromptInputAreDisabledUntil": "终端与提示词输入在下次运行开始前已禁用。",
+ "terminalSessionEnded": "此会话已结束——没有可用的终端。",
"terminalUnavailableNoActiveSession": "终端不可用 — 无活动会话。",
"terminalWithSeq": "终端 {{seq}}",
"terminals": "终端",
@@ -1324,6 +1374,7 @@
"tokenCount_other": "{{formatted}} 个 token",
"tokens": "token",
"tool": "工具",
+ "toolCall": "工具调用",
"toolCallCount_one": "{{count}} 次工具调用",
"toolCallCount_other": "{{count}} 次工具调用",
"toolCount_one": "{{count}} 个工具",
@@ -1340,6 +1391,8 @@
"truncatedParen": "(已截断)",
"tryAgain": "重试",
"tunnel": "隧道",
+ "tunnelStartedOnPort": "隧道已在端口 {{port}} 上启动",
+ "tunnelStopped": "隧道已停止",
"turnForDuration": ",耗时 {{duration}}",
"turnMessageCount_one": "· {{count}} 条消息",
"turnMessageCount_other": "· {{count}} 条消息",
diff --git a/apps/web/src/locales/zh-cn/tasks.json b/apps/web/src/locales/zh-cn/tasks.json
index 88b278b9a5..0280e3359c 100644
--- a/apps/web/src/locales/zh-cn/tasks.json
+++ b/apps/web/src/locales/zh-cn/tasks.json
@@ -10,6 +10,10 @@
"columnWorkflow": "工作流",
"delete": "删除",
"deleteTask": "删除任务",
+ "failedToArchiveTask": "无法归档任务",
+ "failedToDeleteTask": "无法删除任务",
+ "failedToLoadTasks": "无法加载任务",
+ "failedToUnarchiveTask": "无法取消归档任务",
"goToFirstPage": "转到第一页",
"goToLastPage": "转到最后一页",
"goToNextPage": "转到下一页",
@@ -39,6 +43,12 @@
"sortTitleZA": "标题 Z-A",
"sortUpdatedNewest": "更新时间(最新)",
"sortUpdatedOldest": "更新时间(最早)",
+ "taskArchived": "任务已归档",
+ "taskArchivedDescription": "任务已成功归档。",
+ "taskUnarchived": "任务已取消归档",
"unarchive": "取消归档",
+ "unarchivedBranchesMissing_one": "分支 {{branches}} 在本地和远程都已不存在——下一个会话将从基础分支重新开始。",
+ "unarchivedBranchesMissing_other": "分支 {{branches}} 在本地和远程都已不存在——下一个会话将从基础分支重新开始。",
+ "unarchivedTaskRestored": "任务已恢复。",
"unarchiveTask": "取消归档任务"
}
diff --git a/apps/web/src/locales/zh-cn/workflows.json b/apps/web/src/locales/zh-cn/workflows.json
index e29607b383..7b90b3a18b 100644
--- a/apps/web/src/locales/zh-cn/workflows.json
+++ b/apps/web/src/locales/zh-cn/workflows.json
@@ -184,6 +184,8 @@
"startStepHelp": "新任务从此步骤开始。每个工作流只能有一个起始步骤。",
"stepIsPartOfReplayCycle": "{{stepName}} 属于回放循环",
"stepNamePlaceholder": "步骤名称",
+ "workflowDescription": "描述",
+ "workflowDescriptionPlaceholder": "可选描述",
"workflowPrompt": "工作流提示词",
"workflowPromptHelp": "可选说明,会在每一步进入时、步骤提示词之前加入。支持 {task_id} 与 @saved-prompt 引用(展开方式与步骤提示词相同)。{{taskPrompt}} 仅用于步骤。留空即禁用。对代理可见,并在聊天中折叠显示。",
"workflowPromptPlaceholder": "若 PR 已合并或关闭,将任务移至 Done。\n若需要创建多个拉取请求,优先通过子任务编排。",
diff --git a/docs/i18n.md b/docs/i18n.md
index 60207209ba..a0cdef136c 100644
--- a/docs/i18n.md
+++ b/docs/i18n.md
@@ -165,7 +165,37 @@ prefix-only pattern silently never matches — write the full match.
The same hazard, milder, applies to any display string that doubles as a
comparison key or `Map` key (command-palette group names, header titles). Key off
-a stable id and translate only at render.
+a stable id and translate only at render. `kanban-header.tsx` shipped
+`title === "Home"` for a while — true only in English, and silently the wrong
+branch everywhere else.
+
+### …and to any display string that is also PERSISTED
+
+A string that is both shown to the user and written to storage cannot simply be
+wrapped in `t()`: translating it in place writes whichever locale happened to be
+active into the user's saved record, where it stays after a switch. Three places
+in the app are like this, and all three use the same split — a canonical English
+value for storage beside a catalog key for display:
+
+| Module | Persisted | Displayed |
+| --- | --- | --- |
+| `lib/state/layout-manager/constants.ts` | `PANEL_REGISTRY[id].title` | `titleKey` via `panelTitle()` |
+| `lib/layout/layout-profiles.ts` | `BUILT_IN_LAYOUT_PROFILES[i].name` | `nameKey` via `builtInLayoutProfileName()` |
+| `lib/state/slices/ui/sidebar-view-builtins.ts` | `DEFAULT_VIEW.name` | `sidebarViewName()` |
+
+Get the **direction** right, and write a test that pins it. Dockview panel
+titles pass through the serializer twice: `toSerializedDockview` feeds
+`api.fromJSON` and so must localize, while `panelFromDockviewPanel` is what
+`fromDockviewApi` captures into a saved layout and so must canonicalize. The
+first version of that split had them reversed; every unit test passed, because
+the test had been written to the same wrong model. What caught it was the
+pseudo-locale oracle — which could only see it because the layouts screen's
+`allow` list had stopped claiming those titles were expected-English.
+
+The other seeded-preset modules (`my-github/search-bar.ts`, `my-jira/presets.ts`,
+`azure-devops-workspace-defaults.ts`) are deliberately English for the same
+reason and have **not** been split, because their values are also sent to an
+agent or matched against a server-side default. Each says so in a header comment.
## Commands