@jet-w/astro-blog 0.2.0 → 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 (110) hide show
  1. package/dist/{chunk-HVQKQN6B.js → chunk-6D3XRDNY.js} +1 -1
  2. package/dist/{chunk-ATRISB7B.js → chunk-A2E2VSAQ.js} +43 -3
  3. package/dist/{chunk-AZHCNNAC.js → chunk-TJTPX2WP.js} +1 -1
  4. package/dist/config/index.d.ts +1 -1
  5. package/dist/config/index.js +2 -2
  6. package/dist/{i18n-5H4W145i.d.ts → i18n-PgMCFBw0.d.ts} +20 -0
  7. package/dist/index.d.ts +21 -3
  8. package/dist/index.js +20 -3
  9. package/dist/integration.d.ts +1 -1
  10. package/dist/integration.js +2 -2
  11. package/package.json +1 -1
  12. package/src/components/blog/FloatingToc.vue +11 -3
  13. package/src/components/blog/Hero.astro +17 -2
  14. package/src/components/blog/NavigationTabs.vue +46 -15
  15. package/src/components/blog/PostCard.astro +28 -10
  16. package/src/components/blog/RelatedPosts.astro +23 -7
  17. package/src/components/blog/TableOfContents.astro +10 -4
  18. package/src/components/blog/TagCloud.astro +4 -3
  19. package/src/components/home/FeaturedPostsSection.astro +22 -6
  20. package/src/components/home/QuickNavSection.astro +33 -4
  21. package/src/components/home/RecentPostsSection.astro +22 -6
  22. package/src/components/home/StatsSection.astro +24 -6
  23. package/src/components/layout/Header.astro +9 -5
  24. package/src/components/layout/Sidebar.astro +14 -11
  25. package/src/components/ui/SearchBox.vue +13 -5
  26. package/src/components/ui/SearchInterface.vue +49 -25
  27. package/src/pages/archives/[year]/[month].astro +36 -17
  28. package/src/pages/archives/index.astro +36 -20
  29. package/src/pages/categories/[category].astro +33 -16
  30. package/src/pages/categories/index.astro +37 -14
  31. package/src/pages/posts/[...slug].astro +125 -18
  32. package/src/pages/posts/index.astro +59 -37
  33. package/src/pages/posts/page/[page].astro +65 -27
  34. package/src/pages/search.astro +50 -14
  35. package/src/pages/slides/index.astro +25 -6
  36. package/src/pages/tags/[tag].astro +32 -15
  37. package/src/pages/tags/index.astro +39 -16
  38. package/src/plugins/remark-containers.mjs +351 -322
  39. package/src/plugins/remark-protect-code.mjs +69 -0
  40. package/src/styles/global.css +35 -1
  41. package/templates/default/.claude/ralph-loop.local.md +48 -0
  42. package/templates/default/astro.config.mjs +13 -4
  43. package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/01-intro.md +1 -1
  44. package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/02-install.md +1 -1
  45. package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/03-create-post.md +1 -1
  46. package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/04-structure.md +1 -1
  47. package/templates/default/content/posts/blog_docs_en/01.get-started/05-deploy.md +208 -0
  48. package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/README.md +1 -1
  49. package/templates/default/content/posts/blog_docs_en/02.guide/02-containers.md +245 -0
  50. package/templates/default/content/posts/blog_docs_en/{guide/markdown → 02.guide}/03-code-blocks.md +2 -1
  51. package/templates/default/content/posts/blog_docs_en/{guide/features/01-mermaid.md → 02.guide/03-mermaid.md} +1 -1
  52. package/templates/default/content/posts/blog_docs_en/{guide/features → 02.guide}/04-icons.md +4 -2
  53. package/templates/default/content/posts/blog_docs_en/{guide/features/02-latex.md → 02.guide/06-latex.md} +1 -1
  54. package/templates/default/content/posts/blog_docs_en/{guide/features/03-video.md → 02.guide/07-video.md} +1 -1
  55. package/templates/default/content/posts/blog_docs_en/02.guide/08-slides.md +359 -0
  56. package/templates/default/content/posts/blog_docs_en/{guide/markdown → 02.guide}/README.md +22 -3
  57. package/templates/default/content/posts/blog_docs_en/{config → 03.config}/01-site.md +1 -1
  58. package/templates/default/content/posts/blog_docs_en/{config → 03.config}/02-sidebar.md +1 -1
  59. package/templates/default/content/posts/blog_docs_en/{config → 03.config}/03-i18n.md +88 -24
  60. package/templates/default/content/posts/blog_docs_en/{config → 03.config}/README.md +1 -1
  61. package/templates/default/content/posts/blog_docs_en/README.md +2 -1
  62. package/templates/default/content/posts/blog_docs_zh/01.get-started/01-intro.md +81 -0
  63. package/templates/default/content/posts/blog_docs_zh/01.get-started/02-install.md +137 -0
  64. package/templates/default/content/posts/blog_docs_zh/01.get-started/03-create-post.md +176 -0
  65. package/templates/default/content/posts/blog_docs_zh/01.get-started/04-structure.md +173 -0
  66. package/templates/default/content/posts/blog_docs_zh/01.get-started/05-deploy.md +208 -0
  67. package/templates/default/content/posts/blog_docs_zh/01.get-started/README.md +52 -0
  68. package/templates/default/content/posts/blog_docs_zh/02.guide/02-containers.md +245 -0
  69. package/templates/default/content/posts/blog_docs_zh/02.guide/03-code-blocks.md +206 -0
  70. package/templates/default/content/posts/blog_docs_zh/02.guide/03-mermaid.md +194 -0
  71. package/templates/default/content/posts/blog_docs_zh/02.guide/04-icons.md +229 -0
  72. package/templates/default/content/posts/blog_docs_zh/02.guide/06-latex.md +233 -0
  73. package/templates/default/content/posts/blog_docs_zh/02.guide/07-video.md +184 -0
  74. package/templates/default/content/posts/blog_docs_zh/02.guide/08-slides.md +359 -0
  75. package/templates/default/content/posts/blog_docs_zh/02.guide/README.md +213 -0
  76. package/templates/default/content/posts/blog_docs_zh/03.config/01-site.md +208 -0
  77. package/templates/default/content/posts/blog_docs_zh/03.config/02-sidebar.md +240 -0
  78. package/templates/default/content/posts/blog_docs_zh/03.config/03-i18n.md +348 -0
  79. package/templates/default/content/posts/blog_docs_zh/03.config/README.md +85 -0
  80. package/templates/default/content/posts/blog_docs_zh/README.md +78 -0
  81. package/templates/default/src/config/locales/en/index.ts +5 -1
  82. package/templates/default/src/config/locales/en/menu.ts +3 -1
  83. package/templates/default/src/config/locales/en/sidebar.ts +18 -2
  84. package/templates/default/src/config/locales/en/site.ts +1 -1
  85. package/templates/default/src/config/locales/en/ui.ts +29 -0
  86. package/templates/default/src/config/locales/zh-CN/index.ts +5 -1
  87. package/templates/default/src/config/locales/zh-CN/menu.ts +7 -5
  88. package/templates/default/src/config/locales/zh-CN/sidebar.ts +22 -6
  89. package/templates/default/src/config/locales/zh-CN/site.ts +2 -2
  90. package/templates/default/src/config/locales/zh-CN/ui.ts +29 -0
  91. package/templates/default/src/config/site.ts +2 -2
  92. package/templates/default/src/content.config.ts +15 -3
  93. package/templates/default/content/posts/blog_docs/01-quick-start.md +0 -162
  94. package/templates/default/content/posts/blog_docs/02-frontmatter.md +0 -277
  95. package/templates/default/content/posts/blog_docs/03-markdown-basic.md +0 -350
  96. package/templates/default/content/posts/blog_docs/04-containers.md +0 -331
  97. package/templates/default/content/posts/blog_docs/05-code-blocks.md +0 -388
  98. package/templates/default/content/posts/blog_docs/06-mermaid.md +0 -431
  99. package/templates/default/content/posts/blog_docs/07-video.md +0 -243
  100. package/templates/default/content/posts/blog_docs/08-latex.md +0 -382
  101. package/templates/default/content/posts/blog_docs/09-icons.md +0 -326
  102. package/templates/default/content/posts/blog_docs/10-sidebar.md +0 -445
  103. package/templates/default/content/posts/blog_docs/11-config.md +0 -334
  104. package/templates/default/content/posts/blog_docs/12-i18n.md +0 -355
  105. package/templates/default/content/posts/blog_docs/12-slides.mdx +0 -552
  106. package/templates/default/content/posts/blog_docs/README.md +0 -152
  107. package/templates/default/content/posts/blog_docs_en/get-started/05-deploy.md +0 -197
  108. package/templates/default/content/posts/blog_docs_en/guide/README.md +0 -59
  109. package/templates/default/content/posts/blog_docs_en/guide/features/README.md +0 -51
  110. package/templates/default/content/posts/blog_docs_en/guide/markdown/02-containers.md +0 -226
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defaultI18nConfig
3
- } from "./chunk-ATRISB7B.js";
3
+ } from "./chunk-A2E2VSAQ.js";
4
4
 
5
5
  // src/integration.ts
6
6
  import { fileURLToPath } from "url";
@@ -35,6 +35,20 @@ var zhCNTranslations = {
35
35
  searchResults: "\u641C\u7D22\u7ED3\u679C",
36
36
  noResults: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u7ED3\u679C",
37
37
  searching: "\u641C\u7D22\u4E2D...",
38
+ searchArticles: "\u641C\u7D22\u6587\u7AE0",
39
+ searchInAllArticles: "\u5728\u6240\u6709\u6587\u7AE0\u4E2D\u67E5\u627E\u60A8\u611F\u5174\u8DA3\u7684\u5185\u5BB9",
40
+ searchTips: "\u641C\u7D22\u6280\u5DE7",
41
+ basicSearch: "\u57FA\u7840\u641C\u7D22",
42
+ advancedFeatures: "\u9AD8\u7EA7\u529F\u80FD",
43
+ searchTipKeyword: "\u8F93\u5165\u5173\u952E\u8BCD\u641C\u7D22\u6807\u9898\u548C\u5185\u5BB9",
44
+ searchTipMixedLang: "\u652F\u6301\u4E2D\u82F1\u6587\u6DF7\u5408\u641C\u7D22",
45
+ searchTipCaseInsensitive: "\u81EA\u52A8\u5FFD\u7565\u5927\u5C0F\u5199",
46
+ searchTipRealtime: "\u5B9E\u65F6\u641C\u7D22\u5EFA\u8BAE",
47
+ searchTipFilter: "\u6309\u6807\u7B7E\u548C\u5206\u7C7B\u7B5B\u9009",
48
+ searchTipFuzzy: "\u652F\u6301\u6A21\u7CCA\u5339\u914D",
49
+ // Hero section
50
+ browsePosts: "\u6D4F\u89C8\u6587\u7AE0",
51
+ aboutMe: "\u5173\u4E8E\u6211",
38
52
  // Pagination
39
53
  previousPage: "\u4E0A\u4E00\u9875",
40
54
  nextPage: "\u4E0B\u4E00\u9875",
@@ -44,7 +58,8 @@ var zhCNTranslations = {
44
58
  publishedOn: "\u53D1\u5E03\u4E8E",
45
59
  updatedOn: "\u66F4\u65B0\u4E8E",
46
60
  author: "\u4F5C\u8005",
47
- tableOfContents: "\u76EE\u5F55",
61
+ tableOfContents: "\u9875\u9762\u76EE\u5F55",
62
+ readingProgress: "\u9605\u8BFB\u8FDB\u5EA6",
48
63
  relatedPosts: "\u76F8\u5173\u6587\u7AE0",
49
64
  sharePost: "\u5206\u4EAB\u6587\u7AE0",
50
65
  previousPost: "\u4E0A\u4E00\u7BC7",
@@ -73,7 +88,12 @@ var zhCNTranslations = {
73
88
  slides: "\u6F14\u793A",
74
89
  slidesList: "\u6F14\u793A\u5217\u8868",
75
90
  // RSS
76
- rssFeed: "RSS \u8BA2\u9605"
91
+ rssFeed: "RSS \u8BA2\u9605",
92
+ // Quick Navigation
93
+ quickNavigation: "\u5FEB\u901F\u5BFC\u822A",
94
+ timeline: "\u65F6\u95F4\u8F74",
95
+ viewAllTimeline: "\u67E5\u770B\u5168\u90E8\u65F6\u95F4\u8F74",
96
+ postsCount: "\u7BC7"
77
97
  };
78
98
  var enTranslations = {
79
99
  // Navigation
@@ -111,6 +131,20 @@ var enTranslations = {
111
131
  searchResults: "Search Results",
112
132
  noResults: "No results found",
113
133
  searching: "Searching...",
134
+ searchArticles: "Search Articles",
135
+ searchInAllArticles: "Find content that interests you in all articles",
136
+ searchTips: "Search Tips",
137
+ basicSearch: "Basic Search",
138
+ advancedFeatures: "Advanced Features",
139
+ searchTipKeyword: "Enter keywords to search titles and content",
140
+ searchTipMixedLang: "Supports mixed language search",
141
+ searchTipCaseInsensitive: "Case insensitive",
142
+ searchTipRealtime: "Real-time search suggestions",
143
+ searchTipFilter: "Filter by tags and categories",
144
+ searchTipFuzzy: "Supports fuzzy matching",
145
+ // Hero section
146
+ browsePosts: "Browse Posts",
147
+ aboutMe: "About Me",
114
148
  // Pagination
115
149
  previousPage: "Previous",
116
150
  nextPage: "Next",
@@ -121,6 +155,7 @@ var enTranslations = {
121
155
  updatedOn: "Updated on",
122
156
  author: "Author",
123
157
  tableOfContents: "Table of Contents",
158
+ readingProgress: "Reading Progress",
124
159
  relatedPosts: "Related Posts",
125
160
  sharePost: "Share",
126
161
  previousPost: "Previous",
@@ -149,7 +184,12 @@ var enTranslations = {
149
184
  slides: "Slides",
150
185
  slidesList: "All Slides",
151
186
  // RSS
152
- rssFeed: "RSS Feed"
187
+ rssFeed: "RSS Feed",
188
+ // Quick Navigation
189
+ quickNavigation: "Quick Navigation",
190
+ timeline: "Timeline",
191
+ viewAllTimeline: "View all timeline",
192
+ postsCount: "posts"
153
193
  };
154
194
  var builtInTranslations = {
155
195
  "zh-CN": zhCNTranslations,
@@ -1,7 +1,7 @@
1
1
  // src/config/site.ts
2
2
  var siteConfig = {
3
3
  title: "My Astro Blog",
4
- description: "A modern blog built with Astro, Vue, and Tailwind CSS",
4
+ description: "",
5
5
  author: "Author",
6
6
  email: "",
7
7
  avatar: "/images/avatar.svg",
@@ -1,6 +1,6 @@
1
1
  import { SiteConfig, NavigationItem } from '../types/index.js';
2
2
  export { D as DividerConfig, M as ManualConfig, f as MixedConfig, P as PathMatchConfig, e as ScanConfig, b as SidebarConfig, a as SidebarGroup, S as SidebarItem, d as defaultSidebarConfig, c as defineSidebarConfig, s as sidebarConfig } from '../sidebar-Da-W_4Lr.js';
3
- export { F as FooterConfig, m as FooterLink, I as I18nConfig, o as I18nRoutingConfig, L as Locale, n as LocaleConfig, S as SocialLink, U as UITranslations, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from '../i18n-5H4W145i.js';
3
+ export { F as FooterConfig, m as FooterLink, I as I18nConfig, o as I18nRoutingConfig, L as Locale, n as LocaleConfig, S as SocialLink, U as UITranslations, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from '../i18n-PgMCFBw0.js';
4
4
 
5
5
  /**
6
6
  * Default site configuration
@@ -16,7 +16,7 @@ import {
16
16
  sidebarConfig,
17
17
  siteConfig,
18
18
  socialLinks
19
- } from "../chunk-AZHCNNAC.js";
19
+ } from "../chunk-TJTPX2WP.js";
20
20
  import {
21
21
  builtInTranslations,
22
22
  defaultI18nConfig,
@@ -25,7 +25,7 @@ import {
25
25
  enTranslations,
26
26
  getUITranslations,
27
27
  zhCNTranslations
28
- } from "../chunk-ATRISB7B.js";
28
+ } from "../chunk-A2E2VSAQ.js";
29
29
  export {
30
30
  builtInTranslations,
31
31
  defaultFooterConfig,
@@ -99,6 +99,19 @@ interface UITranslations {
99
99
  searchResults: string;
100
100
  noResults: string;
101
101
  searching: string;
102
+ searchArticles: string;
103
+ searchInAllArticles: string;
104
+ searchTips: string;
105
+ basicSearch: string;
106
+ advancedFeatures: string;
107
+ searchTipKeyword: string;
108
+ searchTipMixedLang: string;
109
+ searchTipCaseInsensitive: string;
110
+ searchTipRealtime: string;
111
+ searchTipFilter: string;
112
+ searchTipFuzzy: string;
113
+ browsePosts: string;
114
+ aboutMe: string;
102
115
  previousPage: string;
103
116
  nextPage: string;
104
117
  page: string;
@@ -107,6 +120,7 @@ interface UITranslations {
107
120
  updatedOn: string;
108
121
  author: string;
109
122
  tableOfContents: string;
123
+ readingProgress: string;
110
124
  relatedPosts: string;
111
125
  sharePost: string;
112
126
  previousPost: string;
@@ -133,6 +147,10 @@ interface UITranslations {
133
147
  slides: string;
134
148
  slidesList: string;
135
149
  rssFeed: string;
150
+ quickNavigation: string;
151
+ timeline: string;
152
+ viewAllTimeline: string;
153
+ postsCount: string;
136
154
  }
137
155
  /**
138
156
  * Locale-specific configuration
@@ -148,6 +166,8 @@ interface LocaleConfig {
148
166
  sidebar?: Partial<SidebarConfig>;
149
167
  /** UI translation overrides */
150
168
  ui?: Partial<UITranslations>;
169
+ /** Content path prefix for filtering posts by locale (e.g., 'blog_docs_en' for English) */
170
+ contentPathPrefix?: string;
151
171
  }
152
172
  /**
153
173
  * i18n routing configuration
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { defaultMenu, defaultSEO, defaultSiteConfig, defineMenu, defineSiteConfig, menu, siteConfig } from './config/index.js';
2
2
  import { b as SidebarConfig } from './sidebar-Da-W_4Lr.js';
3
3
  export { D as DividerConfig, M as ManualConfig, f as MixedConfig, P as PathMatchConfig, e as ScanConfig, a as SidebarGroup, S as SidebarItem, d as defaultSidebarConfig, c as defineSidebarConfig, s as sidebarConfig } from './sidebar-Da-W_4Lr.js';
4
- import { I as I18nConfig, L as Locale, F as FooterConfig, U as UITranslations, S as SocialLink } from './i18n-5H4W145i.js';
5
- export { m as FooterLink, o as I18nRoutingConfig, n as LocaleConfig, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from './i18n-5H4W145i.js';
4
+ import { I as I18nConfig, L as Locale, F as FooterConfig, U as UITranslations, S as SocialLink } from './i18n-PgMCFBw0.js';
5
+ export { m as FooterLink, o as I18nRoutingConfig, n as LocaleConfig, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from './i18n-PgMCFBw0.js';
6
6
  import { SiteConfig, NavigationItem } from './types/index.js';
7
7
  export { BlogPost, Category, PostFrontmatter, SEOProps, SearchResult, Tag } from './types/index.js';
8
8
  export { AstroBlogIntegrationOptions, default as astroBlog, default as astroBlogIntegration } from './integration.js';
@@ -117,6 +117,24 @@ declare function isMultiLanguageEnabled(config?: I18nConfig): boolean;
117
117
  * Returns empty string for default locale if prefixDefaultLocale is false
118
118
  */
119
119
  declare function getLocalePrefix(locale: string, config?: I18nConfig): string;
120
+ /**
121
+ * Get content path prefix for a specific locale
122
+ * Returns the contentPathPrefix from locale config, or undefined if not set
123
+ */
124
+ declare function getContentPathPrefix(locale: string, config?: I18nConfig): string | undefined;
125
+ /**
126
+ * Filter posts by locale based on contentPathPrefix
127
+ * If contentPathPrefix is set, only return posts that start with that prefix
128
+ * If not set, return all posts (backward compatible)
129
+ *
130
+ * @example
131
+ * // If en locale has contentPathPrefix: 'blog_docs_en'
132
+ * filterPostsByLocale(posts, 'en', config)
133
+ * // Returns only posts with id starting with 'blog_docs_en/'
134
+ */
135
+ declare function filterPostsByLocale<T extends {
136
+ id: string;
137
+ }>(posts: T[], locale: string, config?: I18nConfig): T[];
120
138
 
121
139
  /**
122
140
  * Vue Composable for i18n
@@ -214,4 +232,4 @@ declare function getAstroConfig(options?: {
214
232
  };
215
233
  };
216
234
 
217
- export { type AlternateLink, type BlogConfig, FooterConfig, I18N_CONFIG_KEY, I18N_LOCALE_KEY, I18N_TRANSLATIONS_KEY, I18nConfig, type I18nContext, Locale, type MergedLocaleConfig, NavigationItem, SidebarConfig, SiteConfig, SocialLink, UITranslations, type UseI18nReturn, createI18nContext, defineBlogConfig, formatDate, formatDateShort, getAlternateLinks, getAstroConfig, getLocaleByCode, getLocaleConfig, getLocaleFromPath, getLocalePrefix, getLocalizedPath, getTextDirection, isMultiLanguageEnabled, isRTL, removeLocalePrefix, t, useI18n };
235
+ export { type AlternateLink, type BlogConfig, FooterConfig, I18N_CONFIG_KEY, I18N_LOCALE_KEY, I18N_TRANSLATIONS_KEY, I18nConfig, type I18nContext, Locale, type MergedLocaleConfig, NavigationItem, SidebarConfig, SiteConfig, SocialLink, UITranslations, type UseI18nReturn, createI18nContext, defineBlogConfig, filterPostsByLocale, formatDate, formatDateShort, getAlternateLinks, getAstroConfig, getContentPathPrefix, getLocaleByCode, getLocaleConfig, getLocaleFromPath, getLocalePrefix, getLocalizedPath, getTextDirection, isMultiLanguageEnabled, isRTL, removeLocalePrefix, t, useI18n };
package/dist/index.js CHANGED
@@ -16,12 +16,12 @@ import {
16
16
  sidebarConfig,
17
17
  siteConfig,
18
18
  socialLinks
19
- } from "./chunk-AZHCNNAC.js";
19
+ } from "./chunk-TJTPX2WP.js";
20
20
  import "./chunk-FXPGR372.js";
21
21
  import {
22
22
  astroBlogIntegration,
23
23
  integration_default
24
- } from "./chunk-HVQKQN6B.js";
24
+ } from "./chunk-6D3XRDNY.js";
25
25
  import {
26
26
  builtInTranslations,
27
27
  defaultI18nConfig,
@@ -30,7 +30,7 @@ import {
30
30
  enTranslations,
31
31
  getUITranslations,
32
32
  zhCNTranslations
33
- } from "./chunk-ATRISB7B.js";
33
+ } from "./chunk-A2E2VSAQ.js";
34
34
 
35
35
  // src/utils/i18n.ts
36
36
  function getLocaleFromPath(pathname, config = defaultI18nConfig) {
@@ -174,6 +174,21 @@ function getLocalePrefix(locale, config = defaultI18nConfig) {
174
174
  }
175
175
  return `/${locale}`;
176
176
  }
177
+ function getContentPathPrefix(locale, config = defaultI18nConfig) {
178
+ const localeConfig = config.localeConfigs[locale];
179
+ return localeConfig?.contentPathPrefix;
180
+ }
181
+ function filterPostsByLocale(posts, locale, config = defaultI18nConfig) {
182
+ const contentPathPrefix = getContentPathPrefix(locale, config);
183
+ if (!contentPathPrefix) {
184
+ return posts;
185
+ }
186
+ return posts.filter((post) => {
187
+ const postPath = post.id.toLowerCase();
188
+ const prefix = contentPathPrefix.toLowerCase();
189
+ return postPath.startsWith(prefix + "/") || postPath === prefix;
190
+ });
191
+ }
177
192
 
178
193
  // src/utils/useI18n.ts
179
194
  import { inject, computed } from "vue";
@@ -275,11 +290,13 @@ export {
275
290
  defineSiteConfig,
276
291
  defineSocialLinks,
277
292
  enTranslations,
293
+ filterPostsByLocale,
278
294
  footerConfig,
279
295
  formatDate,
280
296
  formatDateShort,
281
297
  getAlternateLinks,
282
298
  getAstroConfig,
299
+ getContentPathPrefix,
283
300
  getLocaleByCode,
284
301
  getLocaleConfig,
285
302
  getLocaleFromPath,
@@ -1,5 +1,5 @@
1
1
  import { AstroIntegration } from 'astro';
2
- import { I as I18nConfig } from './i18n-5H4W145i.js';
2
+ import { I as I18nConfig } from './i18n-PgMCFBw0.js';
3
3
  import './types/index.js';
4
4
  import './sidebar-Da-W_4Lr.js';
5
5
 
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  astroBlogIntegration,
3
3
  integration_default
4
- } from "./chunk-HVQKQN6B.js";
5
- import "./chunk-ATRISB7B.js";
4
+ } from "./chunk-6D3XRDNY.js";
5
+ import "./chunk-A2E2VSAQ.js";
6
6
  export {
7
7
  astroBlogIntegration,
8
8
  integration_default as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jet-w/astro-blog",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "A modern Astro blog theme with Vue and Tailwind CSS support",
5
5
  "type": "module",
6
6
  "exports": {
@@ -9,7 +9,7 @@
9
9
  <button
10
10
  class="p-3 bg-white dark:bg-slate-800 rounded-full shadow-lg border border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors"
11
11
  :class="{ 'bg-primary-50 dark:bg-primary-900/30': showToc }"
12
- aria-label="显示目录"
12
+ :aria-label="tocTitle"
13
13
  >
14
14
  <svg class="w-5 h-5 text-slate-600 dark:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
15
15
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
@@ -35,7 +35,7 @@
35
35
  <svg class="w-4 h-4 text-primary-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
36
36
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
37
37
  </svg>
38
- 页面目录
38
+ {{ tocTitle }}
39
39
  </h3>
40
40
  </div>
41
41
 
@@ -66,7 +66,7 @@
66
66
  <!-- 进度条 -->
67
67
  <div class="px-4 py-2 border-t border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/50">
68
68
  <div class="flex items-center justify-between text-xs text-slate-500 dark:text-slate-400 mb-1">
69
- <span>阅读进度</span>
69
+ <span>{{ progressTitle }}</span>
70
70
  <span>{{ Math.round(progress * 100) }}%</span>
71
71
  </div>
72
72
  <div class="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-1.5">
@@ -91,6 +91,14 @@ interface Heading {
91
91
  level: number
92
92
  }
93
93
 
94
+ const props = withDefaults(defineProps<{
95
+ tocTitle?: string
96
+ progressTitle?: string
97
+ }>(), {
98
+ tocTitle: 'Table of Contents',
99
+ progressTitle: 'Reading Progress',
100
+ })
101
+
94
102
  const showToc = ref(false)
95
103
  const headings = ref<Heading[]>([])
96
104
  const activeId = ref('')
@@ -1,5 +1,20 @@
1
1
  ---
2
2
  import { siteConfig } from '@jet-w/astro-blog/config';
3
+ import type { I18nConfig } from '../../config/i18n';
4
+ import { defaultI18nConfig } from '../../config/i18n';
5
+ import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
6
+ import { getLocaleFromPath, getLocaleConfig } from '../../utils/i18n';
7
+
8
+ export interface Props {
9
+ i18nConfig?: I18nConfig;
10
+ }
11
+
12
+ const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
13
+
14
+ // Get current locale from URL
15
+ const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
16
+ const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
17
+ const ui = localeConfig.ui;
3
18
  ---
4
19
 
5
20
  <section class="py-16 mb-16">
@@ -89,7 +104,7 @@ import { siteConfig } from '@jet-w/astro-blog/config';
89
104
  href="/posts"
90
105
  class="btn-primary inline-flex items-center space-x-2"
91
106
  >
92
- <span>浏览文章</span>
107
+ <span>{ui.browsePosts}</span>
93
108
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
94
109
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
95
110
  </svg>
@@ -99,7 +114,7 @@ import { siteConfig } from '@jet-w/astro-blog/config';
99
114
  href="/about"
100
115
  class="btn-secondary inline-flex items-center space-x-2"
101
116
  >
102
- <span>关于我</span>
117
+ <span>{ui.aboutMe}</span>
103
118
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
104
119
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
105
120
  </svg>
@@ -26,7 +26,7 @@
26
26
  <a
27
27
  v-for="tag in tags"
28
28
  :key="tag.name"
29
- :href="`/tags/${encodeTag(tag.name)}`"
29
+ :href="`${props.localePrefix}/tags/${encodeTag(tag.name)}`"
30
30
  class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm transition-colors"
31
31
  :class="getTagColorClass(tag.count)"
32
32
  >
@@ -40,7 +40,7 @@
40
40
  <a
41
41
  v-for="archive in archives"
42
42
  :key="archive.key"
43
- :href="`/archives/${archive.year}/${archive.month}`"
43
+ :href="`${props.localePrefix}/archives/${archive.year}/${archive.month}`"
44
44
  class="flex items-center justify-between px-3 py-2 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors group"
45
45
  >
46
46
  <div class="flex items-center gap-3">
@@ -48,11 +48,11 @@
48
48
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
49
49
  </svg>
50
50
  <span class="text-slate-700 dark:text-slate-300 group-hover:text-primary-600 dark:group-hover:text-primary-400">
51
- {{ archive.year }}年{{ archive.month }}
51
+ {{ formatArchiveDate(archive.year, archive.month) }}
52
52
  </span>
53
53
  </div>
54
54
  <span class="text-xs text-slate-400 bg-slate-100 dark:bg-slate-700 px-2 py-0.5 rounded-full">
55
- {{ archive.count }}
55
+ {{ archive.count }} {{ props.ui?.postsCount || 'posts' }}
56
56
  </span>
57
57
  </a>
58
58
  </div>
@@ -62,7 +62,7 @@
62
62
  <a
63
63
  v-for="category in categories"
64
64
  :key="category.name"
65
- :href="`/categories/${encodeCategory(category.name)}`"
65
+ :href="`${props.localePrefix}/categories/${encodeCategory(category.name)}`"
66
66
  class="flex items-center justify-between px-3 py-2 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors group"
67
67
  >
68
68
  <div class="flex items-center gap-3">
@@ -74,7 +74,7 @@
74
74
  </span>
75
75
  </div>
76
76
  <span class="text-xs text-slate-400 bg-slate-100 dark:bg-slate-700 px-2 py-0.5 rounded-full">
77
- {{ category.count }}
77
+ {{ category.count }} {{ props.ui?.postsCount || 'posts' }}
78
78
  </span>
79
79
  </a>
80
80
  </div>
@@ -86,7 +86,7 @@
86
86
  <div v-for="item in timeline" :key="item.slug" class="relative pl-10">
87
87
  <div class="absolute left-2.5 w-3 h-3 rounded-full bg-primary-500 border-2 border-white dark:border-slate-800"></div>
88
88
  <a
89
- :href="`/posts/${item.slug}`"
89
+ :href="`${props.localePrefix}/posts/${item.slug}`"
90
90
  class="block p-3 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors group"
91
91
  >
92
92
  <div class="text-xs text-slate-400 mb-1">{{ formatDate(item.pubDate) }}</div>
@@ -97,10 +97,10 @@
97
97
  </div>
98
98
  </div>
99
99
  <a
100
- href="/archives"
100
+ :href="`${props.localePrefix}/archives`"
101
101
  class="mt-4 flex items-center justify-center gap-2 py-2 text-sm text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
102
102
  >
103
- <span>查看全部时间轴</span>
103
+ <span>{{ props.ui?.viewAllTimeline || 'View all timeline' }}</span>
104
104
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
105
105
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
106
106
  </svg>
@@ -136,14 +136,37 @@ interface TimelineItem {
136
136
  pubDate: string
137
137
  }
138
138
 
139
+ interface UIProps {
140
+ tags?: string
141
+ archives?: string
142
+ categories?: string
143
+ timeline?: string
144
+ viewAllTimeline?: string
145
+ postsCount?: string
146
+ }
147
+
139
148
  interface Props {
140
149
  tags: TagItem[]
141
150
  archives: ArchiveItem[]
142
151
  categories: CategoryItem[]
143
152
  timeline: TimelineItem[]
153
+ localePrefix?: string
154
+ dateLocale?: string
155
+ ui?: UIProps
144
156
  }
145
157
 
146
- const props = defineProps<Props>()
158
+ const props = withDefaults(defineProps<Props>(), {
159
+ localePrefix: '',
160
+ dateLocale: 'en',
161
+ ui: () => ({
162
+ tags: 'Tags',
163
+ archives: 'Archives',
164
+ categories: 'Categories',
165
+ timeline: 'Timeline',
166
+ viewAllTimeline: 'View all timeline',
167
+ postsCount: 'posts',
168
+ })
169
+ })
147
170
 
148
171
  const activeTab = ref('tags')
149
172
 
@@ -201,10 +224,10 @@ const TimelineIcon = {
201
224
  }
202
225
 
203
226
  const tabs = computed(() => [
204
- { id: 'tags', name: '标签', count: props.tags.length, icon: TagIcon },
205
- { id: 'archives', name: '归档', count: props.archives.length, icon: ArchiveIcon },
206
- { id: 'categories', name: '分类', count: props.categories.length, icon: CategoryIcon },
207
- { id: 'timeline', name: '时间轴', count: props.timeline.length, icon: TimelineIcon },
227
+ { id: 'tags', name: props.ui?.tags || 'Tags', count: props.tags.length, icon: TagIcon },
228
+ { id: 'archives', name: props.ui?.archives || 'Archives', count: props.archives.length, icon: ArchiveIcon },
229
+ { id: 'categories', name: props.ui?.categories || 'Categories', count: props.categories.length, icon: CategoryIcon },
230
+ { id: 'timeline', name: props.ui?.timeline || 'Timeline', count: props.timeline.length, icon: TimelineIcon },
208
231
  ])
209
232
 
210
233
  const encodeTag = (tag: string) => {
@@ -227,12 +250,20 @@ const getTagColorClass = (count: number) => {
227
250
 
228
251
  const formatDate = (dateStr: string) => {
229
252
  const date = new Date(dateStr)
230
- return new Intl.DateTimeFormat('zh-CN', {
253
+ return new Intl.DateTimeFormat(props.dateLocale, {
231
254
  year: 'numeric',
232
255
  month: '2-digit',
233
256
  day: '2-digit'
234
257
  }).format(date)
235
258
  }
259
+
260
+ const formatArchiveDate = (year: string, month: string) => {
261
+ const date = new Date(parseInt(year), parseInt(month) - 1, 1)
262
+ return new Intl.DateTimeFormat(props.dateLocale, {
263
+ year: 'numeric',
264
+ month: 'long'
265
+ }).format(date)
266
+ }
236
267
  </script>
237
268
 
238
269
  <style scoped>
@@ -13,12 +13,25 @@ export interface Props {
13
13
  };
14
14
  featured?: boolean;
15
15
  layout?: 'vertical' | 'horizontal';
16
+ localePrefix?: string;
17
+ locale?: string;
18
+ ui?: {
19
+ readMore?: string;
20
+ minuteRead?: string;
21
+ };
16
22
  }
17
23
 
18
- const { post, featured = false, layout = 'vertical' } = Astro.props;
24
+ const {
25
+ post,
26
+ featured = false,
27
+ layout = 'vertical',
28
+ localePrefix = '',
29
+ locale = 'zh-CN',
30
+ ui = { readMore: '阅读更多', minuteRead: '分钟' }
31
+ } = Astro.props;
19
32
 
20
33
  const formattedDate = post.pubDate
21
- ? new Intl.DateTimeFormat('zh-CN', {
34
+ ? new Intl.DateTimeFormat(locale, {
22
35
  year: 'numeric',
23
36
  month: 'long',
24
37
  day: 'numeric'
@@ -31,12 +44,17 @@ const isHorizontal = layout === 'horizontal';
31
44
  const tagToSlug = (tag: string) => tag.toLowerCase().replace(/\s+/g, '-');
32
45
  // 将分类名转换为 slug 格式
33
46
  const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+/g, '-');
47
+
48
+ // Build URLs with locale prefix
49
+ const postUrl = `${localePrefix}/posts/${post.slug}`;
50
+ const tagUrl = (tag: string) => `${localePrefix}/tags/${tagToSlug(tag)}`;
51
+ const categoryUrl = (category: string) => `${localePrefix}/categories/${categoryToSlug(category)}`;
34
52
  ---
35
53
 
36
54
  <article class={`group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300 ${isHorizontal ? 'flex gap-6 items-center' : 'block'}`}>
37
55
  <!-- 文章图片 -->
38
56
  {post.image && (
39
- <a href={`/posts/${post.slug}`} class={`block overflow-hidden rounded-lg ${
57
+ <a href={postUrl} class={`block overflow-hidden rounded-lg ${
40
58
  isHorizontal ? 'w-48 h-32 flex-shrink-0' : 'w-full h-48 mb-4'
41
59
  }`}>
42
60
  <img
@@ -55,7 +73,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
55
73
  <div class="flex flex-wrap gap-2 mb-2">
56
74
  {post.categories.slice(0, 2).map((category) => (
57
75
  <a
58
- href={`/categories/${categoryToSlug(category)}`}
76
+ href={categoryUrl(category)}
59
77
  class="text-xs px-2 py-1 bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 rounded-full hover:bg-amber-200 dark:hover:bg-amber-900/50 transition-colors"
60
78
  onclick="event.stopPropagation();"
61
79
  >
@@ -70,7 +88,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
70
88
  <div class="flex flex-wrap gap-2 mb-3">
71
89
  {post.tags.slice(0, 3).map((tag) => (
72
90
  <a
73
- href={`/tags/${tagToSlug(tag)}`}
91
+ href={tagUrl(tag)}
74
92
  class="text-xs px-2 py-1 bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 rounded-full hover:bg-primary-200 dark:hover:bg-primary-900/50 transition-colors"
75
93
  onclick="event.stopPropagation();"
76
94
  >
@@ -86,7 +104,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
86
104
  )}
87
105
 
88
106
  <!-- 标题 -->
89
- <a href={`/posts/${post.slug}`}>
107
+ <a href={postUrl}>
90
108
  <h3 class={`font-bold text-slate-900 dark:text-slate-100 hover:text-primary-500 transition-colors line-clamp-2 mb-3 ${
91
109
  featured ? 'text-xl' : isHorizontal ? 'text-lg' : 'text-lg'
92
110
  }`}>
@@ -95,7 +113,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
95
113
  </a>
96
114
 
97
115
  <!-- 描述 -->
98
- <a href={`/posts/${post.slug}`}>
116
+ <a href={postUrl}>
99
117
  <p class={`text-slate-600 dark:text-slate-400 line-clamp-3 mb-4 hover:text-slate-700 dark:hover:text-slate-300 transition-colors ${
100
118
  isHorizontal ? 'text-sm' : 'text-base'
101
119
  }`}>
@@ -129,13 +147,13 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
129
147
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
130
148
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
131
149
  </svg>
132
- <span>{post.readingTime}分钟</span>
150
+ <span>{post.readingTime} {ui.minuteRead}</span>
133
151
  </span>
134
152
  )}
135
153
  </div>
136
154
 
137
- <a href={`/posts/${post.slug}`} class="flex items-center text-primary-500 hover:text-primary-600 transition-colors">
138
- <span class="text-sm font-medium">阅读更多</span>
155
+ <a href={postUrl} class="flex items-center text-primary-500 hover:text-primary-600 transition-colors">
156
+ <span class="text-sm font-medium">{ui.readMore}</span>
139
157
  <svg class="w-4 h-4 ml-1 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
140
158
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
141
159
  </svg>