feat(i18n): localize the last batch of live surfaces before Office (#2357)

Migrates the eleven remaining files that `i18next/no-literal-string` flagged
outside `app/office`, `app/demo` and the task-create surfaces owned by #2326:
the session prepare panel, the onboarding agents step, the host system-metrics
readout, the system-health issues dialog, the shared panel search bar, the
mermaid diagram block, the settings agent card, both release-notes surfaces,
and the shared data-table pair.

Findings drop 884 -> 826 (-58) and the guard allowlist grows 442 -> 453, file
by file rather than by directory since every one of these directories still
holds un-migrated files.

Defects repaired beyond the codemod's output:

- `data-table-pagination.tsx` rendered `{count} results` as a bare JSX
  fragment, invisible to both plural gates. It is now one interpolated key
  with `_one`/`_other`, as are the health-dialog issue count and the
  release-notes "N new releases" helper.
- Two mixed literal+expression blocks were half-converted, rendering
  identically in English while being structurally untranslatable. Both were
  rebuilt as a single message.
- The codemod re-sorted `settings.json` into `localeCompare` order and
  re-encoded every non-ASCII character to `\uXXXX` under a comment claiming it
  matched the existing files. No catalog is stored escaped and only `common`
  is in that order, so both halves were reverted; all four catalogs are
  additions-only.
- Copy in default parameters and plain helpers is invisible to the lint rule,
  so `Search…`, the six prepare-panel header labels, the setup-script step name
  and error, the metric labels, `lastUpdatedText` and `buildDescription` were
  migrated by hand.

`task:preparingEnvironment` was already taken by #2326 holding "Preparing
environment" without the ellipsis this surface renders, so the prepare panel
uses `preparingEnvironmentEllipsis` rather than silently changing its copy.

Units, wire ids, the `{workspaceId}` URL token, the `"ellipsis"` page sentinel
and every mermaid theme/CSS value stay English as protocol.
This commit is contained in:
João Salavisa
2026-08-07 11:34:35 +02:00
committed by GitHub
parent ac520a8cdc
commit f09e938673
20 changed files with 332 additions and 103 deletions
+25 -14
View File
@@ -17,6 +17,7 @@ import { AgentLogo } from "@/components/agent-logo";
import { ProfileFormFields, type ProfileFormData } from "@/components/settings/profile-form-fields";
import type { AvailableAgent, ToolStatus } from "@/lib/types/http";
import { copyToClipboard } from "@/lib/utils/copy-to-clipboard";
import { Trans, useTranslation } from "react-i18next";
export type AgentSetting = {
profileId: string;
@@ -49,12 +50,13 @@ function CopyButton({ text }: { text: string }) {
}
function StatusPill({ status, error }: { status: string; error?: string }) {
const { t } = useTranslation();
switch (status) {
case "auth_required": {
const pill = (
<span className="flex items-center gap-1 text-xs text-amber-600 dark:text-amber-400">
<IconLock className="h-3.5 w-3.5" />
No auth
{t("common:noAuth")}
</span>
);
if (!error) return pill;
@@ -67,13 +69,15 @@ function StatusPill({ status, error }: { status: string; error?: string }) {
}
case "not_installed":
return (
<span className="flex items-center gap-1 text-xs text-muted-foreground">Not installed</span>
<span className="flex items-center gap-1 text-xs text-muted-foreground">
{t("common:notInstalled")}
</span>
);
case "failed": {
const pill = (
<span className="flex items-center gap-1 text-xs text-red-600 dark:text-red-400">
<IconAlertTriangle className="h-3.5 w-3.5" />
Error
{t("common:error")}
</span>
);
if (!error) return pill;
@@ -88,16 +92,20 @@ function StatusPill({ status, error }: { status: string; error?: string }) {
return (
<span className="flex items-center gap-1 text-xs text-muted-foreground">
<IconLoader2 className="h-3.5 w-3.5 animate-spin" />
Probing
{t("common:probing")}
</span>
);
case "not_configured":
return <span className="flex items-center gap-1 text-xs text-muted-foreground">Pending</span>;
return (
<span className="flex items-center gap-1 text-xs text-muted-foreground">
{t("common:pending")}
</span>
);
default:
return (
<span className="flex items-center gap-1 text-xs text-green-600 dark:text-green-400">
<IconCheck className="h-3.5 w-3.5" />
Installed
{t("common:installed")}
</span>
);
}
@@ -172,10 +180,13 @@ function NotInstalledItems({
tools: ToolStatus[];
showLabel: boolean;
}) {
const { t } = useTranslation();
if (agents.length === 0 && tools.length === 0) return null;
return (
<>
{showLabel && <p className="text-xs text-muted-foreground mt-1">Not yet installed</p>}
{showLabel && (
<p className="text-xs text-muted-foreground mt-1">{t("common:notYetInstalled")}</p>
)}
{agents.map((agent) => (
<div
key={agent.name}
@@ -247,13 +258,14 @@ export function StepAgents({
loading: boolean;
onUpdateSetting: (agentName: string, formPatch: Partial<ProfileFormData>) => void;
}) {
const { t } = useTranslation();
const [openAgent, setOpenAgent] = useState<string | null>(null);
if (loading) {
return (
<div className="flex flex-col items-center justify-center h-full gap-3 text-sm text-muted-foreground">
<IconLoader2 className="h-6 w-6 animate-spin" />
Discovering agents...
{t("common:discoveringAgents")}
</div>
);
}
@@ -281,13 +293,12 @@ export function StepAgents({
showLabel={installedAgents.length > 0}
/>
</div>
<p className="text-xs text-muted-foreground">{t("common:onboardingAgentsHelp")}</p>
<p className="text-xs text-muted-foreground">
Expand an agent to configure its model and permissions. Changes are saved when you proceed.
You can also add custom TUI agents later in Settings &gt; Agents.
</p>
<p className="text-xs text-muted-foreground">
<span className="text-yellow-500 font-medium">Careful:</span> The default Agent Profiles run
with Auto Approve enabled (YOLO mode).
<Trans i18nKey="common:onboardingAgentsAutoApproveWarning">
<span className="text-yellow-500 font-medium">Careful:</span> The default Agent Profiles
run with Auto Approve enabled (YOLO mode).
</Trans>
</p>
</div>
);
@@ -4,6 +4,7 @@ import type { ComponentProps } from "react";
import { Button } from "@kandev/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip";
import { IconSparkles } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
type ReleaseNotesButtonProps = {
hasUnseen: boolean;
@@ -12,6 +13,7 @@ type ReleaseNotesButtonProps = {
};
export function ReleaseNotesButton({ hasUnseen, onClick, size = "icon" }: ReleaseNotesButtonProps) {
const { t } = useTranslation();
return (
<Tooltip>
<TooltipTrigger asChild>
@@ -20,10 +22,10 @@ export function ReleaseNotesButton({ hasUnseen, onClick, size = "icon" }: Releas
{hasUnseen && (
<span className="absolute -top-1 -right-1 h-2.5 w-2.5 rounded-full bg-primary border-2 border-background" />
)}
<span className="sr-only">What&apos;s New</span>
<span className="sr-only">{t("common:whatSNew")}</span>
</Button>
</TooltipTrigger>
<TooltipContent>What&apos;s New</TooltipContent>
<TooltipContent>{t("common:whatSNew")}</TooltipContent>
</Tooltip>
);
}
@@ -15,6 +15,8 @@ import { IconExternalLink } from "@tabler/icons-react";
import { remarkPlugins, markdownComponents } from "@/components/shared/markdown-components";
import { getReleaseUrl } from "@/lib/release-notes";
import type { ChangelogEntry } from "@/lib/changelog";
import type { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
type ReleaseNotesDialogProps = {
open: boolean;
@@ -23,11 +25,13 @@ type ReleaseNotesDialogProps = {
latestVersion: string;
};
function buildDescription(entries: ChangelogEntry[]): string {
function buildDescription(t: TFunction, entries: ChangelogEntry[]): string {
if (entries.length === 1) {
return entries[0].date ? `Released on ${entries[0].date}` : "Latest release notes";
return entries[0].date
? t("common:releasedOn", { date: entries[0].date })
: t("common:latestReleaseNotes");
}
return `${entries.length} new releases`;
return t("common:newReleases", { count: entries.length });
}
export function ReleaseNotesDialog({
@@ -36,6 +40,7 @@ export function ReleaseNotesDialog({
entries,
latestVersion,
}: ReleaseNotesDialogProps) {
const { t } = useTranslation();
const releaseUrl = getReleaseUrl(latestVersion);
return (
@@ -43,10 +48,10 @@ export function ReleaseNotesDialog({
<DialogContent className="sm:max-w-[520px]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
What&apos;s New
{t("common:whatSNew")}
<Badge variant="secondary">v{latestVersion}</Badge>
</DialogTitle>
<DialogDescription>{buildDescription(entries)}</DialogDescription>
<DialogDescription>{buildDescription(t, entries)}</DialogDescription>
</DialogHeader>
<ScrollArea className="max-h-[60vh] pr-4">
<div className="space-y-4">
@@ -77,7 +82,7 @@ export function ReleaseNotesDialog({
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
>
View full release on GitHub
{t("common:viewFullReleaseOnGithub")}
<IconExternalLink className="h-3 w-3" />
</a>
</div>
@@ -13,6 +13,7 @@ import {
IconLoader2,
} from "@tabler/icons-react";
import { cn } from "@/lib/utils";
import { useTranslation } from "react-i18next";
export type MatchInfo = {
current: number;
@@ -89,6 +90,7 @@ function ActionButtons({
onPrev: () => void;
onClose: () => void;
}) {
const { t } = useTranslation();
return (
<>
<Button
@@ -96,7 +98,7 @@ function ActionButtons({
variant="ghost"
size="icon-sm"
onClick={onPrev}
title="Previous (Shift+Enter)"
title={t("common:previousShiftEnter")}
className="cursor-pointer"
>
<IconChevronUp />
@@ -106,7 +108,7 @@ function ActionButtons({
variant="ghost"
size="icon-sm"
onClick={onNext}
title="Next (Enter)"
title={t("common:nextEnter")}
className="cursor-pointer"
>
<IconChevronDown />
@@ -116,7 +118,7 @@ function ActionButtons({
variant="ghost"
size="icon-sm"
onClick={onClose}
title="Close (Esc)"
title={t("common:closeEsc")}
className="cursor-pointer"
>
<IconX />
@@ -198,13 +200,16 @@ export function PanelSearchBar({
onClose,
matchInfo,
isLoading = false,
placeholder = "Search…",
placeholder,
toggles,
hasError = false,
errorText,
debounceMs = 150,
className,
}: PanelSearchBarProps) {
const { t } = useTranslation();
// Can't stay a default parameter: `t` is declared later in the function body.
const resolvedPlaceholder = placeholder ?? t("common:searchEllipsis");
const [localValue, setLocalValue] = useState(value);
const inputRef = useRef<HTMLInputElement | null>(null);
const { emit, flush } = useDebouncedChange(onChange, debounceMs);
@@ -257,19 +262,19 @@ export function PanelSearchBar({
localValue={localValue}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder={placeholder}
placeholder={resolvedPlaceholder}
isLoading={isLoading}
hasError={hasError}
inputRef={inputRef}
/>
{matchInfo && <MatchCounter info={matchInfo} />}
{toggles?.caseSensitive && (
<ToggleButton state={toggles.caseSensitive} title="Match case">
<ToggleButton state={toggles.caseSensitive} title={t("common:matchCase")}>
<IconLetterCase />
</ToggleButton>
)}
{toggles?.regex && (
<ToggleButton state={toggles.regex} title="Regular expression">
<ToggleButton state={toggles.regex} title={t("common:regularExpression")}>
<IconRegex />
</ToggleButton>
)}
@@ -10,6 +10,8 @@ import {
IconFolder,
IconGitBranch,
} from "@tabler/icons-react";
import type { TFunction } from "i18next";
import { Trans, useTranslation } from "react-i18next";
import { useAppStore } from "@/components/state-provider";
import { ExpandableRow } from "@/components/task/chat/messages/expandable-row";
import { isFallbackNoticeStep } from "@/lib/prepare/summarize";
@@ -87,6 +89,7 @@ function StepDetails({ step, blockCommand }: { step: PrepareStepInfo; blockComma
}
function StepWarning({ warning, warningDetail }: { warning: string; warningDetail?: string }) {
const { t } = useTranslation();
const [detailExpanded, setDetailExpanded] = useState(false);
return (
<div data-testid="prepare-warning-banner">
@@ -98,7 +101,7 @@ function StepWarning({ warning, warningDetail }: { warning: string; warningDetai
className="text-amber-500/70 hover:text-amber-500 mt-0.5 flex cursor-pointer items-center gap-0.5 text-xs"
onClick={() => setDetailExpanded(!detailExpanded)}
>
Details
{t("task:details")}
</button>
{detailExpanded && (
<pre className="text-amber-500/60 mt-0.5 max-w-full whitespace-pre-wrap break-words text-xs">
@@ -125,6 +128,7 @@ function StepMessages({ step }: { step: PrepareStepInfo }) {
}
function StepRow({ step }: { step: PrepareStepInfo }) {
const { t } = useTranslation();
const inlineCommand = step.command && isInlineCommand(step.command) ? step.command : undefined;
const blockCommand = step.command && !isInlineCommand(step.command) ? step.command : undefined;
const hasExpandable = Boolean(step.output) || Boolean(blockCommand);
@@ -144,7 +148,7 @@ function StepRow({ step }: { step: PrepareStepInfo }) {
<div className="flex-shrink-0">
<StepIcon status={step.status} hasWarning={Boolean(step.warning)} />
</div>
<span className={nameClass}>{step.name || "Preparing..."}</span>
<span className={nameClass}>{step.name || t("task:preparingEllipsis")}</span>
{inlineCommand && (
<code className="text-muted-foreground/50 min-w-0 break-all font-mono text-[10px]">
{inlineCommand}
@@ -279,20 +283,24 @@ function HeaderIcon({ status }: { status: EffectiveStatus }) {
return <IconTerminal2 className="h-4 w-4 text-muted-foreground" />;
}
function getHeaderLabel(status: EffectiveStatus, prepareState: SessionPrepareState): string {
if (status === "preparing") return "Preparing environment...";
if (status === "failed") return "Environment setup failed";
function getHeaderLabel(
t: TFunction,
status: EffectiveStatus,
prepareState: SessionPrepareState,
): string {
if (status === "preparing") return t("task:preparingEnvironmentEllipsis");
if (status === "failed") return t("task:environmentSetupFailed");
if (status === "completed_with_error") {
return "Environment setup finished with errors";
return t("task:environmentSetupFinishedWithErrors");
}
if (status === "completed_with_warnings") {
const warningSteps = prepareState.steps.filter((s) => s.warning);
if (warningSteps.length === 1 && isFallbackNoticeStep(warningSteps[0])) {
return "Environment prepared on a fresh sandbox";
return t("task:environmentPreparedOnFreshSandbox");
}
return "Environment prepared with warnings";
return t("task:environmentPreparedWithWarnings");
}
return "Environment prepared";
return t("task:environmentPrepared");
}
function InfoLine({ icon, children }: { icon: React.ReactNode; children: React.ReactNode }) {
@@ -347,7 +355,7 @@ type ScriptMetadata = {
// Convert a setup-script `script_execution` message into the same step shape
// the existing prepare panel renders, so it slots in alongside Validate/Sync/
// Create-worktree without a parallel render path.
function setupScriptMessageToStep(message: Message): PrepareStepInfo {
function setupScriptMessageToStep(t: TFunction, message: Message): PrepareStepInfo {
const meta = (message.metadata ?? {}) as ScriptMetadata;
const command = meta.command ?? "";
const status = scriptStatusToStepStatus(meta.status, meta.exit_code);
@@ -355,11 +363,11 @@ function setupScriptMessageToStep(message: Message): PrepareStepInfo {
status === "failed"
? meta.error ||
(meta.exit_code !== undefined && meta.exit_code !== 0
? `Script exited with code ${meta.exit_code}`
: "Script failed")
? t("task:scriptExitedWithCode", { code: meta.exit_code })
: t("task:scriptFailed"))
: undefined;
return {
name: "Run repository setup script",
name: t("task:runRepositorySetupScript"),
command,
status,
output: message.content || undefined,
@@ -370,9 +378,10 @@ function setupScriptMessageToStep(message: Message): PrepareStepInfo {
}
function useSetupScriptSteps(sessionId: string): PrepareStepInfo[] {
const { t } = useTranslation();
const messages = useAppStore((state) => state.messages.bySession[sessionId]);
if (!messages || messages.length === 0) return [];
return messages.filter(isSetupScriptMessage).map(setupScriptMessageToStep);
return messages.filter(isSetupScriptMessage).map((m) => setupScriptMessageToStep(t, m));
}
function SessionInfo({ sessionId }: { sessionId: string }) {
@@ -389,24 +398,31 @@ function SessionInfo({ sessionId }: { sessionId: string }) {
<div className="border-muted mt-2 space-y-1 border-t pt-2">
{isWorktree && worktree_path && (
<InfoLine icon={<IconFolder className="h-3 w-3" />}>
Isolated worktree at <Mono>{worktree_path}</Mono>
<Trans i18nKey="task:isolatedWorktreeAt" values={{ path: worktree_path }}>
Isolated worktree at <Mono>{worktree_path}</Mono>
</Trans>
</InfoLine>
)}
{!isWorktree && worktree_path && (
<InfoLine icon={<IconFolder className="h-3 w-3" />}>
Working in <Mono>{worktree_path}</Mono>
<Trans i18nKey="task:workingIn" values={{ path: worktree_path }}>
Working in <Mono>{worktree_path}</Mono>
</Trans>
</InfoLine>
)}
{worktree_branch && (
<InfoLine icon={<IconGitBranch className="h-3 w-3" />}>
{base_branch ? (
<>
<Trans
i18nKey="task:branchBasedOn"
values={{ branch: worktree_branch, base: base_branch }}
>
Branch <Mono>{worktree_branch}</Mono>, based on <Mono>{base_branch}</Mono>
</>
</Trans>
) : (
<>
<Trans i18nKey="task:onBranch" values={{ branch: worktree_branch }}>
On branch <Mono>{worktree_branch}</Mono>
</>
</Trans>
)}
</InfoLine>
)}
@@ -461,6 +477,7 @@ function hasPrepareDetails(
}
export function PrepareProgress({ sessionId }: PrepareProgressProps) {
const { t } = useTranslation();
const { status, prepareState } = usePrepareStatus(sessionId);
const session = useAppStore((state) => state.taskSessions.items[sessionId]);
const setupScriptSteps = useSetupScriptSteps(sessionId);
@@ -501,7 +518,7 @@ export function PrepareProgress({ sessionId }: PrepareProgressProps) {
// belong after Create-worktree finishes. Appending keeps the existing prepare
// step order intact.
const visibleSteps = [...prepareState.steps.filter(isVisibleStep), ...setupScriptSteps];
const headerLabel = getHeaderLabel(status, prepareState);
const headerLabel = getHeaderLabel(t, status, prepareState);
const isErrorStatus = status === "failed" || status === "completed_with_error";
const headerClass = cn("text-xs", isErrorStatus ? "text-destructive" : "text-muted-foreground");
const hasFailureDetails = isErrorStatus && Boolean(prepareState.errorMessage);
@@ -532,14 +549,16 @@ export function PrepareProgress({ sessionId }: PrepareProgressProps) {
<button
type="button"
aria-expanded={expanded}
aria-label={expanded ? "Hide preparation details" : "Show preparation details"}
aria-label={
expanded ? t("task:hidePreparationDetails") : t("task:showPreparationDetails")
}
className="min-h-11 cursor-pointer text-xs text-muted-foreground underline-offset-4 hover:underline sm:min-h-0"
onClick={(event) => {
event.stopPropagation();
setExpanded(!expanded);
}}
>
{expanded ? "Hide details" : "Show details"}
{expanded ? t("task:hideDetails") : t("task:showDetails")}
</button>
)}
</div>
+5 -3
View File
@@ -5,12 +5,14 @@ import { IconRobot, IconChevronRight } from "@tabler/icons-react";
import { Card, CardContent } from "@kandev/ui/card";
import { Badge } from "@kandev/ui/badge";
import type { AgentProfile } from "@/lib/settings/types";
import { useTranslation } from "react-i18next";
type AgentCardProps = {
agent: AgentProfile;
};
export function AgentCard({ agent }: AgentCardProps) {
const { t } = useTranslation();
const agentLabel = agent.agentDisplayName;
return (
@@ -30,13 +32,13 @@ export function AgentCard({ agent }: AgentCardProps) {
</Badge>
{agent.autoApprove && (
<Badge variant="outline" className="text-xs text-green-600">
Auto-approve
{t("settings:autoApprove")}
</Badge>
)}
</div>
<div className="text-sm text-muted-foreground mt-1">
<p>Model: {agent.model}</p>
<p>Temperature: {agent.temperature}</p>
<p>{t("settings:modelWithValue", { model: agent.model })}</p>
<p>{t("settings:temperatureWithValue", { temperature: agent.temperature })}</p>
</div>
</div>
</div>
+5 -2
View File
@@ -12,6 +12,7 @@ import {
import { useMermaidScale, useMermaidViewportWidth } from "./mermaid-block-hooks";
import { useToast } from "@/components/toast-provider";
import { showMermaidErrorToast } from "./mermaid-error-toast";
import { useTranslation } from "react-i18next";
type MermaidAPI = typeof import("mermaid").default;
@@ -119,6 +120,7 @@ function useMermaidRender(
}
export function MermaidBlock({ code, taskId }: MermaidBlockProps) {
const { t } = useTranslation();
const containerRef = useRef<HTMLDivElement>(null);
const svgSizeRef = useRef<{ w: number; h: number } | null>(null);
const [svgSize, setSvgSize] = useState<{ w: number; h: number } | null>(null);
@@ -164,7 +166,7 @@ export function MermaidBlock({ code, taskId }: MermaidBlockProps) {
if (error !== null && svg === null) {
return (
<div className="my-3 rounded-md border border-destructive/30 bg-destructive/5 p-3">
<p className="text-xs text-destructive mb-2">Failed to render diagram</p>
<p className="text-xs text-destructive mb-2">{t("common:failedToRenderDiagram")}</p>
<pre className="text-xs whitespace-pre-wrap font-mono text-muted-foreground">{code}</pre>
</div>
);
@@ -229,6 +231,7 @@ function MermaidToolbar({
onReset,
onToggleCode,
}: MermaidToolbarProps) {
const { t } = useTranslation();
return (
<div className="absolute top-1.5 right-1.5 flex items-center gap-1 px-1.5 py-0.5 rounded-md bg-background/80 border border-border/50 backdrop-blur-sm opacity-0 group-hover/mermaid:opacity-100 transition-opacity z-10">
{!showCode && onZoomOut && onReset && onZoomIn && scale != null && (
@@ -261,7 +264,7 @@ function MermaidToolbar({
type="button"
onClick={onToggleCode}
className="p-0.5 text-muted-foreground hover:text-foreground cursor-pointer"
title={showCode ? "Show diagram" : "Show code"}
title={showCode ? t("common:showDiagram") : t("common:showCode")}
>
<IconCode className="h-3.5 w-3.5" />
</button>
@@ -12,6 +12,7 @@ import {
DialogDescription,
} from "@kandev/ui/dialog";
import { IconAlertTriangle, IconExternalLink } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
import { useAppStore } from "@/components/state-provider";
import type { HealthIssue } from "@/lib/types/health";
@@ -26,6 +27,7 @@ export function HealthIndicatorButton({
onClick,
size = "icon",
}: HealthIndicatorButtonProps) {
const { t } = useTranslation();
if (!hasIssues) return null;
return (
@@ -34,10 +36,10 @@ export function HealthIndicatorButton({
<Button variant="outline" size={size} onClick={onClick} className="cursor-pointer relative">
<IconAlertTriangle className="h-4 w-4 text-amber-500" />
<span className="absolute -top-1 -right-1 h-2.5 w-2.5 rounded-full bg-amber-500 border-2 border-background" />
<span className="sr-only">Setup Issues</span>
<span className="sr-only">{t("system:setupIssues")}</span>
</Button>
</TooltipTrigger>
<TooltipContent>Setup Issues</TooltipContent>
<TooltipContent>{t("system:setupIssues")}</TooltipContent>
</Tooltip>
);
}
@@ -49,6 +51,7 @@ type HealthIssuesDialogProps = {
};
export function HealthIssuesDialog({ open, onOpenChange, issues }: HealthIssuesDialogProps) {
const { t } = useTranslation();
const router = useRouter();
const workspaceId = useAppStore((state) => state.workspaces.activeId);
@@ -60,12 +63,10 @@ export function HealthIssuesDialog({ open, onOpenChange, issues }: HealthIssuesD
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<IconAlertTriangle className="h-5 w-5 text-amber-500" />
Setup Issues
{t("system:setupIssues")}
</DialogTitle>
<DialogDescription>
{issues.length === 1
? "1 issue needs your attention"
: `${issues.length} issues need your attention`}
{t("system:issuesNeedAttention", { count: issues.length })}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 pt-2">
@@ -11,6 +11,8 @@ import {
} from "@tabler/icons-react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip";
import { formatDistanceToNow } from "date-fns";
import type { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import { useAppStore } from "@/components/state-provider";
import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint";
import { useSystemMetricsSubscription } from "@/hooks/use-system-metrics-subscription";
@@ -27,6 +29,7 @@ export function StatusSurfaceMetrics({
density,
drawerOpen,
}: StatusSurfaceMetricsProps) {
const { t } = useTranslation();
// Wire/storage name stays stable for existing user settings and API payloads.
const enabled = useAppStore((state) => state.userSettings.systemMetricsDisplay.showInTopbar);
const simplified = useAppStore((state) => state.userSettings.systemMetricsDisplay.simplified);
@@ -44,10 +47,10 @@ export function StatusSurfaceMetrics({
<section
data-testid="app-status-metrics"
className="space-y-2 py-0.5"
aria-label="System metrics"
aria-label={t("system:systemMetrics")}
>
<h3 className="text-[10px] font-semibold uppercase tracking-[0.12em] text-muted-foreground">
System metrics
{t("system:systemMetrics")}
</h3>
{!host ? (
<EmptyMetrics drawer />
@@ -66,7 +69,7 @@ export function StatusSurfaceMetrics({
<div
data-testid="app-status-metrics"
className="flex h-full max-w-[52vw] items-center overflow-hidden leading-none text-current"
aria-label="System metrics"
aria-label={t("system:systemMetrics")}
>
{!host ? (
<EmptyMetrics />
@@ -84,6 +87,7 @@ export function StatusSurfaceMetrics({
}
function EmptyMetrics({ drawer = false }: { drawer?: boolean }) {
const { t } = useTranslation();
return (
<div
className={
@@ -93,7 +97,7 @@ function EmptyMetrics({ drawer = false }: { drawer?: boolean }) {
}
>
<IconActivity className="h-3.5 w-3.5" />
<span>Metrics unavailable</span>
<span>{t("system:metricsUnavailable")}</span>
</div>
);
}
@@ -154,19 +158,23 @@ function SourceBadge({
updatedAt?: string;
showLabel: boolean;
}) {
const { t } = useTranslation();
return (
<Tooltip>
<TooltipTrigger asChild>
<span className="flex shrink-0 items-center gap-1.5 text-current" aria-label="Host metrics">
<span
className="flex shrink-0 items-center gap-1.5 text-current"
aria-label={t("system:hostMetrics")}
>
<IconServer className="size-3.5" stroke={1.6} />
{showLabel ? <span className="max-w-20 truncate">Host</span> : null}
{showLabel ? <span className="max-w-20 truncate">{t("system:host")}</span> : null}
</span>
</TooltipTrigger>
<TooltipContent>
<div className="space-y-1">
<div className="font-medium">Host</div>
<div className="font-medium">{t("system:host")}</div>
<div className="text-xs text-muted-foreground">{source.label}</div>
<div className="text-xs text-muted-foreground">{lastUpdatedText(updatedAt)}</div>
<div className="text-xs text-muted-foreground">{lastUpdatedText(t, updatedAt)}</div>
</div>
</TooltipContent>
</Tooltip>
@@ -212,16 +220,14 @@ function MetricValue({
updatedAt?: string;
simplified: boolean;
}) {
const help =
metric.id === "io_load"
? "Average number of tasks running or waiting for CPU during the last minute. Compare this value with the host's CPU core count."
: null;
const { t } = useTranslation();
const help = metric.id === "io_load" ? t("system:ioLoadHelp") : null;
return (
<Tooltip>
<TooltipTrigger asChild>
<span
className={`inline-flex shrink-0 items-center gap-1.5 tabular-nums ${metricColor(metric)}`}
aria-label={`${metricLabel(metric.id)} ${formatMetric(metric)}`}
aria-label={`${metricLabel(t, metric.id)} ${formatMetric(metric)}`}
>
{metricIcon(metric.id)}
{!simplified ? <MetricMeter metric={metric} /> : null}
@@ -232,28 +238,32 @@ function MetricValue({
</TooltipTrigger>
<TooltipContent>
<div className="space-y-1">
<div className="font-medium">{metricLabel(metric.id)}</div>
<div className="font-medium">{metricLabel(t, metric.id)}</div>
{help ? <div className="max-w-72 text-xs text-muted-foreground">{help}</div> : null}
<div className="text-xs text-muted-foreground">Host: {source.label}</div>
<div className="text-xs text-muted-foreground">
{t("system:hostWithLabel", { label: source.label })}
</div>
<div className="text-xs tabular-nums">{formatMetric(metric)}</div>
{metric.error ? (
<div className="text-xs text-muted-foreground">{metric.error}</div>
) : null}
<div className="text-xs text-muted-foreground">{lastUpdatedText(updatedAt)}</div>
<div className="text-xs text-muted-foreground">{lastUpdatedText(t, updatedAt)}</div>
</div>
</TooltipContent>
</Tooltip>
);
}
function metricLabel(id: string) {
// The map keys are wire metric ids and the `?? id` fallback returns an
// untranslated id on purpose — both are protocol. Only the values are copy.
function metricLabel(t: TFunction, id: string) {
return (
{
cpu_percent: "CPU",
memory_percent: "Memory",
disk_percent: "Disk",
cpu_temp: "CPU temperature",
io_load: "System load (1 min)",
cpu_percent: t("system:metricCpuPercent"),
memory_percent: t("system:metricMemoryPercent"),
disk_percent: t("system:metricDiskPercent"),
cpu_temp: t("system:metricCpuTemp"),
io_load: t("system:metricIoLoad"),
}[id] ?? id
);
}
@@ -299,9 +309,11 @@ function metricColor(metric: SystemMetricSample) {
return "text-current";
}
function lastUpdatedText(updatedAt?: string) {
if (!updatedAt) return "Last update unknown";
function lastUpdatedText(t: TFunction, updatedAt?: string) {
if (!updatedAt) return t("system:lastUpdateUnknown");
const date = new Date(updatedAt);
if (Number.isNaN(date.getTime())) return "Last update unknown";
return `Updated ${formatDistanceToNow(date, { addSuffix: true })}`;
if (Number.isNaN(date.getTime())) return t("system:lastUpdateUnknown");
return t("system:updatedRelative", {
relative: formatDistanceToNow(date, { addSuffix: true }),
});
}
@@ -15,6 +15,7 @@ import {
IconChevronsLeft,
IconChevronsRight,
} from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
interface DataTablePaginationProps<TData> {
table: Table<TData>;
@@ -71,6 +72,7 @@ function PageButtons({
}
export function DataTablePagination<TData>({ table }: DataTablePaginationProps<TData>) {
const { t } = useTranslation();
const currentPage = table.getState().pagination.pageIndex + 1;
const pageCount = table.getPageCount();
const pages = getPageNumbers(currentPage, pageCount);
@@ -79,17 +81,16 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredRowModel().rows.length > 0 && (
<>
Showing {pagination.pageIndex * pagination.pageSize + 1} to{" "}
{Math.min((pagination.pageIndex + 1) * pagination.pageSize, table.getRowCount())} of{" "}
{table.getRowCount()} results
</>
)}
{table.getFilteredRowModel().rows.length > 0 &&
t("common:showingRange", {
from: pagination.pageIndex * pagination.pageSize + 1,
to: Math.min((pagination.pageIndex + 1) * pagination.pageSize, table.getRowCount()),
count: table.getRowCount(),
})}
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium whitespace-nowrap">Rows per page</p>
<p className="text-sm font-medium whitespace-nowrap">{t("common:rowsPerPage")}</p>
<Select
value={`${pagination.pageSize}`}
onValueChange={(value) => {
@@ -118,7 +119,7 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<span className="sr-only">{t("common:goToFirstPage")}</span>
<IconChevronsLeft className="h-4 w-4" />
</Button>
</PaginationItem>
@@ -130,7 +131,7 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<span className="sr-only">{t("common:goToPreviousPage")}</span>
<IconChevronLeft className="h-4 w-4" />
</Button>
</PaginationItem>
@@ -147,7 +148,7 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<span className="sr-only">{t("common:goToNextPage")}</span>
<IconChevronRight className="h-4 w-4" />
</Button>
</PaginationItem>
@@ -159,7 +160,7 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<span className="sr-only">{t("common:goToLastPage")}</span>
<IconChevronsRight className="h-4 w-4" />
</Button>
</PaginationItem>
+4 -2
View File
@@ -10,6 +10,7 @@ import {
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@kandev/ui/table";
import { DataTablePagination } from "./data-table-pagination";
import { useTranslation } from "react-i18next";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
@@ -32,6 +33,7 @@ export function DataTable<TData, TValue>({
isLoading = false,
onRowClick,
}: DataTableProps<TData, TValue>) {
const { t } = useTranslation();
// eslint-disable-next-line react-hooks/incompatible-library -- TanStack Table's API is designed this way
const table = useReactTable({
data,
@@ -74,7 +76,7 @@ export function DataTable<TData, TValue>({
return (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
Loading...
{t("common:loadingEllipsis")}
</TableCell>
</TableRow>
);
@@ -98,7 +100,7 @@ export function DataTable<TData, TValue>({
return (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
{t("common:noResults")}
</TableCell>
</TableRow>
);
+30
View File
@@ -1736,4 +1736,34 @@ export const i18nGuardFiles = [
"components/task-create-dialog-source-mode.tsx",
"components/task-create-dialog-workspace-repo-chips.tsx",
"components/task-preview-panel.tsx",
// The last batch of live user-facing surfaces before `app/office`: the session
// prepare panel, the onboarding agents step, the host system-metrics readout
// and system-health issues dialog, the shared panel search bar, the mermaid
// diagram block, the settings agent card, the two release-notes surfaces, and
// the shared data-table pair.
//
// Listed FILE BY FILE rather than by directory. Every one of these directories
// still holds un-migrated files — `components/settings/` alone has hundreds —
// so a `components/settings/*.tsx` glob would claim work this PR did not do.
//
// Deliberately NOT migrated in these files, because each is protocol or data
// rather than copy: the `%`/`s`/`ms`/`m` unit suffixes and the `-` empty marker
// in the metrics readout, the `{workspaceId}` URL template token in the health
// dialog, the `"ellipsis"` page sentinel in the data-table pagination, the
// `v` version prefix and the `0 / 0` match counter, the `[-]`/`[+]` toggle
// glyphs in the prepare panel, and every mermaid theme/security identifier and
// CSS value in the diagram block. `metricLabel`'s wire ids stay untranslated
// on the `?? id` fallback path for the same reason.
"components/onboarding/step-agents.tsx",
"components/release-notes/release-notes-button.tsx",
"components/release-notes/release-notes-dialog.tsx",
"components/search/panel-search-bar.tsx",
"components/session/prepare-progress.tsx",
"components/settings/agent-card.tsx",
"components/shared/mermaid-block.tsx",
"components/system-health/health-indicator.tsx",
"components/system-metrics/status-surface-metrics.tsx",
"components/ui/data-table-pagination.tsx",
"components/ui/data-table.tsx",
];
+33
View File
@@ -35,6 +35,7 @@
"cliModeYourPromptWillBe": "CLI mode — your prompt will be auto-injected into the terminal",
"cliPassthrough": "CLI passthrough",
"close": "Close",
"closeEsc": "Close (Esc)",
"commandAgentsSettingsKeywords": "agents, agent settings, agent profiles, installed agents, claude",
"commandArchiveTaskKeywords": "archive, archive task, archive current task, close task, finish task, put away",
"commandConfigChatKeywords": "config chat, config mode, configure kandev, workflow configuration, mcp configuration",
@@ -101,6 +102,7 @@
"discardLocalChanges": "Discard local changes?",
"discardLocalChangesAtPathDescription": "Starting this task will permanently discard the uncommitted changes in your local clone at {{repoPath}}. Back up anything you want to keep before continuing.",
"discardLocalChangesDescription": "Starting this task will permanently discard the uncommitted changes in your local clone. Back up anything you want to keep before continuing.",
"discoveringAgents": "Discovering agents...",
"doNotShowThisAgain": "Do not show this again",
"edit": "Edit",
"editTask": "Edit task",
@@ -108,6 +110,7 @@
"enhancePromptWithAi": "Enhance prompt with AI",
"enterAValue": "Enter a value...",
"enterValue": "Enter value...",
"error": "Error",
"executors": "Executors",
"externalMcp": "External MCP",
"failedToCreateFolder": "Failed to create folder",
@@ -115,6 +118,7 @@
"failedToLoadDirectory": "Failed to load directory",
"failedToLoadRoutine": "Failed to load routine",
"failedToLoadSetup": "Failed to load setup",
"failedToRenderDiagram": "Failed to render diagram",
"featureRequest": "Feature request",
"files": "Files",
"folderName": "Folder name",
@@ -123,6 +127,10 @@
"getStarted": "Get Started",
"githubCliNotAuthenticated": "GitHub CLI not authenticated",
"gitScopeSkipped": "Skipped {{scope}} because a child repository operation failed ({{failedScopes}}).",
"goToFirstPage": "Go to first page",
"goToLastPage": "Go to last page",
"goToNextPage": "Go to next page",
"goToPreviousPage": "Go to previous page",
"hAgo": "{{diffHr}}h ago",
"hideAdvancedOptions": "Hide advanced options",
"howLogContextWorks": "How log context works",
@@ -131,6 +139,7 @@
"improveKandevFeaturePlaceholder": "E.g. Add a keyboard shortcut to mark a task as Done from the task detail view, and show the shortcut in the task action menu...",
"improveKandevIssuePlaceholder": "Describe the problem or request you want published. The agent will ask for any details required by the matching GitHub issue template...",
"includeRecentBackendAndBrowserLogs": "Include recent backend & browser logs as context for the agent",
"installed": "Installed",
"integrations": "Integrations",
"justNow": "just now",
"kandevAllowsEveryAgentPermissionRequest": "Kandev allows every agent permission request without prompting you.",
@@ -138,6 +147,7 @@
"kandevIsOpenSourceAndYou": "Kandev is open source, and you can help make it better.",
"kandevKeepsASmallInMemory": "Kandev keeps a small in-memory ring buffer of the most recent backend logs and browser console events. When enabled, those logs are written to a temporary folder on your machine, and the file paths are appended to the task description so the agent can read them while investigating.",
"lastRefreshFailed": "Last refresh failed: {{error}}",
"latestReleaseNotes": "Latest release notes",
"loading": "Loading…",
"loadingEllipsis": "Loading...",
"loadingFolder": "Loading folder…",
@@ -148,6 +158,7 @@
"loadingTask": "Loading task…",
"loadingWorkflowTemplates": "Loading workflow templates...",
"mAgo": "{{diffMin}}m ago",
"matchCase": "Match case",
"mode": "Mode",
"model": "Model",
"multiRepoUnavailableLocal": "Multi-repo tasks are unavailable on Local until its initial launch path can project sibling repositories.",
@@ -157,14 +168,23 @@
"navigateBetweenPagesSearchTasksTrigger": "Navigate between pages, search tasks, trigger git operations, and manage panels — all without leaving the keyboard. Context-aware commands appear based on the active page.",
"newFolder": "New folder",
"newFolderName": "New folder name",
"newReleases_one": "{{count}} new release",
"newReleases_other": "{{count}} new releases",
"next": "Next",
"nextEnter": "Next (Enter)",
"noAuth": "No auth",
"noCliClientsInstalledYetInstall": "No CLI clients installed yet. Install Claude / Codex / OpenCode / Amp to enable this picker.",
"noCommandsFound": "No commands found.",
"noContentMatchesFound": "No content matches found.",
"noFilesFound": "No files found.",
"noFoldersHerePickThisOne": "No folders here — pick this one or go up",
"noResults": "No results.",
"notInstalled": "Not installed",
"notYetInstalled": "Not yet installed",
"officeIsNotEnabledForThis": "Office is not enabled for this runtime.",
"officeRouteNotPortedYet": "This Office route is handled by the SPA shell, but its dedicated client page is still being ported: <1>{{pathname}}</1>",
"onboardingAgentsAutoApproveWarning": "<0>Careful:</0> The default Agent Profiles run with Auto Approve enabled (YOLO mode).",
"onboardingAgentsHelp": "Expand an agent to configure its model and permissions. Changes are saved when you proceed. You can also add custom TUI agents later in Settings > Agents.",
"onboardingStepAgentsDescription": "Manage discovered agents and install new ones.",
"onboardingStepAgentsTitle": "AI Agents",
"onboardingStepCommandPanelDescription": "Quick access to actions from anywhere with a keyboard shortcut.",
@@ -180,6 +200,7 @@
"optionalTheRepositoryTheAgentWorks": "Optional — the repository the agent works in.",
"panels": "Panels",
"parentDirectory": "Parent directory",
"pending": "Pending",
"permitTheCliToUploadCode": "Permit the CLI to upload code for cloud indexing (auggie / similar).",
"pickACliClient": "Pick a CLI client",
"pickACliClientToLoad": "Pick a CLI client to load its available models and modes.",
@@ -199,12 +220,16 @@
"presetIconTool": "Tool",
"pressCmdKToOpenItAnytime": "<0>Press</0><1></1><2>to open it anytime</2>",
"pressEnterToConfirm": "Press Enter to confirm",
"previousShiftEnter": "Previous (Shift+Enter)",
"probing": "Probing",
"profileName": "Profile name",
"prompts": "Prompts",
"refreshAriaLabel": "Refresh {{label}}",
"refreshBranchesGitFetch": "Refresh branches (git fetch)",
"refreshingLabel": "Refreshing {{label}}...",
"refreshLabelLastFetched": "Refresh {{label}} (last fetched {{time}})",
"regularExpression": "Regular expression",
"releasedOn": "Released on {{date}}",
"reload": "Reload",
"reloadTheApplicationToTryAgain": "Reload the application to try again.",
"rename": "Rename",
@@ -224,6 +249,7 @@
"results": "Results",
"retryPreview": "Retry preview",
"reviewSubmoduleBoundary": "Submodule: {{scope}}",
"rowsPerPage": "Rows per page",
"runsFormatTypecheckTestAndLint": "Runs format, typecheck, test, and lint across the monorepo.",
"runtimeDocker": "Docker",
"runtimeDockerDescription": "Containerized execution for full isolation and reproducibility.",
@@ -239,6 +265,7 @@
"scopeFiles": "Files",
"scopeTitleWithShortcut": "{{label}} ({{shortcut}})",
"scratchWorkspace": "scratch workspace",
"searchEllipsis": "Search…",
"searchFailedEditTheQueryOr": "Search failed. Edit the query or reopen search to retry.",
"searchForFiles": "Search for files...",
"searchForTasks": "Search for tasks...",
@@ -250,6 +277,10 @@
"shell": "Shell",
"shellCommandPlusSlashCommandSkills": "Shell command you can run in the secondary instance, plus slash-command <2>skills</2> you can ask the agent to run during the workflow.",
"showAdvancedOptions": "Show advanced options",
"showCode": "Show code",
"showDiagram": "Show diagram",
"showingRange_one": "Showing {{from}} to {{to}} of {{count}} result",
"showingRange_other": "Showing {{from}} to {{to}} of {{count}} results",
"skip": "Skip",
"stagesAndCommitsChangesUsingConventional": "Stages and commits changes using Conventional Commits.",
"status": "Status",
@@ -277,7 +308,9 @@
"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.",
"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.",
+3
View File
@@ -28,6 +28,7 @@
"appriseUrlRequired": "At least one Apprise service URL is required.",
"archiveConfirmation": "Archive Confirmation",
"arrangeSplit": "Arrange split",
"autoApprove": "Auto-approve",
"autoInstallIfNotFound": "Auto-install if not found",
"autoStart": "Auto-start",
"autoStartLanguageServer": "Auto-start {{language}} language server",
@@ -350,6 +351,8 @@
"makesAgentctlValuesAvailableToPlugins": "Makes agentctl values available to plugins and other consumers. The built-in status bar remains host-only.",
"manageApiKeysAndCredentialsSecrets": "Manage API keys and credentials. Secrets are encrypted at rest and injected into agent environments via executor profile env vars.",
"globalScope": "Global",
"modelWithValue": "Model: {{model}}",
"temperatureWithValue": "Temperature: {{temperature}}",
"workspaceScope": "Workspace",
"workspaceSecretsDescription": "Manage secrets available to repositories in this workspace. Shared agent and executor profiles can use Global secrets only.",
"revealSecretNamed": "Reveal secret {{name}}",
+16
View File
@@ -131,6 +131,9 @@
"healthSystemChecks": "System checks",
"healthTitle": "Health",
"healthWhatsMonitored": "What's monitored",
"host": "Host",
"hostMetrics": "Host metrics",
"hostWithLabel": "Host: {{label}}",
"inviteCopied": "Invite link copied",
"inviteCopy": "Copy invite link",
"inviteCreate": "Create invite link",
@@ -143,10 +146,14 @@
"inviteReadyTitle": "Invite link ready",
"inviteTitle": "Invite a user",
"inviteUrlLabel": "Invite link",
"ioLoadHelp": "Average number of tasks running or waiting for CPU during the last minute. Compare this value with the host's CPU core count.",
"issuesNeedAttention_one": "{{count}} issue needs your attention",
"issuesNeedAttention_other": "{{count}} issues need your attention",
"jobStateDone": "Done",
"jobStateFailed": "Failed",
"jobStateQueued": "Queued",
"jobStateRunning": "Running",
"lastUpdateUnknown": "Last update unknown",
"licensesCount": "{{visible}} of {{total}}",
"licensesEmpty": "No entries match the filter.",
"licensesFilterPlaceholder": "Filter by name, license, or ecosystem...",
@@ -176,6 +183,12 @@
"messageQueueUnlimited": "Unlimited",
"messageQueueUnlimitedHelp": "Set 0 for unlimited. A positive whole number rejects new messages after that many are pending in one session.",
"messageQueueValidation": "Enter a whole number of 0 or greater.",
"metricCpuPercent": "CPU",
"metricCpuTemp": "CPU temperature",
"metricDiskPercent": "Disk",
"metricIoLoad": "System load (1 min)",
"metricMemoryPercent": "Memory",
"metricsUnavailable": "Metrics unavailable",
"navAbout": "About",
"navBackups": "Backups",
"navDatabase": "Database",
@@ -226,6 +239,7 @@
"selfUpdateStartingUnknown": "Starting update to the new version…",
"selfUpdateTimedOut": "Update is taking longer than expected. Refresh to check the current version.",
"settingsRouteNotPorted": "This settings route is handled by the SPA shell, but its dedicated client page is still being ported: <1>{{pathname}}</1>",
"setupIssues": "Setup Issues",
"statusPageDescription": "Health checks, disk usage, and version summary.",
"storageAcknowledgeDaemon": "Acknowledge daemon",
"storageAcknowledgeDedicatedFirst": "Acknowledge a dedicated Docker daemon first.",
@@ -395,11 +409,13 @@
"storageWorkspacesDescription": "Reclaim resources that Kandev can positively identify as no longer in use.",
"storageWorkspacesDetail": "{{reclaimable}} reclaimable after the grace period · {{active}} active",
"storageWorkspacesTitle": "Workspaces and containers",
"systemMetrics": "System metrics",
"uiStateHelp": "Clears layout and UI preferences saved in your browser (panel sizes, pinned tasks, expanded groups, etc.). Use this if the app looks broken, panels are stuck, or the layout feels wrong. Your tasks, sessions, and server data are not affected - only the per-browser UI state. The page reloads after the reset.",
"uiStateReset": "Reset",
"uiStateResetLabel": "Reset browser layout",
"uiStateTitle": "UI state",
"updateAvailable": "Update available",
"updatedRelative": "Updated {{relative}}",
"updatesApply": "Apply update",
"updatesApplyConfirmDesktop": "Kandev will install {{version}} and restart the desktop app.",
"updatesApplyConfirmService": "Kandev will update to {{version}}, reinstall the user service, and restart it.",
+16
View File
@@ -141,6 +141,7 @@
"blocks": "Blocks",
"bothVersionsAreEmpty": "(both versions are empty)",
"branch": "Branch",
"branchBasedOn": "Branch <1>{{branch}}</1>, based on <3>{{base}}</3>",
"branchComparisonAndTaskGitCredentials": "Branch comparison and task Git credentials.",
"branchDetails": "Branch details",
"branchIsNoLongerAvailable": "Branch is no longer available",
@@ -381,7 +382,11 @@
"enterRepositoryUrl": "Enter a GitHub, GitLab, or Azure DevOps repository URL.",
"environment": "Environment {{label}}",
"environmentPreparationFailed": "Environment preparation failed",
"environmentPrepared": "Environment prepared",
"environmentPreparedOnFreshSandbox": "Environment prepared on a fresh sandbox",
"environmentPreparedWithWarnings": "Environment prepared with warnings",
"environmentSetupFailed": "Environment setup failed",
"environmentSetupFinishedWithErrors": "Environment setup finished with errors",
"error": "Error",
"errorData": "Error Data",
"executor2": "Executor",
@@ -528,13 +533,16 @@
"hiddenMessagesInPreview_other": "… {{count}} more messages hidden in this preview (still included in the share) …",
"hideCommandOutput": "Hide command output",
"hideDebugInfo": "Hide Debug Info",
"hideDetails": "Hide details",
"hideErrorDetails": "Hide error details: {{displayLabel}}",
"hidePreparationDetails": "Hide preparation details",
"hidePreviousAgentError": "Hide previous agent error",
"hideRightPanel": "Hide right panel",
"hideRightPanels": "Hide right panels",
"hideSidebar": "Hide sidebar",
"hideTheCommentListTheComments": "Hide the comment list. The comments stay queued and will still be sent next time you submit (here or from the chat box).",
"host": "Host",
"isolatedWorktreeAt": "Isolated worktree at <1>{{path}}</1>",
"iUnderstandAnyUncommittedChangesWill": "I understand any uncommitted changes will be lost.",
"image": "Image",
"imagePreview": "Image preview",
@@ -779,6 +787,7 @@
"noWorkspacesFound": "No workspaces found.",
"none": "None",
"notConfiguredForThisExecutor": "Not configured for this executor",
"onBranch": "On branch <1>{{branch}}</1>",
"onDisk": "on disk",
"ontoBranch": "onto {{branch}}",
"openBrowserPreview": "Open browser preview",
@@ -842,7 +851,9 @@
"portNumber": "Port number",
"position": "Position {{positionLabel}}",
"prChanges": "PR Changes",
"preparingEllipsis": "Preparing...",
"preparingEnvironment": "Preparing environment",
"preparingEnvironmentEllipsis": "Preparing environment...",
"preparingWorkspace": "Preparing workspace...",
"preparingWorkspace2": "Preparing workspace…",
"presets": "Presets",
@@ -1007,6 +1018,7 @@
"runInTerminal": "Run in terminal",
"runInTheParentSMaterialized": "Run in the parent's materialized workspace",
"runInTheParentSWorktree": "Run in the parent's worktree ({{worktreeBranch}})",
"runRepositorySetupScript": "Run repository setup script",
"runScript": "Run script",
"running": "running",
"sameBranchAsCurrentSession": "Same branch as current session",
@@ -1026,6 +1038,8 @@
"savedLayouts": "Saved Layouts",
"saving": "Saving...",
"scriptExecutionMetadataUnavailable": "Script Execution (metadata unavailable)",
"scriptExitedWithCode": "Script exited with code {{code}}",
"scriptFailed": "Script failed",
"scripts": "Scripts",
"scrollToActiveSession": "Scroll to active session",
"scrollToLastPrompt": "Scroll to last prompt",
@@ -1116,6 +1130,7 @@
"showMessageMetadata": "Show message metadata",
"showOlderSessions_one": "Show {{count}} older session",
"showOlderSessions_other": "Show {{count}} older sessions",
"showPreparationDetails": "Show preparation details",
"showRawText": "Show raw text",
"showRightPanel": "Show right panel",
"showSidebar": "Show sidebar",
@@ -1400,6 +1415,7 @@
"working": "Working...",
"working3": "Working",
"working4": "Working…",
"workingIn": "Working in <1>{{path}}</1>",
"workingInBackground": "Working in background",
"workspaceActions": "Workspace actions",
"workspaceCount_one": "{{count}} workspace",
+33
View File
@@ -35,6 +35,7 @@
"cliModeYourPromptWillBe": "ĆĹĨ ḿōďē — ŷōũŕ ƥŕōḿƥţ ŵĩĺĺ ƀē àũţō-ĩńĵēćţēď ĩńţō ţĥē ţēŕḿĩńàĺ",
"cliPassthrough": "ĆĹĨ ƥàśśţĥŕōũĝĥ",
"close": "Ćĺōśē",
"closeEsc": "Ćĺōśē (Ēść)",
"commandAgentsSettingsKeywords": "àĝēńţś, àĝēńţ śēţţĩńĝś, àĝēńţ ƥŕōƒĩĺēś, ĩńśţàĺĺēď àĝēńţś, ćĺàũďē",
"commandArchiveTaskKeywords": "àŕćĥĩvē, àŕćĥĩvē ţàśķ, àŕćĥĩvē ćũŕŕēńţ ţàśķ, ćĺōśē ţàśķ, ƒĩńĩśĥ ţàśķ, ƥũţ àŵàŷ",
"commandConfigChatKeywords": "ćōńƒĩĝ ćĥàţ, ćōńƒĩĝ ḿōďē, ćōńƒĩĝũŕē ķàńďēv, ŵōŕķƒĺōŵ ćōńƒĩĝũŕàţĩōń, ḿćƥ ćōńƒĩĝũŕàţĩōń",
@@ -101,6 +102,7 @@
"discardLocalChanges": "Ďĩśćàŕď ĺōćàĺ ćĥàńĝēś?",
"discardLocalChangesAtPathDescription": "Śţàŕţĩńĝ ţĥĩś ţàśķ ŵĩĺĺ ƥēŕḿàńēńţĺŷ ďĩśćàŕď ţĥē ũńćōḿḿĩţţēď ćĥàńĝēś ĩń ŷōũŕ ĺōćàĺ ćĺōńē àţ {{repoPath}}. Ɓàćķ ũƥ àńŷţĥĩńĝ ŷōũ ŵàńţ ţō ķēēƥ ƀēƒōŕē ćōńţĩńũĩńĝ.",
"discardLocalChangesDescription": "Śţàŕţĩńĝ ţĥĩś ţàśķ ŵĩĺĺ ƥēŕḿàńēńţĺŷ ďĩśćàŕď ţĥē ũńćōḿḿĩţţēď ćĥàńĝēś ĩń ŷōũŕ ĺōćàĺ ćĺōńē. Ɓàćķ ũƥ àńŷţĥĩńĝ ŷōũ ŵàńţ ţō ķēēƥ ƀēƒōŕē ćōńţĩńũĩńĝ.",
"discoveringAgents": "Ďĩśćōvēŕĩńĝ àĝēńţś...",
"doNotShowThisAgain": "Ďō ńōţ śĥōŵ ţĥĩś àĝàĩń",
"edit": "Ēďĩţ",
"editTask": "Ēďĩţ ţàśķ",
@@ -108,6 +110,7 @@
"enhancePromptWithAi": "Ēńĥàńćē ƥŕōḿƥţ ŵĩţĥ ÀĨ",
"enterAValue": "Ēńţēŕ à vàĺũē...",
"enterValue": "Ēńţēŕ vàĺũē...",
"error": "Ēŕŕōŕ",
"executors": "Ēxēćũţōŕś",
"externalMcp": "Ēxţēŕńàĺ ḾĆƤ",
"failedToCreateFolder": "Ƒàĩĺēď ţō ćŕēàţē ƒōĺďēŕ",
@@ -115,6 +118,7 @@
"failedToLoadDirectory": "Ƒàĩĺēď ţō ĺōàď ďĩŕēćţōŕŷ",
"failedToLoadRoutine": "Ƒàĩĺēď ţō ĺōàď ŕōũţĩńē",
"failedToLoadSetup": "Ƒàĩĺēď ţō ĺōàď śēţũƥ",
"failedToRenderDiagram": "Ƒàĩĺēď ţō ŕēńďēŕ ďĩàĝŕàḿ",
"featureRequest": "Ƒēàţũŕē ŕēqũēśţ",
"files": "Ƒĩĺēś",
"folderName": "Ƒōĺďēŕ ńàḿē",
@@ -123,6 +127,10 @@
"getStarted": "Ĝēţ Śţàŕţēď",
"githubCliNotAuthenticated": "ĜĩţĤũƀ ĆĹĨ ńōţ àũţĥēńţĩćàţēď",
"gitScopeSkipped": "Śķĩƥƥēď {{scope}} ƀēćàũśē à ćĥĩĺď ŕēƥōśĩţōŕŷ ōƥēŕàţĩōń ƒàĩĺēď ({{failedScopes}}).",
"goToFirstPage": "Ĝō ţō ƒĩŕśţ ƥàĝē",
"goToLastPage": "Ĝō ţō ĺàśţ ƥàĝē",
"goToNextPage": "Ĝō ţō ńēxţ ƥàĝē",
"goToPreviousPage": "Ĝō ţō ƥŕēvĩōũś ƥàĝē",
"hAgo": "{{diffHr}}ĥ àĝō",
"hideAdvancedOptions": "Ĥĩďē àďvàńćēď ōƥţĩōńś",
"howLogContextWorks": "Ĥōŵ ĺōĝ ćōńţēxţ ŵōŕķś",
@@ -131,6 +139,7 @@
"improveKandevFeaturePlaceholder": "Ē.ĝ. Àďď à ķēŷƀōàŕď śĥōŕţćũţ ţō ḿàŕķ à ţàśķ àś Ďōńē ƒŕōḿ ţĥē ţàśķ ďēţàĩĺ vĩēŵ, àńď śĥōŵ ţĥē śĥōŕţćũţ ĩń ţĥē ţàśķ àćţĩōń ḿēńũ...",
"improveKandevIssuePlaceholder": "Ďēśćŕĩƀē ţĥē ƥŕōƀĺēḿ ōŕ ŕēqũēśţ ŷōũ ŵàńţ ƥũƀĺĩśĥēď. Ţĥē àĝēńţ ŵĩĺĺ àśķ ƒōŕ àńŷ ďēţàĩĺś ŕēqũĩŕēď ƀŷ ţĥē ḿàţćĥĩńĝ ĜĩţĤũƀ ĩśśũē ţēḿƥĺàţē...",
"includeRecentBackendAndBrowserLogs": "Ĩńćĺũďē ŕēćēńţ ƀàćķēńď & ƀŕōŵśēŕ ĺōĝś àś ćōńţēxţ ƒōŕ ţĥē àĝēńţ",
"installed": "Ĩńśţàĺĺēď",
"integrations": "Ĩńţēĝŕàţĩōńś",
"justNow": "ĵũśţ ńōŵ",
"kandevAllowsEveryAgentPermissionRequest": "Ķàńďēv àĺĺōŵś ēvēŕŷ àĝēńţ ƥēŕḿĩśśĩōń ŕēqũēśţ ŵĩţĥōũţ ƥŕōḿƥţĩńĝ ŷōũ.",
@@ -138,6 +147,7 @@
"kandevIsOpenSourceAndYou": "Ķàńďēv ĩś ōƥēń śōũŕćē, àńď ŷōũ ćàń ĥēĺƥ ḿàķē ĩţ ƀēţţēŕ.",
"kandevKeepsASmallInMemory": "Ķàńďēv ķēēƥś à śḿàĺĺ ĩń-ḿēḿōŕŷ ŕĩńĝ ƀũƒƒēŕ ōƒ ţĥē ḿōśţ ŕēćēńţ ƀàćķēńď ĺōĝś àńď ƀŕōŵśēŕ ćōńśōĺē ēvēńţś. Ŵĥēń ēńàƀĺēď, ţĥōśē ĺōĝś àŕē ŵŕĩţţēń ţō à ţēḿƥōŕàŕŷ ƒōĺďēŕ ōń ŷōũŕ ḿàćĥĩńē, àńď ţĥē ƒĩĺē ƥàţĥś àŕē àƥƥēńďēď ţō ţĥē ţàśķ ďēśćŕĩƥţĩōń śō ţĥē àĝēńţ ćàń ŕēàď ţĥēḿ ŵĥĩĺē ĩńvēśţĩĝàţĩńĝ.",
"lastRefreshFailed": "Ĺàśţ ŕēƒŕēśĥ ƒàĩĺēď: {{error}}",
"latestReleaseNotes": "Ĺàţēśţ ŕēĺēàśē ńōţēś",
"loading": "Ĺōàďĩńĝ…",
"loadingEllipsis": "Ĺōàďĩńĝ...",
"loadingFolder": "Ĺōàďĩńĝ ƒōĺďēŕ…",
@@ -148,6 +158,7 @@
"loadingTask": "Ĺōàďĩńĝ ţàśķ…",
"loadingWorkflowTemplates": "Ĺōàďĩńĝ ŵōŕķƒĺōŵ ţēḿƥĺàţēś...",
"mAgo": "{{diffMin}}ḿ àĝō",
"matchCase": "Ḿàţćĥ ćàśē",
"mode": "Ḿōďē",
"model": "Ḿōďēĺ",
"multiRepoUnavailableLocal": "Ḿũĺţĩ-ŕēƥō ţàśķś àŕē ũńàvàĩĺàƀĺē ōń Ĺōćàĺ ũńţĩĺ ĩţś ĩńĩţĩàĺ ĺàũńćĥ ƥàţĥ ćàń ƥŕōĵēćţ śĩƀĺĩńĝ ŕēƥōśĩţōŕĩēś.",
@@ -157,14 +168,23 @@
"navigateBetweenPagesSearchTasksTrigger": "Ńàvĩĝàţē ƀēţŵēēń ƥàĝēś, śēàŕćĥ ţàśķś, ţŕĩĝĝēŕ ĝĩţ ōƥēŕàţĩōńś, àńď ḿàńàĝē ƥàńēĺś — àĺĺ ŵĩţĥōũţ ĺēàvĩńĝ ţĥē ķēŷƀōàŕď. Ćōńţēxţ-àŵàŕē ćōḿḿàńďś àƥƥēàŕ ƀàśēď ōń ţĥē àćţĩvē ƥàĝē.",
"newFolder": "Ńēŵ ƒōĺďēŕ",
"newFolderName": "Ńēŵ ƒōĺďēŕ ńàḿē",
"newReleases_one": "{{count}} ńēŵ ŕēĺēàśē",
"newReleases_other": "{{count}} ńēŵ ŕēĺēàśēś",
"next": "Ńēxţ",
"nextEnter": "Ńēxţ (Ēńţēŕ)",
"noAuth": "Ńō àũţĥ",
"noCliClientsInstalledYetInstall": "Ńō ĆĹĨ ćĺĩēńţś ĩńśţàĺĺēď ŷēţ. Ĩńśţàĺĺ Ćĺàũďē / Ćōďēx / ŌƥēńĆōďē / Àḿƥ ţō ēńàƀĺē ţĥĩś ƥĩćķēŕ.",
"noCommandsFound": "Ńō ćōḿḿàńďś ƒōũńď.",
"noContentMatchesFound": "Ńō ćōńţēńţ ḿàţćĥēś ƒōũńď.",
"noFilesFound": "Ńō ƒĩĺēś ƒōũńď.",
"noFoldersHerePickThisOne": "Ńō ƒōĺďēŕś ĥēŕē — ƥĩćķ ţĥĩś ōńē ōŕ ĝō ũƥ",
"noResults": "Ńō ŕēśũĺţś.",
"notInstalled": "Ńōţ ĩńśţàĺĺēď",
"notYetInstalled": "Ńōţ ŷēţ ĩńśţàĺĺēď",
"officeIsNotEnabledForThis": "Ōƒƒĩćē ĩś ńōţ ēńàƀĺēď ƒōŕ ţĥĩś ŕũńţĩḿē.",
"officeRouteNotPortedYet": "Ţĥĩś Ōƒƒĩćē ŕōũţē ĩś ĥàńďĺēď ƀŷ ţĥē ŚƤÀ śĥēĺĺ, ƀũţ ĩţś ďēďĩćàţēď ćĺĩēńţ ƥàĝē ĩś śţĩĺĺ ƀēĩńĝ ƥōŕţēď: <1>{{pathname}}</1>",
"onboardingAgentsAutoApproveWarning": "<0>Ćàŕēƒũĺ:</0> Ţĥē ďēƒàũĺţ Àĝēńţ Ƥŕōƒĩĺēś ŕũń ŵĩţĥ Àũţō Àƥƥŕōvē ēńàƀĺēď (ŶŌĹŌ ḿōďē).",
"onboardingAgentsHelp": "Ēxƥàńď àń àĝēńţ ţō ćōńƒĩĝũŕē ĩţś ḿōďēĺ àńď ƥēŕḿĩśśĩōńś. Ćĥàńĝēś àŕē śàvēď ŵĥēń ŷōũ ƥŕōćēēď. Ŷōũ ćàń àĺśō àďď ćũśţōḿ ŢŨĨ àĝēńţś ĺàţēŕ ĩń Śēţţĩńĝś > Àĝēńţś.",
"onboardingStepAgentsDescription": "Ḿàńàĝē ďĩśćōvēŕēď àĝēńţś àńď ĩńśţàĺĺ ńēŵ ōńēś.",
"onboardingStepAgentsTitle": "ÀĨ Àĝēńţś",
"onboardingStepCommandPanelDescription": "Qũĩćķ àććēśś ţō àćţĩōńś ƒŕōḿ àńŷŵĥēŕē ŵĩţĥ à ķēŷƀōàŕď śĥōŕţćũţ.",
@@ -180,6 +200,7 @@
"optionalTheRepositoryTheAgentWorks": "Ōƥţĩōńàĺ — ţĥē ŕēƥōśĩţōŕŷ ţĥē àĝēńţ ŵōŕķś ĩń.",
"panels": "Ƥàńēĺś",
"parentDirectory": "Ƥàŕēńţ ďĩŕēćţōŕŷ",
"pending": "Ƥēńďĩńĝ",
"permitTheCliToUploadCode": "Ƥēŕḿĩţ ţĥē ĆĹĨ ţō ũƥĺōàď ćōďē ƒōŕ ćĺōũď ĩńďēxĩńĝ (àũĝĝĩē / śĩḿĩĺàŕ).",
"pickACliClient": "Ƥĩćķ à ĆĹĨ ćĺĩēńţ",
"pickACliClientToLoad": "Ƥĩćķ à ĆĹĨ ćĺĩēńţ ţō ĺōàď ĩţś àvàĩĺàƀĺē ḿōďēĺś àńď ḿōďēś.",
@@ -199,12 +220,16 @@
"presetIconTool": "Ţōōĺ",
"pressCmdKToOpenItAnytime": "<0>Ƥŕēśś</0><1></1><2>ţō ōƥēń ĩţ àńŷţĩḿē</2>",
"pressEnterToConfirm": "Ƥŕēśś Ēńţēŕ ţō ćōńƒĩŕḿ",
"previousShiftEnter": "Ƥŕēvĩōũś (Śĥĩƒţ+Ēńţēŕ)",
"probing": "Ƥŕōƀĩńĝ",
"profileName": "Ƥŕōƒĩĺē ńàḿē",
"prompts": "Ƥŕōḿƥţś",
"refreshAriaLabel": "Ŕēƒŕēśĥ {{label}}",
"refreshBranchesGitFetch": "Ŕēƒŕēśĥ ƀŕàńćĥēś (ĝĩţ ƒēţćĥ)",
"refreshingLabel": "Ŕēƒŕēśĥĩńĝ {{label}}...",
"refreshLabelLastFetched": "Ŕēƒŕēśĥ {{label}} (ĺàśţ ƒēţćĥēď {{time}})",
"regularExpression": "Ŕēĝũĺàŕ ēxƥŕēśśĩōń",
"releasedOn": "Ŕēĺēàśēď ōń {{date}}",
"reload": "Ŕēĺōàď",
"reloadTheApplicationToTryAgain": "Ŕēĺōàď ţĥē àƥƥĺĩćàţĩōń ţō ţŕŷ àĝàĩń.",
"rename": "Ŕēńàḿē",
@@ -224,6 +249,7 @@
"results": "Ŕēśũĺţś",
"retryPreview": "Ŕēţŕŷ ƥŕēvĩēŵ",
"reviewSubmoduleBoundary": "Śũƀḿōďũĺē: {{scope}}",
"rowsPerPage": "Ŕōŵś ƥēŕ ƥàĝē",
"runsFormatTypecheckTestAndLint": "Ŕũńś ƒōŕḿàţ, ţŷƥēćĥēćķ, ţēśţ, àńď ĺĩńţ àćŕōśś ţĥē ḿōńōŕēƥō.",
"runtimeDocker": "Ďōćķēŕ",
"runtimeDockerDescription": "Ćōńţàĩńēŕĩźēď ēxēćũţĩōń ƒōŕ ƒũĺĺ ĩśōĺàţĩōń àńď ŕēƥŕōďũćĩƀĩĺĩţŷ.",
@@ -239,6 +265,7 @@
"scopeFiles": "Ƒĩĺēś",
"scopeTitleWithShortcut": "{{label}} ({{shortcut}})",
"scratchWorkspace": "śćŕàţćĥ ŵōŕķśƥàćē",
"searchEllipsis": "Śēàŕćĥ…",
"searchFailedEditTheQueryOr": "Śēàŕćĥ ƒàĩĺēď. Ēďĩţ ţĥē qũēŕŷ ōŕ ŕēōƥēń śēàŕćĥ ţō ŕēţŕŷ.",
"searchForFiles": "Śēàŕćĥ ƒōŕ ƒĩĺēś...",
"searchForTasks": "Śēàŕćĥ ƒōŕ ţàśķś...",
@@ -250,6 +277,10 @@
"shell": "Śĥēĺĺ",
"shellCommandPlusSlashCommandSkills": "Śĥēĺĺ ćōḿḿàńď ŷōũ ćàń ŕũń ĩń ţĥē śēćōńďàŕŷ ĩńśţàńćē, ƥĺũś śĺàśĥ-ćōḿḿàńď <2>śķĩĺĺś</2> ŷōũ ćàń àśķ ţĥē àĝēńţ ţō ŕũń ďũŕĩńĝ ţĥē ŵōŕķƒĺōŵ.",
"showAdvancedOptions": "Śĥōŵ àďvàńćēď ōƥţĩōńś",
"showCode": "Śĥōŵ ćōďē",
"showDiagram": "Śĥōŵ ďĩàĝŕàḿ",
"showingRange_one": "Śĥōŵĩńĝ {{from}} ţō {{to}} ōƒ {{count}} ŕēśũĺţ",
"showingRange_other": "Śĥōŵĩńĝ {{from}} ţō {{to}} ōƒ {{count}} ŕēśũĺţś",
"skip": "Śķĩƥ",
"stagesAndCommitsChangesUsingConventional": "Śţàĝēś àńď ćōḿḿĩţś ćĥàńĝēś ũśĩńĝ Ćōńvēńţĩōńàĺ Ćōḿḿĩţś.",
"status": "Śţàţũś",
@@ -277,7 +308,9 @@
"usefulCommandsAgentSkills": "Ũśēƒũĺ ćōḿḿàńďś & àĝēńţ śķĩĺĺś",
"useScratchInstead": "Ũśē śćŕàţćĥ ĩńśţēàď",
"useThisFolder": "Ũśē ţĥĩś ƒōĺďēŕ",
"viewFullReleaseOnGithub": "Vĩēŵ ƒũĺĺ ŕēĺēàśē ōń ĜĩţĤũƀ",
"waitsForCiAndAutomatedReviews": "Ŵàĩţś ƒōŕ ĆĨ àńď àũţōḿàţēď ŕēvĩēŵś, ţĥēń ƒĩxēś ƒàĩĺũŕēś àńď àďďŕēśśēś ćōḿḿēńţś.",
"whatSNew": "Ŵĥàţ'ś Ńēŵ",
"whenItsDoneTheAgentOpensAPr": "Ŵĥēń ĩţ'ś ďōńē, ţĥē àĝēńţ ōƥēńś à ƥũĺĺ ŕēqũēśţ ţō <2>{{repo}}</2> ƒōŕ ţĥē ḿàĩńţàĩńēŕś ţō ŕēvĩēŵ, śàvĩńĝ ţĥēḿ ţĩḿē àńď śĥĩƥƥĩńĝ ţĥē ĩḿƥŕōvēḿēńţ ţō ēvēŕŷōńē.",
"workflow": "Ŵōŕķƒĺōŵ",
"workflowsControlTheStepsAutomationAnd": "Ŵōŕķƒĺōŵś ćōńţŕōĺ ţĥē śţēƥś, àũţōḿàţĩōń, àńď àĝēńţ ƀēĥàvĩōŕ ƒōŕ ŷōũŕ ţàśķś. Ŷōũ ćàń àďď ḿōŕē ŵōŕķƒĺōŵś ƒŕōḿ Śēţţĩńĝś.",
@@ -28,6 +28,7 @@
"appriseUrlRequired": "Àţ ĺēàśţ ōńē Àƥƥŕĩśē śēŕvĩćē ŨŔĹ ĩś ŕēqũĩŕēď.",
"archiveConfirmation": "Àŕćĥĩvē Ćōńƒĩŕḿàţĩōń",
"arrangeSplit": "Àŕŕàńĝē śƥĺĩţ",
"autoApprove": "Àũţō-àƥƥŕōvē",
"autoInstallIfNotFound": "Àũţō-ĩńśţàĺĺ ĩƒ ńōţ ƒōũńď",
"autoStart": "Àũţō-śţàŕţ",
"autoStartLanguageServer": "Àũţō-śţàŕţ {{language}} ĺàńĝũàĝē śēŕvēŕ",
@@ -350,6 +351,8 @@
"makesAgentctlValuesAvailableToPlugins": "Ḿàķēś àĝēńţćţĺ vàĺũēś àvàĩĺàƀĺē ţō ƥĺũĝĩńś àńď ōţĥēŕ ćōńśũḿēŕś. Ţĥē ƀũĩĺţ-ĩń śţàţũś ƀàŕ ŕēḿàĩńś ĥōśţ-ōńĺŷ.",
"manageApiKeysAndCredentialsSecrets": "Ḿàńàĝē ÀƤĨ ķēŷś àńď ćŕēďēńţĩàĺś. Śēćŕēţś àŕē ēńćŕŷƥţēď àţ ŕēśţ àńď ĩńĵēćţēď ĩńţō àĝēńţ ēńvĩŕōńḿēńţś vĩà ēxēćũţōŕ ƥŕōƒĩĺē ēńv vàŕś.",
"globalScope": "Ĝĺōƀàĺ",
"modelWithValue": "Ḿōďēĺ: {{model}}",
"temperatureWithValue": "Ţēḿƥēŕàţũŕē: {{temperature}}",
"workspaceScope": "Ŵōŕķśƥàćē",
"workspaceSecretsDescription": "Ḿàńàĝē śēćŕēţś àvàĩĺàƀĺē ţō ŕēƥōśĩţōŕĩēś ĩń ţĥĩś ŵōŕķśƥàćē. Śĥàŕēď àĝēńţ àńď ēxēćũţōŕ ƥŕōƒĩĺēś ćàń ũśē Ĝĺōƀàĺ śēćŕēţś ōńĺŷ.",
"revealSecretNamed": "Ŕēvēàĺ śēćŕēţ {{name}}",
+16
View File
@@ -131,6 +131,9 @@
"healthSystemChecks": "Śŷśţēḿ ćĥēćķś",
"healthTitle": "Ĥēàĺţĥ",
"healthWhatsMonitored": "Ŵĥàţ'ś ḿōńĩţōŕēď",
"host": "Ĥōśţ",
"hostMetrics": "Ĥōśţ ḿēţŕĩćś",
"hostWithLabel": "Ĥōśţ: {{label}}",
"inviteCopied": "Ĩńvĩţē ĺĩńķ ćōƥĩēď",
"inviteCopy": "Ćōƥŷ ĩńvĩţē ĺĩńķ",
"inviteCreate": "Ćŕēàţē ĩńvĩţē ĺĩńķ",
@@ -143,10 +146,14 @@
"inviteReadyTitle": "Ĩńvĩţē ĺĩńķ ŕēàďŷ",
"inviteTitle": "Ĩńvĩţē à ũśēŕ",
"inviteUrlLabel": "Ĩńvĩţē ĺĩńķ",
"ioLoadHelp": "Àvēŕàĝē ńũḿƀēŕ ōƒ ţàśķś ŕũńńĩńĝ ōŕ ŵàĩţĩńĝ ƒōŕ ĆƤŨ ďũŕĩńĝ ţĥē ĺàśţ ḿĩńũţē. Ćōḿƥàŕē ţĥĩś vàĺũē ŵĩţĥ ţĥē ĥōśţ'ś ĆƤŨ ćōŕē ćōũńţ.",
"issuesNeedAttention_one": "{{count}} ĩśśũē ńēēďś ŷōũŕ àţţēńţĩōń",
"issuesNeedAttention_other": "{{count}} ĩśśũēś ńēēď ŷōũŕ àţţēńţĩōń",
"jobStateDone": "Ďōńē",
"jobStateFailed": "Ƒàĩĺēď",
"jobStateQueued": "Qũēũēď",
"jobStateRunning": "Ŕũńńĩńĝ",
"lastUpdateUnknown": "Ĺàśţ ũƥďàţē ũńķńōŵń",
"licensesCount": "{{visible}} ōƒ {{total}}",
"licensesEmpty": "Ńō ēńţŕĩēś ḿàţćĥ ţĥē ƒĩĺţēŕ.",
"licensesFilterPlaceholder": "Ƒĩĺţēŕ ƀŷ ńàḿē, ĺĩćēńśē, ōŕ ēćōśŷśţēḿ...",
@@ -176,6 +183,12 @@
"messageQueueUnlimited": "Ũńĺĩḿĩţēď",
"messageQueueUnlimitedHelp": "Śēţ 0 ƒōŕ ũńĺĩḿĩţēď. À ƥōśĩţĩvē ŵĥōĺē ńũḿƀēŕ ŕēĵēćţś ńēŵ ḿēśśàĝēś àƒţēŕ ţĥàţ ḿàńŷ àŕē ƥēńďĩńĝ ĩń ōńē śēśśĩōń.",
"messageQueueValidation": "Ēńţēŕ à ŵĥōĺē ńũḿƀēŕ ōƒ 0 ōŕ ĝŕēàţēŕ.",
"metricCpuPercent": "ĆƤŨ",
"metricCpuTemp": "ĆƤŨ ţēḿƥēŕàţũŕē",
"metricDiskPercent": "Ďĩśķ",
"metricIoLoad": "Śŷśţēḿ ĺōàď (1 ḿĩń)",
"metricMemoryPercent": "Ḿēḿōŕŷ",
"metricsUnavailable": "Ḿēţŕĩćś ũńàvàĩĺàƀĺē",
"navAbout": "Àƀōũţ",
"navBackups": "Ɓàćķũƥś",
"navDatabase": "Ďàţàƀàśē",
@@ -226,6 +239,7 @@
"selfUpdateStartingUnknown": "Śţàŕţĩńĝ ũƥďàţē ţō ţĥē ńēŵ vēŕśĩōń…",
"selfUpdateTimedOut": "Ũƥďàţē ĩś ţàķĩńĝ ĺōńĝēŕ ţĥàń ēxƥēćţēď. Ŕēƒŕēśĥ ţō ćĥēćķ ţĥē ćũŕŕēńţ vēŕśĩōń.",
"settingsRouteNotPorted": "Ţĥĩś śēţţĩńĝś ŕōũţē ĩś ĥàńďĺēď ƀŷ ţĥē ŚƤÀ śĥēĺĺ, ƀũţ ĩţś ďēďĩćàţēď ćĺĩēńţ ƥàĝē ĩś śţĩĺĺ ƀēĩńĝ ƥōŕţēď: <1>{{pathname}}</1>",
"setupIssues": "Śēţũƥ Ĩśśũēś",
"statusPageDescription": "Ĥēàĺţĥ ćĥēćķś, ďĩśķ ũśàĝē, àńď vēŕśĩōń śũḿḿàŕŷ.",
"storageAcknowledgeDaemon": "Àćķńōŵĺēďĝē ďàēḿōń",
"storageAcknowledgeDedicatedFirst": "Àćķńōŵĺēďĝē à ďēďĩćàţēď Ďōćķēŕ ďàēḿōń ƒĩŕśţ.",
@@ -395,11 +409,13 @@
"storageWorkspacesDescription": "Ŕēćĺàĩḿ ŕēśōũŕćēś ţĥàţ Ķàńďēv ćàń ƥōśĩţĩvēĺŷ ĩďēńţĩƒŷ àś ńō ĺōńĝēŕ ĩń ũśē.",
"storageWorkspacesDetail": "{{reclaimable}} ŕēćĺàĩḿàƀĺē àƒţēŕ ţĥē ĝŕàćē ƥēŕĩōď · {{active}} àćţĩvē",
"storageWorkspacesTitle": "Ŵōŕķśƥàćēś àńď ćōńţàĩńēŕś",
"systemMetrics": "Śŷśţēḿ ḿēţŕĩćś",
"uiStateHelp": "Ćĺēàŕś ĺàŷōũţ àńď ŨĨ ƥŕēƒēŕēńćēś śàvēď ĩń ŷōũŕ ƀŕōŵśēŕ (ƥàńēĺ śĩźēś, ƥĩńńēď ţàśķś, ēxƥàńďēď ĝŕōũƥś, ēţć.). Ũśē ţĥĩś ĩƒ ţĥē àƥƥ ĺōōķś ƀŕōķēń, ƥàńēĺś àŕē śţũćķ, ōŕ ţĥē ĺàŷōũţ ƒēēĺś ŵŕōńĝ. Ŷōũŕ ţàśķś, śēśśĩōńś, àńď śēŕvēŕ ďàţà àŕē ńōţ àƒƒēćţēď - ōńĺŷ ţĥē ƥēŕ-ƀŕōŵśēŕ ŨĨ śţàţē. Ţĥē ƥàĝē ŕēĺōàďś àƒţēŕ ţĥē ŕēśēţ.",
"uiStateReset": "Ŕēśēţ",
"uiStateResetLabel": "Ŕēśēţ ƀŕōŵśēŕ ĺàŷōũţ",
"uiStateTitle": "ŨĨ śţàţē",
"updateAvailable": "Ũƥďàţē àvàĩĺàƀĺē",
"updatedRelative": "Ũƥďàţēď {{relative}}",
"updatesApply": "Àƥƥĺŷ ũƥďàţē",
"updatesApplyConfirmDesktop": "Ķàńďēv ŵĩĺĺ ĩńśţàĺĺ {{version}} àńď ŕēśţàŕţ ţĥē ďēśķţōƥ àƥƥ.",
"updatesApplyConfirmService": "Ķàńďēv ŵĩĺĺ ũƥďàţē ţō {{version}}, ŕēĩńśţàĺĺ ţĥē ũśēŕ śēŕvĩćē, àńď ŕēśţàŕţ ĩţ.",
+16
View File
@@ -141,6 +141,7 @@
"blocks": "Ɓĺōćķś",
"bothVersionsAreEmpty": "(ƀōţĥ vēŕśĩōńś àŕē ēḿƥţŷ)",
"branch": "Ɓŕàńćĥ",
"branchBasedOn": "Ɓŕàńćĥ <1>{{branch}}</1>, ƀàśēď ōń <3>{{base}}</3>",
"branchComparisonAndTaskGitCredentials": "Ɓŕàńćĥ ćōḿƥàŕĩśōń àńď ţàśķ Ĝĩţ ćŕēďēńţĩàĺś.",
"branchDetails": "Ɓŕàńćĥ ďēţàĩĺś",
"branchIsNoLongerAvailable": "Ɓŕàńćĥ ĩś ńō ĺōńĝēŕ àvàĩĺàƀĺē",
@@ -381,7 +382,11 @@
"enterRepositoryUrl": "Ēńţēŕ à ĜĩţĤũƀ, ĜĩţĹàƀ, ōŕ Àźũŕē ĎēvŌƥś ŕēƥōśĩţōŕŷ ŨŔĹ.",
"environment": "Ēńvĩŕōńḿēńţ {{label}}",
"environmentPreparationFailed": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕàţĩōń ƒàĩĺēď",
"environmentPrepared": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕēď",
"environmentPreparedOnFreshSandbox": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕēď ōń à ƒŕēśĥ śàńďƀōx",
"environmentPreparedWithWarnings": "Ēńvĩŕōńḿēńţ ƥŕēƥàŕēď ŵĩţĥ ŵàŕńĩńĝś",
"environmentSetupFailed": "Ēńvĩŕōńḿēńţ śēţũƥ ƒàĩĺēď",
"environmentSetupFinishedWithErrors": "Ēńvĩŕōńḿēńţ śēţũƥ ƒĩńĩśĥēď ŵĩţĥ ēŕŕōŕś",
"error": "Ēŕŕōŕ",
"errorData": "Ēŕŕōŕ Ďàţà",
"executor2": "Ēxēćũţōŕ",
@@ -528,13 +533,16 @@
"hiddenMessagesInPreview_other": "… {{count}} ḿōŕē ḿēśśàĝēś ĥĩďďēń ĩń ţĥĩś ƥŕēvĩēŵ (śţĩĺĺ ĩńćĺũďēď ĩń ţĥē śĥàŕē) …",
"hideCommandOutput": "Ĥĩďē ćōḿḿàńď ōũţƥũţ",
"hideDebugInfo": "Ĥĩďē Ďēƀũĝ Ĩńƒō",
"hideDetails": "Ĥĩďē ďēţàĩĺś",
"hideErrorDetails": "Ĥĩďē ēŕŕōŕ ďēţàĩĺś: {{displayLabel}}",
"hidePreparationDetails": "Ĥĩďē ƥŕēƥàŕàţĩōń ďēţàĩĺś",
"hidePreviousAgentError": "Ĥĩďē ƥŕēvĩōũś àĝēńţ ēŕŕōŕ",
"hideRightPanel": "Ĥĩďē ŕĩĝĥţ ƥàńēĺ",
"hideRightPanels": "Ĥĩďē ŕĩĝĥţ ƥàńēĺś",
"hideSidebar": "Ĥĩďē śĩďēƀàŕ",
"hideTheCommentListTheComments": "Ĥĩďē ţĥē ćōḿḿēńţ ĺĩśţ. Ţĥē ćōḿḿēńţś śţàŷ qũēũēď àńď ŵĩĺĺ śţĩĺĺ ƀē śēńţ ńēxţ ţĩḿē ŷōũ śũƀḿĩţ (ĥēŕē ōŕ ƒŕōḿ ţĥē ćĥàţ ƀōx).",
"host": "Ĥōśţ",
"isolatedWorktreeAt": "Ĩśōĺàţēď ŵōŕķţŕēē àţ <1>{{path}}</1>",
"iUnderstandAnyUncommittedChangesWill": "Ĩ ũńďēŕśţàńď àńŷ ũńćōḿḿĩţţēď ćĥàńĝēś ŵĩĺĺ ƀē ĺōśţ.",
"image": "Ĩḿàĝē",
"imagePreview": "Ĩḿàĝē ƥŕēvĩēŵ",
@@ -779,6 +787,7 @@
"noWorkspacesFound": "Ńō ŵōŕķśƥàćēś ƒōũńď.",
"none": "Ńōńē",
"notConfiguredForThisExecutor": "Ńōţ ćōńƒĩĝũŕēď ƒōŕ ţĥĩś ēxēćũţōŕ",
"onBranch": "Ōń ƀŕàńćĥ <1>{{branch}}</1>",
"onDisk": "ōń ďĩśķ",
"ontoBranch": "ōńţō {{branch}}",
"openBrowserPreview": "Ōƥēń ƀŕōŵśēŕ ƥŕēvĩēŵ",
@@ -842,7 +851,9 @@
"portNumber": "Ƥōŕţ ńũḿƀēŕ",
"position": "Ƥōśĩţĩōń {{positionLabel}}",
"prChanges": "ƤŔ Ćĥàńĝēś",
"preparingEllipsis": "Ƥŕēƥàŕĩńĝ...",
"preparingEnvironment": "Ƥŕēƥàŕĩńĝ ēńvĩŕōńḿēńţ",
"preparingEnvironmentEllipsis": "Ƥŕēƥàŕĩńĝ ēńvĩŕōńḿēńţ...",
"preparingWorkspace": "Ƥŕēƥàŕĩńĝ ŵōŕķśƥàćē...",
"preparingWorkspace2": "Ƥŕēƥàŕĩńĝ ŵōŕķśƥàćē…",
"presets": "Ƥŕēśēţś",
@@ -1007,6 +1018,7 @@
"runInTerminal": "Ŕũń ĩń ţēŕḿĩńàĺ",
"runInTheParentSMaterialized": "Ŕũń ĩń ţĥē ƥàŕēńţ'ś ḿàţēŕĩàĺĩźēď ŵōŕķśƥàćē",
"runInTheParentSWorktree": "Ŕũń ĩń ţĥē ƥàŕēńţ'ś ŵōŕķţŕēē ({{worktreeBranch}})",
"runRepositorySetupScript": "Ŕũń ŕēƥōśĩţōŕŷ śēţũƥ śćŕĩƥţ",
"runScript": "Ŕũń śćŕĩƥţ",
"running": "ŕũńńĩńĝ",
"sameBranchAsCurrentSession": "Śàḿē ƀŕàńćĥ àś ćũŕŕēńţ śēśśĩōń",
@@ -1026,6 +1038,8 @@
"savedLayouts": "Śàvēď Ĺàŷōũţś",
"saving": "Śàvĩńĝ...",
"scriptExecutionMetadataUnavailable": "Śćŕĩƥţ Ēxēćũţĩōń (ḿēţàďàţà ũńàvàĩĺàƀĺē)",
"scriptExitedWithCode": "Śćŕĩƥţ ēxĩţēď ŵĩţĥ ćōďē {{code}}",
"scriptFailed": "Śćŕĩƥţ ƒàĩĺēď",
"scripts": "Śćŕĩƥţś",
"scrollToActiveSession": "Śćŕōĺĺ ţō àćţĩvē śēśśĩōń",
"scrollToLastPrompt": "Śćŕōĺĺ ţō ĺàśţ ƥŕōḿƥţ",
@@ -1116,6 +1130,7 @@
"showMessageMetadata": "Śĥōŵ ḿēśśàĝē ḿēţàďàţà",
"showOlderSessions_one": "Śĥōŵ {{count}} ōĺďēŕ śēśśĩōń",
"showOlderSessions_other": "Śĥōŵ {{count}} ōĺďēŕ śēśśĩōńś",
"showPreparationDetails": "Śĥōŵ ƥŕēƥàŕàţĩōń ďēţàĩĺś",
"showRawText": "Śĥōŵ ŕàŵ ţēxţ",
"showRightPanel": "Śĥōŵ ŕĩĝĥţ ƥàńēĺ",
"showSidebar": "Śĥōŵ śĩďēƀàŕ",
@@ -1400,6 +1415,7 @@
"working": "Ŵōŕķĩńĝ...",
"working3": "Ŵōŕķĩńĝ",
"working4": "Ŵōŕķĩńĝ…",
"workingIn": "Ŵōŕķĩńĝ ĩń <1>{{path}}</1>",
"workingInBackground": "Ŵōŕķĩńĝ ĩń ƀàćķĝŕōũńď",
"workspaceActions": "Ŵōŕķśƥàćē àćţĩōńś",
"workspaceCount_one": "{{count}} ŵōŕķśƥàćē",