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

|
|
1404
|
+
|
|
1405
|
+

|
|
1406
|
+
|
|
1407
|
+
Переконайтеся, що ваша конфігурація TypeScript включає автогенеровані типи.
|
|
1408
|
+
|
|
1409
|
+
```json5 fileName="tsconfig.json"
|
|
1410
|
+
{
|
|
1411
|
+
// ... Ваші існуючі налаштування TypeScript
|
|
1412
|
+
"include": [
|
|
1413
|
+
// ... Ваші існуючі налаштування TypeScript
|
|
1414
|
+
".intlayer/**/*.ts", // Включити автогенеровані типи
|
|
1415
|
+
],
|
|
1416
|
+
}
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
### Налаштування Git
|
|
1420
|
+
|
|
1421
|
+
Рекомендується ігнорувати файли, згенеровані Intlayer. Це дозволить уникнути їх коміту у ваш репозиторій Git.
|
|
1422
|
+
|
|
1423
|
+
Для цього ви можете додати наступні інструкції до файлу `.gitignore`:
|
|
1424
|
+
|
|
1425
|
+
```plaintext fileName=".gitignore"
|
|
1426
|
+
# Ігнорувати файли, згенеровані Intlayer
|
|
1427
|
+
.intlayer
|
|
1428
|
+
```
|
|
1429
|
+
|
|
1430
|
+
### Розширення VS Code
|
|
1431
|
+
|
|
1432
|
+
Щоб покращити досвід розробки з Intlayer, ви можете встановити офіційне **Intlayer VS Code Extension**.
|
|
1433
|
+
|
|
1434
|
+
[Встановити з VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1435
|
+
|
|
1436
|
+
Це розширення надає:
|
|
1437
|
+
|
|
1438
|
+
- **Autocompletion** для ключів перекладу.
|
|
1439
|
+
- **Real-time error detection** для відсутніх перекладів.
|
|
1440
|
+
- **Inline previews** перекладеного контенту.
|
|
1441
|
+
- **Quick actions** для швидкого створення та оновлення перекладів.
|
|
1442
|
+
|
|
1443
|
+
Для детальнішої інформації про використання розширення зверніться до документації [Intlayer VS Code Extension](https://intlayer.org/doc/vs-code-extension).
|
|
1444
|
+
|
|
1445
|
+
---
|
|
1446
|
+
|
|
1447
|
+
### Розширені можливості
|
|
1448
|
+
|
|
1449
|
+
Щоб розширити можливості, ви можете реалізувати [візуальний редактор](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).
|