Маршрутизация с интернационализацией (i18n)
Примеры
Next.js имеет встроенную поддержку интернационализированной (i18n) маршрутизации начиная с версии v10.0.0
. Вы можете предоставить список локалей, локаль по умолчанию и доменные локали, и Next.js автоматически обработает маршрутизацию.
Поддержка i18n маршрутизации в настоящее время предназначена для дополнения существующих решений для интернационализации, таких как react-intl
, react-i18next
, lingui
, rosetta
, next-intl
, next-translate
, next-multilingual
, tolgee
и другие, упрощая обработку маршрутов и разбор локалей.
Начало работы
Для начала добавьте конфигурацию i18n
в ваш файл next.config.js
.
Локали представляют собой идентификаторы локалей UTS, стандартизированный формат для определения локалей.
Обычно идентификатор локали состоит из языка, региона и письменности, разделенных дефисом: язык-регион-письменность
. Регион и письменность являются необязательными. Пример:
en-US
- английский язык, используемый в СШАnl-NL
- нидерландский язык, используемый в Нидерландахnl
- нидерландский язык без указания региона
Если локаль пользователя nl-BE
не указана в вашей конфигурации, пользователь будет перенаправлен на 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 должен использоваться, если это ожидаемое имя хоста
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 и Middleware мы можем добавить префикс для локали по умолчанию с помощью обходного решения.
Например, вот файл next.config.js
с поддержкой нескольких языков. Обратите внимание, что локаль "default"
добавлена намеренно.
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
Затем мы можем использовать Middleware для добавления пользовательских правил маршрутизации:
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)
)
}
}
Этот Middleware пропускает добавление префикса по умолчанию для API Routes и публичных файлов, таких как шрифты или изображения. Если запрос сделан к локали по умолчанию, мы перенаправляем на наш префикс /en
.
Отключение автоматического определения локали
Автоматическое определение локали можно отключить:
module.exports = {
i18n: {
localeDetection: false,
},
}
Когда localeDetection
установлен в false
, Next.js больше не будет автоматически перенаправлять на основе предпочитаемой локали пользователя и будет предоставлять информацию о локали только на основе домена или пути, как описано выше.
Доступ к информации о локали
Вы можете получить доступ к информации о локали через роутер Next.js. Например, используя хук useRouter()
, доступны следующие свойства:
locale
содержит текущую активную локаль.locales
содержит все настроенные локали.defaultLocale
содержит настроенную локаль по умолчанию.
При предварительном рендеринге страниц с getStaticProps
или getServerSideProps
информация о локали предоставляется в контексте, передаваемом в функцию.
При использовании getStaticPaths
настроенные локали предоставляются в параметре контекста функции под locales
, а настроенная локаль по умолчанию — под defaultLocale
.
Переход между локалями
Вы можете использовать next/link
или next/router
для перехода между локалями.
Для next/link
можно указать проп locale
для перехода на другую локаль отличную от текущей активной. Если проп locale
не указан, при клиентских переходах будет использоваться текущая активная локаль. Например:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/another" locale="fr">
Перейти на /fr/another
</Link>
)
}
При использовании методов next/router
напрямую вы можете указать локаль, которая должна быть использована, через параметры перехода. Например:
import { useRouter } from 'next/router'
export default function IndexPage(props) {
const router = useRouter()
return (
<div
onClick={() => {
router.push('/another', '/another', { locale: 'fr' })
}}
>
Перейти на /fr/another
</div>
)
}
Обратите внимание, что для переключения только локали с сохранением всей информации о маршрутизации, такой как значения запроса динамического маршрута или скрытые параметры 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}>
Перейти на /fr/another
</Link>
)
}
Использование куки NEXT_LOCALE
Next.js поддерживает переопределение заголовка accept-language с помощью куки NEXT_LOCALE=the-locale
. Этот куки можно установить с помощью переключателя языка, и когда пользователь вернется на сайт, он будет использовать локаль, указанную в куки, при перенаправлении с /
на правильное расположение локали.
Например, если пользователь предпочитает локаль fr
в заголовке accept-language, но установлен куки NEXT_LOCALE=en
, при посещении /
пользователь будет перенаправлен на расположение локали en
до тех пор, пока куки не будет удален или истечет его срок действия.
Оптимизация для поисковых систем
Поскольку Next.js знает, на каком языке пользователь посещает сайт, он автоматически добавляет атрибут lang
к тегу <html>
.
Next.js не знает о вариантах страницы, поэтому вам нужно самостоятельно добавлять метатеги hreflang
с помощью next/head
. Подробнее о hreflang
можно узнать в документации Google для веб-мастеров.
Как это работает со статической генерацией?
Обратите внимание, что интернационализированная маршрутизация не интегрируется с
output: 'export'
, так как не использует уровень маршрутизации Next.js. Гибридные приложения Next.js, которые не используютoutput: 'export'
, полностью поддерживаются.
Динамические маршруты и страницы с getStaticProps
Для страниц, использующих getStaticProps
с динамическими маршрутами, все варианты локалей страницы, которые должны быть предварительно отрендерены, должны быть возвращены из getStaticPaths
. Наряду с объектом params
, возвращаемым для paths
, вы также можете вернуть поле locale
, указывающее, какую локаль вы хотите отрендерить. Например:
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// Если `locale` не указан, будет сгенерирована только локаль по умолчанию
{ params: { slug: 'post-1' }, locale: 'en-US' },
{ params: { slug: 'post-1' }, locale: 'fr' },
],
fallback: true,
}
}
Для автоматически статически оптимизированных и нединамических страниц с getStaticProps
версия страницы будет сгенерирована для каждой локали. Это важно учитывать, так как это может увеличить время сборки в зависимости от количества локалей, настроенных внутри getStaticProps
.
Например, если у вас настроено 50 локалей и 10 нединамических страниц с getStaticProps
, это означает, что getStaticProps
будет вызван 500 раз. 50 версий каждой из 10 страниц будут сгенерированы во время каждой сборки.
Чтобы уменьшить время сборки динамических страниц с getStaticProps
, используйте режим fallback
. Это позволяет возвращать только самые популярные пути и локали из getStaticPaths
для предварительного рендеринга во время сборки. Затем Next.js будет строить оставшиеся страницы во время выполнения по мере их запроса.
Автоматически статически оптимизированные страницы
Для автоматически статически оптимизированных страниц версия страницы будет сгенерирована для каждой локали.
Нединамические страницы с getStaticProps
Для нединамических страниц с getStaticProps
версия генерируется для каждой локали, как описано выше. getStaticProps
вызывается для каждой локали, которая рендерится. Если вы хотите исключить определенную локаль из предварительного рендеринга, вы можете вернуть notFound: true
из getStaticProps
, и эта версия страницы не будет сгенерирована.
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 }, компонент Blog
// получит `posts` как проп во время сборки
return {
props: {
posts,
},
}
}
Ограничения конфигурации i18n
locales
: всего 100 локалейdomains
: всего 100 элементов доменов локалей
Полезно знать: Эти ограничения были добавлены изначально для предотвращения потенциальных проблем с производительностью во время сборки. Вы можете обойти эти ограничения с помощью пользовательской маршрутизации, используя Middleware в Next.js 12.