diff --git a/index.html b/index.html index 4826812..e78589a 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - +
diff --git a/public/_data/workshops.yml b/public/_data/workshops.yml index f394a77..c1a9977 100644 --- a/public/_data/workshops.yml +++ b/public/_data/workshops.yml @@ -12,9 +12,7 @@ duration: en: 3 × 1h 30min est: 3 × 1h 30min - date: - en: March 2026 - est: Märts 2026 + date: 2026-03 gallery: - temp1 - temp2 @@ -31,11 +29,8 @@ duration: en: 3 × 1h 30min est: 3 × 1h 30min - date: - en: March 2026 - est: Märts 2026 + date: 2026-03 gallery: - temp1 - temp2 - temp3 - diff --git a/src/lib/i18n.js b/src/lib/i18n.js index 5a91d73..e3af82a 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -1,7 +1,9 @@ -import { onScopeDispose, ref, watch } from 'vue'; +import { computed, onScopeDispose, ref, watch } from 'vue'; import { getLanguageFromRoute } from '../routes'; const localeModules = import.meta.glob('./locales/*/*.json'); +const supportedLanguages = new Set(['est', 'en']); +export const languageLocales = { est: 'et-EE', en: 'en-GB' }; function getInitialLanguage() { if (typeof window === 'undefined') return 'est'; @@ -12,13 +14,17 @@ function getInitialLanguage() { return langFromRoute; } - return localStorage.getItem('language') || 'est'; + const savedLanguage = localStorage.getItem('language'); + return supportedLanguages.has(savedLanguage) ? savedLanguage : 'est'; } export const currentLang = ref(getInitialLanguage()); +export const currentLocale = computed(() => languageLocales[currentLang.value]); export const text = ref({}); +let translationRequest = 0; async function loadTranslations(lang) { + const request = ++translationRequest; try { const translations = {}; const matchingModules = Object.entries(localeModules).filter(([path]) => @@ -26,10 +32,10 @@ async function loadTranslations(lang) { ); const modules = await Promise.all(matchingModules.map(([, loader]) => loader())); modules.forEach((module) => Object.assign(translations, module.default || module)); - text.value = translations; + if (request === translationRequest) text.value = translations; } catch (error) { console.error(`Failed to load translations for ${lang}:`, error); - text.value = {}; + if (request === translationRequest) text.value = {}; } } @@ -37,14 +43,16 @@ watch(currentLang, loadTranslations, { immediate: true }); export function usePageText(pageName) { const pageText = ref({}); + let pageRequest = 0; const stop = watch(currentLang, async (lang) => { + const request = ++pageRequest; try { const loader = localeModules[`./locales/${lang}/${pageName}.json`]; const module = loader ? await loader() : null; - pageText.value = module?.default || module || {}; + if (request === pageRequest) pageText.value = module?.default || module || {}; } catch (error) { console.error(`Failed to load ${pageName} translations for ${lang}:`, error); - pageText.value = {}; + if (request === pageRequest) pageText.value = {}; } }, { immediate: true }); @@ -53,10 +61,15 @@ export function usePageText(pageName) { } export function switchLang(lang) { + if (!supportedLanguages.has(lang)) return; currentLang.value = lang; if (typeof window !== 'undefined') localStorage.setItem('language', lang); } +watch(currentLocale, (locale) => { + if (typeof document !== 'undefined') document.documentElement.lang = locale; +}, { immediate: true }); + if (typeof window !== 'undefined') { window.addEventListener('popstate', () => { const lang = getLanguageFromRoute(window.location.pathname); diff --git a/src/lib/index.js b/src/lib/index.js index bb08385..0cd4744 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -1,5 +1,5 @@ export { navigate, goBack, reload, getPath, getQuery, switchLanguageRoute } from './router/router.js'; export { default as Router } from './router/Router.vue'; -export { currentLang, text, switchLang, usePageText } from './i18n.js'; +export { currentLang, currentLocale, languageLocales, text, switchLang, usePageText } from './i18n.js'; export { getLangText } from './langHelpers.js'; diff --git a/src/lib/locales/en/Helpdesk.json b/src/lib/locales/en/Helpdesk.json index 5c1ad23..081a1e6 100644 --- a/src/lib/locales/en/Helpdesk.json +++ b/src/lib/locales/en/Helpdesk.json @@ -23,16 +23,28 @@ "quantity": "Quantity/Time", "price": "Price" }, - "note": "Parts are ordered by the client themselves or by agreement with the technician.- -
+{{ note }}
+{{ text.hours?.note || '' }} @@ -164,7 +174,11 @@
- + {{ part.text }}
{{ text.location?.address || '' }} diff --git a/src/routes/Home.vue b/src/routes/Home.vue index 115e5bc..4069673 100644 --- a/src/routes/Home.vue +++ b/src/routes/Home.vue @@ -4,7 +4,7 @@ import { usePageText, currentLang } from "../lib/index.js"; import { getHeroImagePath, getHeroImageSrcSet, getHeroImageFallback, getOptimisedImagePath, getOptimisedImageFallback } from "../lib/imageHelpers.js"; import { onMounted, ref } from 'vue'; - import yaml from 'js-yaml'; + import { loadYaml } from '../lib/yaml.js'; import { ArrowRight } from "@lucide/vue"; import { ArrowLeft } from "@lucide/vue"; @@ -14,9 +14,7 @@ const text = usePageText("Home"); onMounted(async () => { - const response = await fetch('/_data/partners.yml'); - const yamlText = await response.text(); - partners.value = yaml.load(yamlText) || []; + partners.value = await loadYaml('/_data/partners.yml', []); }); const partnerLogoExtByName = { diff --git a/src/routes/Managment.vue b/src/routes/Managment.vue index 4d015fc..ca62a8a 100644 --- a/src/routes/Managment.vue +++ b/src/routes/Managment.vue @@ -6,7 +6,7 @@ Image, } from "../components/index.js"; import { onMounted, ref } from 'vue'; - import yaml from "js-yaml"; + import { loadYaml } from '../lib/yaml.js'; import { currentLang, getLangText, usePageText } from "../lib/index.js"; import { getOptimisedImagePath, getOptimisedImageFallback } from "../lib/imageHelpers.js"; import { Mail } from "@lucide/vue"; @@ -45,13 +45,10 @@ onMounted(async () => { try { - const currentRes = await fetch('/_data/management.yml'); - const currentYaml = await currentRes.text(); - currentManagement.value = yaml.load(currentYaml) || []; - - const pastRes = await fetch('/_data/past_management.yml'); - const pastYaml = await pastRes.text(); - pastManagement.value = yaml.load(pastYaml) || []; + [currentManagement.value, pastManagement.value] = await Promise.all([ + loadYaml('/_data/management.yml', []), + loadYaml('/_data/past_management.yml', []), + ]); } catch (error) { console.error('Error loading management data:', error); loadError.value = true; diff --git a/src/routes/Mentors.vue b/src/routes/Mentors.vue index f69bab0..9f4636a 100644 --- a/src/routes/Mentors.vue +++ b/src/routes/Mentors.vue @@ -1,7 +1,7 @@ diff --git a/src/routes/Student.vue b/src/routes/Student.vue index 5973416..f2fa414 100644 --- a/src/routes/Student.vue +++ b/src/routes/Student.vue @@ -5,7 +5,7 @@ Button, } from "../components/index.js"; import { computed, onMounted, ref } from 'vue'; - import yaml from 'js-yaml'; + import { loadYaml } from '../lib/yaml.js'; import { usePageText } from "../lib/index.js"; const members = ref({ junior: [], senior: [] }); @@ -13,13 +13,7 @@ const joinFormUrl = "https://pilves.lapikud.ee/apps/forms/s/WXed8sbG2s45GMKGAiXCemgE"; onMounted(async () => { - try { - const response = await fetch('/_data/members.yml'); - const yamlText = await response.text(); - members.value = yaml.load(yamlText) || { junior: [], senior: [] }; - } catch { - members.value = { junior: [], senior: [] }; - } + members.value = await loadYaml('/_data/members.yml', { junior: [], senior: [] }); }); const scrollToJoin = () => { diff --git a/src/routes/Workshops.vue b/src/routes/Workshops.vue index 4a7eb3b..a82f285 100644 --- a/src/routes/Workshops.vue +++ b/src/routes/Workshops.vue @@ -1,17 +1,15 @@