# Rifacimento nomadskiguide.com: note interne di progetto

Data: 18 settembre 2026. Stato: approvato da Paolo in sessione, base per il preventivo (docx in francese) e per il piano di implementazione.

## Contesto

Sito attuale: WordPress 6.9.8 + Jupiter X + Elementor, 30 plugin, su IONOS condiviso (Apache, PHP 8.2, MySQL 5.7). Audit completo in `report/index.html` (bilingue FR/IT) con dati grezzi in `report/data/`. In sintesi: home da 7,5 MB e Lighthouse mobile 55, menu con 17 voci su 26 in redirect a catena, 4 link 404 su ogni pagina, 20 tour duplicati (portfolio + pagina), 489 clic Google in 12 mesi, tracker prima del consenso, 25 plugin da aggiornare, 24 GB sul server.

Il cliente (Fabien Artero, guida alpina UIAGM, Briançon) legge il francese. Paolo l'italiano.

## Decisioni prese

| Tema | Decisione |
|---|---|
| Hosting | Cloudflare Workers (sito + CMS), D1 (SQLite), R2 (immagini). Dominio e caselle email restano su IONOS: solo cambio DNS A/CNAME, MX invariati. |
| Stack | Astro in modalità server (adapter Cloudflare), un solo progetto per sito pubblico e admin. Login JWT in cookie httpOnly. Niente rebuild al salvataggio. |
| Lingue | FR alla radice con gli URL attuali invariati. IT, EN, DE, NL con prefisso `/it/`, `/en/`, `/de/`, `/nl/` e slug tradotti. Hreflang ovunque. Campo vuoto in una lingua = fallback al francese. |
| Traduzioni | Pagine statiche, interfaccia, 20 tour e 107 articoli tradotti da me in migrazione. Nel CMS pulsante "Traduire" (API Claude) che genera le 4 lingue dal francese per tour e articoli nuovi; il cliente rilegge. |
| Foto | Solo le immagini referenziate dalle 184 pagine (~1.500 file), scaricate via HTTP, ridimensionate (max 2000 px, WebP/AVIF, più misure), su R2. Il CMS ridimensiona i nuovi upload. Gli originali restano al cliente. |
| Form | Contatto generico + richiesta prenotazione precompilata dal tour. Turnstile invisibile. Email a contact@nomadskiguide.com via Cloudflare Email Service (o Resend), copia in D1 visibile nel CMS. |
| Commenti blog | Non migrati. |
| Prezzo | Forfait per blocco, senza giornate visibili. Subtotale 2.600 €, sconto 600 €, netto 2.000 €. Acconto 30% (600 €), saldo 70% (1.400 €). Tariffa nominale interna 130 €/gg su 30 gg. |
| Formato preventivo | Docx in francese con segnaposto `[À COMPLÉTER]` per numero, data, dati fiscali. |

## Architettura

### Modello dati (D1)

- `tours`: id, slug_fr, category (voyage | raid | formation), area (alpes_fr | alpes_it | norvege | groenland | japon | emergents), hero_image, in_programme (bool), sort, published.
- `tour_i18n`: tour_id, lang, slug, title, subtitle, summary (budget, niveau, duree, encadrement, groupe, hebergement), description (markdown), highlights (json), price_text, included (json), not_included (json), dates_text, meta_title, meta_description.
- `tour_images`: tour_id, r2_key, alt (per lingua in `image_i18n`), sort.
- `posts`: id, slug_fr, cover_image, published_at, category_ids, published.
- `post_i18n`: post_id, lang, slug, title, body (markdown), excerpt, meta_title, meta_description.
- `post_images` (galleria in fondo), `categories`, `category_i18n`.
- `pages_i18n`: pagine statiche gestite da noi, testo in file di contenuto Astro (non in DB), tradotte a mano.
- `requests`: form ricevuti (tipo, tour_id, nome, email, telefono, date, persone, messaggio, lingua, created_at).
- `users`: uno o due admin, password hash (argon2/bcrypt via WebCrypto o lib), no registrazione.
- `redirects`: from, to, code. Tabella importata dal crawl, modificabile dal CMS.

### Routing pubblico

- `/` home FR; `/it/`, `/en/`, `/de/`, `/nl/` home tradotte.
- Tour FR: URL attuali (`/destination-ski-randonnee-japon/`, `/raid-ski-mercantour/`, `/formation-neige-avalanche/`). Altre lingue: `/en/ski-touring-japan/` ecc.
- Indici: `/destinations-ski-randonnee-monde/`, `/destinations-ski-randonnee-alpes-francaises/`, `/destinations-ski-randonnee-alpes-italiennes/`, `/destinations-ski-randonnee-norvege/`, `/destinations-emergentes-ski-randonnee/`, `/raids-ski/`, `/formations-ski-de-randonnee/`, generati dai dati.
- `/programme/` generata dai tour con `in_programme`.
- Blog: `/blog/`, articoli con slug attuali alla radice FR; `/blog/categorie/<slug>/` per le categorie (301 da `/category/*`).
- Statiche: `/contact-guide-haute-montagne/`, `/faq-ski-randonnee/`, `/guide-expertise/`, `/fabien-artero-guide-haute-montagne/`, `/partenaires/`, `/conditions-generales-de-vente/`, `/thank-you/`.
- `/sitemap-index.xml` con una sitemap per lingua, `/robots.txt`, `/llms.txt` generato.

### Redirect (301)

Tutti importati dal crawl (`report/data/crawl.json` e `links.json`):
- `/portfolio/<slug>/` → tour FR (20).
- `/index.php/<qualsiasi>` → destinazione finale senza catena.
- `/YYYY/MM/DD/<slug>/` → `/<slug>/` (permalink datati, oggi 404).
- `/author/*` → `/fabien-artero-guide-haute-montagne/` (oggi va in home e perde il traffico).
- `/tag/*` → `/blog/`.
- `/category/<slug>/` → `/blog/categorie/<slug>/`.
- `/portfolio-category/*` → indici destinazioni.
- Vecchi alias: `/contact-us/`, `/projet/`, `/monde/`, `/voyages-alpes/`, `/pays-des-ecrins/`, `/norvege-edition-2026/`.
- Pagina 404 vera con link utili.

### CMS (`/admin`)

- Login (email + password), JWT in cookie httpOnly SameSite=Strict, scadenza 7 giorni, rate limit sul login.
- Sezioni: Tours, Articles, Demandes (form ricevuti), Redirections, Compte.
- Editor: campi per lingua a tab (FR, IT, EN, DE, NL), Markdown con toolbar minima e anteprima, upload immagini con drag and drop (ridimensionate lato Worker, alt obbligatorio), riordino galleria.
- Pulsante "Traduire depuis le français" per tour e articolo: chiama l'API Claude, riempie le 4 lingue, evidenzia i campi generati finché il cliente non salva.
- Anteprima SEO (title/description con lunghezze).
- Interfaccia in francese.

### SEO / GEO

- JSON-LD: `Organization` + `Person` (Fabien, `hasCredential` UIAGM), `WebSite`, `BreadcrumbList`, `TouristTrip` con `Offer` (prezzo, durata, gruppo) sui tour, `Article` sul blog, `FAQPage` dove c'è FAQ.
- Meta per pagina e lingua, Open Graph, canonical, hreflang con `x-default` = FR.
- `llms.txt` generato: attività, guida, elenco tour con prezzo e durata, link puliti.
- Consent banner conforme CNIL (accetta / rifiuta / personalizza), Google Consent Mode v2, un solo GA4. Nessun tracker prima del consenso. Via `analytics.js`, GTM doppio, ExactMetrics.
- Header di sicurezza (HSTS, CSP, X-Frame-Options, Referrer-Policy, X-Content-Type-Options).
- Search Console: stessa proprietà (dominio invariato), invio nuove sitemap, monitoraggio 404 e copertura per 30 giorni.

### Performance attese

Home sotto 1 MB, meno di 25 richieste, Lighthouse mobile 90+. Font: le stesse famiglie del design attuale (Montserrat, Roboto, Playfair Display, Dancing Script) ridotte ai pesi usati, self-hosted. Niente icon font: SVG inline.

## Migrazione

1. Crawl del sito attuale (già fatto, da rifare il giorno del cutover per le ultime modifiche): HTML di ogni pagina → estrazione campi tour, testo articoli in Markdown, immagini.
2. Import in D1, download e trasformazione immagini, upload su R2.
3. Traduzioni nelle 4 lingue.
4. Sito di test su `*.workers.dev` per validazione cliente.
5. Cutover: DNS su IONOS verso Cloudflare, MX invariati. WordPress lasciato spento su IONOS per 30 giorni, poi disdetta hosting.
6. Guida d'uso del CMS in francese (pagina web con screenshot).

## Blocchi e importi

| Blocco | gg interne | € nel preventivo |
|---|---|---|
| Base: design riprodotto, struttura, hosting, lingue | 3 | 260 |
| Pagine statiche (16) | 4 | 350 |
| Tour: modello, template, CMS, Programme automatico | 5 | 430 |
| Blog: CMS, migrazione 107 articoli | 4 | 350 |
| Multilingua 5 lingue + assistente traduzione | 5 | 430 |
| Form e notifiche | 1,5 | 130 |
| SEO/GEO, redirect completi, consent, sicurezza | 3,5 | 300 |
| Immagini | 1,5 | 130 |
| Test, messa online, monitoraggio 30 gg, guida | 2,5 | 220 |
| Subtotale | 30 | 2.600 |
| Sconto | | -600 |
| Netto | | 2.000 |

Costi ricorrenti a carico del cliente dopo la messa online: dominio e caselle su IONOS (già in essere), Cloudflare 0 €, API traduzione (stimati 5-20 €/anno), niente licenze tema/plugin. L'hosting WordPress IONOS si disdice.

## Fuori perimetro (da scrivere nel preventivo)

Nuovi contenuti o riscrittura dei testi esistenti (si migra quel che c'è, con correzione dei refusi evidenti), redesign grafico, prenotazione con pagamento online, area clienti, newsletter, traduzione di articoli futuri (la fa l'assistente, rilegge il cliente), manutenzione e assistenza dopo i 30 giorni di monitoraggio, campagne Google Ads, produzione foto/video.

## Rischi

- Il crawl non vede contenuti non pubblicati o bozze in WordPress: verificare con il cliente se ci sono bozze da tenere.
- Il cliente modifica il sito WP durante lo sviluppo: ricrawl finale prima del cutover.
- Accesso DNS IONOS: serve il cliente o le sue credenziali il giorno del cambio.
- Qualità dei testi francesi: refusi corretti in migrazione, ma non riscriviamo.
