@jet-w/astro-blog 0.1.6 → 0.2.1

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 (122) hide show
  1. package/dist/chunk-6D3XRDNY.js +145 -0
  2. package/dist/chunk-A2E2VSAQ.js +246 -0
  3. package/dist/{chunk-GYLSY3OJ.js → chunk-TJTPX2WP.js} +1 -1
  4. package/dist/config/index.d.ts +3 -47
  5. package/dist/config/index.js +18 -2
  6. package/dist/i18n-PgMCFBw0.d.ts +222 -0
  7. package/dist/index.d.ts +204 -7
  8. package/dist/index.js +255 -3
  9. package/dist/integration.d.ts +9 -1
  10. package/dist/integration.js +2 -1
  11. package/dist/{sidebar-DNdiCKBw.d.ts → sidebar-Da-W_4Lr.d.ts} +1 -1
  12. package/dist/utils/sidebar.d.ts +1 -1
  13. package/package.json +1 -1
  14. package/src/components/blog/FloatingToc.vue +11 -3
  15. package/src/components/blog/Hero.astro +17 -2
  16. package/src/components/blog/NavigationTabs.vue +46 -15
  17. package/src/components/blog/PostCard.astro +28 -10
  18. package/src/components/blog/RelatedPosts.astro +23 -7
  19. package/src/components/blog/TableOfContents.astro +10 -4
  20. package/src/components/blog/TagCloud.astro +4 -3
  21. package/src/components/home/FeaturedPostsSection.astro +22 -6
  22. package/src/components/home/QuickNavSection.astro +33 -4
  23. package/src/components/home/RecentPostsSection.astro +22 -6
  24. package/src/components/home/StatsSection.astro +24 -6
  25. package/src/components/layout/Footer.astro +36 -20
  26. package/src/components/layout/Header.astro +75 -17
  27. package/src/components/layout/Sidebar.astro +40 -25
  28. package/src/components/ui/LanguageSwitcher.vue +183 -0
  29. package/src/components/ui/SearchBox.vue +13 -5
  30. package/src/components/ui/SearchInterface.vue +49 -25
  31. package/src/layouts/BaseLayout.astro +77 -52
  32. package/src/layouts/PageLayout.astro +22 -27
  33. package/src/layouts/SlidesLayout.astro +14 -2
  34. package/src/pages/archives/[year]/[month].astro +36 -17
  35. package/src/pages/archives/index.astro +36 -20
  36. package/src/pages/categories/[category].astro +33 -16
  37. package/src/pages/categories/index.astro +37 -14
  38. package/src/pages/posts/[...slug].astro +125 -18
  39. package/src/pages/posts/index.astro +59 -37
  40. package/src/pages/posts/page/[page].astro +65 -27
  41. package/src/pages/rss.xml.ts +18 -6
  42. package/src/pages/search.astro +50 -14
  43. package/src/pages/slides/index.astro +25 -6
  44. package/src/pages/tags/[tag].astro +32 -15
  45. package/src/pages/tags/index.astro +39 -16
  46. package/src/plugins/remark-containers.mjs +351 -322
  47. package/src/plugins/remark-protect-code.mjs +69 -0
  48. package/src/styles/global.css +35 -1
  49. package/templates/default/.claude/ralph-loop.local.md +48 -0
  50. package/templates/default/astro.config.mjs +33 -4
  51. package/templates/default/content/posts/blog_docs_en/01.get-started/01-intro.md +81 -0
  52. package/templates/default/content/posts/blog_docs_en/01.get-started/02-install.md +137 -0
  53. package/templates/default/content/posts/blog_docs_en/01.get-started/03-create-post.md +176 -0
  54. package/templates/default/content/posts/blog_docs_en/01.get-started/04-structure.md +173 -0
  55. package/templates/default/content/posts/blog_docs_en/01.get-started/05-deploy.md +208 -0
  56. package/templates/default/content/posts/blog_docs_en/01.get-started/README.md +52 -0
  57. package/templates/default/content/posts/blog_docs_en/02.guide/02-containers.md +245 -0
  58. package/templates/default/content/posts/blog_docs_en/02.guide/03-code-blocks.md +207 -0
  59. package/templates/default/content/posts/blog_docs_en/02.guide/03-mermaid.md +194 -0
  60. package/templates/default/content/posts/blog_docs_en/02.guide/04-icons.md +229 -0
  61. package/templates/default/content/posts/blog_docs_en/02.guide/06-latex.md +233 -0
  62. package/templates/default/content/posts/blog_docs_en/02.guide/07-video.md +184 -0
  63. package/templates/default/content/posts/blog_docs_en/02.guide/08-slides.md +359 -0
  64. package/templates/default/content/posts/blog_docs_en/02.guide/README.md +213 -0
  65. package/templates/default/content/posts/blog_docs_en/03.config/01-site.md +208 -0
  66. package/templates/default/content/posts/blog_docs_en/03.config/02-sidebar.md +240 -0
  67. package/templates/default/content/posts/blog_docs_en/03.config/03-i18n.md +349 -0
  68. package/templates/default/content/posts/blog_docs_en/03.config/README.md +85 -0
  69. package/templates/default/content/posts/blog_docs_en/README.md +79 -0
  70. package/templates/default/content/posts/blog_docs_zh/01.get-started/01-intro.md +81 -0
  71. package/templates/default/content/posts/blog_docs_zh/01.get-started/02-install.md +137 -0
  72. package/templates/default/content/posts/blog_docs_zh/01.get-started/03-create-post.md +176 -0
  73. package/templates/default/content/posts/blog_docs_zh/01.get-started/04-structure.md +173 -0
  74. package/templates/default/content/posts/blog_docs_zh/01.get-started/05-deploy.md +208 -0
  75. package/templates/default/content/posts/blog_docs_zh/01.get-started/README.md +52 -0
  76. package/templates/default/content/posts/blog_docs_zh/02.guide/02-containers.md +245 -0
  77. package/templates/default/content/posts/blog_docs_zh/02.guide/03-code-blocks.md +206 -0
  78. package/templates/default/content/posts/blog_docs_zh/02.guide/03-mermaid.md +194 -0
  79. package/templates/default/content/posts/blog_docs_zh/02.guide/04-icons.md +229 -0
  80. package/templates/default/content/posts/blog_docs_zh/02.guide/06-latex.md +233 -0
  81. package/templates/default/content/posts/blog_docs_zh/02.guide/07-video.md +184 -0
  82. package/templates/default/content/posts/blog_docs_zh/02.guide/08-slides.md +359 -0
  83. package/templates/default/content/posts/blog_docs_zh/02.guide/README.md +213 -0
  84. package/templates/default/content/posts/blog_docs_zh/03.config/01-site.md +208 -0
  85. package/templates/default/content/posts/blog_docs_zh/03.config/02-sidebar.md +240 -0
  86. package/templates/default/content/posts/blog_docs_zh/03.config/03-i18n.md +348 -0
  87. package/templates/default/content/posts/blog_docs_zh/03.config/README.md +85 -0
  88. package/templates/default/content/posts/blog_docs_zh/README.md +78 -0
  89. package/templates/default/package-lock.json +9667 -0
  90. package/templates/default/package.json +1 -1
  91. package/templates/default/src/config/footer.ts +14 -11
  92. package/templates/default/src/config/locales/en/footer.ts +17 -0
  93. package/templates/default/src/config/locales/en/index.ts +20 -0
  94. package/templates/default/src/config/locales/en/menu.ts +14 -0
  95. package/templates/default/src/config/locales/en/sidebar.ts +34 -0
  96. package/templates/default/src/config/locales/en/site.ts +7 -0
  97. package/templates/default/src/config/locales/en/ui.ts +29 -0
  98. package/templates/default/src/config/locales/index.ts +7 -0
  99. package/templates/default/src/config/locales/zh-CN/footer.ts +17 -0
  100. package/templates/default/src/config/locales/zh-CN/index.ts +20 -0
  101. package/templates/default/src/config/locales/zh-CN/menu.ts +14 -0
  102. package/templates/default/src/config/locales/zh-CN/sidebar.ts +34 -0
  103. package/templates/default/src/config/locales/zh-CN/site.ts +7 -0
  104. package/templates/default/src/config/locales/zh-CN/ui.ts +29 -0
  105. package/templates/default/src/config/sidebar.ts +10 -12
  106. package/templates/default/src/config/site.ts +2 -2
  107. package/templates/default/src/content.config.ts +15 -3
  108. package/templates/default/src/env.d.ts +7 -0
  109. package/dist/chunk-MQXPSOYB.js +0 -124
  110. package/templates/default/content/posts/blog_docs/01-quick-start.md +0 -162
  111. package/templates/default/content/posts/blog_docs/02-frontmatter.md +0 -277
  112. package/templates/default/content/posts/blog_docs/03-markdown-basic.md +0 -350
  113. package/templates/default/content/posts/blog_docs/04-containers.md +0 -331
  114. package/templates/default/content/posts/blog_docs/05-code-blocks.md +0 -388
  115. package/templates/default/content/posts/blog_docs/06-mermaid.md +0 -431
  116. package/templates/default/content/posts/blog_docs/07-video.md +0 -243
  117. package/templates/default/content/posts/blog_docs/08-latex.md +0 -382
  118. package/templates/default/content/posts/blog_docs/09-icons.md +0 -326
  119. package/templates/default/content/posts/blog_docs/10-sidebar.md +0 -445
  120. package/templates/default/content/posts/blog_docs/11-config.md +0 -334
  121. package/templates/default/content/posts/blog_docs/12-slides.mdx +0 -552
  122. package/templates/default/content/posts/blog_docs/README.md +0 -151
@@ -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 } from '../../../utils/i18n';
6
9
 
7
10
  export async function getStaticPaths() {
8
11
  const allPosts = await getCollection('posts', ({ data }) => !data.draft);
@@ -35,8 +38,18 @@ export async function getStaticPaths() {
35
38
 
36
39
  const { year, month, count } = Astro.props;
37
40
 
41
+ // Get i18n config
42
+ const i18nConfig = virtualI18nConfig || defaultI18nConfig;
43
+ const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
44
+ const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
45
+ const ui = localeConfig.ui;
46
+ const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
47
+
38
48
  // 获取该月的所有文章
39
- const allPosts = await getCollection('posts', ({ data }) => !data.draft);
49
+ const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
50
+
51
+ // Filter posts by current locale
52
+ const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
40
53
 
41
54
  const filteredPosts = allPosts
42
55
  .filter(post => {
@@ -62,25 +75,28 @@ const posts = filteredPosts.slice(startIndex, endIndex);
62
75
 
63
76
  // 月份名称
64
77
  const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
65
- const monthName = monthNames[month - 1];
78
+ const monthNamesEn = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
79
+ const monthName = currentLocale.startsWith('en') ? monthNamesEn[month - 1] : monthNames[month - 1];
80
+ const archiveTitle = currentLocale.startsWith('en') ? `${monthName} ${year}` : `${year}年${monthName}`;
66
81
  ---
67
82
 
68
83
  <PageLayout
69
- title={`归档: ${year}年${month}月`}
70
- description={`${year}年${month}月的所有文章归档`}
84
+ title={`${ui.archives}: ${archiveTitle}`}
85
+ description={`${ui.archives} ${archiveTitle}`}
71
86
  showSidebar={true}
87
+ i18nConfig={i18nConfig}
72
88
  >
73
89
  <!-- 面包屑导航 -->
74
90
  <nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
75
- <a href="/" class="hover:text-primary-500 transition-colors">首页</a>
91
+ <a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
76
92
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
77
93
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
78
94
  </svg>
79
- <a href="/archives" class="hover:text-primary-500 transition-colors">归档</a>
95
+ <a href={`${localePrefix}/archives`} class="hover:text-primary-500 transition-colors">{ui.archives}</a>
80
96
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
81
97
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
82
98
  </svg>
83
- <span class="text-slate-900 dark:text-slate-100">{year}年{month}月</span>
99
+ <span class="text-slate-900 dark:text-slate-100">{archiveTitle}</span>
84
100
  </nav>
85
101
 
86
102
  <!-- 页面头部 -->
@@ -92,22 +108,22 @@ const monthName = monthNames[month - 1];
92
108
  </div>
93
109
 
94
110
  <h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
95
- {year}年{monthName}
111
+ {archiveTitle}
96
112
  </h1>
97
113
 
98
114
  <p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
99
- {totalPosts} 篇文章
115
+ {totalPosts} {ui.posts}
100
116
  </p>
101
117
 
102
118
  <!-- 归档统计 -->
103
119
  <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">
104
- <span>{year}年{month}月</span>
120
+ <span>{archiveTitle}</span>
105
121
  <span>•</span>
106
- <span>{totalPosts} 篇文章</span>
122
+ <span>{totalPosts} {ui.posts}</span>
107
123
  {totalPages > 1 && (
108
124
  <>
109
125
  <span>•</span>
110
- <span>共 {totalPages} 页</span>
126
+ <span>{totalPages} {ui.page}</span>
111
127
  </>
112
128
  )}
113
129
  </div>
@@ -129,6 +145,9 @@ const monthName = monthNames[month - 1];
129
145
  image: post.data.image
130
146
  }}
131
147
  layout="horizontal"
148
+ localePrefix={localePrefix}
149
+ locale={localeConfig.locale.dateLocale}
150
+ ui={ui}
132
151
  />
133
152
  ))}
134
153
  </div>
@@ -136,13 +155,13 @@ const monthName = monthNames[month - 1];
136
155
  <div class="text-center py-16">
137
156
  <div class="text-6xl mb-4">📅</div>
138
157
  <h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
139
- 暂无文章
158
+ {ui.noPostsFound}
140
159
  </h3>
141
160
  <p class="text-slate-600 dark:text-slate-400 mb-6">
142
- {year}年{month}月暂无发布的文章
161
+ {ui.noPostsFound}
143
162
  </p>
144
- <a href="/archives" class="btn-secondary">
145
- 浏览所有归档
163
+ <a href={`${localePrefix}/archives`} class="btn-secondary">
164
+ {ui.archives}
146
165
  </a>
147
166
  </div>
148
167
  )}
@@ -152,7 +171,7 @@ const monthName = monthNames[month - 1];
152
171
  <Pagination
153
172
  currentPage={currentPage}
154
173
  totalPages={totalPages}
155
- baseUrl={`/archives/${year}/${String(month).padStart(2, '0')}`}
174
+ baseUrl={`${localePrefix}/archives/${year}/${String(month).padStart(2, '0')}`}
156
175
  />
157
176
  )}
158
177
  </PageLayout>
@@ -1,9 +1,22 @@
1
1
  ---
2
2
  import { getCollection } from 'astro:content';
3
3
  import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
4
+ import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
5
+ import { defaultI18nConfig } from '../../config/i18n';
6
+ import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
7
+
8
+ // Get i18n config
9
+ const i18nConfig = virtualI18nConfig || defaultI18nConfig;
10
+ const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
11
+ const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
12
+ const ui = localeConfig.ui;
13
+ const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
4
14
 
5
15
  // 获取所有非草稿文章
6
- const allPosts = await getCollection('posts', ({ data }) => !data.draft);
16
+ const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
17
+
18
+ // Filter posts by current locale
19
+ const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
7
20
 
8
21
  // 按年月分组文章
9
22
  interface ArchiveMonth {
@@ -26,7 +39,9 @@ interface ArchiveYear {
26
39
  const archiveData = new Map<number, Map<number, ArchiveMonth>>();
27
40
 
28
41
  // 月份名称
29
- const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
42
+ const monthNamesZh = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
43
+ const monthNamesEn = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
44
+ const monthNames = currentLocale === 'zh-CN' ? monthNamesZh : monthNamesEn;
30
45
 
31
46
  allPosts.forEach(post => {
32
47
  // pubDate 现在通过 schema transform 统一包含 date 或 pubDate 的值
@@ -82,17 +97,18 @@ const totalPosts = archives.reduce((sum, y) => sum + y.totalCount, 0);
82
97
  ---
83
98
 
84
99
  <PageLayout
85
- title="文章归档"
86
- description="按时间归档的所有文章"
100
+ title={ui.archives}
101
+ description={ui.archives}
87
102
  showSidebar={true}
103
+ i18nConfig={i18nConfig}
88
104
  >
89
105
  <!-- 面包屑导航 -->
90
106
  <nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
91
- <a href="/" class="hover:text-primary-500 transition-colors">首页</a>
107
+ <a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
92
108
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
93
109
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
94
110
  </svg>
95
- <span class="text-slate-900 dark:text-slate-100">归档</span>
111
+ <span class="text-slate-900 dark:text-slate-100">{ui.archives}</span>
96
112
  </nav>
97
113
 
98
114
  <!-- 页面头部 -->
@@ -104,11 +120,11 @@ const totalPosts = archives.reduce((sum, y) => sum + y.totalCount, 0);
104
120
  </div>
105
121
 
106
122
  <h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
107
- 文章归档
123
+ {ui.archives}
108
124
  </h1>
109
125
 
110
126
  <p class="text-xl text-slate-600 dark:text-slate-400">
111
- {totalPosts} 篇文章,跨越 {archives.length} 年
127
+ {totalPosts} {ui.posts} {archives.length} {currentLocale === 'zh-CN' ? '' : 'years'}
112
128
  </p>
113
129
  </div>
114
130
 
@@ -123,9 +139,9 @@ const totalPosts = archives.reduce((sum, y) => sum + y.totalCount, 0);
123
139
  <div class="relative flex items-center mb-6">
124
140
  <div class="absolute left-4 md:left-8 w-4 h-4 -ml-2 bg-primary-500 rounded-full border-4 border-white dark:border-slate-900 z-10"></div>
125
141
  <h2 class="ml-12 md:ml-16 text-2xl font-bold text-slate-900 dark:text-slate-100 flex items-center gap-3">
126
- {yearData.year}年
142
+ {yearData.year}{currentLocale === 'zh-CN' ? '' : ''}
127
143
  <span class="text-sm font-normal text-slate-500 dark:text-slate-400 bg-slate-100 dark:bg-slate-800 px-3 py-1 rounded-full">
128
- {yearData.totalCount}
144
+ {yearData.totalCount} {ui.posts}
129
145
  </span>
130
146
  </h2>
131
147
  </div>
@@ -140,14 +156,14 @@ const totalPosts = archives.reduce((sum, y) => sum + y.totalCount, 0);
140
156
  <div class="bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 overflow-hidden">
141
157
  <!-- 月份标题 -->
142
158
  <a
143
- href={`/archives/${yearData.year}/${String(monthData.month).padStart(2, '0')}`}
159
+ href={`${localePrefix}/archives/${yearData.year}/${String(monthData.month).padStart(2, '0')}`}
144
160
  class="flex items-center justify-between p-4 bg-slate-50 dark:bg-slate-800/50 hover:bg-slate-100 dark:hover:bg-slate-700/50 transition-colors"
145
161
  >
146
162
  <span class="font-semibold text-slate-900 dark:text-slate-100">
147
163
  {monthData.monthName}
148
164
  </span>
149
165
  <span class="text-sm text-slate-500 dark:text-slate-400 bg-white dark:bg-slate-700 px-2 py-1 rounded">
150
- {monthData.count}
166
+ {monthData.count} {ui.posts}
151
167
  </span>
152
168
  </a>
153
169
 
@@ -155,23 +171,23 @@ const totalPosts = archives.reduce((sum, y) => sum + y.totalCount, 0);
155
171
  <div class="divide-y divide-slate-100 dark:divide-slate-700">
156
172
  {monthData.posts.slice(0, 5).map((post) => (
157
173
  <a
158
- href={`/posts/${post.id.toLowerCase()}`}
174
+ href={`${localePrefix}/posts/${post.id.toLowerCase()}`}
159
175
  class="flex items-center justify-between p-4 hover:bg-slate-50 dark:hover:bg-slate-700/30 transition-colors"
160
176
  >
161
177
  <span class="text-slate-700 dark:text-slate-300 hover:text-primary-500 transition-colors line-clamp-1">
162
178
  {post.title}
163
179
  </span>
164
180
  <time class="text-xs text-slate-500 dark:text-slate-400 ml-4 flex-shrink-0">
165
- {post.pubDate.getDate()}日
181
+ {post.pubDate.getDate()}{currentLocale === 'zh-CN' ? '' : ''}
166
182
  </time>
167
183
  </a>
168
184
  ))}
169
185
  {monthData.posts.length > 5 && (
170
186
  <a
171
- href={`/archives/${yearData.year}/${String(monthData.month).padStart(2, '0')}`}
187
+ href={`${localePrefix}/archives/${yearData.year}/${String(monthData.month).padStart(2, '0')}`}
172
188
  class="block p-4 text-center text-sm text-primary-500 hover:bg-slate-50 dark:hover:bg-slate-700/30 transition-colors"
173
189
  >
174
- 查看全部 {monthData.count} 篇文章
190
+ {currentLocale === 'zh-CN' ? `查看全部 ${monthData.count} 篇文章` : `View all ${monthData.count} posts`}
175
191
  </a>
176
192
  )}
177
193
  </div>
@@ -188,13 +204,13 @@ const totalPosts = archives.reduce((sum, y) => sum + y.totalCount, 0);
188
204
  <div class="text-center py-16">
189
205
  <div class="text-6xl mb-4">📅</div>
190
206
  <h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
191
- 暂无文章
207
+ {ui.noPostsFound}
192
208
  </h3>
193
209
  <p class="text-slate-600 dark:text-slate-400 mb-6">
194
- 还没有发布任何文章
210
+ {currentLocale === 'zh-CN' ? '还没有发布任何文章' : 'No posts published yet'}
195
211
  </p>
196
- <a href="/" class="btn-primary">
197
- 返回首页
212
+ <a href={`${localePrefix}/`} class="btn-primary">
213
+ {ui.home}
198
214
  </a>
199
215
  </div>
200
216
  )}
@@ -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 } from '../../utils/i18n';
6
9
 
7
10
  export async function getStaticPaths() {
8
11
  const allPosts = await getCollection('posts', ({ data }) => !data.draft);
@@ -30,8 +33,18 @@ export async function getStaticPaths() {
30
33
 
31
34
  const { categorySlug, categoryName } = Astro.props;
32
35
 
36
+ // Get i18n config
37
+ const i18nConfig = virtualI18nConfig || defaultI18nConfig;
38
+ const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
39
+ const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
40
+ const ui = localeConfig.ui;
41
+ const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
42
+
33
43
  // 获取所有文章并筛选包含该分类的文章
34
- const allPosts = await getCollection('posts', ({ data }) => !data.draft);
44
+ const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
45
+
46
+ // Filter posts by current locale
47
+ const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
35
48
 
36
49
  // 筛选包含该分类的文章
37
50
  const filteredPosts = allPosts
@@ -75,17 +88,18 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
75
88
  ---
76
89
 
77
90
  <PageLayout
78
- title={`分类: ${categoryName}`}
79
- description={`浏览所有 ${categoryName} 相关的文章`}
91
+ title={`${ui.inCategory}: ${categoryName}`}
92
+ description={`${ui.inCategory} ${categoryName}`}
80
93
  showSidebar={true}
94
+ i18nConfig={i18nConfig}
81
95
  >
82
96
  <!-- 面包屑导航 -->
83
97
  <nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
84
- <a href="/" class="hover:text-primary-500 transition-colors">首页</a>
98
+ <a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
85
99
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
86
100
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
87
101
  </svg>
88
- <a href="/categories" class="hover:text-primary-500 transition-colors">分类</a>
102
+ <a href={`${localePrefix}/categories`} class="hover:text-primary-500 transition-colors">{ui.allCategories}</a>
89
103
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
90
104
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
91
105
  </svg>
@@ -105,18 +119,18 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
105
119
  </h1>
106
120
 
107
121
  <p class="text-xl text-slate-600 dark:text-slate-400 mb-8 max-w-2xl mx-auto">
108
- 浏览 {categoryName} 分类下的所有文章
122
+ {totalPosts} {ui.posts}
109
123
  </p>
110
124
 
111
125
  <!-- 分类统计 -->
112
126
  <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">
113
127
  <span>{categoryName}</span>
114
128
  <span>•</span>
115
- <span>{totalPosts} 篇文章</span>
129
+ <span>{totalPosts} {ui.posts}</span>
116
130
  {totalPages > 1 && (
117
131
  <>
118
132
  <span>•</span>
119
- <span>共 {totalPages} 页</span>
133
+ <span>{totalPages} {ui.page}</span>
120
134
  </>
121
135
  )}
122
136
  </div>
@@ -138,6 +152,9 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
138
152
  image: post.data.image
139
153
  }}
140
154
  layout="horizontal"
155
+ localePrefix={localePrefix}
156
+ locale={localeConfig.locale.dateLocale}
157
+ ui={ui}
141
158
  />
142
159
  ))}
143
160
  </div>
@@ -145,13 +162,13 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
145
162
  <div class="text-center py-16">
146
163
  <div class="text-6xl mb-4">📂</div>
147
164
  <h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
148
- 暂无相关文章
165
+ {ui.noPostsFound}
149
166
  </h3>
150
167
  <p class="text-slate-600 dark:text-slate-400 mb-6">
151
- 目前还没有 {categoryName} 分类的文章,请查看其他分类。
168
+ {ui.noPostsFound}
152
169
  </p>
153
- <a href="/categories" class="btn-secondary">
154
- 浏览所有分类
170
+ <a href={`${localePrefix}/categories`} class="btn-secondary">
171
+ {ui.allCategories}
155
172
  </a>
156
173
  </div>
157
174
  )}
@@ -161,7 +178,7 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
161
178
  <Pagination
162
179
  currentPage={currentPage}
163
180
  totalPages={totalPages}
164
- baseUrl={`/categories/${categorySlug}`}
181
+ baseUrl={`${localePrefix}/categories/${categorySlug}`}
165
182
  />
166
183
  )}
167
184
 
@@ -169,12 +186,12 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
169
186
  {relatedCategories.length > 0 && (
170
187
  <section class="mt-16 pt-8 border-t border-slate-200 dark:border-slate-700">
171
188
  <h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-6">
172
- 相关分类
189
+ {ui.categories}
173
190
  </h2>
174
191
  <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
175
192
  {relatedCategories.map((relatedCategory) => (
176
193
  <a
177
- href={`/categories/${relatedCategory.slug}`}
194
+ href={`${localePrefix}/categories/${relatedCategory.slug}`}
178
195
  class="group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300"
179
196
  >
180
197
  <div class="flex items-center justify-between">
@@ -183,7 +200,7 @@ const relatedCategories = Array.from(relatedCategoryMap.entries())
183
200
  {relatedCategory.name}
184
201
  </h3>
185
202
  <p class="text-sm text-slate-600 dark:text-slate-400">
186
- {relatedCategory.count} 篇文章
203
+ {relatedCategory.count} {ui.posts}
187
204
  </p>
188
205
  </div>
189
206
  <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">
@@ -1,9 +1,22 @@
1
1
  ---
2
2
  import { getCollection } from 'astro:content';
3
3
  import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
4
+ import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
5
+ import { defaultI18nConfig } from '../../config/i18n';
6
+ import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
7
+
8
+ // Get i18n config
9
+ const i18nConfig = virtualI18nConfig || defaultI18nConfig;
10
+ const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
11
+ const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
12
+ const ui = localeConfig.ui;
13
+ const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
4
14
 
5
15
  // 从文章集合动态获取所有分类
6
- const allPosts = await getCollection('posts', ({ data }) => !data.draft);
16
+ const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
17
+
18
+ // Filter posts by current locale
19
+ const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
7
20
 
8
21
  // 统计分类
9
22
  const categoryMap = new Map<string, { name: string; count: number }>();
@@ -55,24 +68,34 @@ const getCategoryIcon = (slug: string) => {
55
68
  ---
56
69
 
57
70
  <PageLayout
58
- title="分类"
59
- description="按分类浏览文章"
71
+ title={ui.allCategories}
72
+ description={ui.filterByCategory}
60
73
  showSidebar={true}
74
+ i18nConfig={i18nConfig}
61
75
  >
76
+ <!-- 面包屑导航 -->
77
+ <nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
78
+ <a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
79
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
80
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
81
+ </svg>
82
+ <span class="text-slate-900 dark:text-slate-100">{ui.allCategories}</span>
83
+ </nav>
84
+
62
85
  <!-- 页面头部 -->
63
86
  <div class="text-center mb-12">
64
87
  <h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
65
- 文章分类
88
+ {ui.allCategories}
66
89
  </h1>
67
90
  <p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
68
- 按主题分类浏览所有文章
91
+ {ui.filterByCategory}
69
92
  </p>
70
93
 
71
94
  <!-- 统计信息 -->
72
95
  <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">
73
- <span>共 {allCategories.length} 个分类</span>
96
+ <span>{allCategories.length} {ui.categories}</span>
74
97
  <span>•</span>
75
- <span>{totalPosts} 篇文章</span>
98
+ <span>{totalPosts} {ui.posts}</span>
76
99
  </div>
77
100
  </div>
78
101
 
@@ -80,7 +103,7 @@ const getCategoryIcon = (slug: string) => {
80
103
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
81
104
  {sortedCategories.map((category) => (
82
105
  <a
83
- href={`/categories/${category.slug}`}
106
+ href={`${localePrefix}/categories/${category.slug}`}
84
107
  class="group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300 h-full"
85
108
  >
86
109
  <div class="flex flex-col h-full">
@@ -99,7 +122,7 @@ const getCategoryIcon = (slug: string) => {
99
122
  <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
100
123
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
101
124
  </svg>
102
- <span>{category.count} 篇文章</span>
125
+ <span>{category.count} {ui.posts}</span>
103
126
  </div>
104
127
  </div>
105
128
  </div>
@@ -127,12 +150,12 @@ const getCategoryIcon = (slug: string) => {
127
150
  {sortedCategories.length > 4 && (
128
151
  <section class="mb-16">
129
152
  <h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
130
- 热门分类
153
+ {ui.categories}
131
154
  </h2>
132
155
  <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
133
156
  {sortedCategories.slice(0, 4).map((category) => (
134
157
  <a
135
- href={`/categories/${category.slug}`}
158
+ href={`${localePrefix}/categories/${category.slug}`}
136
159
  class="group p-6 bg-gradient-to-br from-primary-50 to-secondary-50 dark:from-primary-900/20 dark:to-secondary-900/20 rounded-xl border border-primary-200 dark:border-primary-800 hover:shadow-lg transition-all duration-300"
137
160
  >
138
161
  <div class="text-center">
@@ -152,12 +175,12 @@ const getCategoryIcon = (slug: string) => {
152
175
  <!-- 分类列表 -->
153
176
  <section>
154
177
  <h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
155
- 所有分类
178
+ {ui.allCategories}
156
179
  </h2>
157
180
  <div class="space-y-3">
158
181
  {sortedCategories.map((category) => (
159
182
  <a
160
- href={`/categories/${category.slug}`}
183
+ href={`${localePrefix}/categories/${category.slug}`}
161
184
  class="flex items-center justify-between p-4 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg hover:shadow-md hover:border-primary-300 dark:hover:border-primary-600 transition-all duration-200 group"
162
185
  >
163
186
  <div class="flex items-center space-x-4">
@@ -177,7 +200,7 @@ const getCategoryIcon = (slug: string) => {
177
200
  </div>
178
201
  <div class="flex items-center space-x-3">
179
202
  <span class="text-sm px-3 py-1 bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-400 rounded-full">
180
- {category.count}
203
+ {category.count}
181
204
  </span>
182
205
  <svg class="w-5 h-5 text-slate-400 group-hover:text-primary-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
183
206
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />