add other languages

This commit is contained in:
Fabrice Lamant
2024-07-30 20:07:29 +02:00
parent e575c5606b
commit 99fa4cdf66
6864 changed files with 1484485 additions and 9209 deletions

410
src/calendar.ts Normal file
View File

@ -0,0 +1,410 @@
import Debug from "debug";
import autoprefixer from "autoprefixer";
import postcss from "postcss";
import tailwindcss from "tailwindcss";
import { Event, NOC, Sport } from "./types";
import { getAllSportsKeys, getSportIcon } from "./sports";
import { existsSync, writeFileSync } from "fs";
import { hasFile, readFile, saveFile } from "./io";
import cheerio from "cheerio";
import { getNOCFlag, getNOCName, isValidNOC } from "./nocs";
import * as translate from "./translate";
import { generateICS } from "./ics";
export class Calendar {
private language: string;
private debug: Debug.Debugger;
private events: Event[] = [];
private nocs: string[] = [];
private sports: Sport[] = [];
constructor(language: string) {
this.language = language;
this.debug = Debug(`paris2024:calendar:${language}`);
}
private addSport(sportKey: string, sportName: string) {
if (!this.sports.find(sport => sport.key === sportKey)) {
this.debug(`Adding sport: ${sportName} (${sportKey})`);
this.sports.push({
key: sportKey,
name: sportName,
NOCS: [],
});
}
}
private addNOC(noc: string) {
if (!this.nocs.includes(noc)) {
this.debug(`Adding NOC: ${noc}`);
this.nocs.push(noc);
}
}
private addSportNOC(sportKey: string, sportName: string, noc: string) {
this.addSport(sportKey, sportName);
const sport = this.sports.find((sport) => sport.key === sportKey)!;
if (!sport.NOCS.includes(noc)) {
this.debug(`Adding NOC: ${noc} to sport: ${sportKey}`);
sport.NOCS.push(noc);
}
};
public async generate() {
this.debug(`Generating calendar for ${this.language}`);
for (const sportKey of getAllSportsKeys()) {
await this.getSportCalendar(sportKey);
}
this.genereateEventsCeremonies();
this.generateCalendars();
this.generateMainPage();
this.generateTodaysPage();
this.generateCSS();
}
private async getSportCalendar(sportKey: string) {
const schedule = await this.downloadScheduleFromOfficialWebsite(sportKey);
this.generateEventsFromSchedule(sportKey, schedule);
}
private async downloadScheduleFromOfficialWebsite(sportKey: string) {
this.debug(`Checking cache for schedule for ${sportKey}`);
const cacheFile = `${__dirname}/../cache/${this.language}/${sportKey}.html`;
if (!hasFile(cacheFile)) {
this.debug(`Downloading schedule for ${sportKey} in ${this.language}`);
const response = await fetch(`https://olympics.com/${this.language}/paris-2024/schedule/${sportKey}`);
const content = await response.text();
saveFile(cacheFile, content);
}
const html = readFile(cacheFile);
const $ = cheerio.load(html);
return JSON.parse($("#__NEXT_DATA__").text());
}
private generateEventsFromSchedule(sportKey: string, data: any) {
const sportName = data.query.pDisciplineLabel;
const sportIcon = getSportIcon(sportKey);
this.addSport(sportKey, sportName);
data.props.pageProps.scheduleDataSource.initialSchedule.units.forEach((unit: any) => {
unit.startDateTimeUtc = new Date(unit.startDate).toISOString().replace(".000", "");
unit.endDateTimeUtc = new Date(unit.endDate).toISOString().replace(".000", "");
const slugify = (text: string) => text.toLowerCase().replace(/\s/g, "-")
.replace(/[^a-z0-9-]/g, "")
.replace(/-+/g, "-");
const event: Event = {
UID: `${unit.startDateTimeUtc.replace(/[:-]/g, "")}-${sportKey}-${slugify(unit.eventUnitName).toUpperCase()}`,
DTSTAMP: unit.startDateTimeUtc.replace(/[:-]/g, ""),
DTSTART: unit.startDateTimeUtc.replace(/[:-]/g, ""),
DTEND: unit.endDateTimeUtc.replace(/[:-]/g, ""),
DESCRIPTION: `${sportName} - ${unit.eventUnitName}`,
SUMMARY: `${sportIcon} ${unit.eventUnitName}`.trim(),
LOCATION: unit.venueDescription,
_SPORT: sportKey,
_NOCS: [],
_COMPETITORS: [],
_UNITNAME: unit.eventUnitName,
_MEDAL: !!unit.medalFlag,
_GENDER: unit.genderCode,
};
if (unit.competitors) {
const competitors = unit.competitors
.filter((competitor: any) => competitor.noc && isValidNOC(competitor.noc))
.sort((a: any, b: any) => a.order > b.order ? 1 : -1);
event._NOCS = competitors.map((competitor: any) => {
this.addSportNOC(sportKey, sportName, competitor.noc);
this.addNOC(competitor.noc);
return competitor.noc;
});
// two competitors, we put them in the summary
if (competitors.length === 2) {
const competitor1 = competitors.shift();
const competitor2 = competitors.shift();
event.UID += `-${competitor1.noc}-${competitor2.noc}`;
if (competitor1.name !== getNOCName(competitor1.noc)) {
event.SUMMARY = `${sportIcon} ${competitor1.name} ${getNOCFlag(competitor1.noc)} - ${getNOCFlag(competitor2.noc)} ${competitor2.name}`;
} else {
event.SUMMARY = `${sportIcon} ${competitor1.noc} ${getNOCFlag(competitor1.noc)} - ${getNOCFlag(competitor2.noc)} ${competitor2.noc}`;
}
} else if (competitors.length !== 0) {
// more than two, we put them in the description
competitors
.sort((a: any, b: any) => a.name > b.name ? 1 : -1)
.forEach((competitor: any) => {
if (competitor.name !== getNOCName(competitor.noc)) {
event.DESCRIPTION += `\\n${getNOCFlag(competitor.noc)} ${competitor.name}`;
event._COMPETITORS.push({ noc: competitor.noc, name: `${getNOCFlag(competitor.noc)} ${competitor.name}` });
} else {
event.DESCRIPTION += `\\n${getNOCFlag(competitor.noc)} ${competitor.noc}`;
}
});
}
}
this.events.push(event);
});
}
private genereateEventsCeremonies() {
let startDateUtc = new Date("2024-07-26T17:30:00Z").toISOString().replace(".000", "");
let endDateUtc = new Date("2024-07-26T21:00:00Z").toISOString().replace(".000", "");
const opening: Event = {
UID: `${startDateUtc.replace(/[:-]/g, "")}-opening-ceremony`,
DTSTAMP: startDateUtc.replace(/[:-]/g, ""),
DTSTART: startDateUtc.replace(/[:-]/g, ""),
DTEND: endDateUtc.replace(/[:-]/g, ""),
DESCRIPTION: `Paris 2024 - ${translate.openingCeremony.get(this.language)}`,
SUMMARY: `Paris 2024 - ${translate.openingCeremony.get(this.language)}`,
LOCATION: "Paris",
_COMPETITORS: [],
_GENDER: "",
_MEDAL: false,
_NOCS: this.nocs,
_SPORT: "",
_UNITNAME: "",
};
this.events.push(opening);
startDateUtc = new Date("2024-08-11T19:00:00Z").toISOString().replace(".000", "");
endDateUtc = new Date("2024-08-11T21:15:00Z").toISOString().replace(".000", "");
const closing: Event = {
UID: `${startDateUtc.replace(/[:-]/g, "")}-closing-ceremony`,
DTSTAMP: startDateUtc.replace(/[:-]/g, ""),
DTSTART: startDateUtc.replace(/[:-]/g, ""),
DTEND: endDateUtc.replace(/[:-]/g, ""),
DESCRIPTION: "Paris 2024 - Closing ceremony",
SUMMARY: "Paris 2024 - Closing ceremony",
LOCATION: "Stade de France, Saint-Denis",
_COMPETITORS: [],
_GENDER: "",
_MEDAL: false,
_NOCS: this.nocs,
_SPORT: "",
_UNITNAME: "",
};
this.events.push(closing);
}
private getKey(sportKey: string, noc: string) {
if (this.language === "en") {
return `${sportKey}/${noc}`;
}
return `${sportKey}/${this.language}/${noc}`;
}
private generateCalendars() {
// sports
for (const sport of this.sports) {
// sport/general
let events = this.events
.filter((event) => event._SPORT === sport.key)
.sort((a, b) => a.UID > b.UID ? 1 : -1);
let key = this.getKey(sport.key, "general");
let title = `${getSportIcon(sport.key)} ${sport.name} | Paris 2024`;
if (events.length > 0) {
generateICS(title, key, events);
}
// sport/medals
events = this.events
.filter((event) => event._SPORT === sport.key && event._MEDAL)
.sort((a, b) => a.UID > b.UID ? 1 : -1);
key = this.getKey(sport.key, "medals");
title = `${getSportIcon(sport.key)} ${sport.name} 🏅 | Paris 2024`;
if (events.length > 0) {
generateICS(title, key, events);
}
// sport/noc
for (const noc of sport.NOCS) {
events = this.events
.filter((event) => event._SPORT === sport.key && event._NOCS.includes(noc))
.sort((a, b) => a.UID > b.UID ? 1 : -1);
key = this.getKey(sport.key, noc);
title = `${getNOCFlag(noc)} ${getNOCName(noc)} ${sport.name} | Paris 2024`;
if (events.length > 0) {
generateICS(title, key, events);
}
}
}
// nocs
for (const noc of this.nocs) {
// general/noc
let events = this.events
.filter((event) => event._NOCS.includes(noc))
.sort((a, b) => a.UID > b.UID ? 1 : -1);
let key = this.getKey("general", noc);
let title = `${getNOCFlag(noc)} ${getNOCName(noc)} | Paris 2024`;
if (events.length > 0) {
generateICS(title, key, events);
}
// medals/noc
events = this.events
.filter((event) => event._NOCS.includes(noc) && event._MEDAL)
.sort((a, b) => a.UID > b.UID ? 1 : -1);
key = this.getKey("medals", noc);
title = `${getNOCFlag(noc)} ${getNOCName(noc)} 🏅 | Paris 2024`
if (events.length > 0) {
generateICS(title, key, events);
}
}
// general/general
const events = this.events
.sort((a, b) => a.UID > b.UID ? 1 : -1);
const key = this.getKey("general", "general");
const title = `Paris 2024`;
if (events.length > 0) {
generateICS(title, key, events);
}
// medals/general
const medals = this.events
.filter((event) => event._MEDAL)
.sort((a, b) => a.UID > b.UID ? 1 : -1);
const medalsKey = this.getKey("medals", "general");
const medalsTitle = `🏅 Paris 2024`;
if (medals.length > 0) {
generateICS(medalsTitle, medalsKey, medals);
}
}
private generateMainPage() {
const accordionClass = "collapse collapse-arrow bg-gray-100 mb-1"
const buttonClass = "btn btn-sm bg-gray-300 min-w-24 mb-1";
const calendars: string[] = [];
const todays: string[] = [];
calendars.push(`<div class="${accordionClass}">`);
calendars.push(` <input type="radio" name="accordion" checked="checked">`);
calendars.push(` <div class="collapse-title text-xl font-medium">${translate.allSports.get(this.language)}</div>`);
calendars.push(` <div class="collapse-content text-center">`)
calendars.push(` <div>`);
calendars.push(` <button class="${buttonClass}" onclick="showModal('general/general', '${this.language}');">${translate.fullSchedule.get(this.language)}</button>`);
calendars.push(` </div>`);
for (const noc of this.nocs.sort()) {
calendars.push(` <button class="${buttonClass}" onclick="showModal('general/${noc}', '${this.language}');">${getNOCFlag(noc)} ${noc}</button>`);
}
calendars.push(` </div>`);
calendars.push(`</div>`);
calendars.push(`<div class="${accordionClass}">`);
calendars.push(` <input type="radio" name="accordion">`);
calendars.push(` <div class="collapse-title text-xl font-medium">🏅 ${translate.medalEvents.get(this.language)}</div>`);
calendars.push(` <div class="collapse-content text-center">`)
calendars.push(` <div>`);
calendars.push(` <button class="${buttonClass}" onclick="showModal('medals/general', '${this.language}');">${translate.fullSchedule.get(this.language)}</button>`);
calendars.push(` </div>`);
for (const noc of this.nocs.sort()) {
calendars.push(` <button class="${buttonClass}" onclick="showModal('medals/${noc}', '${this.language}');">${getNOCFlag(noc)} ${noc}</button>`);
}
calendars.push(` </div>`);
calendars.push(`</div>`);
for (const sport of this.sports.sort()) {
calendars.push(`<div class="${accordionClass}">`);
calendars.push(` <input type="radio" name="accordion">`);
calendars.push(` <div class="collapse-title text-xl font-medium">${getSportIcon(sport.key)} ${sport.name}</div>`);
calendars.push(` <div class="collapse-content text-center">`)
calendars.push(` <div>`);
calendars.push(` <button class="${buttonClass}" onclick="showModal('${sport.key}/general', '${this.language}');">${translate.fullSchedule.get(this.language)}</button>`);
calendars.push(` <button class="${buttonClass}" onclick="showModal('${sport.key}/medals', '${this.language}');">🏅 ${translate.medalEvents.get(this.language)}</button>`);
calendars.push(` </div>`);
for (const noc of sport.NOCS.sort()) {
calendars.push(` <button class="${buttonClass}" onclick="showModal('${sport.key}/${noc}', '${this.language}');">${getNOCFlag(noc)} ${noc}</button>`);
}
calendars.push(` </div>`);
calendars.push(`</div>`);
}
const template = readFile(`${__dirname}/index/template.html`);
const output = template
.replace("{{calendars}}", calendars.join("\r\n"))
.replace("{{todays}}", [todays].join("\r\n"));
saveFile(
this.language === "en" ?
"docs/index.html" :
`docs/${this.language}/index.html`,
output);
}
private generateTodaysPage() {
const content: string[] = [];
for (const event of this.events) {
let sport = this.sports.find((sport) => sport.key === event._SPORT);
if (!sport) {
sport = {
name: "Ceremony",
key: "",
NOCS: [],
};
}
const summary = event.SUMMARY.match(/ceremony/gi) ? event.SUMMARY : event.SUMMARY.split(" ").slice(1).join(" ");
content.push(`<div class="event py-4" data-start="${event.DTSTART}" data-end="${event.DTEND}" data-noc="${event._NOCS.join(",")}">`);
content.push(" <div class=\"time w-1/4 align-top text-right inline-block text-5xl text-center tabular-nums pr-2 border-r border-slate-900/10\">__:__</div>");
content.push(" <div class=\"w-3/5 align-top inline-block text-black pl-2\">");
content.push(" <div class=\"text-2xl\">");
content.push(` ${event._MEDAL ? "🏅" : ""}`);
content.push(` ${sport.name.toUpperCase()}`);
if (event._GENDER === "M") {
content.push(" <span class=\"text-xs align-middle bg-blue-400 text-white py-1 px-2 rounded-xl\">M</span>");
} else if (event._GENDER === "W") {
content.push(" <span class=\"text-xs align-middle bg-pink-400 text-white py-1 px-2 rounded-xl\">W</span>");
}
content.push(" </div>");
if (event._UNITNAME.match(summary)) {
content.push(` <div class="">${summary}`);
} else {
content.push(` <div class="">${event._UNITNAME}`);
content.push(` <div class="">${summary}</div>`);
}
if (event._COMPETITORS) {
event._COMPETITORS.forEach((competitor) => {
content.push(`<div class= "competitor ${competitor.noc}"> ${competitor.name} </div>`);
});
}
content.push(" </div>");
content.push(" </div>");
content.push("</div>");
}
const template = readFile(`${__dirname}/today/template.html`);
const output = template
.replace("{{events}}", content.join("\r\n"));
saveFile(
this.language === "en" ?
"docs/today.html" :
`docs/${this.language}/today.html`,
output
);
}
private generateCSS() {
postcss([autoprefixer, tailwindcss])
.process(readFile(`${__dirname}/index/template.css`), { from: "index/template.css", to: "docs/style.css" })
.then((result) => {
saveFile("docs/style.css", result.css);
});
;
}
}

View File

@ -11,6 +11,7 @@ import { Event, Sport } from "./types";
import { getSportIcon } from "./sports";
import { isValidNOC, getNOCName, getNOCFlag } from "./nocs";
import { generateICS } from "./ics";
import { Calendar } from "./calendar";
const debug = Debug("paris2024:index");
@ -358,61 +359,66 @@ const generateCSS = () => {
};
const main = async () => {
await Promise.all(
[
"3x3-basketball",
"archery",
"artistic-gymnastics",
"artistic-swimming",
"athletics",
"badminton",
"basketball",
"beach-volleyball",
"boxing",
"breaking",
"canoe-slalom",
"canoe-sprint",
"cycling-bmx-freestyle",
"cycling-bmx-racing",
"cycling-mountain-bike",
"cycling-road",
"cycling-track",
"diving",
"equestrian",
"fencing",
"football",
"golf",
"handball",
"hockey",
"judo",
"marathon-swimming",
"modern-pentathlon",
"rhythmic-gymnastics",
"rowing",
"rugby-sevens",
"sailing",
"shooting",
"skateboarding",
"sport-climbing",
"surfing",
"swimming",
"table-tennis",
"taekwondo",
"tennis",
"trampoline-gymnastics",
"triathlon",
"volleyball",
"water-polo",
"weightlifting",
"wrestling",
]
.map((key) => extractSportCalendar(key)),
);
generateCeremoniesEvents();
generateCalendars();
generateOutputPage();
generateTodayPage();
generateCSS();
// await Promise.all(
// [
// "3x3-basketball",
// "archery",
// "artistic-gymnastics",
// "artistic-swimming",
// "athletics",
// "badminton",
// "basketball",
// "beach-volleyball",
// "boxing",
// "breaking",
// "canoe-slalom",
// "canoe-sprint",
// "cycling-bmx-freestyle",
// "cycling-bmx-racing",
// "cycling-mountain-bike",
// "cycling-road",
// "cycling-track",
// "diving",
// "equestrian",
// "fencing",
// "football",
// "golf",
// "handball",
// "hockey",
// "judo",
// "marathon-swimming",
// "modern-pentathlon",
// "rhythmic-gymnastics",
// "rowing",
// "rugby-sevens",
// "sailing",
// "shooting",
// "skateboarding",
// "sport-climbing",
// "surfing",
// "swimming",
// "table-tennis",
// "taekwondo",
// "tennis",
// "trampoline-gymnastics",
// "triathlon",
// "volleyball",
// "water-polo",
// "weightlifting",
// "wrestling",
// ]
// .map((key) => extractSportCalendar(key)),
// );
// generateCeremoniesEvents();
// generateCalendars();
// generateOutputPage();
// generateTodayPage();
// generateCSS();
for (const language of ["en", "ja", "ko", "ru", "zh"]) {
const cal = new Calendar(language)
await cal.generate();
}
};
main();

View File

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html>
<html data-theme="cmyk">
<head>
<title>Paris 2024 Summer Olympic Games calendars</title>
<link href="./style.css?refresh=20240827" rel="stylesheet">
<link href="https://github.com/fabrice404/olympics-calendar/style.css?refresh=20240730" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Paris 2024 Summer Olympic Games calendars">
<meta name="keywords" content="Paris 2024, Summer Olympic Games, calendars">
@ -12,36 +12,47 @@
<body>
<div class="p-4">
<div class="flex flex-wrap items-center justify-between border-b pb-4 border-slate-900/10">
<h1 class="text-4xl">Paris 2024 Summer Olympic Games calendars</h1>
<div>
<a href="https://github.com/fabrice404/olympics-calendar" target="_blank"
class="ml-6 block text-slate-200 hover:text-sky-500 w-10 h-10">
<svg viewBox="0 0 16 16" class="w-10 h-10" fill="currentColor" aria-hidden="true">
<path
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z">
</path>
</svg></a>
<div class="navbar bg-base-100">
<div class="navbar-start">
<div class="dropdown">
<div tabindex="0" role="button" class="btn btn-ghost btn-circle">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h7" />
</svg>
</div>
<ul tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
<li><a href="https://fabrice404.github.io/olympics-calendar/index.html">English</a></li>
<li><a href="https://fabrice404.github.io/olympics-calendar/ja/index.html">日本語</a></li>
<li><a href="https://fabrice404.github.io/olympics-calendar/ko/index.html">한국어</a></li>
<li><a href="https://fabrice404.github.io/olympics-calendar/ru/index.html">Русский</a></li>
<li><a href="https://fabrice404.github.io/olympics-calendar/zh/index.html">中文</a></li>
</ul>
</div>
</div>
<div class="navbar-center">
<a class="btn btn-ghost text-xl" href="./">Paris 2024 calendars</a>
</div>
<div class="navbar-end">
</div>
</div>
<div>
<h2 class="text-3xl pb-4 pt-8">How to add a calendar to your Google calendar</h2>
<ol class="list-decimal ml-10">
<li>Right click on a calendar and select "Copy link address"</li>
<li>Open your Google calendar</li>
<li>Click the plus sign next to “Other Calendars” on the left menu</li>
<li>Click "From URL"</li>
<li>Paste your calendar URL</li>
<li>Click "Add calendar"</li>
</ol>
</div>
<div>
{{calendars}}
</div>
<dialog id="modal" class="modal">
<div class="modal-box">
</h3>
<input type="text" class="input input-bordered w-full text-sm" id="link"></input>
<div class="modal-action">
<form method="dialog">
<button class="btn">Close</button>
</form>
</div>
</div>
</dialog>
<div class="text-sm my-10">
<h2 class="text-3xl pb-4 pt-8">View today's events by NOC</h2>
{{todays}}
@ -52,6 +63,13 @@
All trademarks, logos and brand names are the property of their respective owners.
</div>
</div>
<script>
const showModal = (key, language) => {
document.querySelector("#modal #link")
.setAttribute('value', `https://fabrice404.github.io/olympics-calendar/${key}.ics`);
modal.showModal();
};
</script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0KQC1F1K4H"></script>
<script>

11
src/io.ts Normal file
View File

@ -0,0 +1,11 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
export const saveFile = (path: string, content: string): void => {
const folder = path.split("/").slice(0, -1).join("/");
mkdirSync(folder, { recursive: true });
writeFileSync(path, content);
};
export const hasFile = (path: string) => existsSync(path);
export const readFile = (path: string) => readFileSync(path, "utf-8");

View File

@ -53,3 +53,5 @@ export const getSportIcon = (sport: string): string => {
console.error(`No icon set for ${sport}`);
return "";
};
export const getAllSportsKeys = (): string[] => [...SPORTS.keys()];

View File

@ -3,7 +3,7 @@
<head>
<title>Paris 2024 Summer Olympic Games - Today's events</title>
<link href="./style.css?refresh=20240827" rel="stylesheet">
<link href="https://github.com/fabrice404/olympics-calendar/style.css?refresh=20240730" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Paris 2024 Summer Olympic Games - Today's events">
<meta name="keywords" content="Paris 2024, Summer Olympic Games - Today's events">

56
src/translate.ts Normal file
View File

@ -0,0 +1,56 @@
export const allSports = new Map<string, string>([
["en", "All sports"],
["ja", "すべてのスポーツ"],
["ko", "모든 스포츠"],
["ru", "Все виды спорта"],
["zh", "所有运动"],
]);
export const fullSchedule = new Map<string, string>([
["en", "Full schedule"],
["ja", "フルスケジュール"],
["ko", "전체 일정"],
["ru", "Полное расписание"],
["zh", "完整时间表"],
]);
export const medalEvents = new Map<string, string>([
["en", "Medal events"],
["ja", "メダルイベント"],
["ko", "메달 이벤트"],
["ru", "Медальные события"],
["zh", "奖牌赛事"],
]);
export const genderMen = new Map<string, string>([
["en", "M"],
["ja", "男性"],
["ko", "남성"],
["ru", "М"],
["zh", "男"],
]);
export const genderWomen = new Map<string, string>([
["en", "W"],
["ja", "女性"],
["ko", "여성"],
["ru", "Ж"],
["zh", "女"],
]);
export const openingCeremony = new Map<string, string>([
["en", "Opening Ceremony"],
["ja", "開会式"],
["ko", "개막식"],
["ru", "Церемония открытия"],
["zh", "开幕式"],
]);
export const closingCeremony = new Map<string, string>([
["en", "Closing Ceremony"],
["ja", "閉会式"],
["ko", "폐막식"],
["ru", "Церемония закрытия"],
["zh", "闭幕式"],
]);