Skip to content

导航 APIs

想看视频教程吗?

next-intl 提供了对 Next.js 导航 APIs(如 <Link />useRouter)的轻量包装,能够在后台自动处理用户的语言和路径。

要创建这些 API,可以通过传入你的 routing 配置来调用 createNavigation 函数:

navigation.ts
import {createNavigation} from 'next-intl/navigation';
import {routing} from './routing';
 
export const {Link, redirect, usePathname, useRouter, getPathname} =
  createNavigation(routing);

该函数通常在像 src/i18n/navigation.ts 这样的集中模块中调用,以方便组件中访问导航 API。

如果构建时不知道所有语言怎么办?

如果你的应用在运行时允许添加或删除语言,可以调用 createNavigation 时不传 locales 参数,从而允许运行时遇到的任意字符串作为有效语言。在这种情况下,你将不会使用 defineRouting 函数。

navigation.ts
import {createNavigation} from 'next-intl/navigation';
 
export const {Link, redirect, usePathname, useRouter, getPathname} =
  createNavigation({
    // ... 可能有其他路由配置,但不包含 `locales` ...
  });

不过请注意,middleware 中的 locales 参数仍是必需的。如果你需要在运行时获取可用语言,可以为 middleware 动态按请求提供路由配置

APIs

创建的导航 API 是对 Next.js 同名 API 的薄封装,并且大多数保持相同的函数签名。你的路由配置和用户语言会自动融合其中。

如果你在路由配置中使用了 pathnames 设置,那么传递给 href 参数的内部路径名将是严格类型化的,并会针对指定语言进行本地化。

该组件封装了 next/link 并根据需要对路径名进行本地化。

import {Link} from '@/i18n/navigation';
 
// 当用户在 `/en` 时,链接会指向 `/en/about`
<Link href="/about">关于</Link>
 
// 也可以通过 `query` 添加搜索参数
<Link href={{pathname: "/users", query: {sortBy: 'name'}}}>用户</Link>
 
// 你可以通过 `locale` 覆盖语言切换到其他语言
// (这会在 <a> 标签上设置 `hreflang` 属性)
<Link href="/" locale="de">切换到德语</Link>

根据是否使用了 pathnames 设置,动态参数可以按以下两种方式传递:

// 1. 最终字符串(不使用 `pathnames` 时)
<Link href="/users/12">苏珊</Link>
 
// 2. 对象(使用 `pathnames` 时)
<Link href={{
  pathname: '/users/[userId]',
  params: {userId: '5'}
}}>
  苏珊
</Link>

useRouter

如果你需要在事件处理函数中程序化导航,next-intl 提供了包装了 Next.js useRouter 的便利 API,会相应地本地化路径名。

'use client';
 
import {useRouter} from '@/i18n/navigation';
 
const router = useRouter();
 
// 当用户在 `/en` 时,router 会跳转到 `/en/about`
router.push('/about');
 
// 也可以通过 `query` 添加搜索参数
router.push({
  pathname: '/users',
  query: {sortBy: 'name'}
});
 
// 你可以通过 `locale` 覆盖语言切换到其他语言
router.replace('/about', {locale: 'de'});

根据是否使用了 pathnames 设置,动态参数可以这样传递:

// 1. 最终字符串(不使用 `pathnames` 时)
router.push('/users/12');
 
// 2. 对象(使用 `pathnames` 时)
router.push({
  pathname: '/users/[userId]',
  params: {userId: '5'}
});
如何更改当前页面的语言?

结合 usePathnameuseRouter,你可以通过保持相同路径并覆盖 locale 来编程更改当前页面语言。

根据是否使用 pathnames 设置,你可能还需要传递 params 以解析内部路径名。

'use client';
 
import {usePathname, useRouter} from '@/i18n/navigation';
import {useParams} from 'next/navigation';
 
const pathname = usePathname();
const router = useRouter();
 
// 不使用 `pathnames`: 直接传递当前 pathname
router.replace(pathname, {locale: 'de'});
 
// 使用 `pathnames`: 额外传递 params
const params = useParams();
router.replace(
  // @ts-expect-error -- TypeScript 会验证仅允许给定 `pathname` 的已知 `params`,由于两者总是对应当前路由,可以跳过运行时检查
  {pathname, params},
  {locale: 'de'}
);

了解更多:

usePathname

获取当前路径名(不含语言前缀)可以调用 usePathname

'use client';
 
import {usePathname} from '@/i18n/navigation';
 
// 当用户在 `/en` 时,这里返回的是 `/`
const pathname = usePathname();

如果你启用了 pathnames 设置,返回的路径名将对应一个内部路径模板(动态参数不会被替换成具体值)。

// 当用户在 `/de/über-uns` 时,返回 `/about`
const pathname = usePathname();
 
// 当用户在 `/de/neuigkeiten/produktneuheit` 时,返回 `/news/[articleSlug]`
const pathname = usePathname();

redirect

如果你想中断渲染并重定向到其他页面,可以调用 redirect 函数。该函数封装了 Next.js 的 redirect,并按需本地化路径名。

注意:即使只是传递当前语言,也必须提供 locale 属性。

import {redirect} from '@/i18n/navigation';
 
// 重定向到 `/en/login`
redirect({href: '/login', locale: 'en'});
 
// 也可通过 `query` 添加搜索参数
redirect({href: '/users', query: {sortBy: 'name'}, locale: 'en'});

根据是否使用了 pathnames 设置,动态参数可以这么传递:

// 1. 最终字符串(不使用 `pathnames`)
redirect({href: '/users/12', locale: 'en'});
 
// 2. 对象(使用 `pathnames`)
redirect({
  href: {
    pathname: '/users/[userId]',
    params: {userId: '5'}
  },
  locale: 'en'
});

如果你使用的是非 alwayslocalePrefix 设置,可以通过将 forcePrefix 设为 true 强制添加语言前缀。这在切换用户语言并需要先更新语言 cookie时非常有用:

// 无视你的 `localePrefix` 设置,首次会跳转到 `/en/about` 以更新语言 cookie
redirect({href: '/about', locale: 'en', forcePrefix: true});
💡

permanentRedirect 也同样支持。

为什么调用 redirect 后,TypeScript 不会正确缩小类型?

TypeScript 目前存在一个限制,导致在调用 redirect 后,类型缩小以及不可达代码检测无法正确生效:

import {redirect} from '@/i18n/navigation';
 
function UserProfile({userId}: {userId?: string}) {
  if (!userId) {
    redirect({href: '/login', locale: 'en'});
  }
 
  // 这里 userId 本应被收窄为 string,但 TypeScript 不能正确分析
}

解决此限制的方式是将调用 redirect 函数作为 return:

if (!userId) {
  return redirect({href: '/login', locale: 'en'});
}
 
// ✅ 这里的 userId 已被收窄为 string

getPathname

当你需要基于某个语言构建特定路径名时,可以调用 getPathname 函数。

import {getPathname} from '@/i18n/navigation';
 
// 返回 `/en/about`
const pathname = getPathname({
  locale: 'en',
  href: '/about'
});
 
// 也可以通过 `query` 添加搜索参数
const pathname = getPathname({
  locale: 'en',
  href: {
    pathname: '/users',
    query: {sortBy: 'name'}
  }
});

根据是否使用了 pathnames 设置,动态参数可以这样传递:

// 1. 最终字符串(不使用 `pathnames`)
const pathname = getPathname({
  locale: 'en',
  href: '/users/12'
});
 
// 2. 对象(使用 `pathnames`)
const pathname = getPathname({
  locale: 'en',
  href: {
    pathname: '/users/[userId]',
    params: {userId: '5'}
  }
});

使用场景示例: