From 55f7ea5a048050a2412249afd3ff28e56e66be9f Mon Sep 17 00:00:00 2001 From: AI Directory Platform Date: Thu, 10 Sep 2026 07:39:16 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20/en/*=20=E6=94=B9=E4=B8=BA=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=20404=EF=BC=88URL=20=E5=8D=B3=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=EF=BC=8C=E6=97=A0=E9=BB=98=E8=AE=A4=E8=AF=AD=E8=A8=80=E5=88=AB?= =?UTF-8?q?=E5=90=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 设计契约变更:/ 与 /* 英文站、/zh 与 /zh/* 中文站、/en 与 /en/* 非法路径 直接 404(品牌 404 页),不再 307 规范化到无前缀形式 - proxy rewrite 到内部不存在路径渲染 not-found(404),URL 保持原样 - LanguageSwitcher 同步修复:切英文生成无前缀路径(此前产生 /en/ 会撞 404) - 回归:/en/* 全部 404;/、/login、/zh/* 全部 200;语言切换双向闭环正常; mock 登录正常 --- .../core/src/components/language-switcher.tsx | 6 ++++-- packages/core/src/proxy.ts | 20 +++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/core/src/components/language-switcher.tsx b/packages/core/src/components/language-switcher.tsx index 99e0cc6..039677e 100644 --- a/packages/core/src/components/language-switcher.tsx +++ b/packages/core/src/components/language-switcher.tsx @@ -2,7 +2,7 @@ import { useLocale } from 'next-intl'; import { useRouter, usePathname } from 'next/navigation'; -import { locales, localeNames } from '@/i18n.config'; +import { locales, localeNames, defaultLocale } from '@/i18n.config'; import type { Locale } from '@/i18n.config'; import { useState, useRef, useEffect } from 'react'; import { ChevronDown, Globe, Check } from 'lucide-react'; @@ -15,6 +15,7 @@ export function LanguageSwitcher() { const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); + // URL 即语言:默认语言(en)无前缀,其他语言带 /{locale};去掉当前路径上的语言段后再拼目标前缀 const switchLocale = (newLocale: Locale) => { if (newLocale === locale) { setIsOpen(false); @@ -31,7 +32,8 @@ export function LanguageSwitcher() { } const normalizedPath = path === '/' ? '' : path; - router.push(`/${newLocale}${normalizedPath}`); + const prefix = newLocale === defaultLocale ? '' : `/${newLocale}`; + router.push(`${prefix}${normalizedPath}`); setIsOpen(false); }; diff --git a/packages/core/src/proxy.ts b/packages/core/src/proxy.ts index 4edc9a9..d34c428 100644 --- a/packages/core/src/proxy.ts +++ b/packages/core/src/proxy.ts @@ -6,20 +6,20 @@ const intlMiddleware = createIntlMiddleware(routing); /** * # proxy - * 说明:站点入口中间件。 - * - 显式规范化:/en 与 /en/* 307 → 去前缀形式(/ 与 /*)。 - * 底座契约:默认语言 URL 不带前缀;历史/外部 /en/* 链接永远落到规范 URL, - * 行为不依赖 next-intl 内部实现。中文 /zh/* 由 intlMiddleware 正常处理。 + * 说明:站点入口中间件。URL 即语言(设计契约): + * - / 与 /* …… 英文站(默认语言,无前缀) + * - /zh 与 /zh/* …… 中文站 + * - /en 与 /en/* …… 非法路径,直接 404(品牌 404 页)。 + * 默认语言不提供带前缀的别名,避免 /en/* 与 /* 产生重复内容与重定向链。 */ export function proxy(request: NextRequest) { - const { pathname, search } = request.nextUrl; + const { pathname } = request.nextUrl; if (pathname === '/en' || pathname.startsWith('/en/')) { - const target = pathname === '/en' ? '/' : pathname.slice(3); - return NextResponse.redirect( - new URL(`${target}${search}`, request.url), - 307 - ); + // rewrite 到不存在的内部路径 → Next 渲染 not-found(404),URL 保持原样 + const url = request.nextUrl.clone(); + url.pathname = '/__en_not_available__'; + return NextResponse.rewrite(url, { status: 404 }); } return intlMiddleware(request);