adding alternative language versions to article metadata
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||||
import { getArticleBySlug, getAllArticles } from "@/lib/blog/get-articles";
|
import { getArticleBySlug, getAllArticles, getAlternatesArticle } from "@/lib/blog/get-articles";
|
||||||
import { ArticleHero } from "@/components/sections/ArticleHero";
|
import { ArticleHero } from "@/components/sections/ArticleHero";
|
||||||
import { ArticleSummary } from "@/components/sections/ArticleSummary";
|
import { ArticleSummary } from "@/components/sections/ArticleSummary";
|
||||||
import { locales } from "@/middleware";
|
import { locales } from "@/middleware";
|
||||||
@@ -24,6 +24,14 @@ export async function generateMetadata({ params }: props): Promise<Metadata> {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const languageAlternates = getAlternatesArticle(article);
|
||||||
|
const absoluteLanguageAlternates = Object.fromEntries(
|
||||||
|
Object.entries(languageAlternates).map(([language, path]) => [
|
||||||
|
language,
|
||||||
|
`${siteUrl}${path}`,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
const url = `${siteUrl}/${locale}/blog/${slug}`;
|
const url = `${siteUrl}/${locale}/blog/${slug}`;
|
||||||
const title = article.title;
|
const title = article.title;
|
||||||
const description = article.description;
|
const description = article.description;
|
||||||
@@ -38,6 +46,10 @@ export async function generateMetadata({ params }: props): Promise<Metadata> {
|
|||||||
description,
|
description,
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: url,
|
canonical: url,
|
||||||
|
languages: {
|
||||||
|
...absoluteLanguageAlternates,
|
||||||
|
"x-default": url,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title,
|
title,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
ready: true
|
ready: true
|
||||||
|
translationKey: "how-I-built-my-new-website"
|
||||||
title: "How I built my new website, and why ?"
|
title: "How I built my new website, and why ?"
|
||||||
description: "Discover how I built my website, the origin of this need, and the steps I went through."
|
description: "Discover how I built my website, the origin of this need, and the steps I went through."
|
||||||
date: "2026-04-17"
|
date: "2026-04-17"
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
---
|
---
|
||||||
ready: true
|
ready: true
|
||||||
|
translationKey: "claude-design-vs-figma-make"
|
||||||
title: "Claude Design vs. Figma Make: How I Combined the Two to Optimize My Prototyping"
|
title: "Claude Design vs. Figma Make: How I Combined the Two to Optimize My Prototyping"
|
||||||
description: A comparison of the two tools and how I used them
|
description: "A comparison of the two tools and how I used them"
|
||||||
date: 2026-04-30
|
date: "2026-04-30"
|
||||||
category: Technologies
|
category: "Technologies"
|
||||||
tags:
|
tags:
|
||||||
- Design
|
- "Design"
|
||||||
- AI
|
- "AI"
|
||||||
- Experience
|
- "Experience"
|
||||||
- Comparison
|
- "Comparison"
|
||||||
- Technologies
|
- "Technologies"
|
||||||
readTime: 5 min
|
readTime: "5 min"
|
||||||
summary:
|
summary:
|
||||||
- The Arrival of Claude Design
|
- "The Arrival of Claude Design"
|
||||||
- "The Flip Side: A Restrictive Pricing Model"
|
- "The Flip Side: A Restrictive Pricing Model"
|
||||||
- "The Alternative: Figma Make"
|
- "The Alternative: Figma Make"
|
||||||
- "The Ideal Workflow: Making Claude and Figma Talk"
|
- "The Ideal Workflow: Making Claude and Figma Talk"
|
||||||
- Epilogue
|
- "Epilogue"
|
||||||
---
|
---
|
||||||
## The Arrival of Claude Design
|
## The Arrival of Claude Design
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
---
|
---
|
||||||
ready: true
|
ready: true
|
||||||
title: Comment j'ai construit mon nouveau site internet, et pourquoi ?
|
translationKey: "how-I-built-my-new-website"
|
||||||
description: Découvrez comment j'ai construit mon site, l'origine de ce besoin et les étapes par lesquelles je suis passé.
|
title: "Comment j'ai construit mon nouveau site internet, et pourquoi ?"
|
||||||
date: 2026-04-17
|
description: "Découvrez comment j'ai construit mon site, l'origine de ce besoin et les étapes par lesquelles je suis passé."
|
||||||
category: Développement Web
|
date: "2026-04-17"
|
||||||
|
category: "Développement Web"
|
||||||
tags:
|
tags:
|
||||||
- Portfolio
|
- "Portfolio"
|
||||||
- Design
|
- "Design"
|
||||||
- Apprentissage
|
- "Apprentissage"
|
||||||
readTime: 5 min
|
readTime: "5 min"
|
||||||
summary:
|
summary:
|
||||||
- Pourquoi maintenant et pas des années plus tôt ?
|
- "Pourquoi maintenant et pas des années plus tôt ?"
|
||||||
- "Le plus long : maquette Figma"
|
- "Le plus long : maquette Figma"
|
||||||
- Comment et avec quoi ? La stack de rêve
|
- "Comment et avec quoi ? La stack de rêve"
|
||||||
- Mon terrain de jeu personnel
|
- "Mon terrain de jeu personnel"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Pourquoi maintenant et pas des années plus tôt ?
|
## Pourquoi maintenant et pas des années plus tôt ?
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
---
|
---
|
||||||
ready: true
|
ready: true
|
||||||
|
translationKey: "claude-design-vs-figma-make"
|
||||||
title: "Claude Design face à Figma Make : Comment j'ai combiné les deux pour optimiser mon prototypage"
|
title: "Claude Design face à Figma Make : Comment j'ai combiné les deux pour optimiser mon prototypage"
|
||||||
description: Comparatif des deux outils et comment je m'en suis servie
|
description: "Comparatif des deux outils et comment je m'en suis servie"
|
||||||
date: 2026-04-30
|
date: "2026-04-30"
|
||||||
category: Technologies
|
category: "Technologies"
|
||||||
tags:
|
tags:
|
||||||
- Design
|
- "Design"
|
||||||
- IA
|
- "IA"
|
||||||
- Expérience
|
- "Expérience"
|
||||||
- Comparatif
|
- "Comparatif"
|
||||||
- Technologies
|
- "Technologies"
|
||||||
readTime: 5 min
|
readTime: "5 min"
|
||||||
summary:
|
summary:
|
||||||
- L'arrivée de Claude Design
|
- "L'arrivée de Claude Design"
|
||||||
- "Le revers de la médaille : un modèle économique contraignant"
|
- "Le revers de la médaille : un modèle économique contraignant"
|
||||||
- "L'alternative : Figma Make"
|
- "L'alternative : Figma Make"
|
||||||
- "Le workflow idéal : faire dialoguer Claude et Figma"
|
- "Le workflow idéal : faire dialoguer Claude et Figma"
|
||||||
- Épilogue
|
- "Épilogue"
|
||||||
---
|
---
|
||||||
## L'arrivée de Claude Design
|
## L'arrivée de Claude Design
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import matter from "gray-matter";
|
import matter from "gray-matter";
|
||||||
|
import { locales } from "@/middleware";
|
||||||
|
|
||||||
import { Article, ArticleFrontmatter } from "./types";
|
import { Article, ArticleFrontmatter } from "./types";
|
||||||
|
|
||||||
const CONTENT_DIR = path.join(process.cwd(), "content/blog");
|
const CONTENT_DIR = path.join(process.cwd(), "content/blog");
|
||||||
@@ -42,3 +44,22 @@ export function getAllArticles(locale: string): Article[] {
|
|||||||
.filter((article): article is Article => article !== null && article.ready)
|
.filter((article): article is Article => article !== null && article.ready)
|
||||||
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAlternatesArticle(article: Article) {
|
||||||
|
if (!article.translationKey) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return locales.reduce<Record<string, string>>((acc, locale) => {
|
||||||
|
const translatedArticle = getAllArticles(locale).find(
|
||||||
|
(candidate) =>
|
||||||
|
candidate.translationKey === article.translationKey
|
||||||
|
);
|
||||||
|
|
||||||
|
if (translatedArticle) {
|
||||||
|
acc[locale] = `/${locale}/blog/${translatedArticle.slug}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export type ArticleFrontmatter = {
|
export type ArticleFrontmatter = {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
|
translationKey: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
date: string;
|
date: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user