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>
336 lines
34 KiB
Markdown
336 lines
34 KiB
Markdown
# 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`, `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 `<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`**
|
||
|
||
```tsx
|
||
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` 39–179 € (`[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** | **2–3 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 6–8 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** | **4–6 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.
|