@intlayer/docs 7.5.12 → 7.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/per-component_vs_centralized_i18n.md +248 -0
- package/blog/de/per-component_vs_centralized_i18n.md +248 -0
- package/blog/en/_per-component_vs_centralized_i18n.md +252 -0
- package/blog/en/per-component_vs_centralized_i18n.md +248 -0
- package/blog/en-GB/per-component_vs_centralized_i18n.md +247 -0
- package/blog/es/per-component_vs_centralized_i18n.md +245 -0
- package/blog/fr/per-component_vs_centralized_i18n.md +245 -0
- package/blog/hi/per-component_vs_centralized_i18n.md +249 -0
- package/blog/id/per-component_vs_centralized_i18n.md +248 -0
- package/blog/it/per-component_vs_centralized_i18n.md +247 -0
- package/blog/ja/per-component_vs_centralized_i18n.md +247 -0
- package/blog/ko/per-component_vs_centralized_i18n.md +246 -0
- package/blog/pl/per-component_vs_centralized_i18n.md +247 -0
- package/blog/pt/per-component_vs_centralized_i18n.md +246 -0
- package/blog/ru/per-component_vs_centralized_i18n.md +251 -0
- package/blog/tr/per-component_vs_centralized_i18n.md +244 -0
- package/blog/uk/compiler_vs_declarative_i18n.md +224 -0
- package/blog/uk/i18n_using_next-i18next.md +1086 -0
- package/blog/uk/i18n_using_next-intl.md +760 -0
- package/blog/uk/index.md +69 -0
- package/blog/uk/internationalization_and_SEO.md +273 -0
- package/blog/uk/intlayer_with_i18next.md +211 -0
- package/blog/uk/intlayer_with_next-i18next.md +202 -0
- package/blog/uk/intlayer_with_next-intl.md +203 -0
- package/blog/uk/intlayer_with_react-i18next.md +200 -0
- package/blog/uk/intlayer_with_react-intl.md +202 -0
- package/blog/uk/intlayer_with_vue-i18n.md +206 -0
- package/blog/uk/l10n_platform_alternative/Lokalise.md +80 -0
- package/blog/uk/l10n_platform_alternative/crowdin.md +80 -0
- package/blog/uk/l10n_platform_alternative/phrase.md +78 -0
- package/blog/uk/list_i18n_technologies/CMS/drupal.md +143 -0
- package/blog/uk/list_i18n_technologies/CMS/wix.md +167 -0
- package/blog/uk/list_i18n_technologies/CMS/wordpress.md +189 -0
- package/blog/uk/list_i18n_technologies/frameworks/angular.md +125 -0
- package/blog/uk/list_i18n_technologies/frameworks/flutter.md +128 -0
- package/blog/uk/list_i18n_technologies/frameworks/react-native.md +217 -0
- package/blog/uk/list_i18n_technologies/frameworks/react.md +155 -0
- package/blog/uk/list_i18n_technologies/frameworks/svelte.md +145 -0
- package/blog/uk/list_i18n_technologies/frameworks/vue.md +144 -0
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +1499 -0
- package/blog/uk/nextjs-multilingual-seo-comparison.md +360 -0
- package/blog/uk/per-component_vs_centralized_i18n.md +248 -0
- package/blog/uk/rag_powered_documentation_assistant.md +288 -0
- package/blog/uk/react-i18next_vs_react-intl_vs_intlayer.md +164 -0
- package/blog/uk/vue-i18n_vs_intlayer.md +279 -0
- package/blog/uk/what_is_internationalization.md +167 -0
- package/blog/vi/per-component_vs_centralized_i18n.md +246 -0
- package/blog/zh/per-component_vs_centralized_i18n.md +248 -0
- package/dist/cjs/common.cjs.map +1 -1
- package/dist/cjs/generated/blog.entry.cjs +20 -0
- package/dist/cjs/generated/blog.entry.cjs.map +1 -1
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/cjs/generated/frequentQuestions.entry.cjs +20 -0
- package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
- package/dist/cjs/generated/legal.entry.cjs.map +1 -1
- package/dist/esm/common.mjs.map +1 -1
- package/dist/esm/generated/blog.entry.mjs +20 -0
- package/dist/esm/generated/blog.entry.mjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +20 -0
- package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
- package/dist/esm/generated/legal.entry.mjs.map +1 -1
- package/dist/types/generated/blog.entry.d.ts +1 -0
- package/dist/types/generated/blog.entry.d.ts.map +1 -1
- package/dist/types/generated/frequentQuestions.entry.d.ts +1 -0
- package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
- package/docs/ar/configuration.md +6 -1
- package/docs/ar/dictionary/content_file.md +6 -1
- package/docs/de/configuration.md +6 -1
- package/docs/de/dictionary/content_file.md +6 -1
- package/docs/en/configuration.md +6 -1
- package/docs/en/dictionary/content_file.md +6 -1
- package/docs/en-GB/configuration.md +6 -1
- package/docs/en-GB/dictionary/content_file.md +3 -1
- package/docs/es/configuration.md +6 -1
- package/docs/es/dictionary/content_file.md +6 -1
- package/docs/fr/configuration.md +6 -1
- package/docs/fr/dictionary/content_file.md +3 -1
- package/docs/hi/configuration.md +6 -1
- package/docs/hi/dictionary/content_file.md +3 -1
- package/docs/id/configuration.md +6 -1
- package/docs/id/dictionary/content_file.md +3 -1
- package/docs/it/configuration.md +6 -1
- package/docs/it/dictionary/content_file.md +3 -1
- package/docs/ja/configuration.md +6 -1
- package/docs/ja/dictionary/content_file.md +3 -1
- package/docs/ko/configuration.md +6 -1
- package/docs/ko/dictionary/content_file.md +3 -1
- package/docs/pl/configuration.md +3 -1
- package/docs/pl/dictionary/content_file.md +3 -1
- package/docs/pt/configuration.md +6 -1
- package/docs/pt/dictionary/content_file.md +3 -1
- package/docs/ru/configuration.md +6 -1
- package/docs/ru/dictionary/content_file.md +6 -1
- package/docs/tr/configuration.md +6 -1
- package/docs/tr/dictionary/content_file.md +3 -1
- package/docs/uk/CI_CD.md +198 -0
- package/docs/uk/autoFill.md +307 -0
- package/docs/uk/bundle_optimization.md +185 -0
- package/docs/uk/cli/build.md +64 -0
- package/docs/uk/cli/ci.md +137 -0
- package/docs/uk/cli/configuration.md +63 -0
- package/docs/uk/cli/debug.md +46 -0
- package/docs/uk/cli/doc-review.md +43 -0
- package/docs/uk/cli/doc-translate.md +132 -0
- package/docs/uk/cli/editor.md +28 -0
- package/docs/uk/cli/fill.md +130 -0
- package/docs/uk/cli/index.md +190 -0
- package/docs/uk/cli/init.md +84 -0
- package/docs/uk/cli/list.md +90 -0
- package/docs/uk/cli/list_projects.md +128 -0
- package/docs/uk/cli/live.md +41 -0
- package/docs/uk/cli/login.md +157 -0
- package/docs/uk/cli/pull.md +78 -0
- package/docs/uk/cli/push.md +98 -0
- package/docs/uk/cli/sdk.md +71 -0
- package/docs/uk/cli/test.md +76 -0
- package/docs/uk/cli/transform.md +65 -0
- package/docs/uk/cli/version.md +24 -0
- package/docs/uk/cli/watch.md +37 -0
- package/docs/uk/configuration.md +742 -0
- package/docs/uk/dictionary/condition.md +237 -0
- package/docs/uk/dictionary/content_file.md +1134 -0
- package/docs/uk/dictionary/enumeration.md +245 -0
- package/docs/uk/dictionary/file.md +232 -0
- package/docs/uk/dictionary/function_fetching.md +212 -0
- package/docs/uk/dictionary/gender.md +273 -0
- package/docs/uk/dictionary/insertion.md +187 -0
- package/docs/uk/dictionary/markdown.md +383 -0
- package/docs/uk/dictionary/nesting.md +273 -0
- package/docs/uk/dictionary/translation.md +332 -0
- package/docs/uk/formatters.md +595 -0
- package/docs/uk/how_works_intlayer.md +256 -0
- package/docs/uk/index.md +175 -0
- package/docs/uk/interest_of_intlayer.md +297 -0
- package/docs/uk/intlayer_CMS.md +569 -0
- package/docs/uk/intlayer_visual_editor.md +292 -0
- package/docs/uk/intlayer_with_angular.md +710 -0
- package/docs/uk/intlayer_with_astro.md +256 -0
- package/docs/uk/intlayer_with_create_react_app.md +1258 -0
- package/docs/uk/intlayer_with_express.md +429 -0
- package/docs/uk/intlayer_with_fastify.md +446 -0
- package/docs/uk/intlayer_with_lynx+react.md +548 -0
- package/docs/uk/intlayer_with_nestjs.md +283 -0
- package/docs/uk/intlayer_with_next-i18next.md +640 -0
- package/docs/uk/intlayer_with_next-intl.md +456 -0
- package/docs/uk/intlayer_with_nextjs_page_router.md +1541 -0
- package/docs/uk/intlayer_with_nuxt.md +711 -0
- package/docs/uk/intlayer_with_react_router_v7.md +600 -0
- package/docs/uk/intlayer_with_react_router_v7_fs_routes.md +669 -0
- package/docs/uk/intlayer_with_svelte_kit.md +579 -0
- package/docs/uk/intlayer_with_tanstack.md +818 -0
- package/docs/uk/intlayer_with_vite+preact.md +1748 -0
- package/docs/uk/intlayer_with_vite+react.md +1449 -0
- package/docs/uk/intlayer_with_vite+solid.md +302 -0
- package/docs/uk/intlayer_with_vite+svelte.md +520 -0
- package/docs/uk/intlayer_with_vite+vue.md +1113 -0
- package/docs/uk/introduction.md +222 -0
- package/docs/uk/locale_mapper.md +242 -0
- package/docs/uk/mcp_server.md +211 -0
- package/docs/uk/packages/express-intlayer/t.md +465 -0
- package/docs/uk/packages/intlayer/getEnumeration.md +159 -0
- package/docs/uk/packages/intlayer/getHTMLTextDir.md +121 -0
- package/docs/uk/packages/intlayer/getLocaleLang.md +81 -0
- package/docs/uk/packages/intlayer/getLocaleName.md +135 -0
- package/docs/uk/packages/intlayer/getLocalizedUrl.md +338 -0
- package/docs/uk/packages/intlayer/getMultilingualUrls.md +359 -0
- package/docs/uk/packages/intlayer/getPathWithoutLocale.md +75 -0
- package/docs/uk/packages/intlayer/getPrefix.md +213 -0
- package/docs/uk/packages/intlayer/getTranslation.md +190 -0
- package/docs/uk/packages/intlayer/getTranslationContent.md +189 -0
- package/docs/uk/packages/next-intlayer/t.md +365 -0
- package/docs/uk/packages/next-intlayer/useDictionary.md +276 -0
- package/docs/uk/packages/next-intlayer/useIntlayer.md +263 -0
- package/docs/uk/packages/next-intlayer/useLocale.md +166 -0
- package/docs/uk/packages/react-intlayer/t.md +311 -0
- package/docs/uk/packages/react-intlayer/useDictionary.md +295 -0
- package/docs/uk/packages/react-intlayer/useI18n.md +250 -0
- package/docs/uk/packages/react-intlayer/useIntlayer.md +251 -0
- package/docs/uk/packages/react-intlayer/useLocale.md +210 -0
- package/docs/uk/per_locale_file.md +345 -0
- package/docs/uk/plugins/sync-json.md +398 -0
- package/docs/uk/readme.md +265 -0
- package/docs/uk/releases/v6.md +305 -0
- package/docs/uk/releases/v7.md +624 -0
- package/docs/uk/roadmap.md +346 -0
- package/docs/uk/testing.md +204 -0
- package/docs/vi/configuration.md +6 -1
- package/docs/vi/dictionary/content_file.md +6 -1
- package/docs/zh/configuration.md +6 -1
- package/docs/zh/dictionary/content_file.md +6 -1
- package/frequent_questions/ar/error-vite-env-only.md +77 -0
- package/frequent_questions/de/error-vite-env-only.md +77 -0
- package/frequent_questions/en/error-vite-env-only.md +77 -0
- package/frequent_questions/en-GB/error-vite-env-only.md +77 -0
- package/frequent_questions/es/error-vite-env-only.md +76 -0
- package/frequent_questions/fr/error-vite-env-only.md +77 -0
- package/frequent_questions/hi/error-vite-env-only.md +77 -0
- package/frequent_questions/id/error-vite-env-only.md +77 -0
- package/frequent_questions/it/error-vite-env-only.md +77 -0
- package/frequent_questions/ja/error-vite-env-only.md +77 -0
- package/frequent_questions/ko/error-vite-env-only.md +77 -0
- package/frequent_questions/pl/error-vite-env-only.md +77 -0
- package/frequent_questions/pt/error-vite-env-only.md +77 -0
- package/frequent_questions/ru/error-vite-env-only.md +77 -0
- package/frequent_questions/tr/error-vite-env-only.md +77 -0
- package/frequent_questions/uk/SSR_Next_no_[locale].md +104 -0
- package/frequent_questions/uk/array_as_content_declaration.md +72 -0
- package/frequent_questions/uk/build_dictionaries.md +58 -0
- package/frequent_questions/uk/build_error_CI_CD.md +74 -0
- package/frequent_questions/uk/bun_set_up.md +53 -0
- package/frequent_questions/uk/customized_locale_list.md +64 -0
- package/frequent_questions/uk/domain_routing.md +113 -0
- package/frequent_questions/uk/error-vite-env-only.md +77 -0
- package/frequent_questions/uk/esbuild_error.md +29 -0
- package/frequent_questions/uk/get_locale_cookie.md +142 -0
- package/frequent_questions/uk/intlayer_command_undefined.md +155 -0
- package/frequent_questions/uk/locale_incorect_in_url.md +73 -0
- package/frequent_questions/uk/package_version_error.md +181 -0
- package/frequent_questions/uk/static_rendering.md +44 -0
- package/frequent_questions/uk/translated_path_url.md +55 -0
- package/frequent_questions/uk/unknown_command.md +97 -0
- package/frequent_questions/vi/error-vite-env-only.md +77 -0
- package/frequent_questions/zh/error-vite-env-only.md +77 -0
- package/legal/uk/privacy_notice.md +83 -0
- package/legal/uk/terms_of_service.md +55 -0
- package/package.json +9 -9
- package/src/generated/blog.entry.ts +20 -0
- package/src/generated/frequentQuestions.entry.ts +20 -0
|
@@ -0,0 +1,1541 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2024-12-07
|
|
3
|
+
updatedAt: 2025-12-30
|
|
4
|
+
title: Як перекласти ваш застосунок Next.js з Page Router – посібник i18n 2026
|
|
5
|
+
description: Дізнайтеся, як зробити ваш вебсайт на Next.js з Page Router багатомовним. Дотримуйтесь документації, щоб інтернаціоналізувати (i18n) і перекласти його.
|
|
6
|
+
keywords:
|
|
7
|
+
- Інтернаціоналізація
|
|
8
|
+
- Документація
|
|
9
|
+
- Intlayer
|
|
10
|
+
- Page Router
|
|
11
|
+
- Next.js
|
|
12
|
+
- JavaScript
|
|
13
|
+
- React
|
|
14
|
+
slugs:
|
|
15
|
+
- doc
|
|
16
|
+
- environment
|
|
17
|
+
- nextjs
|
|
18
|
+
- next-with-page-router
|
|
19
|
+
history:
|
|
20
|
+
- version: 7.5.9
|
|
21
|
+
date: 2025-12-30
|
|
22
|
+
changes: Додано команду init
|
|
23
|
+
- version: 5.6.0
|
|
24
|
+
date: 2025-07-06
|
|
25
|
+
changes: Перетворено функцію `withIntlayer()` на функцію на основі промісів
|
|
26
|
+
- version: 5.5.10
|
|
27
|
+
date: 2025-06-29
|
|
28
|
+
changes: Ініціалізовано історію
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Перекладіть свій вебсайт на Next.js із Page Router за допомогою Intlayer | Інтернаціоналізація (i18n)
|
|
32
|
+
|
|
33
|
+
## Зміст
|
|
34
|
+
|
|
35
|
+
<TOC/>
|
|
36
|
+
|
|
37
|
+
## Що таке Intlayer?
|
|
38
|
+
|
|
39
|
+
**Intlayer** — інноваційна open-source бібліотека інтернаціоналізації (i18n), створена для спрощення підтримки багатомовності в сучасних вебдодатках. Intlayer безшовно інтегрується з останніми версіями **Next.js**, включно з традиційним **Page Router**.
|
|
40
|
+
|
|
41
|
+
За допомогою Intlayer ви можете:
|
|
42
|
+
|
|
43
|
+
- **Легко керувати перекладами**, використовуючи декларативні словники на рівні компонентів.
|
|
44
|
+
- **Динамічно локалізувати метадані**, маршрути та контент.
|
|
45
|
+
- **Забезпечити підтримку TypeScript** завдяки автогенерованим типам, що покращує автозаповнення й виявлення помилок.
|
|
46
|
+
- **Отримати вигоду від розширених можливостей**, таких як динамічне визначення локалі та її перемикання.
|
|
47
|
+
|
|
48
|
+
> 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).
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Покроковий посібник щодо налаштування Intlayer у застосунку Next.js із Page Router
|
|
53
|
+
|
|
54
|
+
### Крок 1: Встановіть залежності
|
|
55
|
+
|
|
56
|
+
Встановіть необхідні пакунки, використовуючи обраний менеджер пакетів:
|
|
57
|
+
|
|
58
|
+
```bash packageManager="npm"
|
|
59
|
+
npm install intlayer next-intlayer
|
|
60
|
+
npx intlayer init
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```bash packageManager="pnpm"
|
|
64
|
+
pnpm add intlayer next-intlayer
|
|
65
|
+
pnpm intlayer init
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```bash packageManager="yarn"
|
|
69
|
+
yarn add intlayer next-intlayer
|
|
70
|
+
yarn intlayer init
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```bash packageManager="bun"
|
|
74
|
+
bun add intlayer next-intlayer
|
|
75
|
+
bunx intlayer init
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- **intlayer**
|
|
79
|
+
|
|
80
|
+
Основний пакет, який надає інструменти інтернаціоналізації для керування конфігурацією, перекладу, [оголошення вмісту](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).
|
|
81
|
+
|
|
82
|
+
- **next-intlayer**
|
|
83
|
+
|
|
84
|
+
Пакет, що інтегрує 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.
|
|
85
|
+
|
|
86
|
+
### Крок 2: Налаштуйте свій проєкт
|
|
87
|
+
|
|
88
|
+
Створіть файл конфігурації, щоб визначити мови, які підтримує ваш застосунок:
|
|
89
|
+
|
|
90
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
91
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
92
|
+
|
|
93
|
+
const config: IntlayerConfig = {
|
|
94
|
+
internationalization: {
|
|
95
|
+
// Локалі, які підтримуються вашим додатком.
|
|
96
|
+
locales: [
|
|
97
|
+
Locales.ENGLISH,
|
|
98
|
+
Locales.FRENCH,
|
|
99
|
+
Locales.SPANISH,
|
|
100
|
+
// Додайте тут інші локалі
|
|
101
|
+
],
|
|
102
|
+
defaultLocale: Locales.ENGLISH,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export default config;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```javascript fileName="intlayer.config.mjs" codeFormat="esm"
|
|
110
|
+
import { Locales } from "intlayer";
|
|
111
|
+
|
|
112
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
113
|
+
const config = {
|
|
114
|
+
internationalization: {
|
|
115
|
+
locales: [
|
|
116
|
+
Locales.ENGLISH,
|
|
117
|
+
Locales.FRENCH,
|
|
118
|
+
Locales.SPANISH,
|
|
119
|
+
// Додайте тут інші локалі
|
|
120
|
+
],
|
|
121
|
+
defaultLocale: Locales.ENGLISH,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default config;
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
129
|
+
const { Locales } = require("intlayer");
|
|
130
|
+
|
|
131
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
132
|
+
const config = {
|
|
133
|
+
internationalization: {
|
|
134
|
+
locales: [
|
|
135
|
+
Locales.ENGLISH,
|
|
136
|
+
Locales.FRENCH,
|
|
137
|
+
Locales.SPANISH,
|
|
138
|
+
// Додайте тут інші локалі
|
|
139
|
+
],
|
|
140
|
+
defaultLocale: Locales.ENGLISH,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
module.exports = config;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
> Через цей конфігураційний файл ви можете налаштувати локалізовані URL-адреси, перенаправлення в middleware, імена cookie, розташування та розширення файлів декларацій вмісту, вимкнути логи Intlayer у консолі тощо. Для повного переліку доступних параметрів зверніться до [документації з конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md).
|
|
148
|
+
|
|
149
|
+
### Крок 3: Інтеграція Intlayer з конфігурацією Next.js
|
|
150
|
+
|
|
151
|
+
Змініть конфігурацію Next.js, щоб інтегрувати Intlayer:
|
|
152
|
+
|
|
153
|
+
```typescript fileName="next.config.mjs"
|
|
154
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
155
|
+
|
|
156
|
+
/** @type {import('next').NextConfig} */
|
|
157
|
+
const nextConfig = {
|
|
158
|
+
// Ваша існуюча конфігурація Next.js
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default withIntlayer(nextConfig);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
> Плагін Next.js `withIntlayer()` використовується для інтеграції Intlayer з Next.js. Він забезпечує побудову файлів декларацій контенту та відстежує їх у режимі розробки. Він визначає змінні середовища Intlayer у середовищах [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack). Крім того, він надає алиаси для оптимізації продуктивності й забезпечує сумісність із серверними компонентами.
|
|
165
|
+
|
|
166
|
+
> Функція `withIntlayer()` повертає проміс. Якщо ви хочете використовувати її разом з іншими плагінами, ви можете дочекатися її виконання за допомогою await. Приклад:
|
|
167
|
+
>
|
|
168
|
+
> ```tsx
|
|
169
|
+
> const nextConfig = await withIntlayer(nextConfig);
|
|
170
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
171
|
+
>
|
|
172
|
+
> export default nextConfigWithOtherPlugins;
|
|
173
|
+
> ```
|
|
174
|
+
|
|
175
|
+
### Крок 4: Налаштуйте middleware для визначення локалі
|
|
176
|
+
|
|
177
|
+
Налаштуйте middleware для автоматичного визначення та обробки бажаної користувачем локалі:
|
|
178
|
+
|
|
179
|
+
```typescript fileName="src/middleware.ts" codeFormat="typescript"
|
|
180
|
+
export { intlayerProxy as middleware } from "next-intlayer/middleware";
|
|
181
|
+
|
|
182
|
+
export const config = {
|
|
183
|
+
matcher:
|
|
184
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```javascript fileName="src/middleware.mjs" codeFormat="esm"
|
|
189
|
+
export { intlayerProxy as middleware } from "next-intlayer/middleware";
|
|
190
|
+
|
|
191
|
+
export const config = {
|
|
192
|
+
matcher:
|
|
193
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
194
|
+
};
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```javascript fileName="src/middleware.cjs" codeFormat="commonjs"
|
|
198
|
+
const { intlayerProxy } = require("next-intlayer/middleware");
|
|
199
|
+
|
|
200
|
+
const config = {
|
|
201
|
+
matcher:
|
|
202
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
module.exports = { middleware: intlayerProxy, config };
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
> Налаштуйте параметр `matcher`, щоб відповідати маршрутам вашого застосунку. Для детальнішої інформації див. [документацію Next.js щодо конфігурації matcher](https://nextjs.org/docs/app/building-your-application/routing/middleware).
|
|
209
|
+
|
|
210
|
+
### Крок 5: Визначте динамічні маршрути локалей
|
|
211
|
+
|
|
212
|
+
Реалізуйте динамічну маршрутизацію для подачі локалізованого контенту залежно від локалі користувача.
|
|
213
|
+
|
|
214
|
+
1. **Створіть сторінки для конкретної локалі:**
|
|
215
|
+
|
|
216
|
+
Перейменуйте головний файл сторінки, щоб додати динамічний сегмент `[locale]`.
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
mv src/pages/index.tsx src/pages/[locale]/index.tsx
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
2. **Оновіть `_app.tsx`, щоб обробляти локалізацію:**
|
|
223
|
+
|
|
224
|
+
Змініть свій `_app.tsx`, щоб включити провайдери Intlayer.
|
|
225
|
+
|
|
226
|
+
```tsx fileName="src/pages/_app.tsx" codeFormat="typescript"
|
|
227
|
+
import type { FC } from "react";
|
|
228
|
+
import type { AppProps } from "next/app";
|
|
229
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
230
|
+
|
|
231
|
+
const App = FC<AppProps>({ Component, pageProps }) => {
|
|
232
|
+
const { locale } = pageProps;
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
<IntlayerClientProvider locale={locale}>
|
|
236
|
+
<Component {...pageProps} />
|
|
237
|
+
</IntlayerClientProvider>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default MyApp;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```jsx fileName="src/pages/_app.mjx" codeFormat="esm"
|
|
245
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
246
|
+
|
|
247
|
+
const App = ({ Component, pageProps }) => (
|
|
248
|
+
<IntlayerClientProvider locale={locale}>
|
|
249
|
+
<Component {...pageProps} />
|
|
250
|
+
</IntlayerClientProvider>
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
export default App;
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```jsx fileName="src/pages/_app.csx" codeFormat="commonjs"
|
|
257
|
+
const { IntlayerClientProvider } = require("next-intlayer");
|
|
258
|
+
|
|
259
|
+
const App = ({ Component, pageProps }) => (
|
|
260
|
+
<IntlayerClientProvider locale={locale}>
|
|
261
|
+
<Component {...pageProps} />
|
|
262
|
+
</IntlayerClientProvider>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
module.exports = App;
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
3. **Налаштуйте `getStaticPaths` та `getStaticProps`:**
|
|
269
|
+
|
|
270
|
+
У файлі `[locale]/index.tsx` визначте paths і props для обробки різних локалей.
|
|
271
|
+
|
|
272
|
+
```tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript"
|
|
273
|
+
import type { FC } from "react";
|
|
274
|
+
import type { GetStaticPaths, GetStaticProps } from "next";
|
|
275
|
+
import { type Locales, getConfiguration } from "intlayer";
|
|
276
|
+
|
|
277
|
+
const HomePage: FC = () => <div>{/* Ваш контент тут */}</div>;
|
|
278
|
+
|
|
279
|
+
export const getStaticPaths: GetStaticPaths = () => {
|
|
280
|
+
const { internationalization } = getConfiguration();
|
|
281
|
+
const { locales } = internationalization;
|
|
282
|
+
|
|
283
|
+
const paths = locales.map((locale) => ({
|
|
284
|
+
params: { locale },
|
|
285
|
+
}));
|
|
286
|
+
|
|
287
|
+
return { paths, fallback: false };
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export const getStaticProps: GetStaticProps = ({ params }) => {
|
|
291
|
+
const locale = params?.locale as string;
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
props: {
|
|
295
|
+
locale,
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export default HomePage;
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm"
|
|
304
|
+
import { getConfiguration } from "intlayer";
|
|
305
|
+
import { ComponentExample } from "@components/ComponentExample";
|
|
306
|
+
|
|
307
|
+
const HomePage = () => <div>{/* Ваш контент тут */}</div>;
|
|
308
|
+
|
|
309
|
+
export const getStaticPaths = () => {
|
|
310
|
+
const { internationalization } = getConfiguration();
|
|
311
|
+
const { locales } = internationalization;
|
|
312
|
+
|
|
313
|
+
const paths = locales.map((locale) => ({
|
|
314
|
+
params: { locale },
|
|
315
|
+
}));
|
|
316
|
+
|
|
317
|
+
return { paths, fallback: false };
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export const getStaticProps = ({ params }) => {
|
|
321
|
+
const locale = params?.locale;
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
props: {
|
|
325
|
+
locale,
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs"
|
|
332
|
+
const { getConfiguration } = require("intlayer");
|
|
333
|
+
const { ComponentExample } = require("@components/ComponentExample");
|
|
334
|
+
|
|
335
|
+
const HomePage = () => <div>{/* Ваш контент тут */}</div>;
|
|
336
|
+
|
|
337
|
+
const getStaticPaths = async () => {
|
|
338
|
+
const { internationalization } = getConfiguration();
|
|
339
|
+
const { locales } = internationalization;
|
|
340
|
+
|
|
341
|
+
const paths = locales.map((locale) => ({
|
|
342
|
+
params: { locale },
|
|
343
|
+
}));
|
|
344
|
+
|
|
345
|
+
return { paths, fallback: false };
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const getStaticProps = async ({ params }) => {
|
|
349
|
+
const locale = params?.locale;
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
props: {
|
|
353
|
+
locale,
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
module.exports = {
|
|
359
|
+
getStaticProps,
|
|
360
|
+
getStaticPaths,
|
|
361
|
+
default: HomePage,
|
|
362
|
+
};
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
> `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).
|
|
366
|
+
|
|
367
|
+
### Крок 6: Оголосіть свій контент
|
|
368
|
+
|
|
369
|
+
Створіть та керуйте деклараціями контенту для зберігання перекладів.
|
|
370
|
+
|
|
371
|
+
```tsx fileName="src/pages/[locale]/home.content.ts" contentDeclarationFormat="typescript"
|
|
372
|
+
import { t, type Dictionary } from "intlayer";
|
|
373
|
+
|
|
374
|
+
const homeContent = {
|
|
375
|
+
key: "home",
|
|
376
|
+
content: {
|
|
377
|
+
title: t({
|
|
378
|
+
uk: "Ласкаво просимо на мій вебсайт",
|
|
379
|
+
en: "Welcome to My Website",
|
|
380
|
+
fr: "Bienvenue sur mon site Web",
|
|
381
|
+
es: "Bienvenido a mi sitio web",
|
|
382
|
+
}),
|
|
383
|
+
description: t({
|
|
384
|
+
uk: "Почніть, редагуючи цю сторінку.",
|
|
385
|
+
en: "Get started by editing this page.",
|
|
386
|
+
fr: "Commencez par éditer cette page.",
|
|
387
|
+
es: "Comience por editar esta página.",
|
|
388
|
+
}),
|
|
389
|
+
},
|
|
390
|
+
} satisfies Dictionary;
|
|
391
|
+
|
|
392
|
+
export default homeContent;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```javascript fileName="src/pages/[locale]/home.content.mjs" contentDeclarationFormat="esm"
|
|
396
|
+
import { t } from "intlayer";
|
|
397
|
+
|
|
398
|
+
/** @type {import('intlayer').Dictionary} */
|
|
399
|
+
const homeContent = {
|
|
400
|
+
key: "home",
|
|
401
|
+
content: {
|
|
402
|
+
getStarted: {
|
|
403
|
+
main: t({
|
|
404
|
+
uk: "Почніть, редагуючи цю сторінку.",
|
|
405
|
+
en: "Get started by editing this page.",
|
|
406
|
+
fr: "Commencez par éditer cette page.",
|
|
407
|
+
es: "Comience por editar esta página.",
|
|
408
|
+
}),
|
|
409
|
+
pageLink: "src/app/page.tsx",
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
export default homeContent;
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
```javascript fileName="src/pages/[locale]/home.content.cjs" contentDeclarationFormat="commonjs"
|
|
418
|
+
const { t } = require("intlayer");
|
|
419
|
+
|
|
420
|
+
/** @type {import('intlayer').Dictionary} */
|
|
421
|
+
const homeContent = {
|
|
422
|
+
key: "home",
|
|
423
|
+
content: {
|
|
424
|
+
getStarted: {
|
|
425
|
+
main: t({
|
|
426
|
+
uk: "Почніть з редагування цієї сторінки.",
|
|
427
|
+
en: "Get started by editing this page.",
|
|
428
|
+
fr: "Commencez par éditer cette page.",
|
|
429
|
+
es: "Comience por editar esta página.",
|
|
430
|
+
}),
|
|
431
|
+
pageLink: "src/app/page.tsx",
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
module.exports = homeContent;
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
```json fileName="src/pages/[locale]/home.content.json" contentDeclarationFormat="json"
|
|
440
|
+
{
|
|
441
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
442
|
+
"key": "home",
|
|
443
|
+
"content": {
|
|
444
|
+
"getStarted": {
|
|
445
|
+
"nodeType": "translation",
|
|
446
|
+
"translation": {
|
|
447
|
+
"uk": "Почніть з редагування цієї сторінки.",
|
|
448
|
+
"en": "Get started by editing this page.",
|
|
449
|
+
"fr": "Commencez par éditer cette page.",
|
|
450
|
+
"es": "Comience por editar esta página."
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
"pageLink": {
|
|
454
|
+
"nodeType": "translation",
|
|
455
|
+
"translation": {
|
|
456
|
+
"uk": "src/app/page.tsx",
|
|
457
|
+
"en": "src/app/page.tsx",
|
|
458
|
+
"fr": "src/app/page.tsx",
|
|
459
|
+
"es": "src/app/page.tsx"
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Для отримання додаткової інформації про оголошення контенту зверніться до [керівництва з оголошення контенту](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md).
|
|
467
|
+
|
|
468
|
+
### Крок 7: Використання контенту у вашому коді
|
|
469
|
+
|
|
470
|
+
Отримуйте доступ до словників контенту у всьому застосунку, щоб відображати перекладений вміст.
|
|
471
|
+
|
|
472
|
+
```tsx {2,6} fileName="src/pages/[locale]/index.tsx" codeFormat="typescript"
|
|
473
|
+
import type { FC } from "react";
|
|
474
|
+
import { useIntlayer } from "next-intlayer";
|
|
475
|
+
import { ComponentExample } from "@components/ComponentExample";
|
|
476
|
+
|
|
477
|
+
const HomePage: FC = () => {
|
|
478
|
+
const content = useIntlayer("home");
|
|
479
|
+
|
|
480
|
+
return (
|
|
481
|
+
<div>
|
|
482
|
+
<h1>{content.title}</h1>
|
|
483
|
+
<p>{content.description}</p>
|
|
484
|
+
<ComponentExample />
|
|
485
|
+
{/* Додаткові компоненти */}
|
|
486
|
+
</div>
|
|
487
|
+
);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// ... Решта коду, включаючи getStaticPaths та getStaticProps
|
|
491
|
+
|
|
492
|
+
export default HomePage;
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
```jsx {1,5} fileName="src/pages/[locale]/index.mjx" codeFormat="esm"
|
|
496
|
+
import { useIntlayer } from "next-intlayer";
|
|
497
|
+
import { ComponentExample } from "@components/ComponentExample";
|
|
498
|
+
|
|
499
|
+
const HomePage = () => {
|
|
500
|
+
const content = useIntlayer("home");
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<div>
|
|
504
|
+
<h1>{content.getStarted.main}</h1>
|
|
505
|
+
<code>{content.getStarted.pageLink}</code>
|
|
506
|
+
|
|
507
|
+
<ComponentExample />
|
|
508
|
+
{/* Додаткові компоненти */}
|
|
509
|
+
</div>
|
|
510
|
+
);
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// ... Решта коду, включаючи getStaticPaths та getStaticProps
|
|
514
|
+
|
|
515
|
+
export default HomePage;
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
```jsx {1,5} fileName="src/pages/[locale]/index.csx" codeFormat="commonjs"
|
|
519
|
+
const { useIntlayer } = require("next-intlayer");
|
|
520
|
+
const { ComponentExample } = require("@components/ComponentExample");
|
|
521
|
+
|
|
522
|
+
const HomePage = () => {
|
|
523
|
+
const content = useIntlayer("home");
|
|
524
|
+
|
|
525
|
+
return (
|
|
526
|
+
<div>
|
|
527
|
+
<h1>{content.getStarted.main}</h1>
|
|
528
|
+
<code>{content.getStarted.pageLink}</code>
|
|
529
|
+
|
|
530
|
+
<ComponentExample />
|
|
531
|
+
{/* Додаткові компоненти */}
|
|
532
|
+
</div>
|
|
533
|
+
);
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// ... Решта коду, включаючи getStaticPaths та getStaticProps
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
```tsx fileName="src/components/ComponentExample.tsx" codeFormat="typescript"
|
|
540
|
+
import type { FC } from "react";
|
|
541
|
+
import { useIntlayer } from "next-intlayer";
|
|
542
|
+
|
|
543
|
+
export const ComponentExample: FC = () => {
|
|
544
|
+
const content = useIntlayer("component-example"); // Переконайтесь, що у вас є відповідна декларація вмісту
|
|
545
|
+
|
|
546
|
+
return (
|
|
547
|
+
<div>
|
|
548
|
+
<h2>{content.title}</h2>
|
|
549
|
+
<p>{content.content}</p>
|
|
550
|
+
</div>
|
|
551
|
+
);
|
|
552
|
+
};
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
```jsx fileName="src/components/ComponentExample.mjx" codeFormat="esm"
|
|
556
|
+
import { useIntlayer } from "next-intlayer";
|
|
557
|
+
|
|
558
|
+
const ComponentExample = () => {
|
|
559
|
+
const content = useIntlayer("component-example"); // Переконайтесь, що у вас є відповідна декларація вмісту
|
|
560
|
+
|
|
561
|
+
return (
|
|
562
|
+
<div>
|
|
563
|
+
<h2>{content.title}</h2>
|
|
564
|
+
<p>{content.content}</p>
|
|
565
|
+
</div>
|
|
566
|
+
);
|
|
567
|
+
};
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
```jsx fileName="src/components/ComponentExample.csx" codeFormat="commonjs"
|
|
571
|
+
const { useIntlayer } = require("next-intlayer");
|
|
572
|
+
|
|
573
|
+
const ComponentExample = () => {
|
|
574
|
+
const content = useIntlayer("component-example"); // Переконайтеся, що у вас є відповідна декларація вмісту
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
<div>
|
|
578
|
+
<h2>{content.title}</h2>
|
|
579
|
+
<p>{content.content}</p>
|
|
580
|
+
</div>
|
|
581
|
+
);
|
|
582
|
+
};
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
> Коли ви використовуєте переклади в рядкових атрибутах (наприклад, `alt`, `title`, `href`, `aria-label`), викликайте
|
|
586
|
+
|
|
587
|
+
> значення функції таким чином:
|
|
588
|
+
|
|
589
|
+
> ```jsx
|
|
590
|
+
> <img src={content.image.src.value} alt={content.image.value} />
|
|
591
|
+
> ```
|
|
592
|
+
|
|
593
|
+
> Щоб дізнатися більше про хук `useIntlayer`, зверніться до [документації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useIntlayer.md).
|
|
594
|
+
|
|
595
|
+
### (Необов'язково) Крок 8: Інтернаціоналізація ваших метаданих
|
|
596
|
+
|
|
597
|
+
Якщо ви хочете інтернаціоналізувати метадані, наприклад заголовок вашої сторінки, ви можете використовувати функцію `getStaticProps`, яку надає Next.js Page Router. Всередині ви можете отримати контент за допомогою функції `getIntlayer`, щоб перекласти ваші метадані.
|
|
598
|
+
|
|
599
|
+
```typescript fileName="src/pages/[locale]/metadata.content.ts" contentDeclarationFormat="typescript"
|
|
600
|
+
import { type Dictionary, t } from "intlayer";
|
|
601
|
+
import { type Metadata } from "next";
|
|
602
|
+
|
|
603
|
+
const metadataContent = {
|
|
604
|
+
key: "page-metadata",
|
|
605
|
+
content: {
|
|
606
|
+
title: t({
|
|
607
|
+
uk: "Створити додаток Next.js",
|
|
608
|
+
en: "Create Next App",
|
|
609
|
+
uk: "Створити додаток Next.js",
|
|
610
|
+
fr: "Créer une application Next.js",
|
|
611
|
+
es: "Crear una aplicación Next.js",
|
|
612
|
+
}),
|
|
613
|
+
description: t({
|
|
614
|
+
uk: "Згенеровано за допомогою create next app",
|
|
615
|
+
en: "Generated by create next app",
|
|
616
|
+
fr: "Généré par create next app",
|
|
617
|
+
es: "Generado por create next app",
|
|
618
|
+
}),
|
|
619
|
+
},
|
|
620
|
+
} satisfies Dictionary<Metadata>;
|
|
621
|
+
|
|
622
|
+
export default metadataContent;
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
```javascript fileName="src/pages/[locale]/metadata.content.mjs" contentDeclarationFormat="esm"
|
|
626
|
+
import { t } from "intlayer";
|
|
627
|
+
|
|
628
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
629
|
+
const metadataContent = {
|
|
630
|
+
key: "page-metadata",
|
|
631
|
+
content: {
|
|
632
|
+
title: t({
|
|
633
|
+
uk: "Створити додаток Next.js",
|
|
634
|
+
en: "Create Next App",
|
|
635
|
+
fr: "Créer une application Next.js",
|
|
636
|
+
es: "Crear una aplicación Next.js",
|
|
637
|
+
}),
|
|
638
|
+
description: t({
|
|
639
|
+
uk: "Згенеровано за допомогою create next app",
|
|
640
|
+
en: "Generated by create next app",
|
|
641
|
+
uk: "Згенеровано за допомогою create next app",
|
|
642
|
+
fr: "Généré par create next app",
|
|
643
|
+
es: "Generado por create next app",
|
|
644
|
+
}),
|
|
645
|
+
},
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
export default metadataContent;
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
```javascript fileName="src/pages/[locale]/metadata.content.cjs" contentDeclarationFormat="commonjs"
|
|
652
|
+
const { t } = require("intlayer");
|
|
653
|
+
|
|
654
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
655
|
+
const metadataContent = {
|
|
656
|
+
key: "page-metadata",
|
|
657
|
+
content: {
|
|
658
|
+
title: t({
|
|
659
|
+
uk: "Створити додаток Next.js",
|
|
660
|
+
en: "Create Next App",
|
|
661
|
+
fr: "Créer une application Next.js",
|
|
662
|
+
es: "Crear una aplicación Next.js",
|
|
663
|
+
}),
|
|
664
|
+
description: t({
|
|
665
|
+
uk: "Згенеровано за допомогою create next app",
|
|
666
|
+
en: "Generated by create next app",
|
|
667
|
+
fr: "Généré par create next app",
|
|
668
|
+
es: "Generado por create next app",
|
|
669
|
+
}),
|
|
670
|
+
},
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
module.exports = metadataContent;
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
```json fileName="src/pages/[locale]/metadata.content.json" contentDeclarationFormat="json"
|
|
677
|
+
{
|
|
678
|
+
"key": "page-metadata",
|
|
679
|
+
"content": {
|
|
680
|
+
"title": {
|
|
681
|
+
"nodeType": "translation",
|
|
682
|
+
"translation": {
|
|
683
|
+
"uk": "Логотип Preact",
|
|
684
|
+
"en": "Preact logo",
|
|
685
|
+
"fr": "Logo Preact",
|
|
686
|
+
"es": "Logo Preact",
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
"description": {
|
|
690
|
+
"nodeType": "translation",
|
|
691
|
+
"translation": {
|
|
692
|
+
"uk": "Згенеровано за допомогою create next app",
|
|
693
|
+
"en": "Generated by create next app",
|
|
694
|
+
"fr": "Généré par create next app",
|
|
695
|
+
"es": "Generado por create next app",
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
````tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript"
|
|
703
|
+
import { GetStaticPaths, GetStaticProps } from "next";
|
|
704
|
+
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
705
|
+
import { useIntlayer } from "next-intlayer";
|
|
706
|
+
`
|
|
707
|
+
import Head from "next/head";
|
|
708
|
+
import type { FC } from "react";
|
|
709
|
+
|
|
710
|
+
interface HomePageProps {
|
|
711
|
+
locale: string;
|
|
712
|
+
metadata: {
|
|
713
|
+
title: string;
|
|
714
|
+
description: string;
|
|
715
|
+
};
|
|
716
|
+
multilingualUrls: Record<string, string>;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const HomePage: FC<HomePageProps> = ({
|
|
720
|
+
metadata,
|
|
721
|
+
multilingualUrls,
|
|
722
|
+
locale,
|
|
723
|
+
}) => {
|
|
724
|
+
const content = useIntlayer("page");
|
|
725
|
+
|
|
726
|
+
return (
|
|
727
|
+
<div>
|
|
728
|
+
<Head>
|
|
729
|
+
<title>{metadata.title}</title>
|
|
730
|
+
<meta name="description" content={metadata.description} />
|
|
731
|
+
{/* Генерує теги hreflang для SEO */}
|
|
732
|
+
{Object.entries(multilingualUrls).map(([lang, url]) => (
|
|
733
|
+
<link key={lang} rel="alternate" hrefLang={lang} href={url} />
|
|
734
|
+
))}
|
|
735
|
+
<link rel="canonical" href={multilingualUrls[locale]} />
|
|
736
|
+
</Head>
|
|
737
|
+
|
|
738
|
+
{/* Вміст сторінки */}
|
|
739
|
+
<main>{/* Ваш вміст сторінки тут */}</main>
|
|
740
|
+
</div>
|
|
741
|
+
);
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
export const getStaticProps: GetStaticProps<HomePageProps> = async ({
|
|
745
|
+
params,
|
|
746
|
+
}) => {
|
|
747
|
+
const locale = params?.locale as string;
|
|
748
|
+
|
|
749
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Генерує об'єкт, що містить усі URL для кожної локалі.
|
|
753
|
+
*
|
|
754
|
+
* Приклад:
|
|
755
|
+
* ```ts
|
|
756
|
+
* getMultilingualUrls('/about');
|
|
757
|
+
*
|
|
758
|
+
* // Повертає
|
|
759
|
+
* // {
|
|
760
|
+
* // en: '/about',
|
|
761
|
+
* // fr: '/fr/about',
|
|
762
|
+
* // es: '/es/about',
|
|
763
|
+
* // }
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
766
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
767
|
+
|
|
768
|
+
return {
|
|
769
|
+
props: {
|
|
770
|
+
locale,
|
|
771
|
+
metadata,
|
|
772
|
+
multilingualUrls,
|
|
773
|
+
},
|
|
774
|
+
};
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
export default HomePage;
|
|
778
|
+
|
|
779
|
+
// ... Решта коду, включаючи getStaticPaths
|
|
780
|
+
````
|
|
781
|
+
|
|
782
|
+
````jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm"
|
|
783
|
+
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
784
|
+
import { useIntlayer } from "next-intlayer";
|
|
785
|
+
import Head from "next/head";
|
|
786
|
+
|
|
787
|
+
const HomePage = ({ metadata, multilingualUrls, locale }) => {
|
|
788
|
+
const content = useIntlayer("page");
|
|
789
|
+
|
|
790
|
+
return (
|
|
791
|
+
<div>
|
|
792
|
+
<Head>
|
|
793
|
+
<title>{metadata.title}</title>
|
|
794
|
+
<meta name="description" content={metadata.description} />
|
|
795
|
+
{/* Генерує теги hreflang для SEO */}
|
|
796
|
+
{Object.entries(multilingualUrls).map(([lang, url]) => (
|
|
797
|
+
<link key={lang} rel="alternate" hrefLang={lang} href={url} />
|
|
798
|
+
))}
|
|
799
|
+
<link rel="canonical" href={multilingualUrls[locale]} />
|
|
800
|
+
</Head>
|
|
801
|
+
|
|
802
|
+
{/* Вміст сторінки */}
|
|
803
|
+
<main>{/* Ваш вміст сторінки тут */}</main>
|
|
804
|
+
</div>
|
|
805
|
+
);
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
export const getStaticProps = async ({ params }) => {
|
|
809
|
+
const locale = params?.locale;
|
|
810
|
+
|
|
811
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Генерує об'єкт, що містить усі URL для кожної локалі.
|
|
815
|
+
*
|
|
816
|
+
* Приклад:
|
|
817
|
+
* ```ts
|
|
818
|
+
* getMultilingualUrls('/about');
|
|
819
|
+
*
|
|
820
|
+
* // Повертає
|
|
821
|
+
* // {
|
|
822
|
+
* // en: '/about',
|
|
823
|
+
* // fr: '/fr/about',
|
|
824
|
+
* // es: '/es/about',
|
|
825
|
+
* // }
|
|
826
|
+
* ```
|
|
827
|
+
*/
|
|
828
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
829
|
+
|
|
830
|
+
return {
|
|
831
|
+
props: {
|
|
832
|
+
locale,
|
|
833
|
+
metadata,
|
|
834
|
+
multilingualUrls,
|
|
835
|
+
},
|
|
836
|
+
};
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
export default HomePage;
|
|
840
|
+
|
|
841
|
+
// ... Решта коду, включаючи getStaticPaths
|
|
842
|
+
````
|
|
843
|
+
|
|
844
|
+
````jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs"
|
|
845
|
+
const { getIntlayer, getMultilingualUrls } = require("intlayer");
|
|
846
|
+
const { useIntlayer } = require("next-intlayer");
|
|
847
|
+
const Head = require("next/head");
|
|
848
|
+
|
|
849
|
+
const HomePage = ({ metadata, multilingualUrls, locale }) => {
|
|
850
|
+
const content = useIntlayer("page");
|
|
851
|
+
|
|
852
|
+
return (
|
|
853
|
+
<div>
|
|
854
|
+
<Head>
|
|
855
|
+
<title>{metadata.title}</title>
|
|
856
|
+
<meta name="description" content={metadata.description} />
|
|
857
|
+
{/* Генерує теги hreflang для SEO */}
|
|
858
|
+
{Object.entries(multilingualUrls).map(([lang, url]) => (
|
|
859
|
+
<link key={lang} rel="alternate" hrefLang={lang} href={url} />
|
|
860
|
+
))}
|
|
861
|
+
<link rel="canonical" href={multilingualUrls[locale]} />
|
|
862
|
+
</Head>
|
|
863
|
+
|
|
864
|
+
{/* Вміст сторінки */}
|
|
865
|
+
<main>{/* Тут розмістіть контент сторінки */}</main>
|
|
866
|
+
</div>
|
|
867
|
+
);
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
const getStaticProps = async ({ params }) => {
|
|
871
|
+
const locale = params?.locale;
|
|
872
|
+
|
|
873
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Генерує об'єкт, що містить всі URL для кожної локалі.
|
|
877
|
+
*
|
|
878
|
+
* Приклад:
|
|
879
|
+
* ```ts
|
|
880
|
+
* getMultilingualUrls('/about');
|
|
881
|
+
*
|
|
882
|
+
* // Повертає
|
|
883
|
+
* // {
|
|
884
|
+
* // en: '/about',
|
|
885
|
+
* // fr: '/fr/about',
|
|
886
|
+
* // es: '/es/about',
|
|
887
|
+
* // }
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
891
|
+
|
|
892
|
+
return {
|
|
893
|
+
props: {
|
|
894
|
+
locale,
|
|
895
|
+
metadata,
|
|
896
|
+
multilingualUrls,
|
|
897
|
+
},
|
|
898
|
+
};
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
module.exports = {
|
|
902
|
+
getStaticProps,
|
|
903
|
+
getStaticPaths,
|
|
904
|
+
default: HomePage,
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
// ... Інші частини коду, включаючи getStaticPaths
|
|
908
|
+
````
|
|
909
|
+
|
|
910
|
+
> Зверніть увагу, що функція `getIntlayer`, імпортована з `next-intlayer`, повертає ваш контент, обгорнутий у `IntlayerNode`, що дозволяє інтеграцію з візуальним редактором. Натомість функція `getIntlayer`, імпортована з `intlayer`, повертає контент напряму без додаткових властивостей.
|
|
911
|
+
|
|
912
|
+
Альтернативно, ви можете використовувати функцію `getTranslation` для декларації ваших метаданих. Проте рекомендовано використовувати файли декларації контенту, щоб автоматизувати переклад метаданих та винести контент у зовнішні файли.
|
|
913
|
+
|
|
914
|
+
```tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript"
|
|
915
|
+
import { GetStaticPaths, GetStaticProps } from "next";
|
|
916
|
+
import {
|
|
917
|
+
type IConfigLocales,
|
|
918
|
+
getTranslation,
|
|
919
|
+
getMultilingualUrls,
|
|
920
|
+
} from "intlayer";
|
|
921
|
+
import { useIntlayer } from "next-intlayer";
|
|
922
|
+
import Head from "next/head";
|
|
923
|
+
import type { FC } from "react";
|
|
924
|
+
|
|
925
|
+
interface HomePageProps {
|
|
926
|
+
locale: string;
|
|
927
|
+
metadata: {
|
|
928
|
+
title: string;
|
|
929
|
+
description: string;
|
|
930
|
+
};
|
|
931
|
+
multilingualUrls: Record<string, string>;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const HomePage: FC<HomePageProps> = ({ metadata, multilingualUrls, locale }) => {
|
|
935
|
+
const content = useIntlayer("page");
|
|
936
|
+
|
|
937
|
+
return (
|
|
938
|
+
<div>
|
|
939
|
+
<Head>
|
|
940
|
+
<title>{metadata.title}</title>
|
|
941
|
+
<meta name="description" content={metadata.description} />
|
|
942
|
+
{/* Згенерувати теги hreflang для SEO */}
|
|
943
|
+
{Object.entries(multilingualUrls).map(([lang, url]) => (
|
|
944
|
+
<link
|
|
945
|
+
key={lang}
|
|
946
|
+
rel="alternate"
|
|
947
|
+
hrefLang={lang}
|
|
948
|
+
href={url}
|
|
949
|
+
/>
|
|
950
|
+
))}
|
|
951
|
+
<link rel="canonical" href={multilingualUrls[locale]} />
|
|
952
|
+
</Head>
|
|
953
|
+
|
|
954
|
+
{/* Вміст сторінки */}
|
|
955
|
+
<main>
|
|
956
|
+
{/* Ваш вміст сторінки тут */}
|
|
957
|
+
</main>
|
|
958
|
+
</div>
|
|
959
|
+
);
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
export const getStaticProps: GetStaticProps<HomePageProps> = async ({
|
|
963
|
+
params
|
|
964
|
+
}) => {
|
|
965
|
+
const locale = params?.locale as string;
|
|
966
|
+
const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);
|
|
967
|
+
|
|
968
|
+
const metadata = {
|
|
969
|
+
title: t<string>({
|
|
970
|
+
uk: "Мій заголовок",
|
|
971
|
+
en: "My title",
|
|
972
|
+
fr: "Mon titre",
|
|
973
|
+
es: "Mi título",
|
|
974
|
+
}),
|
|
975
|
+
description: t({
|
|
976
|
+
uk: "Мій опис",
|
|
977
|
+
en: "My description",
|
|
978
|
+
fr: "Ma description",
|
|
979
|
+
es: "Mi descripción",
|
|
980
|
+
}),
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
984
|
+
|
|
985
|
+
return {
|
|
986
|
+
props: {
|
|
987
|
+
locale,
|
|
988
|
+
metadata,
|
|
989
|
+
multilingualUrls,
|
|
990
|
+
},
|
|
991
|
+
};
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
export default HomePage;
|
|
995
|
+
|
|
996
|
+
// ... Rest of the code including getStaticPaths
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm"
|
|
1000
|
+
import { getTranslation, getMultilingualUrls } from "intlayer";
|
|
1001
|
+
import { useIntlayer } from "next-intlayer";
|
|
1002
|
+
import Head from "next/head";
|
|
1003
|
+
|
|
1004
|
+
const HomePage = ({ metadata, multilingualUrls, locale }) => {
|
|
1005
|
+
const content = useIntlayer("page");
|
|
1006
|
+
|
|
1007
|
+
return (
|
|
1008
|
+
<div>
|
|
1009
|
+
<Head>
|
|
1010
|
+
<title>{metadata.title}</title>
|
|
1011
|
+
<meta name="description" content={metadata.description} />
|
|
1012
|
+
{/* Генеруємо hreflang-теги для SEO */}
|
|
1013
|
+
{Object.entries(multilingualUrls).map(([lang, url]) => (
|
|
1014
|
+
<link key={lang} rel="alternate" hrefLang={lang} href={url} />
|
|
1015
|
+
))}
|
|
1016
|
+
<link rel="canonical" href={multilingualUrls[locale]} />
|
|
1017
|
+
</Head>
|
|
1018
|
+
|
|
1019
|
+
{/* Контент сторінки */}
|
|
1020
|
+
<main>{/* Вставте тут контент вашої сторінки */}</main>
|
|
1021
|
+
</div>
|
|
1022
|
+
);
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
export const getStaticProps = async ({ params }) => {
|
|
1026
|
+
const locale = params?.locale;
|
|
1027
|
+
const t = (content) => getTranslation(content, locale);
|
|
1028
|
+
|
|
1029
|
+
const metadata = {
|
|
1030
|
+
title: t({
|
|
1031
|
+
uk: "Мій заголовок",
|
|
1032
|
+
en: "My title",
|
|
1033
|
+
fr: "Mon titre",
|
|
1034
|
+
es: "Mi título",
|
|
1035
|
+
}),
|
|
1036
|
+
description: t({
|
|
1037
|
+
uk: "Мій опис",
|
|
1038
|
+
en: "My description",
|
|
1039
|
+
fr: "Ma description",
|
|
1040
|
+
es: "Mi descripción",
|
|
1041
|
+
}),
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
1045
|
+
|
|
1046
|
+
return {
|
|
1047
|
+
props: {
|
|
1048
|
+
locale,
|
|
1049
|
+
metadata,
|
|
1050
|
+
multilingualUrls,
|
|
1051
|
+
},
|
|
1052
|
+
};
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
export default HomePage;
|
|
1056
|
+
|
|
1057
|
+
// ... Решта коду, включно з getStaticPaths
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs"
|
|
1061
|
+
const { getTranslation, getMultilingualUrls } = require("intlayer");
|
|
1062
|
+
const { useIntlayer } = require("next-intlayer");
|
|
1063
|
+
const Head = require("next/head");
|
|
1064
|
+
|
|
1065
|
+
const HomePage = ({ metadata, multilingualUrls, locale }) => {
|
|
1066
|
+
const content = useIntlayer("page");
|
|
1067
|
+
|
|
1068
|
+
return (
|
|
1069
|
+
<div>
|
|
1070
|
+
<Head>
|
|
1071
|
+
<title>{metadata.title}</title>
|
|
1072
|
+
<meta name="description" content={metadata.description} />
|
|
1073
|
+
{/* Генерує теги hreflang для SEO */}
|
|
1074
|
+
{Object.entries(multilingualUrls).map(([lang, url]) => (
|
|
1075
|
+
<link key={lang} rel="alternate" hrefLang={lang} href={url} />
|
|
1076
|
+
))}
|
|
1077
|
+
<link rel="canonical" href={multilingualUrls[locale]} />
|
|
1078
|
+
</Head>
|
|
1079
|
+
|
|
1080
|
+
{/* Вміст сторінки */}
|
|
1081
|
+
<main>{/* Тут розмістіть вміст сторінки */}</main>
|
|
1082
|
+
</div>
|
|
1083
|
+
);
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
const getStaticProps = async ({ params }) => {
|
|
1087
|
+
const locale = params?.locale;
|
|
1088
|
+
const t = (content) => getTranslation(content, locale);
|
|
1089
|
+
|
|
1090
|
+
const metadata = {
|
|
1091
|
+
title: t({
|
|
1092
|
+
uk: "Мій заголовок",
|
|
1093
|
+
en: "My title",
|
|
1094
|
+
fr: "Mon titre",
|
|
1095
|
+
es: "Mi título",
|
|
1096
|
+
}),
|
|
1097
|
+
description: t({
|
|
1098
|
+
uk: "Мій опис",
|
|
1099
|
+
en: "My description",
|
|
1100
|
+
fr: "Ma description",
|
|
1101
|
+
es: "Mi descripción",
|
|
1102
|
+
}),
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
1106
|
+
|
|
1107
|
+
return {
|
|
1108
|
+
props: {
|
|
1109
|
+
locale,
|
|
1110
|
+
metadata,
|
|
1111
|
+
multilingualUrls,
|
|
1112
|
+
},
|
|
1113
|
+
};
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
module.exports = {
|
|
1117
|
+
getStaticProps,
|
|
1118
|
+
getStaticPaths,
|
|
1119
|
+
default: HomePage,
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
// ... Решта коду, включно з getStaticPaths
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
> Дізнайтеся більше про оптимізацію метаданих [в офіційній документації Next.js](https://nextjs.org/docs/pages/building-your-application/optimizing/metadata).
|
|
1126
|
+
|
|
1127
|
+
### (Необов'язково) Крок 9: Змініть мову вашого вмісту
|
|
1128
|
+
|
|
1129
|
+
Щоб змінити мову вашого вмісту в Next.js, рекомендовано використовувати компонент `Link` для перенаправлення користувачів на відповідну локалізовану сторінку. Компонент `Link` дає змогу передзавантажувати (prefetch) сторінку, що допомагає уникнути повного перезавантаження сторінки.
|
|
1130
|
+
|
|
1131
|
+
```tsx fileName="src/components/LanguageSwitcher.tsx" codeFormat="typescript"
|
|
1132
|
+
import {
|
|
1133
|
+
Locales,
|
|
1134
|
+
getHTMLTextDir,
|
|
1135
|
+
getLocaleName,
|
|
1136
|
+
getLocalizedUrl,
|
|
1137
|
+
} from "intlayer";
|
|
1138
|
+
import { useLocalePageRouter } from "next-intlayer";
|
|
1139
|
+
import { type FC } from "react";
|
|
1140
|
+
import Link from "next/link";
|
|
1141
|
+
|
|
1142
|
+
const LocaleSwitcher: FC = () => {
|
|
1143
|
+
const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter();
|
|
1144
|
+
|
|
1145
|
+
return (
|
|
1146
|
+
<div>
|
|
1147
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1148
|
+
<div id="localePopover" popover="auto">
|
|
1149
|
+
{availableLocales.map((localeItem) => (
|
|
1150
|
+
<Link
|
|
1151
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1152
|
+
hrefLang={localeItem}
|
|
1153
|
+
key={localeItem}
|
|
1154
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1155
|
+
onClick={() => setLocale(localeItem)}
|
|
1156
|
+
>
|
|
1157
|
+
<span>
|
|
1158
|
+
{/* Локаль — наприклад FR */}
|
|
1159
|
+
{localeItem}
|
|
1160
|
+
</span>
|
|
1161
|
+
<span>
|
|
1162
|
+
{/* Мова у власній локалі — наприклад Français */}
|
|
1163
|
+
{getLocaleName(localeItem, locale)}
|
|
1164
|
+
</span>
|
|
1165
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1166
|
+
{/* Мова в поточній локалі — наприклад Francés, коли поточна локаль встановлена на Locales.SPANISH */}
|
|
1167
|
+
{getLocaleName(localeItem)}
|
|
1168
|
+
</span>
|
|
1169
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1170
|
+
{/* Мова англійською — наприклад French */}
|
|
1171
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1172
|
+
</span>
|
|
1173
|
+
</Link>
|
|
1174
|
+
))}
|
|
1175
|
+
</div>
|
|
1176
|
+
</div>
|
|
1177
|
+
);
|
|
1178
|
+
};
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
```jsx fileName="src/components/LanguageSwitcher.msx" codeFormat="esm"
|
|
1182
|
+
import {
|
|
1183
|
+
Locales,
|
|
1184
|
+
getHTMLTextDir,
|
|
1185
|
+
getLocaleName,
|
|
1186
|
+
getLocalizedUrl,
|
|
1187
|
+
} from "intlayer";
|
|
1188
|
+
import { useLocalePageRouter } from "next-intlayer";
|
|
1189
|
+
import Link from "next/link";
|
|
1190
|
+
|
|
1191
|
+
const LocaleSwitcher = () => {
|
|
1192
|
+
const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter();
|
|
1193
|
+
|
|
1194
|
+
return (
|
|
1195
|
+
<div>
|
|
1196
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1197
|
+
<div id="localePopover" popover="auto">
|
|
1198
|
+
{availableLocales.map((localeItem) => (
|
|
1199
|
+
<Link
|
|
1200
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1201
|
+
hrefLang={localeItem}
|
|
1202
|
+
key={localeItem}
|
|
1203
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1204
|
+
onClick={() => setLocale(localeItem)}
|
|
1205
|
+
>
|
|
1206
|
+
<span>
|
|
1207
|
+
{/* Локаль - наприклад FR */}
|
|
1208
|
+
{localeItem}
|
|
1209
|
+
</span>
|
|
1210
|
+
<span>
|
|
1211
|
+
{/* Мова в власній локалі - наприклад Français */}
|
|
1212
|
+
{getLocaleName(localeItem, locale)}
|
|
1213
|
+
</span>
|
|
1214
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1215
|
+
{/* Мова в поточній локалі — наприклад, Francés при поточній локалі Locales.SPANISH */}
|
|
1216
|
+
{getLocaleName(localeItem)}
|
|
1217
|
+
</span>
|
|
1218
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1219
|
+
{/* Мова англійською — наприклад, French */}
|
|
1220
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1221
|
+
</span>
|
|
1222
|
+
</Link>
|
|
1223
|
+
))}
|
|
1224
|
+
</div>
|
|
1225
|
+
</div>
|
|
1226
|
+
);
|
|
1227
|
+
};
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
```jsx fileName="src/components/LanguageSwitcher.msx" codeFormat="commonjs"
|
|
1231
|
+
const {
|
|
1232
|
+
Locales,
|
|
1233
|
+
getHTMLTextDir,
|
|
1234
|
+
getLocaleName,
|
|
1235
|
+
getLocalizedUrl,
|
|
1236
|
+
} = require("intlayer");
|
|
1237
|
+
const { useLocalePageRouter } = require("next-intlayer");
|
|
1238
|
+
const Link = require("next/link");
|
|
1239
|
+
|
|
1240
|
+
const LocaleSwitcher = () => {
|
|
1241
|
+
const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter();
|
|
1242
|
+
|
|
1243
|
+
return (
|
|
1244
|
+
<select>
|
|
1245
|
+
{availableLocales.map((localeItem) => (
|
|
1246
|
+
<option value={localeItem} key={localeItem}>
|
|
1247
|
+
<Link
|
|
1248
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1249
|
+
hrefLang={localeItem}
|
|
1250
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1251
|
+
onClick={() => setLocale(localeItem)}
|
|
1252
|
+
>
|
|
1253
|
+
<span>
|
|
1254
|
+
{/* Локаль - наприклад FR */}
|
|
1255
|
+
{localeItem}
|
|
1256
|
+
</span>
|
|
1257
|
+
<span>
|
|
1258
|
+
{/* Мова у власній локалі - наприклад Français */}
|
|
1259
|
+
{getLocaleName(localeItem, locale)}
|
|
1260
|
+
</span>
|
|
1261
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1262
|
+
{/* Мова у поточній локалі - наприклад Francés коли поточна локаль встановлена на Locales.SPANISH */}
|
|
1263
|
+
{getLocaleName(localeItem)}
|
|
1264
|
+
</span>
|
|
1265
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1266
|
+
{/* Мова англійською - наприклад French */}
|
|
1267
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1268
|
+
</span>
|
|
1269
|
+
</Link>
|
|
1270
|
+
</option>
|
|
1271
|
+
))}
|
|
1272
|
+
</select>
|
|
1273
|
+
);
|
|
1274
|
+
};
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
> Альтернативний спосіб — використати функцію `setLocale`, яку надає хук `useLocale`. Ця функція не дозволяє попереднє завантаження (prefetch) сторінки і призведе до перезавантаження сторінки.
|
|
1278
|
+
|
|
1279
|
+
> У такому випадку, без перенаправлення через `router.push`, змінить локаль вмісту лише ваш серверний код.
|
|
1280
|
+
|
|
1281
|
+
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
1282
|
+
"use client";
|
|
1283
|
+
|
|
1284
|
+
import { useRouter } from "next/navigation";
|
|
1285
|
+
import { useLocale } from "next-intlayer";
|
|
1286
|
+
import { getLocalizedUrl } from "intlayer";
|
|
1287
|
+
|
|
1288
|
+
// ... Решта коду
|
|
1289
|
+
|
|
1290
|
+
const router = useRouter();
|
|
1291
|
+
const { setLocale } = useLocale({
|
|
1292
|
+
onLocaleChange: (locale) => {
|
|
1293
|
+
router.push(getLocalizedUrl(pathWithoutLocale, locale));
|
|
1294
|
+
},
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
return (
|
|
1298
|
+
<button onClick={() => setLocale(Locales.FRENCH)}>Change to French</button>
|
|
1299
|
+
);
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
> API `useLocalePageRouter` такий же, як `useLocale`. Щоб дізнатися більше про хук `useLocale`, зверніться до [документації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useLocale.md).
|
|
1303
|
+
|
|
1304
|
+
> Посилання на документацію:
|
|
1305
|
+
>
|
|
1306
|
+
> - [`getLocaleName` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getLocaleName.md)
|
|
1307
|
+
> - [`getLocalizedUrl` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getLocalizedUrl.md)
|
|
1308
|
+
> - [`getHTMLTextDir` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getHTMLTextDir.md)
|
|
1309
|
+
> - [`hrefLang` атрибут](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)
|
|
1310
|
+
> - [`lang` атрибут](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang)
|
|
1311
|
+
> - [`dir` атрибут](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir)
|
|
1312
|
+
> - [`aria-current` атрибут](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
|
|
1313
|
+
|
|
1314
|
+
### (Необов'язково) Крок 10: Створення локалізованого компонента Link
|
|
1315
|
+
|
|
1316
|
+
Щоб гарантувати, що навігація вашого застосунку враховує поточну локаль, ви можете створити кастомний компонент `Link`. Цей компонент автоматично додає префікс поточної мови до внутрішніх URL-адрес, так щоб, наприклад, коли франкомовний користувач натискає на посилання на сторінку "About", він буде перенаправлений на `/fr/about` замість `/about`.
|
|
1317
|
+
|
|
1318
|
+
Ця поведінка корисна з кількох причин:
|
|
1319
|
+
|
|
1320
|
+
- **SEO та досвід користувача**: локалізовані URL-адреси допомагають пошуковим системам правильно індексувати сторінки для конкретних мов і надають користувачам контент їхньою переважною мовою.
|
|
1321
|
+
- **Послідовність**: Використовуючи локалізоване посилання в усьому вашому застосунку, ви гарантуєте, що навігація залишатиметься в межах поточної локалі, запобігаючи несподіваним змінам мови.
|
|
1322
|
+
- **Підтримуваність**: Централізація логіки локалізації в одному компоненті спрощує управління URL-адресами, роблячи вашу codebase простішою для підтримки та розширення у міру зростання застосунку.
|
|
1323
|
+
|
|
1324
|
+
Нижче наведено реалізацію локалізованого компонента `Link` на TypeScript:
|
|
1325
|
+
|
|
1326
|
+
```tsx fileName="src/components/Link.tsx" codeFormat="typescript"
|
|
1327
|
+
"use client";
|
|
1328
|
+
|
|
1329
|
+
import { getLocalizedUrl } from "intlayer";
|
|
1330
|
+
import NextLink, { type LinkProps as NextLinkProps } from "next/link";
|
|
1331
|
+
import { useLocale } from "next-intlayer";
|
|
1332
|
+
import { forwardRef, PropsWithChildren, type ForwardedRef } from "react";
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* Утилітна функція для перевірки, чи є заданий URL зовнішнім.
|
|
1336
|
+
* Якщо URL починається з http:// або https://, він вважається зовнішнім.
|
|
1337
|
+
*/
|
|
1338
|
+
export const checkIsExternalLink = (href?: string): boolean =>
|
|
1339
|
+
/^https?:\/\//.test(href ?? "");
|
|
1340
|
+
|
|
1341
|
+
/**
|
|
1342
|
+
* Кастомний компонент Link, який адаптує атрибут href залежно від поточної локалі.
|
|
1343
|
+
* Для внутрішніх посилань використовується `getLocalizedUrl`, щоб додати префікс локалі до URL (наприклад, /fr/about).
|
|
1344
|
+
* Це гарантує, що навігація залишатиметься в межах одного мовного контексту.
|
|
1345
|
+
*/
|
|
1346
|
+
export const Link = forwardRef<
|
|
1347
|
+
HTMLAnchorElement,
|
|
1348
|
+
PropsWithChildren<NextLinkProps>
|
|
1349
|
+
>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => {
|
|
1350
|
+
const { locale } = useLocale();
|
|
1351
|
+
const isExternalLink = checkIsExternalLink(href.toString());
|
|
1352
|
+
|
|
1353
|
+
// Якщо посилання внутрішнє й надано дійсний href, отримати локалізований URL.
|
|
1354
|
+
const hrefI18n: NextLinkProps["href"] =
|
|
1355
|
+
href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
|
|
1356
|
+
|
|
1357
|
+
return (
|
|
1358
|
+
<NextLink href={hrefI18n} ref={ref} {...props}>
|
|
1359
|
+
{children}
|
|
1360
|
+
</NextLink>
|
|
1361
|
+
);
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1364
|
+
Link.displayName = "Link";
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
```jsx fileName="src/components/Link.mjx" codeFormat="esm"
|
|
1368
|
+
'use client';
|
|
1369
|
+
|
|
1370
|
+
import { getLocalizedUrl } from 'intlayer';
|
|
1371
|
+
import NextLink, { type LinkProps as NextLinkProps } from 'next/link';
|
|
1372
|
+
import { useLocale } from 'next-intlayer';
|
|
1373
|
+
import { forwardRef, PropsWithChildren, type ForwardedRef } from 'react';
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Утилітна функція для перевірки, чи є заданий URL зовнішнім.
|
|
1377
|
+
* Якщо URL починається з http:// або https://, він вважається зовнішнім.
|
|
1378
|
+
*/
|
|
1379
|
+
export const checkIsExternalLink = (href) =>
|
|
1380
|
+
/^https?:\/\//.test(href ?? '');
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Користувацький компонент Link, який адаптує атрибут href залежно від поточної локалі.
|
|
1384
|
+
* Для внутрішніх посилань він використовує `getLocalizedUrl`, щоб додати префікс локалі до URL (наприклад, /fr/about).
|
|
1385
|
+
* Це забезпечує, що навігація відбувається в межах тієї самої локалі.
|
|
1386
|
+
*/
|
|
1387
|
+
export const Link = forwardRef(({ href, children, ...props }, ref) => {
|
|
1388
|
+
const { locale } = useLocale();
|
|
1389
|
+
const isExternalLink = checkIsExternalLink(href.toString());
|
|
1390
|
+
|
|
1391
|
+
// Якщо посилання внутрішнє і надано валідний href, отримати локалізований URL.
|
|
1392
|
+
const hrefI18n =
|
|
1393
|
+
href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
|
|
1394
|
+
|
|
1395
|
+
return (
|
|
1396
|
+
<NextLink href={hrefI18n} ref={ref} {...props}>
|
|
1397
|
+
{children}
|
|
1398
|
+
</NextLink>
|
|
1399
|
+
);
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
Link.displayName = 'Link';
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
```jsx fileName="src/components/Link.csx" codeFormat="commonjs"
|
|
1406
|
+
'use client';
|
|
1407
|
+
|
|
1408
|
+
const { getLocalizedUrl } = require("intlayer");
|
|
1409
|
+
const NextLink = require("next/link");
|
|
1410
|
+
const { useLocale } = require("next-intlayer");
|
|
1411
|
+
const { forwardRef } = require("react");
|
|
1412
|
+
|
|
1413
|
+
/**
|
|
1414
|
+
* Утилітна функція для перевірки, чи є заданий URL зовнішнім.
|
|
1415
|
+
* Якщо URL починається з http:// або https://, він вважається зовнішнім.
|
|
1416
|
+
*/
|
|
1417
|
+
const checkIsExternalLink = (href) =>
|
|
1418
|
+
/^https?:\/\//.test(href ?? '');
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
const Link = forwardRef(({ href, children, ...props }, ref) => {
|
|
1422
|
+
const { locale } = useLocale();
|
|
1423
|
+
const isExternalLink = checkIsExternalLink(href.toString());
|
|
1424
|
+
|
|
1425
|
+
// Якщо посилання внутрішнє і вказано дійсний href, отримаємо локалізований URL.
|
|
1426
|
+
const hrefI18n: NextLinkProps['href'] =
|
|
1427
|
+
href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
|
|
1428
|
+
|
|
1429
|
+
return (
|
|
1430
|
+
<NextLink href={hrefI18n} ref={ref} {...props}>
|
|
1431
|
+
{children}
|
|
1432
|
+
</NextLink>
|
|
1433
|
+
);
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
Link.displayName = 'Link';
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
#### Як це працює
|
|
1440
|
+
|
|
1441
|
+
- **Виявлення зовнішніх посилань**:
|
|
1442
|
+
Допоміжна функція `checkIsExternalLink` визначає, чи є URL зовнішнім. Зовнішні посилання залишаються без змін, оскільки їх не потрібно локалізувати.
|
|
1443
|
+
|
|
1444
|
+
- **Отримання поточної локалі**:
|
|
1445
|
+
Хук `useLocale` повертає поточну локаль (наприклад, `fr` для французької).
|
|
1446
|
+
|
|
1447
|
+
- **Локалізація URL**:
|
|
1448
|
+
Для внутрішніх посилань (тобто не зовнішніх) використовується `getLocalizedUrl`, яка автоматично додає префікс поточної локалі до URL. Це означає, що якщо ваш користувач перебуває у французькій локалі, передавання `/about` як `href` перетвориться на `/fr/about`.
|
|
1449
|
+
|
|
1450
|
+
- **Повернення посилання**:
|
|
1451
|
+
Компонент повертає елемент `<a>` з локалізованим URL, що забезпечує узгоджену навігацію відповідно до локалі.
|
|
1452
|
+
|
|
1453
|
+
Інтегруючи цей компонент `Link` у ваш додаток, ви підтримуєте послідовний і орієнтований на мову досвід користувача, а також отримуєте переваги у вигляді покращеного SEO та зручності використання.
|
|
1454
|
+
|
|
1455
|
+
### (Необов'язково) Крок 11: Оптимізуйте розмір bundle
|
|
1456
|
+
|
|
1457
|
+
Коли використовується `next-intlayer`, словники за замовчуванням включаються в бандл для кожної сторінки. Щоб оптимізувати розмір бандла, Intlayer надає опційний SWC-плагін, який розумно замінює виклики `useIntlayer` за допомогою макросів. Це гарантує, що словники включаються в бандли лише для сторінок, які дійсно їх використовують.
|
|
1458
|
+
|
|
1459
|
+
Щоб увімкнути цю оптимізацію, встановіть пакет `@intlayer/swc`. Після встановлення `next-intlayer` автоматично виявить і використовуватиме плагін:
|
|
1460
|
+
|
|
1461
|
+
```bash packageManager="npm"
|
|
1462
|
+
npm install @intlayer/swc --save-dev
|
|
1463
|
+
npx intlayer init
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
```bash packageManager="pnpm"
|
|
1467
|
+
pnpm add @intlayer/swc --save-dev
|
|
1468
|
+
pnpm intlayer init
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
```bash packageManager="yarn"
|
|
1472
|
+
yarn add @intlayer/swc --save-dev
|
|
1473
|
+
yarn intlayer init
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
```bash packageManager="bun"
|
|
1477
|
+
bun add @intlayer/swc --dev
|
|
1478
|
+
bunx intlayer init
|
|
1479
|
+
```
|
|
1480
|
+
|
|
1481
|
+
> Примітка: Ця оптимізація доступна лише для Next.js 13 та новіших версій.
|
|
1482
|
+
|
|
1483
|
+
> Примітка: Цей пакет не встановлюється за замовчуванням, оскільки SWC-плагіни все ще є експериментальними в Next.js. Це може змінитися в майбутньому.
|
|
1484
|
+
|
|
1485
|
+
### Налаштування TypeScript
|
|
1486
|
+
|
|
1487
|
+
Intlayer використовує module augmentation, щоб отримати переваги TypeScript і зробити вашу codebase більш надійною.
|
|
1488
|
+
|
|
1489
|
+

|
|
1490
|
+
|
|
1491
|
+

|
|
1492
|
+
|
|
1493
|
+
Переконайтеся, що у вашій конфігурації TypeScript включені автогенеровані типи.
|
|
1494
|
+
|
|
1495
|
+
```json5 fileName="tsconfig.json"
|
|
1496
|
+
{
|
|
1497
|
+
// ... Ваші існуючі конфігурації TypeScript
|
|
1498
|
+
"include": [
|
|
1499
|
+
// ... Ваші існуючі конфігурації TypeScript
|
|
1500
|
+
".intlayer/**/*.ts", // Включити автогенеровані типи
|
|
1501
|
+
],
|
|
1502
|
+
}
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1505
|
+
### Налаштування Git
|
|
1506
|
+
|
|
1507
|
+
Щоб зберегти репозиторій чистим і уникнути коміту згенерованих файлів, рекомендується ігнорувати файли, створені Intlayer.
|
|
1508
|
+
|
|
1509
|
+
Додайте наступні рядки до вашого файлу `.gitignore`:
|
|
1510
|
+
|
|
1511
|
+
```plaintext fileName=".gitignore"
|
|
1512
|
+
# Ігнорувати файли, згенеровані Intlayer
|
|
1513
|
+
.intlayer
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
### Розширення для VS Code
|
|
1517
|
+
|
|
1518
|
+
Щоб покращити ваш досвід розробки з Intlayer, ви можете встановити офіційне **Intlayer VS Code Extension**.
|
|
1519
|
+
|
|
1520
|
+
[Встановити з VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1521
|
+
|
|
1522
|
+
Це розширення надає:
|
|
1523
|
+
|
|
1524
|
+
- **Автодоповнення** для ключів перекладу.
|
|
1525
|
+
- **Виявлення помилок у реальному часі** для відсутніх перекладів.
|
|
1526
|
+
- **Вбудовані попередні перегляди** перекладеного вмісту.
|
|
1527
|
+
- **Швидкі дії** для легкого створення та оновлення перекладів.
|
|
1528
|
+
|
|
1529
|
+
Для детальнішої інформації про використання розширення див. [Документація розширення Intlayer для VS Code](https://intlayer.org/doc/vs-code-extension).
|
|
1530
|
+
|
|
1531
|
+
## Додаткові ресурси
|
|
1532
|
+
|
|
1533
|
+
- **Документація Intlayer:** [Репозиторій GitHub](https://github.com/aymericzip/intlayer)
|
|
1534
|
+
- **Посібник словника:** [Словник](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md)
|
|
1535
|
+
- **Документація з конфігурації:** [Керівництво з конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md)
|
|
1536
|
+
|
|
1537
|
+
Дотримуючись цього посібника, ви можете ефективно інтегрувати Intlayer у ваш Next.js застосунок, що використовує Page Router, забезпечивши надійну та масштабовану підтримку інтернаціоналізації для ваших веб‑проєктів.
|
|
1538
|
+
|
|
1539
|
+
### Далі
|
|
1540
|
+
|
|
1541
|
+
Щоб піти далі, ви можете реалізувати [візуальний редактор](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).
|