Avvia i18n reale della home su IT EN TH.

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
This commit is contained in:
Javaxman
2026-04-27 09:12:00 +02:00
parent ddab32d3ef
commit 0714ec1335
14 changed files with 1068 additions and 308 deletions

View File

@ -1,5 +1,10 @@
--- ---
import { cta } from '../data/home'; import { getCta } from '../data/home';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const cta = getCta(currentLocale);
const cf = cta.contactForm; const cf = cta.contactForm;
const model = { const model = {
@ -14,6 +19,15 @@ const model = {
fieldDepartment: cf.fieldDepartment, fieldDepartment: cf.fieldDepartment,
fieldEmail: cf.fieldEmail, fieldEmail: cf.fieldEmail,
fieldMessage: cf.fieldMessage, fieldMessage: cf.fieldMessage,
turnstileError: cf.errors.turnstile,
configError: cf.errors.config,
mailtoFallbackSubject: cf.mailtoFallbackSubject,
mailtoLabelName: cf.mailtoLabels.name,
mailtoLabelCompany: cf.mailtoLabels.company,
mailtoLabelEmail: cf.mailtoLabels.email,
mailtoLabelCountry: cf.mailtoLabels.country,
mailtoLabelDepartment: cf.mailtoLabels.department,
mailtoDefaultDepartment: cf.mailtoLabels.defaultDepartment,
}; };
const hasFormspree = const hasFormspree =
typeof cf.actionUrl === 'string' && cf.actionUrl.length > 0; typeof cf.actionUrl === 'string' && cf.actionUrl.length > 0;
@ -203,11 +217,11 @@ const fieldClass =
class="cf-turnstile" class="cf-turnstile"
data-sitekey={cf.turnstileSiteKey} data-sitekey={cf.turnstileSiteKey}
data-theme="dark" data-theme="dark"
data-language="it" data-language={cf.turnstileLanguage}
data-size="normal" data-size="normal"
></div> ></div>
<p class="mt-1.5 text-xs leading-relaxed text-nx-muted"> <p class="mt-1.5 text-xs leading-relaxed text-nx-muted">
Verifica anti-spam: completa il controllo prima di inviare il messaggio. {cf.turnstileHint}
</p> </p>
</div> </div>
) : null ) : null

View File

@ -1,5 +1,10 @@
--- ---
import { footer } from '../data/home'; import { getFooter } from '../data/home';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const footer = getFooter(currentLocale);
const hasNewsletterAction = const hasNewsletterAction =
typeof footer.newsletter.actionUrl === 'string' && typeof footer.newsletter.actionUrl === 'string' &&
@ -17,9 +22,6 @@ const notifySubject =
const linkClass = const linkClass =
'text-sm text-sky-400/90 transition-colors hover:text-sky-300'; 'text-sm text-sky-400/90 transition-colors hover:text-sky-300';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it';
const withLocale = (href: string) => { const withLocale = (href: string) => {
if (currentLocale === 'it') return href; if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href; if (href.startsWith(`/${currentLocale}`)) return href;
@ -91,7 +93,7 @@ const withLocale = (href: string) => {
) : ( ) : (
<span <span
class="cursor-not-allowed text-nx-muted opacity-50" class="cursor-not-allowed text-nx-muted opacity-50"
title="URL da configurare in footer.ts" title={footer.labels.socialPlaceholderTitle}
> >
{item.label} {item.label}
</span> </span>
@ -115,7 +117,7 @@ const withLocale = (href: string) => {
{link.label} {link.label}
</a> </a>
) : ( ) : (
<span class="text-sm text-nx-muted" title="Collegamento in arrivo"> <span class="text-sm text-nx-muted" title={footer.labels.companyPlaceholderTitle}>
{link.label} {link.label}
</span> </span>
)} )}
@ -131,7 +133,7 @@ const withLocale = (href: string) => {
> >
<div class="min-w-0"> <div class="min-w-0">
<p class="text-xs font-semibold uppercase tracking-wider text-nx-fg"> <p class="text-xs font-semibold uppercase tracking-wider text-nx-fg">
Impostazioni cookie {footer.labels.cookieTitle}
</p> </p>
<p class="mt-1.5 max-w-xl text-xs leading-relaxed text-nx-muted"> <p class="mt-1.5 max-w-xl text-xs leading-relaxed text-nx-muted">
{footer.cookiePreferences.hint} {footer.cookiePreferences.hint}
@ -204,7 +206,7 @@ const withLocale = (href: string) => {
{nl.buttonLabel} {nl.buttonLabel}
</button> </button>
<p id="nx-newsletter-hint" class="text-xs leading-relaxed text-nx-muted"> <p id="nx-newsletter-hint" class="text-xs leading-relaxed text-nx-muted">
Iscrizione in arrivo: configura lendpoint nel progetto per attivare il modulo. {footer.labels.newsletterComingSoon}
</p> </p>
</div> </div>
) )

View File

@ -5,58 +5,165 @@ const contactSuccessRedirect = (env.PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT ?? '').
const contactMailto = (env.PUBLIC_CONTACT_FORM_MAILTO ?? '').trim(); const contactMailto = (env.PUBLIC_CONTACT_FORM_MAILTO ?? '').trim();
const contactTurnstileSiteKey = (env.PUBLIC_CONTACT_TURNSTILE_SITE_KEY ?? '').trim(); const contactTurnstileSiteKey = (env.PUBLIC_CONTACT_TURNSTILE_SITE_KEY ?? '').trim();
export const cta = { import type { SupportedLocale } from './navigation';
/** Riga sezione (stesso ruolo di FAQ, Stack, …) */
eyebrow: 'Contatti',
/** Titolo CTA su due righe (dopo il punto) */ const ctaByLocale = {
titleLine1: 'Automatizza il complesso.', it: {
titleLine2: 'Velocizza il processo.', eyebrow: 'Contatti',
body: titleLine1: 'Automatizza il complesso.',
'Dai modelli LLM alla distribuzione globale in pochi millisecondi. La directory definitiva per il LegalTech e MedTech. Utilizza il modulo contatto qui a fianco e ti forniremo tutti i chiarimenti che ti servono.', titleLine2: 'Velocizza il processo.',
body:
/** 'Dai modelli LLM alla distribuzione globale in pochi millisecondi. La directory definitiva per il LegalTech e MedTech. Utilizza il modulo contatto qui a fianco e ti forniremo tutti i chiarimenti che ti servono.',
* Modulo contatti in pagina (sezione Contatti). contactForm: {
* title: 'Parliamone',
* **Formspree:** crea un form su https://formspree.io e incolla lURL in `actionUrl`. description:
* **Solo mailto:** lascia `actionUrl` vuoto e imposta `mailto` con lindirizzo destinatario 'Lascia i tuoi dati e il messaggio: ti risponde il team NexStudio.',
* (si apre il client di posta dellutente). nameLabel: 'Nome',
*/ companyLabel: 'Azienda (opzionale)',
contactForm: { countryLabel: 'Paese',
/** Lasciare vuoti endpoint/chiavi nelle fasi di sola UI. */ departmentLabel: 'Area',
title: 'Parliamone', emailLabel: 'Email',
description: messageLabel: 'Scrivi qui il tuo messaggio',
'Lascia i tuoi dati e il messaggio: ti risponde il team NexStudio.', submitLabel: 'Invia messaggio',
nameLabel: 'Nome', actionUrl: contactActionUrl,
companyLabel: 'Azienda (opzionale)', successRedirect: contactSuccessRedirect,
countryLabel: 'Paese', turnstileSiteKey: contactTurnstileSiteKey,
departmentLabel: 'Area', turnstileLanguage: 'it',
emailLabel: 'Email', turnstileHint:
messageLabel: 'Scrivi qui il tuo messaggio', 'Verifica anti-spam: completa il controllo prima di inviare il messaggio.',
submitLabel: 'Invia messaggio', notifySubject: 'Richiesta contatto — sito NexStudio',
/** Formspree: `https://formspree.io/f/xxxx` — valorizzato via `PUBLIC_CONTACT_FORM_ACTION_URL` */ mailto: contactMailto,
actionUrl: contactActionUrl, mailtoFallbackSubject: 'Contatto sito',
/** Formspree `_next` — URL assoluta in produzione (`PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT`) */ mailtoLabels: {
successRedirect: contactSuccessRedirect, name: 'Nome',
/** company: 'Azienda',
* Cloudflare Turnstile (anti-spam): inserisci qui la site key pubblica. email: 'Email',
* Se vuota, il CAPTCHA non viene mostrato. country: 'Paese',
*/ department: 'Area',
turnstileSiteKey: contactTurnstileSiteKey, defaultDepartment: 'general',
/** Formspree `_subject` e oggetto mailto */ },
notifySubject: 'Richiesta contatto — sito NexStudio', errors: {
/** Fallback se `actionUrl` e' vuoto (`PUBLIC_CONTACT_FORM_MAILTO`) */ turnstile: 'Completa il controllo anti-spam prima di inviare il messaggio.',
mailto: contactMailto, config:
fieldName: 'name', 'Configura `contactForm.actionUrl` (Formspree) o `contactForm.mailto` in `src/data/home/cta.ts`.',
fieldCompany: 'company', },
fieldCountry: 'country', fieldName: 'name',
fieldDepartment: 'department', fieldCompany: 'company',
fieldEmail: 'email', fieldCountry: 'country',
fieldMessage: 'message', fieldDepartment: 'department',
departmentOptions: [ fieldEmail: 'email',
{ value: 'info', label: 'Informazioni generali e commerciali' }, fieldMessage: 'message',
{ value: 'privacy', label: 'Privacy' }, departmentOptions: [
{ value: 'careers', label: 'Careers / Collaborazioni' }, { value: 'info', label: 'Informazioni generali e commerciali' },
], { value: 'privacy', label: 'Privacy' },
{ value: 'careers', label: 'Careers / Collaborazioni' },
],
},
},
en: {
eyebrow: 'Contacts',
titleLine1: 'Automate complexity.',
titleLine2: 'Accelerate operations.',
body:
'From LLM models to global distribution in milliseconds. Use the contact form and our team will provide all the details you need.',
contactForm: {
title: 'Lets talk',
description: 'Share your details and message: the NexStudio team will reply.',
nameLabel: 'Name',
companyLabel: 'Company (optional)',
countryLabel: 'Country',
departmentLabel: 'Area',
emailLabel: 'Email',
messageLabel: 'Write your message',
submitLabel: 'Send message',
actionUrl: contactActionUrl,
successRedirect: contactSuccessRedirect,
turnstileSiteKey: contactTurnstileSiteKey,
turnstileLanguage: 'en',
turnstileHint:
'Anti-spam verification: complete the challenge before sending your message.',
notifySubject: 'Contact request — NexStudio website',
mailto: contactMailto,
mailtoFallbackSubject: 'Website contact',
mailtoLabels: {
name: 'Name',
company: 'Company',
email: 'Email',
country: 'Country',
department: 'Area',
defaultDepartment: 'general',
},
errors: {
turnstile: 'Complete the anti-spam check before sending your message.',
config:
'Configure `contactForm.actionUrl` (Formspree) or `contactForm.mailto` in `src/data/home/cta.ts`.',
},
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldDepartment: 'department',
fieldEmail: 'email',
fieldMessage: 'message',
departmentOptions: [
{ value: 'info', label: 'General and commercial information' },
{ value: 'privacy', label: 'Privacy' },
{ value: 'careers', label: 'Careers / Collaborations' },
],
},
},
th: {
eyebrow: 'ติดต่อ',
titleLine1: 'ทำงานซับซ้อนให้อัตโนมัติ',
titleLine2: 'เร่งความเร็วกระบวนการ',
body:
'จากโมเดล LLM สู่การกระจายระบบระดับโลกภายในไม่กี่มิลลิวินาที ใช้ฟอร์มติดต่อด้านข้าง แล้วทีม NexStudio จะตอบกลับพร้อมข้อมูลที่คุณต้องการ',
contactForm: {
title: 'คุยกับเรา',
description: 'กรอกข้อมูลและข้อความ แล้วทีม NexStudio จะติดต่อกลับ',
nameLabel: 'ชื่อ',
companyLabel: 'บริษัท (ไม่บังคับ)',
countryLabel: 'ประเทศ',
departmentLabel: 'แผนก',
emailLabel: 'อีเมล',
messageLabel: 'พิมพ์ข้อความของคุณ',
submitLabel: 'ส่งข้อความ',
actionUrl: contactActionUrl,
successRedirect: contactSuccessRedirect,
turnstileSiteKey: contactTurnstileSiteKey,
turnstileLanguage: 'th',
turnstileHint: 'การยืนยันป้องกันสแปม: กรุณาทำขั้นตอนให้เสร็จก่อนส่งข้อความ',
notifySubject: 'คำขอติดต่อ — เว็บไซต์ NexStudio',
mailto: contactMailto,
mailtoFallbackSubject: 'ติดต่อจากเว็บไซต์',
mailtoLabels: {
name: 'ชื่อ',
company: 'บริษัท',
email: 'อีเมล',
country: 'ประเทศ',
department: 'แผนก',
defaultDepartment: 'general',
},
errors: {
turnstile: 'กรุณายืนยันการป้องกันสแปมก่อนส่งข้อความ',
config:
'กรุณาตั้งค่า `contactForm.actionUrl` (Formspree) หรือ `contactForm.mailto` ใน `src/data/home/cta.ts`',
},
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldDepartment: 'department',
fieldEmail: 'email',
fieldMessage: 'message',
departmentOptions: [
{ value: 'info', label: 'ข้อมูลทั่วไปและเชิงพาณิชย์' },
{ value: 'privacy', label: 'ความเป็นส่วนตัว' },
{ value: 'careers', label: 'Careers / Collaborations' },
],
},
}, },
} as const; } as const;
export const getCta = (locale: SupportedLocale) => ctaByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const cta = ctaByLocale.it;

View File

@ -1,47 +1,148 @@
import type { SupportedLocale } from './navigation';
/** FAQ — domande/risposte allineate a hero, prodotti, servizi e stack */ /** FAQ — domande/risposte allineate a hero, prodotti, servizi e stack */
export const faq = { const faqByLocale = {
eyebrow: 'FAQ', it: {
title: 'Domande frequenti', eyebrow: 'FAQ',
items: [ title: 'Domande frequenti',
{ items: [
question: 'Che tipo di organizzazioni seguite con LexAura e MediAura?', {
answer: question: 'Che tipo di organizzazioni seguite con LexAura e MediAura?',
'Lavoriamo con studi legali e strutture sanitarie — dal professionista singolo a team e sedi multiunità — che gestiscono volumi elevati di informazione e hanno bisogno di strumenti affidabili in ambiti regolamentati. La verticalità su Legal Tech e Health Tech è parte della nostra proposta: meno genericismo, più requisiti operativi, tracciabilità e continuità del servizio.', answer:
}, 'Lavoriamo con studi legali e strutture sanitarie — dal professionista singolo a team e sedi multiunità — che gestiscono volumi elevati di informazione e hanno bisogno di strumenti affidabili in ambiti regolamentati. La verticalità su Legal Tech e Health Tech è parte della nostra proposta: meno genericismo, più requisiti operativi, tracciabilità e continuità del servizio.',
{ },
question: 'Lintelligenza artificiale sostituisce avvocati o medici?', {
answer: question: 'Lintelligenza artificiale sostituisce avvocati o medici?',
'No. I nostri prodotti sono pensati per integrarsi nei flussi professionali: sintesi, ricerca, drafting assistito, automazione della documentazione e interfacce vocali liberano tempo, ma la decisione finale e la responsabilità restano del professionista. Lobiettivo è ridurre il carico ripetitivo e offrire una base analitica solida, non sostituire il giudizio umano.', answer:
}, 'No. I nostri prodotti sono pensati per integrarsi nei flussi professionali: sintesi, ricerca, drafting assistito, automazione della documentazione e interfacce vocali liberano tempo, ma la decisione finale e la responsabilità restano del professionista. Lobiettivo è ridurre il carico ripetitivo e offrire una base analitica solida, non sostituire il giudizio umano.',
{ },
question: 'Cosa succede dopo ladozione del software?', {
answer: question: 'Cosa succede dopo ladozione del software?',
'Il modello di servizio va oltre la licenza: onboarding e formazione specialistica per team legali e sanitari, supporto tecnico e consulenza operativa continuativi, aggiornamento dei motori di analisi e manutenzione evolutiva rispetto a normative e contesto di mercato. Per criticità urgenti sono previsti protocolli di intervento prioritario, così che studi e strutture non restino isolati dai propri strumenti.', answer:
}, 'Il modello di servizio va oltre la licenza: onboarding e formazione specialistica per team legali e sanitari, supporto tecnico e consulenza operativa continuativi, aggiornamento dei motori di analisi e manutenzione evolutiva rispetto a normative e contesto di mercato. Per criticità urgenti sono previsti protocolli di intervento prioritario, così che studi e strutture non restino isolati dai propri strumenti.',
{ },
question: 'Potete collegare LexAura o MediAura ai nostri sistemi già in uso?', {
answer: question: 'Potete collegare LexAura o MediAura ai nostri sistemi già in uso?',
'Sì, quando serve. Oltre ai prodotti standard supportiamo integrazioni tramite API e moduli personalizzati nel perimetro dei nostri SaaS, per collegare gestionali o flussi documentali preesistenti senza reinventare lintero stack informativo.', answer:
}, 'Sì, quando serve. Oltre ai prodotti standard supportiamo integrazioni tramite API e moduli personalizzati nel perimetro dei nostri SaaS, per collegare gestionali o flussi documentali preesistenti senza reinventare lintero stack informativo.',
{ },
question: 'Come gestite sicurezza, disponibilità e dati sensibili?', {
answer: question: 'Come gestite sicurezza, disponibilità e dati sensibili?',
'Progettiamo su architetture cloudnative ad alta disponibilità, con attenzione a crittografia, protezione di perimetro (es. WAF, mitigazione DDoS) e continuità operativa. Il trattamento di informazioni legali e sanitarie è affrontato con criteri di integrità dei dati e conformità alle normative applicabili, inclusa la chiarezza su dove risiedono i dati e sui requisiti di due diligence che i settori regolamentati si attendono.', answer:
}, 'Progettiamo su architetture cloudnative ad alta disponibilità, con attenzione a crittografia, protezione di perimetro (es. WAF, mitigazione DDoS) e continuità operativa. Il trattamento di informazioni legali e sanitarie è affrontato con criteri di integrità dei dati e conformità alle normative applicabili, inclusa la chiarezza su dove risiedono i dati e sui requisiti di due diligence che i settori regolamentati si attendono.',
{ },
question: 'Lavorate solo in loco o anche con clienti in Italia e allestero?', {
answer: question: 'Lavorate solo in loco o anche con clienti in Italia e allestero?',
'Siamo una software company con base operativa a Bangkok e collaboriamo con clienti a livello internazionale. Workshop e incontri possono essere online o in presenza in base al progetto; supporto e comunicazioni operative in italiano, inglese, tedesco, francese, spagnolo o tailandese, con orari concordati.', answer:
}, 'Siamo una software company con base operativa a Bangkok e collaboriamo con clienti a livello internazionale. Workshop e incontri possono essere online o in presenza in base al progetto; supporto e comunicazioni operative in italiano, inglese, tedesco, francese, spagnolo o tailandese, con orari concordati.',
{ },
question: 'Diritto duso e proprietà del software: come le regolate?', {
answer: question: 'Diritto duso e proprietà del software: come le regolate?',
'Dipende dal contratto: in genere cediamo lesclusiva sul deliverable a saldo, con clausole esplicite su componenti open source e librerie di terze parti. Per licenze SaaS, permessi duso e responsabilità sono definiti nei termini commerciali e nel contratto quadro.', answer:
}, 'Dipende dal contratto: in genere cediamo lesclusiva sul deliverable a saldo, con clausole esplicite su componenti open source e librerie di terze parti. Per licenze SaaS, permessi duso e responsabilità sono definiti nei termini commerciali e nel contratto quadro.',
{ },
question: 'Qual è il modo più rapido per parlarne con voi?', {
answer: question: 'Qual è il modo più rapido per parlarne con voi?',
'Il punto di ingresso principale è la chat dal pulsante «Chattiamo» in header: prima vedi se il canale è con operatore umano, solo chatbot o misto, poi lasci email (e facoltativamente il nome) per ricevere un link o un codice monouso e aprire la sessione senza perdite di tempo. In chat operatore umano e chatbot collaborano per orientamento iniziale, requisiti e passi successivi. Per il supporto via web ci interfacciamo in italiano, inglese, tedesco, francese, spagnolo o tailandese. Per materiale sensibile useremo sempre i canali concordati in fase di rapporto commerciale. Se ci si sente più a proprio agio usando un form di contatto, in fondo alla homepage nella sezione Contatti trovate il modulo a destra del testo introduttivo.', answer:
}, 'Il punto di ingresso principale è la chat dal pulsante «Chattiamo» in header: prima vedi se il canale è con operatore umano, solo chatbot o misto, poi lasci email (e facoltativamente il nome) per ricevere un link o un codice monouso e aprire la sessione senza perdite di tempo. In chat operatore umano e chatbot collaborano per orientamento iniziale, requisiti e passi successivi. Per il supporto via web ci interfacciamo in italiano, inglese, tedesco, francese, spagnolo o tailandese. Per materiale sensibile useremo sempre i canali concordati in fase di rapporto commerciale. Se ci si sente più a proprio agio usando un form di contatto, in fondo alla homepage nella sezione Contatti trovate il modulo a destra del testo introduttivo.',
] as const, },
] as const,
},
en: {
eyebrow: 'FAQ',
title: 'Frequently asked questions',
items: [
{
question: 'What organizations do you support with LexAura and MediAura?',
answer:
'We work with law firms and healthcare organizations, from solo professionals to multi-site teams, that manage high information volumes and need reliable tools in regulated environments.',
},
{
question: 'Does AI replace lawyers or doctors?',
answer:
'No. Our products support professional workflows with summarization, research, assisted drafting, and automation. Final decisions and responsibility always remain with the professional.',
},
{
question: 'What happens after software adoption?',
answer:
'Our service model goes beyond licensing: specialist onboarding and training, continuous technical support and consulting, and ongoing engine updates aligned with regulations and market context.',
},
{
question: 'Can you integrate LexAura or MediAura with our existing systems?',
answer:
'Yes, when needed. We support API integrations and custom modules within our SaaS perimeter to connect existing management or document flows.',
},
{
question: 'How do you handle security, availability, and sensitive data?',
answer:
'We design for high-availability cloud-native architectures, encryption, perimeter protection, and operational continuity, with clear data-governance practices aligned with applicable regulations.',
},
{
question: 'Do you work only locally or also with international clients?',
answer:
'We are based in Bangkok and collaborate internationally. Workshops can be remote or on-site depending on the project.',
},
{
question: 'How do you handle software usage rights and ownership?',
answer:
'This depends on the contract. In many engagements, exclusive rights on deliverables are transferred after final payment, with explicit clauses for open source and third-party components.',
},
{
question: 'What is the fastest way to talk with your team?',
answer:
'The primary entry point is the chat button in the header. You provide your email to receive a one-time link or code, then start the session without delay.',
},
] as const,
},
th: {
eyebrow: 'คำถามที่พบบ่อย',
title: 'คำถามที่พบบ่อย',
items: [
{
question: 'องค์กรแบบไหนที่เหมาะกับ LexAura และ MediAura?',
answer:
'เราทำงานกับสำนักงานกฎหมายและองค์กรสุขภาพ ตั้งแต่ผู้เชี่ยวชาญเดี่ยวไปจนถึงทีมหลายสาขา ที่ต้องจัดการข้อมูลจำนวนมากในบริบทที่มีข้อกำกับสูง',
},
{
question: 'AI จะแทนที่ทนายหรือแพทย์หรือไม่?',
answer:
'ไม่ใช่ เป้าหมายของเราคือช่วยงานเชิงวิเคราะห์ งานเอกสาร และเวิร์กโฟลว์ซ้ำ ๆ โดยการตัดสินใจสุดท้ายยังคงเป็นหน้าที่ของผู้เชี่ยวชาญ',
},
{
question: 'หลังเริ่มใช้งานซอฟต์แวร์แล้วจะเกิดอะไรขึ้น?',
answer:
'เรามีบริการหลังการใช้งานครบวงจร ทั้ง onboarding การอบรมเฉพาะทาง การสนับสนุนทางเทคนิค และการอัปเดตระบบอย่างต่อเนื่อง',
},
{
question: 'สามารถเชื่อม LexAura หรือ MediAura กับระบบเดิมของเราได้ไหม?',
answer:
'ได้ เรารองรับการเชื่อมต่อผ่าน API และโมดูลแบบกำหนดเองภายในขอบเขต SaaS ของเรา',
},
{
question: 'จัดการความปลอดภัย ความพร้อมใช้งาน และข้อมูลอ่อนไหวอย่างไร?',
answer:
'เราใช้สถาปัตยกรรมคลาวด์ที่พร้อมใช้งานสูง การเข้ารหัส การป้องกันระดับเครือข่าย และแนวปฏิบัติด้านการกำกับข้อมูลให้สอดคล้องข้อกำกับที่เกี่ยวข้อง',
},
{
question: 'รับงานเฉพาะในพื้นที่หรือรับลูกค้าต่างประเทศด้วย?',
answer:
'สำนักงานใหญ่ปฏิบัติการของเราอยู่ที่กรุงเทพฯ และทำงานกับลูกค้าต่างประเทศได้ ทั้งออนไลน์และออนไซต์ตามความเหมาะสมของโครงการ',
},
{
question: 'สิทธิการใช้งานและความเป็นเจ้าของซอฟต์แวร์กำหนดอย่างไร?',
answer:
'ขึ้นกับสัญญา โดยทั่วไปจะระบุเงื่อนไขการใช้งาน ความรับผิดชอบ และสิทธิในชิ้นงานอย่างชัดเจน รวมถึงส่วนประกอบโอเพนซอร์สและ third-party',
},
{
question: 'ถ้าต้องการคุยเร็วที่สุดควรติดต่อช่องทางไหน?',
answer:
'กดปุ่มแชทบนส่วนหัวเว็บไซต์ ใส่อีเมลเพื่อรับลิงก์หรือโค้ดแบบใช้ครั้งเดียว แล้วเริ่มเซสชันได้ทันที',
},
] as const,
},
} as const; } as const;
export const getFaq = (locale: SupportedLocale) => faqByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const faq = faqByLocale.it;

View File

@ -1,87 +1,178 @@
import type { SupportedLocale } from './navigation';
/** Footer — griglia 4 colonne (link); newsletter in blocco unico sotto, allineata a sinistra */ /** Footer — griglia 4 colonne (link); newsletter in blocco unico sotto, allineata a sinistra */
export const footer = { const footerByLocale = {
brandName: 'NexStudio', it: {
tagline: 'Ingegneria del software e IA per settori regolamentati.', brandName: 'NexStudio',
legalLine: 'P.IVA e note legali: da inserire.', tagline: 'Ingegneria del software e IA per settori regolamentati.',
legalLine: 'P.IVA e note legali: da inserire.',
columnTitles: { columnTitles: {
sections: 'Sezioni', sections: 'Sezioni',
legal: 'Normative', legal: 'Normative',
social: 'Reti sociali', social: 'Reti sociali',
company: 'Azienda', company: 'Azienda',
} as const, } as const,
homeNav: [
/** Colonna 1 — ancore sulla homepage */ { label: 'Prodotti', href: '/#prodotti' },
homeNav: [ { label: 'Servizi', href: '/#servizi' },
{ label: 'Prodotti', href: '/#prodotti' }, { label: 'Stack', href: '/#stack' },
{ label: 'Servizi', href: '/#servizi' }, { label: 'FAQ', href: '/#faq' },
{ label: 'Stack', href: '/#stack' }, ] as const,
{ label: 'FAQ', href: '/#faq' }, cookiePreferences: {
] as const, label: 'Impostazioni cookie',
hint: 'Preferenze di consenso sul tuo browser (non è un testo legale).',
/** Pannello preferenze (CMP): non è un documento legale; tenuto fuori dalla colonna Normative */ } as const,
cookiePreferences: { legalColumnNav: [
label: 'Impostazioni cookie', { label: 'Condizioni duso', href: '/terms' },
hint: 'Preferenze di consenso sul tuo browser (non è un testo legale).', { label: 'Informativa cookie', href: '/cookies' },
} as const, { label: 'Privacy', href: '/privacy' },
{ label: 'Diritti privacy (GDPR)', href: '/gdpr' },
/** Colonna 2 — documenti legali (testi bozza nelle rispettive pagine) */ ] as const,
legalColumnNav: [ socialLinks: [
{ label: 'Condizioni duso', href: '/terms' }, { label: 'Facebook', href: '' },
{ label: 'Informativa cookie', href: '/cookies' }, { label: 'LinkedIn', href: '' },
{ label: 'Privacy', href: '/privacy' }, { label: 'Twitter / X', href: '' },
{ label: 'Diritti privacy (GDPR)', href: '/gdpr' }, { label: 'GitHub', href: '' },
] as const, ] as const,
companyNav: [
/** { label: 'Chi siamo', href: '/about' },
* Colonna 3 — social (sostituire gli URL con i profili reali). { label: 'Codice etico', href: '/codice-etico' },
* Lasciare `href` vuoto per mostrare solo etichetta disabilitata finché non configurato. { label: 'Modello organizzativo', href: '/modello-organizzativo' },
*/ { label: 'Dove siamo', href: '/dove-siamo' },
socialLinks: [ ] as const,
{ label: 'Facebook', href: '' }, newsletter: {
{ label: 'LinkedIn', href: '' }, title: 'Newsletter',
{ label: 'Twitter / X', href: '' }, description: 'Aggiornamenti su prodotti e roadmap.',
{ label: 'GitHub', href: '' }, placeholder: 'La tua email',
] as const, buttonLabel: 'Iscriviti',
actionUrl: '',
/** Colonna 4 — pagine istituzionali e compliance */ fieldName: 'email',
companyNav: [ successRedirect: '',
{ label: 'Chi siamo', href: '/about' }, notifySubject: 'Nuova iscrizione newsletter — NexStudio',
{ label: 'Codice etico', href: '/codice-etico' }, },
{ label: 'Modello organizzativo', href: '/modello-organizzativo' }, labels: {
{ label: 'Dove siamo', href: '/dove-siamo' }, cookieTitle: 'Impostazioni cookie',
] as const, socialPlaceholderTitle: 'URL da configurare in footer.ts',
companyPlaceholderTitle: 'Collegamento in arrivo',
/** newsletterComingSoon:
* Newsletter — invio via POST a un endpoint esterno. 'Iscrizione in arrivo: configura lendpoint nel progetto per attivare il modulo.',
* },
* Opzione A (consigliata per partire): Formspree },
* 1. Crea account su https://formspree.io e un nuovo form. en: {
* 2. Copia lURL tipo `https://formspree.io/f/abcdefgh` in `actionUrl`. brandName: 'NexStudio',
* 3. Opzionale: `successRedirect` = URL assoluta della pagina di ringraziamento tagline: 'Software engineering and AI for regulated industries.',
* (es. `https://tuodominio.com/newsletter/grazie` — vedi `src/pages/newsletter/grazie.astro`). legalLine: 'VAT number and legal notes: to be added.',
* 4. In Formspree collega linvio a email / Slack / Google Sheets. columnTitles: {
* sections: 'Sections',
* Opzione B: Brevo, Mailchimp, Buttondown, ConvertKit — usano spesso un URL legal: 'Legal',
* dedicato o un embed; adatta `fieldName` al nome campo richiesto dalla doc. social: 'Social',
* company: 'Company',
* Opzione C: Worker Cloudflare + lista in KV/D1 (più controllo, più codice). } as const,
*/ homeNav: [
newsletter: { { label: 'Products', href: '/#prodotti' },
title: 'Newsletter', { label: 'Services', href: '/#servizi' },
description: 'Aggiornamenti su prodotti e roadmap.', { label: 'Stack', href: '/#stack' },
placeholder: 'La tua email', { label: 'FAQ', href: '/#faq' },
buttonLabel: 'Iscriviti', ] as const,
/** Formspree: `https://formspree.io/f/xxxx` — vuoto = modulo disabilitato */ cookiePreferences: {
actionUrl: '', label: 'Cookie settings',
/** Nome campo email richiesto dal provider (Formspree: di solito `email`) */ hint: 'Consent preferences in your browser (this is not a legal document).',
fieldName: 'email', } as const,
/** legalColumnNav: [
* Formspree: redirect dopo invio (`_next`). Deve essere URL **assoluta** { label: 'Terms of use', href: '/terms' },
* (es. produzione). Vuoto = pagina di ringrazio predefinita Formspree. { label: 'Cookie policy', href: '/cookies' },
*/ { label: 'Privacy', href: '/privacy' },
successRedirect: '', { label: 'Privacy rights (GDPR)', href: '/gdpr' },
/** Formspree: oggetto della mail di notifica (`_subject`) */ ] as const,
notifySubject: 'Nuova iscrizione newsletter — NexStudio', socialLinks: [
{ label: 'Facebook', href: '' },
{ label: 'LinkedIn', href: '' },
{ label: 'Twitter / X', href: '' },
{ label: 'GitHub', href: '' },
] as const,
companyNav: [
{ label: 'About us', href: '/about' },
{ label: 'Code of ethics', href: '/codice-etico' },
{ label: 'Organizational model', href: '/modello-organizzativo' },
{ label: 'Where we are', href: '/dove-siamo' },
] as const,
newsletter: {
title: 'Newsletter',
description: 'Updates on products and roadmap.',
placeholder: 'Your email',
buttonLabel: 'Subscribe',
actionUrl: '',
fieldName: 'email',
successRedirect: '',
notifySubject: 'New newsletter signup — NexStudio',
},
labels: {
cookieTitle: 'Cookie settings',
socialPlaceholderTitle: 'URL to configure in footer.ts',
companyPlaceholderTitle: 'Coming soon',
newsletterComingSoon:
'Newsletter signups coming soon: configure the endpoint in the project to enable this form.',
},
},
th: {
brandName: 'NexStudio',
tagline: 'วิศวกรรมซอฟต์แวร์และ AI สำหรับอุตสาหกรรมที่มีข้อกำกับ',
legalLine: 'เลขประจำตัวผู้เสียภาษีและข้อมูลทางกฎหมาย: รอเพิ่มภายหลัง',
columnTitles: {
sections: 'ส่วนต่าง ๆ',
legal: 'กฎหมาย',
social: 'โซเชียล',
company: 'บริษัท',
} as const,
homeNav: [
{ label: 'ผลิตภัณฑ์', href: '/#prodotti' },
{ label: 'บริการ', href: '/#servizi' },
{ label: 'สแตก', href: '/#stack' },
{ label: 'คำถามที่พบบ่อย', href: '/#faq' },
] as const,
cookiePreferences: {
label: 'ตั้งค่าคุกกี้',
hint: 'การตั้งค่าความยินยอมในเบราว์เซอร์ของคุณ (ไม่ใช่เอกสารทางกฎหมาย)',
} as const,
legalColumnNav: [
{ label: 'เงื่อนไขการใช้งาน', href: '/terms' },
{ label: 'นโยบายคุกกี้', href: '/cookies' },
{ label: 'ความเป็นส่วนตัว', href: '/privacy' },
{ label: 'สิทธิความเป็นส่วนตัว (GDPR)', href: '/gdpr' },
] as const,
socialLinks: [
{ label: 'Facebook', href: '' },
{ label: 'LinkedIn', href: '' },
{ label: 'Twitter / X', href: '' },
{ label: 'GitHub', href: '' },
] as const,
companyNav: [
{ label: 'เกี่ยวกับเรา', href: '/about' },
{ label: 'จรรยาบรรณ', href: '/codice-etico' },
{ label: 'โมเดลองค์กร', href: '/modello-organizzativo' },
{ label: 'ที่ตั้ง', href: '/dove-siamo' },
] as const,
newsletter: {
title: 'จดหมายข่าว',
description: 'อัปเดตเกี่ยวกับผลิตภัณฑ์และโรดแมป',
placeholder: 'อีเมลของคุณ',
buttonLabel: 'สมัครรับข่าวสาร',
actionUrl: '',
fieldName: 'email',
successRedirect: '',
notifySubject: 'มีผู้สมัครจดหมายข่าวใหม่ — NexStudio',
},
labels: {
cookieTitle: 'ตั้งค่าคุกกี้',
socialPlaceholderTitle: 'ต้องตั้งค่า URL ใน footer.ts',
companyPlaceholderTitle: 'กำลังจะเปิดใช้งาน',
newsletterComingSoon:
'ระบบสมัครจดหมายข่าวกำลังจะเปิดใช้งาน: ตั้งค่า endpoint ในโปรเจกต์เพื่อเปิดฟอร์มนี้',
},
}, },
} as const; } as const;
export const getFooter = (locale: SupportedLocale) => footerByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const footer = footerByLocale.it;

View File

@ -1,29 +1,85 @@
import type { SupportedLocale } from './navigation';
/** Sezione hero */ /** Sezione hero */
export const hero = { const heroByLocale = {
badge: 'Restituiamo valore al tuo tempo', it: {
/** Parte prima dellevidenziazione nel titolo */ badge: 'Restituiamo valore al tuo tempo',
titleBefore: "Ingegneria del software per l'era dell'", titleBefore: "Ingegneria del software per l'era dell'",
/** Segmento con gradiente nel titolo */ titleHighlight: 'Intelligenza Artificiale',
titleHighlight: 'Intelligenza Artificiale', titleAfter: '.',
titleAfter: '.', subtitle:
subtitle: 'Sviluppiamo soluzioni SaaS ad alta complessità per settori regolamentati.',
'Sviluppiamo soluzioni SaaS ad alta complessità per settori regolamentati.', paragraphs: [
/** Paragrafi introduttivi */ "Ci occupiamo di progettazione di ecosistemi digitali evoluti. La nostra specializzazione risiede nell'integrazione di modelli di Intelligenza Artificiale all'interno di flussi di lavoro professionali dove precisione, sicurezza e affidabilità sono requisiti non negoziabili.",
paragraphs: [ 'Il nostro approccio si concentra sulla trasformazione di dati complessi in strumenti operativi fluidi, attraverso lo sviluppo di algoritmi proprietari, interfacce vocali e architetture cloud scalabili.',
"Ci occupiamo di progettazione di ecosistemi digitali evoluti. La nostra specializzazione risiede nell'integrazione di modelli di Intelligenza Artificiale all'interno di flussi di lavoro professionali dove precisione, sicurezza e affidabilità sono requisiti non negoziabili.", ] as const,
'Il nostro approccio si concentra sulla trasformazione di dati complessi in strumenti operativi fluidi, attraverso lo sviluppo di algoritmi proprietari, interfacce vocali e architetture cloud scalabili.', businessUnitsHeading: 'Le nostre Business Unit:',
] as const, businessUnitsIntro:
businessUnitsHeading: 'Le nostre Business Unit:', 'Operiamo attraverso divisioni specializzate per garantire la massima verticalità e competenza di dominio:',
businessUnitsIntro: businessUnits: [
'Operiamo attraverso divisioni specializzate per garantire la massima verticalità e competenza di dominio:', {
businessUnits: [ name: 'Legal Tech',
{ description: "Soluzioni per l'automazione e l'analisi normativa.",
name: 'Legal Tech', },
description: "Soluzioni per l'automazione e l'analisi normativa.", {
}, name: 'Health Tech',
{ description: 'Sistemi avanzati per la gestione e lautomazione sanitaria.',
name: 'Health Tech', },
description: 'Sistemi avanzati per la gestione e lautomazione sanitaria.', ] as const,
}, },
] as const, en: {
badge: 'We give your time back',
titleBefore: "Software engineering for the era of ",
titleHighlight: 'Artificial Intelligence',
titleAfter: '.',
subtitle:
'We build high-complexity SaaS solutions for regulated industries.',
paragraphs: [
'We design advanced digital ecosystems. Our specialization is integrating AI models into professional workflows where accuracy, security, and reliability are non-negotiable requirements.',
'Our approach turns complex data into smooth operational tools through proprietary algorithms, voice interfaces, and scalable cloud-native architectures.',
] as const,
businessUnitsHeading: 'Our Business Units:',
businessUnitsIntro:
'We operate through specialized divisions to ensure deep domain expertise and vertical focus:',
businessUnits: [
{
name: 'Legal Tech',
description: 'Solutions for legal automation and regulatory analysis.',
},
{
name: 'Health Tech',
description:
'Advanced systems for healthcare workflow management and automation.',
},
] as const,
},
th: {
badge: 'คืนคุณค่าให้กับเวลาของคุณ',
titleBefore: 'วิศวกรรมซอฟต์แวร์สำหรับยุคของ',
titleHighlight: 'ปัญญาประดิษฐ์',
titleAfter: '.',
subtitle: 'เราพัฒนาโซลูชัน SaaS ที่ซับซ้อนสูงสำหรับอุตสาหกรรมที่มีข้อกำกับ',
paragraphs: [
'เราออกแบบระบบนิเวศดิจิทัลขั้นสูง โดยเชี่ยวชาญในการผสานโมเดล AI เข้ากับเวิร์กโฟลว์ระดับมืออาชีพที่ต้องการความแม่นยำ ความปลอดภัย และความน่าเชื่อถือ',
'แนวทางของเราคือเปลี่ยนข้อมูลที่ซับซ้อนให้เป็นเครื่องมือการทำงานที่ลื่นไหล ผ่านอัลกอริทึมเฉพาะทาง อินเทอร์เฟซเสียง และสถาปัตยกรรมคลาวด์ที่ขยายได้',
] as const,
businessUnitsHeading: 'หน่วยธุรกิจของเรา:',
businessUnitsIntro:
'เราดำเนินงานผ่านทีมเฉพาะทางเพื่อให้ได้ความเชี่ยวชาญเชิงลึกในแต่ละโดเมน:',
businessUnits: [
{
name: 'Legal Tech',
description: 'โซลูชันสำหรับงานกฎหมายอัตโนมัติและการวิเคราะห์ข้อกำกับ',
},
{
name: 'Health Tech',
description: 'ระบบขั้นสูงสำหรับการจัดการและทำงานอัตโนมัติในภาคสุขภาพ',
},
] as const,
},
} as const; } as const;
export const getHero = (locale: SupportedLocale) => heroByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const hero = heroByLocale.it;

View File

@ -3,13 +3,21 @@
* Modifica i file in questa cartella per aggiornare testi senza toccare il markup. * Modifica i file in questa cartella per aggiornare testi senza toccare il markup.
*/ */
export { siteMeta } from './site-meta'; export { siteMeta } from './site-meta';
export { getSiteMeta } from './site-meta';
export { chat } from './chat'; export { chat } from './chat';
export { navigation } from './navigation'; export { navigation } from './navigation';
export { getNavigation } from './navigation';
export { hero } from './hero'; export { hero } from './hero';
export { getHero } from './hero';
export { stats } from './stats'; export { stats } from './stats';
export { services } from './services'; export { services } from './services';
export { getServices } from './services';
export { stackSection } from './stack'; export { stackSection } from './stack';
export { getStackSection } from './stack';
export { productsSection } from './products'; export { productsSection } from './products';
export { faq } from './faq'; export { faq } from './faq';
export { getFaq } from './faq';
export { cta } from './cta'; export { cta } from './cta';
export { getCta } from './cta';
export { footer } from './footer'; export { footer } from './footer';
export { getFooter } from './footer';

View File

@ -1,19 +1,77 @@
import { chat } from './chat'; import { chat } from './chat';
/** Header — CTA unico: ingresso chat live (config in `chat.ts`) */ export type SupportedLocale = 'it' | 'en' | 'th';
export const navigation = {
brandName: 'NexStudio', const navigationByLocale = {
items: [ it: {
{ label: 'Prodotti', href: '/#prodotti' }, brandName: 'NexStudio',
{ label: 'Servizi', href: '/#servizi' }, items: [
{ label: 'Stack', href: '/#stack' }, { label: 'Prodotti', href: '/#prodotti' },
{ label: 'FAQ', href: '/#faq' }, { label: 'Servizi', href: '/#servizi' },
{ label: 'Contattaci', href: '/#contatti' }, { label: 'Stack', href: '/#stack' },
] as const, { label: 'FAQ', href: '/#faq' },
cta: { { label: 'Contattaci', href: '/#contatti' },
label: 'Chattiamo', ] as const,
linkTitle: chat.linkTitle, cta: {
ariaLabel: label: 'Chattiamo',
'Richiedi accesso alla chat NexStudio: stato operatore e chatbot, identificazione con email (link o codice) prima della sessione.', linkTitle: chat.linkTitle,
ariaLabel:
'Richiedi accesso alla chat NexStudio: stato operatore e chatbot, identificazione con email (link o codice) prima della sessione.',
},
labels: {
mainNavAria: 'Principale',
mobileNavAria: 'Navigazione mobile',
languageSelectorAria: 'Seleziona lingua',
openMenuAria: 'Apri menu',
},
},
en: {
brandName: 'NexStudio',
items: [
{ label: 'Products', href: '/#prodotti' },
{ label: 'Services', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
{ label: 'Contact', href: '/#contatti' },
] as const,
cta: {
label: 'Lets chat',
linkTitle: 'Open NexStudio chat access request.',
ariaLabel:
'Request access to NexStudio chat: operator and chatbot availability, email verification link or code before session start.',
},
labels: {
mainNavAria: 'Main navigation',
mobileNavAria: 'Mobile navigation',
languageSelectorAria: 'Select language',
openMenuAria: 'Open menu',
},
},
th: {
brandName: 'NexStudio',
items: [
{ label: 'ผลิตภัณฑ์', href: '/#prodotti' },
{ label: 'บริการ', href: '/#servizi' },
{ label: 'สแตก', href: '/#stack' },
{ label: 'คำถามที่พบบ่อย', href: '/#faq' },
{ label: 'ติดต่อเรา', href: '/#contatti' },
] as const,
cta: {
label: 'เริ่มแชท',
linkTitle: 'เปิดคำขอเข้าใช้งานแชทของ NexStudio',
ariaLabel:
'ขอเข้าใช้งานแชท NexStudio: ตรวจสอบสถานะผู้ดูแลและแชทบอต พร้อมยืนยันตัวตนด้วยอีเมลก่อนเริ่มใช้งาน',
},
labels: {
mainNavAria: 'เมนูหลัก',
mobileNavAria: 'เมนูบนมือถือ',
languageSelectorAria: 'เลือกภาษา',
openMenuAria: 'เปิดเมนู',
},
}, },
} as const; } as const;
export const getNavigation = (locale: SupportedLocale) => navigationByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const navigation = navigationByLocale.it;

View File

@ -2,51 +2,153 @@
* Servizi (griglia bento). * Servizi (griglia bento).
* layout: wide = 2 colonne, tall = 2 righe + elenco puntato * layout: wide = 2 colonne, tall = 2 righe + elenco puntato
*/ */
export const services = { import type { SupportedLocale } from './navigation';
eyebrow: 'I nostri servizi',
title: 'Implementazione, evoluzione e supporto', const servicesByLocale = {
lead: it: {
'In NexStudio, il nostro impegno non si esaurisce con la fornitura della licenza software. Il nostro modello di servizio è progettato per garantire che ogni professionista e struttura possa estrarre il massimo valore dalle potenzialità dellIntelligenza Artificiale integrata in LexAura e MediAura.', eyebrow: 'I nostri servizi',
cards: [ title: 'Implementazione, evoluzione e supporto',
{ lead:
layout: 'tall' as const, 'In NexStudio, il nostro impegno non si esaurisce con la fornitura della licenza software. Il nostro modello di servizio è progettato per garantire che ogni professionista e struttura possa estrarre il massimo valore dalle potenzialità dellIntelligenza Artificiale integrata in LexAura e MediAura.',
/** Sfera geodetica vettoriale (SVG) in basso a destra */ cards: [
decoration: 'wireframe-globe' as const, {
title: 'Onboarding e formazione specialistica', layout: 'tall' as const,
body: decoration: 'wireframe-globe' as const,
'Ladozione di un sistema basato su IA richiede un passaggio metodologico, non solo tecnico. Offriamo programmi di formazione dedicati per team legali e medici, finalizzati a:', title: 'Onboarding e formazione specialistica',
bullets: [
'Ottimizzare linterazione con gli algoritmi di ricerca e sintesi.',
'Padroneggiare i comandi vocali e lautomazione dei flussi documentali.',
'Ridurre drasticamente la curva di apprendimento tecnologico.',
],
},
{
layout: 'wide' as const,
title: 'Supporto Tecnico e Consulenza Operativa',
body:
'Forniamo unassistenza continua e di alto profilo per garantire la costante continuità operativa dei vostri sistemi. NexStudio non agisce come un semplice fornitore di supporto tecnico, ma come un partner strategico che affianca lo studio o la struttura sanitaria nellottimizzazione dei processi interni attraverso luso dei nostri SaaS.',
callout: {
title: 'Intervento «Breaking Glass»',
body: body:
'Per le situazioni di estrema urgenza o criticità sistemica, garantiamo protocolli di accesso rapido e intervento prioritario. Questa procedura di emergenza permette di superare i normali flussi di assistenza per risolvere istantaneamente anomalie che potrebbero impattare sulla continuità del servizio, assicurando che il professionista non resti mai isolato dai propri dati o strumenti decisionali.', 'Ladozione di un sistema basato su IA richiede un passaggio metodologico, non solo tecnico. Offriamo programmi di formazione dedicati per team legali e medici, finalizzati a:',
bullets: [
'Ottimizzare linterazione con gli algoritmi di ricerca e sintesi.',
'Padroneggiare i comandi vocali e lautomazione dei flussi documentali.',
'Ridurre drasticamente la curva di apprendimento tecnologico.',
],
}, },
}, {
{ layout: 'wide' as const,
layout: 'default' as const, title: 'Supporto Tecnico e Consulenza Operativa',
title: 'Estensioni e integrazioni personalizzate', body:
body: 'Forniamo unassistenza continua e di alto profilo per garantire la costante continuità operativa dei vostri sistemi. NexStudio non agisce come un semplice fornitore di supporto tecnico, ma come un partner strategico che affianca lo studio o la struttura sanitaria nellottimizzazione dei processi interni attraverso luso dei nostri SaaS.',
'Sebbene LexAura e MediAura siano prodotti finiti e pronti alluso, comprendiamo che strutture complesse possano avere esigenze di interconnessione specifiche.', callout: {
bullets: [ title: 'Intervento «Breaking Glass»',
'API integration: supportiamo lintegrazione dei nostri SaaS con sistemi gestionali preesistenti.', body:
'Custom modules: progettiamo estensioni su misura che aggiungono funzionalità specifiche allinterno del perimetro dei nostri prodotti, per rispondere a flussi di lavoro unici.', 'Per le situazioni di estrema urgenza o criticità sistemica, garantiamo protocolli di accesso rapido e intervento prioritario. Questa procedura di emergenza permette di superare i normali flussi di assistenza per risolvere istantaneamente anomalie che potrebbero impattare sulla continuità del servizio, assicurando che il professionista non resti mai isolato dai propri dati o strumenti decisionali.',
], },
}, },
{ {
layout: 'default' as const, layout: 'default' as const,
title: 'Aggiornamento e manutenzione evolutiva', title: 'Estensioni e integrazioni personalizzate',
body: body:
'Il mondo dellIA e delle normative (legali e sanitarie) è in costante mutamento. Il nostro servizio include laggiornamento continuo dei motori di analisi, garantendo che il software sia sempre allineato con le ultime tecnologie e le evoluzioni del mercato di riferimento.', 'Sebbene LexAura e MediAura siano prodotti finiti e pronti alluso, comprendiamo che strutture complesse possano avere esigenze di interconnessione specifiche.',
}, bullets: [
], 'API integration: supportiamo lintegrazione dei nostri SaaS con sistemi gestionali preesistenti.',
'Custom modules: progettiamo estensioni su misura che aggiungono funzionalità specifiche allinterno del perimetro dei nostri prodotti, per rispondere a flussi di lavoro unici.',
],
},
{
layout: 'default' as const,
title: 'Aggiornamento e manutenzione evolutiva',
body:
'Il mondo dellIA e delle normative (legali e sanitarie) è in costante mutamento. Il nostro servizio include laggiornamento continuo dei motori di analisi, garantendo che il software sia sempre allineato con le ultime tecnologie e le evoluzioni del mercato di riferimento.',
},
],
},
en: {
eyebrow: 'Our services',
title: 'Implementation, evolution, and support',
lead:
'At NexStudio, our commitment goes beyond software licensing. Our service model helps each professional and organization unlock the full value of AI integrated into LexAura and MediAura.',
cards: [
{
layout: 'tall' as const,
decoration: 'wireframe-globe' as const,
title: 'Onboarding and specialist training',
body:
'Adopting AI-powered systems is a methodological shift, not only a technical one. We provide dedicated programs for legal and medical teams to:',
bullets: [
'Improve interaction with search and summarization algorithms.',
'Master voice commands and document workflow automation.',
'Significantly reduce the technology learning curve.',
],
},
{
layout: 'wide' as const,
title: 'Technical support and operational consulting',
body:
'We provide continuous, high-level assistance to ensure operational continuity. NexStudio is not just a support vendor, but a strategic partner for legal firms and healthcare organizations.',
callout: {
title: '“Breaking Glass” intervention',
body:
'For critical urgency or systemic incidents, we offer rapid-access and priority intervention protocols. This emergency procedure bypasses standard support flows to restore continuity as fast as possible.',
},
},
{
layout: 'default' as const,
title: 'Custom extensions and integrations',
body:
'Although LexAura and MediAura are complete, ready-to-use products, complex organizations may still require specific interoperability needs.',
bullets: [
'API integration: we integrate our SaaS with your existing management systems.',
'Custom modules: we build tailored extensions inside our product perimeter to support unique workflows.',
],
},
{
layout: 'default' as const,
title: 'Evolutionary updates and maintenance',
body:
'AI and regulated frameworks evolve continuously. Our service includes ongoing analysis-engine updates to keep software aligned with new technologies and market requirements.',
},
],
},
th: {
eyebrow: 'บริการของเรา',
title: 'การติดตั้ง การพัฒนา และการสนับสนุน',
lead:
'ที่ NexStudio ความมุ่งมั่นของเราไม่ได้จบแค่การให้สิทธิ์ใช้งานซอฟต์แวร์ โมเดลบริการของเราช่วยให้ทีมงานดึงศักยภาพสูงสุดจาก AI ที่ผสานใน LexAura และ MediAura',
cards: [
{
layout: 'tall' as const,
decoration: 'wireframe-globe' as const,
title: 'การเริ่มใช้งานและการอบรมเฉพาะทาง',
body:
'การนำระบบ AI มาใช้ต้องอาศัยการเปลี่ยนผ่านเชิงวิธีการ ไม่ใช่แค่เทคนิค เรามีโปรแกรมอบรมสำหรับทีมกฎหมายและการแพทย์เพื่อ:',
bullets: [
'เพิ่มประสิทธิภาพการใช้งานอัลกอริทึมค้นหาและสรุปผล',
'ใช้งานคำสั่งเสียงและระบบงานเอกสารอัตโนมัติได้อย่างคล่องตัว',
'ลดช่วงเวลาเรียนรู้เทคโนโลยีอย่างมีนัยสำคัญ',
],
},
{
layout: 'wide' as const,
title: 'สนับสนุนทางเทคนิคและที่ปรึกษาการปฏิบัติงาน',
body:
'เราให้การสนับสนุนอย่างต่อเนื่องเพื่อคงความต่อเนื่องในการดำเนินงาน NexStudio ไม่ได้เป็นเพียงผู้ให้บริการซัพพอร์ต แต่เป็นพาร์ตเนอร์เชิงกลยุทธ์สำหรับองค์กร',
callout: {
title: 'การแทรกแซงแบบ “Breaking Glass”',
body:
'เมื่อเกิดเหตุเร่งด่วนหรือความผิดปกติระดับวิกฤต เรามีโปรโตคอลเข้าถึงด่วนและจัดลำดับความสำคัญสูงสุด เพื่อแก้ปัญหาได้ทันทีและรักษาความต่อเนื่องของบริการ',
},
},
{
layout: 'default' as const,
title: 'ส่วนขยายและการเชื่อมต่อแบบกำหนดเอง',
body:
'แม้ LexAura และ MediAura จะพร้อมใช้งานทันที แต่บางองค์กรอาจต้องการการเชื่อมต่อเฉพาะทางกับระบบเดิม',
bullets: [
'API integration: เชื่อม SaaS ของเราเข้ากับระบบจัดการที่ใช้อยู่แล้ว',
'Custom modules: ออกแบบส่วนขยายเฉพาะงานภายในขอบเขตผลิตภัณฑ์ของเรา',
],
},
{
layout: 'default' as const,
title: 'อัปเดตและบำรุงรักษาเชิงพัฒนา',
body:
'โลกของ AI และข้อกำกับเปลี่ยนแปลงตลอดเวลา บริการของเรารวมถึงการอัปเดตเอนจินวิเคราะห์อย่างต่อเนื่องเพื่อให้ซอฟต์แวร์ทันสมัยและสอดคล้องบริบทตลาด',
},
],
},
} as const; } as const;
export const getServices = (locale: SupportedLocale) => servicesByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const services = servicesByLocale.it;

View File

@ -1,6 +1,25 @@
import type { SupportedLocale } from './navigation';
/** Meta pagina principale — usato da BaseLayout */ /** Meta pagina principale — usato da BaseLayout */
export const siteMeta = { const siteMetaByLocale = {
title: 'NexStudio — Ingegneria del software e IA per settori regolamentati', it: {
description: title: 'NexStudio — Ingegneria del software e IA per settori regolamentati',
'NexStudio: software company con sede a Bangkok. SaaS ad alta complessità, integrazione di IA in flussi professionali, Legal Tech e Health Tech.', description:
'NexStudio: software company con sede a Bangkok. SaaS ad alta complessità, integrazione di IA in flussi professionali, Legal Tech e Health Tech.',
},
en: {
title: 'NexStudio — Software engineering and AI for regulated industries',
description:
'NexStudio is a software company based in Bangkok. We build high-complexity SaaS products and integrate AI into professional workflows across Legal Tech and Health Tech.',
},
th: {
title: 'NexStudio — วิศวกรรมซอฟต์แวร์และ AI สำหรับอุตสาหกรรมที่มีข้อกำกับ',
description:
'NexStudio คือบริษัทซอฟต์แวร์ในกรุงเทพฯ พัฒนา SaaS ความซับซ้อนสูง และผสาน AI เข้ากับเวิร์กโฟลว์ระดับมืออาชีพในสาย Legal Tech และ Health Tech',
},
} as const; } as const;
export const getSiteMeta = (locale: SupportedLocale) => siteMetaByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const siteMeta = siteMetaByLocale.it;

View File

@ -1,9 +1,12 @@
import type { SupportedLocale } from './navigation';
/** Sezione stack — infrastruttura Cloudflare e sicurezza */ /** Sezione stack — infrastruttura Cloudflare e sicurezza */
export const stackSection = { const stackIt = {
eyebrow: 'Lo stack tecnologico', eyebrow: 'Lo stack tecnologico',
title: 'Infrastruttura e sicurezza', title: 'Infrastruttura e sicurezza',
lead: lead:
'In NexStudio, la qualità dellesperienza utente è inscindibile dalla solidità dellinfrastruttura. Per questo motivo ci affidiamo a partner globali ad alta efficienza che garantiscono i più elevati standard di disponibilità e protezione dei dati.', 'In NexStudio, la qualità dellesperienza utente è inscindibile dalla solidità dellinfrastruttura. Per questo motivo ci affidiamo a partner globali ad alta efficienza che garantiscono i più elevati standard di disponibilità e protezione dei dati.',
infrastructureHeading: 'Infrastruttura',
cloudflare: { cloudflare: {
title: 'Ecosistema Cloudflare', title: 'Ecosistema Cloudflare',
intro: intro:
@ -145,3 +148,168 @@ export const stackSection = {
] as const, ] as const,
}, },
} as const; } as const;
const stackByLocale = {
it: stackIt,
en: {
...stackIt,
eyebrow: 'Technology stack',
title: 'Infrastructure and security',
lead:
'At NexStudio, user experience quality is inseparable from infrastructure resilience. That is why we rely on high-efficiency global partners that provide strong availability and data protection standards.',
infrastructureHeading: 'Infrastructure',
cloudflare: {
...stackIt.cloudflare,
title: 'Cloudflare ecosystem',
intro:
'"Cloudflare helps accelerate performance, secure app availability, and optimize web experiences globally."',
points: [
{
title: 'Performance without compromise',
body:
'Access to our services remains low-latency worldwide, ensuring fluid usage even during complex data-processing operations.',
},
{
title: 'Advanced security',
body:
'We apply modern protection layers including end-to-end encryption, DDoS mitigation, and Web Application Firewall controls.',
},
{
title: 'Compliance and data integrity',
body:
'We use geographic data-control capabilities to align legal and medical data processing with local and international privacy requirements.',
},
] as const,
},
continuity: {
title: 'Continuity and scalability',
body:
'We provide professional-grade SLA uptime. Our cloud-native architecture scales instantly for workload peaks, large offices, and multi-site organizations.',
},
whyThisChoice: {
title: 'Why NexStudio',
points: [
'Regulated vertical focus: we design for Legal Tech and Health Tech, where traceability, privacy, and service continuity are core product requirements.',
'AI in real professional workflows: we embed models and automation with attention to accuracy, auditability, and human oversight.',
'End-to-end responsibility: architecture, development, and evolution stay under one ownership model for clearer priorities and delivery.',
] as const,
},
cloudflareBadge: {
...stackIt.cloudflareBadge,
label: 'Cloudflare ecosystem',
slogan: '"Helping build a better Internet."',
},
awsBadge: {
...stackIt.awsBadge,
label: 'AWS ecosystem',
slogan: '"The worlds most comprehensive and broadly adopted cloud platform."',
},
infrastructureCarousel: {
...stackIt.infrastructureCarousel,
ariaLabel: 'NexStudio infrastructure and technology partners',
items: [
{
...stackIt.infrastructureCarousel.items[0],
label: 'Cloudflare ecosystem',
slogan: '"Helping build a better Internet."',
},
{
...stackIt.infrastructureCarousel.items[1],
label: 'AWS ecosystem',
slogan: '"The worlds most comprehensive and broadly adopted cloud platform."',
},
{
...stackIt.infrastructureCarousel.items[2],
label: 'GitHub ecosystem',
},
{
...stackIt.infrastructureCarousel.items[3],
label: 'OpenAI ecosystem',
},
] as const,
},
},
th: {
...stackIt,
eyebrow: 'เทคโนโลยีสแตก',
title: 'โครงสร้างพื้นฐานและความปลอดภัย',
lead:
'ที่ NexStudio คุณภาพประสบการณ์ผู้ใช้แยกไม่ออกจากความมั่นคงของโครงสร้างพื้นฐาน เราจึงเลือกพาร์ตเนอร์ระดับโลกที่มีประสิทธิภาพสูงเพื่อให้ได้มาตรฐานด้านความพร้อมใช้งานและความปลอดภัยของข้อมูล',
infrastructureHeading: 'โครงสร้างพื้นฐาน',
cloudflare: {
...stackIt.cloudflare,
title: 'ระบบนิเวศ Cloudflare',
intro:
'"Cloudflare ช่วยเร่งประสิทธิภาพ รับประกันความพร้อมใช้งานของแอป และยกระดับประสบการณ์เว็บทั่วโลก"',
points: [
{
title: 'ประสิทธิภาพที่ไม่ประนีประนอม',
body:
'เข้าถึงบริการของเราได้อย่างหน่วงต่ำจากทุกที่ทั่วโลก แม้ในงานประมวลผลข้อมูลที่ซับซ้อน',
},
{
title: 'ความปลอดภัยขั้นสูง',
body:
'เราใช้กลไกป้องกันสมัยใหม่ เช่น การเข้ารหัส end-to-end การป้องกัน DDoS และ Web Application Firewall',
},
{
title: 'การปฏิบัติตามข้อกำกับและความถูกต้องของข้อมูล',
body:
'เราใช้ความสามารถในการกำกับตำแหน่งข้อมูลเพื่อให้การจัดการข้อมูลอ่อนไหวด้านกฎหมายและการแพทย์สอดคล้องกับข้อกำกับความเป็นส่วนตัว',
},
] as const,
},
continuity: {
title: 'ความต่อเนื่องและการขยายตัว',
body:
'เรารับประกัน uptime ระดับมืออาชีพตาม SLA และสถาปัตยกรรม cloud-native ที่ขยายได้ทันทีเมื่อมีโหลดสูง',
},
whyThisChoice: {
title: 'ทำไมต้อง NexStudio',
points: [
'เชี่ยวชาญอุตสาหกรรมที่มีข้อกำกับ: Legal Tech และ Health Tech',
'ผสาน AI เข้ากับงานจริง โดยคำนึงถึงความแม่นยำ การตรวจสอบย้อนหลัง และการกำกับโดยมนุษย์',
'รับผิดชอบแบบ end-to-end: สถาปัตยกรรม การพัฒนา และการต่อยอดอยู่ภายใต้ทีมเดียว',
] as const,
},
cloudflareBadge: {
...stackIt.cloudflareBadge,
label: 'ระบบนิเวศ Cloudflare',
slogan: '"ช่วยกันสร้างอินเทอร์เน็ตที่ดีกว่า"',
},
awsBadge: {
...stackIt.awsBadge,
label: 'ระบบนิเวศ AWS',
slogan: '"แพลตฟอร์มคลาวด์ที่ครอบคลุมและได้รับการใช้งานอย่างกว้างขวางที่สุดในโลก"',
},
infrastructureCarousel: {
...stackIt.infrastructureCarousel,
ariaLabel: 'พาร์ตเนอร์โครงสร้างพื้นฐานและเทคโนโลยีของ NexStudio',
items: [
{
...stackIt.infrastructureCarousel.items[0],
label: 'ระบบนิเวศ Cloudflare',
slogan: '"ช่วยกันสร้างอินเทอร์เน็ตที่ดีกว่า"',
},
{
...stackIt.infrastructureCarousel.items[1],
label: 'ระบบนิเวศ AWS',
slogan: '"แพลตฟอร์มคลาวด์ที่ครอบคลุมและได้รับการใช้งานอย่างกว้างขวางที่สุดในโลก"',
},
{
...stackIt.infrastructureCarousel.items[2],
label: 'ระบบนิเวศ GitHub',
},
{
...stackIt.infrastructureCarousel.items[3],
label: 'ระบบนิเวศ OpenAI',
},
] as const,
},
},
} as const;
export const getStackSection = (locale: SupportedLocale) => stackByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const stackSection = stackByLocale.it;

View File

@ -1,6 +1,7 @@
--- ---
import BaseLayout from './BaseLayout.astro'; import BaseLayout from './BaseLayout.astro';
import SiteFooter from '../components/SiteFooter.astro'; import SiteFooter from '../components/SiteFooter.astro';
import { getNavigation } from '../data/home';
interface Props { interface Props {
title: string; title: string;
@ -29,7 +30,8 @@ const printDate = new Intl.DateTimeFormat('it-IT', {
const printSource = Astro.url.href; const printSource = Astro.url.href;
const path = Astro.url.pathname; const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/); const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it'; const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const navigation = getNavigation(currentLocale);
const strippedPath = path.replace(/^\/(en|th)(?=\/|$)/, '') || '/'; const strippedPath = path.replace(/^\/(en|th)(?=\/|$)/, '') || '/';
const localeHref = (locale: 'it' | 'en' | 'th') => { const localeHref = (locale: 'it' | 'en' | 'th') => {
if (locale === 'it') return strippedPath; if (locale === 'it') return strippedPath;
@ -71,7 +73,7 @@ const langMeta = {
<details class="relative" data-lang-dropdown> <details class="relative" data-lang-dropdown>
<summary <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" 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" 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" /> <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> <span>{langMeta[currentLocale].code}</span>
@ -129,7 +131,7 @@ const langMeta = {
</details> </details>
<a <a
class="text-sm font-medium text-sky-400/90 hover:text-sky-300" class="text-sm font-medium text-sky-400/90 hover:text-sky-300"
href={localize('/#contatti')}>Contattaci</a href={localize('/#contatti')}>{navigation.items[4]?.label ?? 'Contattaci'}</a
> >
</div> </div>
</div> </div>

View File

@ -6,21 +6,33 @@ import ContactFormInline from '../components/ContactFormInline.astro';
import CardBottomRightTexture from '../components/CardBottomRightTexture.astro'; import CardBottomRightTexture from '../components/CardBottomRightTexture.astro';
import SiteFooter from '../components/SiteFooter.astro'; import SiteFooter from '../components/SiteFooter.astro';
import { import {
siteMeta, getSiteMeta,
navigation, getNavigation,
hero, getHero,
stats, stats,
services, getServices,
stackSection, getStackSection,
productsSection, productsSection,
faq, getFaq,
cta, getCta,
} from '../data/home'; } from '../data/home';
const infrastructureCarouselItems = stackSection.infrastructureCarousel.items; const infrastructureCarouselItems = stackSection.infrastructureCarousel.items;
const path = Astro.url.pathname; const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/); const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it'; const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const navigation = getNavigation(currentLocale);
const siteMeta = getSiteMeta(currentLocale);
const hero = getHero(currentLocale);
const services = getServices(currentLocale);
const stackSection = getStackSection(currentLocale);
const faq = getFaq(currentLocale);
const cta = getCta(currentLocale);
const pageLabels = {
it: { focus: 'Focus' },
en: { focus: 'Focus' },
th: { focus: 'จุดเน้น' },
}[currentLocale];
const localize = (href: string) => { const localize = (href: string) => {
if (currentLocale === 'it') return href; if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href; if (href.startsWith(`/${currentLocale}`)) return href;
@ -64,7 +76,7 @@ const langMeta = {
<nav <nav
class="hidden items-center gap-8 text-sm md:flex" class="hidden items-center gap-8 text-sm md:flex"
aria-label="Principale" aria-label={navigation.labels.mainNavAria}
> >
{navigation.items.map((item) => { {navigation.items.map((item) => {
const sectionId = item.href.includes('#') const sectionId = item.href.includes('#')
@ -87,7 +99,7 @@ const langMeta = {
<details class="relative hidden sm:block" data-lang-dropdown> <details class="relative hidden sm:block" data-lang-dropdown>
<summary <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" 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" 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" /> <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> <span>{langMeta[currentLocale].code}</span>
@ -157,7 +169,7 @@ const langMeta = {
@click="mobile = !mobile" @click="mobile = !mobile"
:aria-expanded="mobile" :aria-expanded="mobile"
aria-controls="mobile-nav" aria-controls="mobile-nav"
aria-label="Apri menu" aria-label={navigation.labels.openMenuAria}
> >
<svg <svg
x-show="!mobile" x-show="!mobile"
@ -191,7 +203,7 @@ const langMeta = {
x-cloak x-cloak
class="border-b border-nx-border bg-black px-4 py-4 md:hidden" class="border-b border-nx-border bg-black px-4 py-4 md:hidden"
> >
<nav class="flex flex-col gap-3 text-sm" aria-label="Mobile"> <nav class="flex flex-col gap-3 text-sm" aria-label={navigation.labels.mobileNavAria}>
{navigation.items.map((item) => { {navigation.items.map((item) => {
const sectionId = item.href.includes('#') const sectionId = item.href.includes('#')
? item.href.split('#').pop()! ? item.href.split('#').pop()!
@ -356,7 +368,7 @@ const langMeta = {
pi === 0 ? 'text-sky-200/85' : 'text-emerald-200/85', pi === 0 ? 'text-sky-200/85' : 'text-emerald-200/85',
]} ]}
> >
<span class="text-nx-muted font-normal">Focus — </span> <span class="text-nx-muted font-normal">{pageLabels.focus} — </span>
{product.focus} {product.focus}
</p> </p>
<p class="mt-5 text-sm leading-relaxed text-nx-muted sm:text-base"> <p class="mt-5 text-sm leading-relaxed text-nx-muted sm:text-base">
@ -497,7 +509,7 @@ const langMeta = {
class="relative z-[1] mt-12 rounded-2xl border border-orange-500/20 bg-gradient-to-br from-orange-950/20 to-nx-elevated/40 p-6 sm:p-8" class="relative z-[1] mt-12 rounded-2xl border border-orange-500/20 bg-gradient-to-br from-orange-950/20 to-nx-elevated/40 p-6 sm:p-8"
> >
<p class="text-xs font-medium uppercase tracking-wider text-orange-200/90"> <p class="text-xs font-medium uppercase tracking-wider text-orange-200/90">
Infrastruttura {stackSection.infrastructureHeading}
</p> </p>
<div <div
class="nx-infra-carousel mt-5" class="nx-infra-carousel mt-5"

View File

@ -11,6 +11,15 @@ interface ContactFormCfg {
fieldDepartment: string; fieldDepartment: string;
fieldEmail: string; fieldEmail: string;
fieldMessage: string; fieldMessage: string;
turnstileError: string;
configError: string;
mailtoFallbackSubject: string;
mailtoLabelName: string;
mailtoLabelCompany: string;
mailtoLabelEmail: string;
mailtoLabelCountry: string;
mailtoLabelDepartment: string;
mailtoDefaultDepartment: string;
} }
function readCfg(): ContactFormCfg { function readCfg(): ContactFormCfg {
@ -29,6 +38,15 @@ function readCfg(): ContactFormCfg {
fieldDepartment: 'department', fieldDepartment: 'department',
fieldEmail: 'email', fieldEmail: 'email',
fieldMessage: 'message', fieldMessage: 'message',
turnstileError: 'Complete anti-spam verification before sending.',
configError: 'Contact form endpoint is not configured.',
mailtoFallbackSubject: 'Website contact',
mailtoLabelName: 'Name',
mailtoLabelCompany: 'Company',
mailtoLabelEmail: 'Email',
mailtoLabelCountry: 'Country',
mailtoLabelDepartment: 'Department',
mailtoDefaultDepartment: 'general',
}; };
} }
return JSON.parse(raw) as ContactFormCfg; return JSON.parse(raw) as ContactFormCfg;
@ -52,8 +70,7 @@ function bind(): void {
if (!token) { if (!token) {
e.preventDefault(); e.preventDefault();
if (err) { if (err) {
err.textContent = err.textContent = cfg.turnstileError;
'Completa il controllo anti-spam prima di inviare il messaggio.';
err.classList.remove('hidden'); err.classList.remove('hidden');
} }
return; return;
@ -72,17 +89,20 @@ function bind(): void {
const department = String(fd.get(cfg.fieldDepartment) ?? '').trim(); const department = String(fd.get(cfg.fieldDepartment) ?? '').trim();
const message = String(fd.get(cfg.fieldMessage) ?? '').trim(); const message = String(fd.get(cfg.fieldMessage) ?? '').trim();
if (cfg.mailto) { if (cfg.mailto) {
const subject = encodeURIComponent(cfg.notifySubject || 'Contatto sito'); const subject = encodeURIComponent(
const companyLine = company ? `\nAzienda: ${company}` : ''; cfg.notifySubject || cfg.mailtoFallbackSubject || 'Website contact',
);
const companyLine = company
? `\n${cfg.mailtoLabelCompany}: ${company}`
: '';
const body = encodeURIComponent( const body = encodeURIComponent(
`Nome: ${name}${companyLine}\nEmail: ${email}\nPaese: ${country}\nReparto: ${department || 'general'}\n\n${message}`, `${cfg.mailtoLabelName}: ${name}${companyLine}\n${cfg.mailtoLabelEmail}: ${email}\n${cfg.mailtoLabelCountry}: ${country}\n${cfg.mailtoLabelDepartment}: ${department || cfg.mailtoDefaultDepartment}\n\n${message}`,
); );
window.location.href = `mailto:${cfg.mailto}?subject=${subject}&body=${body}`; window.location.href = `mailto:${cfg.mailto}?subject=${subject}&body=${body}`;
return; return;
} }
if (err) { if (err) {
err.textContent = err.textContent = cfg.configError;
'Configura `contactForm.actionUrl` (Formspree) o `contactForm.mailto` in `src/data/home/cta.ts`.';
err.classList.remove('hidden'); err.classList.remove('hidden');
} }
}); });