Локализованная маршрутизация (интернационализация)¶
Next.js имеет встроенную поддержку локализованного роутинга. Достаточно указать список локалей, дефолтную локаль и привязанные к домену локали.
Поддержка роутинга i18n
означает интеграцию с такими библиотеками, как react-intl
, react-i18next
, lingui
, rosetta
, next-intl
и др.
Начало работы¶
Для начала работы необходимо настроить i18n
в файле next.config.js
. Идентификатор локали выглядит как язык-регион-скрипт, например:
en-US
— американский английскийnl-NL
— нидерландский (голландский)nl
— нидерландский без учета региона
// next.config.js
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,
},
],
},
};
Стратегии локализации¶
Существует 2 стратегии локализации: маршрутизация на основе субпутей (subpaths
) и роутинг на основе доменов.
Роутинг на основе субпутей¶
В этом случае локаль помещается в url
:
// next.config.js
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL'],
defaultLocale: 'en-US',
},
};
Здесь en-US
, fr
и nl-NL
будет доступны для перехода, а en-US
будет использоваться по умолчанию. Для страницы pages/blog
будут доступны следующие 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: [
{
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
// Список указанных локалей будет обслуживаться этим доменом
locales: ['nl-BE'],
},
],
},
};
Для страницы pages/blog
будут доступны следующие url
:
example.com/blog
example.fr/blog
example.nl/blog
example.nl/nl-BE/blog
Автоматическое определение локали¶
Когда пользователь запускает приложение, Next.js пытается автоматически определить его локаль на основе заголовка Accept-Language
и текущего домена. При обнаружении локали, отличающейся от дефолтной, выполняется перенаправление.
Если при посещении example.com
запрос содержит заголовок Accept-Language: fr;q=0.9
, в случае роутинга на основе домена выполняется перенаправление на example.fr
, а в случае роутинга на основе субпутей — на /fr
.
Отключение автоматического определения локали¶
// next.config.js
module.exports = {
i18n: {
localeDetection: false,
},
};
Доступ к информации о локали¶
Информация о локали содержится в роутере, доступ к которому можно получить с помощью хука useRouter
. Доступны следующие свойства:
locale
— текущая локальlocales
— доступные локалиdefaultLocale
— локаль по умолчанию
В случае предварительного рендеринга страниц с помощью getStaticProps
или getServerSideProps
информация о локали содержится в контексте, передаваемом функции.
При использовании getStaticPaths
локали также содержатся в параметре context
, передаваемом функции, в свойствах locales
и defaultLocale
.
Переключение между локалями¶
Для переключения между локалями можно использовать next/link
или next/router
.
Для next/link
может быть указан проп locale
для переключения на другую локаль. Если такой проп не указан, используется текущая локаль:
import Link from 'next/link';
export default function IndexPage(props) {
return (
<Link href="/another" locale="fr">
<a>Перейти к `/fr/another`</a>
</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
можно использовать объект:
import { useRouter } from 'next/router';
const router = useRouter();
const { pathname, asPath, query } = router;
// Переключаем локаль с сохранением другой информации
router.push({ pathname, query }, asPath, {
locale: nextLocale,
});
Если href
включает локаль, автоматическое добавления префикса можно отключить:
import Link from 'next/link';
export default function IndexPage(props) {
return (
<Link href="/fr/another" locale={false}>
<a>Перейти к `/fr/another`</a>
</Link>
);
}
Заметки:
- Next.js позволяет перезаписывать значение заголовка
Accept-Language
с помощью кукиNEXT_LOCALE=локаль
. При установке такой куки,Accept-Language
будет игнорироваться. - Next.js автоматически добавляет атрибут
lang
к тегуhtml
. Однако, он не знает о возможных вариантах страницы, поэтому добавление мета-тегаhreflang
— задача разработчика (это можно сделать с помощьюnext/head
).
Статическая генерация¶
Динамические роуты и getStaticProps¶
Для страниц, на которых используется динамическая маршрутизация с помощью getStaticProps
, все локали, которые должны быть предварительно отрендерены, должны возвращаться из getStaticPaths
. Вместе с объектом params
возвращается поле locale
, определяющее локаль для рендеринга:
// pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// При отсутствии `locale` генерируется только локаль по умолчанию
{ params: { slug: 'post-1' }, locale: 'en-US' },
{ params: { slug: 'post-1' }, locale: 'fr' },
],
fallback: true,
};
};
Для автоматической статической оптимизации и нединамических страниц с getStaticProps
, для каждой локали генерируется отдельная версия страницы. Это может существенно повлиять на время сборки в зависимости от количества локалей, определенных в getStaticProps
.
Для решения этой проблемы следует использовать режим fallback
. Это позволяет возвращать из getStaticPaths
только самые популярные пути и локали для предварительного рендеринга во время сборки. Остальные страницы будут рендерится во время выполнения по запросу.
Если из getStaticProps
нединамической страницы вернуть notFound: true
, то соответствующий вариант страницы сгенерирован не будет:
export async function getStaticProps({ locale }) {
// Получаем посты из внешнего `API`
const res = await fetch(
`https://example.com/posts?locale=${locale}`
);
const posts = await res.json();
if (posts.length === 0) {
return {
notFound: true,
};
}
return {
props: {
posts,
},
};
}
Ограничения¶
locales
: максимум 100 локалейdomains
: максимум 100 доменов