refactor: optimize all i18n route pages

This commit is contained in:
radishzzz 2025-03-17 18:15:17 +00:00
parent 0888b59c5f
commit 473b13d0ab
8 changed files with 125 additions and 133 deletions

8
pnpm-lock.yaml generated
View file

@ -1818,8 +1818,8 @@ packages:
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint-plugin-jsdoc@50.6.7:
resolution: {integrity: sha512-8JrJRI6oSCHAdr5MvOD1L8nwywmiusk5RKfTisqq2rN5t65QmzmfBzAUkK0lbvwZ442HN33x+IbUon8d+axKoA==}
eslint-plugin-jsdoc@50.6.8:
resolution: {integrity: sha512-PPZVqhoXaalMQwDGzcQrJtPSPIPOYsSMtvkjYAdsIazOW20yhYtVX4+jLL+XznD4zYTXyZbPWPRKkNev4D4lyw==}
engines: {node: '>=18'}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
@ -3862,7 +3862,7 @@ snapshots:
eslint-plugin-antfu: 3.1.1(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-command: 3.1.0(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import-x: 4.8.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
eslint-plugin-jsdoc: 50.6.7(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-jsdoc: 50.6.8(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-jsonc: 2.19.1(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-n: 17.16.2(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-no-only-tests: 3.3.0
@ -5769,7 +5769,7 @@ snapshots:
- supports-color
- typescript
eslint-plugin-jsdoc@50.6.7(eslint@9.22.0(jiti@2.4.2)):
eslint-plugin-jsdoc@50.6.8(eslint@9.22.0(jiti@2.4.2)):
dependencies:
'@es-joy/jsdoccomment': 0.49.0
are-docs-informative: 0.0.2

View file

@ -2,6 +2,18 @@ import { defaultLocale, moreLocales } from '@/config'
import { getLangFromPath, getNextGlobalLang } from '@/i18n/lang'
import { cleanPath } from '@/utils/page'
/**
* Get path to tag page with language support
* @param tagName Tag name
* @param lang Current language code
* @returns Path to tag page
*/
export function getTagPath(tagName: string, lang: string): string {
return lang === defaultLocale
? `/tags/${tagName}/`
: `/${lang}/tags/${tagName}/`
}
// Generates a localized path based on current language
export function getLocalizedPath(path: string, currentLang?: string) {
const clean = cleanPath(path)

View file

@ -1,5 +1,5 @@
---
import { allLocales, defaultLocale } from '@/config'
import { defaultLocale, moreLocales } from '@/config'
import Layout from '@/layouts/Layout.astro'
import { getCollection } from 'astro:content'
@ -13,18 +13,16 @@ export async function getStaticPaths() {
// Default locale
paths.push({
params: { about: 'about' },
params: { about: 'about/' },
props: { lang: defaultLocale },
})
// More locales
allLocales.forEach((lang: string) => {
if (lang !== defaultLocale) {
paths.push({
params: { about: `${lang}/about` },
props: { lang },
})
}
moreLocales.forEach((lang: string) => {
paths.push({
params: { about: `${lang}/about/` },
props: { lang },
})
})
return paths
@ -32,6 +30,7 @@ export async function getStaticPaths() {
const { lang } = Astro.props
// Get about page content with different language
const allAboutEntries = await getCollection('about')
const aboutEntry = allAboutEntries.find(entry => entry.data.lang === lang)
|| allAboutEntries.find(entry => entry.data.lang === '')

View file

@ -1,11 +1,10 @@
---
import PostList from '@/components/PostList.astro'
import { allLocales, defaultLocale } from '@/config'
import { defaultLocale, moreLocales } from '@/config'
import Layout from '@/layouts/Layout.astro'
import { getPinnedPosts, getPostsByYear } from '@/utils/content'
export async function getStaticPaths() {
// 定义路径数组的类型
type PathItem = {
params: { index: string | undefined }
props: { lang: string }
@ -13,31 +12,31 @@ export async function getStaticPaths() {
const paths: PathItem[] = []
// 默认语言的首页
// Default locale
paths.push({
params: { index: undefined },
props: { lang: defaultLocale },
})
// 更多语言的首页
allLocales.forEach((lang: string) => {
if (lang !== defaultLocale) {
paths.push({
params: { index: lang },
props: { lang },
})
}
// More locales
moreLocales.forEach((lang: string) => {
paths.push({
params: { index: `${lang}/` },
props: { lang },
})
})
return paths
}
const { lang } = Astro.props
const pinnedPosts = await getPinnedPosts(lang)
const postsByYear = await getPostsByYear(lang)
---
<Layout>
<main>
<!-- Pinned Posts -->
{pinnedPosts.length > 0 && (
<section class="mb-7.5 lg:mb-9.5">
@ -45,6 +44,7 @@ const postsByYear = await getPostsByYear(lang)
<PostList posts={pinnedPosts} lang={lang} />
</section>
)}
<!-- Regular Posts -->
{[...postsByYear.entries()].map(([_year, posts]) => (
<section class="mb-7.5 lg:mb-9.5">
@ -52,5 +52,6 @@ const postsByYear = await getPostsByYear(lang)
<PostList posts={posts} lang={lang} />
</section>
))}
</main>
</Layout>

View file

@ -2,52 +2,48 @@
import type { CollectionEntry } from 'astro:content'
import Comments from '@/components/Comments/index.astro'
import PostDate from '@/components/PostDate.astro'
import { allLocales, defaultLocale } from '@/config'
import { allLocales, defaultLocale, moreLocales } from '@/config'
import { getTagPath } from '@/i18n/path'
import Layout from '@/layouts/Layout.astro'
import { checkSlugDuplication } from '@/utils/content'
import { checkPostSlugDuplication } from '@/utils/content'
import { generateDescription } from '@/utils/description'
import { getCollection } from 'astro:content'
export async function getStaticPaths() {
const posts = await getCollection('posts')
const duplicates = await checkSlugDuplication(posts)
// Check if there are duplicate post slugs
const duplicates = await checkPostSlugDuplication(posts)
if (duplicates.length > 0) {
throw new Error(`Slug conflicts found:\n${duplicates.join('\n')}`)
throw new Error(`Duplicate post slugs:\n${duplicates.join('\n')}`)
}
// 创建slug到语言的映射
const slugToLangs: Record<string, string[]> = {}
// 填充映射
posts.forEach((post: CollectionEntry<'posts'>) => {
// Use a Map to store the relationship between post slugs and their supported languages
// Set is used to store the supported languages for each post
const slugToLangsMap = posts.reduce((map, post) => {
const slug = post.data.abbrlink || post.slug
const lang = post.data.lang || defaultLocale
const lang = post.data.lang
// 如果文章没有指定语言,初始化为所有支持的语言
if (!slugToLangs[slug]) {
if (!post.data.lang) {
slugToLangs[slug] = [...allLocales] // 文章支持所有语言
}
else {
slugToLangs[slug] = [defaultLocale] // 仅默认语言和指定语言
}
if (!map.has(slug)) {
map.set(slug, new Set(lang ? [lang] : allLocales))
}
else if (lang) {
map.get(slug)?.add(lang)
}
if (!slugToLangs[slug].includes(lang)) {
slugToLangs[slug].push(lang)
}
})
return map
}, new Map<string, Set<string>>())
// 对每个文章的supportedLangs按照allLocales的顺序排序
Object.keys(slugToLangs).forEach((slug) => {
// 按照allLocales的顺序排序
slugToLangs[slug].sort((a, b) => {
return allLocales.indexOf(a) - allLocales.indexOf(b)
})
})
// Convert Map<slug, Set<langs>> to Record<slug, langs[]> structure
// Sort languages according to the order defined in allLocales
const slugToLangs = Object.fromEntries(
Array.from(slugToLangsMap.entries()).map(([slug, langs]) => [
slug,
[...langs].sort((a, b) => allLocales.indexOf(a) - allLocales.indexOf(b)),
]),
)
// 定义路径数组的类型
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
type PathItem = {
params: { posts_slug: string }
props: { post: any, lang: string, supportedLangs: string[] }
@ -55,16 +51,16 @@ export async function getStaticPaths() {
const paths: PathItem[] = []
// 默认语言的文章页面 (没有语言前缀)
// Default locale
posts.forEach((post: CollectionEntry<'posts'>) => {
// Show drafts in dev mode only
if (import.meta.env.DEV || !post.data.draft) {
const slug = post.data.abbrlink || post.slug
const postLang = post.data.lang || defaultLocale
const lang = post.data.lang
// 只有当文章语言是默认语言或没有指定语言时才生成默认语言路径
if (postLang === defaultLocale || post.data.lang === '') {
if (lang === defaultLocale || lang === '') {
paths.push({
params: { posts_slug: `posts/${slug}` },
params: { posts_slug: `posts/${slug}/` },
props: {
post,
lang: defaultLocale,
@ -75,23 +71,22 @@ export async function getStaticPaths() {
}
})
// 更多语言的文章页面 (有语言前缀)
allLocales.forEach((lang: string) => {
if (lang !== defaultLocale) {
posts.forEach((post: CollectionEntry<'posts'>) => {
if ((import.meta.env.DEV || !post.data.draft) && (post.data.lang === lang || post.data.lang === '')) {
const slug = post.data.abbrlink || post.slug
paths.push({
params: { posts_slug: `${lang}/posts/${slug}` },
props: {
post,
lang,
supportedLangs: slugToLangs[slug] || [],
},
})
}
})
}
// More locales
moreLocales.forEach((lang: string) => {
posts.forEach((post: CollectionEntry<'posts'>) => {
// Process posts with matching language or no language specified
if ((import.meta.env.DEV || !post.data.draft) && (post.data.lang === lang || post.data.lang === '')) {
const slug = post.data.abbrlink || post.slug
paths.push({
params: { posts_slug: `${lang}/posts/${slug}/` },
props: {
post,
lang,
supportedLangs: slugToLangs[slug] || [],
},
})
}
})
})
return paths
@ -100,13 +95,6 @@ export async function getStaticPaths() {
const { post, lang, supportedLangs } = Astro.props
const description = generateDescription(post, 'meta')
const { Content, remarkPluginFrontmatter } = await post.render()
// 构建标签链接
function getTagUrl(tagName: string): string {
return lang === defaultLocale
? `/tags/${tagName}/`
: `/${lang}/tags/${tagName}/`
}
---
<Layout
@ -148,7 +136,7 @@ function getTagUrl(tagName: string): string {
<div class="uno-tags-wrapper">
{post.data.tags.map((tag: string) => (
<a
href={getTagUrl(tag)}
href={getTagPath(tag, lang)}
class="uno-tags-style"
>
{tag}

View file

@ -1,10 +1,10 @@
---
import { allLocales, defaultLocale } from '@/config'
import { defaultLocale, moreLocales } from '@/config'
import { getTagPath } from '@/i18n/path'
import Layout from '@/layouts/Layout.astro'
import { getAllTags } from '@/utils/content'
export async function getStaticPaths() {
// 定义路径数组的类型
type PathItem = {
params: { tags: string }
props: { lang: string }
@ -12,20 +12,18 @@ export async function getStaticPaths() {
const paths: PathItem[] = []
// 默认语言的标签索引页
// Default locale
paths.push({
params: { tags: 'tags' },
params: { tags: 'tags/' },
props: { lang: defaultLocale },
})
// 更多语言的标签索引页
allLocales.forEach((lang: string) => {
if (lang !== defaultLocale) {
paths.push({
params: { tags: `${lang}/tags` },
props: { lang },
})
}
// More locales
moreLocales.forEach((lang: string) => {
paths.push({
params: { tags: `${lang}/tags/` },
props: { lang },
})
})
return paths
@ -33,24 +31,17 @@ export async function getStaticPaths() {
const { lang } = Astro.props
const allTags = await getAllTags(lang)
// 构建标签链接
function getTagUrl(tagName: string): string {
return lang === defaultLocale
? `/tags/${tagName}/`
: `/${lang}/tags/${tagName}/`
}
---
<Layout>
<div class="uno-decorative-line"></div>
<div class="uno-tags-wrapper">
{allTags.map(tagName => (
{allTags.map(tag => (
<a
href={getTagUrl(tagName)}
href={getTagPath(tag, lang)}
class="uno-tags-style"
>
{tagName}
{tag}
</a>
))}
</div>

View file

@ -1,11 +1,11 @@
---
import PostList from '@/components/PostList.astro'
import { allLocales, defaultLocale } from '@/config'
import { allLocales, defaultLocale, moreLocales } from '@/config'
import { getTagPath } from '@/i18n/path'
import Layout from '@/layouts/Layout.astro'
import { getAllTags, getPostsByTag } from '@/utils/content'
export async function getStaticPaths() {
// 定义路径数组的类型
type PathItem = {
params: { tags_tag: string }
props: { tag: string, lang: string }
@ -13,26 +13,24 @@ export async function getStaticPaths() {
const paths: PathItem[] = []
// 默认语言的标签页面 (没有语言前缀)
// Default locale
const defaultTags = await getAllTags(defaultLocale)
defaultTags.forEach((tag: string) => {
paths.push({
params: { tags_tag: `tags/${tag}` },
params: { tags_tag: `tags/${tag}/` },
props: { tag, lang: defaultLocale },
})
})
// 更多语言的标签页面 (有语言前缀)
for (const lang of allLocales) {
if (lang !== defaultLocale) {
const langTags = await getAllTags(lang)
langTags.forEach((tag: string) => {
paths.push({
params: { tags_tag: `${lang}/tags/${tag}` },
props: { tag, lang },
})
// More locales
for (const lang of moreLocales) {
const langTags = await getAllTags(lang)
langTags.forEach((tag: string) => {
paths.push({
params: { tags_tag: `${lang}/tags/${tag}/` },
props: { tag, lang },
})
}
})
}
return paths
@ -42,22 +40,16 @@ const { tag, lang } = Astro.props
const posts = await getPostsByTag(tag, lang)
const allTags = await getAllTags(lang)
// 获取当前标签在每种语言下是否有文章
// Check if tag has posts in each language, return language code if exists, null if not
const tagSupportedLangs = await Promise.all(
allLocales.map(async (locale) => {
const postsInLang = await getPostsByTag(tag, locale)
return postsInLang.length > 0 ? locale : null
}),
)
// 过滤出支持当前标签的语言列表
const supportedLangs = tagSupportedLangs.filter(Boolean) as string[]
// 构建标签链接
function getTagUrl(tagName: string): string {
return lang === defaultLocale
? `/tags/${tagName}/`
: `/${lang}/tags/${tagName}/`
}
// Filter to get supported languages
const supportedLangs = tagSupportedLangs.filter(Boolean) as string[]
---
<Layout supportedLangs={supportedLangs}>
@ -65,7 +57,7 @@ function getTagUrl(tagName: string): string {
<div class="uno-tags-wrapper">
{allTags.map(tagName => (
<a
href={getTagUrl(tagName)}
href={getTagPath(tagName, lang)}
class={`uno-tags-style ${
tag === tagName
? 'border-secondary/75 text-primary'

View file

@ -18,8 +18,12 @@ async function getPostMeta(post: CollectionEntry<'posts'>): Promise<Post> {
return { ...post, remarkPluginFrontmatter }
}
// Check if the slug is duplicated under the same language
export async function checkSlugDuplication(posts: Post[]): Promise<string[]> {
/**
* Check if the post slug is duplicated under the same language
* @param posts Array of blog posts
* @returns Array of duplicate slugs with language information
*/
export async function checkPostSlugDuplication(posts: Post[]): Promise<string[]> {
const slugMap = new Map<string, Set<string>>()
const duplicates: string[] = []
@ -33,7 +37,12 @@ export async function checkSlugDuplication(posts: Post[]): Promise<string[]> {
const slugSet = slugMap.get(lang)!
if (slugSet.has(slug)) {
duplicates.push(`Duplicate slug "${slug}" found in language "${lang || 'default'}"`)
if (!lang) {
duplicates.push(`Duplicate slug "${slug}" found in universal post (applies to all languages)`)
}
else {
duplicates.push(`Duplicate slug "${slug}" found in "${lang}" language post`)
}
}
else {
slugSet.add(slug)