import type { MetadataRoute } from "next"; import { routing } from "@/i18n/routing"; import { getAllPosts } from "@/lib/posts"; import { siteConfig } from "@/lib/site-config"; /** * 动态生成 sitemap.xml。 * * 覆盖:首页、关于页,以及全部已发布文章(草稿不会出现在这里)。 * 每个条目都带上 hreflang alternate,帮助搜索引擎理解多语言对应关系。 * * 内容来自数据库,因此 sitemap 也在请求时生成并缓存, * 而不是在构建期固化——否则构建之后新增的文章不会出现在 sitemap 中。 * * 注意:Next.js 要求 sitemap 返回绝对 URL。 */ export const revalidate = 60; export default async function sitemap(): Promise { const posts = await getAllPosts(); /** 为一条路径生成所有语言的 alternates */ const buildAlternates = (path: string) => ({ languages: Object.fromEntries( routing.locales.map((locale) => [ locale, `${siteConfig.url}/${locale}${path}`, ]), ), }); const staticEntries: MetadataRoute.Sitemap = routing.locales.flatMap( (locale) => [ { url: `${siteConfig.url}/${locale}`, lastModified: new Date(), changeFrequency: "daily" as const, priority: 1, alternates: buildAlternates(""), }, { url: `${siteConfig.url}/${locale}/about`, lastModified: new Date(), changeFrequency: "monthly" as const, priority: 0.6, alternates: buildAlternates("/about"), }, ], ); const postEntries: MetadataRoute.Sitemap = routing.locales.flatMap((locale) => posts.map((post) => ({ url: `${siteConfig.url}/${locale}/posts/${post.slug}`, lastModified: new Date(`${post.date}T00:00:00Z`), changeFrequency: "weekly" as const, priority: 0.8, alternates: buildAlternates(`/posts/${post.slug}`), })), ); return [...staticEntries, ...postEntries]; }