切换语言
切换主题

Next.js 国际化完全指南:next-intl 最佳实践

Next.js 国际化完全指南:next-intl 最佳实践

去年接手一个需要支持多语言的 Next.js 项目,看到配置文件里各种 i18n 的设置,说实话当时有点懵。翻了半天文档才发现,App Router 和以前的 Pages Router 在国际化这块完全是两个思路。折腾了一周,终于把 next-intl 配好了,顺便也踩了不少坑。

今天就来聊聊 Next.js 的国际化方案,特别是在 App Router 下怎么用 next-intl 优雅地实现多语言。

为什么选择 next-intl?

可能你会问,Next.js 不是自带国际化功能吗?确实,在 Pages Router 时代,Next.js 有内置的 i18n 路由支持。但到了 App Router,这个功能被移除了。

官方的建议是:使用第三方库。而 next-intl 就是最受欢迎的选择之一。

next-intl 的优势:

  • App Router 原生支持 - 专门为 App Router 设计,用起来特别顺手
  • 类型安全 - 配合 TypeScript 能做到翻译文本的类型检查
  • 灵活的路由方案 - 支持子路径、域名、Cookie 等多种语言切换方式
  • 强大的功能 - 支持复数、日期格式化、数字格式化、富文本等
  • 性能优秀 - Server Component 友好,支持静态渲染

说实话,相比其他方案,next-intl 的文档写得也挺清楚,上手没那么痛苦。

基础配置:从零开始

1. 安装依赖

首先当然是安装 next-intl:

npm install next-intl
# 或
pnpm add next-intl
# 或
yarn add next-intl

2. 创建翻译文件

在项目根目录创建 messages 文件夹(也可以叫 locales 或其他名字),然后按语言创建 JSON 文件:

messages/
├── en.json
├── zh.json
└── ja.json

messages/zh.json:

{
  "HomePage": {
    "title": "欢迎来到我的网站",
    "description": "这是一个支持多语言的 Next.js 应用"
  },
  "Navigation": {
    "home": "首页",
    "about": "关于",
    "contact": "联系我们"
  }
}

messages/en.json:

{
  "HomePage": {
    "title": "Welcome to My Website",
    "description": "This is a multilingual Next.js application"
  },
  "Navigation": {
    "home": "Home",
    "about": "About",
    "contact": "Contact Us"
  }
}

这种嵌套结构不是必须的,但我发现按页面或组件分组管理起来会方便很多。

3. 配置 i18n.ts

创建 i18n.ts(或 i18n/config.ts)来配置支持的语言:

import { getRequestConfig } from 'next-intl/server';

export default getRequestConfig(async ({ locale }) => ({
  messages: (await import(`./messages/${locale}.json`)).default
}));

这个配置告诉 next-intl 去哪里找翻译文件。locale 参数会自动从 URL 中提取。

4. 创建中间件

在项目根目录创建 middleware.ts,这是处理多语言路由的关键:

import createMiddleware from 'next-intl/middleware';

export default createMiddleware({
  // 支持的语言列表
  locales: ['en', 'zh', 'ja'],

  // 默认语言
  defaultLocale: 'zh',

  // 是否在 URL 中始终显示默认语言
  localePrefix: 'as-needed'
});

export const config = {
  // 匹配所有路径,除了 api、_next/static、_next/image、favicon.ico
  matcher: ['/', '/(zh|en|ja)/:path*', '/((?!api|_next|_next/static|_next/image|favicon.ico).*)']
};

关于 localePrefix 的选项:

  • 'always' - 所有语言都显示前缀,包括默认语言 (/zh/about, /en/about)
  • 'as-needed' - 默认语言不显示前缀 (/about, /en/about)
  • 'never' - 所有语言都不显示前缀(需要其他方式识别语言,比如域名)

我一般用 'as-needed',因为对中文用户友好,URL 看起来也更简洁。

5. 调整 app 目录结构

这是最关键的一步。你需要把所有路由放到 [locale] 动态路由下:

之前的结构:

app/
├── page.tsx
├── about/
│   └── page.tsx
└── layout.tsx

调整后:

app/
├── [locale]/
│   ├── page.tsx
│   ├── about/
│   │   └── page.tsx
│   └── layout.tsx
└── layout.tsx (可选,用于全局配置)

6. 配置根 Layout

app/[locale]/layout.tsx 中配置语言:

import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
import { notFound } from 'next/navigation';

const locales = ['en', 'zh', 'ja'];

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

export default async function LocaleLayout({
  children,
  params: { locale }
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  // 验证语言是否支持
  if (!locales.includes(locale)) {
    notFound();
  }

  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

注意这里的 generateStaticParams,如果你用静态生成,这个函数会告诉 Next.js 需要为哪些语言生成页面。

在组件中使用翻译

配置完成后,就可以在组件中使用翻译了。

Server Component 中使用

import { useTranslations } from 'next-intl';

export default function HomePage() {
  const t = useTranslations('HomePage');

  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

useTranslations 的参数是翻译文件中的命名空间(就是最外层的 key)。如果不传参数,可以用 t('HomePage.title') 这样的完整路径。

Client Component 中使用

在 Client Component 中使用方式完全一样:

'use client';

import { useTranslations } from 'next-intl';

export default function Navigation() {
  const t = useTranslations('Navigation');

  return (
    <nav>
      <a href="/">{t('home')}</a>
      <a href="/about">{t('about')}</a>
      <a href="/contact">{t('contact')}</a>
    </nav>
  );
}

这就是 next-intl 的优雅之处 - Server 和 Client Component 用法一致。

多语言路由处理

获取当前语言

import { useLocale } from 'next-intl';

export default function LanguageSwitcher() {
  const locale = useLocale();

  return <div>Current language: {locale}</div>;
}

创建语言切换器

这是每个国际化网站都需要的功能:

'use client';

import { useLocale } from 'next-intl';
import { usePathname, useRouter } from 'next/navigation';

export default function LanguageSwitcher() {
  const locale = useLocale();
  const router = useRouter();
  const pathname = usePathname();

  const switchLanguage = (newLocale: string) => {
    // 替换路径中的语言部分
    const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
    router.push(newPath);
  };

  return (
    <select value={locale} onChange={(e) => switchLanguage(e.target.value)}>
      <option value="zh">中文</option>
      <option value="en">English</option>
      <option value="ja">日本語</option>
    </select>
  );
}

不过这个方案有个小问题:如果用户在默认语言(比如中文)页面,URL 是 /about,切换到英文后应该是 /en/about。上面的代码需要改进一下:

const switchLanguage = (newLocale: string) => {
  // 移除当前语言前缀
  let path = pathname;
  if (pathname.startsWith(`/${locale}`)) {
    path = pathname.substring(locale.length + 1);
  }

  // 添加新语言前缀(除非是默认语言且配置为 as-needed)
  const newPath = newLocale === 'zh' ? path : `/${newLocale}${path}`;
  router.push(newPath);
};

next-intl 提供了一个增强版的 Link 组件,会自动处理语言前缀:

import { Link } from '@/navigation'; // 需要先配置

<Link href="/about">
  {t('about')}
</Link>

配置 navigation.ts

import { createSharedPathnamesNavigation } from 'next-intl/navigation';

export const locales = ['en', 'zh', 'ja'] as const;
export const localePrefix = 'as-needed';

export const { Link, redirect, usePathname, useRouter } =
  createSharedPathnamesNavigation({ locales, localePrefix });

这样导出的 LinkuseRouter 等组件都会自动处理语言路径。

高级功能

1. 带参数的翻译

翻译内容经常需要插入变量,next-intl 支持这样写:

messages/zh.json:

{
  "welcome": "欢迎回来,{username}!",
  "items": "你有 {count} 个新消息"
}

使用:

const t = useTranslations();

<p>{t('welcome', { username: 'John' })}</p>
<p>{t('items', { count: 5 })}</p>

2. 复数处理

不同语言的复数规则不一样,next-intl 提供了 t.rich 来处理:

messages/en.json:

{
  "messages": {
    "one": "You have {count} message",
    "other": "You have {count} messages"
  }
}

使用:

t('messages', { count: 1 })  // "You have 1 message"
t('messages', { count: 5 })  // "You have 5 messages"

中文没有复数概念,可以这样写:

messages/zh.json:

{
  "messages": "你有 {count} 条消息"
}

3. 日期和数字格式化

next-intl 提供了专门的格式化函数:

import { useFormatter } from 'next-intl';

export default function DateExample() {
  const format = useFormatter();
  const now = new Date();

  return (
    <div>
      <p>{format.dateTime(now, { dateStyle: 'full' })}</p>
      {/* 中文:2025年12月25日星期三 */}
      {/* 英文:Wednesday, December 25, 2025 */}

      <p>{format.number(1234567.89, { style: 'currency', currency: 'CNY' })}</p>
      {/* 中文:¥1,234,567.89 */}
      {/* 英文:CN¥1,234,567.89 */}
    </div>
  );
}

4. 富文本翻译

有时候翻译内容需要包含 HTML 标签或 React 组件:

messages/zh.json:

{
  "richText": "我同意<terms>服务条款</terms>和<privacy>隐私政策</privacy>"
}

使用:

import { useTranslations } from 'next-intl';

export default function Agreement() {
  const t = useTranslations();

  return (
    <p>
      {t.rich('richText', {
        terms: (chunks) => <a href="/terms">{chunks}</a>,
        privacy: (chunks) => <a href="/privacy">{chunks}</a>
      })}
    </p>
  );
}

翻译文件管理最佳实践

随着项目变大,翻译文件会越来越难管理。这里分享几个实用技巧:

1. 按功能模块拆分

不要把所有翻译都塞在一个大 JSON 里,按页面或功能拆分:

messages/
├── zh/
│   ├── common.json      # 通用翻译(按钮、错误提示等)
│   ├── home.json        # 首页
│   ├── about.json       # 关于页
│   └── auth.json        # 认证相关
├── en/
│   ├── common.json
│   ├── home.json
│   ├── about.json
│   └── auth.json

然后在 i18n.ts 中合并:

import { getRequestConfig } from 'next-intl/server';

export default getRequestConfig(async ({ locale }) => {
  const common = (await import(`./messages/${locale}/common.json`)).default;
  const home = (await import(`./messages/${locale}/home.json`)).default;
  const about = (await import(`./messages/${locale}/about.json`)).default;
  const auth = (await import(`./messages/${locale}/auth.json`)).default;

  return {
    messages: {
      common,
      home,
      about,
      auth
    }
  };
});

2. 使用 TypeScript 类型检查

这是我觉得特别有用的功能。定义翻译文件的类型,防止拼写错误:

types/i18n.ts:

import zh from '@/messages/zh.json';

type Messages = typeof zh;

declare global {
  interface IntlMessages extends Messages {}
}

配置 TypeScript(tsconfig.json):

{
  "compilerOptions": {
    "types": ["./types/i18n"]
  }
}

这样在使用 t('xxx') 时,如果 key 不存在,TypeScript 会报错。非常实用!

3. 提取共用翻译

一些通用文本(比如”确定”、“取消”、“保存”)会在很多地方用到,建议单独管理:

messages/zh/common.json:

{
  "actions": {
    "save": "保存",
    "cancel": "取消",
    "delete": "删除",
    "confirm": "确认",
    "edit": "编辑"
  },
  "status": {
    "success": "操作成功",
    "error": "操作失败",
    "loading": "加载中..."
  }
}

使用:

const t = useTranslations('common.actions');
<button>{t('save')}</button>

4. 使用翻译管理工具

项目大了之后,手动维护 JSON 文件会很痛苦。可以考虑这些工具:

  • Tolgee - 开源的翻译管理平台,支持实时编辑
  • Localazy - 自动化翻译工作流
  • i18n Ally (VSCode 插件) - 在编辑器中直接管理翻译

我现在主要用 i18n Ally,在写代码时能直接看到翻译内容,改起来也方便。

5. 缺失翻译的处理

开发时经常遇到某个语言的翻译还没写完的情况。可以配置回退逻辑:

// i18n.ts
export default getRequestConfig(async ({ locale }) => {
  const messages = (await import(`./messages/${locale}.json`)).default;
  const fallback = locale !== 'zh'
    ? (await import(`./messages/zh.json`)).default
    : {};

  return {
    messages: {
      ...fallback,
      ...messages
    }
  };
});

这样如果英文翻译缺失,会自动回退到中文。

性能优化

1. 代码分割

如果翻译文件很大,可以按需加载:

// 只在需要时加载
export default function AdminPage() {
  const t = useTranslations('admin'); // 只加载 admin 命名空间
  // ...
}

2. 静态生成

对于不常变的页面,使用静态生成可以大幅提升性能:

// app/[locale]/about/page.tsx
export const dynamic = 'force-static';

export function generateStaticParams() {
  return [
    { locale: 'zh' },
    { locale: 'en' },
    { locale: 'ja' }
  ];
}

3. 翻译预加载

对于首屏内容,可以预加载翻译文件:

import { getTranslations } from 'next-intl/server';

// 在服务端预加载
export default async function Home() {
  const t = await getTranslations('HomePage');

  return <h1>{t('title')}</h1>;
}

常见问题和解决方案

1. 动态路由的语言切换

动态路由(比如 /blog/[slug])切换语言时需要保持 slug:

const switchLanguage = (newLocale: string) => {
  const segments = pathname.split('/').filter(Boolean);
  // 移除旧的语言前缀
  if (['zh', 'en', 'ja'].includes(segments[0])) {
    segments.shift();
  }
  // 添加新的语言前缀(如果需要)
  if (newLocale !== 'zh' || localePrefix === 'always') {
    segments.unshift(newLocale);
  }
  router.push('/' + segments.join('/'));
};

2. SEO 优化

多语言网站的 SEO 需要特别注意:

// app/[locale]/layout.tsx
export async function generateMetadata({ params: { locale } }) {
  const t = await getTranslations({ locale, namespace: 'metadata' });

  return {
    title: t('title'),
    description: t('description'),
    alternates: {
      canonical: `https://example.com/${locale}`,
      languages: {
        'zh-CN': 'https://example.com/zh',
        'en-US': 'https://example.com/en',
        'ja-JP': 'https://example.com/ja'
      }
    }
  };
}

3. 语言检测

首次访问时自动检测用户语言:

// middleware.ts
import createMiddleware from 'next-intl/middleware';
import { NextRequest } from 'next/server';

const intlMiddleware = createMiddleware({
  locales: ['en', 'zh', 'ja'],
  defaultLocale: 'zh',
  localeDetection: true // 开启自动检测
});

export default function middleware(request: NextRequest) {
  return intlMiddleware(request);
}

next-intl 会根据 Accept-Language 请求头自动选择语言。

4. 保持语言偏好

用户选择语言后,最好记住这个选择:

// 使用 Cookie 保存
const switchLanguage = (newLocale: string) => {
  document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000`;
  router.push(newPath);
};

next-intl 的中间件会自动读取这个 Cookie。

实战案例:完整的国际化项目

最后,我把之前做过的一个小项目的关键代码整理了一下,供参考。

项目结构:

├── app/
│   ├── [locale]/
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── blog/
│   │       └── [slug]/
│   │           └── page.tsx
├── components/
│   ├── LanguageSwitcher.tsx
│   └── Navigation.tsx
├── messages/
│   ├── zh/
│   │   ├── common.json
│   │   └── blog.json
│   ├── en/
│   │   ├── common.json
│   │   └── blog.json
├── i18n.ts
├── middleware.ts
└── navigation.ts

navigation.ts(路由配置):

import { createSharedPathnamesNavigation } from 'next-intl/navigation';

export const locales = ['zh', 'en'] as const;
export const localePrefix = 'as-needed';

export const { Link, redirect, usePathname, useRouter } =
  createSharedPathnamesNavigation({ locales, localePrefix });

components/Navigation.tsx:

'use client';

import { Link } from '@/navigation';
import { useTranslations } from 'next-intl';
import LanguageSwitcher from './LanguageSwitcher';

export default function Navigation() {
  const t = useTranslations('common.navigation');

  return (
    <nav className="flex items-center justify-between p-4">
      <div className="flex gap-4">
        <Link href="/">{t('home')}</Link>
        <Link href="/blog">{t('blog')}</Link>
        <Link href="/about">{t('about')}</Link>
      </div>
      <LanguageSwitcher />
    </nav>
  );
}

这个项目上线后,切换语言非常流畅,没遇到什么问题。

总结

Next.js 的国际化在 App Router 时代确实有点复杂,但掌握了 next-intl 之后,其实并不难:

  1. 核心配置:中间件 + i18n.ts + [locale] 目录
  2. 翻译使用useTranslations Hook 在 Server 和 Client Component 中都能用
  3. 路由处理:使用 next-intl 提供的 Link 和 Router
  4. 文件管理:按模块拆分 + TypeScript 类型检查
  5. 性能优化:静态生成 + 按需加载

说实话,最开始接触这套方案时觉得挺绕的,特别是中间件和动态路由那块。但多写几次就习惯了,现在做国际化项目基本都是这个套路。

如果你正在做或者准备做多语言项目,强烈建议试试 next-intl。虽然有学习成本,但长远来看值得投入时间。

参考资源

希望这篇文章能帮到正在折腾 Next.js 国际化的你!

Next.js国际化完整配置流程

从安装next-intl到配置多语言路由、翻译文件管理的完整步骤

⏱️ 预计耗时: 2 小时

  1. 1

    步骤1: 安装next-intl和基础配置

    安装:
    ```bash
    npm install next-intl
    ```

    创建翻译文件:
    ```
    messages/
    zh.json
    en.json
    ```

    配置middleware.ts:
    ```ts
    import createMiddleware from 'next-intl/middleware'
    import { routing } from './i18n/routing'

    export default createMiddleware(routing)

    export const config = {
    matcher: ['/', '/(zh|en)/:path*']
    }
    ```

    配置app/[locale]/layout.tsx:
    ```tsx
    import { NextIntlClientProvider } from 'next-intl'
    import { getMessages } from 'next-intl/server'

    export default async function LocaleLayout({
    children,
    params: { locale }
    }) {
    const messages = await getMessages()

    return (
    <html lang={locale}>
    <body>
    <NextIntlClientProvider messages={messages}>
    {children}
    </NextIntlClientProvider>
    </body>
    </html>
    )
    }
    ```

    关键点:
    • 使用[locale]动态路由
    • 在layout中提供翻译
    • 配置middleware处理语言切换
  2. 2

    步骤2: 配置多语言路由方案

    方案1:子路径(推荐)
    • URL格式:/zh/about、/en/about
    • 配置简单
    • SEO友好

    方案2:域名
    • URL格式:zh.example.com、en.example.com
    • 需要配置多个域名
    • 更专业

    方案3:Cookie
    • 通过Cookie切换语言
    • URL不包含语言前缀
    • 适合单语言用户

    配置子路径:
    ```ts
    // i18n/routing.ts
    export const routing = {
    locales: ['zh', 'en'],
    defaultLocale: 'zh'
    }
    ```

    使用:
    ```tsx
    import { useTranslations } from 'next-intl'

    export function Page() {
    const t = useTranslations('common')
    return <h1>{t('title')}</h1>
    }
    ```

    关键点:选择适合项目的路由方案,大多数项目用子路径就够了
  3. 3

    步骤3: 管理翻译文件

    创建翻译文件:
    ```json
    // messages/zh.json
    {
    "common": {
    "title": "欢迎",
    "description": "这是一个多语言网站"
    },
    "nav": {
    "home": "首页",
    "about": "关于"
    }
    }
    ```

    使用翻译:
    ```tsx
    import { useTranslations } from 'next-intl'

    export function Page() {
    const t = useTranslations('common')
    return (
    <div>
    <h1>{t('title')}</h1>
    <p>{t('description')}</p>
    </div>
    )
    }
    ```

    类型安全:
    ```ts
    // i18n/request.ts
    import { getRequestConfig } from 'next-intl/server'

    export default getRequestConfig(async ({ locale }) => ({
    messages: (await import(`../messages/${locale}.json`)).default
    }))
    ```

    关键点:
    • 使用嵌套结构组织翻译
    • 配合TypeScript实现类型安全
    • 使用i18n Ally VSCode插件提升开发体验

常见问题

为什么App Router需要next-intl?
原因:App Router移除了Pages Router的内置i18n功能。

Pages Router:
• 有内置的i18n路由支持
• 在next.config.js中配置i18n字段
• 自动处理语言切换

App Router:
• 移除了内置i18n功能
• 需要使用第三方库
• next-intl是最受欢迎的选择

next-intl优势:
• App Router原生支持
• 类型安全
• 灵活的路由方案
• 强大的功能(复数、日期格式化等)
• 性能优秀

建议:如果使用App Router,优先选择next-intl。
next-intl有几种路由方案?
三种路由方案:

方案1:子路径(推荐)
• URL格式:/zh/about、/en/about
• 配置简单
• SEO友好
• 适合大多数项目

方案2:域名
• URL格式:zh.example.com、en.example.com
• 需要配置多个域名
• 更专业
• 适合大型项目

方案3:Cookie
• 通过Cookie切换语言
• URL不包含语言前缀
• 适合单语言用户
• 配置复杂

选择建议:
• 大多数项目 → 子路径
• 大型项目 → 域名
• 特殊需求 → Cookie

关键点:选择适合项目的路由方案,大多数项目用子路径就够了。
如何配置next-intl?
安装:
```bash
npm install next-intl
```

创建翻译文件:
```
messages/
zh.json
en.json
```

配置middleware.ts:
```ts
import createMiddleware from 'next-intl/middleware'
import { routing } from './i18n/routing'

export default createMiddleware(routing)

export const config = {
matcher: ['/', '/(zh|en)/:path*']
}
```

配置app/[locale]/layout.tsx:
```tsx
import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'

export default async function LocaleLayout({
children,
params: { locale }
}) {
const messages = await getMessages()

return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
)
}
```

关键点:
• 使用[locale]动态路由
• 在layout中提供翻译
• 配置middleware处理语言切换
如何管理翻译文件?
创建翻译文件:
```json
// messages/zh.json
{
"common": {
"title": "欢迎",
"description": "这是一个多语言网站"
},
"nav": {
"home": "首页",
"about": "关于"
}
}
```

使用翻译:
```tsx
import { useTranslations } from 'next-intl'

export function Page() {
const t = useTranslations('common')
return (
<div>
<h1>{t('title')}</h1>
<p>{t('description')}</p>
</div>
)
}
```

类型安全:
```ts
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server'

export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default
}))
```

关键点:
• 使用嵌套结构组织翻译
• 配合TypeScript实现类型安全
• 使用i18n Ally VSCode插件提升开发体验

建议:按功能模块组织翻译文件,避免单个文件过大。
next-intl支持哪些功能?
核心功能:
• 翻译文本(t函数)
• 复数处理
• 日期格式化
• 数字格式化
• 富文本支持

使用示例:
```tsx
import { useTranslations, useFormatter } from 'next-intl'

export function Page() {
const t = useTranslations('common')
const format = useFormatter()

return (
<div>
<h1>{t('title')}</h1>
<p>{format.dateTime(new Date(), { dateStyle: 'long' })}</p>
<p>{format.number(1234.56, { style: 'currency', currency: 'USD' })}</p>
</div>
)
}
```

优势:
• 功能强大
• 类型安全
• 性能优秀
• Server Component友好

建议:充分利用next-intl的功能,提升用户体验。
如何实现语言切换?
使用Link组件:
```tsx
import { Link } from '@/i18n/navigation'

<Link href="/about" locale="en">
English
</Link>
<Link href="/about" locale="zh">
中文
</Link>
```

使用useRouter:
```tsx
'use client'
import { useRouter, usePathname } from '@/i18n/navigation'

export function LanguageSwitcher() {
const router = useRouter()
const pathname = usePathname()

const switchLanguage = (locale: string) => {
router.replace(pathname, { locale })
}

return (
<button onClick={() => switchLanguage('en')}>
English
</button>
)
}
```

关键点:
• 使用next-intl提供的Link和useRouter
• 保持当前路径,只切换语言
• 用户体验好

建议:在导航栏或页脚添加语言切换按钮。

9 分钟阅读 · 发布于: 2025年12月25日 · 修改于: 2026年1月22日

评论

使用 GitHub 账号登录后即可评论

相关文章