@jet-w/astro-blog 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Z3O3JK56.js → chunk-CEZBWSMU.js} +34 -5
- package/dist/index.js +1 -1
- package/dist/utils/i18n.d.ts +34 -2
- package/dist/utils/i18n.js +6 -2
- package/package.json +1 -1
- package/src/components/blog/Hero.astro +13 -6
- package/src/components/blog/PostCard.astro +1 -1
- package/src/components/home/FeaturedPostsSection.astro +10 -4
- package/src/components/home/QuickNavSection.astro +10 -4
- package/src/components/home/RecentPostsSection.astro +10 -4
- package/src/components/home/StatsSection.astro +9 -3
- package/src/components/layout/Footer.astro +17 -7
- package/src/components/layout/Header.astro +20 -13
- package/src/components/layout/Sidebar.astro +12 -6
- package/src/components/media/Slides.astro +22 -6
- package/src/components/ui/LanguageSwitcher.vue +24 -12
- package/src/components/ui/MobileMenu.vue +20 -3
- package/src/layouts/BaseLayout.astro +19 -8
- package/src/layouts/PageLayout.astro +9 -3
- package/src/layouts/SlidesLayout.astro +34 -16
- package/src/pages/archives/[year]/[month]/page/[page].astro +42 -18
- package/src/pages/archives/[year]/[month].astro +8 -2
- package/src/pages/archives/index.astro +8 -3
- package/src/pages/categories/[category]/page/[page].astro +40 -18
- package/src/pages/categories/[category].astro +8 -2
- package/src/pages/categories/index.astro +8 -3
- package/src/pages/posts/[...slug].astro +8 -3
- package/src/pages/posts/index.astro +8 -3
- package/src/pages/posts/page/[page].astro +8 -2
- package/src/pages/search.astro +9 -3
- package/src/pages/slides/index.astro +8 -3
- package/src/pages/tags/[tag]/page/[page].astro +39 -17
- package/src/pages/tags/[tag].astro +8 -2
- package/src/pages/tags/index.astro +8 -3
- package/src/plugins/rehype-relative-links.mjs +90 -14
- package/src/utils/i18n.ts +83 -4
- package/templates/default/.claude/ralph-loop.local.md +4 -43
- package/templates/default/Makefile +37 -0
- package/templates/default/astro.config.mjs +7 -3
- package/templates/default/{package.dev.json → package.prod.json} +1 -1
- package/templates/default/package-lock.json +0 -9667
|
@@ -145,11 +145,16 @@ function getTextDirection(locale, config = defaultI18nConfig) {
|
|
|
145
145
|
function isMultiLanguageEnabled(config = defaultI18nConfig) {
|
|
146
146
|
return config.locales.length > 1;
|
|
147
147
|
}
|
|
148
|
-
function getLocalePrefix(locale, config = defaultI18nConfig) {
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
function getLocalePrefix(locale, config = defaultI18nConfig, base) {
|
|
149
|
+
const normalizedBase = base ? base.replace(/\/$/, "") : "";
|
|
150
|
+
let localePrefix = "";
|
|
151
|
+
if (locale !== config.defaultLocale || config.routing.prefixDefaultLocale) {
|
|
152
|
+
localePrefix = `/${locale}`;
|
|
153
|
+
}
|
|
154
|
+
if (!normalizedBase || normalizedBase === "") {
|
|
155
|
+
return localePrefix;
|
|
151
156
|
}
|
|
152
|
-
return
|
|
157
|
+
return `${normalizedBase}${localePrefix}`;
|
|
153
158
|
}
|
|
154
159
|
function getContentPathPrefix(locale, config = defaultI18nConfig) {
|
|
155
160
|
const localeConfig = config.localeConfigs[locale];
|
|
@@ -166,6 +171,28 @@ function filterPostsByLocale(posts, locale, config = defaultI18nConfig) {
|
|
|
166
171
|
return postPath.startsWith(prefix + "/") || postPath === prefix;
|
|
167
172
|
});
|
|
168
173
|
}
|
|
174
|
+
function withBase(path, base) {
|
|
175
|
+
const baseUrl = (base || "/").replace(/\/$/, "");
|
|
176
|
+
if (!baseUrl || baseUrl === "") {
|
|
177
|
+
return path;
|
|
178
|
+
}
|
|
179
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
180
|
+
if (normalizedPath === "/") {
|
|
181
|
+
return `${baseUrl}/`;
|
|
182
|
+
}
|
|
183
|
+
return `${baseUrl}${normalizedPath}`;
|
|
184
|
+
}
|
|
185
|
+
function removeBase(path, base) {
|
|
186
|
+
const baseUrl = (base || "/").replace(/\/$/, "");
|
|
187
|
+
if (!baseUrl || baseUrl === "") {
|
|
188
|
+
return path;
|
|
189
|
+
}
|
|
190
|
+
if (path.startsWith(baseUrl)) {
|
|
191
|
+
const rest = path.slice(baseUrl.length);
|
|
192
|
+
return rest || "/";
|
|
193
|
+
}
|
|
194
|
+
return path;
|
|
195
|
+
}
|
|
169
196
|
|
|
170
197
|
export {
|
|
171
198
|
getLocaleFromPath,
|
|
@@ -182,5 +209,7 @@ export {
|
|
|
182
209
|
isMultiLanguageEnabled,
|
|
183
210
|
getLocalePrefix,
|
|
184
211
|
getContentPathPrefix,
|
|
185
|
-
filterPostsByLocale
|
|
212
|
+
filterPostsByLocale,
|
|
213
|
+
withBase,
|
|
214
|
+
removeBase
|
|
186
215
|
};
|
package/dist/index.js
CHANGED
package/dist/utils/i18n.d.ts
CHANGED
|
@@ -109,8 +109,18 @@ declare function isMultiLanguageEnabled(config?: I18nConfig): boolean;
|
|
|
109
109
|
/**
|
|
110
110
|
* Get prefix for a locale in routes
|
|
111
111
|
* Returns empty string for default locale if prefixDefaultLocale is false
|
|
112
|
+
* If base is provided, it will be prepended to the locale prefix
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* // Without base
|
|
116
|
+
* getLocalePrefix('en', config) // '' (for default locale)
|
|
117
|
+
* getLocalePrefix('zh-CN', config) // '/zh-CN'
|
|
118
|
+
*
|
|
119
|
+
* // With base '/my-blog'
|
|
120
|
+
* getLocalePrefix('en', config, '/my-blog') // '/my-blog'
|
|
121
|
+
* getLocalePrefix('zh-CN', config, '/my-blog') // '/my-blog/zh-CN'
|
|
112
122
|
*/
|
|
113
|
-
declare function getLocalePrefix(locale: string, config?: I18nConfig): string;
|
|
123
|
+
declare function getLocalePrefix(locale: string, config?: I18nConfig, base?: string): string;
|
|
114
124
|
/**
|
|
115
125
|
* Get content path prefix for a specific locale
|
|
116
126
|
* Returns the contentPathPrefix from locale config, or undefined if not set
|
|
@@ -129,5 +139,27 @@ declare function getContentPathPrefix(locale: string, config?: I18nConfig): stri
|
|
|
129
139
|
declare function filterPostsByLocale<T extends {
|
|
130
140
|
id: string;
|
|
131
141
|
}>(posts: T[], locale: string, config?: I18nConfig): T[];
|
|
142
|
+
/**
|
|
143
|
+
* Add base URL prefix to a path
|
|
144
|
+
* This is used when the site is deployed to a subdirectory (e.g., /jet-w.astro-blog/)
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* // If BASE_URL is '/jet-w.astro-blog'
|
|
148
|
+
* withBase('/posts') // '/jet-w.astro-blog/posts'
|
|
149
|
+
* withBase('/') // '/jet-w.astro-blog/'
|
|
150
|
+
*
|
|
151
|
+
* // If BASE_URL is '/'
|
|
152
|
+
* withBase('/posts') // '/posts'
|
|
153
|
+
*/
|
|
154
|
+
declare function withBase(path: string, base?: string): string;
|
|
155
|
+
/**
|
|
156
|
+
* Remove base URL prefix from a path
|
|
157
|
+
* Useful for getting the actual path without base prefix
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* // If BASE_URL is '/jet-w.astro-blog'
|
|
161
|
+
* removeBase('/jet-w.astro-blog/posts', '/jet-w.astro-blog') // '/posts'
|
|
162
|
+
*/
|
|
163
|
+
declare function removeBase(path: string, base?: string): string;
|
|
132
164
|
|
|
133
|
-
export { type AlternateLink, I18nConfig, Locale, type MergedLocaleConfig, UITranslations, filterPostsByLocale, formatDate, formatDateShort, getAlternateLinks, getContentPathPrefix, getLocaleByCode, getLocaleConfig, getLocaleFromPath, getLocalePrefix, getLocalizedPath, getTextDirection, isMultiLanguageEnabled, isRTL, removeLocalePrefix, t };
|
|
165
|
+
export { type AlternateLink, I18nConfig, Locale, type MergedLocaleConfig, UITranslations, filterPostsByLocale, formatDate, formatDateShort, getAlternateLinks, getContentPathPrefix, getLocaleByCode, getLocaleConfig, getLocaleFromPath, getLocalePrefix, getLocalizedPath, getTextDirection, isMultiLanguageEnabled, isRTL, removeBase, removeLocalePrefix, t, withBase };
|
package/dist/utils/i18n.js
CHANGED
|
@@ -12,9 +12,11 @@ import {
|
|
|
12
12
|
getTextDirection,
|
|
13
13
|
isMultiLanguageEnabled,
|
|
14
14
|
isRTL,
|
|
15
|
+
removeBase,
|
|
15
16
|
removeLocalePrefix,
|
|
16
|
-
t
|
|
17
|
-
|
|
17
|
+
t,
|
|
18
|
+
withBase
|
|
19
|
+
} from "../chunk-CEZBWSMU.js";
|
|
18
20
|
import "../chunk-DAH2XP4W.js";
|
|
19
21
|
import {
|
|
20
22
|
builtInTranslations,
|
|
@@ -43,7 +45,9 @@ export {
|
|
|
43
45
|
getUITranslations,
|
|
44
46
|
isMultiLanguageEnabled,
|
|
45
47
|
isRTL,
|
|
48
|
+
removeBase,
|
|
46
49
|
removeLocalePrefix,
|
|
47
50
|
t,
|
|
51
|
+
withBase,
|
|
48
52
|
zhCNTranslations
|
|
49
53
|
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import { siteConfig } from '@jet-w/astro-blog/config';
|
|
|
3
3
|
import type { I18nConfig } from '../../config/i18n';
|
|
4
4
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
5
5
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
6
|
-
import { getLocaleFromPath, getLocaleConfig } from '../../utils/i18n';
|
|
6
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, withBase, removeBase } from '../../utils/i18n';
|
|
7
7
|
|
|
8
8
|
export interface Props {
|
|
9
9
|
i18nConfig?: I18nConfig;
|
|
@@ -11,10 +11,17 @@ export interface Props {
|
|
|
11
11
|
|
|
12
12
|
const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
13
13
|
|
|
14
|
-
// Get
|
|
15
|
-
const
|
|
14
|
+
// Get base URL for prefixing links
|
|
15
|
+
const base = import.meta.env.BASE_URL;
|
|
16
|
+
|
|
17
|
+
// Remove base URL from current path for locale detection
|
|
18
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
19
|
+
|
|
20
|
+
// Get current locale from URL (use path without base)
|
|
21
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
16
22
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
17
23
|
const ui = localeConfig.ui;
|
|
24
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
18
25
|
---
|
|
19
26
|
|
|
20
27
|
<section class="py-16 mb-16">
|
|
@@ -23,7 +30,7 @@ const ui = localeConfig.ui;
|
|
|
23
30
|
{siteConfig.avatar && (
|
|
24
31
|
<div class="mb-8">
|
|
25
32
|
<img
|
|
26
|
-
src={siteConfig.avatar}
|
|
33
|
+
src={withBase(siteConfig.avatar, base)}
|
|
27
34
|
alt={siteConfig.author}
|
|
28
35
|
class="w-32 h-32 rounded-full mx-auto shadow-lg"
|
|
29
36
|
/>
|
|
@@ -101,7 +108,7 @@ const ui = localeConfig.ui;
|
|
|
101
108
|
<!-- 行动按钮 -->
|
|
102
109
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
|
103
110
|
<a
|
|
104
|
-
href=
|
|
111
|
+
href={`${localePrefix}/posts`}
|
|
105
112
|
class="btn-primary inline-flex items-center space-x-2"
|
|
106
113
|
>
|
|
107
114
|
<span>{ui.browsePosts}</span>
|
|
@@ -111,7 +118,7 @@ const ui = localeConfig.ui;
|
|
|
111
118
|
</a>
|
|
112
119
|
|
|
113
120
|
<a
|
|
114
|
-
href=
|
|
121
|
+
href={`${localePrefix}/about`}
|
|
115
122
|
class="btn-secondary inline-flex items-center space-x-2"
|
|
116
123
|
>
|
|
117
124
|
<span>{ui.aboutMe}</span>
|
|
@@ -45,7 +45,7 @@ const tagToSlug = (tag: string) => tag.toLowerCase().replace(/\s+/g, '-');
|
|
|
45
45
|
// 将分类名转换为 slug 格式
|
|
46
46
|
const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+/g, '-');
|
|
47
47
|
|
|
48
|
-
// Build URLs with locale prefix
|
|
48
|
+
// Build URLs with locale prefix (localePrefix already contains base URL)
|
|
49
49
|
const postUrl = `${localePrefix}/posts/${post.slug}`;
|
|
50
50
|
const tagUrl = (tag: string) => `${localePrefix}/tags/${tagToSlug(tag)}`;
|
|
51
51
|
const categoryUrl = (category: string) => `${localePrefix}/categories/${categoryToSlug(category)}`;
|
|
@@ -7,7 +7,7 @@ import { getCollection } from 'astro:content';
|
|
|
7
7
|
import type { I18nConfig } from '../../config/i18n';
|
|
8
8
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
9
9
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
10
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
10
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
11
11
|
|
|
12
12
|
export interface Props {
|
|
13
13
|
count?: number;
|
|
@@ -16,11 +16,17 @@ export interface Props {
|
|
|
16
16
|
|
|
17
17
|
const { count = 3, i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
18
18
|
|
|
19
|
-
// Get
|
|
20
|
-
const
|
|
19
|
+
// Get base URL for prefixing links
|
|
20
|
+
const base = import.meta.env.BASE_URL;
|
|
21
|
+
|
|
22
|
+
// Remove base URL from current path for locale detection
|
|
23
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
24
|
+
|
|
25
|
+
// Get current locale from URL (use path without base)
|
|
26
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
21
27
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
22
28
|
const ui = localeConfig.ui;
|
|
23
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
29
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
24
30
|
|
|
25
31
|
const allPosts = await getCollection('posts');
|
|
26
32
|
const publishedPosts = allPosts
|
|
@@ -7,7 +7,7 @@ import { getCollection } from 'astro:content';
|
|
|
7
7
|
import type { I18nConfig } from '../../config/i18n';
|
|
8
8
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
9
9
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
10
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
10
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
11
11
|
|
|
12
12
|
interface Props {
|
|
13
13
|
i18nConfig?: I18nConfig;
|
|
@@ -15,11 +15,17 @@ interface Props {
|
|
|
15
15
|
|
|
16
16
|
const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
17
17
|
|
|
18
|
-
// Get
|
|
19
|
-
const
|
|
18
|
+
// Get base URL for prefixing links
|
|
19
|
+
const base = import.meta.env.BASE_URL;
|
|
20
|
+
|
|
21
|
+
// Remove base URL from current path for locale detection
|
|
22
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
23
|
+
|
|
24
|
+
// Get current locale and translations (use path without base)
|
|
25
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
20
26
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
21
27
|
const ui = localeConfig.ui;
|
|
22
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
28
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
23
29
|
|
|
24
30
|
const allPosts = await getCollection('posts', ({ data }) => !data.draft);
|
|
25
31
|
|
|
@@ -7,7 +7,7 @@ import { getCollection } from 'astro:content';
|
|
|
7
7
|
import type { I18nConfig } from '../../config/i18n';
|
|
8
8
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
9
9
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
10
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
10
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
11
11
|
|
|
12
12
|
export interface Props {
|
|
13
13
|
count?: number;
|
|
@@ -16,11 +16,17 @@ export interface Props {
|
|
|
16
16
|
|
|
17
17
|
const { count = 6, i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
18
18
|
|
|
19
|
-
// Get
|
|
20
|
-
const
|
|
19
|
+
// Get base URL for prefixing links
|
|
20
|
+
const base = import.meta.env.BASE_URL;
|
|
21
|
+
|
|
22
|
+
// Remove base URL from current path for locale detection
|
|
23
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
24
|
+
|
|
25
|
+
// Get current locale from URL (use path without base)
|
|
26
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
21
27
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
22
28
|
const ui = localeConfig.ui;
|
|
23
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
29
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
24
30
|
|
|
25
31
|
const allPosts = await getCollection('posts');
|
|
26
32
|
const publishedPosts = allPosts
|
|
@@ -6,7 +6,7 @@ import { getCollection } from 'astro:content';
|
|
|
6
6
|
import type { I18nConfig } from '../../config/i18n';
|
|
7
7
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
8
8
|
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
9
|
-
import { getLocaleFromPath, getLocaleConfig, filterPostsByLocale } from '../../utils/i18n';
|
|
9
|
+
import { getLocaleFromPath, getLocaleConfig, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
10
10
|
|
|
11
11
|
export interface Props {
|
|
12
12
|
i18nConfig?: I18nConfig;
|
|
@@ -14,8 +14,14 @@ export interface Props {
|
|
|
14
14
|
|
|
15
15
|
const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
16
16
|
|
|
17
|
-
// Get
|
|
18
|
-
const
|
|
17
|
+
// Get base URL for prefixing links
|
|
18
|
+
const base = import.meta.env.BASE_URL;
|
|
19
|
+
|
|
20
|
+
// Remove base URL from current path for locale detection
|
|
21
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
22
|
+
|
|
23
|
+
// Get current locale from URL (use path without base)
|
|
24
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
19
25
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
20
26
|
const ui = localeConfig.ui;
|
|
21
27
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { siteConfig, footerConfig, defaultIcons } from '@jet-w/astro-blog/config';
|
|
3
3
|
import type { I18nConfig } from '../../config/i18n';
|
|
4
4
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
5
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix } from '../../utils/i18n';
|
|
5
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, removeBase, withBase } from '../../utils/i18n';
|
|
6
6
|
|
|
7
7
|
export interface Props {
|
|
8
8
|
i18nConfig?: I18nConfig;
|
|
@@ -10,7 +10,13 @@ export interface Props {
|
|
|
10
10
|
|
|
11
11
|
const { i18nConfig = defaultI18nConfig } = Astro.props;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Get base URL for prefixing links
|
|
14
|
+
const base = import.meta.env.BASE_URL;
|
|
15
|
+
|
|
16
|
+
// Remove base URL from current path for locale detection
|
|
17
|
+
const pathWithoutBase = removeBase(Astro.url.pathname, base);
|
|
18
|
+
|
|
19
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
14
20
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
15
21
|
const ui = localeConfig.ui;
|
|
16
22
|
|
|
@@ -24,9 +30,13 @@ function getIcon(link: { type: string; icon?: string }): string {
|
|
|
24
30
|
return link.icon || defaultIcons[link.type] || '';
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
// Get locale prefix for RSS link
|
|
28
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
29
|
-
|
|
33
|
+
// Get locale prefix for RSS link (already includes base)
|
|
34
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
35
|
+
// Apply withBase to rssUrl if it's a relative path
|
|
36
|
+
const configRssUrl = localeFooterConfig.rssUrl || footerConfig.rssUrl;
|
|
37
|
+
const rssUrl = configRssUrl
|
|
38
|
+
? (configRssUrl.startsWith('/') && !configRssUrl.startsWith(base) ? withBase(configRssUrl, base) : configRssUrl)
|
|
39
|
+
: `${localePrefix}/rss.xml`;
|
|
30
40
|
|
|
31
41
|
const copyright = (localeFooterConfig.copyright || footerConfig.copyright)
|
|
32
42
|
.replace('{year}', String(currentYear))
|
|
@@ -40,7 +50,7 @@ const copyright = (localeFooterConfig.copyright || footerConfig.copyright)
|
|
|
40
50
|
<div class="flex items-center space-x-3 mb-3">
|
|
41
51
|
{(localeSiteConfig.avatar || siteConfig.avatar) && (
|
|
42
52
|
<img
|
|
43
|
-
src={localeSiteConfig.avatar || siteConfig.avatar}
|
|
53
|
+
src={withBase(localeSiteConfig.avatar || siteConfig.avatar || '', base)}
|
|
44
54
|
alt={localeSiteConfig.title || siteConfig.title}
|
|
45
55
|
class="w-8 h-8 rounded-full"
|
|
46
56
|
/>
|
|
@@ -63,7 +73,7 @@ const copyright = (localeFooterConfig.copyright || footerConfig.copyright)
|
|
|
63
73
|
<div class="flex flex-wrap gap-x-4 gap-y-1">
|
|
64
74
|
{(localeFooterConfig.quickLinks || footerConfig.quickLinks).map((item) => (
|
|
65
75
|
<a
|
|
66
|
-
href={item.href}
|
|
76
|
+
href={withBase(item.href, base)}
|
|
67
77
|
class="text-sm text-slate-600 dark:text-slate-400 hover:text-primary-500 transition-colors"
|
|
68
78
|
>
|
|
69
79
|
{item.name}
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
getLocaleFromPath,
|
|
12
12
|
getLocaleConfig,
|
|
13
13
|
removeLocalePrefix,
|
|
14
|
+
removeBase,
|
|
14
15
|
isMultiLanguageEnabled,
|
|
16
|
+
withBase,
|
|
15
17
|
} from '../../utils/i18n';
|
|
16
18
|
|
|
17
19
|
export interface Props {
|
|
@@ -21,7 +23,15 @@ export interface Props {
|
|
|
21
23
|
const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
22
24
|
|
|
23
25
|
const currentPath = Astro.url.pathname;
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
// Get base URL for prefixing links
|
|
28
|
+
const base = import.meta.env.BASE_URL;
|
|
29
|
+
|
|
30
|
+
// Get base path without base URL - needed for locale detection
|
|
31
|
+
const pathWithoutBase = removeBase(currentPath, base);
|
|
32
|
+
|
|
33
|
+
// Detect current locale from path (without base URL)
|
|
34
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
25
35
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
26
36
|
const ui = localeConfig.ui;
|
|
27
37
|
|
|
@@ -29,8 +39,8 @@ const ui = localeConfig.ui;
|
|
|
29
39
|
const localeSiteConfig = localeConfig.site;
|
|
30
40
|
const menu = localeConfig.menu;
|
|
31
41
|
|
|
32
|
-
// Get
|
|
33
|
-
const
|
|
42
|
+
// Get path without locale prefix for language switcher
|
|
43
|
+
const pathWithoutLocale = removeLocalePrefix(pathWithoutBase, i18nConfig);
|
|
34
44
|
|
|
35
45
|
// Check if multi-language is enabled
|
|
36
46
|
const showLanguageSwitcher = isMultiLanguageEnabled(i18nConfig);
|
|
@@ -46,10 +56,10 @@ const localesForSwitcher = i18nConfig.locales.map(l => ({
|
|
|
46
56
|
<div class="container mx-auto px-4">
|
|
47
57
|
<nav class="flex items-center justify-between h-16">
|
|
48
58
|
<div class="flex items-center space-x-4">
|
|
49
|
-
<a href=
|
|
59
|
+
<a href={withBase('/', base)} class="flex items-center space-x-3 hover:opacity-80 transition-opacity">
|
|
50
60
|
{(localeSiteConfig.avatar || siteConfig.avatar) && (
|
|
51
61
|
<img
|
|
52
|
-
src={localeSiteConfig.avatar || siteConfig.avatar}
|
|
62
|
+
src={withBase(localeSiteConfig.avatar || siteConfig.avatar || '', base)}
|
|
53
63
|
alt={localeSiteConfig.title || siteConfig.title}
|
|
54
64
|
class="w-8 h-8 rounded-full"
|
|
55
65
|
/>
|
|
@@ -70,9 +80,9 @@ const localesForSwitcher = i18nConfig.locales.map(l => ({
|
|
|
70
80
|
<div class="hidden md:flex items-center space-x-8">
|
|
71
81
|
{menu.map((item) => (
|
|
72
82
|
<a
|
|
73
|
-
href={item.href}
|
|
83
|
+
href={withBase(item.href, base)}
|
|
74
84
|
class={`text-sm font-medium transition-colors hover:text-primary-500 ${
|
|
75
|
-
currentPath === item.href || (item.href !== '/' && currentPath.startsWith(item.href))
|
|
85
|
+
currentPath === withBase(item.href, base) || (item.href !== '/' && currentPath.startsWith(withBase(item.href, base)))
|
|
76
86
|
? 'text-primary-500'
|
|
77
87
|
: 'text-slate-700 dark:text-slate-300'
|
|
78
88
|
}`}
|
|
@@ -98,9 +108,10 @@ const localesForSwitcher = i18nConfig.locales.map(l => ({
|
|
|
98
108
|
client:load
|
|
99
109
|
locales={localesForSwitcher}
|
|
100
110
|
currentLocale={currentLocale}
|
|
101
|
-
currentPath={
|
|
111
|
+
currentPath={pathWithoutLocale}
|
|
102
112
|
defaultLocale={i18nConfig.defaultLocale}
|
|
103
113
|
prefixDefaultLocale={i18nConfig.routing.prefixDefaultLocale}
|
|
114
|
+
base={base}
|
|
104
115
|
/>
|
|
105
116
|
</div>
|
|
106
117
|
)}
|
|
@@ -113,11 +124,7 @@ const localesForSwitcher = i18nConfig.locales.map(l => ({
|
|
|
113
124
|
<MobileMenu
|
|
114
125
|
client:load
|
|
115
126
|
navigation={menu}
|
|
116
|
-
|
|
117
|
-
currentLocale={currentLocale}
|
|
118
|
-
currentPath={basePath}
|
|
119
|
-
defaultLocale={i18nConfig.defaultLocale}
|
|
120
|
-
prefixDefaultLocale={i18nConfig.routing.prefixDefaultLocale}
|
|
127
|
+
base={base}
|
|
121
128
|
/>
|
|
122
129
|
</div>
|
|
123
130
|
</div>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from '@jet-w/astro-blog/utils/sidebar';
|
|
11
11
|
import type { I18nConfig } from '../../config/i18n';
|
|
12
12
|
import { defaultI18nConfig } from '../../config/i18n';
|
|
13
|
-
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, formatDate, filterPostsByLocale } from '../../utils/i18n';
|
|
13
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, formatDate, filterPostsByLocale, removeBase } from '../../utils/i18n';
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
16
16
|
currentPath?: string;
|
|
@@ -19,11 +19,17 @@ interface Props {
|
|
|
19
19
|
|
|
20
20
|
const { currentPath = Astro.url.pathname, i18nConfig = defaultI18nConfig } = Astro.props;
|
|
21
21
|
|
|
22
|
-
// Get
|
|
23
|
-
const
|
|
22
|
+
// Get base URL for prefixing links
|
|
23
|
+
const base = import.meta.env.BASE_URL;
|
|
24
|
+
|
|
25
|
+
// Remove base URL from current path for locale detection and path filtering
|
|
26
|
+
const pathWithoutBase = removeBase(currentPath, base);
|
|
27
|
+
|
|
28
|
+
// Get current locale and translations (use path without base)
|
|
29
|
+
const currentLocale = getLocaleFromPath(pathWithoutBase, i18nConfig);
|
|
24
30
|
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
25
31
|
const ui = localeConfig.ui;
|
|
26
|
-
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
32
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig, base);
|
|
27
33
|
|
|
28
34
|
// Get locale-specific sidebar config (already merged with defaults in getLocaleConfig)
|
|
29
35
|
const sidebarConfig = localeConfig.sidebar;
|
|
@@ -34,8 +40,8 @@ const allPosts = await getCollection('posts', ({ data }) => !data.draft);
|
|
|
34
40
|
// Filter posts by current locale for sidebar widgets
|
|
35
41
|
const localePosts = filterPostsByLocale(allPosts, currentLocale, i18nConfig);
|
|
36
42
|
|
|
37
|
-
// 根据当前路径过滤侧边栏组
|
|
38
|
-
const filteredGroups = filterGroupsByPath(sidebarConfig.groups,
|
|
43
|
+
// 根据当前路径过滤侧边栏组 (use path without base for pattern matching)
|
|
44
|
+
const filteredGroups = filterGroupsByPath(sidebarConfig.groups, pathWithoutBase);
|
|
39
45
|
|
|
40
46
|
// 创建过滤后的配置
|
|
41
47
|
const filteredConfig = { ...sidebarConfig, groups: filteredGroups };
|
|
@@ -78,6 +78,9 @@ const revealConfig = JSON.stringify({
|
|
|
78
78
|
touch: true,
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
// Get base URL for static assets
|
|
82
|
+
const base = import.meta.env.BASE_URL;
|
|
83
|
+
|
|
81
84
|
// 获取 slot 内容
|
|
82
85
|
const slotContent = await Astro.slots.render('default');
|
|
83
86
|
const hasInlineContent = slotContent && slotContent.trim().length > 0;
|
|
@@ -164,13 +167,26 @@ const slidesData = hasInlineContent ? parseSlides(slotContent) : [];
|
|
|
164
167
|
</div>
|
|
165
168
|
|
|
166
169
|
<!-- Reveal.js 资源 -->
|
|
167
|
-
<link rel="stylesheet" href=
|
|
168
|
-
<link rel="stylesheet" href={
|
|
169
|
-
<link rel="stylesheet" href=
|
|
170
|
+
<link rel="stylesheet" href={`${base}slides/reveal.css`} />
|
|
171
|
+
<link rel="stylesheet" href={`${base}slides/theme/${theme}.css`} />
|
|
172
|
+
<link rel="stylesheet" href={`${base}slides/plugin/highlight/monokai.css`} />
|
|
170
173
|
|
|
171
|
-
|
|
172
|
-
<script is:inline
|
|
173
|
-
|
|
174
|
+
<!-- Reveal.js - dynamically loaded with base URL -->
|
|
175
|
+
<script is:inline define:vars={{ baseUrl: base }}>
|
|
176
|
+
(function() {
|
|
177
|
+
var scripts = [
|
|
178
|
+
baseUrl + 'slides/reveal.js',
|
|
179
|
+
baseUrl + 'slides/plugin/markdown/markdown.js',
|
|
180
|
+
baseUrl + 'slides/plugin/highlight/highlight.js'
|
|
181
|
+
];
|
|
182
|
+
scripts.forEach(function(src) {
|
|
183
|
+
var script = document.createElement('script');
|
|
184
|
+
script.src = src;
|
|
185
|
+
script.async = false;
|
|
186
|
+
document.head.appendChild(script);
|
|
187
|
+
});
|
|
188
|
+
})();
|
|
189
|
+
</script>
|
|
174
190
|
|
|
175
191
|
<script is:inline define:vars={{ slidesId, revealConfig }}>
|
|
176
192
|
(function() {
|
|
@@ -101,16 +101,19 @@ interface Props {
|
|
|
101
101
|
locales: LocaleInfo[];
|
|
102
102
|
/** Current locale code */
|
|
103
103
|
currentLocale: string;
|
|
104
|
-
/** Current page path (without locale prefix) */
|
|
104
|
+
/** Current page path (without locale prefix and without base) */
|
|
105
105
|
currentPath: string;
|
|
106
106
|
/** Default locale code */
|
|
107
107
|
defaultLocale: string;
|
|
108
108
|
/** Whether to prefix the default locale in URLs */
|
|
109
109
|
prefixDefaultLocale?: boolean;
|
|
110
|
+
/** Base URL for the site (e.g., '/jet-w.astro-blog') */
|
|
111
|
+
base?: string;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
const props = withDefaults(defineProps<Props>(), {
|
|
113
115
|
prefixDefaultLocale: false,
|
|
116
|
+
base: '/',
|
|
114
117
|
});
|
|
115
118
|
|
|
116
119
|
const isOpen = ref(false);
|
|
@@ -138,23 +141,32 @@ function closeDropdown() {
|
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
function getLocalizedUrl(targetLocale: string): string {
|
|
141
|
-
// Normalize the path
|
|
142
|
-
let
|
|
143
|
-
if (!
|
|
144
|
-
|
|
144
|
+
// Normalize the current path (without base and without locale)
|
|
145
|
+
let pagePath = props.currentPath;
|
|
146
|
+
if (!pagePath.startsWith('/')) {
|
|
147
|
+
pagePath = '/' + pagePath;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
// Normalize base URL - remove trailing slash
|
|
151
|
+
const baseUrl = props.base?.replace(/\/$/, '') || '';
|
|
152
|
+
|
|
153
|
+
// Build locale prefix
|
|
154
|
+
let localePrefix = '';
|
|
155
|
+
if (targetLocale !== props.defaultLocale || props.prefixDefaultLocale) {
|
|
156
|
+
localePrefix = `/${targetLocale}`;
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
//
|
|
153
|
-
if (
|
|
154
|
-
|
|
159
|
+
// Combine: base + locale + path
|
|
160
|
+
if (pagePath === '/') {
|
|
161
|
+
// Home page
|
|
162
|
+
if (localePrefix) {
|
|
163
|
+
return baseUrl ? `${baseUrl}${localePrefix}/` : `${localePrefix}/`;
|
|
164
|
+
}
|
|
165
|
+
return baseUrl ? `${baseUrl}/` : '/';
|
|
155
166
|
}
|
|
156
167
|
|
|
157
|
-
|
|
168
|
+
// Other pages
|
|
169
|
+
return `${baseUrl}${localePrefix}${pagePath}`;
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
// Close dropdown when clicking outside
|