chore: optimize blog page metadata

This commit is contained in:
javayhu 2025-05-30 18:27:22 +08:00
parent 707399057e
commit c23fdee88f
3 changed files with 109 additions and 40 deletions

View File

@ -2,21 +2,49 @@ import BlogGrid from '@/components/blog/blog-grid';
import EmptyGrid from '@/components/shared/empty-grid';
import CustomPagination from '@/components/shared/pagination';
import { websiteConfig } from '@/config/website';
import { LOCALES } from '@/i18n/routing';
import { constructMetadata } from '@/lib/metadata';
import { getUrlWithLocale } from '@/lib/urls/urls';
import type { NextPageProps } from '@/types/next-page-props';
import { allCategories, allPosts } from 'content-collections';
import type { Metadata } from 'next';
import type { Locale } from 'next-intl';
import { getTranslations } from 'next-intl/server';
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string; locale: Locale }>;
}): Promise<Metadata | undefined> {
const { slug, locale } = await params;
// Generate all static params for SSG (locale + category + pagination)
export function generateStaticParams() {
const categories = allCategories;
const publishedPosts = allPosts.filter((post) => post.published);
const paginationSize = websiteConfig.blog.paginationSize;
const params: { locale: string; slug: string; page?: string }[] = [];
for (const locale of LOCALES) {
const localeCategories = categories.filter((cat) => cat.locale === locale);
for (const category of localeCategories) {
const filteredPosts = publishedPosts.filter(
(post) =>
post.locale === locale &&
post.categories.some((cat) => cat && cat.slug === category.slug)
);
const totalCount = filteredPosts.length;
const totalPages = Math.max(1, Math.ceil(totalCount / paginationSize));
for (let pageNumber = 1; pageNumber <= totalPages; pageNumber++) {
if (pageNumber === 1) {
params.push({ locale, slug: category.slug });
} else {
params.push({
locale,
slug: category.slug,
page: String(pageNumber),
});
}
}
}
}
console.log('BlogCategoryPage, generateStaticParams', params);
return params;
}
// Generate metadata for each static category page (locale + slug + page)
export async function generateMetadata({ params }: BlogCategoryPageProps) {
const { locale, slug, page } = await params;
// Find category with matching slug and locale
const category = allCategories.find(
(category) => category.slug === slug && category.locale === locale
@ -28,26 +56,38 @@ export async function generateMetadata({
);
return {};
}
const t = await getTranslations({ locale, namespace: 'Metadata' });
// Build canonical URL with pagination
let canonicalPath = `/blog/category/${slug}`;
if (page && page !== '1') {
canonicalPath += `?page=${page}`;
}
console.log(
`locale: ${locale}, slug: ${slug}, page: ${page}, canonicalPath: ${canonicalPath}`
);
return constructMetadata({
title: `${category.name} | ${t('title')}`,
description: category.description,
canonicalUrl: getUrlWithLocale(`/blog/category/${slug}`, locale),
canonicalUrl: getUrlWithLocale(canonicalPath, locale),
});
}
interface BlogCategoryPageProps {
params: Promise<{
slug: string;
locale: Locale;
page?: string;
}>;
}
export default async function BlogCategoryPage({
params,
searchParams,
}: NextPageProps) {
const paginationSize = websiteConfig.blog.paginationSize;
const resolvedParams = await params;
const { slug, locale } = resolvedParams;
const resolvedSearchParams = await searchParams;
const { page } = (resolvedSearchParams as { [key: string]: string }) || {};
}: BlogCategoryPageProps) {
const { slug, locale, page } = await params;
const currentPage = page ? Number(page) : 1;
const paginationSize = websiteConfig.blog.paginationSize;
const startIndex = (currentPage - 1) * paginationSize;
const endIndex = startIndex + paginationSize;
@ -92,7 +132,7 @@ export default async function BlogCategoryPage({
<div className="mt-8 flex items-center justify-center">
<CustomPagination
routePreix={`/${locale}/blog/category/${resolvedParams.slug}`}
routePreix={`/${locale}/blog/category/${slug}`}
totalPages={totalPages}
/>
</div>

View File

@ -2,39 +2,70 @@ import BlogGrid from '@/components/blog/blog-grid';
import EmptyGrid from '@/components/shared/empty-grid';
import CustomPagination from '@/components/shared/pagination';
import { websiteConfig } from '@/config/website';
import { LOCALES } from '@/i18n/routing';
import { constructMetadata } from '@/lib/metadata';
import { getUrlWithLocale } from '@/lib/urls/urls';
import type { NextPageProps } from '@/types/next-page-props';
import { allPosts } from 'content-collections';
import type { Metadata } from 'next';
import type { Locale } from 'next-intl';
import { getTranslations } from 'next-intl/server';
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: Locale }>;
}): Promise<Metadata | undefined> {
const { locale } = await params;
// Generate all static params for SSG (locale + pagination)
export function generateStaticParams() {
const publishedPosts = allPosts.filter((post) => post.published);
const paginationSize = websiteConfig.blog.paginationSize;
const params: { locale: string; page?: string }[] = [];
for (const locale of LOCALES) {
const localePosts = publishedPosts.filter((post) => post.locale === locale);
if (localePosts.length <= 0) {
continue;
}
const totalCount = localePosts.length;
const totalPages = Math.max(1, Math.ceil(totalCount / paginationSize));
for (let pageNumber = 1; pageNumber <= totalPages; pageNumber++) {
if (pageNumber === 1) {
params.push({ locale });
} else {
params.push({ locale, page: String(pageNumber) });
}
}
}
console.log('BlogPage, generateStaticParams', params);
return params;
}
// Generate metadata for each static page (locale + page)
export async function generateMetadata({ params }: BlogPageProps) {
const { locale, page } = await params;
const t = await getTranslations({ locale, namespace: 'Metadata' });
const pt = await getTranslations({ locale, namespace: 'BlogPage' });
// Build canonical URL with pagination
let canonicalPath = '/blog';
if (page && page !== '1') {
canonicalPath += `?page=${page}`;
}
console.log(
`locale: ${locale}, page: ${page}, canonicalPath: ${canonicalPath}`
);
return constructMetadata({
title: pt('title') + ' | ' + t('title'),
title: `${pt('title')} | ${t('title')}`,
description: pt('description'),
canonicalUrl: getUrlWithLocale('/blog', locale),
canonicalUrl: getUrlWithLocale(canonicalPath, locale),
});
}
export default async function BlogPage({
params,
searchParams,
}: NextPageProps) {
const paginationSize = websiteConfig.blog.paginationSize;
const resolvedParams = await params;
const { locale } = resolvedParams;
const resolvedSearchParams = await searchParams;
const { page } = (resolvedSearchParams as { [key: string]: string }) || {};
interface BlogPageProps {
params: Promise<{
locale: Locale;
page?: string;
}>;
}
export default async function BlogPage({ params }: BlogPageProps) {
const { locale, page } = await params;
const currentPage = page ? Number(page) : 1;
const paginationSize = websiteConfig.blog.paginationSize;
const startIndex = (currentPage - 1) * paginationSize;
const endIndex = startIndex + paginationSize;

View File

@ -16,16 +16,14 @@ import {
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
import type { Metadata } from 'next';
import type { Locale } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import type { ReactNode } from 'react';
export function generateStaticParams() {
const locales = LOCALES;
const slugParams = source.generateParams();
const params = locales.flatMap((locale) =>
const params = LOCALES.flatMap((locale) =>
slugParams.map((param) => ({
locale,
slug: param.slug,