@jet-w/astro-blog 0.2.5 → 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.
Files changed (35) hide show
  1. package/package.json +1 -1
  2. package/src/components/blog/Hero.astro +9 -6
  3. package/src/components/blog/PostCard.astro +4 -8
  4. package/src/components/home/FeaturedPostsSection.astro +9 -4
  5. package/src/components/home/QuickNavSection.astro +9 -4
  6. package/src/components/home/RecentPostsSection.astro +9 -4
  7. package/src/components/home/StatsSection.astro +9 -3
  8. package/src/components/layout/Footer.astro +16 -7
  9. package/src/components/layout/Header.astro +15 -12
  10. package/src/components/layout/Sidebar.astro +11 -6
  11. package/src/components/media/Slides.astro +22 -6
  12. package/src/components/ui/LanguageSwitcher.vue +24 -12
  13. package/src/components/ui/MobileMenu.vue +20 -3
  14. package/src/layouts/BaseLayout.astro +14 -7
  15. package/src/layouts/PageLayout.astro +9 -3
  16. package/src/layouts/SlidesLayout.astro +34 -16
  17. package/src/pages/archives/[year]/[month]/page/[page].astro +42 -18
  18. package/src/pages/archives/[year]/[month].astro +7 -2
  19. package/src/pages/archives/index.astro +7 -3
  20. package/src/pages/categories/[category]/page/[page].astro +40 -18
  21. package/src/pages/categories/[category].astro +7 -2
  22. package/src/pages/categories/index.astro +7 -3
  23. package/src/pages/posts/[...slug].astro +7 -3
  24. package/src/pages/posts/index.astro +7 -3
  25. package/src/pages/posts/page/[page].astro +7 -2
  26. package/src/pages/search.astro +9 -3
  27. package/src/pages/slides/index.astro +7 -3
  28. package/src/pages/tags/[tag]/page/[page].astro +39 -17
  29. package/src/pages/tags/[tag].astro +7 -2
  30. package/src/pages/tags/index.astro +7 -3
  31. package/src/plugins/rehype-relative-links.mjs +90 -14
  32. package/templates/default/.claude/ralph-loop.local.md +2 -32
  33. package/templates/default/Makefile +37 -0
  34. package/templates/default/astro.config.mjs +7 -3
  35. package/templates/default/package.prod.json +31 -0
@@ -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 current locale
37
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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="/favicon.svg" />
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="/slides/reveal.css" />
79
- <link rel="stylesheet" href={`/slides/theme/${theme}.css`} id="theme" />
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="/slides/plugin/highlight/monokai.css" />
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 src="/slides/reveal.js"></script>
148
- <script is:inline src="/slides/plugin/markdown/markdown.js"></script>
149
- <script is:inline src="/slides/plugin/highlight/highlight.js"></script>
150
- <script is:inline src="/slides/plugin/notes/notes.js"></script>
151
- <script is:inline src="/slides/plugin/math/math.js"></script>
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 allPosts = await getCollection('posts', ({ data }) => !data.draft);
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 monthName = monthNames[month - 1];
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={`归档: ${year}年${month}月 - ${currentPage} 页`}
88
- description={`${year}年${month}月的所有文章归档 - ${currentPage} 页`}
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="/" class="hover:text-primary-500 transition-colors">首页</a>
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="/archives" class="hover:text-primary-500 transition-colors">归档</a>
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={`/archives/${year}/${String(month).padStart(2, '0')}`} class="hover:text-primary-500 transition-colors">{year}年{month}月</a>
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">第 {currentPage} 页</span>
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
- {year}年{monthName}
138
+ {archiveTitle}
118
139
  </h1>
119
140
 
120
141
  <p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
121
- {totalPosts} 篇文章
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>{year}年{month}月</span>
147
+ <span>{archiveTitle}</span>
127
148
  <span>•</span>
128
- <span>{totalPosts} 篇文章</span>
149
+ <span>{totalPosts} {ui.posts}</span>
129
150
  <span>•</span>
130
- <span>第 {currentPage} / {totalPages} 页</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
- {year}年{month}月暂无发布的文章
184
+ {ui.noPostsFound}
161
185
  </p>
162
- <a href="/archives" class="btn-secondary">
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={`/archives/${year}/${String(month).padStart(2, '0')}`}
197
+ baseUrl={`${localePrefix}/archives/${year}/${String(month).padStart(2, '0')}`}
174
198
  />
175
199
  )}
176
200
  </PageLayout>
@@ -40,10 +40,15 @@ const { year, month, count } = Astro.props;
40
40
 
41
41
  // Get i18n config
42
42
  const i18nConfig = virtualI18nConfig || defaultI18nConfig;
43
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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 base = import.meta.env.BASE_URL;
47
52
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
48
53
 
49
54
  // 获取该月的所有文章
@@ -3,14 +3,18 @@ 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 currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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 base = import.meta.env.BASE_URL;
14
18
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
15
19
 
16
20
  // 获取所有非草稿文章
@@ -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 allPosts = await getCollection('posts', ({ data }) => !data.draft);
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={`分类: ${categoryName} - ${currentPage} 页`}
99
- description={`浏览所有 ${categoryName} 相关的文章 - ${currentPage} 页`}
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="/" class="hover:text-primary-500 transition-colors">首页</a>
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="/categories" class="hover:text-primary-500 transition-colors">分类</a>
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={`/categories/${categorySlug}`} class="hover:text-primary-500 transition-colors">{categoryName}</a>
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">第 {currentPage} 页</span>
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
- 浏览 {categoryName} 分类下的所有文章
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} 篇文章</span>
158
+ <span>{totalPosts} {ui.posts}</span>
140
159
  <span>•</span>
141
- <span>第 {currentPage} / {totalPages} 页</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
- 目前还没有 {categoryName} 分类的文章,请查看其他分类。
193
+ {ui.noPostsFound}
172
194
  </p>
173
- <a href="/categories" class="btn-secondary">
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={`/categories/${categorySlug}`}
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={`/categories/${relatedCategory.slug}`}
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,15 @@ const { categorySlug, categoryName } = Astro.props;
35
35
 
36
36
  // Get i18n config
37
37
  const i18nConfig = virtualI18nConfig || defaultI18nConfig;
38
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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 base = import.meta.env.BASE_URL;
42
47
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
43
48
 
44
49
  // 获取所有文章并筛选包含该分类的文章
@@ -3,14 +3,18 @@ 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 currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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 base = import.meta.env.BASE_URL;
14
18
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
15
19
 
16
20
  // 从文章集合动态获取所有分类
@@ -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,14 @@ const Content = post ? (await render(post)).Content : null;
153
153
 
154
154
  // Get i18n config
155
155
  const i18nConfig = virtualI18nConfig || defaultI18nConfig;
156
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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 base = import.meta.env.BASE_URL;
160
164
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
161
165
 
162
166
  // Get the current slug from URL
@@ -6,14 +6,18 @@ import NavigationTabs from '@jet-w/astro-blog/components/blog/NavigationTabs.vue
6
6
  import { getCollection } from 'astro:content';
7
7
  import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
8
8
  import { defaultI18nConfig } from '../../config/i18n';
9
- import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
9
+ import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../utils/i18n';
10
10
 
11
11
  // Get i18n config
12
12
  const i18nConfig = virtualI18nConfig || defaultI18nConfig;
13
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
13
+ const base = import.meta.env.BASE_URL;
14
+
15
+ // Remove base URL from current path for locale detection
16
+ const pathWithoutBase = removeBase(Astro.url.pathname, base);
17
+
18
+ const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
14
19
  const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
15
20
  const ui = localeConfig.ui;
16
- const base = import.meta.env.BASE_URL;
17
21
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
18
22
 
19
23
  // 从新的内容目录获取文章数据
@@ -25,10 +25,15 @@ const currentPage = parseInt(page as string, 10);
25
25
 
26
26
  // Get i18n config
27
27
  const i18nConfig = virtualI18nConfig || defaultI18nConfig;
28
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
28
+ const base = import.meta.env.BASE_URL;
29
+
30
+ // Remove base URL from current path for locale detection
31
+ const { removeBase } = await import('../../../utils/i18n');
32
+ const pathWithoutBase = removeBase(Astro.url.pathname, base);
33
+
34
+ const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
29
35
  const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
30
36
  const ui = localeConfig.ui;
31
- const base = import.meta.env.BASE_URL;
32
37
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
33
38
 
34
39
  // 从新的内容目录获取文章数据
@@ -4,7 +4,7 @@ import SearchInterface from '@jet-w/astro-blog/components/ui/SearchInterface.vue
4
4
  import type { I18nConfig } from '../config/i18n';
5
5
  import { defaultI18nConfig } from '../config/i18n';
6
6
  import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
7
- import { getLocaleFromPath, getLocaleConfig } from '../utils/i18n';
7
+ import { getLocaleFromPath, getLocaleConfig, removeBase } from '../utils/i18n';
8
8
 
9
9
  export interface Props {
10
10
  i18nConfig?: I18nConfig;
@@ -12,8 +12,14 @@ export interface Props {
12
12
 
13
13
  const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
14
14
 
15
- // Get current locale from URL
16
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
15
+ // Get base URL for prefixing links
16
+ const base = import.meta.env.BASE_URL;
17
+
18
+ // Remove base URL from current path for locale detection
19
+ const pathWithoutBase = removeBase(Astro.url.pathname, base);
20
+
21
+ // Get current locale from URL (use path without base)
22
+ const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
17
23
  const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
18
24
  const ui = localeConfig.ui;
19
25
 
@@ -3,14 +3,18 @@ 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 } from '../../utils/i18n';
6
+ import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, removeBase } from '../../utils/i18n';
7
7
 
8
8
  // Get i18n config
9
9
  const i18nConfig = virtualI18nConfig || defaultI18nConfig;
10
- const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
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 base = import.meta.env.BASE_URL;
14
18
  const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
15
19
 
16
20
  // 获取所有非草稿的 slides