213 lines
7.9 KiB
TypeScript
213 lines
7.9 KiB
TypeScript
// 경량 i18n — ?lang=en 쿼리로 한/영 전환. SSG 유지(서버에서 lang 주입).
|
|
import type { Team } from "./types";
|
|
|
|
export type Lang = "ko" | "en";
|
|
|
|
export function parseLang(v: unknown): Lang {
|
|
return v === "en" ? "en" : "ko";
|
|
}
|
|
|
|
// 팀 짧은 표기 (KO=한글, EN=영문 약식)
|
|
const TEAM_EN: Record<string, string> = {
|
|
KOR: "Korea",
|
|
CZE: "Czechia",
|
|
MEX: "Mexico",
|
|
RSA: "S. Africa",
|
|
};
|
|
export function teamShort(team: Team, lang: Lang): string {
|
|
return lang === "en" ? TEAM_EN[team.code] ?? team.name : team.shortName;
|
|
}
|
|
|
|
const DAYS: Record<Lang, string[]> = {
|
|
ko: ["일", "월", "화", "수", "목", "금", "토"],
|
|
en: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
};
|
|
const MONTHS_EN = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
|
|
|
export function dayName(lang: Lang, dow: number): string {
|
|
return DAYS[lang][dow];
|
|
}
|
|
export function monthEn(m: number): string {
|
|
return MONTHS_EN[m - 1];
|
|
}
|
|
|
|
// roundLabel 안의 한국어 토막 번역
|
|
export function roundLabel(label: string, lang: Lang): string {
|
|
if (lang === "ko") return label;
|
|
return label.replace("개막전", "Opener");
|
|
}
|
|
|
|
type Dict = {
|
|
heroPill: string;
|
|
back: string;
|
|
share: string;
|
|
shareCopied: string;
|
|
introLead: string;
|
|
prizeLine1: string;
|
|
prizeLine2: string;
|
|
schedTitle: string;
|
|
schedGuide: string;
|
|
phase: { open: string; scheduled: string; locked: string; finished: string };
|
|
aiPicks: string;
|
|
draw: string;
|
|
joined: (n: string) => string;
|
|
aiBattle: string;
|
|
yourPickLabel: string;
|
|
yourChoice: string;
|
|
win: string;
|
|
drawLabel: string;
|
|
winProb: (team: string) => string;
|
|
drawOdds: string;
|
|
ctaBeat: string;
|
|
ctaResult: string;
|
|
ctaLocked: string;
|
|
ctaOpens: (d: string) => string;
|
|
emailPh: string;
|
|
notify: string;
|
|
submit: string;
|
|
myPick: string;
|
|
sameAI: (models: string, exact: boolean) => string;
|
|
soloPick: string;
|
|
shareThis: string;
|
|
copyLink: string;
|
|
copied: string;
|
|
goldDesc: string;
|
|
goldBtn: string;
|
|
crowdSub: string;
|
|
finalResult: string;
|
|
hit: string;
|
|
miss: string;
|
|
matchEnded: string;
|
|
cdUntil: string;
|
|
cdClosed: string;
|
|
final: string;
|
|
group: (g: string) => string;
|
|
footerNotOfficial: string;
|
|
footerDisc1: string;
|
|
footerDisc2: string;
|
|
footerDisc3: string;
|
|
hook: (a: string, b: string) => string;
|
|
adLabel: string;
|
|
ado2Tagline: string;
|
|
ado2Sub: string;
|
|
};
|
|
|
|
export const DICT: Record<Lang, Dict> = {
|
|
ko: {
|
|
heroPill: "AI와 함께하는 2026 월드컵 승부예측 챌린지",
|
|
back: "← 전체 일정",
|
|
share: "투표 공유하기",
|
|
shareCopied: "링크 복사됨 ✓",
|
|
introLead: "GPT · Claude · Gemini가 매 경기를\n서로 다르게 예측합니다.",
|
|
prizeLine1: "가장 많이 참여하고, 가장 정확하게 예측한",
|
|
prizeLine2: "최종 우승자에게 100만원 상금",
|
|
schedTitle: "전체 일정",
|
|
schedGuide: "투표하고 싶은 경기를 선택해주세요",
|
|
phase: { open: "투표 중", scheduled: "오픈 예정", locked: "투표 마감", finished: "종료" },
|
|
aiPicks: "AI 픽",
|
|
draw: "무",
|
|
joined: (n) => `${n}명 참여`,
|
|
aiBattle: "AI의 예측 대결",
|
|
yourPickLabel: "Your Pick · 내 예측",
|
|
yourChoice: "당신의 선택",
|
|
win: "승",
|
|
drawLabel: "무승부",
|
|
winProb: (t) => `${t} 승리 확률`,
|
|
drawOdds: "무승부 가능성",
|
|
ctaBeat: "AI보다 잘 맞히기",
|
|
ctaResult: "결과 보기",
|
|
ctaLocked: "예측 마감 (경기 시작)",
|
|
ctaOpens: (d) => `투표 오픈 ${d}`,
|
|
emailPh: "이메일 (결과·다음 경기 알림)",
|
|
notify: "경기 결과가 나오면 알려주세요. 다음 경기·100만원 챌린지 소식도 받겠습니다.",
|
|
submit: "제출하고 AI와 겨루기",
|
|
myPick: "내 예측",
|
|
sameAI: (m, exact) =>
|
|
`당신은 ${m}와 같은 선택입니다.${exact ? " 스코어까지 일치!" : ""} 나머지 AI는 생각이 다릅니다.`,
|
|
soloPick: "당신은 AI 셋 모두와 다른 선택! 소신 픽 🔥",
|
|
shareThis: "이 경기 공유하기",
|
|
copyLink: "링크 복사",
|
|
copied: "복사됨 ✓",
|
|
goldDesc: "경기마다 승패·스코어를 맞혀 포인트를 쌓고, 누적 1위에게 최종 100만원",
|
|
goldBtn: "100만 원에\n도전하기",
|
|
crowdSub: "| 참여자들의 선택",
|
|
finalResult: "최종 결과",
|
|
hit: "적중 ✓",
|
|
miss: "빗나감",
|
|
matchEnded: "경기 종료 · 최종 결과",
|
|
cdUntil: "투표 종료까지",
|
|
cdClosed: "투표가 마감되었습니다",
|
|
final: "Final",
|
|
group: (g) => `Group ${g}`,
|
|
footerNotOfficial:
|
|
"Independent AI prediction game — not affiliated with, endorsed by, or sponsored by FIFA or the official World Cup.",
|
|
footerDisc1: "FIFA 및 공식 월드컵과 무관한 독립 AI 예측 게임입니다 (제휴·후원·운영 아님).",
|
|
footerDisc2: "스포츠 분석·엔터테인먼트 목적의 예측 게임이며 베팅·도박을 권유하지 않습니다. AI 예측은 실제 결과를 보장하지 않습니다.",
|
|
footerDisc3: "100만 원 이벤트는 무료 참여형 챌린지입니다. 지급·동점 처리 조건은 별도 약관에 따릅니다. 이메일은 결과·이벤트 알림 목적으로만 사용됩니다.",
|
|
hook: (a, b) => `${a} vs ${b}, AI의 선택은 갈렸다`,
|
|
adLabel: "광고",
|
|
ado2Tagline: "콘텐츠 마케팅 자동화 플랫폼",
|
|
ado2Sub: "Content Marketing Automation Platform →",
|
|
},
|
|
en: {
|
|
heroPill: "AI-powered World Cup 2026 match predictions",
|
|
back: "← All matches",
|
|
share: "Share vote",
|
|
shareCopied: "Link copied ✓",
|
|
introLead: "GPT · Claude · Gemini predict\nevery match differently.",
|
|
prizeLine1: "The most active, most accurate",
|
|
prizeLine2: "overall winner takes ₩1,000,000",
|
|
schedTitle: "All Matches",
|
|
schedGuide: "Pick a match to predict",
|
|
phase: { open: "Voting", scheduled: "Opens soon", locked: "Closed", finished: "Ended" },
|
|
aiPicks: "AI picks",
|
|
draw: "Draw",
|
|
joined: (n) => `${n} joined`,
|
|
aiBattle: "AI Prediction Battle",
|
|
yourPickLabel: "Your Pick",
|
|
yourChoice: "Your Choice",
|
|
win: "Win",
|
|
drawLabel: "Draw",
|
|
winProb: (t) => `${t} win prob.`,
|
|
drawOdds: "Draw odds",
|
|
ctaBeat: "Beat the AI",
|
|
ctaResult: "View result",
|
|
ctaLocked: "Closed (kickoff)",
|
|
ctaOpens: (d) => `Opens ${d}`,
|
|
emailPh: "Email (result & next-match alerts)",
|
|
notify: "Notify me when the result is out. I'll also get next-match & ₩1,000,000 challenge news.",
|
|
submit: "Submit & face the AI",
|
|
myPick: "My Pick",
|
|
sameAI: (m, exact) =>
|
|
`You matched ${m}.${exact ? " Exact score too!" : ""} The other AIs disagree.`,
|
|
soloPick: "You disagree with all three AIs — bold pick 🔥",
|
|
shareThis: "Share this match",
|
|
copyLink: "Copy link",
|
|
copied: "Copied ✓",
|
|
goldDesc: "Earn points each match by nailing the result & score — the overall #1 wins ₩1,000,000.",
|
|
goldBtn: "Go for\n₩1,000,000",
|
|
crowdSub: "| What the crowd picked",
|
|
finalResult: "Final result",
|
|
hit: "Hit ✓",
|
|
miss: "Miss",
|
|
matchEnded: "Match ended · Final",
|
|
cdUntil: "Voting closes in",
|
|
cdClosed: "Voting closed",
|
|
final: "Final",
|
|
group: (g) => `Group ${g}`,
|
|
footerNotOfficial:
|
|
"Independent AI prediction game — not affiliated with, endorsed by, or sponsored by FIFA or the official World Cup.",
|
|
footerDisc1: "A fan-run prediction game using public match schedules; all picks are independent.",
|
|
footerDisc2: "A sports-analysis & entertainment prediction game. No betting or gambling. AI predictions do not guarantee real outcomes.",
|
|
footerDisc3: "The ₩1,000,000 event is a free-to-enter challenge. Payout & tie-break terms follow separate rules. Email is used only for result & event alerts.",
|
|
hook: (a, b) => `AI is split on ${a} vs ${b}`,
|
|
adLabel: "AD",
|
|
ado2Tagline: "Content Marketing Automation Platform",
|
|
ado2Sub: "Visit ado2.o2osolution.ai →",
|
|
},
|
|
};
|
|
|
|
export function dict(lang: Lang): Dict {
|
|
return DICT[lang];
|
|
}
|