Международная маршрутизация (i18n)
Примеры
Next.js имеет встроенную поддержку интернационализированной (i18n) маршрутизации начиная с версии v10.0.0
. Вы можете предоставить список локалей, локаль по умолчанию и доменные локали, а Next.js автоматически обработает маршрутизацию.
Поддержка i18n маршрутизации предназначена для дополнения существующих решений для интернационализации, таких как react-intl
, react-i18next
, lingui
, rosetta
, next-intl
, next-translate
, next-multilingual
, typesafe-i18n
, 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
до тех пор, пока кука не будет удалена или не истечёт её срок действия.
Поисковая оптимизация (SEO)
Поскольку 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.