# Audit SEO / GEO — myinfomate-landing > Audit en lecture seule du repo `myinfomate-landing`, réalisé le 2026-09-09. > Périmètre : code source uniquement. Aucun test réseau sur `https://myinfomate.be` n'a été fait — > tout ce qui n'est pas dérivable du code est marqué **non vérifiable depuis le code**. --- ## 0. Suivi d'avancement > Mis à jour le 2026-09-10. Les numéros renvoient au plan d'action du §6. | # | Action | État | |---|---|---| | 1 | Hiérarchie `h2`/`h3` | ✅ 2026-09-10 — libellé décoratif en `
`, titre de section en `h2`, cartes en `h3` (home + segments) | | 2 | Schema `Organization` | ✅ 2026-09-10 — adresse, `vatID`, `taxID`, `areaServed`, `sameAs` LinkedIn Unov ; `url` passé à `https://unov.be` | | 3 | 9 panneaux `ModulesSection` dans le DOM | ✅ 2026-09-10 — vérifié dans le HTML servi | | 4 | Offres Essentiel/Pro/Premium + `AggregateOffer` | ✅ 2026-09-10 — `src/data/structured-data.ts`, HTVA (`valueAddedTaxIncluded: false`) | | 5 | Liens morts `href="#"` | ✅ 2026-09-10 — logo et ACCUEIL → `/{lang}`, globe du footer → unov.be (home **et** segments, le 2ᵉ n'était pas relevé par l'audit) | | M4 | TVA écrite dans les mentions légales | ✅ 2026-09-10 | | M7 | Profils sociaux | ✅ 2026-09-10 — vitrine LinkedIn MyInfoMate créée, dans `sameAs` du `SoftwareApplication` ; fiche produit LinkedIn soumise pour évaluation | | 6 | `llms.txt` FR + prix + `robots.txt` | ✅ 2026-09-10 — `llms.txt` en français (vocabulaire acheteur, grille complète, TVA), anglais déplacé dans `llms-en.txt`, les deux cités dans `robots.txt` | | 7 | Prérendu statique | ✅ 2026-09-10 — `` dans `[lang]/layout.tsx` (layout racine par langue), pages légales dans le groupe `(legal)` avec leur propre layout, plus de `headers()` ni de `x-pathname`. **58 pages prérendues**, servies en `x-nextjs-cache: HIT` | | 8 | `/{lang}/tarifs` | ✅ 2026-09-10 — grille + 7 FAQ tarifaires ×4 langues, `FAQPage` + offres ; lien « Tarifs » du menu (home et segments) pointe dessus ; grille extraite dans `components/PricingPlans.tsx` (était copiée deux fois) | | 9 | `/{lang}/assistant-ia` | ✅ 2026-09-10 — **4 langues** (736 à 846 mots), 6 FAQ + `FAQPage` par langue, `hreflang` complet, liée depuis la section IA de la home et dans le sitemap. Section « Ce qu'il ne fait pas » assumée : dire les limites plutôt que survendre l'IA | | — | Liens légaux du footer | ✅ 2026-09-10 — passés en `` (home, segments, signup) : **le lint du repo est à 0 erreur**, il en comptait 15 | | — | Page CGU | ✅ 2026-09-10 — `/conditions-generales` publiée (~4 000 mots) : la case de consentement de `/signup` faisait accepter des CGU **introuvables**. Liée depuis les pieds de page et le sitemap. ⚠️ validation juridique toujours due | | M4 | Frais de mise en place | ✅ 2026-09-10 — **décision de Thomas : pas de montant affiché.** À la place, dans les 4 langues : « frais de mise en place **uniques, chiffrés dans le devis** », et la FAQ de `/tarifs` dit ce qu'ils couvrent et qu'ils précèdent la signature (voir carte kanban 180) | | 12 | Vocabulaire acheteur dans les 6 segments | 🟠 2026-09-10 — **français fait**. Nouvelles FAQ par segment : RGPD (les 6), marchés publics (5 — pas hôtels, secteur privé), accessibilité/Access-i (3). « Médiation numérique », « parcours de visite » et « cartel numérique » injectés dans les intros. **NL/EN/DE pas encore traduits** : les FAQ françaises sont plus fournies que les autres langues | | 14 | Page marchés publics | 🟠 partiellement couvert par la FAQ des segments ; page dédiée toujours à faire | | 10 | FAQ sur la homepage | ✅ 2026-09-10 — 8 questions ×4 langues + `FAQPage` (produit, prix, canaux, hors ligne, marque blanche, délai, hébergement des données). C'était la page qu'un LLM atteint en premier, et elle n'avait aucune FAQ | | 13 | `BreadcrumbList` | ✅ 2026-09-10 — sur segments, cas clients (liste et détail), tarifs et assistant IA ; helper `breadcrumbSchema()` dans `structured-data.ts` | | 21 | Dates du sitemap | ✅ 2026-09-10 — `lastModified: new Date()` (date du build sur les 55 URLs) remplacé par des dates réelles par famille de pages, constante `LAST_MODIFIED` en tête de `sitemap.ts` **à mettre à jour avec le contenu** | | 12 bis | Traduction des FAQ segments | ✅ 2026-09-10 — **42 items** ajoutés en EN/NL/DE. Parité atteinte : 10 / 10 / 8 / 7 / 8 / 9 FAQ par segment dans les 4 langues | | 16 | Images | ✅ 2026-09-10 — recompressées (palette PNG, `effort: 10`) : `app-screenshot-1` 731 → **232 Ko**, `app-screenshot-2` 547 → **185 Ko**, `myinfomate-logo` 240 → **57 Ko**. Qualité vérifiée à l'œil. `map_generated` déjà optimale. **1,1 Mo économisé.** `sharp` a servi en installation temporaire, il n'est **pas** ajouté aux dépendances | | — | `google-maps-route.png` (699 Ko) | ⚠️ **référencé nulle part dans `src/`** — supprimable, mais je n'y touche pas sans ton accord | | — | Captures d'écran de l'app | ✅ 2026-09-10 — remplacées par les **vraies captures de la V3** fournies par Thomas (app du Fort de Saint-Héribert) : accueil sombre en grille bento et fiche article dans les deux téléphones du hero, liste des sections dans le mockup « white label », où un cadre vide affichait une icône de palette. Anciennes captures `.png` supprimées, nouvelles en JPEG (129 / 121 / 107 Ko). **Textes alternatifs réécrits et localisés** dans les 4 langues — ils étaient en anglais et décrivaient l'ancienne interface | | 14 | `/{lang}/marches-publics` | ✅ 2026-09-10 — 4 langues, 5 FAQ. Argument central : **Visit Namur, attribué sur cahier des charges publié**. Dit franchement l'absence de certification Access-i et d'audit, et renvoie au service marchés pour les seuils plutôt que de citer un chiffre non vérifié | | 17 | `/{lang}/borne-kiosk` et `/{lang}/visite-hors-ligne` | ✅ 2026-09-10 — 4 langues, 4 FAQ chacune, adossées à Visit Namur et au Fort de Saint-Héribert. Gabarit partagé `components/ContentPage.tsx` | | — | Optimisation d'images en prod | ✅ 2026-09-10 — **`sharp` ajouté aux dépendances, après mesure** : sans lui, `/_next/image` renvoyait le JPEG source (64 Ko) ; avec lui, du **WebP de 26 Ko**. ⚠️ à confirmer au premier build Docker (binaires `linuxmusl`) | | — | Maillage interne vers les 5 pages produit | ✅ 2026-09-10 — colonne « Solutions » dans le pied de page de la **home et des pages segment** (3 colonnes vides récupérées), plus des liens **dans le contenu**, qui valent davantage : carte « Mode Kiosk » → `/borne-kiosk`, point « Contenu hors ligne » → `/visite-hors-ligne`, bandeau acheteur public sous la grille de `/tarifs` → `/marches-publics`. Compté sur les pages servies : chaque page produit reçoit 1 à 3 liens par page. **Sitemap : 67 URLs** | | 15 | Pages légales en 4 langues | ✅ 2026-09-10 — mentions légales et confidentialité **traduites**, CGU laissées en français avec un **avis traduit** (« la version française fait foi ») et le corps marqué `lang="fr"` : traduire un contrat qu'un juriste va corriger, c'est du travail à refaire. Les 3 pages vivent sous `/{lang}/…`, les anciennes URLs redirigent vers la langue du visiteur | | 18 | Pages comparatives | ✅ 2026-09-10 — `/fr/comparatif/myinfomate-vs-smartify` et `…-vs-stqry`, **prix relevés à la source le 10/09** (Smartify : 1 800 / 3 500 / 9 500 £ par an ; STQRY : 199 $/mois, +100 $ Pro, 2 000 $ de frais uniques d'app native), sources liées et datées. Chaque page dit **ce que le concurrent fait mieux**. ⚠️ Guideius et izi.TRAVEL écartés : le premier ne publie aucun prix, le second a un modèle gratuit non comparable | | 20 | Blog `/fr/ressources` | ✅ 2026-09-10 — index + 2 articles (audioguide vs application, borne vs BYOD), schema `BlogPosting`. **Français seulement** ; les autres langues répondent 404, c'est volontaire | | 11 | Témoignages clients | ⏸️ **écarté le 2026-09-10 par Thomas** — il ne souhaite pas solliciter les clients pour l'instant, même satisfaits. Le type `CasClient` reste sans champ témoignage : à rouvrir le jour où un client en propose un spontanément | | 19 | Pages XR | ⬜ **deux pages à prévoir, pas une** : `/lunettes-connectees` et `/casque-vr`. À écrire quand chaque module sera démontrable — cf. `DOCS/v2/vr-quest-unity-plan.md` et `DOCS/rayban-meta-integration.md` | Tout est **stagé, pas encore committé** au 2026-09-10 (en attente de validation). Point connu : une URL inexistante (`/fr/xyz`) répond bien **404 + `noindex`**, mais son HTML initial est le shell d'erreur de Next ; la page 404 (`[lang]/not-found.tsx`) s'affiche après hydratation. Effet de l'absence de layout racine commun ; le corriger demanderait `experimental.globalNotFound`. Sans impact SEO. Posts LinkedIn de lancement : `DOCS/linkedin-posts-lancement.md`. --- ## 1. Verdict en 5 lignes 1. **Oui, partiellement citable.** Le contenu textuel principal est rendu côté serveur (composants serveur + client components pré-rendus par Next), les prix sont en texte brut, `llms.txt` existe et est bon, et aucun crawler IA n'est bloqué. Un LLM interrogé sur « audioguide pour musée en Belgique » a de la matière. 2. **Mais le site n'est trouvable que sur des requêtes produit, pas sur les requêtes d'acheteur.** Les 6 pages segment sont bien construites (~1 130 mots FR + 6 FAQ chacune), mais il n'existe **aucune** page `/tarifs`, `/assistant-ia`, `/borne-kiosk`, `/visite-offline`, ni page lunettes connectées : le différenciateur IA n'a pas d'URL propre. 3. **Le vocabulaire de l'acheteur public belge est absent** : zéro occurrence de « médiation numérique », « scénographie », « cartel numérique », « accessibilité », « Access-i », « marché public », « RGPD » dans les ~5 700 lignes de contenu de `src/data/`. 4. **Deux fuites de contenu réelles** : 8 des 9 descriptions de modules de la homepage ne sont pas dans le HTML (`ModulesSection.tsx:135`), et aucune page n'est pré-rendue statiquement (`layout.tsx:36` force le SSR sur tout l'arbre). 5. **Les preuves clients sont là mais désincarnées** : 3 cas clients datés et nommés, mais aucun logo, aucun témoignage attribué, aucun lien sortant vers un site client ou une fiche store — c'est ce qui manque le plus à un dossier de marché public. --- ## 2. Inventaire factuel ### Framework et mode de rendu | Élément | Valeur | Source | |---|---|---| | Framework | Next.js **16.1.6**, React 19.2.3, App Router | `package.json:12-13` | | CSS | Tailwind v4 (`@tailwindcss/postcss`) | `package.json:22` | | Build | `output: "standalone"`, React Compiler activé | `next.config.ts:5-6` | | Déploiement | Docker `node:22-alpine`, `node server.js` derrière Traefik | `Dockerfile:8-18`, `README.md` | | Routing i18n | Middleware Next 16 (`src/proxy.ts`) → redirect vers `/{locale}` selon `Accept-Language` | `src/proxy.ts:28-32` | | Locales | `fr`, `en`, `nl`, `de` — `fr` par défaut, html lang `fr-BE` / `nl-BE` | `src/i18n.ts:1-17` | | **Mode de rendu effectif** | **SSR dynamique sur 100 % des pages de contenu — 0 page prérendue** | voir ci-dessous | **Le contenu texte est bien dans le HTML servi** — ce n'est pas une SPA client-only, donc le point critique redouté n'est pas présent. Deux composants seulement sont des client components porteurs de contenu (`SegmentPageClient.tsx:1`, `ModulesSection.tsx:1`) ; Next les rend quand même dans le HTML initial, donc leur texte est extractible **à l'exception de ce qui est conditionné par un `useState`** (voir bloquant B2). **En revanche, la génération statique ne fonctionne pas.** Le `prerender-manifest.json` du dernier build présent dans `.next/` (daté du 13/08) ne contient que trois routes prérendues : ``` PRERENDERED: ['/_global-error', '/icon.png', '/sitemap.xml'] DYNAMIC: [] ``` Cause : `src/app/layout.tsx:36-37` appelle `await headers()` pour lire `x-pathname`. Cet accès aux headers de requête opte **tout l'arbre de routes** hors du rendu statique. Résultat : les `generateStaticParams()` déclarés dans `[lang]/page.tsx:27`, `[lang]/[segment]/page.tsx:9` et `[lang]/signup/page.tsx:25` ne produisent aucun HTML statique — chaque visite, y compris chaque crawl, déclenche un rendu React complet. ### Routes trouvées | Route | Fichier | Titre (FR) | H1 | Contenu | |---|---|---|---|---| | `/{lang}` | `src/app/[lang]/page.tsx` | *MyInfoMate — Application guide visiteur pour musées & lieux* | `MyInfoMate - La solution facile pour créer, gérer et diffuser du contenu interactif` (`HomeClient.tsx:29-31`) | ~1 990 mots FR | | `/{lang}/musees` | `[segment]/page.tsx` | *Application Guide de Visite pour Musées \| MyInfoMate* | `Votre musée mérite une application à la hauteur de vos collections` | ~1 134 mots FR, 6 FAQ | | `/{lang}/offices-tourisme` | idem | même structure, meta propre | H1 hero segment | ~1 100 mots, 6 FAQ | | `/{lang}/parcs-naturels` | idem | | | ~1 100 mots, 6 FAQ | | `/{lang}/hotels-loisirs` | idem | | | ~1 100 mots, 6 FAQ | | `/{lang}/evenements` | idem | | | ~1 100 mots, 6 FAQ | | `/{lang}/education-culture` | idem | | | ~1 100 mots, 6 FAQ | | `/{lang}/cas-clients` | `cas-clients/page.tsx` | listing | oui | court | | `/{lang}/cas-clients/musee-de-la-fraise` | `cas-clients/[slug]/page.tsx` | *Musée de la Fraise — Premier client MyInfoMate \| Kiosk & App Mobile* | `De la vitrine au numérique : la naissance de MyInfoMate` (`:101`) | ~400 mots | | `/{lang}/cas-clients/fort-de-saint-heribert` | idem | | | ~400 mots | | `/{lang}/cas-clients/visit-namur` | idem | | | ~400 mots | | `/{lang}/signup` | `signup/page.tsx` | *Créer mon compte — Plan Essentiel* | — | **`robots: {index:false}`** (`:41`) — correct | | `/mentions-legales` | `app/mentions-legales/page.tsx` | *Mentions légales* | `Mentions légales` (`:18`) | FR uniquement, hors `[lang]` | | `/confidentialite` | `app/confidentialite/page.tsx` | *Politique de confidentialité* | `Politique de confidentialité` (`:18`) | FR uniquement, hors `[lang]` | **Total indexable : 11 URLs × 4 langues + 2 pages légales = 46 URLs.** Cohérent avec `src/app/sitemap.ts`, qui génère exactement ces entrées avec `alternates.languages` par entrée (`sitemap.ts:8-15`). ### Fondations techniques — état | Point | État | Détail | |---|---|---| | `robots.txt` | ✅ présent | `public/robots.txt` — `User-agent: *` / `Allow: /` / `Disallow: /api/` | | Blocage crawlers IA | ✅ **aucun bloqué** | GPTBot, ClaudeBot, anthropic-ai, PerplexityBot, Google-Extended, CCBot, Bingbot sont **tous autorisés** par le `User-agent: *`. Aucune règle nominative, ni Allow ni Disallow (`robots.txt:1-3`) | | `sitemap.xml` | ✅ généré + référencé | `src/app/sitemap.ts`, référencé `robots.txt:5` | | `llms.txt` | ✅ présent et de bonne qualité | `public/llms.txt`, 85 lignes — produit, features, modes de déploiement, assistant IA, clients, pricing, société | | Canonical | ✅ sur toutes les pages | `[lang]/page.tsx:60`, `[segment]/page.tsx:48`, `cas-clients/[slug]`, légales `:7` | | hreflang | ✅ complet + `x-default` | `layout.tsx:68-71`, `[lang]/page.tsx:59-62`, et aussi dans `sitemap.ts:13` | | Open Graph | ✅ | `layout.tsx:53-62` + per-page ; **OG image dynamique** `[lang]/opengraph-image.tsx` (edge runtime, 1200×630, locale-aware) et variante par segment | | Twitter Card | ✅ `summary_large_image` | `layout.tsx:63-67` | | Données structurées | ✅ **JSON-LD** (pas microdata) | Organization + WebSite + SoftwareApplication (home) ; FAQPage + SoftwareApplication (segments) ; Article (cas clients) | | BreadcrumbList | ❌ absent | aucune occurrence dans le repo | | Images | ⚠️ | `next/image` partout (donc WebP/AVIF à la volée), `priority` sur le hero (`HomeClient.tsx:53`), `alt` renseignés. Mais les sources sont des PNG lourds : `app-screenshot-1.png` 748 Ko, `google-maps-route.png` 715 Ko, `app-screenshot-2.png` 560 Ko, `myinfomate-logo.png` 246 Ko | | Temps de chargement réel | **non vérifiable depuis le code** — le Lighthouse est d'ailleurs encore coché « à faire » dans `TODO-SEO.md:39` | --- ## 3. Bloquants critiques ### B1 — Aucune page n'est prérendue : tout est SSR à chaque requête **Fichier : `src/app/layout.tsx:36-37`** ```ts const h = await headers(); const pathname = h.get("x-pathname") ?? "/"; ``` Cet appel dans le layout racine force le rendu dynamique de l'intégralité de l'application. `prerender-manifest.json` le confirme : trois routes prérendues, aucune page de contenu. Les `generateStaticParams()` déclarés dans trois fichiers sont inertes. *Conséquence* : pas de blocage d'indexation (le HTML est bien complet dans la réponse), mais un TTFB dépendant du serveur à chaque crawl, et la perte de toute mise en cache statique/CDN. Sur un VPS unique derrière Traefik (`README.md`), c'est le premier facteur de lenteur. *Correctif* : le layout racine n'a besoin de la locale que pour `` et des métadonnées de repli — or elle est déjà disponible via le segment `[lang]`. Déplacer `resolveLocale()` + `generateMetadata` vers un `src/app/[lang]/layout.tsx` (à créer) recevant `params.lang`, et laisser le layout racine muet. `[lang]/page.tsx:31` et `[segment]/page.tsx:16` définissent déjà leurs métadonnées complètes, donc le layout racine est en grande partie redondant. ### B2 — 8 des 9 descriptions de modules de la homepage sont absentes du HTML **Fichier : `src/components/ModulesSection.tsx:20, 73, 125, 135`** ```tsx const [activeModule, setActiveModule] = useState(0); ...
{modules[activeModule].description}
``` Seul le module d'index 0 (Carte interactive) voit sa `description` et sa `value` rendues. Les huit autres — Quiz/jeux, Vidéo, Article, Agenda, **Offline**, Web, Statistiques, **Escape game** — n'existent dans le HTML que sous forme de libellé de bouton (`ModulesSection.tsx:59`). Or « mode offline » et « escape game » sont deux des arguments les plus différenciants : ils figurent dans `llms.txt:26,29` mais pas dans le HTML de la homepage. *Correctif* : rendre les neuf panneaux et masquer les inactifs en CSS (`hidden` / `aria-hidden`), exactement comme le fait déjà correctement la FAQ des pages segment (`SegmentPageClient.tsx:652` — collapse par `max-h-0`, contenu toujours présent dans le DOM). ### B3 — Aucun schema `Offer` pour les plans Pro / Premium / Enterprise **Fichiers : `src/app/[lang]/page.tsx:100-110` et `[segment]/page.tsx:84-94`** Le `SoftwareApplication` ne déclare qu'une seule offre, à 39 €. Les prix 99 € et 179 € sont bien en texte brut dans le HTML (`SegmentPageClient.tsx:460` et suivantes) — donc lisibles par un LLM — mais invisibles pour Google Rich Results et pour tout agent qui s'appuie sur les données structurées. À la question « combien coûte MyInfoMate pour avoir l'assistant IA ? », un LLM devra deviner. *Correctif* : remplacer `offers` par un tableau de trois à quatre `Offer` (`name: "Essentiel" / "Pro" / "Premium"`, `price`, `priceCurrency: "EUR"`, périodicité), plus un `AggregateOffer` avec `lowPrice: 39` / `highPrice: 179`. --- ## 4. Manques importants (existe mais mal fait) ### M1 — Hiérarchie de titres inversée sur toutes les pages Le label décoratif en petites capitales est un `