本文将带你走通 Next.js 15集成 next-intl步骤,解决多语言配置难题。
1 安装next-intl
npm install next-intl
2 使用next-intl实现国际化
在src目录下新建i18n和components文件夹,i18n用于放置国际化相关配置文件,components用于放置相关组件。
在与src同级目录下新建messages文件夹,messages文件夹主要放置每种语言的配置json文件。
项目文件组织结构如下:
📦 项目根目录
├── 📄 next.config.ts # Next.js配置 + next-intl插件
├── 📄 package.json # 依赖管理
├── 📄 tsconfig.json # TypeScript配置
│
├── 📂 messages/ # 🔑 翻译文件目录
│ ├── 📄 en.json # 英文翻译
│ ├── 📄 zh.json # 中文翻译
│ └── 📄 [其他语言].json # 其他语言文件
│
└── 📂 src/
├── 📂 app/
│ ├── 📂 [locale]/ # 🔑 动态路由段 (必须是locale)
│ │ ├── 📄 layout.tsx # 🔑 国际化布局
│ │ ├── 📄 page.tsx # 主页
│ │ ├── 📂 about/ # 子页面示例
│ │ │ └── 📄 page.tsx
│ │ └── 📂 [其他页面]/
│ │
│ ├── 📄 globals.css # 全局样式
│ └── 📄 favicon.ico # 图标
│
├── 📂 i18n/ # 🔑 国际化配置目录
│ ├── 📄 request.ts # 🔑 请求配置 (必须是request.ts)
│ ├── 📄 routing.ts # 🔑 路由配置
│ └── 📄 navigation.ts # 🔑 导航配置
│
├── 📂 components/ # React组件
│ ├── 📄 LanguageSwitcher.tsx # 语言切换器
│ └── 📄 [其他组件].tsx
│
└── 📄 middleware.ts # 🔑 中间件
2.1 修改next.config.ts
// next.config.ts
import {NextConfig} from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
const nextConfig: NextConfig = {};
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
export default withNextIntl(nextConfig);
2.2 配置路由 src/i18n/routing.ts
创建路由器配置定义支持的区域设置,在src/i18n/routing.ts中增加一下代码
// src/i18n/routing.ts
import {defineRouting} from 'next-intl/routing';
export const routing = defineRouting({
// A list of all locales that are supported
locales: ['en', 'zh'],
// Used when no locale matches
defaultLocale: 'en'
});
2.3 配置导航 src/i18n/navigation.ts
// src/i18n/navigation.ts
import {createNavigation} from 'next-intl/navigation';
import {routing} from './routing';
// Lightweight wrappers around Next.js' navigation
// APIs that consider the routing configuration
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
2.4 配置请求 src/i18n/request.ts
// src/i18n/request.ts
import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';
export default getRequestConfig(async ({requestLocale}) => {
// Typically corresponds to the `[locale]` segment
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default
};
});
2.5 配置中间件 src/middleware.ts
// src/middleware.ts
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';
export default createMiddleware(routing);
export const config = {
// Match all pathnames except for
// - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel`
// - … the ones containing a dot (e.g. `favicon.ico`)
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};
2.6 增加切换语言组件
在src/app目录新建一个components文件夹,然后在该路径下新建一个LanguageSwitcher.tsx文件用于切换语言,具体代码如下
// src/app/components/LanguageSwitcher.tsx
'use client';
import { useLocale, useTranslations } from 'next-intl';
import { useRouter, usePathname } from '@/i18n/navigation';
import { routing } from '@/i18n/routing';
export default function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const t = useTranslations('common');
const handleLanguageChange = (newLocale: string) => {
// Use the internationalized router to change locale
router.replace(pathname, {locale: newLocale});
};
return (
<div className="flex items-center space-x-2">
<span className="text-sm text-gray-600">{t('language')}:</span>
<select
value={locale}
onChange={(e) => handleLanguageChange(e.target.value)}
className="px-3 py-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{routing.locales.map((loc) => (
<option key={loc} value={loc}>
{loc === 'en' ? 'English' : '中文'}
</option>
))}
</select>
</div>
);
}
2.7 修改布局和页面
在src/app目录新建一个[locale]文件夹,必须命名为[locale],不能是其他名称,然后修改src/app/[locale]/layout.tsx文件代码,如下
// src/app/[locale]/layout.tsx
import type { Metadata } from "next";
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { routing } from '@/i18n/routing';
import "../globals.css";
import LanguageSwitcher from '@/components/LanguageSwitcher';
export const metadata: Metadata = {
title: "Next.js 国际化应用",
description: "使用 Next.js 和 next-intl 构建的多语言应用",
};
export function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}
export default async function LocaleLayout({
children,
params
}: {
children: React.ReactNode;
params: Promise<{locale: string}>;
}) {
const {locale} = await params;
// Ensure that the incoming `locale` is valid
if (!routing.locales.includes(locale as any)) {
notFound();
}
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body className="antialiased">
<NextIntlClientProvider messages={messages}>
<header className="border-b border-gray-200 p-4">
<div className="max-w-4xl mx-auto flex justify-between items-center">
<h1 className="text-lg font-semibold">Next.js App</h1>
<LanguageSwitcher />
</div>
</header>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
修改src/app/[locale]/page.tsx文件代码,如下
// src/app/[locale]/page.tsx
import { useTranslations } from 'next-intl';
import { Link } from '@/i18n/navigation';
export default function Home() {
const t = useTranslations('welcome');
const tNav = useTranslations('navigation');
return (
<div className="min-h-screen p-8">
<main className="max-w-4xl mx-auto">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4 text-foreground">
{t('title')}
</h1>
<p className="text-xl text-gray-600 mb-8">
{t('description')}
</p>
<div className="space-x-4 mb-8">
<button className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
{t('getStarted')}
</button>
<Link
href="/about"
className="inline-block px-6 py-3 border border-gray-300 text-foreground rounded-lg hover:bg-gray-50 transition-colors"
>
{t('learnMore')}
</Link>
</div>
{/* Navigation Links */}
<div className="mt-12 pt-8 border-t border-gray-200">
<h2 className="text-lg font-semibold mb-4">导航</h2>
<div className="flex justify-center space-x-6">
<Link
href="/about"
className="text-blue-600 hover:text-blue-800 underline"
>
{tNav('about')}
</Link>
</div>
</div>
</div>
</main>
</div>
);
}
修改src/app/[locale]/about/page.tsx文件代码,如下
// src/app/[locale]/about/page.tsx
import { useTranslations } from 'next-intl';
import { Link } from '@/i18n/navigation';
export default function About() {
const t = useTranslations('navigation');
const tAbout = useTranslations('about');
return (
<div className="min-h-screen p-8">
<main className="max-w-4xl mx-auto">
<div className="mb-6">
<Link
href="/"
className="text-blue-600 hover:text-blue-800 underline"
>
← {t('home')}
</Link>
</div>
<div className="prose prose-lg">
<h1 className="text-3xl font-bold mb-6">{tAbout('title')}</h1>
<div className="space-y-4 text-gray-700">
<p>
{tAbout('description')}
</p>
<p>
{tAbout('features')}
</p>
<ul className="list-disc list-inside space-y-2">
<li>{tAbout('feature1')}</li>
<li>{tAbout('feature2')}</li>
<li>{tAbout('feature3')}</li>
<li>{tAbout('feature4')}</li>
<li>{tAbout('feature5')}</li>
</ul>
</div>
</div>
</main>
</div>
);
}
3 结果
修改完上述代码之后,使用
npm run dev
启动开发服务器。
中文语言界面
英文语言界面
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:Next.js – Next.js 15集成next-intl实现国际化小白教程
原文链接:https://www.stubbornhuang.com/3187/
发布于:2025年09月10日 13:53:30
修改于:2025年09月10日 13:53:30
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论
70