Fix #397: Adjust URL for Chinese Categories and Tags

This commit is contained in:
prototypa
2024-04-14 13:20:55 -04:00
parent c830d68321
commit f234e63eb4
9 changed files with 71 additions and 39 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@onwidget/astrowind", "name": "@onwidget/astrowind",
"version": "1.0.0-beta.29", "version": "1.0.0-beta.30",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@onwidget/astrowind", "name": "@onwidget/astrowind",
"version": "1.0.0-beta.28", "version": "1.0.0-beta.30",
"dependencies": { "dependencies": {
"@astrojs/rss": "^4.0.5", "@astrojs/rss": "^4.0.5",
"@astrojs/sitemap": "^3.1.2", "@astrojs/sitemap": "^3.1.2",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@onwidget/astrowind", "name": "@onwidget/astrowind",
"version": "1.0.0-beta.29", "version": "1.0.0-beta.30",
"description": "AstroWind: A free template using Astro 4.0 and Tailwind CSS. Astro starter theme.", "description": "AstroWind: A free template using Astro 4.0 and Tailwind CSS. Astro starter theme.",
"type": "module", "type": "module",
"private": true, "private": true,

View File

@@ -64,8 +64,8 @@ const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') :
<> <>
{' '} {' '}
·{' '} ·{' '}
<a class="capitalize hover:underline" href={getPermalink(post.category, 'category')}> <a class="hover:underline" href={getPermalink(post.category.slug, 'category')}>
{post.category.replaceAll('-', ' ')} {post.category.title}
</a> </a>
</> </>
) )
@@ -87,8 +87,14 @@ const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') :
</header> </header>
{post.excerpt && <p class="flex-grow text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>} {post.excerpt && <p class="flex-grow text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>}
<footer class="mt-5"> {
<PostTags tags={post.tags} /> post.tags && Array.isArray(post.tags) ? (
</footer> <footer class="mt-5">
<PostTags tags={post.tags} />
</footer>
) : (
<Fragment />
)
}
</div> </div>
</article> </article>

View File

@@ -40,8 +40,8 @@ const { Content } = post;
<> <>
{' '} {' '}
·{' '} ·{' '}
<a class="capitalize hover:underline inline-block" href={getPermalink(post.category, 'category')}> <a class="hover:underline inline-block" href={getPermalink(post.category.slug, 'category')}>
{post.category.replaceAll('-', ' ')} {post.category.title}
</a> </a>
</> </>
) )

View File

@@ -24,13 +24,13 @@ const { tags, class: className = 'text-sm', title = undefined, isCategory = fals
{tags.map((tag) => ( {tags.map((tag) => (
<li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 rtl:mr-0 rtl:ml-2 mb-2 py-0.5 px-2 lowercase font-medium"> <li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 rtl:mr-0 rtl:ml-2 mb-2 py-0.5 px-2 lowercase font-medium">
{!APP_BLOG?.tag?.isEnabled ? ( {!APP_BLOG?.tag?.isEnabled ? (
tag tag.title
) : ( ) : (
<a <a
href={getPermalink(tag, (isCategory ? 'category' : 'tag'))} href={getPermalink(tag.slug, (isCategory ? 'category' : 'tag'))}
class="text-muted dark:text-slate-300 hover:text-primary dark:hover:text-gray-200" class="text-muted dark:text-slate-300 hover:text-primary dark:hover:text-gray-200"
> >
{tag} {tag.title}
</a> </a>
)} )}
</li> </li>

View File

@@ -13,14 +13,14 @@ export const getStaticPaths = (async ({ paginate }) => {
return await getStaticPathsBlogCategory({ paginate }); return await getStaticPathsBlogCategory({ paginate });
}) satisfies GetStaticPaths; }) satisfies GetStaticPaths;
type Props = InferGetStaticPropsType<typeof getStaticPaths> & { category: string }; type Props = InferGetStaticPropsType<typeof getStaticPaths> & { category: Record<string, string> };
const { page, category } = Astro.props as Props; const { page, category } = Astro.props as Props;
const currentPage = page.currentPage ?? 1; const currentPage = page.currentPage ?? 1;
const metadata = { const metadata = {
title: `Category '${category}' ${currentPage > 1 ? ` — Page ${currentPage}` : ''}`, title: `Category '${category.title}' ${currentPage > 1 ? ` — Page ${currentPage}` : ''}`,
robots: { robots: {
index: blogCategoryRobots?.index, index: blogCategoryRobots?.index,
follow: blogCategoryRobots?.follow, follow: blogCategoryRobots?.follow,
@@ -30,7 +30,7 @@ const metadata = {
<Layout metadata={metadata}> <Layout metadata={metadata}>
<section class="px-4 md:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl"> <section class="px-4 md:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl">
<Headline><span class="capitalize">{category.replaceAll('-', ' ')}</span></Headline> <Headline>{category.title}</Headline>
<BlogList posts={page.data} /> <BlogList posts={page.data} />
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} /> <Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
</section> </section>

View File

@@ -20,7 +20,7 @@ const { page, tag } = Astro.props as Props;
const currentPage = page.currentPage ?? 1; const currentPage = page.currentPage ?? 1;
const metadata = { const metadata = {
title: `Posts by tag '${tag}'${currentPage > 1 ? ` — Page ${currentPage} ` : ''}`, title: `Posts by tag '${tag.title}'${currentPage > 1 ? ` — Page ${currentPage} ` : ''}`,
robots: { robots: {
index: blogTagRobots?.index, index: blogTagRobots?.index,
follow: blogTagRobots?.follow, follow: blogTagRobots?.follow,
@@ -30,7 +30,7 @@ const metadata = {
<Layout metadata={metadata}> <Layout metadata={metadata}>
<section class="px-4 md:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl"> <section class="px-4 md:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl">
<Headline>Tag: {tag}</Headline> <Headline>Tag: {tag.title}</Headline>
<BlogList posts={page.data} /> <BlogList posts={page.data} />
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} /> <Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
</section> </section>

9
src/types.d.ts vendored
View File

@@ -24,9 +24,9 @@ export interface Post {
image?: ImageMetadata | string; image?: ImageMetadata | string;
/** */ /** */
category?: string; category?: Taxonomy
/** */ /** */
tags?: Array<string>; tags?: Taxonomy[];
/** */ /** */
author?: string; author?: string;
@@ -44,6 +44,11 @@ export interface Post {
readingTime?: number; readingTime?: number;
} }
export interface Taxonomy {
slug: string;
title: string;
}
export interface MetaData { export interface MetaData {
title?: string; title?: string;
ignoreTitleTemplate?: boolean; ignoreTitleTemplate?: boolean;

View File

@@ -1,7 +1,7 @@
import type { PaginateFunction } from 'astro'; import type { PaginateFunction } from 'astro';
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content'; import type { CollectionEntry } from 'astro:content';
import type { Post } from '~/types'; import type { Post, Taxonomy } from '~/types';
import { APP_BLOG } from 'astrowind:config'; import { APP_BLOG } from 'astrowind:config';
import { cleanSlug, trimSlash, BLOG_BASE, POST_PERMALINK_PATTERN, CATEGORY_BASE, TAG_BASE } from './permalinks'; import { cleanSlug, trimSlash, BLOG_BASE, POST_PERMALINK_PATTERN, CATEGORY_BASE, TAG_BASE } from './permalinks';
@@ -60,13 +60,23 @@ const getNormalizedPost = async (post: CollectionEntry<'post'>): Promise<Post> =
const slug = cleanSlug(rawSlug); // cleanSlug(rawSlug.split('/').pop()); const slug = cleanSlug(rawSlug); // cleanSlug(rawSlug.split('/').pop());
const publishDate = new Date(rawPublishDate); const publishDate = new Date(rawPublishDate);
const updateDate = rawUpdateDate ? new Date(rawUpdateDate) : undefined; const updateDate = rawUpdateDate ? new Date(rawUpdateDate) : undefined;
const category = rawCategory ? cleanSlug(rawCategory) : undefined;
const tags = rawTags.map((tag: string) => cleanSlug(tag)); const category = rawCategory
? {
slug: cleanSlug(rawCategory),
title: rawCategory,
}
: undefined;
const tags = rawTags.map((tag: string) => ({
slug: cleanSlug(tag),
title: tag,
}));
return { return {
id: id, id: id,
slug: slug, slug: slug,
permalink: await generatePermalink({ id, slug, publishDate, category }), permalink: await generatePermalink({ id, slug, publishDate, category: category?.slug }),
publishDate: publishDate, publishDate: publishDate,
updateDate: updateDate, updateDate: updateDate,
@@ -102,6 +112,17 @@ const getRandomizedPosts = (array: Post[], num: number) => {
return newArray; return newArray;
}; };
function hasMatchingTaxonomies(
arr1?: Taxonomy[],
arr2?: Taxonomy[]
): boolean {
if (!arr1 || !arr2) {
return false;
}
const slugsSet = new Set(arr1.map((item) => item.slug));
return arr2.some((item) => slugsSet.has(item.slug));
}
const load = async function (): Promise<Array<Post>> { const load = async function (): Promise<Array<Post>> {
const posts = await getCollection('post'); const posts = await getCollection('post');
const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post)); const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post));
@@ -200,18 +221,18 @@ export const getStaticPathsBlogCategory = async ({ paginate }: { paginate: Pagin
if (!isBlogEnabled || !isBlogCategoryRouteEnabled) return []; if (!isBlogEnabled || !isBlogCategoryRouteEnabled) return [];
const posts = await fetchPosts(); const posts = await fetchPosts();
const categories = new Set<string>(); const categories = {};
posts.map((post) => { posts.map((post) => {
typeof post.category === 'string' && categories.add(post.category.toLowerCase()); post.category?.slug && (categories[post.category?.slug] = post.category);
}); });
return Array.from(categories).flatMap((category) => return Array.from(Object.keys(categories)).flatMap((categorySlug) =>
paginate( paginate(
posts.filter((post) => typeof post.category === 'string' && category === post.category.toLowerCase()), posts.filter((post) => post.category?.slug && categorySlug === post.category?.slug),
{ {
params: { category: category, blog: CATEGORY_BASE || undefined }, params: { category: categorySlug, blog: CATEGORY_BASE || undefined },
pageSize: blogPostsPerPage, pageSize: blogPostsPerPage,
props: { category }, props: { category: categories[categorySlug] },
} }
) )
); );
@@ -222,35 +243,35 @@ export const getStaticPathsBlogTag = async ({ paginate }: { paginate: PaginateFu
if (!isBlogEnabled || !isBlogTagRouteEnabled) return []; if (!isBlogEnabled || !isBlogTagRouteEnabled) return [];
const posts = await fetchPosts(); const posts = await fetchPosts();
const tags = new Set<string>(); const tags = {};
posts.map((post) => { posts.map((post) => {
Array.isArray(post.tags) && post.tags.map((tag) => tags.add(tag.toLowerCase())); Array.isArray(post.tags) && post.tags.map((tag) => { tags[tag?.slug] = tag; });
}); });
return Array.from(tags).flatMap((tag) => return Array.from(Object.keys(tags)).flatMap((tagSlug) =>
paginate( paginate(
posts.filter((post) => Array.isArray(post.tags) && post.tags.find((elem) => elem.toLowerCase() === tag)), posts.filter((post) => Array.isArray(post.tags) && post.tags.find((elem) => elem.slug === tagSlug)),
{ {
params: { tag: tag, blog: TAG_BASE || undefined }, params: { tag: tagSlug, blog: TAG_BASE || undefined },
pageSize: blogPostsPerPage, pageSize: blogPostsPerPage,
props: { tag }, props: { tag: tags[tagSlug] },
} }
) )
); );
}; };
/** */ /** */
export function getRelatedPosts(allPosts: Post[], currentSlug: string, currentTags: string[]) { export function getRelatedPosts(allPosts: Post[], currentSlug: string, currentTags: Array<Taxonomy>) {
if (!isBlogEnabled || !isRelatedPostsEnabled) return []; if (!isBlogEnabled || !isRelatedPostsEnabled) return [];
const relatedPosts = getRandomizedPosts( const relatedPosts = getRandomizedPosts(
allPosts.filter((post) => post.slug !== currentSlug && post.tags?.some((tag) => currentTags.includes(tag))), allPosts.filter((post) => post.slug !== currentSlug && hasMatchingTaxonomies(post.tags, currentTags)),
APP_BLOG.relatedPostsCount APP_BLOG.relatedPostsCount
); );
if (relatedPosts.length < APP_BLOG.relatedPostsCount) { if (relatedPosts.length < APP_BLOG.relatedPostsCount) {
const morePosts = getRandomizedPosts( const morePosts = getRandomizedPosts(
allPosts.filter((post) => post.slug !== currentSlug && !post.tags?.some((tag) => currentTags.includes(tag))), allPosts.filter((post) => post.slug !== currentSlug && hasMatchingTaxonomies(post.tags, currentTags)),
APP_BLOG.relatedPostsCount - relatedPosts.length APP_BLOG.relatedPostsCount - relatedPosts.length
); );
relatedPosts.push(...morePosts); relatedPosts.push(...morePosts);