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

|
|
1603
|
+
|
|
1604
|
+

|
|
1605
|
+
|
|
1606
|
+
Переконайтеся, що ваша конфігурація TypeScript включає автогенеровані типи.
|
|
1607
|
+
|
|
1608
|
+
```json5 fileName="tsconfig.json"
|
|
1609
|
+
{
|
|
1610
|
+
// ... Ваші існуючі конфігурації TypeScript
|
|
1611
|
+
"include": [
|
|
1612
|
+
// ... Ваші існуючі конфігурації TypeScript
|
|
1613
|
+
".intlayer/**/*.ts", // Включити автогенеровані типи
|
|
1614
|
+
],
|
|
1615
|
+
}
|
|
1616
|
+
```
|
|
1617
|
+
|
|
1618
|
+
### Налаштування Git
|
|
1619
|
+
|
|
1620
|
+
Рекомендується ігнорувати файли, згенеровані Intlayer. Це дозволяє уникнути їх додавання до вашого Git-репозиторію.
|
|
1621
|
+
|
|
1622
|
+
Для цього ви можете додати наступні інструкції до файлу `.gitignore`:
|
|
1623
|
+
|
|
1624
|
+
```plaintext fileName=".gitignore"
|
|
1625
|
+
# Ігноруйте файли, згенеровані Intlayer
|
|
1626
|
+
.intlayer
|
|
1627
|
+
```
|
|
1628
|
+
|
|
1629
|
+
### Розширення VS Code
|
|
1630
|
+
|
|
1631
|
+
Щоб покращити ваш досвід розробки з Intlayer, ви можете встановити офіційне **Intlayer VS Code Extension**.
|
|
1632
|
+
|
|
1633
|
+
[Встановити з VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1634
|
+
|
|
1635
|
+
Це розширення надає:
|
|
1636
|
+
|
|
1637
|
+
- **Автодоповнення** для ключів перекладу.
|
|
1638
|
+
- **Перевірку помилок у реальному часі** для відсутніх перекладів.
|
|
1639
|
+
- **Вбудовані попередні перегляди** перекладеного вмісту.
|
|
1640
|
+
- **Швидкі дії** для простого створення та оновлення перекладів.
|
|
1641
|
+
|
|
1642
|
+
Для детальнішої інформації про використання розширення зверніться до [документації Intlayer VS Code Extension](https://intlayer.org/doc/vs-code-extension).
|
|
1643
|
+
|
|
1644
|
+
### Подальші кроки
|
|
1645
|
+
|
|
1646
|
+
Щоб піти далі, ви можете реалізувати [візуальний редактор](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).
|