Aggiunge menu lingua dropdown e base i18n IT/EN/TH.

Introduce routing multilingua con locale tailandese, selettore lingua a tendina con icone bandiera SVG e gestione chiusura click-outside/Esc, oltre a integrazione Turnstile lato form contatti.

Made-with: Cursor
This commit is contained in:
Javaxman
2026-04-22 22:49:46 +02:00
parent fd6eaac03a
commit a81ed6f76b
34 changed files with 407 additions and 12 deletions

View File

@ -18,6 +18,24 @@ import {
} from '../data/home';
const techSliderItems = stackSection.techSlider.items;
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it';
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
const langItHref = '/';
const langEnHref = '/en';
const langThHref = '/th';
const langMeta = {
it: { code: 'IT', label: 'Italiano', flagSrc: '/images/flags/it.svg' },
en: { code: 'EN', label: 'English', flagSrc: '/images/flags/en.svg' },
th: { code: 'TH', label: 'ไทย', flagSrc: '/images/flags/th.svg' },
} as const;
---
<BaseLayout title={siteMeta.title} description={siteMeta.description}>
@ -37,7 +55,7 @@ const techSliderItems = stackSection.techSlider.items;
<div
class="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-3 sm:px-6"
>
<a href="/" class="group no-underline">
<a href={localize('/')} class="group no-underline">
<span
class="text-2xl font-semibold tracking-tight text-nx-fg group-hover:text-white sm:text-3xl"
>{navigation.brandName}</span
@ -55,7 +73,7 @@ const techSliderItems = stackSection.techSlider.items;
return (
<a
class="nx-nav-link"
href={item.href}
href={localize(item.href)}
data-section={sectionId}
data-nav-active="false"
>
@ -66,6 +84,65 @@ const techSliderItems = stackSection.techSlider.items;
</nav>
<div class="flex items-center gap-2 sm:gap-3">
<details class="relative hidden sm:block" data-lang-dropdown>
<summary
class="inline-flex h-10 cursor-pointer list-none items-center gap-2 rounded-lg border border-nx-border/80 bg-nx-bg/50 px-3 text-xs font-medium text-nx-fg transition-colors hover:border-sky-500/35 hover:text-white"
aria-label="Seleziona lingua"
>
<img src={langMeta[currentLocale].flagSrc} alt="" class="h-3.5 w-5 rounded-[2px] object-cover" loading="lazy" decoding="async" />
<span>{langMeta[currentLocale].code}</span>
<svg class="h-3.5 w-3.5 text-nx-muted" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.938a.75.75 0 1 1 1.08 1.04l-4.25 4.5a.75.75 0 0 1-1.08 0l-4.25-4.5a.75.75 0 0 1 .02-1.06Z" clip-rule="evenodd"></path>
</svg>
</summary>
<div
class="absolute right-0 top-12 z-50 min-w-44 overflow-hidden rounded-lg border border-nx-border bg-nx-elevated/95 p-1 shadow-xl backdrop-blur"
role="menu"
>
<a
href={langItHref}
class:list={[
'flex items-center justify-between rounded-md px-3 py-2 text-sm text-nx-muted transition-colors hover:bg-white/5 hover:text-nx-fg',
currentLocale === 'it' && 'bg-white/5 text-nx-fg',
]}
aria-current={currentLocale === 'it' ? 'page' : undefined}
>
<span class="flex items-center gap-2">
<img src={langMeta.it.flagSrc} alt="" class="h-3.5 w-5 rounded-[2px] object-cover" loading="lazy" decoding="async" />
{langMeta.it.label}
</span>
<span class="text-xs">{langMeta.it.code}</span>
</a>
<a
href={langEnHref}
class:list={[
'flex items-center justify-between rounded-md px-3 py-2 text-sm text-nx-muted transition-colors hover:bg-white/5 hover:text-nx-fg',
currentLocale === 'en' && 'bg-white/5 text-nx-fg',
]}
aria-current={currentLocale === 'en' ? 'page' : undefined}
>
<span class="flex items-center gap-2">
<img src={langMeta.en.flagSrc} alt="" class="h-3.5 w-5 rounded-[2px] object-cover" loading="lazy" decoding="async" />
{langMeta.en.label}
</span>
<span class="text-xs">{langMeta.en.code}</span>
</a>
<a
href={langThHref}
class:list={[
'flex items-center justify-between rounded-md px-3 py-2 text-sm text-nx-muted transition-colors hover:bg-white/5 hover:text-nx-fg',
currentLocale === 'th' && 'bg-white/5 text-nx-fg',
]}
aria-current={currentLocale === 'th' ? 'page' : undefined}
>
<span class="flex items-center gap-2">
<img src={langMeta.th.flagSrc} alt="" class="h-3.5 w-5 rounded-[2px] object-cover" loading="lazy" decoding="async" />
{langMeta.th.label}
</span>
<span class="text-xs">{langMeta.th.code}</span>
</a>
</div>
</details>
<button
type="button"
data-nx-chat-open
@ -122,7 +199,7 @@ const techSliderItems = stackSection.techSlider.items;
return (
<a
class="nx-nav-link block py-1"
href={item.href}
href={localize(item.href)}
data-section={sectionId}
data-nav-active="false"
@click="mobile = false"
@ -663,5 +740,6 @@ const techSliderItems = stackSection.techSlider.items;
</div>
<script>
import '../scripts/nx-nav-spy.ts';
import '../scripts/nx-lang-dropdown.ts';
</script>
</BaseLayout>