@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.
Files changed (229) hide show
  1. package/blog/ar/per-component_vs_centralized_i18n.md +248 -0
  2. package/blog/de/per-component_vs_centralized_i18n.md +248 -0
  3. package/blog/en/_per-component_vs_centralized_i18n.md +252 -0
  4. package/blog/en/per-component_vs_centralized_i18n.md +248 -0
  5. package/blog/en-GB/per-component_vs_centralized_i18n.md +247 -0
  6. package/blog/es/per-component_vs_centralized_i18n.md +245 -0
  7. package/blog/fr/per-component_vs_centralized_i18n.md +245 -0
  8. package/blog/hi/per-component_vs_centralized_i18n.md +249 -0
  9. package/blog/id/per-component_vs_centralized_i18n.md +248 -0
  10. package/blog/it/per-component_vs_centralized_i18n.md +247 -0
  11. package/blog/ja/per-component_vs_centralized_i18n.md +247 -0
  12. package/blog/ko/per-component_vs_centralized_i18n.md +246 -0
  13. package/blog/pl/per-component_vs_centralized_i18n.md +247 -0
  14. package/blog/pt/per-component_vs_centralized_i18n.md +246 -0
  15. package/blog/ru/per-component_vs_centralized_i18n.md +251 -0
  16. package/blog/tr/per-component_vs_centralized_i18n.md +244 -0
  17. package/blog/uk/compiler_vs_declarative_i18n.md +224 -0
  18. package/blog/uk/i18n_using_next-i18next.md +1086 -0
  19. package/blog/uk/i18n_using_next-intl.md +760 -0
  20. package/blog/uk/index.md +69 -0
  21. package/blog/uk/internationalization_and_SEO.md +273 -0
  22. package/blog/uk/intlayer_with_i18next.md +211 -0
  23. package/blog/uk/intlayer_with_next-i18next.md +202 -0
  24. package/blog/uk/intlayer_with_next-intl.md +203 -0
  25. package/blog/uk/intlayer_with_react-i18next.md +200 -0
  26. package/blog/uk/intlayer_with_react-intl.md +202 -0
  27. package/blog/uk/intlayer_with_vue-i18n.md +206 -0
  28. package/blog/uk/l10n_platform_alternative/Lokalise.md +80 -0
  29. package/blog/uk/l10n_platform_alternative/crowdin.md +80 -0
  30. package/blog/uk/l10n_platform_alternative/phrase.md +78 -0
  31. package/blog/uk/list_i18n_technologies/CMS/drupal.md +143 -0
  32. package/blog/uk/list_i18n_technologies/CMS/wix.md +167 -0
  33. package/blog/uk/list_i18n_technologies/CMS/wordpress.md +189 -0
  34. package/blog/uk/list_i18n_technologies/frameworks/angular.md +125 -0
  35. package/blog/uk/list_i18n_technologies/frameworks/flutter.md +128 -0
  36. package/blog/uk/list_i18n_technologies/frameworks/react-native.md +217 -0
  37. package/blog/uk/list_i18n_technologies/frameworks/react.md +155 -0
  38. package/blog/uk/list_i18n_technologies/frameworks/svelte.md +145 -0
  39. package/blog/uk/list_i18n_technologies/frameworks/vue.md +144 -0
  40. package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +1499 -0
  41. package/blog/uk/nextjs-multilingual-seo-comparison.md +360 -0
  42. package/blog/uk/per-component_vs_centralized_i18n.md +248 -0
  43. package/blog/uk/rag_powered_documentation_assistant.md +288 -0
  44. package/blog/uk/react-i18next_vs_react-intl_vs_intlayer.md +164 -0
  45. package/blog/uk/vue-i18n_vs_intlayer.md +279 -0
  46. package/blog/uk/what_is_internationalization.md +167 -0
  47. package/blog/vi/per-component_vs_centralized_i18n.md +246 -0
  48. package/blog/zh/per-component_vs_centralized_i18n.md +248 -0
  49. package/dist/cjs/common.cjs.map +1 -1
  50. package/dist/cjs/generated/blog.entry.cjs +20 -0
  51. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  52. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  53. package/dist/cjs/generated/frequentQuestions.entry.cjs +20 -0
  54. package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
  55. package/dist/cjs/generated/legal.entry.cjs.map +1 -1
  56. package/dist/esm/common.mjs.map +1 -1
  57. package/dist/esm/generated/blog.entry.mjs +20 -0
  58. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  59. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  60. package/dist/esm/generated/frequentQuestions.entry.mjs +20 -0
  61. package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
  62. package/dist/esm/generated/legal.entry.mjs.map +1 -1
  63. package/dist/types/generated/blog.entry.d.ts +1 -0
  64. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  65. package/dist/types/generated/frequentQuestions.entry.d.ts +1 -0
  66. package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
  67. package/docs/ar/configuration.md +6 -1
  68. package/docs/ar/dictionary/content_file.md +6 -1
  69. package/docs/de/configuration.md +6 -1
  70. package/docs/de/dictionary/content_file.md +6 -1
  71. package/docs/en/configuration.md +6 -1
  72. package/docs/en/dictionary/content_file.md +6 -1
  73. package/docs/en-GB/configuration.md +6 -1
  74. package/docs/en-GB/dictionary/content_file.md +3 -1
  75. package/docs/es/configuration.md +6 -1
  76. package/docs/es/dictionary/content_file.md +6 -1
  77. package/docs/fr/configuration.md +6 -1
  78. package/docs/fr/dictionary/content_file.md +3 -1
  79. package/docs/hi/configuration.md +6 -1
  80. package/docs/hi/dictionary/content_file.md +3 -1
  81. package/docs/id/configuration.md +6 -1
  82. package/docs/id/dictionary/content_file.md +3 -1
  83. package/docs/it/configuration.md +6 -1
  84. package/docs/it/dictionary/content_file.md +3 -1
  85. package/docs/ja/configuration.md +6 -1
  86. package/docs/ja/dictionary/content_file.md +3 -1
  87. package/docs/ko/configuration.md +6 -1
  88. package/docs/ko/dictionary/content_file.md +3 -1
  89. package/docs/pl/configuration.md +3 -1
  90. package/docs/pl/dictionary/content_file.md +3 -1
  91. package/docs/pt/configuration.md +6 -1
  92. package/docs/pt/dictionary/content_file.md +3 -1
  93. package/docs/ru/configuration.md +6 -1
  94. package/docs/ru/dictionary/content_file.md +6 -1
  95. package/docs/tr/configuration.md +6 -1
  96. package/docs/tr/dictionary/content_file.md +3 -1
  97. package/docs/uk/CI_CD.md +198 -0
  98. package/docs/uk/autoFill.md +307 -0
  99. package/docs/uk/bundle_optimization.md +185 -0
  100. package/docs/uk/cli/build.md +64 -0
  101. package/docs/uk/cli/ci.md +137 -0
  102. package/docs/uk/cli/configuration.md +63 -0
  103. package/docs/uk/cli/debug.md +46 -0
  104. package/docs/uk/cli/doc-review.md +43 -0
  105. package/docs/uk/cli/doc-translate.md +132 -0
  106. package/docs/uk/cli/editor.md +28 -0
  107. package/docs/uk/cli/fill.md +130 -0
  108. package/docs/uk/cli/index.md +190 -0
  109. package/docs/uk/cli/init.md +84 -0
  110. package/docs/uk/cli/list.md +90 -0
  111. package/docs/uk/cli/list_projects.md +128 -0
  112. package/docs/uk/cli/live.md +41 -0
  113. package/docs/uk/cli/login.md +157 -0
  114. package/docs/uk/cli/pull.md +78 -0
  115. package/docs/uk/cli/push.md +98 -0
  116. package/docs/uk/cli/sdk.md +71 -0
  117. package/docs/uk/cli/test.md +76 -0
  118. package/docs/uk/cli/transform.md +65 -0
  119. package/docs/uk/cli/version.md +24 -0
  120. package/docs/uk/cli/watch.md +37 -0
  121. package/docs/uk/configuration.md +742 -0
  122. package/docs/uk/dictionary/condition.md +237 -0
  123. package/docs/uk/dictionary/content_file.md +1134 -0
  124. package/docs/uk/dictionary/enumeration.md +245 -0
  125. package/docs/uk/dictionary/file.md +232 -0
  126. package/docs/uk/dictionary/function_fetching.md +212 -0
  127. package/docs/uk/dictionary/gender.md +273 -0
  128. package/docs/uk/dictionary/insertion.md +187 -0
  129. package/docs/uk/dictionary/markdown.md +383 -0
  130. package/docs/uk/dictionary/nesting.md +273 -0
  131. package/docs/uk/dictionary/translation.md +332 -0
  132. package/docs/uk/formatters.md +595 -0
  133. package/docs/uk/how_works_intlayer.md +256 -0
  134. package/docs/uk/index.md +175 -0
  135. package/docs/uk/interest_of_intlayer.md +297 -0
  136. package/docs/uk/intlayer_CMS.md +569 -0
  137. package/docs/uk/intlayer_visual_editor.md +292 -0
  138. package/docs/uk/intlayer_with_angular.md +710 -0
  139. package/docs/uk/intlayer_with_astro.md +256 -0
  140. package/docs/uk/intlayer_with_create_react_app.md +1258 -0
  141. package/docs/uk/intlayer_with_express.md +429 -0
  142. package/docs/uk/intlayer_with_fastify.md +446 -0
  143. package/docs/uk/intlayer_with_lynx+react.md +548 -0
  144. package/docs/uk/intlayer_with_nestjs.md +283 -0
  145. package/docs/uk/intlayer_with_next-i18next.md +640 -0
  146. package/docs/uk/intlayer_with_next-intl.md +456 -0
  147. package/docs/uk/intlayer_with_nextjs_page_router.md +1541 -0
  148. package/docs/uk/intlayer_with_nuxt.md +711 -0
  149. package/docs/uk/intlayer_with_react_router_v7.md +600 -0
  150. package/docs/uk/intlayer_with_react_router_v7_fs_routes.md +669 -0
  151. package/docs/uk/intlayer_with_svelte_kit.md +579 -0
  152. package/docs/uk/intlayer_with_tanstack.md +818 -0
  153. package/docs/uk/intlayer_with_vite+preact.md +1748 -0
  154. package/docs/uk/intlayer_with_vite+react.md +1449 -0
  155. package/docs/uk/intlayer_with_vite+solid.md +302 -0
  156. package/docs/uk/intlayer_with_vite+svelte.md +520 -0
  157. package/docs/uk/intlayer_with_vite+vue.md +1113 -0
  158. package/docs/uk/introduction.md +222 -0
  159. package/docs/uk/locale_mapper.md +242 -0
  160. package/docs/uk/mcp_server.md +211 -0
  161. package/docs/uk/packages/express-intlayer/t.md +465 -0
  162. package/docs/uk/packages/intlayer/getEnumeration.md +159 -0
  163. package/docs/uk/packages/intlayer/getHTMLTextDir.md +121 -0
  164. package/docs/uk/packages/intlayer/getLocaleLang.md +81 -0
  165. package/docs/uk/packages/intlayer/getLocaleName.md +135 -0
  166. package/docs/uk/packages/intlayer/getLocalizedUrl.md +338 -0
  167. package/docs/uk/packages/intlayer/getMultilingualUrls.md +359 -0
  168. package/docs/uk/packages/intlayer/getPathWithoutLocale.md +75 -0
  169. package/docs/uk/packages/intlayer/getPrefix.md +213 -0
  170. package/docs/uk/packages/intlayer/getTranslation.md +190 -0
  171. package/docs/uk/packages/intlayer/getTranslationContent.md +189 -0
  172. package/docs/uk/packages/next-intlayer/t.md +365 -0
  173. package/docs/uk/packages/next-intlayer/useDictionary.md +276 -0
  174. package/docs/uk/packages/next-intlayer/useIntlayer.md +263 -0
  175. package/docs/uk/packages/next-intlayer/useLocale.md +166 -0
  176. package/docs/uk/packages/react-intlayer/t.md +311 -0
  177. package/docs/uk/packages/react-intlayer/useDictionary.md +295 -0
  178. package/docs/uk/packages/react-intlayer/useI18n.md +250 -0
  179. package/docs/uk/packages/react-intlayer/useIntlayer.md +251 -0
  180. package/docs/uk/packages/react-intlayer/useLocale.md +210 -0
  181. package/docs/uk/per_locale_file.md +345 -0
  182. package/docs/uk/plugins/sync-json.md +398 -0
  183. package/docs/uk/readme.md +265 -0
  184. package/docs/uk/releases/v6.md +305 -0
  185. package/docs/uk/releases/v7.md +624 -0
  186. package/docs/uk/roadmap.md +346 -0
  187. package/docs/uk/testing.md +204 -0
  188. package/docs/vi/configuration.md +6 -1
  189. package/docs/vi/dictionary/content_file.md +6 -1
  190. package/docs/zh/configuration.md +6 -1
  191. package/docs/zh/dictionary/content_file.md +6 -1
  192. package/frequent_questions/ar/error-vite-env-only.md +77 -0
  193. package/frequent_questions/de/error-vite-env-only.md +77 -0
  194. package/frequent_questions/en/error-vite-env-only.md +77 -0
  195. package/frequent_questions/en-GB/error-vite-env-only.md +77 -0
  196. package/frequent_questions/es/error-vite-env-only.md +76 -0
  197. package/frequent_questions/fr/error-vite-env-only.md +77 -0
  198. package/frequent_questions/hi/error-vite-env-only.md +77 -0
  199. package/frequent_questions/id/error-vite-env-only.md +77 -0
  200. package/frequent_questions/it/error-vite-env-only.md +77 -0
  201. package/frequent_questions/ja/error-vite-env-only.md +77 -0
  202. package/frequent_questions/ko/error-vite-env-only.md +77 -0
  203. package/frequent_questions/pl/error-vite-env-only.md +77 -0
  204. package/frequent_questions/pt/error-vite-env-only.md +77 -0
  205. package/frequent_questions/ru/error-vite-env-only.md +77 -0
  206. package/frequent_questions/tr/error-vite-env-only.md +77 -0
  207. package/frequent_questions/uk/SSR_Next_no_[locale].md +104 -0
  208. package/frequent_questions/uk/array_as_content_declaration.md +72 -0
  209. package/frequent_questions/uk/build_dictionaries.md +58 -0
  210. package/frequent_questions/uk/build_error_CI_CD.md +74 -0
  211. package/frequent_questions/uk/bun_set_up.md +53 -0
  212. package/frequent_questions/uk/customized_locale_list.md +64 -0
  213. package/frequent_questions/uk/domain_routing.md +113 -0
  214. package/frequent_questions/uk/error-vite-env-only.md +77 -0
  215. package/frequent_questions/uk/esbuild_error.md +29 -0
  216. package/frequent_questions/uk/get_locale_cookie.md +142 -0
  217. package/frequent_questions/uk/intlayer_command_undefined.md +155 -0
  218. package/frequent_questions/uk/locale_incorect_in_url.md +73 -0
  219. package/frequent_questions/uk/package_version_error.md +181 -0
  220. package/frequent_questions/uk/static_rendering.md +44 -0
  221. package/frequent_questions/uk/translated_path_url.md +55 -0
  222. package/frequent_questions/uk/unknown_command.md +97 -0
  223. package/frequent_questions/vi/error-vite-env-only.md +77 -0
  224. package/frequent_questions/zh/error-vite-env-only.md +77 -0
  225. package/legal/uk/privacy_notice.md +83 -0
  226. package/legal/uk/terms_of_service.md +55 -0
  227. package/package.json +9 -9
  228. package/src/generated/blog.entry.ts +20 -0
  229. package/src/generated/frequentQuestions.entry.ts +20 -0
@@ -0,0 +1,456 @@
1
+ ---
2
+ createdAt: 2025-10-05
3
+ updatedAt: 2025-10-05
4
+ title: "Як перекласти Next.js 15 за допомогою next-intl — керівництво з i18n 2026"
5
+ description: "Дізнайтеся, як зробити ваш вебсайт на Next.js 15 (App Router) багатомовним. Дотримуйтеся документації, щоб інтернаціоналізувати (i18n) та перекласти його."
6
+ keywords:
7
+ - Інтернаціоналізація
8
+ - Документація
9
+ - Intlayer
10
+ - Next.js 15
11
+ - next-intl
12
+ - JavaScript
13
+ - React
14
+ slugs:
15
+ - doc
16
+ - next-intl
17
+ applicationTemplate: https://github.com/aymericzip/intlayer-next-intl-template
18
+ ---
19
+
20
+ # Перекладіть ваш вебсайт Next.js 15 з next-intl за допомогою Intlayer | Інтернаціоналізація (i18n)
21
+
22
+ Цей посібник проведе вас через кращі практики next-intl у застосунку Next.js 15 (App Router) і покаже, як накласти Intlayer зверху для надійного керування перекладами та автоматизації.
23
+
24
+ Дивіться порівняння в [next-i18next vs next-intl vs Intlayer](https://github.com/aymericzip/intlayer/blob/main/docs/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md).
25
+
26
+ - Для junior-розробників: дотримуйтесь покрокових розділів, щоб отримати працездатний мультимовний додаток.
27
+ - Для mid-level розробників: зверніть увагу на оптимізацію payload та розділення server/client.
28
+ - Для senior-розробників: зверніть увагу на static generation, middleware, інтеграцію SEO та automation hooks.
29
+
30
+ Що ми розглянемо:
31
+
32
+ - Налаштування та структура файлів
33
+ - Оптимізація завантаження повідомлень
34
+ - Використання клієнтських та серверних компонентів
35
+ - Метадані, sitemap, robots для SEO
36
+ - Middleware для маршрутизації локалі
37
+ - Додавання Intlayer поверх (CLI та автоматизація)
38
+
39
+ ## Налаштуйте свій додаток за допомогою next-intl
40
+
41
+ Встановіть залежності next-intl:
42
+
43
+ ```bash packageManager="npm"
44
+ npm install next-intl
45
+ ```
46
+
47
+ ```bash packageManager="pnpm"
48
+ pnpm add next-intl
49
+ ```
50
+
51
+ ```bash packageManager="yarn"
52
+ yarn add next-intl
53
+ ```
54
+
55
+ ```bash packageManager="bun"
56
+ bun add next-intl
57
+ ```
58
+
59
+ ```bash
60
+ .
61
+ ├── locales
62
+ │ ├── en
63
+ │ │ ├── common.json
64
+ │ │ └── about.json
65
+ │ ├── fr
66
+ │ │ ├── common.json
67
+ │ │ └── about.json
68
+ │ └── es
69
+ │ ├── common.json
70
+ │ └── about.json
71
+ └── src
72
+ ├── i18n.ts
73
+ ├── middleware.ts
74
+ ├── app
75
+ │ └── [locale]
76
+ │ ├── layout.tsx
77
+ │ └── about
78
+ │ └── page.tsx
79
+ └── components
80
+ ├── ClientComponentExample.tsx
81
+ └── ServerComponent.tsx
82
+ ```
83
+
84
+ #### Налаштування та завантаження контенту
85
+
86
+ Завантажуйте лише ті простори імен, які потрібні вашим маршрутам, і перевіряйте локалі на ранньому етапі. За можливості тримайте серверні компоненти синхронними та надсилайте на клієнт лише необхідні повідомлення.
87
+
88
+ ```tsx fileName="src/i18n.ts"
89
+ import { getRequestConfig } from "next-intl/server";
90
+ import { notFound } from "next/navigation";
91
+
92
+ export const locales = ["en", "fr", "es"] as const;
93
+ export const defaultLocale = "en" as const;
94
+
95
+ async function loadMessages(locale: string) {
96
+ // Завантажуйте лише ті namespaces, які потрібні вашим layout/pages
97
+ const [common, about] = await Promise.all([
98
+ import(`../locales/${locale}/common.json`).then((m) => m.default),
99
+ import(`../locales/${locale}/about.json`).then((m) => m.default),
100
+ ]);
101
+
102
+ return { common, about } as const;
103
+ }
104
+
105
+ export default getRequestConfig(async ({ locale }) => {
106
+ if (!locales.includes(locale as any)) notFound();
107
+
108
+ return {
109
+ messages: await loadMessages(locale),
110
+ };
111
+ });
112
+ ```
113
+
114
+ ```tsx fileName="src/app/[locale]/layout.tsx"
115
+ import type { ReactNode } from "react";
116
+ import { locales } from "@/i18n";
117
+ import {
118
+ getLocaleDirection,
119
+ unstable_setRequestLocale,
120
+ } from "next-intl/server";
121
+
122
+ export const dynamic = "force-static";
123
+
124
+ export function generateStaticParams() {
125
+ return locales.map((locale) => ({ locale }));
126
+ }
127
+
128
+ export default async function LocaleLayout({
129
+ children,
130
+ params,
131
+ }: {
132
+ children: ReactNode;
133
+ params: { locale: string };
134
+ }) {
135
+ const { locale } = params;
136
+
137
+ // Встановіть активну локаль запиту для цього серверного рендерингу (RSC)
138
+ unstable_setRequestLocale(locale);
139
+
140
+ const dir = getLocaleDirection(locale);
141
+
142
+ return (
143
+ <html lang={locale} dir={dir}>
144
+ <body>{children}</body>
145
+ </html>
146
+ );
147
+ }
148
+ ```
149
+
150
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
151
+ import { getTranslations, getMessages, getFormatter } from "next-intl/server";
152
+ import { NextIntlClientProvider } from "next-intl";
153
+ import pick from "lodash/pick";
154
+ import ServerComponent from "@/components/ServerComponent";
155
+ import ClientComponentExample from "@/components/ClientComponentExample";
156
+
157
+ export const dynamic = "force-static";
158
+
159
+ export default async function AboutPage({
160
+ params,
161
+ }: {
162
+ params: { locale: string };
163
+ }) {
164
+ const { locale } = params;
165
+
166
+ // Повідомлення завантажуються на сервері. Передавайте клієнту лише те, що потрібно.
167
+ const messages = await getMessages();
168
+ const clientMessages = pick(messages, ["common", "about"]);
169
+
170
+ // Переклади та форматування, що виконуються виключно на сервері
171
+ const tAbout = await getTranslations("about");
172
+ const tCounter = await getTranslations("about.counter");
173
+ const format = await getFormatter();
174
+
175
+ const initialFormattedCount = format.number(0);
176
+
177
+ return (
178
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
179
+ <main>
180
+ <h1>{tAbout("title")}</h1>
181
+ <ClientComponentExample />
182
+ <ServerComponent
183
+ formattedCount={initialFormattedCount}
184
+ label={tCounter("label")}
185
+ increment={tCounter("increment")}
186
+ />
187
+ </main>
188
+ </NextIntlClientProvider>
189
+ );
190
+ }
191
+ ```
192
+
193
+ ### Використання в клієнтському компоненті
194
+
195
+ Розглянемо приклад клієнтського компонента, який відображає лічильник.
196
+
197
+ **Переклади (структура збережена; завантажте їх у повідомлення next-intl на свій розсуд)**
198
+
199
+ ```json fileName="locales/en/about.json"
200
+ {
201
+ "counter": {
202
+ "label": "Лічильник",
203
+ "increment": "Збільшити"
204
+ }
205
+ }
206
+ ```
207
+
208
+ ```json fileName="locales/fr/about.json"
209
+ {
210
+ "counter": {
211
+ "label": "Лічильник",
212
+ "increment": "Збільшити"
213
+ }
214
+ }
215
+ ```
216
+
217
+ **Клієнтський компонент**
218
+
219
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
220
+ "use client";
221
+
222
+ import React, { useState } from "react";
223
+ import { useTranslations, useFormatter } from "next-intl";
224
+
225
+ const ClientComponentExample = () => {
226
+ // Задаємо область безпосередньо для вкладеного об'єкта
227
+ const t = useTranslations("about.counter");
228
+ const format = useFormatter();
229
+ const [count, setCount] = useState(0);
230
+
231
+ return (
232
+ <div>
233
+ <p>{format.number(count)}</p>
234
+ <button
235
+ aria-label={t("label")}
236
+ onClick={() => setCount((count) => count + 1)}
237
+ >
238
+ {t("increment")}
239
+ </button>
240
+ </div>
241
+ );
242
+ };
243
+ ```
244
+
245
+ > Не забудьте додати повідомлення "about" у клієнтські повідомлення сторінки
246
+ > (включайте лише ті простори імен, які справді потрібні вашому клієнтському компоненту).
247
+
248
+ ### Використання в серверному компоненті
249
+
250
+ Цей UI-компонент є серверним компонентом і може рендеритися під клієнтським компонентом (page → client → server). Зберігайте його синхронним, передаючи попередньо обчислені рядки.
251
+
252
+ ```tsx fileName="src/components/ServerComponent.tsx"
253
+ type ServerComponentProps = {
254
+ formattedCount: string;
255
+ label: string;
256
+ increment: string;
257
+ };
258
+
259
+ const ServerComponent = ({
260
+ formattedCount,
261
+ label,
262
+ increment,
263
+ }: ServerComponentProps) => {
264
+ return (
265
+ <div>
266
+ <p>{formattedCount}</p>
267
+ <button aria-label={label}>{increment}</button>
268
+ </div>
269
+ );
270
+ };
271
+ ```
272
+
273
+ Примітки:
274
+
275
+ - Обчислюйте `formattedCount` на сервері (наприклад, `const initialFormattedCount = format.number(0)`).
276
+ - Уникайте передачі функцій або несеріалізованих об'єктів у server components.
277
+
278
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
279
+ import type { Metadata } from "next";
280
+ import { locales, defaultLocale } from "@/i18n";
281
+ import { getTranslations } from "next-intl/server";
282
+
283
+ function localizedPath(locale: string, path: string) {
284
+ return locale === defaultLocale ? path : "/" + locale + path;
285
+ }
286
+
287
+ export async function generateMetadata({
288
+ params,
289
+ }: {
290
+ params: { locale: string };
291
+ }): Promise<Metadata> {
292
+ const { locale } = params;
293
+ const t = await getTranslations({ locale, namespace: "about" });
294
+
295
+ const url = "/about";
296
+ const languages = Object.fromEntries(
297
+ locales.map((locale) => [locale, localizedPath(locale, url)])
298
+ );
299
+
300
+ return {
301
+ title: t("title"),
302
+ description: t("description"),
303
+ alternates: {
304
+ canonical: localizedPath(locale, url),
305
+ languages: { ...languages, "x-default": url },
306
+ },
307
+ };
308
+ }
309
+
310
+ // ... Rest of the page code
311
+ ```
312
+
313
+ ```tsx fileName="src/app/sitemap.ts"
314
+ import type { MetadataRoute } from "next";
315
+ import { locales, defaultLocale } from "@/i18n";
316
+
317
+ const origin = "https://example.com";
318
+
319
+ const formatterLocalizedPath = (locale: string, path: string) =>
320
+ locale === defaultLocale ? origin + path : origin + "/" + locale + path;
321
+
322
+ export default function sitemap(): MetadataRoute.Sitemap {
323
+ const aboutLanguages = Object.fromEntries(
324
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
325
+ );
326
+
327
+ return [
328
+ {
329
+ url: formatterLocalizedPath(defaultLocale, "/about"),
330
+ lastModified: new Date(),
331
+ changeFrequency: "monthly",
332
+ priority: 0.7,
333
+ alternates: { languages: aboutLanguages },
334
+ },
335
+ ];
336
+ }
337
+ ```
338
+
339
+ ```tsx fileName="src/app/robots.ts"
340
+ import type { MetadataRoute } from "next";
341
+ import { locales, defaultLocale } from "@/i18n";
342
+
343
+ const origin = "https://example.com";
344
+ const withAllLocales = (path: string) => [
345
+ path,
346
+ ...locales
347
+ .filter((locale) => locale !== defaultLocale)
348
+ .map((locale) => "/" + locale + path),
349
+ ];
350
+
351
+ export default function robots(): MetadataRoute.Robots {
352
+ const disallow = [
353
+ ...withAllLocales("/dashboard"),
354
+ ...withAllLocales("/admin"),
355
+ ];
356
+
357
+ return {
358
+ rules: { userAgent: "*", allow: ["/"], disallow },
359
+ host: origin,
360
+ sitemap: origin + "/sitemap.xml",
361
+ };
362
+ }
363
+ ```
364
+
365
+ ### Middleware для маршрутизації локалі
366
+
367
+ Додайте middleware для виявлення локалі та маршрутизації:
368
+
369
+ ```ts fileName="src/middleware.ts"
370
+ import createMiddleware from "next-intl/middleware";
371
+ import { locales, defaultLocale } from "@/i18n";
372
+
373
+ export default createMiddleware({
374
+ locales: [...locales],
375
+ defaultLocale,
376
+ localeDetection: true,
377
+ });
378
+
379
+ export const config = {
380
+ // Пропустити API, внутрішні компоненти Next і статичні файли
381
+ matcher: ["/((?!api|_next|.*\\..*).*)"],
382
+ };
383
+ ```
384
+
385
+ ### Найкращі практики
386
+
387
+ - **Встановіть атрибути `lang` та `dir` для html**: У `src/app/[locale]/layout.tsx` обчисліть `dir` за допомогою `getLocaleDirection(locale)` і встановіть `<html lang={locale} dir={dir}>`.
388
+ - **Розділяйте повідомлення за просторами імен**: Організуйте JSON за локалями та просторами імен (наприклад, `common.json`, `about.json`).
389
+ - **Мінімізуйте навантаження на клієнт**: На сторінках надсилайте в `NextIntlClientProvider` лише необхідні простори імен (наприклад, `pick(messages, ['common', 'about'])`).
390
+ - **Віддавайте перевагу статичним сторінкам**: Експортуйте `export const dynamic = 'force-static'` та генеруйте статичні параметри для всіх `locales`.
391
+ - **Синхронні серверні компоненти**: Передавайте попередньо обчислені рядки (перекладені підписи, відформатовані числа) замість асинхронних викликів або несеріалізованих функцій.
392
+
393
+ ## Впровадження Intlayer поверх next-intl
394
+
395
+ Встановіть залежності intlayer:
396
+
397
+ ```bash packageManager="npm"
398
+ npm install intlayer @intlayer/sync-json-plugin --save-dev
399
+ ```
400
+
401
+ ```bash packageManager="yarn"
402
+ yarn add intlayer @intlayer/sync-json-plugin --dev
403
+ ```
404
+
405
+ ```bash packageManager="pnpm"
406
+ pnpm add intlayer @intlayer/sync-json-plugin --save-dev
407
+ ```
408
+
409
+ ```bash packageManager="bun"
410
+ bun add intlayer @intlayer/sync-json-plugin --dev
411
+ bunx intlayer init
412
+ ```
413
+
414
+ Створіть файл конфігурації intlayer:
415
+
416
+ ```tsx fileName="intlayer.config.ts"
417
+ import { type IntlayerConfig, Locales } from "intlayer";
418
+ import { syncJSON } from "@intlayer/sync-json-plugin";
419
+
420
+ const config: IntlayerConfig = {
421
+ internationalization: {
422
+ locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
423
+ defaultLocale: Locales.ENGLISH,
424
+ },
425
+ ai: {
426
+ apiKey: process.env.OPENAI_API_KEY,
427
+ },
428
+ plugins: [
429
+ // Підтримуйте структуру папок для кожного namespace синхронізованою з Intlayer
430
+ syncJSON({
431
+ format: "icu",
432
+ source: ({ key, locale }) => `./locales/${locale}/${key}.json`,
433
+ }),
434
+ ],
435
+ };
436
+
437
+ export default config;
438
+ ```
439
+
440
+ Додайте скрипти в `package.json`:
441
+
442
+ ```json fileName="package.json"
443
+ {
444
+ "scripts": {
445
+ "i18n:fill": "intlayer fill",
446
+ "i18n:test": "intlayer test"
447
+ }
448
+ }
449
+ ```
450
+
451
+ Примітки:
452
+
453
+ - `intlayer fill`: використовує вашого AI-провайдера для заповнення відсутніх перекладів на основі налаштованих локалей.
454
+ - `intlayer test`: перевіряє на наявність відсутніх/недійсних перекладів (використовуйте у CI).
455
+
456
+ Ви можете налаштувати аргументи та провайдерів; див. [Intlayer CLI](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/cli/index.md).