@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,381 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-09-13
|
|
3
|
+
updatedAt: 2026-09-13
|
|
4
|
+
title: "Lingui مقابل Intlayer: اختبار الأداء والمقارنة لعام 2026"
|
|
5
|
+
description: "مقارنة بين مكتبتي تدويل (i18n) تعتمدان على المترجم تم قياسهما على Next.js وTanStack Start. حجم الحزمة، تسرب المحتوى، حجم المكونات، الترطيب، سرعة تبديل اللغة وتجربة المطور."
|
|
6
|
+
keywords:
|
|
7
|
+
- Lingui
|
|
8
|
+
- Intlayer
|
|
9
|
+
- Internationalization
|
|
10
|
+
- i18n
|
|
11
|
+
- Benchmark
|
|
12
|
+
- Bundle size
|
|
13
|
+
- Compiler
|
|
14
|
+
- Blog
|
|
15
|
+
- Next.js
|
|
16
|
+
- TanStack Start
|
|
17
|
+
- JavaScript
|
|
18
|
+
- React
|
|
19
|
+
slugs:
|
|
20
|
+
- blog
|
|
21
|
+
- lingui-vs-intlayer
|
|
22
|
+
author: aymericzip
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Lingui مقابل Intlayer | مقارنة أداء التدويل (i18n) في React وNext.js
|
|
26
|
+
|
|
27
|
+
تعد Lingui وIntlayer المكتبتين الوحيدتين في هذا الاختبار اللتين تعتمدان على **مترجم (Compiler)** بدلاً من بيئة تشغيل بحتة (Pure Runtime). تستخرج Lingui الرسائل من وحدات الماكرو في وقت البناء وتقوم بتجميع الكتالوجات لكل لغة. بينما يقوم Intlayer بتجميع القواميس لكل مكون مع تقليم الشجرة (Tree-shaking) لكل لغة. نظرياً، يجب أن تكون النتائج متقاربة، لكن الأرقام توضح أين يختلفان.
|
|
28
|
+
|
|
29
|
+
البيانات مأخوذة من [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom)، وهي مجموعة اختبارات مفتوحة المصدر تبني التطبيق نفسه مع كل مكتبة وتسجل ما يقوم المتصفح بتنزيله وتنفيذه فعلياً.
|
|
30
|
+
|
|
31
|
+
<TOC/>
|
|
32
|
+
|
|
33
|
+
> **باختصار (tl;dr)**: Lingui هي الأقرب إلى Intlayer في حجم JavaScript الخام لكل صفحة: **115-120 كيلوبايت** مقابل **118.6 كيلوبايت** على TanStack Start بمجرد تهيئة التحميل الكسول (Lazy loading)، و**148.6 كيلوبايت** مقابل **141.3 كيلوبايت** على Next.js. لكن الفجوة تتسع في جوانب أخرى: مكون Lingui مجمع بمعزل يزن **58-153 كيلوبايت** مقابل **6-8 كيلوبايت** لـ Intlayer، ويستغرق الترطيب (Hydration) مدة **28-34 مللي ثانية** مقابل **11-14 مللي ثانية**، كما يتسرب من لغة المصدر الاحتياطية **3-15%** من نصوص `en` إلى صفحات `fr` في جميع التهيئات المحسنة. والوصول إلى هذه التهيئة المحسنة يعني استخراج الكتالوجات وتجميعها واختيارها يدوياً لكل مسار، بينما يحقق Intlayer ذلك دون أي إعدادات إضافية.
|
|
34
|
+
|
|
35
|
+
## نظرة عامة
|
|
36
|
+
|
|
37
|
+
- **Lingui** - يعتمد على وحدات الماكرو (`` t`...` ``, `<Trans>`, `msg`)، ومعيار ICU MessageFormat، وكتالوجات `.po` / JSON، وسير عمل `lingui extract` + `lingui compile`. يقوم بتجميع معرفات الرسائل إلى تجزئات قصيرة (hashes)، ويدعم التحميل الديناميكي للكتالوجات لكل لغة. مشروع راسخ، ومستقل عن أطر العمل، وله توافقية ممتازة مع أدوات المترجمين حول ملفات `.po`.
|
|
38
|
+
- **Intlayer** - نموذج محتوى يتمحور حول المكونات. توضع قواميس `.content.ts` بجانب المكون الذي تخدمه، ويقوم مترجم وقت البناء بتقليم الشجرة والتحميل الكسول لكل مكون ولكل لغة، مع توليد أنواع TypeScript صارمة تلقائياً من محتواك، وفشل البناء عند فقدان أي ترجمات. يتضمن برمجيات وسيطة، ومساعدات SEO، ومحرراً مرئياً / CMS، وترجمة مدعومة بالذكاء الاصطناعي.
|
|
39
|
+
|
|
40
|
+
| المكتبة | نجوم GitHub | إجمالي التعديلات (Commits) | آخر تعديل | الإصدار الأول | إصدار NPM | تنزيلات NPM |
|
|
41
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
42
|
+
| `aymericzip/intlayer` | [](https://github.com/aymericzip/intlayer/stargazers) | [](https://github.com/aymericzip/intlayer/commits) | [](https://github.com/aymericzip/intlayer/commits) | أبريل 2024 | [](https://www.npmjs.com/package/intlayer) | [](https://www.npmjs.com/package/intlayer) |
|
|
43
|
+
| `lingui/js-lingui` | [](https://github.com/lingui/js-lingui/stargazers) | [](https://github.com/lingui/js-lingui/commits) | [](https://github.com/lingui/js-lingui/commits) | ديسمبر 2016 | [](https://www.npmjs.com/package/@lingui/core) | [](https://www.npmjs.com/package/@lingui/core) |
|
|
44
|
+
|
|
45
|
+
> يتم تحديث الشارات تلقائياً. تختلف اللقطات بمرور الوقت.
|
|
46
|
+
|
|
47
|
+
## مقارنة الميزات وجهاً لوجه
|
|
48
|
+
|
|
49
|
+
| الميزة | Intlayer (`react-intlayer` / `next-intlayer`) | Lingui (`@lingui/core` / `@lingui/react`) |
|
|
50
|
+
| ------------------------------------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
51
|
+
| **الترجمات بجانب المكونات** | ✅ نعم، ملف `.content.ts` موجود بجوار كل مكون | ⚠️ نصوص المصدر مضمنة داخل JSX عبر الماكرو؛ الترجمات في كتالوجات `.po` مركزية |
|
|
52
|
+
| **التكامل مع TypeScript** | ✅ أنواع صارمة يتم إنشاؤها تلقائياً من المحتوى | ⚠️ وحدات الماكرو ذات أنواع محددة؛ لكن معرفات الرسائل والترجمات المفقودة لا يتم كشفها |
|
|
53
|
+
| **اكتشاف الترجمات المفقودة** | ✅ خطأ TypeScript + خطأ/تحذير في وقت البناء | ⚠️ يعرض `lingui extract` إحصائيات؛ بينما تعود بيئة التشغيل إلى نص المصدر تلقائياً |
|
|
54
|
+
| **المحتوى الغني (JSX / Markdown / مكونات)** | ✅ دعم مباشر | ✅ `<Trans>` مع مكونات متداخلة |
|
|
55
|
+
| **دعم معيار ICU** | ⚠️ قيد التطوير | ✅ نعم (وحدات ماكرو `plural` و`select` و`selectOrdinal`) |
|
|
56
|
+
| **التنسيق (التواريخ، الأرقام، العملات)** | ✅ `useNumber` و`useDate` وغيرها (تعتمد على Intl داخلياً) | ✅ `i18n.date()` و`i18n.number()` |
|
|
57
|
+
| **التوجيه المترجم والبرمجيات الوسيطة** | ✅ وكيل وبرمجية وسيطة مدمجة، دالة `getMultilingualUrls` | ❌ ليست جزءاً من النواة |
|
|
58
|
+
| **مساعدات تحسين محركات البحث SEO** | ✅ مساعدات مدمجة (hreflang وsitemap وrobots) | ❌ يدوية |
|
|
59
|
+
| **مكونات الخادم المتزامنة (Server Components)** | ✅ دالة `useIntlayer` من `next-intlayer/server` تعمل في أي مكون خادم فرعي | ⚠️ تحتاج إلى كائن `I18n` لكل طلب، يتم تمريره لأسفل أو تعيينه عبر `setI18n` |
|
|
60
|
+
| **تقليم الشجرة Tree-shaking (إرسال المحتوى المستخدم)** | ✅ لكل مكون، ولكل لغة، وبشكل تلقائي بواسطة المترجم | ⚠️ لكل لغة عبر `lingui compile`؛ وتتطلب تقسيماً يدوياً لكل مسار |
|
|
61
|
+
| **التحميل الكسول (Lazy loading)** | ✅ `importMode: 'dynamic'` (سطر إعداد واحد) | ⚠️ استخدام `import()` يدوي للكتالوجات المجمعة + `i18n.load()` / `i18n.activate()` |
|
|
62
|
+
| **تنظيف المحتوى غير المستخدم** | ✅ يتم التخلص من القواميس غير المستخدمة أثناء البناء | ✅ أمر `lingui extract --clean` يزيل الرسائل المهملة |
|
|
63
|
+
| **اختبار الترجمات المفقودة (CLI / CI)** | ✅ `npx intlayer content test` | ⚠️ إحصائيات `lingui extract` (لا يوجد رمز خروج بالخطأ افتراضياً) |
|
|
64
|
+
| **مسار البناء (Build pipeline)** | ✅ إضافة واحدة (`@intlayer/swc` / `@intlayer/babel` / `vite-intlayer`) | ⚠️ إضافة ماكرو (Babel أو SWC) + خطوات `extract` + `compile` |
|
|
65
|
+
| **ترجمة مدعومة بالذكاء الاصطناعي** | ✅ مدمجة وتستخدم مفاتيح الموفر الخاصة بك | ❌ لا يوجد |
|
|
66
|
+
| **محرر مرئي / CMS** | ✅ محرر مرئي مجاني + نظام إدارة محتوى (CMS) اختياري | ❌ لا يوجد (ملفات `.po` تعمل مع أنظمة TMS خارجية) |
|
|
67
|
+
| **خادم MCP ومهارات الوكلاء (Agent Skills)** | ✅ نعم | ❌ لا يوجد |
|
|
68
|
+
| **النظام البيئي والمجتمع** | ⚠️ أصغر لكنه ينمو بسرعة | ✅ راسخ ومستقل عن أطر العمل |
|
|
69
|
+
|
|
70
|
+
## الاختبار
|
|
71
|
+
|
|
72
|
+
### ما تم قياسه
|
|
73
|
+
|
|
74
|
+
تبني مجموعة [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom) **التطبيق نفسه** باستخدام كل مكتبة: **10 صفحات** (الرئيسية، من نحن، المدونة، الوظائف، اتصل بنا، الأسئلة الشائعة، الأسعار، المنتجات، الإعدادات، الفريق)، و**10 لغات** (`en`، `fr`، `es`، `de`، `it`، `pt`، `zh`، `ja`، `ko`، `ru`)، مع مكونات متطابقة ومحتوى متطابق. يتم قياس الصفحات باللغتين `en` و`fr`. يتم تنفيذ كل مكتبة في ما يصل إلى أربع **استراتيجيات تحميل**، بدءاً من التهيئة البسيطة وحتى التهيئة المثلى:
|
|
75
|
+
|
|
76
|
+
| الاستراتيجية | الوصف | من يستخدمها |
|
|
77
|
+
| ------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------ |
|
|
78
|
+
| **static** | يتم استيراد الكتالوج المجمع لكل لغة وتحميله مسبقاً بالكامل | النماذج الأولية السريعة، الأكواد المولدة بالذكاء |
|
|
79
|
+
| **dynamic** | يتم استيراد كتالوج اللغة النشطة فقط عبر `import()`، ولكنه يحتوي على جميع الصفحات | معظم المشاريع |
|
|
80
|
+
| **scoped-static** | كتالوج واحد لكل مسار، يتم تجميعها جميعاً مسبقاً | نادرة الاستخدام |
|
|
81
|
+
| **scoped-dynamic** | كتالوج واحد لكل مسار + تحميل كسول عبر `import()`. فقط الصفحة الحالية واللغة الحالية | التطبيقات ذات ميزانية الأداء الصارمة |
|
|
82
|
+
|
|
83
|
+
لا يحتوي Intlayer على متغير "مخصص النطاق" (scoped): يقوم المترجم بتحديد نطاق المحتوى **لكل مكون** تلقائياً، وبالتالي فإن صفي `static` و`dynamic` محصوران بالفعل بطبيعتهما.
|
|
84
|
+
|
|
85
|
+
لكل بناء، تسجل المجموعة:
|
|
86
|
+
|
|
87
|
+
- **حجم المكتبة (Lib size)**: حجم gzip لمكون فارغ يستورد مكتبة i18n فقط. وهي التكلفة الثابتة لبيئة التشغيل.
|
|
88
|
+
- **حجم JS للصفحة (Page JS)**: متوسط حجم JavaScript بتنسيق gzip الذي تم تنزيله لكل صفحة عبر جميع الصفحات واللغات.
|
|
89
|
+
- **نسبة تسرب اللغة (Locale leak %)**: نسبة السلاسل المترجمة الموجودة في ملف JS والتي تنتمي إلى لغة **لا** يعرضها المستخدم (تمت المقارنة على `en` و`fr`، لذا فإن 50% تعني "اللغة الأخرى موجودة بالكامل"؛ ومع وجود 10 لغات مجمعة، يكون الهدر الفعلي أكبر بكثير).
|
|
90
|
+
- **نسبة تسرب الصفحة (Page leak %)**: نسبة السلاسل المترجمة الموجودة في ملف JS والتي تنتمي إلى صفحة **لا** يتصفحها المستخدم.
|
|
91
|
+
- **متوسط حجم المكون (Component avg)**: متوسط حجم gzip لكل مكون تم تجميعه بمعزل. يوضح حجم بيئة التشغيل والكتالوج الذي يجلبه مكون واحد.
|
|
92
|
+
- **تفاعلية E2E**: الوقت المنقضي بين اختيار لغة جديدة وتحديث `html[lang]` في الـ DOM (اختبار Playwright، 5 تكرارات).
|
|
93
|
+
- **الترطيب (Hydration)**: مدة مرحلة ترطيب React.
|
|
94
|
+
|
|
95
|
+
> الأرقام أدناه مأخوذة من اختبار بتاريخ **2026-09-12** باستخدام `@lingui/react` 6.6.0 و`intlayer` 9.5.1. تطبيق الاختبار صغير عن قصد (بضع عشرات من النصوص لكل لغة)، لذا تصف نسب التسرب **نمطاً سلوكياً**: حيث تزداد مع نمو المحتوى بينما تظل تكلفة بيئة التشغيل ثابتة.
|
|
96
|
+
|
|
97
|
+
### النتائج على Next.js
|
|
98
|
+
|
|
99
|
+
| المكتبة | الاستراتيجية | حجم المكتبة (gz) | متوسط JS للصفحة (gz) | تسرب اللغة | تسرب الصفحة | متوسط المكون (gz) | تفاعلية E2E | الترطيب |
|
|
100
|
+
| -------------------- | -------------- | ---------------: | -------------------: | ---------: | ----------: | ----------------: | ----------: | ------: |
|
|
101
|
+
| **base** (بدون i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
|
|
102
|
+
| Lingui | static | 11.9 KB | 207.4 KB | 50.0% | 90.0% | 73.3 KB | 15.3 ms | 15.2 ms |
|
|
103
|
+
| Lingui | dynamic | 11.9 KB | 145.4 KB | 2.8% | 89.9% | 19.9 KB | 15.7 ms | 12.7 ms |
|
|
104
|
+
| Lingui | scoped-static | 11.9 KB | 148.2 KB | 2.7% | 89.1% | 20.4 KB | 15.1 ms | 13.1 ms |
|
|
105
|
+
| Lingui | scoped-dynamic | 11.9 KB | 148.6 KB | 14.8% | 0.0% | 152.6 KB | 16.1 ms | 14.8 ms |
|
|
106
|
+
| **`next-intlayer`** | static | **5.5 KB** | **141.3 KB** | **0.0%** | **0.0%** | **8.5 KB** | **15.5 ms** | 16.9 ms |
|
|
107
|
+
| **`next-intlayer`** | dynamic | **5.5 KB** | **141.3 KB** | **0.0%** | **0.0%** | **6.9 KB** | **15.3 ms** | 15.9 ms |
|
|
108
|
+
|
|
109
|
+
**كيف تقرأ هذه النتائج**
|
|
110
|
+
|
|
111
|
+
- **تكلفة بيئة التشغيل.** يكلف المكون الفارغ 11.9 كيلوبايت مع Lingui، مقابل 5.5 كيلوبايت مع Intlayer. وفي الصفحة الكاملة، تزيد أفضل تهيئة لـ Lingui بمقدار **+7.3 كيلوبايت** عن Intlayer (148.6 مقابل 141.3 كيلوبايت)؛ بينما تزيد Intlayer بمقدار **+0.3 كيلوبايت** فقط عن التطبيق الأساسي الخالي من التدويل.
|
|
112
|
+
- **التهيئة البسيطة مكلفة للغاية.** يؤدي تحميل كل كتالوج مجمع مسبقاً إلى **207.4 كيلوبايت لكل صفحة**، أي زيادة قدرها +66 كيلوبايت عن التطبيق الأساسي. نصف النصوص تعود للغة الخاطئة، و90% منها تعود لصفحات أخرى.
|
|
113
|
+
- **التحميل الديناميكي يحل مشكلة اللغة لا الصفحة.** مع وجود كتالوج واحد لكل لغة، يظل تسرب الصفحة عند ~90%: يتم إرسال كتالوج `fr` بأكمله في كل صفحة فرنسية. ويتطلب الوصول إلى تسرب 0% استخدام تهيئة `scoped-dynamic`: كتالوج منفصل لكل مسار، واستخراجه وتجميعه يدوياً لكل صفحة.
|
|
114
|
+
- **تسرب اللغة المصدرية.** حتى في التهيئات المحسنة، **تتسرب 3-15% من نصوص `en` داخل صفحات `fr`**. تحتفظ وحدات ماكرو Lingui بالرسالة المصدرية كخيار احتياطي، مما يجعلها تستقر في الحزمة بجوار الترجمة. بينما يقوم Intlayer بحل الخيارات الاحتياطية أثناء البناء ويرسل اللغة النشطة فقط.
|
|
115
|
+
- **تضخم حجم المكون في `scoped-dynamic`.** يبلغ متوسط حجم المكون المجمع بمعزل **152.6 كيلوبايت**، لأن كتالوج كل مسار يصبح قابلاً للوصول من المكون الذي يستورده. في المقابل يبلغ متوسط المكون نفسه مع `useIntlayer()` حوالي **6.9 كيلوبايت**.
|
|
116
|
+
|
|
117
|
+
### النتائج على TanStack Start
|
|
118
|
+
|
|
119
|
+
| المكتبة | الاستراتيجية | حجم المكتبة (gz) | متوسط JS للصفحة (gz) | تسرب اللغة | تسرب الصفحة | متوسط المكون (gz) | تفاعلية E2E | الترطيب |
|
|
120
|
+
| -------------------------- | -------------- | ---------------: | -------------------: | ---------: | ----------: | ----------------: | ----------: | ------: |
|
|
121
|
+
| **base** (بدون i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
|
|
122
|
+
| Lingui | static | 11.2 KB | 152.2 KB | 50.0% | 90.0% | 58.0 KB | 3.9 ms | 19.9 ms |
|
|
123
|
+
| Lingui | dynamic | 11.2 KB | 115.2 KB | 9.3% | 0.0% | 85.5 KB | 5.9 ms | 28.0 ms |
|
|
124
|
+
| Lingui | scoped-static | 11.2 KB | 120.8 KB | 4.0% | 0.0% | 147.9 KB | 7.1 ms | 33.9 ms |
|
|
125
|
+
| Lingui | scoped-dynamic | 11.2 KB | 120.2 KB | 8.6% | 0.0% | 83.7 KB | 42.1 ms | 32.9 ms |
|
|
126
|
+
| **`intlayer`** | static | **5.0 KB** | **125.8 KB** | 50.0% | **0.0%** | **8.1 KB** | **3.2 ms** | 11.5 ms |
|
|
127
|
+
| **`intlayer`** | dynamic | **5.0 KB** | **118.6 KB** | **0.0%** | **0.0%** | **6.3 KB** | **3.6 ms** | 14.1 ms |
|
|
128
|
+
| `@intlayer/lingui` (توافق) | dynamic | 10.3 KB | 137.0 KB | 9.9% | 0.0% | 12.8 KB | 2.9 ms | 19.7 ms |
|
|
129
|
+
|
|
130
|
+
**كيف تقرأ هذه النتائج**
|
|
131
|
+
|
|
132
|
+
- **في حجم JavaScript لكل صفحة، يتفوق Lingui بفارق ضئيل.** يصل Lingui بنمط `dynamic` إلى **115.2 كيلوبايت**، أي أقل بـ 3.4 كيلوبايت من Intlayer (118.6 كيلوبايت). كتالوجات Lingui المجمعة ذات المعرفات المجزأة مدمجة جداً، كما يقسم موجه TanStack Start المسارات بكفاءة كافية تجعل تسرب الصفحات 0% في نمط `dynamic`.
|
|
133
|
+
- **لكن جميع المعايير الأخرى تنقلب لصالح Intlayer.** يستغرق الترطيب **28-34 مللي ثانية** مع Lingui مقابل **11-14 مللي ثانية** مع Intlayer: حيث يتم تشغيل `i18n.load()` و`i18n.activate()` في المتصفح قبل أن يتمكن React من إتمام الترطيب. المكونات المجمعة بمعزل تزن **58-148 كيلوبايت** مقابل **6-8 كيلوبايت**. ولا يصل تسرب اللغة أبداً إلى 0% (يظل بين 4-9%) بسبب نصوص اللغة المصدرية.
|
|
134
|
+
- **تبديل اللغة بطيء في التهيئة المحسنة.** يستغرق `scoped-dynamic` في Lingui مدة **42 مللي ثانية** لتحديث `html[lang]`، حيث يجب جلب كتالوج المسار الجديد وتحميله وتنشيطه قبل أن يظهر التغيير. بينما يقوم Intlayer بالتبديل في **3-4 مللي ثانية** في كلا الوضعين.
|
|
135
|
+
- **يمتلك صف `static` في Intlayer نسبة تسرب 0% للصفحة تلقائياً** لأنه لا يتم تضمين سوى القواميس التي تستوردها مكونات الصفحة المعنية. سطر إعداد واحد (`importMode: 'dynamic'`) يزيل تسرب اللغة أيضاً.
|
|
136
|
+
- **مكتبة `@intlayer/lingui`** تحافظ على صياغة ماكرو Lingui وتخدمها من قواميس Intlayer، مما يوفر مكونات أصغر حجماً (12.8 كيلوبايت) وترطيباً أسرع بكثير من Lingui الأصلي. إنها خطوة انتقال ممتازة وليست الوجهة النهائية.
|
|
137
|
+
|
|
138
|
+
## لماذا هذا الفارق؟ مترجمان بوحدتي عمل مختلفتين
|
|
139
|
+
|
|
140
|
+
تعتمد كلتا المكتبتين على الترجمة البرمجية (Compilation)، لكن الفارق يكمن في **ما** تقومان بترجمته.
|
|
141
|
+
|
|
142
|
+
**يقوم Lingui بتجميع الكتالوجات.** يتم استخراج الماكرو في الكود المصدري إلى ملف `.po` لكل لغة، ثم تجميعه في وحدة JS لكل لغة. وحدة العمل هنا هي **اللغة**. ولتقسيمها بشكل أدق، حسب المسار أو المكون، يلزم إنشاء كتالوجات متعددة وتعديل `lingui.config.ts` والتحميل اليدوي لكل مسار. كائن `I18n` عام، وكل استدعاء لـ `useLingui()` يربط المكون به.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
.
|
|
146
|
+
├── lingui.config.ts
|
|
147
|
+
└── src
|
|
148
|
+
├── i18n.ts # setupI18n(), load(), activate()
|
|
149
|
+
├── locales
|
|
150
|
+
│ ├── en
|
|
151
|
+
│ │ ├── messages.po
|
|
152
|
+
│ │ └── messages.mjs # مخرجات lingui compile
|
|
153
|
+
│ └── fr
|
|
154
|
+
│ ├── messages.po
|
|
155
|
+
│ └── messages.mjs
|
|
156
|
+
├── components
|
|
157
|
+
│ └── Counter.tsx # const { t } = useLingui(); t`Increment`
|
|
158
|
+
└── routes
|
|
159
|
+
└── $locale
|
|
160
|
+
└── about.tsx # await import(`../locales/${locale}/messages.mjs`)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**يقوم Intlayer بتجميع القواميس.** كل ملف `.content.ts` هو قاموس مرتبط بمفتاح محدد؛ يحدد المترجم المكونات التي تستورد كل مفتاح ويصدر، لكل قاموس ولكل لغة، كود JSON الذي يحتاجه هذا المكون فقط. وحدة العمل هنا هي **المكون**. وتحديد النطاق لكل مسار هو نتيجة طبيعية: تسحب الصفحة فقط قواميس المكونات التي تعرضها فعلياً.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
.
|
|
167
|
+
├── intlayer.config.ts
|
|
168
|
+
└── src
|
|
169
|
+
├── components
|
|
170
|
+
│ └── Counter
|
|
171
|
+
│ ├── index.tsx # useIntlayer("counter")
|
|
172
|
+
│ └── index.content.ts
|
|
173
|
+
└── routes
|
|
174
|
+
└── $locale
|
|
175
|
+
├── about.tsx
|
|
176
|
+
└── about.content.ts
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
لذلك فإن نمط `scoped-dynamic` هو نتاج بناء تلقائي لـ Intlayer، ومشروع تهيئة يدوي معقد لـ Lingui.
|
|
180
|
+
|
|
181
|
+
> للحصول على أرقام صف `dynamic`، قم بضبط `dictionary.importMode: 'dynamic'` في ملف `intlayer.config.ts`. راجع [دليل تحسين الحزمة](https://intlayer.org/ar/doc/concept/bundle-optimization).
|
|
182
|
+
|
|
183
|
+
## تجربة المطور
|
|
184
|
+
|
|
185
|
+
### الإعداد
|
|
186
|
+
|
|
187
|
+
**Lingui**
|
|
188
|
+
|
|
189
|
+
```ts fileName="lingui.config.ts"
|
|
190
|
+
import { defineConfig } from "@lingui/cli";
|
|
191
|
+
|
|
192
|
+
export default defineConfig({
|
|
193
|
+
sourceLocale: "en",
|
|
194
|
+
locales: ["en", "fr"],
|
|
195
|
+
catalogs: [
|
|
196
|
+
{
|
|
197
|
+
path: "<rootDir>/src/locales/{locale}/messages",
|
|
198
|
+
include: ["src"],
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
```ts fileName="src/i18n.ts"
|
|
205
|
+
import { setupI18n } from "@lingui/core";
|
|
206
|
+
|
|
207
|
+
export const loadCatalog = async (locale: string) => {
|
|
208
|
+
const { messages } = await import(`./locales/${locale}/messages.mjs`);
|
|
209
|
+
const i18n = setupI18n();
|
|
210
|
+
i18n.load(locale, messages);
|
|
211
|
+
i18n.activate(locale);
|
|
212
|
+
return i18n;
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
ثم إضافة `@lingui/babel-plugin-lingui-macro` (أو `@lingui/swc-plugin`) لأداة التحزيم، وتشغيل `lingui extract` بعد التعديل، و`lingui compile` قبل البناء، وتغليف الشجرة بـ `<I18nProvider i18n={i18n}>`.
|
|
217
|
+
|
|
218
|
+
**Intlayer**
|
|
219
|
+
|
|
220
|
+
```ts fileName="intlayer.config.ts"
|
|
221
|
+
import { type IntlayerConfig, Locales } from "intlayer";
|
|
222
|
+
|
|
223
|
+
const config: IntlayerConfig = {
|
|
224
|
+
internationalization: {
|
|
225
|
+
locales: [Locales.ENGLISH, Locales.FRENCH],
|
|
226
|
+
defaultLocale: Locales.ENGLISH,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export default config;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
أضف `intlayer()` في `vite.config.ts` (أو `withIntlayer()` في `next.config.ts`) وغلف التطبيق بـ `<IntlayerProvider>`. لا حاجة لأي خطوة استخراج أو تجميع: تُبنى القواميس تلقائياً أثناء عمل أداة التحزيم.
|
|
234
|
+
|
|
235
|
+
### المكونات
|
|
236
|
+
|
|
237
|
+
**Lingui**
|
|
238
|
+
|
|
239
|
+
```tsx fileName="src/components/Counter.tsx"
|
|
240
|
+
import { useState } from "react";
|
|
241
|
+
import { useLingui } from "@lingui/react/macro";
|
|
242
|
+
import { Trans } from "@lingui/react/macro";
|
|
243
|
+
|
|
244
|
+
export const Counter = () => {
|
|
245
|
+
const { t, i18n } = useLingui();
|
|
246
|
+
const [count, setCount] = useState(0);
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<div>
|
|
250
|
+
<p>{i18n.number(count)}</p>
|
|
251
|
+
<button aria-label={t`Counter`} onClick={() => setCount((c) => c + 1)}>
|
|
252
|
+
<Trans>Increment</Trans>
|
|
253
|
+
</button>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
يعيش النص الإنجليزي في المكون؛ ويعيش الفرنسي في `src/locales/fr/messages.po` تحت معرف مجزأ بعد تشغيل `lingui extract`. نسيان تشغيله أو نسيان `compile` يعيد النص بصمت إلى الإنجليزية.
|
|
260
|
+
|
|
261
|
+
**Intlayer**
|
|
262
|
+
|
|
263
|
+
```ts fileName="src/components/Counter/index.content.ts"
|
|
264
|
+
import { t, type Dictionary } from "intlayer";
|
|
265
|
+
|
|
266
|
+
const counterContent = {
|
|
267
|
+
key: "counter",
|
|
268
|
+
content: {
|
|
269
|
+
label: t({ en: "Counter", fr: "Compteur" }),
|
|
270
|
+
increment: t({ en: "Increment", fr: "Incrémenter" }),
|
|
271
|
+
},
|
|
272
|
+
} satisfies Dictionary;
|
|
273
|
+
|
|
274
|
+
export default counterContent;
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
```tsx fileName="src/components/Counter/index.tsx"
|
|
278
|
+
import { useState } from "react";
|
|
279
|
+
import { useIntlayer } from "react-intlayer";
|
|
280
|
+
import { useNumber } from "react-intlayer/format";
|
|
281
|
+
|
|
282
|
+
export const Counter = () => {
|
|
283
|
+
const { label, increment } = useIntlayer("counter");
|
|
284
|
+
const number = useNumber();
|
|
285
|
+
const [count, setCount] = useState(0);
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<div>
|
|
289
|
+
<p>{number(count)}</p>
|
|
290
|
+
<button aria-label={label} onClick={() => setCount((c) => c + 1)}>
|
|
291
|
+
{increment}
|
|
292
|
+
</button>
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
توجد كلتا اللغتين في ملف واحد بجوار المكون. غياب ترجمة `fr` يسبب خطأ في البناء، والمفتاح الخاطئ يسبب خطأ في TypeScript.
|
|
299
|
+
|
|
300
|
+
### خارج المكونات
|
|
301
|
+
|
|
302
|
+
البيانات الوصفية (Metadata)، والـ loaders، ودوال الخادم: أي مكان خارج شجرة React.
|
|
303
|
+
|
|
304
|
+
**Lingui**
|
|
305
|
+
|
|
306
|
+
```ts fileName="src/routes/$locale/about.tsx"
|
|
307
|
+
import { setupI18n } from "@lingui/core";
|
|
308
|
+
import { msg } from "@lingui/core/macro";
|
|
309
|
+
|
|
310
|
+
const title = msg`About us`;
|
|
311
|
+
|
|
312
|
+
export const loader = async ({ params }: { params: { locale: string } }) => {
|
|
313
|
+
const { messages } = await import(
|
|
314
|
+
`../../locales/${params.locale}/messages.mjs`
|
|
315
|
+
);
|
|
316
|
+
const i18n = setupI18n({
|
|
317
|
+
locale: params.locale,
|
|
318
|
+
messages: { [params.locale]: messages },
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return { title: i18n._(title) };
|
|
322
|
+
};
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
إنشاء كائن `I18n` جديد لكل استدعاء، وتحميل الكتالوج يدوياً، واستخدام `msg` + `i18n._()` بدلاً من `t`. كما تشير [ملاحظات الاختبار](https://github.com/intlayer-org/benchmark-bloom/blob/main/report/NOTE.md)، معرفة متى تستخدم `t` أو `` t` ` `` أو `i18n.t()` أو `msg` أو `<Trans>` "ليس بالأمر البديهي".
|
|
326
|
+
|
|
327
|
+
**Intlayer**
|
|
328
|
+
|
|
329
|
+
```ts fileName="src/routes/$locale/about.tsx"
|
|
330
|
+
import { getIntlayer } from "intlayer";
|
|
331
|
+
|
|
332
|
+
export const loader = async ({ params }: { params: { locale: string } }) => {
|
|
333
|
+
const { title } = getIntlayer("about-metadata", params.locale);
|
|
334
|
+
|
|
335
|
+
return { title };
|
|
336
|
+
};
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## الاحتفاظ بماكرو Lingui مع قواميس Intlayer
|
|
340
|
+
|
|
341
|
+
تعد حزمة `@intlayer/lingui` محولاً مباشراً لـ `@lingui/core` و`@lingui/react`. تواصل وحدات الماكرو التجميع كالمعتاد، بينما يتم توفير تنفيذ `i18n._()` من قواميس Intlayer، مع مزامنة ملفات `.po` للاحتفاظ بكتالوجاتك الحالية. تعمل خيارات وصيغ الجمع في ICU بالطريقة نفسها.
|
|
342
|
+
|
|
343
|
+
```ts fileName="vite.config.ts"
|
|
344
|
+
import { defineConfig } from "vite";
|
|
345
|
+
import { lingui } from "@intlayer/lingui/plugin";
|
|
346
|
+
|
|
347
|
+
export default defineConfig({
|
|
348
|
+
plugins: [lingui()],
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
احتفظ بـ `@lingui/babel-plugin-lingui-macro` أو `@lingui/swc-plugin` ليعمل قبل مترجم Intlayer. راجع [دليل التوافق مع Lingui](https://intlayer.org/ar/doc/compatibility/lingui).
|
|
353
|
+
|
|
354
|
+
## متى تختار أياً منهما؟
|
|
355
|
+
|
|
356
|
+
- **اختر Lingui** إذا كنت بحاجة إلى **ICU MessageFormat** مع وحدات ماكرو نمطية، وكان المترجمون يعملون بملفات **`.po`** مع نظام TMS حالي، وتفضل وجود نصوص المصدر مضمنة داخل JSX، ولا يمانع فريقك إدارة عملية الاستخراج والتجميع وتوزيع الكتالوجات يدوياً.
|
|
357
|
+
- **اختر Intlayer** إذا كنت تريد **محتوى بنطاق المكونات**، و**TypeScript صارماً**، و**أخطاء للمفاتيح المفقودة عند البناء**، و**تقليماً للشجرة وتحميلاً كسولاً تلقائياً بدون إعدادات**، ومكونات خفيفة، وترطيباً سريعاً، وتبديلاً فورياً للغة، وأدوات تحرير مدمجة (محرر مرئي، CMS، ترجمة بالذكاء الاصطناعي، خادم MCP).
|
|
358
|
+
- **اختر `@intlayer/lingui`** إذا كنت تستخدم Lingui حالياً وتريد الانتقال تدريجياً إلى قواميس Intlayer دون تغيير صياغة وحدات الماكرو.
|
|
359
|
+
|
|
360
|
+
## مقارنات ذات صلة
|
|
361
|
+
|
|
362
|
+
- [next-intl مقابل Intlayer](https://intlayer.org/ar/blog/next-intl-vs-intlayer) (الاختبار نفسه)
|
|
363
|
+
- [i18next مقابل Intlayer](https://intlayer.org/ar/blog/i18next-vs-intlayer) (الاختبار نفسه)
|
|
364
|
+
- [vue-i18n مقابل Intlayer](https://intlayer.org/ar/blog/vue-i18n-vs-intlayer-benchmark) (الاختبار نفسه)
|
|
365
|
+
- [المترجم مقابل التدويل التصريحي](https://intlayer.org/ar/blog/compiler-vs-declarative-i18n)
|
|
366
|
+
|
|
367
|
+
## نجوم GitHub
|
|
368
|
+
|
|
369
|
+
تعد نجوم GitHub مؤشراً قوياً على شعبية المشروع وثقة المجتمع وأهميته على المدى الطويل. على الرغم من أنها ليست مقياساً مباشراً للجودة التقنية، إلا أنها تعكس عدد المطورين الذين يجدون المشروع مفيداً ويتابعون تطوره.
|
|
370
|
+
|
|
371
|
+
[](https://star-history.com/#lingui/js-lingui&aymericzip/intlayer)
|
|
372
|
+
|
|
373
|
+
## الخاتمة
|
|
374
|
+
|
|
375
|
+
Lingui هي أقوى مكتبة تجمع بين بيئة التشغيل والمترجم في هذا الاختبار. تمنحها كتالوجاتها المجمعة والمجزأة حجماً صغيراً لـ JavaScript لكل صفحة يفصل بينه وبين Intlayer بضع كيلوبايت فقط، بل وتتفوق عليه قليلاً في TanStack Start. وإذا كان حجم البايتات لكل صفحة هو المعيار الوحيد، لكانت النتيجة تعادلاً.
|
|
376
|
+
|
|
377
|
+
لكن الأمر يتجاوز ذلك. يقف مترجم Lingui عند حدود اللغة؛ وكل ما دون ذلك (الكتالوجات لكل مسار، التحميل الكسول، منع تسرب لغة المصدر الاحتياطية) يتطلب تهيئة يدوية. ويظهر الاختبار ثمن هذا الحد: مكونات **أكبر بـ 10-20 ضعفاً**، وترطيب **أبطأ بـ 2-3 أضعاف**، و**تسرب للغة بنسبة 3-15%** لا يختفي أبداً، وتبديل لغة يستغرق **42 مللي ثانية** في التهيئة المحسنة. في المقابل يعمل مترجم Intlayer على مستوى المكون مباشرة، لتكون تلك الأرقام **6-8 كيلوبايت**، و**11-14 مللي ثانية**، و**0%**، و**3-4 مللي ثانية** بدون أي تهيئة يدوية.
|
|
378
|
+
|
|
379
|
+
جميع البيانات الأولية والتطبيقات وسيناريوهات الاختبار متوفرة في [مستودع Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom). يمكنك تشغيله بنفسك.
|
|
380
|
+
|
|
381
|
+
راجع وثيقة ['لماذا Intlayer؟'](https://intlayer.org/ar/doc/why) لمزيد من التفاصيل.
|
|
@@ -1437,7 +1437,7 @@ The set up of the middleware centralized in the `intlayer.config.ts` file.
|
|
|
1437
1437
|
|
|
1438
1438
|
نجوم GitHub هي مؤشر قوي على شعبية المشروع، وثقة المجتمع، وأهميته على المدى الطويل. وعلى الرغم من أنها ليست مقياسًا مباشرًا للجودة التقنية، إلا أنها تعكس عدد المطورين الذين يجدون المشروع مفيدًا، ويتابعون تقدمه، ومن المحتمل أن يتبنوه. لتقدير قيمة المشروع، تساعد النجوم في مقارنة الجذب بين البدائل وتوفر رؤى حول نمو النظام البيئي.
|
|
1439
1439
|
|
|
1440
|
-
[](https://
|
|
1440
|
+
[](https://star-history.com/#i18next/next-i18next&amannn/next-intl&aymericzip/intlayer)
|
|
1441
1441
|
|
|
1442
1442
|
## الخلاصة
|
|
1443
1443
|
|