refactor translations

This commit is contained in:
Fabrice Lamant
2024-08-04 09:00:47 +02:00
parent 9e525f3cd9
commit 4fe7181e0d
7 changed files with 138 additions and 102 deletions

View File

@ -18,7 +18,7 @@ export default [
{
rules: {
"comma-dangle": ["error", "always-multiline"],
complexity: ["error", 8],
complexity: ["error", 15],
quotes: ["error", "double"],
semi: ["error", "always"],
},

View File

@ -5,6 +5,7 @@
"scripts": {
"start": "find ./cache/**/*.html -mmin +10 -exec rm -f {} \\; | DEBUG=paris2024:* ts-node src/index.ts",
"dev": "DEBUG=paris2024:* nodemon src/index.ts",
"build": "tsc",
"lint": "eslint . --fix",
"test": "vitest run --coverage"
},

View File

@ -3,7 +3,7 @@ import autoprefixer from "autoprefixer";
import postcss from "postcss";
import tailwindcss from "tailwindcss";
import { Event, NOC, Sport } from "./types";
import { Event, Medal, NOC, Sport } from "./types";
import { getAllSportsKeys, getSportIcon } from "./sports";
import { existsSync, writeFileSync } from "fs";
import { hasFile, readFile, saveFile } from "./io";
@ -20,6 +20,7 @@ export class Calendar {
private events: Event[] = [];
private nocs: string[] = [];
private sports: Sport[] = [];
private medals: Medal[] = [];
constructor(language: string) {
this.language = language;
@ -164,8 +165,8 @@ export class Calendar {
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)}`,
DESCRIPTION: `Paris 2024 - {{translate_openingCeremony}}`,
SUMMARY: `Paris 2024 - {{translate_openingCeremon}}`,
LOCATION: "Paris",
_COMPETITORS: [],
_GENDER: "",
@ -307,10 +308,10 @@ export class Calendar {
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-title text-xl font-medium">{{translate_allSports}}</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(` <button class="${buttonClass}" onclick="showModal('general/general', '${this.language}');">{{translate_fullSchedule}}</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>`);
@ -320,10 +321,10 @@ export class Calendar {
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-title text-xl font-medium">🏅 {{translate_medalEvents}</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(` <button class="${buttonClass}" onclick="showModal('medals/general', '${this.language}');">{{translate_fullSchedule}}</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>`);
@ -333,7 +334,7 @@ export class Calendar {
calendars.push(`<div class="${accordionClass}">`);
calendars.push(` <input type="radio" name="accordion">`);
calendars.push(` <div class="collapse-title text-xl font-medium">📅 ${translate.todaysEvents.get(this.language)}</div>`);
calendars.push(` <div class="collapse-title text-xl font-medium">📅 {{translate_todaysEvents}}</div>`);
calendars.push(` <div class="collapse-content text-center">`)
for (const noc of this.nocs.sort()) {
calendars.push(` <a class="${buttonClass}" href="./today.html?noc=${noc}">${getNOCFlag(noc)} ${noc}</a>`);
@ -347,8 +348,8 @@ export class Calendar {
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(` <button class="${buttonClass}" onclick="showModal('${sport.key}/general', '${this.language}');">{{translate_fullSchedule}}</button>`);
calendars.push(` <button class="${buttonClass}" onclick="showModal('${sport.key}/medals', '${this.language}');">🏅 {{translate_medalEvents}}</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>`);
@ -358,11 +359,10 @@ export class Calendar {
}
const template = readFile(`${__dirname}/index/template.html`);
const output = template
.replace("{{calendars}}", calendars.join("\r\n"))
.replace(/\{\{title}}/gi, translate.calendars.get(this.language)!)
.replace("{{disclaimer}}", translate.disclaimer.get(this.language)!)
;
const output = translate.translate(
template.replace("{{calendars}}", calendars.join("\r\n")),
this.language,
);
saveFile(
this.language === "en" ?
"docs/index.html" :
@ -390,9 +390,9 @@ export class Calendar {
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\">${translate.genderMen.get(this.language)}</span>`);
content.push(` <span class=\"text-xs align-middle bg-blue-400 text-white py-1 px-2 rounded-xl\">{{translate_genderMen}}</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\">${translate.genderWomen.get(this.language)}</span>`);
content.push(` <span class=\"text-xs align-middle bg-pink-400 text-white py-1 px-2 rounded-xl\">{{translate_genderWomen}}</span>`);
}
content.push(" </div>");
content.push(` <div>${event._UNITNAME}</div>`);
@ -417,12 +417,10 @@ export class Calendar {
}
const template = readFile(`${__dirname}/today/template.html`);
const output = template
.replace("{{events}}", content.join("\r\n"))
.replace(/\{\{title}}/gi, translate.todaysEvents.get(this.language)!)
.replace("{{disclaimer}}", translate.disclaimer.get(this.language)!)
.replace("{{noEventToday}}", translate.noEventToday.get(this.language)!)
;
const output = translate.translate(
template.replace("{{events}}", content.join("\r\n")),
this.language,
);
saveFile(
this.language === "en" ?
"docs/today.html" :

View File

@ -2,11 +2,11 @@
<html data-theme="cmyk">
<head>
<title>Paris 2024 - {{title}}</title>
<title>Paris 2024 - {{translate_calendars}}</title>
<link href="https://fabrice404.github.io/olympics-calendar/style.css?refresh=20240730" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Paris 2024 - {{title}}">
<meta name="keywords" content="Paris 2024 - {{title}}">
<meta name="description" content="Paris 2024 - {{translate_calendars}}">
<meta name="keywords" content="Paris 2024 - {{translate_calendars}}">
<meta name="author" content="Fabrice LAMANT">
</head>
@ -22,6 +22,10 @@
</svg>
</div>
<ul tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-50 mt-3 w-52 p-2 shadow">
<li><a href="./index.html">{{translate_calendars}}</a></li>
<li><a href="./today.html">{{translate_todaysEvents}}</a></li>
<li><a href="./medals.html">{{translate_medalsTable}}</a></li>
<hr />
<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>
@ -33,7 +37,7 @@
</div>
</div>
<div class="navbar-center">
<a class="btn btn-ghost text-xl" href="./">Paris 2024 - {{title}}</a>
<a class="btn btn-ghost text-xl" href="./">Paris 2024 - {{translate_calendars}}</a>
</div>
<div class="navbar-end">
</div>
@ -56,7 +60,7 @@
</dialog>
<div class="text-sm my-10 text-center">
{{disclaimer}}
{{translate_disclaimer}}
</div>
</div>
<script>

View File

@ -2,11 +2,11 @@
<html data-theme="cmyk">
<head>
<title>Paris 2024 - {{title}}</title>
<title>Paris 2024 - {{translate_todaysEvents}}</title>
<link href="https://fabrice404.github.io/olympics-calendar/style.css?refresh=20240730" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Paris 2024 - {{title}}">
<meta name="keywords" content="Paris 2024 - {{title}}">
<meta name="description" content="Paris 2024 - {{translate_todaysEvents}}">
<meta name="keywords" content="Paris 2024 - {{translate_todaysEvents}}">
<meta name="author" content="Fabrice LAMANT">
<meta http-equiv="refresh" content="300">
@ -26,6 +26,10 @@
</svg>
</div>
<ul tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-50 mt-3 w-52 p-2 shadow">
<li><a href="./index.html">{{translate_calendars}}</a></li>
<li><a href="./today.html">{{translate_todaysEvents}}</a></li>
<li><a href="./medals.html">{{translate_medalsTable}}</a></li>
<hr />
<li><a href="https://fabrice404.github.io/olympics-calendar/today.html">English</a></li>
<li><a href="https://fabrice404.github.io/olympics-calendar/ja/today.html">日本語</a></li>
<li><a href="https://fabrice404.github.io/olympics-calendar/ko/today.html">한국어</a></li>
@ -37,7 +41,7 @@
</div>
</div>
<div class="navbar-center">
<a class="btn btn-ghost text-xl" href="./">Paris 2024 - {{title}}</a>
<a class="btn btn-ghost text-xl" href="./">Paris 2024 - {{translate_todaysEvents}}</a>
</div>
<div class="navbar-end">
</div>
@ -48,11 +52,11 @@
</div>
<div class="no-event my-10 text-center text-2xl hidden">
{{noEventToday}}
{{translate_noEventToday}}
</div>
<div class="text-sm my-10 text-center">
{{disclaimer}}
{{translate_disclaimer}}
</div>
</div>
<script type="text/javascript">

View File

@ -1,69 +1,12 @@
export const allSports = new Map<string, string>([
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", "闭幕式"],
]);
export const disclaimer = new Map<string, string>([
["en", "This webiste is not affiliated with the International Olympic Committee. All trademarks, logos and brand names are the property of their respective owners."],
["ja", "このウェブサイトは国際オリンピック委員会とは関係ありません。すべての商標、ロゴ、およびブランド名はそれぞれの所有者の財産です。"],
["ko", "이 웹 사이트는 국제 올림픽 위원회와 관련이 없습니다. 모든 상표, 로고 및 상표는 각 소유자의 소유입니다."],
["ru", "Этот сайт не связан с Международным олимпийским комитетом. Все товарные знаки, логотипы и бренды являются собственностью их соответствующих владельцев."],
["zh", "本网站与国际奥林匹克委员会无关。所有商标、标志和品牌名称均为其各自所有者的财产。"],
]);
export const calendars = new Map<string, string>([
const calendars = new Map<string, string>([
["en", "Calendars"],
["ja", "カレンダー"],
["ko", "달력"],
@ -71,7 +14,79 @@ export const calendars = new Map<string, string>([
["zh", "日历"],
]);
export const todaysEvents = new Map<string, string>([
const closingCeremony = new Map<string, string>([
["en", "Closing Ceremony"],
["ja", "閉会式"],
["ko", "폐막식"],
["ru", "Церемония закрытия"],
["zh", "闭幕式"],
]);
const disclaimer = new Map<string, string>([
["en", "This webiste is not affiliated with the International Olympic Committee. All trademarks, logos and brand names are the property of their respective owners."],
["ja", "このウェブサイトは国際オリンピック委員会とは関係ありません。すべての商標、ロゴ、およびブランド名はそれぞれの所有者の財産です。"],
["ko", "이 웹 사이트는 국제 올림픽 위원회와 관련이 없습니다. 모든 상표, 로고 및 상표는 각 소유자의 소유입니다."],
["ru", "Этот сайт не связан с Международным олимпийским комитетом. Все товарные знаки, логотипы и бренды являются собственностью их соответствующих владельцев."],
["zh", "本网站与国际奥林匹克委员会无关。所有商标、标志和品牌名称均为其各自所有者的财产。"],
]);
const fullSchedule = new Map<string, string>([
["en", "Full schedule"],
["ja", "フルスケジュール"],
["ko", "전체 일정"],
["ru", "Полное расписание"],
["zh", "完整时间表"],
]);
const genderMen = new Map<string, string>([
["en", "M"],
["ja", "男性"],
["ko", "남성"],
["ru", "М"],
["zh", "男"],
]);
const genderWomen = new Map<string, string>([
["en", "W"],
["ja", "女性"],
["ko", "여성"],
["ru", "Ж"],
["zh", "女"],
]);
const medalEvents = new Map<string, string>([
["en", "Medal events"],
["ja", "メダルイベント"],
["ko", "메달 이벤트"],
["ru", "Медальные события"],
["zh", "奖牌赛事"],
]);
const medalsTable = new Map<string, string>([
["en", "Medals table"],
["ja", "メダル表"],
["ko", "메달 테이블"],
["ru", "Таблица медалей"],
["zh", "奖牌榜"],
]);
const noEventToday = new Map<string, string>([
["en", "No event today, come back tomorrow! :)"],
["ja", "今日のイベントはありません。明日また来てください! :)"],
["ko", "오늘 이벤트가 없습니다. 내일 다시 오세요! :)"],
["ru", "Сегодня нет событий, вернитесь завтра! :)"],
["zh", "今天没有活动,请明天再来! :)"],
]);
const openingCeremony = new Map<string, string>([
["en", "Opening Ceremony"],
["ja", "開会式"],
["ko", "개막식"],
["ru", "Церемония открытия"],
["zh", "开幕式"],
]);
const todaysEvents = new Map<string, string>([
["en", "Today's events"],
["ja", "今日のイベント"],
["ko", "오늘의 이벤트"],
@ -79,10 +94,18 @@ export const todaysEvents = new Map<string, string>([
["zh", "今天的活动"],
]);
export const noEventToday = new Map<string, string>([
["en", "No event today, come back tomorrow! :)"],
["ja", "今日のイベントはありません。明日また来てください! :)"],
["ko", "오늘 이벤트가 없습니다. 내일 다시 오세요! :)"],
["ru", "Сегодня нет событий, вернитесь завтра! :)"],
["zh", "今天没有活动,请明天再来! :)"],
]);
export const translate = (text: string, language: string) => text
.replace(/\{\{translate_allSports}}/gi, allSports.get(language)!)
.replace(/\{\{translate_calendars}}/gi, calendars.get(language)!)
.replace(/\{\{translate_closingCeremony}}/gi, closingCeremony.get(language)!)
.replace(/\{\{translate_disclaimer}}/gi, disclaimer.get(language)!)
.replace(/\{\{translate_fullSchedule}}/gi, fullSchedule.get(language)!)
.replace(/\{\{translate_genderMen}}/gi, genderMen.get(language)!)
.replace(/\{\{translate_genderWomen}}/gi, genderWomen.get(language)!)
.replace(/\{\{translate_medalEvents}}/gi, medalEvents.get(language)!)
.replace(/\{\{translate_medalsTable}}/gi, medalsTable.get(language)!)
.replace(/\{\{translate_noEventToday}}/gi, noEventToday.get(language)!)
.replace(/\{\{translate_openingCeremony}}/gi, openingCeremony.get(language)!)
.replace(/\{\{translate_todaysEvents}}/gi, todaysEvents.get(language)!)
;

6
src/types.d.ts vendored
View File

@ -30,3 +30,9 @@ export interface NOC {
icon: string;
name: string;
}
export interface Medal {
color: "gold" | "silver" | "bronze";
name: string;
noc: string;
}