Fix tag URL encoding for non-ASCII characters

Fixed tag matching issue where tags with spaces and non-ASCII characters (like "Medicine - 醫學") were not working correctly on Vercel.

Changes:
1. Updated getTagSlug() to normalize tags without encoding - Next.js handles URL encoding automatically
2. Added decodeURIComponent() in tag page to decode incoming URL parameters
3. This ensures proper matching between generated slugs and URL parameters

The fix resolves:
- Tag archive pages showing wrong characters
- Articles not being collected under correct tags
- URL display issues with encoded characters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 23:20:04 +08:00
parent 5d3d754252
commit 912c70332e
2 changed files with 12 additions and 8 deletions

View File

@@ -27,10 +27,12 @@ interface Props {
export async function generateMetadata({ params }: Props): Promise<Metadata> { export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { tag: slug } = await params; const { tag: slug } = await params;
// Decode the slug since Next.js encodes non-ASCII characters in URLs
const decodedSlug = decodeURIComponent(slug);
// Find original tag label by slug // Find original tag label by slug
const tag = allPosts const tag = allPosts
.flatMap((post) => post.tags ?? []) .flatMap((post) => post.tags ?? [])
.find((t) => getTagSlug(t) === slug); .find((t) => getTagSlug(t) === decodedSlug);
return { return {
title: tag ? `標籤:${tag}` : '標籤' title: tag ? `標籤:${tag}` : '標籤'
@@ -39,13 +41,15 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
export default async function TagPage({ params }: Props) { export default async function TagPage({ params }: Props) {
const { tag: slug } = await params; const { tag: slug } = await params;
// Decode the slug since Next.js encodes non-ASCII characters in URLs
const decodedSlug = decodeURIComponent(slug);
const posts = allPosts.filter( const posts = allPosts.filter(
(post) => post.tags && post.tags.some((t) => getTagSlug(t) === slug) (post) => post.tags && post.tags.some((t) => getTagSlug(t) === decodedSlug)
); );
const tagLabel = const tagLabel =
posts[0]?.tags?.find((t) => getTagSlug(t) === slug) ?? slug; posts[0]?.tags?.find((t) => getTagSlug(t) === decodedSlug) ?? decodedSlug;
return ( return (
<SidebarLayout> <SidebarLayout>

View File

@@ -27,14 +27,14 @@ export function getPageBySlug(slug: string): Page | undefined {
} }
export function getTagSlug(tag: string): string { export function getTagSlug(tag: string): string {
// Normalize spaces and convert to lowercase first // Normalize spaces and convert to lowercase
// Replace multiple spaces/dashes with single dash // Replace multiple spaces/dashes with single dash
const normalized = tag // Next.js will handle URL encoding automatically, so we don't encode here
return tag
.toLowerCase() .toLowerCase()
.replace(/\s+/g, '-') .replace(/\s+/g, '-')
.replace(/-+/g, '-'); .replace(/-+/g, '-')
// Encode URI components to handle non-ASCII characters properly .trim();
return encodeURIComponent(normalized);
} }
export function getAllTagsWithCount(): { tag: string; slug: string; count: number }[] { export function getAllTagsWithCount(): { tag: string; slug: string; count: number }[] {