@jet-w/astro-blog 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Z3O3JK56.js → chunk-CEZBWSMU.js} +34 -5
- package/dist/index.js +1 -1
- package/dist/utils/i18n.d.ts +34 -2
- package/dist/utils/i18n.js +6 -2
- package/package.json +1 -1
- package/src/components/blog/Hero.astro +13 -6
- package/src/components/blog/PostCard.astro +1 -1
- package/src/components/home/FeaturedPostsSection.astro +10 -4
- package/src/components/home/QuickNavSection.astro +10 -4
- package/src/components/home/RecentPostsSection.astro +10 -4
- package/src/components/home/StatsSection.astro +9 -3
- package/src/components/layout/Footer.astro +17 -7
- package/src/components/layout/Header.astro +20 -13
- package/src/components/layout/Sidebar.astro +12 -6
- package/src/components/media/Slides.astro +22 -6
- package/src/components/ui/LanguageSwitcher.vue +24 -12
- package/src/components/ui/MobileMenu.vue +20 -3
- package/src/layouts/BaseLayout.astro +19 -8
- package/src/layouts/PageLayout.astro +9 -3
- package/src/layouts/SlidesLayout.astro +34 -16
- package/src/pages/archives/[year]/[month]/page/[page].astro +42 -18
- package/src/pages/archives/[year]/[month].astro +8 -2
- package/src/pages/archives/index.astro +8 -3
- package/src/pages/categories/[category]/page/[page].astro +40 -18
- package/src/pages/categories/[category].astro +8 -2
- package/src/pages/categories/index.astro +8 -3
- package/src/pages/posts/[...slug].astro +8 -3
- package/src/pages/posts/index.astro +8 -3
- package/src/pages/posts/page/[page].astro +8 -2
- package/src/pages/search.astro +9 -3
- package/src/pages/slides/index.astro +8 -3
- package/src/pages/tags/[tag]/page/[page].astro +39 -17
- package/src/pages/tags/[tag].astro +8 -2
- package/src/pages/tags/index.astro +8 -3
- package/src/plugins/rehype-relative-links.mjs +90 -14
- package/src/utils/i18n.ts +83 -4
- package/templates/default/.claude/ralph-loop.local.md +4 -43
- package/templates/default/Makefile +37 -0
- package/templates/default/astro.config.mjs +7 -3
- package/templates/default/{package.dev.json → package.prod.json} +1 -1
- package/templates/default/package-lock.json +0 -9667
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<a
|
|
72
72
|
v-for="item in navigation"
|
|
73
73
|
:key="item.href"
|
|
74
|
-
:href="item.href"
|
|
74
|
+
:href="withBase(item.href)"
|
|
75
75
|
@click="isOpen = false"
|
|
76
76
|
class="flex items-center px-4 py-3 rounded-lg text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
|
|
77
77
|
:class="{ 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400': isActive(item.href) }"
|
|
@@ -98,16 +98,33 @@
|
|
|
98
98
|
</template>
|
|
99
99
|
|
|
100
100
|
<script setup lang="ts">
|
|
101
|
-
import { ref, onMounted, onUnmounted } from 'vue'
|
|
101
|
+
import { ref, onMounted, onUnmounted, withDefaults } from 'vue'
|
|
102
102
|
import type { NavigationItem } from '@jet-w/astro-blog/types'
|
|
103
103
|
import SearchBox from './SearchBox.vue'
|
|
104
104
|
import ThemeToggle from './ThemeToggle.vue'
|
|
105
105
|
|
|
106
106
|
interface Props {
|
|
107
107
|
navigation: NavigationItem[]
|
|
108
|
+
/** Base URL for the site (e.g., '/jet-w.astro-blog') */
|
|
109
|
+
base?: string
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
const props = defineProps<Props>()
|
|
112
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
113
|
+
base: '/',
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Helper function to add base URL to paths
|
|
117
|
+
function withBase(path: string): string {
|
|
118
|
+
const baseUrl = props.base?.replace(/\/$/, '') || '';
|
|
119
|
+
if (!baseUrl || baseUrl === '') {
|
|
120
|
+
return path;
|
|
121
|
+
}
|
|
122
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
123
|
+
if (normalizedPath === '/') {
|
|
124
|
+
return `${baseUrl}/`;
|
|
125
|
+
}
|
|
126
|
+
return `${baseUrl}${normalizedPath}`;
|
|
127
|
+
}
|
|
111
128
|
const isOpen = ref(false)
|
|
112
129
|
const currentPath = ref('')
|
|
113
130
|
const isMounted = ref(false)
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
getAlternateLinks,
|
|
11
11
|
getTextDirection,
|
|
12
12
|
isMultiLanguageEnabled,
|
|
13
|
+
withBase,
|
|
14
|
+
removeBase,
|
|
13
15
|
t,
|
|
14
16
|
type I18nConfig,
|
|
15
17
|
} from '../utils/i18n';
|
|
@@ -39,8 +41,14 @@ const {
|
|
|
39
41
|
i18nConfig = virtualI18nConfig || defaultI18nConfig,
|
|
40
42
|
} = Astro.props;
|
|
41
43
|
|
|
42
|
-
// Get
|
|
43
|
-
const
|
|
44
|
+
// Get base URL for prefixing links
|
|
45
|
+
const base = import.meta.env.BASE_URL;
|
|
46
|
+
|
|
47
|
+
// Remove base URL from current path for locale detection
|
|
48
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
49
|
+
|
|
50
|
+
// Get current locale from URL (use path without base)
|
|
51
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
44
52
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
45
53
|
const localeData = localeConfig.locale;
|
|
46
54
|
const ui = localeConfig.ui;
|
|
@@ -54,9 +62,12 @@ const fullTitle = title === siteTitle ? title : `${title} | ${siteTitle}`;
|
|
|
54
62
|
const fullImage = new URL(image, Astro.site);
|
|
55
63
|
|
|
56
64
|
// Get alternate links for SEO (hreflang)
|
|
57
|
-
|
|
65
|
+
// We need to pass the pathname without base URL to getAlternateLinks
|
|
66
|
+
// Then the baseUrl for combining should include the base path
|
|
67
|
+
const siteUrl = Astro.site?.toString().replace(/\/$/, '') || '';
|
|
68
|
+
const baseUrlWithPath = base && base !== '/' ? `${siteUrl}${base.replace(/\/$/, '')}` : siteUrl;
|
|
58
69
|
const alternateLinks = isMultiLanguageEnabled(i18nConfig)
|
|
59
|
-
? getAlternateLinks(
|
|
70
|
+
? getAlternateLinks(pathWithoutBase, baseUrlWithPath, i18nConfig)
|
|
60
71
|
: [];
|
|
61
72
|
|
|
62
73
|
// Get locale prefix for RSS link
|
|
@@ -77,7 +88,7 @@ const faviconType = faviconSvgExists ? 'image/svg+xml' : faviconIcoExists ? 'ima
|
|
|
77
88
|
<meta charset="UTF-8" />
|
|
78
89
|
<meta name="description" content={description} />
|
|
79
90
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
80
|
-
<link rel="icon" type={faviconType} href={faviconHref} />
|
|
91
|
+
<link rel="icon" type={faviconType} href={withBase(faviconHref || '', base)} />
|
|
81
92
|
<meta name="generator" content={Astro.generator} />
|
|
82
93
|
|
|
83
94
|
<!-- SEO -->
|
|
@@ -115,7 +126,7 @@ const faviconType = faviconSvgExists ? 'image/svg+xml' : faviconIcoExists ? 'ima
|
|
|
115
126
|
<meta name="twitter:image" content={fullImage} />
|
|
116
127
|
|
|
117
128
|
<!-- RSS -->
|
|
118
|
-
<link rel="alternate" type="application/rss+xml" title={siteTitle} href={rssPath} />
|
|
129
|
+
<link rel="alternate" type="application/rss+xml" title={siteTitle} href={withBase(rssPath, base)} />
|
|
119
130
|
|
|
120
131
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
121
132
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
@@ -158,10 +169,10 @@ const faviconType = faviconSvgExists ? 'image/svg+xml' : faviconIcoExists ? 'ima
|
|
|
158
169
|
</div>
|
|
159
170
|
|
|
160
171
|
<!-- Mermaid -->
|
|
161
|
-
<script src=
|
|
172
|
+
<script src={withBase('/js/mermaid-container.js', base)} is:inline></script>
|
|
162
173
|
|
|
163
174
|
<!-- Tabs -->
|
|
164
|
-
<script src=
|
|
175
|
+
<script src={withBase('/js/tabs-init.js', base)} is:inline></script>
|
|
165
176
|
|
|
166
177
|
<!-- Store i18n data for client-side scripts -->
|
|
167
178
|
<script is:inline define:vars={{ locale: currentLocale, uiTranslations: ui }}>
|
|
@@ -6,7 +6,7 @@ import Sidebar from '../components/layout/Sidebar.astro';
|
|
|
6
6
|
import SidebarToggle from '../components/ui/SidebarToggle.vue';
|
|
7
7
|
import type { I18nConfig } from '../config/i18n';
|
|
8
8
|
import { defaultI18nConfig } from '../config/i18n';
|
|
9
|
-
import { getLocaleFromPath, getLocaleConfig } from '../utils/i18n';
|
|
9
|
+
import { getLocaleFromPath, getLocaleConfig, removeBase } from '../utils/i18n';
|
|
10
10
|
// Import i18n config from virtual module (injected by integration)
|
|
11
11
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
12
12
|
|
|
@@ -36,8 +36,14 @@ const {
|
|
|
36
36
|
i18nConfig = virtualI18nConfig || defaultI18nConfig,
|
|
37
37
|
} = Astro.props;
|
|
38
38
|
|
|
39
|
-
// Get
|
|
40
|
-
const
|
|
39
|
+
// Get base URL for prefixing links
|
|
40
|
+
const base = import.meta.env.BASE_URL;
|
|
41
|
+
|
|
42
|
+
// Remove base URL from current path for locale detection
|
|
43
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
44
|
+
|
|
45
|
+
// Get current locale (use path without base)
|
|
46
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
41
47
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
42
48
|
const ui = localeConfig.ui;
|
|
43
49
|
---
|
|
@@ -3,7 +3,7 @@ import { siteConfig } from '@jet-w/astro-blog/config';
|
|
|
3
3
|
import '@jet-w/astro-blog/styles/slides.css';
|
|
4
4
|
import type { I18nConfig } from '../config/i18n';
|
|
5
5
|
import { defaultI18nConfig } from '../config/i18n';
|
|
6
|
-
import { getLocaleFromPath, getLocaleConfig } from '../utils/i18n';
|
|
6
|
+
import { getLocaleFromPath, getLocaleConfig, removeBase } from '../utils/i18n';
|
|
7
7
|
|
|
8
8
|
export interface Props {
|
|
9
9
|
title: string;
|
|
@@ -33,8 +33,14 @@ const {
|
|
|
33
33
|
i18nConfig = defaultI18nConfig,
|
|
34
34
|
} = Astro.props;
|
|
35
35
|
|
|
36
|
-
// Get
|
|
37
|
-
const
|
|
36
|
+
// Get base URL for prefixing links
|
|
37
|
+
const base = import.meta.env.BASE_URL;
|
|
38
|
+
|
|
39
|
+
// Remove base URL from current path for locale detection
|
|
40
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
41
|
+
|
|
42
|
+
// Get current locale (use path without base)
|
|
43
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
38
44
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
39
45
|
const localeData = localeConfig.locale;
|
|
40
46
|
const localeSiteConfig = localeConfig.site;
|
|
@@ -65,7 +71,7 @@ const revealConfig = JSON.stringify({
|
|
|
65
71
|
<meta charset="UTF-8" />
|
|
66
72
|
<meta name="description" content={description} />
|
|
67
73
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
68
|
-
<link rel="icon" type="image/svg+xml" href=
|
|
74
|
+
<link rel="icon" type="image/svg+xml" href={`${base}favicon.svg`} />
|
|
69
75
|
<meta name="generator" content={Astro.generator} />
|
|
70
76
|
<title>{fullTitle}</title>
|
|
71
77
|
|
|
@@ -75,11 +81,11 @@ const revealConfig = JSON.stringify({
|
|
|
75
81
|
<meta property="og:description" content={description} />
|
|
76
82
|
|
|
77
83
|
<!-- Reveal.js CSS -->
|
|
78
|
-
<link rel="stylesheet" href=
|
|
79
|
-
<link rel="stylesheet" href={
|
|
84
|
+
<link rel="stylesheet" href={`${base}slides/reveal.css`} />
|
|
85
|
+
<link rel="stylesheet" href={`${base}slides/theme/${theme}.css`} id="theme" />
|
|
80
86
|
|
|
81
87
|
<!-- Reveal.js 代码高亮主题 -->
|
|
82
|
-
<link rel="stylesheet" href=
|
|
88
|
+
<link rel="stylesheet" href={`${base}slides/plugin/highlight/monokai.css`} />
|
|
83
89
|
|
|
84
90
|
<!-- KaTeX for math -->
|
|
85
91
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" />
|
|
@@ -117,7 +123,7 @@ const revealConfig = JSON.stringify({
|
|
|
117
123
|
</a>
|
|
118
124
|
|
|
119
125
|
<!-- 检测嵌入模式并隐藏返回按钮,设置返回链接为父路径 -->
|
|
120
|
-
<script is:inline>
|
|
126
|
+
<script is:inline define:vars={{ baseUrl: base }}>
|
|
121
127
|
(function() {
|
|
122
128
|
var btn = document.getElementById('slides-back-btn');
|
|
123
129
|
if (!btn) return;
|
|
@@ -138,17 +144,29 @@ const revealConfig = JSON.stringify({
|
|
|
138
144
|
}
|
|
139
145
|
// 获取父路径
|
|
140
146
|
var parentPath = path.substring(0, path.lastIndexOf('/'));
|
|
141
|
-
//
|
|
142
|
-
btn.href = parentPath ||
|
|
147
|
+
// 如果父路径为空,返回base URL
|
|
148
|
+
btn.href = parentPath || baseUrl;
|
|
143
149
|
})();
|
|
144
150
|
</script>
|
|
145
151
|
|
|
146
|
-
<!-- Reveal.js -->
|
|
147
|
-
<script is:inline
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
<!-- Reveal.js - dynamically loaded with base URL -->
|
|
153
|
+
<script is:inline define:vars={{ baseUrl: base }}>
|
|
154
|
+
(function() {
|
|
155
|
+
var scripts = [
|
|
156
|
+
baseUrl + 'slides/reveal.js',
|
|
157
|
+
baseUrl + 'slides/plugin/markdown/markdown.js',
|
|
158
|
+
baseUrl + 'slides/plugin/highlight/highlight.js',
|
|
159
|
+
baseUrl + 'slides/plugin/notes/notes.js',
|
|
160
|
+
baseUrl + 'slides/plugin/math/math.js'
|
|
161
|
+
];
|
|
162
|
+
scripts.forEach(function(src) {
|
|
163
|
+
var script = document.createElement('script');
|
|
164
|
+
script.src = src;
|
|
165
|
+
script.async = false;
|
|
166
|
+
document.head.appendChild(script);
|
|
167
|
+
});
|
|
168
|
+
})();
|
|
169
|
+
</script>
|
|
152
170
|
|
|
153
171
|
<!-- Mermaid -->
|
|
154
172
|
<script is:inline src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
@@ -3,6 +3,9 @@ import { getCollection } from 'astro:content';
|
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
4
|
import PostCard from '@jet-w/astro-blog/components/blog/PostCard.astro';
|
|
5
5
|
import Pagination from '@jet-w/astro-blog/components/ui/Pagination.astro';
|
|
6
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
7
|
+
import { defaultI18nConfig } from '../../../../../config/i18n';
|
|
8
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../../../../utils/i18n';
|
|
6
9
|
|
|
7
10
|
export async function getStaticPaths() {
|
|
8
11
|
const postsPerPage = 10;
|
|
@@ -57,8 +60,23 @@ export async function getStaticPaths() {
|
|
|
57
60
|
const { year, month, page: currentPage, totalPages } = Astro.props;
|
|
58
61
|
const postsPerPage = 10;
|
|
59
62
|
|
|
63
|
+
// Get i18n config
|
|
64
|
+
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
65
|
+
const base = import.meta.env.BASE_URL;
|
|
66
|
+
|
|
67
|
+
// Remove base URL from current path for locale detection
|
|
68
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
69
|
+
|
|
70
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
71
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
72
|
+
const ui = localeConfig.ui;
|
|
73
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
74
|
+
|
|
60
75
|
// 获取该月的所有文章
|
|
61
|
-
const
|
|
76
|
+
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
77
|
+
|
|
78
|
+
// Filter posts by current locale
|
|
79
|
+
const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
|
|
62
80
|
|
|
63
81
|
const filteredPosts = allPosts
|
|
64
82
|
.filter(post => {
|
|
@@ -80,29 +98,32 @@ const posts = filteredPosts.slice(startIndex, endIndex);
|
|
|
80
98
|
|
|
81
99
|
// 月份名称
|
|
82
100
|
const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
|
|
83
|
-
const
|
|
101
|
+
const monthNamesEn = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
102
|
+
const monthName = currentLocale.startsWith('en') ? monthNamesEn[month - 1] : monthNames[month - 1];
|
|
103
|
+
const archiveTitle = currentLocale.startsWith('en') ? `${monthName} ${year}` : `${year}年${monthName}`;
|
|
84
104
|
---
|
|
85
105
|
|
|
86
106
|
<PageLayout
|
|
87
|
-
title={
|
|
88
|
-
description={`${
|
|
107
|
+
title={`${ui.archives}: ${archiveTitle} - ${ui.page} ${currentPage}`}
|
|
108
|
+
description={`${ui.archives} ${archiveTitle} - ${ui.page} ${currentPage}`}
|
|
89
109
|
showSidebar={true}
|
|
110
|
+
i18nConfig={i18nConfig}
|
|
90
111
|
>
|
|
91
112
|
<!-- 面包屑导航 -->
|
|
92
113
|
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
93
|
-
<a href=
|
|
114
|
+
<a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
|
|
94
115
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
95
116
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
96
117
|
</svg>
|
|
97
|
-
<a href=
|
|
118
|
+
<a href={`${localePrefix}/archives`} class="hover:text-primary-500 transition-colors">{ui.archives}</a>
|
|
98
119
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
99
120
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
100
121
|
</svg>
|
|
101
|
-
<a href={
|
|
122
|
+
<a href={`${localePrefix}/archives/${year}/${String(month).padStart(2, '0')}`} class="hover:text-primary-500 transition-colors">{archiveTitle}</a>
|
|
102
123
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
103
124
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
104
125
|
</svg>
|
|
105
|
-
<span class="text-slate-900 dark:text-slate-100"
|
|
126
|
+
<span class="text-slate-900 dark:text-slate-100">{ui.page} {currentPage}</span>
|
|
106
127
|
</nav>
|
|
107
128
|
|
|
108
129
|
<!-- 页面头部 -->
|
|
@@ -114,20 +135,20 @@ const monthName = monthNames[month - 1];
|
|
|
114
135
|
</div>
|
|
115
136
|
|
|
116
137
|
<h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
|
117
|
-
{
|
|
138
|
+
{archiveTitle}
|
|
118
139
|
</h1>
|
|
119
140
|
|
|
120
141
|
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
|
|
121
|
-
|
|
142
|
+
{totalPosts} {ui.posts}
|
|
122
143
|
</p>
|
|
123
144
|
|
|
124
145
|
<!-- 归档统计 -->
|
|
125
146
|
<div class="inline-flex items-center space-x-6 text-sm text-slate-500 dark:text-slate-400 bg-slate-50 dark:bg-slate-800 px-6 py-3 rounded-lg">
|
|
126
|
-
<span>{
|
|
147
|
+
<span>{archiveTitle}</span>
|
|
127
148
|
<span>•</span>
|
|
128
|
-
<span>{totalPosts}
|
|
149
|
+
<span>{totalPosts} {ui.posts}</span>
|
|
129
150
|
<span>•</span>
|
|
130
|
-
<span
|
|
151
|
+
<span>{ui.page} {currentPage} / {totalPages}</span>
|
|
131
152
|
</div>
|
|
132
153
|
</div>
|
|
133
154
|
|
|
@@ -147,6 +168,9 @@ const monthName = monthNames[month - 1];
|
|
|
147
168
|
image: post.data.image
|
|
148
169
|
}}
|
|
149
170
|
layout="horizontal"
|
|
171
|
+
localePrefix={localePrefix}
|
|
172
|
+
locale={localeConfig.locale.dateLocale}
|
|
173
|
+
ui={ui}
|
|
150
174
|
/>
|
|
151
175
|
))}
|
|
152
176
|
</div>
|
|
@@ -154,13 +178,13 @@ const monthName = monthNames[month - 1];
|
|
|
154
178
|
<div class="text-center py-16">
|
|
155
179
|
<div class="text-6xl mb-4">📅</div>
|
|
156
180
|
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
|
|
157
|
-
|
|
181
|
+
{ui.noPostsFound}
|
|
158
182
|
</h3>
|
|
159
183
|
<p class="text-slate-600 dark:text-slate-400 mb-6">
|
|
160
|
-
{
|
|
184
|
+
{ui.noPostsFound}
|
|
161
185
|
</p>
|
|
162
|
-
<a href=
|
|
163
|
-
|
|
186
|
+
<a href={`${localePrefix}/archives`} class="btn-secondary">
|
|
187
|
+
{ui.archives}
|
|
164
188
|
</a>
|
|
165
189
|
</div>
|
|
166
190
|
)}
|
|
@@ -170,7 +194,7 @@ const monthName = monthNames[month - 1];
|
|
|
170
194
|
<Pagination
|
|
171
195
|
currentPage={currentPage}
|
|
172
196
|
totalPages={totalPages}
|
|
173
|
-
baseUrl={
|
|
197
|
+
baseUrl={`${localePrefix}/archives/${year}/${String(month).padStart(2, '0')}`}
|
|
174
198
|
/>
|
|
175
199
|
)}
|
|
176
200
|
</PageLayout>
|
|
@@ -40,10 +40,16 @@ const { year, month, count } = Astro.props;
|
|
|
40
40
|
|
|
41
41
|
// Get i18n config
|
|
42
42
|
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
43
|
-
const
|
|
43
|
+
const base = import.meta.env.BASE_URL;
|
|
44
|
+
|
|
45
|
+
// Remove base URL from current path for locale detection
|
|
46
|
+
const { removeBase } = await import('../../../utils/i18n');
|
|
47
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
48
|
+
|
|
49
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
44
50
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
45
51
|
const ui = localeConfig.ui;
|
|
46
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
52
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
47
53
|
|
|
48
54
|
// 获取该月的所有文章
|
|
49
55
|
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
@@ -3,14 +3,19 @@ import { getCollection } from 'astro:content';
|
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
4
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
5
5
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
6
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
6
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
7
7
|
|
|
8
8
|
// Get i18n config
|
|
9
9
|
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
10
|
-
const
|
|
10
|
+
const base = import.meta.env.BASE_URL;
|
|
11
|
+
|
|
12
|
+
// Remove base URL from current path for locale detection
|
|
13
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
14
|
+
|
|
15
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
11
16
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
12
17
|
const ui = localeConfig.ui;
|
|
13
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
18
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
14
19
|
|
|
15
20
|
// 获取所有非草稿文章
|
|
16
21
|
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
@@ -3,6 +3,9 @@ import { getCollection } from 'astro:content';
|
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
4
|
import PostCard from '@jet-w/astro-blog/components/blog/PostCard.astro';
|
|
5
5
|
import Pagination from '@jet-w/astro-blog/components/ui/Pagination.astro';
|
|
6
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
7
|
+
import { defaultI18nConfig } from '../../../../config/i18n';
|
|
8
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../../../utils/i18n';
|
|
6
9
|
|
|
7
10
|
export async function getStaticPaths() {
|
|
8
11
|
const postsPerPage = 10;
|
|
@@ -53,8 +56,23 @@ export async function getStaticPaths() {
|
|
|
53
56
|
const { categorySlug, categoryName, page: currentPage, totalPages } = Astro.props;
|
|
54
57
|
const postsPerPage = 10;
|
|
55
58
|
|
|
59
|
+
// Get i18n config
|
|
60
|
+
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
61
|
+
const base = import.meta.env.BASE_URL;
|
|
62
|
+
|
|
63
|
+
// Remove base URL from current path for locale detection
|
|
64
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
65
|
+
|
|
66
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
67
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
68
|
+
const ui = localeConfig.ui;
|
|
69
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
70
|
+
|
|
56
71
|
// 获取所有文章并筛选包含该分类的文章
|
|
57
|
-
const
|
|
72
|
+
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
73
|
+
|
|
74
|
+
// Filter posts by current locale
|
|
75
|
+
const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
|
|
58
76
|
|
|
59
77
|
// 筛选包含该分类的文章
|
|
60
78
|
const filteredPosts = allPosts
|
|
@@ -95,25 +113,26 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
95
113
|
---
|
|
96
114
|
|
|
97
115
|
<PageLayout
|
|
98
|
-
title={
|
|
99
|
-
description={
|
|
116
|
+
title={`${ui.inCategory}: ${categoryName} - ${ui.page} ${currentPage}`}
|
|
117
|
+
description={`${ui.inCategory} ${categoryName} - ${ui.page} ${currentPage}`}
|
|
100
118
|
showSidebar={true}
|
|
119
|
+
i18nConfig={i18nConfig}
|
|
101
120
|
>
|
|
102
121
|
<!-- 面包屑导航 -->
|
|
103
122
|
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
104
|
-
<a href=
|
|
123
|
+
<a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
|
|
105
124
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
106
125
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
107
126
|
</svg>
|
|
108
|
-
<a href=
|
|
127
|
+
<a href={`${localePrefix}/categories`} class="hover:text-primary-500 transition-colors">{ui.allCategories}</a>
|
|
109
128
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
110
129
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
111
130
|
</svg>
|
|
112
|
-
<a href={
|
|
131
|
+
<a href={`${localePrefix}/categories/${categorySlug}`} class="hover:text-primary-500 transition-colors">{categoryName}</a>
|
|
113
132
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
114
133
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
115
134
|
</svg>
|
|
116
|
-
<span class="text-slate-900 dark:text-slate-100"
|
|
135
|
+
<span class="text-slate-900 dark:text-slate-100">{ui.page} {currentPage}</span>
|
|
117
136
|
</nav>
|
|
118
137
|
|
|
119
138
|
<!-- 页面头部 -->
|
|
@@ -129,16 +148,16 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
129
148
|
</h1>
|
|
130
149
|
|
|
131
150
|
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8 max-w-2xl mx-auto">
|
|
132
|
-
|
|
151
|
+
{totalPosts} {ui.posts}
|
|
133
152
|
</p>
|
|
134
153
|
|
|
135
154
|
<!-- 分类统计 -->
|
|
136
155
|
<div class="inline-flex items-center space-x-6 text-sm text-slate-500 dark:text-slate-400 bg-slate-50 dark:bg-slate-800 px-6 py-3 rounded-lg">
|
|
137
156
|
<span>{categoryName}</span>
|
|
138
157
|
<span>•</span>
|
|
139
|
-
<span>{totalPosts}
|
|
158
|
+
<span>{totalPosts} {ui.posts}</span>
|
|
140
159
|
<span>•</span>
|
|
141
|
-
<span
|
|
160
|
+
<span>{ui.page} {currentPage} / {totalPages}</span>
|
|
142
161
|
</div>
|
|
143
162
|
</div>
|
|
144
163
|
|
|
@@ -158,6 +177,9 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
158
177
|
image: post.data.image
|
|
159
178
|
}}
|
|
160
179
|
layout="horizontal"
|
|
180
|
+
localePrefix={localePrefix}
|
|
181
|
+
locale={localeConfig.locale.dateLocale}
|
|
182
|
+
ui={ui}
|
|
161
183
|
/>
|
|
162
184
|
))}
|
|
163
185
|
</div>
|
|
@@ -165,13 +187,13 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
165
187
|
<div class="text-center py-16">
|
|
166
188
|
<div class="text-6xl mb-4">📂</div>
|
|
167
189
|
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
|
|
168
|
-
|
|
190
|
+
{ui.noPostsFound}
|
|
169
191
|
</h3>
|
|
170
192
|
<p class="text-slate-600 dark:text-slate-400 mb-6">
|
|
171
|
-
|
|
193
|
+
{ui.noPostsFound}
|
|
172
194
|
</p>
|
|
173
|
-
<a href=
|
|
174
|
-
|
|
195
|
+
<a href={`${localePrefix}/categories`} class="btn-secondary">
|
|
196
|
+
{ui.allCategories}
|
|
175
197
|
</a>
|
|
176
198
|
</div>
|
|
177
199
|
)}
|
|
@@ -181,7 +203,7 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
181
203
|
<Pagination
|
|
182
204
|
currentPage={currentPage}
|
|
183
205
|
totalPages={totalPages}
|
|
184
|
-
baseUrl={
|
|
206
|
+
baseUrl={`${localePrefix}/categories/${categorySlug}`}
|
|
185
207
|
/>
|
|
186
208
|
)}
|
|
187
209
|
|
|
@@ -189,12 +211,12 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
189
211
|
{relatedCategories.length > 0 && (
|
|
190
212
|
<section class="mt-16 pt-8 border-t border-slate-200 dark:border-slate-700">
|
|
191
213
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-6">
|
|
192
|
-
|
|
214
|
+
{ui.categories}
|
|
193
215
|
</h2>
|
|
194
216
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
195
217
|
{relatedCategories.map((relatedCategory) => (
|
|
196
218
|
<a
|
|
197
|
-
href={
|
|
219
|
+
href={`${localePrefix}/categories/${relatedCategory.slug}`}
|
|
198
220
|
class="group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300"
|
|
199
221
|
>
|
|
200
222
|
<div class="flex items-center justify-between">
|
|
@@ -203,7 +225,7 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
|
203
225
|
{relatedCategory.name}
|
|
204
226
|
</h3>
|
|
205
227
|
<p class="text-sm text-slate-600 dark:text-slate-400">
|
|
206
|
-
{relatedCategory.count}
|
|
228
|
+
{relatedCategory.count} {ui.posts}
|
|
207
229
|
</p>
|
|
208
230
|
</div>
|
|
209
231
|
<svg class="w-5 h-5 text-slate-400 group-hover:text-amber-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -35,10 +35,16 @@ const { categorySlug, categoryName } = Astro.props;
|
|
|
35
35
|
|
|
36
36
|
// Get i18n config
|
|
37
37
|
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
38
|
-
const
|
|
38
|
+
const base = import.meta.env.BASE_URL;
|
|
39
|
+
|
|
40
|
+
// Remove base URL from current path for locale detection
|
|
41
|
+
const { removeBase } = await import('../../utils/i18n');
|
|
42
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
43
|
+
|
|
44
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
39
45
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
40
46
|
const ui = localeConfig.ui;
|
|
41
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
47
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
42
48
|
|
|
43
49
|
// 获取所有文章并筛选包含该分类的文章
|
|
44
50
|
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
@@ -3,14 +3,19 @@ import { getCollection } from 'astro:content';
|
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
4
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
5
5
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
6
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
6
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
7
7
|
|
|
8
8
|
// Get i18n config
|
|
9
9
|
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
10
|
-
const
|
|
10
|
+
const base = import.meta.env.BASE_URL;
|
|
11
|
+
|
|
12
|
+
// Remove base URL from current path for locale detection
|
|
13
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
14
|
+
|
|
15
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
11
16
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
12
17
|
const ui = localeConfig.ui;
|
|
13
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
18
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
14
19
|
|
|
15
20
|
// 从文章集合动态获取所有分类
|
|
16
21
|
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
@@ -5,7 +5,7 @@ import SlidesLayout from '@jet-w/astro-blog/layouts/SlidesLayout.astro';
|
|
|
5
5
|
import FloatingToc from '@jet-w/astro-blog/components/blog/FloatingToc.vue';
|
|
6
6
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
7
7
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
8
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix } from '../../utils/i18n';
|
|
8
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, removeBase } from '../../utils/i18n';
|
|
9
9
|
|
|
10
10
|
export async function getStaticPaths() {
|
|
11
11
|
const posts = await getCollection('posts');
|
|
@@ -153,10 +153,15 @@ const Content = post ? (await render(post)).Content : null;
|
|
|
153
153
|
|
|
154
154
|
// Get i18n config
|
|
155
155
|
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
156
|
-
const
|
|
156
|
+
const base = import.meta.env.BASE_URL;
|
|
157
|
+
|
|
158
|
+
// Remove base URL from current path for locale detection
|
|
159
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
160
|
+
|
|
161
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
157
162
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
158
163
|
const ui = localeConfig.ui;
|
|
159
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
164
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
160
165
|
|
|
161
166
|
// Get the current slug from URL
|
|
162
167
|
const urlPath = Astro.url.pathname;
|