136 lines
3.9 KiB
Text
136 lines
3.9 KiB
Text
|
|
---
|
||
|
|
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<string, string> = {
|
||
|
|
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",
|
||
|
|
],
|
||
|
|
};
|
||
|
|
|
||
|
|
// 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;
|
||
|
|
---
|
||
|
|
|
||
|
|
<!-- Meta tags -->
|
||
|
|
<meta name="description" content={description} />
|
||
|
|
<link rel="canonical" href={canonicalUrl} />
|
||
|
|
{noindex && <meta name="robots" content="noindex, nofollow" />}
|
||
|
|
|
||
|
|
<!-- Open Graph -->
|
||
|
|
<meta property="og:title" content={title} />
|
||
|
|
<meta property="og:description" content={description} />
|
||
|
|
<meta property="og:url" content={canonicalUrl} />
|
||
|
|
<meta property="og:image" content={imageUrl} />
|
||
|
|
<meta property="og:type" content={ogType} />
|
||
|
|
<meta property="og:locale" content={ogLocaleMap[locale]} />
|
||
|
|
<meta property="og:site_name" content="Jalil Arfaoui" />
|
||
|
|
{article?.publishedTime && (
|
||
|
|
<meta property="article:published_time" content={article.publishedTime} />
|
||
|
|
)}
|
||
|
|
{article?.tags?.map((tag) => (
|
||
|
|
<meta property="article:tag" content={tag} />
|
||
|
|
))}
|
||
|
|
|
||
|
|
<!-- Twitter Card -->
|
||
|
|
<meta name="twitter:card" content="summary_large_image" />
|
||
|
|
<meta name="twitter:site" content="@jalilarfaoui" />
|
||
|
|
<meta name="twitter:title" content={title} />
|
||
|
|
<meta name="twitter:description" content={description} />
|
||
|
|
<meta name="twitter:image" content={imageUrl} />
|
||
|
|
|
||
|
|
<!-- Hreflang -->
|
||
|
|
{alternateUrls && (
|
||
|
|
<>
|
||
|
|
<link rel="alternate" hreflang="fr" href={new URL(alternateUrls.fr, siteUrl).href} />
|
||
|
|
<link rel="alternate" hreflang="en" href={new URL(alternateUrls.en, siteUrl).href} />
|
||
|
|
<link rel="alternate" hreflang="ar" href={new URL(alternateUrls.ar, siteUrl).href} />
|
||
|
|
<link rel="alternate" hreflang="x-default" href={new URL(alternateUrls.fr, siteUrl).href} />
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<!-- JSON-LD Person -->
|
||
|
|
<script type="application/ld+json" set:html={JSON.stringify(personJsonLd)} />
|
||
|
|
|
||
|
|
<!-- JSON-LD WebSite (home pages only) -->
|
||
|
|
{websiteJsonLd && (
|
||
|
|
<script type="application/ld+json" set:html={JSON.stringify(websiteJsonLd)} />
|
||
|
|
)}
|