@intlayer/docs 7.5.11 → 7.5.13
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/uk/compiler_vs_declarative_i18n.md +224 -0
- package/blog/uk/i18n_using_next-i18next.md +1086 -0
- package/blog/uk/i18n_using_next-intl.md +760 -0
- package/blog/uk/index.md +69 -0
- package/blog/uk/internationalization_and_SEO.md +273 -0
- package/blog/uk/intlayer_with_i18next.md +211 -0
- package/blog/uk/intlayer_with_next-i18next.md +202 -0
- package/blog/uk/intlayer_with_next-intl.md +203 -0
- package/blog/uk/intlayer_with_react-i18next.md +200 -0
- package/blog/uk/intlayer_with_react-intl.md +202 -0
- package/blog/uk/intlayer_with_vue-i18n.md +206 -0
- package/blog/uk/l10n_platform_alternative/Lokalise.md +80 -0
- package/blog/uk/l10n_platform_alternative/crowdin.md +80 -0
- package/blog/uk/l10n_platform_alternative/phrase.md +78 -0
- package/blog/uk/list_i18n_technologies/CMS/drupal.md +143 -0
- package/blog/uk/list_i18n_technologies/CMS/wix.md +167 -0
- package/blog/uk/list_i18n_technologies/CMS/wordpress.md +189 -0
- package/blog/uk/list_i18n_technologies/frameworks/angular.md +125 -0
- package/blog/uk/list_i18n_technologies/frameworks/flutter.md +128 -0
- package/blog/uk/list_i18n_technologies/frameworks/react-native.md +217 -0
- package/blog/uk/list_i18n_technologies/frameworks/react.md +155 -0
- package/blog/uk/list_i18n_technologies/frameworks/svelte.md +145 -0
- package/blog/uk/list_i18n_technologies/frameworks/vue.md +144 -0
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +1499 -0
- package/blog/uk/nextjs-multilingual-seo-comparison.md +360 -0
- package/blog/uk/rag_powered_documentation_assistant.md +288 -0
- package/blog/uk/react-i18next_vs_react-intl_vs_intlayer.md +164 -0
- package/blog/uk/vue-i18n_vs_intlayer.md +279 -0
- package/blog/uk/what_is_internationalization.md +167 -0
- package/blog/vi/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 +218 -99
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/cjs/generated/frequentQuestions.entry.cjs +50 -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 +218 -99
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +50 -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 +1 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/dist/types/generated/frequentQuestions.entry.d.ts +1 -0
- package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
- package/dist/types/generated/legal.entry.d.ts.map +1 -1
- package/docs/ar/configuration.md +6 -1
- package/docs/ar/dictionary/content_file.md +6 -1
- 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/configuration.md +6 -1
- package/docs/de/dictionary/content_file.md +6 -1
- 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/configuration.md +6 -1
- package/docs/en/dictionary/content_file.md +6 -1
- 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/configuration.md +6 -1
- package/docs/en-GB/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/es/dictionary/content_file.md +6 -1
- 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/configuration.md +6 -1
- package/docs/fr/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/hi/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/id/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/it/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/ja/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/ko/dictionary/content_file.md +3 -1
- 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/configuration.md +3 -1
- package/docs/pl/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/pt/dictionary/content_file.md +3 -1
- 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/configuration.md +6 -1
- package/docs/ru/dictionary/content_file.md +6 -1
- 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/configuration.md +6 -1
- package/docs/tr/dictionary/content_file.md +3 -1
- 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/CI_CD.md +198 -0
- package/docs/uk/autoFill.md +307 -0
- package/docs/uk/bundle_optimization.md +185 -0
- package/docs/uk/cli/build.md +64 -0
- package/docs/uk/cli/ci.md +137 -0
- package/docs/uk/cli/configuration.md +63 -0
- package/docs/uk/cli/debug.md +46 -0
- package/docs/uk/cli/doc-review.md +43 -0
- package/docs/uk/cli/doc-translate.md +132 -0
- package/docs/uk/cli/editor.md +28 -0
- package/docs/uk/cli/fill.md +130 -0
- package/docs/uk/cli/index.md +190 -0
- package/docs/uk/cli/init.md +84 -0
- package/docs/uk/cli/list.md +90 -0
- package/docs/uk/cli/list_projects.md +128 -0
- package/docs/uk/cli/live.md +41 -0
- package/docs/uk/cli/login.md +157 -0
- package/docs/uk/cli/pull.md +78 -0
- package/docs/uk/cli/push.md +98 -0
- package/docs/uk/cli/sdk.md +71 -0
- package/docs/uk/cli/test.md +76 -0
- package/docs/uk/cli/transform.md +65 -0
- package/docs/uk/cli/version.md +24 -0
- package/docs/uk/cli/watch.md +37 -0
- package/docs/uk/compiler.md +133 -0
- package/docs/uk/component_i18n.md +194 -0
- package/docs/uk/configuration.md +742 -0
- package/docs/uk/dictionary/condition.md +237 -0
- package/docs/uk/dictionary/content_file.md +1134 -0
- package/docs/uk/dictionary/enumeration.md +245 -0
- package/docs/uk/dictionary/file.md +232 -0
- package/docs/uk/dictionary/function_fetching.md +212 -0
- package/docs/uk/dictionary/gender.md +273 -0
- package/docs/uk/dictionary/insertion.md +187 -0
- package/docs/uk/dictionary/markdown.md +383 -0
- package/docs/uk/dictionary/nesting.md +273 -0
- package/docs/uk/dictionary/translation.md +332 -0
- package/docs/uk/formatters.md +595 -0
- package/docs/uk/how_works_intlayer.md +256 -0
- package/docs/uk/index.md +175 -0
- package/docs/uk/interest_of_intlayer.md +297 -0
- package/docs/uk/intlayer_CMS.md +569 -0
- package/docs/uk/intlayer_visual_editor.md +292 -0
- package/docs/uk/intlayer_with_angular.md +710 -0
- package/docs/uk/intlayer_with_astro.md +256 -0
- package/docs/uk/intlayer_with_create_react_app.md +1258 -0
- package/docs/uk/intlayer_with_express.md +429 -0
- package/docs/uk/intlayer_with_fastify.md +446 -0
- package/docs/uk/intlayer_with_lynx+react.md +548 -0
- package/docs/uk/intlayer_with_nestjs.md +283 -0
- package/docs/uk/intlayer_with_next-i18next.md +640 -0
- package/docs/uk/intlayer_with_next-intl.md +456 -0
- package/docs/uk/intlayer_with_nextjs_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_nextjs_page_router.md +1541 -0
- package/docs/uk/intlayer_with_nuxt.md +711 -0
- package/docs/uk/intlayer_with_react_native+expo.md +715 -0
- package/docs/uk/intlayer_with_react_router_v7.md +600 -0
- package/docs/uk/intlayer_with_react_router_v7_fs_routes.md +669 -0
- package/docs/uk/intlayer_with_svelte_kit.md +579 -0
- package/docs/uk/intlayer_with_tanstack.md +818 -0
- package/docs/uk/intlayer_with_vite+preact.md +1748 -0
- package/docs/uk/intlayer_with_vite+react.md +1449 -0
- package/docs/uk/intlayer_with_vite+solid.md +302 -0
- package/docs/uk/intlayer_with_vite+svelte.md +520 -0
- package/docs/uk/intlayer_with_vite+vue.md +1113 -0
- package/docs/uk/introduction.md +222 -0
- package/docs/uk/locale_mapper.md +242 -0
- package/docs/uk/mcp_server.md +211 -0
- package/docs/uk/packages/express-intlayer/t.md +465 -0
- package/docs/uk/packages/intlayer/getConfiguration.md +145 -0
- package/docs/uk/packages/intlayer/getEnumeration.md +159 -0
- package/docs/uk/packages/intlayer/getHTMLTextDir.md +121 -0
- package/docs/uk/packages/intlayer/getLocaleLang.md +81 -0
- package/docs/uk/packages/intlayer/getLocaleName.md +135 -0
- package/docs/uk/packages/intlayer/getLocalizedUrl.md +338 -0
- package/docs/uk/packages/intlayer/getMultilingualUrls.md +359 -0
- package/docs/uk/packages/intlayer/getPathWithoutLocale.md +75 -0
- package/docs/uk/packages/intlayer/getPrefix.md +213 -0
- package/docs/uk/packages/intlayer/getTranslation.md +190 -0
- package/docs/uk/packages/intlayer/getTranslationContent.md +189 -0
- package/docs/uk/packages/next-intlayer/t.md +365 -0
- package/docs/uk/packages/next-intlayer/useDictionary.md +276 -0
- package/docs/uk/packages/next-intlayer/useIntlayer.md +263 -0
- package/docs/uk/packages/next-intlayer/useLocale.md +166 -0
- package/docs/uk/packages/react-intlayer/t.md +311 -0
- package/docs/uk/packages/react-intlayer/useDictionary.md +295 -0
- package/docs/uk/packages/react-intlayer/useI18n.md +250 -0
- package/docs/uk/packages/react-intlayer/useIntlayer.md +251 -0
- package/docs/uk/packages/react-intlayer/useLocale.md +210 -0
- package/docs/uk/per_locale_file.md +345 -0
- package/docs/uk/plugins/sync-json.md +398 -0
- package/docs/uk/readme.md +265 -0
- package/docs/uk/releases/v6.md +305 -0
- package/docs/uk/releases/v7.md +624 -0
- package/docs/uk/roadmap.md +346 -0
- package/docs/uk/testing.md +204 -0
- package/docs/uk/vs_code_extension.md +133 -0
- package/docs/vi/configuration.md +6 -1
- package/docs/vi/dictionary/content_file.md +6 -1
- 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/configuration.md +6 -1
- package/docs/zh/dictionary/content_file.md +6 -1
- 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/ar/error-vite-env-only.md +77 -0
- package/frequent_questions/de/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/de/error-vite-env-only.md +77 -0
- package/frequent_questions/en/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/en/error-vite-env-only.md +77 -0
- package/frequent_questions/en-GB/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/en-GB/error-vite-env-only.md +77 -0
- package/frequent_questions/es/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/es/error-vite-env-only.md +76 -0
- package/frequent_questions/fr/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/fr/error-vite-env-only.md +77 -0
- package/frequent_questions/hi/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/hi/error-vite-env-only.md +77 -0
- package/frequent_questions/id/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/id/error-vite-env-only.md +77 -0
- package/frequent_questions/it/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/it/error-vite-env-only.md +77 -0
- package/frequent_questions/ja/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ja/error-vite-env-only.md +77 -0
- package/frequent_questions/ko/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ko/error-vite-env-only.md +77 -0
- package/frequent_questions/pl/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/pl/error-vite-env-only.md +77 -0
- package/frequent_questions/pt/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/pt/error-vite-env-only.md +77 -0
- package/frequent_questions/ru/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ru/error-vite-env-only.md +77 -0
- package/frequent_questions/tr/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/tr/error-vite-env-only.md +77 -0
- package/frequent_questions/uk/SSR_Next_no_[locale].md +104 -0
- package/frequent_questions/uk/array_as_content_declaration.md +72 -0
- package/frequent_questions/uk/build_dictionaries.md +58 -0
- package/frequent_questions/uk/build_error_CI_CD.md +74 -0
- package/frequent_questions/uk/bun_set_up.md +53 -0
- package/frequent_questions/uk/customized_locale_list.md +64 -0
- package/frequent_questions/uk/domain_routing.md +113 -0
- package/frequent_questions/uk/error-vite-env-only.md +77 -0
- package/frequent_questions/uk/esbuild_error.md +29 -0
- package/frequent_questions/uk/get_locale_cookie.md +142 -0
- package/frequent_questions/uk/intlayer_command_undefined.md +155 -0
- package/frequent_questions/uk/locale_incorect_in_url.md +73 -0
- package/frequent_questions/uk/package_version_error.md +181 -0
- package/frequent_questions/uk/static_rendering.md +44 -0
- package/frequent_questions/uk/translated_path_url.md +55 -0
- package/frequent_questions/uk/unknown_command.md +97 -0
- package/frequent_questions/vi/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/vi/error-vite-env-only.md +77 -0
- package/frequent_questions/zh/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/zh/error-vite-env-only.md +77 -0
- package/legal/uk/privacy_notice.md +83 -0
- package/legal/uk/terms_of_service.md +55 -0
- package/package.json +6 -6
- package/src/generated/blog.entry.ts +29 -0
- package/src/generated/docs.entry.ts +119 -0
- package/src/generated/frequentQuestions.entry.ts +35 -0
- package/src/generated/legal.entry.ts +2 -0
|
@@ -0,0 +1,1910 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-10-25
|
|
3
|
+
updatedAt: 2025-12-30
|
|
4
|
+
title: Як перекласти ваш додаток Next.js 15 — посібник з i18n 2026
|
|
5
|
+
description: Дізнайтеся, як зробити ваш вебсайт на Next.js 15 багатомовним. Дотримуйтесь документації для інтернаціоналізації (i18n) та перекладу.
|
|
6
|
+
keywords:
|
|
7
|
+
- Інтернаціоналізація
|
|
8
|
+
- Документація
|
|
9
|
+
- Intlayer
|
|
10
|
+
- Next.js 15
|
|
11
|
+
- JavaScript
|
|
12
|
+
- React
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- environment
|
|
16
|
+
- nextjs
|
|
17
|
+
- 15
|
|
18
|
+
applicationTemplate: https://github.com/aymericzip/intlayer-next-15-template
|
|
19
|
+
youtubeVideo: https://www.youtube.com/watch?v=e_PPG7PTqGU
|
|
20
|
+
history:
|
|
21
|
+
- version: 7.5.9
|
|
22
|
+
date: 2025-12-30
|
|
23
|
+
changes: Додано команду init
|
|
24
|
+
- version: 7.0.6
|
|
25
|
+
date: 2025-11-01
|
|
26
|
+
changes: Додано згадку про `x-default` в об'єкті `alternates`
|
|
27
|
+
- version: 7.0.0
|
|
28
|
+
date: 2025-10-25
|
|
29
|
+
changes: Додано згадку про функцію `withIntlayerSync()`
|
|
30
|
+
- version: 6.2.0
|
|
31
|
+
date: 2025-10-09
|
|
32
|
+
changes: Додано документацію для хука `useLocale` з опцією `onLocaleChange`
|
|
33
|
+
- version: 5.6.6
|
|
34
|
+
date: 2025-10-02
|
|
35
|
+
changes: Додано документацію для функції `getLocale` у server actions
|
|
36
|
+
- version: 5.6.2
|
|
37
|
+
date: 2025-09-23
|
|
38
|
+
changes: Додано документацію щодо відстеження змін словників у Turbopack
|
|
39
|
+
- version: 5.6.2
|
|
40
|
+
date: 2025-09-22
|
|
41
|
+
changes: Додано документацію для хелпера `multipleMiddlewares`
|
|
42
|
+
- version: 5.6.0
|
|
43
|
+
date: 2025-07-06
|
|
44
|
+
changes: Трансформовано функцію `withIntlayer()` на promise-based функцію
|
|
45
|
+
- version: 5.5.10
|
|
46
|
+
date: 2025-06-29
|
|
47
|
+
changes: Ініціалізація історії
|
|
48
|
+
changes: Додано згадку про функцію `withIntlayerSync()`
|
|
49
|
+
- version: 6.2.0
|
|
50
|
+
date: 2025-10-09
|
|
51
|
+
changes: Додано документацію для хука `useLocale` з опцією `onLocaleChange`
|
|
52
|
+
- version: 5.6.6
|
|
53
|
+
date: 2025-10-02
|
|
54
|
+
changes: Додано документацію для функції `getLocale` у server actions
|
|
55
|
+
- version: 5.6.2
|
|
56
|
+
date: 2025-09-23
|
|
57
|
+
changes: Додано документацію щодо відстеження змін словників у Turbopack
|
|
58
|
+
- version: 5.6.2
|
|
59
|
+
date: 2025-09-22
|
|
60
|
+
changes: Додано документацію для хелпера `multipleMiddlewares`
|
|
61
|
+
- version: 5.6.0
|
|
62
|
+
date: 2025-07-06
|
|
63
|
+
changes: Перетворено функцію `withIntlayer()` на функцію, яка повертає Promise
|
|
64
|
+
- version: 5.5.10
|
|
65
|
+
date: 2025-06-29
|
|
66
|
+
changes: Ініціалізація історії
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
# Перекладіть ваш вебсайт Next.js 15 за допомогою Intlayer | Інтернаціоналізація (i18n)
|
|
70
|
+
|
|
71
|
+
## Зміст
|
|
72
|
+
|
|
73
|
+
<TOC/>
|
|
74
|
+
|
|
75
|
+
## Що таке Intlayer?
|
|
76
|
+
|
|
77
|
+
**Intlayer** — інноваційна бібліотека для інтернаціоналізації (i18n) з відкритим кодом, створена для спрощення багатомовної підтримки в сучасних веб‑додатках. Intlayer плавно інтегрується з останньою версією **Next.js 15**, включаючи його потужний **App Router**. Вона оптимізована для роботи з **Server Components** для ефективного рендерингу і повністю сумісна з [**Turbopack**](https://nextjs.org/docs/architecture/turbopack).
|
|
78
|
+
|
|
79
|
+
З Intlayer ви можете:
|
|
80
|
+
|
|
81
|
+
- **Легко керувати перекладами** за допомогою декларативних словників на рівні компонентів.
|
|
82
|
+
- **Динамічно локалізувати метадані**, маршрути та вміст.
|
|
83
|
+
- **Отримувати доступ до перекладів як у клієнтських, так і в серверних компонентах**.
|
|
84
|
+
- **Забезпечте підтримку TypeScript** за допомогою автогенерованих типів, що покращують автозаповнення та виявлення помилок.
|
|
85
|
+
- **Отримайте переваги від розширених функцій**, таких як динамічне визначення локалі та її перемикання.
|
|
86
|
+
|
|
87
|
+
> Intlayer сумісний з Next.js 12, 13, 14 та 15. Якщо ви використовуєте 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).
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Покроковий посібник з налаштування Intlayer у додатку Next.js
|
|
92
|
+
|
|
93
|
+
<Tab defaultTab="video">
|
|
94
|
+
<TabItem label="Відео" value="video">
|
|
95
|
+
|
|
96
|
+
<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"/>
|
|
97
|
+
|
|
98
|
+
</TabItem>
|
|
99
|
+
<TabItem label="Код" value="code">
|
|
100
|
+
|
|
101
|
+
<iframe
|
|
102
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-next-15-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
103
|
+
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
104
|
+
title="Демо CodeSandbox — Як інтернаціоналізувати ваш застосунок за допомогою Intlayer"
|
|
105
|
+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
106
|
+
loading="lazy"
|
|
107
|
+
/>
|
|
108
|
+
|
|
109
|
+
</TabItem>
|
|
110
|
+
</Tab>
|
|
111
|
+
|
|
112
|
+
Дивіться [Application Template](https://github.com/aymericzip/intlayer-next-15-template) на GitHub.
|
|
113
|
+
|
|
114
|
+
### Крок 1: Встановлення залежностей
|
|
115
|
+
|
|
116
|
+
Встановіть необхідні пакети за допомогою npm:
|
|
117
|
+
|
|
118
|
+
```bash packageManager="npm"
|
|
119
|
+
npm install intlayer next-intlayer
|
|
120
|
+
npx intlayer init
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```bash packageManager="pnpm"
|
|
124
|
+
pnpm add intlayer next-intlayer
|
|
125
|
+
pnpm intlayer init
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```bash packageManager="yarn"
|
|
129
|
+
yarn add intlayer next-intlayer
|
|
130
|
+
yarn intlayer init
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```bash packageManager="bun"
|
|
134
|
+
bun add intlayer next-intlayer
|
|
135
|
+
bunx intlayer init
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- **intlayer**
|
|
139
|
+
|
|
140
|
+
Основний пакет, який надає інструменти інтернаціоналізації для управління конфігурацією, перекладів, [декларації контенту](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).
|
|
141
|
+
|
|
142
|
+
- **next-intlayer**
|
|
143
|
+
|
|
144
|
+
Пакет, що інтегрує Intlayer з Next.js. Він забезпечує провайдери контексту та хуки для інтернаціоналізації в Next.js. Крім того, він включає плагін для Next.js для інтеграції Intlayer з [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack), а також middleware для визначення пріоритетної локалі користувача, керування cookies та обробки перенаправлень URL.
|
|
145
|
+
|
|
146
|
+
Основний пакет, який надає інструменти інтернаціоналізації для управління конфігурацією, перекладу, [декларації контенту](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).
|
|
147
|
+
|
|
148
|
+
- **next-intlayer**
|
|
149
|
+
|
|
150
|
+
Пакет, який інтегрує Intlayer з Next.js. Він надає провайдери контексту та хуки для інтернаціоналізації в Next.js. Крім того, включає плагін Next.js для інтеграції Intlayer з [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack), а також middleware для виявлення переважної локалі користувача, керування cookie та обробки перенаправлень URL.
|
|
151
|
+
|
|
152
|
+
### Крок 2: Налаштуйте свій проєкт
|
|
153
|
+
|
|
154
|
+
Here is the final structure that we will make:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
.
|
|
158
|
+
├── src
|
|
159
|
+
│ ├── app
|
|
160
|
+
│ │ ├── [locale]
|
|
161
|
+
│ │ │ ├── layout.tsx # Locale layout for the Intlayer provider
|
|
162
|
+
│ │ │ ├── page.content.ts
|
|
163
|
+
│ │ │ └── page.tsx
|
|
164
|
+
│ │ └── layout.tsx # Root layout for style and global providers
|
|
165
|
+
│ ├── components
|
|
166
|
+
│ │ ├── client-component-example.content.ts
|
|
167
|
+
│ │ ├── ClientComponentExample.tsx
|
|
168
|
+
│ │ ├── LocaleSwitcher
|
|
169
|
+
│ │ │ ├── localeSwitcher.content.ts
|
|
170
|
+
│ │ │ └── LocaleSwitcher.tsx
|
|
171
|
+
│ │ ├── server-component-example.content.ts
|
|
172
|
+
│ │ └── ServerComponentExample.tsx
|
|
173
|
+
│ └── middleware.ts
|
|
174
|
+
├── intlayer.config.ts
|
|
175
|
+
├── next.config.ts
|
|
176
|
+
├── package.json
|
|
177
|
+
└── tsconfig.json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
> If you don't want locale routing, intlayer can be used as a simple provider / hook. See [this guide](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_with_nextjs_no_locale_path.md) for more details.
|
|
181
|
+
|
|
182
|
+
Створіть файл конфігурації, щоб налаштувати мови вашого застосунку:
|
|
183
|
+
|
|
184
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
185
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
186
|
+
|
|
187
|
+
const config: IntlayerConfig = {
|
|
188
|
+
internationalization: {
|
|
189
|
+
locales: [
|
|
190
|
+
Locales.ENGLISH,
|
|
191
|
+
Locales.FRENCH,
|
|
192
|
+
Locales.SPANISH,
|
|
193
|
+
// Інші ваші локалі
|
|
194
|
+
],
|
|
195
|
+
defaultLocale: Locales.ENGLISH,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export default config;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```javascript fileName="intlayer.config.mjs" codeFormat="esm"
|
|
203
|
+
import { Locales } from "intlayer";
|
|
204
|
+
|
|
205
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
206
|
+
const config = {
|
|
207
|
+
internationalization: {
|
|
208
|
+
locales: [
|
|
209
|
+
Locales.ENGLISH,
|
|
210
|
+
Locales.FRENCH,
|
|
211
|
+
Locales.SPANISH,
|
|
212
|
+
// Інші ваші локалі
|
|
213
|
+
],
|
|
214
|
+
defaultLocale: Locales.ENGLISH,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export default config;
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
222
|
+
const { Locales } = require("intlayer");
|
|
223
|
+
|
|
224
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
225
|
+
const config = {
|
|
226
|
+
internationalization: {
|
|
227
|
+
locales: [
|
|
228
|
+
Locales.ENGLISH,
|
|
229
|
+
Locales.FRENCH,
|
|
230
|
+
Locales.SPANISH,
|
|
231
|
+
// Your other locales
|
|
232
|
+
],
|
|
233
|
+
defaultLocale: Locales.ENGLISH,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
module.exports = config;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> Через цей файл конфігурації ви можете налаштувати локалізовані URL-адреси, перенаправлення через middleware, назви cookie, розташування та розширення ваших декларацій контенту, відключити логи Intlayer у консолі та інше. Для повного переліку доступних параметрів зверніться до [документації з конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md).
|
|
241
|
+
|
|
242
|
+
### Крок 3: Інтеграція Intlayer у конфігурацію Next.js
|
|
243
|
+
|
|
244
|
+
Налаштуйте конфігурацію Next.js для використання Intlayer:
|
|
245
|
+
|
|
246
|
+
```typescript fileName="next.config.ts" codeFormat="typescript"
|
|
247
|
+
import type { NextConfig } from "next";
|
|
248
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
249
|
+
|
|
250
|
+
const nextConfig: NextConfig = {
|
|
251
|
+
/* параметри конфігурації тут */
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export default withIntlayer(nextConfig);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```typescript fileName="next.config.mjs" codeFormat="esm"
|
|
258
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
259
|
+
|
|
260
|
+
/** @type {import('next').NextConfig} */
|
|
261
|
+
const nextConfig = {
|
|
262
|
+
/* опції конфігурації тут */
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export default withIntlayer(nextConfig);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```typescript fileName="next.config.cjs" codeFormat="commonjs"
|
|
269
|
+
const { withIntlayer } = require("next-intlayer/server");
|
|
270
|
+
|
|
271
|
+
/** @type {import('next').NextConfig} */
|
|
272
|
+
const nextConfig = {
|
|
273
|
+
/* опції конфігурації тут */
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
module.exports = withIntlayer(nextConfig);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> Плагін Next.js `withIntlayer()` використовується для інтеграції Intlayer з Next.js. Він забезпечує побудову файлів декларації контенту та відстежує їх у режимі розробки. Він визначає змінні середовища Intlayer у середовищах [Webpack](https://webpack.js.org/) або [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack). Додатково він надає aliases для оптимізації продуктивності та забезпечує сумісність із серверними компонентами.
|
|
280
|
+
>
|
|
281
|
+
> Функція `withIntlayer()` повертає проміс. Вона дозволяє підготувати словники Intlayer перед початком збірки. Якщо ви хочете використовувати її з іншими плагінами, ви можете використати await. Приклад:
|
|
282
|
+
>
|
|
283
|
+
> ```tsx
|
|
284
|
+
> const nextConfig = await withIntlayer(nextConfig);
|
|
285
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
286
|
+
>
|
|
287
|
+
> export default nextConfigWithOtherPlugins;
|
|
288
|
+
> ```
|
|
289
|
+
>
|
|
290
|
+
> Якщо ви хочете використовувати його синхронно, ви можете скористатися функцією `withIntlayerSync()`. Приклад:
|
|
291
|
+
>
|
|
292
|
+
> ```tsx
|
|
293
|
+
> const nextConfig = withIntlayerSync(nextConfig);
|
|
294
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
295
|
+
>
|
|
296
|
+
> export default nextConfigWithOtherPlugins;
|
|
297
|
+
> ```
|
|
298
|
+
|
|
299
|
+
### Крок 4: Визначення динамічних маршрутів локалі
|
|
300
|
+
|
|
301
|
+
Видаліть усе з `RootLayout` і замініть на наступний код:
|
|
302
|
+
|
|
303
|
+
```tsx {3} fileName="src/app/layout.tsx" codeFormat="typescript"
|
|
304
|
+
import type { PropsWithChildren, FC } from "react";
|
|
305
|
+
import "./globals.css";
|
|
306
|
+
|
|
307
|
+
const RootLayout: FC<PropsWithChildren> = ({ children }) => (
|
|
308
|
+
// Ви все ще можете обгорнути children іншими провайдерами, наприклад `next-themes`, `react-query`, `framer-motion` тощо.
|
|
309
|
+
<>{children}</>
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
export default RootLayout;
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
```jsx {3} fileName="src/app/layout.mjx" codeFormat="esm"
|
|
316
|
+
import "./globals.css";
|
|
317
|
+
|
|
318
|
+
const RootLayout = ({ children }) => (
|
|
319
|
+
// Ви все ще можете обгорнути children іншими провайдерами, наприклад `next-themes`, `react-query`, `framer-motion` тощо.
|
|
320
|
+
<>{children}</>
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
export default RootLayout;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
```jsx {1,8} fileName="src/app/layout.csx" codeFormat="commonjs"
|
|
327
|
+
require("./globals.css");
|
|
328
|
+
|
|
329
|
+
const RootLayout = ({ children }) => (
|
|
330
|
+
// Ви все ще можете обгорнути children іншими провайдерами, наприклад `next-themes`, `react-query`, `framer-motion` тощо.
|
|
331
|
+
<>{children}</>
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
module.exports = {
|
|
335
|
+
default: RootLayout,
|
|
336
|
+
generateStaticParams,
|
|
337
|
+
};
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
> Залишаючи компонент `RootLayout` порожнім, ви дозволяєте встановлювати атрибути [`lang`](https://developer.mozilla.org/fr/docs/Web/HTML/Global_attributes/lang) та [`dir`](https://developer.mozilla.org/fr/docs/Web/HTML/Global_attributes/dir) для тега `<html>`.
|
|
341
|
+
|
|
342
|
+
Щоб реалізувати динамічну маршрутизацію, вкажіть шлях для локалі, додавши новий layout у ваш каталог `[locale]`:
|
|
343
|
+
|
|
344
|
+
```tsx fileName="src/app/[locale]/layout.tsx" codeFormat="typescript"
|
|
345
|
+
import type { NextLayoutIntlayer } from "next-intlayer";
|
|
346
|
+
import { Inter } from "next/font/google";
|
|
347
|
+
import { getHTMLTextDir } from "intlayer";
|
|
348
|
+
|
|
349
|
+
const inter = Inter({ subsets: ["latin"] });
|
|
350
|
+
|
|
351
|
+
const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
|
|
352
|
+
const { locale } = await params;
|
|
353
|
+
return (
|
|
354
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
355
|
+
<body className={inter.className}>{children}</body>
|
|
356
|
+
</html>
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export default LocaleLayout;
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```jsx fileName="src/app/[locale]/layout.mjx" codeFormat="esm"
|
|
364
|
+
import { getHTMLTextDir } from "intlayer";
|
|
365
|
+
|
|
366
|
+
const inter = Inter({ subsets: ["latin"] });
|
|
367
|
+
|
|
368
|
+
const LocaleLayout = async ({ children, params: { locale } }) => {
|
|
369
|
+
const { locale } = await params;
|
|
370
|
+
return (
|
|
371
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
372
|
+
<body className={inter.className}>{children}</body>
|
|
373
|
+
</html>
|
|
374
|
+
);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export default LocaleLayout;
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
```jsx fileName="src/app/[locale]/layout.csx" codeFormat="commonjs"
|
|
381
|
+
const { Inter } = require("next/font/google");
|
|
382
|
+
const { getHTMLTextDir } = require("intlayer");
|
|
383
|
+
|
|
384
|
+
const inter = Inter({ subsets: ["latin"] });
|
|
385
|
+
|
|
386
|
+
const LocaleLayout = async ({ children, params: { locale } }) => {
|
|
387
|
+
const { locale } = await params;
|
|
388
|
+
return (
|
|
389
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
390
|
+
<body className={inter.className}>{children}</body>
|
|
391
|
+
</html>
|
|
392
|
+
);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
module.exports = LocaleLayout;
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
> Сегмент шляху `[locale]` використовується для визначення локалі. Приклад: `/en-US/about` відповідатиме `en-US`, а `/fr/about` — `fr`.
|
|
399
|
+
|
|
400
|
+
> На цьому етапі ви зіткнетесь з помилкою: `Error: Missing <html> and <body> tags in the root layout.`. Це очікувано, оскільки файл `/app/page.tsx` більше не використовується і його можна видалити. Натомість сегмент шляху `[locale]` активуватиме сторінку `/app/[locale]/page.tsx`. Отже, сторінки будуть доступні за шляхами на кшталт `/en`, `/fr`, `/es` у вашому браузері. Щоб встановити локаль за замовчуванням для кореневої сторінки, зверніться до налаштування `middleware` у кроці 7.
|
|
401
|
+
|
|
402
|
+
Далі реалізуйте функцію `generateStaticParams` у Layout вашого застосунку.
|
|
403
|
+
|
|
404
|
+
```tsx {1} fileName="src/app/[locale]/layout.tsx" codeFormat="typescript"
|
|
405
|
+
export { generateStaticParams } from "next-intlayer"; // Line to insert
|
|
406
|
+
|
|
407
|
+
const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
|
|
408
|
+
/*... Решта коду */
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
export default LocaleLayout;
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
```jsx {1} fileName="src/app/[locale]/layout.mjx" codeFormat="esm"
|
|
415
|
+
export { generateStaticParams } from "next-intlayer"; // Рядок для вставки
|
|
416
|
+
|
|
417
|
+
const LocaleLayout = async ({ children, params: { locale } }) => {
|
|
418
|
+
/*... Решта коду*/
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// ... Решта коду
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
```jsx {1,7} fileName="src/app/[locale]/layout.csx" codeFormat="commonjs"
|
|
425
|
+
const { generateStaticParams } = require("next-intlayer"); // Рядок для вставки
|
|
426
|
+
|
|
427
|
+
const LocaleLayout = async ({ children, params: { locale } }) => {
|
|
428
|
+
/*... Решта коду*/
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
module.exports = { default: LocaleLayout, generateStaticParams };
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
> `generateStaticParams` гарантує, що ваш додаток попередньо збирає необхідні сторінки для всіх локалей, зменшуючи обчислення під час виконання та покращуючи взаємодію користувача. Для детальнішої інформації зверніться до [документації Next.js щодо generateStaticParams](https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic-rendering#generate-static-params).
|
|
435
|
+
|
|
436
|
+
> Intlayer працює з `export const dynamic = 'force-static';`, щоб забезпечити попередню збірку сторінок для всіх локалей.
|
|
437
|
+
|
|
438
|
+
### Крок 5: Оголосіть контент
|
|
439
|
+
|
|
440
|
+
Створіть і керуйте оголошеннями контенту для збереження перекладів:
|
|
441
|
+
|
|
442
|
+
```tsx fileName="src/app/[locale]/page.content.ts" contentDeclarationFormat="typescript"
|
|
443
|
+
import { t, type Dictionary } from "intlayer";
|
|
444
|
+
|
|
445
|
+
const pageContent = {
|
|
446
|
+
key: "page",
|
|
447
|
+
content: {
|
|
448
|
+
getStarted: {
|
|
449
|
+
main: t({
|
|
450
|
+
uk: "Почніть із редагування",
|
|
451
|
+
en: "Get started by editing",
|
|
452
|
+
fr: "Commencez par éditer",
|
|
453
|
+
es: "Comience por editar",
|
|
454
|
+
}),
|
|
455
|
+
pageLink: "src/app/page.tsx",
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
} satisfies Dictionary;
|
|
459
|
+
|
|
460
|
+
export default pageContent;
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
```javascript fileName="src/app/[locale]/page.content.mjs" contentDeclarationFormat="esm"
|
|
464
|
+
import { t } from "intlayer";
|
|
465
|
+
|
|
466
|
+
/** @type {import('intlayer').Dictionary} */
|
|
467
|
+
const pageContent = {
|
|
468
|
+
key: "page",
|
|
469
|
+
content: {
|
|
470
|
+
getStarted: {
|
|
471
|
+
main: t({
|
|
472
|
+
uk: "Почніть із редагування",
|
|
473
|
+
en: "Get started by editing",
|
|
474
|
+
fr: "Commencez par éditer",
|
|
475
|
+
es: "Comience por editar",
|
|
476
|
+
}),
|
|
477
|
+
pageLink: "src/app/page.tsx",
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
export default pageContent;
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
```javascript fileName="src/app/[locale]/page.content.cjs" contentDeclarationFormat="commonjs"
|
|
486
|
+
|
|
487
|
+
javascript fileName="src/app/[locale]/page.content.cjs" contentDeclarationFormat="commonjs"
|
|
488
|
+
const { t } = require("intlayer");
|
|
489
|
+
|
|
490
|
+
/** @type {import('intlayer').Dictionary} */
|
|
491
|
+
const pageContent = {
|
|
492
|
+
key: "page",
|
|
493
|
+
content: {
|
|
494
|
+
getStarted: {
|
|
495
|
+
main: t({
|
|
496
|
+
uk: "Почніть з редагування",
|
|
497
|
+
en: "Get started by editing",
|
|
498
|
+
fr: "Commencez par éditer",
|
|
499
|
+
es: "Comience por editar",
|
|
500
|
+
}),
|
|
501
|
+
pageLink: "src/app/page.tsx",
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
module.exports = pageContent;
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
```json fileName="src/app/[locale]/page.content.json" contentDeclarationFormat="json"
|
|
510
|
+
{
|
|
511
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
512
|
+
"key": "page",
|
|
513
|
+
"content": {
|
|
514
|
+
"getStarted": {
|
|
515
|
+
"nodeType": "translation",
|
|
516
|
+
"translation": {
|
|
517
|
+
"uk": "Почніть з редагування",
|
|
518
|
+
"en": "Get started by editing",
|
|
519
|
+
"fr": "Commencez par éditer",
|
|
520
|
+
"es": "Comience por editar"
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
"pageLink": "src/app/page.tsx"
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
> Декларації контенту можна визначати будь-де у вашому застосунку, якщо вони включені в директорію `contentDir` (за замовчуванням — `./src`). І вони повинні відповідати розширенню файлу декларації контенту (за замовчуванням — `.content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}`).
|
|
529
|
+
|
|
530
|
+
> Для детальнішої інформації зверніться до [документації щодо декларації контенту](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/dictionary/content_file.md).
|
|
531
|
+
|
|
532
|
+
### Крок 6: Використання контенту у коді
|
|
533
|
+
|
|
534
|
+
Отримуйте доступ до словників контенту у всьому застосунку:
|
|
535
|
+
|
|
536
|
+
```tsx fileName="src/app/[locale]/page.tsx" codeFormat="typescript"
|
|
537
|
+
import type { FC } from "react";
|
|
538
|
+
import { ClientComponentExample } from "@components/ClientComponentExample";
|
|
539
|
+
import { ServerComponentExample } from "@components/ServerComponentExample";
|
|
540
|
+
import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";
|
|
541
|
+
import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
|
|
542
|
+
|
|
543
|
+
const PageContent: FC = () => {
|
|
544
|
+
const content = useIntlayer("page");
|
|
545
|
+
|
|
546
|
+
return (
|
|
547
|
+
<>
|
|
548
|
+
<p>{content.getStarted.main}</p>
|
|
549
|
+
<code>{content.getStarted.pageLink}</code>
|
|
550
|
+
</>
|
|
551
|
+
);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
const Page: NextPageIntlayer = async ({ params }) => {
|
|
555
|
+
const { locale } = await params;
|
|
556
|
+
|
|
557
|
+
return (
|
|
558
|
+
<IntlayerServerProvider locale={locale}>
|
|
559
|
+
<PageContent />
|
|
560
|
+
<ServerComponentExample />
|
|
561
|
+
|
|
562
|
+
<IntlayerClientProvider locale={locale}>
|
|
563
|
+
<ClientComponentExample />
|
|
564
|
+
</IntlayerClientProvider>
|
|
565
|
+
</IntlayerServerProvider>
|
|
566
|
+
);
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export default Page;
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
```jsx fileName="src/app/[locale]/page.mjx" codeFormat="esm"
|
|
573
|
+
import { ClientComponentExample } from "@components/ClientComponentExample";
|
|
574
|
+
import { ServerComponentExample } from "@components/ServerComponentExample";
|
|
575
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
576
|
+
import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
|
|
577
|
+
|
|
578
|
+
const PageContent = () => {
|
|
579
|
+
const content = useIntlayer("page");
|
|
580
|
+
|
|
581
|
+
return (
|
|
582
|
+
<>
|
|
583
|
+
<p>{content.getStarted.main}</p>
|
|
584
|
+
<code>{content.getStarted.pageLink}</code>
|
|
585
|
+
</>
|
|
586
|
+
);
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const Page = async ({ params }) => {
|
|
590
|
+
const { locale } = await params;
|
|
591
|
+
|
|
592
|
+
return (
|
|
593
|
+
<IntlayerServerProvider locale={locale}>
|
|
594
|
+
<PageContent />
|
|
595
|
+
<ServerComponentExample />
|
|
596
|
+
|
|
597
|
+
<IntlayerClientProvider locale={locale}>
|
|
598
|
+
<ClientComponentExample />
|
|
599
|
+
</IntlayerClientProvider>
|
|
600
|
+
</IntlayerServerProvider>
|
|
601
|
+
);
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
export default Page;
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
```jsx fileName="src/app/[locale]/page.csx" codeFormat="commonjs"
|
|
608
|
+
import { ClientComponentExample } from "@components/ClientComponentExample";
|
|
609
|
+
import { ServerComponentExample } from "@components/ServerComponentExample";
|
|
610
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
611
|
+
import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
|
|
612
|
+
|
|
613
|
+
const PageContent = () => {
|
|
614
|
+
const content = useIntlayer("page");
|
|
615
|
+
|
|
616
|
+
return (
|
|
617
|
+
<>
|
|
618
|
+
<p>{content.getStarted.main}</p>
|
|
619
|
+
<code>{content.getStarted.pageLink}</code>
|
|
620
|
+
</>
|
|
621
|
+
);
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const Page = async ({ params }) => {
|
|
625
|
+
const { locale } = await params;
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<IntlayerServerProvider locale={locale}>
|
|
629
|
+
<PageContent />
|
|
630
|
+
<ServerComponentExample />
|
|
631
|
+
</IntlayerClientProvider>
|
|
632
|
+
);
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
export default Page;
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
- **`IntlayerClientProvider`** використовується для передачі локалі клієнтським компонентам. Його можна розмістити в будь-якому батьківському компоненті, включно з layout. Однак рекомендовано розміщувати його в layout, оскільки Next.js ділиться кодом layout між сторінками, що робить це ефективнішим. Використовуючи `IntlayerClientProvider` у layout, ви уникаєте повторної ініціалізації для кожної сторінки, покращуєте продуктивність і підтримуєте послідовний контекст локалізації у всьому вашому додатку.
|
|
639
|
+
- **`IntlayerServerProvider`** використовується для надання локалі серверним дочірнім компонентам. Його не можна встановлювати в layout.
|
|
640
|
+
|
|
641
|
+
> Layout і сторінка не можуть спільно використовувати спільний server context, оскільки система server context базується на сховищі даних для кожного запиту (через механізм [React's cache](https://react.dev/reference/react/cache)), що призводить до повторного створення кожного "контексту" для різних сегментів додатку. Розміщення провайдера в загальному layout порушить цю ізоляцію і не дозволить правильно передавати значення server context вашим server components.
|
|
642
|
+
|
|
643
|
+
> Layout і page не можуть мати спільного server context, оскільки система server context базується на сховищі даних на запит (через механізм [React's cache](https://react.dev/reference/react/cache)), внаслідок чого кожен «context» створюється заново для різних сегментів застосунку. Розміщення провайдера в спільному layout порушило б цю ізоляцію й не дозволило б коректно пропагувати значення server context до ваших server components.
|
|
644
|
+
|
|
645
|
+
```tsx {4,7} fileName="src/components/ClientComponentExample.tsx" codeFormat="typescript"
|
|
646
|
+
"use client";
|
|
647
|
+
|
|
648
|
+
import type { FC } from "react";
|
|
649
|
+
import { useIntlayer } from "next-intlayer";
|
|
650
|
+
|
|
651
|
+
export const ClientComponentExample: FC = () => {
|
|
652
|
+
const content = useIntlayer("client-component-example"); // Створити пов'язану декларацію вмісту
|
|
653
|
+
|
|
654
|
+
return (
|
|
655
|
+
<div>
|
|
656
|
+
<h2>{content.title}</h2>
|
|
657
|
+
<p>{content.content}</p>
|
|
658
|
+
</div>
|
|
659
|
+
);
|
|
660
|
+
};
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
```jsx {3,6} fileName="src/components/ClientComponentExample.mjx" codeFormat="esm"
|
|
664
|
+
"use client";
|
|
665
|
+
|
|
666
|
+
import { useIntlayer } from "next-intlayer";
|
|
667
|
+
|
|
668
|
+
const ClientComponentExample = () => {
|
|
669
|
+
const content = useIntlayer("client-component-example"); // Створити пов'язану декларацію контенту
|
|
670
|
+
|
|
671
|
+
return (
|
|
672
|
+
<div>
|
|
673
|
+
<h2>{content.title}</h2>
|
|
674
|
+
<p>{content.content}</p>
|
|
675
|
+
</div>
|
|
676
|
+
);
|
|
677
|
+
};
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
```jsx {3,6} fileName="src/components/ClientComponentExample.csx" codeFormat="commonjs"
|
|
681
|
+
"use client";
|
|
682
|
+
|
|
683
|
+
const { useIntlayer } = require("next-intlayer");
|
|
684
|
+
|
|
685
|
+
const ClientComponentExample = () => {
|
|
686
|
+
const content = useIntlayer("client-component-example"); // Створити пов'язану декларацію контенту
|
|
687
|
+
|
|
688
|
+
return (
|
|
689
|
+
<div>
|
|
690
|
+
<h2>{content.title}</h2>
|
|
691
|
+
<p>{content.content}</p>
|
|
692
|
+
</div>
|
|
693
|
+
);
|
|
694
|
+
};
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
```tsx {2} fileName="src/components/ServerComponentExample.tsx" codeFormat="typescript"
|
|
698
|
+
import type { FC } from "react";
|
|
699
|
+
import { useIntlayer } from "next-intlayer/server";
|
|
700
|
+
|
|
701
|
+
export const ServerComponentExample: FC = () => {
|
|
702
|
+
const content = useIntlayer("server-component-example"); // Створити пов'язане оголошення контенту
|
|
703
|
+
|
|
704
|
+
return (
|
|
705
|
+
<div>
|
|
706
|
+
<h2>{content.title}</h2>
|
|
707
|
+
<p>{content.content}</p>
|
|
708
|
+
</div>
|
|
709
|
+
);
|
|
710
|
+
};
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
```jsx {1} fileName="src/components/ServerComponentExample.mjx" codeFormat="esm"
|
|
714
|
+
import { useIntlayer } from "next-intlayer/server";
|
|
715
|
+
|
|
716
|
+
const ServerComponentExample = () => {
|
|
717
|
+
const content = useIntlayer("server-component-example"); // Створити пов'язане оголошення контенту
|
|
718
|
+
|
|
719
|
+
return (
|
|
720
|
+
<div>
|
|
721
|
+
<h2>{content.title}</h2>
|
|
722
|
+
<p>{content.content}</p>
|
|
723
|
+
</div>
|
|
724
|
+
);
|
|
725
|
+
};
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
```jsx {1} fileName="src/components/ServerComponentExample.csx" codeFormat="commonjs"
|
|
729
|
+
const { useIntlayer } = require("next-intlayer/server");
|
|
730
|
+
|
|
731
|
+
const ServerComponentExample = () => {
|
|
732
|
+
const content = useIntlayer("server-component-example"); // Створіть декларацію пов'язаного контенту
|
|
733
|
+
|
|
734
|
+
return (
|
|
735
|
+
<div>
|
|
736
|
+
<h2>{content.title}</h2>
|
|
737
|
+
<p>{content.content}</p>
|
|
738
|
+
</div>
|
|
739
|
+
);
|
|
740
|
+
};
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
> Якщо ви хочете використовувати свій вміст в атрибуті типу `string`, наприклад `alt`, `title`, `href`, `aria-label` тощо, ви повинні викликати значення функції, наприклад:
|
|
744
|
+
|
|
745
|
+
> ```jsx
|
|
746
|
+
> <img src={content.image.src.value} alt={content.image.value} />
|
|
747
|
+
> ```
|
|
748
|
+
|
|
749
|
+
> Щоб дізнатися більше про хук `useIntlayer`, див. [документацію](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useIntlayer.md).
|
|
750
|
+
|
|
751
|
+
### (Необов'язково) Крок 7: Налаштування middleware для визначення локалі
|
|
752
|
+
|
|
753
|
+
Налаштуйте middleware для визначення переважної локалі користувача:
|
|
754
|
+
|
|
755
|
+
```typescript fileName="src/middleware.ts" codeFormat="typescript"
|
|
756
|
+
export { intlayerMiddleware as middleware } from "next-intlayer/middleware";
|
|
757
|
+
|
|
758
|
+
export const config = {
|
|
759
|
+
matcher:
|
|
760
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
761
|
+
};
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
```javascript fileName="src/middleware.mjs" codeFormat="esm"
|
|
765
|
+
export { intlayerMiddleware as middleware } from "next-intlayer/middleware";
|
|
766
|
+
|
|
767
|
+
export const config = {
|
|
768
|
+
matcher:
|
|
769
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
770
|
+
};
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
```javascript fileName="src/middleware.cjs" codeFormat="commonjs"
|
|
774
|
+
const { intlayerMiddleware } = require("next-intlayer/middleware");
|
|
775
|
+
|
|
776
|
+
const config = {
|
|
777
|
+
matcher:
|
|
778
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
module.exports = { middleware: intlayerMiddleware, config };
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
> `intlayerMiddleware` використовується для визначення переважної мови користувача та перенаправлення його на відповідний URL, як вказано в [конфігурації](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/configuration.md). Крім того, він дозволяє зберігати перевагу мови користувача в cookie.
|
|
785
|
+
|
|
786
|
+
> Якщо вам потрібно зв'язати кілька middleware разом (наприклад, `intlayerMiddleware` з автентифікацією або кастомними middleware), Intlayer тепер надає хелпер під назвою `multipleMiddlewares`.
|
|
787
|
+
|
|
788
|
+
```ts
|
|
789
|
+
import {
|
|
790
|
+
multipleMiddlewares,
|
|
791
|
+
intlayerMiddleware,
|
|
792
|
+
} from "next-intlayer/middleware";
|
|
793
|
+
import { customMiddleware } from "@utils/customMiddleware";
|
|
794
|
+
|
|
795
|
+
export const middleware = multipleMiddlewares([
|
|
796
|
+
intlayerMiddleware,
|
|
797
|
+
customMiddleware,
|
|
798
|
+
]);
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### (Необов'язково) Крок 8: Інтернаціоналізація ваших метаданих
|
|
802
|
+
|
|
803
|
+
Якщо ви хочете інтернаціоналізувати свої метадані, такі як заголовок вашої сторінки, ви можете використати функцію `generateMetadata`, яку надає Next.js. Всередині неї ви можете отримати вміст через функцію `getIntlayer`, щоб перекласти свої метадані.
|
|
804
|
+
|
|
805
|
+
```typescript fileName="src/app/[locale]/metadata.content.ts" contentDeclarationFormat="typescript"
|
|
806
|
+
import { type Dictionary, t } from "intlayer";
|
|
807
|
+
import { Metadata } from "next";
|
|
808
|
+
|
|
809
|
+
const metadataContent = {
|
|
810
|
+
key: "page-metadata",
|
|
811
|
+
content: {
|
|
812
|
+
title: t({
|
|
813
|
+
uk: "Створити додаток Next",
|
|
814
|
+
en: "Create Next App",
|
|
815
|
+
fr: "Créer une application Next.js",
|
|
816
|
+
es: "Crear una aplicación Next.js",
|
|
817
|
+
}),
|
|
818
|
+
description: t({
|
|
819
|
+
uk: "Згенеровано за допомогою create next app",
|
|
820
|
+
en: "Generated by create next app",
|
|
821
|
+
fr: "Généré par create next app",
|
|
822
|
+
es: "Generado por create next app",
|
|
823
|
+
}),
|
|
824
|
+
},
|
|
825
|
+
} satisfies Dictionary<Metadata>;
|
|
826
|
+
|
|
827
|
+
export default metadataContent;
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
```javascript fileName="src/app/[locale]/metadata.content.mjs" contentDeclarationFormat="esm"
|
|
831
|
+
import { t } from "intlayer";
|
|
832
|
+
|
|
833
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
834
|
+
const metadataContent = {
|
|
835
|
+
key: "page-metadata",
|
|
836
|
+
content: {
|
|
837
|
+
title: t({
|
|
838
|
+
uk: "Створити додаток Next.js",
|
|
839
|
+
en: "Create Next App",
|
|
840
|
+
fr: "Créer une application Next.js",
|
|
841
|
+
es: "Crear una aplicación Next.js",
|
|
842
|
+
}),
|
|
843
|
+
description: t({
|
|
844
|
+
uk: "Згенеровано за допомогою create next app",
|
|
845
|
+
en: "Generated by create next app",
|
|
846
|
+
fr: "Généré par create next app",
|
|
847
|
+
es: "Generado por create next app",
|
|
848
|
+
}),
|
|
849
|
+
},
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
export default metadataContent;
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
```javascript fileName="src/app/[locale]/metadata.content.cjs" contentDeclarationFormat="commonjs"
|
|
856
|
+
const { t } = require("intlayer");
|
|
857
|
+
|
|
858
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
859
|
+
const metadataContent = {
|
|
860
|
+
key: "page-metadata",
|
|
861
|
+
content: {
|
|
862
|
+
title: t({
|
|
863
|
+
uk: "Створити додаток Next.js",
|
|
864
|
+
en: "Create Next App",
|
|
865
|
+
fr: "Créer une application Next.js",
|
|
866
|
+
es: "Crear una aplicación Next.js",
|
|
867
|
+
}),
|
|
868
|
+
description: t({
|
|
869
|
+
uk: "Згенеровано за допомогою create next app",
|
|
870
|
+
en: "Generated by create next app",
|
|
871
|
+
fr: "Généré par create next app",
|
|
872
|
+
es: "Generado por create next app",
|
|
873
|
+
}),
|
|
874
|
+
},
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
module.exports = metadataContent;
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
```json fileName="src/app/[locale]/metadata.content.json" contentDeclarationFormat="json"
|
|
881
|
+
{
|
|
882
|
+
"key": "page-metadata",
|
|
883
|
+
"content": {
|
|
884
|
+
"title": {
|
|
885
|
+
"nodeType": "translation",
|
|
886
|
+
"translation": {
|
|
887
|
+
"uk": "Логотип Preact",
|
|
888
|
+
"en": "Preact logo",
|
|
889
|
+
"fr": "Logo Preact",
|
|
890
|
+
"es": "Logo Preact",
|
|
891
|
+
},
|
|
892
|
+
},
|
|
893
|
+
"description": {
|
|
894
|
+
"nodeType": "translation",
|
|
895
|
+
"translation": {
|
|
896
|
+
"uk": "Згенеровано за допомогою create next app",
|
|
897
|
+
"en": "Generated by create next app",
|
|
898
|
+
"fr": "Généré par create next app",
|
|
899
|
+
"es": "Generado por create next app",
|
|
900
|
+
},
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
````typescript fileName="src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx" codeFormat="typescript"
|
|
907
|
+
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
908
|
+
import type { Metadata } from "next";
|
|
909
|
+
import type { LocalPromiseParams } from "next-intlayer";
|
|
910
|
+
|
|
911
|
+
export const generateMetadata = async ({
|
|
912
|
+
params,
|
|
913
|
+
}: LocalPromiseParams): Promise<Metadata> => {
|
|
914
|
+
const { locale } = await params;
|
|
915
|
+
|
|
916
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Генерує об'єкт, що містить всі URL для кожної локалі.
|
|
920
|
+
*
|
|
921
|
+
* Приклад:
|
|
922
|
+
* ```ts
|
|
923
|
+
* getMultilingualUrls('/about');
|
|
924
|
+
*
|
|
925
|
+
* // Повертає
|
|
926
|
+
* // {
|
|
927
|
+
* // en: '/about',
|
|
928
|
+
* // fr: '/fr/about',
|
|
929
|
+
* // es: '/es/about',
|
|
930
|
+
* // }
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
933
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
934
|
+
const localizedUrl =
|
|
935
|
+
multilingualUrls[locale as keyof typeof multilingualUrls];
|
|
936
|
+
|
|
937
|
+
return {
|
|
938
|
+
...metadata,
|
|
939
|
+
alternates: {
|
|
940
|
+
canonical: localizedUrl,
|
|
941
|
+
languages: { ...multilingualUrls, "x-default": "/" },
|
|
942
|
+
},
|
|
943
|
+
openGraph: {
|
|
944
|
+
url: localizedUrl,
|
|
945
|
+
},
|
|
946
|
+
};
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
// ... Rest of the code
|
|
950
|
+
````
|
|
951
|
+
|
|
952
|
+
````javascript fileName="src/app/[locale]/layout.mjs or src/app/[locale]/page.mjs" codeFormat="esm"
|
|
953
|
+
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
954
|
+
|
|
955
|
+
export const generateMetadata = async ({ params }) => {
|
|
956
|
+
const { locale } = await params;
|
|
957
|
+
|
|
958
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Генерує об'єкт, що містить всі URL для кожної локалі.
|
|
962
|
+
*
|
|
963
|
+
* Приклад:
|
|
964
|
+
* ```ts```
|
|
965
|
+
*/
|
|
966
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
967
|
+
const localizedUrl =
|
|
968
|
+
multilingualUrls[locale as keyof typeof multilingualUrls];
|
|
969
|
+
|
|
970
|
+
return {
|
|
971
|
+
...metadata,
|
|
972
|
+
alternates: {
|
|
973
|
+
canonical: localizedUrl,
|
|
974
|
+
languages: { ...multilingualUrls, "x-default": "/" },
|
|
975
|
+
},
|
|
976
|
+
openGraph: {
|
|
977
|
+
url: localizedUrl,
|
|
978
|
+
},
|
|
979
|
+
};
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
// ... Rest of the code
|
|
983
|
+
````
|
|
984
|
+
|
|
985
|
+
````javascript fileName="src/app/[locale]/layout.mjs or src/app/[locale]/page.mjs" codeFormat="esm"
|
|
986
|
+
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
987
|
+
|
|
988
|
+
export const generateMetadata = async ({ params }) => {
|
|
989
|
+
const { locale } = await params;
|
|
990
|
+
|
|
991
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Генерує об'єкт, що містить всі URL для кожної локалі.
|
|
995
|
+
*
|
|
996
|
+
* Приклад:
|
|
997
|
+
* ```ts
|
|
998
|
+
* getMultilingualUrls('/about');
|
|
999
|
+
*
|
|
1000
|
+
* // Повертає
|
|
1001
|
+
* // {
|
|
1002
|
+
* // en: '/about',
|
|
1003
|
+
* // fr: '/fr/about',
|
|
1004
|
+
* // es: '/es/about'
|
|
1005
|
+
* // }
|
|
1006
|
+
* ```
|
|
1007
|
+
*/
|
|
1008
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
1009
|
+
const localizedUrl = multilingualUrls[locale];
|
|
1010
|
+
|
|
1011
|
+
return {
|
|
1012
|
+
...metadata,
|
|
1013
|
+
alternates: {
|
|
1014
|
+
canonical: localizedUrl,
|
|
1015
|
+
languages: { ...multilingualUrls, "x-default": "/" },
|
|
1016
|
+
},
|
|
1017
|
+
openGraph: {
|
|
1018
|
+
url: localizedUrl,
|
|
1019
|
+
},
|
|
1020
|
+
};
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
// ... Rest of the code
|
|
1024
|
+
````
|
|
1025
|
+
|
|
1026
|
+
````javascript fileName="src/app/[locale]/layout.cjs or src/app/[locale]/page.cjs" codeFormat="commonjs"
|
|
1027
|
+
const { getIntlayer, getMultilingualUrls } = require("intlayer");
|
|
1028
|
+
|
|
1029
|
+
const generateMetadata = async ({ params }) => {
|
|
1030
|
+
const { locale } = await params;
|
|
1031
|
+
|
|
1032
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Генерує об'єкт, що містить усі URL для кожної локалі.
|
|
1036
|
+
*
|
|
1037
|
+
* Приклад:
|
|
1038
|
+
* ```ts
|
|
1039
|
+
* getMultilingualUrls('/about');
|
|
1040
|
+
*
|
|
1041
|
+
* // Повертає
|
|
1042
|
+
* // {
|
|
1043
|
+
* // en: '/about',
|
|
1044
|
+
* // fr: '/fr/about',
|
|
1045
|
+
* // es: '/es/about'
|
|
1046
|
+
* // }
|
|
1047
|
+
* ```
|
|
1048
|
+
*/
|
|
1049
|
+
const multilingualUrls = getMultilingualUrls("/");
|
|
1050
|
+
const localizedUrl = multilingualUrls[locale];
|
|
1051
|
+
|
|
1052
|
+
return {
|
|
1053
|
+
...metadata,
|
|
1054
|
+
alternates: {
|
|
1055
|
+
canonical: localizedUrl,
|
|
1056
|
+
languages: { ...multilingualUrls, "x-default": "/" },
|
|
1057
|
+
},
|
|
1058
|
+
openGraph: {
|
|
1059
|
+
url: localizedUrl,
|
|
1060
|
+
},
|
|
1061
|
+
};
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
module.exports = { generateMetadata };
|
|
1065
|
+
|
|
1066
|
+
// ... Решта коду
|
|
1067
|
+
````
|
|
1068
|
+
|
|
1069
|
+
> Зверніть увагу, що функція `getIntlayer`, імпортована з `next-intlayer`, повертає ваш контент, обгорнутий в `IntlayerNode`, що дозволяє інтеграцію з візуальним редактором. Натомість функція `getIntlayer`, імпортована з `intlayer`, повертає ваш контент без додаткових властивостей.
|
|
1070
|
+
|
|
1071
|
+
Альтернативно, ви можете використовувати функцію `getTranslation` для оголошення ваших метаданих. Проте рекомендується використовувати файли декларацій контенту, щоб автоматизувати переклад ваших метаданих і згодом винести контент.
|
|
1072
|
+
|
|
1073
|
+
```typescript fileName="src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx" codeFormat="typescript"
|
|
1074
|
+
import {
|
|
1075
|
+
type IConfigLocales,
|
|
1076
|
+
getTranslation,
|
|
1077
|
+
getMultilingualUrls,
|
|
1078
|
+
} from "intlayer";
|
|
1079
|
+
import type { Metadata } from "next";
|
|
1080
|
+
import type { LocalPromiseParams } from "next-intlayer";
|
|
1081
|
+
|
|
1082
|
+
export const generateMetadata = async ({
|
|
1083
|
+
params,
|
|
1084
|
+
}: LocalPromiseParams): Promise<Metadata> => {
|
|
1085
|
+
const { locale } = await params;
|
|
1086
|
+
const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);
|
|
1087
|
+
|
|
1088
|
+
return {
|
|
1089
|
+
title: t<string>({
|
|
1090
|
+
uk: "Мій заголовок",
|
|
1091
|
+
en: "My title",
|
|
1092
|
+
fr: "Mon titre",
|
|
1093
|
+
es: "Mi título",
|
|
1094
|
+
}),
|
|
1095
|
+
description: t({
|
|
1096
|
+
uk: "Мій опис",
|
|
1097
|
+
en: "My description",
|
|
1098
|
+
fr: "Ma description",
|
|
1099
|
+
es: "Mi descripción",
|
|
1100
|
+
}),
|
|
1101
|
+
};
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
// ... Rest of the code
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
```javascript fileName="src/app/[locale]/layout.mjs or src/app/[locale]/page.mjs" codeFormat="esm"
|
|
1108
|
+
import { getTranslation, getMultilingualUrls } from "intlayer";
|
|
1109
|
+
|
|
1110
|
+
export const generateMetadata = async ({ params }) => {
|
|
1111
|
+
const { locale } = await params;
|
|
1112
|
+
const t = (content) => getTranslation(content, locale);
|
|
1113
|
+
|
|
1114
|
+
return {
|
|
1115
|
+
title: t({
|
|
1116
|
+
uk: "Мій заголовок",
|
|
1117
|
+
en: "My title",
|
|
1118
|
+
fr: "Mon titre",
|
|
1119
|
+
es: "Mi título",
|
|
1120
|
+
}),
|
|
1121
|
+
description: t({
|
|
1122
|
+
uk: "Мій опис",
|
|
1123
|
+
en: "My description",
|
|
1124
|
+
fr: "Ma description",
|
|
1125
|
+
es: "Mi descripción",
|
|
1126
|
+
}),
|
|
1127
|
+
};
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
// ... Rest of the code
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
```javascript fileName="src/app/[locale]/layout.cjs or src/app/[locale]/page.cjs" codeFormat="commonjs"
|
|
1134
|
+
const { getTranslation, getMultilingualUrls } = require("intlayer");
|
|
1135
|
+
|
|
1136
|
+
const generateMetadata = async ({ params }) => {
|
|
1137
|
+
const { locale } = await params;
|
|
1138
|
+
|
|
1139
|
+
const t = (content) => getTranslation(content, locale);
|
|
1140
|
+
|
|
1141
|
+
return {
|
|
1142
|
+
title: t({
|
|
1143
|
+
uk: "Мій заголовок",
|
|
1144
|
+
en: "My title",
|
|
1145
|
+
fr: "Mon titre",
|
|
1146
|
+
es: "Mi título",
|
|
1147
|
+
}),
|
|
1148
|
+
description: t({
|
|
1149
|
+
uk: "Мій опис",
|
|
1150
|
+
en: "My description",
|
|
1151
|
+
fr: "Ma description",
|
|
1152
|
+
es: "Mi descripción",
|
|
1153
|
+
}),
|
|
1154
|
+
};
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
module.exports = { generateMetadata };
|
|
1158
|
+
|
|
1159
|
+
// ... Rest of the code
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
> Дізнайтеся більше про оптимізацію метаданих [в офіційній документації Next.js](https://nextjs.org/docs/app/building-your-application/optimizing/metadata).
|
|
1163
|
+
|
|
1164
|
+
### (Необов'язково) Крок 9: Інтернаціоналізація вашого sitemap.xml та robots.txt
|
|
1165
|
+
|
|
1166
|
+
Щоб інтернаціоналізувати `sitemap.xml` та `robots.txt`, ви можете використати функцію `getMultilingualUrls`, надану Intlayer. Ця функція дозволяє генерувати багатомовні URL-адреси для вашого sitemap.
|
|
1167
|
+
|
|
1168
|
+
```tsx fileName="src/app/sitemap.ts" codeFormat="typescript"
|
|
1169
|
+
import { getMultilingualUrls } from "intlayer";
|
|
1170
|
+
import type { MetadataRoute } from "next";
|
|
1171
|
+
|
|
1172
|
+
const sitemap = (): MetadataRoute.Sitemap => [
|
|
1173
|
+
{
|
|
1174
|
+
url: "https://example.com",
|
|
1175
|
+
alternates: {
|
|
1176
|
+
languages: {
|
|
1177
|
+
...getMultilingualUrls("https://example.com"),
|
|
1178
|
+
"x-default": "https://example.com",
|
|
1179
|
+
},
|
|
1180
|
+
},
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
url: "https://example.com/login",
|
|
1184
|
+
alternates: {
|
|
1185
|
+
languages: {
|
|
1186
|
+
...getMultilingualUrls("https://example.com/login"),
|
|
1187
|
+
"x-default": "https://example.com/login",
|
|
1188
|
+
},
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
url: "https://example.com/register",
|
|
1193
|
+
alternates: {
|
|
1194
|
+
languages: {
|
|
1195
|
+
...getMultilingualUrls("https://example.com/register"),
|
|
1196
|
+
"x-default": "https://example.com/register",
|
|
1197
|
+
},
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1200
|
+
];
|
|
1201
|
+
|
|
1202
|
+
export default sitemap;
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
```jsx fileName="src/app/sitemap.mjx" codeFormat="esm"
|
|
1206
|
+
import { getMultilingualUrls } from "intlayer";
|
|
1207
|
+
|
|
1208
|
+
const sitemap = () => [
|
|
1209
|
+
{
|
|
1210
|
+
url: "https://example.com",
|
|
1211
|
+
alternates: {
|
|
1212
|
+
languages: {
|
|
1213
|
+
...getMultilingualUrls("https://example.com"),
|
|
1214
|
+
"x-default": "https://example.com",
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
url: "https://example.com/login",
|
|
1220
|
+
alternates: {
|
|
1221
|
+
languages: {
|
|
1222
|
+
...getMultilingualUrls("https://example.com/login"),
|
|
1223
|
+
"x-default": "https://example.com/login",
|
|
1224
|
+
},
|
|
1225
|
+
},
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
url: "https://example.com/register",
|
|
1229
|
+
alternates: {
|
|
1230
|
+
languages: {
|
|
1231
|
+
...getMultilingualUrls("https://example.com/register"),
|
|
1232
|
+
"x-default": "https://example.com/register",
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
},
|
|
1236
|
+
];
|
|
1237
|
+
|
|
1238
|
+
export default sitemap;
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
```jsx fileName="src/app/sitemap.csx" codeFormat="commonjs"
|
|
1242
|
+
const { getMultilingualUrls } = require("intlayer");
|
|
1243
|
+
|
|
1244
|
+
const sitemap = () => [
|
|
1245
|
+
{
|
|
1246
|
+
url: "https://example.com",
|
|
1247
|
+
alternates: {
|
|
1248
|
+
languages: {
|
|
1249
|
+
...getMultilingualUrls("https://example.com"),
|
|
1250
|
+
"x-default": "https://example.com",
|
|
1251
|
+
},
|
|
1252
|
+
},
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
url: "https://example.com/login",
|
|
1256
|
+
alternates: {
|
|
1257
|
+
languages: {
|
|
1258
|
+
...getMultilingualUrls("https://example.com/login"),
|
|
1259
|
+
"x-default": "https://example.com/login",
|
|
1260
|
+
},
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
url: "https://example.com/register",
|
|
1265
|
+
alternates: {
|
|
1266
|
+
languages: {
|
|
1267
|
+
...getMultilingualUrls("https://example.com/register"),
|
|
1268
|
+
"x-default": "https://example.com/register",
|
|
1269
|
+
},
|
|
1270
|
+
},
|
|
1271
|
+
},
|
|
1272
|
+
];
|
|
1273
|
+
|
|
1274
|
+
module.exports = sitemap;
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
```tsx fileName="src/app/robots.ts" codeFormat="typescript"
|
|
1278
|
+
import type { MetadataRoute } from "next";
|
|
1279
|
+
import { getMultilingualUrls } from "intlayer";
|
|
1280
|
+
|
|
1281
|
+
const getAllMultilingualUrls = (urls: string[]) =>
|
|
1282
|
+
urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
|
|
1283
|
+
|
|
1284
|
+
const robots = (): MetadataRoute.Robots => ({
|
|
1285
|
+
rules: {
|
|
1286
|
+
userAgent: "*",
|
|
1287
|
+
allow: ["/"],
|
|
1288
|
+
disallow: getAllMultilingualUrls(["/login", "/register"]),
|
|
1289
|
+
},
|
|
1290
|
+
host: "https://example.com",
|
|
1291
|
+
sitemap: `https://example.com/sitemap.xml`,
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
export default robots;
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
```jsx fileName="src/app/robots.mjx" codeFormat="esm"
|
|
1298
|
+
import { getMultilingualUrls } from "intlayer";
|
|
1299
|
+
|
|
1300
|
+
const getAllMultilingualUrls = (urls) =>
|
|
1301
|
+
urls.flatMap((url) => Object.values(getMultilingualUrls(url)));
|
|
1302
|
+
|
|
1303
|
+
const robots = () => ({
|
|
1304
|
+
rules: {
|
|
1305
|
+
userAgent: "*",
|
|
1306
|
+
allow: ["/"],
|
|
1307
|
+
disallow: getAllMultilingualUrls(["/login", "/register"]),
|
|
1308
|
+
},
|
|
1309
|
+
host: "https://example.com",
|
|
1310
|
+
sitemap: `https://example.com/sitemap.xml`,
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
export default robots;
|
|
1314
|
+
```
|
|
1315
|
+
|
|
1316
|
+
```jsx fileName="src/app/robots.csx" codeFormat="commonjs"
|
|
1317
|
+
const { getMultilingualUrls } = require("intlayer");
|
|
1318
|
+
|
|
1319
|
+
const getAllMultilingualUrls = (urls) =>
|
|
1320
|
+
urls.flatMap((url) => Object.values(getMultilingualUrls(url)));
|
|
1321
|
+
|
|
1322
|
+
const robots = () => ({
|
|
1323
|
+
rules: {
|
|
1324
|
+
userAgent: "*",
|
|
1325
|
+
allow: ["/"],
|
|
1326
|
+
disallow: getAllMultilingualUrls(["/login", "/register"]),
|
|
1327
|
+
},
|
|
1328
|
+
host: "https://example.com",
|
|
1329
|
+
sitemap: `https://example.com/sitemap.xml`,
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
module.exports = robots;
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
> Дізнайтеся більше про оптимізацію sitemap у [офіційній документації Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap). Дізнайтеся більше про оптимізацію robots.txt у [офіційній документації Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots).
|
|
1336
|
+
|
|
1337
|
+
### (Необов'язково) Крок 10: Зміна мови вашого контенту
|
|
1338
|
+
|
|
1339
|
+
Щоб змінити мову вашого контенту в Next.js, рекомендується використовувати компонент `Link` для перенаправлення користувачів на відповідну локалізовану сторінку. Компонент `Link` дозволяє виконувати prefetch сторінки, що допомагає уникнути повного перезавантаження сторінки.
|
|
1340
|
+
|
|
1341
|
+
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
1342
|
+
"use client";
|
|
1343
|
+
|
|
1344
|
+
import type { FC } from "react";
|
|
1345
|
+
import {
|
|
1346
|
+
Locales,
|
|
1347
|
+
getHTMLTextDir,
|
|
1348
|
+
getLocaleName,
|
|
1349
|
+
getLocalizedUrl,
|
|
1350
|
+
} from "intlayer";
|
|
1351
|
+
import { useLocale } from "next-intlayer";
|
|
1352
|
+
import Link from "next/link";
|
|
1353
|
+
|
|
1354
|
+
export const LocaleSwitcher: FC = () => {
|
|
1355
|
+
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1356
|
+
useLocale();
|
|
1357
|
+
|
|
1358
|
+
return (
|
|
1359
|
+
<div>
|
|
1360
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1361
|
+
<div id="localePopover" popover="auto">
|
|
1362
|
+
{availableLocales.map((localeItem) => (
|
|
1363
|
+
<Link
|
|
1364
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1365
|
+
key={localeItem}
|
|
1366
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1367
|
+
onClick={() => setLocale(localeItem)}
|
|
1368
|
+
replace // Забезпечить, що кнопка "назад" у браузері перенаправлятиме на попередню сторінку
|
|
1369
|
+
>
|
|
1370
|
+
getLocaleName,
|
|
1371
|
+
getLocalizedUrl,
|
|
1372
|
+
} from "intlayer";
|
|
1373
|
+
import { useLocale } from "next-intlayer";
|
|
1374
|
+
import Link from "next/link";
|
|
1375
|
+
|
|
1376
|
+
export const LocaleSwitcher: FC = () => {
|
|
1377
|
+
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1378
|
+
useLocale();
|
|
1379
|
+
|
|
1380
|
+
return (
|
|
1381
|
+
<div>
|
|
1382
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1383
|
+
<div id="localePopover" popover="auto">
|
|
1384
|
+
{availableLocales.map((localeItem) => (
|
|
1385
|
+
<Link
|
|
1386
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1387
|
+
key={localeItem}
|
|
1388
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1389
|
+
onClick={() => setLocale(localeItem)}
|
|
1390
|
+
replace // Гарантує, що кнопка «назад» в браузері перенаправлятиме на попередню сторінку
|
|
1391
|
+
>
|
|
1392
|
+
<span>
|
|
1393
|
+
{/* Локаль — наприклад, FR */}
|
|
1394
|
+
{localeItem}
|
|
1395
|
+
</span>
|
|
1396
|
+
<span>
|
|
1397
|
+
{/* Мова у власній локалі — наприклад, Français */}
|
|
1398
|
+
{getLocaleName(localeItem, locale)}
|
|
1399
|
+
</span>
|
|
1400
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1401
|
+
{/* Мова у поточній локалі — наприклад, Francés коли поточна локаль встановлена на Locales.SPANISH */}
|
|
1402
|
+
{getLocaleName(localeItem)}
|
|
1403
|
+
</span>
|
|
1404
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1405
|
+
{/* Мова англійською — наприклад, French */}
|
|
1406
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1407
|
+
</span>
|
|
1408
|
+
</Link>
|
|
1409
|
+
))}
|
|
1410
|
+
</div>
|
|
1411
|
+
</div>
|
|
1412
|
+
);
|
|
1413
|
+
};
|
|
1414
|
+
<span>
|
|
1415
|
+
{/* Локаль — наприклад: FR */}
|
|
1416
|
+
{localeItem}
|
|
1417
|
+
</span>
|
|
1418
|
+
<span>
|
|
1419
|
+
{/* Назва мови в її власній локалі — наприклад: Français */}
|
|
1420
|
+
{getLocaleName(localeItem, locale)}
|
|
1421
|
+
</span>
|
|
1422
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1423
|
+
{/* Назва мови в поточній локалі — наприклад: Francés при поточній локалі Locales.SPANISH */}
|
|
1424
|
+
{getLocaleName(localeItem)}
|
|
1425
|
+
</span>
|
|
1426
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1427
|
+
{/* Назва мови англійською — наприклад: French */}
|
|
1428
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1429
|
+
</span>
|
|
1430
|
+
</Link>
|
|
1431
|
+
))}
|
|
1432
|
+
</div>
|
|
1433
|
+
</div>
|
|
1434
|
+
);
|
|
1435
|
+
};
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
```jsx fileName="src/components/LocaleSwitcher.msx" codeFormat="esm"
|
|
1439
|
+
"use client";
|
|
1440
|
+
|
|
1441
|
+
import {
|
|
1442
|
+
Locales,
|
|
1443
|
+
getHTMLTextDir,
|
|
1444
|
+
getLocaleName,
|
|
1445
|
+
getLocalizedUrl,
|
|
1446
|
+
} from "intlayer";
|
|
1447
|
+
import { useLocale } from "next-intlayer";
|
|
1448
|
+
import Link from "next/link";
|
|
1449
|
+
|
|
1450
|
+
export const LocaleSwitcher = () => {
|
|
1451
|
+
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1452
|
+
useLocale();
|
|
1453
|
+
|
|
1454
|
+
return (
|
|
1455
|
+
<div>
|
|
1456
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1457
|
+
<div id="localePopover" popover="auto">
|
|
1458
|
+
{availableLocales.map((localeItem) => (
|
|
1459
|
+
<Link
|
|
1460
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1461
|
+
key={localeItem}
|
|
1462
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1463
|
+
onClick={() => setLocale(localeItem)}
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
```jsx fileName="src/components/LocaleSwitcher.msx" codeFormat="esm"
|
|
1467
|
+
"use client";
|
|
1468
|
+
|
|
1469
|
+
import {
|
|
1470
|
+
Locales,
|
|
1471
|
+
getHTMLTextDir,
|
|
1472
|
+
getLocaleName,
|
|
1473
|
+
getLocalizedUrl,
|
|
1474
|
+
} from "intlayer";
|
|
1475
|
+
import { useLocale } from "next-intlayer";
|
|
1476
|
+
import Link from "next/link";
|
|
1477
|
+
|
|
1478
|
+
export const LocaleSwitcher = () => {
|
|
1479
|
+
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1480
|
+
useLocale();
|
|
1481
|
+
|
|
1482
|
+
return (
|
|
1483
|
+
<div>
|
|
1484
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1485
|
+
<div id="localePopover" popover="auto">
|
|
1486
|
+
{availableLocales.map((localeItem) => (
|
|
1487
|
+
<Link
|
|
1488
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1489
|
+
key={localeItem}
|
|
1490
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1491
|
+
onClick={() => setLocale(localeItem)}
|
|
1492
|
+
replace // Це гарантує, що кнопка "назад" в браузері перенаправить на попередню сторінку
|
|
1493
|
+
>
|
|
1494
|
+
<span>
|
|
1495
|
+
{/* Локаль — напр., FR */}
|
|
1496
|
+
{localeItem}
|
|
1497
|
+
</span>
|
|
1498
|
+
<span>
|
|
1499
|
+
{/* Мова у власній локалі — напр., Français */}
|
|
1500
|
+
{getLocaleName(localeItem, locale)}
|
|
1501
|
+
</span>
|
|
1502
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1503
|
+
{/* Мова у поточній локалі — напр., Francés коли поточна локаль встановлена в Locales.SPANISH */}
|
|
1504
|
+
{getLocaleName(localeItem)}
|
|
1505
|
+
</span>
|
|
1506
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1507
|
+
{/* Назва мови англійською — напр., French */}
|
|
1508
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1509
|
+
key={localeItem}
|
|
1510
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1511
|
+
onClick={() => setLocale(localeItem)}
|
|
1512
|
+
replace // Забезпечить, що кнопка браузера "назад" у браузері перенаправлятиме на попередню сторінку
|
|
1513
|
+
>
|
|
1514
|
+
<span>
|
|
1515
|
+
{/* Локаль - напр. FR */}
|
|
1516
|
+
{localeItem}
|
|
1517
|
+
</span>
|
|
1518
|
+
<span>
|
|
1519
|
+
{/* Мова у власній локалі - напр. Français */}
|
|
1520
|
+
{getLocaleName(localeItem, locale)}
|
|
1521
|
+
</span>
|
|
1522
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1523
|
+
{/* Мова у поточній локалі - напр. Francés коли поточна локаль встановлена як Locales.SPANISH */}
|
|
1524
|
+
{getLocaleName(localeItem)}
|
|
1525
|
+
</span>
|
|
1526
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1527
|
+
{/* Мова англійською - напр. French */}
|
|
1528
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1529
|
+
</span>
|
|
1530
|
+
</Link>
|
|
1531
|
+
))}
|
|
1532
|
+
</div>
|
|
1533
|
+
</div>
|
|
1534
|
+
);
|
|
1535
|
+
};
|
|
1536
|
+
```
|
|
1537
|
+
|
|
1538
|
+
```jsx fileName="src/components/LocaleSwitcher.csx" codeFormat="commonjs"
|
|
1539
|
+
"use client";
|
|
1540
|
+
|
|
1541
|
+
const {
|
|
1542
|
+
Locales,
|
|
1543
|
+
getHTMLTextDir,
|
|
1544
|
+
getLocaleName,
|
|
1545
|
+
getLocalizedUrl,
|
|
1546
|
+
} = require("intlayer");
|
|
1547
|
+
const { useLocale } = require("next-intlayer");
|
|
1548
|
+
const Link = require("next/link");
|
|
1549
|
+
|
|
1550
|
+
export const LocaleSwitcher = () => {
|
|
1551
|
+
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1552
|
+
useLocale();
|
|
1553
|
+
|
|
1554
|
+
return (
|
|
1555
|
+
<div>
|
|
1556
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1557
|
+
<div id="localePopover" popover="auto">
|
|
1558
|
+
{availableLocales.map((localeItem) => (
|
|
1559
|
+
<Link
|
|
1560
|
+
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1561
|
+
key={localeItem}
|
|
1562
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1563
|
+
onClick={() => setLocale(localeItem)}
|
|
1564
|
+
replace // Забезпечить, що кнопка браузера "назад" у браузері перенаправлятиме на попередню сторінку
|
|
1565
|
+
>
|
|
1566
|
+
<span>
|
|
1567
|
+
{/* Локаль - напр. FR */}
|
|
1568
|
+
{localeItem}
|
|
1569
|
+
</span>
|
|
1570
|
+
<span>
|
|
1571
|
+
{/* Мова у власній локалі - напр. Français */}
|
|
1572
|
+
{getLocaleName(localeItem, locale)}
|
|
1573
|
+
</span>
|
|
1574
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1575
|
+
{/* Мова у поточній локалі - напр. Francés коли поточна локаль встановлена як Locales.SPANISH */}
|
|
1576
|
+
{getLocaleName(localeItem)}
|
|
1577
|
+
</span>
|
|
1578
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1579
|
+
{/* Мова англійською - напр. French */}
|
|
1580
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1581
|
+
</span>
|
|
1582
|
+
</Link>
|
|
1583
|
+
))}
|
|
1584
|
+
</div>
|
|
1585
|
+
</div>
|
|
1586
|
+
);
|
|
1587
|
+
};
|
|
1588
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1589
|
+
onClick={() => setLocale(localeItem)}
|
|
1590
|
+
replace // Це гарантує, що кнопка браузера "назад" перенаправлятиме на попередню сторінку
|
|
1591
|
+
>
|
|
1592
|
+
<span>
|
|
1593
|
+
{/* Локаль — наприклад FR */}
|
|
1594
|
+
{localeItem}
|
|
1595
|
+
</span>
|
|
1596
|
+
<span>
|
|
1597
|
+
{/* Назва мови у власній локалі — наприклад Français */}
|
|
1598
|
+
{getLocaleName(localeItem, locale)}
|
|
1599
|
+
</span>
|
|
1600
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1601
|
+
{/* Назва мови у поточній локалі — наприклад Francés, коли поточна локаль встановлена на Locales.SPANISH */}
|
|
1602
|
+
{getLocaleName(localeItem)}
|
|
1603
|
+
</span>
|
|
1604
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1605
|
+
{/* Мова англійською — наприклад French */}
|
|
1606
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1607
|
+
</span>
|
|
1608
|
+
</Link>
|
|
1609
|
+
))}
|
|
1610
|
+
</div>
|
|
1611
|
+
</div>
|
|
1612
|
+
);
|
|
1613
|
+
};
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
> Іншим способом є використання функції `setLocale`, яку надає хук `useLocale`. Ця функція не дозволяє робити prefetch сторінки. Див. документацію по [`useLocale` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useLocale.md) для детальнішої інформації.
|
|
1617
|
+
|
|
1618
|
+
> Ви також можете встановити функцію в опції `onLocaleChange`, щоб викликати користувацьку функцію при зміні локалі.
|
|
1619
|
+
|
|
1620
|
+
```tsx fileName="src/components/LocaleSwitcher.tsx"
|
|
1621
|
+
"use client";
|
|
1622
|
+
|
|
1623
|
+
import { useRouter } from "next/navigation";
|
|
1624
|
+
import { useLocale } from "next-intlayer";
|
|
1625
|
+
import { getLocalizedUrl } from "intlayer";
|
|
1626
|
+
|
|
1627
|
+
// ... Rest of the code
|
|
1628
|
+
|
|
1629
|
+
const router = useRouter();
|
|
1630
|
+
const { setLocale } = useLocale({
|
|
1631
|
+
onLocaleChange: (locale) => {
|
|
1632
|
+
router.push(getLocalizedUrl(pathWithoutLocale, locale));
|
|
1633
|
+
},
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
return (
|
|
1637
|
+
<button onClick={() => setLocale(Locales.FRENCH)}>Change to French</button>
|
|
1638
|
+
);
|
|
1639
|
+
```
|
|
1640
|
+
|
|
1641
|
+
> Посилання на документацію:
|
|
1642
|
+
>
|
|
1643
|
+
> - [`useLocale` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/next-intlayer/useLocale.md)
|
|
1644
|
+
> - [`getLocaleName` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getLocaleName.md)
|
|
1645
|
+
> - [`getLocalizedUrl` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getLocalizedUrl.md)
|
|
1646
|
+
> - [`getHTMLTextDir` хук](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/packages/intlayer/getHTMLTextDir.md)
|
|
1647
|
+
> - [атрибут `hrefLang`](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)
|
|
1648
|
+
> - [атрибут `lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang)
|
|
1649
|
+
> - [атрибут `dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir)
|
|
1650
|
+
> - [атрибут `aria-current`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
|
|
1651
|
+
|
|
1652
|
+
### (Необов'язково) Крок 11: Створення локалізованого компонента `Link`
|
|
1653
|
+
|
|
1654
|
+
Щоб гарантувати, що навігація вашого додатку враховує поточну локаль, ви можете створити кастомний компонент `Link`. Цей компонент автоматично додає префікс внутрішнім URL відповідно до поточної мови. Наприклад, коли франкофонний користувач клікає на посилання на сторінку "About", він буде перенаправлений на `/fr/about` замість `/about`.
|
|
1655
|
+
|
|
1656
|
+
Ця поведінка корисна з кількох причин:
|
|
1657
|
+
|
|
1658
|
+
- **SEO та User Experience**: Локалізовані URL допомагають пошуковим системам правильно індексувати мовно-специфічні сторінки та надавати користувачам контент їхньою бажаною мовою.
|
|
1659
|
+
- **Consistency**: Використовуючи локалізоване посилання в усьому додатку, ви гарантуєте, що навігація залишатиметься в межах поточної локалі, запобігаючи несподіваним перемиканням мови.
|
|
1660
|
+
- **Підтримуваність**: Централізація логіки локалізації в одному компоненті спрощує керування URL-адресами, роблячи вашу codebase легшою для підтримки та розширення у міру зростання застосунку.
|
|
1661
|
+
|
|
1662
|
+
Нижче наведено реалізацію локалізованого компонента `Link` на TypeScript:
|
|
1663
|
+
|
|
1664
|
+
```tsx fileName="src/components/Link.tsx" codeFormat="typescript"
|
|
1665
|
+
"use client";
|
|
1666
|
+
|
|
1667
|
+
import { getLocalizedUrl } from "intlayer";
|
|
1668
|
+
import NextLink, { type LinkProps as NextLinkProps } from "next/link";
|
|
1669
|
+
import { useLocale } from "next-intlayer";
|
|
1670
|
+
import type { PropsWithChildren, FC } from "react";
|
|
1671
|
+
|
|
1672
|
+
/**
|
|
1673
|
+
* Утилітна функція для перевірки, чи є вказаний URL зовнішнім.
|
|
1674
|
+
* Якщо URL починається з http:// або https://, він вважається зовнішнім.
|
|
1675
|
+
*/
|
|
1676
|
+
export const checkIsExternalLink = (href?: string): boolean =>
|
|
1677
|
+
/^https?:\/\//.test(href ?? "");
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* Кастомний компонент Link, який адаптує атрибут href залежно від поточної локалі.
|
|
1681
|
+
* Для внутрішніх посилань він використовує `getLocalizedUrl`, щоб додати префікс локалі до URL (наприклад, /fr/about).
|
|
1682
|
+
* Це гарантує, що навігація відбувається в межах тієї ж локалі.
|
|
1683
|
+
*/
|
|
1684
|
+
export const Link: FC<PropsWithChildren<NextLinkProps>> = ({
|
|
1685
|
+
href,
|
|
1686
|
+
children,
|
|
1687
|
+
...props
|
|
1688
|
+
}) => {
|
|
1689
|
+
const { locale } = useLocale();
|
|
1690
|
+
const isExternalLink = checkIsExternalLink(href.toString());
|
|
1691
|
+
|
|
1692
|
+
// Якщо посилання внутрішнє і передано дійсний href, отримати локалізований URL.
|
|
1693
|
+
const hrefI18n: NextLinkProps["href"] =
|
|
1694
|
+
href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
|
|
1695
|
+
|
|
1696
|
+
return (
|
|
1697
|
+
<NextLink href={hrefI18n} {...props}>
|
|
1698
|
+
{children}
|
|
1699
|
+
</NextLink>
|
|
1700
|
+
);
|
|
1701
|
+
};
|
|
1702
|
+
```
|
|
1703
|
+
|
|
1704
|
+
```jsx fileName="src/components/Link.mjx" codeFormat="esm"
|
|
1705
|
+
"use client";
|
|
1706
|
+
|
|
1707
|
+
import { getLocalizedUrl } from "intlayer";
|
|
1708
|
+
import NextLink from "next/link";
|
|
1709
|
+
import { useLocale } from "next-intlayer";
|
|
1710
|
+
|
|
1711
|
+
/**
|
|
1712
|
+
* Утилітна функція для перевірки, чи є даний URL зовнішнім.
|
|
1713
|
+
* Якщо URL починається з http:// або https://, він вважається зовнішнім.
|
|
1714
|
+
*/
|
|
1715
|
+
export const checkIsExternalLink = (href) => /^https?:\/\//.test(href ?? "");
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* Кастомний компонент Link, який адаптує атрибут href відповідно до поточної локалі.
|
|
1719
|
+
* Для внутрішніх посилань він використовує `getLocalizedUrl` для додавання префіксу локалі в URL (наприклад, /fr/about).
|
|
1720
|
+
* Це забезпечує, що навігація відбувається в межах тієї самої локалі.
|
|
1721
|
+
*/
|
|
1722
|
+
export const Link = ({ href, children, ...props }) => {
|
|
1723
|
+
const { locale } = useLocale();
|
|
1724
|
+
const isExternalLink = checkIsExternalLink(href.toString());
|
|
1725
|
+
|
|
1726
|
+
// Якщо посилання внутрішнє і передано дійсний href, отримати локалізовану URL.
|
|
1727
|
+
const hrefI18n =
|
|
1728
|
+
href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
|
|
1729
|
+
|
|
1730
|
+
return (
|
|
1731
|
+
<NextLink href={hrefI18n} {...props}>
|
|
1732
|
+
{children}
|
|
1733
|
+
</NextLink>
|
|
1734
|
+
);
|
|
1735
|
+
};
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1738
|
+
```jsx fileName="src/components/Link.csx" codeFormat="commonjs"
|
|
1739
|
+
"use client";
|
|
1740
|
+
|
|
1741
|
+
const { getLocalizedUrl } = require("intlayer");
|
|
1742
|
+
const NextLink = require("next/link");
|
|
1743
|
+
const { useLocale } = require("next-intlayer");
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* Утиліта для перевірки, чи є даний URL зовнішнім.
|
|
1747
|
+
* Якщо URL починається з http:// або https://, він вважається зовнішнім.
|
|
1748
|
+
*/
|
|
1749
|
+
const checkIsExternalLink = (href) => /^https?:\/\//.test(href ?? "");
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Користувацький компонент Link, який адаптує атрибут href залежно від поточної локалі.
|
|
1753
|
+
* Для внутрішніх посилань він використовує `getLocalizedUrl`, щоб додати префікс локалі до URL (наприклад, /fr/about).
|
|
1754
|
+
* Це гарантує, що навігація залишатиметься в контексті тієї ж локалі.
|
|
1755
|
+
*/
|
|
1756
|
+
const Link = ({ href, children, ...props }) => {
|
|
1757
|
+
const { locale } = useLocale();
|
|
1758
|
+
const isExternalLink = checkIsExternalLink(href.toString());
|
|
1759
|
+
|
|
1760
|
+
// Якщо посилання внутрішнє і надано дійсний href, отримати локалізований URL.
|
|
1761
|
+
const hrefI18n =
|
|
1762
|
+
href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
|
|
1763
|
+
|
|
1764
|
+
return (
|
|
1765
|
+
<NextLink href={hrefI18n} {...props}>
|
|
1766
|
+
{children}
|
|
1767
|
+
</NextLink>
|
|
1768
|
+
);
|
|
1769
|
+
};
|
|
1770
|
+
```
|
|
1771
|
+
|
|
1772
|
+
#### Як це працює
|
|
1773
|
+
|
|
1774
|
+
- **Визначення зовнішніх посилань**:
|
|
1775
|
+
|
|
1776
|
+
Допоміжна функція `checkIsExternalLink` визначає, чи є URL зовнішнім. Зовнішні посилання залишаються без змін, оскільки їх не потрібно локалізувати.
|
|
1777
|
+
|
|
1778
|
+
- **Отримання поточної локалі**:
|
|
1779
|
+
Хук `useLocale` надає поточну локаль (наприклад, `fr` для французької).
|
|
1780
|
+
|
|
1781
|
+
- **Локалізація URL**:
|
|
1782
|
+
Для внутрішніх посилань (тобто таких, що не є зовнішніми) використовується `getLocalizedUrl` для автоматичного додавання префіксу з поточною локаллю. Це означає, що якщо ваш користувач використовує французьку, передання `/about` як `href` перетвориться на `/fr/about`.
|
|
1783
|
+
|
|
1784
|
+
- **Повернення посилання**:
|
|
1785
|
+
Компонент повертає елемент `<a>` з локалізованим URL, гарантуючи, що навігація відповідає локалі.
|
|
1786
|
+
|
|
1787
|
+
Інтегруючи цей компонент `Link` у вашому застосунку, ви підтримуєте узгоджений і орієнтований на мову досвід користувача, а також отримуєте переваги покращеного SEO та зручності використання.
|
|
1788
|
+
|
|
1789
|
+
### (Необов'язково) Крок 12: Отримати поточну локаль у Server Actions
|
|
1790
|
+
|
|
1791
|
+
Якщо вам потрібна активна локаль всередині Server Action (наприклад, щоб локалізувати електронні листи або виконувати логіку, залежну від локалі), викличте `getLocale` з `next-intlayer/server`:
|
|
1792
|
+
|
|
1793
|
+
```tsx fileName="src/app/actions/getLocale.ts" codeFormat="typescript"
|
|
1794
|
+
"use server";
|
|
1795
|
+
|
|
1796
|
+
import { getLocale } from "next-intlayer/server";
|
|
1797
|
+
|
|
1798
|
+
export const myServerAction = async () => {
|
|
1799
|
+
const locale = await getLocale();
|
|
1800
|
+
|
|
1801
|
+
// Виконайте дію з локаллю
|
|
1802
|
+
};
|
|
1803
|
+
```
|
|
1804
|
+
|
|
1805
|
+
> Функція `getLocale` дотримується каскадної стратегії для визначення локалі користувача:
|
|
1806
|
+
>
|
|
1807
|
+
> 1. Спочатку перевіряє заголовки запиту на наявність значення локалі, яке могло бути встановлене middleware
|
|
1808
|
+
> 2. Якщо локаль не знайдена в заголовках, вона шукає локаль у cookies
|
|
1809
|
+
> 3. Якщо cookie не знайдено, вона намагається визначити мову, переважну для користувача, за налаштуваннями браузера
|
|
1810
|
+
> 4. Як останній варіант, вона повертається до локалі за замовчуванням, налаштованої в застосунку
|
|
1811
|
+
>
|
|
1812
|
+
> Це забезпечує вибір найвідповіднішої локалі на основі наявного контексту.
|
|
1813
|
+
|
|
1814
|
+
### (Необов'язково) Крок 13: Оптимізуйте розмір bundle
|
|
1815
|
+
|
|
1816
|
+
Під час використання `next-intlayer` словники за замовчуванням включаються в bundle для кожної сторінки. Щоб оптимізувати розмір bundle, Intlayer пропонує необов'язковий плагін для SWC, який за допомогою макросів інтелектуально замінює виклики `useIntlayer`. Це гарантує, що словники будуть включені в bundle лише для сторінок, які їх справді використовують.
|
|
1817
|
+
|
|
1818
|
+
Щоб увімкнути цю оптимізацію, встановіть пакет `@intlayer/swc`. Після встановлення `next-intlayer` автоматично виявить і використовуватиме плагін:
|
|
1819
|
+
|
|
1820
|
+
```bash packageManager="npm"
|
|
1821
|
+
npm install @intlayer/swc --save-dev
|
|
1822
|
+
npx intlayer init
|
|
1823
|
+
```
|
|
1824
|
+
|
|
1825
|
+
```bash packageManager="pnpm"
|
|
1826
|
+
pnpm add @intlayer/swc --save-dev
|
|
1827
|
+
pnpm intlayer init
|
|
1828
|
+
```
|
|
1829
|
+
|
|
1830
|
+
```bash packageManager="yarn"
|
|
1831
|
+
yarn add @intlayer/swc --save-dev
|
|
1832
|
+
yarn intlayer init
|
|
1833
|
+
```
|
|
1834
|
+
|
|
1835
|
+
```bash packageManager="bun"
|
|
1836
|
+
bun add @intlayer/swc --dev
|
|
1837
|
+
bunx intlayer init
|
|
1838
|
+
```
|
|
1839
|
+
|
|
1840
|
+
> Примітка: Ця оптимізація доступна тільки для Next.js 13 і новіших версій.
|
|
1841
|
+
|
|
1842
|
+
> Примітка: Цей пакет не встановлюється за замовчуванням, оскільки SWC-плагіни в Next.js все ще є експериментальними. Це може змінитися в майбутньому.
|
|
1843
|
+
|
|
1844
|
+
> Примітка: Якщо ви встановите опцію як `importMode: 'dynamic'` або `importMode: 'live'`, це буде покладатися на Suspense, тож вам доведеться обгорнути виклики `useIntlayer` у межу `Suspense`. Це означає, що ви не зможете використовувати `useIntlayer` безпосередньо на верхньому рівні вашого компоненту Page / Layout.
|
|
1845
|
+
|
|
1846
|
+
### Слідкування за змінами словників у Turbopack
|
|
1847
|
+
|
|
1848
|
+
При використанні Turbopack як серверу розробки за допомогою команди `next dev --turbopack`, зміни в словниках за замовчуванням не будуть автоматично виявлятися.
|
|
1849
|
+
|
|
1850
|
+
Це обмеження виникає тому, що Turbopack не може запускати webpack-плагіни паралельно для відстеження змін у ваших файлах контенту. Щоб обійти це, потрібно використовувати команду `intlayer watch`, яка дозволяє одночасно запускати сервер розробки та спостерігача збірки Intlayer.
|
|
1851
|
+
|
|
1852
|
+
```json5 fileName="package.json"
|
|
1853
|
+
{
|
|
1854
|
+
// ... Ваші існуючі конфігурації package.json
|
|
1855
|
+
"scripts": {
|
|
1856
|
+
// ... Ваші існуючі налаштування скриптів
|
|
1857
|
+
"dev": "intlayer watch --with 'next dev --turbopack'",
|
|
1858
|
+
},
|
|
1859
|
+
}
|
|
1860
|
+
```
|
|
1861
|
+
|
|
1862
|
+
### Налаштування TypeScript
|
|
1863
|
+
|
|
1864
|
+
Intlayer використовує module augmentation, щоб скористатися перевагами TypeScript і зробити вашу codebase надійнішою.
|
|
1865
|
+
|
|
1866
|
+

|
|
1867
|
+
|
|
1868
|
+

|
|
1869
|
+
|
|
1870
|
+
Переконайтеся, що ваша конфігурація TypeScript включає автогенеровані типи.
|
|
1871
|
+
|
|
1872
|
+
```json5 fileName="tsconfig.json"
|
|
1873
|
+
{
|
|
1874
|
+
// ... Ваші поточні конфігурації TypeScript
|
|
1875
|
+
"include": [
|
|
1876
|
+
// ... Ваші поточні конфігурації TypeScript
|
|
1877
|
+
".intlayer/**/*.ts", // Включити автогенеровані типи
|
|
1878
|
+
],
|
|
1879
|
+
}
|
|
1880
|
+
```
|
|
1881
|
+
|
|
1882
|
+
### Налаштування Git
|
|
1883
|
+
|
|
1884
|
+
Рекомендується ігнорувати файли, згенеровані Intlayer. Це дозволяє уникнути додавання їх у ваш Git-репозиторій.
|
|
1885
|
+
|
|
1886
|
+
Для цього ви можете додати наступні інструкції до файлу `.gitignore`:
|
|
1887
|
+
|
|
1888
|
+
```plaintext fileName=".gitignore"
|
|
1889
|
+
# Ігнорувати файли, згенеровані Intlayer
|
|
1890
|
+
.intlayer
|
|
1891
|
+
```
|
|
1892
|
+
|
|
1893
|
+
### Розширення для VS Code
|
|
1894
|
+
|
|
1895
|
+
Щоб покращити ваш досвід розробки з Intlayer, ви можете встановити офіційне **розширення Intlayer для VS Code**.
|
|
1896
|
+
|
|
1897
|
+
[Встановити з Marketplace для VS Code](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1898
|
+
|
|
1899
|
+
Це розширення надає:
|
|
1900
|
+
|
|
1901
|
+
- **Автодоповнення** для ключів перекладу.
|
|
1902
|
+
- **Виявлення помилок у реальному часі** для відсутніх перекладів.
|
|
1903
|
+
- **Вбудований перегляд** перекладеного контенту.
|
|
1904
|
+
- **Швидкі дії** для зручного створення та оновлення перекладів.
|
|
1905
|
+
|
|
1906
|
+
Для детальнішої інформації про використання розширення зверніться до [документації розширення Intlayer для VS Code](https://intlayer.org/doc/vs-code-extension).
|
|
1907
|
+
|
|
1908
|
+
### Далі
|
|
1909
|
+
|
|
1910
|
+
Щоб розширити можливості, ви можете реалізувати [візуальний редактор](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).
|