diff --git a/.vitepress/data/locales/en.ts b/.vitepress/data/locales/en.ts
index ce7bf7b..1209aa4 100644
--- a/.vitepress/data/locales/en.ts
+++ b/.vitepress/data/locales/en.ts
@@ -68,6 +68,11 @@ export const en: LocaleMessages = {
alsoDownload: 'Or download directly from:',
},
+ region: {
+ blocked: 'Downloads are not available in your country or region',
+ hint: 'Sorry, this content is currently unavailable in your region.',
+ },
+
history: {
title: 'Historical Releases',
subtitle: 'Select a release, choose source, then download the installer.',
diff --git a/.vitepress/data/locales/index.ts b/.vitepress/data/locales/index.ts
index 338e0b9..c937c65 100644
--- a/.vitepress/data/locales/index.ts
+++ b/.vitepress/data/locales/index.ts
@@ -62,6 +62,10 @@ export interface LocaleMessages {
historicalBtn: string;
alsoDownload: string;
};
+ region: {
+ blocked: string;
+ hint: string;
+ };
history: {
title: string;
subtitle: string;
diff --git a/.vitepress/data/locales/zh.ts b/.vitepress/data/locales/zh.ts
index 9b8b18d..7047b9b 100644
--- a/.vitepress/data/locales/zh.ts
+++ b/.vitepress/data/locales/zh.ts
@@ -69,6 +69,11 @@ export const zh: LocaleMessages = {
alsoDownload: '你也可以直接在下方的链接中下载',
},
+ region: {
+ blocked: '您所在的国家或地区暂不支持下载',
+ hint: '很抱歉,该内容目前无法在您的所在地区提供。',
+ },
+
history: {
title: '历史版本下载',
subtitle: '选择版本并切换下载源后下载对应安装包。',
diff --git a/.vitepress/theme/components/HistoryReleases.vue b/.vitepress/theme/components/HistoryReleases.vue
index b9c7619..e263bdc 100644
--- a/.vitepress/theme/components/HistoryReleases.vue
+++ b/.vitepress/theme/components/HistoryReleases.vue
@@ -2,6 +2,7 @@
import { useData } from 'vitepress';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { getMessages, type Lang } from '../../data/i18n';
+import { useRegionGuard } from '../composables/useRegionGuard';
interface ReleaseAsset {
name: string;
@@ -54,6 +55,13 @@ const messages = computed(() => {
return getMessages(currentLang).history;
});
+// biome-ignore lint/correctness/noUnusedVariables: used in Vue template
+const {
+ blocked,
+ message: blockedMessage,
+ hint: blockedHint,
+} = useRegionGuard();
+
function formatDate(value: string) {
if (!value) return '';
const date = new Date(value);
@@ -282,7 +290,11 @@ function handleOutsideClick(event: MouseEvent) {
-
+
+
{{ blockedMessage }}
+
{{ blockedHint }}
+
+
+import { useRegionGuard } from '../composables/useRegionGuard';
+
interface MirrorItem {
href: string;
label: string;
@@ -7,18 +9,27 @@ interface MirrorItem {
defineProps<{
items: MirrorItem[];
}>();
+
+// biome-ignore lint/correctness/noUnusedVariables: used in Vue template
+const { blocked, message, hint } = useRegionGuard();
@@ -48,6 +59,22 @@ defineProps<{
color: var(--vp-c-brand-1);
}
+.mirror-blocked {
+ grid-column: 1 / -1;
+ padding: 0.75rem 1rem;
+ background: var(--vp-c-bg-soft);
+ border: 1px dashed var(--vp-c-divider);
+ border-radius: 8px;
+ text-align: center;
+ color: var(--vp-c-text-2);
+}
+
+.mirror-blocked-hint {
+ margin-top: 0.35rem;
+ font-size: 0.875rem;
+ color: var(--vp-c-text-3);
+}
+
@media (max-width: 768px) {
.mirror-grid {
grid-template-columns: 1fr;
diff --git a/.vitepress/theme/components/RegionGate.vue b/.vitepress/theme/components/RegionGate.vue
new file mode 100644
index 0000000..c0ef2b3
--- /dev/null
+++ b/.vitepress/theme/components/RegionGate.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
{{ message }}
+
{{ hint }}
+
+
+
+
+
diff --git a/.vitepress/theme/composables/useRegionGuard.ts b/.vitepress/theme/composables/useRegionGuard.ts
new file mode 100644
index 0000000..25abbae
--- /dev/null
+++ b/.vitepress/theme/composables/useRegionGuard.ts
@@ -0,0 +1,66 @@
+import { useData } from 'vitepress';
+import { computed, onMounted, ref } from 'vue';
+import { getMessages, type Lang } from '../../data/i18n';
+
+const TRACE_URL = 'https://www.cloudflare.com/cdn-cgi/trace';
+const TRACE_TIMEOUT = 5000;
+
+const CHINA_TIME_ZONES = new Set([
+ 'Asia/Shanghai',
+ 'Asia/Chongqing',
+ 'Asia/Harbin',
+ 'Asia/Urumqi',
+ 'Asia/Kashgar',
+ 'PRC',
+]);
+
+const blocked = ref(false);
+let checkStarted = false;
+
+function isChinaTimeZone(): boolean {
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? '';
+ return CHINA_TIME_ZONES.has(timeZone);
+}
+
+async function requestTraceLocation(): Promise {
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(), TRACE_TIMEOUT);
+ try {
+ const response = await fetch(TRACE_URL, {
+ cache: 'no-store',
+ signal: controller.signal,
+ });
+ if (!response.ok) return '';
+ const text = await response.text();
+ return (/(?:^|\n)loc=([^\s\r]+)/.exec(text)?.[1] ?? '').toUpperCase();
+ } catch {
+ return '';
+ } finally {
+ clearTimeout(timer);
+ }
+}
+
+function startRegionCheck() {
+ if (checkStarted) return;
+ checkStarted = true;
+ if (isChinaTimeZone()) {
+ blocked.value = true;
+ return;
+ }
+ void requestTraceLocation().then((loc) => {
+ if (loc === 'CN') blocked.value = true;
+ });
+}
+
+export function useRegionGuard() {
+ const { lang } = useData();
+ onMounted(startRegionCheck);
+ const region = computed(
+ () => getMessages((lang.value || 'zh-CN') as Lang).region,
+ );
+ return {
+ blocked,
+ message: computed(() => region.value.blocked),
+ hint: computed(() => region.value.hint),
+ };
+}
diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts
index a206f4b..4d138e5 100644
--- a/.vitepress/theme/index.ts
+++ b/.vitepress/theme/index.ts
@@ -12,6 +12,7 @@ import HistoryReleases from './components/HistoryReleases.vue';
import HomeUnderline from './components/HomeUnderline.vue';
import ImageGallery from './components/ImageGallery.vue';
import MirrorGrid from './components/MirrorGrid.vue';
+import RegionGate from './components/RegionGate.vue';
import SiteFooter from './components/SiteFooter.vue';
import VideoSection from './components/VideoSection.vue';
@@ -36,6 +37,7 @@ export default {
app.component('Copy', CopyText);
// 注册自定义组件
app.component('MirrorGrid', MirrorGrid);
+ app.component('RegionGate', RegionGate);
app.component('AboutPage', AboutPage);
app.component('HistoryReleases', HistoryReleases);
app.component('VideoSection', VideoSection);
diff --git a/src/download.md b/src/download.md
index ca2ca42..f561c36 100644
--- a/src/download.md
+++ b/src/download.md
@@ -32,8 +32,12 @@ keywords: PCL下载,Minecraft启动器下载,PCL社区版下载,启动器下载
如果你有 Mirror 酱 CDK 可以直接使用此高速下载源
+
+
[Mirror 酱下载](https://mirrorchyan.com/zh/projects?rid=PCL2-CE&source=pcl2ce-web){.download-btn}
+
+
你也可以直接在下方的链接中下载
## 下载链接
diff --git a/src/en/download.md b/src/en/download.md
index 275e077..9ae761d 100644
--- a/src/en/download.md
+++ b/src/en/download.md
@@ -32,8 +32,12 @@ By clicking any download button below and running the program, you agree to our
If you have a MirrorChyan CDK, you can use this high-speed download source directly
+
+
[MirrorChyan Download](https://mirrorchyan.com/en/projects?rid=PCL2-CE&source=pcl2ce-gh-release){.download-btn}
+
+
You can also download directly from the links below
## Download Links