fix(i18n): correct English singular forms that carried a plural noun (#2350)
Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
@@ -46,7 +46,9 @@ function setup(runs: AutomationRun[]) {
|
||||
});
|
||||
render(<RunsSection automationId="auto-1" workspaceId="ws-1" />);
|
||||
// The runs table only renders once the "Recent Runs" disclosure is expanded.
|
||||
fireEvent.click(screen.getByText(/Recent Runs/));
|
||||
// The heading is count-aware ("Recent Run (1)" / "Recent Runs (2)"), so the
|
||||
// matcher must tolerate both forms rather than pinning the plural.
|
||||
fireEvent.click(screen.getByText(/Recent Runs?\s*\(/));
|
||||
}
|
||||
|
||||
describe("RunsSection status badges", () => {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
|
||||
import { PRStatusBadges } from "./pr-status-badges";
|
||||
import type { GitHubPR, GitHubPRStatus } from "@/lib/types/github";
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
function makePR(): GitHubPR {
|
||||
return {
|
||||
number: 1,
|
||||
title: "Test PR",
|
||||
url: "https://api.github.com/repos/o/r/pulls/1",
|
||||
html_url: "https://github.com/o/r/pull/1",
|
||||
state: "open",
|
||||
head_branch: "feature",
|
||||
base_branch: "main",
|
||||
author_login: "octocat",
|
||||
repo_owner: "o",
|
||||
repo_name: "r",
|
||||
draft: false,
|
||||
mergeable: true,
|
||||
additions: 1,
|
||||
deletions: 0,
|
||||
requested_reviewers: [],
|
||||
created_at: "2026-08-06T00:00:00Z",
|
||||
updated_at: "2026-08-06T00:00:00Z",
|
||||
merged_at: null,
|
||||
closed_at: null,
|
||||
};
|
||||
}
|
||||
|
||||
function makeStatus(pendingReviewCount: number): GitHubPRStatus {
|
||||
return {
|
||||
pr: makePR(),
|
||||
review_state: "",
|
||||
checks_state: "",
|
||||
mergeable_state: "clean",
|
||||
review_count: 0,
|
||||
pending_review_count: pendingReviewCount,
|
||||
checks_total: 0,
|
||||
checks_passing: 0,
|
||||
};
|
||||
}
|
||||
|
||||
describe("PRStatusBadges review chip tooltip", () => {
|
||||
// The tooltip used to be `pendingReviewS`, interpolating {{pending}} into a
|
||||
// hardcoded "review(s)". i18next cannot select a plural from a value that is
|
||||
// not `count`, so the parenthetical was the only way English could stay
|
||||
// truthful — and every translation inherited it. These two cases pin the
|
||||
// plural-aware replacement.
|
||||
// The query is deliberately loose and the assertion exact: a wrong plural then
|
||||
// fails on the value ("expected '1 pending reviews' to be '1 pending review'")
|
||||
// rather than on the lookup, which would only report a missing element.
|
||||
it("uses the singular form for exactly one pending review", () => {
|
||||
render(<PRStatusBadges pr={makePR()} status={makeStatus(1)} />);
|
||||
expect(screen.getByTitle(/pending review/).getAttribute("title")).toBe("1 pending review");
|
||||
});
|
||||
|
||||
it("uses the plural form for more than one pending review", () => {
|
||||
render(<PRStatusBadges pr={makePR()} status={makeStatus(3)} />);
|
||||
expect(screen.getByTitle(/pending review/).getAttribute("title")).toBe("3 pending reviews");
|
||||
});
|
||||
});
|
||||
@@ -72,7 +72,7 @@ function ReviewChip({
|
||||
Icon={IconClockHour4}
|
||||
label={t("github:pending", { pending })}
|
||||
tone="pending"
|
||||
title={t("github:pendingReviewS", { pending })}
|
||||
title={t("github:pendingReviews", { count: pending })}
|
||||
/>
|
||||
);
|
||||
return null;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"authRequiredOpenShell": "Authentication required - click to open a shell and sign in",
|
||||
"authenticationRequired": "Authentication required",
|
||||
"automaticallyAvailable": "Automatically available",
|
||||
"availableCommandsCount_one": "Available commands ({{count}})",
|
||||
"availableCommandsCount_one": "Available command ({{count}})",
|
||||
"availableCommandsCount_other": "Available commands ({{count}})",
|
||||
"availableSlashCommands": "Available slash commands",
|
||||
"availableSlashCommandsHelp": "Type these during a session chat to invoke them - e.g. <0>{{example}}</0>.",
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"prTriggerRepositoryHelp": "PR triggers always use the PR's own repository.",
|
||||
"pushTriggerHelp": "Triggers when code is pushed to matching branches",
|
||||
"recentRuns": "Recent runs",
|
||||
"recentRuns_one": "Recent Runs ({{count}})",
|
||||
"recentRuns_one": "Recent Run ({{count}})",
|
||||
"recentRuns_other": "Recent Runs ({{count}})",
|
||||
"refresh": "Refresh",
|
||||
"removeRepository": "Remove repository",
|
||||
|
||||
@@ -352,7 +352,8 @@
|
||||
"pendingReview": "Pending review",
|
||||
"pendingReviewCount_one": "({{count}} pending)",
|
||||
"pendingReviewCount_other": "({{count}} pending)",
|
||||
"pendingReviewS": "{{pending}} pending review(s)",
|
||||
"pendingReviews_one": "{{count}} pending review",
|
||||
"pendingReviews_other": "{{count}} pending reviews",
|
||||
"permissionsAvailableToKandevThroughThis": "Permissions available to Kandev through this workspace's GitHub App installation.",
|
||||
"personalAccessToken": "Personal access token",
|
||||
"personalAccount": "Personal account",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"enterNameAndTokenBeforeSaving": "Enter an instance name and auth token before saving.",
|
||||
"environment": "Environment",
|
||||
"environmentHelp": "Optional — restrict to one environment.",
|
||||
"eventsCount_one": "events {{count}}",
|
||||
"eventsCount_one": "event {{count}}",
|
||||
"eventsCount_other": "events {{count}}",
|
||||
"executorProfile": "Executor Profile",
|
||||
"failedToLoadInstances": "Failed to load Sentry instances: {{error}}",
|
||||
@@ -161,7 +161,7 @@
|
||||
"update": "Update",
|
||||
"updateFailed": "Update failed: {{error}}",
|
||||
"useStepDefault": "(use step default)",
|
||||
"usersCount_one": "users {{count}}",
|
||||
"usersCount_one": "user {{count}}",
|
||||
"usersCount_other": "users {{count}}",
|
||||
"watchDialogDescription": "Poll Sentry with a structured filter and auto-create a Kandev task for each newly-matching issue. Optionally bind a repository so each task runs against that codebase, or leave it unset to run with no repository.",
|
||||
"watcherCreated": "Watcher created",
|
||||
|
||||
@@ -358,7 +358,7 @@
|
||||
"dropFilesHere": "Drop files here",
|
||||
"duration": "duration",
|
||||
"durationValue": "Duration: {{duration}}",
|
||||
"earlierUpdates_one": "Earlier updates ({{count}})",
|
||||
"earlierUpdates_one": "Earlier update ({{count}})",
|
||||
"earlierUpdates_other": "Earlier updates ({{count}})",
|
||||
"editBranch": "Edit branch {{branch}}",
|
||||
"editBranch2": "Edit branch",
|
||||
@@ -1138,7 +1138,7 @@
|
||||
"sidebarFilters": "Sidebar filters",
|
||||
"skipAllQuestions": "Skip all questions",
|
||||
"skipped": "Skipped",
|
||||
"snapshotMessageCount_one": "{{count}} messages",
|
||||
"snapshotMessageCount_one": "{{count}} message",
|
||||
"snapshotMessageCount_other": "{{count}} messages",
|
||||
"softReset": "Soft Reset",
|
||||
"someone": "Someone",
|
||||
@@ -1248,7 +1248,7 @@
|
||||
"taskTreeRestored": "Task tree restored",
|
||||
"taskTreeResumed": "Task tree resumed",
|
||||
"tasks": "Tasks",
|
||||
"tasksAffected_one": "{{count}} tasks affected",
|
||||
"tasksAffected_one": "{{count}} task affected",
|
||||
"tasksAffected_other": "{{count}} tasks affected",
|
||||
"tcpPortsWithActiveListenersInside": "TCP ports with active listeners inside the remote executor. Click refresh to re-scan.",
|
||||
"technicalDetails": "Technical details",
|
||||
@@ -1379,7 +1379,7 @@
|
||||
"versionNumber": "Version {{version}}",
|
||||
"viaSession": "via session",
|
||||
"view": "View:",
|
||||
"viewLimitReached_one": "You can save up to {{count}} views.",
|
||||
"viewLimitReached_one": "You can save up to {{count}} view.",
|
||||
"viewLimitReached_other": "You can save up to {{count}} views.",
|
||||
"viewName": "View name",
|
||||
"viewPlanHistory": "View plan history",
|
||||
@@ -1404,7 +1404,7 @@
|
||||
"websocketClientNotAvailable": "WebSocket client not available",
|
||||
"whatShouldTheAgentWorkOn": "What should the agent work on?",
|
||||
"workdir": "Workdir",
|
||||
"workedRanCommands_one": "Worked -- ran {{count}} commands",
|
||||
"workedRanCommands_one": "Worked -- ran {{count}} command",
|
||||
"workedRanCommands_other": "Worked -- ran {{count}} commands",
|
||||
"workflow": "workflow",
|
||||
"workflowCount_one": "{{count}} workflow",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"authRequiredOpenShell": "Àũţĥēńţĩćàţĩōń ŕēqũĩŕēď - ćĺĩćķ ţō ōƥēń à śĥēĺĺ àńď śĩĝń ĩń",
|
||||
"authenticationRequired": "Àũţĥēńţĩćàţĩōń ŕēqũĩŕēď",
|
||||
"automaticallyAvailable": "Àũţōḿàţĩćàĺĺŷ àvàĩĺàƀĺē",
|
||||
"availableCommandsCount_one": "Àvàĩĺàƀĺē ćōḿḿàńďś ({{count}})",
|
||||
"availableCommandsCount_one": "Àvàĩĺàƀĺē ćōḿḿàńď ({{count}})",
|
||||
"availableCommandsCount_other": "Àvàĩĺàƀĺē ćōḿḿàńďś ({{count}})",
|
||||
"availableSlashCommands": "Àvàĩĺàƀĺē śĺàśĥ ćōḿḿàńďś",
|
||||
"availableSlashCommandsHelp": "Ţŷƥē ţĥēśē ďũŕĩńĝ à śēśśĩōń ćĥàţ ţō ĩńvōķē ţĥēḿ - ē.ĝ. <0>{{example}}</0>.",
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"prTriggerRepositoryHelp": "ƤŔ ţŕĩĝĝēŕś àĺŵàŷś ũśē ţĥē ƤŔ'ś ōŵń ŕēƥōśĩţōŕŷ.",
|
||||
"pushTriggerHelp": "Ţŕĩĝĝēŕś ŵĥēń ćōďē ĩś ƥũśĥēď ţō ḿàţćĥĩńĝ ƀŕàńćĥēś",
|
||||
"recentRuns": "Ŕēćēńţ ŕũńś",
|
||||
"recentRuns_one": "Ŕēćēńţ Ŕũńś ({{count}})",
|
||||
"recentRuns_one": "Ŕēćēńţ Ŕũń ({{count}})",
|
||||
"recentRuns_other": "Ŕēćēńţ Ŕũńś ({{count}})",
|
||||
"refresh": "Ŕēƒŕēśĥ",
|
||||
"removeRepository": "Ŕēḿōvē ŕēƥōśĩţōŕŷ",
|
||||
|
||||
@@ -352,7 +352,8 @@
|
||||
"pendingReview": "Ƥēńďĩńĝ ŕēvĩēŵ",
|
||||
"pendingReviewCount_one": "({{count}} ƥēńďĩńĝ)",
|
||||
"pendingReviewCount_other": "({{count}} ƥēńďĩńĝ)",
|
||||
"pendingReviewS": "{{pending}} ƥēńďĩńĝ ŕēvĩēŵ(ś)",
|
||||
"pendingReviews_one": "{{count}} ƥēńďĩńĝ ŕēvĩēŵ",
|
||||
"pendingReviews_other": "{{count}} ƥēńďĩńĝ ŕēvĩēŵś",
|
||||
"permissionsAvailableToKandevThroughThis": "Ƥēŕḿĩśśĩōńś àvàĩĺàƀĺē ţō Ķàńďēv ţĥŕōũĝĥ ţĥĩś ŵōŕķśƥàćē'ś ĜĩţĤũƀ Àƥƥ ĩńśţàĺĺàţĩōń.",
|
||||
"personalAccessToken": "Ƥēŕśōńàĺ àććēśś ţōķēń",
|
||||
"personalAccount": "Ƥēŕśōńàĺ àććōũńţ",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"enterNameAndTokenBeforeSaving": "Ēńţēŕ àń ĩńśţàńćē ńàḿē àńď àũţĥ ţōķēń ƀēƒōŕē śàvĩńĝ.",
|
||||
"environment": "Ēńvĩŕōńḿēńţ",
|
||||
"environmentHelp": "Ōƥţĩōńàĺ — ŕēśţŕĩćţ ţō ōńē ēńvĩŕōńḿēńţ.",
|
||||
"eventsCount_one": "ēvēńţś {{count}}",
|
||||
"eventsCount_one": "ēvēńţ {{count}}",
|
||||
"eventsCount_other": "ēvēńţś {{count}}",
|
||||
"executorProfile": "Ēxēćũţōŕ Ƥŕōƒĩĺē",
|
||||
"failedToLoadInstances": "Ƒàĩĺēď ţō ĺōàď Śēńţŕŷ ĩńśţàńćēś: {{error}}",
|
||||
@@ -161,7 +161,7 @@
|
||||
"update": "Ũƥďàţē",
|
||||
"updateFailed": "Ũƥďàţē ƒàĩĺēď: {{error}}",
|
||||
"useStepDefault": "(ũśē śţēƥ ďēƒàũĺţ)",
|
||||
"usersCount_one": "ũśēŕś {{count}}",
|
||||
"usersCount_one": "ũśēŕ {{count}}",
|
||||
"usersCount_other": "ũśēŕś {{count}}",
|
||||
"watchDialogDescription": "Ƥōĺĺ Śēńţŕŷ ŵĩţĥ à śţŕũćţũŕēď ƒĩĺţēŕ àńď àũţō-ćŕēàţē à Ķàńďēv ţàśķ ƒōŕ ēàćĥ ńēŵĺŷ-ḿàţćĥĩńĝ ĩśśũē. Ōƥţĩōńàĺĺŷ ƀĩńď à ŕēƥōśĩţōŕŷ śō ēàćĥ ţàśķ ŕũńś àĝàĩńśţ ţĥàţ ćōďēƀàśē, ōŕ ĺēàvē ĩţ ũńśēţ ţō ŕũń ŵĩţĥ ńō ŕēƥōśĩţōŕŷ.",
|
||||
"watcherCreated": "Ŵàţćĥēŕ ćŕēàţēď",
|
||||
|
||||
@@ -358,7 +358,7 @@
|
||||
"dropFilesHere": "Ďŕōƥ ƒĩĺēś ĥēŕē",
|
||||
"duration": "ďũŕàţĩōń",
|
||||
"durationValue": "Ďũŕàţĩōń: {{duration}}",
|
||||
"earlierUpdates_one": "Ēàŕĺĩēŕ ũƥďàţēś ({{count}})",
|
||||
"earlierUpdates_one": "Ēàŕĺĩēŕ ũƥďàţē ({{count}})",
|
||||
"earlierUpdates_other": "Ēàŕĺĩēŕ ũƥďàţēś ({{count}})",
|
||||
"editBranch": "Ēďĩţ ƀŕàńćĥ {{branch}}",
|
||||
"editBranch2": "Ēďĩţ ƀŕàńćĥ",
|
||||
@@ -1138,7 +1138,7 @@
|
||||
"sidebarFilters": "Śĩďēƀàŕ ƒĩĺţēŕś",
|
||||
"skipAllQuestions": "Śķĩƥ àĺĺ qũēśţĩōńś",
|
||||
"skipped": "Śķĩƥƥēď",
|
||||
"snapshotMessageCount_one": "{{count}} ḿēśśàĝēś",
|
||||
"snapshotMessageCount_one": "{{count}} ḿēśśàĝē",
|
||||
"snapshotMessageCount_other": "{{count}} ḿēśśàĝēś",
|
||||
"softReset": "Śōƒţ Ŕēśēţ",
|
||||
"someone": "Śōḿēōńē",
|
||||
@@ -1248,7 +1248,7 @@
|
||||
"taskTreeRestored": "Ţàśķ ţŕēē ŕēśţōŕēď",
|
||||
"taskTreeResumed": "Ţàśķ ţŕēē ŕēśũḿēď",
|
||||
"tasks": "Ţàśķś",
|
||||
"tasksAffected_one": "{{count}} ţàśķś àƒƒēćţēď",
|
||||
"tasksAffected_one": "{{count}} ţàśķ àƒƒēćţēď",
|
||||
"tasksAffected_other": "{{count}} ţàśķś àƒƒēćţēď",
|
||||
"tcpPortsWithActiveListenersInside": "ŢĆƤ ƥōŕţś ŵĩţĥ àćţĩvē ĺĩśţēńēŕś ĩńśĩďē ţĥē ŕēḿōţē ēxēćũţōŕ. Ćĺĩćķ ŕēƒŕēśĥ ţō ŕē-śćàń.",
|
||||
"technicalDetails": "Ţēćĥńĩćàĺ ďēţàĩĺś",
|
||||
@@ -1379,7 +1379,7 @@
|
||||
"versionNumber": "Vēŕśĩōń {{version}}",
|
||||
"viaSession": "vĩà śēśśĩōń",
|
||||
"view": "Vĩēŵ:",
|
||||
"viewLimitReached_one": "Ŷōũ ćàń śàvē ũƥ ţō {{count}} vĩēŵś.",
|
||||
"viewLimitReached_one": "Ŷōũ ćàń śàvē ũƥ ţō {{count}} vĩēŵ.",
|
||||
"viewLimitReached_other": "Ŷōũ ćàń śàvē ũƥ ţō {{count}} vĩēŵś.",
|
||||
"viewName": "Vĩēŵ ńàḿē",
|
||||
"viewPlanHistory": "Vĩēŵ ƥĺàń ĥĩśţōŕŷ",
|
||||
@@ -1404,7 +1404,7 @@
|
||||
"websocketClientNotAvailable": "ŴēƀŚōćķēţ ćĺĩēńţ ńōţ àvàĩĺàƀĺē",
|
||||
"whatShouldTheAgentWorkOn": "Ŵĥàţ śĥōũĺď ţĥē àĝēńţ ŵōŕķ ōń?",
|
||||
"workdir": "Ŵōŕķďĩŕ",
|
||||
"workedRanCommands_one": "Ŵōŕķēď -- ŕàń {{count}} ćōḿḿàńďś",
|
||||
"workedRanCommands_one": "Ŵōŕķēď -- ŕàń {{count}} ćōḿḿàńď",
|
||||
"workedRanCommands_other": "Ŵōŕķēď -- ŕàń {{count}} ćōḿḿàńďś",
|
||||
"workflow": "ŵōŕķƒĺōŵ",
|
||||
"workflowCount_one": "{{count}} ŵōŕķƒĺōŵ",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"authRequiredOpenShell": "É necessária autenticação - clique para abrir uma shell e iniciar sessão",
|
||||
"authenticationRequired": "É necessária autenticação",
|
||||
"automaticallyAvailable": "Disponível automaticamente",
|
||||
"availableCommandsCount_one": "Comandos disponíveis ({{count}})",
|
||||
"availableCommandsCount_one": "Comando disponível ({{count}})",
|
||||
"availableCommandsCount_other": "Comandos disponíveis ({{count}})",
|
||||
"availableSlashCommands": "Comandos slash disponíveis",
|
||||
"availableSlashCommandsHelp": "Escreva-os durante o chat de uma sessão para os invocar - ex.: <0>{{example}}</0>.",
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"prTriggerRepositoryHelp": "Os acionadores de PR usam sempre o repositório do próprio PR.",
|
||||
"pushTriggerHelp": "Aciona quando é feito push de código para branches correspondentes",
|
||||
"recentRuns": "Execuções recentes",
|
||||
"recentRuns_one": "Execuções recentes ({{count}})",
|
||||
"recentRuns_one": "Execução recente ({{count}})",
|
||||
"recentRuns_other": "Execuções recentes ({{count}})",
|
||||
"refresh": "Atualizar",
|
||||
"removeRepository": "Remover repositório",
|
||||
|
||||
@@ -352,7 +352,8 @@
|
||||
"pendingReview": "Revisão pendente",
|
||||
"pendingReviewCount_one": "({{count}} pendente)",
|
||||
"pendingReviewCount_other": "({{count}} pendentes)",
|
||||
"pendingReviewS": "{{pending}} revisão(ões) pendente(s)",
|
||||
"pendingReviews_one": "{{count}} revisão pendente",
|
||||
"pendingReviews_other": "{{count}} revisões pendentes",
|
||||
"permissionsAvailableToKandevThroughThis": "Permissões disponíveis para o Kandev através da instalação da App do GitHub nesta área de trabalho.",
|
||||
"personalAccessToken": "Token de acesso pessoal",
|
||||
"personalAccount": "Conta pessoal",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"enterNameAndTokenBeforeSaving": "Introduza um nome de instância e um token de autenticação antes de guardar.",
|
||||
"environment": "Ambiente",
|
||||
"environmentHelp": "Opcional — restringir a um ambiente.",
|
||||
"eventsCount_one": "eventos {{count}}",
|
||||
"eventsCount_one": "evento {{count}}",
|
||||
"eventsCount_other": "eventos {{count}}",
|
||||
"executorProfile": "Perfil de executor",
|
||||
"failedToLoadInstances": "Não foi possível carregar as instâncias do Sentry: {{error}}",
|
||||
@@ -161,7 +161,7 @@
|
||||
"update": "Atualizar",
|
||||
"updateFailed": "Não foi possível atualizar: {{error}}",
|
||||
"useStepDefault": "(usar a pré-definição do passo)",
|
||||
"usersCount_one": "utilizadores {{count}}",
|
||||
"usersCount_one": "utilizador {{count}}",
|
||||
"usersCount_other": "utilizadores {{count}}",
|
||||
"watchDialogDescription": "Sonde o Sentry com um filtro estruturado e crie automaticamente uma tarefa do Kandev para cada nova issue correspondente. Opcionalmente, associe um repositório para que cada tarefa seja executada sobre essa base de código, ou deixe por definir para executar sem repositório.",
|
||||
"watcherCreated": "Vigilância criada",
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
"draftCreated": "Rascunho de {{longName}} criado",
|
||||
"duration": "duração",
|
||||
"durationValue": "Duração: {{duration}}",
|
||||
"earlierUpdates_one": "Atualizações anteriores ({{count}})",
|
||||
"earlierUpdates_one": "Atualização anterior ({{count}})",
|
||||
"earlierUpdates_other": "Atualizações anteriores ({{count}})",
|
||||
"editComment": "Editar comentário",
|
||||
"empty": "(vazio)",
|
||||
|
||||
Reference in New Issue
Block a user