diff --git a/packages/app/e2e/support/helpers/pr-pane.ts b/packages/app/e2e/support/helpers/pr-pane.ts index 56f0ef444c..37fe719e0f 100644 --- a/packages/app/e2e/support/helpers/pr-pane.ts +++ b/packages/app/e2e/support/helpers/pr-pane.ts @@ -1,5 +1,5 @@ import { expect, type Page } from "@playwright/test"; -import { getStateLabel } from "@/git/pull-request-panel/data"; +import { en } from "@/i18n/resources/en"; import { openPullRequestPanel } from "./workspace-tabs"; export async function openPrPane(page: Page): Promise { @@ -14,7 +14,7 @@ export async function expectPrPaneState( page: Page, state: "open" | "merged" | "closed" | "draft", ): Promise { - await expect(page.getByTestId("pr-pane-state")).toHaveText(getStateLabel(state), { + await expect(page.getByTestId("pr-pane-state")).toHaveText(en.workspace.git.pr.states[state], { timeout: 15_000, }); } diff --git a/packages/app/src/git/pull-request-panel/data.test.ts b/packages/app/src/git/pull-request-panel/data.test.ts index 21b145bedc..837433417e 100644 --- a/packages/app/src/git/pull-request-panel/data.test.ts +++ b/packages/app/src/git/pull-request-panel/data.test.ts @@ -9,12 +9,13 @@ import { isPipelineActiveStatus, mapPipelineStatus, } from "@/git/forges/gitlab"; +import { i18n } from "@/i18n/i18next"; import { IDENTITY_COLOR_NAMES, identityColor } from "@/styles/identity-colors"; import { deriveAvatarColor, formatAge, - getActivityVerb, - getStateLabel, + getActivityVerbKey, + getStateLabelKey, mapPrPaneData, } from "./data"; @@ -779,37 +780,34 @@ describe("formatAge", () => { }); }); -describe("getStateLabel", () => { +const english = i18n.getFixedT("en"); +const french = i18n.getFixedT("fr"); + +describe("getStateLabelKey", () => { it.each([ - ["open", "Open"], - ["draft", "Draft"], - ["merged", "Merged"], - ["closed", "Closed"], - ] as const)("maps %s → %s", (state, expected) => { - expect(getStateLabel(state)).toBe(expected); + ["open", "Open", "Ouverte"], + ["draft", "Draft", "Brouillon"], + ["merged", "Merged", "Fusionnée"], + ["closed", "Closed", "Fermée"], + ] as const)("labels %s as %s, and %s in French", (state, englishLabel, frenchLabel) => { + expect(english(getStateLabelKey(state))).toBe(englishLabel); + expect(french(getStateLabelKey(state))).toBe(frenchLabel); }); }); -describe("getActivityVerb", () => { - it("returns Commented for comment kind", () => { - expect(getActivityVerb({ kind: "comment" })).toBe("Commented"); - }); - - it("returns Approved for approved review", () => { - expect(getActivityVerb({ kind: "review", reviewState: "approved" })).toBe("Approved"); - }); - - it("returns Requested changes for changes_requested review", () => { - expect(getActivityVerb({ kind: "review", reviewState: "changes_requested" })).toBe( +describe("getActivityVerbKey", () => { + it.each([ + [{ kind: "comment" }, "Commented", "A commenté"], + [{ kind: "review", reviewState: "approved" }, "Approved", "A approuvé"], + [ + { kind: "review", reviewState: "changes_requested" }, "Requested changes", - ); - }); - - it("returns Reviewed for a commented review with body (generic case)", () => { - expect(getActivityVerb({ kind: "review", reviewState: "commented" })).toBe("Reviewed"); - }); - - it("returns Reviewed when reviewState is undefined", () => { - expect(getActivityVerb({ kind: "review" })).toBe("Reviewed"); + "A demandé des modifications", + ], + [{ kind: "review", reviewState: "commented" }, "Reviewed", "A relu"], + [{ kind: "review" }, "Reviewed", "A relu"], + ] as const)("labels %o as %s, and %s in French", (item, englishVerb, frenchVerb) => { + expect(english(getActivityVerbKey(item))).toBe(englishVerb); + expect(french(getActivityVerbKey(item))).toBe(frenchVerb); }); }); diff --git a/packages/app/src/git/pull-request-panel/data.ts b/packages/app/src/git/pull-request-panel/data.ts index 0f1e2014e9..d1275f2f1e 100644 --- a/packages/app/src/git/pull-request-panel/data.ts +++ b/packages/app/src/git/pull-request-panel/data.ts @@ -318,16 +318,18 @@ function parsePullRequestNumber(url: string): number | null { } } -export function getStateLabel(state: PrState): string { - if (state === "draft") return "Draft"; - if (state === "merged") return "Merged"; - if (state === "closed") return "Closed"; - return "Open"; +export function getStateLabelKey(state: PrState) { + if (state === "draft") return "workspace.git.pr.states.draft"; + if (state === "merged") return "workspace.git.pr.states.merged"; + if (state === "closed") return "workspace.git.pr.states.closed"; + return "workspace.git.pr.states.open"; } -export function getActivityVerb(item: Pick): string { - if (item.kind === "comment") return "Commented"; - if (item.reviewState === "approved") return "Approved"; - if (item.reviewState === "changes_requested") return "Requested changes"; - return "Reviewed"; +export function getActivityVerbKey(item: Pick) { + if (item.kind === "comment") return "workspace.git.pr.activity.commented"; + if (item.reviewState === "approved") return "workspace.git.pr.activity.approved"; + if (item.reviewState === "changes_requested") { + return "workspace.git.pr.activity.requestedChanges"; + } + return "workspace.git.pr.activity.reviewed"; } diff --git a/packages/app/src/git/pull-request-panel/pane.tsx b/packages/app/src/git/pull-request-panel/pane.tsx index 4b468e14e2..429dd2be0e 100644 --- a/packages/app/src/git/pull-request-panel/pane.tsx +++ b/packages/app/src/git/pull-request-panel/pane.tsx @@ -63,7 +63,7 @@ import { canAddPullRequestActivityToChat, } from "./context-attachment"; import { ChecksSection, getCheckIdentity } from "./checks-section"; -import { getActivityVerb, getStateLabel } from "./data"; +import { getActivityVerbKey, getStateLabelKey } from "./data"; import type { PrPaneActivity, PrPaneCheck, PrPaneData, PrState } from "./data"; import type { ForgeSpecificStatusFacts } from "@/git/merge-capability"; import { CheckPresentationIcon } from "@/git/check-presentation.view"; @@ -538,7 +538,7 @@ export function PullRequestPane({ - {getStateLabel(data.state)} + {t(getStateLabelKey(data.state))} {nativeHeaderMeta} {repoIdentity ? ( @@ -760,7 +760,8 @@ function ActivityAvatar({ activity, size }: { activity: PrPaneActivity; size: nu } function ActivityVerb({ activity }: { activity: PrPaneActivity }) { - const verb = getActivityVerb(activity).toLowerCase(); + const { t } = useTranslation(); + const verb = t(getActivityVerbKey(activity)).toLowerCase(); if (activity.kind === "review" && activity.reviewState === "approved") { return ( @@ -1065,8 +1066,12 @@ function ThreadBlock({ ? formatPullRequestThreadPath(thread.location) : t("workspace.git.pr.thread.discussion")} - {thread.isResolved ? : null} - {thread.location?.isOutdated ? : null} + {thread.isResolved ? ( + + ) : null} + {thread.location?.isOutdated ? ( + + ) : null} {collapsed ? ( diff --git a/packages/app/src/i18n/resources.test.ts b/packages/app/src/i18n/resources.test.ts index 039242710e..bf42a3b61a 100644 --- a/packages/app/src/i18n/resources.test.ts +++ b/packages/app/src/i18n/resources.test.ts @@ -76,6 +76,7 @@ const untranslatedLocalFallbacks = [ const pullRequestPanelSources = [ "git/pull-request-panel/pane.tsx", + "git/pull-request-panel/data.ts", "git/pull-request-panel/checks-section.tsx", "components/sidebar/sidebar-status-list.tsx", ] as const; @@ -87,6 +88,16 @@ const untranslatedPullRequestPanelLabels = [ "Adding...", "Comment actions", "Thread actions", + "Commented", + "Approved", + "Requested changes", + "Reviewed", + "Draft", + "Merged", + "Closed", + "Open", + "Resolved", + "Outdated", ] as const; function findUntranslatedPullRequestPanelLabels(): string[] { @@ -244,6 +255,14 @@ describe("translation resources", () => { expect(pr.actions.addingToChat).not.toBe(englishPr.actions.addingToChat); expect(pr.accessibility.commentActions).not.toBe(englishPr.accessibility.commentActions); expect(pr.accessibility.threadActions).not.toBe(englishPr.accessibility.threadActions); + for (const verb of Object.keys(englishPr.activity) as (keyof typeof englishPr.activity)[]) { + expect(pr.activity[verb]).not.toBe(englishPr.activity[verb]); + } + for (const state of Object.keys(englishPr.states) as (keyof typeof englishPr.states)[]) { + expect(pr.states[state]).not.toBe(englishPr.states[state]); + } + expect(pr.thread.resolved).not.toBe(englishPr.thread.resolved); + expect(pr.thread.outdated).not.toBe(englishPr.thread.outdated); expect(resource.sidebar.statusGroupAccessibility).not.toBe( en.sidebar.statusGroupAccessibility, ); diff --git a/packages/app/src/i18n/resources/ar.ts b/packages/app/src/i18n/resources/ar.ts index 5b684c4545..9ebd34c728 100644 --- a/packages/app/src/i18n/resources/ar.ts +++ b/packages/app/src/i18n/resources/ar.ts @@ -1070,6 +1070,8 @@ export const ar: TranslationResources = { }, thread: { discussion: "سلسلة المناقشة", + resolved: "تم الحل", + outdated: "قديم", }, errors: { statusLoadFailed: "غير قادر على تحميل حالة طلب السحب", diff --git a/packages/app/src/i18n/resources/en.ts b/packages/app/src/i18n/resources/en.ts index a1464907ce..5ddb309806 100644 --- a/packages/app/src/i18n/resources/en.ts +++ b/packages/app/src/i18n/resources/en.ts @@ -1078,6 +1078,8 @@ export const en = { }, thread: { discussion: "Discussion thread", + resolved: "Resolved", + outdated: "Outdated", }, errors: { statusLoadFailed: "Unable to load pull request status", diff --git a/packages/app/src/i18n/resources/es.ts b/packages/app/src/i18n/resources/es.ts index 18346ad126..896e2fe947 100644 --- a/packages/app/src/i18n/resources/es.ts +++ b/packages/app/src/i18n/resources/es.ts @@ -1093,16 +1093,18 @@ export const es: TranslationResources = { open: "Abierto", }, activity: { - commented: "Comentado", - approved: "Aprobado", - requestedChanges: "Cambios solicitados", - reviewed: "Revisado", + commented: "Comentó", + approved: "Aprobó", + requestedChanges: "Solicitó cambios", + reviewed: "Revisó", }, time: { justNow: "En este momento", }, thread: { discussion: "Hilo de discusión", + resolved: "Resuelto", + outdated: "Desactualizado", }, errors: { statusLoadFailed: "No se puede cargar el estado de la solicitud de extracción", diff --git a/packages/app/src/i18n/resources/fr.ts b/packages/app/src/i18n/resources/fr.ts index 6f21c9ad90..3d6466445c 100644 --- a/packages/app/src/i18n/resources/fr.ts +++ b/packages/app/src/i18n/resources/fr.ts @@ -1094,6 +1094,8 @@ export const fr: TranslationResources = { }, thread: { discussion: "Fil de discussion", + resolved: "Résolu", + outdated: "Obsolète", }, errors: { statusLoadFailed: "Impossible de charger le statut de la pull request", diff --git a/packages/app/src/i18n/resources/ja.ts b/packages/app/src/i18n/resources/ja.ts index 14fc6f2f69..30931c642b 100644 --- a/packages/app/src/i18n/resources/ja.ts +++ b/packages/app/src/i18n/resources/ja.ts @@ -1083,6 +1083,8 @@ export const ja: TranslationResources = { }, thread: { discussion: "ディスカッションスレッド", + resolved: "解決済み", + outdated: "古い", }, errors: { statusLoadFailed: "プルリクエストのステータスを読み込めません", diff --git a/packages/app/src/i18n/resources/ko.ts b/packages/app/src/i18n/resources/ko.ts index 95ac1e2bbd..d3b9688c44 100644 --- a/packages/app/src/i18n/resources/ko.ts +++ b/packages/app/src/i18n/resources/ko.ts @@ -1077,6 +1077,8 @@ export const ko: TranslationResources = { }, thread: { discussion: "토론 스레드", + resolved: "해결됨", + outdated: "오래됨", }, errors: { statusLoadFailed: "풀 리퀘스트 상태를 불러올 수 없습니다", diff --git a/packages/app/src/i18n/resources/pt-BR.ts b/packages/app/src/i18n/resources/pt-BR.ts index 626dec1e3c..aa426a4ce6 100644 --- a/packages/app/src/i18n/resources/pt-BR.ts +++ b/packages/app/src/i18n/resources/pt-BR.ts @@ -1084,15 +1084,17 @@ export const ptBR: TranslationResources = { }, activity: { commented: "Comentou", - approved: "Aprovado", + approved: "Aprovou", requestedChanges: "Solicitou alterações", - reviewed: "Revisado", + reviewed: "Revisou", }, time: { justNow: "agora mesmo", }, thread: { discussion: "Tópico de discussão", + resolved: "Resolvido", + outdated: "Desatualizado", }, errors: { statusLoadFailed: "Não foi possível carregar o status da pull request", diff --git a/packages/app/src/i18n/resources/ru.ts b/packages/app/src/i18n/resources/ru.ts index 59562e626b..a49fe1f9cb 100644 --- a/packages/app/src/i18n/resources/ru.ts +++ b/packages/app/src/i18n/resources/ru.ts @@ -1087,6 +1087,8 @@ export const ru: TranslationResources = { }, thread: { discussion: "Ветка обсуждения", + resolved: "Решено", + outdated: "Устарело", }, errors: { statusLoadFailed: "Не удалось загрузить статус PR", diff --git a/packages/app/src/i18n/resources/zh-CN.ts b/packages/app/src/i18n/resources/zh-CN.ts index deb75a2a92..f42e5a3e8c 100644 --- a/packages/app/src/i18n/resources/zh-CN.ts +++ b/packages/app/src/i18n/resources/zh-CN.ts @@ -1046,22 +1046,24 @@ export const zhCN: TranslationResources = { }, }, states: { - draft: "Draft", - merged: "已 merge", + draft: "草稿", + merged: "已合并", closed: "已关闭", - open: "Open", + open: "开放", }, activity: { commented: "已评论", approved: "已批准", requestedChanges: "请求修改", - reviewed: "已 review", + reviewed: "已审查", }, time: { justNow: "刚刚", }, thread: { discussion: "讨论主题", + resolved: "已解决", + outdated: "已过时", }, errors: { statusLoadFailed: "无法加载 Pull Request 状态",