Next.js 13+ App Router 国际化方案实践

Next.js 13+ App Router 国际化方案实践

为什么要写这篇文章?

在开发 Next.js 应用时,我们经常需要支持多语言特性。特别是 Next.js 13+ 版本采用了全新的 App Router,原有的国际化方案已经不再适用。今天我们就来聊聊如何在 Next.js 13+ 中优雅地实现国际化。

方案特点

  • ✨ 完整支持服务端渲染(SSR)
  • 🔍 智能的语言自动检测
  • 🛡️ TypeScript 类型安全
  • 🚀 按需加载翻译资源
  • 💾 用户语言偏好持久化

开始动手

第一步:安装依赖

首先让我们准备好必要的工具库:

css 复制代码
Bash
npm install accept-language i18next i18next-resources-to-backend

这些库各自承担着重要角色:

  • accept-language ➜ 处理浏览器语言检测
  • i18next ➜ 提供核心翻译能力
  • i18next-resources-to-backend ➜ 实现翻译文件按需加载

第二步:组织项目结构

我们需要一个清晰的目录结构来管理国际化相关文件:

ini 复制代码
src/
├── app/
│ ├── [lng]/        # 语言路由目录
│ │ ├── layout.tsx  
│ │ └── page.tsx    
│ └── lib/
│   └── i18n/       # 国际化核心逻辑
│     ├── setting.ts 
│     └── index.ts  
├── middleware.ts    # 语言检测中间件
└── locales/        # 翻译资源目录
    ├── en/
    │ └── translation.json
    └── zh/
      └── translation.json

第三步:配置核心功能

1. 基础配置 (setting.ts)

ini 复制代码
TypeScript
export const fallbackLng = "en";
export const languages = [fallbackLng, "zh"];
export const defaultNS = "translation";

export function getOptions(lng = fallbackLng, ns = defaultNS) {
  return {
    supportedLngs: languages,
    fallbackLng,
    lng,
    fallbackNS: defaultNS,
    defaultNS,
    ns
  };
}

2. 语言检测中间件 (middleware.ts)

javascript 复制代码
TypeScript
import { NextResponse } from "next/server";
import acceptLanguage from "accept-language";
import { fallbackLng, languages } from "@/app/lib/i18n/setting";

acceptLanguage.languages(languages);

export const config = {
  matcher: ["/((?!api|next|images|svg|assets|favicon.ico).*)"]
};

export async function middleware(req: any) {
  const cookieName = "lang";
  
  // ❗️ 跳过内部路由
  if (req.nextUrl.pathname.startsWith("/_next")) {
    return NextResponse.next();
  }
  
  // 智能语言检测
  let lng = req.cookies.has(cookieName) 
    ? acceptLanguage.get(req.cookies.get(cookieName).value)
    : acceptLanguage.get(req.headers.get("Accept-Language"));
    
  lng = lng || fallbackLng;

  // 路径重定向处理
  if (!languages.some(loc => req.nextUrl.pathname.startsWith(`/${loc}`))) {
    return NextResponse.redirect(
      new URL(`/${lng}${req.nextUrl.pathname}`, req.url)
    );
  }

  // 保存语言偏好
  const response = NextResponse.next();
  const pathLng = languages.find(l => req.nextUrl.pathname.startsWith(`/${l}`));
  if (pathLng) response.cookies.set(cookieName, pathLng);
  
  return response;
}

3. 翻译工具函数 (index.ts)

typescript 复制代码
TypeScript
import { createInstance } from "i18next";
import resourcesToBackend from "i18next-resources-to-backend";
import { getOptions } from "./setting";

const initI18next = async (lng: string, ns: string) => {
  const i18nInstance = createInstance();
  await i18nInstance
    .use(
      resourcesToBackend((language: string, namespace: string) =>
        import(`@/locales/${language}/${namespace}.json`)
      )
    )
    .init(getOptions(lng, ns));
  return i18nInstance;
};

export async function useTranslation(lng: string, ns: string = "translation") {
  const i18nextInstance = await initI18next(lng, ns);
  return {
    t: i18nextInstance.getFixedT(lng, ns),
    i18n: i18nextInstance,
  };
}

第四步:实现语言切换

ini 复制代码
TypeScript
'use client';

import { useRouter } from "next/navigation";

export default function LanguageSwitcher({ currentLang }: { currentLang: string }) {
  const router = useRouter();

  const switchLanguage = (newLang: string) => {
    const path = window.location.pathname;
    const newPath = path.replace(`/${currentLang}`, `/${newLang}`);
    router.push(newPath);
  };

  return (
    <select 
      onChange={(e) => switchLanguage(e.target.value)}
      value={currentLang}
      className="p-2 border rounded"
    >
      <option value="en">English</option>
      <option value="zh">中文</option>
    </select>
  );
}

实战应用

让我们看看如何在页面中使用这套国际化方案:

javascript 复制代码
TypeScript
// app/[lng]/page.tsx
export default async function Page({ params }) {
  const { lng } = await params;
  const { t } = await useTranslation(lng);
  return (
    <div>
      <h1>{t("welcome")}</h1>
      <LanguageSwitcher currentLang={lng} />
    </div>
  );
}

翻译文件示例:

javascript 复制代码
JSON
// locales/en/translation.json
{
  "welcome": "Welcome to Next.js i18n"
}

// locales/zh/translation.json
{
  "welcome": "欢迎使用 Next.js 国际化"
}

踩坑指南

在实施过程中,我们需要注意以下几点:

  1. 路由限制 :所有页面必须放在 [lng] 动态路由下
  2. 配置冲突 :不要在 next.config.ts 中使用旧版 i18n 配置
  3. 性能优化:建议按功能模块拆分翻译文件,避免一次性加载过多资源
  4. 类型安全:推荐使用 TypeScript 定义翻译键,提供更好的开发体验

参考

App Router 国际化:2024年Nextjs国际化配置方案

结语

希望这篇文章能帮助你在项目中更好地实现国际化功能。

如果觉得文章对你有帮助,别忘了点个赞 👍

相关推荐
前端双越老师2 小时前
【万字总结】2025 前端+大前端+全栈 知识体系(下)
vue.js·react.js·node.js
Mr.NickJJ9 小时前
JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件
开发语言·javascript·react.js
Mr.NickJJ11 小时前
React Native v0.78 更新
javascript·react native·react.js
你会发光耶15 小时前
彻底理解Redux的使用
前端·react.js·编程语言
十八般不精通15 小时前
react-项目目录认识
前端·react.js
十八般不精通15 小时前
react-项目搭建
前端·react.js
程序员王天18 小时前
阿里云oss开发实践:大文件分片、断点续传、实时进度 React+Node+Socket.IO
前端·react.js·阿里云·node.js
shmily_yyA20 小时前
【2025】Electron + React 架构筑基——从零到一的跨平台开发
react.js·架构·electron
hamburgerDaddy11 天前
从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(十二) socketio 消息处理
前端·javascript·websocket·mongodb·react.js·node.js·express
机巧咸鱼不会受伤1 天前
React Fiber 架构与虚拟 DOM 和 Diff 算法
前端·react.js