@intlayer/docs 9.5.2 → 9.5.3
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/README.md +1 -1
- package/blog/ar/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/ar/i18next_vs_intlayer.md +445 -0
- package/blog/ar/is_i18next_outdated.md +1 -1
- package/blog/ar/is_next-intl_outdated.md +1 -1
- package/blog/ar/is_vue-i18n_outdated.md +1 -1
- package/blog/ar/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/ar/lingui_vs_intlayer.md +381 -0
- package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ar/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/ar/next-intl_vs_intlayer.md +397 -0
- package/blog/ar/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/ar/vue-i18n_vs_intlayer-vue-i18n.md +286 -0
- package/blog/ar/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/de/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/de/i18next_vs_intlayer.md +445 -0
- package/blog/de/is_i18next_outdated.md +1 -1
- package/blog/de/is_next-intl_outdated.md +1 -1
- package/blog/de/is_vue-i18n_outdated.md +1 -1
- package/blog/de/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/de/lingui_vs_intlayer.md +381 -0
- package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/de/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/de/next-intl_vs_intlayer.md +415 -0
- package/blog/de/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/de/vue-i18n_vs_intlayer_benchmark.md +355 -0
- package/blog/en/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/en/i18next_vs_intlayer.md +445 -0
- package/blog/en/is_i18next_outdated.md +1 -1
- package/blog/en/is_next-intl_outdated.md +1 -1
- package/blog/en/is_vue-i18n_outdated.md +1 -1
- package/blog/en/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/en/lingui_vs_intlayer.md +381 -0
- package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/en/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/en/next-intl_vs_intlayer.md +415 -0
- package/blog/en/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/en/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/en/vue-i18n_vs_intlayer.md +1 -1
- package/blog/en/vue-i18n_vs_intlayer_benchmark.md +361 -0
- package/blog/en-GB/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/en-GB/i18next_vs_intlayer.md +445 -0
- package/blog/en-GB/is_i18next_outdated.md +1 -1
- package/blog/en-GB/is_next-intl_outdated.md +1 -1
- package/blog/en-GB/is_vue-i18n_outdated.md +1 -1
- package/blog/en-GB/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/en-GB/lingui_vs_intlayer.md +381 -0
- package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/en-GB/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/en-GB/next-intl_vs_intlayer.md +415 -0
- package/blog/en-GB/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/en-GB/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/en-GB/vue-i18n_vs_intlayer.md +1 -1
- package/blog/en-GB/vue-i18n_vs_intlayer_benchmark.md +245 -0
- package/blog/es/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/es/i18next_vs_intlayer.md +445 -0
- package/blog/es/is_i18next_outdated.md +1 -1
- package/blog/es/is_next-intl_outdated.md +1 -1
- package/blog/es/is_vue-i18n_outdated.md +1 -1
- package/blog/es/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/es/lingui_vs_intlayer.md +381 -0
- package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/es/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/es/next-intl_vs_intlayer.md +417 -0
- package/blog/es/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/es/vue-i18n_vs_intlayer_benchmark.md +363 -0
- package/blog/fr/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/fr/i18next_vs_intlayer.md +445 -0
- package/blog/fr/is_i18next_outdated.md +1 -1
- package/blog/fr/is_next-intl_outdated.md +1 -1
- package/blog/fr/is_vue-i18n_outdated.md +1 -1
- package/blog/fr/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/fr/lingui_vs_intlayer.md +381 -0
- package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/fr/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/fr/next-intl_vs_intlayer.md +417 -0
- package/blog/fr/vue-i18n_vs_intlayer-vue-i18n.md +287 -0
- package/blog/fr/vue-i18n_vs_intlayer_benchmark.md +39 -0
- package/blog/hi/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/hi/i18next_vs_intlayer.md +445 -0
- package/blog/hi/is_i18next_outdated.md +1 -1
- package/blog/hi/is_next-intl_outdated.md +1 -1
- package/blog/hi/is_vue-i18n_outdated.md +1 -1
- package/blog/hi/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/hi/lingui_vs_intlayer.md +381 -0
- package/blog/hi/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/hi/next-intl_vs_intlayer.md +397 -0
- package/blog/hi/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/hi/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/hi/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/id/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/id/i18next_vs_intlayer.md +445 -0
- package/blog/id/is_i18next_outdated.md +1 -1
- package/blog/id/is_next-intl_outdated.md +1 -1
- package/blog/id/is_vue-i18n_outdated.md +1 -1
- package/blog/id/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/id/lingui_vs_intlayer.md +381 -0
- package/blog/id/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/id/next-intl_vs_intlayer-next-intl.md +310 -0
- package/blog/id/next-intl_vs_intlayer.md +397 -0
- package/blog/id/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/id/vue-i18n_vs_intlayer-vue-i18n.md +36 -0
- package/blog/id/vue-i18n_vs_intlayer.md +1 -1
- package/blog/id/vue-i18n_vs_intlayer_benchmark.md +41 -0
- package/blog/it/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/it/i18next_vs_intlayer.md +445 -0
- package/blog/it/is_i18next_outdated.md +1 -1
- package/blog/it/is_next-intl_outdated.md +1 -1
- package/blog/it/is_vue-i18n_outdated.md +1 -1
- package/blog/it/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/it/lingui_vs_intlayer.md +381 -0
- package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/it/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/it/next-intl_vs_intlayer.md +418 -0
- package/blog/it/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/it/vue-i18n_vs_intlayer_benchmark.md +34 -0
- package/blog/ja/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/ja/i18next_vs_intlayer.md +447 -0
- package/blog/ja/is_i18next_outdated.md +1 -1
- package/blog/ja/is_next-intl_outdated.md +1 -1
- package/blog/ja/is_vue-i18n_outdated.md +1 -1
- package/blog/ja/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/ja/lingui_vs_intlayer.md +381 -0
- package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ja/next-intl_vs_intlayer-next-intl.md +310 -0
- package/blog/ja/next-intl_vs_intlayer.md +422 -0
- package/blog/ja/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/ja/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/ja/vue-i18n_vs_intlayer.md +1 -1
- package/blog/ja/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/ko/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/ko/i18next_vs_intlayer.md +447 -0
- package/blog/ko/is_i18next_outdated.md +1 -1
- package/blog/ko/is_next-intl_outdated.md +1 -1
- package/blog/ko/is_vue-i18n_outdated.md +1 -1
- package/blog/ko/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/ko/lingui_vs_intlayer.md +381 -0
- package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ko/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/ko/next-intl_vs_intlayer.md +397 -0
- package/blog/ko/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/ko/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/ko/vue-i18n_vs_intlayer.md +1 -1
- package/blog/ko/vue-i18n_vs_intlayer_benchmark.md +366 -0
- package/blog/pl/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/pl/i18next_vs_intlayer.md +445 -0
- package/blog/pl/is_i18next_outdated.md +1 -1
- package/blog/pl/is_next-intl_outdated.md +1 -1
- package/blog/pl/is_vue-i18n_outdated.md +1 -1
- package/blog/pl/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/pl/lingui_vs_intlayer.md +381 -0
- package/blog/pl/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/pl/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/pl/next-intl_vs_intlayer.md +397 -0
- package/blog/pl/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/pl/vue-i18n_vs_intlayer-vue-i18n.md +289 -0
- package/blog/pl/vue-i18n_vs_intlayer.md +1 -1
- package/blog/pl/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/pt/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/pt/i18next_vs_intlayer.md +445 -0
- package/blog/pt/is_i18next_outdated.md +1 -1
- package/blog/pt/is_next-intl_outdated.md +1 -1
- package/blog/pt/is_vue-i18n_outdated.md +1 -1
- package/blog/pt/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/pt/lingui_vs_intlayer.md +381 -0
- package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/pt/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/pt/next-intl_vs_intlayer.md +411 -0
- package/blog/pt/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/pt/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/ru/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/ru/i18next_vs_intlayer.md +445 -0
- package/blog/ru/is_i18next_outdated.md +1 -1
- package/blog/ru/is_next-intl_outdated.md +1 -1
- package/blog/ru/is_vue-i18n_outdated.md +1 -1
- package/blog/ru/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/ru/lingui_vs_intlayer.md +381 -0
- package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ru/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/ru/next-intl_vs_intlayer.md +417 -0
- package/blog/ru/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/ru/vue-i18n_vs_intlayer_benchmark.md +54 -0
- package/blog/tr/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/tr/i18next_vs_intlayer.md +445 -0
- package/blog/tr/is_i18next_outdated.md +1 -1
- package/blog/tr/is_next-intl_outdated.md +1 -1
- package/blog/tr/is_vue-i18n_outdated.md +1 -1
- package/blog/tr/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/tr/lingui_vs_intlayer.md +381 -0
- package/blog/tr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/tr/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/tr/next-intl_vs_intlayer.md +397 -0
- package/blog/tr/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/tr/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/tr/vue-i18n_vs_intlayer.md +1 -1
- package/blog/tr/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/uk/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/uk/i18next_vs_intlayer.md +445 -0
- package/blog/uk/is_i18next_outdated.md +1 -1
- package/blog/uk/is_next-intl_outdated.md +1 -1
- package/blog/uk/is_vue-i18n_outdated.md +1 -1
- package/blog/uk/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/uk/lingui_vs_intlayer.md +381 -0
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/uk/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/uk/next-intl_vs_intlayer.md +397 -0
- package/blog/uk/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/uk/vue-i18n_vs_intlayer-vue-i18n.md +270 -0
- package/blog/uk/vue-i18n_vs_intlayer.md +1 -1
- package/blog/uk/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/vi/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/vi/i18next_vs_intlayer.md +445 -0
- package/blog/vi/is_i18next_outdated.md +1 -1
- package/blog/vi/is_next-intl_outdated.md +1 -1
- package/blog/vi/is_vue-i18n_outdated.md +1 -1
- package/blog/vi/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/vi/lingui_vs_intlayer.md +381 -0
- package/blog/vi/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/vi/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/vi/next-intl_vs_intlayer.md +397 -0
- package/blog/vi/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/vi/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/vi/vue-i18n_vs_intlayer.md +1 -1
- package/blog/vi/vue-i18n_vs_intlayer_benchmark.md +0 -0
- package/blog/zh/i18next_vs_intlayer-i18next.md +327 -0
- package/blog/zh/i18next_vs_intlayer.md +445 -0
- package/blog/zh/is_i18next_outdated.md +1 -1
- package/blog/zh/is_next-intl_outdated.md +1 -1
- package/blog/zh/is_vue-i18n_outdated.md +1 -1
- package/blog/zh/lingui_vs_intlayer-lingui.md +304 -0
- package/blog/zh/lingui_vs_intlayer.md +381 -0
- package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/zh/next-intl_vs_intlayer-next-intl.md +309 -0
- package/blog/zh/next-intl_vs_intlayer.md +422 -0
- package/blog/zh/react-i18next_vs_react-intl_vs_intlayer.md +1 -1
- package/blog/zh/vue-i18n_vs_intlayer-vue-i18n.md +288 -0
- package/blog/zh/vue-i18n_vs_intlayer.md +1 -1
- package/blog/zh/vue-i18n_vs_intlayer_benchmark.md +30 -0
- package/dist/cjs/generated/blog.entry.cjs +160 -0
- package/dist/cjs/generated/blog.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/esm/generated/blog.entry.mjs +160 -0
- package/dist/esm/generated/blog.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/types/generated/blog.entry.d.ts +8 -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/benchmark/nextjs.md +1 -1
- package/docs/ar/benchmark/solid.md +1 -1
- package/docs/ar/benchmark/svelte.md +1 -1
- package/docs/ar/benchmark/tanstack.md +1 -1
- package/docs/ar/benchmark/vue.md +1 -1
- package/docs/ar/interest_of_intlayer.md +1 -1
- package/docs/bn/interest_of_intlayer.md +1 -1
- package/docs/cs/interest_of_intlayer.md +1 -1
- package/docs/de/benchmark/nextjs.md +1 -1
- package/docs/de/benchmark/solid.md +1 -1
- package/docs/de/benchmark/svelte.md +1 -1
- package/docs/de/benchmark/tanstack.md +1 -1
- package/docs/de/benchmark/vue.md +1 -1
- package/docs/de/interest_of_intlayer.md +1 -1
- package/docs/en/benchmark/nextjs.md +1 -1
- package/docs/en/benchmark/solid.md +1 -1
- package/docs/en/benchmark/svelte.md +1 -1
- package/docs/en/benchmark/tanstack.md +1 -1
- package/docs/en/benchmark/vue.md +1 -1
- package/docs/en/interest_of_intlayer.md +1 -1
- package/docs/en-GB/benchmark/nextjs.md +1 -1
- package/docs/en-GB/benchmark/solid.md +1 -1
- package/docs/en-GB/benchmark/svelte.md +1 -1
- package/docs/en-GB/benchmark/tanstack.md +1 -1
- package/docs/en-GB/benchmark/vue.md +1 -1
- package/docs/en-GB/interest_of_intlayer.md +1 -1
- package/docs/es/benchmark/nextjs.md +1 -1
- package/docs/es/benchmark/solid.md +1 -1
- package/docs/es/benchmark/svelte.md +1 -1
- package/docs/es/benchmark/tanstack.md +1 -1
- package/docs/es/benchmark/vue.md +1 -1
- package/docs/es/interest_of_intlayer.md +1 -1
- package/docs/fr/benchmark/nextjs.md +1 -1
- package/docs/fr/benchmark/solid.md +1 -1
- package/docs/fr/benchmark/svelte.md +1 -1
- package/docs/fr/benchmark/tanstack.md +1 -1
- package/docs/fr/benchmark/vue.md +1 -1
- package/docs/fr/interest_of_intlayer.md +1 -1
- package/docs/hi/benchmark/nextjs.md +1 -1
- package/docs/hi/benchmark/solid.md +1 -1
- package/docs/hi/benchmark/svelte.md +1 -1
- package/docs/hi/benchmark/tanstack.md +1 -1
- package/docs/hi/benchmark/vue.md +1 -1
- package/docs/hi/interest_of_intlayer.md +1 -1
- package/docs/id/benchmark/nextjs.md +1 -1
- package/docs/id/benchmark/solid.md +1 -1
- package/docs/id/benchmark/svelte.md +1 -1
- package/docs/id/benchmark/tanstack.md +1 -1
- package/docs/id/benchmark/vue.md +1 -1
- package/docs/id/interest_of_intlayer.md +1 -1
- package/docs/it/benchmark/nextjs.md +1 -1
- package/docs/it/benchmark/solid.md +1 -1
- package/docs/it/benchmark/svelte.md +1 -1
- package/docs/it/benchmark/tanstack.md +1 -1
- package/docs/it/benchmark/vue.md +1 -1
- package/docs/it/interest_of_intlayer.md +1 -1
- package/docs/ja/benchmark/nextjs.md +1 -1
- package/docs/ja/benchmark/solid.md +1 -1
- package/docs/ja/benchmark/svelte.md +1 -1
- package/docs/ja/benchmark/tanstack.md +1 -1
- package/docs/ja/benchmark/vue.md +1 -1
- package/docs/ja/interest_of_intlayer.md +1 -1
- package/docs/ko/benchmark/nextjs.md +1 -1
- package/docs/ko/benchmark/solid.md +1 -1
- package/docs/ko/benchmark/svelte.md +1 -1
- package/docs/ko/benchmark/tanstack.md +1 -1
- package/docs/ko/benchmark/vue.md +1 -1
- package/docs/ko/interest_of_intlayer.md +1 -1
- package/docs/nl/interest_of_intlayer.md +1 -1
- package/docs/pl/benchmark/nextjs.md +1 -1
- package/docs/pl/benchmark/solid.md +1 -1
- package/docs/pl/benchmark/svelte.md +1 -1
- package/docs/pl/benchmark/tanstack.md +1 -1
- package/docs/pl/benchmark/vue.md +1 -1
- package/docs/pl/interest_of_intlayer.md +1 -1
- package/docs/pt/benchmark/nextjs.md +1 -1
- package/docs/pt/benchmark/solid.md +1 -1
- package/docs/pt/benchmark/svelte.md +1 -1
- package/docs/pt/benchmark/tanstack.md +1 -1
- package/docs/pt/benchmark/vue.md +1 -1
- package/docs/pt/interest_of_intlayer.md +1 -1
- package/docs/ru/benchmark/nextjs.md +1 -1
- package/docs/ru/benchmark/solid.md +1 -1
- package/docs/ru/benchmark/svelte.md +1 -1
- package/docs/ru/benchmark/tanstack.md +1 -1
- package/docs/ru/benchmark/vue.md +1 -1
- package/docs/ru/interest_of_intlayer.md +1 -1
- package/docs/tr/benchmark/nextjs.md +1 -1
- package/docs/tr/benchmark/solid.md +1 -1
- package/docs/tr/benchmark/svelte.md +1 -1
- package/docs/tr/benchmark/tanstack.md +1 -1
- package/docs/tr/benchmark/vue.md +1 -1
- package/docs/tr/interest_of_intlayer.md +1 -1
- package/docs/uk/benchmark/nextjs.md +1 -1
- package/docs/uk/benchmark/solid.md +1 -1
- package/docs/uk/benchmark/svelte.md +1 -1
- package/docs/uk/benchmark/tanstack.md +1 -1
- package/docs/uk/benchmark/vue.md +1 -1
- package/docs/uk/interest_of_intlayer.md +1 -1
- package/docs/ur/interest_of_intlayer.md +1 -1
- package/docs/vi/benchmark/nextjs.md +1 -1
- package/docs/vi/benchmark/solid.md +1 -1
- package/docs/vi/benchmark/svelte.md +1 -1
- package/docs/vi/benchmark/tanstack.md +1 -1
- package/docs/vi/benchmark/vue.md +1 -1
- package/docs/vi/interest_of_intlayer.md +1 -1
- package/docs/zh/benchmark/nextjs.md +1 -1
- package/docs/zh/benchmark/solid.md +1 -1
- package/docs/zh/benchmark/svelte.md +1 -1
- package/docs/zh/benchmark/tanstack.md +1 -1
- package/docs/zh/benchmark/vue.md +1 -1
- package/docs/zh/interest_of_intlayer.md +1 -1
- package/docs/zh-TW/interest_of_intlayer.md +1 -1
- package/frequent_questions/ar/claude_code_token_consumption.md +152 -0
- package/frequent_questions/de/claude_code_token_consumption.md +152 -0
- package/frequent_questions/en/claude_code_token_consumption.md +152 -0
- package/frequent_questions/en-GB/claude_code_token_consumption.md +152 -0
- package/frequent_questions/es/claude_code_token_consumption.md +152 -0
- package/frequent_questions/fr/claude_code_token_consumption.md +152 -0
- package/frequent_questions/hi/claude_code_token_consumption.md +152 -0
- package/frequent_questions/id/claude_code_token_consumption.md +152 -0
- package/frequent_questions/it/claude_code_token_consumption.md +152 -0
- package/frequent_questions/ja/claude_code_token_consumption.md +152 -0
- package/frequent_questions/ko/claude_code_token_consumption.md +152 -0
- package/frequent_questions/pl/claude_code_token_consumption.md +152 -0
- package/frequent_questions/pt/claude_code_token_consumption.md +152 -0
- package/frequent_questions/ru/claude_code_token_consumption.md +152 -0
- package/frequent_questions/tr/claude_code_token_consumption.md +152 -0
- package/frequent_questions/uk/claude_code_token_consumption.md +152 -0
- package/frequent_questions/vi/claude_code_token_consumption.md +152 -0
- package/frequent_questions/zh/claude_code_token_consumption.md +152 -0
- package/package.json +6 -6
- package/src/generated/blog.entry.ts +160 -0
- package/src/generated/frequentQuestions.entry.ts +20 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-09-13
|
|
3
|
+
updatedAt: 2026-09-13
|
|
4
|
+
title: "vue-i18n مقابل @intlayer/vue-i18n: نفس الواجهة البرمجية، حزمة مختلفة"
|
|
5
|
+
description: ما الذي يتغير عندما تحتفظ تطبيقات Vue 3 باستدعاءات vue-i18n الخاصة بها لكنها تخدمها من خلال محول التوافق @intlayer/vue-i18n. JavaScript لكل صفحة، حجم وقت التشغيل، حجم المكون والتسرب المقاس على نفس كود Vite + Vue، بالإضافة إلى ما يحتفظ به المحول وما يتجاهله وما لا يمكنه استبداله.
|
|
6
|
+
keywords:
|
|
7
|
+
- vue-i18n
|
|
8
|
+
- "@intlayer/vue-i18n"
|
|
9
|
+
- Intlayer
|
|
10
|
+
- محول التوافق
|
|
11
|
+
- الهجرة
|
|
12
|
+
- الدولية
|
|
13
|
+
- i18n
|
|
14
|
+
- مقارنة الأداء
|
|
15
|
+
- حجم الحزمة
|
|
16
|
+
- مدونة
|
|
17
|
+
- Vue
|
|
18
|
+
- Nuxt
|
|
19
|
+
- Vite
|
|
20
|
+
slugs:
|
|
21
|
+
- blog
|
|
22
|
+
- vue-i18n-vs-intlayer-vue-i18n
|
|
23
|
+
author: aymericzip
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# vue-i18n مقابل @intlayer/vue-i18n | نفس الواجهة البرمجية، حزمة مختلفة
|
|
27
|
+
|
|
28
|
+
`@intlayer/vue-i18n` هو محول توافقي: يعرّض واجهة برمجية (API) من `vue-i18n` (`createI18n`، `useI18n`، `t()`، `d()`، `n()`، `$t`، `v-t`، `i18n.global.locale`...) ويقدمها من القواميس المترجمة بواسطة Intlayer. ملفات `.vue` الخاصة بك لا تتغير. ما يرتبط به `t("footer.github")` هو ما يتغير.
|
|
29
|
+
|
|
30
|
+
تقيس هذه المقالة هذا التبديل على نفس تطبيق Vite + Vue 3، تم بناؤه مرة واحدة باستخدام `vue-i18n` ومرة أخرى باستخدام المحول. تأتي الأرقام من [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom). لمقارنة `vue-i18n` و Intlayer كمكتبات، اقرأ [vue-i18n vs Intlayer](https://intlayer.org/blog/vue-i18n-vs-intlayer) و[vue-i18n vs Intlayer benchmark](https://intlayer.org/blog/vue-i18n-vs-intlayer-benchmark). هذا يتعلق بما يغيره المحول عندما تحتفظ بمكوناتك كما هي.
|
|
31
|
+
|
|
32
|
+
<TOC/>
|
|
33
|
+
|
|
34
|
+
> **ملخص سريع**: على نفس تطبيق Vite + Vue 3، استبدال `vue-i18n` بـ `@intlayer/vue-i18n` أخذ JavaScript لكل صفحة من **134.9 KB إلى 47.0 KB** gzip (التطبيق بدون i18n يزن 41.3 KB)، وقت التشغيل من **24.3 KB إلى 7.9 KB**، متوسط المكون من **196 KB إلى 8.4 KB**، وتسرب السلاسل النصية للصفحات الأجنبية من **90% إلى 0%**، بدون تعديل أي ملف `.vue`. `createI18n({ messages })` يستمر في العمل كخيار بديل؛ أزل استيراد JSON للحصول على الأرقام أعلاه. كتل SFC `<i18n>` و `setLocaleMessage()` في وقت التشغيل هما الميزتان اللتان لا تنتقلان.
|
|
35
|
+
|
|
36
|
+
## ما هو `@intlayer/vue-i18n`
|
|
37
|
+
|
|
38
|
+
`vue-i18n` هي runtime. `createI18n({ messages: { en, fr, ... } })` تبني instance عام يحتفظ بكل رسالة من كل locale؛ `useI18n()` تربط كل component بها؛ `t("footer.github")` تمشي عبر الشجرة في وقت rendering. هذا التصميم هو ما يجعل SFC `<i18n>` blocks و `setLocaleMessage()` ممكنة، وهو أيضاً السبب في أن dependency graph لكل component يتضمن الشجرة بأكملها.
|
|
39
|
+
|
|
40
|
+
`@intlayer/vue-i18n` تحافظ على API وتستبدل الشجرة:
|
|
41
|
+
|
|
42
|
+
1. **Import aliasing.** `vueI18nVitePlugin()` من `@intlayer/vue-i18n/plugin` تغلف `vite-intlayer` وتضيف `resolve.alias` بحيث أن `vue-i18n` يتم حلها إلى `@intlayer/vue-i18n`. لم يتم إعادة تسمية أي import.
|
|
43
|
+
2. **JSON كمصدر الحقيقة.** يقرأ plugin `syncJSON` ملف `locales/{locale}.json` الموجود لديك باستخدام `format: "vue-i18n"` (لذلك يتم تحليل استيفاء `{name}`، `{0}` list والجمع بين الأنابيب `"car | cars"` بشكل صحيح) ويكتب الترجمات مرة أخرى عندما يحدثها CLI أو CMS.
|
|
44
|
+
3. **ربط موقع الاستدعاء.** تعيد مرحلة تحسين Intlayer كتابة مواقع استدعاء `useI18n()` بحيث يحصل المكون على القواميس الخاصة به مفاتيح الأسماء، في الإعدادات المحلية النشطة، كواردات يمكن للـ bundler تتبعها وتقسيمها.
|
|
45
|
+
|
|
46
|
+
```vue fileName="src/components/Footer.vue"
|
|
47
|
+
<!-- الكود الخاص بك، بدون تغيير -->
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import { useI18n } from "vue-i18n";
|
|
50
|
+
|
|
51
|
+
const { t } = useI18n();
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<template>
|
|
55
|
+
<a href="https://github.com/intlayer-org/benchmark-bloom">{{
|
|
56
|
+
t("footer.github")
|
|
57
|
+
}}</a>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts fileName="ما يصدره المترجم (مبسط)"
|
|
62
|
+
import _dicHash_footer from "../.intlayer/dictionaries/footer.mjs";
|
|
63
|
+
import { useDictionary as useI18n } from "@intlayer/vue-i18n";
|
|
64
|
+
|
|
65
|
+
const { t } = useI18n(_dicHash_footer);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
لم تعد المكون تصل إلى شجرة الرسائل العامة. تصل إلى `footer`. هذا هو السبب في انخفاض عمود حجم المكون أدناه من 196 كيلوبايت إلى 8 كيلوبايت.
|
|
69
|
+
|
|
70
|
+
## ما يحافظ عليه المحول وما يتجاهله وما لا يستبدله
|
|
71
|
+
|
|
72
|
+
| `vue-i18n` API | مع `@intlayer/vue-i18n` |
|
|
73
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
74
|
+
| `useI18n()` → `{ t, d, n, te, tm, rt, locale, availableLocales }` | ✅ تم الاحتفاظ به. مفاتيح `t` مُكتوبة ضد قاموسك |
|
|
75
|
+
| `t("key", { name })`, `t("key", [a, b])`, `t("key", count)` | ✅ تم الاحتفاظ به. يتم حل `{name}`، `{0}` والجمع الفاصل بينها كما هو الحال من قبل |
|
|
76
|
+
| `d(date, "long")`, `n(value, "currency")` | ✅ تم الاحتفاظ به. يتم احترام `datetimeFormats` / `numberFormats` من `createI18n()`، مدعومة بـ `Intl` الأصلي |
|
|
77
|
+
| `i18n.global.locale.value = "fr"` | ✅ تم الحفاظ عليه. `WritableComputedRef` مدعوم بـ Intlayer الخاص بالعميل؛ التفاعلية تتصرف كما هي |
|
|
78
|
+
| `$t`, `$tc`, `$te`, `$tm`, `$rt`, `$d`, `$n`, `$i18n` (Options API) | ✅ تم الحفاظ عليه. تم تسجيله على `app.config.globalProperties` بواسطة `app.use(i18n)` |
|
|
79
|
+
| `v-t` directive | ✅ تم الحفاظ عليه |
|
|
80
|
+
| `legacy: true` | ✅ مقبول |
|
|
81
|
+
| `createI18n({ messages })` | ⚠️ يتم استخدام `messages` كـ **fallback في وقت التشغيل** مع تحذير في بيئة التطوير. قم بإزالة استيرادات JSON للحصول على مكاسب في حجم الحزمة |
|
|
82
|
+
| `setLocaleMessage()`, `mergeLocaleMessage()` | ❌ تحذير وعدم القيام بأي شيء. يتم استبدال تحميل الرسائل في وقت التشغيل بقواميس build-time |
|
|
83
|
+
| كتل `<i18n>` المخصصة في SFC | ❌ لا يتم قراءتها. انقل تلك الرسائل إلى JSON اللغة (أو `.content.ts` بجانب المكون) |
|
|
84
|
+
| `@nuxtjs/i18n` | ⚠️ محول منفصل، انظر [وثائق توافق Nuxt](https://intlayer.org/doc/compatibility/nuxtjs-i18n) |
|
|
85
|
+
|
|
86
|
+
## المعايير
|
|
87
|
+
|
|
88
|
+
### ما تم قياسه
|
|
89
|
+
|
|
90
|
+
تبني مجموعة [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom) **نفس تطبيق Vite + Vue 3** مع كل إعداد: **10 صفحات** (الرئيسية، حول، مدونة، الوظائف، اتصل، الأسئلة الشائعة، التسعير، المنتجات، الإعدادات، الفريق)، **10 لغات** (`en`، `fr`، `es`، `de`، `it`، `pt`، `zh`، `ja`، `ko`، `ru`)، مكونات متطابقة وأمتعة متطابقة. تُقاس الصفحات في `en` و `fr`.
|
|
91
|
+
|
|
92
|
+
تم بناء كليهما في **الإعداد الثابت**، الذي تشحنه معظم مشاريع Vue: بالنسبة لـ `vue-i18n`، يتم استيراد JSON لكل لغة وتمريره إلى `createI18n({ messages })`؛ بالنسبة للمحول، نفس المكونات مع تغيير `vite.config.ts` و `intlayer.config.ts` وإزالة استيراد `messages`. يتم تضمين `vue-intlayer` الأصلي للمرجع.
|
|
93
|
+
|
|
94
|
+
لكل بناء، تسجل المجموعة:
|
|
95
|
+
|
|
96
|
+
- **حجم المكتبة**: حجم gzip (والمصغر) لمكون فارغ يستورد فقط مكتبة i18n.
|
|
97
|
+
- **صفحة JS**: JavaScript بصيغة gzip يتم تنزيله لكل صفحة، بمتوسط على جميع الصفحات والإعدادات المحلية.
|
|
98
|
+
- **تسرب الإعدادات المحلية %**: حصة السلاسل المترجمة في JavaScript المُنزَّل التي تنتمي إلى إعداد محلي لا يعرضه المستخدم.
|
|
99
|
+
- **تسرب الصفحة %**: حصة السلاسل المترجمة في JavaScript المُنزَّل التي تنتمي إلى صفحة لا يوجد عليها المستخدم.
|
|
100
|
+
- **متوسط المكون**: متوسط حجم gzip لكل مكون تم تجميعه بشكل منفصل.
|
|
101
|
+
- **تفاعلية E2E**: وقت الساعة الحائط بين تحديد إعداد محلي جديد وتحديث `html[lang]` في DOM (Playwright، 5 تكرارات).
|
|
102
|
+
- **تحميل الصفحة**: `PerformanceNavigationTiming.duration`.
|
|
103
|
+
|
|
104
|
+
> الأرقام أدناه تأتي من التشغيل بتاريخ **2026-09-12** مع `vue-i18n` 11.4.0 و `@intlayer/vue-i18n` 9.5.1. تطبيق الاختبار صغير عن قصد (عدة عشرات من السلاسل النصية لكل locale)، لذا فإن نسب تسرب الـ locale تصف **نمطًا**: فهي تزداد مع محتواك بينما تبقى تكلفة التشغيل ثابتة.
|
|
105
|
+
|
|
106
|
+
### النتائج على Vite + Vue 3
|
|
107
|
+
|
|
108
|
+
| الإعداد | الإستراتيجية | حجم المكتبة (gz) | حجم المكتبة (min) | متوسط صفحة JS (gz) | تسرب Locale | تسرب الصفحة | متوسط المكون (gz) | استجابة E2E | تحميل الصفحة |
|
|
109
|
+
| ------------------------ | ------------ | ---------------: | ----------------: | -----------------: | ----------: | ----------: | ----------------: | ----------: | -----------: |
|
|
110
|
+
| **base** (no i18n) | - | 0.0 KB | 0.0 KB | 41.3 KB | 0.0% | - | 1.1 KB | 1.8 ms | 10.8 ms |
|
|
111
|
+
| `vue-i18n` | static | 24.3 KB | 83.2 KB | 134.9 KB | 50.0% | 90.0% | 196.0 KB | 2.8 ms | 13.6 ms |
|
|
112
|
+
| **`@intlayer/vue-i18n`** | static | **7.9 KB** | **23.2 KB** | **47.0 KB** | **15.0%** | **0.0%** | **8.4 KB** | **1.5 ms** | **9.3 ms** |
|
|
113
|
+
| `vue-intlayer` (native) | static | 3.9 KB | 11.1 KB | 57.1 KB | 56.8% | 0.0% | 7.7 KB | 4.5 ms | 13.8 ms |
|
|
114
|
+
| `vue-intlayer` (native) | dynamic | 3.9 KB | 11.1 KB | 59.8 KB | 50.0% | 0.0% | 6.5 KB | 4.0 ms | 15.8 ms |
|
|
115
|
+
|
|
116
|
+
> عمود تسرب الصفحة في تطبيق الأساس مترك فارغاً: بدون مكتبة i18n، يلتقط بصمة الأصابع السلاسل المشفرة في الأجزاء المشتركة والرقم ليس ذا معنى.
|
|
117
|
+
|
|
118
|
+
**كيفية قراءته**
|
|
119
|
+
|
|
120
|
+
- **88 KB أقل لكل صفحة، نفس المكونات.** يأخذ `vue-i18n` تطبيق 41.3 KB إلى **134.9 KB**. يصل بناء المحول للمكونات نفسها إلى **47.0 KB**، 5.7 KB فوق تطبيق الأساس. معظم الفرق هو 74.9 KB من `src/locales` التي يسحبها `createI18n({ messages })` إلى كل صفحة والمحول لا يجمعها أبداً كتكتل.
|
|
121
|
+
- **Runtime ينكمش 3 مرات.** مكون فارغ يستورد فقط `vue-i18n` يكلف **24.3 KB gzip / 83.2 KB minified**: `@intlify/core-base`، مترجم الرسائل والـ runtime. المحول يكلف **7.9 KB / 23.2 KB**، معظمها نواة Intlayer بالإضافة إلى سطح API `vue-i18n`.
|
|
122
|
+
- **المكونات: أصغر 23 مرة.** مكون `useI18n()` مترجم بشكل معزول يبلغ متوسطه **196 KB**، لأن `t` مرتبط بالمثيل الذي يحتفظ بكل رسالة من كل locale. مع المحول، نفس المكون يبلغ متوسطه **8.4 KB**: فهو يصل إلى قاموسه الخاص.
|
|
123
|
+
- **تسرب البيانات.** يشحن `vue-i18n` كل locale وسلاسل كل صفحة على كل صفحة: تسرب locale بنسبة 50% (على locale المرقمة الاثنين؛ مع عشرة locales مجمعة فإن الهدر الفعلي أعلى)، تسرب صفحة بنسبة 90%. يقلل المحول تسرب الصفحة إلى **0%** لأن كل مكون يستورد فقط قاموسه الخاص. يجلس تسرب Locale عند 15% في هذا التشغيل `static`؛ `importMode: 'dynamic'` هو الإعداد الذي يزيله، وكان هذا التكوين ليس جزءاً من تشغيل Vue هذا.
|
|
124
|
+
- **التفاعلية وتحميل الصفحة.** تبديل Locale رخيص لكليهما (1.5-2.8 ms)؛ نظام التفاعلية في Vue يجعل الأمر كذلك بمجرد أن تكون الرسائل في الذاكرة. يذهب تحميل الصفحة من 13.6 ms إلى **9.3 ms**، متماشياً مع 88 KB أقل من JavaScript للتحليل.
|
|
125
|
+
- **حول الصفوف الأصلية.** `vue-intlayer` في هذا التشغيل جمعت كل اللغة في وضع `static` وحققت 57.1 كيلوبايت مع وقت تشغيل 3.9 كيلوبايت؛ قاموس المحول المتزامن حمل سلاسل لغات أجنبية أقل، وبالتالي الرقم الأقل لكل صفحة. وقت التشغيل الأصلي يبقى الأخف من بين الثلاثة، وموديل `.content.ts` الخاص به هو حيث تجد كتل SFC `<i18n>` معادلتها.
|
|
126
|
+
|
|
127
|
+
## لماذا تتحرك الأرقام
|
|
128
|
+
|
|
129
|
+
لم يتغير شيء في `src/components/`، لذا الفوائد تأتي من ما ترتبط به `useI18n`.
|
|
130
|
+
|
|
131
|
+
**مع `vue-i18n`**, الربط هو المثيل العام. `createI18n({ messages: { en, fr, ... } })` هو استيراد واحد يحتوي على كل شيء؛ كل مكون يستدعي `useI18n()` يمكنه الوصول إلى كل شيء، لذلك لا يمكن للـ bundler أن ينقسم دون المثيل. التحسين يعني _أنت_ تقسم `en.json` حسب المسار، تستدعي `setLocaleMessage()` في حارس التوجيه، وتحافظ على خريطة المسار إلى الملف صحيحة عند تحرك المكونات.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
.
|
|
135
|
+
├── locales
|
|
136
|
+
│ ├── en.json # سلاسل كل صفحة
|
|
137
|
+
│ └── fr.json
|
|
138
|
+
└── src
|
|
139
|
+
├── i18n.ts # createI18n({ messages: { en, fr, ... } })
|
|
140
|
+
├── main.ts # app.use(i18n)
|
|
141
|
+
└── components
|
|
142
|
+
└── Footer.vue # useI18n(); t("footer.github")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**مع `@intlayer/vue-i18n`**، الربط هو القاموس. `syncJSON` يحول كل مفتاح من المستوى الأعلى في `en.json` إلى قاموس؛ تمرير التحسين يسلم المكون الذي يسمي مفاتيحه، حيث يتتبع bundler الواردات ويقسمها حسب الصفحة.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
.
|
|
149
|
+
├── intlayer.config.ts # syncJSON({ format: "vue-i18n", source: ... })
|
|
150
|
+
├── locales
|
|
151
|
+
│ ├── en.json # لم يتغير، لا يزال مصدر الحقيقة
|
|
152
|
+
│ └── fr.json
|
|
153
|
+
├── .intlayer/ # تم إنشاؤه: قاموس واحد لكل مفتاح من المستوى الأعلى، لكل locale
|
|
154
|
+
└── src
|
|
155
|
+
├── i18n.ts # createI18n({}) ← تم إزالة استيراد messages
|
|
156
|
+
├── main.ts # app.use(i18n) ← لم يتغير
|
|
157
|
+
└── components
|
|
158
|
+
└── Footer.vue # useI18n(); t("footer.github") ← لم يتغير
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
سطر `messages` المستورد في `i18n.ts` هو السطر الوحيد الذي يجب حذفه. هذا هو 88 كيلوبايت.
|
|
162
|
+
|
|
163
|
+
## الهجرة في ثلاث خطوات
|
|
164
|
+
|
|
165
|
+
<Steps>
|
|
166
|
+
<Step number={1} title="التثبيت">
|
|
167
|
+
|
|
168
|
+
```bash packageManager="npm"
|
|
169
|
+
npx intlayer init --interactive
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```bash packageManager="pnpm"
|
|
173
|
+
pnpm dlx intlayer init --interactive
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```bash packageManager="yarn"
|
|
177
|
+
yarn dlx intlayer init --interactive
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```bash packageManager="bun"
|
|
181
|
+
bunx intlayer init --interactive
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
يكتشف الأمر `vue-i18n`، ويثبت `intlayer`، و`vue-intlayer`، و`@intlayer/vue-i18n` و`@intlayer/sync-json-plugin`، ويملأ `intlayer.config.ts` مسبقاً. احتفظ بـ `vue-i18n` مثبتاً: فهو peer dependency ويوفر الأنواع.
|
|
185
|
+
|
|
186
|
+
</Step>
|
|
187
|
+
<Step number={2} title="وجّه Intlayer إلى ملفات اللغة الخاصة بك">
|
|
188
|
+
|
|
189
|
+
```ts fileName="intlayer.config.ts"
|
|
190
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
191
|
+
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
192
|
+
|
|
193
|
+
const config: IntlayerConfig = {
|
|
194
|
+
internationalization: {
|
|
195
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
196
|
+
defaultLocale: Locales.ENGLISH,
|
|
197
|
+
},
|
|
198
|
+
dictionary: {
|
|
199
|
+
// "static" تجميع كل locale؛ "dynamic" تحميل النشط عند الطلب
|
|
200
|
+
importMode: "dynamic",
|
|
201
|
+
format: "vue-i18n",
|
|
202
|
+
},
|
|
203
|
+
plugins: [
|
|
204
|
+
syncJSON({
|
|
205
|
+
// لهجة vue-i18n: {name}, {0}, "car | cars"
|
|
206
|
+
format: "vue-i18n",
|
|
207
|
+
source: ({ locale }) => `./locales/${locale}.json`,
|
|
208
|
+
location: "locales",
|
|
209
|
+
}),
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export default config;
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
يبقى `locales/{locale}.json` في مكانه. كل مفتاح على المستوى الأعلى (`footer`, `hero`...) يصبح قاموس.
|
|
217
|
+
<Step number={3} title="أضف الـ plugin وحذف استيراد الرسائل">
|
|
218
|
+
|
|
219
|
+
```ts fileName="vite.config.ts"
|
|
220
|
+
import { defineConfig } from "vite";
|
|
221
|
+
import vue from "@vitejs/plugin-vue";
|
|
222
|
+
import { vueI18nVitePlugin } from "@intlayer/vue-i18n/plugin";
|
|
223
|
+
|
|
224
|
+
export default defineConfig({
|
|
225
|
+
plugins: [vue(), vueI18nVitePlugin()],
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
```ts fileName="src/i18n.ts"
|
|
230
|
+
import { createI18n } from "vue-i18n";
|
|
231
|
+
|
|
232
|
+
// قبل: createI18n({ locale: "en", messages: { en, fr, es } })
|
|
233
|
+
export const i18n = createI18n({ locale: "en" });
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`vueI18nVitePlugin()` يغلف `vite-intlayer` (مراقبة المحتوى، تجميع القاموس، عملية التحسين) ويقوم بإنشاء alias لـ `vue-i18n` إلى الـ adapter. إزالة استيراد `messages` هو ما يقلل 88 KB؛ تركه في الكود يبقي التطبيق يعمل لكنه يشحن الاثنين معاً.
|
|
237
|
+
|
|
238
|
+
</Step>
|
|
239
|
+
</Steps>
|
|
240
|
+
|
|
241
|
+
### ما يمكنك حذفه لاحقًا
|
|
242
|
+
|
|
243
|
+
| الملف / النمط | السبب |
|
|
244
|
+
| ------------------------------------------------------ | --------------------------------------------------------------------- |
|
|
245
|
+
| `import en from "./locales/en.json"` والملفات المشابهة | يُستخدم فقط كآلية احتياطية من قِبل adapter. هنا كان موقع 88 كيلوبايت |
|
|
246
|
+
| `setLocaleMessage()` في route guards | عملية بلا تأثير. تحميل لكل مسار هو وظيفة المترجم الآن |
|
|
247
|
+
| `@intlify/unplugin-vue-i18n` | غير مطلوب: فهو يقوم بتجميع الرسائل وكتل SFC التي لا يقرأها adapter |
|
|
248
|
+
| كتل `<i18n>` في المكونات أحادية الملف (SFC) | لا تُقرأ؛ انقلها إلى ملفات JSON المحلية أو إلى `.content.ts` لكل مكون |
|
|
249
|
+
|
|
250
|
+
### ما الذي تكسبه بعيداً عن البايتات
|
|
251
|
+
|
|
252
|
+
- **مفاتيح مكتوبة.** `t("footer.github")` مكتوب مقابل قاموس `footer` المترجم؛ المسار الخاطئ هو خطأ في TypeScript بدلاً من عرض المفتاح كنص.
|
|
253
|
+
- **`npx intlayer test`** يفشل في CI عند وجود مفتاح مفقود في أي محلية. **`npx intlayer fill`** يترجم الناقصة باستخدام مفتاح مزود خاص بك (OpenAI أو Anthropic أو Mistral أو Gemini...) ويكتبها مرة أخرى في `locales/{locale}.json`.
|
|
254
|
+
- **محرر مرئي و CMS** يعملان على نفس JSON، لذا يقوم المطورون بعدم التحرير من خلال واجهة المستخدم وتحديث الملفات.
|
|
255
|
+
- **الانتقال التدريجي إلى `.content.ts`.** يمكن لأي مكون الانتقال من `useI18n()` إلى `useIntlayer("footer")` مع ملف محتوى مرافق. قواميس JSON و`.content.ts` موجودة وتندمج معاً.
|
|
256
|
+
|
|
257
|
+
## الحدود التي يجب معرفتها قبل البدء
|
|
258
|
+
|
|
259
|
+
- **كتل SFC `<i18n>` لا تُقرأ.** إذا كانت رسائلك موجودة داخل المكونات، فيجب نقلها إلى ملفات الإعدادات الإقليمية (أو إلى `.content.ts`، وهي نفس الفكرة مع الأنواع).
|
|
260
|
+
- **تحميل الرسائل في وقت التشغيل قد اختفى.** `setLocaleMessage()` و `mergeLocaleMessage()` تحذر وتعود. الترجمات المجلوبة من نظام إدارة المحتوى في وقت التشغيل تحتاج إلى نظام إدارة محتوى Intlayer، أو أوامر `intlayer pull` / `push`.
|
|
261
|
+
- **`messages` هو بديل، وليس مجاني.** الاحتفاظ باستيرادات JSON في `createI18n()` يحتفظ بـ 75 KB في الحزمة. احذفها بمجرد نجاح `intlayer test`.
|
|
262
|
+
- **المحول ليس runtime الأصلي.** 7.9 KB مقابل 3.9 KB لـ `vue-intlayer`. بمجرد انتقال كل component إلى `useIntlayer`، يمكنك حذفه.
|
|
263
|
+
|
|
264
|
+
## متى تستخدم أي منها؟
|
|
265
|
+
|
|
266
|
+
- **ابق على `vue-i18n`** إذا كان تطبيقك يعتمد على كتل SFC `<i18n>`، على تدفقات runtime `setLocaleMessage()`، أو إذا لم يكن 90 KB لكل صفحة مصدر قلق لجمهورك.
|
|
267
|
+
- **استخدم `@intlayer/vue-i18n`** إذا كنت على `vue-i18n` وتريد توفير 88 KB، و23x أصغر components، 0% page leakage، typed keys وفحوصات CI دون تعديل ملف `.vue`. هذه هي نقطة الدخول لـ codebase `vue-i18n` موجود.
|
|
268
|
+
- **اذهب للنسخة الأصلية (`vue-intlayer`)** للمشاريع الجديدة، أو بمجرد انتهاء المحول من عمله. لديها أخف runtime (3.9 KB) والنموذج `.content.ts` لكل component الذي يحل محل كتل `<i18n>` بمحتوى مكتوب (typed).
|
|
269
|
+
|
|
270
|
+
## المقارنات ذات الصلة
|
|
271
|
+
|
|
272
|
+
- [vue-i18n مقابل Intlayer](https://intlayer.org/blog/vue-i18n-vs-intlayer) (الميزات وتجربة المطور)
|
|
273
|
+
- [معيار vue-i18n مقابل Intlayer](https://intlayer.org/blog/vue-i18n-vs-intlayer-benchmark) (المكتبات، نفس المعيار)
|
|
274
|
+
- [next-intl مقابل @intlayer/next-intl](https://intlayer.org/blog/next-intl-vs-intlayer-next-intl) (نفس سلسلة المحولات)
|
|
275
|
+
- [i18next مقابل @intlayer/i18next](https://intlayer.org/blog/i18next-vs-intlayer-i18next) (نفس سلسلة المحولات)
|
|
276
|
+
- [Lingui مقابل @intlayer/lingui](https://intlayer.org/blog/lingui-vs-intlayer-lingui) (نفس سلسلة المحولات)
|
|
277
|
+
- [دليل الترحيل: من vue-i18n إلى Intlayer](https://intlayer.org/doc/migration/vue-i18n)
|
|
278
|
+
- [مرجع محول التوافق: vue-i18n](https://intlayer.org/doc/compatibility/vue-i18n), [Nuxt i18n](https://intlayer.org/doc/compatibility/nuxtjs-i18n)
|
|
279
|
+
|
|
280
|
+
## الخلاصة
|
|
281
|
+
|
|
282
|
+
يغير `@intlayer/vue-i18n` ما يرتبط به `useI18n()`: من مثيل عام يحمل كل رسالة من كل locale إلى قاموس مُجمّع لتلك المكون. في نفس تطبيق Vite + Vue 3 الذي هو **88 KB أقل لكل صفحة**، وقت تشغيل **أصغر 3 مرات**، مكونات **أصغر 23 مرة** و**تسرب صفحة 0%**، لملف إعدادات وسطر plugin وواحد استيراد محذوف. كتل SFC `<i18n>` وتحميل الرسائل في وقت التشغيل هما الشيئان اللذان لا يحملهما، وبقي runtime `vue-intlayer` الأصلي نصف حجمه.
|
|
283
|
+
|
|
284
|
+
جميع البيانات الأولية وتطبيقات الاختبار والسكريبتات موجودة في [مستودع Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom). قم بتشغيلها بنفسك.
|
|
285
|
+
|
|
286
|
+
راجع وثيقة ['Why Intlayer?'](https://intlayer.org/doc/why) للحصول على مزيد من التفاصيل.
|
|
File without changes
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-09-13
|
|
3
|
+
updatedAt: 2026-09-13
|
|
4
|
+
title: "i18next vs @intlayer/i18next: Gleiche API, anderes Bundle"
|
|
5
|
+
description: Was sich ändert, wenn eine React- oder Next.js-App ihre i18next-, react-i18next- und next-i18next-Aufrufe beibehält, diese jedoch über die @intlayer/i18next-Adapter bereitstellt. JavaScript pro Seite, Komponentengröße, Content-Leakage und Hydration gemessen am gleichen Code, plus was die Adapter behalten, ignorieren und nicht ersetzen können.
|
|
6
|
+
keywords:
|
|
7
|
+
- i18next
|
|
8
|
+
- react-i18next
|
|
9
|
+
- next-i18next
|
|
10
|
+
- "@intlayer/i18next"
|
|
11
|
+
- "@intlayer/react-i18next"
|
|
12
|
+
- "@intlayer/next-i18next"
|
|
13
|
+
- Intlayer
|
|
14
|
+
- Kompatibilitätsadapter
|
|
15
|
+
- Migration
|
|
16
|
+
- Internationalisierung
|
|
17
|
+
- i18n
|
|
18
|
+
- Benchmark
|
|
19
|
+
- Bundle-Größe
|
|
20
|
+
- Blog
|
|
21
|
+
- Next.js
|
|
22
|
+
- React
|
|
23
|
+
slugs:
|
|
24
|
+
- blog
|
|
25
|
+
- i18next-vs-intlayer-i18next
|
|
26
|
+
author: aymericzip
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# i18next VS @intlayer/i18next | Gleiche API, anderes Bundle
|
|
30
|
+
|
|
31
|
+
`@intlayer/i18next`, `@intlayer/react-i18next` und `@intlayer/next-i18next` sind Kompatibilitätsadapter. Sie stellen die `i18next`-API bereit, die Ihr Code bereits verwendet (`useTranslation`, `t()`, `<Trans>`, `i18n.changeLanguage()`, `getFixedT`, `serverSideTranslations`...), und bedienen sie aus von Intlayer kompilierten Wörterbüchern. Die Komponenten ändern sich nicht. Die Runtime darunter jedoch schon.
|
|
32
|
+
|
|
33
|
+
Dieser Artikel misst diesen Austausch an derselben Next.js-Anwendung, die einmal mit `next-i18next` und einmal mit `@intlayer/next-i18next` gebaut wurde. Die Zahlen stammen aus [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom). Für einen Vergleich von `i18next` und Intlayer als eigenständige Bibliotheken lesen Sie [i18next vs Intlayer](https://intlayer.org/de/blog/i18next-vs-intlayer). Hier geht es darum, was der Adapter verändert, wenn Sie Ihren bestehenden Code unverändert beibehalten.
|
|
34
|
+
|
|
35
|
+
<TOC/>
|
|
36
|
+
|
|
37
|
+
> **tl;dr**: In derselben Next.js-App reduzierte der Wechsel von `next-i18next` zu `@intlayer/next-i18next` das JavaScript pro Seite von **218.5 KB auf 150.7 KB** gzip (Basis-Setup) und unterbot selbst das vollständig optimierte `next-i18next`-Setup (163.4 KB) um **12.7 KB**. Die durchschnittliche Komponentengröße sank von **78.5 KB auf 9.7 KB**, das String-Leakage fremder Seiten von **~90% auf 0%**, die Hydration-Dauer von **15.6 ms auf 11.3 ms** und die Runtime von **19.7 KB auf 9.4 KB**. Keine einzige Komponente musste angepasst werden; lediglich eine Provider-Datei wurde geändert. `i18next`-Plugins (Backends, Spracherkenner) werden akzeptiert, bewirken aber nichts: Zur Laufzeit muss nichts mehr geladen oder erkannt werden.
|
|
38
|
+
|
|
39
|
+
## Was `@intlayer/i18next` ist
|
|
40
|
+
|
|
41
|
+
`i18next` ist eine Runtime. `i18n.init({ resources })` oder ein Backend-Plugin lädt `locales/{lng}/{ns}.json` in eine globale Instanz; `useTranslation("about")` abonniert die Komponente darauf; `t("title")` schlägt den Schlüssel zur Renderzeit nach. Namespaces, Lazy Loading, seitenbasierte Namespace-Listen und Typsicherheit müssen von Ihnen konfiguriert und gepflegt werden.
|
|
42
|
+
|
|
43
|
+
Die Adapter behalten die API bei und ersetzen die globale Instanz:
|
|
44
|
+
|
|
45
|
+
1. **Import-Aliasing.** `createNextI18nPlugin()` aus `@intlayer/next-i18next/plugin` (oder `withI18next`) umschließt `withIntlayer` und richtet Webpack- / Turbopack-Aliase ein, sodass `next-i18next`, `react-i18next` und `i18next` auf ihre `@intlayer/*`-Gegenstücke aufgelöst werden. Unter Vite übernimmt `reactI18nextVitePlugin()` aus `@intlayer/react-i18next/plugin` die gleiche Aufgabe. Kein einziger Import muss umbenannt werden.
|
|
46
|
+
2. **JSON als Source of Truth.** Das `syncJSON`-Plugin liest Ihre bestehenden `locales/{lng}/{ns}.json`-Dateien mit `format: "i18next"` (sodass `{{name}}`, `$t()`-Verschachtelungen, `_one` / `_other` und Kontext-Suffixe korrekt geparst werden) und schreibt Übersetzungen zurück, sobald die CLI oder das CMS sie aktualisiert.
|
|
47
|
+
3. **Bindung am Aufrufpunkt.** Der Optimierungsschritt von Intlayer schreibt `useTranslation("about")` in einen Aufruf um, der das Wörterbuch `about` direkt in der aktiven Locale empfängt. Die Komponente greift nicht mehr auf den globalen Store zu.
|
|
48
|
+
|
|
49
|
+
```tsx fileName="components/About.tsx"
|
|
50
|
+
// Ihr Code, unverändert
|
|
51
|
+
import { useTranslation } from "react-i18next";
|
|
52
|
+
|
|
53
|
+
const About = () => {
|
|
54
|
+
const { t } = useTranslation("about");
|
|
55
|
+
return <h1>{t("title")}</h1>;
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx fileName="Was der Compiler ausgibt (vereinfacht)"
|
|
60
|
+
import _dicHash_about from "../.intlayer/dictionaries/about.mjs";
|
|
61
|
+
import { useDictionary as useTranslation } from "@intlayer/react-i18next";
|
|
62
|
+
|
|
63
|
+
const About = () => {
|
|
64
|
+
const { t } = useTranslation(_dicHash_about);
|
|
65
|
+
return <h1>{t("title")}</h1>;
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Dieser Umschreibprozess sorgt für die drastische Verkleinerung der Komponenten und den Wegfall des Seiten-Leakages in den folgenden Messungen.
|
|
70
|
+
|
|
71
|
+
## Was die Adapter beibehalten, ignorieren und nicht ersetzen
|
|
72
|
+
|
|
73
|
+
| `i18next`-API | Mit `@intlayer/*` |
|
|
74
|
+
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `useTranslation("ns")`, `useTranslation("ns", { keyPrefix })` | ✅ Beibehalten. Zur Build-Zeit an das `ns`-Wörterbuch gebunden; typisiert gegen Ihre Inhalte |
|
|
76
|
+
| `t("key", { name })`, `{{interpolation}}`, `$t(key)`-Verschachtelung | ✅ Beibehalten |
|
|
77
|
+
| Pluralformen `key_one` / `key_other`, Kontext `key_male`, `returnObjects` | ✅ Beibehalten. Plurale werden über `Intl.PluralRules` ausgewertet |
|
|
78
|
+
| `<Trans>` mit `components`, nummerierten Tags `<1>...</1>`, `values` | ✅ Beibehalten |
|
|
79
|
+
| `withTranslation`, `Translation`, `I18nContext` | ✅ Beibehalten |
|
|
80
|
+
| `i18n.changeLanguage()`, `i18n.language`, `i18n.dir()`, `on("languageChanged")` | ✅ Beibehalten. `changeLanguage` steuert die Locale von Intlayer |
|
|
81
|
+
| `getFixedT(lng, ns, keyPrefix)`, `i18n.exists()`, `hasLoadedNamespace()` | ✅ Beibehalten |
|
|
82
|
+
| `i18n.use(Backend).use(LanguageDetector).init({...})` | ⚠️ `use()` ruft `init` des Plugins auf und beendet; Backends und Detectors müssen nichts laden oder ermitteln |
|
|
83
|
+
| `init({ resources })`, `addResourceBundle()` | ⚠️ `resources` wird mit einer Warnung **ignoriert**; entfernen Sie JSON-Imports für optimale Bundle-Größen |
|
|
84
|
+
| `I18nextProvider i18n={i18n}` | ⚠️ Rendert einen `IntlayerProvider`; die `i18n`-Prop wird ignoriert. Im App Router Locale übergeben (siehe unten) |
|
|
85
|
+
| `serverSideTranslations(locale, ["common"])` (next-i18next) | ⚠️ Gibt die erwartete Struktur zurück und lädt nichts. Unschädlich beizubehalten, sicher zu löschen |
|
|
86
|
+
| `appWithTranslation(App)` (next-i18next) | ✅ Beibehalten |
|
|
87
|
+
| `next-i18next.config.js` | ⚠️ Wird nicht gelesen. Locales stammen aus `intlayer.config.ts` |
|
|
88
|
+
| Einfaches `useTranslation()` ohne Namespace | ✅ Funktioniert gegen das `translation`-Gesamtwörterbuch der Datei (`splitKeys: false`) |
|
|
89
|
+
|
|
90
|
+
## Der Benchmark
|
|
91
|
+
|
|
92
|
+
### Was gemessen wurde
|
|
93
|
+
|
|
94
|
+
Die Testsuite [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom) erstellt **dieselbe Anwendung** in jedem Setup: **10 Seiten** (Home, About, Blog, Careers, Contact, FAQ, Pricing, Products, Settings, Team), **10 Locales** (`en`, `fr`, `es`, `de`, `it`, `pt`, `zh`, `ja`, `ko`, `ru`), identische Komponenten und identischer Inhalt. Die Seiten werden in `en` und `fr` gemessen.
|
|
95
|
+
|
|
96
|
+
`next-i18next` wurde in vier Ladestrategien getestet: vom Import aller Locale-JSONs in `resources` (`static`) bis hin zu einem Namespace pro Route, der über ein Backend nachgeladen wird (`scoped-dynamic`). Der Adapter lief auf **denselben Komponenten wie das Basis-Setup**, wobei lediglich `next.config.ts`, `intlayer.config.ts` und die Provider-Datei angepasst wurden. Er benötigt keine manuell "gescopte" Variante: Der Compiler grenzt den Inhalt automatisch pro Komponente ab.
|
|
97
|
+
|
|
98
|
+
Für jeden Build erfasst die Suite:
|
|
99
|
+
|
|
100
|
+
- **Lib-Größe**: gzip-Größe einer leeren Komponente, die lediglich die i18n-Bibliothek importiert.
|
|
101
|
+
- **Seiten-JS**: Durchschnittliches gzip-JavaScript, das pro Seite über alle Seiten und Locales heruntergeladen wird.
|
|
102
|
+
- **Locale-Leakage %**: Anteil übersetzter Strings im heruntergeladenen JS, der zu einer Sprache gehört, die der Nutzer **nicht** betrachtet.
|
|
103
|
+
- **Seiten-Leakage %**: Anteil übersetzter Strings im heruntergeladenen JS, der zu einer Seite gehört, auf der sich der Nutzer **nicht** befindet.
|
|
104
|
+
- **Komponenten-Durchschnitt**: Durchschnittliche gzip-Größe jeder isoliert kompilierten Komponente.
|
|
105
|
+
- **E2E-Reaktivität**: Gemessene Zeitspanne zwischen der Sprachauswahl und der Aktualisierung von `html[lang]` im DOM (Playwright, 5 Durchläufe).
|
|
106
|
+
- **Hydration**: Dauer der React-Hydration-Phase.
|
|
107
|
+
|
|
108
|
+
> Die folgenden Zahlen stammen aus dem Durchlauf vom **12.09.2026** mit `next-i18next` 16.3.0 (`react-i18next` 17.0.13, `i18next` 26.4.2) und `@intlayer/next-i18next` 9.5.1. Die Testanwendung ist bewusst kompakt gehalten (wenige Dutzend Zeichenketten pro Sprache), sodass die Leakage-Prozentwerte ein **Muster** abbilden: Sie steigen mit wachsendem Content, während die Runtime-Kosten konstant bleiben.
|
|
109
|
+
|
|
110
|
+
### Ergebnisse auf Next.js
|
|
111
|
+
|
|
112
|
+
| Setup | Strategie | Lib-Größe (gz) | Seiten-JS Ø (gz) | Locale-Leakage | Seiten-Leakage | Komp Ø (gz) | E2E-Reaktivität | Hydration |
|
|
113
|
+
| ---------------------------- | -------------- | -------------: | ---------------: | -------------: | -------------: | ----------: | --------------: | ----------: |
|
|
114
|
+
| **base** (ohne i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
|
|
115
|
+
| `next-i18next` | static | 19.7 KB | 218.5 KB | 0.0% | 89.8% | 78.5 KB | 16.4 ms | 15.6 ms |
|
|
116
|
+
| `next-i18next` | dynamic | 19.7 KB | 169.5 KB | 50.0% | 89.8% | 26.1 KB | 15.4 ms | 27.7 ms |
|
|
117
|
+
| `next-i18next` | scoped-static | 19.7 KB | 220.1 KB | 0.0% | 89.8% | 78.9 KB | 16.4 ms | 14.7 ms |
|
|
118
|
+
| `next-i18next` | scoped-dynamic | 19.7 KB | 163.4 KB | 0.0% | 0.0% | 27.1 KB | 15.9 ms | 15.1 ms |
|
|
119
|
+
| **`@intlayer/next-i18next`** | static | **9.4 KB** | **150.7 KB** | **0.0%** | **0.0%** | **9.7 KB** | **10.7 ms** | **11.3 ms** |
|
|
120
|
+
| **`@intlayer/next-i18next`** | dynamic | **9.4 KB** | **150.7 KB** | **0.0%** | **0.0%** | **9.7 KB** | **11.9 ms** | **10.6 ms** |
|
|
121
|
+
| `next-intlayer` (nativ) | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
|
|
122
|
+
| `next-intlayer` (nativ) | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
|
|
123
|
+
|
|
124
|
+
**Einordnung der Ergebnisse**
|
|
125
|
+
|
|
126
|
+
- **68 KB weniger pro Seite gegenüber dem Basis-Setup.** `resources: { en, fr, ... }` liefert jede Sprache und jeden Namespace auf jeder Seite aus: **218.5 KB**. Der Adapter-Build derselben Komponenten landet bei **150.7 KB**. Er unterbietet auch die beste Konfiguration von `next-i18next` (163.4 KB, ein Namespace pro Route, nachgeladen) um 12.7 KB, da die `i18next`-Runtime allein 19.7 KB gegenüber 9.4 KB wiegt.
|
|
127
|
+
- **Leakage sinkt auf 0%, ohne Komponenten zu verändern.** Jedes `next-i18next`-Setup außer der vollkommen isolierten Variante liefert ~90% fremde Seiten-Strings aus. Das `dynamic`-Setup schneidet schlechter ab als erwartet: Es behält das Seiten-Leakage bei und erzeugt zusätzlich **50% Locale-Leakage**, da das Backend pro Sprache stets den gesamten `translation`-Namespace abruft. Der Adapter erreicht 0% / 0% direkt auf dem Ursprungscode.
|
|
128
|
+
- **Komponenten: 8x kleiner.** Eine isoliert kompilierte `useTranslation()`-Komponente wiegt durchschnittlich **78.5 KB** mit eingebetteten `resources` und **26-27 KB** mit Backend, da `t` an den globalen Store gebunden ist. Mit dem Adapter sind es durchschnittlich **9.7 KB**.
|
|
129
|
+
- **Schnellere Hydration und Sprachwechsel.** Die Hydration sinkt von 15.6 ms auf **11.3 ms** (und von 27.7 ms im `dynamic`-Setup, wo der Backend-Aufruf auf dem kritischen Pfad liegt). Der Sprachwechsel beschleunigt sich von 15-16 ms auf **11-12 ms**.
|
|
130
|
+
- **Der Adapter ist nicht die native Runtime.** `next-intlayer` kommt auf **141.3 KB**, lediglich +0.3 KB über der Basis-App. Der Adapter bringt die API-Oberfläche von `i18next` (Interpolation, Plural- und Kontext-Suffixe, `<Trans>`-Parsing) auf dem Core von Intlayer mit: 9.4 KB und +9.4 KB pro Seite über nativ. Er ist die Brücke, nicht das Endziel.
|
|
131
|
+
|
|
132
|
+
> Der `react-i18next`-Adapter unter Vite / TanStack Start war nicht Teil dieser Messreihe. Die Werte für `react-i18next` auf TanStack Start finden sich in [i18next vs Intlayer](https://intlayer.org/de/blog/i18next-vs-intlayer): 127-184 KB pro Seite und 123-185 ms beim Sprachwechsel bei nachgeladenem Backend.
|
|
133
|
+
|
|
134
|
+
## Warum sich die Zahlen verändern
|
|
135
|
+
|
|
136
|
+
Am Ordner `components/` wurde nichts geändert; die Einsparungen resultieren daraus, woran `useTranslation` gebunden ist.
|
|
137
|
+
|
|
138
|
+
**Bei `i18next`** erfolgt die Bindung an die globale Instanz. Alles, was hineingeladen wurde (alle Sprachen bei `static`, der gesamte Namespace der aktiven Sprache bei `dynamic`), ist für jede Komponente erreichbar, die `useTranslation()` aufruft. Der Bundler kann nicht feiner trennen als der Instanzinhalt, und die Runtime kann nicht wissen, welche Schlüssel eine Komponente beim Rendern anfordern wird.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
.
|
|
142
|
+
├── next-i18next.config.js
|
|
143
|
+
├── public/locales
|
|
144
|
+
│ ├── en/translation.json # Strings aller Seiten
|
|
145
|
+
│ └── fr/translation.json
|
|
146
|
+
├── i18n/i18n.ts # i18n.use(initReactI18next).init({ resources })
|
|
147
|
+
└── components
|
|
148
|
+
├── AppProviders.tsx # <I18nextProvider i18n={i18n}>
|
|
149
|
+
└── About.tsx # useTranslation(); t("about.title")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Bei `@intlayer/next-i18next`** bindet der Aufruf direkt an das Wörterbuch. `syncJSON` wandelt jede Namespace-Datei in ein Wörterbuch um; der Optimierungsschritt übergibt der Komponente exakt das benötigte Wörterbuch als Import, den der Bundler pro Seite und pro Sprache isolieren und aufteilen kann.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
.
|
|
156
|
+
├── intlayer.config.ts # syncJSON({ format: "i18next", source: ... })
|
|
157
|
+
├── public/locales
|
|
158
|
+
│ ├── en/translation.json # unverändert, weiterhin die Source of Truth
|
|
159
|
+
│ └── fr/translation.json
|
|
160
|
+
├── .intlayer/ # generiert: ein Wörterbuch pro Namespace, pro Sprache
|
|
161
|
+
└── components
|
|
162
|
+
├── AppProviders.tsx # <IntlayerClientProvider locale={locale}>
|
|
163
|
+
└── About.tsx # useTranslation(); t("about.title") ← unverändert
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`i18n/i18n.ts` und dessen `resources`-Import werden zu totem Code. Genau daraus resultieren die 68 KB Einsparung.
|
|
167
|
+
|
|
168
|
+
## Migration in drei Schritten
|
|
169
|
+
|
|
170
|
+
<Steps>
|
|
171
|
+
<Step number={1} title="Installation">
|
|
172
|
+
|
|
173
|
+
```bash packageManager="npm"
|
|
174
|
+
npx intlayer init --interactive
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```bash packageManager="pnpm"
|
|
178
|
+
pnpm dlx intlayer init --interactive
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```bash packageManager="yarn"
|
|
182
|
+
yarn dlx intlayer init --interactive
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```bash packageManager="bun"
|
|
186
|
+
bunx intlayer init --interactive
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Der Befehl erkennt `i18next` / `react-i18next` / `next-i18next`, installiert `intlayer`, das Framework-Paket (`next-intlayer` oder `react-intlayer`), den passenden `@intlayer/*`-Adapter sowie `@intlayer/sync-json-plugin` und füllt `intlayer.config.ts` vor. Behalten Sie die bisherigen Pakete installiert: Sie fungieren als Peer-Dependencies und liefern die TypeScript-Definitionen.
|
|
190
|
+
|
|
191
|
+
</Step>
|
|
192
|
+
<Step number={2} title="Intlayer auf Ihre Sprachdateien ausrichten">
|
|
193
|
+
|
|
194
|
+
```ts fileName="intlayer.config.ts"
|
|
195
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
196
|
+
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
197
|
+
|
|
198
|
+
const config: IntlayerConfig = {
|
|
199
|
+
internationalization: {
|
|
200
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
201
|
+
defaultLocale: Locales.ENGLISH,
|
|
202
|
+
},
|
|
203
|
+
dictionary: {
|
|
204
|
+
importMode: "dynamic",
|
|
205
|
+
format: "i18next",
|
|
206
|
+
},
|
|
207
|
+
plugins: [
|
|
208
|
+
syncJSON({
|
|
209
|
+
// i18next-Dialekt: {{name}}, $t(key), key_one / key_other, key_male
|
|
210
|
+
format: "i18next",
|
|
211
|
+
// Eine Datei pro Namespace: `useTranslation("about")` → about.json
|
|
212
|
+
source: ({ locale, key }) => `./public/locales/${locale}/${key}.json`,
|
|
213
|
+
location: "public/locales",
|
|
214
|
+
}),
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export default config;
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Wenn Sie eine einzige `translation.json` pro Sprache verwenden (der Standard-Namespace von i18next), setzen Sie `splitKeys: false`, damit die gesamte Datei ein einziges Wörterbuch bleibt und einfache Aufrufe von `useTranslation()` weiterhin funktionieren.
|
|
222
|
+
|
|
223
|
+
</Step>
|
|
224
|
+
<Step number={3} title="Plugin hinzufügen">
|
|
225
|
+
|
|
226
|
+
<Tabs>
|
|
227
|
+
<Tab label="Next.js">
|
|
228
|
+
|
|
229
|
+
```ts fileName="next.config.ts"
|
|
230
|
+
import type { NextConfig } from "next";
|
|
231
|
+
import { withI18next } from "@intlayer/next-i18next/plugin";
|
|
232
|
+
|
|
233
|
+
const nextConfig: NextConfig = {};
|
|
234
|
+
|
|
235
|
+
export default withI18next(nextConfig);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Im App Router erhalten Client-Komponenten ihre Sprache über das `[locale]`-Segment. Der `I18nextProvider` des Adapters nimmt keine Sprache entgegen, daher ersetzen Sie ihn einmalig in Ihrer Provider-Datei:
|
|
239
|
+
|
|
240
|
+
```tsx fileName="components/AppProviders.tsx"
|
|
241
|
+
"use client";
|
|
242
|
+
|
|
243
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
244
|
+
import type { LocalesValues } from "intlayer";
|
|
245
|
+
|
|
246
|
+
export const AppProviders = ({
|
|
247
|
+
locale,
|
|
248
|
+
children,
|
|
249
|
+
}: {
|
|
250
|
+
locale: LocalesValues;
|
|
251
|
+
children: React.ReactNode;
|
|
252
|
+
}) => (
|
|
253
|
+
<IntlayerClientProvider locale={locale}>{children}</IntlayerClientProvider>
|
|
254
|
+
);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Alle darunter liegenden Komponenten rufen weiterhin wie gewohnt `useTranslation()` auf.
|
|
258
|
+
|
|
259
|
+
</Tab>
|
|
260
|
+
<Tab label="Vite / TanStack Start">
|
|
261
|
+
|
|
262
|
+
```ts fileName="vite.config.ts"
|
|
263
|
+
import { defineConfig } from "vite";
|
|
264
|
+
import react from "@vitejs/plugin-react";
|
|
265
|
+
import { reactI18nextVitePlugin } from "@intlayer/react-i18next/plugin";
|
|
266
|
+
|
|
267
|
+
export default defineConfig({
|
|
268
|
+
plugins: [react(), reactI18nextVitePlugin()],
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`reactI18nextVitePlugin()` umschließt `vite-intlayer` und richtet Aliase für `react-i18next` und `i18next` ein. Für ein Projekt ohne React aliast `i18nextVitePlugin()` aus `@intlayer/i18next/plugin` das Basispaket `i18next`.
|
|
273
|
+
|
|
274
|
+
</Tab>
|
|
275
|
+
</Tabs>
|
|
276
|
+
|
|
277
|
+
</Step>
|
|
278
|
+
</Steps>
|
|
279
|
+
|
|
280
|
+
### Was Sie anschließend entfernen können
|
|
281
|
+
|
|
282
|
+
| Datei / Muster | Grund |
|
|
283
|
+
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
|
284
|
+
| `resources: { en, fr, ... }` und die JSON-Imports | Werden vom Adapter ignoriert. Hier lagen die 68 KB |
|
|
285
|
+
| `i18next-http-backend`, `i18next-resources-to-backend` | Zur Laufzeit muss nichts mehr abgerufen werden |
|
|
286
|
+
| `i18next-browser-languagedetector` | Die Spracherkennung übernimmt das Routing von Intlayer (URL-Präfix, Cookie, Header) |
|
|
287
|
+
| `serverSideTranslations()` in `getStaticProps` | Liefert eine leere Struktur; harmlos, aber überflüssig |
|
|
288
|
+
| `next-i18next.config.js` | Wird nicht gelesen. Sprachen liegen in `intlayer.config.ts` |
|
|
289
|
+
| Listen mit `ns: [...]` pro Seite | Der Compiler wählt Namespaces komponentenweise aus |
|
|
290
|
+
|
|
291
|
+
### Was Sie über Dateigrößen hinaus gewinnen
|
|
292
|
+
|
|
293
|
+
- **Typisierte Schlüssel.** `useTranslation("about")` ist gegen das kompilierte `about`-Wörterbuch typisiert; `t("does.not.exist")` führt zu einem TypeScript-Fehler statt zu einem zurückgegebenen Key-String.
|
|
294
|
+
- **`npx intlayer test`** lässt die CI bei fehlenden Schlüsseln in beliebigen Sprachen fehlschlagen. **`npx intlayer fill`** übersetzt fehlende Einträge mit Ihrem eigenen Provider-Schlüssel (OpenAI, Anthropic, Mistral, Gemini...) und schreibt sie in `locales/{lng}/{ns}.json` zurück.
|
|
295
|
+
- **Visueller Editor und CMS** arbeiten auf demselben JSON, sodass Übersetzer Inhalte per UI bearbeiten können und Dateien synchronisiert werden.
|
|
296
|
+
- **Schrittweiser Wechsel zu `.content.ts`.** Jede Komponente kann unabhängig von `useTranslation("about")` auf `useIntlayer("about")` mit einer zugehörigen Inhaltsdatei umgestellt werden. JSON und `.content.ts`-Dateien koexistieren reibungslos.
|
|
297
|
+
|
|
298
|
+
## Grenzen, die Sie vorab kennen sollten
|
|
299
|
+
|
|
300
|
+
- **Backends und Detektoren sind inaktiv.** `i18n.use(HttpBackend)` ruft das `init` des Plugins auf und nichts weiter. Wenn Ihre App darauf angewiesen war, Übersetzungen zur Laufzeit von einem CMS abzurufen, entfällt dieser Pfad; nutzen Sie stattdessen das Intlayer-CMS oder die Befehle `intlayer pull` / `push`.
|
|
301
|
+
- **`resources` wird ignoriert, nicht zusammengeführt.** Im Gegensatz zu manchen anderen Adaptern nutzt `@intlayer/i18next` eingebettete `resources` nicht als Fallback. Jeder Schlüssel muss in den synchronisierten Wörterbüchern vorhanden sein, was `intlayer test` absichert.
|
|
302
|
+
- **App Router erfordert die Provider-Änderung.** Eine Datei, oben dargestellt. Pages Router mit `appWithTranslation` benötigt keinerlei Änderungen.
|
|
303
|
+
- **`next-i18next.config.js` wird ignoriert.** `localePath`, `fallbackLng`, `reloadOnPrerender` und Verwandte haben keine Wirkung; Sprachen und Fallbacks werden in `intlayer.config.ts` definiert.
|
|
304
|
+
- **Der Adapter ist nicht kostenlos.** 9.4 KB Runtime und +9.4 KB pro Seite gegenüber `next-intlayer`. Sobald alle Komponenten auf `useIntlayer` umgestellt sind, kann er entfernt werden.
|
|
305
|
+
|
|
306
|
+
## Wann welche Lösung wählen?
|
|
307
|
+
|
|
308
|
+
- **Bleiben Sie bei `i18next`**, wenn Ihre Anwendung zwingend auf Runtime-Backends (zur Anfragezeit aus einem CMS geladene Übersetzungen), das Plugin-Ökosystem oder ein Nicht-React-Target angewiesen ist, das die Adapter nicht abdecken.
|
|
309
|
+
- **Nutzen Sie `@intlayer/*`**, wenn Sie `react-i18next` / `next-i18next` einsetzen und 68 KB sparen, 8x kleinere Komponenten, 0% Leakage, typisierte Schlüssel und CI-Prüfungen ohne Code-Rewrite erhalten möchten. Dies ist der beste Einstieg für bestehende `i18next`-Codebasen.
|
|
310
|
+
- **Wählen Sie nativ (`next-intlayer` / `react-intlayer`)** für Neuprojekte oder sobald der Adapter seinen Dienst getan hat. Es ist die schlankste Variante (5.5 KB, +0.3 KB pro Seite) und schaltet synchrone Server-Komponenten sowie komponentenbasierte `.content.ts`-Dateien frei.
|
|
311
|
+
|
|
312
|
+
## Verwandte Vergleiche
|
|
313
|
+
|
|
314
|
+
- [i18next vs Intlayer](https://intlayer.org/de/blog/i18next-vs-intlayer) (Bibliotheksvergleich, gleiche Benchmark-Basis)
|
|
315
|
+
- [next-intl vs @intlayer/next-intl](https://intlayer.org/de/blog/next-intl-vs-intlayer-next-intl) (gleiche Adapter-Serie)
|
|
316
|
+
- [Lingui vs @intlayer/lingui](https://intlayer.org/de/blog/lingui-vs-intlayer-lingui) (gleiche Adapter-Serie)
|
|
317
|
+
- [vue-i18n vs @intlayer/vue-i18n](https://intlayer.org/de/blog/vue-i18n-vs-intlayer-vue-i18n) (gleiche Adapter-Serie)
|
|
318
|
+
- Migrationsanleitungen: [i18next](https://intlayer.org/de/doc/migration/i18next), [react-i18next](https://intlayer.org/de/doc/migration/react-i18next), [next-i18next](https://intlayer.org/de/doc/migration/next-i18next)
|
|
319
|
+
- Adapter-Referenzen: [i18next](https://intlayer.org/de/doc/compatibility/i18next), [react-i18next](https://intlayer.org/de/doc/compatibility/react-i18next), [next-i18next](https://intlayer.org/de/doc/compatibility/next-i18next)
|
|
320
|
+
|
|
321
|
+
## Fazit
|
|
322
|
+
|
|
323
|
+
`i18next` ist die schwerste Runtime in diesem Benchmark, und die Adapter entfernen den Großteil davon, ohne dass Sie die gewohnte API aufgeben müssen. Auf derselben Next.js-App bedeutet das **68 KB weniger pro Seite** als im Basis-Setup, **12.7 KB weniger** als in der am stärksten handoptimierten Version, **8x kleinere Komponenten**, **0% Leakage** und **4 ms schnellere Hydration** für den Preis einer Konfigurationsdatei, einer Plugin-Zeile und einer Provider-Anpassung. Backends und Detektoren werden wirkungslos, `resources` wird ignoriert statt gemergt, und die native `next-intlayer`-Runtime bleibt nochmals 9 KB schlanker.
|
|
324
|
+
|
|
325
|
+
Alle Rohdaten, Testanwendungen und Skripte stehen im [Benchmark Bloom Repository](https://github.com/intlayer-org/benchmark-bloom) bereit.
|
|
326
|
+
|
|
327
|
+
Weitere Details finden Sie in der Dokumentation [Warum Intlayer?](https://intlayer.org/de/doc/why).
|