feat(seo): expand sitemap and structured data
This commit is contained in:
+115
-85
@@ -1,10 +1,54 @@
|
||||
import { listBlogDetails } from "@/data/blog-detail";
|
||||
import { isNewsletterCategory } from "@/data/blog";
|
||||
import { listAnimationSources } from "@/data/animation-sources";
|
||||
import type { MetadataRoute } from "next";
|
||||
import { listAnimationSources } from "@/data/animation-sources";
|
||||
import { isNewsletterCategory } from "@/data/blog";
|
||||
import { listBlogDetails } from "@/data/blog-detail";
|
||||
import {
|
||||
getAbsoluteUrl,
|
||||
getLocalizedUrl,
|
||||
type SiteLocale,
|
||||
} from "@/lib/seo";
|
||||
|
||||
const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||
const LOCALES: SiteLocale[] = ["tr", "en"];
|
||||
|
||||
const STATIC_ROUTES = [
|
||||
{ path: "/", changeFrequency: "weekly", priority: 1 },
|
||||
{ path: "/about", changeFrequency: "monthly", priority: 0.8 },
|
||||
{ path: "/about/references", changeFrequency: "monthly", priority: 0.5 },
|
||||
{
|
||||
path: "/about/volunteer-community",
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.5,
|
||||
},
|
||||
{ path: "/blog", changeFrequency: "weekly", priority: 0.9 },
|
||||
{ path: "/agenda", changeFrequency: "weekly", priority: 0.9 },
|
||||
{ path: "/projects", changeFrequency: "monthly", priority: 0.8 },
|
||||
{ path: "/content", changeFrequency: "weekly", priority: 0.7 },
|
||||
{ path: "/gallery", changeFrequency: "monthly", priority: 0.6 },
|
||||
{ path: "/contact", changeFrequency: "yearly", priority: 0.5 },
|
||||
{ path: "/links", changeFrequency: "monthly", priority: 0.4 },
|
||||
{
|
||||
path: "/animation-sources",
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
},
|
||||
] as const;
|
||||
|
||||
function toValidDate(value: string) {
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? undefined : date;
|
||||
}
|
||||
|
||||
function getLanguageLinks(paths: Partial<Record<SiteLocale, string>>) {
|
||||
const languages: Record<string, string> = {};
|
||||
|
||||
if (paths.tr) languages["tr-TR"] = getLocalizedUrl("tr", paths.tr);
|
||||
if (paths.en) languages["en-US"] = getLocalizedUrl("en", paths.en);
|
||||
languages["x-default"] = paths.tr
|
||||
? getLocalizedUrl("tr", paths.tr)
|
||||
: getLocalizedUrl("en", paths.en!);
|
||||
|
||||
return languages;
|
||||
}
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const [posts, animationSources] = await Promise.all([
|
||||
@@ -12,89 +56,75 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
listAnimationSources(),
|
||||
]);
|
||||
|
||||
const staticRoutes: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: SITE_URL,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 1.0,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/about`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/about/references`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.5,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/about/volunteer-community`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.5,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/blog`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/agenda`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/projects`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/content`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/contact`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.5,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/links`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.4,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/animation-sources`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
},
|
||||
];
|
||||
const staticRoutes: MetadataRoute.Sitemap = STATIC_ROUTES.flatMap((route) => {
|
||||
const paths = { tr: route.path, en: route.path };
|
||||
const languages = getLanguageLinks(paths);
|
||||
|
||||
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => ({
|
||||
url: `${SITE_URL}/${isNewsletterCategory(post.category) ? "agenda" : "blog"}/${post.slug}`,
|
||||
lastModified: post.date ? new Date(post.date) : new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
}));
|
||||
return LOCALES.map((locale) => ({
|
||||
url: getLocalizedUrl(locale, route.path),
|
||||
changeFrequency: route.changeFrequency,
|
||||
priority: route.priority,
|
||||
alternates: { languages },
|
||||
}));
|
||||
});
|
||||
|
||||
const postGroups = new Map<string, typeof posts>();
|
||||
for (const post of posts) {
|
||||
const key = `${post.category.toLocaleLowerCase()}:${post.coverImage}`;
|
||||
postGroups.set(key, [...(postGroups.get(key) ?? []), post]);
|
||||
}
|
||||
|
||||
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => {
|
||||
const section = isNewsletterCategory(post.category) ? "agenda" : "blog";
|
||||
const key = `${post.category.toLocaleLowerCase()}:${post.coverImage}`;
|
||||
const translations = postGroups.get(key) ?? [post];
|
||||
const paths = Object.fromEntries(
|
||||
translations.map((translation) => [
|
||||
translation.lang,
|
||||
`/${section}/${translation.slug}`,
|
||||
]),
|
||||
);
|
||||
|
||||
return {
|
||||
url: getLocalizedUrl(post.lang, `/${section}/${post.slug}`),
|
||||
lastModified: toValidDate(post.date),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
alternates: { languages: getLanguageLinks(paths) },
|
||||
images: [getAbsoluteUrl(post.coverImage)],
|
||||
};
|
||||
});
|
||||
|
||||
const animationGroups = new Map<string, typeof animationSources>();
|
||||
for (const source of animationSources) {
|
||||
animationGroups.set(source.slug, [
|
||||
...(animationGroups.get(source.slug) ?? []),
|
||||
source,
|
||||
]);
|
||||
}
|
||||
|
||||
const animationSourceRoutes: MetadataRoute.Sitemap = animationSources.map(
|
||||
(source) => ({
|
||||
url: `${SITE_URL}${source.lang === "en" ? "/en" : ""}/animation-sources/${source.slug}`,
|
||||
lastModified: source.date ? new Date(source.date) : new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.6,
|
||||
}),
|
||||
(source) => {
|
||||
const translations = animationGroups.get(source.slug) ?? [source];
|
||||
const paths = Object.fromEntries(
|
||||
translations.map((translation) => [
|
||||
translation.lang,
|
||||
`/animation-sources/${translation.slug}`,
|
||||
]),
|
||||
);
|
||||
|
||||
return {
|
||||
url: getLocalizedUrl(
|
||||
source.lang,
|
||||
`/animation-sources/${source.slug}`,
|
||||
),
|
||||
lastModified: toValidDate(source.date),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.6,
|
||||
alternates: { languages: getLanguageLinks(paths) },
|
||||
images: [getAbsoluteUrl(source.coverImage)],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return [...staticRoutes, ...blogRoutes, ...animationSourceRoutes];
|
||||
|
||||
Reference in New Issue
Block a user