foundation

Multilingua base (it/en)

Sito/app bilingue IT/EN con file di traduzione Laravel, URL localizzati (/it/... e /en/...), language switcher, hreflang SEO, detect lingua browser, fallback IT, helper __() e Blade @lang ovunque — pronto a scalare ad altre lingue (es/fr/de) aggiungendo solo un file.

Cosa fa questo modulo

Modulo «Multilingua base (it/en)»: scheda inventario dello scaffolding fondazionale di internazionalizzazione che trasforma un sito vetrina o un'app Laravel da single-language IT a bilingue IT/EN production-ready, con architettura pulita pronta a scalare ad altre lingue (es/fr/de/pt/de-CH/русский) aggiungendo solo nuovi file di traduzione e abilitando la lingua in config — senza dover toccare controller, view o routing. **Componenti** del modulo: (1) **File traduzione Laravel** organizzati in `lang/{locale}/` (Laravel 10+ flat) o `resources/lang/{locale}/` (Laravel 9 e precedenti — questo repo footility.com usa proprio `resources/lang/{it,en}/` con file `messages.php`, `auth.php`, `passwords.php`, `validation.php` come fonte vivente del pattern), un file `.php` per modulo funzionale (`messages.php` per copy generale sito, `auth.php` per messaggi login/registrazione/reset, `validation.php` per messaggi errori form Laravel built-in, `passwords.php` per reset password flow, + file custom per dominio cliente: `booking.php`, `shop.php`, `dashboard.php`, `emails.php`, `seo.php`, `legal.php`), struttura array nested con dot-notation key (`messages.welcome`, `messages.cta.book_now`, `messages.footer.privacy_link`) per organizzare gerarchicamente, supporto placeholder named (`:name`, `:count`) e plurali via `trans_choice()` con regola ICU (`{0} nessun risultato|{1} un risultato|[2,*] :count risultati`); opzionale formato JSON `lang/{locale}.json` (Laravel 6+) per traduzioni inline lunghe da view (`__("Benvenuto nella nostra struttura, prenota ora!")` come chiave naturale che fallback su sé stessa se mancante) — pattern utile per copy marketing che cambia spesso, evitando hardcoded testo italiano in view; sincronizzazione `it.json ⟷ en.json` via comando custom `php artisan lang:scan-missing` che fa diff chiavi e segnala mancanze. (2) **URL localizzati** con prefisso lingua nel path (`/it/contatti`, `/en/contact`, `/it/prenota`, `/en/book`) implementato via route group con middleware `SetLocale` che intercetta segmento URL: `Route::prefix('{locale}')->where(['locale' => 'it|en'])->middleware('set.locale')->group(function(){...routes...})`; alternativa più sofisticata con **slug tradotti per pagina** (es. `/it/chi-siamo` e `/en/about-us` non solo lingua diversa ma slug semantico SEO-friendly nella lingua target) tramite pacchetto `mcamara/laravel-localization` o `tightenco/ziggy` + tabella `page_translations` (page_id, locale, slug, title, meta_description, meta_keywords, og_image, content_blocks JSON per CMS-driven) o file route per lingua (`routes/web-it.php` + `routes/web-en.php` con slug nativi); pattern alternativo con **subdomain per lingua** (`it.cliente.it`, `en.cliente.it`) per casi enterprise dove SEO autorità di dominio per lingua è prioritaria — meno comune per siti vetrina cliente workspace, scelta default = prefisso path. (3) **Middleware `SetLocale`**: pipeline `EnsureValidLocale` (whitelist locale da `config/app.php` `supported_locales => ['it','en']`, redirect a default IT se locale invalida in URL), `ApplyLocale` (`App::setLocale($locale)` + persisti scelta in session `Session::put('locale', $locale)` + cookie 1 anno `Cookie::queue('locale', $locale, 525600)` per ricordare preferenza utente cross-session), `SetCarbonLocale` (`Carbon::setLocale($locale)` per date format IT/EN automatico), opzionale `SetMoneyLocale` (formato valuta con `NumberFormatter` PHP intl), gestione `Accept-Language` HTTP header come fallback se utente arriva su root `/` senza prefisso (auto-detect: parser `Accept-Language: en-US,en;q=0.9,it;q=0.8` → primo locale supportato wins → redirect 302 a `/{locale}/`); rispetto preferenza utente persistita (cookie/session vince su browser detect se diversi). (4) **Language switcher** componente Blade riusabile `<x-lang-switcher variant="dropdown|inline|flag-only|button-group" :show-flags="true" :show-native-name="true" />` che renderizza dropdown/lista con bandiere SVG (🇮🇹/🇬🇧 inline SVG no emoji-fonts dependency) + nome lingua nativa (`Italiano` / `English` non `Inglese`/`Italian` — UX research dice gli utenti riconoscono meglio il proprio nome nativo), link a versione tradotta della stessa pagina corrente (logic: `route(Route::currentRouteName(), array_merge(request()->route()->parameters(), ['locale' => $targetLocale]))`), preserva querystring/anchor, attivo evidenziato con badge `aria-current="true"`, accessibile keyboard nav (arrow keys + enter), responsive mobile-first (dropdown su mobile, inline su desktop). (5) **Hreflang SEO**: layout Blade master include automatico `<link rel="alternate" hreflang="it" href="https://cliente.it/it/pagina">`, `<link rel="alternate" hreflang="en" href="https://cliente.it/en/page">`, `<link rel="alternate" hreflang="x-default" href="https://cliente.it/it/pagina">` per default fallback (best practice Google: x-default punta alla lingua principale del business per fallback motori di ricerca quando lingua utente non matcha), generati da helper `@hreflangTags()` o componente `<x-seo.hreflang />` che legge route corrente + locale supportate + URL canonici per ogni lingua dalla tabella `page_translations` o convenzione path; sitemap XML multilingua via `spatie/laravel-sitemap` con metodo `addAlternateUrls()` per ogni URL → genera `<xhtml:link rel="alternate" hreflang="it" href="..."/>` nel sitemap per indicizzazione Google corretta. (6) **`<html lang="{{ app()->getLocale() }}">`** nel layout master per accessibility (screen reader pronuncia testo nella lingua corretta), `<meta http-equiv="content-language" content="{{ app()->getLocale() }}">` per browser legacy, direzione testo `dir="ltr"` (preparato a `dir="rtl"` se in roadmap arabo/ebraico). (7) **Helper e convenzioni codice**: usare sempre `__('messages.welcome')` nelle view (no testo hardcoded italiano), `@lang('messages.welcome')` shorthand Blade, `trans_choice('messages.results', $count)` per plurali, `Lang::has('key', $locale)` per check esistenza traduzione prima di renderizzare fallback, controller usano `app()->getLocale()` per logica condizionale rara (es. scelta provider mail con template diverso per lingua), Eloquent model con campi traducibili via pacchetto `spatie/laravel-translatable` (tabella con colonne JSON: `title => {"it":"Titolo","en":"Title"}` accessibile via accessor `$model->title` che ritorna lingua corrente automaticamente, `$model->setTranslation('title', 'en', 'New title')` per setter localizzato) — utile per contenuti CMS-driven (articoli blog, schede prodotto, pagine statiche editabili da admin in entrambe le lingue), backoffice CRUD con tab IT/EN per ogni campo localizzato (UI Livewire con switcher tab inline + indicatore «traduzione mancante» badge giallo se campo EN vuoto mentre IT compilato). (8) **Validazione form localizzata**: messaggi errore validation Laravel automatici in lingua corrente (vendor lang publish `resources/lang/{it,en}/validation.php` con tutti i `required`, `email`, `min`, `max`, etc. tradotti — pattern già presente in questo repo), attribute names localizzati (`attributes => ['email' => 'indirizzo email', 'name' => 'nome']` per messaggi naturali tipo «Il campo indirizzo email è obbligatorio» invece di «Il campo email è obbligatorio»). (9) **Mail e notifiche localizzate**: template mail (#8331) con view per lingua (`emails.welcome.it.blade.php` + `emails.welcome.en.blade.php`) o singola view con `__()` per ogni stringa, scelta lingua mail da preferenza utente persistita (campo `users.locale` enum `it|en` + getter) o da contesto (lead da `/en/contact` → mail conferma in EN), Subject mail localizzato (`->subject(__('emails.welcome.subject'))`), Carbon date format in lingua mail (`->format(__('date.format_long'))`). (10) **Slug e URL semantici tradotti** (avanzato): tabella `route_translations` (`route_name`, `locale`, `slug`, `parameters JSON`) per gestire URL diversi per stessa pagina logica (`/it/chi-siamo` ↔ `/en/about-us`), helper `localized_route('pages.about', ['locale' => 'en'])` che lookup slug tradotto invece di concatenare prefisso; alternativa con `mcamara/laravel-localization` pacchetto Composer maturo che fornisce out-of-the-box: middleware locale, route groups con slug per lingua, hreflang generation, localized URL helper, alternate URL building. (11) **CMS-driven content multilingua**: per contenuti editabili (pagine `chi-siamo`, articoli blog, schede prodotto/servizio, FAQ, testimonianze) tabella `page_translations`/`article_translations`/`product_translations` con campo `locale` + tutti i campi testuali tradotti (`title`, `slug`, `excerpt`, `body_html`, `meta_title`, `meta_description`, `og_image_alt`); admin UI con tab IT/EN per ogni campo + indicatore completamento traduzione + auto-suggest traduzione mancante via API LLM (Claude/GPT translate IT → EN con tone of voice cliente, draft per revisione admin invece di publish automatico — preserva qualità copy professionale). (12) **Detect lingua intelligente al primo accesso**: utente arriva su `/` root senza prefisso → middleware `DetectLocale` ordina sorgenti per priorità: (a) cookie `locale` persistito sessione precedente, (b) preferenza utente loggato da `users.locale`, (c) header `Accept-Language` browser parsed via `Locale::acceptFromHttp()` + intersezione con `supported_locales`, (d) GeoIP IP-based fallback (utente da .it → IT, altri → EN — opzionale per evitare dipendenza GeoIP lib se non già attiva), (e) default config `app.locale` come ultima rete; redirect 302 a `/{detected_locale}/`. (13) **SEO meta tags localizzati**: `<title>`, `<meta description>`, `<meta keywords>`, Open Graph (`og:title`, `og:description`, `og:locale` = `it_IT` o `en_US`, `og:locale:alternate` per altra lingua), Twitter Card, JSON-LD schema.org `inLanguage` property — tutti da file lang o tabella `page_translations` con fallback chain. (14) **Static assets localizzati**: immagini con testo (banner hero, infografiche, badge) con file separato per lingua in `public/img/{locale}/hero.jpg`, video con sottotitoli WebVTT multi-lingua (`<track kind="subtitles" srclang="it" src="video.it.vtt">`), PDF allegati (es. brochure, listino) con versione lingua scaricabile da pagine localizzate. (15) **Comandi artisan**: `php artisan lang:add {locale}` (scaffolda struttura `resources/lang/{locale}/` clonando file da default + segna stringhe da tradurre), `php artisan lang:scan-missing {--locale=}` (parser delle view + controller per estrarre tutte le chiavi `__()`/`@lang()`/`trans_choice()` e diff vs file lang esistenti, lista chiavi mancanti per lingua), `php artisan lang:sync-keys` (allinea chiavi tra file lang IT/EN segnalando orfane in una lingua ma non nell'altra), `php artisan lang:translate-missing --locale=en --provider=claude` (chiama LLM per generare draft traduzione delle chiavi mancanti in lingua target con tone of voice config — output va in file `lang/{locale}/*.draft.php` per revisione admin prima di merge), `php artisan lang:audit-hardcoded` (grep su view Blade per testo italiano hardcoded non passato per `__()` — output warning con file:line per refactor sistematico). (16) **Performance**: cache traduzioni precaricate via service provider con `Cache::rememberForever('translations.{locale}', fn() => Lang::get('*', [], $locale))`, invalidazione cache su deploy via `php artisan lang:cache-clear`, opzionale pre-compile JSON traduzioni statiche servite via CDN per lingua. **Stato attuale repo footility.com**: già presente scaffolding base con `config/app.php` `locale=it`, directory `resources/lang/{it,en}/` con file `auth.php`+`passwords.php`+`validation.php`+`messages.php` (IT) e `auth.php`+`passwords.php`+`validation.php`+`pagination.php` (EN) — questo modulo formalizza e completa il pattern come prodotto riusabile per nuovi siti cliente workspace, partendo dal pattern già in produzione. **Modello dati** per CMS multilingua: tabella `page_translations` (id, page_id FK, locale enum, slug unique per locale, title, meta_title, meta_description, meta_keywords, og_image_url, og_image_alt, content_html, content_blocks_json per builder, status enum `draft`/`published`/`needs_review` per workflow editoriale, translated_by FK user, translated_at, published_at), tabella `article_translations`/`product_translations` analoghe per altri contenuti CMS-driven, indici composti `(page_id, locale)` unique + `(locale, slug)` unique per lookup veloce; tabella opzionale `translation_memory` per riusare traduzioni già fatte (segment hash IT → segment EN approvato, suggerimento auto nel CMS quando admin traduce stringa simile). **Configurazione**: `config/app.php` `locale => 'it'`, `fallback_locale => 'it'`, custom `supported_locales => ['it' => ['name' => 'Italiano', 'native' => 'Italiano', 'flag' => '🇮🇹', 'iso' => 'it_IT'], 'en' => ['name' => 'Inglese', 'native' => 'English', 'flag' => '🇬🇧', 'iso' => 'en_US']]`, `auto_detect_browser_locale => true`, `persist_locale_in_cookie => true`, `redirect_root_to_detected => true`. **Differenze** e relazioni con altri moduli: **#8311 SEO meta dinamici + Open Graph** è dipendenza naturale — il modulo SEO consuma traduzioni meta_title/meta_description dalle tabelle `page_translations` ed emette tag OG localizzati con `og:locale` + `og:locale:alternate`; **#8312 multilingua-base** fornisce l'infrastruttura su cui SEO opera; **#8329 importer CSV** può importare traduzioni bulk da spreadsheet IT/EN forniti dal cliente (mapping colonne → tabella `*_translations`); **#8310 cookie banner GDPR** ha proprio testo banner localizzato (lingua banner = lingua corrente utente, lista cookie con descrizione tradotta); **#8327 datatable CRUD** ha admin UI con tab IT/EN per editing record traducibili (integrazione spatie/laravel-translatable); **#8331 mail transazionali template** ha versione mail per lingua (welcome.it.blade + welcome.en.blade); **#8332 SMS gateway** ha template SMS per lingua (limite 160 char rispettato per entrambe); **#8313 form contatti antispam** ha messaggi errore validation + thank-you page localizzati; **#8324 prenotazione caparra Stripe** ha email conferma pagamento in lingua scelta da customer durante checkout (campo `bookings.locale` salvato); **#8326 area cliente** ha intera UI tradotta + preference `users.locale` editabile da profilo; **#6870 sistema multitenant Footility** può estendere a multilingua per cliente (es. cliente A solo IT, cliente B IT+EN+FR — ogni tenant abilita lingue da admin settings). **Casi d'uso workspace**: **Footility.com** (sito vetrina IT+EN per attrarre clienti workspace internazionali — IT mercato primario PMI italiane, EN per startup europee/USA che cercano agenzia AI dev — pagine `/contatti`+`/contact`, `/servizi`+`/services`, `/book` già esistente, blog articoli tech in entrambe), **Holiday Self Drive** (caso d'uso ad alto volume EN dato il mercato turistico self-drive Namibia/Africa = clientela internazionale → EN deve essere first-class non afterthought, possibili lingue extra DE/NL per mercati primari noleggio camper outdoor, schede veicolo + pagine destinazioni con `page_translations` per slug semantici `/en/destinations/etosha-national-park` vs `/it/destinazioni/parco-nazionale-etosha`), **Klabhouse** (corsi formazione con descrizione corso multilingua per espansione mercato europeo non solo italiano — IT primario, EN per studenti internazionali in formazione tech), **gestionali B2B** (Casarile, Altramusica, LB advisory: principalmente IT-only oggi ma utile dashboard EN per clienti con dipendenti non italofoni o per partner internazionali — opzionale on-demand cliente), **the-body-code/realpilates** (centri fitness in zone turistiche → schede corsi in EN per clienti expat/turisti, prenotazione lezione prova bilingue), **mscarichi** (preventivi servizi grandi volumi → IT-only oggi, ma scalabile a EN per clienti b2b multinazionali con sede italiana ma management estero), **prontointervento** (IT-only per natura locale del servizio emergenza — non in scope multilingua). **Estensioni roadmap**: AI-powered traduzione automatica con tone-of-voice cliente (Claude prompt con esempi brand voice → draft traduzioni di alta qualità da revisionare), translation memory cross-progetto del workspace (riuso traduzioni già fatte tra siti cliente Footility), supporto RTL (arabo/ebraico) con CSS logical properties + flip layout, supporto lingue regionali (it-CH, en-GB vs en-US, de-CH vs de-DE) per nuance copy localizzato, integrazione con servizi traduzione professionale (Crowdin, Lokalise, POEditor) via webhook export/import file lang, glossario terminologico per coerenza traduzione di termini chiave (es. «prenotazione» → «booking» sempre, mai «reservation»), A/B test copy per lingua per ottimizzare conversion per mercato. **Performance**: file lang precaricati in OPcache (Laravel boot legge tutti file `lang/{locale}/*.php` una volta per request, OPcache mantiene compiled), traduzioni JSON pre-bundled in JS per stringhe client-side (helper `Lang::all()` esposto a JS via Blade `<script>window.translations = @json(__('js'));</script>`), cache hreflang tags per pagina (TTL lungo, invalidazione su deploy), nessun overhead misurabile su Core Web Vitals. **GDPR e legale**: privacy policy + cookie banner + informative legali devono esistere in entrambe le lingue (obbligo normativo se sito accetta utenti EU non italofoni), preference center lingua nel cookie banner stesso (`Italiano | English`), termini servizio con clausola lingua prevalente in caso di contenzioso (default «in caso di discrepanza tra versione italiana e versione inglese, prevale la versione italiana» o viceversa secondo giurisdizione cliente). **Accessibilità WCAG 2.1**: `<html lang>` corretto, `lang` attribute su elementi con testo lingua diversa dal documento (es. citazione in inglese in pagina italiana: `<blockquote lang="en">`), language switcher con `aria-label="Cambia lingua"`, etichette form tradotte coerenti, screen reader test pronuncia corretta per entrambe le lingue. **Costo nuovo modulo** (greenfield setup IT/EN su sito esistente IT-only): 0.5g middleware `SetLocale` + `DetectLocale` + route group `{locale}` prefix + config `supported_locales`, 0.5g componente Blade `<x-lang-switcher>` con varianti UI + accessibilità + responsive, 0.5g layout master `<html lang>` + hreflang component + meta OG localizzati + sitemap multilingua via spatie, 1g audit + refactor view esistenti per sostituire testo italiano hardcoded con `__()`/`@lang()` (lavoro proporzionale a # view — sito vetrina 10-20 pagine = 1g, sito complesso 50+ pagine = 2-3g), 1g traduzione iniziale stringhe IT → EN (può essere LLM draft + revisione umana, lavoro proporzionale al volume copy: tipico sito vetrina 500-1500 stringhe = 0.5-1g LLM + 0.5g revisione), 0.5g file lang `validation.php`/`auth.php`/`passwords.php` da vendor publish + custom domain-specific (`booking.php`, `shop.php`), 0.5g model traducibili via spatie/laravel-translatable + migration colonne JSON per contenuti CMS-driven + admin UI tab IT/EN, 0.5g mail template versione EN + scelta lingua da preferenza utente, 0.5g comandi artisan `lang:scan-missing` + `lang:audit-hardcoded` + `lang:translate-missing` con LLM provider, 0.5g testing E2E (switch lingua persistito, redirect root con detect browser, validation form EN messaggi corretti, hreflang Google Search Console validation, mail conferma in lingua giusta). **Costo riuso modulo per nuovo cliente** (modulo già strutturato in agency template): 0.25g abilitazione lingue + config `supported_locales`, 0.25g traduzione stringhe custom del cliente (LLM draft + revisione copy), 0.25g aggiunta route localizzate cliente-specific (es. `/en/products` mapping su pagine vendita), 0.25g test E2E switch lingua + hreflang + mail localizzate. **Aggiungere lingua extra** (es. spagnolo/francese/tedesco a sito già IT+EN): 0.5g traduzione stringhe IT → ES/FR/DE via LLM + revisione, 0.1g config `supported_locales` aggiunta entry, 0.1g test smoke (switch funziona, hreflang corretti, validation messages). **Dipendenze**: Laravel 10/11 core (i18n out-of-the-box: `Lang` facade, `__()`, `@lang`, `trans_choice`, `App::setLocale`), opz. `mcamara/laravel-localization` (pacchetto maturo per route prefix + slug tradotti + hreflang helper — alternativa a implementazione custom), opz. `spatie/laravel-translatable` per Eloquent model con colonne JSON traducibili (raccomandato per CMS-driven content), opz. `spatie/laravel-sitemap` per sitemap multilingua con `alternateUrls()`, opz. `astrotomic/laravel-translatable` alternativa a spatie (relazione 1-to-many a tabella `*_translations` invece di JSON — preferibile per ricerca full-text per lingua), opz. GeoIP lib (`geoip2/geoip2` o `torann/geoip`) per detect lingua da IP fallback, opz. provider LLM per draft traduzioni (Claude/GPT via routing Footility), opz. servizi traduzione professionale (Crowdin/Lokalise/POEditor) se cliente ha budget per traduttore umano nativo, vendor lang publish Laravel per messaggi validation/auth/passwords pre-tradotti, font webfont con supporto charset esteso (latin-ext per ES/PT, cyrillic per RU, etc.), #8311 SEO meta dinamici come consumer naturale, #8310 cookie banner GDPR localizzato, #8327 datatable per admin CRUD record traducibili, #8331 mail transazionali con versione per lingua.

Esempi d'uso

  • foundation