From 32c927f386da113e7a2304408b5c2e71fea89655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Salavisa?= Date: Sun, 2 Aug 2026 21:59:46 +0200 Subject: [PATCH] =?UTF-8?q?feat(i18n):=20localize=20Settings=20=E2=86=92?= =?UTF-8?q?=20Integrations=20=E2=86=92=20Jira=20(#2177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kandev Agent Co-authored-by: Claude Opus 5 (1M context) --- .../jira/jira-issue-watch-dialog.tsx | 116 ++++--- .../jira/jira-issue-watch-placeholders.ts | 146 ++++---- .../jira/jira-issue-watch-table.tsx | 55 +-- .../jira/jira-issue-watchers-section.tsx | 49 +-- .../components/jira/jira-secret-help.test.tsx | 76 +++++ apps/web/components/jira/jira-secret-help.tsx | 191 +++++++++++ apps/web/components/jira/jira-settings.tsx | 319 ++++++++---------- apps/web/components/jira/my-jira/presets.ts | 31 +- .../components/jira/task-presets-section.tsx | 186 ++++++---- .../e2e/tests/i18n/pseudo-coverage.spec.ts | 29 +- apps/web/eslint.i18n.options.mjs | 28 ++ apps/web/src/locales/en/jira.json | 149 ++++++++ apps/web/src/locales/pseudo/jira.json | 149 ++++++++ 13 files changed, 1097 insertions(+), 427 deletions(-) create mode 100644 apps/web/components/jira/jira-secret-help.test.tsx create mode 100644 apps/web/components/jira/jira-secret-help.tsx create mode 100644 apps/web/src/locales/en/jira.json create mode 100644 apps/web/src/locales/pseudo/jira.json diff --git a/apps/web/components/jira/jira-issue-watch-dialog.tsx b/apps/web/components/jira/jira-issue-watch-dialog.tsx index d918d758e8..0aea0d5994 100644 --- a/apps/web/components/jira/jira-issue-watch-dialog.tsx +++ b/apps/web/components/jira/jira-issue-watch-dialog.tsx @@ -28,7 +28,7 @@ import { computeEditorHeight, } from "@/components/settings/profile-edit/script-editor"; import { - JIRA_ISSUE_WATCH_PLACEHOLDERS, + 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"; @@ -39,6 +39,8 @@ import type { JiraIssueWatch, UpdateJiraIssueWatchInput, } from "@/lib/types/jira"; +import { useTranslation } from "react-i18next"; +import type { TFunction } from "i18next"; type Props = { open: boolean; @@ -88,6 +90,10 @@ function parseMaxInflightTasks(raw: string): number | null | "invalid" { return n; } +// JQL, not copy. Every token here is parsed by Jira — field names (`project`, +// `status`, `created`), operators and a project key — and the string is +// persisted on the watch and sent upstream verbatim. Translating any of it would +// produce a query Jira rejects. const DEFAULT_JQL = `project = PROJ AND status = "Open" ORDER BY created DESC`; function makeEmptyForm(workspaceId: string): FormState { @@ -192,6 +198,7 @@ function JQLField({ jql: string; onChange: (v: string) => void; }) { + const { t } = useTranslation(); const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null); const [testing, setTesting] = useState(false); const handleTest = useCallback(async () => { @@ -199,13 +206,14 @@ function JQLField({ setResult(null); try { const res = await searchJiraTickets({ jql, maxResults: 5 }, { workspaceId }); - setResult({ ok: true, message: `Matched ${res.tickets.length} ticket(s) in this page.` }); + // "ticket(s)" was an English plural hack; the count now selects the form. + setResult({ ok: true, message: t("jira:matchedTickets", { count: res.tickets.length }) }); } catch (err) { - setResult({ ok: false, message: `JQL error: ${String(err)}` }); + setResult({ ok: false, message: t("jira:jqlError", { error: String(err) }) }); } finally { setTesting(false); } - }, [workspaceId, jql]); + }, [workspaceId, jql, t]); return (
@@ -219,19 +227,20 @@ function JQLField({ disabled={!jql.trim() || testing} className="cursor-pointer h-7" > - {testing ? "Testing…" : "Test JQL"} + {testing ? t("jira:testingJql") : t("jira:testJql")}