diff --git a/apps/web/components/github/issue-watch-dialog.tsx b/apps/web/components/github/issue-watch-dialog.tsx index 946cfdc8af..454d43f1e6 100644 --- a/apps/web/components/github/issue-watch-dialog.tsx +++ b/apps/web/components/github/issue-watch-dialog.tsx @@ -31,7 +31,7 @@ import { DEFAULT_ISSUE_WATCH_PROMPT, } from "@/components/github/issue-watch-placeholders"; import { RepoFilterSelector } from "@/components/github/repo-filter-selector"; -import { STEP_DEFAULT, STEP_DEFAULT_LABEL, resolveProfileId } from "@/lib/watcher-profile-default"; +import { STEP_DEFAULT, resolveProfileId } from "@/lib/watcher-profile-default"; import type { RepoFilter, IssueWatch, @@ -271,6 +271,7 @@ function IssueAutomationFields({ }) { const { t } = useTranslation(); const { workflows, agentProfiles, allExecutorProfiles } = useWatchFormData(form.workspaceId); + const stepDefaultLabel = t("common:useStepDefaultOption"); const { steps: workflowSteps, loading: stepsLoading } = useWorkflowSteps(form.workflowId); const placeholders = useMemo(() => issueWatchPlaceholders(t), [t]); @@ -302,9 +303,9 @@ function IssueAutomationFields({ description={t("github:optionalFallsBackToStepDefault")} value={form.agentProfileId || STEP_DEFAULT} onChange={(v) => setForm((prev) => ({ ...prev, agentProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...agentProfiles.map((p) => ({ id: p.id, label: p.label, @@ -317,9 +318,9 @@ function IssueAutomationFields({ description={t("github:optionalFallsBackToStepDefault")} value={form.executorProfileId || STEP_DEFAULT} onChange={(v) => setForm((prev) => ({ ...prev, executorProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...allExecutorProfiles.map((p) => ({ id: p.id, label: p.name })), ]} /> diff --git a/apps/web/components/github/review-watch-dialog.tsx b/apps/web/components/github/review-watch-dialog.tsx index 9e7bb7dc4b..986b221fa4 100644 --- a/apps/web/components/github/review-watch-dialog.tsx +++ b/apps/web/components/github/review-watch-dialog.tsx @@ -29,7 +29,7 @@ import { import { DEFAULT_REVIEW_WATCH_PROMPT } from "@/components/github/review-watch-placeholders"; import { ReviewWatchPromptField } from "@/components/github/review-watch-prompt-field"; import { RepoFilterSelector } from "@/components/github/repo-filter-selector"; -import { STEP_DEFAULT, STEP_DEFAULT_LABEL, resolveProfileId } from "@/lib/watcher-profile-default"; +import { STEP_DEFAULT, resolveProfileId } from "@/lib/watcher-profile-default"; import type { RepoFilter, ReviewWatch, @@ -433,6 +433,7 @@ function ProfileFields({ executorProfiles: Array<{ id: string; name: string }>; }) { const { t } = useTranslation(); + const stepDefaultLabel = t("common:useStepDefaultOption"); return (
setForm((prev) => ({ ...prev, agentProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...agentProfiles.map((p) => ({ id: p.id, label: p.label, @@ -469,10 +470,10 @@ function ProfileFields({ } > - + - {STEP_DEFAULT_LABEL} + {stepDefaultLabel} {executorProfiles.map((p) => ( {p.name} diff --git a/apps/web/components/gitlab/watch-dialog.tsx b/apps/web/components/gitlab/watch-dialog.tsx index 4cd07ef2c6..50b2266e4d 100644 --- a/apps/web/components/gitlab/watch-dialog.tsx +++ b/apps/web/components/gitlab/watch-dialog.tsx @@ -21,7 +21,7 @@ import { WatcherRepositoryFields } from "@/components/watcher-repository-fields" import { useSettingsData } from "@/hooks/domains/settings/use-settings-data"; import { useWorkflowSteps, stepPlaceholder } from "@/hooks/use-workflow-steps"; import { useWorkflows } from "@/hooks/use-workflows"; -import { STEP_DEFAULT, STEP_DEFAULT_LABEL, resolveProfileId } from "@/lib/watcher-profile-default"; +import { STEP_DEFAULT, resolveProfileId } from "@/lib/watcher-profile-default"; import type { CreateIssueWatchRequest, CreateReviewWatchRequest, @@ -205,6 +205,7 @@ type FormFieldsProps = { function AutomationFields({ kind, form, setForm }: FormFieldsProps) { const { t } = useTranslation(); + const stepDefaultLabel = t("common:useStepDefaultOption"); const data = useDialogData(form.workspaceId, form.workflowId); return (
@@ -247,9 +248,9 @@ function AutomationFields({ kind, form, setForm }: FormFieldsProps) { onChange={(value) => setForm((current) => ({ ...current, agentProfileId: resolveProfileId(value) })) } - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...data.agentProfiles.map((item) => ({ id: item.id, label: item.label })), ]} /> @@ -260,9 +261,9 @@ function AutomationFields({ kind, form, setForm }: FormFieldsProps) { onChange={(value) => setForm((current) => ({ ...current, executorProfileId: resolveProfileId(value) })) } - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...data.executorProfiles.map((item) => ({ id: item.id, label: item.name })), ]} /> diff --git a/apps/web/components/jira/jira-issue-watch-dialog.tsx b/apps/web/components/jira/jira-issue-watch-dialog.tsx index dc58416ad2..257a4d5eb5 100644 --- a/apps/web/components/jira/jira-issue-watch-dialog.tsx +++ b/apps/web/components/jira/jira-issue-watch-dialog.tsx @@ -31,7 +31,7 @@ import { jiraIssueWatchPlaceholders, DEFAULT_JIRA_ISSUE_WATCH_PROMPT, } from "@/components/jira/jira-issue-watch-placeholders"; -import { STEP_DEFAULT, STEP_DEFAULT_LABEL, resolveProfileId } from "@/lib/watcher-profile-default"; +import { STEP_DEFAULT, resolveProfileId } from "@/lib/watcher-profile-default"; import { WatcherRepositoryFields } from "@/components/watcher-repository-fields"; import { clearWorkspaceScopedForm } from "@/lib/watcher-repository-default"; import type { @@ -339,6 +339,7 @@ function AutomationFields({ setForm: React.Dispatch>; }) { const { t } = useTranslation(); + const stepDefaultLabel = t("common:useStepDefaultOption"); const { workflows, agentProfiles, allExecutorProfiles } = useFormData(form.workspaceId); const { steps, loading: stepsLoading } = useWorkflowSteps(form.workflowId); return ( @@ -377,9 +378,9 @@ function AutomationFields({ description={t("jira:optionalFallsBackToStepDefault")} value={form.agentProfileId || STEP_DEFAULT} onChange={(v) => setForm((p) => ({ ...p, agentProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...agentProfiles.map((p) => ({ id: p.id, label: p.label, @@ -392,9 +393,9 @@ function AutomationFields({ description={t("jira:optionalFallsBackToStepDefault")} value={form.executorProfileId || STEP_DEFAULT} onChange={(v) => setForm((p) => ({ ...p, executorProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...allExecutorProfiles.map((p) => ({ id: p.id, label: p.name })), ]} /> diff --git a/apps/web/components/linear/linear-issue-watch-dialog.tsx b/apps/web/components/linear/linear-issue-watch-dialog.tsx index 7e2b561a3b..553ac95ce3 100644 --- a/apps/web/components/linear/linear-issue-watch-dialog.tsx +++ b/apps/web/components/linear/linear-issue-watch-dialog.tsx @@ -26,7 +26,7 @@ import { import { SettingsFields } from "./linear-issue-watch-fields"; import { FilterFields, SelectField } from "./linear-issue-watch-filter-fields"; import { linearIssueWatchPlaceholders } from "./linear-issue-watch-placeholders"; -import { STEP_DEFAULT, STEP_DEFAULT_LABEL, resolveProfileId } from "@/lib/watcher-profile-default"; +import { STEP_DEFAULT, resolveProfileId } from "@/lib/watcher-profile-default"; import { WatcherRepositoryFields } from "@/components/watcher-repository-fields"; import { clearWorkspaceScopedForm } from "@/lib/watcher-repository-default"; import { @@ -159,6 +159,7 @@ function AutomationFields({ setForm: React.Dispatch>; }) { const { t } = useTranslation(); + const stepDefaultLabel = t("common:useStepDefaultOption"); const { workflows, agentProfiles, allExecutorProfiles } = useFormData(form.workspaceId); const { steps, loading: stepsLoading } = useWorkflowSteps(form.workflowId); return ( @@ -197,9 +198,9 @@ function AutomationFields({ description={t("linear:fallsBackToStepDefault")} value={form.agentProfileId || STEP_DEFAULT} onChange={(v) => setForm((p) => ({ ...p, agentProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...agentProfiles.map((p) => ({ id: p.id, label: p.label, @@ -212,9 +213,9 @@ function AutomationFields({ description={t("linear:fallsBackToStepDefault")} value={form.executorProfileId || STEP_DEFAULT} onChange={(v) => setForm((p) => ({ ...p, executorProfileId: resolveProfileId(v) }))} - placeholder={STEP_DEFAULT_LABEL} + placeholder={stepDefaultLabel} items={[ - { id: STEP_DEFAULT, label: STEP_DEFAULT_LABEL }, + { id: STEP_DEFAULT, label: stepDefaultLabel }, ...allExecutorProfiles.map((p) => ({ id: p.id, label: p.name })), ]} /> diff --git a/apps/web/components/task/chat/messages/chat-message.test.tsx b/apps/web/components/task/chat/messages/chat-message.test.tsx index 6bf3e4c8a5..1a81653933 100644 --- a/apps/web/components/task/chat/messages/chat-message.test.tsx +++ b/apps/web/components/task/chat/messages/chat-message.test.tsx @@ -1,10 +1,11 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { ReactNode } from "react"; -import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { act, cleanup, fireEvent, render, screen } from "@testing-library/react"; import { StateProvider } from "@/components/state-provider"; import { ChatMessage } from "./chat-message"; import { entityReferenceMarkdown } from "@/lib/entity-references/message-references"; import type { EntityReference } from "@/lib/types/entity-reference"; +import { activateLocale } from "@/lib/i18n"; import { sessionId as toSessionId, taskId as toTaskId, @@ -370,6 +371,26 @@ describe("ChatMessage sender badge", () => { expect(badge?.textContent).toContain("Old title"); }); + it("localizes the fallback when no sender task title is available", async () => { + await activateLocale("en"); + const { container } = renderWithSender([], { + sender_task_id: "task-deleted", + sender_task_title: "", + }); + + const badge = container.querySelector(SENDER_BADGE_SELECTOR); + expect(badge?.textContent).toContain("(unknown task)"); + + await act(async () => { + await activateLocale("pseudo"); + }); + + expect(badge?.textContent).toContain("(ũńķńōŵń ţàśķ)"); + await act(async () => { + await activateLocale("en"); + }); + }); + it("uses the live title when it differs from the snapshot", () => { // The badge re-resolves the title from the kanban store so renames are // reflected without re-sending the message. diff --git a/apps/web/components/task/chat/messages/sender-task-badge.tsx b/apps/web/components/task/chat/messages/sender-task-badge.tsx index e849f0e713..4b2f498fba 100644 --- a/apps/web/components/task/chat/messages/sender-task-badge.tsx +++ b/apps/web/components/task/chat/messages/sender-task-badge.tsx @@ -50,7 +50,7 @@ export function SenderTaskBadge({ sender, size = "sm" }: SenderTaskBadgeProps) { const liveSessionName = useAppStore((state) => sender.sessionId ? (state.taskSessions.items[sender.sessionId]?.name ?? null) : null, ); - const fullTitle = liveTask?.title || sender.snapshotTitle || "(unknown task)"; + const fullTitle = liveTask?.title || sender.snapshotTitle || t("task:unknownTaskFallback"); const sessionName = liveSessionName ?? sender.sessionName ?? ""; const truncated = sessionName ? `${truncateTitle(fullTitle)} · ${truncateTitle(sessionName)}` diff --git a/apps/web/components/watcher-repository-fields.test.tsx b/apps/web/components/watcher-repository-fields.test.tsx new file mode 100644 index 0000000000..6cc156ba67 --- /dev/null +++ b/apps/web/components/watcher-repository-fields.test.tsx @@ -0,0 +1,49 @@ +import { cleanup, render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { WatcherRepositoryFields } from "./watcher-repository-fields"; + +const branchState = vi.hoisted(() => ({ loading: false })); + +vi.mock("@/hooks/domains/workspace/use-repositories", () => ({ + useRepositories: () => ({ repositories: [] }), +})); + +vi.mock("@/hooks/domains/workspace/use-repository-branches", () => ({ + useBranches: () => ({ branches: [], isLoading: branchState.loading }), +})); + +afterEach(() => { + cleanup(); + branchState.loading = false; +}); + +describe("WatcherRepositoryFields", () => { + it("shows the repository-first placeholder while the branch selector is disabled", () => { + render( + , + ); + + expect(screen.getByLabelText("Base Branch").textContent).toContain("Pick a repository first"); + }); + + it("shows the loading placeholder instead of selecting the default branch", () => { + branchState.loading = true; + render( + , + ); + + expect(screen.getByLabelText("Base Branch").textContent).toContain("Loading…"); + }); +}); diff --git a/apps/web/components/watcher-repository-fields.tsx b/apps/web/components/watcher-repository-fields.tsx index c63470f21c..2e94be11ff 100644 --- a/apps/web/components/watcher-repository-fields.tsx +++ b/apps/web/components/watcher-repository-fields.tsx @@ -7,9 +7,7 @@ import { useRepositories } from "@/hooks/domains/workspace/use-repositories"; import { useBranches } from "@/hooks/domains/workspace/use-repository-branches"; import { NO_REPOSITORY, - NO_REPOSITORY_LABEL, DEFAULT_BRANCH, - DEFAULT_BRANCH_LABEL, branchPlaceholder, resolveBaseBranch, resolveRepositoryId, @@ -21,7 +19,7 @@ type PickItem = { id: string; label: string }; function PickSelect(props: { label: string; description: string; - value: string; + value: string | undefined; onChange: (v: string) => void; placeholder: string; items: PickItem[]; @@ -83,6 +81,15 @@ export function WatcherRepositoryFields({ const { repositories } = useRepositories(workspaceId, !!workspaceId, true); const branchSource = repositoryId ? ({ kind: "id", workspaceId, repositoryId } as const) : null; const { branches, isLoading: branchesLoading } = useBranches(branchSource, !!repositoryId); + const noRepositoryLabel = t("common:noRepositoryOption"); + const defaultBranchLabel = t("common:repositoryDefaultBranchOption"); + const branchPlaceholderLabels = { + defaultBranch: defaultBranchLabel, + loading: t("common:watcherLoadingBranchesPlaceholder"), + pickRepository: t("common:watcherPickRepositoryPlaceholder"), + }; + const branchPlaceholderLabel = + branchPlaceholderLabels[branchPlaceholder(repositoryId, branchesLoading)]; // A branch name can appear twice (local + remote tracking, e.g. "main" and // origin/"main"), which would emit two — Radix then // renders every matching item's text in the trigger ("mainmain") and React @@ -95,20 +102,20 @@ export function WatcherRepositoryFields({ description={t("common:optionalTheRepositoryTheAgentWorks")} value={repositoryId || NO_REPOSITORY} onChange={(v) => onRepositoryChange(resolveRepositoryId(v))} - placeholder={NO_REPOSITORY_LABEL} + placeholder={noRepositoryLabel} items={[ - { id: NO_REPOSITORY, label: NO_REPOSITORY_LABEL }, + { id: NO_REPOSITORY, label: noRepositoryLabel }, ...repositories.map((r) => ({ id: r.id, label: r.name })), ]} /> onBaseBranchChange(resolveBaseBranch(v))} - placeholder={branchPlaceholder(repositoryId, branchesLoading)} + placeholder={branchPlaceholderLabel} items={[ - { id: DEFAULT_BRANCH, label: DEFAULT_BRANCH_LABEL }, + { id: DEFAULT_BRANCH, label: defaultBranchLabel }, ...uniqueBranchNames.map((name) => ({ id: name, label: name })), ]} disabled={!repositoryId || branchesLoading} diff --git a/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts b/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts index f4790cff9d..c7da1d52e1 100644 --- a/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts +++ b/apps/web/e2e/tests/i18n/pseudo-coverage.spec.ts @@ -422,10 +422,8 @@ const SCREENS: Screen[] = [ // the nav. `components/integrations/**` is now migrated, which cleared // `drafted-integration-enabled-control.tsx` ("Enabled"/"Disabled"), // `auth-status-banner.tsx` ("Authenticated", "· checked ") and the - // watcher card's loading state. What is left is - // `components/watcher-repository-fields.tsx` ("Repository", "Base Branch", - // "(no repository)"), `STEP_DEFAULT_LABEL` and `stepPlaceholder` — all shared - // with the un-migrated Azure DevOps surface — plus + // watcher card's loading state. What is left is `stepPlaceholder`, shared + // with the un-migrated Azure DevOps surface, plus // `components/integrations/settings-section.tsx` chrome and `@kandev/ui`'s // built-in dialog "Close" label. // diff --git a/apps/web/lib/watcher-profile-default.ts b/apps/web/lib/watcher-profile-default.ts index 118f1e102a..cee8e4afd0 100644 --- a/apps/web/lib/watcher-profile-default.ts +++ b/apps/web/lib/watcher-profile-default.ts @@ -5,7 +5,6 @@ // disallows , so the dropdown item carries this sentinel // value and maps back to "" on change. export const STEP_DEFAULT = "__step_default__"; -export const STEP_DEFAULT_LABEL = "(use step default)"; // Map a select value back to the stored profile id, collapsing the sentinel to // "" so the payload keeps signalling "use step default". diff --git a/apps/web/lib/watcher-repository-default.test.ts b/apps/web/lib/watcher-repository-default.test.ts index a5c77ad7f6..da2e2b9865 100644 --- a/apps/web/lib/watcher-repository-default.test.ts +++ b/apps/web/lib/watcher-repository-default.test.ts @@ -2,7 +2,6 @@ import { describe, it, expect } from "vitest"; import { NO_REPOSITORY, DEFAULT_BRANCH, - DEFAULT_BRANCH_LABEL, resolveRepositoryId, resolveBaseBranch, clearWorkspaceScopedForm, @@ -29,15 +28,15 @@ describe("resolveBaseBranch", () => { describe("branchPlaceholder", () => { it("prompts to pick a repository first when none is selected", () => { - expect(branchPlaceholder("", false)).toBe("Pick a repository first"); + expect(branchPlaceholder("", false)).toBe("pickRepository"); // No repo takes precedence over the loading state. - expect(branchPlaceholder("", true)).toBe("Pick a repository first"); + expect(branchPlaceholder("", true)).toBe("pickRepository"); }); it("shows a loading hint while branches stream in", () => { - expect(branchPlaceholder("repo-1", true)).toBe("Loading…"); + expect(branchPlaceholder("repo-1", true)).toBe("loading"); }); it("shows the default-branch label once a repo is selected and loaded", () => { - expect(branchPlaceholder("repo-1", false)).toBe(DEFAULT_BRANCH_LABEL); + expect(branchPlaceholder("repo-1", false)).toBe("defaultBranch"); }); }); diff --git a/apps/web/lib/watcher-repository-default.ts b/apps/web/lib/watcher-repository-default.ts index 054885a481..37625b19ee 100644 --- a/apps/web/lib/watcher-repository-default.ts +++ b/apps/web/lib/watcher-repository-default.ts @@ -11,9 +11,7 @@ // or repository id (which would otherwise let resolve*() silently rewrite a // user's selection). export const NO_REPOSITORY = "kandev:no-repository"; -export const NO_REPOSITORY_LABEL = "(no repository)"; export const DEFAULT_BRANCH = "kandev:default-branch"; -export const DEFAULT_BRANCH_LABEL = "(repository default branch)"; // Map a repository select value back to the stored id, collapsing the sentinel // to "" so the payload keeps signalling "no repository". @@ -56,8 +54,10 @@ export function clearWorkspaceScopedForm< // branchPlaceholder mirrors the workflow-step placeholder pattern: it nudges the // user to pick a repository first, then shows a loading hint while branches // stream in. -export function branchPlaceholder(repositoryId: string, loading: boolean): string { - if (!repositoryId) return "Pick a repository first"; - if (loading) return "Loading…"; - return DEFAULT_BRANCH_LABEL; +export type BranchPlaceholder = "pickRepository" | "loading" | "defaultBranch"; + +export function branchPlaceholder(repositoryId: string, loading: boolean): BranchPlaceholder { + if (!repositoryId) return "pickRepository"; + if (loading) return "loading"; + return "defaultBranch"; } diff --git a/apps/web/src/locales/en/agents.json b/apps/web/src/locales/en/agents.json index fbd4940984..d16b6d20d5 100644 --- a/apps/web/src/locales/en/agents.json +++ b/apps/web/src/locales/en/agents.json @@ -161,7 +161,6 @@ "profileNameRequiredTitle": "Profile name is required", "profileNotFound": "Profile not found.", "profileSettings": "Profile settings", - "profileStartModelSettings": "Profile start model settings", "promptTokenHint": "<0>{{token}} will be replaced with your task description or follow-up message.", "ptyConnectionError": "Connection error", "refresh": "Refresh", @@ -185,7 +184,6 @@ "scanningForInstalledAgents": "Scanning for installed agents...", "searchModels": "Search models...", "searchModes": "Search modes...", - "selectAModel": "Select a model...", "selectAModelShort": "Select a model", "selectModel": "Select model...", "sessionEnded": "Session ended.", diff --git a/apps/web/src/locales/en/common.json b/apps/web/src/locales/en/common.json index 76c06c8b93..b8e520bcf0 100644 --- a/apps/web/src/locales/en/common.json +++ b/apps/web/src/locales/en/common.json @@ -319,6 +319,8 @@ "reloadTheApplicationToTryAgain": "Reload the application to try again.", "rename": "Rename", "repository": "Repository", + "noRepositoryOption": "(no repository)", + "repositoryDefaultBranchOption": "(repository default branch)", "repositoryName": "Repository name", "requestFailed": "Request failed", "reset": "Reset", @@ -422,6 +424,9 @@ "untitledTask": "Untitled task", "usefulCommandsAgentSkills": "Useful commands & agent skills", "useScratchInstead": "Use scratch instead", + "useStepDefaultOption": "(use step default)", + "watcherLoadingBranchesPlaceholder": "Loading…", + "watcherPickRepositoryPlaceholder": "Pick a repository first", "useThisFolder": "Use this folder", "utilities": "Utilities", "viewFullReleaseOnGithub": "View full release on GitHub", diff --git a/apps/web/src/locales/en/github.json b/apps/web/src/locales/en/github.json index 3a8b9f3461..cca9655e34 100644 --- a/apps/web/src/locales/en/github.json +++ b/apps/web/src/locales/en/github.json @@ -531,7 +531,6 @@ "thisWorkspaceUsesOneCredentialFor": "This workspace uses one credential for repository sync, watches, background jobs, and managed agent GitHub access. Executor profile credentials can still take precedence.", "titleIssues": "Issues", "titlePullRequests": "Pull requests", - "togglePrEventPromptAutomation": "Toggle PR event prompt automation", "toggleReviewFollowUpAutomation": "Toggle review follow-up automation", "typeACustomQueryFirst": "Type a custom query first", "typeOwnerRepoToSearch": "Type {{format}} to search", diff --git a/apps/web/src/locales/en/settings.json b/apps/web/src/locales/en/settings.json index 344fe22ac4..d8efeff281 100644 --- a/apps/web/src/locales/en/settings.json +++ b/apps/web/src/locales/en/settings.json @@ -81,7 +81,6 @@ "completeTheRequiredEditorFieldsBefore": "Complete the required editor fields before saving.", "configChatAgentDescription": "Choose which agent profile to use for the Configuration Chat. This agent can manage your workflows, agent profiles, and MCP configuration.", "configChatAgentNoDefault": "No default", - "configChatAgentPlaceholder": "Choose an agent profile...", "configChatAgentTitle": "Configuration Chat Agent", "configureArchiveSafeguardsAndDefaultsFor": "Configure archive safeguards and defaults for tasks created by agents", "configureBackendAndExecutionResourceSampling": "Configure backend and execution resource sampling", @@ -413,8 +412,6 @@ "mergeWithTarget": "Merge with {{target}}", "messageSubmitKey": "Message Submit Key", "metrics": "Metrics", - "mode": "Mode", - "model": "Model", "moveTabLeft": "Move tab left", "moveTabRight": "Move tab right", "moveTabToSplit": "Move tab to split", @@ -682,13 +679,8 @@ "utilityCustomAgentsDescription": "Create your own utility agents with custom prompts.", "utilityCustomAgentsEmpty": "No custom utility agents.", "utilityCustomAgentsTitle": "Custom utility agents", - "utilityDefaultModelAriaLabel": "Default utility model settings", "utilityDefaultModelDescription": "Select the profile used by built-in utility actions. The profile controls the model, permissions, and launch settings.", "utilityDefaultModelTitle": "Default utility agent model", - "utilityDefaultWithModel": "Default ({{model}})", - "utilityModelNotConfigured": "Not configured", - "utilitySelectAgent": "Select agent...", - "utilitySelectModel": "Select model...", "utilityAgentProfile": "Agent profile", "utilitySelectProfile": "Select profile...", "utilitySearchProfiles": "Search agent profiles...", diff --git a/apps/web/src/locales/en/task.json b/apps/web/src/locales/en/task.json index 6204d98e02..e01ca99d7e 100644 --- a/apps/web/src/locales/en/task.json +++ b/apps/web/src/locales/en/task.json @@ -1009,7 +1009,6 @@ "remoteStateLine": "State: {{state}}", "remoteStatusFailedLine": "Status failed: {{error}}", "currentPRCommit": "Current PR commit", - "currentPRCommits": "Current PR commits", "remoteUrlPressEnter": "<0>Remote URL; press Enter to submit it.", "remove": "Remove", "remove2": "Remove {{name}}", @@ -1499,6 +1498,7 @@ "workspaceRepository": "Workspace repository", "workspaceRequiresConfiguration": "Workspace requires configuration", "workspaceUnavailable": "Workspace unavailable", + "unknownTaskFallback": "(unknown task)", "worktree": "Worktree", "wouldCreateABlockerCycle": "Would create a blocker cycle: {{cycle}}", "writeAComment": "Write a comment…", diff --git a/apps/web/src/locales/pseudo/agents.json b/apps/web/src/locales/pseudo/agents.json index 49b530d1ef..f9b7b19369 100644 --- a/apps/web/src/locales/pseudo/agents.json +++ b/apps/web/src/locales/pseudo/agents.json @@ -161,7 +161,6 @@ "profileNameRequiredTitle": "Ƥŕōƒĩĺē ńàḿē ĩś ŕēqũĩŕēď", "profileNotFound": "Ƥŕōƒĩĺē ńōţ ƒōũńď.", "profileSettings": "Ƥŕōƒĩĺē śēţţĩńĝś", - "profileStartModelSettings": "Ƥŕōƒĩĺē śţàŕţ ḿōďēĺ śēţţĩńĝś", "promptTokenHint": "<0>{{token}} ŵĩĺĺ ƀē ŕēƥĺàćēď ŵĩţĥ ŷōũŕ ţàśķ ďēśćŕĩƥţĩōń ōŕ ƒōĺĺōŵ-ũƥ ḿēśśàĝē.", "ptyConnectionError": "Ćōńńēćţĩōń ēŕŕōŕ", "refresh": "Ŕēƒŕēśĥ", @@ -185,7 +184,6 @@ "scanningForInstalledAgents": "Śćàńńĩńĝ ƒōŕ ĩńśţàĺĺēď àĝēńţś...", "searchModels": "Śēàŕćĥ ḿōďēĺś...", "searchModes": "Śēàŕćĥ ḿōďēś...", - "selectAModel": "Śēĺēćţ à ḿōďēĺ...", "selectAModelShort": "Śēĺēćţ à ḿōďēĺ", "selectModel": "Śēĺēćţ ḿōďēĺ...", "sessionEnded": "Śēśśĩōń ēńďēď.", diff --git a/apps/web/src/locales/pseudo/common.json b/apps/web/src/locales/pseudo/common.json index f2537dfc5b..13b71804b9 100644 --- a/apps/web/src/locales/pseudo/common.json +++ b/apps/web/src/locales/pseudo/common.json @@ -319,6 +319,8 @@ "reloadTheApplicationToTryAgain": "Ŕēĺōàď ţĥē àƥƥĺĩćàţĩōń ţō ţŕŷ àĝàĩń.", "rename": "Ŕēńàḿē", "repository": "Ŕēƥōśĩţōŕŷ", + "noRepositoryOption": "(ńō ŕēƥōśĩţōŕŷ)", + "repositoryDefaultBranchOption": "(ŕēƥōśĩţōŕŷ ďēƒàũĺţ ƀŕàńćĥ)", "repositoryName": "Ŕēƥōśĩţōŕŷ ńàḿē", "requestFailed": "Ŕēqũēśţ ƒàĩĺēď", "reset": "Ŕēśēţ", @@ -422,6 +424,9 @@ "untitledTask": "Ũńţĩţĺēď ţàśķ", "usefulCommandsAgentSkills": "Ũśēƒũĺ ćōḿḿàńďś & àĝēńţ śķĩĺĺś", "useScratchInstead": "Ũśē śćŕàţćĥ ĩńśţēàď", + "useStepDefaultOption": "(ũśē śţēƥ ďēƒàũĺţ)", + "watcherLoadingBranchesPlaceholder": "Ĺōàďĩńĝ…", + "watcherPickRepositoryPlaceholder": "Ƥĩćķ à ŕēƥōśĩţōŕŷ ƒĩŕśţ", "useThisFolder": "Ũśē ţĥĩś ƒōĺďēŕ", "utilities": "Ũţĩĺĩţĩēś", "viewFullReleaseOnGithub": "Vĩēŵ ƒũĺĺ ŕēĺēàśē ōń ĜĩţĤũƀ", diff --git a/apps/web/src/locales/pseudo/github.json b/apps/web/src/locales/pseudo/github.json index 33629bf795..275751f3f4 100644 --- a/apps/web/src/locales/pseudo/github.json +++ b/apps/web/src/locales/pseudo/github.json @@ -531,7 +531,6 @@ "thisWorkspaceUsesOneCredentialFor": "Ţĥĩś ŵōŕķśƥàćē ũśēś ōńē ćŕēďēńţĩàĺ ƒōŕ ŕēƥōśĩţōŕŷ śŷńć, ŵàţćĥēś, ƀàćķĝŕōũńď ĵōƀś, àńď ḿàńàĝēď àĝēńţ ĜĩţĤũƀ àććēśś. Ēxēćũţōŕ ƥŕōƒĩĺē ćŕēďēńţĩàĺś ćàń śţĩĺĺ ţàķē ƥŕēćēďēńćē.", "titleIssues": "Ĩśśũēś", "titlePullRequests": "Ƥũĺĺ ŕēqũēśţś", - "togglePrEventPromptAutomation": "Ţōĝĝĺē ƤŔ ēvēńţ ƥŕōḿƥţ àũţōḿàţĩōń", "toggleReviewFollowUpAutomation": "Ţōĝĝĺē ŕēvĩēŵ ƒōĺĺōŵ-ũƥ àũţōḿàţĩōń", "typeACustomQueryFirst": "Ţŷƥē à ćũśţōḿ qũēŕŷ ƒĩŕśţ", "typeOwnerRepoToSearch": "Ţŷƥē {{format}} ţō śēàŕćĥ", diff --git a/apps/web/src/locales/pseudo/settings.json b/apps/web/src/locales/pseudo/settings.json index 5056e76318..72654969e3 100644 --- a/apps/web/src/locales/pseudo/settings.json +++ b/apps/web/src/locales/pseudo/settings.json @@ -81,7 +81,6 @@ "completeTheRequiredEditorFieldsBefore": "Ćōḿƥĺēţē ţĥē ŕēqũĩŕēď ēďĩţōŕ ƒĩēĺďś ƀēƒōŕē śàvĩńĝ.", "configChatAgentDescription": "Ćĥōōśē ŵĥĩćĥ àĝēńţ ƥŕōƒĩĺē ţō ũśē ƒōŕ ţĥē Ćōńƒĩĝũŕàţĩōń Ćĥàţ. Ţĥĩś àĝēńţ ćàń ḿàńàĝē ŷōũŕ ŵōŕķƒĺōŵś, àĝēńţ ƥŕōƒĩĺēś, àńď ḾĆƤ ćōńƒĩĝũŕàţĩōń.", "configChatAgentNoDefault": "Ńō ďēƒàũĺţ", - "configChatAgentPlaceholder": "Ćĥōōśē àń àĝēńţ ƥŕōƒĩĺē...", "configChatAgentTitle": "Ćōńƒĩĝũŕàţĩōń Ćĥàţ Àĝēńţ", "configureArchiveSafeguardsAndDefaultsFor": "Ćōńƒĩĝũŕē àŕćĥĩvē śàƒēĝũàŕďś àńď ďēƒàũĺţś ƒōŕ ţàśķś ćŕēàţēď ƀŷ àĝēńţś", "configureBackendAndExecutionResourceSampling": "Ćōńƒĩĝũŕē ƀàćķēńď àńď ēxēćũţĩōń ŕēśōũŕćē śàḿƥĺĩńĝ", @@ -413,8 +412,6 @@ "mergeWithTarget": "Ḿēŕĝē ŵĩţĥ {{target}}", "messageSubmitKey": "Ḿēśśàĝē Śũƀḿĩţ Ķēŷ", "metrics": "Ḿēţŕĩćś", - "mode": "Ḿōďē", - "model": "Ḿōďēĺ", "moveTabLeft": "Ḿōvē ţàƀ ĺēƒţ", "moveTabRight": "Ḿōvē ţàƀ ŕĩĝĥţ", "moveTabToSplit": "Ḿōvē ţàƀ ţō śƥĺĩţ", @@ -682,13 +679,8 @@ "utilityCustomAgentsDescription": "Ćŕēàţē ŷōũŕ ōŵń ũţĩĺĩţŷ àĝēńţś ŵĩţĥ ćũśţōḿ ƥŕōḿƥţś.", "utilityCustomAgentsEmpty": "Ńō ćũśţōḿ ũţĩĺĩţŷ àĝēńţś.", "utilityCustomAgentsTitle": "Ćũśţōḿ ũţĩĺĩţŷ àĝēńţś", - "utilityDefaultModelAriaLabel": "Ďēƒàũĺţ ũţĩĺĩţŷ ḿōďēĺ śēţţĩńĝś", "utilityDefaultModelDescription": "Śēĺēćţ ţĥē ƥŕōƒĩĺē ũśēď ƀŷ ƀũĩĺţ-ĩń ũţĩĺĩţŷ àćţĩōńś. Ţĥē ƥŕōƒĩĺē ćōńţŕōĺś ţĥē ḿōďēĺ, ƥēŕḿĩśśĩōńś, àńď ĺàũńćĥ śēţţĩńĝś.", "utilityDefaultModelTitle": "Ďēƒàũĺţ ũţĩĺĩţŷ àĝēńţ ḿōďēĺ", - "utilityDefaultWithModel": "Ďēƒàũĺţ ({{model}})", - "utilityModelNotConfigured": "Ńōţ ćōńƒĩĝũŕēď", - "utilitySelectAgent": "Śēĺēćţ àĝēńţ...", - "utilitySelectModel": "Śēĺēćţ ḿōďēĺ...", "utilityAgentProfile": "Àĝēńţ ƥŕōƒĩĺē", "utilitySelectProfile": "Śēĺēćţ ƥŕōƒĩĺē...", "utilitySearchProfiles": "Śēàŕćĥ àĝēńţ ƥŕōƒĩĺēś...", diff --git a/apps/web/src/locales/pseudo/task.json b/apps/web/src/locales/pseudo/task.json index f8021a5475..cd08d7911b 100644 --- a/apps/web/src/locales/pseudo/task.json +++ b/apps/web/src/locales/pseudo/task.json @@ -1009,7 +1009,6 @@ "remoteStateLine": "Śţàţē: {{state}}", "remoteStatusFailedLine": "Śţàţũś ƒàĩĺēď: {{error}}", "currentPRCommit": "Ćũŕŕēńţ ƤŔ ćōḿḿĩţ", - "currentPRCommits": "Ćũŕŕēńţ ƤŔ ćōḿḿĩţś", "remoteUrlPressEnter": "<0>Ŕēḿōţē ŨŔĹ; ƥŕēśś Ēńţēŕ ţō śũƀḿĩţ ĩţ.", "remove": "Ŕēḿōvē", "remove2": "Ŕēḿōvē {{name}}", @@ -1499,6 +1498,7 @@ "workspaceRepository": "Ŵōŕķśƥàćē ŕēƥōśĩţōŕŷ", "workspaceRequiresConfiguration": "Ŵōŕķśƥàćē ŕēqũĩŕēś ćōńƒĩĝũŕàţĩōń", "workspaceUnavailable": "Ŵōŕķśƥàćē ũńàvàĩĺàƀĺē", + "unknownTaskFallback": "(ũńķńōŵń ţàśķ)", "worktree": "Ŵōŕķţŕēē", "wouldCreateABlockerCycle": "Ŵōũĺď ćŕēàţē à ƀĺōćķēŕ ćŷćĺē: {{cycle}}", "writeAComment": "Ŵŕĩţē à ćōḿḿēńţ…", diff --git a/apps/web/src/locales/pt-pt/agents.json b/apps/web/src/locales/pt-pt/agents.json index 100d0d1a30..a26ebdaf91 100644 --- a/apps/web/src/locales/pt-pt/agents.json +++ b/apps/web/src/locales/pt-pt/agents.json @@ -153,7 +153,6 @@ "profileNameRequiredTitle": "O nome do perfil é obrigatório", "profileNotFound": "Perfil não encontrado.", "profileSettings": "Definições do perfil", - "profileStartModelSettings": "Definições do modelo inicial do perfil", "promptTokenHint": "<0>{{token}} será substituído pela descrição da sua tarefa ou pela mensagem de seguimento.", "ptyConnectionError": "Erro de ligação", "refresh": "Atualizar", @@ -177,7 +176,6 @@ "scanningForInstalledAgents": "À procura de agentes instalados...", "searchModels": "Pesquisar modelos...", "searchModes": "Pesquisar modos...", - "selectAModel": "Selecione um modelo...", "selectAModelShort": "Selecionar um modelo", "selectModel": "Selecionar modelo...", "sessionEnded": "A sessão terminou.", diff --git a/apps/web/src/locales/pt-pt/common.json b/apps/web/src/locales/pt-pt/common.json index fc30c131e1..b2c69907fe 100644 --- a/apps/web/src/locales/pt-pt/common.json +++ b/apps/web/src/locales/pt-pt/common.json @@ -312,6 +312,8 @@ "reloadTheApplicationToTryAgain": "Recarregue a aplicação para tentar novamente.", "rename": "Mudar o nome", "repository": "Repositório", + "noRepositoryOption": "(sem repositório)", + "repositoryDefaultBranchOption": "(ramo predefinido do repositório)", "repositoryName": "Nome do repositório", "requestFailed": "O pedido falhou", "reset": "Repor", @@ -413,6 +415,9 @@ "untitledTask": "Tarefa sem título", "usefulCommandsAgentSkills": "Comandos úteis e competências do agente", "useScratchInstead": "Usar antes uma área temporária", + "useStepDefaultOption": "(usar predefinição do passo)", + "watcherLoadingBranchesPlaceholder": "A carregar…", + "watcherPickRepositoryPlaceholder": "Escolha primeiro um repositório", "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.", diff --git a/apps/web/src/locales/pt-pt/github.json b/apps/web/src/locales/pt-pt/github.json index 645b1838fa..e897e183a6 100644 --- a/apps/web/src/locales/pt-pt/github.json +++ b/apps/web/src/locales/pt-pt/github.json @@ -512,7 +512,6 @@ "thisWorkspaceUsesOneCredentialFor": "Esta área de trabalho usa uma credencial para a sincronização de repositórios, as vigilâncias, as tarefas em segundo plano e o acesso ao GitHub dos agentes geridos. As credenciais dos perfis de executor podem continuar a ter precedência.", "titleIssues": "Issues", "titlePullRequests": "Pull requests", - "togglePrEventPromptAutomation": "Alternar a automatização de prompts de eventos de PR", "typeACustomQueryFirst": "Escreva primeiro uma consulta personalizada", "typeOwnerRepoToSearch": "Escreva {{format}} para pesquisar", "unableToLoadTheCurrentTask": "Não foi possível carregar a definição atual de acesso das tarefas.", diff --git a/apps/web/src/locales/pt-pt/settings.json b/apps/web/src/locales/pt-pt/settings.json index efda6f9eaf..886cb76ac2 100644 --- a/apps/web/src/locales/pt-pt/settings.json +++ b/apps/web/src/locales/pt-pt/settings.json @@ -79,7 +79,6 @@ "completeTheRequiredEditorFieldsBefore": "Preencha os campos obrigatórios do editor antes de guardar.", "configChatAgentDescription": "Escolha o perfil de agente a usar no Chat de configuração. Este agente pode gerir os seus fluxos de trabalho, perfis de agente e configuração MCP.", "configChatAgentNoDefault": "Sem pré-definição", - "configChatAgentPlaceholder": "Escolha um perfil de agente...", "configChatAgentTitle": "Agente do Chat de configuração", "configureArchiveSafeguardsAndDefaultsFor": "Configure as salvaguardas de arquivo e as pré-definições das tarefas criadas por agentes", "configureBackendAndExecutionResourceSampling": "Configure a amostragem de recursos do backend e da execução", @@ -378,7 +377,6 @@ "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.", @@ -407,7 +405,6 @@ "mergeWithTarget": "Fundir com {{target}}", "messageSubmitKey": "Tecla de envio de mensagens", "metrics": "Métricas", - "model": "Modelo", "moveTabLeft": "Mover o separador para a esquerda", "moveTabRight": "Mover o separador para a direita", "moveTabToSplit": "Mover o separador para uma divisão", @@ -667,13 +664,8 @@ "utilityCustomAgentsDescription": "Crie os seus próprios agentes utilitários com prompts personalizados.", "utilityCustomAgentsEmpty": "Não há agentes utilitários personalizados.", "utilityCustomAgentsTitle": "Agentes utilitários personalizados", - "utilityDefaultModelAriaLabel": "Definições do modelo utilitário pré-definido", "utilityDefaultModelDescription": "Selecione o perfil usado pelas ações utilitárias incorporadas. O perfil define o modelo, as permissões e as definições de execução.", "utilityDefaultModelTitle": "Modelo pré-definido dos agentes utilitários", - "utilityDefaultWithModel": "Pré-definido ({{model}})", - "utilityModelNotConfigured": "Não configurado", - "utilitySelectAgent": "Selecionar agente...", - "utilitySelectModel": "Selecionar modelo...", "utilityAgentProfile": "Perfil do agente", "utilitySelectProfile": "Selecionar perfil...", "utilitySearchProfiles": "Pesquisar perfis de agente...", diff --git a/apps/web/src/locales/pt-pt/task.json b/apps/web/src/locales/pt-pt/task.json index ddee8438de..82d522fde8 100644 --- a/apps/web/src/locales/pt-pt/task.json +++ b/apps/web/src/locales/pt-pt/task.json @@ -1009,7 +1009,6 @@ "remoteStateLine": "Estado: {{state}}", "remoteStatusFailedLine": "Falha ao obter o estado: {{error}}", "currentPRCommit": "Commit atual do PR", - "currentPRCommits": "Commits atuais do PR", "remoteUrlPressEnter": "<0>URL remoto; prima Enter para o submeter.", "remove": "Remover", "remove2": "Remover {{name}}", @@ -1499,6 +1498,7 @@ "workspaceRepository": "Repositório da área de trabalho", "workspaceRequiresConfiguration": "A área de trabalho requer configuração", "workspaceUnavailable": "Área de trabalho indisponível", + "unknownTaskFallback": "(tarefa desconhecida)", "worktree": "Worktree", "wouldCreateABlockerCycle": "Criaria um ciclo de bloqueios: {{cycle}}", "writeAComment": "Escreva um comentário…", diff --git a/apps/web/src/locales/zh-cn/agents.json b/apps/web/src/locales/zh-cn/agents.json index b08b895623..a5be3fc0b5 100644 --- a/apps/web/src/locales/zh-cn/agents.json +++ b/apps/web/src/locales/zh-cn/agents.json @@ -161,7 +161,6 @@ "profileNameRequiredTitle": "配置名称为必填项", "profileNotFound": "未找到配置。", "profileSettings": "配置设置", - "profileStartModelSettings": "配置启动模型设置", "promptTokenHint": "<0>{{token}} 将替换为你的任务描述或后续消息。", "ptyConnectionError": "连接错误", "refresh": "刷新", @@ -185,7 +184,6 @@ "scanningForInstalledAgents": "正在扫描已安装的代理…", "searchModels": "搜索模型…", "searchModes": "搜索模式…", - "selectAModel": "选择模型…", "selectAModelShort": "选择模型", "selectModel": "选择模型...", "sessionEnded": "会话已结束。", diff --git a/apps/web/src/locales/zh-cn/common.json b/apps/web/src/locales/zh-cn/common.json index 8987bd00a5..eb96399834 100644 --- a/apps/web/src/locales/zh-cn/common.json +++ b/apps/web/src/locales/zh-cn/common.json @@ -319,6 +319,8 @@ "reloadTheApplicationToTryAgain": "重新加载应用以重试。", "rename": "重命名", "repository": "仓库", + "noRepositoryOption": "(无仓库)", + "repositoryDefaultBranchOption": "(仓库默认分支)", "repositoryName": "仓库名称", "requestFailed": "请求失败", "reset": "重置", @@ -422,6 +424,9 @@ "untitledTask": "未命名任务", "usefulCommandsAgentSkills": "实用命令与代理技能", "useScratchInstead": "改用临时工作区", + "useStepDefaultOption": "(使用步骤默认值)", + "watcherLoadingBranchesPlaceholder": "加载中…", + "watcherPickRepositoryPlaceholder": "请先选择仓库", "useThisFolder": "使用此文件夹", "utilities": "实用工具", "viewFullReleaseOnGithub": "在 GitHub 上查看完整发行说明", diff --git a/apps/web/src/locales/zh-cn/github.json b/apps/web/src/locales/zh-cn/github.json index 6ccb67603d..19e94853aa 100644 --- a/apps/web/src/locales/zh-cn/github.json +++ b/apps/web/src/locales/zh-cn/github.json @@ -529,7 +529,6 @@ "thisWorkspaceUsesOneCredentialFor": "此工作区使用一个凭据执行仓库同步、监控、后台任务和托管代理的 GitHub 访问。执行器配置凭据仍可优先使用。", "titleIssues": "议题", "titlePullRequests": "拉取请求", - "togglePrEventPromptAutomation": "切换 PR 事件提示自动化", "typeACustomQueryFirst": "请先输入自定义查询", "typeOwnerRepoToSearch": "输入 {{format}} 进行搜索", "unableToLoadTheCurrentTask": "无法加载当前任务访问设置。", diff --git a/apps/web/src/locales/zh-cn/settings.json b/apps/web/src/locales/zh-cn/settings.json index 5fc5256432..18968674aa 100644 --- a/apps/web/src/locales/zh-cn/settings.json +++ b/apps/web/src/locales/zh-cn/settings.json @@ -81,7 +81,6 @@ "completeTheRequiredEditorFieldsBefore": "保存前请填写必需的编辑器字段。", "configChatAgentDescription": "选择用于配置聊天的代理配置。此代理可管理你的工作流、代理配置与 MCP 配置。", "configChatAgentNoDefault": "无默认", - "configChatAgentPlaceholder": "选择代理配置…", "configChatAgentTitle": "配置聊天代理", "configureArchiveSafeguardsAndDefaultsFor": "配置代理创建任务时的归档保护和默认设置", "configureBackendAndExecutionResourceSampling": "配置后端和执行资源采样", @@ -413,8 +412,6 @@ "mergeWithTarget": "与 {{target}} 合并", "messageSubmitKey": "消息提交快捷键", "metrics": "指标", - "mode": "模式", - "model": "模型", "moveTabLeft": "左移标签页", "moveTabRight": "右移标签页", "moveTabToSplit": "将标签页移至拆分区域", @@ -682,13 +679,8 @@ "utilityCustomAgentsDescription": "使用自定义提示词创建你自己的实用代理。", "utilityCustomAgentsEmpty": "无自定义实用代理。", "utilityCustomAgentsTitle": "自定义实用代理", - "utilityDefaultModelAriaLabel": "默认实用模型设置", "utilityDefaultModelDescription": "选择内置实用操作使用的配置。配置包含模型、权限和启动设置。", "utilityDefaultModelTitle": "默认实用代理模型", - "utilityDefaultWithModel": "默认({{model}})", - "utilityModelNotConfigured": "未配置", - "utilitySelectAgent": "选择代理…", - "utilitySelectModel": "选择模型…", "utilityAgentProfile": "代理配置", "utilitySelectProfile": "选择配置…", "utilitySearchProfiles": "搜索代理配置…", diff --git a/apps/web/src/locales/zh-cn/task.json b/apps/web/src/locales/zh-cn/task.json index e7f1107065..43572a937a 100644 --- a/apps/web/src/locales/zh-cn/task.json +++ b/apps/web/src/locales/zh-cn/task.json @@ -1009,7 +1009,6 @@ "remoteStateLine": "状态:{{state}}", "remoteStatusFailedLine": "状态失败:{{error}}", "currentPRCommit": "当前 PR 提交", - "currentPRCommits": "当前 PR 提交", "remoteUrlPressEnter": "<0>远程 URL; 按 Enter 提交。", "remove": "移除", "remove2": "移除 {{name}}", @@ -1499,6 +1498,7 @@ "workspaceRepository": "工作区仓库", "workspaceRequiresConfiguration": "工作区需要配置", "workspaceUnavailable": "工作区不可用", + "unknownTaskFallback": "(未知任务)", "worktree": "工作树", "wouldCreateABlockerCycle": "会形成阻塞循环:{{cycle}}", "writeAComment": "撰写评论…", diff --git a/docs/plans/i18n-audit-watcher-copy/plan.md b/docs/plans/i18n-audit-watcher-copy/plan.md new file mode 100644 index 0000000000..04d0fc4b95 --- /dev/null +++ b/docs/plans/i18n-audit-watcher-copy/plan.md @@ -0,0 +1,95 @@ +--- +spec: docs/specs/i18n-audit-watcher-copy/spec.md +created: 2026-08-12 +status: completed +--- + +# Implementation Plan: Watcher And Task Fallback Localization + +## Overview + +Separate stable watcher domain values from render-time copy, then update every watcher consumer and +the sender-task fallback. Finish by deleting the 12 audit-confirmed obsolete keys from all catalogs +and running focused tests plus the required i18n audits. The confirmed root cause is that display +labels were exported from plain helper modules as English constants or returned as English strings; +the orphan keys survived UI replacements without source references. + +## Frontend + +### Watcher profile defaults + +- In `apps/web/lib/watcher-profile-default.ts`, retain `STEP_DEFAULT` and `resolveProfileId` as the + untranslated domain contract and remove `STEP_DEFAULT_LABEL`. +- Add a shared catalog key for the step-default display label and resolve it through each dialog's + existing `useTranslation()` call in GitHub issue/review, GitLab issue/review, Jira, and Linear + watcher dialogs. +- Keep the sentinel-to-empty-string payload behavior unchanged and update focused helper/dialog tests + where they assert the rendered option. + +### Watcher repository defaults + +- In `apps/web/lib/watcher-repository-default.ts`, retain only sentinel/domain behavior and replace + `branchPlaceholder`'s English return values with a locale-neutral state/key result. +- In `apps/web/components/watcher-repository-fields.tsx`, resolve no-repository, default-branch, + repository-first, and loading copy from catalog keys during render. Never translate repository + names, IDs, or branch names. +- Extend `apps/web/lib/watcher-repository-default.test.ts` to prove locale-neutral state selection and + unchanged sentinel normalization before changing production behavior. + +### Sender task fallback + +- In `apps/web/components/task/chat/messages/sender-task-badge.tsx`, resolve the unknown-task fallback + from `task` catalog copy inside the component. +- Extend the nearest existing chat-message component test to render a sender with no live or snapshot + title and assert the visible fallback and tooltip text. + +### Catalog cleanup + +- Remove the following confirmed obsolete entries from English, pseudo, Portuguese, and Simplified + Chinese catalogs: `agents:profileStartModelSettings`, `agents:selectAModel`, + `github:togglePrEventPromptAutomation`, `settings:configChatAgentPlaceholder`, `settings:mode`, + `settings:model`, `settings:utilityDefaultModelAriaLabel`, `settings:utilityDefaultWithModel`, + `settings:utilityModelNotConfigured`, `settings:utilitySelectAgent`, + `settings:utilitySelectModel`, and `task:currentPRCommits`. +- The source-history audit ties these keys to superseded profile model controls, profile-based utility + agent pickers, per-PR automation controls, configuration chat selection, core Voice Mode, and an old + Changes header. Current replacement surfaces already use different, more specific keys. + +### Mobile parity + +This is copy-only work inside existing responsive dialogs and badges. It changes no composition, +touch target, scrolling, navigation, or viewport branch, so focused component/rendered tests satisfy +the mobile-parity exception; no new mobile Playwright scenario is required. + +## Tests + +- **What:** watcher sentinels remain stable while placeholder selection becomes locale-neutral. + **File:** `apps/web/lib/watcher-profile-default.test.ts` and + `apps/web/lib/watcher-repository-default.test.ts`. **How:** focused Vitest unit assertions. +- **What:** translated watcher defaults appear through representative dialog/shared-field render + paths without changing submitted IDs. **File:** existing watcher dialog tests and, if the shared + fields lack a suitable harness, `apps/web/components/watcher-repository-fields.test.tsx`. + **How:** focused Vitest component tests using the initialized test i18n instance. +- **What:** an absent sender task title renders the translated fallback. **File:** + `apps/web/components/task/chat/messages/chat-message.test.tsx` or the closest existing sender badge + harness. **How:** focused Vitest component assertion. +- **What:** catalogs contain no audit-reported orphan keys and new references are valid. **How:** + `pnpm run i18n:check` and `pnpm run i18n:sweep -- components lib` from `apps/web`. + +## Verification Results + +- Focused Vitest: 46 tests passed across 7 files, including repository-first and branch-loading rendered placeholder coverage added during PR review. +- CI remediation: 31 focused component tests and `pnpm run typecheck` passed after allowing the disabled branch select to carry an unset presentation value; live locale switching is asserted with exact English and pseudo fallback copy. +- `pnpm run i18n:check`: passed with 0 orphans, pseudo in sync, and 127 unchanged out-of-scope real-locale parity advisories. +- `pnpm run i18n:sweep -- components lib`: completed across 1,850 files; only the two documented prompt-builder plural findings and 91 unrelated review-by-eye candidates remain. + +## Implementation Waves And Parallel Candidates + +Wave 1: +- [x] [task-01-localize-watcher-and-task-fallbacks](task-01-localize-watcher-and-task-fallbacks.md) + +Wave 2: +- [x] [task-02-remove-obsolete-catalog-entries](task-02-remove-obsolete-catalog-entries.md) + +Tasks are sequential because both touch shared locale catalogs and the final orphan audit depends on +the new references from Task 01. diff --git a/docs/plans/i18n-audit-watcher-copy/task-01-localize-watcher-and-task-fallbacks.md b/docs/plans/i18n-audit-watcher-copy/task-01-localize-watcher-and-task-fallbacks.md new file mode 100644 index 0000000000..ce177d50f5 --- /dev/null +++ b/docs/plans/i18n-audit-watcher-copy/task-01-localize-watcher-and-task-fallbacks.md @@ -0,0 +1,74 @@ +--- +id: "01-localize-watcher-and-task-fallbacks" +title: "Localize watcher and task fallbacks" +status: done +wave: 1 +depends_on: [] +plan: "plan.md" +spec: "../../specs/i18n-audit-watcher-copy/spec.md" +--- + +# Task 01: Localize Watcher And Task Fallbacks + +## Acceptance + +- Every GitHub, GitLab, Jira, and Linear watcher profile default resolves localized copy during + render while sentinel values and submitted empty IDs remain unchanged. +- Watcher repository empty/loading/default copy resolves during render while repository and branch + domain values remain untranslated. +- A sender badge with no live or snapshot title renders the localized unknown-task fallback. + +## Verification + +```bash +cd apps/web && pnpm test -- --run lib/watcher-profile-default.test.ts lib/watcher-repository-default.test.ts components/task/chat/messages/chat-message.test.tsx components/github/review-watch-dialog.test.tsx components/gitlab/issue-watch-dialog.test.tsx components/gitlab/review-watch-dialog.test.tsx +``` + +## Files likely touched + +- `apps/web/lib/watcher-profile-default.ts` +- `apps/web/lib/watcher-profile-default.test.ts` +- `apps/web/lib/watcher-repository-default.ts` +- `apps/web/lib/watcher-repository-default.test.ts` +- `apps/web/components/watcher-repository-fields.tsx` +- focused shared-field test if needed +- `apps/web/components/github/issue-watch-dialog.tsx` +- `apps/web/components/github/review-watch-dialog.tsx` +- `apps/web/components/gitlab/watch-dialog.tsx` +- `apps/web/components/jira/jira-issue-watch-dialog.tsx` +- `apps/web/components/linear/linear-issue-watch-dialog.tsx` +- `apps/web/components/task/chat/messages/sender-task-badge.tsx` +- closest existing sender badge/chat-message test +- affected locale namespace files for newly referenced keys +- this task file and `plan.md` + +## Dependencies + +None. + +## Parallelism + +Sequential. The dialogs share helper and catalog contracts. + +## Inputs + +- Spec scenarios for watcher profile defaults, repository states, and unknown sender tasks. +- Existing sentinel normalization tests and existing watcher dialog component-test patterns. +- Frontend i18n rule forbidding module-scope `t()` and translation of domain values. + +## Output contract + +Report RED/GREEN evidence, exact key ownership, unchanged sentinel/payload behavior, files changed, +tests run, blockers and risks, then update this task and `plan.md` statuses. + +## Results + +RED: `cd apps/web && pnpm test -- --run lib/watcher-repository-default.test.ts components/task/chat/messages/chat-message.test.tsx` failed on the three English-returning placeholder states and the pseudo-locale unknown-task fallback. The remaining chat failures were cascading locale leakage after the intended assertion aborted. + +GREEN: `cd apps/web && pnpm test -- --run lib/watcher-profile-default.test.ts lib/watcher-repository-default.test.ts components/task/chat/messages/chat-message.test.tsx components/github/review-watch-dialog.test.tsx components/gitlab/issue-watch-dialog.test.tsx components/gitlab/review-watch-dialog.test.tsx` passed 44 tests in 6 files. + +PR review remediation: `cd apps/web && pnpm test -- --run components/watcher-repository-fields.test.tsx lib/watcher-profile-default.test.ts lib/watcher-repository-default.test.ts components/task/chat/messages/chat-message.test.tsx components/github/review-watch-dialog.test.tsx components/gitlab/issue-watch-dialog.test.tsx components/gitlab/review-watch-dialog.test.tsx` passed 46 tests in 7 files after proving that repository-first and loading placeholders remain visible while the branch selector is disabled. + +CI/review follow-up: `cd apps/web && pnpm test -- --run components/watcher-repository-fields.test.tsx components/task/chat/messages/chat-message.test.tsx && pnpm run typecheck` passed 31 tests in 2 files and the full web TypeScript check. The sender fallback test now proves an already-mounted badge changes from exact English to exact pseudo copy after a live locale switch. + +Sentinel values and empty-ID normalization are unchanged. Display copy resolves inside React render paths; no external side effects or security boundaries apply. diff --git a/docs/plans/i18n-audit-watcher-copy/task-02-remove-obsolete-catalog-entries.md b/docs/plans/i18n-audit-watcher-copy/task-02-remove-obsolete-catalog-entries.md new file mode 100644 index 0000000000..a662829269 --- /dev/null +++ b/docs/plans/i18n-audit-watcher-copy/task-02-remove-obsolete-catalog-entries.md @@ -0,0 +1,62 @@ +--- +id: "02-remove-obsolete-catalog-entries" +title: "Remove obsolete catalog entries" +status: done +wave: 2 +depends_on: ["01-localize-watcher-and-task-fallbacks"] +plan: "plan.md" +spec: "../../specs/i18n-audit-watcher-copy/spec.md" +--- + +# Task 02: Remove Obsolete Catalog Entries + +## Acceptance + +- All 12 audit-reported keys, confirmed obsolete through current-source and source-history review, + are absent from English, pseudo, Portuguese, and Simplified Chinese catalogs. +- `i18n:check` reports none of those orphan entries and pseudo remains synchronized; existing + real-locale parity advisories are not repaired in this task. +- The required component/lib sweep reports no hardcoded user-facing copy introduced or retained by + this repair. + +## Verification + +```bash +cd apps/web && pnpm run i18n:check && pnpm run i18n:sweep -- components lib +``` + +## Files likely touched + +- `apps/web/src/locales/en/agents.json` +- `apps/web/src/locales/en/github.json` +- `apps/web/src/locales/en/settings.json` +- `apps/web/src/locales/en/task.json` +- matching files under `apps/web/src/locales/pseudo/` +- matching files under `apps/web/src/locales/pt-pt/` +- matching files under `apps/web/src/locales/zh-cn/` +- this task file and `plan.md` + +## Dependencies + +Task 01. + +## Parallelism + +Sequential. It validates shared catalogs after Task 01 adds the new fallback references. + +## Inputs + +- The source-history classification recorded in `plan.md`. +- Task 01's final catalog key set. +- User constraint excluding the real-locale parity finding. + +## Output contract + +Report the removed key list, exact checker/sweep results including unchanged advisory count, files +changed, blockers and risks, then update this task and `plan.md` statuses. + +## Results + +`cd apps/web && pnpm run i18n:check && pnpm run i18n:sweep -- components lib` passed. The checker reports 0 orphan entries, pseudo in sync, and the unchanged 127 out-of-scope Portuguese/Chinese parity advisories. The sweep scanned 1,850 files and reported only the two documented prompt-builder plural concatenations plus 91 review-by-eye literals; none is introduced or retained by this repair. + +All 12 named obsolete keys were removed from English, pseudo, Portuguese, and Simplified Chinese catalogs. External side effects and security/trust boundaries: None. diff --git a/docs/specs/i18n-audit-watcher-copy/spec.md b/docs/specs/i18n-audit-watcher-copy/spec.md new file mode 100644 index 0000000000..498bf4d4d1 --- /dev/null +++ b/docs/specs/i18n-audit-watcher-copy/spec.md @@ -0,0 +1,46 @@ +--- +status: draft +created: 2026-08-12 +owner: Kandev +--- + +# Watcher And Task Fallback Localization + +## Why + +Watcher dialogs and inter-task message badges expose several English-only fallback labels even +when another locale is active. Obsolete catalog entries also obscure whether the current UI is +fully covered by localization checks. + +## What + +- Watcher profile selectors display the localized equivalent of `(use step default)` while their + stored sentinel and submitted empty profile ID remain locale-independent. +- Watcher repository and branch selectors localize the no-repository option, default-branch option, + repository-first prompt, and loading prompt at render time. Repository IDs, branch names, and + sentinel values remain untranslated. +- Inter-task sender badges display a localized unknown-task fallback when neither a live title nor a + snapshot title exists. +- English catalog entries with no current source reference and no current user-interface owner are + removed from every catalog. Current behavior is not changed merely to retain an obsolete key. +- Translation lookup does not occur at module scope. + +## Scenarios + +- **GIVEN** any GitHub issue/review, GitLab issue/review, Jira, or Linear watcher dialog is rendered + in a non-English locale, **WHEN** no agent or executor profile override is selected, **THEN** the + step-default option and placeholder use that locale while saving still submits an empty profile ID. +- **GIVEN** a watcher repository picker is rendered in a non-English locale, **WHEN** repository and + branch selection move through empty, loading, and loaded states, **THEN** only UI fallback copy is + translated and all repository/branch domain values remain unchanged. +- **GIVEN** an inter-task message has no resolvable live or snapshot task title, **WHEN** its sender + badge renders, **THEN** the badge and tooltip use the localized unknown-task fallback. +- **GIVEN** the catalogs contain the 12 audit-reported unreferenced keys, **WHEN** the i18n checker + runs after cleanup, **THEN** it reports no orphan entries from that set and pseudo remains in sync. + +## Out of scope + +- Resolving the existing Portuguese and Simplified Chinese catalog parity advisories. +- Changing watcher persistence, payload contracts, repository selection behavior, dialog layout, or + mobile interaction behavior. +- Restoring UI that was previously removed or replaced solely to consume an old catalog key.