Introduco contenuti localizzati per menu, footer e principali sezioni della homepage, includendo il form contatti e i messaggi client per mantenere coerenza UX tra le tre lingue. Made-with: Cursor
176 lines
7.2 KiB
Plaintext
176 lines
7.2 KiB
Plaintext
---
|
||
import BaseLayout from './BaseLayout.astro';
|
||
import SiteFooter from '../components/SiteFooter.astro';
|
||
import { getNavigation } from '../data/home';
|
||
|
||
interface Props {
|
||
title: string;
|
||
description?: string;
|
||
heading: string;
|
||
/** Sottotitolo opzionale sotto l’H1 */
|
||
lead?: string;
|
||
/**
|
||
* Stili `@media print` (sfondo chiaro, moduli footer ridotti, modali nascosti).
|
||
* Imposta `false` se la pagina non deve attivare il foglio stampa dedicato.
|
||
*/
|
||
printFriendly?: boolean;
|
||
}
|
||
|
||
const {
|
||
title,
|
||
description = 'NexStudio — software e IA per settori regolamentati.',
|
||
heading,
|
||
lead,
|
||
printFriendly = true,
|
||
} = Astro.props;
|
||
|
||
const printDate = new Intl.DateTimeFormat('it-IT', {
|
||
dateStyle: 'long',
|
||
}).format(new Date());
|
||
const printSource = Astro.url.href;
|
||
const path = Astro.url.pathname;
|
||
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
|
||
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
|
||
const navigation = getNavigation(currentLocale);
|
||
const strippedPath = path.replace(/^\/(en|th)(?=\/|$)/, '') || '/';
|
||
const localeHref = (locale: 'it' | 'en' | 'th') => {
|
||
if (locale === 'it') return strippedPath;
|
||
return `/${locale}${strippedPath === '/' ? '' : strippedPath}`;
|
||
};
|
||
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 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={title} description={description}>
|
||
<div
|
||
class:list={[
|
||
'nx-grid-bg relative flex min-h-screen flex-col',
|
||
printFriendly && 'nx-print-sheet',
|
||
]}
|
||
>
|
||
<header
|
||
class="sticky top-0 z-50 border-b border-nx-border/40 bg-black"
|
||
>
|
||
<div
|
||
class="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-3 sm:px-6"
|
||
>
|
||
<a
|
||
href={localize('/')}
|
||
class="text-lg font-semibold tracking-tight text-nx-fg hover:text-white sm:text-xl"
|
||
>NexStudio</a
|
||
>
|
||
<div class="flex items-center gap-2">
|
||
<details class="relative" 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={navigation.labels.languageSelectorAria}
|
||
>
|
||
<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={localeHref('it')}
|
||
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={localeHref('en')}
|
||
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={localeHref('th')}
|
||
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>
|
||
<a
|
||
class="text-sm font-medium text-sky-400/90 hover:text-sky-300"
|
||
href={localize('/#contatti')}>{navigation.items[4]?.label ?? 'Contattaci'}</a
|
||
>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<main class="relative z-[1] flex-1 px-4 py-12 sm:px-6 sm:py-16">
|
||
<article class="mx-auto max-w-3xl">
|
||
{
|
||
printFriendly ? (
|
||
<p class="nx-print-meta mb-6 hidden border-b border-slate-300 pb-3 text-xs leading-relaxed text-slate-600">
|
||
{printDate}
|
||
<span class="text-slate-500"> · </span>
|
||
<span class="break-all">{printSource}</span>
|
||
</p>
|
||
) : null
|
||
}
|
||
<h1
|
||
class="text-3xl font-semibold tracking-tight text-nx-fg sm:text-4xl"
|
||
>
|
||
{heading}
|
||
</h1>
|
||
{
|
||
lead && (
|
||
<p class="mt-3 text-base leading-relaxed text-nx-muted sm:text-lg">
|
||
{lead}
|
||
</p>
|
||
)
|
||
}
|
||
<div
|
||
class="mt-10 space-y-6 font-sans text-sm leading-relaxed text-nx-muted antialiased sm:text-base [&_h2]:mt-10 [&_h2]:text-lg [&_h2]:font-semibold [&_h2]:text-nx-fg [&_h2]:sm:text-xl [&_h2]:first:mt-0 [&_li]:mt-2 [&_ol]:list-decimal [&_ol]:space-y-2 [&_ol]:pl-5 [&_p]:mt-4 [&_p]:first:mt-0 [&_strong]:text-nx-fg [&_ul]:list-disc [&_ul]:space-y-2 [&_ul]:pl-5"
|
||
>
|
||
<slot />
|
||
</div>
|
||
</article>
|
||
</main>
|
||
<SiteFooter />
|
||
</div>
|
||
</BaseLayout>
|
||
|
||
<script>
|
||
import '../scripts/nx-lang-dropdown.ts';
|
||
</script>
|