Next.js 16 Page Router 国际化 [特殊字符]

Next.js 16 Page Router 国际化 🌐

引言

在现代 Web 应用开发中,国际化(i18n)已经成为一个必备功能。传统的 Next.js 国际化方案通常采用 URL 前缀方式(如 /en/page/zh-CN/page),这种方式虽然实现简单,但存在一些明显的问题:

  1. URL 频繁变更:用户切换语言时,页面 URL 会发生变化
  2. SEO 分散:相同内容分散在不同 URL 下,影响搜索引擎优化
  3. 用户体验不佳:复制链接时需要考虑语言前缀

那么,有没有一种方式可以在不改变 URL 的情况下实现国际化呢?答案是肯定的!本文将详细介绍我在 Next.js 16 项目中实现的无 URL 变更的国际化方案,采用浏览器缓存 + Cookie 机制管理语言切换,保持 URL 稳定的同时提供流畅的国际化体验。

技术栈

技术 版本 用途
Next.js 16.0.7 前端框架
React 19.2.0 UI 库
TypeScript 5.5.4 类型系统
next-i18next 15.4.3 国际化核心库
react-i18next 16.3.5 React 国际化集成
js-cookie 3.0.5 Cookie 管理
ahooks 3.9.6 React Hooks 工具库

项目结构

src/
├── components/            # 组件目录
│   └── I18nLngSelector.tsx  # 语言选择器组件
├── i18n/                 # 国际化配置目录
│   ├── hooks/            # 自定义 Hooks
│   │   └── useI18n.ts    # 语言切换钩子
│   ├── locales/          # 翻译资源文件
│   │   ├── en/           # 英文翻译
│   │   │   ├── common.json       # 通用翻译
│   │   │   └── index_page.json   # 首页翻译
│   │   └── zh-CN/        # 中文翻译
│   │       ├── common.json       # 通用翻译
│   │       └── index_page.json   # 首页翻译
│   ├── type.ts           # TypeScript 类型定义
│   └── i18next.d.ts      # 类型声明文件
└── pages/                # 页面目录
    ├── _app.tsx          # 应用入口(语言初始化)
    └── index.tsx         # 首页

核心实现

1. next-i18next 配置

首先,我们需要配置 next-i18next,创建 next-i18next.config.js 文件:

// next-i18next.config.js
// @ts-check

/**
 * @type {import('next-i18next').UserConfig}
 */
module.exports = {
   
   
  // 开发环境下启用调试模式
  debug: process.env.NODE_ENV === 'development',
  // 国际化配置
  i18n: {
   
   
    // 默认语言
    defaultLocale: 'zh-CN',
    // 支持的语言列表
    locales: ['zh-CN', 'en'],
    // 禁用自动语言检测,使用自定义逻辑
    localeDetection: false,
  },
  // 语言资源文件路径
  localePath: './src/i18n/locales',
  // 开发环境下在预渲染时重新加载语言资源
  reloadOnPrerender: process.env.NODE_ENV === 'development',
}

配置说明

  • debug: true:开发环境下启用调试模式,便于开发调试
  • defaultLocale: 'zh-CN':设置默认语言为中文
  • locales: ['zh-CN', 'en']:配置支持的语言列表
  • localeDetection: false:禁用自动语言检测,使用自定义的语言检测和切换逻辑
  • localePath: './src/i18n/locales':指定语言资源文件的存放路径

2. 自定义语言切换钩子

核心逻辑在于自定义的 useI18nLng 钩子,它负责处理语言的存储、切换和初始化:

// src/i18n/hooks/useI18n.ts
import {
   
    useTranslation } from 'next-i18next';
import {
   
    LangEnum } from '@/i18n/type';
import Cookies from "js-cookie";

// 语言存储的键名
const LANG_KEY = 'NEXT_LOCALE';

/**
 * 检查当前是否在 iframe 中
 */
const isInIframe = () => {
   
   
  try {
   
   
    return window.self !== window.top;
  } catch (e) {
   
   
    return true; // 发生异常时默认认为在 iframe 中
  }
};

/**
 * 设置语言到存储中
 */
const setLang = (value: string) => {
   
   
  if (isInIframe()) {
   
   
    // 在 iframe 中只使用 localStorage
    localStorage.setItem(LANG_KEY, value);
  } else {
   
   
    // 不在 iframe 中,同时使用 Cookie 和 localStorage
    Cookies.set(LANG_KEY, value, {
   
    expires: 30 }); // Cookie 有效期30天
    localStorage.setItem(LANG_KEY, value);
  }
};

/**
 * 从存储中获取语言
 */
const getLang = () => {
   
   
  return localStorage.getItem(LANG_KEY) || Cookies.get(LANG_KEY);
};

/**
 * 自定义 i18n 语言切换钩子
 */
export const useI18nLng = () => {
   
   
  // 获取 i18n 实例
  const {
   
    i18n } = useTranslation();
  
  // 语言映射表,确保语言代码的一致性
  const languageMap: Record<string, string> = {
   
   
    'zh-CN': LangEnum.zh_CN,
    en: LangEnum.en
### App Router 的结构与组织 Next.js 的 App Router 采用了一种基于文件系统的路由机制,其核心在于 `app/` 目录的结构设计。在该目录下,每个子目录代表一个路由段,而 `page.tsx` 文件则对应于该路由的入口页面。例如,`app/about/page.tsx` 会映射到 `/about` 路径。这种结构允许开发者通过简单的文件组织来定义复杂的 URL 模式[^2]。 ### 嵌套布局与共享组件 在 App Router 中,可以通过创建 `layout.tsx` 文件来定义共享布局。这些布局文件可以嵌套在不同的目录中,以支持多层布局结构。例如,在 `app/dashboard/layout.tsx` 中定义的布局可以被 `app/dashboard/settings/page.tsx` 和 `app/dashboard/analytics/page.tsx` 共享。这种嵌套机制使得开发者能够轻松地在多个页面之间复用相同的 UI 元素,如导航栏或侧边栏[^3]。 ### 动态路由与参数捕获 动态路由在 App Router 中通过使用方括号 `[]` 来定义。例如,`app/posts/[id]/page.tsx` 会匹配所有形如 `/posts/123` 的路径,其中 `123` 是动态参数。这种机制使得开发者能够根据路由参数加载相应的内容。动态参数可以通过 `params` 对象在页面组件中访问,从而实现动态内容的渲染[^4]。 ### 并行路由与多模块渲染 App Router 支持并行路由的概念,允许在同一 URL 路径下渲染多个独立模块。这通过使用 `@` 符号定义路由组来实现。例如,`app/(main)/home/@modal/page.tsx` 可以与 `app/(main)/home/page.tsx` 同时渲染,其中 `@modal` 表示一个独立的路由组。这种方式非常适合于需要在同一个页面上展示多个独立部分的应用场景,如模态框与主内容共存的情况。 ### 拦截路由与路径匹配 拦截路由是 App Router 提供的一个高级特性,它允许开发者通过特定的命名规则来匹配和处理路径。命名文件夹时,可以使用 `(.)` 来匹配同一层级,`(..)` 来匹配上一层级,`(..)(..)` 来匹配上上层级,以及 `(...)` 来匹配根目录。这种机制为开发者提供了强大的路径匹配能力,使得路由逻辑更加灵活和强大[^5]。 ### 数据预取与性能优化 Next.js 的 App Router 还支持数据预取功能,这有助于提升应用的性能。当用户导航到一个新页面时,Next.js 会自动预取目标页面的数据,从而减少用户的等待时间。这种预取机制可以通过 `Link` 组件的 `prefetch` 属性来控制,开发者可以选择性地启用或禁用某些页面的数据预取[^4]。 ### 示例代码 以下是一个简单的动态路由示例,展示了如何在 App Router 中实现动态内容加载: ```tsx // app/posts/[id]/page.tsx import { useRouter } from 'next/router'; const PostPage = () => { const router = useRouter(); const { id } = router.query; return ( <div> <h1>Post ID: {id}</h1> {/* 根据 id 加载相应的文章内容 */} </div> ); }; export default PostPage; ``` 上述代码展示了如何在 `page.tsx` 文件中获取动态路由参数,并根据这些参数渲染不同的内容。通过这种方式,开发者可以轻松地构建出响应式和动态的 Web 应用程序。 ### 相关问题 1. 如何在 Next.js 中使用嵌套布局? 2. 如何在 App Router 中实现并行路由? 3. 如何在 Next.js 中定义拦截路由? 4. 如何在 App Router 中进行数据预取? 5. 如何在 Next.js 中处理动态路由参数?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值