Add sponsor slots and translated clues
Build and push image / build (push) Successful in 1m19s

This commit is contained in:
alexandrev-tibco
2026-05-06 09:25:40 +02:00
parent bee80c7776
commit 3048be5c29
9 changed files with 554 additions and 5 deletions
+18
View File
@@ -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
View File
@@ -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()} />;
}
+90
View File
@@ -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>
);
}
+12 -1
View File
@@ -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}
+7 -1
View File
@@ -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]}
+44
View File
@@ -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>
);
}
+36
View File
@@ -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
View File
@@ -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)}.`;
}
+28
View File
@@ -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,
};
}