如何在 Next.js 中实现国际化
示例
自 v10.0.0
起,Next.js 内置支持国际化 (i18n) 路由。您可以提供区域设置列表、默认区域设置和特定于域的区域设置,Next.js 将自动处理路由。
i18n 路由支持目前旨在通过简化路由和区域设置解析来补充现有的 i18n 库解决方案,例如 react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、tolgee
、paraglide-next
、next-intlayer
、gt-react
等。
开始使用
要开始使用,请将 i18n
配置添加到您的 next.config.js
文件中。
区域设置是 UTS 区域设置标识符,一种用于定义区域设置的标准化格式。
通常,区域设置标识符由语言、地区和脚本组成,并以破折号分隔:language-region-script
。地区和脚本是可选的。例如:
en-US
- 在美国使用的英语nl-NL
- 在荷兰使用的荷兰语nl
- 荷兰语,无特定地区
如果用户区域设置是 nl-BE
并且未在您的配置中列出,他们将被重定向到 nl
(如果可用),否则将重定向到默认区域设置。因此,如果您不打算支持某个国家的所有地区,最好包含可作为备用方案的国家区域设置。
module.exports = {
i18n: {
// These are all the locales you want to support in
// your application
locales: ['en-US', 'fr', 'nl-NL'],
// This is the default locale you want to be used when visiting
// a non-locale prefixed path e.g. `/hello`
defaultLocale: 'en-US',
// This is a list of locale domains and the default locale they
// should handle (these are only required when setting up domain routing)
// Note: subdomains must be included in the domain value to be matched e.g. "fr.example.com".
domains: [
{
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
// an optional http field can also be used to test
// locale domains locally with http instead of 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: [
{
// Note: subdomains must be included in the domain value to be matched
// e.g. www.example.com should be used if that is the expected hostname
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
// specify other locales that should be redirected
// to this domain
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 和 Proxy,我们可以通过 变通方法 为默认区域设置添加前缀。
例如,这是一个支持多种语言的 next.config.js
文件。请注意,"default"
区域设置是故意添加的。
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
接下来,我们可以使用 Proxy 添加自定义路由规则。
import { NextRequest, NextResponse } from 'next/server'
const PUBLIC_FILE = /\.(.*)$/
export async function proxy(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
// change just the locale and maintain all other route information including href's query
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
cookie
Next.js 允许设置 NEXT_LOCALE=the-locale
cookie,该 cookie 优先于 accept-language 头部。可以使用语言切换器设置此 cookie,然后当用户返回网站时,它将在从 /
重定向到正确的区域设置位置时利用 cookie 中指定的区域设置。
例如,如果用户在他们的 accept-language 头部中偏好区域设置 fr
,但设置了 NEXT_LOCALE=en
cookie,则在访问 /
时,用户将被重定向到 en
区域设置位置,直到该 cookie 被移除或过期。
搜索引擎优化
由于 Next.js 知道用户访问的语言,它会自动将 lang
属性添加到 <html>
标签。
Next.js 不了解页面的变体,因此您需要使用 next/head
添加 hreflang
元标签。您可以在 Google 网站管理员文档 中了解更多关于 hreflang
的信息。
这与静态生成如何协同工作?
请注意,国际化路由不与
output: 'export'
集成,因为它不利用 Next.js 路由层。不使用output: 'export'
的混合 Next.js 应用程序完全受支持。
动态路由和 getStaticProps
页面
对于使用 getStaticProps
和 动态路由 的页面,所有希望预渲染的页面区域设置变体都需要从 getStaticPaths
返回。除了为 paths
返回的 params
对象外,您还可以返回一个 locale
字段,指定要渲染的区域设置。例如:
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// if no `locale` is provided only the defaultLocale will be generated
{ 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 }) {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch(`https://.../posts?locale=${locale}`)
const posts = await res.json()
if (posts.length === 0) {
return {
notFound: true,
}
}
// By returning { props: posts }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
i18n 配置的限制
locales
:总共 100 个区域设置domains
:总共 100 个区域设置域项
温馨提示:这些限制最初是为了防止潜在的构建时性能问题而添加的。您可以通过在 Next.js 12 中使用 Proxy 进行自定义路由来绕过这些限制。
这有帮助吗?