From c5136a5e350760f013bd15e73acd9f0c1d5820b0 Mon Sep 17 00:00:00 2001 From: Daniel Sepp <43651556+bloomlive@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:51:05 +0300 Subject: [PATCH 1/3] Add translations --- package.json | 3 +- pnpm-lock.yaml | 22 ++++ src/background.ts | 233 ++++++++++++++++++++++--------------------- src/usei18n.ts | 48 +++++++++ types/ILanguage.d.ts | 1 + 5 files changed, 190 insertions(+), 117 deletions(-) create mode 100644 src/usei18n.ts create mode 100644 types/ILanguage.d.ts diff --git a/package.json b/package.json index f6b04c5..7c5b68d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "webpack-cli": "^5.1.4" }, "dependencies": { - "calendar-link": "^2.4.0" + "calendar-link": "^2.4.0", + "i18n-js": "^4.2.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c14e350..2b8642d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,7 @@ specifiers: calendar-link: ^2.4.0 clean-webpack-plugin: ^4.0.0 copy-webpack-plugin: ^11.0.0 + i18n-js: ^4.2.3 prettier: 2.8.8 ts-loader: ^9.4.4 typescript: ^5.1.6 @@ -13,6 +14,7 @@ specifiers: dependencies: calendar-link: 2.4.0 + i18n-js: 4.2.3 devDependencies: '@types/chrome': 0.0.239 @@ -362,6 +364,10 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /bignumber.js/9.1.1: + resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} + dev: false + /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -697,6 +703,14 @@ packages: function-bind: 1.1.1 dev: true + /i18n-js/4.2.3: + resolution: {integrity: sha512-UPt7tQTLsMNX2ylYmcfkromOlU4Azo0ueMJ6DG1n1Dv2bfp/szDeB7i/0Ie/Luzx24P00Fz4vsKuMluoLGdYjw==} + dependencies: + bignumber.js: 9.1.1 + lodash: 4.17.21 + make-plural: 7.3.0 + dev: false + /ignore/5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} @@ -823,6 +837,10 @@ packages: p-locate: 4.1.0 dev: true + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -830,6 +848,10 @@ packages: yallist: 4.0.0 dev: true + /make-plural/7.3.0: + resolution: {integrity: sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw==} + dev: false + /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true diff --git a/src/background.ts b/src/background.ts index 2163f3f..35f7e17 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,155 +1,156 @@ -import { CalendarEvent, google, ics, office365, outlook, yahoo } from "calendar-link"; -import { IEventFileType } from "../types/IEventFileType"; +import { CalendarEvent, google, ics, office365, outlook, yahoo } from 'calendar-link' +import useI18n from "./usei18n"; -const rows = document.querySelectorAll(".event-row"); +import type { IEventFileType } from '../types/IEventFileType' +import type { ILanguage } from '../types/ILanguage' +import usei18n from "./usei18n"; -let currentType: IEventFileType = "ics"; +const rows = document.querySelectorAll('.event-row') -const locations = new Map() - .set(4936143, "Kaevumägi") - .set(4936141, "I Kirsimägi") - .set(4936142, "II Kirsimägi") - .set(4936144, "Laululava") - .set(4936145, "Aida suur saal") - .set(4936147, "Jaani kirik") - .set(4936149, "Babtistikirik") - .set(4938876, "Jaak Johansoni Kultra lava"); +let currentType: IEventFileType = 'ics' + +const currentLanguage: ILanguage = document.querySelector('html')!.getAttribute('lang')! as ILanguage + +useI18n.locale = currentLanguage; const linkType = new Map() - .set("ics", "iCal") - .set("google", "Google Calendar") - .set("yahoo", "Yahoo Calendar") - .set("outlook", "Outlook.com") - .set("office365", "Office 365"); + .set('ics', useI18n.t("formats.ics")) + .set('google', 'Google Calendar') + .set('yahoo', 'Yahoo Calendar') + .set('outlook', 'Outlook.com') + .set('office365', 'Office 365') function createButton(): HTMLElement { - const button = document.createElement("a"); + const button = document.createElement('a') - button.style.position = "absolute"; - button.style.fontWeight = "medium"; - button.style.background = "none"; - button.style.border = "none"; - button.style.padding = "4px"; - button.style.fontSize = "1rem"; - button.style.cursor = "pointer"; - button.innerText = "+"; + button.style.position = 'absolute' + button.style.fontWeight = 'medium' + button.style.background = 'none' + button.style.border = 'none' + button.style.padding = '4px' + button.style.fontSize = '1rem' + button.style.cursor = 'pointer' + button.innerText = '+' - styleButtonResponsive(isMobile(), button); + styleButtonResponsive(isMobile(), button) - return button; + return button } function isMobile(): boolean { - return window.innerWidth < 993; + return window.innerWidth < 993 } function getEventData(row: Element): CalendarEvent { - const start = row.getAttribute("data-box-start"); - const duration = Number((row as HTMLElement).style.height?.replace("px", "")) / 80; - - const startTime = new Date(Number(start) * 1000); - const endTime = new Date(Number(start) * 1000 + duration * 60 * 60 * 1000); - - if (startTime.getHours() < 6) { - startTime.setDate(startTime.getDate() + 1); - endTime.setDate(endTime.getDate() + 1); - } - - return { - title: row.querySelector(".title")?.querySelector("div")?.textContent?.trim() ?? "Something went wrong.", - description: "Vaata rohkem siit: " + row.querySelector("a")?.href ?? "https://www.viljandifolk.ee/", - start: startTime, - end: endTime, - location: location - ? locations.get(Number(row.parentElement?.parentElement?.parentElement?.getAttribute("data-id"))) - : "Viljandi Pärimusmuusika Festival" - }; + const start = row.getAttribute('data-box-start') + const duration = Number((row as HTMLElement).style.height?.replace('px', '')) / 80 + + const startTime = new Date(Number(start) * 1000) + const endTime = new Date(Number(start) * 1000 + duration * 60 * 60 * 1000) + + if (startTime.getHours() < 6) { + startTime.setDate(startTime.getDate() + 1) + endTime.setDate(endTime.getDate() + 1) + } + + return { + title: row.querySelector('.title')?.querySelector('div')?.textContent?.trim() ?? useI18n.t("error"), + description: useI18n.t("description.prefix") + ': ' + row.querySelector('a')?.href ?? 'https://www.viljandifolk.ee/', + start: startTime, + end: endTime, + location: location + ? usei18n.t("stages." + Number(row.parentElement?.parentElement?.parentElement?.getAttribute('data-id'))) + : useI18n.t("stages.default") + } } function createEventHref(row: Element): string { - const data = getEventData(row); - - switch (currentType) { - case "google": - return google(data); - case "yahoo": - return yahoo(data); - case "outlook": - return outlook(data); - case "office365": - return office365(data); - case "ics": - return ics(data); - default: - return ics(data); - } + const data = getEventData(row) + + switch (currentType) { + case 'google': + return google(data) + case 'yahoo': + return yahoo(data) + case 'outlook': + return outlook(data) + case 'office365': + return office365(data) + case 'ics': + return ics(data) + default: + return ics(data) + } } function styleButtonResponsive(isMobile: boolean, button: HTMLElement) { - button.style.left = isMobile ? "10px" : "0"; - button.style.top = isMobile ? "0" : "0"; + button.style.left = isMobile ? '10px' : '0' + button.style.top = isMobile ? '0' : '0' } function addSelector() { - const element = document.querySelector(".nav-links.d-flex"); - const selector = document.createElement("select"); - selector.setAttribute("id", "calendar-link-selector"); - selector.style.marginBottom = "12px"; - selector.style.fontSize = "1rem"; - - linkType.forEach((value, key) => { - const option = document.createElement("option"); - option.value = key; - option.text = value; - selector.appendChild(option); - }); - - selector.addEventListener("change", function() { - currentType = this.value as IEventFileType; - document.querySelectorAll('.calendar-link-add').forEach((link: Element) => { - link.parentElement?.removeChild(link) + const element = document.querySelector('.nav-links.d-flex') + const selector = document.createElement('select') + selector.setAttribute('id', 'calendar-link-selector') + selector.style.marginBottom = '12px' + selector.style.fontSize = '1rem' + + linkType.forEach((value, key) => { + const option = document.createElement('option') + option.value = key + option.text = value + selector.appendChild(option) + }) + + selector.addEventListener('change', function () { + currentType = this.value as IEventFileType + document.querySelectorAll('.calendar-link-add').forEach((link: Element) => { + link.parentElement?.removeChild(link) + }) + + createAddButtons() }) - createAddButtons(); - }); - - if (element) { - element.after(selector); - const label = document.createElement("label"); - label.setAttribute("for", "calendar-link-selector"); - label.innerText = "Vali kalendri tüüp: "; - label.style.marginRight = "4px"; - selector.before(label); - } + if (element) { + element.after(selector) + const label = document.createElement('label') + label.setAttribute('for', 'calendar-link-selector') + label.innerText = useI18n.t('calendarTypeLabel') + ' ' + label.style.marginRight = '4px' + selector.before(label) + } } function createAddButtons() { - rows.forEach((row: Element) => { - ;(row as HTMLElement).style.position = "relative"; + rows.forEach((row: Element) => { + ;(row as HTMLElement).style.position = 'relative' - const button = createButton(); + const button = createButton() - button.classList.add('calendar-link-add') + button.classList.add('calendar-link-add') - button.setAttribute('href', createEventHref(row)); - button.setAttribute('target', '_blank') - button.setAttribute("download", row.querySelector(".title")?.querySelector("div")?.textContent?.trim()! + ".ics"); + button.setAttribute('href', createEventHref(row)) + button.setAttribute('target', '_blank') + button.setAttribute( + 'download', + row.querySelector('.title')?.querySelector('div')?.textContent?.trim()! + '.ics' + ) - row.querySelector("a")?.after(button); + row.querySelector('a')?.after(button) - window.addEventListener("resize", () => { - styleButtonResponsive(isMobile(), button); - }); + window.addEventListener('resize', () => { + styleButtonResponsive(isMobile(), button) + }) - button.addEventListener("mouseenter", () => { - button.style.color = "#b31b34"; - }); + button.addEventListener('mouseenter', () => { + button.style.color = '#b31b34' + }) - button.addEventListener("mouseleave", () => { - button.style.color = "black"; - }); - }); + button.addEventListener('mouseleave', () => { + button.style.color = 'black' + }) + }) } -addSelector(); -createAddButtons(); +addSelector() +createAddButtons() diff --git a/src/usei18n.ts b/src/usei18n.ts new file mode 100644 index 0000000..30cc544 --- /dev/null +++ b/src/usei18n.ts @@ -0,0 +1,48 @@ +import { I18n } from "i18n-js"; + +const useI18n = new I18n({ + en: { + stages: { + 4936143: "Kaevumägi", + 4936237: "I Kirsimägi", + 4936238: "II Kirsimägi", + 4936243: "Viljand Song Festival Grounds", + 4936240: "Traditional Music Center", + 4936244: "St. John's Church", + 4936245: "Babtist Church", + 4938875: "Jaak Johanson Stage", + default: "Viljandi Folk Music Festival" + }, + calendarTypeLabel: "Pick your calendar type", + description: { + prefix: "See more here" + }, + error: "Something went wrong.", + formats: { + ics: ".ics file", + } + }, + et: { + stages: { + 4936143: "Kaevumägi", + 4936141: "I Kirsimägi", + 4936142: "II Kirsimägi", + 4936144: "Laululava", + 4936145: "Aida suur saal", + 4936147: "Jaani kirik", + 4936149: "Babtistikirik", + 4938876: "Jaak Johansoni Kultra lava", + default: "Viljandi Pärimusmuusika festival" + }, + calendarTypeLabel: "Vali oma kalendriliik", + description: { + prefix: "Vaata rohkem siit" + }, + error: "Midagi läks valesti.", + formats: { + ics: ".ics fail", + } + } +}); + +export default useI18n; diff --git a/types/ILanguage.d.ts b/types/ILanguage.d.ts new file mode 100644 index 0000000..e2e2f91 --- /dev/null +++ b/types/ILanguage.d.ts @@ -0,0 +1 @@ +export type ILanguage = 'en' | 'et'; From 1e249e806503354ce6fbc48aa09d881daf8a5838 Mon Sep 17 00:00:00 2001 From: Daniel Sepp <43651556+bloomlive@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:51:49 +0300 Subject: [PATCH 2/3] Formatting --- src/background.ts | 17 +++++----- src/usei18n.ts | 86 +++++++++++++++++++++++------------------------ 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/background.ts b/src/background.ts index 35f7e17..fadf3e6 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,9 +1,9 @@ import { CalendarEvent, google, ics, office365, outlook, yahoo } from 'calendar-link' -import useI18n from "./usei18n"; +import useI18n from './usei18n' import type { IEventFileType } from '../types/IEventFileType' import type { ILanguage } from '../types/ILanguage' -import usei18n from "./usei18n"; +import usei18n from './usei18n' const rows = document.querySelectorAll('.event-row') @@ -11,10 +11,10 @@ let currentType: IEventFileType = 'ics' const currentLanguage: ILanguage = document.querySelector('html')!.getAttribute('lang')! as ILanguage -useI18n.locale = currentLanguage; +useI18n.locale = currentLanguage const linkType = new Map() - .set('ics', useI18n.t("formats.ics")) + .set('ics', useI18n.t('formats.ics')) .set('google', 'Google Calendar') .set('yahoo', 'Yahoo Calendar') .set('outlook', 'Outlook.com') @@ -54,13 +54,14 @@ function getEventData(row: Element): CalendarEvent { } return { - title: row.querySelector('.title')?.querySelector('div')?.textContent?.trim() ?? useI18n.t("error"), - description: useI18n.t("description.prefix") + ': ' + row.querySelector('a')?.href ?? 'https://www.viljandifolk.ee/', + title: row.querySelector('.title')?.querySelector('div')?.textContent?.trim() ?? useI18n.t('error'), + description: + useI18n.t('description.prefix') + ': ' + row.querySelector('a')?.href ?? 'https://www.viljandifolk.ee/', start: startTime, end: endTime, location: location - ? usei18n.t("stages." + Number(row.parentElement?.parentElement?.parentElement?.getAttribute('data-id'))) - : useI18n.t("stages.default") + ? usei18n.t('stages.' + Number(row.parentElement?.parentElement?.parentElement?.getAttribute('data-id'))) + : useI18n.t('stages.default') } } diff --git a/src/usei18n.ts b/src/usei18n.ts index 30cc544..c881aeb 100644 --- a/src/usei18n.ts +++ b/src/usei18n.ts @@ -1,48 +1,48 @@ -import { I18n } from "i18n-js"; +import { I18n } from 'i18n-js' const useI18n = new I18n({ - en: { - stages: { - 4936143: "Kaevumägi", - 4936237: "I Kirsimägi", - 4936238: "II Kirsimägi", - 4936243: "Viljand Song Festival Grounds", - 4936240: "Traditional Music Center", - 4936244: "St. John's Church", - 4936245: "Babtist Church", - 4938875: "Jaak Johanson Stage", - default: "Viljandi Folk Music Festival" + en: { + stages: { + 4936143: 'Kaevumägi', + 4936237: 'I Kirsimägi', + 4936238: 'II Kirsimägi', + 4936243: 'Viljand Song Festival Grounds', + 4936240: 'Traditional Music Center', + 4936244: "St. John's Church", + 4936245: 'Babtist Church', + 4938875: 'Jaak Johanson Stage', + default: 'Viljandi Folk Music Festival' + }, + calendarTypeLabel: 'Pick your calendar type', + description: { + prefix: 'See more here' + }, + error: 'Something went wrong.', + formats: { + ics: '.ics file' + } }, - calendarTypeLabel: "Pick your calendar type", - description: { - prefix: "See more here" - }, - error: "Something went wrong.", - formats: { - ics: ".ics file", - } - }, - et: { - stages: { - 4936143: "Kaevumägi", - 4936141: "I Kirsimägi", - 4936142: "II Kirsimägi", - 4936144: "Laululava", - 4936145: "Aida suur saal", - 4936147: "Jaani kirik", - 4936149: "Babtistikirik", - 4938876: "Jaak Johansoni Kultra lava", - default: "Viljandi Pärimusmuusika festival" - }, - calendarTypeLabel: "Vali oma kalendriliik", - description: { - prefix: "Vaata rohkem siit" - }, - error: "Midagi läks valesti.", - formats: { - ics: ".ics fail", + et: { + stages: { + 4936143: 'Kaevumägi', + 4936141: 'I Kirsimägi', + 4936142: 'II Kirsimägi', + 4936144: 'Laululava', + 4936145: 'Aida suur saal', + 4936147: 'Jaani kirik', + 4936149: 'Babtistikirik', + 4938876: 'Jaak Johansoni Kultra lava', + default: 'Viljandi Pärimusmuusika festival' + }, + calendarTypeLabel: 'Vali oma kalendriliik', + description: { + prefix: 'Vaata rohkem siit' + }, + error: 'Midagi läks valesti.', + formats: { + ics: '.ics fail' + } } - } -}); +}) -export default useI18n; +export default useI18n From 211a77910148234d343187191880c1be01b22055 Mon Sep 17 00:00:00 2001 From: Daniel Sepp <43651556+bloomlive@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:54:51 +0300 Subject: [PATCH 3/3] Add language support --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 950d7c8..ae3063d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Actually only adds a select field to choose calendar type (ICS by default) and a small "+" anchor link to every concert in the schedule that allows you to add the concert to your calendar. Fills in all the details I deemed needed, but you can always edit the event in your calendar afterwards. +Supports both Estonian and English versions of the schedule (based on user's preference on the website). + ## Installation 1. Clone the repository 2. `pnpm install` (or yarn or npm) @@ -14,7 +16,7 @@ Actually only adds a select field to choose calendar type (ICS by default) and a 5. Go to `chrome://extensions/` 6. Enable "Developer mode" 7. Click "Load unpacked extension..." -8. Select the folder where you cloned the repository +8. Select the `dist` folder in the root of the project (ignored on Git) 9. Go to [Viljandi Folk schedule](https://www.viljandifolk.ee/en/schedule/) and see a "+" on the top left of every concert If you use watch, changes will be reloaded only when you press "Update" in Google Chrome.