--- import type { ImageMetadata } from "astro"; import { getAlternateUrls } from "../utils/page-translations"; import defaultOgImage from "../assets/images/jalil-2.jpg"; interface Props { title: string; description: string; ogImage?: ImageMetadata; ogType?: string; article?: { publishedTime?: string; tags?: string[] }; noindex?: boolean; } const { title, description, ogImage, ogType = "website", article, noindex = false, } = Astro.props; const siteUrl = Astro.site!.origin; const canonicalUrl = new URL(Astro.url.pathname, siteUrl).href; const imageUrl = new URL((ogImage ?? defaultOgImage).src, siteUrl).href; // Locale detection const pathname = Astro.url.pathname.replace(/\/$/, "") || "/"; const locale = pathname.startsWith("/en") ? "en" : pathname.startsWith("/ar") ? "ar" : "fr"; const ogLocaleMap: Record = { fr: "fr_FR", en: "en_US", ar: "ar_SA", }; // Hreflang const alternateUrls = getAlternateUrls(pathname); // Home pages for WebSite schema const isHomePage = pathname === "/" || pathname === "/en" || pathname === "/ar"; // Person JSON-LD (on every page) const personJsonLd = { "@context": "https://schema.org", "@type": "Person", "@id": `${siteUrl}/#person`, name: "Jalil Arfaoui", url: siteUrl, image: imageUrl, jobTitle: "Software Craftsman", knowsAbout: [ "Software Craftsmanship", "TDD", "DDD", "Improv Theater", "Photography", ], sameAs: [ "https://www.linkedin.com/in/jalil/", "https://github.com/JalilArfaoui", "https://gitlab.gnome.org/Jalil", "https://forge.tiqa.fr/jalil", "https://framagit.org/jalil", "https://www.malt.fr/profile/jalilarfaoui?overview", "https://www.collective.work/profile/jalil-arfaoui-mrr", "https://500px.com/p/jalilarfaoui", "https://commons.wikimedia.org/wiki/User:JalilArfaoui", "https://www.instagram.com/l.i.l.a.j", "https://x.com/jalilarfaoui", ], }; // Article JSON-LD (for blog posts / articles) const articleJsonLd = ogType === "article" && article ? { "@context": "https://schema.org", "@type": "BlogPosting", headline: title, description, image: imageUrl, url: canonicalUrl, inLanguage: locale, ...(article.publishedTime && { datePublished: article.publishedTime }), author: { "@id": `${siteUrl}/#person` }, } : null; // BreadcrumbList JSON-LD (all pages except home) const breadcrumbJsonLd = !isHomePage ? (() => { const segments = pathname.split("/").filter(Boolean); // Build cumulative paths: /en/code → [{name: "Home", url: /en}, {name: "Code", url: /en/code}] const homeUrl = locale === "fr" ? "/" : `/${locale}`; const homeName = locale === "ar" ? "الرئيسية" : locale === "en" ? "Home" : "Accueil"; const items = [{ name: homeName, url: new URL(homeUrl, siteUrl).href }]; // Skip locale prefix for building readable names const startIdx = locale !== "fr" ? 1 : 0; let cumulativePath = locale !== "fr" ? `/${locale}` : ""; for (let i = startIdx; i < segments.length; i++) { cumulativePath += `/${segments[i]}`; const name = decodeURIComponent(segments[i]) .replace(/-/g, " ") .replace(/^\w/, (c) => c.toUpperCase()); items.push({ name, url: new URL(cumulativePath, siteUrl).href }); } return { "@context": "https://schema.org", "@type": "BreadcrumbList", itemListElement: items.map((item, i) => ({ "@type": "ListItem", position: i + 1, name: item.name, item: item.url, })), }; })() : null; // WebSite JSON-LD (only on home pages) const websiteJsonLd = isHomePage ? { "@context": "https://schema.org", "@type": "WebSite", name: "Jalil Arfaoui", url: siteUrl, inLanguage: locale, author: { "@id": `${siteUrl}/#person` }, } : null; --- {noindex && } {article?.publishedTime && ( )} {article?.tags?.map((tag) => ( ))} {alternateUrls && ( <> )}