locale second stage

This commit is contained in:
2026-07-01 11:21:29 -05:00
parent dca3a66206
commit 09020b7aef
12 changed files with 237 additions and 191 deletions

View File

@@ -1,47 +1,48 @@
"use client";
import { useMemo, useState, useEffect, useRef } from "react";
import { Match, Team } from "@/lib/types";
import { Match, Team, teamName } from "@/lib/types";
import { STADIUMS, MATCH_STADIUMS } from "@/lib/stadiums";
import { Dictionary } from "@/lib/i18n";
import Flag from "./Flag";
function fmtShortDate(iso: string): string {
function fmtShortDate(iso: string, locale: string): string {
const d = new Date(iso);
return d.toLocaleDateString("de-DE", { weekday: "short", day: "2-digit", month: "2-digit" });
return d.toLocaleDateString(locale === "en" ? "en-US" : "de-DE", { weekday: "short", day: "2-digit", month: "2-digit" });
}
function fmtTime(iso: string): string {
function fmtTime(iso: string, locale: string): string {
const d = new Date(iso);
return d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
return d.toLocaleTimeString(locale === "en" ? "en-US" : "de-DE", { hour: "2-digit", minute: "2-digit" });
}
function scoreDisplay(m: Match): string {
function scoreDisplay(m: Match, dict: Dictionary): string {
if (m.status === "SCHEDULED" || m.status === "POSTPONED" || (m.homeScore == null && m.awayScore == null)) return " : ";
const base = `${m.homeScore ?? 0} : ${m.awayScore ?? 0}`;
if (m.homePenalty != null && m.awayPenalty != null) {
return `${base} (${m.homePenalty}:${m.awayPenalty} i.E.)`;
return `${base} (${m.homePenalty}:${m.awayPenalty} ${dict.bracket.penaltyShootout})`;
}
return base;
}
function statusLabel(m: Match): string {
switch (m.status) {
case "LIVE": case "IN_PLAY": return m.minute != null ? `${m.minute}'` : "Live";
case "PAUSED": return "Halbzeit";
case "POSTPONED": return "Verzögert";
case "FINISHED": return "Beendet";
default: return fmtTime(m.utcDate);
}
}
function isLive(m: Match): boolean {
return m.status === "LIVE" || m.status === "IN_PLAY" || m.status === "PAUSED";
}
export default function KoFixtures({ teams, matches }: { teams: Team[]; matches: Match[] }) {
export default function KoFixtures({ teams, matches, dict, locale }: { teams: Team[]; matches: Match[]; dict: Dictionary; locale: string }) {
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);
function statusLabel(m: Match): string {
switch (m.status) {
case "LIVE": case "IN_PLAY": return m.minute != null ? `${m.minute}'` : dict.status.live;
case "PAUSED": return dict.status.halftime;
case "POSTPONED": return dict.status.postponed;
case "FINISHED": return dict.status.finished;
default: return fmtTime(m.utcDate, locale);
}
}
const koMatches = useMemo(() => {
return matches
.filter((m) =>
@@ -107,7 +108,7 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
}, []);
if (koMatches.length === 0) {
return <div className="notice">Keine K.o.-Spiele mit bekannten Teams verfügbar.</div>;
return <div className="notice">{dict.koFixtures.noMatches}</div>;
}
return (
@@ -120,7 +121,7 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
textTransform: "uppercase", letterSpacing: "0.06em",
color: "var(--ink-dim)", margin: "0 0 10px",
}}>
{fmtShortDate(groupMatches[0].utcDate)}
{fmtShortDate(groupMatches[0].utcDate, locale)}
</h3>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{groupMatches.map((m) => {
@@ -128,8 +129,8 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
const away = m.awayTeamId ? teams.find(t => t.id === m.awayTeamId) : undefined;
const live = isLive(m);
const stageMap: Record<string, string> = {
R32: "R32", R16: "Achtelfinale", QF: "Viertelfinale",
SF: "Halbfinale", "3RD": "Platz 3", FINAL: "Finale",
R32: "R32", R16: dict.stage.r16, QF: dict.stage.qf,
SF: dict.stage.sf, "3RD": dict.stage.thirdPlace, FINAL: dict.stage.final,
};
const city = STADIUMS[MATCH_STADIUMS[m.matchNumber]]?.city;
@@ -162,7 +163,7 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
padding: "1px 6px", borderRadius: 999,
letterSpacing: "0.04em",
}}>
{live ? "LIVE" : "NÄCHSTES SPIEL"}
{live ? dict.koFixtures.liveBadge : dict.koFixtures.nextMatch}
</span>
)}
{m.status === "POSTPONED" && (
@@ -172,10 +173,10 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
padding: "1px 6px", borderRadius: 999,
letterSpacing: "0.04em",
}}>
VERZÖGERT
{dict.koFixtures.postponedBadge}
</span>
)}
{stageMap[m.stage] ?? m.stage} · Spiel {m.matchNumber || "—"}
{stageMap[m.stage] ?? m.stage} · {m.matchNumber ? dict.koFixtures.match(m.matchNumber) : dict.koFixtures.match(0).replace("0", "—")}
{city ? ` · ${city}` : ""}
</span>
<span style={{ color: live ? "var(--turf)" : undefined, fontWeight: live ? 700 : undefined }}>
@@ -185,15 +186,15 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
{/* Teams + Score */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<TeamLabel team={home} />
<TeamLabel team={home} locale={locale} />
<span style={{
fontFamily: "var(--font-mono)", fontSize: 22, fontWeight: 700,
color: live ? "var(--turf)" : "var(--ink)",
padding: "0 16px",
}}>
{scoreDisplay(m)}
{scoreDisplay(m, dict)}
</span>
<TeamLabel team={away} reverse />
<TeamLabel team={away} reverse locale={locale} />
</div>
{/* Torabfolge */}
@@ -204,7 +205,7 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
fontSize: 12, fontFamily: "var(--font-mono)",
}}>
<div style={{ color: "var(--ink-faint)", fontSize: 10, marginBottom: 4 }}>
Tore
{dict.label.goals}
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
{m.goals.map((g, i) => (
@@ -251,7 +252,7 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 22, lineHeight: 1,
}}
title="Nach oben scrollen"
title={dict.label.scrollToTop}
>
</button>
@@ -260,7 +261,7 @@ export default function KoFixtures({ teams, matches }: { teams: Team[]; matches:
);
}
function TeamLabel({ team, reverse }: { team?: Team; reverse?: boolean }) {
function TeamLabel({ team, reverse, locale }: { team?: Team; reverse?: boolean; locale: string }) {
return (
<div style={{
display: "flex", alignItems: "center", gap: 8,
@@ -273,7 +274,7 @@ function TeamLabel({ team, reverse }: { team?: Team; reverse?: boolean }) {
whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis",
maxWidth: 120,
}}>
{team?.localisedName ?? team?.name ?? "—"}
{teamName(team, locale)}
</span>
</div>
);