From aae5dae320e7c20040176fb1e8017c6135e8fae0 Mon Sep 17 00:00:00 2001 From: fabio Date: Sat, 7 Mar 2026 18:34:46 +0100 Subject: [PATCH] implement language selector component and update internationalization logic --- web/static/i18n.js | 37 +++++-- web/templates/partials/language_dropdown.html | 13 +-- web_components/README.md | 7 +- web_components/index.html | 4 + web_components/public/flags/ch.svg | 5 + web_components/public/flags/de.svg | 5 + web_components/public/flags/en.svg | 9 ++ web_components/public/flags/en_us.svg | 53 ++++++++++ web_components/public/flags/fr.svg | 5 + web_components/public/flags/it.svg | 5 + .../src/components/LangSelector.ce.vue | 100 ++++++++++++++++++ web_components/src/playground.ts | 5 + web_components/src/register.ts | 17 ++- web_components/src/style.css | 16 +++ 14 files changed, 254 insertions(+), 27 deletions(-) create mode 100644 web_components/public/flags/ch.svg create mode 100644 web_components/public/flags/de.svg create mode 100644 web_components/public/flags/en.svg create mode 100644 web_components/public/flags/en_us.svg create mode 100644 web_components/public/flags/fr.svg create mode 100644 web_components/public/flags/it.svg create mode 100644 web_components/src/components/LangSelector.ce.vue diff --git a/web/static/i18n.js b/web/static/i18n.js index bf035ba..c9f49bb 100644 --- a/web/static/i18n.js +++ b/web/static/i18n.js @@ -166,20 +166,35 @@ }); } - var langSelect = document.getElementById(''); + function persistSelectedLang(selectedLang, isAuthenticated) { + localStorage.setItem(STORAGE_KEY, selectedLang); + if (!isAuthenticated) return; + fetch('/preferences/lang', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'lang=' + encodeURIComponent(selectedLang) + }).catch(function () { + // Keep UI responsive even if persistence fails. + }); + } + + var langSelect = document.getElementById('lang-select'); if (langSelect) { langSelect.addEventListener('change', function () { var selectedLang = normalizeLang(langSelect.value); - localStorage.setItem(STORAGE_KEY, selectedLang); - if (langSelect.dataset.authenticated === '1') { - fetch('/preferences/lang', { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: 'lang=' + encodeURIComponent(selectedLang) - }).catch(function () { - // Keep UI responsive even if persistence fails. - }); - } + persistSelectedLang(selectedLang, langSelect.dataset.authenticated === '1'); + applyTranslations(document); + }); + } + + var langSelectorElement = document.querySelector('lang-selector'); + if (langSelectorElement) { + langSelectorElement.addEventListener('lang-changed', function (event) { + var eventLang = event && event.detail ? event.detail.langCode : ''; + var selectedLang = normalizeLang(eventLang); + if (!isSupportedLang(selectedLang)) return; + var authAttr = langSelectorElement.getAttribute('authenticated'); + persistSelectedLang(selectedLang, authAttr === '1' || authAttr === 'true'); applyTranslations(document); }); } diff --git a/web/templates/partials/language_dropdown.html b/web/templates/partials/language_dropdown.html index cb94894..a7c2a8a 100644 --- a/web/templates/partials/language_dropdown.html +++ b/web/templates/partials/language_dropdown.html @@ -1,14 +1,3 @@ {{define "language_dropdown"}} -
- Italiano - -
+ {{end}} diff --git a/web_components/README.md b/web_components/README.md index 5499e0d..c01c8a2 100644 --- a/web_components/README.md +++ b/web_components/README.md @@ -22,10 +22,15 @@ Output in `dist/`: - `user-menu.es.js` - `user-menu.iife.js` +Componenti registrati dal bundle: +- `` +- `` + ## Uso nel browser ```html - + + ``` diff --git a/web_components/index.html b/web_components/index.html index 4e30343..1d88303 100644 --- a/web_components/index.html +++ b/web_components/index.html @@ -9,6 +9,10 @@

Web Components Playground

+
+ +
+
Admin User diff --git a/web_components/public/flags/ch.svg b/web_components/public/flags/ch.svg new file mode 100644 index 0000000..618ca8c --- /dev/null +++ b/web_components/public/flags/ch.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web_components/public/flags/de.svg b/web_components/public/flags/de.svg new file mode 100644 index 0000000..b5455e1 --- /dev/null +++ b/web_components/public/flags/de.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web_components/public/flags/en.svg b/web_components/public/flags/en.svg new file mode 100644 index 0000000..2b8a36c --- /dev/null +++ b/web_components/public/flags/en.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web_components/public/flags/en_us.svg b/web_components/public/flags/en_us.svg new file mode 100644 index 0000000..1dc4d69 --- /dev/null +++ b/web_components/public/flags/en_us.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web_components/public/flags/fr.svg b/web_components/public/flags/fr.svg new file mode 100644 index 0000000..ca80393 --- /dev/null +++ b/web_components/public/flags/fr.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web_components/public/flags/it.svg b/web_components/public/flags/it.svg new file mode 100644 index 0000000..ae33534 --- /dev/null +++ b/web_components/public/flags/it.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web_components/src/components/LangSelector.ce.vue b/web_components/src/components/LangSelector.ce.vue new file mode 100644 index 0000000..cd40276 --- /dev/null +++ b/web_components/src/components/LangSelector.ce.vue @@ -0,0 +1,100 @@ + + + diff --git a/web_components/src/playground.ts b/web_components/src/playground.ts index f97979c..78abc86 100644 --- a/web_components/src/playground.ts +++ b/web_components/src/playground.ts @@ -1,3 +1,8 @@ import { registerWebComponents } from './register'; registerWebComponents(); + +const test = document.getElementById('test'); +test?.addEventListener('lang-changed', (e) => { + console.log('Language changed to:', (e as CustomEvent).detail); +}); diff --git a/web_components/src/register.ts b/web_components/src/register.ts index c861f5e..4099a15 100644 --- a/web_components/src/register.ts +++ b/web_components/src/register.ts @@ -1,13 +1,15 @@ import { defineCustomElement } from 'vue'; import UserMenuElement from './components/UserMenu.ce.vue'; +import LangSelectorElement from './components/LangSelector.ce.vue'; import './style.css'; -const TAG_NAME = 'user-menu'; +const USER_MENU_TAG = 'user-menu'; +const LANG_SELECTOR_TAG = 'lang-selector'; export function registerWebComponents(): void { - if (!customElements.get(TAG_NAME)) { + if (!customElements.get(USER_MENU_TAG)) { customElements.define( - TAG_NAME, + USER_MENU_TAG, defineCustomElement(UserMenuElement, { // Tailwind is generated as global CSS; without Shadow DOM // utility classes apply correctly inside the custom element. @@ -15,6 +17,15 @@ export function registerWebComponents(): void { }), ); } + + if (!customElements.get(LANG_SELECTOR_TAG)) { + customElements.define( + LANG_SELECTOR_TAG, + defineCustomElement(LangSelectorElement, { + shadowRoot: false, + }), + ); + } } registerWebComponents(); diff --git a/web_components/src/style.css b/web_components/src/style.css index b5c61c9..ee1965a 100644 --- a/web_components/src/style.css +++ b/web_components/src/style.css @@ -1,3 +1,19 @@ @tailwind base; @tailwind components; @tailwind utilities; +img.falg{ + width:32px; + height:22px; +} +img.falg-min{ + width:22px; + height:14px; +} +.flag-ch{ + width:22px; + height:22px; +} +.flag-ch-min{ + width:22px; + height:14px; +} \ No newline at end of file