
在 Next.js 应用中,通过动态路由与国际化(i18n)结合,可实现同一页面根据语言环境自动映射不同路径名(如 /en/privacy-policy → /fr/politique-de-confidentialite),无需重复页面逻辑。
在 next.js 应用中,通过动态路由与国际化(i18n)结合,可实现同一页面根据语言环境自动映射不同路径名(如 `/en/privacy-policy` → `/fr/politique-de-confidentialite`),无需重复页面逻辑。
要实现在切换语言时自动更新 URL 路径名(如 privacy-policy → politique-de-confidentialite),同时保持页面逻辑复用,核心在于 将语言(locale)和页面标识符(page slug)均设为动态段,并配合翻译层完成路径映射。
✅ 推荐目录结构(App Router)
app/ ├── [locale]/ # 动态语言段(如 en, fr) │ ├── [slug]/ # 动态内容页段(如 privacy-policy, politique-de-confidentialite) │ │ └── page.tsx # 统一渲染逻辑 │ └── layout.tsx # 可选:含语言切换器的布局
✅ 页面逻辑示例(app/[locale]/[slug]/page.tsx)
import { notFound } from 'next/navigation';
import { getTranslations } from '@/i18n/utils'; // 假设你使用自定义 i18n 工具或 next-intl
export default async function LocalizedPage({
params,
}: {
params: { locale: string; slug: string };
}) {
const { locale, slug } = params;
// 校验 locale 是否合法(如只允许 en/fr/es)
if (!['en', 'fr', 'es'].includes(locale)) notFound();
// 获取当前 locale 下的路由映射表(建议预加载或缓存)
const routeMap = {
en: { 'privacy-policy': 'privacy-policy' },
fr: { 'privacy-policy': 'politique-de-confidentialite' },
es: { 'privacy-policy': 'politica-de-privacidad' },
};
// 反向查找:根据当前 slug 找到对应英文 key(用于内容翻译)
const pageKey = Object.entries(routeMap[locale] || {})
.find(([, translatedSlug]) => translatedSlug === slug)?.[0];
if (!pageKey) notFound();
// 获取多语言内容(标题、正文等)
const t = await getTranslations(locale);
const content = t(`pages.${pageKey}`);
return (
<article>
<h1>{content.title}</h1>
<p>{content.body}</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill4567" title="Comprehensive Three.js 3D graphics reference"><img
src="https://img.php.cn/upload/skill/000/000/081/179012895030480.jpg" alt="Comprehensive Three.js 3D graphics reference" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill4567" title="Comprehensive Three.js 3D graphics reference">Comprehensive Three.js 3D graphics reference</a>
<p>详细的 Three.js 3D 图形参考,涵盖场景设置、相机、几何体、材质、光照、动画、控制器、加载器、数学工具和调试。</p>
</div>
<a href="/xiazai/skill4567" title="Comprehensive Three.js 3D graphics reference" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
{/* 语言切换链接(带目标路径映射) */}
<LanguageSwitcher currentLocale={locale} pageKey={pageKey} />
</article>
);
}✅ 语言切换组件(LanguageSwitcher.tsx)
'use client';
import { usePathname, useRouter } from 'next/navigation';
import { locales, routeMap } from '@/i18n/config';
export function LanguageSwitcher({ currentLocale, pageKey }: {
currentLocale: string;
pageKey: string;
}) {
const pathname = usePathname();
const router = useRouter();
const switchLocale = (nextLocale: string) => {
// 解析当前路径:/en/privacy-policy → ['en', 'privacy-policy']
const segments = pathname.split('/').filter(Boolean);
const [, , ...rest] = segments;
const currentSlug = rest.join('/') || '';
// 查找目标语言下对应的 slug
const targetSlug = routeMap[nextLocale]?.[pageKey] || pageKey;
// 构建新路径并跳转
router.push(`/${nextLocale}/${targetSlug}`);
};
return (
<div className="flex gap-2">
{locales.map((locale) => (
<button
key={locale}
onClick={() => switchLocale(locale)}
disabled={locale === currentLocale}
className={locale === currentLocale ? 'opacity-50' : ''}
>
{locale.toUpperCase()}
</button>
))}
</div>
);
}⚠️ 关键注意事项
-
预定义路由映射:
routeMap必须是静态可分析的(避免运行时动态生成),否则影响 SSG/ISR;建议放在i18n/config.ts中导出。 -
SEO 友好性:每个
<locale>/<slug></slug></locale>组合都应有独立的generateStaticParams(若启用静态生成),确保预渲染所有语言变体。 -
404 处理:对非法 locale 或未映射 slug 主动调用
notFound(),避免错误内容展示。 -
不要混用旧版
pages路由:本方案基于 App Router 的嵌套动态段,pages目录不支持多级动态路径组合。
通过该模式,你完全解耦了「URL 表达」与「业务逻辑」——同一个 page.tsx 文件承载所有语言版本,仅靠参数和翻译层驱动视图,既保障可维护性,又满足 SEO 与用户体验双重要求。


















