国际化 (i18n) 路由
Examples
自 v10.0.0
起,Next.js 就内置了对国际化 (国际化) 路由的支持。你可以提供区域设置列表、默认区域设置和特定于域的区域设置,Next.js 将自动处理路由。
¥Next.js has built-in support for internationalized (i18n) routing since v10.0.0
. You can provide a list of locales, the default locale, and domain-specific locales and Next.js will automatically handle the routing.
i18n 路由支持目前旨在通过简化路由和语言环境解析来补充现有的 i18n 库解决方案,例如 react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、tolgee
、paraglide-next
等。
¥The i18n routing support is currently meant to complement existing i18n library solutions like react-intl
, react-i18next
, lingui
, rosetta
, next-intl
, next-translate
, next-multilingual
, tolgee
, paraglide-next
and others by streamlining the routes and locale parsing.
入门
¥Getting started
首先,将 i18n
配置添加到 next.config.js
文件中。
¥To get started, add the i18n
config to your next.config.js
file.
区域设置是 UTS 区域设置标识符,这是定义区域设置的标准化格式。
¥Locales are UTS Locale Identifiers, a standardized format for defining locales.
通常,区域设置标识符由语言、区域和脚本组成,并用破折号分隔:language-region-script
。区域和脚本是可选的。一个例子:
¥Generally a Locale Identifier is made up of a language, region, and script separated by a dash: language-region-script
. The region and script are optional. An example:
-
en-US
- 美国所说的英语¥
en-US
- English as spoken in the United States -
nl-NL
- 荷兰语¥
nl-NL
- Dutch as spoken in the Netherlands -
nl
- 荷兰语,无特定地区¥
nl
- Dutch, no specific region
如果用户区域设置为 nl-BE
并且未在你的配置中列出,则它们将被重定向到 nl
(如果可用),否则将重定向到默认区域设置。如果你不打算支持一个国家/地区的所有区域,那么最好将国家/地区区域设置作为后备。
¥If user locale is nl-BE
and it is not listed in your configuration, they will be redirected to nl
if available, or to the default locale otherwise.
If you don't plan to support all regions of a country, it is therefore a good practice to include country locales that will act as fallbacks.
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,
},
],
},
}
区域设置策略
¥Locale Strategies
有两种区域设置处理策略:子路径路由和域路由。
¥There are two locale handling strategies: Sub-path Routing and Domain Routing.
子路径路由
¥Sub-path Routing
子路径路由将区域设置放入 url 路径中。
¥Sub-path Routing puts the locale in the url path.
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL'],
defaultLocale: 'en-US',
},
}
通过上述配置,en-US
、fr
和 nl-NL
将可路由到,en-US
是默认区域设置。如果你有 pages/blog.js
,则可以使用以下网址:
¥With the above configuration en-US
, fr
, and nl-NL
will be available to be routed to, and en-US
is the default locale. If you have a pages/blog.js
the following urls would be available:
-
/blog
-
/fr/blog
-
/nl-nl/blog
默认区域设置没有前缀。
¥The default locale does not have a prefix.
域路由
¥Domain Routing
通过使用域路由,你可以配置从不同域提供服务的区域设置:
¥By using domain routing you can configure locales to be served from different domains:
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
,则以下网址将可用:
¥For example if you have pages/blog.js
the following urls will be available:
-
example.com/blog
-
www.example.com/blog
-
example.fr/blog
-
example.nl/blog
-
example.nl/nl-BE/blog
自动区域设置检测
¥Automatic Locale Detection
当用户访问应用根目录(通常是 /
)时,Next.js 将尝试根据 Accept-Language
标头和当前域自动检测用户喜欢的区域设置。
¥When a user visits the application root (generally /
), Next.js will try to automatically detect which locale the user prefers based on the Accept-Language
header and the current domain.
如果检测到默认区域设置以外的区域设置,用户将被重定向到:
¥If a locale other than the default locale is detected, the user will be redirected to either:
-
使用子路径路由时:区域设置前缀路径
¥When using Sub-path Routing: The locale prefixed path
-
使用域路由时:将该区域设置指定为默认值的域
¥When using Domain Routing: The domain with that locale specified as the default
使用域路由时,如果具有 Accept-Language
标头 fr;q=0.9
的用户访问 example.com
,他们将被重定向到 example.fr
,因为该域默认处理 fr
区域设置。
¥When using Domain Routing, if a user with the Accept-Language
header fr;q=0.9
visits example.com
, they will be redirected to example.fr
since that domain handles the fr
locale by default.
使用子路径路由时,用户将被重定向到 /fr
。
¥When using Sub-path Routing, the user would be redirected to /fr
.
为默认区域设置添加前缀
¥Prefixing the Default Locale
使用 Next.js 12 和 中间件,我们可以使用 workaround 添加前缀到默认语言环境。
¥With Next.js 12 and Middleware, we can add a prefix to the default locale with a workaround.
例如,这是一个支持几种语言的 next.config.js
文件。请注意,"default"
语言环境是有意添加的。
¥For example, here's a next.config.js
file with support for a few languages. Note the "default"
locale has been added intentionally.
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
接下来我们可以使用 中间件 来添加自定义路由规则:
¥Next, we can use Middleware to add custom routing rules:
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 路由 和 public 文件(如字体或图片)添加默认前缀。如果向默认区域设置发出请求,我们将重定向到前缀 /en
。
¥This Middleware skips adding the default prefix to API Routes and public files like fonts or images. If a request is made to the default locale, we redirect to our prefix /en
.
禁用自动区域设置检测
¥Disabling Automatic Locale Detection
可以通过以下方式禁用自动区域设置检测:
¥The automatic locale detection can be disabled with:
module.exports = {
i18n: {
localeDetection: false,
},
}
当 localeDetection
设置为 false
时,Next.js 将不再根据用户的首选区域设置自动重定向,并且仅提供从基于区域设置的域或区域设置路径检测到的区域设置信息(如上所述)。
¥When localeDetection
is set to false
Next.js will no longer automatically redirect based on the user's preferred locale and will only provide locale information detected from either the locale based domain or locale path as described above.
访问区域设置信息
¥Accessing the locale information
你可以通过 Next.js 路由访问区域设置信息。例如,使用 useRouter()
钩子,可以使用以下属性:
¥You can access the locale information via the Next.js router. For example, using the useRouter()
hook the following properties are available:
-
locale
包含当前活动的区域设置。¥
locale
contains the currently active locale. -
locales
包含所有配置的区域设置。¥
locales
contains all configured locales. -
defaultLocale
包含配置的默认区域设置。¥
defaultLocale
contains the configured default locale.
当 pre-rendering 寻呼 getStaticProps
或 getServerSideProps
时,区域设置信息在提供给函数的 上下文 中提供。
¥When pre-rendering pages with getStaticProps
or getServerSideProps
, the locale information is provided in the context provided to the function.
当利用 getStaticPaths
时,配置的区域设置在 locales
下的函数的上下文参数中提供,并在 defaultLocale
下的配置的 defaultLocale 中提供。
¥When leveraging getStaticPaths
, the configured locales are provided in the context parameter of the function under locales
and the configured defaultLocale under defaultLocale
.
区域设置之间的转换
¥Transition between locales
你可以使用 next/link
或 next/router
在区域设置之间进行转换。
¥You can use next/link
or next/router
to transition between locales.
对于 next/link
,可以提供 locale
属性来转换到与当前活动区域不同的区域设置。如果未提供 locale
属性,则在客户端转换期间使用当前活动的 locale
。例如:
¥For next/link
, a locale
prop can be provided to transition to a different locale from the currently active one. If no locale
prop is provided, the currently active locale
is used during client-transitions. For example:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/another" locale="fr">
To /fr/another
</Link>
)
}
直接使用 next/router
方法时,你可以通过转换选项指定应使用的 locale
。例如:
¥When using the next/router
methods directly, you can specify the locale
that should be used via the transition options. For example:
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
参数作为对象:
¥Note that to handle switching only the locale
while preserving all routing information such as dynamic route query values or hidden href query values, you can provide the href
parameter as an object:
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
的对象结构的更多信息,请参阅 此处。
¥See here for more information on the object structure for router.push
.
如果你的 href
已包含区域设置,你可以选择不自动处理区域设置前缀:
¥If you have a href
that already includes the locale you can opt-out of automatically handling the locale prefixing:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/fr/another" locale={false}>
To /fr/another
</Link>
)
}
利用 NEXT_LOCALE
cookie
¥Leveraging the NEXT_LOCALE
cookie
Next.js 允许设置 NEXT_LOCALE=the-locale
cookie,该 cookie 优先于接受语言标头。可以使用语言切换器设置此 cookie,然后当用户返回站点时,它将利用 cookie 中指定的区域设置从 /
重定向到正确的区域设置位置。
¥Next.js allows setting a NEXT_LOCALE=the-locale
cookie, which takes priority over the accept-language header. This cookie can be set using a language switcher and then when a user comes back to the site it will leverage the locale specified in the cookie when redirecting from /
to the correct locale location.
例如,如果用户在接受语言标头中更喜欢区域设置 fr
,但在访问 /
时将 NEXT_LOCALE=en
cookie 设置为 en
区域设置,则用户将被重定向到 en
区域设置位置,直到 cookie 被删除或过期。
¥For example, if a user prefers the locale fr
in their accept-language header but a NEXT_LOCALE=en
cookie is set the en
locale when visiting /
the user will be redirected to the en
locale location until the cookie is removed or expired.
搜索引擎优化
¥Search Engine Optimization
由于 Next.js 知道用户正在访问什么语言,因此它会自动将 lang
属性添加到 <html>
标记中。
¥Since Next.js knows what language the user is visiting it will automatically add the lang
attribute to the <html>
tag.
Next.js 不知道页面的变体,因此你可以使用 next/head
添加 hreflang
元标记。你可以在 Google 网站管理员文档 中了解有关 hreflang
的更多信息。
¥Next.js doesn't know about variants of a page so it's up to you to add the hreflang
meta tags using next/head
. You can learn more about hreflang
in the Google Webmasters documentation.
这如何与静态生成一起使用?
¥How does this work with Static Generation?
请注意,国际化路由不与
output: 'export'
集成,因为它不利用 Next.js 路由层。完全支持不使用output: 'export'
的混合 Next.js 应用。¥Note that Internationalized Routing does not integrate with
output: 'export'
as it does not leverage the Next.js routing layer. Hybrid Next.js applications that do not useoutput: 'export'
are fully supported.
动态路由和 getStaticProps
页面
¥Dynamic Routes and getStaticProps
Pages
对于使用 getStaticProps
和 动态路由 的页面,需要预渲染的页面的所有区域设置变体都需要从 getStaticPaths
返回。除了为 paths
返回 params
对象之外,你还可以返回 locale
字段,指定要渲染的区域设置。例如:
¥For pages using getStaticProps
with Dynamic Routes, all locale variants of the page desired to be prerendered need to be returned from getStaticPaths
. Along with the params
object returned for paths
, you can also return a locale
field specifying which locale you want to render. For example:
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
内配置的语言环境数量。
¥For Automatically Statically Optimized and non-dynamic getStaticProps
pages, a version of the page will be generated for each locale. This is important to consider because it can increase build times depending on how many locales are configured inside getStaticProps
.
例如,如果你使用 getStaticProps
配置了 50 个语言环境并配置了 10 个非动态页面,则这意味着 getStaticProps
将被调用 500 次。每次构建期间将生成 10 个页面的 50 个版本。
¥For example, if you have 50 locales configured with 10 non-dynamic pages using getStaticProps
, this means getStaticProps
will be called 500 times. 50 versions of the 10 pages will be generated during each build.
要减少使用 getStaticProps
的动态页面的构建时间,请使用 fallback
模式。这允许你仅返回 getStaticPaths
中最流行的路径和区域设置,以便在构建期间进行预渲染。然后,Next.js 将在运行时根据请求构建剩余页面。
¥To decrease the build time of dynamic pages with getStaticProps
, use a fallback
mode. This allows you to return only the most popular paths and locales from getStaticPaths
for prerendering during the build. Then, Next.js will build the remaining pages at runtime as they are requested.
自动静态优化页面
¥Automatically Statically Optimized Pages
对于 自动静态优化 的页面,将为每个区域设置生成该页面的版本。
¥For pages that are automatically statically optimized, a version of the page will be generated for each locale.
非动态 getStaticProps 页面
¥Non-dynamic getStaticProps Pages
对于非动态 getStaticProps
页面,将为每个区域设置生成一个版本,如上所示。每个正在渲染的 locale
都会调用 getStaticProps
。如果你想选择不预渲染某个区域设置,你可以从 getStaticProps
返回 notFound: true
,这样就不会生成该页面的变体。
¥For non-dynamic getStaticProps
pages, a version is generated for each locale like above. getStaticProps
is called with each locale
that is being rendered. If you would like to opt-out of a certain locale from being pre-rendered, you can return notFound: true
from getStaticProps
and this variant of the page will not be generated.
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 配置的限制
¥Limits for the i18n config
-
locales
:总共 100 个区域设置¥
locales
: 100 total locales -
domains
:总共 100 个区域设置域项目¥
domains
: 100 total locale domain items
很高兴知道:最初添加这些限制是为了防止潜在的 构建时的性能问题。你可以使用 Next.js 12 中的 中间件 通过自定义路由来解决这些限制。
¥Good to know: These limits have been added initially to prevent potential performance issues at build time. You can workaround these limits with custom routing using Middleware in Next.js 12.