@intlayer/docs 7.5.10 → 7.5.12
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/intlayer_with_i18next.md +0 -2
- package/blog/ar/intlayer_with_next-i18next.md +0 -2
- package/blog/ar/intlayer_with_react-i18next.md +0 -2
- package/blog/de/intlayer_with_i18next.md +0 -45
- package/blog/de/intlayer_with_next-i18next.md +0 -46
- package/blog/de/intlayer_with_react-i18next.md +0 -2
- package/blog/en/intlayer_with_i18next.md +0 -46
- package/blog/en/intlayer_with_next-i18next.md +0 -48
- package/blog/en/intlayer_with_next-intl.md +0 -44
- package/blog/en/intlayer_with_react-i18next.md +0 -44
- package/blog/en/intlayer_with_react-intl.md +0 -42
- package/blog/en/intlayer_with_vue-i18n.md +0 -44
- package/blog/en-GB/intlayer_with_i18next.md +0 -45
- package/blog/en-GB/intlayer_with_next-i18next.md +0 -47
- package/blog/en-GB/intlayer_with_next-intl.md +0 -42
- package/blog/en-GB/intlayer_with_react-i18next.md +0 -43
- package/blog/en-GB/intlayer_with_react-intl.md +0 -42
- package/blog/en-GB/intlayer_with_vue-i18n.md +0 -46
- package/blog/es/intlayer_with_i18next.md +0 -45
- package/blog/es/intlayer_with_next-i18next.md +0 -47
- package/blog/es/intlayer_with_next-intl.md +0 -42
- package/blog/es/intlayer_with_react-i18next.md +0 -43
- package/blog/es/intlayer_with_react-intl.md +0 -42
- package/blog/es/intlayer_with_vue-i18n.md +0 -46
- package/blog/fr/intlayer_with_i18next.md +0 -45
- package/blog/fr/intlayer_with_next-i18next.md +0 -47
- package/blog/fr/intlayer_with_next-intl.md +0 -42
- package/blog/fr/intlayer_with_react-i18next.md +0 -43
- package/blog/fr/intlayer_with_react-intl.md +0 -42
- package/blog/fr/intlayer_with_vue-i18n.md +0 -46
- package/blog/hi/intlayer_with_i18next.md +0 -2
- package/blog/hi/intlayer_with_next-i18next.md +0 -2
- package/blog/hi/intlayer_with_react-i18next.md +0 -2
- package/blog/id/intlayer_with_i18next.md +0 -2
- package/blog/id/intlayer_with_next-i18next.md +0 -2
- package/blog/id/intlayer_with_react-i18next.md +0 -2
- package/blog/it/intlayer_with_i18next.md +0 -2
- package/blog/it/intlayer_with_next-i18next.md +0 -2
- package/blog/it/intlayer_with_react-i18next.md +0 -2
- package/blog/ja/intlayer_with_i18next.md +0 -45
- package/blog/ja/intlayer_with_next-i18next.md +0 -46
- package/blog/ja/intlayer_with_next-intl.md +0 -42
- package/blog/ja/intlayer_with_react-i18next.md +0 -42
- package/blog/ja/intlayer_with_react-intl.md +0 -42
- package/blog/ja/intlayer_with_vue-i18n.md +0 -46
- package/blog/ko/intlayer_with_i18next.md +0 -2
- package/blog/ko/intlayer_with_next-i18next.md +0 -2
- package/blog/ko/intlayer_with_react-i18next.md +0 -1
- package/blog/pl/intlayer_with_i18next.md +0 -45
- package/blog/pl/intlayer_with_next-i18next.md +0 -46
- package/blog/pl/intlayer_with_next-intl.md +0 -42
- package/blog/pl/intlayer_with_react-i18next.md +0 -43
- package/blog/pl/intlayer_with_react-intl.md +0 -42
- package/blog/pl/intlayer_with_vue-i18n.md +0 -46
- package/blog/pt/intlayer_with_i18next.md +0 -2
- package/blog/pt/intlayer_with_next-i18next.md +0 -2
- package/blog/pt/intlayer_with_react-i18next.md +0 -2
- package/blog/ru/intlayer_with_i18next.md +0 -45
- package/blog/ru/intlayer_with_next-i18next.md +0 -47
- package/blog/ru/intlayer_with_next-intl.md +0 -42
- package/blog/ru/intlayer_with_react-i18next.md +0 -43
- package/blog/ru/intlayer_with_react-intl.md +0 -42
- package/blog/ru/intlayer_with_vue-i18n.md +0 -46
- package/blog/tr/intlayer_with_i18next.md +0 -2
- package/blog/tr/intlayer_with_next-i18next.md +0 -1
- package/blog/tr/intlayer_with_react-i18next.md +0 -2
- package/blog/vi/intlayer_with_i18next.md +0 -2
- package/blog/vi/intlayer_with_next-i18next.md +0 -2
- package/blog/vi/intlayer_with_react-i18next.md +0 -2
- package/blog/zh/intlayer_with_i18next.md +0 -2
- package/blog/zh/intlayer_with_next-i18next.md +0 -2
- package/blog/zh/intlayer_with_react-i18next.md +0 -2
- package/blog/zh/intlayer_with_vue-i18n.md +0 -46
- package/dist/cjs/generated/blog.entry.cjs +58 -29
- package/dist/cjs/generated/blog.entry.cjs.map +1 -1
- package/dist/cjs/generated/docs.entry.cjs +254 -97
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/cjs/generated/frequentQuestions.entry.cjs +30 -15
- package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
- package/dist/cjs/generated/legal.entry.cjs +4 -2
- package/dist/cjs/generated/legal.entry.cjs.map +1 -1
- package/dist/esm/generated/blog.entry.mjs +58 -29
- package/dist/esm/generated/blog.entry.mjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +254 -97
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +30 -15
- package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
- package/dist/esm/generated/legal.entry.mjs +4 -2
- package/dist/esm/generated/legal.entry.mjs.map +1 -1
- package/dist/types/generated/blog.entry.d.ts.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +3 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
- package/dist/types/generated/legal.entry.d.ts.map +1 -1
- package/docs/ar/cli/ci.md +137 -0
- package/docs/ar/cli/index.md +7 -1
- package/docs/ar/cli/list.md +39 -2
- package/docs/ar/cli/list_projects.md +131 -0
- package/docs/ar/intlayer_CMS.md +20 -0
- package/docs/ar/intlayer_with_next-i18next.md +0 -1
- package/docs/ar/intlayer_with_nextjs_14.md +28 -0
- package/docs/ar/intlayer_with_nextjs_15.md +28 -0
- package/docs/ar/intlayer_with_nextjs_16.md +28 -0
- package/docs/ar/intlayer_with_nextjs_no_locale_path.md +1159 -0
- package/docs/ar/plugins/sync-json.md +6 -2
- package/docs/de/cli/ci.md +137 -0
- package/docs/de/cli/index.md +7 -1
- package/docs/de/cli/list.md +39 -2
- package/docs/de/cli/list_projects.md +130 -0
- package/docs/de/intlayer_CMS.md +20 -0
- package/docs/de/intlayer_with_next-i18next.md +0 -1
- package/docs/de/intlayer_with_nextjs_14.md +28 -0
- package/docs/de/intlayer_with_nextjs_15.md +28 -0
- package/docs/de/intlayer_with_nextjs_16.md +28 -0
- package/docs/de/intlayer_with_nextjs_no_locale_path.md +1152 -0
- package/docs/de/plugins/sync-json.md +6 -2
- package/docs/en/cli/ci.md +137 -0
- package/docs/en/cli/index.md +14 -1
- package/docs/en/cli/list.md +39 -2
- package/docs/en/cli/list_projects.md +128 -0
- package/docs/en/interest_of_intlayer.md +1 -1
- package/docs/en/intlayer_CMS.md +20 -0
- package/docs/en/intlayer_with_next-i18next.md +0 -1
- package/docs/en/intlayer_with_nextjs_14.md +28 -0
- package/docs/en/intlayer_with_nextjs_15.md +28 -0
- package/docs/en/intlayer_with_nextjs_16.md +31 -1
- package/docs/en/intlayer_with_nextjs_no_locale_path.md +1132 -0
- package/docs/en/plugins/sync-json.md +6 -2
- package/docs/en-GB/cli/ci.md +137 -0
- package/docs/en-GB/cli/index.md +7 -1
- package/docs/en-GB/cli/list.md +39 -2
- package/docs/en-GB/cli/list_projects.md +130 -0
- package/docs/en-GB/intlayer_CMS.md +20 -0
- package/docs/en-GB/intlayer_with_next-i18next.md +0 -1
- package/docs/en-GB/intlayer_with_nextjs_14.md +28 -0
- package/docs/en-GB/intlayer_with_nextjs_15.md +28 -0
- package/docs/en-GB/intlayer_with_nextjs_16.md +28 -0
- package/docs/en-GB/intlayer_with_nextjs_no_locale_path.md +1154 -0
- package/docs/en-GB/plugins/sync-json.md +6 -2
- package/docs/es/cli/ci.md +137 -0
- package/docs/es/cli/index.md +7 -1
- package/docs/es/cli/list.md +39 -2
- package/docs/es/cli/list_projects.md +130 -0
- package/docs/es/intlayer_CMS.md +20 -0
- package/docs/es/intlayer_with_next-i18next.md +0 -1
- package/docs/es/intlayer_with_nextjs_14.md +28 -0
- package/docs/es/intlayer_with_nextjs_15.md +28 -0
- package/docs/es/intlayer_with_nextjs_16.md +28 -0
- package/docs/es/intlayer_with_nextjs_no_locale_path.md +1143 -0
- package/docs/es/plugins/sync-json.md +6 -2
- package/docs/fr/cli/ci.md +137 -0
- package/docs/fr/cli/index.md +7 -1
- package/docs/fr/cli/list.md +39 -2
- package/docs/fr/cli/list_projects.md +131 -0
- package/docs/fr/intlayer_CMS.md +20 -0
- package/docs/fr/intlayer_with_next-i18next.md +0 -1
- package/docs/fr/intlayer_with_nextjs_14.md +28 -0
- package/docs/fr/intlayer_with_nextjs_15.md +28 -0
- package/docs/fr/intlayer_with_nextjs_16.md +28 -0
- package/docs/fr/intlayer_with_nextjs_no_locale_path.md +1174 -0
- package/docs/fr/plugins/sync-json.md +9 -5
- package/docs/hi/cli/ci.md +137 -0
- package/docs/hi/cli/index.md +7 -1
- package/docs/hi/cli/list.md +38 -1
- package/docs/hi/cli/list_projects.md +130 -0
- package/docs/hi/intlayer_CMS.md +20 -0
- package/docs/hi/intlayer_with_next-i18next.md +0 -1
- package/docs/hi/intlayer_with_nextjs_14.md +28 -0
- package/docs/hi/intlayer_with_nextjs_15.md +28 -0
- package/docs/hi/intlayer_with_nextjs_16.md +28 -0
- package/docs/hi/intlayer_with_nextjs_no_locale_path.md +1151 -0
- package/docs/hi/plugins/sync-json.md +6 -2
- package/docs/id/cli/ci.md +137 -0
- package/docs/id/cli/index.md +7 -1
- package/docs/id/cli/list.md +38 -1
- package/docs/id/cli/list_projects.md +128 -0
- package/docs/id/intlayer_CMS.md +20 -0
- package/docs/id/intlayer_with_next-i18next.md +0 -1
- package/docs/id/intlayer_with_nextjs_14.md +28 -0
- package/docs/id/intlayer_with_nextjs_15.md +28 -0
- package/docs/id/intlayer_with_nextjs_16.md +28 -0
- package/docs/id/intlayer_with_nextjs_no_locale_path.md +1154 -0
- package/docs/id/plugins/sync-json.md +6 -2
- package/docs/it/cli/ci.md +137 -0
- package/docs/it/cli/index.md +7 -1
- package/docs/it/cli/list.md +39 -2
- package/docs/it/cli/list_projects.md +130 -0
- package/docs/it/intlayer_CMS.md +20 -0
- package/docs/it/intlayer_with_next-i18next.md +0 -1
- package/docs/it/intlayer_with_nextjs_14.md +28 -0
- package/docs/it/intlayer_with_nextjs_15.md +28 -0
- package/docs/it/intlayer_with_nextjs_16.md +28 -0
- package/docs/it/intlayer_with_nextjs_no_locale_path.md +1148 -0
- package/docs/it/plugins/sync-json.md +6 -2
- package/docs/ja/cli/ci.md +137 -0
- package/docs/ja/cli/index.md +7 -1
- package/docs/ja/cli/list.md +38 -1
- package/docs/ja/cli/list_projects.md +136 -0
- package/docs/ja/intlayer_CMS.md +20 -0
- package/docs/ja/intlayer_with_next-i18next.md +0 -1
- package/docs/ja/intlayer_with_nextjs_14.md +28 -0
- package/docs/ja/intlayer_with_nextjs_15.md +28 -0
- package/docs/ja/intlayer_with_nextjs_16.md +28 -0
- package/docs/ja/intlayer_with_nextjs_no_locale_path.md +1222 -0
- package/docs/ja/plugins/sync-json.md +6 -2
- package/docs/ko/cli/ci.md +137 -0
- package/docs/ko/cli/index.md +7 -1
- package/docs/ko/cli/list.md +38 -1
- package/docs/ko/cli/list_projects.md +128 -0
- package/docs/ko/intlayer_CMS.md +20 -0
- package/docs/ko/intlayer_with_next-i18next.md +0 -1
- package/docs/ko/intlayer_with_nextjs_14.md +28 -0
- package/docs/ko/intlayer_with_nextjs_15.md +28 -0
- package/docs/ko/intlayer_with_nextjs_16.md +28 -0
- package/docs/ko/intlayer_with_nextjs_no_locale_path.md +1205 -0
- package/docs/ko/plugins/sync-json.md +6 -2
- package/docs/pl/cli/ci.md +137 -0
- package/docs/pl/cli/index.md +7 -1
- package/docs/pl/cli/list.md +39 -2
- package/docs/pl/cli/list_projects.md +130 -0
- package/docs/pl/intlayer_CMS.md +20 -0
- package/docs/pl/intlayer_with_next-i18next.md +0 -1
- package/docs/pl/intlayer_with_nextjs_14.md +28 -0
- package/docs/pl/intlayer_with_nextjs_15.md +28 -0
- package/docs/pl/intlayer_with_nextjs_16.md +28 -0
- package/docs/pl/intlayer_with_nextjs_no_locale_path.md +1149 -0
- package/docs/pl/plugins/sync-json.md +6 -2
- package/docs/pt/cli/ci.md +137 -0
- package/docs/pt/cli/index.md +7 -1
- package/docs/pt/cli/list.md +39 -2
- package/docs/pt/cli/list_projects.md +134 -0
- package/docs/pt/intlayer_CMS.md +20 -0
- package/docs/pt/intlayer_with_next-i18next.md +0 -1
- package/docs/pt/intlayer_with_nextjs_14.md +28 -0
- package/docs/pt/intlayer_with_nextjs_15.md +28 -0
- package/docs/pt/intlayer_with_nextjs_16.md +28 -0
- package/docs/pt/intlayer_with_nextjs_no_locale_path.md +1152 -0
- package/docs/pt/plugins/sync-json.md +6 -2
- package/docs/ru/cli/ci.md +137 -0
- package/docs/ru/cli/index.md +7 -1
- package/docs/ru/cli/list.md +39 -2
- package/docs/ru/cli/list_projects.md +130 -0
- package/docs/ru/intlayer_CMS.md +20 -0
- package/docs/ru/intlayer_with_next-i18next.md +0 -1
- package/docs/ru/intlayer_with_nextjs_14.md +28 -0
- package/docs/ru/intlayer_with_nextjs_15.md +28 -0
- package/docs/ru/intlayer_with_nextjs_16.md +28 -0
- package/docs/ru/intlayer_with_nextjs_no_locale_path.md +1204 -0
- package/docs/ru/plugins/sync-json.md +6 -2
- package/docs/tr/cli/ci.md +137 -0
- package/docs/tr/cli/index.md +7 -1
- package/docs/tr/cli/list.md +39 -2
- package/docs/tr/cli/list_projects.md +134 -0
- package/docs/tr/intlayer_CMS.md +20 -0
- package/docs/tr/intlayer_with_next-i18next.md +0 -1
- package/docs/tr/intlayer_with_nextjs_14.md +28 -0
- package/docs/tr/intlayer_with_nextjs_15.md +28 -0
- package/docs/tr/intlayer_with_nextjs_16.md +28 -0
- package/docs/tr/intlayer_with_nextjs_no_locale_path.md +1159 -0
- package/docs/tr/plugins/sync-json.md +6 -2
- package/docs/uk/compiler.md +133 -0
- package/docs/uk/component_i18n.md +194 -0
- package/docs/uk/intlayer_with_nextjs_14.md +1646 -0
- package/docs/uk/intlayer_with_nextjs_15.md +1910 -0
- package/docs/uk/intlayer_with_nextjs_16.md +1763 -0
- package/docs/uk/intlayer_with_nextjs_no_locale_path.md +1159 -0
- package/docs/uk/intlayer_with_react_native+expo.md +715 -0
- package/docs/uk/packages/intlayer/getConfiguration.md +145 -0
- package/docs/uk/vs_code_extension.md +133 -0
- package/docs/vi/cli/ci.md +137 -0
- package/docs/vi/cli/index.md +7 -1
- package/docs/vi/cli/list.md +38 -1
- package/docs/vi/cli/list_projects.md +130 -0
- package/docs/vi/intlayer_CMS.md +20 -0
- package/docs/vi/intlayer_with_next-i18next.md +0 -1
- package/docs/vi/intlayer_with_nextjs_14.md +28 -0
- package/docs/vi/intlayer_with_nextjs_15.md +28 -0
- package/docs/vi/intlayer_with_nextjs_16.md +28 -0
- package/docs/vi/intlayer_with_nextjs_no_locale_path.md +1151 -0
- package/docs/vi/plugins/sync-json.md +6 -2
- package/docs/zh/cli/ci.md +137 -0
- package/docs/zh/cli/index.md +7 -1
- package/docs/zh/cli/list.md +38 -1
- package/docs/zh/cli/list_projects.md +130 -0
- package/docs/zh/intlayer_CMS.md +20 -0
- package/docs/zh/intlayer_with_next-i18next.md +0 -1
- package/docs/zh/intlayer_with_nextjs_14.md +28 -0
- package/docs/zh/intlayer_with_nextjs_15.md +28 -0
- package/docs/zh/intlayer_with_nextjs_16.md +28 -0
- package/docs/zh/intlayer_with_nextjs_no_locale_path.md +1206 -0
- package/docs/zh/plugins/sync-json.md +9 -5
- package/frequent_questions/ar/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/de/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/en/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/en-GB/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/es/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/fr/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/hi/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/id/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/it/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ja/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ko/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/pl/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/pt/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ru/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/tr/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/vi/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/zh/SSR_Next_no_[locale].md +1 -1
- package/package.json +6 -6
- package/src/generated/blog.entry.ts +29 -0
- package/src/generated/docs.entry.ts +157 -0
- package/src/generated/frequentQuestions.entry.ts +15 -0
- package/src/generated/legal.entry.ts +2 -0
|
@@ -0,0 +1,1159 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-01-10
|
|
3
|
+
updatedAt: 2026-01-10
|
|
4
|
+
title: Як перекласти ваш додаток Next.js 16 (без [locale] у шляху сторінки) – посібник з i18n 2026
|
|
5
|
+
description: Дізнайтеся, як зробити ваш сайт Next.js 16 багатомовним без [locale] у шляху сторінки. Дотримуйтесь документації, щоб інтернаціоналізувати (i18n) його та перекласти.
|
|
6
|
+
keywords:
|
|
7
|
+
- Internationalization
|
|
8
|
+
- Documentation
|
|
9
|
+
- Intlayer
|
|
10
|
+
- Next.js 16
|
|
11
|
+
- JavaScript
|
|
12
|
+
- React
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- environment
|
|
16
|
+
- nextjs
|
|
17
|
+
- no-locale-path
|
|
18
|
+
applicationTemplate: https://github.com/aymericzip/intlayer-next-no-lolale-path-template
|
|
19
|
+
youtubeVideo: https://www.youtube.com/watch?v=e_PPG7PTqGU
|
|
20
|
+
history:
|
|
21
|
+
- version: 1.0.0
|
|
22
|
+
date: 2026-01-10
|
|
23
|
+
changes: Початковий випуск
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Перекладіть ваш сайт Next.js 16 (без [locale] у шляху сторінки) за допомогою Intlayer | Інтернаціоналізація (i18n)
|
|
27
|
+
|
|
28
|
+
<Tab defaultTab="video">
|
|
29
|
+
<TabItem label="Відео" value="video">
|
|
30
|
+
|
|
31
|
+
<iframe title="Найкраще i18n-рішення для Next.js? Дізнайтеся про Intlayer" class="m-auto aspect-16/9 w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/e_PPG7PTqGU?autoplay=0&origin=http://intlayer.org&controls=0&rel=1"/>
|
|
32
|
+
|
|
33
|
+
</TabItem>
|
|
34
|
+
<TabItem label="Код" value="code">
|
|
35
|
+
|
|
36
|
+
<iframe
|
|
37
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-next-16-no-locale-path-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
38
|
+
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
39
|
+
title="Демо CodeSandbox - Як інтернаціоналізувати ваш додаток за допомогою Intlayer"
|
|
40
|
+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
41
|
+
loading="lazy"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
</TabItem>
|
|
45
|
+
</Tab>
|
|
46
|
+
|
|
47
|
+
Дивіться [Шаблон додатка](https://github.com/aymericzip/intlayer-next-no-lolale-path-template) на GitHub.
|
|
48
|
+
|
|
49
|
+
## Зміст
|
|
50
|
+
|
|
51
|
+
<TOC/>
|
|
52
|
+
|
|
53
|
+
## Що таке Intlayer?
|
|
54
|
+
|
|
55
|
+
**Intlayer** — інноваційна відкрита бібліотека інтернаціоналізації (i18n), створена для спрощення підтримки багатомовності в сучасних вебзастосунках. Intlayer безшовно інтегрується з останньою версією фреймворку **Next.js 16**, включно з його потужним **App Router**. Вона оптимізована для роботи з **Server Components** для ефективного рендерингу і повністю сумісна з [**Turbopack**](https://nextjs.org/docs/architecture/turbopack).
|
|
56
|
+
|
|
57
|
+
З Intlayer ви можете:
|
|
58
|
+
|
|
59
|
+
- **Легко керувати перекладами**, використовуючи декларативні словники на рівні компонентів.
|
|
60
|
+
- **Динамічно локалізувати метадані**, маршрути та контент.
|
|
61
|
+
- **Отримувати доступ до перекладів як у клієнтських, так і в серверних компонентах**.
|
|
62
|
+
- **Забезпечити підтримку TypeScript** за допомогою автозгенерованих типів, що покращує автозаповнення та виявлення помилок.
|
|
63
|
+
- **Отримуйте переваги розширених можливостей**, таких як динамічне визначення та перемикання локалі.
|
|
64
|
+
|
|
65
|
+
> Intlayer сумісний з Next.js 12, 13, 14 та 16. Якщо ви використовуєте Next.js Page Router, зверніться до цього [посібника](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_with_nextjs_page_router.md). Для Next.js 12, 13, 14 з App Router зверніться до цього [посібника](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_with_nextjs_14.md).
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Покроковий посібник зі встановлення Intlayer у застосунку Next.js
|
|
70
|
+
|
|
71
|
+
### Крок 1: Встановлення залежностей
|
|
72
|
+
|
|
73
|
+
Встановіть необхідні пакети за допомогою npm:
|
|
74
|
+
|
|
75
|
+
```bash packageManager="npm"
|
|
76
|
+
npm install intlayer next-intlayer
|
|
77
|
+
npx intlayer init
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```bash packageManager="pnpm"
|
|
81
|
+
pnpm add intlayer next-intlayer
|
|
82
|
+
pnpm intlayer init
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```bash packageManager="yarn"
|
|
86
|
+
yarn add intlayer next-intlayer
|
|
87
|
+
yarn intlayer init
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash packageManager="bun"
|
|
91
|
+
bun add intlayer next-intlayer
|
|
92
|
+
bunx intlayer init
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **intlayer**
|
|
96
|
+
|
|
97
|
+
Основний пакет, який надає інструменти інтернаціоналізації для управління конфігурацією, перекладів, [оголошення контенту](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).
|
|
98
|
+
|
|
99
|
+
- **next-intlayer**
|
|
100
|
+
|
|
101
|
+
Пакет, який інтегрує Intlayer з Next.js. Він надає провайдери контексту та хуки для інтернаціоналізації в Next.js. Крім того, він включає плагін для Next.js для інтеграції Intlayer з [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack), а також proxy для визначення переважної локалі користувача, керування cookie та обробки перенаправлень URL.
|
|
102
|
+
|
|
103
|
+
### Крок 2: Налаштуйте ваш проєкт
|
|
104
|
+
|
|
105
|
+
Ось фінальна структура, яку ми створимо:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
.
|
|
109
|
+
├── src
|
|
110
|
+
│ ├── app
|
|
111
|
+
│ │ ├── layout.tsx
|
|
112
|
+
│ │ ├── page.content.ts
|
|
113
|
+
│ │ └── page.tsx
|
|
114
|
+
│ ├── components
|
|
115
|
+
│ │ ├── clientComponentExample
|
|
116
|
+
│ │ │ ├── client-component-example.content.ts
|
|
117
|
+
│ │ │ └── ClientComponentExample.tsx
|
|
118
|
+
│ │ ├── localeSwitcher
|
|
119
|
+
│ │ │ ├── localeSwitcher.content.ts
|
|
120
|
+
│ │ │ └── LocaleSwitcher.tsx
|
|
121
|
+
│ │ └── serverComponentExample
|
|
122
|
+
│ │ ├── server-component-example.content.ts
|
|
123
|
+
│ │ └── ServerComponentExample.tsx
|
|
124
|
+
│ └── proxy.ts
|
|
125
|
+
├── intlayer.config.ts
|
|
126
|
+
├── next.config.ts
|
|
127
|
+
├── package.json
|
|
128
|
+
└── tsconfig.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
> Якщо ви не хочете маршрутизацію за локалями, intlayer можна використовувати як простий provider / hook. Дивіться [цей посібник](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_with_nextjs_no_locale_path.md) для детальнішої інформації.
|
|
132
|
+
|
|
133
|
+
Створіть конфігураційний файл для налаштування мов вашого застосунку:
|
|
134
|
+
|
|
135
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
136
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
137
|
+
|
|
138
|
+
const config: IntlayerConfig = {
|
|
139
|
+
internationalization: {
|
|
140
|
+
locales: [
|
|
141
|
+
Locales.ENGLISH,
|
|
142
|
+
Locales.FRENCH,
|
|
143
|
+
Locales.SPANISH,
|
|
144
|
+
// Ваші інші локалі
|
|
145
|
+
],
|
|
146
|
+
defaultLocale: Locales.ENGLISH,
|
|
147
|
+
},
|
|
148
|
+
routing: {
|
|
149
|
+
mode: "search-params", // або `no-prefix` - Корисно для виявлення в middleware
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export default config;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```javascript fileName="intlayer.config.mjs" codeFormat="esm"
|
|
157
|
+
import { Locales } from "intlayer";
|
|
158
|
+
|
|
159
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
160
|
+
const config = {
|
|
161
|
+
internationalization: {
|
|
162
|
+
locales: [
|
|
163
|
+
Locales.ENGLISH,
|
|
164
|
+
Locales.FRENCH,
|
|
165
|
+
Locales.SPANISH,
|
|
166
|
+
// Ваші інші локалі
|
|
167
|
+
],
|
|
168
|
+
defaultLocale: Locales.ENGLISH,
|
|
169
|
+
},
|
|
170
|
+
routing: {
|
|
171
|
+
mode: "search-params", // або `no-prefix` - Корисно для виявлення в middleware
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export default config;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
179
|
+
const { Locales } = require("intlayer");
|
|
180
|
+
|
|
181
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
182
|
+
const config = {
|
|
183
|
+
internationalization: {
|
|
184
|
+
locales: [
|
|
185
|
+
Locales.ENGLISH,
|
|
186
|
+
Locales.FRENCH,
|
|
187
|
+
Locales.SPANISH,
|
|
188
|
+
// Ваші інші локалі
|
|
189
|
+
],
|
|
190
|
+
defaultLocale: Locales.ENGLISH,
|
|
191
|
+
},
|
|
192
|
+
routing: {
|
|
193
|
+
mode: "search-params", // або `no-prefix` - корисно для виявлення в middleware
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
module.exports = config;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
> Через цей файл конфігурації ви можете налаштувати локалізовані URL-адреси, перенаправлення через проксі, назви cookie, розташування та розширення декларацій вашого контенту, відключити логи Intlayer у консолі та інше. Для повного списку доступних параметрів зверніться до [документації з конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md).
|
|
201
|
+
|
|
202
|
+
### Крок 3: Інтегруйте Intlayer у конфігурацію Next.js
|
|
203
|
+
|
|
204
|
+
Налаштуйте ваш Next.js для використання Intlayer:
|
|
205
|
+
|
|
206
|
+
```typescript fileName="next.config.ts" codeFormat="typescript"
|
|
207
|
+
import type { NextConfig } from "next";
|
|
208
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
209
|
+
|
|
210
|
+
const nextConfig: NextConfig = {
|
|
211
|
+
/* опції конфігурації тут */
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export default withIntlayer(nextConfig);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```typescript fileName="next.config.mjs" codeFormat="esm"
|
|
218
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
219
|
+
|
|
220
|
+
/** @type {import('next').NextConfig} */
|
|
221
|
+
const nextConfig = {
|
|
222
|
+
/* опції конфігурації тут */
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export default withIntlayer(nextConfig);
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```typescript fileName="next.config.cjs" codeFormat="commonjs"
|
|
229
|
+
const { withIntlayer } = require("next-intlayer/server");
|
|
230
|
+
|
|
231
|
+
/** @type {import('next').NextConfig} */
|
|
232
|
+
const nextConfig = {
|
|
233
|
+
/* опції конфігурації тут */
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
module.exports = withIntlayer(nextConfig);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
> Плагін Next.js `withIntlayer()` використовується для інтеграції Intlayer з Next.js. Він забезпечує побудову файлів декларацій вмісту та відстежує їх у режимі розробки. Він визначає змінні оточення Intlayer у середовищах [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack). Додатково він надає aliases для оптимізації продуктивності та забезпечує сумісність із server components.
|
|
240
|
+
>
|
|
241
|
+
> Функція `withIntlayer()` повертає Promise. Вона дозволяє підготувати словники Intlayer перед початком збірки. Якщо ви хочете використовувати її разом з іншими плагінами, ви можете дочекатися її виконання за допомогою await. Приклад:
|
|
242
|
+
>
|
|
243
|
+
> ```ts
|
|
244
|
+
> const nextConfig = await withIntlayer(nextConfig);
|
|
245
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
246
|
+
>
|
|
247
|
+
> export default nextConfigWithOtherPlugins;
|
|
248
|
+
> ```
|
|
249
|
+
>
|
|
250
|
+
> Якщо ви хочете використовувати його синхронно, ви можете використати функцію `withIntlayerSync()`. Приклад:
|
|
251
|
+
>
|
|
252
|
+
> ```ts
|
|
253
|
+
> const nextConfig = withIntlayerSync(nextConfig);
|
|
254
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
255
|
+
>
|
|
256
|
+
> export default nextConfigWithOtherPlugins;
|
|
257
|
+
> ```
|
|
258
|
+
|
|
259
|
+
> Intlayer автоматично визначає, чи ваш проєкт використовує **webpack** чи **Turbopack** на основі прапорів командного рядка `--webpack`, `--turbo`, або `--turbopack`, а також вашої поточної версії **Next.js**.
|
|
260
|
+
>
|
|
261
|
+
> Оскільки `next>=16`, якщо ви використовуєте **Rspack**, ви повинні явно змусити Intlayer використовувати конфігурацію webpack, вимкнувши Turbopack:
|
|
262
|
+
>
|
|
263
|
+
> ```ts
|
|
264
|
+
> withRspack(withIntlayer(nextConfig, { enableTurbopack: false }));
|
|
265
|
+
> ```
|
|
266
|
+
|
|
267
|
+
### Крок 4: Визначте динамічні маршрути локалей
|
|
268
|
+
|
|
269
|
+
Видаліть усе з `RootLayout` і замініть на наступний код:
|
|
270
|
+
|
|
271
|
+
```tsx {3} fileName="src/app/layout.tsx" codeFormat="typescript"
|
|
272
|
+
import type { Metadata } from "next";
|
|
273
|
+
import type { ReactNode } from "react";
|
|
274
|
+
import "./globals.css";
|
|
275
|
+
import { IntlayerClientProvider, LocalPromiseParams } from "next-intlayer";
|
|
276
|
+
import { getHTMLTextDir, getIntlayer } from "intlayer";
|
|
277
|
+
import { getLocale } from "next-intlayer/server";
|
|
278
|
+
export { generateStaticParams } from "next-intlayer";
|
|
279
|
+
|
|
280
|
+
export const generateMetadata = async ({
|
|
281
|
+
params,
|
|
282
|
+
}: LocalPromiseParams): Promise<Metadata> => {
|
|
283
|
+
const { locale } = await params;
|
|
284
|
+
const { title, description, keywords } = getIntlayer("metadata", locale);
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
title,
|
|
288
|
+
description,
|
|
289
|
+
keywords,
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const RootLayout = async ({
|
|
294
|
+
children,
|
|
295
|
+
}: Readonly<{
|
|
296
|
+
children: ReactNode;
|
|
297
|
+
}>) => {
|
|
298
|
+
const locale = await getLocale();
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
302
|
+
<IntlayerClientProvider defaultLocale={locale}>
|
|
303
|
+
<body>{children}</body>
|
|
304
|
+
</IntlayerClientProvider>
|
|
305
|
+
</html>
|
|
306
|
+
);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export default RootLayout;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
```jsx {3} fileName="src/app/layout.mjx" codeFormat="esm"
|
|
313
|
+
import "./globals.css";
|
|
314
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
315
|
+
import { getHTMLTextDir, getIntlayer } from "intlayer";
|
|
316
|
+
import { getLocale } from "next-intlayer/server";
|
|
317
|
+
export { generateStaticParams } from "next-intlayer";
|
|
318
|
+
|
|
319
|
+
export const generateMetadata = async ({ params }) => {
|
|
320
|
+
const { locale } = await params;
|
|
321
|
+
const { title, description, keywords } = getIntlayer("metadata", locale);
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
title,
|
|
325
|
+
description,
|
|
326
|
+
keywords,
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const RootLayout = async ({ children }) => {
|
|
331
|
+
const locale = await getLocale();
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
335
|
+
<IntlayerClientProvider defaultLocale={locale}>
|
|
336
|
+
<body>{children}</body>
|
|
337
|
+
</IntlayerClientProvider>
|
|
338
|
+
</html>
|
|
339
|
+
);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export default RootLayout;
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
```jsx {1,8} fileName="src/app/layout.csx" codeFormat="commonjs"
|
|
346
|
+
require("./globals.css");
|
|
347
|
+
const { IntlayerClientProvider } = require("next-intlayer");
|
|
348
|
+
const { getHTMLTextDir, getIntlayer } = require("intlayer");
|
|
349
|
+
const { getLocale } = require("next-intlayer/server");
|
|
350
|
+
const { generateStaticParams } = require("next-intlayer");
|
|
351
|
+
|
|
352
|
+
const generateMetadata = async ({ params }) => {
|
|
353
|
+
const { locale } = await params;
|
|
354
|
+
const { title, description, keywords } = getIntlayer("metadata", locale);
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
title,
|
|
358
|
+
description,
|
|
359
|
+
keywords,
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const RootLayout = async ({ children }) => {
|
|
364
|
+
const locale = await getLocale();
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
368
|
+
<IntlayerClientProvider defaultLocale={locale}>
|
|
369
|
+
<body>{children}</body>
|
|
370
|
+
</IntlayerClientProvider>
|
|
371
|
+
</html>
|
|
372
|
+
);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
module.exports = {
|
|
376
|
+
default: RootLayout,
|
|
377
|
+
generateStaticParams,
|
|
378
|
+
generateMetadata,
|
|
379
|
+
};
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Крок 5: Оголосіть свій контент
|
|
383
|
+
|
|
384
|
+
Створюйте та керуйте деклараціями контенту для збереження перекладів:
|
|
385
|
+
|
|
386
|
+
```tsx fileName="src/app/metadata.content.ts" contentDeclarationFormat="typescript"
|
|
387
|
+
import { t, type Dictionary } from "intlayer";
|
|
388
|
+
import { Metadata } from "next";
|
|
389
|
+
|
|
390
|
+
const metadataContent = {
|
|
391
|
+
key: "metadata",
|
|
392
|
+
content: {
|
|
393
|
+
title: t({
|
|
394
|
+
uk: "Назва мого проєкту",
|
|
395
|
+
en: "My Project Title",
|
|
396
|
+
fr: "Le Titre de mon Projet",
|
|
397
|
+
es: "El Título de mi Proyecto",
|
|
398
|
+
}),
|
|
399
|
+
|
|
400
|
+
description: t({
|
|
401
|
+
uk: "Відкрийте для себе нашу інноваційну платформу, розроблену для оптимізації робочого процесу та підвищення продуктивності.",
|
|
402
|
+
en: "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
403
|
+
fr: "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
404
|
+
es: "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad.",
|
|
405
|
+
}),
|
|
406
|
+
|
|
407
|
+
keywords: t({
|
|
408
|
+
uk: ["інновації", "продуктивність", "робочий процес", "SaaS"],
|
|
409
|
+
en: ["innovation", "productivity", "workflow", "SaaS"],
|
|
410
|
+
uk: ["інновації", "продуктивність", "робочий процес", "SaaS"],
|
|
411
|
+
fr: ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
412
|
+
es: ["innovación", "productividad", "flujo de trabajo", "SaaS"],
|
|
413
|
+
}),
|
|
414
|
+
},
|
|
415
|
+
} as Dictionary<Metadata>;
|
|
416
|
+
|
|
417
|
+
export default metadataContent;
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
```tsx fileName="src/app/metadata.content.mjs" contentDeclarationFormat="typescript"
|
|
421
|
+
import { t, type Dictionary } from "intlayer";
|
|
422
|
+
|
|
423
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
424
|
+
const metadataContent = {
|
|
425
|
+
key: "metadata",
|
|
426
|
+
content: {
|
|
427
|
+
title: t({
|
|
428
|
+
uk: "Назва мого проєкту",
|
|
429
|
+
en: "My Project Title",
|
|
430
|
+
fr: "Le Titre de mon Projet",
|
|
431
|
+
es: "El Título de mi Proyecto",
|
|
432
|
+
}),
|
|
433
|
+
|
|
434
|
+
description: t({
|
|
435
|
+
uk: "Відкрийте для себе нашу інноваційну платформу, створену для оптимізації робочого процесу та підвищення продуктивності.",
|
|
436
|
+
en: "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
437
|
+
uk: "Відкрийте для себе нашу інноваційну платформу, створену для оптимізації вашого робочого процесу та підвищення продуктивності.",
|
|
438
|
+
fr: "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
439
|
+
es: "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad.",
|
|
440
|
+
}),
|
|
441
|
+
|
|
442
|
+
keywords: t({
|
|
443
|
+
uk: ["інновація", "продуктивність", "робочий процес", "SaaS"],
|
|
444
|
+
en: ["innovation", "productivity", "workflow", "SaaS"],
|
|
445
|
+
fr: ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
446
|
+
es: ["innovación", "productividad", "flujo de trabajo", "SaaS"],
|
|
447
|
+
}),
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
export default metadataContent;
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
```javascript fileName="src/app/metadata.content.cjs" contentDeclarationFormat="commonjs"
|
|
455
|
+
const { t } = require("intlayer");
|
|
456
|
+
|
|
457
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
458
|
+
const metadataContent = {
|
|
459
|
+
key: "metadata",
|
|
460
|
+
content: {
|
|
461
|
+
title: t({
|
|
462
|
+
uk: "Назва мого проєкту",
|
|
463
|
+
en: "My Project Title",
|
|
464
|
+
fr: "Le Titre de mon Projet",
|
|
465
|
+
es: "El Título de mi Proyecto",
|
|
466
|
+
}),
|
|
467
|
+
|
|
468
|
+
description: t({
|
|
469
|
+
uk: "Відкрийте для себе нашу інноваційну платформу, створену для оптимізації робочих процесів і підвищення продуктивності.",
|
|
470
|
+
en: "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
471
|
+
fr: "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
472
|
+
es: "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad.",
|
|
473
|
+
}),
|
|
474
|
+
|
|
475
|
+
keywords: t({
|
|
476
|
+
uk: ["інновації", "продуктивність", "робочий процес", "SaaS"],
|
|
477
|
+
en: ["innovation", "productivity", "workflow", "SaaS"],
|
|
478
|
+
fr: ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
479
|
+
es: ["innovación", "productividad", "flujo de trabajo", "SaaS"],
|
|
480
|
+
}),
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
module.exports = metadataContent;
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
```json fileName="src/app/metadata.content.json" contentDeclarationFormat="json"
|
|
488
|
+
{
|
|
489
|
+
"key": "metadata",
|
|
490
|
+
"content": {
|
|
491
|
+
"title": {
|
|
492
|
+
"nodeType": "translation",
|
|
493
|
+
"translation": {
|
|
494
|
+
"uk": "Назва мого проєкту",
|
|
495
|
+
"en": "My Project Title",
|
|
496
|
+
"fr": "Le Titre de mon Projet",
|
|
497
|
+
"es": "El Título de mi Proyecto"
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
"description": {
|
|
501
|
+
"nodeType": "translation",
|
|
502
|
+
"translation": {
|
|
503
|
+
"uk": "Відкрийте для себе нашу інноваційну платформу, створену для оптимізації вашого робочого процесу та підвищення продуктивності.",
|
|
504
|
+
"en": "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
505
|
+
"fr": "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
506
|
+
"es": "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad."
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
"keywords": {
|
|
512
|
+
"nodeType": "translation",
|
|
513
|
+
"translation": {
|
|
514
|
+
"uk": ["інновації", "продуктивність", "робочий процес", "SaaS"],
|
|
515
|
+
"en": ["innovation", "productivity", "workflow", "SaaS"],
|
|
516
|
+
"fr": ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
517
|
+
"es": ["innovación", "productividad", "flujo de trabajo", "SaaS"]
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
````
|
|
525
|
+
|
|
526
|
+
```tsx fileName="src/app/page.content.ts" contentDeclarationFormat="typescript"
|
|
527
|
+
import { t, type Dictionary } from "intlayer";
|
|
528
|
+
|
|
529
|
+
const pageContent = {
|
|
530
|
+
key: "page",
|
|
531
|
+
content: {
|
|
532
|
+
getStarted: {
|
|
533
|
+
main: t({
|
|
534
|
+
uk: "Почніть з редагування",
|
|
535
|
+
en: "Get started by editing",
|
|
536
|
+
fr: "Commencez par éditer",
|
|
537
|
+
es: "Comience por editar",
|
|
538
|
+
}),
|
|
539
|
+
pageLink: "src/app/page.tsx",
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
} satisfies Dictionary;
|
|
543
|
+
|
|
544
|
+
export default pageContent;
|
|
545
|
+
````
|
|
546
|
+
|
|
547
|
+
```javascript fileName="src/app/page.content.mjs" contentDeclarationFormat="esm"
|
|
548
|
+
import { t } from "intlayer";
|
|
549
|
+
|
|
550
|
+
/** @type {import('intlayer').Dictionary} */
|
|
551
|
+
const pageContent = {
|
|
552
|
+
key: "page",
|
|
553
|
+
content: {
|
|
554
|
+
getStarted: {
|
|
555
|
+
main: t({
|
|
556
|
+
uk: "Почніть з редагування",
|
|
557
|
+
en: "Get started by editing",
|
|
558
|
+
fr: "Commencez par éditer",
|
|
559
|
+
es: "Comience por editar",
|
|
560
|
+
}),
|
|
561
|
+
pageLink: "src/app/page.tsx",
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
export default pageContent;
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
```javascript fileName="src/app/page.content.cjs" contentDeclarationFormat="commonjs"
|
|
570
|
+
const { t } = require("intlayer");
|
|
571
|
+
|
|
572
|
+
/** @type {import('intlayer').Dictionary} */
|
|
573
|
+
const pageContent = {
|
|
574
|
+
key: "page",
|
|
575
|
+
content: {
|
|
576
|
+
getStarted: {
|
|
577
|
+
main: t({
|
|
578
|
+
uk: "Почніть з редагування",
|
|
579
|
+
en: "Get started by editing",
|
|
580
|
+
fr: "Commencez par éditer",
|
|
581
|
+
uk: "Почніть з редагування",
|
|
582
|
+
es: "Comience por editar",
|
|
583
|
+
}),
|
|
584
|
+
pageLink: "src/app/page.tsx",
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
module.exports = pageContent;
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
```json fileName="src/app/page.content.json" contentDeclarationFormat="json"
|
|
593
|
+
{
|
|
594
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
595
|
+
"key": "page",
|
|
596
|
+
"content": {
|
|
597
|
+
"getStarted": {
|
|
598
|
+
"nodeType": "translation",
|
|
599
|
+
"translation": {
|
|
600
|
+
"uk": "Почніть з редагування",
|
|
601
|
+
"en": "Get started by editing",
|
|
602
|
+
"fr": "Commencez par éditer",
|
|
603
|
+
"es": "Comience por editar"
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
"pageLink": "src/app/page.tsx"
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
> Ваші декларації вмісту можуть бути визначені будь-де у вашому додатку, як тільки вони будуть додані до директорії `contentDir` (за замовчуванням `./src`). І вони повинні відповідати розширенню файлу декларації вмісту (за замовчуванням `.content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}`).
|
|
612
|
+
|
|
613
|
+
> Для докладнішої інформації див. [документацію щодо декларації вмісту](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md).
|
|
614
|
+
|
|
615
|
+
### Крок 6: Використання вмісту у вашому коді
|
|
616
|
+
|
|
617
|
+
Отримуйте доступ до ваших словників вмісту по всьому додатку:
|
|
618
|
+
|
|
619
|
+
```tsx fileName="src/app/page.tsx" codeFormat="typescript"
|
|
620
|
+
import type { FC } from "react";
|
|
621
|
+
import { ClientComponentExample } from "@components/clientComponentExample/ClientComponentExample";
|
|
622
|
+
import { ServerComponentExample } from "@components/serverComponentExample/ServerComponentExample";
|
|
623
|
+
import {
|
|
624
|
+
IntlayerServerProvider,
|
|
625
|
+
useIntlayer,
|
|
626
|
+
getLocale,
|
|
627
|
+
} from "next-intlayer/server";
|
|
628
|
+
import { NextPage } from "next";
|
|
629
|
+
import { headers, cookies } from "next/headers";
|
|
630
|
+
|
|
631
|
+
const PageContent: FC = () => {
|
|
632
|
+
const content = useIntlayer("page");
|
|
633
|
+
|
|
634
|
+
return (
|
|
635
|
+
<>
|
|
636
|
+
<p>{content.getStarted.main}</p>
|
|
637
|
+
<code>{content.getStarted.pageLink}</code>
|
|
638
|
+
</>
|
|
639
|
+
);
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const Page: NextPage = async () => {
|
|
643
|
+
const locale = await getLocale();
|
|
644
|
+
|
|
645
|
+
return (
|
|
646
|
+
<IntlayerServerProvider locale={locale}>
|
|
647
|
+
<PageContent />
|
|
648
|
+
<ServerComponentExample />
|
|
649
|
+
<ClientComponentExample />
|
|
650
|
+
</IntlayerServerProvider>
|
|
651
|
+
);
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
export default Page;
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
```jsx fileName="src/app/page.mjx" codeFormat="esm"
|
|
658
|
+
import { ClientComponentExample } from "@components/clientComponentExample/ClientComponentExample";
|
|
659
|
+
import { ServerComponentExample } from "@components/serverComponentExample/ServerComponentExample";
|
|
660
|
+
import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
|
|
661
|
+
import { getLocale } from "intlayer";
|
|
662
|
+
import { headers, cookies } from "next/headers";
|
|
663
|
+
import { NextPage } from "next";
|
|
664
|
+
|
|
665
|
+
const Page: NextPage = async () => {
|
|
666
|
+
const content = useIntlayer("page");
|
|
667
|
+
|
|
668
|
+
return (
|
|
669
|
+
<>
|
|
670
|
+
<p>{content.getStarted.main}</p>
|
|
671
|
+
<code>{content.getStarted.pageLink}</code>
|
|
672
|
+
</>
|
|
673
|
+
);
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const Page = async () => {
|
|
677
|
+
|
|
678
|
+
const locale = await getLocale();
|
|
679
|
+
|
|
680
|
+
return (
|
|
681
|
+
<IntlayerServerProvider locale={locale}>
|
|
682
|
+
<PageContent />
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
<ServerComponentExample />
|
|
686
|
+
<ClientComponentExample />
|
|
687
|
+
</IntlayerServerProvider>
|
|
688
|
+
|
|
689
|
+
);
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
export default Page;
|
|
693
|
+
|
|
694
|
+
````
|
|
695
|
+
|
|
696
|
+
```jsx fileName="src/app/page.csx" codeFormat="commonjs"
|
|
697
|
+
import { ClientComponentExample } from "@components/clientComponentExample/ClientComponentExample";
|
|
698
|
+
import { ServerComponentExample } from "@components/serverComponentExample/ServerComponentExample";
|
|
699
|
+
import { IntlayerServerProvider, useIntlayer, getLocale } from "next-intlayer/server";
|
|
700
|
+
import { NextPage } from "next";
|
|
701
|
+
import { headers, cookies } from "next/headers";
|
|
702
|
+
|
|
703
|
+
const Page: NextPage = async () => {
|
|
704
|
+
const content = useIntlayer("page");
|
|
705
|
+
|
|
706
|
+
return (
|
|
707
|
+
<>
|
|
708
|
+
<p>{content.getStarted.main}</p>
|
|
709
|
+
<code>{content.getStarted.pageLink}</code>
|
|
710
|
+
</>
|
|
711
|
+
);
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
const Page: NextPage = async () => {
|
|
715
|
+
const locale = await getLocale();
|
|
716
|
+
|
|
717
|
+
return (
|
|
718
|
+
<IntlayerServerProvider locale={locale}>
|
|
719
|
+
<PageContent />
|
|
720
|
+
<ServerComponentExample />
|
|
721
|
+
<ClientComponentExample />
|
|
722
|
+
</IntlayerServerProvider>
|
|
723
|
+
);
|
|
724
|
+
};
|
|
725
|
+
````
|
|
726
|
+
|
|
727
|
+
- **`IntlayerClientProvider`** використовується для надання локалі компонентам на стороні клієнта. Його можна розмістити в будь-якому батьківському компоненті, включно з layout. Однак рекомендовано розміщувати його в layout, оскільки Next.js спільно використовує код layout між сторінками, що робить це ефективнішим. Використовуючи `IntlayerClientProvider` у layout, ви уникаєте повторної ініціалізації для кожної сторінки, покращуєте продуктивність і підтримуєте послідовний контекст локалізації в усьому застосунку.
|
|
728
|
+
- **`IntlayerServerProvider`** використовується для надання локалі дочірнім серверним компонентам. Його не можна встановлювати в layout.
|
|
729
|
+
|
|
730
|
+
> Layout і сторінка не можуть ділитись загальним server context, оскільки система server context базується на сховищі даних для кожного запиту (через механізм [React's cache](https://react.dev/reference/react/cache)), внаслідок чого кожен «context» створюється заново для різних сегментів застосунку. Розміщення провайдера в спільному layout порушить цю ізоляцію та перешкоджатиме правильній передачі значень server context вашим server компонентам.
|
|
731
|
+
|
|
732
|
+
```tsx {4,7} fileName="src/components/clientComponentExample/ClientComponentExample.tsx" codeFormat="typescript"
|
|
733
|
+
"use client";
|
|
734
|
+
|
|
735
|
+
import type { FC } from "react";
|
|
736
|
+
import { useIntlayer } from "next-intlayer";
|
|
737
|
+
|
|
738
|
+
export const ClientComponentExample: FC = () => {
|
|
739
|
+
const content = useIntlayer("client-component-example"); // Створити декларацію пов'язаного контенту
|
|
740
|
+
|
|
741
|
+
return (
|
|
742
|
+
<div>
|
|
743
|
+
<h2>{content.title}</h2>
|
|
744
|
+
<p>{content.content}</p>
|
|
745
|
+
</div>
|
|
746
|
+
);
|
|
747
|
+
};
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
```jsx {3,6} fileName="src/components/clientComponentExample/ClientComponentExample.mjx" codeFormat="esm"
|
|
751
|
+
"use client";
|
|
752
|
+
|
|
753
|
+
import { useIntlayer } from "next-intlayer";
|
|
754
|
+
|
|
755
|
+
const ClientComponentExample = () => {
|
|
756
|
+
const content = useIntlayer("client-component-example"); // Створити декларацію пов'язаного контенту
|
|
757
|
+
|
|
758
|
+
return (
|
|
759
|
+
<div>
|
|
760
|
+
<h2>{content.title}</h2>
|
|
761
|
+
<p>{content.content}</p>
|
|
762
|
+
</div>
|
|
763
|
+
);
|
|
764
|
+
};
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
```jsx {3,6} fileName="src/components/clientComponentExample/ClientComponentExample.csx" codeFormat="commonjs"
|
|
768
|
+
"use client";
|
|
769
|
+
|
|
770
|
+
const { useIntlayer } = require("next-intlayer");
|
|
771
|
+
|
|
772
|
+
const ClientComponentExample = () => {
|
|
773
|
+
const content = useIntlayer("client-component-example"); // Створити декларацію пов'язаного контенту
|
|
774
|
+
|
|
775
|
+
return (
|
|
776
|
+
<div>
|
|
777
|
+
<h2>{content.title}</h2>
|
|
778
|
+
<p>{content.content}</p>
|
|
779
|
+
</div>
|
|
780
|
+
);
|
|
781
|
+
};
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
```tsx {2} fileName="src/components/serverComponentExample/ServerComponentExample.tsx" codeFormat="typescript"
|
|
785
|
+
import type { FC } from "react";
|
|
786
|
+
import { useIntlayer } from "next-intlayer/server";
|
|
787
|
+
|
|
788
|
+
export const ServerComponentExample: FC = () => {
|
|
789
|
+
const content = useIntlayer("server-component-example"); // Створити декларацію пов'язаного контенту
|
|
790
|
+
|
|
791
|
+
return (
|
|
792
|
+
<div>
|
|
793
|
+
<h2>{content.title}</h2>
|
|
794
|
+
<p>{content.content}</p>
|
|
795
|
+
</div>
|
|
796
|
+
);
|
|
797
|
+
};
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
```jsx {1} fileName="src/components/serverComponentExample/ServerComponentExample.mjx" codeFormat="esm"
|
|
801
|
+
import { useIntlayer } from "next-intlayer/server";
|
|
802
|
+
|
|
803
|
+
const ServerComponentExample = () => {
|
|
804
|
+
const content = useIntlayer("server-component-example"); // Створення декларації пов'язаного вмісту
|
|
805
|
+
|
|
806
|
+
return (
|
|
807
|
+
<div>
|
|
808
|
+
<h2>{content.title}</h2>
|
|
809
|
+
<p>{content.content}</p>
|
|
810
|
+
</div>
|
|
811
|
+
);
|
|
812
|
+
};
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
```jsx {1} fileName="src/components/serverComponentExample/ServerComponentExample.csx" codeFormat="commonjs"
|
|
816
|
+
const { useIntlayer } = require("next-intlayer/server");
|
|
817
|
+
|
|
818
|
+
const ServerComponentExample = () => {
|
|
819
|
+
const content = useIntlayer("server-component-example"); // Створення декларації пов'язаного вмісту
|
|
820
|
+
|
|
821
|
+
return (
|
|
822
|
+
<div>
|
|
823
|
+
<h2>{content.title}</h2>
|
|
824
|
+
<p>{content.content}</p>
|
|
825
|
+
</div>
|
|
826
|
+
);
|
|
827
|
+
};
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
> Якщо ви хочете використовувати ваш контент у рядковому атрибуті, наприклад `alt`, `title`, `href`, `aria-label` тощо, ви повинні викликати значення функції, наприклад:
|
|
831
|
+
|
|
832
|
+
> ```jsx
|
|
833
|
+
> <img src={content.image.src.value} alt={content.image.value} />
|
|
834
|
+
> ```
|
|
835
|
+
|
|
836
|
+
> Щоб дізнатися більше про хук `useIntlayer`, зверніться до [документації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useIntlayer.md).
|
|
837
|
+
|
|
838
|
+
### (Необов'язково) Крок 7: Налаштування проксі для визначення локалі
|
|
839
|
+
|
|
840
|
+
Налаштуйте проксі для визначення преференованої локалі користувача:
|
|
841
|
+
|
|
842
|
+
```typescript fileName="src/proxy.ts" codeFormat="typescript"
|
|
843
|
+
export { intlayerProxy as proxy } from "next-intlayer/proxy";
|
|
844
|
+
|
|
845
|
+
export const config = {
|
|
846
|
+
matcher:
|
|
847
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
848
|
+
};
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
```javascript fileName="src/proxy.mjs" codeFormat="esm"
|
|
852
|
+
export { intlayerProxy as proxy } from "next-intlayer/proxy";
|
|
853
|
+
|
|
854
|
+
export const config = {
|
|
855
|
+
matcher:
|
|
856
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
857
|
+
};
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
```javascript fileName="src/proxy.cjs" codeFormat="commonjs"
|
|
861
|
+
const { intlayerProxy } = require("next-intlayer/proxy");
|
|
862
|
+
|
|
863
|
+
const config = {
|
|
864
|
+
matcher:
|
|
865
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
module.exports = { proxy: intlayerProxy, config };
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
> Проксі `intlayerProxy` використовується для визначення преферованої локалі користувача та перенаправлення його на відповідну URL-адресу, як зазначено в [конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md). Крім того, воно дозволяє зберігати преферовану локаль користувача в cookie.
|
|
872
|
+
|
|
873
|
+
> Якщо потрібно з'єднати кілька проксі разом (наприклад, `intlayerProxy` з авторизацією або власними проксі), Intlayer тепер надає допоміжну функцію `multipleProxies`.
|
|
874
|
+
|
|
875
|
+
```ts
|
|
876
|
+
import { multipleProxies, intlayerProxy } from "next-intlayer/proxy";
|
|
877
|
+
import { customProxy } from "@utils/customProxy";
|
|
878
|
+
|
|
879
|
+
export const proxy = multipleProxies([intlayerProxy, customProxy]);
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
### (Необов'язково) Крок 8: Змініть мову вашого контенту
|
|
883
|
+
|
|
884
|
+
Щоб змінити мову вашого контенту в Next.js, рекомендований спосіб — використовувати компонент Link для перенаправлення користувачів на відповідну локалізовану сторінку. Компонент Link забезпечує prefetch сторінки, що допомагає уникнути повного перезавантаження сторінки.
|
|
885
|
+
|
|
886
|
+
```tsx fileName="src/components/localeSwitcher/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
887
|
+
"use client";
|
|
888
|
+
|
|
889
|
+
import type { FC } from "react";
|
|
890
|
+
import { Locales, getHTMLTextDir, getLocaleName } from "intlayer";
|
|
891
|
+
import { useLocale } from "next-intlayer";
|
|
892
|
+
|
|
893
|
+
export const LocaleSwitcher: FC = () => {
|
|
894
|
+
const { locale, availableLocales, setLocale } = useLocale({
|
|
895
|
+
onChange: () => window.location.reload(),
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
return (
|
|
899
|
+
<div>
|
|
900
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
901
|
+
<div id="localePopover" popover="auto">
|
|
902
|
+
{availableLocales.map((localeItem) => (
|
|
903
|
+
<button
|
|
904
|
+
key={localeItem}
|
|
905
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
906
|
+
onClick={() => setLocale(localeItem)}
|
|
907
|
+
>
|
|
908
|
+
<span>
|
|
909
|
+
{/* Локаль — напр., FR */}
|
|
910
|
+
{localeItem}
|
|
911
|
+
</span>
|
|
912
|
+
<span>
|
|
913
|
+
{/* Мова у власній локалі — напр., Français */}
|
|
914
|
+
{getLocaleName(localeItem, locale)}
|
|
915
|
+
</span>
|
|
916
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
917
|
+
{/* Мова в поточній локалі — напр., Francés коли поточна локаль встановлена на Locales.SPANISH */}
|
|
918
|
+
{getLocaleName(localeItem)}
|
|
919
|
+
</span>
|
|
920
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
921
|
+
{/* Мова англійською — наприклад, French */}
|
|
922
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
923
|
+
</span>
|
|
924
|
+
</button>
|
|
925
|
+
))}
|
|
926
|
+
</div>
|
|
927
|
+
</div>
|
|
928
|
+
);
|
|
929
|
+
};
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
```jsx fileName="src/components/localeSwitcher/LocaleSwitcher.msx" codeFormat="esm"
|
|
933
|
+
"use client";
|
|
934
|
+
|
|
935
|
+
import { Locales, getHTMLTextDir, getLocaleName } from "intlayer";
|
|
936
|
+
import { useLocale } from "next-intlayer";
|
|
937
|
+
|
|
938
|
+
export const LocaleSwitcher = () => {
|
|
939
|
+
const { locale, availableLocales, setLocale } = useLocale({
|
|
940
|
+
onChange: () => window.location.reload(),
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
return (
|
|
944
|
+
<div>
|
|
945
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
946
|
+
<div id="localePopover" popover="auto">
|
|
947
|
+
{availableLocales.map((localeItem) => (
|
|
948
|
+
<button
|
|
949
|
+
key={localeItem}
|
|
950
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
951
|
+
onClick={() => setLocale(localeItem)}
|
|
952
|
+
>
|
|
953
|
+
<span>
|
|
954
|
+
{/* Локаль - напр., FR */}
|
|
955
|
+
{localeItem}
|
|
956
|
+
</span>
|
|
957
|
+
<span>
|
|
958
|
+
{/* Назва мови у власній локалі - напр., Français */}
|
|
959
|
+
{getLocaleName(localeItem, locale)}
|
|
960
|
+
</span>
|
|
961
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
962
|
+
{/* Назва мови поточною локаллю - напр., Francés коли поточна локаль встановлена на Locales.SPANISH */}
|
|
963
|
+
{getLocaleName(localeItem)}
|
|
964
|
+
</span>
|
|
965
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
966
|
+
{/* Назва мови англійською - напр., French */}
|
|
967
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
968
|
+
</span>
|
|
969
|
+
</button>
|
|
970
|
+
))}
|
|
971
|
+
</div>
|
|
972
|
+
</div>
|
|
973
|
+
);
|
|
974
|
+
};
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
```jsx fileName="src/components/localeSwitcher/LocaleSwitcher.csx" codeFormat="commonjs"
|
|
978
|
+
"use client";
|
|
979
|
+
|
|
980
|
+
const { Locales, getHTMLTextDir, getLocaleName } = require("intlayer");
|
|
981
|
+
const { useLocale } = require("next-intlayer");
|
|
982
|
+
|
|
983
|
+
export const LocaleSwitcher = () => {
|
|
984
|
+
const path
|
|
985
|
+
const { locale availableLocales, setLocale } = useLocale({
|
|
986
|
+
onChange: ()=> window.location.reload(),
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
return (
|
|
990
|
+
<div>
|
|
991
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
992
|
+
<div id="localePopover" popover="auto">
|
|
993
|
+
{availableLocales.map((localeItem) => (
|
|
994
|
+
<button
|
|
995
|
+
key={localeItem}
|
|
996
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
997
|
+
onClick={() => setLocale(localeItem)}
|
|
998
|
+
>
|
|
999
|
+
<span>
|
|
1000
|
+
{/* Локаль — наприклад FR */}
|
|
1001
|
+
{localeItem}
|
|
1002
|
+
</span>
|
|
1003
|
+
<span>
|
|
1004
|
+
{/* Назва мови у власній локалі — наприклад Français */}
|
|
1005
|
+
{getLocaleName(localeItem, locale)}
|
|
1006
|
+
</span>
|
|
1007
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1008
|
+
{/* Назва мови в поточній локалі — наприклад Francés, коли поточна локаль встановлена на Locales.SPANISH */}
|
|
1009
|
+
{getLocaleName(localeItem)}
|
|
1010
|
+
</span>
|
|
1011
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1012
|
+
{/* Назва мови англійською — наприклад French */}
|
|
1013
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1014
|
+
</span>
|
|
1015
|
+
</button>
|
|
1016
|
+
))}
|
|
1017
|
+
</div>
|
|
1018
|
+
</div>
|
|
1019
|
+
);
|
|
1020
|
+
};
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
> Альтернативний спосіб — використати функцію `setLocale`, яку надає хук `useLocale`. Ця функція не дозволяє виконувати prefetch сторінки. Детальніше див. документацію хуку [`useLocale`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useLocale.md).
|
|
1024
|
+
|
|
1025
|
+
> Посилання на документацію:
|
|
1026
|
+
>
|
|
1027
|
+
> - [`useLocale` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useLocale.md)
|
|
1028
|
+
> - [`getLocaleName` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/getLocaleName.md)
|
|
1029
|
+
> - [`getLocalizedUrl` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/getLocalizedUrl.md)
|
|
1030
|
+
> - [`getHTMLTextDir` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getHTMLTextDir.md)
|
|
1031
|
+
> - [`hrefLang` атрибут](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)
|
|
1032
|
+
> - [`lang` атрибут](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang)
|
|
1033
|
+
> - [`dir` атрибут](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir)
|
|
1034
|
+
> - [`aria-current` атрибут](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
|
|
1035
|
+
|
|
1036
|
+
### (Необов'язково) Крок 9: Отримати поточну локаль у Server Actions
|
|
1037
|
+
|
|
1038
|
+
Якщо вам потрібна активна локаль всередині Server Action (наприклад, для локалізації електронних листів або виконання логіки, що залежить від локалі), викличте `getLocale` з `next-intlayer/server`:
|
|
1039
|
+
|
|
1040
|
+
```tsx fileName="src/app/actions/getLocale.ts" codeFormat="typescript"
|
|
1041
|
+
"use server";
|
|
1042
|
+
|
|
1043
|
+
import { getLocale } from "next-intlayer/server";
|
|
1044
|
+
|
|
1045
|
+
export const myServerAction = async () => {
|
|
1046
|
+
const locale = await getLocale();
|
|
1047
|
+
|
|
1048
|
+
// Виконайте необхідні дії з локаллю
|
|
1049
|
+
};
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
> Функція `getLocale` використовує каскадну стратегію для визначення локалі користувача:
|
|
1053
|
+
>
|
|
1054
|
+
> 1. Спочатку вона перевіряє заголовки запиту на наявність значення локалі, яке могло бути встановлене проксі
|
|
1055
|
+
> 2. Якщо локаль не знайдена в заголовках, вона шукає локаль, збережену в cookies
|
|
1056
|
+
> 3. Якщо cookie не знайдено, вона намагається визначити бажану мову користувача з налаштувань браузера
|
|
1057
|
+
> 4. Як останній варіант, вона повертається до локалі за замовчуванням, налаштованої в застосунку
|
|
1058
|
+
>
|
|
1059
|
+
> Це гарантує, що вибирається найбільш відповідна локаль на основі доступного контексту.
|
|
1060
|
+
|
|
1061
|
+
### (Необов'язково) Крок 10: Оптимізуйте розмір бандла
|
|
1062
|
+
|
|
1063
|
+
При використанні `next-intlayer` словники за замовчуванням включаються в бандл для кожної сторінки. Щоб оптимізувати розмір бандла, Intlayer надає необов'язковий SWC-плагін, який інтелектуально замінює виклики `useIntlayer` за допомогою макросів. Це гарантує, що словники включатимуться лише в бандли сторінок, які їх фактично використовують.
|
|
1064
|
+
|
|
1065
|
+
Щоб увімкнути цю оптимізацію, встановіть пакет `@intlayer/swc`. Після встановлення `next-intlayer` автоматично виявить і використає плагін:
|
|
1066
|
+
|
|
1067
|
+
```bash packageManager="npm"
|
|
1068
|
+
npm install @intlayer/swc --save-dev
|
|
1069
|
+
npx intlayer init
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
```bash packageManager="pnpm"
|
|
1073
|
+
pnpm add @intlayer/swc --save-dev
|
|
1074
|
+
pnpm intlayer init
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
```bash packageManager="yarn"
|
|
1078
|
+
yarn add @intlayer/swc --save-dev
|
|
1079
|
+
yarn intlayer init
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
```bash packageManager="bun"
|
|
1083
|
+
bun add @intlayer/swc --dev
|
|
1084
|
+
bunx intlayer init
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
> Примітка: Ця оптимізація доступна лише для Next.js 13 і новіших версій.
|
|
1088
|
+
|
|
1089
|
+
> Примітка: Цей пакет не встановлюється за замовчуванням, оскільки SWC-плагіни в Next.js все ще є експериментальними. Це може змінитися в майбутньому.
|
|
1090
|
+
|
|
1091
|
+
> Примітка: Якщо ви встановите опцію як `importMode: 'dynamic'` або `importMode: 'live'`, це залежатиме від Suspense, тож вам доведеться обгорнути виклики `useIntlayer` у межі `Suspense`. Це означає, що ви не зможете використовувати `useIntlayer` безпосередньо на верхньому рівні вашого компонента Page або Layout.
|
|
1092
|
+
|
|
1093
|
+
### Стеження за змінами словників у Turbopack
|
|
1094
|
+
|
|
1095
|
+
При використанні Turbopack як сервера розробки з командою `next dev`, зміни в словниках за замовчуванням не будуть виявлятися автоматично.
|
|
1096
|
+
|
|
1097
|
+
Це обмеження виникає через те, що Turbopack не може запускати плагіни webpack паралельно, щоб відстежувати зміни у ваших файлах контенту. Щоб обійти це, потрібно використовувати команду `intlayer watch`, щоб одночасно запускати сервер розробки та спостерігача складання Intlayer.
|
|
1098
|
+
|
|
1099
|
+
```json5 fileName="package.json"
|
|
1100
|
+
{
|
|
1101
|
+
// ... Ваші існуючі конфігурації package.json
|
|
1102
|
+
"scripts": {
|
|
1103
|
+
// ... Ваші існуючі налаштування скриптів
|
|
1104
|
+
"dev": "intlayer watch --with 'next dev'",
|
|
1105
|
+
},
|
|
1106
|
+
}
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
> Якщо ви використовуєте next-intlayer@<=6.x.x, потрібно зберегти прапорець `--turbopack`, щоб додаток Next.js 16 працював правильно з Turbopack. Ми рекомендуємо використовувати next-intlayer@>=7.x.x, щоб уникнути цього обмеження.
|
|
1110
|
+
|
|
1111
|
+
### Налаштування TypeScript
|
|
1112
|
+
|
|
1113
|
+
Intlayer використовує module augmentation, щоб отримати переваги TypeScript і зміцнити вашу codebase.
|
|
1114
|
+
|
|
1115
|
+

|
|
1116
|
+
|
|
1117
|
+

|
|
1118
|
+
|
|
1119
|
+
Переконайтеся, що ваша конфігурація TypeScript включає автозгенеровані типи.
|
|
1120
|
+
|
|
1121
|
+
```json5 fileName="tsconfig.json"
|
|
1122
|
+
{
|
|
1123
|
+
// ... Ваші існуючі конфігурації TypeScript
|
|
1124
|
+
"include": [
|
|
1125
|
+
// ... Ваші існуючі конфігурації TypeScript
|
|
1126
|
+
".intlayer/**/*.ts", // Include the auto-generated types
|
|
1127
|
+
],
|
|
1128
|
+
}
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
### Налаштування Git
|
|
1132
|
+
|
|
1133
|
+
Рекомендується ігнорувати файли, згенеровані Intlayer. Це дозволить уникнути їх додавання до вашого Git-репозиторію.
|
|
1134
|
+
|
|
1135
|
+
Для цього можна додати наступні інструкції до файлу `.gitignore`:
|
|
1136
|
+
|
|
1137
|
+
```plaintext fileName=".gitignore"
|
|
1138
|
+
# Ігнорувати файли, згенеровані Intlayer
|
|
1139
|
+
.intlayer
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
### Розширення VS Code
|
|
1143
|
+
|
|
1144
|
+
Щоб покращити досвід розробки з Intlayer, ви можете встановити офіційне розширення **Intlayer VS Code Extension**.
|
|
1145
|
+
|
|
1146
|
+
[Встановити з VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1147
|
+
|
|
1148
|
+
Це розширення надає:
|
|
1149
|
+
|
|
1150
|
+
- **Автодоповнення** для ключів перекладу.
|
|
1151
|
+
- **Виявлення помилок у реальному часі** для відсутніх перекладів.
|
|
1152
|
+
- **Вбудований попередній перегляд** перекладеного вмісту.
|
|
1153
|
+
- **Швидкі дії** для легкого створення та оновлення перекладів.
|
|
1154
|
+
|
|
1155
|
+
Для детальнішої інформації про використання розширення зверніться до [документації Intlayer VS Code Extension](https://intlayer.org/doc/vs-code-extension).
|
|
1156
|
+
|
|
1157
|
+
### Розширені можливості
|
|
1158
|
+
|
|
1159
|
+
Щоб розширити можливості, ви можете реалізувати [візуальний редактор](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).
|