This commit is contained in:
@@ -20,6 +20,15 @@ Optional feedback env vars:
|
||||
|
||||
- `N8N_FEEDBACK_WEBHOOK_URL`: n8n webhook URL used to receive feedback submissions
|
||||
|
||||
Optional sponsor env vars:
|
||||
|
||||
- `NEXT_PUBLIC_SPONSOR_ENABLED`: set to `false` to hide the sponsor slot
|
||||
- `NEXT_PUBLIC_SPONSOR_NAME`: sponsor name shown in the slot
|
||||
- `NEXT_PUBLIC_SPONSOR_TAGLINE`: short sponsor copy
|
||||
- `NEXT_PUBLIC_SPONSOR_CTA`: sponsor button text
|
||||
- `NEXT_PUBLIC_SPONSOR_URL`: outbound sponsor URL
|
||||
- `NEXT_PUBLIC_SPONSOR_LABEL`: small label above the sponsor name
|
||||
|
||||
## Production build
|
||||
|
||||
```bash
|
||||
@@ -73,6 +82,15 @@ To enable feedback submission, create a secret named `hidden11-feedback` in the
|
||||
|
||||
- `N8N_FEEDBACK_WEBHOOK_URL`
|
||||
|
||||
To configure the sponsor slot in Kubernetes, create a secret named `hidden11-sponsor` in the `hidden11` namespace with any of:
|
||||
|
||||
- `NEXT_PUBLIC_SPONSOR_ENABLED`
|
||||
- `NEXT_PUBLIC_SPONSOR_NAME`
|
||||
- `NEXT_PUBLIC_SPONSOR_TAGLINE`
|
||||
- `NEXT_PUBLIC_SPONSOR_CTA`
|
||||
- `NEXT_PUBLIC_SPONSOR_URL`
|
||||
- `NEXT_PUBLIC_SPONSOR_LABEL`
|
||||
|
||||
## ArgoCD
|
||||
|
||||
```bash
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
import HomeClient from "@/components/HomeClient";
|
||||
import { getSponsorConfig } from "@/lib/sponsor";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default function Page() {
|
||||
return <HomeClient />;
|
||||
return <HomeClient sponsor={getSponsorConfig()} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Sponsor hidden11",
|
||||
description: "Sponsor the daily football puzzle played by sharp football fans.",
|
||||
alternates: {
|
||||
canonical: "https://hidden11.app/sponsor",
|
||||
},
|
||||
openGraph: {
|
||||
title: "Sponsor hidden11",
|
||||
description: "Reach football fans inside a daily football puzzle with shareable results.",
|
||||
url: "https://hidden11.app/sponsor",
|
||||
siteName: "hidden11",
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Sponsor hidden11",
|
||||
description: "Reach football fans inside a daily football puzzle with shareable results.",
|
||||
},
|
||||
};
|
||||
|
||||
const sponsorEmail = "sponsors@hidden11.app";
|
||||
|
||||
export default function SponsorPage() {
|
||||
const subject = encodeURIComponent("Sponsor hidden11");
|
||||
const body = encodeURIComponent(
|
||||
"Hi hidden11,\n\nI am interested in sponsoring hidden11.\n\nBrand:\nWebsite:\nCampaign dates:\nGoal:\n",
|
||||
);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,#115c39_0,#061b12_45%,#04110c_100%)] text-white">
|
||||
<div className="mx-auto flex min-h-screen w-full max-w-4xl flex-col px-4 py-6 sm:px-6 lg:px-8">
|
||||
<header className="flex items-center justify-between gap-3">
|
||||
<Link href="/" className="text-lg font-black tracking-wide">
|
||||
hidden11
|
||||
</Link>
|
||||
<a
|
||||
href={`mailto:${sponsorEmail}?subject=${subject}&body=${body}`}
|
||||
className="rounded-lg bg-emerald-400 px-4 py-2 text-xs font-black uppercase text-pitch-950 transition hover:bg-emerald-300"
|
||||
>
|
||||
Email us
|
||||
</a>
|
||||
</header>
|
||||
|
||||
<section className="flex flex-1 flex-col justify-center py-12">
|
||||
<p className="text-xs font-black uppercase text-emerald-300">Sponsorship</p>
|
||||
<h1 className="mt-3 max-w-3xl text-4xl font-black leading-tight sm:text-6xl">
|
||||
Reach football fans in a daily game they come back to.
|
||||
</h1>
|
||||
<p className="mt-5 max-w-2xl text-base font-semibold leading-7 text-white/65 sm:text-lg">
|
||||
hidden11 is a daily football lineup puzzle with shareable results, streaks and football-history clues.
|
||||
Sponsorship appears directly in the playing flow and result moment.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 grid gap-3 sm:grid-cols-3">
|
||||
<Metric label="Format" value="Daily sponsor slot" />
|
||||
<Metric label="Placement" value="Home + result modal" />
|
||||
<Metric label="Contact" value={sponsorEmail} />
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
|
||||
<a
|
||||
href={`mailto:${sponsorEmail}?subject=${subject}&body=${body}`}
|
||||
className="inline-flex h-12 items-center justify-center rounded-lg bg-emerald-400 px-5 text-sm font-black uppercase text-pitch-950 transition hover:bg-emerald-300"
|
||||
>
|
||||
Sponsor hidden11
|
||||
</a>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex h-12 items-center justify-center rounded-lg border border-white/15 px-5 text-sm font-black uppercase text-white transition hover:bg-white/10"
|
||||
>
|
||||
View game
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function Metric({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-white/10 bg-white/[0.06] p-4">
|
||||
<p className="text-xs font-black uppercase text-white/45">{label}</p>
|
||||
<p className="mt-2 text-lg font-black text-white">{value}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,9 +8,11 @@ import Pitch from "@/components/Pitch";
|
||||
import PlayerSearch from "@/components/PlayerSearch";
|
||||
import FeedbackModal from "@/components/FeedbackModal";
|
||||
import ShareResult from "@/components/ShareResult";
|
||||
import SponsorSlot from "@/components/SponsorSlot";
|
||||
import StatsPanel from "@/components/StatsPanel";
|
||||
import { trackEvent } from "@/lib/analytics-events";
|
||||
import { buildGlobalStatsShareText } from "@/lib/share";
|
||||
import type { SponsorConfig } from "@/lib/sponsor";
|
||||
import {
|
||||
DIFFICULTIES,
|
||||
MAX_ATTEMPTS,
|
||||
@@ -37,7 +39,11 @@ import {
|
||||
} from "@/lib/storage";
|
||||
import type { DailyProgress, Difficulty, GlobalStats, Position } from "@/lib/types";
|
||||
|
||||
export default function Home() {
|
||||
type HomeProps = {
|
||||
sponsor: SponsorConfig;
|
||||
};
|
||||
|
||||
export default function Home({ sponsor }: HomeProps) {
|
||||
const dateKey = useMemo(() => getTodayKey(), []);
|
||||
const dailyPuzzles = useMemo(() => getDailyPuzzles(dateKey), [dateKey]);
|
||||
const [locale, setLocale] = useState<Locale>("en");
|
||||
@@ -316,6 +322,10 @@ export default function Home() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<SponsorSlot placement="home" sponsor={sponsor} />
|
||||
</div>
|
||||
|
||||
<section
|
||||
className="mt-3 overflow-hidden rounded-lg border border-white/10 bg-white/[0.06]"
|
||||
aria-label={t.puzzle}
|
||||
@@ -421,6 +431,7 @@ export default function Home() {
|
||||
stats={stats}
|
||||
t={t}
|
||||
onOpenStats={() => setStatsOpen(true)}
|
||||
sponsor={sponsor}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import type { Dictionary } from "@/lib/i18n";
|
||||
import type { Locale } from "@/lib/i18n";
|
||||
import { DIFFICULTIES, MAX_ATTEMPTS, getPuzzleNumber } from "@/lib/game-engine";
|
||||
import { buildChallengeShareText, buildShareText, buildShareUrl } from "@/lib/share";
|
||||
import type { SponsorConfig } from "@/lib/sponsor";
|
||||
import { formatElapsed } from "@/lib/time";
|
||||
import type { DailyProgress, GlobalStats } from "@/lib/types";
|
||||
import SponsorSlot from "./SponsorSlot";
|
||||
import StatsPanel from "./StatsPanel";
|
||||
|
||||
type ShareResultProps = {
|
||||
@@ -15,11 +17,12 @@ type ShareResultProps = {
|
||||
locale: Locale;
|
||||
onOpenStats: () => void;
|
||||
quote: { author: string; text: Record<Locale, string> };
|
||||
sponsor: SponsorConfig;
|
||||
stats: GlobalStats;
|
||||
t: Dictionary;
|
||||
};
|
||||
|
||||
export default function ShareResult({ dateKey, locale, onClose, onOpenStats, progress, quote, stats, t }: ShareResultProps) {
|
||||
export default function ShareResult({ dateKey, locale, onClose, onOpenStats, progress, quote, sponsor, stats, t }: ShareResultProps) {
|
||||
const [shared, setShared] = useState(false);
|
||||
const [origin, setOrigin] = useState("https://hidden11.app");
|
||||
const shareUrl = useMemo(() => buildShareUrl(dateKey, progress, origin), [dateKey, origin, progress]);
|
||||
@@ -143,6 +146,9 @@ export default function ShareResult({ dateKey, locale, onClose, onOpenStats, pro
|
||||
</section>
|
||||
<div className="mt-4" />
|
||||
<StatsPanel stats={stats} t={t} />
|
||||
<div className="mt-4">
|
||||
<SponsorSlot placement="result" sponsor={sponsor} />
|
||||
</div>
|
||||
<div className="mt-4 rounded-md border border-emerald-300/20 bg-emerald-300/10 p-3">
|
||||
<p className="text-sm font-semibold leading-6 text-emerald-50">
|
||||
“{quote.text[locale]}”
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { trackEvent } from "@/lib/analytics-events";
|
||||
import type { SponsorConfig } from "@/lib/sponsor";
|
||||
|
||||
type SponsorSlotProps = {
|
||||
placement: "home" | "result";
|
||||
sponsor: SponsorConfig;
|
||||
};
|
||||
|
||||
export default function SponsorSlot({ placement, sponsor }: SponsorSlotProps) {
|
||||
if (!sponsor.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function trackClick() {
|
||||
trackEvent("sponsor_click", {
|
||||
placement,
|
||||
sponsor_name: sponsor.name,
|
||||
sponsor_url: sponsor.url,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="rounded-lg border border-emerald-300/20 bg-white/[0.05] p-3">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[10px] font-black uppercase text-emerald-200/55">{sponsor.label}</p>
|
||||
<p className="mt-1 truncate text-sm font-black text-white">{sponsor.name}</p>
|
||||
<p className="mt-1 text-xs font-semibold leading-5 text-white/55">{sponsor.tagline}</p>
|
||||
</div>
|
||||
<a
|
||||
href={sponsor.url}
|
||||
target={sponsor.url.startsWith("mailto:") ? undefined : "_blank"}
|
||||
rel={sponsor.url.startsWith("mailto:") ? undefined : "noopener noreferrer"}
|
||||
onClick={trackClick}
|
||||
className="inline-flex h-9 shrink-0 items-center justify-center rounded-lg bg-emerald-400 px-3 text-xs font-black uppercase text-pitch-950 transition hover:bg-emerald-300"
|
||||
>
|
||||
{sponsor.cta}
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -40,6 +40,42 @@ spec:
|
||||
name: hidden11-feedback
|
||||
key: N8N_FEEDBACK_WEBHOOK_URL
|
||||
optional: true
|
||||
- name: NEXT_PUBLIC_SPONSOR_ENABLED
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hidden11-sponsor
|
||||
key: NEXT_PUBLIC_SPONSOR_ENABLED
|
||||
optional: true
|
||||
- name: NEXT_PUBLIC_SPONSOR_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hidden11-sponsor
|
||||
key: NEXT_PUBLIC_SPONSOR_NAME
|
||||
optional: true
|
||||
- name: NEXT_PUBLIC_SPONSOR_TAGLINE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hidden11-sponsor
|
||||
key: NEXT_PUBLIC_SPONSOR_TAGLINE
|
||||
optional: true
|
||||
- name: NEXT_PUBLIC_SPONSOR_CTA
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hidden11-sponsor
|
||||
key: NEXT_PUBLIC_SPONSOR_CTA
|
||||
optional: true
|
||||
- name: NEXT_PUBLIC_SPONSOR_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hidden11-sponsor
|
||||
key: NEXT_PUBLIC_SPONSOR_URL
|
||||
optional: true
|
||||
- name: NEXT_PUBLIC_SPONSOR_LABEL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hidden11-sponsor
|
||||
key: NEXT_PUBLIC_SPONSOR_LABEL
|
||||
optional: true
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
|
||||
+317
-2
@@ -636,9 +636,324 @@ export function getDictionary(locale: Locale): Dictionary {
|
||||
return {
|
||||
...fallback,
|
||||
...dictionary,
|
||||
clue: {
|
||||
clue: createClueDictionary(locale, {
|
||||
...fallback.clue,
|
||||
...dictionary.clue,
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function createClueDictionary(locale: Locale, overrides: Record<string, string>): Record<string, string> {
|
||||
return new Proxy(overrides, {
|
||||
get(target, property) {
|
||||
if (typeof property !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return target[property] ?? translateGeneratedClue(property, locale);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function translateGeneratedClue(clue: string, locale: Locale): string {
|
||||
if (locale === "en") {
|
||||
return clue;
|
||||
}
|
||||
|
||||
const sharedClub = clue.match(/^(\d+|Two) players share the same club\.$/);
|
||||
if (sharedClub) {
|
||||
const count = sharedClub[1] === "Two" ? 2 : Number(sharedClub[1]);
|
||||
return tSharedClub(locale, count);
|
||||
}
|
||||
|
||||
const leagueCount = clue.match(/^(\d+) players? play in (.+)\.$/);
|
||||
if (leagueCount) {
|
||||
return tLeagueCount(locale, Number(leagueCount[1]), leagueCount[2]);
|
||||
}
|
||||
|
||||
const differentClubs = clue.match(/^All five players come from different clubs\.$/);
|
||||
if (differentClubs) {
|
||||
return tAllDifferentClubs(locale);
|
||||
}
|
||||
|
||||
const moreChampions = clue.match(/^The (.+) has more Champions League titles than the (.+)\.$/);
|
||||
if (moreChampions) {
|
||||
return tMoreChampions(locale, moreChampions[1], moreChampions[2]);
|
||||
}
|
||||
|
||||
const sameChampions = clue.match(/^The (.+) and (.+) have won the same number of Champions League titles\.$/);
|
||||
if (sameChampions) {
|
||||
return tSameChampions(locale, sameChampions[1], sameChampions[2]);
|
||||
}
|
||||
|
||||
const wonLeagueTitles = clue.match(/^The (.+) has won (\d+) league titles\.$/);
|
||||
if (wonLeagueTitles) {
|
||||
return tWonLeagueTitles(locale, wonLeagueTitles[1], Number(wonLeagueTitles[2]));
|
||||
}
|
||||
|
||||
const wonCountries = clue.match(/^The (.+) has won league titles in (.+)\.$/);
|
||||
if (wonCountries) {
|
||||
return tWonCountries(locale, wonCountries[1], wonCountries[2]);
|
||||
}
|
||||
|
||||
const bothTopFlight = clue.match(/^The (.+) and (.+) both have played in top-flight football\.$/);
|
||||
if (bothTopFlight) {
|
||||
return tBothTopFlight(locale, bothTopFlight[1], bothTopFlight[2]);
|
||||
}
|
||||
|
||||
const teammates = clue.match(/^The (.+) and (.+) were once teammates at (.+)\.$/);
|
||||
if (teammates) {
|
||||
return tTeammates(locale, teammates[1], teammates[2], teammates[3]);
|
||||
}
|
||||
|
||||
const sameLeagueTitles = clue.match(/^The (.+) and (.+) have won the same number of league titles\.$/);
|
||||
if (sameLeagueTitles) {
|
||||
return tSameLeagueTitles(locale, sameLeagueTitles[1], sameLeagueTitles[2]);
|
||||
}
|
||||
|
||||
const moreLeagueTitles = clue.match(/^The (.+) has more league titles than the (.+)\.$/);
|
||||
if (moreLeagueTitles) {
|
||||
return tMoreLeagueTitles(locale, moreLeagueTitles[1], moreLeagueTitles[2]);
|
||||
}
|
||||
|
||||
const nationality = clue.match(/^The (.+) is from (.+)\.$/);
|
||||
if (nationality) {
|
||||
return tNationality(locale, nationality[1], nationality[2]);
|
||||
}
|
||||
|
||||
return clue;
|
||||
}
|
||||
|
||||
const positionTranslations: Record<Locale, Record<string, string>> = {
|
||||
en: { defender: "defender", goalkeeper: "goalkeeper", midfielder: "midfielder", striker: "striker", winger: "winger" },
|
||||
es: { defender: "defensa", goalkeeper: "portero", midfielder: "centrocampista", striker: "delantero", winger: "extremo" },
|
||||
fr: { defender: "défenseur", goalkeeper: "gardien", midfielder: "milieu", striker: "attaquant", winger: "ailier" },
|
||||
de: { defender: "Verteidiger", goalkeeper: "Torwart", midfielder: "Mittelfeldspieler", striker: "Stürmer", winger: "Flügelspieler" },
|
||||
it: { defender: "difensore", goalkeeper: "portiere", midfielder: "centrocampista", striker: "attaccante", winger: "ala" },
|
||||
pt: { defender: "defesa", goalkeeper: "guarda-redes", midfielder: "médio", striker: "avançado", winger: "extremo" },
|
||||
};
|
||||
|
||||
const countryTranslations: Record<Locale, Record<string, string>> = {
|
||||
en: {},
|
||||
es: {
|
||||
Argentina: "Argentina",
|
||||
Austria: "Austria",
|
||||
Belgium: "Bélgica",
|
||||
Brazil: "Brasil",
|
||||
Canada: "Canadá",
|
||||
Croatia: "Croacia",
|
||||
England: "Inglaterra",
|
||||
France: "Francia",
|
||||
Georgia: "Georgia",
|
||||
Germany: "Alemania",
|
||||
Italy: "Italia",
|
||||
Netherlands: "Países Bajos",
|
||||
Norway: "Noruega",
|
||||
Poland: "Polonia",
|
||||
Portugal: "Portugal",
|
||||
Scotland: "Escocia",
|
||||
Spain: "España",
|
||||
Sweden: "Suecia",
|
||||
Switzerland: "Suiza",
|
||||
Ukraine: "Ucrania",
|
||||
},
|
||||
fr: {
|
||||
Argentina: "Argentine",
|
||||
Austria: "Autriche",
|
||||
Belgium: "Belgique",
|
||||
Brazil: "Brésil",
|
||||
Canada: "Canada",
|
||||
Croatia: "Croatie",
|
||||
England: "Angleterre",
|
||||
France: "France",
|
||||
Georgia: "Géorgie",
|
||||
Germany: "Allemagne",
|
||||
Italy: "Italie",
|
||||
Netherlands: "Pays-Bas",
|
||||
Norway: "Norvège",
|
||||
Poland: "Pologne",
|
||||
Portugal: "Portugal",
|
||||
Scotland: "Écosse",
|
||||
Spain: "Espagne",
|
||||
Sweden: "Suède",
|
||||
Switzerland: "Suisse",
|
||||
Ukraine: "Ukraine",
|
||||
},
|
||||
de: {
|
||||
Argentina: "Argentinien",
|
||||
Austria: "Österreich",
|
||||
Belgium: "Belgien",
|
||||
Brazil: "Brasilien",
|
||||
Canada: "Kanada",
|
||||
Croatia: "Kroatien",
|
||||
England: "England",
|
||||
France: "Frankreich",
|
||||
Georgia: "Georgien",
|
||||
Germany: "Deutschland",
|
||||
Italy: "Italien",
|
||||
Netherlands: "Niederlande",
|
||||
Norway: "Norwegen",
|
||||
Poland: "Polen",
|
||||
Portugal: "Portugal",
|
||||
Scotland: "Schottland",
|
||||
Spain: "Spanien",
|
||||
Sweden: "Schweden",
|
||||
Switzerland: "Schweiz",
|
||||
Ukraine: "Ukraine",
|
||||
},
|
||||
it: {
|
||||
Argentina: "Argentina",
|
||||
Austria: "Austria",
|
||||
Belgium: "Belgio",
|
||||
Brazil: "Brasile",
|
||||
Canada: "Canada",
|
||||
Croatia: "Croazia",
|
||||
England: "Inghilterra",
|
||||
France: "Francia",
|
||||
Georgia: "Georgia",
|
||||
Germany: "Germania",
|
||||
Italy: "Italia",
|
||||
Netherlands: "Paesi Bassi",
|
||||
Norway: "Norvegia",
|
||||
Poland: "Polonia",
|
||||
Portugal: "Portogallo",
|
||||
Scotland: "Scozia",
|
||||
Spain: "Spagna",
|
||||
Sweden: "Svezia",
|
||||
Switzerland: "Svizzera",
|
||||
Ukraine: "Ucraina",
|
||||
},
|
||||
pt: {
|
||||
Argentina: "Argentina",
|
||||
Austria: "Áustria",
|
||||
Belgium: "Bélgica",
|
||||
Brazil: "Brasil",
|
||||
Canada: "Canadá",
|
||||
Croatia: "Croácia",
|
||||
England: "Inglaterra",
|
||||
France: "França",
|
||||
Georgia: "Geórgia",
|
||||
Germany: "Alemanha",
|
||||
Italy: "Itália",
|
||||
Netherlands: "Países Baixos",
|
||||
Norway: "Noruega",
|
||||
Poland: "Polónia",
|
||||
Portugal: "Portugal",
|
||||
Scotland: "Escócia",
|
||||
Spain: "Espanha",
|
||||
Sweden: "Suécia",
|
||||
Switzerland: "Suíça",
|
||||
Ukraine: "Ucrânia",
|
||||
},
|
||||
};
|
||||
|
||||
function pos(locale: Locale, position: string): string {
|
||||
return positionTranslations[locale][position] ?? position;
|
||||
}
|
||||
|
||||
function countries(locale: Locale, value: string): string {
|
||||
return value
|
||||
.split(" and ")
|
||||
.map((country) => countryTranslations[locale][country] ?? country)
|
||||
.join(tAnd(locale));
|
||||
}
|
||||
|
||||
function tAnd(locale: Locale): string {
|
||||
return locale === "de" ? " und " : locale === "it" ? " e " : locale === "pt" ? " e " : locale === "fr" ? " et " : " y ";
|
||||
}
|
||||
|
||||
function tSharedClub(locale: Locale, count: number): string {
|
||||
if (locale === "es") return count === 2 ? "Dos jugadores comparten club." : `${count} jugadores comparten club.`;
|
||||
if (locale === "fr") return count === 2 ? "Deux joueurs partagent le même club." : `${count} joueurs partagent le même club.`;
|
||||
if (locale === "de") return count === 2 ? "Zwei Spieler haben denselben Klub." : `${count} Spieler haben denselben Klub.`;
|
||||
if (locale === "it") return count === 2 ? "Due giocatori condividono lo stesso club." : `${count} giocatori condividono lo stesso club.`;
|
||||
return count === 2 ? "Dois jogadores partilham o mesmo clube." : `${count} jogadores partilham o mesmo clube.`;
|
||||
}
|
||||
|
||||
function tLeagueCount(locale: Locale, count: number, league: string): string {
|
||||
if (locale === "es") return `${count} ${count === 1 ? "jugador juega" : "jugadores juegan"} en ${league}.`;
|
||||
if (locale === "fr") return `${count} ${count === 1 ? "joueur évolue" : "joueurs évoluent"} en ${league}.`;
|
||||
if (locale === "de") return `${count} ${count === 1 ? "Spieler spielt" : "Spieler spielen"} in der ${league}.`;
|
||||
if (locale === "it") return `${count} ${count === 1 ? "giocatore gioca" : "giocatori giocano"} in ${league}.`;
|
||||
return `${count} ${count === 1 ? "jogador joga" : "jogadores jogam"} na ${league}.`;
|
||||
}
|
||||
|
||||
function tAllDifferentClubs(locale: Locale): string {
|
||||
if (locale === "es") return "Los cinco jugadores son de clubes diferentes.";
|
||||
if (locale === "fr") return "Les cinq joueurs viennent de clubs différents.";
|
||||
if (locale === "de") return "Alle fünf Spieler kommen aus unterschiedlichen Klubs.";
|
||||
if (locale === "it") return "Tutti e cinque i giocatori vengono da club diversi.";
|
||||
return "Os cinco jogadores vêm de clubes diferentes.";
|
||||
}
|
||||
|
||||
function tMoreChampions(locale: Locale, left: string, right: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, left)} tiene más Champions League que el ${pos(locale, right)}.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, left)} a plus de Ligues des champions que le ${pos(locale, right)}.`;
|
||||
if (locale === "de") return `Der ${pos(locale, left)} hat mehr Champions-League-Titel als der ${pos(locale, right)}.`;
|
||||
if (locale === "it") return `Il ${pos(locale, left)} ha più Champions League del ${pos(locale, right)}.`;
|
||||
return `O ${pos(locale, left)} tem mais Champions League do que o ${pos(locale, right)}.`;
|
||||
}
|
||||
|
||||
function tSameChampions(locale: Locale, left: string, right: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, left)} y el ${pos(locale, right)} han ganado el mismo número de Champions League.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, left)} et le ${pos(locale, right)} ont gagné le même nombre de Ligues des champions.`;
|
||||
if (locale === "de") return `Der ${pos(locale, left)} und der ${pos(locale, right)} haben gleich viele Champions-League-Titel gewonnen.`;
|
||||
if (locale === "it") return `Il ${pos(locale, left)} e il ${pos(locale, right)} hanno vinto lo stesso numero di Champions League.`;
|
||||
return `O ${pos(locale, left)} e o ${pos(locale, right)} ganharam o mesmo número de Champions League.`;
|
||||
}
|
||||
|
||||
function tWonLeagueTitles(locale: Locale, position: string, count: number): string {
|
||||
if (locale === "es") return `El ${pos(locale, position)} ha ganado ${count} ${count === 1 ? "liga" : "ligas"}.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, position)} a gagné ${count} ${count === 1 ? "championnat" : "championnats"}.`;
|
||||
if (locale === "de") return `Der ${pos(locale, position)} hat ${count} Ligatitel gewonnen.`;
|
||||
if (locale === "it") return `Il ${pos(locale, position)} ha vinto ${count} ${count === 1 ? "campionato" : "campionati"}.`;
|
||||
return `O ${pos(locale, position)} ganhou ${count} ${count === 1 ? "campeonato" : "campeonatos"}.`;
|
||||
}
|
||||
|
||||
function tWonCountries(locale: Locale, position: string, value: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, position)} ha ganado ligas en ${countries(locale, value)}.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, position)} a gagné des championnats en ${countries(locale, value)}.`;
|
||||
if (locale === "de") return `Der ${pos(locale, position)} hat Ligatitel in ${countries(locale, value)} gewonnen.`;
|
||||
if (locale === "it") return `Il ${pos(locale, position)} ha vinto campionati in ${countries(locale, value)}.`;
|
||||
return `O ${pos(locale, position)} ganhou campeonatos em ${countries(locale, value)}.`;
|
||||
}
|
||||
|
||||
function tBothTopFlight(locale: Locale, left: string, right: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, left)} y el ${pos(locale, right)} han jugado en primera división.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, left)} et le ${pos(locale, right)} ont joué en première division.`;
|
||||
if (locale === "de") return `Der ${pos(locale, left)} und der ${pos(locale, right)} haben erstklassig gespielt.`;
|
||||
if (locale === "it") return `Il ${pos(locale, left)} e il ${pos(locale, right)} hanno giocato in massima serie.`;
|
||||
return `O ${pos(locale, left)} e o ${pos(locale, right)} jogaram na primeira divisão.`;
|
||||
}
|
||||
|
||||
function tTeammates(locale: Locale, left: string, right: string, club: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, left)} y el ${pos(locale, right)} fueron compañeros en ${club}.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, left)} et le ${pos(locale, right)} ont été coéquipiers à ${club}.`;
|
||||
if (locale === "de") return `Der ${pos(locale, left)} und der ${pos(locale, right)} waren einst Teamkollegen bei ${club}.`;
|
||||
if (locale === "it") return `Il ${pos(locale, left)} e il ${pos(locale, right)} sono stati compagni al ${club}.`;
|
||||
return `O ${pos(locale, left)} e o ${pos(locale, right)} foram colegas no ${club}.`;
|
||||
}
|
||||
|
||||
function tSameLeagueTitles(locale: Locale, left: string, right: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, left)} y el ${pos(locale, right)} han ganado el mismo número de ligas.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, left)} et le ${pos(locale, right)} ont gagné le même nombre de championnats.`;
|
||||
if (locale === "de") return `Der ${pos(locale, left)} und der ${pos(locale, right)} haben gleich viele Ligatitel gewonnen.`;
|
||||
if (locale === "it") return `Il ${pos(locale, left)} e il ${pos(locale, right)} hanno vinto lo stesso numero di campionati.`;
|
||||
return `O ${pos(locale, left)} e o ${pos(locale, right)} ganharam o mesmo número de campeonatos.`;
|
||||
}
|
||||
|
||||
function tMoreLeagueTitles(locale: Locale, left: string, right: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, left)} tiene más ligas que el ${pos(locale, right)}.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, left)} a plus de championnats que le ${pos(locale, right)}.`;
|
||||
if (locale === "de") return `Der ${pos(locale, left)} hat mehr Ligatitel als der ${pos(locale, right)}.`;
|
||||
if (locale === "it") return `Il ${pos(locale, left)} ha più campionati del ${pos(locale, right)}.`;
|
||||
return `O ${pos(locale, left)} tem mais campeonatos do que o ${pos(locale, right)}.`;
|
||||
}
|
||||
|
||||
function tNationality(locale: Locale, position: string, country: string): string {
|
||||
if (locale === "es") return `El ${pos(locale, position)} es de ${countries(locale, country)}.`;
|
||||
if (locale === "fr") return `Le ${pos(locale, position)} vient de ${countries(locale, country)}.`;
|
||||
if (locale === "de") return `Der ${pos(locale, position)} kommt aus ${countries(locale, country)}.`;
|
||||
if (locale === "it") return `Il ${pos(locale, position)} viene da ${countries(locale, country)}.`;
|
||||
return `O ${pos(locale, position)} é de ${countries(locale, country)}.`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
export type SponsorConfig = {
|
||||
cta: string;
|
||||
enabled: boolean;
|
||||
label: string;
|
||||
name: string;
|
||||
tagline: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export function getSponsorConfig(): SponsorConfig {
|
||||
const name = process.env.NEXT_PUBLIC_SPONSOR_NAME?.trim() || "Sponsor hidden11";
|
||||
const url = process.env.NEXT_PUBLIC_SPONSOR_URL?.trim() || "https://hidden11.app/sponsor";
|
||||
const tagline =
|
||||
process.env.NEXT_PUBLIC_SPONSOR_TAGLINE?.trim() ||
|
||||
"Reach football fans inside a daily puzzle they actually finish.";
|
||||
const cta = process.env.NEXT_PUBLIC_SPONSOR_CTA?.trim() || "Become a sponsor";
|
||||
const label = process.env.NEXT_PUBLIC_SPONSOR_LABEL?.trim() || "Today's sponsor";
|
||||
const enabled = process.env.NEXT_PUBLIC_SPONSOR_ENABLED !== "false";
|
||||
|
||||
return {
|
||||
cta,
|
||||
enabled,
|
||||
label,
|
||||
name,
|
||||
tagline,
|
||||
url,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user