diff --git a/frontend/src/components/InProgressMatches.tsx b/frontend/src/components/InProgressMatches.tsx
index e6044dc..750232e 100644
--- a/frontend/src/components/InProgressMatches.tsx
+++ b/frontend/src/components/InProgressMatches.tsx
@@ -1,7 +1,7 @@
import { ChevronRight } from "lucide-react";
import { useNavigate } from "react-router-dom";
-import { formatTime } from "../domain/format";
+import { formatTime, plural } from "../domain/format";
import type { HomeInProgressMatch } from "../domain/types";
import { PlayerLink } from "./PlayerLink";
@@ -37,7 +37,8 @@ export function InProgressMatches({ items }: { items: HomeInProgressMatch[] }) {
идёт
- {formatTime(m.started_at)} · {m.player_count} игр.
+ {formatTime(m.started_at)} · {m.player_count}{" "}
+ {plural(m.player_count, "игрок", "игрока", "игроков")}
Пока нет сыгранных партий.
;
+ return Сыгранных партий пока нет.
;
}
return (
diff --git a/frontend/src/components/NotificationPanel.tsx b/frontend/src/components/NotificationPanel.tsx
index 2cf981d..53f1c6f 100644
--- a/frontend/src/components/NotificationPanel.tsx
+++ b/frontend/src/components/NotificationPanel.tsx
@@ -22,7 +22,7 @@ export function NotificationPanel({
Уведомления
{items.length === 0 ? (
-
Уведомлений нет
+
Уведомлений пока нет.
) : (
{items.map((n) => (
diff --git a/frontend/src/components/ProfileStatsCard.tsx b/frontend/src/components/ProfileStatsCard.tsx
index 63f7a05..8651d9b 100644
--- a/frontend/src/components/ProfileStatsCard.tsx
+++ b/frontend/src/components/ProfileStatsCard.tsx
@@ -24,7 +24,7 @@ export function ProfileStatsCard({
{o.wins}
-
Winrate
+
Винрейт
{pct(o.win_rate)}
diff --git a/frontend/src/domain/format.ts b/frontend/src/domain/format.ts
index 8a4b6cd..5885c61 100644
--- a/frontend/src/domain/format.ts
+++ b/frontend/src/domain/format.ts
@@ -9,6 +9,15 @@ function shifted(iso: string): Date {
return new Date(new Date(iso).getTime() + APP_TZ_OFFSET_MIN * 60_000);
}
+// Склонение существительного при числе: plural(3, "игрок", "игрока", "игроков").
+export function plural(n: number, one: string, few: string, many: string): string {
+ const m10 = n % 10;
+ const m100 = n % 100;
+ if (m10 === 1 && m100 !== 11) return one;
+ if (m10 >= 2 && m10 <= 4 && (m100 < 12 || m100 > 14)) return few;
+ return many;
+}
+
export function formatDuration(minutes: number | null | undefined): string {
if (minutes == null) return "—";
const h = Math.floor(minutes / 60);
diff --git a/frontend/src/pages/AccountPage.tsx b/frontend/src/pages/AccountPage.tsx
index 7c8a336..004fbee 100644
--- a/frontend/src/pages/AccountPage.tsx
+++ b/frontend/src/pages/AccountPage.tsx
@@ -145,7 +145,7 @@ export function AccountPage() {
setNickname(e.target.value)} placeholder={me.nickname} />
diff --git a/frontend/src/pages/CreateMatchPage.tsx b/frontend/src/pages/CreateMatchPage.tsx
index 5e005c6..0d443ae 100644
--- a/frontend/src/pages/CreateMatchPage.tsx
+++ b/frontend/src/pages/CreateMatchPage.tsx
@@ -134,7 +134,7 @@ export function CreateMatchPage() {
setError(null);
const filled = rows.filter((r) => r.user_id !== "" && r.faction_id !== "");
if (filled.length < 2) {
- setError("Нужно минимум 2 участника с выбранным игроком и фракцией.");
+ setError("Нужно минимум 2 игрока с выбранной фракцией.");
return;
}
if (filled.length > MAX_MATCH_PLAYERS) {
diff --git a/frontend/src/pages/GroupSettingsPage.tsx b/frontend/src/pages/GroupSettingsPage.tsx
index bca3365..be2b233 100644
--- a/frontend/src/pages/GroupSettingsPage.tsx
+++ b/frontend/src/pages/GroupSettingsPage.tsx
@@ -120,7 +120,8 @@ export function GroupSettingsPage() {
{/* Кликабельна вся левая часть строки, не только текст ника. */}
- {m.nickname} · {m.role}
+ {m.nickname}{" "}
+ · {m.role === "owner" ? "владелец" : "участник"}
{isOwner && m.role !== "owner" && (
{error &&
{error}
}
);
diff --git a/frontend/src/pages/MatchDetailPage.tsx b/frontend/src/pages/MatchDetailPage.tsx
index d8ebc67..9858497 100644
--- a/frontend/src/pages/MatchDetailPage.tsx
+++ b/frontend/src/pages/MatchDetailPage.tsx
@@ -293,7 +293,6 @@ export function MatchDetailPage() {
: "Сыгранная партия и её результаты будут удалены. Действие необратимо."
}
confirmLabel={inProgress ? "Отменить партию" : "Удалить"}
- cancelLabel="Назад"
onCancel={() => setConfirmRemove(false)}
onConfirm={() => {
setConfirmRemove(false);
diff --git a/frontend/src/pages/admin/AdminAccountsPage.tsx b/frontend/src/pages/admin/AdminAccountsPage.tsx
index 3876bda..e481452 100644
--- a/frontend/src/pages/admin/AdminAccountsPage.tsx
+++ b/frontend/src/pages/admin/AdminAccountsPage.tsx
@@ -28,7 +28,7 @@ export function AdminAccountsPage() {
if (editId == null || !name) return;
try {
await update.mutateAsync({ userId: editId, nickname: name });
- toast.show("Имя обновлено");
+ toast.show("Ник обновлён");
setEditId(null);
} catch (e) {
toast.show(e instanceof ApiError ? e.message : "Не удалось переименовать");
@@ -63,7 +63,7 @@ export function AdminAccountsPage() {
disabled={update.isPending || !editName.trim() || editName.trim() === u.nickname}
onClick={saveName}
>
- ОК
+ Сохранить
))}
- {(data?.items ?? []).length === 0 && Событий нет.
}
+ {(data?.items ?? []).length === 0 && Событий пока нет.
}
)}
diff --git a/frontend/src/pages/admin/AdminRecordsPage.tsx b/frontend/src/pages/admin/AdminRecordsPage.tsx
index e4de2f4..6f762d4 100644
--- a/frontend/src/pages/admin/AdminRecordsPage.tsx
+++ b/frontend/src/pages/admin/AdminRecordsPage.tsx
@@ -3,7 +3,7 @@ import { useMemo, useState } from "react";
import { ApiError } from "../../api/client";
import { ConfirmDialog } from "../../components/ConfirmDialog";
-import { formatDate } from "../../domain/format";
+import { formatDate, plural } from "../../domain/format";
import { Spinner } from "../../components/Spinner";
import { Switch } from "../../components/Switch";
import { useToast } from "../../context/ToastContext";
@@ -58,7 +58,8 @@ export function AdminRecordsPage() {
#{m.id} · {formatDate(m.played_at)}
{!grouped && · {m.group_name}}
- {m.player_count} игр. · автор {m.created_by}
+ {m.player_count} {plural(m.player_count, "игрок", "игрока", "игроков")} · автор{" "}
+ {m.created_by}
@@ -120,7 +121,7 @@ export function AdminRecordsPage() {
{isLoading ? (
) : (matches ?? []).length === 0 ? (
-
Записей нет.
+
Записей пока нет.
) : grouped ? (
byGroup.map(([gid, { name, items }]) => (