mirror of
https://github.com/reonokiy/blog.nokiy.net.git
synced 2025-06-16 03:32:51 +02:00
✨ feat: complete language switching functionality and centralized page routing
This commit is contained in:
parent
4651828dd1
commit
05d3a8034b
26 changed files with 253 additions and 146 deletions
|
@ -1,12 +1,13 @@
|
||||||
|
// Astro integrations
|
||||||
import mdx from '@astrojs/mdx'
|
import mdx from '@astrojs/mdx'
|
||||||
import partytown from '@astrojs/partytown'
|
import partytown from '@astrojs/partytown'
|
||||||
import sitemap from '@astrojs/sitemap'
|
import sitemap from '@astrojs/sitemap'
|
||||||
import { transformerCopyButton } from '@rehype-pretty/transformers'
|
import { transformerCopyButton } from '@rehype-pretty/transformers'
|
||||||
import compress from 'astro-compress'
|
import compress from 'astro-compress'
|
||||||
import robotsTxt from 'astro-robots-txt'
|
import robotsTxt from 'astro-robots-txt'
|
||||||
import { defineConfig } from 'astro/config'
|
|
||||||
|
|
||||||
// Rehype plugins
|
// Rehype plugins (HTML processors)
|
||||||
|
import { defineConfig } from 'astro/config'
|
||||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
|
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
|
||||||
import rehypeComponents from 'rehype-components'
|
import rehypeComponents from 'rehype-components'
|
||||||
import rehypeExternalLinks from 'rehype-external-links'
|
import rehypeExternalLinks from 'rehype-external-links'
|
||||||
|
@ -14,21 +15,21 @@ import rehypeKatex from 'rehype-katex'
|
||||||
import rehypePrettyCode from 'rehype-pretty-code'
|
import rehypePrettyCode from 'rehype-pretty-code'
|
||||||
import rehypeSlug from 'rehype-slug'
|
import rehypeSlug from 'rehype-slug'
|
||||||
|
|
||||||
// Remark plugins
|
// Remark plugins (Markdown processors)
|
||||||
import remarkDirective from 'remark-directive'
|
import remarkDirective from 'remark-directive'
|
||||||
import remarkGithubAdmonitionsToDirectives from 'remark-github-admonitions-to-directives'
|
import remarkGithubAdmonitionsToDirectives from 'remark-github-admonitions-to-directives'
|
||||||
import remarkMath from 'remark-math'
|
import remarkMath from 'remark-math'
|
||||||
import remarkSectionize from 'remark-sectionize'
|
import remarkSectionize from 'remark-sectionize'
|
||||||
import UnoCSS from 'unocss/astro'
|
|
||||||
import { themeConfig } from './src/config.js'
|
|
||||||
|
|
||||||
// Local plugins
|
// Project configuration and utilities
|
||||||
import { AdmonitionComponent } from './src/plugins/rehype-component-admonition.js'
|
import UnoCSS from 'unocss/astro'
|
||||||
import { GithubCardComponent } from './src/plugins/rehype-component-github-card.js'
|
import { themeConfig } from './src/config'
|
||||||
import { parseDirectiveNode } from './src/plugins/remark-directive-rehype.js'
|
import { langMap } from './src/i18n/ui'
|
||||||
import { remarkExcerpt } from './src/plugins/remark-excerpt.js'
|
import { AdmonitionComponent } from './src/plugins/rehype-component-admonition'
|
||||||
import { remarkReadingTime } from './src/plugins/remark-reading-time.js'
|
import { GithubCardComponent } from './src/plugins/rehype-component-github-card'
|
||||||
import { langMap } from './src/utils/i18n/ui.js'
|
import { parseDirectiveNode } from './src/plugins/remark-directive-rehype'
|
||||||
|
import { remarkExcerpt } from './src/plugins/remark-excerpt'
|
||||||
|
import { remarkReadingTime } from './src/plugins/remark-reading-time'
|
||||||
|
|
||||||
const url = themeConfig.site.url
|
const url = themeConfig.site.url
|
||||||
const { light, dark } = themeConfig.color
|
const { light, dark } = themeConfig.color
|
||||||
|
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
|
@ -1623,8 +1623,8 @@ packages:
|
||||||
duplexer@0.1.2:
|
duplexer@0.1.2:
|
||||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||||
|
|
||||||
electron-to-chromium@1.5.117:
|
electron-to-chromium@1.5.118:
|
||||||
resolution: {integrity: sha512-G4+CYIJBiQ72N0gi868tmG4WsD8bwLE9XytBdfgXO5zdlTlvOP2ABzWYILYxCIHmsbm2HjBSgm/E/H/QfcnIyQ==}
|
resolution: {integrity: sha512-yNDUus0iultYyVoEFLnQeei7LOQkL8wg8GQpkPCRrOlJXlcCwa6eGKZkxQ9ciHsqZyYbj8Jd94X1CTPzGm+uIA==}
|
||||||
|
|
||||||
emmet@2.4.11:
|
emmet@2.4.11:
|
||||||
resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==}
|
resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==}
|
||||||
|
@ -5221,7 +5221,7 @@ snapshots:
|
||||||
browserslist@4.24.4:
|
browserslist@4.24.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001704
|
caniuse-lite: 1.0.30001704
|
||||||
electron-to-chromium: 1.5.117
|
electron-to-chromium: 1.5.118
|
||||||
node-releases: 2.0.19
|
node-releases: 2.0.19
|
||||||
update-browserslist-db: 1.1.3(browserslist@4.24.4)
|
update-browserslist-db: 1.1.3(browserslist@4.24.4)
|
||||||
|
|
||||||
|
@ -5480,7 +5480,7 @@ snapshots:
|
||||||
|
|
||||||
duplexer@0.1.2: {}
|
duplexer@0.1.2: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.117: {}
|
electron-to-chromium@1.5.118: {}
|
||||||
|
|
||||||
emmet@2.4.11:
|
emmet@2.4.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import { themeConfig } from '@/config'
|
import { themeConfig } from '@/config'
|
||||||
import { getWalineLang } from '@/utils/i18n/ui'
|
import { getWalineLang } from '@/i18n/ui'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
serverURL = '',
|
serverURL = '',
|
||||||
|
@ -33,8 +33,7 @@ const {
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Not use 'is:inline' or 'define:vars' -->
|
<!-- Not use is:inline or define:vars -->
|
||||||
<!-- 'define:vars' ≈ 'is:inline' -->
|
|
||||||
<script>
|
<script>
|
||||||
import { init } from '@waline/client'
|
import { init } from '@waline/client'
|
||||||
import '@waline/client/style'
|
import '@waline/client/style'
|
||||||
|
|
|
@ -5,9 +5,10 @@ import themeConfig from '@/config'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class?: string
|
class?: string
|
||||||
|
supportedLangs?: string[] // 文章支持的语言列表
|
||||||
}
|
}
|
||||||
|
|
||||||
const { class: className } = Astro.props
|
const { class: className, supportedLangs = [] } = Astro.props
|
||||||
const { author } = themeConfig.site
|
const { author } = themeConfig.site
|
||||||
const { links, startYear } = themeConfig.footer
|
const { links, startYear } = themeConfig.footer
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ const year = Number(startYear) === currentYear
|
||||||
<!-- only show on desktop -->
|
<!-- only show on desktop -->
|
||||||
<div class="mb-11.5 ml-1.5 hidden gap-7 lg:flex">
|
<div class="mb-11.5 ml-1.5 hidden gap-7 lg:flex">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher supportedLangs={supportedLangs} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import themeConfig from '@/config'
|
import themeConfig from '@/config'
|
||||||
import { getPagePath } from '@/utils/i18n/path'
|
import { getPagePath } from '@/i18n/path'
|
||||||
|
|
||||||
const { title, subtitle } = themeConfig.site
|
const { title, subtitle } = themeConfig.site
|
||||||
const { titleSpace } = themeConfig.global
|
const { titleSpace } = themeConfig.global
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import themeConfig from '@/config'
|
import themeConfig from '@/config'
|
||||||
import { getPagePath } from '@/utils/i18n/path'
|
import { getPagePath } from '@/i18n/path'
|
||||||
|
|
||||||
const { title, subtitle } = themeConfig.site
|
const { title, subtitle } = themeConfig.site
|
||||||
const { titleSpace } = themeConfig.global
|
const { titleSpace } = themeConfig.global
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import { getPagePath } from '@/utils/i18n/path'
|
import { getPagePath } from '@/i18n/path'
|
||||||
import { ui } from '@/utils/i18n/ui'
|
import { ui } from '@/i18n/ui'
|
||||||
|
|
||||||
const currentPath = Astro.url.pathname
|
const currentPath = Astro.url.pathname
|
||||||
const { currentLang, isHome, isPost, isTag, isAbout, getLocalizedPath }
|
const { currentLang, isHome, isPost, isTag, isAbout, getLocalizedPath }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import { themeConfig } from '@/config'
|
import { themeConfig } from '@/config'
|
||||||
import { isPostPage } from '@/utils/i18n/path'
|
import { isPostPage } from '@/i18n/path'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
date: Date
|
date: Date
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
---
|
---
|
||||||
import { getNextLangUrl } from '@/utils/i18n/lang'
|
import { getNextLangUrl, getPostNextLangUrl } from '@/i18n/lang'
|
||||||
|
import { isPostPage } from '@/i18n/path'
|
||||||
|
|
||||||
// 获取当前路径
|
interface Props {
|
||||||
|
supportedLangs?: string[] // 文章支持的语言列表
|
||||||
|
}
|
||||||
|
|
||||||
|
const { supportedLangs = [] } = Astro.props
|
||||||
const currentPath = Astro.url.pathname
|
const currentPath = Astro.url.pathname
|
||||||
|
const isPost = isPostPage(currentPath)
|
||||||
|
|
||||||
// 直接获取下一个语言的URL
|
// 根据页面类型选择不同的URL获取函数
|
||||||
const nextUrl = getNextLangUrl(currentPath)
|
const nextUrl = isPost && supportedLangs.length > 0
|
||||||
|
? getPostNextLangUrl(currentPath, supportedLangs)
|
||||||
|
: getNextLangUrl(currentPath)
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { themeConfig } from '@/config'
|
import themeConfig from '@/config'
|
||||||
|
|
||||||
|
// 从配置中获取默认语言和更多语言配置
|
||||||
|
const defaultLocale = themeConfig.global.locale
|
||||||
|
// const moreLocale = themeConfig.global.moreLocale
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取下一个语言代码
|
* 获取下一个语言代码
|
||||||
|
@ -89,6 +93,24 @@ export function getLangFromPath(currentPath: string): string {
|
||||||
return currentLang
|
return currentLang
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文章支持的语言
|
||||||
|
* @param lang 文章的语言属性
|
||||||
|
* @returns 支持的语言数组
|
||||||
|
*/
|
||||||
|
export function getSupportedLangs(lang?: string): string[] {
|
||||||
|
const defaultLocale = themeConfig.global.locale
|
||||||
|
const allLocales = [defaultLocale, ...themeConfig.global.moreLocale]
|
||||||
|
|
||||||
|
// 如果指定了语言且不为空
|
||||||
|
if (lang && typeof lang === 'string' && lang.trim() !== '') {
|
||||||
|
return [lang]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则返回所有支持的语言
|
||||||
|
return allLocales
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 直接从当前路径获取下一个语言的URL
|
* 直接从当前路径获取下一个语言的URL
|
||||||
* @param currentPath 当前页面路径
|
* @param currentPath 当前页面路径
|
||||||
|
@ -104,3 +126,36 @@ export function getNextLangUrl(currentPath: string): string {
|
||||||
// 构建下一个语言的URL
|
// 构建下一个语言的URL
|
||||||
return buildNextLangUrl(currentPath, currentLang, nextLang)
|
return buildNextLangUrl(currentPath, currentLang, nextLang)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据支持的语言列表获取下一个语言的URL
|
||||||
|
* @param currentPath 当前路径
|
||||||
|
* @param supportedLangs 文章支持的语言列表
|
||||||
|
* @returns 下一个可用语言的URL
|
||||||
|
*/
|
||||||
|
export function getPostNextLangUrl(currentPath: string, supportedLangs: string[]): string {
|
||||||
|
// 从路径提取当前语言
|
||||||
|
const currentLang = getLangFromPath(currentPath)
|
||||||
|
|
||||||
|
// 如果没有提供支持的语言或列表为空,使用普通的语言切换
|
||||||
|
if (!supportedLangs || supportedLangs.length === 0) {
|
||||||
|
return getNextLangUrl(currentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到当前语言在支持的语言中的索引
|
||||||
|
const currentIndex = supportedLangs.indexOf(currentLang)
|
||||||
|
|
||||||
|
// 如果当前语言不在支持的语言中,或者路径是根路径,返回第一个支持的语言
|
||||||
|
if (currentIndex === -1 || currentPath === '/') {
|
||||||
|
const nextLang = supportedLangs[0]
|
||||||
|
// 如果下一个语言是默认语言,返回根路径
|
||||||
|
return nextLang === defaultLocale ? '/' : `/${nextLang}/`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算下一个语言的索引
|
||||||
|
const nextIndex = (currentIndex + 1) % supportedLangs.length
|
||||||
|
const nextLang = supportedLangs[nextIndex]
|
||||||
|
|
||||||
|
// 构建下一个语言的URL
|
||||||
|
return buildNextLangUrl(currentPath, currentLang, nextLang)
|
||||||
|
}
|
131
src/i18n/route.ts
Normal file
131
src/i18n/route.ts
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import type { CollectionEntry } from 'astro:content'
|
||||||
|
import { themeConfig } from '@/config'
|
||||||
|
|
||||||
|
// 默认语言和更多语言
|
||||||
|
const defaultLocale = themeConfig.global.locale
|
||||||
|
const moreLocale = themeConfig.global.moreLocale
|
||||||
|
// 所有支持的语言
|
||||||
|
const allLocales = [defaultLocale, ...moreLocale]
|
||||||
|
|
||||||
|
// 生成默认语言标签页面的路径配置
|
||||||
|
export function generateTagPaths(tags: string[]) {
|
||||||
|
return tags.map(tag => ({
|
||||||
|
params: { tag },
|
||||||
|
props: { tag },
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成默认语言文章页面的路径配置
|
||||||
|
export function generatePostPaths(posts: CollectionEntry<'posts'>[]) {
|
||||||
|
// 创建slug到语言的映射
|
||||||
|
const slugToLangs: Record<string, string[]> = {}
|
||||||
|
|
||||||
|
// 填充映射
|
||||||
|
posts.forEach((post) => {
|
||||||
|
const slug = post.data.abbrlink || post.slug
|
||||||
|
const lang = post.data.lang || defaultLocale
|
||||||
|
|
||||||
|
// 如果文章没有指定语言,初始化为所有支持的语言
|
||||||
|
if (!slugToLangs[slug]) {
|
||||||
|
if (!post.data.lang) {
|
||||||
|
slugToLangs[slug] = [...allLocales] // 文章支持所有语言
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
slugToLangs[slug] = [defaultLocale] // 仅默认语言和指定语言
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slugToLangs[slug].includes(lang)) {
|
||||||
|
slugToLangs[slug].push(lang)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return posts.map(post => ({
|
||||||
|
params: {
|
||||||
|
slug: post.data.abbrlink || post.slug,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
post,
|
||||||
|
supportedLangs: slugToLangs[post.data.abbrlink || post.slug] || [],
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成更多语言静态路径
|
||||||
|
export function generateMultiLangPaths() {
|
||||||
|
return moreLocale.map(lang => ({
|
||||||
|
params: { lang },
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成更多语言标签页面的路径配置
|
||||||
|
export function generateMultiLangTagPaths(tags: string[]) {
|
||||||
|
return moreLocale.flatMap(lang => (
|
||||||
|
tags.map(tag => ({
|
||||||
|
params: { lang, tag },
|
||||||
|
props: { tag },
|
||||||
|
}))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成更多语言文章页面的路径配置
|
||||||
|
export function generateMultiLangPostPaths(posts: CollectionEntry<'posts'>[]) {
|
||||||
|
// 创建slug到语言的映射
|
||||||
|
const slugToLangs: Record<string, string[]> = {}
|
||||||
|
|
||||||
|
// 填充映射
|
||||||
|
posts.forEach((post) => {
|
||||||
|
const slug = post.data.abbrlink || post.slug
|
||||||
|
const lang = post.data.lang || defaultLocale
|
||||||
|
|
||||||
|
// 如果文章没有指定语言,初始化为所有支持的语言
|
||||||
|
if (!slugToLangs[slug]) {
|
||||||
|
if (!post.data.lang) {
|
||||||
|
slugToLangs[slug] = [...allLocales] // 文章支持所有语言
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
slugToLangs[slug] = [defaultLocale] // 仅默认语言和指定语言
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slugToLangs[slug].includes(lang)) {
|
||||||
|
slugToLangs[slug].push(lang)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
interface PathResult {
|
||||||
|
params: {
|
||||||
|
lang: string
|
||||||
|
slug: string
|
||||||
|
}
|
||||||
|
props: {
|
||||||
|
post: CollectionEntry<'posts'>
|
||||||
|
supportedLangs: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts.flatMap((post) => {
|
||||||
|
const result: PathResult[] = []
|
||||||
|
const slug = post.data.abbrlink || post.slug
|
||||||
|
|
||||||
|
// 确定文章的语言支持
|
||||||
|
const postLang = post.data.lang && typeof post.data.lang === 'string' && post.data.lang.trim() !== ''
|
||||||
|
? [post.data.lang]
|
||||||
|
: moreLocale
|
||||||
|
|
||||||
|
// 获取这篇文章支持的所有语言
|
||||||
|
const supportedLangs = slugToLangs[slug] || []
|
||||||
|
|
||||||
|
// 添加非默认语言路径
|
||||||
|
postLang.forEach((lang) => {
|
||||||
|
if (lang !== defaultLocale) {
|
||||||
|
result.push({
|
||||||
|
params: { lang, slug },
|
||||||
|
props: { post, supportedLangs },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ import LanguageSwitcher from '@/components/Widgets/LanguageSwitcher.astro'
|
||||||
// import Scrollbar from '@/components/Scrollbar.astro'
|
// import Scrollbar from '@/components/Scrollbar.astro'
|
||||||
import ThemeToggle from '@/components/Widgets/ThemeToggle.astro'
|
import ThemeToggle from '@/components/Widgets/ThemeToggle.astro'
|
||||||
import themeConfig from '@/config'
|
import themeConfig from '@/config'
|
||||||
|
import { getPagePath } from '@/i18n/path'
|
||||||
import Head from '@/layouts/Head.astro'
|
import Head from '@/layouts/Head.astro'
|
||||||
import { getPagePath } from '@/utils/i18n/path'
|
|
||||||
|
|
||||||
import '@/styles/font.css'
|
import '@/styles/font.css'
|
||||||
import '@/styles/global.css'
|
import '@/styles/global.css'
|
||||||
|
@ -20,9 +20,10 @@ interface Props {
|
||||||
postTitle?: string
|
postTitle?: string
|
||||||
postDescription?: string
|
postDescription?: string
|
||||||
postSlug?: string
|
postSlug?: string
|
||||||
|
supportedLangs?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const { postTitle, postDescription, postSlug } = Astro.props
|
const { postTitle, postDescription, postSlug, supportedLangs = [] } = Astro.props
|
||||||
const { isHome, isPost } = getPagePath(Astro.url.pathname)
|
const { isHome, isPost } = getPagePath(Astro.url.pathname)
|
||||||
const { light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
|
const { light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
|
||||||
const fontStyle = themeConfig.global.fontStyle === 'serif' ? 'font-serif' : 'font-sans'
|
const fontStyle = themeConfig.global.fontStyle === 'serif' ? 'font-serif' : 'font-sans'
|
||||||
|
@ -55,7 +56,7 @@ const footerMarginClass = isPost && themeConfig.comment?.waline?.serverURL
|
||||||
<MainHeader />
|
<MainHeader />
|
||||||
<Navigation />
|
<Navigation />
|
||||||
<!-- only show footer on desktop-->
|
<!-- only show footer on desktop-->
|
||||||
<Footer class="fixed hidden lg:block" />
|
<Footer class="fixed hidden lg:block" supportedLangs={supportedLangs} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- show simple header on mobile for post pages -->
|
<!-- show simple header on mobile for post pages -->
|
||||||
|
@ -67,7 +68,7 @@ const footerMarginClass = isPost && themeConfig.comment?.waline?.serverURL
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- show footer on mobile -->
|
<!-- show footer on mobile -->
|
||||||
<Footer class={`block lg:hidden ${footerMarginClass}`} />
|
<Footer class={`block lg:hidden ${footerMarginClass}`} supportedLangs={supportedLangs} />
|
||||||
</div>
|
</div>
|
||||||
<!-- <Scrollbar /> -->
|
<!-- <Scrollbar /> -->
|
||||||
<!-- <BackToTop /> -->
|
<!-- <BackToTop /> -->
|
||||||
|
@ -75,7 +76,7 @@ const footerMarginClass = isPost && themeConfig.comment?.waline?.serverURL
|
||||||
|
|
||||||
<!-- only show on mobile (fix position issue on ios / fix right distance)-->
|
<!-- only show on mobile (fix position issue on ios / fix right distance)-->
|
||||||
<div class="absolute right-7.25vw top-13.5 flex gap-6 [@supports(-webkit-touch-callout:none)]:top-12.5 min-[823px]:right-[calc(50vw-22rem)] lg:hidden">
|
<div class="absolute right-7.25vw top-13.5 flex gap-6 [@supports(-webkit-touch-callout:none)]:top-12.5 min-[823px]:right-[calc(50vw-22rem)] lg:hidden">
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher supportedLangs={supportedLangs} />
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
|
import { generateMultiLangPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { generateLanguagePaths } from '@/utils/i18n/route'
|
|
||||||
|
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return generateLanguagePaths()
|
return generateMultiLangPaths()
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { APIContext } from 'astro'
|
import type { APIContext } from 'astro'
|
||||||
import themeConfig from '@/config'
|
import themeConfig from '@/config'
|
||||||
|
import { generateMultiLangPaths } from '@/i18n/route'
|
||||||
import { generateRSS } from '@/utils/rss'
|
import { generateRSS } from '@/utils/rss'
|
||||||
|
|
||||||
const { moreLocale } = themeConfig.global
|
const { moreLocale } = themeConfig.global
|
||||||
|
@ -9,7 +10,7 @@ type SupportedLanguage = typeof moreLocale[number]
|
||||||
|
|
||||||
// Generate static paths for all supported languages
|
// Generate static paths for all supported languages
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return moreLocale.map((lang: SupportedLanguage) => ({ params: { lang } }))
|
return generateMultiLangPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function GET({ params }: APIContext) {
|
export async function GET({ params }: APIContext) {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
---
|
---
|
||||||
import PostList from '@/components/PostList.astro'
|
import PostList from '@/components/PostList.astro'
|
||||||
import { themeConfig } from '@/config'
|
import { generateMultiLangPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { getPinnedPosts, getPostsByYear } from '@/utils/content'
|
import { getPinnedPosts, getPostsByYear } from '@/utils/content'
|
||||||
|
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return themeConfig.global.moreLocale.map(lang => ({
|
return generateMultiLangPaths()
|
||||||
params: { lang },
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { lang } = Astro.params
|
const { lang } = Astro.params
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
import Waline from '@/components/Comments/Waline.astro'
|
import Waline from '@/components/Comments/Waline.astro'
|
||||||
|
import { generateMultiLangPostPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { checkSlugDuplication } from '@/utils/content'
|
import { checkSlugDuplication } from '@/utils/content'
|
||||||
import { generateDescription } from '@/utils/description'
|
import { generateDescription } from '@/utils/description'
|
||||||
import { generateMultiLangPostPaths } from '@/utils/i18n/route'
|
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
|
@ -17,7 +17,7 @@ export async function getStaticPaths() {
|
||||||
return generateMultiLangPostPaths(posts)
|
return generateMultiLangPostPaths(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { post } = Astro.props
|
const { post, supportedLangs = [] } = Astro.props
|
||||||
const description = generateDescription(post)
|
const description = generateDescription(post)
|
||||||
const { Content, remarkPluginFrontmatter } = await post.render()
|
const { Content, remarkPluginFrontmatter } = await post.render()
|
||||||
---
|
---
|
||||||
|
@ -26,6 +26,7 @@ const { Content, remarkPluginFrontmatter } = await post.render()
|
||||||
postTitle={post.data.title}
|
postTitle={post.data.title}
|
||||||
postDescription={description}
|
postDescription={description}
|
||||||
postSlug={post.slug}
|
postSlug={post.slug}
|
||||||
|
supportedLangs={supportedLangs}
|
||||||
>
|
>
|
||||||
<article>
|
<article>
|
||||||
<h1>{post.data.title}</h1>
|
<h1>{post.data.title}</h1>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { APIContext } from 'astro'
|
import type { APIContext } from 'astro'
|
||||||
import themeConfig from '@/config'
|
import themeConfig from '@/config'
|
||||||
|
import { generateMultiLangPaths } from '@/i18n/route'
|
||||||
import { generateRSS } from '@/utils/rss'
|
import { generateRSS } from '@/utils/rss'
|
||||||
|
|
||||||
const { moreLocale } = themeConfig.global
|
const { moreLocale } = themeConfig.global
|
||||||
|
@ -9,7 +10,7 @@ type SupportedLanguage = typeof moreLocale[number]
|
||||||
|
|
||||||
// Generate static paths for all supported languages
|
// Generate static paths for all supported languages
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return moreLocale.map((lang: SupportedLanguage) => ({ params: { lang } }))
|
return generateMultiLangPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function GET({ params }: APIContext) {
|
export async function GET({ params }: APIContext) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
|
import { generateMultiLangTagPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { getAllTags, getPostsByTag } from '@/utils/content'
|
import { getAllTags, getPostsByTag } from '@/utils/content'
|
||||||
import { generateMultiLangTagPaths } from '@/utils/i18n/route'
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const tags = await getAllTags()
|
const tags = await getAllTags()
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
|
import { generateMultiLangPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { getAllTags } from '@/utils/content'
|
import { getAllTags } from '@/utils/content'
|
||||||
import { generateLanguagePaths } from '@/utils/i18n/route'
|
|
||||||
|
|
||||||
export function getStaticPaths() {
|
export function getStaticPaths() {
|
||||||
return generateLanguagePaths()
|
return generateMultiLangPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { lang } = Astro.params
|
const { lang } = Astro.params
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
import Comments from '@/components/Comments/index.astro'
|
import Comments from '@/components/Comments/index.astro'
|
||||||
import PostTime from '@/components/PostTime.astro'
|
import PostTime from '@/components/PostTime.astro'
|
||||||
|
import { generatePostPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { checkSlugDuplication } from '@/utils/content'
|
import { checkSlugDuplication } from '@/utils/content'
|
||||||
import { generateDescription } from '@/utils/description'
|
import { generateDescription } from '@/utils/description'
|
||||||
import { generatePostPaths } from '@/utils/i18n/route'
|
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
|
@ -18,7 +18,7 @@ export async function getStaticPaths() {
|
||||||
return generatePostPaths(posts)
|
return generatePostPaths(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { post } = Astro.props
|
const { post, supportedLangs = [] } = Astro.props
|
||||||
const description = generateDescription(post)
|
const description = generateDescription(post)
|
||||||
const { Content, remarkPluginFrontmatter } = await post.render()
|
const { Content, remarkPluginFrontmatter } = await post.render()
|
||||||
---
|
---
|
||||||
|
@ -27,6 +27,7 @@ const { Content, remarkPluginFrontmatter } = await post.render()
|
||||||
postTitle={post.data.title}
|
postTitle={post.data.title}
|
||||||
postDescription={description}
|
postDescription={description}
|
||||||
postSlug={post.slug}
|
postSlug={post.slug}
|
||||||
|
supportedLangs={supportedLangs}
|
||||||
>
|
>
|
||||||
<article class="heti mb-12.6">
|
<article class="heti mb-12.6">
|
||||||
<h1 class="post-title">
|
<h1 class="post-title">
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
---
|
---
|
||||||
import PostList from '@/components/PostList.astro'
|
import PostList from '@/components/PostList.astro'
|
||||||
|
import { generateTagPaths } from '@/i18n/route'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { getAllTags, getPostsByTag } from '@/utils/content'
|
import { getAllTags, getPostsByTag } from '@/utils/content'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const tags = await getAllTags()
|
const tags = await getAllTags()
|
||||||
return tags.map(tag => ({
|
return generateTagPaths(tags)
|
||||||
params: { tag },
|
|
||||||
props: { tag },
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tag } = Astro.props
|
const { tag } = Astro.props
|
||||||
|
|
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
|
@ -1,4 +1,4 @@
|
||||||
import type { langPath } from '@/utils/i18n/ui'
|
import type { langPath } from '@/i18n/ui'
|
||||||
|
|
||||||
type Exclude<T, U> = T extends U ? never : T
|
type Exclude<T, U> = T extends U ? never : T
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import themeConfig from '@/config'
|
import themeConfig from '@/config'
|
||||||
import { langPath } from '@/utils/i18n/ui'
|
import { langPath } from '@/i18n/ui'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
|
|
||||||
// Type definitions
|
// Type definitions
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
import type { CollectionEntry } from 'astro:content'
|
|
||||||
import { themeConfig } from '@/config'
|
|
||||||
|
|
||||||
// 获取默认语言
|
|
||||||
const defaultLocale = themeConfig.global.locale
|
|
||||||
|
|
||||||
export function generateLanguagePaths() {
|
|
||||||
return themeConfig.global.moreLocale.map(lang => ({
|
|
||||||
params: { lang },
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generatePostPaths(posts: CollectionEntry<'posts'>[]) {
|
|
||||||
return posts.map(post => ({
|
|
||||||
params: {
|
|
||||||
slug: post.data.abbrlink || post.slug,
|
|
||||||
},
|
|
||||||
props: { post },
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateMultiLangPostPaths(posts: CollectionEntry<'posts'>[]) {
|
|
||||||
interface PathResult {
|
|
||||||
params: {
|
|
||||||
lang: string
|
|
||||||
slug: string
|
|
||||||
}
|
|
||||||
props: {
|
|
||||||
post: CollectionEntry<'posts'>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: PathResult[] = []
|
|
||||||
|
|
||||||
posts.forEach((post) => {
|
|
||||||
// 确定这篇文章应该生成哪些语言版本
|
|
||||||
let postLangs: string[] = themeConfig.global.moreLocale
|
|
||||||
|
|
||||||
if (post.data.lang && typeof post.data.lang === 'string' && post.data.lang.trim() !== '') {
|
|
||||||
// 如果lang是单个字符串,转为数组
|
|
||||||
postLangs = [post.data.lang]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理非默认语言的路径
|
|
||||||
postLangs.forEach((lang) => {
|
|
||||||
// 跳过默认语言,它将通过 generatePostPaths 生成
|
|
||||||
if (lang !== defaultLocale) {
|
|
||||||
result.push({
|
|
||||||
params: {
|
|
||||||
lang,
|
|
||||||
slug: post.data.abbrlink || post.slug,
|
|
||||||
},
|
|
||||||
props: { post },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 如果文章支持默认语言,则生成无语言代码的路径
|
|
||||||
// 默认语言条件:未指定lang属性,或lang属性等于defaultLocale
|
|
||||||
const supportsDefaultLang = !post.data.lang
|
|
||||||
|| (typeof post.data.lang === 'string' && post.data.lang === defaultLocale)
|
|
||||||
|
|
||||||
if (supportsDefaultLang) {
|
|
||||||
// 默认语言的路径不包含语言代码,在这里用特殊参数标记
|
|
||||||
// 这将由 [slug].astro 页面处理,不在URL中显示语言代码
|
|
||||||
result.push({
|
|
||||||
params: {
|
|
||||||
lang: 'default',
|
|
||||||
slug: post.data.abbrlink || post.slug,
|
|
||||||
},
|
|
||||||
props: { post },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateMultiLangTagPaths(tags: string[]) {
|
|
||||||
return themeConfig.global.moreLocale.flatMap(lang =>
|
|
||||||
// 跳过默认语言,它将通过其他路径生成
|
|
||||||
lang !== defaultLocale
|
|
||||||
? tags.map(tag => ({
|
|
||||||
params: { lang, tag },
|
|
||||||
props: { tag },
|
|
||||||
}))
|
|
||||||
: [],
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue