--- createdAt: 2024-12-07 updatedAt: 2025-12-30 title: Як перекласти ваш застосунок Next.js з Page Router – посібник i18n 2026 description: Дізнайтеся, як зробити ваш вебсайт на Next.js з Page Router багатомовним. Дотримуйтесь документації, щоб інтернаціоналізувати (i18n) і перекласти його. keywords: - Інтернаціоналізація - Документація - Intlayer - Page Router - Next.js - JavaScript - React slugs: - doc - environment - nextjs - next-with-page-router history: - version: 7.5.9 date: 2025-12-30 changes: Додано команду init - version: 5.6.0 date: 2025-07-06 changes: Перетворено функцію `withIntlayer()` на функцію на основі промісів - version: 5.5.10 date: 2025-06-29 changes: Ініціалізовано історію --- # Перекладіть свій вебсайт на Next.js із Page Router за допомогою Intlayer | Інтернаціоналізація (i18n) ## Зміст ## Що таке Intlayer? **Intlayer** — інноваційна open-source бібліотека інтернаціоналізації (i18n), створена для спрощення підтримки багатомовності в сучасних вебдодатках. Intlayer безшовно інтегрується з останніми версіями **Next.js**, включно з традиційним **Page Router**. За допомогою Intlayer ви можете: - **Легко керувати перекладами**, використовуючи декларативні словники на рівні компонентів. - **Динамічно локалізувати метадані**, маршрути та контент. - **Забезпечити підтримку TypeScript** завдяки автогенерованим типам, що покращує автозаповнення й виявлення помилок. - **Отримати вигоду від розширених можливостей**, таких як динамічне визначення локалі та її перемикання. > Intlayer сумісний з Next.js 12, 13, 14 та 15. Якщо ви використовуєте Next.js App Router, див. [посібник для App Router](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_with_nextjs_14.md). Для Next.js 15 дотримуйтесь цього [посібника](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_with_nextjs_15.md). --- ## Покроковий посібник щодо налаштування Intlayer у застосунку Next.js із Page Router ### Крок 1: Встановіть залежності Встановіть необхідні пакунки, використовуючи обраний менеджер пакетів: ```bash packageManager="npm" npm install intlayer next-intlayer npx intlayer init ``` ```bash packageManager="pnpm" pnpm add intlayer next-intlayer pnpm intlayer init ``` ```bash packageManager="yarn" yarn add intlayer next-intlayer yarn intlayer init ``` ```bash packageManager="bun" bun add intlayer next-intlayer bunx intlayer init ``` - **intlayer** Основний пакет, який надає інструменти інтернаціоналізації для керування конфігурацією, перекладу, [оголошення вмісту](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md), транспіляції та [CLI-команд](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/cli/index.md). - **next-intlayer** Пакет, що інтегрує Intlayer з Next.js. Забезпечує провайдери контексту та хуки для інтернаціоналізації у Next.js. Додатково містить плагін для Next.js для інтеграції Intlayer з [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack), а також middleware для визначення переважної локалі користувача, керування cookie та обробки перенаправлення URL. ### Крок 2: Налаштуйте свій проєкт Створіть файл конфігурації, щоб визначити мови, які підтримує ваш застосунок: ```typescript fileName="intlayer.config.ts" codeFormat="typescript" import { Locales, type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { internationalization: { // Локалі, які підтримуються вашим додатком. locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // Додайте тут інші локалі ], defaultLocale: Locales.ENGLISH, }, }; export default config; ``` ```javascript fileName="intlayer.config.mjs" codeFormat="esm" import { Locales } from "intlayer"; /** @type {import('intlayer').IntlayerConfig} */ const config = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // Додайте тут інші локалі ], defaultLocale: Locales.ENGLISH, }, }; export default config; ``` ```javascript fileName="intlayer.config.cjs" codeFormat="commonjs" const { Locales } = require("intlayer"); /** @type {import('intlayer').IntlayerConfig} */ const config = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // Додайте тут інші локалі ], defaultLocale: Locales.ENGLISH, }, }; module.exports = config; ``` > Через цей конфігураційний файл ви можете налаштувати локалізовані URL-адреси, перенаправлення в middleware, імена cookie, розташування та розширення файлів декларацій вмісту, вимкнути логи Intlayer у консолі тощо. Для повного переліку доступних параметрів зверніться до [документації з конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md). ### Крок 3: Інтеграція Intlayer з конфігурацією Next.js Змініть конфігурацію Next.js, щоб інтегрувати Intlayer: ```typescript fileName="next.config.mjs" import { withIntlayer } from "next-intlayer/server"; /** @type {import('next').NextConfig} */ const nextConfig = { // Ваша існуюча конфігурація Next.js }; export default withIntlayer(nextConfig); ``` > Плагін Next.js `withIntlayer()` використовується для інтеграції Intlayer з Next.js. Він забезпечує побудову файлів декларацій контенту та відстежує їх у режимі розробки. Він визначає змінні середовища Intlayer у середовищах [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack). Крім того, він надає алиаси для оптимізації продуктивності й забезпечує сумісність із серверними компонентами. > Функція `withIntlayer()` повертає проміс. Якщо ви хочете використовувати її разом з іншими плагінами, ви можете дочекатися її виконання за допомогою await. Приклад: > > ```tsx > const nextConfig = await withIntlayer(nextConfig); > const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig); > > export default nextConfigWithOtherPlugins; > ``` ### Крок 4: Налаштуйте middleware для визначення локалі Налаштуйте middleware для автоматичного визначення та обробки бажаної користувачем локалі: ```typescript fileName="src/middleware.ts" codeFormat="typescript" export { intlayerProxy as middleware } from "next-intlayer/middleware"; export const config = { matcher: "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)", }; ``` ```javascript fileName="src/middleware.mjs" codeFormat="esm" export { intlayerProxy as middleware } from "next-intlayer/middleware"; export const config = { matcher: "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)", }; ``` ```javascript fileName="src/middleware.cjs" codeFormat="commonjs" const { intlayerProxy } = require("next-intlayer/middleware"); const config = { matcher: "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)", }; module.exports = { middleware: intlayerProxy, config }; ``` > Налаштуйте параметр `matcher`, щоб відповідати маршрутам вашого застосунку. Для детальнішої інформації див. [документацію Next.js щодо конфігурації matcher](https://nextjs.org/docs/app/building-your-application/routing/middleware). ### Крок 5: Визначте динамічні маршрути локалей Реалізуйте динамічну маршрутизацію для подачі локалізованого контенту залежно від локалі користувача. 1. **Створіть сторінки для конкретної локалі:** Перейменуйте головний файл сторінки, щоб додати динамічний сегмент `[locale]`. ```bash mv src/pages/index.tsx src/pages/[locale]/index.tsx ``` 2. **Оновіть `_app.tsx`, щоб обробляти локалізацію:** Змініть свій `_app.tsx`, щоб включити провайдери Intlayer. ```tsx fileName="src/pages/_app.tsx" codeFormat="typescript" import type { FC } from "react"; import type { AppProps } from "next/app"; import { IntlayerClientProvider } from "next-intlayer"; const App = FC({ Component, pageProps }) => { const { locale } = pageProps; return ( ); } export default MyApp; ``` ```jsx fileName="src/pages/_app.mjx" codeFormat="esm" import { IntlayerClientProvider } from "next-intlayer"; const App = ({ Component, pageProps }) => ( ); export default App; ``` ```jsx fileName="src/pages/_app.csx" codeFormat="commonjs" const { IntlayerClientProvider } = require("next-intlayer"); const App = ({ Component, pageProps }) => ( ); module.exports = App; ``` 3. **Налаштуйте `getStaticPaths` та `getStaticProps`:** У файлі `[locale]/index.tsx` визначте paths і props для обробки різних локалей. ```tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript" import type { FC } from "react"; import type { GetStaticPaths, GetStaticProps } from "next"; import { type Locales, getConfiguration } from "intlayer"; const HomePage: FC = () =>
{/* Ваш контент тут */}
; export const getStaticPaths: GetStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps: GetStaticProps = ({ params }) => { const locale = params?.locale as string; return { props: { locale, }, }; }; export default HomePage; ``` ```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getConfiguration } from "intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage = () =>
{/* Ваш контент тут */}
; export const getStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps = ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; ``` ```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getConfiguration } = require("intlayer"); const { ComponentExample } = require("@components/ComponentExample"); const HomePage = () =>
{/* Ваш контент тут */}
; const getStaticPaths = async () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; const getStaticProps = async ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; ``` > `getStaticPaths` та `getStaticProps` гарантують, що ваш застосунок попередньо збудує необхідні сторінки для всіх локалей у Next.js Page Router. Такий підхід зменшує обчислення під час виконання і покращує взаємодію з користувачем. Для детальнішої інформації зверніться до документації Next.js щодо [`getStaticPaths`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths) та [`getStaticProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props). ### Крок 6: Оголосіть свій контент Створіть та керуйте деклараціями контенту для зберігання перекладів. ```tsx fileName="src/pages/[locale]/home.content.ts" contentDeclarationFormat="typescript" import { t, type Dictionary } from "intlayer"; const homeContent = { key: "home", content: { title: t({ uk: "Ласкаво просимо на мій вебсайт", en: "Welcome to My Website", fr: "Bienvenue sur mon site Web", es: "Bienvenido a mi sitio web", }), description: t({ uk: "Почніть, редагуючи цю сторінку.", en: "Get started by editing this page.", fr: "Commencez par éditer cette page.", es: "Comience por editar esta página.", }), }, } satisfies Dictionary; export default homeContent; ``` ```javascript fileName="src/pages/[locale]/home.content.mjs" contentDeclarationFormat="esm" import { t } from "intlayer"; /** @type {import('intlayer').Dictionary} */ const homeContent = { key: "home", content: { getStarted: { main: t({ uk: "Почніть, редагуючи цю сторінку.", en: "Get started by editing this page.", fr: "Commencez par éditer cette page.", es: "Comience por editar esta página.", }), pageLink: "src/app/page.tsx", }, }, }; export default homeContent; ``` ```javascript fileName="src/pages/[locale]/home.content.cjs" contentDeclarationFormat="commonjs" const { t } = require("intlayer"); /** @type {import('intlayer').Dictionary} */ const homeContent = { key: "home", content: { getStarted: { main: t({ uk: "Почніть з редагування цієї сторінки.", en: "Get started by editing this page.", fr: "Commencez par éditer cette page.", es: "Comience por editar esta página.", }), pageLink: "src/app/page.tsx", }, }, }; module.exports = homeContent; ``` ```json fileName="src/pages/[locale]/home.content.json" contentDeclarationFormat="json" { "$schema": "https://intlayer.org/schema.json", "key": "home", "content": { "getStarted": { "nodeType": "translation", "translation": { "uk": "Почніть з редагування цієї сторінки.", "en": "Get started by editing this page.", "fr": "Commencez par éditer cette page.", "es": "Comience por editar esta página." } }, "pageLink": { "nodeType": "translation", "translation": { "uk": "src/app/page.tsx", "en": "src/app/page.tsx", "fr": "src/app/page.tsx", "es": "src/app/page.tsx" } } } } ``` Для отримання додаткової інформації про оголошення контенту зверніться до [керівництва з оголошення контенту](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md). ### Крок 7: Використання контенту у вашому коді Отримуйте доступ до словників контенту у всьому застосунку, щоб відображати перекладений вміст. ```tsx {2,6} fileName="src/pages/[locale]/index.tsx" codeFormat="typescript" import type { FC } from "react"; import { useIntlayer } from "next-intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage: FC = () => { const content = useIntlayer("home"); return (

{content.title}

{content.description}

{/* Додаткові компоненти */}
); }; // ... Решта коду, включаючи getStaticPaths та getStaticProps export default HomePage; ``` ```jsx {1,5} fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { useIntlayer } from "next-intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage = () => { const content = useIntlayer("home"); return (

{content.getStarted.main}

{content.getStarted.pageLink} {/* Додаткові компоненти */}
); }; // ... Решта коду, включаючи getStaticPaths та getStaticProps export default HomePage; ``` ```jsx {1,5} fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { useIntlayer } = require("next-intlayer"); const { ComponentExample } = require("@components/ComponentExample"); const HomePage = () => { const content = useIntlayer("home"); return (

{content.getStarted.main}

{content.getStarted.pageLink} {/* Додаткові компоненти */}
); }; // ... Решта коду, включаючи getStaticPaths та getStaticProps ``` ```tsx fileName="src/components/ComponentExample.tsx" codeFormat="typescript" import type { FC } from "react"; import { useIntlayer } from "next-intlayer"; export const ComponentExample: FC = () => { const content = useIntlayer("component-example"); // Переконайтесь, що у вас є відповідна декларація вмісту return (

{content.title}

{content.content}

); }; ``` ```jsx fileName="src/components/ComponentExample.mjx" codeFormat="esm" import { useIntlayer } from "next-intlayer"; const ComponentExample = () => { const content = useIntlayer("component-example"); // Переконайтесь, що у вас є відповідна декларація вмісту return (

{content.title}

{content.content}

); }; ``` ```jsx fileName="src/components/ComponentExample.csx" codeFormat="commonjs" const { useIntlayer } = require("next-intlayer"); const ComponentExample = () => { const content = useIntlayer("component-example"); // Переконайтеся, що у вас є відповідна декларація вмісту return (

{content.title}

{content.content}

); }; ``` > Коли ви використовуєте переклади в рядкових атрибутах (наприклад, `alt`, `title`, `href`, `aria-label`), викликайте > значення функції таким чином: > ```jsx > {content.image.value} > ``` > Щоб дізнатися більше про хук `useIntlayer`, зверніться до [документації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useIntlayer.md). ### (Необов'язково) Крок 8: Інтернаціоналізація ваших метаданих Якщо ви хочете інтернаціоналізувати метадані, наприклад заголовок вашої сторінки, ви можете використовувати функцію `getStaticProps`, яку надає Next.js Page Router. Всередині ви можете отримати контент за допомогою функції `getIntlayer`, щоб перекласти ваші метадані. ```typescript fileName="src/pages/[locale]/metadata.content.ts" contentDeclarationFormat="typescript" import { type Dictionary, t } from "intlayer"; import { type Metadata } from "next"; const metadataContent = { key: "page-metadata", content: { title: t({ uk: "Створити додаток Next.js", en: "Create Next App", uk: "Створити додаток Next.js", fr: "Créer une application Next.js", es: "Crear una aplicación Next.js", }), description: t({ uk: "Згенеровано за допомогою create next app", en: "Generated by create next app", fr: "Généré par create next app", es: "Generado por create next app", }), }, } satisfies Dictionary; export default metadataContent; ``` ```javascript fileName="src/pages/[locale]/metadata.content.mjs" contentDeclarationFormat="esm" import { t } from "intlayer"; /** @type {import('intlayer').Dictionary} */ const metadataContent = { key: "page-metadata", content: { title: t({ uk: "Створити додаток Next.js", en: "Create Next App", fr: "Créer une application Next.js", es: "Crear una aplicación Next.js", }), description: t({ uk: "Згенеровано за допомогою create next app", en: "Generated by create next app", uk: "Згенеровано за допомогою create next app", fr: "Généré par create next app", es: "Generado por create next app", }), }, }; export default metadataContent; ``` ```javascript fileName="src/pages/[locale]/metadata.content.cjs" contentDeclarationFormat="commonjs" const { t } = require("intlayer"); /** @type {import('intlayer').Dictionary} */ const metadataContent = { key: "page-metadata", content: { title: t({ uk: "Створити додаток Next.js", en: "Create Next App", fr: "Créer une application Next.js", es: "Crear una aplicación Next.js", }), description: t({ uk: "Згенеровано за допомогою create next app", en: "Generated by create next app", fr: "Généré par create next app", es: "Generado por create next app", }), }, }; module.exports = metadataContent; ``` ```json fileName="src/pages/[locale]/metadata.content.json" contentDeclarationFormat="json" { "key": "page-metadata", "content": { "title": { "nodeType": "translation", "translation": { "uk": "Логотип Preact", "en": "Preact logo", "fr": "Logo Preact", "es": "Logo Preact", }, }, "description": { "nodeType": "translation", "translation": { "uk": "Згенеровано за допомогою create next app", "en": "Generated by create next app", "fr": "Généré par create next app", "es": "Generado por create next app", }, }, }, }; ``` ````tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript" import { GetStaticPaths, GetStaticProps } from "next"; import { getIntlayer, getMultilingualUrls } from "intlayer"; import { useIntlayer } from "next-intlayer"; ` import Head from "next/head"; import type { FC } from "react"; interface HomePageProps { locale: string; metadata: { title: string; description: string; }; multilingualUrls: Record; } const HomePage: FC = ({ metadata, multilingualUrls, locale, }) => { const content = useIntlayer("page"); return (
{metadata.title} {/* Генерує теги hreflang для SEO */} {Object.entries(multilingualUrls).map(([lang, url]) => ( ))} {/* Вміст сторінки */}
{/* Ваш вміст сторінки тут */}
); }; export const getStaticProps: GetStaticProps = async ({ params, }) => { const locale = params?.locale as string; const metadata = getIntlayer("page-metadata", locale); /** * Генерує об'єкт, що містить усі URL для кожної локалі. * * Приклад: * ```ts * getMultilingualUrls('/about'); * * // Повертає * // { * // en: '/about', * // fr: '/fr/about', * // es: '/es/about', * // } * ``` */ const multilingualUrls = getMultilingualUrls("/"); return { props: { locale, metadata, multilingualUrls, }, }; }; export default HomePage; // ... Решта коду, включаючи getStaticPaths ```` ````jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getIntlayer, getMultilingualUrls } from "intlayer"; import { useIntlayer } from "next-intlayer"; import Head from "next/head"; const HomePage = ({ metadata, multilingualUrls, locale }) => { const content = useIntlayer("page"); return (
{metadata.title} {/* Генерує теги hreflang для SEO */} {Object.entries(multilingualUrls).map(([lang, url]) => ( ))} {/* Вміст сторінки */}
{/* Ваш вміст сторінки тут */}
); }; export const getStaticProps = async ({ params }) => { const locale = params?.locale; const metadata = getIntlayer("page-metadata", locale); /** * Генерує об'єкт, що містить усі URL для кожної локалі. * * Приклад: * ```ts * getMultilingualUrls('/about'); * * // Повертає * // { * // en: '/about', * // fr: '/fr/about', * // es: '/es/about', * // } * ``` */ const multilingualUrls = getMultilingualUrls("/"); return { props: { locale, metadata, multilingualUrls, }, }; }; export default HomePage; // ... Решта коду, включаючи getStaticPaths ```` ````jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getIntlayer, getMultilingualUrls } = require("intlayer"); const { useIntlayer } = require("next-intlayer"); const Head = require("next/head"); const HomePage = ({ metadata, multilingualUrls, locale }) => { const content = useIntlayer("page"); return (
{metadata.title} {/* Генерує теги hreflang для SEO */} {Object.entries(multilingualUrls).map(([lang, url]) => ( ))} {/* Вміст сторінки */}
{/* Тут розмістіть контент сторінки */}
); }; const getStaticProps = async ({ params }) => { const locale = params?.locale; const metadata = getIntlayer("page-metadata", locale); /** * Генерує об'єкт, що містить всі URL для кожної локалі. * * Приклад: * ```ts * getMultilingualUrls('/about'); * * // Повертає * // { * // en: '/about', * // fr: '/fr/about', * // es: '/es/about', * // } * ``` */ const multilingualUrls = getMultilingualUrls("/"); return { props: { locale, metadata, multilingualUrls, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; // ... Інші частини коду, включаючи getStaticPaths ```` > Зверніть увагу, що функція `getIntlayer`, імпортована з `next-intlayer`, повертає ваш контент, обгорнутий у `IntlayerNode`, що дозволяє інтеграцію з візуальним редактором. Натомість функція `getIntlayer`, імпортована з `intlayer`, повертає контент напряму без додаткових властивостей. Альтернативно, ви можете використовувати функцію `getTranslation` для декларації ваших метаданих. Проте рекомендовано використовувати файли декларації контенту, щоб автоматизувати переклад метаданих та винести контент у зовнішні файли. ```tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript" import { GetStaticPaths, GetStaticProps } from "next"; import { type IConfigLocales, getTranslation, getMultilingualUrls, } from "intlayer"; import { useIntlayer } from "next-intlayer"; import Head from "next/head"; import type { FC } from "react"; interface HomePageProps { locale: string; metadata: { title: string; description: string; }; multilingualUrls: Record; } const HomePage: FC = ({ metadata, multilingualUrls, locale }) => { const content = useIntlayer("page"); return (
{metadata.title} {/* Згенерувати теги hreflang для SEO */} {Object.entries(multilingualUrls).map(([lang, url]) => ( ))} {/* Вміст сторінки */}
{/* Ваш вміст сторінки тут */}
); }; export const getStaticProps: GetStaticProps = async ({ params }) => { const locale = params?.locale as string; const t = (content: IConfigLocales) => getTranslation(content, locale); const metadata = { title: t({ uk: "Мій заголовок", en: "My title", fr: "Mon titre", es: "Mi título", }), description: t({ uk: "Мій опис", en: "My description", fr: "Ma description", es: "Mi descripción", }), }; const multilingualUrls = getMultilingualUrls("/"); return { props: { locale, metadata, multilingualUrls, }, }; }; export default HomePage; // ... Rest of the code including getStaticPaths ``` ```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getTranslation, getMultilingualUrls } from "intlayer"; import { useIntlayer } from "next-intlayer"; import Head from "next/head"; const HomePage = ({ metadata, multilingualUrls, locale }) => { const content = useIntlayer("page"); return (
{metadata.title} {/* Генеруємо hreflang-теги для SEO */} {Object.entries(multilingualUrls).map(([lang, url]) => ( ))} {/* Контент сторінки */}
{/* Вставте тут контент вашої сторінки */}
); }; export const getStaticProps = async ({ params }) => { const locale = params?.locale; const t = (content) => getTranslation(content, locale); const metadata = { title: t({ uk: "Мій заголовок", en: "My title", fr: "Mon titre", es: "Mi título", }), description: t({ uk: "Мій опис", en: "My description", fr: "Ma description", es: "Mi descripción", }), }; const multilingualUrls = getMultilingualUrls("/"); return { props: { locale, metadata, multilingualUrls, }, }; }; export default HomePage; // ... Решта коду, включно з getStaticPaths ``` ```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getTranslation, getMultilingualUrls } = require("intlayer"); const { useIntlayer } = require("next-intlayer"); const Head = require("next/head"); const HomePage = ({ metadata, multilingualUrls, locale }) => { const content = useIntlayer("page"); return (
{metadata.title} {/* Генерує теги hreflang для SEO */} {Object.entries(multilingualUrls).map(([lang, url]) => ( ))} {/* Вміст сторінки */}
{/* Тут розмістіть вміст сторінки */}
); }; const getStaticProps = async ({ params }) => { const locale = params?.locale; const t = (content) => getTranslation(content, locale); const metadata = { title: t({ uk: "Мій заголовок", en: "My title", fr: "Mon titre", es: "Mi título", }), description: t({ uk: "Мій опис", en: "My description", fr: "Ma description", es: "Mi descripción", }), }; const multilingualUrls = getMultilingualUrls("/"); return { props: { locale, metadata, multilingualUrls, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; // ... Решта коду, включно з getStaticPaths ``` > Дізнайтеся більше про оптимізацію метаданих [в офіційній документації Next.js](https://nextjs.org/docs/pages/building-your-application/optimizing/metadata). ### (Необов'язково) Крок 9: Змініть мову вашого вмісту Щоб змінити мову вашого вмісту в Next.js, рекомендовано використовувати компонент `Link` для перенаправлення користувачів на відповідну локалізовану сторінку. Компонент `Link` дає змогу передзавантажувати (prefetch) сторінку, що допомагає уникнути повного перезавантаження сторінки. ```tsx fileName="src/components/LanguageSwitcher.tsx" codeFormat="typescript" import { Locales, getHTMLTextDir, getLocaleName, getLocalizedUrl, } from "intlayer"; import { useLocalePageRouter } from "next-intlayer"; import { type FC } from "react"; import Link from "next/link"; const LocaleSwitcher: FC = () => { const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter(); return (
{availableLocales.map((localeItem) => ( setLocale(localeItem)} > {/* Локаль — наприклад FR */} {localeItem} {/* Мова у власній локалі — наприклад Français */} {getLocaleName(localeItem, locale)} {/* Мова в поточній локалі — наприклад Francés, коли поточна локаль встановлена на Locales.SPANISH */} {getLocaleName(localeItem)} {/* Мова англійською — наприклад French */} {getLocaleName(localeItem, Locales.ENGLISH)} ))}
); }; ``` ```jsx fileName="src/components/LanguageSwitcher.msx" codeFormat="esm" import { Locales, getHTMLTextDir, getLocaleName, getLocalizedUrl, } from "intlayer"; import { useLocalePageRouter } from "next-intlayer"; import Link from "next/link"; const LocaleSwitcher = () => { const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter(); return (
{availableLocales.map((localeItem) => ( setLocale(localeItem)} > {/* Локаль - наприклад FR */} {localeItem} {/* Мова в власній локалі - наприклад Français */} {getLocaleName(localeItem, locale)} {/* Мова в поточній локалі — наприклад, Francés при поточній локалі Locales.SPANISH */} {getLocaleName(localeItem)} {/* Мова англійською — наприклад, French */} {getLocaleName(localeItem, Locales.ENGLISH)} ))}
); }; ``` ```jsx fileName="src/components/LanguageSwitcher.msx" codeFormat="commonjs" const { Locales, getHTMLTextDir, getLocaleName, getLocalizedUrl, } = require("intlayer"); const { useLocalePageRouter } = require("next-intlayer"); const Link = require("next/link"); const LocaleSwitcher = () => { const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter(); return ( ); }; ``` > Альтернативний спосіб — використати функцію `setLocale`, яку надає хук `useLocale`. Ця функція не дозволяє попереднє завантаження (prefetch) сторінки і призведе до перезавантаження сторінки. > У такому випадку, без перенаправлення через `router.push`, змінить локаль вмісту лише ваш серверний код. ```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript" "use client"; import { useRouter } from "next/navigation"; import { useLocale } from "next-intlayer"; import { getLocalizedUrl } from "intlayer"; // ... Решта коду const router = useRouter(); const { setLocale } = useLocale({ onLocaleChange: (locale) => { router.push(getLocalizedUrl(pathWithoutLocale, locale)); }, }); return ( ); ``` > API `useLocalePageRouter` такий же, як `useLocale`. Щоб дізнатися більше про хук `useLocale`, зверніться до [документації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useLocale.md). > Посилання на документацію: > > - [`getLocaleName` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getLocaleName.md) > - [`getLocalizedUrl` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getLocalizedUrl.md) > - [`getHTMLTextDir` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getHTMLTextDir.md) > - [`hrefLang` атрибут](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr) > - [`lang` атрибут](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) > - [`dir` атрибут](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) > - [`aria-current` атрибут](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current) ### (Необов'язково) Крок 10: Створення локалізованого компонента Link Щоб гарантувати, що навігація вашого застосунку враховує поточну локаль, ви можете створити кастомний компонент `Link`. Цей компонент автоматично додає префікс поточної мови до внутрішніх URL-адрес, так щоб, наприклад, коли франкомовний користувач натискає на посилання на сторінку "About", він буде перенаправлений на `/fr/about` замість `/about`. Ця поведінка корисна з кількох причин: - **SEO та досвід користувача**: локалізовані URL-адреси допомагають пошуковим системам правильно індексувати сторінки для конкретних мов і надають користувачам контент їхньою переважною мовою. - **Послідовність**: Використовуючи локалізоване посилання в усьому вашому застосунку, ви гарантуєте, що навігація залишатиметься в межах поточної локалі, запобігаючи несподіваним змінам мови. - **Підтримуваність**: Централізація логіки локалізації в одному компоненті спрощує управління URL-адресами, роблячи вашу codebase простішою для підтримки та розширення у міру зростання застосунку. Нижче наведено реалізацію локалізованого компонента `Link` на TypeScript: ```tsx fileName="src/components/Link.tsx" codeFormat="typescript" "use client"; import { getLocalizedUrl } from "intlayer"; import NextLink, { type LinkProps as NextLinkProps } from "next/link"; import { useLocale } from "next-intlayer"; import { forwardRef, PropsWithChildren, type ForwardedRef } from "react"; /** * Утилітна функція для перевірки, чи є заданий URL зовнішнім. * Якщо URL починається з http:// або https://, він вважається зовнішнім. */ export const checkIsExternalLink = (href?: string): boolean => /^https?:\/\//.test(href ?? ""); /** * Кастомний компонент Link, який адаптує атрибут href залежно від поточної локалі. * Для внутрішніх посилань використовується `getLocalizedUrl`, щоб додати префікс локалі до URL (наприклад, /fr/about). * Це гарантує, що навігація залишатиметься в межах одного мовного контексту. */ export const Link = forwardRef< HTMLAnchorElement, PropsWithChildren >(({ href, children, ...props }, ref: ForwardedRef) => { const { locale } = useLocale(); const isExternalLink = checkIsExternalLink(href.toString()); // Якщо посилання внутрішнє й надано дійсний href, отримати локалізований URL. const hrefI18n: NextLinkProps["href"] = href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href; return ( {children} ); }); Link.displayName = "Link"; ``` ```jsx fileName="src/components/Link.mjx" codeFormat="esm" 'use client'; import { getLocalizedUrl } from 'intlayer'; import NextLink, { type LinkProps as NextLinkProps } from 'next/link'; import { useLocale } from 'next-intlayer'; import { forwardRef, PropsWithChildren, type ForwardedRef } from 'react'; /** * Утилітна функція для перевірки, чи є заданий URL зовнішнім. * Якщо URL починається з http:// або https://, він вважається зовнішнім. */ export const checkIsExternalLink = (href) => /^https?:\/\//.test(href ?? ''); /** * Користувацький компонент Link, який адаптує атрибут href залежно від поточної локалі. * Для внутрішніх посилань він використовує `getLocalizedUrl`, щоб додати префікс локалі до URL (наприклад, /fr/about). * Це забезпечує, що навігація відбувається в межах тієї самої локалі. */ export const Link = forwardRef(({ href, children, ...props }, ref) => { const { locale } = useLocale(); const isExternalLink = checkIsExternalLink(href.toString()); // Якщо посилання внутрішнє і надано валідний href, отримати локалізований URL. const hrefI18n = href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href; return ( {children} ); }); Link.displayName = 'Link'; ``` ```jsx fileName="src/components/Link.csx" codeFormat="commonjs" 'use client'; const { getLocalizedUrl } = require("intlayer"); const NextLink = require("next/link"); const { useLocale } = require("next-intlayer"); const { forwardRef } = require("react"); /** * Утилітна функція для перевірки, чи є заданий URL зовнішнім. * Якщо URL починається з http:// або https://, він вважається зовнішнім. */ const checkIsExternalLink = (href) => /^https?:\/\//.test(href ?? ''); const Link = forwardRef(({ href, children, ...props }, ref) => { const { locale } = useLocale(); const isExternalLink = checkIsExternalLink(href.toString()); // Якщо посилання внутрішнє і вказано дійсний href, отримаємо локалізований URL. const hrefI18n: NextLinkProps['href'] = href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href; return ( {children} ); }); Link.displayName = 'Link'; ``` #### Як це працює - **Виявлення зовнішніх посилань**: Допоміжна функція `checkIsExternalLink` визначає, чи є URL зовнішнім. Зовнішні посилання залишаються без змін, оскільки їх не потрібно локалізувати. - **Отримання поточної локалі**: Хук `useLocale` повертає поточну локаль (наприклад, `fr` для французької). - **Локалізація URL**: Для внутрішніх посилань (тобто не зовнішніх) використовується `getLocalizedUrl`, яка автоматично додає префікс поточної локалі до URL. Це означає, що якщо ваш користувач перебуває у французькій локалі, передавання `/about` як `href` перетвориться на `/fr/about`. - **Повернення посилання**: Компонент повертає елемент `` з локалізованим URL, що забезпечує узгоджену навігацію відповідно до локалі. Інтегруючи цей компонент `Link` у ваш додаток, ви підтримуєте послідовний і орієнтований на мову досвід користувача, а також отримуєте переваги у вигляді покращеного SEO та зручності використання. ### (Необов'язково) Крок 11: Оптимізуйте розмір bundle Коли використовується `next-intlayer`, словники за замовчуванням включаються в бандл для кожної сторінки. Щоб оптимізувати розмір бандла, Intlayer надає опційний SWC-плагін, який розумно замінює виклики `useIntlayer` за допомогою макросів. Це гарантує, що словники включаються в бандли лише для сторінок, які дійсно їх використовують. Щоб увімкнути цю оптимізацію, встановіть пакет `@intlayer/swc`. Після встановлення `next-intlayer` автоматично виявить і використовуватиме плагін: ```bash packageManager="npm" npm install @intlayer/swc --save-dev npx intlayer init ``` ```bash packageManager="pnpm" pnpm add @intlayer/swc --save-dev pnpm intlayer init ``` ```bash packageManager="yarn" yarn add @intlayer/swc --save-dev yarn intlayer init ``` ```bash packageManager="bun" bun add @intlayer/swc --dev bunx intlayer init ``` > Примітка: Ця оптимізація доступна лише для Next.js 13 та новіших версій. > Примітка: Цей пакет не встановлюється за замовчуванням, оскільки SWC-плагіни все ще є експериментальними в Next.js. Це може змінитися в майбутньому. ### Налаштування TypeScript Intlayer використовує module augmentation, щоб отримати переваги TypeScript і зробити вашу codebase більш надійною. ![Автозаповнення](https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true) ![Помилка перекладу](https://github.com/aymericzip/intlayer/blob/main/docs/assets/translation_error.png?raw=true) Переконайтеся, що у вашій конфігурації TypeScript включені автогенеровані типи. ```json5 fileName="tsconfig.json" { // ... Ваші існуючі конфігурації TypeScript "include": [ // ... Ваші існуючі конфігурації TypeScript ".intlayer/**/*.ts", // Включити автогенеровані типи ], } ``` ### Налаштування Git Щоб зберегти репозиторій чистим і уникнути коміту згенерованих файлів, рекомендується ігнорувати файли, створені Intlayer. Додайте наступні рядки до вашого файлу `.gitignore`: ```plaintext fileName=".gitignore" # Ігнорувати файли, згенеровані Intlayer .intlayer ``` ### Розширення для VS Code Щоб покращити ваш досвід розробки з Intlayer, ви можете встановити офіційне **Intlayer VS Code Extension**. [Встановити з VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension) Це розширення надає: - **Автодоповнення** для ключів перекладу. - **Виявлення помилок у реальному часі** для відсутніх перекладів. - **Вбудовані попередні перегляди** перекладеного вмісту. - **Швидкі дії** для легкого створення та оновлення перекладів. Для детальнішої інформації про використання розширення див. [Документація розширення Intlayer для VS Code](https://intlayer.org/doc/vs-code-extension). ## Додаткові ресурси - **Документація Intlayer:** [Репозиторій GitHub](https://github.com/aymericzip/intlayer) - **Посібник словника:** [Словник](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md) - **Документація з конфігурації:** [Керівництво з конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md) Дотримуючись цього посібника, ви можете ефективно інтегрувати Intlayer у ваш Next.js застосунок, що використовує Page Router, забезпечивши надійну та масштабовану підтримку інтернаціоналізації для ваших веб‑проєктів. ### Далі Щоб піти далі, ви можете реалізувати [візуальний редактор](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_visual_editor.md) або винести свій контент за допомогою [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_CMS.md).