國際化 (i18n) 路由
範例
Next.js 內建支援國際化 (i18n) 路由,自 v10.0.0
版本開始。您可以提供 Locale 清單、預設 Locale 和網域特定的 Locale,Next.js 將自動處理路由。
i18n 路由支援目前旨在補充現有的 i18n 函式庫解決方案,例如 react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、tolgee
、paraglide-next
、next-intlayer
和其他函式庫,透過簡化路由和 Locale 解析。
開始使用
若要開始使用,請將 i18n
設定新增至您的 next.config.js
檔案。
Locale 是 UTS Locale 識別碼,這是一種用於定義 Locale 的標準化格式。
一般來說,Locale 識別碼由語言、地區和腳本組成,並以破折號分隔:language-region-script
。地區和腳本是選填的。以下範例
en-US
- 在美國使用的英語nl-NL
- 在荷蘭使用的荷蘭語nl
- 荷蘭語,無特定地區
如果使用者 Locale 是 nl-BE
,且未在您的設定中列出,則他們將被重新導向至 nl
(如果有的話),否則會重新導向至預設 Locale。如果您不打算支援一個國家的所有地區,那麼包含將作為後備的國家/地區 Locale 會是一個好習慣。
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 策略
有兩種 Locale 處理策略:子路徑路由和網域路由。
子路徑路由
子路徑路由將 Locale 放在 URL 路徑中。
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL'],
defaultLocale: 'en-US',
},
}
透過上述設定,en-US
、fr
和 nl-NL
將可供路由,而 en-US
是預設 Locale。如果您有 pages/blog.js
,則以下 URL 將可用
/blog
/fr/blog
/nl-nl/blog
預設 Locale 沒有前綴。
網域路由
透過使用網域路由,您可以設定從不同網域提供服務的 Locale
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
自動 Locale 偵測
當使用者造訪應用程式根目錄 (通常是 /
) 時,Next.js 將嘗試根據 Accept-Language
標頭和目前網域自動偵測使用者偏好的 Locale。
如果偵測到預設 Locale 以外的 Locale,使用者將被重新導向至
- 當使用子路徑路由時:具有 Locale 前綴的路徑
- 當使用網域路由時:以該 Locale 指定為預設值的網域
當使用網域路由時,如果具有 Accept-Language
標頭 fr;q=0.9
的使用者造訪 example.com
,他們將被重新導向至 example.fr
,因為該網域預設處理 fr
Locale。
當使用子路徑路由時,使用者將被重新導向至 /fr
。
為預設 Locale 添加前綴
使用 Next.js 12 和中介軟體,我們可以透過變通方法為預設 Locale 新增前綴。
例如,以下是支援多種語言的 next.config.js
檔案。請注意,已刻意新增 "default"
Locale。
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 路由和public 檔案 (例如字體或圖片) 新增預設前綴。如果對預設 Locale 發出請求,我們會重新導向至我們的前綴 /en
。
停用自動 Locale 偵測
可以使用以下程式碼停用自動 Locale 偵測
module.exports = {
i18n: {
localeDetection: false,
},
}
當 localeDetection
設定為 false
時,Next.js 將不再根據使用者偏好的 Locale 自動重新導向,並且只會提供從基於 Locale 的網域或如上所述的 Locale 路徑偵測到的 Locale 資訊。
存取 Locale 資訊
您可以透過 Next.js 路由器存取 Locale 資訊。例如,使用 useRouter()
Hook,以下屬性可用
locale
包含目前作用中的 Locale。locales
包含所有已設定的 Locale。defaultLocale
包含已設定的預設 Locale。
當使用 getStaticProps
或 getServerSideProps
預先渲染頁面時,Locale 資訊會在提供給函式的內容中提供。
當利用 getStaticPaths
時,已設定的 Locale 會在函式的內容參數中以 locales
提供,而已設定的 defaultLocale 則以 defaultLocale
提供。
Locale 之間的轉換
您可以使用 next/link
或 next/router
在 Locale 之間轉換。
對於 next/link
,可以提供 locale
屬性以從目前作用中的 Locale 轉換到不同的 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
物件結構的更多資訊。
如果您有已包含 Locale 的 href
,您可以選擇不自動處理 Locale 前綴
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,其優先順序高於 accept-language 標頭。可以使用語言切換器設定此 Cookie,然後當使用者返回網站時,從 /
重新導向至正確的 Locale 位置時,將利用 Cookie 中指定的 Locale。
例如,如果使用者在其 accept-language 標頭中偏好 Locale fr
,但設定了 NEXT_LOCALE=en
Cookie,則當造訪 /
時,使用者將被重新導向至 en
Locale 位置,直到移除或過期 Cookie 為止。
搜尋引擎優化
由於 Next.js 知道使用者正在造訪哪種語言,因此它會自動將 lang
屬性新增至 <html>
標籤。
Next.js 不知道頁面的變體,因此您必須使用 next/head
新增 hreflang
Meta 標籤。您可以在 Google 網站管理員說明文件中瞭解更多關於 hreflang
的資訊。
這如何與靜態產生搭配運作?
請注意,國際化路由不與
output: 'export'
整合,因為它不利用 Next.js 路由層。完全支援不使用output: 'export'
的混合 Next.js 應用程式。
動態路由和 getStaticProps
頁面
對於使用 getStaticProps
和動態路由的頁面,需要從getStaticPaths
傳回要預先渲染的頁面的所有 Locale 變體。除了為 paths
傳回的 params
物件之外,您還可以傳回 locale
欄位,指定您要渲染的 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
頁面,將為每個 Locale 產生頁面的版本。這很重要,因為它可能會根據 getStaticProps
內設定的 Locale 數量而增加建置時間。
例如,如果您設定了 50 個 Locale,其中有 10 個非動態頁面使用 getStaticProps
,這表示 getStaticProps
將被呼叫 500 次。在每次建置期間將產生 10 個頁面的 50 個版本。
若要減少具有 getStaticProps
的動態頁面的建置時間,請使用fallback
模式。這可讓您僅從 getStaticPaths
傳回最熱門的路徑和 Locale,以便在建置期間進行預先渲染。然後,Next.js 將在執行階段根據請求建置其餘頁面。
自動靜態最佳化頁面
對於自動靜態最佳化的頁面,將為每個 Locale 產生頁面的版本。
非動態 getStaticProps 頁面
對於非動態 getStaticProps
頁面,會像上面一樣為每個 Locale 產生一個版本。getStaticProps
會針對正在渲染的每個 locale
呼叫。如果您想要選擇退出預先渲染的特定 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 個 Localedomains
:總共 100 個 Locale 網域項目
小知識:最初新增這些限制是為了防止潛在的建置時效能問題。您可以使用 Next.js 12 中的中介軟體,透過自訂路由來解決這些限制。
這有幫助嗎?