Fix #397: Adjust URL for Chinese Categories and Tags
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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",
|
||||||
|
@@ -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,
|
||||||
|
@@ -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>}
|
||||||
|
{
|
||||||
|
post.tags && Array.isArray(post.tags) ? (
|
||||||
<footer class="mt-5">
|
<footer class="mt-5">
|
||||||
<PostTags tags={post.tags} />
|
<PostTags tags={post.tags} />
|
||||||
</footer>
|
</footer>
|
||||||
|
) : (
|
||||||
|
<Fragment />
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
@@ -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>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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
9
src/types.d.ts
vendored
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user