跳到內容

如何在 Next.js 中實現國際化

示例

v10.0.0 起,Next.js 內建支援國際化 (i18n) 路由。您可以提供區域設定列表、預設區域設定和特定於域的區域設定,Next.js 將自動處理路由。

i18n 路由支援目前旨在透過簡化路由和區域設定解析來補充現有的 i18n 庫解決方案,例如 react-intlreact-i18nextlinguirosettanext-intlnext-translatenext-multilingualtolgeeparaglide-nextnext-intlayergt-react 等。

開始使用

要開始使用,請將 i18n 配置新增到您的 next.config.js 檔案中。

區域設定是 UTS 區域設定識別符號,一種用於定義區域設定的標準化格式。

通常,區域設定識別符號由語言、地區和指令碼組成,並以破折號分隔:language-region-script。地區和指令碼是可選的。例如:

  • en-US - 在美國使用的英語
  • nl-NL - 在荷蘭使用的荷蘭語
  • nl - 荷蘭語,無特定地區

如果使用者區域設定是 nl-BE 並且未在您的配置中列出,他們將被重定向到 nl(如果可用),否則將重定向到預設區域設定。因此,如果您不打算支援某個國家的所有地區,最好包含可作為備用方案的國家區域設定。

next.config.js
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 路徑中。

next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL'],
    defaultLocale: 'en-US',
  },
}

透過上述配置,en-USfrnl-NL 將可用於路由,並且 en-US 是預設區域設定。如果您有 pages/blog.js,則以下 URL 將可用:

  • /blog
  • /fr/blog
  • /nl-nl/blog

預設區域設定沒有字首。

域路由

透過使用域路由,您可以配置從不同域提供區域設定。

next.config.js
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" 區域設定是故意新增的。

next.config.js
module.exports = {
  i18n: {
    locales: ['default', 'en', 'de', 'fr'],
    defaultLocale: 'default',
    localeDetection: false,
  },
  trailingSlash: true,
}

接下來,我們可以使用 Proxy 新增自定義路由規則。

proxy.ts
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

停用自動區域設定檢測

可以使用以下方式停用自動區域設定檢測:

next.config.js
module.exports = {
  i18n: {
    localeDetection: false,
  },
}

localeDetection 設定為 false 時,Next.js 將不再根據使用者的首選區域設定自動重定向,並且只會提供從基於區域設定的域或區域設定路徑(如上所述)檢測到的區域設定資訊。

訪問區域設定資訊

您可以透過 Next.js 路由器訪問區域設定資訊。例如,使用 useRouter() 鉤子,以下屬性可用:

  • locale 包含當前活動的區域設定。
  • locales 包含所有已配置的區域設定。
  • defaultLocale 包含已配置的預設區域設定。

當使用 getStaticPropsgetServerSideProps 預渲染 頁面時,區域設定資訊在提供給函式的 上下文 中提供。

當使用 getStaticPaths 時,配置的區域設定在函式的上下文引數中的 locales 下提供,配置的 defaultLocaledefaultLocale 下提供。

區域設定之間的轉換

您可以使用 next/linknext/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.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 欄位,指定要渲染的區域設定。例如:

pages/blog/[slug].js
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 進行自定義路由來繞過這些限制。