Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions apps/web/src/app/dashboard/[guildId]/board/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { requireUser } from "@/lib/auth";
import { resolveStatus, statusOptions } from "@/lib/forms";
import { getGuildSubmissions, getReviewScope } from "@/lib/guild";
import { signGuildRealtimeToken } from "@/lib/realtime-token";
import { getDict } from "@/i18n";
import { getDict, getLocale } from "@/i18n";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -101,8 +101,12 @@ export default async function BoardPage({ params }: { params: Promise<{ guildId:
moved: t.board.moved,
empty: t.board.columnEmpty,
anonymous: t.anonymous,
countOne: t.countSubmission,
countMany: t.countSubmissions,
count: {
one: t.countSubmission,
few: t.countSubmissionsFew,
other: t.countSubmissions,
},
locale: await getLocale(),
saveFailed: t.board.saveFailed,
dismiss: dict.kit.dismiss,
}}
Expand Down
9 changes: 7 additions & 2 deletions apps/web/src/app/dashboard/[guildId]/submissions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { Button } from "@/components/ui/button";
import { requireUser } from "@/lib/auth";
import { resolveStatus, statusOptions } from "@/lib/forms";
import { countArchivedSubmissions, getGuildSubmissions, getReviewScope } from "@/lib/guild";
import { countForm } from "@/lib/plural";
import { signGuildRealtimeToken } from "@/lib/realtime-token";
import { getDict } from "@/i18n";
import { getDict, getLocale } from "@/i18n";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -78,7 +79,11 @@ export default async function GuildSubmissionsPage({
{liveToken && <DashboardLive guildId={guildId} token={liveToken} />}
<PageHeader
title={t.submissionsTab}
description={`${total} ${total === 1 ? t.countSubmission : t.countSubmissions}`}
description={`${total} ${countForm(total, await getLocale(), {
one: t.countSubmission,
few: t.countSubmissionsFew,
other: t.countSubmissions,
})}`}
/>

<div className="grid gap-5">
Expand Down
8 changes: 5 additions & 3 deletions apps/web/src/components/board/kanban-board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useEffect, useId, useRef, useState } from "react";
import { Notice, RowMenu } from "@/components/dashboard-kit";
import { LocalDateTime } from "@/components/public/local-datetime";
import { dotColors } from "@/lib/color-dot";
import { countForm, type CountForms } from "@/lib/plural";

export interface BoardColumn {
key: string;
Expand All @@ -33,8 +34,9 @@ export interface BoardLabels {
moved: string;
empty: string;
anonymous: string;
countOne: string;
countMany: string;
/** The noun after a column's card count, in the forms the locale needs. */
count: CountForms;
locale: string;
saveFailed: string;
dismiss: string;
}
Expand Down Expand Up @@ -150,7 +152,7 @@ export function KanbanBoard({
{cards.length}
<span className="sr-only">
{" "}
{cards.length === 1 ? labels.countOne : labels.countMany}
{countForm(cards.length, labels.locale, labels.count)}
</span>
</span>
</h2>
Expand Down
7 changes: 7 additions & 0 deletions apps/web/src/i18n/dictionaries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ const en = {
countForms: "forms",
countSubmission: "submission",
countSubmissions: "submissions",
countSubmissionsFew: "submissions",
newForm: "New form",
noForms: "No forms yet. Create your first one.",
edit: "Edit",
Expand Down Expand Up @@ -1842,6 +1843,7 @@ const de: Dictionary = {
countForms: "Formulare",
countSubmission: "Einreichung",
countSubmissions: "Einreichungen",
countSubmissionsFew: "Einreichungen",
newForm: "Neues Formular",
noForms: "Noch keine Formulare. Erstelle dein erstes.",
edit: "Bearbeiten",
Expand Down Expand Up @@ -3198,6 +3200,7 @@ const hu: Dictionary = {
countForms: "űrlap",
countSubmission: "beküldés",
countSubmissions: "beküldés",
countSubmissionsFew: "beküldés",
newForm: "Új űrlap",
noForms: "Még nincsenek űrlapok. Hozd létre az elsőt.",
edit: "Szerkesztés",
Expand Down Expand Up @@ -4542,6 +4545,7 @@ const fr: Dictionary = {
countForms: "formulaires",
countSubmission: "soumission",
countSubmissions: "soumissions",
countSubmissionsFew: "soumissions",
newForm: "Nouveau formulaire",
noForms: "Aucun formulaire. Créez le premier.",
edit: "Modifier",
Expand Down Expand Up @@ -5892,6 +5896,7 @@ const es: Dictionary = {
countForms: "formularios",
countSubmission: "envío",
countSubmissions: "envíos",
countSubmissionsFew: "envíos",
newForm: "Nuevo formulario",
noForms: "Aún no hay formularios. Crea el primero.",
edit: "Editar",
Expand Down Expand Up @@ -7233,6 +7238,7 @@ const pt: Dictionary = {
countForms: "formulários",
countSubmission: "candidatura",
countSubmissions: "candidaturas",
countSubmissionsFew: "candidaturas",
newForm: "Novo formulário",
noForms: "Nenhum formulário ainda. Crie o seu primeiro.",
edit: "Editar",
Expand Down Expand Up @@ -8572,6 +8578,7 @@ const pl: Dictionary = {
countForms: "formularzy",
countSubmission: "zgłoszenie",
countSubmissions: "zgłoszeń",
countSubmissionsFew: "zgłoszenia",
newForm: "Nowy formularz",
noForms: "Brak formularzy. Utwórz pierwszy.",
edit: "Edytuj",
Expand Down
19 changes: 19 additions & 0 deletions apps/web/src/lib/plural.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, it } from "vitest";

import { countForm } from "./plural";

const pl = { one: "zgłoszenie", few: "zgłoszenia", other: "zgłoszeń" };
const en = { one: "submission", few: "submissions", other: "submissions" };

describe("countForm", () => {
it("picks the Polish form for 1, 2 to 4 and 5 and up", () => {
expect(countForm(1, "pl", pl)).toBe("zgłoszenie");
for (const n of [2, 3, 4, 22, 34]) expect(countForm(n, "pl", pl)).toBe("zgłoszenia");
for (const n of [5, 11, 12, 13, 14, 25, 100]) expect(countForm(n, "pl", pl)).toBe("zgłoszeń");
});

it("keeps singular and plural in English", () => {
expect(countForm(1, "en", en)).toBe("submission");
expect(countForm(3, "en", en)).toBe("submissions");
});
});
13 changes: 13 additions & 0 deletions apps/web/src/lib/plural.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* A noun in the forms a count can need. `few` is the Polish 2 to 4 (also 22
* to 24 and so on); languages without that form repeat `other` there.
*/
export type CountForms = { one: string; few: string; other: string };

/** The form of a noun that goes with `count` in `locale`, by the CLDR plural rules. */
export function countForm(count: number, locale: string, forms: CountForms): string {
const category = new Intl.PluralRules(locale).select(count);
if (category === "one") return forms.one;
if (category === "few") return forms.few;
return forms.other;
}
Loading