From 9fd53c16aa660324fa4e4925504a837f6a0fc696 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Mon, 5 Oct 2026 19:55:56 +0100 Subject: [PATCH] web: pluralize copy correctly Signed-off-by: oppiliappan --- web/src/lib/components/home/SocialPost.svelte | 11 +++-- .../import/GitHubRepoSelectStep.svelte | 3 +- .../profile/FollowCardContent.svelte | 4 +- .../profile/FollowerFollowing.svelte | 4 +- .../components/profile/NetworkVouches.svelte | 7 ++- .../profile/ProfileActivityChart.svelte | 4 +- .../profile/ProfileSocialLinks.svelte | 6 +-- web/src/lib/components/repo/BlobHeader.svelte | 3 +- web/src/lib/components/repo/DiffView.svelte | 9 ++-- web/src/lib/components/repo/LabelPanel.svelte | 4 +- .../components/repo/StarButton.stories.svelte | 47 +++++++++---------- web/src/lib/components/repo/TreeHeader.svelte | 6 +-- .../repo/pipelines/PipelineToolbar.svelte | 4 +- web/src/lib/components/repo/pipelines/logs.ts | 4 +- .../repo/tickets/TicketCardContent.svelte | 5 +- .../components/search/CodeSearchView.svelte | 5 +- .../lib/components/search/FileMatch.svelte | 9 ++-- .../lib/components/strings/StringForm.svelte | 5 +- .../components/timeline/TimelineGroup.svelte | 3 +- .../timeline/TimelineTargetHeader.svelte | 3 +- .../lib/components/ui/CodeBlockToolbar.svelte | 4 +- web/src/lib/format.ts | 10 ++++ .../[version]/[[range]]/PullViewPage.svelte | 9 +++- .../settings/members/InviteMembers.svelte | 3 +- web/src/routes/dev/components/Shell.svelte | 4 +- 25 files changed, 104 insertions(+), 72 deletions(-) diff --git a/web/src/lib/components/home/SocialPost.svelte b/web/src/lib/components/home/SocialPost.svelte index 6d70d5244..324a5d00c 100644 --- a/web/src/lib/components/home/SocialPost.svelte +++ b/web/src/lib/components/home/SocialPost.svelte @@ -4,6 +4,7 @@ import Repeat2 from "$icon/repeat-2"; import Avatar from "$lib/components/ui/Avatar.svelte"; import type { SocialPostData } from "$lib/components/home/content"; + import { plural } from "$lib/format"; interface Props { post: SocialPostData; @@ -13,9 +14,13 @@ let { post, class: className = "" }: Props = $props(); const stats = $derived([ - { icon: MessageCircle, count: post.replies, label: "replies" }, - { icon: Repeat2, count: post.reposts, label: "reposts" }, - { icon: Heart, count: post.likes, label: "likes" } + { + icon: MessageCircle, + count: post.replies, + label: plural(post.replies, "reply", "replies") + }, + { icon: Repeat2, count: post.reposts, label: plural(post.reposts, "repost", "reposts") }, + { icon: Heart, count: post.likes, label: plural(post.likes, "like", "likes") } ]); diff --git a/web/src/lib/components/import/GitHubRepoSelectStep.svelte b/web/src/lib/components/import/GitHubRepoSelectStep.svelte index f21120cc0..a86cf671c 100644 --- a/web/src/lib/components/import/GitHubRepoSelectStep.svelte +++ b/web/src/lib/components/import/GitHubRepoSelectStep.svelte @@ -6,6 +6,7 @@ import type { Draft } from "./gitHubImportState.svelte"; import Button from "$lib/components/ui/Button.svelte"; import FormStep from "$lib/components/ui/FormStep.svelte"; + import { plural } from "$lib/format"; import RepoConfig from "./RepoConfig.svelte"; import RepoRow from "./RepoRow.svelte"; import { isMigratable } from "./gitHubImportState.svelte"; @@ -132,7 +133,7 @@ diff --git a/web/src/lib/components/profile/FollowCardContent.svelte b/web/src/lib/components/profile/FollowCardContent.svelte index 9985c4b6d..76cc6e7d3 100644 --- a/web/src/lib/components/profile/FollowCardContent.svelte +++ b/web/src/lib/components/profile/FollowCardContent.svelte @@ -6,7 +6,7 @@ import type { FollowChange, PersonData } from "$lib/components/profile/types"; import { createOptimisticCount } from "$lib/optimistic.svelte"; import Separator from "$lib/components/ui/Separator.svelte"; - import { formatCount } from "$lib/format"; + import { formatCount, pluralize } from "$lib/format"; let { person, @@ -60,7 +60,7 @@ >{formatCount(followerCount.value)} followers{pluralize(followerCount.value, "follower", "followers")} diff --git a/web/src/lib/components/profile/FollowerFollowing.svelte b/web/src/lib/components/profile/FollowerFollowing.svelte index 16f5841ca..3eafa0607 100644 --- a/web/src/lib/components/profile/FollowerFollowing.svelte +++ b/web/src/lib/components/profile/FollowerFollowing.svelte @@ -2,7 +2,7 @@ import { resolve } from "$app/paths"; import Users from "$icon/users"; import Separator from "$lib/components/ui/Separator.svelte"; - import { formatCount } from "$lib/format"; + import { formatCount, pluralize } from "$lib/format"; interface Props { handle: string; @@ -20,7 +20,7 @@ {formatCount(followers)} followers{pluralize(followers, "follower", "followers")} diff --git a/web/src/lib/components/profile/NetworkVouches.svelte b/web/src/lib/components/profile/NetworkVouches.svelte index c8518af1e..84db32a7c 100644 --- a/web/src/lib/components/profile/NetworkVouches.svelte +++ b/web/src/lib/components/profile/NetworkVouches.svelte @@ -4,6 +4,7 @@ import AvatarList from "$lib/components/profile/AvatarList.svelte"; import type { NetworkVouch } from "$lib/components/profile/types"; import Link from "$lib/components/ui/Link.svelte"; + import { plural } from "$lib/format"; interface Props { // handle of the profile these vouches are about @@ -19,8 +20,6 @@ const denouncers = $derived(vouches.filter((v) => v.kind === "denounce")); const href = $derived(resolve(`/${subjectHandle}?tab=vouches` as "/")); - const plural = (n: number) => (n === 1 ? "user" : "users"); - const both = $derived(vouchers.length > 0 && denouncers.length > 0); const vouchBorder = $derived( both @@ -46,7 +45,7 @@ Vouched for by {vouchers.length} - {plural(vouchers.length)} + {plural(vouchers.length, "user", "users")} @@ -62,7 +61,7 @@ class="typography-paragraph-regular text-red-800 hover:no-underline dark:text-red-200" > Denounced by {denouncers.length} - {plural(denouncers.length)} + {plural(denouncers.length, "user", "users")} diff --git a/web/src/lib/components/profile/ProfileActivityChart.svelte b/web/src/lib/components/profile/ProfileActivityChart.svelte index 53e2391c2..f529dfa2e 100644 --- a/web/src/lib/components/profile/ProfileActivityChart.svelte +++ b/web/src/lib/components/profile/ProfileActivityChart.svelte @@ -10,7 +10,7 @@ import GitCommit from "$icon/git-commit-horizontal"; import GitPullRequest from "$icon/git-pull-request"; import EmptyState from "$lib/components/ui/EmptyState.svelte"; - import { formatCount } from "$lib/format"; + import { formatCount, plural } from "$lib/format"; import { ACTIVITY_METRICS, layoutActivityChart, @@ -53,7 +53,7 @@ const valueOf = (month: ProfileActivityMonth, key: ActivityMetric) => key === "commits" ? month.commits : month[key].length; const nameOf = (value: number, metric: (typeof metrics)[number]) => - value === 1 ? metric.singular : metric.label; + plural(value, metric.singular, metric.label); const values = $derived( Object.fromEntries( ACTIVITY_METRICS.map((metric) => [ diff --git a/web/src/lib/components/profile/ProfileSocialLinks.svelte b/web/src/lib/components/profile/ProfileSocialLinks.svelte index 133e4bb22..f621e2c2d 100644 --- a/web/src/lib/components/profile/ProfileSocialLinks.svelte +++ b/web/src/lib/components/profile/ProfileSocialLinks.svelte @@ -5,7 +5,7 @@ import UsersRound from "$icon/users-round"; import type { ProfileRecord } from "$lib/api/records"; import Link, { link } from "$lib/components/ui/Link.svelte"; - import { formatCount } from "$lib/format"; + import { formatCount, pluralize } from "$lib/format"; interface Props { identity: { did: string; handle: string }; @@ -39,7 +39,7 @@
@@ -50,7 +50,7 @@ variant="inherit" size="small" > - {formatCount(followers)} followers + {pluralize(followers, "follower", "followers")} {#if following !== undefined} ยท diff --git a/web/src/lib/components/repo/BlobHeader.svelte b/web/src/lib/components/repo/BlobHeader.svelte index 73df26e2f..66186ceaf 100644 --- a/web/src/lib/components/repo/BlobHeader.svelte +++ b/web/src/lib/components/repo/BlobHeader.svelte @@ -17,6 +17,7 @@ import RefChip from "$lib/components/repo/RefChip.svelte"; import RepoCrumbs from "$lib/components/repo/RepoCrumbs.svelte"; import { baseName } from "$lib/components/repo/urls"; + import { plural } from "$lib/format"; interface Props { path: string; @@ -109,7 +110,7 @@ {/if} {#if showLines} - {lines} {lines === 1 ? "line" : "lines"} + {lines} {plural(lines, "line", "lines")} {/if}
diff --git a/web/src/lib/components/repo/DiffView.svelte b/web/src/lib/components/repo/DiffView.svelte index 0e6b72676..55e69f76d 100644 --- a/web/src/lib/components/repo/DiffView.svelte +++ b/web/src/lib/components/repo/DiffView.svelte @@ -29,6 +29,7 @@ import repoDiffviewSkeletonFixtureCollapsed from "$lib/bones/repo-diffview--skeleton-fixture-collapsed.bones.json"; import Bones from "$lib/components/ui/Bones.svelte"; import ResizeHandle from "$lib/components/ui/ResizeHandle.svelte"; + import { plural } from "$lib/format"; import { browser } from "$app/environment"; import { tick, untrack } from "svelte"; import { tv } from "tailwind-variants"; @@ -230,9 +231,11 @@ {#if diff} - {diff.files.length} changed {diff.files.length === 1 - ? "file" - : "files"} + {diff.files.length} changed {plural( + diff.files.length, + "file", + "files" + )} {/if} diff --git a/web/src/lib/components/repo/LabelPanel.svelte b/web/src/lib/components/repo/LabelPanel.svelte index 38f80f6b0..ecd2ad544 100644 --- a/web/src/lib/components/repo/LabelPanel.svelte +++ b/web/src/lib/components/repo/LabelPanel.svelte @@ -30,6 +30,7 @@ import Link from "$lib/components/ui/Link.svelte"; import Combobox from "$lib/components/ui/Combobox.svelte"; import type { SelectOption } from "$lib/components/ui/selectField"; + import { plural } from "$lib/format"; interface Props { direction?: "horizontal" | "vertical"; @@ -214,7 +215,8 @@ onclick={() => (expanded = true)} icon={TagPlus} > - Display all {labels.length} labels + Display all {labels.length} + {plural(labels.length, "label", "labels")} {/if} diff --git a/web/src/lib/components/repo/StarButton.stories.svelte b/web/src/lib/components/repo/StarButton.stories.svelte index 4c77898da..da7782558 100644 --- a/web/src/lib/components/repo/StarButton.stories.svelte +++ b/web/src/lib/components/repo/StarButton.stories.svelte @@ -1,7 +1,6 @@
@@ -27,13 +27,13 @@
{#if folders > 0} - {plural(folders, "folder")} + {folders} {plural(folders, "folder", "folders")} {/if} {#if folders > 0 && files > 0} {/if} {#if files > 0} - {plural(files, "file")} + {files} {plural(files, "file", "files")} {/if}
diff --git a/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte b/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte index 674291bbd..4447efe04 100644 --- a/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte +++ b/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte @@ -13,7 +13,7 @@ import bonesLoadingprimitivesCount from "$lib/bones/bones-loadingprimitives--count.bones.json"; import type { PipelineFilter } from "$lib/components/repo/pipelines/pipeline"; import PipelineSearch from "$lib/components/repo/pipelines/PipelineSearch.svelte"; - import { formatCount } from "$lib/format"; + import { formatCount, plural } from "$lib/format"; interface Props { ownerHandle: string; @@ -80,7 +80,7 @@ {#if value > 0} {/if} diff --git a/web/src/lib/components/repo/pipelines/logs.ts b/web/src/lib/components/repo/pipelines/logs.ts index 4a57e0cde..ba2d5ce2a 100644 --- a/web/src/lib/components/repo/pipelines/logs.ts +++ b/web/src/lib/components/repo/pipelines/logs.ts @@ -4,6 +4,7 @@ import { AnsiUp } from "ansi_up"; import type { LogFrame } from "$lib/api/spindleLogs"; import type { PipelineStatus } from "$lib/components/repo/types"; import { isLive, STATUS_LABELS } from "$lib/components/repo/pipelines/pipeline"; +import { plural } from "$lib/format"; export interface LogStep { id: number; @@ -152,8 +153,7 @@ export const narrate = (steps: LogStep[], workflow: PipelineStatus): string => { if (errorIndex !== -1) return `${STATUS_LABELS[workflow]} at ${steps[errorIndex].name}`; if (workflow === "success" && statuses.every((status) => status === "done")) { - const step = steps.length === 1 ? "step" : "steps"; - return `All ${steps.length} ${step} complete`; + return `All ${steps.length} ${plural(steps.length, "step", "steps")} complete`; } return ""; diff --git a/web/src/lib/components/repo/tickets/TicketCardContent.svelte b/web/src/lib/components/repo/tickets/TicketCardContent.svelte index 757030de5..ae28e79f1 100644 --- a/web/src/lib/components/repo/tickets/TicketCardContent.svelte +++ b/web/src/lib/components/repo/tickets/TicketCardContent.svelte @@ -3,7 +3,7 @@ import TicketInfoBar from "$lib/components/repo/tickets/TicketInfoBar.svelte"; import Separator from "$lib/components/ui/Separator.svelte"; import type { IssueSummary, PullSummary } from "$lib/components/repo/types"; - import { formatCount } from "$lib/format"; + import { pluralize } from "$lib/format"; interface Props { ownerHandle: string; @@ -38,8 +38,7 @@ {#snippet extraInfo()} - {formatCount(ticket.commentCount)} - {ticket.commentCount === 1 ? "comment" : "comments"} + {pluralize(ticket.commentCount, "comment", "comments")} {/snippet} diff --git a/web/src/lib/components/search/CodeSearchView.svelte b/web/src/lib/components/search/CodeSearchView.svelte index 66de23716..324b822e2 100644 --- a/web/src/lib/components/search/CodeSearchView.svelte +++ b/web/src/lib/components/search/CodeSearchView.svelte @@ -15,6 +15,7 @@ import { searchUrl } from "$lib/components/search/SearchTabs.svelte"; import { matchCount } from "$lib/components/search/chunks"; import { createCodeSearch } from "$lib/components/search/codeSearch.svelte"; + import { plural } from "$lib/format"; const HINT = "rounded-md bg-background-subtle px-2xs py-3xs typography-monospace-small"; @@ -130,8 +131,8 @@ class="border-t border-border-default py-xs typography-paragraph-small text-foreground-muted" > Found {matches} - {matches === 1 ? "match" : "matches"} in {search.results.length} - {search.results.length === 1 ? "file" : "files"} + {plural(matches, "match", "matches")} in {search.results.length} + {plural(search.results.length, "file", "files")} {/if} {/snippet} diff --git a/web/src/lib/components/search/FileMatch.svelte b/web/src/lib/components/search/FileMatch.svelte index 979ec1f66..b109806b9 100644 --- a/web/src/lib/components/search/FileMatch.svelte +++ b/web/src/lib/components/search/FileMatch.svelte @@ -13,6 +13,7 @@ import FileCardHeader from "$lib/components/repo/FileCardHeader.svelte"; import { chunkLines, type ThemedToken } from "$lib/components/search/chunks"; import { highlightChunks, resolveLanguage } from "$lib/components/search/highlight"; + import { plural } from "$lib/format"; const VISIBLE_CHUNKS = 3; @@ -142,9 +143,11 @@ {:else}