@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,247 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-09-10
|
|
3
|
+
updatedAt: 2025-09-10
|
|
4
|
+
title: i18n per-komponentowy kontra scentralizowany: nowe podejście z Intlayer
|
|
5
|
+
description: Dogłębna analiza strategii internacjonalizacji w React, porównująca podejścia scentralizowane, per-key i per-component oraz wprowadzająca Intlayer.
|
|
6
|
+
keywords:
|
|
7
|
+
- i18n
|
|
8
|
+
- React
|
|
9
|
+
- Internacjonalizacja
|
|
10
|
+
- Intlayer
|
|
11
|
+
- Optymalizacja
|
|
12
|
+
- Rozmiar bundla
|
|
13
|
+
slugs:
|
|
14
|
+
- blog
|
|
15
|
+
- per-component-vs-centralized-i18n
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# i18n per-komponentowy kontra scentralizowany
|
|
19
|
+
|
|
20
|
+
Podejście per-komponentowe nie jest nowym pojęciem. Na przykład w ekosystemie Vue `vue-i18n` obsługuje [i18n SFC (Single File Component)](https://vue-i18n.intlify.dev/guide/advanced/sfc.html). Nuxt również oferuje [tłumaczenia per-component](https://i18n.nuxtjs.org/docs/guide/per-component-translations), a Angular wykorzystuje podobny wzorzec poprzez swoje [Feature Modules](https://v17.angular.io/guide/feature-modules).
|
|
21
|
+
|
|
22
|
+
Nawet w aplikacji Flutter często można znaleźć taki wzorzec:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
lib/
|
|
26
|
+
└── features/
|
|
27
|
+
└── login/
|
|
28
|
+
├── login_screen.dart
|
|
29
|
+
└── login_screen.i18n.dart # <- Tłumaczenia znajdują się tutaj
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```dart fileName='lib/features/login/login_screen.i18n.dart'
|
|
33
|
+
import 'package:i18n_extension/i18n_extension.dart';
|
|
34
|
+
|
|
35
|
+
extension Localization on String {
|
|
36
|
+
static var _t = Translations.byText("en") +
|
|
37
|
+
{
|
|
38
|
+
"Hello": {
|
|
39
|
+
"en": "Hello",
|
|
40
|
+
"fr": "Bonjour",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
String get i18n => localize(this, _t);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Jednak w świecie React głównie widzimy różne podejścia, które pogrupuję w trzy kategorie:
|
|
49
|
+
|
|
50
|
+
<Columns>
|
|
51
|
+
<Column>
|
|
52
|
+
|
|
53
|
+
**Podejście scentralizowane** (i18next, next-intl, react-intl, lingui)
|
|
54
|
+
|
|
55
|
+
- (bez namespaces) zakłada pojedyncze źródło pobierania treści. Domyślnie ładujesz treści ze wszystkich stron przy uruchomieniu aplikacji.
|
|
56
|
+
|
|
57
|
+
</Column>
|
|
58
|
+
<Column>
|
|
59
|
+
|
|
60
|
+
Drobnoziarniste podejście (intlayer, inlang)
|
|
61
|
+
|
|
62
|
+
- pobieranie treści w sposób drobnoziarnisty — na poziomie klucza lub komponentu.
|
|
63
|
+
|
|
64
|
+
</Column>
|
|
65
|
+
</Columns>
|
|
66
|
+
|
|
67
|
+
> W tym wpisie na blogu nie będę się skupiał na rozwiązaniach opartych na kompilatorze, które już omówiłem tutaj: [Kompilator vs deklaratywne i18n](https://github.com/aymericzip/intlayer/blob/main/docs/blog/pl/compiler_vs_declarative_i18n.md).
|
|
68
|
+
> Zwróć uwagę, że i18n oparty na kompilatorze (np. Lingui) jedynie automatyzuje ekstrakcję i ładowanie treści. Pod maską często ma te same ograniczenia co inne podejścia.
|
|
69
|
+
|
|
70
|
+
> Im bardziej drobnoziarnie kontrolujesz pobieranie treści, tym większe ryzyko wprowadzenia dodatkowego stanu i logiki do Twoich komponentów.
|
|
71
|
+
|
|
72
|
+
Podejścia granularne są bardziej elastyczne niż scentralizowane, ale często wiąże się to z kompromisem. Nawet jeśli biblioteki reklamują "tree shaking", w praktyce często i tak skończysz ładując stronę we wszystkich językach.
|
|
73
|
+
|
|
74
|
+
Ogólnie rzecz biorąc, decyzja wygląda mniej więcej tak:
|
|
75
|
+
|
|
76
|
+
- Jeśli Twoja aplikacja ma więcej stron niż języków, warto preferować podejście granularne.
|
|
77
|
+
- Jeśli masz więcej języków niż stron, powinieneś skłaniać się ku podejściu scentralizowanemu.
|
|
78
|
+
|
|
79
|
+
Oczywiście autorzy bibliotek zdają sobie sprawę z tych ograniczeń i proponują obejścia. Wśród nich: dzielenie na namespaces, dynamiczne ładowanie plików JSON (`await import()`), albo oczyszczanie zawartości podczas procesu budowania.
|
|
80
|
+
|
|
81
|
+
Jednocześnie powinieneś wiedzieć, że gdy dynamicznie ładujesz swoją zawartość, wprowadzasz dodatkowe żądania do serwera. Każde dodatkowe `useState` lub hook oznacza dodatkowe żądanie do serwera.
|
|
82
|
+
|
|
83
|
+
> Aby rozwiązać ten problem, Intlayer proponuje grupowanie wielu definicji treści pod tym samym kluczem — Intlayer następnie scali tę zawartość.
|
|
84
|
+
|
|
85
|
+
Ale z tych wszystkich rozwiązań jasno wynika, że najbardziej popularnym podejściem jest podejście scentralizowane.
|
|
86
|
+
|
|
87
|
+
### Dlaczego więc podejście scentralizowane jest tak popularne?
|
|
88
|
+
|
|
89
|
+
- Po pierwsze, i18next było pierwszym rozwiązaniem, które zyskało szerokie zastosowanie, podążając za filozofią inspirowaną architekturami PHP i Java (MVC), które opierają się na ścisłym rozdziale odpowiedzialności (trzymaniu treści oddzielnie od kodu). Pojawiło się w 2011 roku, ustanawiając swoje standardy jeszcze przed masowym przejściem na architektury oparte na komponentach (takie jak React).
|
|
90
|
+
- Po drugie, gdy biblioteka zostanie szeroko przyjęta, trudno jest przestawić ekosystem na inne wzorce.
|
|
91
|
+
- Stosowanie podejścia scentralizowanego ułatwia też pracę w systemach zarządzania tłumaczeniami (TMS) takich jak Crowdin, Phrase czy Localized.
|
|
92
|
+
- Logika stojąca za podejściem per-component jest bardziej złożona niż w podejściu scentralizowanym i wymaga więcej czasu na rozwój, zwłaszcza gdy trzeba rozwiązywać problemy takie jak identyfikacja miejsca, w którym znajduje się dany content.
|
|
93
|
+
|
|
94
|
+
### Ok, ale dlaczego nie pozostać przy podejściu scentralizowanym?
|
|
95
|
+
|
|
96
|
+
Pozwól, że wyjaśnię, dlaczego może to być problematyczne dla Twojej aplikacji:
|
|
97
|
+
|
|
98
|
+
- **Nieużywane dane:**
|
|
99
|
+
Gdy ładuje się strona, często pobierane są treści ze wszystkich pozostałych stron. (W aplikacji z 10 stronami to 90% załadowanej zawartości jest nieużywane). Lazy-loadujesz modal? Biblioteka i18n i tak na to nie zważa — i tak najpierw ładuje stringi.
|
|
100
|
+
- **Wydajność:**
|
|
101
|
+
Przy każdym re-renderze każdy komponent zostaje obciążony olbrzymim ładunkiem JSON, co negatywnie wpływa na reaktywność aplikacji w miarę jej rozrostu.
|
|
102
|
+
- **Utrzymanie:**
|
|
103
|
+
Utrzymywanie dużych plików JSON jest uciążliwe. Musisz skakać między plikami, aby dodać tłumaczenie, upewniając się, że żadne tłumaczenia nie są pominięte i że nie pozostają żadne **orphan keys**.
|
|
104
|
+
- **Design-system:**
|
|
105
|
+
To powoduje niekompatybilność z design systemami (np. komponent `LoginForm`) i ogranicza możliwość duplikowania komponentów między różnymi aplikacjami.
|
|
106
|
+
|
|
107
|
+
**"Ale wymyśliliśmy Namespaces!"**
|
|
108
|
+
|
|
109
|
+
Oczywiście — to ogromny krok naprzód. Przyjrzyjmy się porównaniu rozmiaru głównego bundle'a dla konfiguracji Vite + React + React Router v7 + Intlayer. Zasymulowaliśmy aplikację z 20 stronami.
|
|
110
|
+
|
|
111
|
+
Pierwszy przykład nie uwzględnia leniwego ładowania tłumaczeń dla każdego locale i nie stosuje podziału na namespaces. Drugi obejmuje content purging + dynamiczne ładowanie tłumaczeń.
|
|
112
|
+
|
|
113
|
+
| Zoptymalizowany bundle | Bundle bez optymalizacji |
|
|
114
|
+
| ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
|  |  |
|
|
116
|
+
|
|
117
|
+
Dzięki namespaces, przeszliśmy z tej struktury:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
locale/
|
|
121
|
+
├── en.json
|
|
122
|
+
├── fr.json
|
|
123
|
+
└── es.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Do takiej:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
locale/
|
|
130
|
+
├── en/
|
|
131
|
+
│ ├── common.json
|
|
132
|
+
│ ├── navbar.json
|
|
133
|
+
│ ├── footer.json
|
|
134
|
+
│ ├── home.json
|
|
135
|
+
│ └── about.json
|
|
136
|
+
├── fr/
|
|
137
|
+
│ └── ...
|
|
138
|
+
└── es/
|
|
139
|
+
└── ...
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Teraz musisz precyzyjnie zarządzać, która część zawartości Twojej aplikacji powinna być ładowana i gdzie. W praktyce zdecydowana większość projektów pomija ten etap ze względu na jego złożoność (zobacz na przykład [przewodnik next-i18next](https://github.com/aymericzip/intlayer/blob/main/docs/blog/pl/i18n_using_next-i18next.md), aby zobaczyć wyzwania, jakie stanowi (tylko) przestrzeganie dobrych praktyk).
|
|
144
|
+
W konsekwencji te projekty kończą z opisanym wcześniej problemem masywnego ładowania plików JSON.
|
|
145
|
+
|
|
146
|
+
> Należy zauważyć, że problem ten nie dotyczy tylko i18next, lecz wszystkich scentralizowanych podejść wymienionych powyżej.
|
|
147
|
+
|
|
148
|
+
Jednak chcę przypomnieć, że nie wszystkie podejścia granularne to rozwiązują. Na przykład podejścia `vue-i18n SFC` czy `inlang` nie realizują domyślnie lazy loadingu tłumaczeń per-locale, więc po prostu zamieniasz problem wielkości bundla na inny.
|
|
149
|
+
|
|
150
|
+
Co więcej, bez odpowiedniego separation of concerns znacznie trudniej jest wyodrębnić tłumaczenia i udostępnić je tłumaczom do przeglądu.
|
|
151
|
+
|
|
152
|
+
### Jak podejście per-component w Intlayer rozwiązuje ten problem
|
|
153
|
+
|
|
154
|
+
Intlayer działa w kilku etapach:
|
|
155
|
+
|
|
156
|
+
1. **Deklaracja:** Zadeklaruj swoją zawartość w dowolnym miejscu kodu, używając plików `*.content.{ts|jsx|cjs|json|json5|...}`. Zapewnia to separation of concerns przy jednoczesnym utrzymaniu zawartości zlokalizowanej obok kodu. Plik zawartości może być per-locale lub wielojęzyczny.
|
|
157
|
+
2. **Przetwarzanie:** Intlayer uruchamia krok builda, aby przetworzyć logikę JS, obsłużyć brakujące tłumaczenia (fallbacki), wygenerować typy TypeScript, zarządzać zduplikowaną zawartością, pobierać treści z Twojego CMS i inne.
|
|
158
|
+
3. **Czyszczenie (Purging):** Gdy Twoja aplikacja się buduje, Intlayer usuwa nieużywaną zawartość (trochę jak Tailwind zarządza klasami) poprzez zastąpienie zawartości w następujący sposób:
|
|
159
|
+
|
|
160
|
+
**Deklaracja:**
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// src/MyComponent.tsx
|
|
164
|
+
export const MyComponent = () => {
|
|
165
|
+
const content = useIntlayer("my-key");
|
|
166
|
+
return <h1>{content.title}</h1>;
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
// src/myComponent.content.ts
|
|
172
|
+
export const {
|
|
173
|
+
key: "my-key",
|
|
174
|
+
content: t({
|
|
175
|
+
en: { title: "My title" },
|
|
176
|
+
fr: { title: "Mon titre" }
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Przetwarzanie:** Intlayer buduje słownik na podstawie pliku `.content` i generuje:
|
|
183
|
+
|
|
184
|
+
```json5
|
|
185
|
+
// .intlayer/dynamic_dictionary/pl/my-key.json
|
|
186
|
+
{
|
|
187
|
+
"key": "my-key",
|
|
188
|
+
"content": { "title": "Mój tytuł" },
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Zastąpienie:** Intlayer przekształca twój komponent podczas budowania aplikacji.
|
|
193
|
+
|
|
194
|
+
**- Tryb importu statycznego:**
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
// Reprezentacja komponentu w składni podobnej do JSX
|
|
198
|
+
export const MyComponent = () => {
|
|
199
|
+
const content = useDictionary({
|
|
200
|
+
key: "my-key",
|
|
201
|
+
content: {
|
|
202
|
+
nodeType: "translation",
|
|
203
|
+
translation: {
|
|
204
|
+
pl: { title: "Mój tytuł" },
|
|
205
|
+
en: { title: "My title" },
|
|
206
|
+
fr: { title: "Mon titre" },
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return <h1>{content.title}</h1>;
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**- Tryb importu dynamicznego:**
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
// Reprezentacja komponentu w składni podobnej do JSX
|
|
219
|
+
export const MyComponent = () => {
|
|
220
|
+
const content = useDictionaryAsync({
|
|
221
|
+
en: () =>
|
|
222
|
+
import(".intlayer/dynamic_dictionary/en/my-key.json", {
|
|
223
|
+
with: { type: "json" },
|
|
224
|
+
}).then((mod) => mod.default),
|
|
225
|
+
// Same for other languages
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return <h1>{content.title}</h1>;
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
> `useDictionaryAsync` używa mechanizmu podobnego do Suspense, aby ładować zlokalizowany JSON tylko wtedy, gdy jest to potrzebne.
|
|
233
|
+
|
|
234
|
+
**Kluczowe korzyści tego podejścia per-component:**
|
|
235
|
+
|
|
236
|
+
- Utrzymywanie deklaracji treści blisko komponentów umożliwia lepszą konserwowalność (np. przeniesienie komponentu do innej aplikacji lub design systemu. Usunięcie folderu komponentu usuwa także powiązaną treść, tak jak prawdopodobnie już robisz dla swoich `.test` i `.stories`)
|
|
237
|
+
|
|
238
|
+
/// Podejście per-component zapobiega konieczności, by agenty AI musiały przeskakiwać przez wszystkie różne pliki. Traktuje wszystkie tłumaczenia w jednym miejscu, ograniczając złożoność zadania i liczbę używanych tokenów.
|
|
239
|
+
|
|
240
|
+
### Ograniczenia
|
|
241
|
+
|
|
242
|
+
Oczywiście to podejście wiąże się z kompromisami:
|
|
243
|
+
|
|
244
|
+
- Trudniej je połączyć z innymi systemami l10n i dodatkowymi narzędziami.
|
|
245
|
+
- Możesz zostać zablokowany w konkretnym rozwiązaniu (co właściwie już ma miejsce przy każdym rozwiązaniu i18n ze względu na ich specyficzną składnię).
|
|
246
|
+
|
|
247
|
+
Dlatego Intlayer stara się dostarczyć kompletny zestaw narzędzi do i18n (100% darmowy i OSS), w tym tłumaczenia AI przy użyciu własnego dostawcy AI i kluczy API. Intlayer dostarcza również narzędzia do synchronizacji plików JSON, działające jak formatery wiadomości ICU / vue-i18n / i18next, mapujące treść do ich specyficznych formatów.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-09-10
|
|
3
|
+
updatedAt: 2025-09-10
|
|
4
|
+
title: i18n por componente vs. centralizado: Uma nova abordagem com Intlayer
|
|
5
|
+
description: Uma análise aprofundada das estratégias de internacionalização no React, comparando abordagens centralizadas, por chave e por componente, e apresentando o Intlayer.
|
|
6
|
+
keywords:
|
|
7
|
+
- i18n
|
|
8
|
+
- React
|
|
9
|
+
- Internationalization
|
|
10
|
+
- Intlayer
|
|
11
|
+
- Optimization
|
|
12
|
+
- Bundle Size
|
|
13
|
+
slugs:
|
|
14
|
+
- blog
|
|
15
|
+
- per-component-vs-centralized-i18n
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# i18n por componente vs. centralizado
|
|
19
|
+
|
|
20
|
+
A abordagem por componente não é um conceito novo. Por exemplo, no ecossistema Vue, o `vue-i18n` suporta [i18n SFC (Single File Component)](https://vue-i18n.intlify.dev/guide/advanced/sfc.html). O Nuxt também oferece [traduções por componente](https://i18n.nuxtjs.org/docs/guide/per-component-translations), e o Angular emprega um padrão similar através dos seus [Feature Modules](https://v17.angular.io/guide/feature-modules).
|
|
21
|
+
|
|
22
|
+
Mesmo em um app Flutter, muitas vezes encontramos este padrão:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
lib/
|
|
26
|
+
└── features/
|
|
27
|
+
└── login/
|
|
28
|
+
├── login_screen.dart
|
|
29
|
+
└── login_screen.i18n.dart # <- As traduções ficam aqui
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```dart fileName='lib/features/login/login_screen.i18n.dart'
|
|
33
|
+
import 'package:i18n_extension/i18n_extension.dart';
|
|
34
|
+
|
|
35
|
+
extension Localization on String {
|
|
36
|
+
static var _t = Translations.byText("en") +
|
|
37
|
+
{
|
|
38
|
+
"Hello": {
|
|
39
|
+
"en": "Hello",
|
|
40
|
+
"fr": "Bonjour",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
String get i18n => localize(this, _t);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
No entanto, no mundo React, vemos principalmente abordagens diferentes, que agruparei em três categorias:
|
|
49
|
+
|
|
50
|
+
<Columns>
|
|
51
|
+
<Column>
|
|
52
|
+
|
|
53
|
+
**Abordagem centralizada** (i18next, next-intl, react-intl, lingui)
|
|
54
|
+
|
|
55
|
+
- (sem namespaces) considera uma única fonte para recuperar conteúdo. Por padrão, você carrega o conteúdo de todas as páginas quando seu app é carregado.
|
|
56
|
+
|
|
57
|
+
</Column>
|
|
58
|
+
<Column>
|
|
59
|
+
|
|
60
|
+
**Abordagem granular** (intlayer, inlang)
|
|
61
|
+
|
|
62
|
+
- refina a recuperação de conteúdo por chave ou por componente.
|
|
63
|
+
|
|
64
|
+
</Column>
|
|
65
|
+
</Columns>
|
|
66
|
+
|
|
67
|
+
> Neste blog, não vou focar em soluções baseadas em compilador, que já abordei aqui: [Compiler vs Declarative i18n](https://github.com/aymericzip/intlayer/blob/main/docs/blog/pt/compiler_vs_declarative_i18n.md).
|
|
68
|
+
> Note que i18n baseado em compilador (por exemplo, Lingui) simplesmente automatiza a extração e o carregamento do conteúdo. Por baixo do capô, eles frequentemente compartilham as mesmas limitações que outras abordagens.
|
|
69
|
+
|
|
70
|
+
> Note que quanto mais você refina a forma como recupera seu conteúdo, maior o risco de inserir estado e lógica adicionais nos seus componentes.
|
|
71
|
+
|
|
72
|
+
As abordagens granulares são mais flexíveis do que as centralizadas, mas frequentemente implicam um compromisso. Mesmo que o "tree shaking" seja divulgado por essas bibliotecas, na prática acaba por carregar uma página em todas as línguas.
|
|
73
|
+
|
|
74
|
+
Portanto, de forma geral, a decisão resume-se assim:
|
|
75
|
+
|
|
76
|
+
- Se a sua aplicação tem mais páginas do que línguas, deve favorecer uma abordagem granular.
|
|
77
|
+
- Se tiver mais línguas do que páginas, deve optar por uma abordagem centralizada.
|
|
78
|
+
|
|
79
|
+
Obviamente, os autores das bibliotecas estão cientes dessas limitações e oferecem soluções alternativas. Entre elas: dividir em namespaces, carregar ficheiros JSON dinamicamente (`await import()`), ou purgar conteúdo em build time.
|
|
80
|
+
|
|
81
|
+
Ao mesmo tempo, deve saber que quando carrega dinamicamente o seu conteúdo, introduz pedidos adicionais ao seu servidor. Cada `useState` extra ou hook significa um pedido extra ao servidor.
|
|
82
|
+
|
|
83
|
+
> Para resolver este ponto, o Intlayer sugere agrupar múltiplas definições de conteúdo sob a mesma chave; o Intlayer irá então mesclar esse conteúdo.
|
|
84
|
+
|
|
85
|
+
Mas, de todas essas soluções, fica claro que a abordagem mais popular é a centralizada.
|
|
86
|
+
|
|
87
|
+
### Então por que a abordagem centralizada é tão popular?
|
|
88
|
+
|
|
89
|
+
- Primeiro, o i18next foi a primeira solução a tornar-se amplamente utilizada, seguindo uma filosofia inspirada nas arquiteturas PHP e Java (MVC), que se baseiam numa separação estrita de responsabilidades (mantendo o conteúdo separado do código). Chegou em 2011, estabelecendo os seus padrões mesmo antes da grande mudança para arquiteturas baseadas em Componentes (como o React).
|
|
90
|
+
- Depois, uma vez que uma biblioteca é amplamente adotada, torna-se difícil migrar o ecossistema para outros padrões.
|
|
91
|
+
- Usar uma abordagem centralizada também facilita as coisas em sistemas de gestão de traduções (Translation Management Systems) como Crowdin, Phrase ou Localized.
|
|
92
|
+
- A lógica da abordagem por componente é mais complexa do que a centralizada e requer tempo extra de desenvolvimento, especialmente quando é necessário resolver problemas como identificar onde o conteúdo está localizado.
|
|
93
|
+
|
|
94
|
+
### Ok, mas por que não ficar apenas com uma abordagem Centralizada?
|
|
95
|
+
|
|
96
|
+
Deixe-me explicar por que isso pode ser problemático para a sua app:
|
|
97
|
+
|
|
98
|
+
- **Dados não utilizados:**
|
|
99
|
+
Quando uma página carrega, costuma-se carregar o conteúdo de todas as outras páginas. (Numa app de 10 páginas, isso significa 90% de conteúdo carregado que não é utilizado). Você faz lazy load de um modal? A biblioteca i18n não se importa — ela carrega as strings primeiro de qualquer forma.
|
|
100
|
+
- **Desempenho:**
|
|
101
|
+
A cada re-render, todos os seus componentes são hidratados com um payload JSON massivo, o que impacta a reatividade da sua app à medida que ela cresce.
|
|
102
|
+
- **Manutenção:**
|
|
103
|
+
Manter ficheiros JSON grandes é doloroso. Você tem de saltar entre ficheiros para inserir uma tradução, garantindo que não faltam traduções e que não restam **chaves órfãs**.
|
|
104
|
+
- **Sistema de design:**
|
|
105
|
+
Isto cria incompatibilidade com design systems (por exemplo, um componente `LoginForm`) e restringe a duplicação de componentes entre diferentes apps.
|
|
106
|
+
|
|
107
|
+
**"Mas inventámos Namespaces!"**
|
|
108
|
+
|
|
109
|
+
Claro, e isso é um enorme avanço. Vamos ver a comparação do tamanho do bundle principal de uma stack Vite + React + React Router v7 + Intlayer. Simulámos uma aplicação com 20 páginas.
|
|
110
|
+
|
|
111
|
+
O primeiro exemplo não inclui traduções lazy-loaded por locale nem divisão por namespaces. O segundo inclui purga de conteúdo + carregamento dinâmico das traduções.
|
|
112
|
+
|
|
113
|
+
| Bundle otimizado | Bundle não otimizado |
|
|
114
|
+
| ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
115
|
+
|  |  |
|
|
116
|
+
|
|
117
|
+
Portanto, graças aos namespaces, mudámos desta estrutura:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
locale/
|
|
121
|
+
├── en.json
|
|
122
|
+
├── fr.json
|
|
123
|
+
└── es.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
To this one:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
locale/
|
|
130
|
+
├── en/
|
|
131
|
+
│ ├── common.json
|
|
132
|
+
│ ├── navbar.json
|
|
133
|
+
│ ├── footer.json
|
|
134
|
+
│ ├── home.json
|
|
135
|
+
│ └── about.json
|
|
136
|
+
├── fr/
|
|
137
|
+
│ └── ...
|
|
138
|
+
└── es/
|
|
139
|
+
└── ...
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Agora tem de gerir com precisão que parte do conteúdo da sua app deve ser carregada e onde. Em conclusão, a grande maioria dos projetos simplesmente ignora esta etapa devido à complexidade (veja o [guia do next-i18next](https://github.com/aymericzip/intlayer/blob/main/docs/blog/pt/i18n_using_next-i18next.md), por exemplo, para ver os desafios que isso representa (apenas) ao seguir as boas práticas). Consequentemente, esses projetos acabam com o problema do carregamento massivo de JSON explicado anteriormente.
|
|
144
|
+
|
|
145
|
+
> Note que este problema não é específico do i18next, mas de todas as abordagens centralizadas listadas acima.
|
|
146
|
+
|
|
147
|
+
No entanto, quero relembrar que nem todas as abordagens granulares resolvem isto. Por exemplo, as abordagens `vue-i18n SFC` ou `inlang` não fazem, por si só, lazy load das traduções por locale, pelo que você está simplesmente a trocar o problema do tamanho do bundle por outro.
|
|
148
|
+
|
|
149
|
+
Além disso, sem uma separação de responsabilidades adequada, torna-se muito mais difícil extrair e fornecer as suas traduções aos tradutores para revisão.
|
|
150
|
+
|
|
151
|
+
### Como a abordagem por componente do Intlayer resolve isto
|
|
152
|
+
|
|
153
|
+
O Intlayer procede em vários passos:
|
|
154
|
+
|
|
155
|
+
1. **Declaração:** Declare o seu conteúdo em qualquer parte da sua codebase usando ficheiros `*.content.{ts|jsx|cjs|json|json5|...}`. Isto assegura separação de responsabilidades enquanto mantém o conteúdo co-localizado com o código. Um ficheiro de conteúdo pode ser por locale ou multilíngue.
|
|
156
|
+
2. **Processamento:** Intlayer executa uma etapa de build para processar a lógica JS, tratar fallbacks de traduções ausentes, gerar tipos TypeScript, gerenciar conteúdo duplicado, buscar conteúdo do seu CMS e mais.
|
|
157
|
+
3. **Purgamento:** Quando sua aplicação é construída, o Intlayer purga o conteúdo não utilizado (um pouco como o Tailwind gerencia suas classes) substituindo o conteúdo da seguinte forma:
|
|
158
|
+
|
|
159
|
+
**Declaração:**
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
// src/MyComponent.tsx
|
|
163
|
+
export const MyComponent = () => {
|
|
164
|
+
const content = useIntlayer("my-key");
|
|
165
|
+
return <h1>{content.title}</h1>;
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// src/myComponent.content.ts
|
|
171
|
+
export const {
|
|
172
|
+
key: "my-key",
|
|
173
|
+
content: t({
|
|
174
|
+
en: { title: "My title" },
|
|
175
|
+
fr: { title: "Mon titre" }
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Processamento:** Intlayer constrói o dicionário com base no arquivo `.content` e gera:
|
|
182
|
+
|
|
183
|
+
```json5
|
|
184
|
+
// .intlayer/dynamic_dictionary/en/my-key.json
|
|
185
|
+
{
|
|
186
|
+
"key": "my-key",
|
|
187
|
+
"content": { "title": "My title" },
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Substituição:** Intlayer transforma seu componente durante o build da aplicação.
|
|
192
|
+
|
|
193
|
+
**- Modo de Importação Estática:**
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
// Representação do componente em sintaxe semelhante a JSX
|
|
197
|
+
export const MyComponent = () => {
|
|
198
|
+
const content = useDictionary({
|
|
199
|
+
key: "my-key",
|
|
200
|
+
content: {
|
|
201
|
+
nodeType: "translation",
|
|
202
|
+
translation: {
|
|
203
|
+
pt: { title: "Meu título" },
|
|
204
|
+
en: { title: "My title" },
|
|
205
|
+
fr: { title: "Mon titre" },
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return <h1>{content.title}</h1>;
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**- Modo de Importação Dinâmica:**
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
// Representação do componente em sintaxe semelhante a JSX
|
|
218
|
+
export const MyComponent = () => {
|
|
219
|
+
const content = useDictionaryAsync({
|
|
220
|
+
en: () =>
|
|
221
|
+
import(".intlayer/dynamic_dictionary/en/my-key.json", {
|
|
222
|
+
with: { type: "json" },
|
|
223
|
+
}).then((mod) => mod.default),
|
|
224
|
+
// O mesmo para outras línguas
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return <h1>{content.title}</h1>;
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> `useDictionaryAsync` usa um mecanismo semelhante ao Suspense para carregar o JSON localizado apenas quando necessário.
|
|
232
|
+
|
|
233
|
+
**Principais vantagens desta abordagem por componente:**
|
|
234
|
+
|
|
235
|
+
- Manter a declaração do conteúdo próxima dos seus componentes permite uma melhor facilidade de manutenção (por exemplo, mover um componente para outra app ou design system. Eliminar a pasta do componente remove também o conteúdo relacionado, como provavelmente já faz com os seus `.test`, `.stories`)
|
|
236
|
+
|
|
237
|
+
/// Uma abordagem por componente impede que agentes de IA precisem saltar entre todos os seus diferentes ficheiros. Trata todas as traduções num só lugar, limitando a complexidade da tarefa e a quantidade de tokens utilizados.
|
|
238
|
+
|
|
239
|
+
### Limitações
|
|
240
|
+
|
|
241
|
+
Claro, esta abordagem implica compensações:
|
|
242
|
+
|
|
243
|
+
- É mais difícil ligar a outros sistemas de l10n e a ferramentas adicionais.
|
|
244
|
+
- Fica-se preso (o que basicamente já acontece com qualquer solução i18n devido à sua sintaxe específica).
|
|
245
|
+
|
|
246
|
+
É por isso que o Intlayer tenta fornecer um conjunto completo de ferramentas para i18n (100% free and OSS), incluindo tradução por IA usando o seu próprio AI Provider e as suas chaves de API. O Intlayer também fornece ferramentas para sincronizar os seus ficheiros JSON, funcionando como formatadores de mensagens do ICU / vue-i18n / i18next para mapear o conteúdo para os seus formatos específicos.
|