myinfomate-landing/audit-seo-geo.md
Thomas Fransolet 0dfefc7bb4 SEO/GEO : prérendu statique, 8 nouvelles pages, schémas et vraies captures
Chantier de l'audit audit-seo-geo.md — 20 des 21 actions.

Technique
- Prérendu statique rétabli : le layout racine passe dans [lang], plus de
  headers() ni de x-pathname. 0 page prérendue avant, 89 après.
- Schémas : offres 39/99/179 €, Organization ancrée en Belgique (adresse,
  TVA, areaServed, LinkedIn), FAQPage sur la home, BreadcrumbList partout.
- Hiérarchie h1/h2/h3 remise dans l'ordre ; liens morts corrigés ; liens
  légaux en <Link> — le lint passe de 15 erreurs à 0.
- Les 9 modules de la home sont enfin dans le HTML (8 manquaient).
- sharp en dépendance : mesuré, sans lui /_next/image renvoyait le JPEG
  source (64 Ko), avec lui du WebP (26 Ko).

Contenu
- Nouvelles pages en 4 langues : /tarifs, /assistant-ia, /borne-kiosk,
  /visite-hors-ligne, /marches-publics.
- Blog /fr/ressources (2 articles) et comparatifs sourcés et datés
  myinfomate-vs-smartify et -vs-stqry, en français.
- Pages légales sous /{lang}/ : mentions et confidentialité traduites, CGU
  publiées (le lien de consentement de /signup ne menait nulle part) avec
  un avis traduit, la version française faisant foi.
- Vocabulaire acheteur dans les 6 segments : RGPD, marchés publics,
  accessibilité — en disant l'absence de certification Access-i.
- Captures réelles de la V3 et carte de parcours ; alt textes localisés.
  1,1 Mo d'images économisé.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 18:03:44 +02:00

34 KiB
Raw Blame History

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 <p>, 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 — <html lang> 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 <Link> (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, defr 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.txtUser-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

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 <html lang> 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

const [activeModule, setActiveModule] = useState(0);
...
<h3 ...>{modules[activeModule].title}</h3>
<p  ...>{modules[activeModule].description}</p>

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 <h2>, et le vrai titre de section un <h3>. Vérifiable partout :

  • HomeClient.tsx:78-79<h2>{t.strategic.sectionLabel}</h2> puis <h3>{t.strategic.sectionTitle}</h3>
  • HomeClient.tsx:126-127, :181-182, :301-302, :421-422, :474-475
  • SegmentPageClient.tsx:310-311, :333-334, :363-364, :398-399, :636-637
  • ModulesSection.tsx:34-35

Un extracteur de plan de page lit donc « LE DÉFI DES MUSÉES » comme titre de niveau 2 et « Ce que vivent la plupart des musées » comme sous-titre : l'inverse exact. Correctif purement cosmétique — inverser les balises, garder les classes.

M2 — Le schema Organization n'ancre pas l'entreprise en Belgique

src/app/[lang]/page.tsx:70-81 ne contient que name, url, logo, contactPoint. Manquent address (PostalAddress : Rue René Copette 3, 5020 Namur, BE), areaServed (BE / FR / LU), vatID ou taxID (BCE 0776.895.467), et sameAs (aucun profil externe déclaré). Toutes ces données existent pourtant en clair dans mentions-legales/page.tsx:24-28. C'est le signal le plus direct qu'un LLM utilise pour répondre à « une solution belge ».

M3 — Les mentions légales sont hors du système i18n

src/app/mentions-legales/page.tsx et src/app/confidentialite/page.tsx vivent hors de [lang], sont explicitement exclues du middleware (proxy.ts:5), n'existent qu'en français et n'ont pas de hreflang. Un acheteur néerlandophone ou luxembourgeois qui vérifie la conformité de l'éditeur tombe sur du français. Le sitemap ne les référence qu'en une seule version (sitemap.ts:66-77).

M4 — TVA jamais écrite, RGPD jamais mis en avant côté commercial

mentions-legales/page.tsx:25 donne « Numéro d'entreprise BCE : 0776.895.467 » mais jamais le format TVA BE 0776.895.467 — c'est pourtant la chaîne que cherche le service comptable d'une commune. Par ailleurs « RGPD » n'apparaît que dans confidentialite/page.tsx : grep -ric "RGPD" src/data/*.ts retourne 0. Le seul argument d'hébergement européen est une ligne de la page signup (translations.ts:207, « Hébergé en Europe ») — sur une page en noindex.

M5 — Cas clients : aucune preuve vérifiable de l'extérieur

src/data/cas-clients.ts donne pour chaque client client, year, location (:49-52, :223-225, :397-399), mais l'interface CasClient (:36-42) ne prévoit ni URL du site client, ni lien vers une fiche store, ni témoignage, ni logo.

  • Aucun témoignage nommé : zéro occurrence de quote / testimonial / author dans le fichier.
  • Aucun logo client : public/ ne contient que le logo MyInfoMate et quatre screenshots génériques.
  • Aucun lien sortant : pas un seul href="http" dans cas-clients/[slug]/page.tsx.
  • Les « résultats » sont qualitatifs — {value:'2021', label:'Premier déploiement'}, {value:'100%', label:'Autonomie éditoriale'} (cas-clients.ts:97-100) : aucun chiffre de fréquentation, de nombre de POI, de langues ou de sessions.
  • Le schema Article (cas-clients/[slug]/page.tsx:56-65) n'a ni image, ni dateModified, ni about/mentions pointant vers le lieu.

Les chiffres concrets présents ailleurs restent maigres : llms.txt:64-68 mentionne iBeacon, offline 100 %, iOS + Android — c'est le contenu le plus factuel du site, et il n'est pas dans le HTML.

M6 — Aucune date de mise à jour visible

sitemap.ts:18 fixe lastModified: new Date(), c'est-à-dire la date du build, appliquée uniformément à toutes les URLs : ce n'est pas un signal de fraîcheur, c'est du bruit. Aucune page n'affiche de date dans son HTML ; les cas clients affichent l'année du projet (cas-clients/[slug]/page.tsx:99), pas la date de rédaction.

M7 — Deux liens morts et aucun profil social

  • HomeClient.tsx:766 : <a href="#"> avec une icône « public » dans le footer — bouton social qui ne pointe nulle part.
  • NavBar.tsx:59 et :149 : logo et « ACCUEIL » en href="#" (scroll JS) au lieu de href={/${lang}}. Un crawler n'a pas de lien de retour vers la home depuis le header.
  • Aucun sameAs LinkedIn — la création de la page LinkedIn Unov est d'ailleurs encore ouverte dans TODO-SEO.md:40.

M8 — Images sources non optimisées

748 Ko, 715 Ko et 560 Ko en PNG pour des screenshots d'app. next/image réencode, mais l'optimiseur travaille sur ces sources à chaque cache miss. myinfomate-logo.png (246 Ko) sert à la fois de favicon (layout.tsx:52), d'image OG de repli (layout.tsx:61) et d'image 32 px dans le footer (HomeClient.tsx:755-760). Le paquet sharp n'est pas dans package.json ; le comportement réel de l'optimiseur dans le conteneur node:22-alpine est non vérifiable depuis le code.


5. Opportunités (n'existe pas et devrait exister)

O1 — Pages par intention manquantes

Aucune de ces routes n'existe dans src/app/ :

URL attendue État Commentaire
/tarifs Les prix ne vivent que dans des sections #pricing de la home et des six pages segment. Une requête « prix audioguide musée » n'a aucune page à faire remonter. Le contenu existe déjà (SegmentPageClient.tsx:395-628) — il suffit de l'extraire dans une route.
/assistant-ia Le différenciateur n'a pas de page. Il est une section de la home (HomeClient.tsx:170-260) et un paragraphe de llms.txt:42-49. Face à Guideius ou izi.TRAVEL, c'est l'argument qui n'existe nulle part ailleurs — et il n'est pas adressable.
/borne-kiosk Le kiosk n'est qu'un mode de déploiement dans #deployment-modes (translations.ts:53) ; le cas Visit Namur porte entièrement dessus. Mérite une page.
/visite-offline-sans-reseau Le mode offline est un module (ModulesSection.tsx:12) dont la description n'est même pas dans le HTML (bloquant B2). Le cas Fort de Saint-Héribert est un argument offline parfait (iBeacon, aucune couverture réseau).
/musee-plein-air Partiellement couvert par /parcs-naturels.
/lunettes-connectees Une seule occurrence dans tout le contenu : translations.ts:60, « Meta Quest et lunettes connectées », dans un bloc « à venir ». Pas de page, pas de schema.
/application-office-de-tourisme équivalent /{lang}/offices-tourisme existe et est complet.

O2 — Vocabulaire de l'acheteur culturel : absent

grep -ric sur src/data/translations.ts, segments.ts, cas-clients.ts :

Terme Occurrences
« médiation numérique » 0
« scénographie » 0
« cartel numérique » 0
« accessibilité » 0
« Access-i » 0
« marché public » 0
« RGPD » 0 (hors page confidentialité)
« réalité augmentée » 0
« BYOD » 8 (bon)
« audioguide » / « audio guide » 24 / 30 (bon)
« Namur » 34 (bon, surtout via les cas clients)
« Wallonie » 2
« Belgique » 3 (uniquement dans cas-clients.ts)

Le site parle la langue du produit, pas celle du conservateur ni celle du service marchés publics. « Médiation numérique » est le terme employé par les appels à projets de la Fédération Wallonie-Bruxelles : ne pas l'avoir écrit une seule fois est une occasion manquée gratuite.

O3 — Aucune page marchés publics

Rien dans le repo sur les seuils, la procédure de marché de faible montant, la capacité à répondre à un cahier des charges, ni de document type. C'est pourtant l'objection numéro un d'une commune ou d'une province.

O4 — Aucune page comparative

src/app/ ne contient aucune route comparatif. Le comparatif existe à l'intérieur des pages segment (Segment.comparison, segments.ts:23-26) mais compare des catégories abstraites (« App visiteur générique », « Fiches pédagogiques papier »). Les seuls concurrents nommés le sont dans les meta descriptions (« Alternative à Smartify », segments.ts:81) et dans un pain point (segments.ts:~120 : « Smartify, SmartGuide ou STQRY coûtent entre €100 et €925/mois »). Aucune mention de Guideius, izi.TRAVEL, MySmartJourney, Musa Guide, Cirkwi ou Apptree dans tout le repo — alors que ce sont les noms réellement tapés par un acheteur belge ou français.

Ce n'est pas une erreur, c'est le levier GEO le plus rentable qui reste : les pages « alternative à X » sont exactement ce qu'un LLM ingère pour répondre à une question de comparaison. TODO-SEO.md:56-59 les liste déjà comme à faire.

O5 — Aucun blog

src/app/ ne contient aucune route blog, ressources ou articles. TODO-SEO.md:49-54 a déjà les cinq sujets d'articles identifiés ; aucun n'est écrit. Le seul contenu éditorial du site est du contenu produit — pas un texte ne répond à une question que se pose un responsable de musée avant d'avoir entendu parler de MyInfoMate.

O6 — llms.txt : bon fichier, trois angles morts

public/llms.txt est de bonne facture (85 lignes, structuré, avec liens vers les trois cas clients). Ses faiblesses :

  • Il est uniquement en anglais (llms.txt:3) alors que le marché cible est francophone. Un LLM interrogé en français citera plus volontiers une source française.
  • Il n'est pas référencé dans robots.txt, ni lié depuis le HTML.
  • Les prix y sont incomplets (llms.txt:74, « Starting from €39/month ») alors que la grille 39 / 99 / 179 est publique sur le site.

O7 — Pas de FAQ sur la homepage

Les six pages segment ont chacune six questions et un FAQPage JSON-LD ([segment]/page.tsx:65-76) — c'est très bien fait. La homepage n'a aucune FAQ ni FAQPage. C'est pourtant la page qu'un LLM atteint en premier.


6. Plan d'action priorisé

Trié par ratio impact / effort. T = correctif technique, C = écriture de contenu.

# Action Type Impact Effort
1 Inverser h2/h3 sur les 12 sections concernées (HomeClient.tsx:78,126,181,301,421,474 ; SegmentPageClient.tsx:310,333,363,398,636 ; ModulesSection.tsx:34) T Moyen 0,5 h
2 Enrichir le schema Organization : address, areaServed: ["BE","FR","LU"], vatID: "BE0776895467", sameAs ([lang]/page.tsx:70-81) T Fort 0,5 h
3 Rendre les 9 panneaux de ModulesSection dans le DOM, masquer en CSS au lieu de modules[activeModule] (ModulesSection.tsx:73,125) — récupère « offline » et « escape game » dans le HTML T Fort 1 h
4 Passer SoftwareApplication.offers à un tableau de 3 Offer + AggregateOffer 39179 € ([lang]/page.tsx:100, [segment]/page.tsx:84) T Fort 1 h
5 Corriger les liens morts : footer href="#" (HomeClient.tsx:766), logo et ACCUEIL de la NavBar (NavBar.tsx:59,149) → /{lang} T Moyen 0,5 h
6 Traduire llms.txt en français (ou publier FR + llms-en.txt), y ajouter la grille 39/99/179 et le référencer depuis robots.txt C Fort 1,5 h
7 Déplacer resolveLocale()/generateMetadata de layout.tsx:36 vers un [lang]/layout.tsx → réactive le prérendu statique des 46 URLs T Fort 23 h
8 Créer /{lang}/tarifs en réutilisant la section pricing existante + une FAQ tarifaire T+C Fort 3 h
9 Créer /{lang}/assistant-ia — le différenciateur mérite son URL, avec FAQ et schema C Fort 4 h
10 Ajouter un FAQPage de 68 questions en bas de homepage (réutiliser le format de segments.ts) C Moyen 3 h
11 Ajouter au type CasClient : clientUrl, storeUrls, testimonial {quote, name, role} — et les remplir après accord des 3 clients T+C Fort 4 h + relance clients
12 Injecter le vocabulaire acheteur (« médiation numérique », « accessibilité », « scénographie », « parcours de visite », « RGPD », « hébergement européen ») dans translations.ts et les 6 segments FR C Fort 46 h
13 Ajouter BreadcrumbList sur les pages segment et cas clients T Faible 1 h
14 Créer /{lang}/marches-publics : seuils, marché de faible montant, capacité à répondre, coordonnées BCE/TVA C Fort (public belge) 4 h
15 Traduire /mentions-legales et /confidentialite en 4 langues et les faire passer sous [lang] (ajuster proxy.ts:5) T+C Moyen 3 h
16 Recompresser les 4 PNG de public/ (748 / 715 / 560 / 246 Ko → cible < 150 Ko) et produire un logo 32 px distinct du logo OG T Moyen 1 h
17 Créer /{lang}/borne-kiosk et /{lang}/visite-offline-sans-reseau, adossées aux cas Visit Namur et Fort de Saint-Héribert C Moyen 6 h
18 Trois pages comparatives : myinfomate-vs-izi-travel, myinfomate-vs-guideius, myinfomate-vs-smartify — factuelles, sourcées, sans dénigrement C Fort (GEO) 8 h
19 Créer /{lang}/lunettes-connectees (à faire quand le module sera démontrable — cf. DOCS/rayban-meta-integration.md) C Faible aujourd'hui 4 h
20 Ouvrir un blog /{lang}/ressources avec les 5 articles déjà listés dans TODO-SEO.md:49-54 C Fort à 6 mois 20 h+
21 Remplacer lastModified: new Date() par une date réelle par contenu (sitemap.ts:18) et afficher une date de mise à jour sur les cas clients T Faible 2 h

Séquence recommandée. Les items 1 à 6 tiennent en une demi-journée et corrigent l'essentiel des pertes gratuites. L'item 7 est le seul chantier technique de fond. Les items 8, 9, 12 et 14 sont ceux qui changent réellement la réponse d'un LLM à « quelle solution d'audioguide pour mon musée en Belgique ? ».


Annexe — ce qui est déjà bien fait

À ne pas casser :

  • Le contenu textuel est intégralement dans le HTML servi — ce n'est pas une SPA client-only.
  • Aucun crawler IA n'est bloqué (robots.txt:1-2).
  • llms.txt existe et est structuré — la majorité des concurrents n'en ont pas.
  • Les six pages segment sont de vraies pages d'intention : ~1 130 mots FR, pain points, features, comparatif, six FAQ, le tout en quatre langues, avec FAQPage JSON-LD.
  • Les FAQ des pages segment sont collapsées en CSS (max-h-0, SegmentPageClient.tsx:652) et non conditionnées par un useState : le texte est bien dans le DOM. C'est le bon pattern — exactement ce que ModulesSection ne fait pas.
  • hreflang complet avec x-default, sur chaque page et dans le sitemap.
  • /signup est correctement en noindex, follow (signup/page.tsx:41).
  • OG image générée dynamiquement et localisée, avec une variante par segment.
  • Les prix (39 / 99 / 179 €) sont en texte brut dans le HTML, pas en image.