init localize
This commit is contained in:
140
lib/i18n/de.ts
Normal file
140
lib/i18n/de.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { Dictionary } from "./types";
|
||||
|
||||
const de: Dictionary = {
|
||||
meta: {
|
||||
title: "WM 26 — Gruppen & K.o.-Baum",
|
||||
description:
|
||||
"Live-Gruppentabellen, Drittplatzierten-Wertung und der vollständige K.o.-Baum der FIFA WM 2026 mit Annex-C-Zuordnung und Polymarket-Wahrscheinlichkeiten.",
|
||||
},
|
||||
header: {
|
||||
hostCountries: "USA · Kanada · Mexiko",
|
||||
},
|
||||
status: {
|
||||
feedOffline: "Feed offline",
|
||||
updated: (time: string) => `Aktualisiert ${time}`,
|
||||
live: "Live",
|
||||
loading: "Lade Daten…",
|
||||
running: "läuft",
|
||||
halftime: "Halbzeit",
|
||||
finished: "Beendet",
|
||||
postponed: "Verzögert",
|
||||
},
|
||||
nav: {
|
||||
koFixtures: "K.O.-Spiele",
|
||||
groups: "Gruppen",
|
||||
groupFixtures: (group: string) => `Gruppenspiele – ${group}`,
|
||||
groupFixturesNoGroup: "Gruppenspiele",
|
||||
thirdPlace: "Drittplatzierte",
|
||||
bracket: "K.o.-Baum",
|
||||
simulation: "Simulation",
|
||||
},
|
||||
error: {
|
||||
feedUnreachable:
|
||||
"Die Live-Feeds sind gerade nicht erreichbar. Die Seite versucht es automatisch erneut.",
|
||||
unknown: "unbekannter Fehler",
|
||||
},
|
||||
footer: {
|
||||
dashboard: "WM 2026 Dashboard",
|
||||
dataSources: "Daten: football-data.org · Polymarket Gamma API · FIFA Annex C",
|
||||
},
|
||||
sim: {
|
||||
notice:
|
||||
"Simulation — alle ungespielten Spiele sind mit dem Polymarket-Favoriten vorbelegt. Die angezeigten Wahrscheinlichkeiten stammen von Polymarket.",
|
||||
heading: "Simulierter K.o.-Baum",
|
||||
},
|
||||
koFixtures: {
|
||||
noMatches: "Keine K.o.-Spiele mit bekannten Teams verfügbar.",
|
||||
liveBadge: "LIVE",
|
||||
nextMatch: "NÄCHSTES SPIEL",
|
||||
postponedBadge: "VERZÖGERT",
|
||||
match: (n: number) => `Spiel ${n}`,
|
||||
},
|
||||
label: {
|
||||
goals: "Tore",
|
||||
liveIndicator: "● LIVE",
|
||||
scrollToTop: "Nach oben scrollen",
|
||||
securelyQualified: "sicher qualifiziert",
|
||||
},
|
||||
stage: {
|
||||
r16: "Achtelfinale",
|
||||
qf: "Viertelfinale",
|
||||
sf: "Halbfinale",
|
||||
thirdPlace: "Platz 3",
|
||||
final: "Finale",
|
||||
},
|
||||
round: {
|
||||
r32: "Letzte 32",
|
||||
r16: "Achtelfinale",
|
||||
qf: "Viertelfinale",
|
||||
sf: "Halbfinale",
|
||||
final: "Finale",
|
||||
thirdPlace: "Spiel um Platz 3",
|
||||
},
|
||||
bracket: {
|
||||
provisionalTooltip: "vorläufig – Gruppe/Zuordnung noch nicht fix",
|
||||
match: (n: number) => `Spiel ${n}`,
|
||||
penaltyShootout: "i.E.",
|
||||
annexHeader: "Annex-C-Zuordnung der Drittplatzierten:",
|
||||
annexResolved:
|
||||
"aufgelöst — die acht Dritten sind den Gruppensiegern fest zugeteilt",
|
||||
annexPending:
|
||||
"noch offen — sobald die 8 besten Dritten feststehen, verbindet sich der Baum automatisch",
|
||||
winner: "Sieger",
|
||||
loser: "Verlierer",
|
||||
winnerFromMatch: (n: number) => `Sieger aus Spiel ${n}`,
|
||||
loserFromMatch: (n: number) => `Verlierer aus Spiel ${n}`,
|
||||
},
|
||||
legend: {
|
||||
winner: "Sieger / weiter",
|
||||
final: "Finale",
|
||||
fixed: "fix qualifiziert",
|
||||
provisional: "vorläufig (≈, nach aktueller Tabelle)",
|
||||
placeholder: "Platzhalter offen",
|
||||
probExplanation:
|
||||
"%-Werte: Polymarket-Wahrscheinlichkeit (falls verfügbar)",
|
||||
},
|
||||
tooltips: {
|
||||
provisionalPrefix: "aktuell",
|
||||
firstOfGroup: (group: string) => `1. Gruppe ${group}`,
|
||||
secondOfGroup: (group: string) => `2. Gruppe ${group}`,
|
||||
thirdOfGroup: (group: string) => `3. der Gruppe ${group}`,
|
||||
provisionalThird: (pool: string) => `aktuell 3. Gruppe ${pool}`,
|
||||
},
|
||||
slot: {
|
||||
winner: (group: string) => `Sieger ${group}`,
|
||||
runnerUp: (group: string) => `Zweiter ${group}`,
|
||||
thirdOfGroup: (group: string) => `3. der Gruppe ${group}`,
|
||||
thirdPlacePool: (pool: string) => `3. ${pool}`,
|
||||
},
|
||||
table: {
|
||||
rank: "#",
|
||||
team: "Mannschaft",
|
||||
group: "Gruppe",
|
||||
matches: "Sp",
|
||||
won: "S",
|
||||
drawn: "U",
|
||||
lost: "N",
|
||||
goals: "Tore",
|
||||
goalDiff: "±",
|
||||
points: "Pkt",
|
||||
status: "Status",
|
||||
},
|
||||
thirds: {
|
||||
explanation:
|
||||
"Acht der zwölf Gruppendritten erreichen die Runde der letzten 32. Gewertet wird gruppenübergreifend nach Punkten, Tordifferenz und Toren — der Direktvergleich entfällt, weil diese Teams nie gegeneinander gespielt haben. Die Trennlinie markiert den Schnitt zwischen Platz 8 und 9.",
|
||||
advances: "weiter",
|
||||
eliminated: "raus",
|
||||
},
|
||||
fixtures: {
|
||||
standingsTitle: (group: string) => `Tabelle – Gruppe ${group}`,
|
||||
noMatches: (group: string) =>
|
||||
`Für Gruppe ${group} liegen noch keine Spiele im Feed vor.`,
|
||||
},
|
||||
groups: {
|
||||
viewGroupGames: (group: string) => `Spiele der Gruppe ${group} ansehen`,
|
||||
groupLabel: (group: string) => `Gruppe ${group}`,
|
||||
viewGames: "Spiele ansehen →",
|
||||
},
|
||||
};
|
||||
|
||||
export default de;
|
||||
145
lib/i18n/en.ts
Normal file
145
lib/i18n/en.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Dictionary } from "./types";
|
||||
|
||||
function ordinal(n: number): string {
|
||||
const s = ["th", "st", "nd", "rd"];
|
||||
const v = n % 100;
|
||||
return n + (s[(v - 20) % 10] || s[v] || s[0]);
|
||||
}
|
||||
|
||||
const en: Dictionary = {
|
||||
meta: {
|
||||
title: "WC 26 — Groups & KO Bracket",
|
||||
description:
|
||||
"Live group tables, third-place ranking and the complete KO bracket for the FIFA World Cup 2026 with Annex C assignment and Polymarket probabilities.",
|
||||
},
|
||||
header: {
|
||||
hostCountries: "USA · Canada · Mexico",
|
||||
},
|
||||
status: {
|
||||
feedOffline: "Feed offline",
|
||||
updated: (time: string) => `Updated ${time}`,
|
||||
live: "Live",
|
||||
loading: "Loading…",
|
||||
running: "running",
|
||||
halftime: "Half time",
|
||||
finished: "Finished",
|
||||
postponed: "Postponed",
|
||||
},
|
||||
nav: {
|
||||
koFixtures: "KO Matches",
|
||||
groups: "Groups",
|
||||
groupFixtures: (group: string) => `Group Stage – ${group}`,
|
||||
groupFixturesNoGroup: "Group Stage",
|
||||
thirdPlace: "Third Place",
|
||||
bracket: "KO Bracket",
|
||||
simulation: "Simulation",
|
||||
},
|
||||
error: {
|
||||
feedUnreachable:
|
||||
"Live feeds are currently unavailable. The page will retry automatically.",
|
||||
unknown: "unknown error",
|
||||
},
|
||||
footer: {
|
||||
dashboard: "WC 2026 Dashboard",
|
||||
dataSources: "Data: football-data.org · Polymarket Gamma API · FIFA Annex C",
|
||||
},
|
||||
sim: {
|
||||
notice:
|
||||
"Simulation — all unplayed matches are pre-filled with the Polymarket favourite. Probabilities shown are from Polymarket.",
|
||||
heading: "Simulated KO Bracket",
|
||||
},
|
||||
koFixtures: {
|
||||
noMatches: "No KO matches with known teams available.",
|
||||
liveBadge: "LIVE",
|
||||
nextMatch: "NEXT MATCH",
|
||||
postponedBadge: "POSTPONED",
|
||||
match: (n: number) => `Match ${n}`,
|
||||
},
|
||||
label: {
|
||||
goals: "Goals",
|
||||
liveIndicator: "● LIVE",
|
||||
scrollToTop: "Scroll to top",
|
||||
securelyQualified: "securely qualified",
|
||||
},
|
||||
stage: {
|
||||
r16: "Round of 16",
|
||||
qf: "Quarter-finals",
|
||||
sf: "Semi-finals",
|
||||
thirdPlace: "3rd Place",
|
||||
final: "Final",
|
||||
},
|
||||
round: {
|
||||
r32: "Round of 32",
|
||||
r16: "Round of 16",
|
||||
qf: "Quarter-finals",
|
||||
sf: "Semi-finals",
|
||||
final: "Final",
|
||||
thirdPlace: "Third Place Match",
|
||||
},
|
||||
bracket: {
|
||||
provisionalTooltip: "provisional – group/assignment not yet fixed",
|
||||
match: (n: number) => `Match ${n}`,
|
||||
penaltyShootout: "PSO",
|
||||
annexHeader: "Annex C third-place assignment:",
|
||||
annexResolved:
|
||||
"resolved — the eight third-placed teams are permanently assigned to group winners",
|
||||
annexPending:
|
||||
"pending — once the 8 best third-placed teams are determined, the bracket auto-connects",
|
||||
winner: "Winner",
|
||||
loser: "Loser",
|
||||
winnerFromMatch: (n: number) => `Winner of match ${n}`,
|
||||
loserFromMatch: (n: number) => `Loser of match ${n}`,
|
||||
},
|
||||
legend: {
|
||||
winner: "Winner / advances",
|
||||
final: "Final",
|
||||
fixed: "securely qualified",
|
||||
provisional: "provisional (≈, current standings)",
|
||||
placeholder: "placeholder open",
|
||||
probExplanation: "% values: Polymarket probability (if available)",
|
||||
},
|
||||
tooltips: {
|
||||
provisionalPrefix: "currently",
|
||||
firstOfGroup: (group: string) => `${ordinal(1)} in Group ${group}`,
|
||||
secondOfGroup: (group: string) => `${ordinal(2)} in Group ${group}`,
|
||||
thirdOfGroup: (group: string) => `${ordinal(3)} in Group ${group}`,
|
||||
provisionalThird: (pool: string) => `currently 3rd of Group ${pool}`,
|
||||
},
|
||||
slot: {
|
||||
winner: (group: string) => `Winner ${group}`,
|
||||
runnerUp: (group: string) => `Runner-up ${group}`,
|
||||
thirdOfGroup: (group: string) => `3rd of Group ${group}`,
|
||||
thirdPlacePool: (pool: string) => `3rd ${pool}`,
|
||||
},
|
||||
table: {
|
||||
rank: "#",
|
||||
team: "Team",
|
||||
group: "Group",
|
||||
matches: "GP",
|
||||
won: "W",
|
||||
drawn: "D",
|
||||
lost: "L",
|
||||
goals: "Goals",
|
||||
goalDiff: "±",
|
||||
points: "Pts",
|
||||
status: "Status",
|
||||
},
|
||||
thirds: {
|
||||
explanation:
|
||||
"Eight of the twelve group third-placed teams advance to the Round of 32. Ranking is across all groups by points, goal difference and goals scored — head-to-head does not apply because these teams have never met. The separator marks the cut between 8th and 9th place.",
|
||||
advances: "advances",
|
||||
eliminated: "out",
|
||||
},
|
||||
fixtures: {
|
||||
standingsTitle: (group: string) => `Standings – Group ${group}`,
|
||||
noMatches: (group: string) =>
|
||||
`No matches available yet for Group ${group}.`,
|
||||
},
|
||||
groups: {
|
||||
viewGroupGames: (group: string) => `View Group ${group} matches`,
|
||||
groupLabel: (group: string) => `Group ${group}`,
|
||||
viewGames: "View matches →",
|
||||
},
|
||||
};
|
||||
|
||||
export default en;
|
||||
11
lib/i18n/index.ts
Normal file
11
lib/i18n/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Locale, Dictionary } from "./types";
|
||||
import de from "./de";
|
||||
import en from "./en";
|
||||
|
||||
export const dictionaries: Record<Locale, Dictionary> = { de, en };
|
||||
|
||||
export function getDictionary(locale: Locale): Dictionary {
|
||||
return dictionaries[locale] ?? en;
|
||||
}
|
||||
|
||||
export { type Locale, type Dictionary } from "./types";
|
||||
130
lib/i18n/types.ts
Normal file
130
lib/i18n/types.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
export type Locale = "de" | "en";
|
||||
|
||||
export interface Dictionary {
|
||||
meta: {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
header: {
|
||||
hostCountries: string;
|
||||
};
|
||||
status: {
|
||||
feedOffline: string;
|
||||
updated: (time: string) => string;
|
||||
live: string;
|
||||
loading: string;
|
||||
running: string;
|
||||
halftime: string;
|
||||
finished: string;
|
||||
postponed: string;
|
||||
};
|
||||
nav: {
|
||||
koFixtures: string;
|
||||
groups: string;
|
||||
groupFixtures: (group: string) => string;
|
||||
groupFixturesNoGroup: string;
|
||||
thirdPlace: string;
|
||||
bracket: string;
|
||||
simulation: string;
|
||||
};
|
||||
error: {
|
||||
feedUnreachable: string;
|
||||
unknown: string;
|
||||
};
|
||||
footer: {
|
||||
dashboard: string;
|
||||
dataSources: string;
|
||||
};
|
||||
sim: {
|
||||
notice: string;
|
||||
heading: string;
|
||||
};
|
||||
koFixtures: {
|
||||
noMatches: string;
|
||||
liveBadge: string;
|
||||
nextMatch: string;
|
||||
postponedBadge: string;
|
||||
match: (n: number) => string;
|
||||
};
|
||||
label: {
|
||||
goals: string;
|
||||
liveIndicator: string;
|
||||
scrollToTop: string;
|
||||
securelyQualified: string;
|
||||
};
|
||||
stage: {
|
||||
r16: string;
|
||||
qf: string;
|
||||
sf: string;
|
||||
thirdPlace: string;
|
||||
final: string;
|
||||
};
|
||||
round: {
|
||||
r32: string;
|
||||
r16: string;
|
||||
qf: string;
|
||||
sf: string;
|
||||
final: string;
|
||||
thirdPlace: string;
|
||||
};
|
||||
bracket: {
|
||||
provisionalTooltip: string;
|
||||
match: (n: number) => string;
|
||||
penaltyShootout: string;
|
||||
annexHeader: string;
|
||||
annexResolved: string;
|
||||
annexPending: string;
|
||||
winner: string;
|
||||
loser: string;
|
||||
winnerFromMatch: (n: number) => string;
|
||||
loserFromMatch: (n: number) => string;
|
||||
};
|
||||
legend: {
|
||||
winner: string;
|
||||
final: string;
|
||||
fixed: string;
|
||||
provisional: string;
|
||||
placeholder: string;
|
||||
probExplanation: string;
|
||||
};
|
||||
tooltips: {
|
||||
provisionalPrefix: string;
|
||||
firstOfGroup: (group: string) => string;
|
||||
secondOfGroup: (group: string) => string;
|
||||
thirdOfGroup: (group: string) => string;
|
||||
provisionalThird: (pool: string) => string;
|
||||
};
|
||||
slot: {
|
||||
winner: (group: string) => string;
|
||||
runnerUp: (group: string) => string;
|
||||
thirdOfGroup: (group: string) => string;
|
||||
thirdPlacePool: (pool: string) => string;
|
||||
};
|
||||
table: {
|
||||
rank: string;
|
||||
team: string;
|
||||
group: string;
|
||||
matches: string;
|
||||
won: string;
|
||||
drawn: string;
|
||||
lost: string;
|
||||
goals: string;
|
||||
goalDiff: string;
|
||||
points: string;
|
||||
status: string;
|
||||
};
|
||||
thirds: {
|
||||
explanation: string;
|
||||
advances: string;
|
||||
eliminated: string;
|
||||
};
|
||||
fixtures: {
|
||||
standingsTitle: (group: string) => string;
|
||||
noMatches: (group: string) => string;
|
||||
};
|
||||
groups: {
|
||||
viewGroupGames: (group: string) => string;
|
||||
groupLabel: (group: string) => string;
|
||||
viewGames: string;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user