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>
114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import type { Metadata } from 'next';
|
|
import { notFound } from 'next/navigation';
|
|
import { getSegmentData, getAllSegmentSlugs } from '@/data/segments';
|
|
import { LOCALES, LOCALE_HTML_LANG, LOCALE_OG, DEFAULT_LOCALE, isLocale } from '@/i18n';
|
|
import { MYINFOMATE_LINKEDIN, breadcrumbSchema, softwareOffers } from '@/data/structured-data';
|
|
import SegmentPageClient from './SegmentPageClient';
|
|
|
|
const SITE_URL = 'https://myinfomate.be';
|
|
|
|
export function generateStaticParams() {
|
|
const slugs = getAllSegmentSlugs();
|
|
return LOCALES.flatMap((lang) =>
|
|
slugs.map((segment) => ({ lang, segment }))
|
|
);
|
|
}
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ lang: string; segment: string }>;
|
|
}): Promise<Metadata> {
|
|
const { lang, segment } = await params;
|
|
if (!isLocale(lang)) return {};
|
|
const data = getSegmentData(segment);
|
|
if (!data) return {};
|
|
const m = data.meta[lang];
|
|
const languages = Object.fromEntries(
|
|
LOCALES.map((l) => [LOCALE_HTML_LANG[l], `/${l}/${segment}`])
|
|
);
|
|
return {
|
|
title: m.title,
|
|
description: m.description,
|
|
openGraph: {
|
|
title: m.title,
|
|
description: m.description,
|
|
url: `${SITE_URL}/${lang}/${segment}`,
|
|
siteName: 'MyInfoMate',
|
|
locale: LOCALE_OG[lang],
|
|
alternateLocale: LOCALES.filter((l) => l !== lang).map((l) => LOCALE_OG[l]),
|
|
type: 'website',
|
|
images: [{ url: '/myinfomate-logo.png' }],
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: m.title,
|
|
description: m.description,
|
|
},
|
|
alternates: {
|
|
canonical: `/${lang}/${segment}`,
|
|
languages: { ...languages, 'x-default': `/${DEFAULT_LOCALE}/${segment}` },
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function SegmentPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ lang: string; segment: string }>;
|
|
}) {
|
|
const { lang, segment } = await params;
|
|
if (!isLocale(lang)) notFound();
|
|
const data = getSegmentData(segment);
|
|
if (!data) notFound();
|
|
|
|
const faqItems = data.translations[lang].faq.items;
|
|
const faqSchema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'FAQPage',
|
|
mainEntity: faqItems.map((item) => ({
|
|
'@type': 'Question',
|
|
name: item.question,
|
|
acceptedAnswer: {
|
|
'@type': 'Answer',
|
|
text: item.answer,
|
|
},
|
|
})),
|
|
};
|
|
|
|
const softwareSchema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'SoftwareApplication',
|
|
name: 'MyInfoMate',
|
|
applicationCategory: 'BusinessApplication',
|
|
operatingSystem: 'iOS, Android, Web',
|
|
offers: softwareOffers,
|
|
sameAs: [MYINFOMATE_LINKEDIN],
|
|
description: data.meta[lang].description,
|
|
url: `${SITE_URL}/${lang}/${segment}`,
|
|
};
|
|
|
|
const breadcrumb = breadcrumbSchema([
|
|
{ name: 'MyInfoMate', path: `/${lang}` },
|
|
{ name: data.translations[lang].hero.badge, path: `/${lang}/${segment}` },
|
|
]);
|
|
|
|
return (
|
|
<>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumb) }}
|
|
/>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
|
|
/>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(softwareSchema) }}
|
|
/>
|
|
<SegmentPageClient data={data} lang={lang} />
|
|
</>
|
|
);
|
|
}
|