示例
Next.js 自 v10.0.0
起内置支持国际化(i18n)路由。您可以提供一系列地区设置、默认地区设置以及特定于域的地区设置,Next.js 将自动处理路由。
i18n 路由支持目前旨在补充现有的 i18n 库解决方案,如 react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、tolgee
、inlang
等,通过简化路由和地区解析来实现。
开始使用
要开始使用,请将 i18n
配置添加到您的 next.config.js
文件中。
地区设置是 UTS 地区设置标识符,这是定义地区设置的标准格式。
通常,地区设置标识符由语言、地区和脚本组成,它们之间用连字符分隔:language-region-script
。地区和脚本是可选的。例如:
en-US
- 在美国使用的英语nl-NL
- 在荷兰使用的荷兰语nl
- 荷兰语,没有特定地区
如果用户地区设置为 nl-BE
并且未在您的配置中列出,如果有可用的 nl
,则它们将被重定向到 nl
,否则将被重定向到默认地区设置。
如果您不打算支持一个国家的所有地区,那么包含将作为回退的国家地区设置是一个好习惯。
module.exports = {
i18n: {
// 这些都是您希望在
// 您的应用程序中支持的所有地区设置
locales: ['en-US', 'fr', 'nl-NL'],
// 当访问非地区前缀路径时,例如 `/hello`
// 您希望使用的默认地区设置
defaultLocale: 'en-US',
// 这是地区设置域列表以及它们应该处理的默认地区设置
// (这些仅在设置域路由时需要)
// 注意:要匹配,子域必须包含在域值中,例如 "fr.example.com"。
domains: [
{
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
// 还可以使用可选的 http 字段来测试
// 使用 http 而不是 https 本地测试地区设置域
http: true,
},
],
},
}
国际化策略
有两种国际化处理策略:子路径路由和域名路由。
子路径路由
子路径路由将语言环境信息放在URL路径中。
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL'],
defaultLocale: 'en-US',
},
}
使用上述配置,en-US
、fr
和 nl-NL
将可用于路由,en-US
是默认的语言环境。如果你有一个 pages/blog.js
,那么以下URL将可用:
/blog
/fr/blog
/nl-nl/blog
默认语言环境没有前缀。
域名路由
通过使用域名路由,你可以配置不同的域名来提供不同的语言环境:
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
defaultLocale: 'en-US',
domains: [
{
// 注意:子域名必须包含在域名值中才能匹配
// 例如,如果预期的主机名是 www.example.com,则应使用 www.example.com
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
// 指定应重定向到该域名的其他语言环境
locales: ['nl-BE'],
},
],
},
}
例如,如果你有 pages/blog.js
,那么以下URL将可用:
example.com/blog
www.example.com/blog
example.fr/blog
example.nl/blog
example.nl/nl-BE/blog
自动地区检测
当用户访问应用程序的根目录(通常是 /
)时,Next.js 会尝试根据 Accept-Language
头部和当前域名自动检测用户偏好的语言环境。
如果检测到的地区与默认地区不同,用户将被重定向到:
- 使用子路径路由时: 带有地区前缀的路径
- 使用域名路由时: 指定该地区为默认值的域名
使用域名路由时,如果用户带有 Accept-Language
头部 fr;q=0.9
访问 example.com
,他们将被重定向到 example.fr
,因为该域名默认处理 fr
地区。
使用子路径路由时,用户将被重定向到 /fr
。
为默认地区添加前缀
在 Next.js 12 和 中间件 中,我们可以通过一个变通方法为默认地区添加前缀。
例如,下面是一个支持几种语言的 next.config.js
文件。请注意,"default"
地区是有意添加的。
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
接下来,我们可以使用 中间件 添加自定义路由规则:
import { NextRequest, NextResponse } from 'next/server'
const PUBLIC_FILE = /\.(.*)$/
export async function middleware(req: NextRequest) {
if (
req.nextUrl.pathname.startsWith('/_next') ||
req.nextUrl.pathname.includes('/api/') ||
PUBLIC_FILE.test(req.nextUrl.pathname)
) {
return
}
if (req.nextUrl.locale === 'default') {
const locale = req.cookies.get('NEXT_LOCALE')?.value || 'en'
return NextResponse.redirect(
new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
)
}
}
这个 中间件 跳过了为 API 路由 和 公共 文件(如字体或图片)添加默认前缀。如果请求是针对默认地区的,我们将重定向到我们的前缀 /en
。
禁用自动地区检测
可以通过以下方式禁用自动地区检测:
module.exports = {
i18n: {
localeDetection: false,
},
}
当 localeDetection
设置为 false
时,Next.js 将不再根据用户偏好的地区自动重定向,并且只会提供从上述基于地区的域名或地区路径检测到的地区信息。
访问地区信息
您可以通过 Next.js 路由器访问地区信息。例如,使用 useRouter()
钩子,可以访问以下属性:
locale
包含当前活动的地区。locales
包含所有配置的地区。defaultLocale
包含配置的默认地区。
当使用 getStaticProps
或 getServerSideProps
预渲染 页面时,地区信息会在提供给函数的 上下文 中提供。
当利用 getStaticPaths
时,配置的地区会在函数的上下文参数中提供,在 locales
下,配置的 defaultLocale 在 defaultLocale
下。
切换语言环境
您可以使用 next/link
或 next/router
在不同的语言环境之间进行切换。
对于 next/link
,可以提供一个 locale
属性来切换到当前激活的语言环境之外的其他语言环境。如果没有提供 locale
属性,在客户端过渡期间将使用当前激活的 locale
。例如:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/another" locale="fr">
To /fr/another
</Link>
)
}
当直接使用 next/router
方法时,您可以通过过渡选项指定应使用的 locale
。例如:
import { useRouter } from 'next/router'
export default function IndexPage(props) {
const router = useRouter()
return (
<div
onClick={() => {
router.push('/another', '/another', { locale: 'fr' })
}}
>
to /fr/another
</div>
)
}
请注意,为了在仅切换 locale
同时保留所有路由信息,如动态路由查询值或隐藏的 href 查询值,您可以将 href
参数作为对象提供:
import { useRouter } from 'next/router'
const router = useRouter()
const { pathname, asPath, query } = router
// 仅更改语言环境并保留包括 href 查询在内的所有其他路由信息
router.push({ pathname, query }, asPath, { locale: nextLocale })
有关 router.push
对象结构的更多信息,请参见此处。
如果您有一个已经包含语言环境的 href
,您可以选择不自动处理语言环境前缀:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/fr/another" locale={false}>
To /fr/another
</Link>
)
}
利用 NEXT_LOCALE
饼干
Next.js 允许设置一个 NEXT_LOCALE=the-locale
饼干,它优先于接受语言头部。这个饼干可以使用语言切换器设置,然后当用户回到网站时,它将在从 /
重定向到正确的语言环境位置时利用饼干中指定的语言环境。
例如,如果用户在其接受语言头部中喜欢语言环境 fr
,但是设置了 NEXT_LOCALE=en
饼干,那么在访问 /
时用户将被重定向到 en
语言环境位置,直到饼干被移除或过期。
搜索引擎优化
由于 Next.js 知道用户访问的语言,它将自动向 <html>
标签添加 lang
属性。
Next.js 不知道页面的变体,因此需要您使用 next/head
添加 hreflang
元标签。您可以在 Google Webmasters 文档中了解更多关于 hreflang
的信息。
静态生成与国际化路由如何协同工作?
注意,国际化路由并不与
output: 'export'
集成,因为它不利用 Next.js 的路由层。不使用output: 'export'
的混合 Next.js 应用程序是完全支持的。
动态路由和 getStaticProps
页面
对于使用 getStaticProps
和 动态路由 的页面,所有希望预渲染的页面的本地化变体都需要从 getStaticPaths
返回。除了为 paths
返回的 params
对象外,您还可以返回一个 locale
字段,指定您想要渲染的本地化设置。例如:
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// 如果没有提供 `locale`,则只生成 defaultLocale
{ params: { slug: 'post-1' }, locale: 'en-US' },
{ params: { slug: 'post-1' }, locale: 'fr' },
],
fallback: true,
}
}
对于 自动静态优化 和非动态 getStaticProps
页面,将为每种本地化设置生成页面的一个版本。这很重要,因为它可能会根据 getStaticProps
中配置的本地化设置数量增加构建时间。
例如,如果您配置了50种本地化设置,并且有10个使用 getStaticProps
的非动态页面,这意味着 getStaticProps
将被调用500次。在每次构建期间将生成10个页面的50个版本。
要减少具有 getStaticProps
的动态页面的构建时间,请使用 fallback
模式。这允许您在构建期间仅从 getStaticPaths
返回最受欢迎的路径和本地化设置以进行预渲染。然后,Next.js 将在运行时根据请求构建剩余页面。
自动静态优化页面
对于 自动静态优化 的页面,将为每种本地化设置生成页面的一个版本。
非动态 getStaticProps 页面
对于非动态 getStaticProps
页面,如上所述,为每种本地化设置生成一个版本。getStaticProps
将使用正在渲染的每种 locale
被调用。如果您希望从预渲染中选择退出某个特定的本地化设置,您可以从 getStaticProps
返回 notFound: true
,这样页面的这个变体就不会被生成。
export async function getStaticProps({ locale }) {
// 调用外部 API 端点以获取帖子。
// 您可以使用任何数据获取库
const res = await fetch(`https://.../posts?locale=${locale}`)
const posts = await res.json()
if (posts.length === 0) {
return {
notFound: true,
}
}
// 通过返回 { props: posts },博客组件
// 将在构建时作为属性接收 `posts`
return {
props: {
posts,
},
}
}
i18n 配置的限制
locales
: 总共100个本地化设置domains
: 总共100个本地化设置域名项
须知:这些限制最初是为了防止 构建时的性能问题。您可以使用 Next.js 12 中的 中间件 进行自定义路由来绕过这些限制。