@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,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-09-13
|
|
3
|
+
updatedAt: 2026-09-13
|
|
4
|
+
title: "next-intl vs @intlayer/next-intl: Cùng API, Bundle Khác Nhau"
|
|
5
|
+
description: Những thay đổi khi các import next-intl của một ứng dụng Next.js được phục vụ bởi compat adapter @intlayer/next-intl. Kích thước bundle, rò rỉ, kích thước component và hydration được đo lường trên cùng một code, cộng với những gì adapter giữ lại, bỏ qua và không thể thay thế.
|
|
6
|
+
keywords:
|
|
7
|
+
- next-intl
|
|
8
|
+
- use-intl
|
|
9
|
+
- "@intlayer/next-intl"
|
|
10
|
+
- Intlayer
|
|
11
|
+
- Compat adapter
|
|
12
|
+
- Migration
|
|
13
|
+
- Internationalization
|
|
14
|
+
- i18n
|
|
15
|
+
- Benchmark
|
|
16
|
+
- Bundle size
|
|
17
|
+
- Blog
|
|
18
|
+
- Next.js
|
|
19
|
+
- TanStack Start
|
|
20
|
+
- React
|
|
21
|
+
slugs:
|
|
22
|
+
- blog
|
|
23
|
+
- next-intl-vs-intlayer-next-intl
|
|
24
|
+
author: aymericzip
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# next-intl VS @intlayer/next-intl | Cùng API, Bundle Khác Nhau
|
|
28
|
+
|
|
29
|
+
`@intlayer/next-intl` là một compat adapter: nó cung cấp API của `next-intl` (`useTranslations`, `getTranslations`, `useLocale`, `t.rich()`, ICU plurals, `NextIntlClientProvider`...) và phục vụ nó từ các từ điển được biên dịch bởi Intlayer. Mã ứng dụng không thay đổi. Bundle thì có.
|
|
30
|
+
|
|
31
|
+
Bài viết này so sánh hai cái trên cùng một ứng dụng Next.js, được xây dựng một lần với `next-intl` và một lần với adapter. Các con số này đến từ [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom), một bộ mã nguồn mở ghi lại những gì trình duyệt thực sự tải xuống. Nếu bạn muốn so sánh `next-intl` vs Intlayer dưới dạng các thư viện, hãy đọc [next-intl vs Intlayer](https://intlayer.org/blog/next-intl-vs-intlayer). Cái này là về những gì adapter thay đổi khi bạn giữ nguyên các component của mình.
|
|
32
|
+
|
|
33
|
+
<TOC/>
|
|
34
|
+
|
|
35
|
+
> **tl;dr**: Trên cùng một ứng dụng Next.js, việc thay thế `next-intl` bằng `@intlayer/next-intl` đã giảm JavaScript trên mỗi trang từ **153.6 KB xuống 147.5 KB** gzip, thành phần trung bình từ **21.8 KB xuống 8.1 KB**, rò rỉ chuỗi trang nước ngoài từ **~90% xuống 0%**, và hydration từ **14.7 ms xuống 12.8 ms**, mà không cần chỉnh sửa thành phần nào. Trên TanStack Start, equivalent `use-intl` (`@intlayer/use-intl`) đã giảm các thành phần từ **76-87 KB xuống 9-11 KB** và chuyển đổi locale từ **7-21 ms xuống 4-9 ms**. Adapter tiêu tốn **8.0 KB** runtime so với **14.7 KB** cho `next-intl` và **5.5 KB** cho `next-intlayer` gốc. Navigation và middleware được triển khai lại trên cấu hình định tuyến của Intlayer; `pathnames` được địa phương hóa là tính năng duy nhất không được chuyển qua.
|
|
36
|
+
|
|
37
|
+
## `@intlayer/next-intl` là gì
|
|
38
|
+
|
|
39
|
+
`next-intl` là một runtime: `getRequestConfig` tải một `messages/{locale}.json` cho mỗi request, `NextIntlClientProvider` gửi nó đến client, và `useTranslations("about")` đọc các keys từ object đó tại thời điểm render. Mọi tối ưu hóa (namespaces, `pick(messages, [...])` cho mỗi page, lazy loading) đều phải được bạn viết.
|
|
40
|
+
|
|
41
|
+
`@intlayer/next-intl` giữ lại phần đầu và phần cuối của chuỗi đó và thay thế phần giữa. Các components của bạn vẫn gọi `useTranslations("about")`; những gì họ nhận được đến từ một dictionary Intlayer được biên dịch tại build time, được phạm vi hóa cho component đó, chỉ trong locale hoạt động.
|
|
42
|
+
|
|
43
|
+
Ba cơ chế làm cho nó hoạt động:
|
|
44
|
+
|
|
45
|
+
1. **Import aliasing.** `createNextIntlPlugin()` từ `@intlayer/next-intl/plugin` bao bọc `withIntlayer` và thêm các alias Webpack / Turbopack để `next-intl`, `next-intl/server`, `next-intl/navigation` và `next-intl/middleware` được phân giải thành `@intlayer/next-intl`. Không có import nào trong codebase của bạn bị đổi tên.
|
|
46
|
+
2. **JSON as source of truth.** Plugin `syncJSON` đọc `messages/{locale}.json` hiện tại của bạn, tách các khóa cấp cao nhất thành một dictionary cho mỗi namespace, và ghi lại các bản dịch vào cùng các tệp khi CLI hoặc CMS cập nhật chúng. Quy trình làm việc của các dịch giả của bạn không bị thay đổi.
|
|
47
|
+
3. **Call-site binding.** Lần pass tối ưu hóa Intlayer (Babel hoặc SWC) viết lại `useTranslations("about")` thành một lệnh gọi nhận trực tiếp dictionary `about`. Component không còn tiếp cận một cây message toàn cục; nó tiếp cận nội dung của riêng nó.
|
|
48
|
+
|
|
49
|
+
```tsx fileName="app/[locale]/about/page.tsx"
|
|
50
|
+
// Mã của bạn, không thay đổi
|
|
51
|
+
import { useTranslations } from "next-intl";
|
|
52
|
+
|
|
53
|
+
const AboutPage = () => {
|
|
54
|
+
const t = useTranslations("about");
|
|
55
|
+
return <h1>{t("title")}</h1>;
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx fileName="Những gì trình biên dịch phát ra (đơn giản hóa)"
|
|
60
|
+
import _dicHash_about from "../.intlayer/dictionaries/about.mjs";
|
|
61
|
+
import { useDictionary as useTranslations } from "@intlayer/next-intl";
|
|
62
|
+
|
|
63
|
+
const AboutPage = () => {
|
|
64
|
+
const t = useTranslations(_dicHash_about);
|
|
65
|
+
return <h1>{t("title")}</h1>;
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Đó là lý do tại sao các cột component-size và page-leakage bên dưới thay đổi: một trang chỉ lấy các từ điển của các component mà nó render, và chỉ ở locale đang được phục vụ.
|
|
70
|
+
|
|
71
|
+
## Những gì adapter giữ lại, bỏ qua và không thay thế
|
|
72
|
+
|
|
73
|
+
| `next-intl` API | Với `@intlayer/next-intl` |
|
|
74
|
+
| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `useTranslations("ns")` / `getTranslations("ns")` | ✅ Được giữ lại. Được ràng buộc với từ điển `ns` tại thời điểm build. Các khóa được gõ dựa trên nội dung của bạn. |
|
|
76
|
+
| `getTranslations({ locale, namespace })` | ✅ Được giữ lại |
|
|
77
|
+
| `t("key", { name })`, `t.rich()`, `t.markup()`, `t.raw()` | ✅ Được giữ lại. ICU plurals, `select`, `selectordinal`, `#`, `{ts, date, long}` chạy qua Intlayer's ICU resolver |
|
|
78
|
+
| `useLocale()` / `getLocale()` / `setRequestLocale()` / `setLocale` | ✅ Được giữ lại |
|
|
79
|
+
| `useFormatter()` | ✅ Được giữ lại. `dateTime`, `number`, `relativeTime`, `list`, `dateTimeRange` kết nối với native `Intl` |
|
|
80
|
+
| `NextIntlClientProvider` | ✅ Được giữ lại. Các props `messages`, `timeZone` và `now` **được chấp nhận nhưng bị bỏ qua** (một cảnh báo cho dev sẽ thông báo cho bạn) |
|
|
81
|
+
| `getMessages()` | ✅ Được giữ lại để tương thích; không còn cần thiết |
|
|
82
|
+
| `getRequestConfig()` trong `src/i18n.ts` | ⚠️ Không cần thiết. Các từ điển được biên dịch tại thời điểm build; không có việc tải message cho từng request |
|
|
83
|
+
| `defineRouting()` | ✅ Được giữ lại. Các trường bị bỏ qua (`locales`, `defaultLocale`, `localePrefix`) được đọc từ `intlayer.config.ts` |
|
|
84
|
+
| `createNavigation()`, `Link`, `redirect`, `usePathname`, `useRouter` | ✅ Được giữ lại. Được triển khai lại trên cấu hình định tuyến của Intlayer; tham số `routing` được chấp nhận nhưng bị bỏ qua |
|
|
85
|
+
| `pathnames` (tên route được bản địa hóa) | ❌ Được chấp nhận để gõ, **không được nội suy**. Giữ tên đường dẫn đơn giản hoặc chuyển ánh xạ đó sang `rewrite` của Intlayer |
|
|
86
|
+
| `createMiddleware()` | ✅ Được giữ lại. Trả về proxy của Intlayer; đặt cookie `NEXT_LOCALE` để `useLocale()` và bộ chuyển đổi của bạn tiếp tục hoạt động |
|
|
87
|
+
| `NEXT_LOCALE` cookie | ✅ Được đọc theo mặc định (trừ khi bạn tự cấu hình `routing.storage`) |
|
|
88
|
+
| Bare `useTranslations()` với không có namespace | ⚠️ Hoạt động, nhưng call site không được ràng buộc: nó được giải quyết thông qua registry runtime. Hãy truyền một namespace để có được bundle gains |
|
|
89
|
+
|
|
90
|
+
## Benchmark
|
|
91
|
+
|
|
92
|
+
### Những gì được đo lường
|
|
93
|
+
|
|
94
|
+
Suite [Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom) xây dựng **cùng một ứng dụng** với mỗi setup: **10 trang** (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), **10 locale** (`en`, `fr`, `es`, `de`, `it`, `pt`, `zh`, `ja`, `ko`, `ru`), các component giống hệt nhau và nội dung giống hệt nhau. Các trang được đo lường trong `en` và `fr`.
|
|
95
|
+
|
|
96
|
+
`next-intl` được xây dựng với bốn chiến lược tải, từ cách thiết lập ngây thơ (`messages/{locale}.json` tải toàn bộ) đến cách tối ưu nhất (một namespace cho mỗi route + `pick()` cho mỗi trang). Adapter được xây dựng trên **các component giống như cách thiết lập ngây thơ**, chỉ thay đổi `next.config.ts` và `intlayer.config.ts`. Nó không có biến thể "scoped": compiler scopes content cho mỗi component, vì vậy các hàng `static` và `dynamic` của nó đã được scoped.
|
|
97
|
+
|
|
98
|
+
Đối với mỗi build, suite ghi lại:
|
|
99
|
+
|
|
100
|
+
- **Lib size**: gzip size của một component trống chỉ import thư viện i18n. Chi phí cố định của runtime.
|
|
101
|
+
- **Page JS**: gzip JavaScript được tải xuống cho mỗi trang, trung bình trên tất cả các trang và locale.
|
|
102
|
+
- **Locale leak %**: tỷ lệ các chuỗi dịch được tìm thấy trong JS đã tải xuống mà người dùng **không** xem.
|
|
103
|
+
- **Page leak %**: tỷ lệ các chuỗi dịch được tìm thấy trong JS đã tải xuống mà người dùng **không** ở trên trang đó.
|
|
104
|
+
- **Component avg**: kích thước gzip trung bình của mỗi component được biên dịch riêng lẻ. Cho thấy bao nhiêu runtime i18n và catalog mà một component đơn lẻ kéo theo.
|
|
105
|
+
- **E2E reactivity**: thời gian tính theo giờ tường giữa việc chọn một locale mới và `html[lang]` cập nhật trong DOM (Playwright, 5 lần lặp).
|
|
106
|
+
- **Hydration**: thời lượng pha hydration của React.
|
|
107
|
+
|
|
108
|
+
> Các số liệu dưới đây đến từ lần chạy ngày **2026-09-12** với `next-intl` / `use-intl` 4.14.2 và `@intlayer/*` 9.5.1. Ứng dụng kiểm tra được thiết kế có ý định nhỏ (một vài chục chuỗi cho mỗi locale), vì vậy tỷ lệ phần trăm rò rỉ mô tả một **mô hình**: chúng tăng theo nội dung của bạn trong khi chi phí runtime vẫn cố định.
|
|
109
|
+
|
|
110
|
+
### Kết quả trên Next.js
|
|
111
|
+
|
|
112
|
+
| Setup | Strategy | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | E2E reactivity | Hydration |
|
|
113
|
+
| ------------------------- | -------------- | ------------: | ---------------: | ----------: | --------: | -----------------: | -------------: | ----------: |
|
|
114
|
+
| **base** (no i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
|
|
115
|
+
| `next-intl` | static | 14.7 KB | 153.6 KB | 4.2% | 89.8% | 21.8 KB | 16.0 ms | 14.7 ms |
|
|
116
|
+
| `next-intl` | dynamic | 14.7 KB | 153.6 KB | 9.7% | 89.9% | 21.8 KB | 15.6 ms | 14.8 ms |
|
|
117
|
+
| `next-intl` | scoped-static | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 80.1 KB | 17.9 ms | 17.4 ms |
|
|
118
|
+
| `next-intl` | scoped-dynamic | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 22.9 KB | 17.8 ms | 16.8 ms |
|
|
119
|
+
| **`@intlayer/next-intl`** | static | **8.0 KB** | **147.5 KB** | **0.0%** | **0.0%** | **8.1 KB** | **14.5 ms** | **12.8 ms** |
|
|
120
|
+
| **`@intlayer/next-intl`** | dynamic | **8.0 KB** | **148.7 KB** | **0.0%** | **0.0%** | **8.1 KB** | **11.7 ms** | **12.8 ms** |
|
|
121
|
+
| `next-intlayer` (native) | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
|
|
122
|
+
| `next-intlayer` (native) | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
|
|
123
|
+
|
|
124
|
+
**Cách đọc nó**
|
|
125
|
+
|
|
126
|
+
- **Cùng các component, ít hơn 6 KB trên mỗi trang.** Phiên bản build của adapter trên ứng dụng naive đạt **147.5 KB**, thấp hơn mọi cấu hình `next-intl` bao gồm cấu hình được tối ưu hóa hoàn toàn (153.6 KB). Bản thân runtime là sự khác biệt: 8.0 KB so với 14.7 KB, được trả trên mỗi trang.
|
|
127
|
+
- **Rò rỉ xuống 0% mà không cần chạm vào component.** Cài đặt `next-intl` ngây thơ vận chuyển ~90% các chuỗi trang nước ngoài trên mỗi trang. Để đạt 0% với `next-intl` có nghĩa là các cài đặt `scoped-*`: một namespace cho mỗi route, và `pick(messages, [...])` trên mỗi trang. Adapter đạt 0% từ mã ngây thơ vì quá trình tối ưu hóa liên kết mỗi `useTranslations("ns")` với từ điển riêng của nó.
|
|
128
|
+
- **Components co lại 2.7x.** Một component biên dịch độc lập trung bình **21.8 KB** với `next-intl` (nó đạt đến provider và cây message) và **8.1 KB** với adapter. Trong cài đặt `scoped-static` của `next-intl`, con số đó _tăng lên_ thành 80 KB, vì tệp namespace của mỗi route trở nên có thể tiếp cận từ trang chọn nó.
|
|
129
|
+
- **Hydration nhanh hơn 2 ms** (12.8 vs 14.7 ms): không có message object để deserialize từ RSC payload trước khi React có thể hydrate.
|
|
130
|
+
- **Adapter không phải là native runtime.** `next-intlayer` nằm ở **141.3 KB**, +0.3 KB so với base app, với 5.5 KB runtime. Adapter mang API surface của `next-intl` (`useFormatter`, `t.rich`, ICU resolver) trên top của core của Intlayer, do đó 8.0 KB và +6 KB trên mỗi page. Đó là cây cầu, không phải đích đến.
|
|
131
|
+
|
|
132
|
+
### Kết quả trên TanStack Start (`use-intl`)
|
|
133
|
+
|
|
134
|
+
`use-intl` là core framework-agnostic của `next-intl`. Adapter của nó, `@intlayer/use-intl`, tuân theo cùng design với Vite plugin (`@intlayer/use-intl/plugin`).
|
|
135
|
+
|
|
136
|
+
| Thiết lập | Chiến lược | Kích thước Lib (gz) | Page JS trung bình (gz) | Rò rỉ Locale | Rò rỉ Page | Component trung bình (gz) | E2E reactivity | Hydration |
|
|
137
|
+
| ------------------------ | -------------- | ------------------: | ----------------------: | -----------: | ---------: | ------------------------: | -------------: | ----------: |
|
|
138
|
+
| **base** (không i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
|
|
139
|
+
| `use-intl` | static | 14.1 KB | 179.8 KB | 50.0% | 89.8% | 76.0 KB | 6.7 ms | 15.3 ms |
|
|
140
|
+
| `use-intl` | dynamic | 14.1 KB | 119.4 KB | 0.0% | 89.8% | 75.9 KB | 7.0 ms | 15.4 ms |
|
|
141
|
+
| `use-intl` | scoped-static | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 20.9 ms | 24.8 ms |
|
|
142
|
+
| `use-intl` | scoped-dynamic | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 13.3 ms | 25.9 ms |
|
|
143
|
+
| **`@intlayer/use-intl`** | static | **7.3 KB** | 135.8 KB | 49.7% | **0.0%** | **10.9 KB** | **4.2 ms** | **10.5 ms** |
|
|
144
|
+
| **`@intlayer/use-intl`** | dynamic | **7.3 KB** | **129.7 KB** | **0.0%** | **0.0%** | **9.3 KB** | **8.7 ms** | 16.1 ms |
|
|
145
|
+
| `intlayer` (native) | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
|
|
146
|
+
| `intlayer` (native) | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
|
|
147
|
+
|
|
148
|
+
**Cách đọc bảng**
|
|
149
|
+
|
|
150
|
+
- **Bytes trên mỗi trang tương đương với `use-intl` được tối ưu hóa.** `@intlayer/use-intl` ở chế độ `dynamic` (129.7 KB) nằm trong 1 KB của `use-intl`'s `scoped-dynamic` (128.7 KB), và cao hơn 10 KB so với `use-intl`'s `dynamic` đơn giản (119.4 KB). Hàng `dynamic` đơn giản đó vẫn rò rỉ 90% chuỗi từ các trang khác; byte count thấp vì nội dung ứng dụng kiểm tra nhỏ. Adapter's 0% là những gì giữ nguyên khi nội dung phát triển.
|
|
151
|
+
- **Các component nhỏ hơn 7-9 lần.** Các component `use-intl` trung bình **76-87 KB** trong mọi chiến lược, bởi vì `useTranslations` được liên kết với toàn bộ đối tượng message của provider. Adapter trung bình **9-11 KB**.
|
|
152
|
+
- **Chuyển đổi locale nhanh hơn.** Các setup `use-intl` được tối ưu hóa mất **13-21 ms** để cập nhật `html[lang]`; adapter mất **4-9 ms**. Ít component hơn được re-render, và không có gì được lấy lại từ một message tree.
|
|
153
|
+
- **`static` giữ lại mọi locale.** Hàng `static` của adapter cho thấy 49.7% rò rỉ locale, giống như native Intlayer ở chế độ `static`: tất cả locale được bundled, chỉ các từ điển của trang được bundled. Một dòng config (`importMode: 'dynamic'`) sẽ loại bỏ nó.
|
|
154
|
+
|
|
155
|
+
## Tại sao các con số thay đổi
|
|
156
|
+
|
|
157
|
+
Không có gì trong component thay đổi, vì vậy các lợi ích đều đến từ những gì `useTranslations` được liên kết tới.
|
|
158
|
+
|
|
159
|
+
**Với `next-intl`**, ràng buộc là provider. `NextIntlClientProvider` nhận toàn bộ object `messages` cho locale; mỗi `useTranslations("about")` đọc từ nó. Bundler thấy một component import một hook đọc một context, và không thể biết rằng chỉ có branch `about` được sử dụng. Các routes dưới đây đều chia sẻ cùng một object message, vì vậy cột page-leak đọc ~90% cho đến khi bạn tự chia nhỏ file.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
.
|
|
163
|
+
├── messages
|
|
164
|
+
│ ├── en.json # mọi namespace, mọi page
|
|
165
|
+
│ └── fr.json
|
|
166
|
+
└── src
|
|
167
|
+
├── i18n.ts # getRequestConfig({ messages: await import(...) })
|
|
168
|
+
├── middleware.ts # createMiddleware(routing)
|
|
169
|
+
└── app/[locale]
|
|
170
|
+
├── layout.tsx # <NextIntlClientProvider messages={messages}>
|
|
171
|
+
└── about/page.tsx # useTranslations("about")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Với `@intlayer/next-intl`**, binding là dictionary. `syncJSON` chuyển `messages/en.json` thành một dictionary cho mỗi key cấp cao; compiler giải quyết component nào gọi `useTranslations("about")` và gửi nó `about` trực tiếp, trong locale hiện hoạt, dưới dạng một import mà bundler có thể trace và split.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
.
|
|
178
|
+
├── intlayer.config.ts # syncJSON({ source: ({ locale }) => `./messages/${locale}.json` })
|
|
179
|
+
├── messages
|
|
180
|
+
│ ├── en.json # unchanged, still the source of truth
|
|
181
|
+
│ └── fr.json
|
|
182
|
+
├── .intlayer/ # generated: one dictionary per namespace, per locale
|
|
183
|
+
└── src
|
|
184
|
+
├── middleware.ts # createMiddleware() giờ trả về proxy của Intlayer
|
|
185
|
+
└── app/[locale]
|
|
186
|
+
├── layout.tsx # <NextIntlClientProvider> (không có props messages)
|
|
187
|
+
└── about/page.tsx # useTranslations("about") ← không thay đổi
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`src/i18n.ts` và props `messages` biến mất. Mọi thứ khác giống hệt.
|
|
191
|
+
|
|
192
|
+
## Migration trong ba bước
|
|
193
|
+
|
|
194
|
+
<Steps>
|
|
195
|
+
<Step number={1} title="Cài đặt">
|
|
196
|
+
|
|
197
|
+
```bash packageManager="npm"
|
|
198
|
+
npx intlayer init --interactive
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```bash packageManager="pnpm"
|
|
202
|
+
pnpm dlx intlayer init --interactive
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```bash packageManager="yarn"
|
|
206
|
+
yarn dlx intlayer init --interactive
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```bash packageManager="bun"
|
|
210
|
+
bunx intlayer init --interactive
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Lệnh phát hiện `next-intl` và cài đặt `intlayer`, `next-intlayer`, `@intlayer/next-intl` và `@intlayer/sync-json-plugin`. Giữ `next-intl` được cài đặt: nó là một peer dependency của adapter và cung cấp các types.
|
|
214
|
+
|
|
215
|
+
</Step>
|
|
216
|
+
<Step number={2} title="Chỉ định Intlayer đến messages của bạn">
|
|
217
|
+
|
|
218
|
+
```ts fileName="intlayer.config.ts"
|
|
219
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
220
|
+
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
221
|
+
|
|
222
|
+
const config: IntlayerConfig = {
|
|
223
|
+
internationalization: {
|
|
224
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
225
|
+
defaultLocale: Locales.ENGLISH,
|
|
226
|
+
},
|
|
227
|
+
dictionary: {
|
|
228
|
+
// "static" đóng gói mọi locale; "dynamic" tải locale hoạt động theo yêu cầu
|
|
229
|
+
importMode: "dynamic",
|
|
230
|
+
},
|
|
231
|
+
plugins: [
|
|
232
|
+
syncJSON({
|
|
233
|
+
// ICU placeholders: {name}, {count, plural, one {# item} other {# items}}
|
|
234
|
+
format: "icu",
|
|
235
|
+
source: ({ locale }) => `./messages/${locale}.json`,
|
|
236
|
+
location: "messages",
|
|
237
|
+
}),
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export default config;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`messages/{locale}.json` vẫn ở vị trí của nó. Mỗi khóa cấp cao nhất trở thành một dictionary; `useTranslations("about")` ánh xạ tới dictionary `about`.
|
|
245
|
+
|
|
246
|
+
</Step>
|
|
247
|
+
<Step number={3} title="Bao bọc next.config.ts">
|
|
248
|
+
|
|
249
|
+
```ts fileName="next.config.ts"
|
|
250
|
+
import type { NextConfig } from "next";
|
|
251
|
+
import { createNextIntlPlugin } from "@intlayer/next-intl/plugin";
|
|
252
|
+
|
|
253
|
+
const withIntlayer = createNextIntlPlugin();
|
|
254
|
+
|
|
255
|
+
const nextConfig: NextConfig = {};
|
|
256
|
+
|
|
257
|
+
export default withIntlayer(nextConfig);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`createNextIntlPlugin()` kết hợp `withIntlayer` (xem nội dung, biên dịch từ điển, bước tối ưu hóa) và các alias `next-intl` → `@intlayer/next-intl` cho Webpack và Turbopack. Build, và các số trong bảng trên là của bạn.
|
|
261
|
+
|
|
262
|
+
</Step>
|
|
263
|
+
</Steps>
|
|
264
|
+
|
|
265
|
+
### Những gì bạn có thể xóa sau đó
|
|
266
|
+
|
|
267
|
+
| Tệp / pattern | Lý do |
|
|
268
|
+
| -------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
269
|
+
| `getRequestConfig` in `src/i18n.ts` | Không tải tin nhắn theo yêu cầu. Chỉ giữ tệp nếu nó cũng xuất `createNavigation` helpers |
|
|
270
|
+
| `messages={...}` on `NextIntlClientProvider` | Adapter đọc output được biên dịch; prop bị bỏ qua và ghi log cảnh báo trong development |
|
|
271
|
+
| `await getMessages()` in layouts | Lý do tương tự |
|
|
272
|
+
| Per-page `pick(messages, [...])` | Compiler thực hiện picking, per component |
|
|
273
|
+
|
|
274
|
+
### Những gì bạn nhận được ngoài bytes
|
|
275
|
+
|
|
276
|
+
- **Typed keys.** `useTranslations("about")` được gõ chống lại compiled `about` dictionary. `t("does.not.exist")` là lỗi TypeScript, không phải fallback runtime.
|
|
277
|
+
- **`npx intlayer test`** làm CI thất bại khi một locale thiếu một khóa. **`npx intlayer fill`** dịch các khóa bị thiếu bằng nhà cung cấp của bạn lựa chọn (OpenAI, Anthropic, Mistral, Gemini...) sử dụng khóa của riêng bạn, và ghi kết quả trở lại `messages/{locale}.json`.
|
|
278
|
+
- **Visual Editor và CMS** làm việc trên các từ điển giống nhau, vì vậy những người không phải là nhà phát triển có thể chỉnh sửa `messages/fr.json` thông qua một giao diện người dùng và tệp được cập nhật.
|
|
279
|
+
- **Di chuyển từng phần sang `.content.ts`.** Bất kỳ component nào cũng có thể chuyển từ `useTranslations("about")` sang `useIntlayer("about")` với một tệp nội dung đồng vị trí, từng cái một. Các từ điển JSON và `.content.ts` tồn tại cùng nhau và hợp nhất.
|
|
280
|
+
|
|
281
|
+
## Những giới hạn cần biết trước khi bạn bắt đầu
|
|
282
|
+
|
|
283
|
+
- **Routing config chuyển sang `intlayer.config.ts`.** `createNavigation(routing)` và `createMiddleware(routing)` giữ nguyên signature của chúng nhưng bỏ qua argument: locales, default locale và prefix strategy đến từ Intlayer's `routing` config. Nếu bạn sử dụng `next-intl`'s localized `pathnames` (`/about` → `/a-propos`), adapter không interpolate chúng; Intlayer's `routing.rewrite` bao gồm trường hợp đó nhưng đó là một thay đổi riêng biệt.
|
|
284
|
+
- **Namespace-less `useTranslations()` không được bind.** Optimize pass cần một namespace tĩnh để biết dictionary nào cần import. Một bare call vẫn hoạt động thông qua một runtime registry tham chiếu đến mọi dictionary, đó chính xác là sự rò rỉ mà bạn đang cố gắng loại bỏ. Truyền namespace.
|
|
285
|
+
- **Adapter không miễn phí.** 8.0 KB runtime so với 5.5 KB cho `next-intlayer`, và +6-7 KB cho mỗi trang so với native build. Nó chi trả cho bề mặt API của `next-intl`. Nếu bạn đến điểm mà mọi component đã được chuyển sang `useIntlayer`, hãy bỏ adapter.
|
|
286
|
+
- **`messages`, `timeZone`, `now` trên provider bị bỏ qua.** Các formatter được hỗ trợ bởi `Intl` native và chỉ locale ảnh hưởng đến output của chúng; nếu bạn dựa vào một time zone bắt buộc hoặc một `now` cố định cho các ngày ổn định hydration, hãy xử lý nó tại call site.
|
|
287
|
+
|
|
288
|
+
## Khi nào nên sử dụng cái nào?
|
|
289
|
+
|
|
290
|
+
- **Tiếp tục với `next-intl`** nếu ứng dụng của bạn nhỏ, bundle của bạn không phải là mối quan tâm, và team của bạn thoải mái sở hữu namespaces và `pick()` cho mỗi trang.
|
|
291
|
+
- **Sử dụng `@intlayer/next-intl`** nếu bạn đang dùng `next-intl` và muốn có những lợi ích về bundle, rò rỉ và hydration, typed keys cũng như CLI / CMS tooling mà không cần viết lại toàn bộ. Đây là điểm vào được khuyến nghị cho bất kỳ codebase `next-intl` hiện có nào.
|
|
292
|
+
- **Đi native (`next-intlayer`)** cho các dự án mới, hoặc khi adapter đã hoàn thành công việc của nó. Đây là lựa chọn nhẹ nhất trong ba cái (5.5 KB, +0.3 KB per page) và mở khóa các server components đồng bộ, các file `.content.ts` per-component và bộ tính năng đầy đủ.
|
|
293
|
+
|
|
294
|
+
## Các so sánh liên quan
|
|
295
|
+
|
|
296
|
+
- [next-intl vs Intlayer](https://intlayer.org/blog/next-intl-vs-intlayer) (các thư viện, cùng benchmark)
|
|
297
|
+
- [i18next vs @intlayer/i18next](https://intlayer.org/blog/i18next-vs-intlayer-i18next) (cùng chuỗi adapter)
|
|
298
|
+
- [Lingui vs @intlayer/lingui](https://intlayer.org/blog/lingui-vs-intlayer-lingui) (cùng chuỗi adapter)
|
|
299
|
+
- [vue-i18n vs @intlayer/vue-i18n](https://intlayer.org/blog/vue-i18n-vs-intlayer-vue-i18n) (cùng chuỗi adapter)
|
|
300
|
+
- [Hướng dẫn di chuyển: next-intl sang Intlayer](https://intlayer.org/doc/migration/next-intl)
|
|
301
|
+
- [Tham khảo adapter tương thích: next-intl](https://intlayer.org/doc/compatibility/next-intl)
|
|
302
|
+
|
|
303
|
+
## Kết luận
|
|
304
|
+
|
|
305
|
+
`@intlayer/next-intl` làm một việc: nó thay đổi những gì `useTranslations` được liên kết đến, từ một provider chứa mọi message đến một dictionary được biên dịch cho component đó. Trên cùng một ứng dụng Next.js mà trị giá **6 KB mỗi trang**, **các component nhỏ hơn 2.7x**, **0% leakage** và **2 ms hydration**, trước khi bất kỳ ai mở một file component. Navigation và middleware giữ API của họ trên cấu hình routing của Intlayer, và runtime `next-intlayer` native vẫn còn nhẹ hơn nữa.
|
|
306
|
+
|
|
307
|
+
Tất cả dữ liệu thô, các test apps và scripts có trong [repository Benchmark Bloom](https://github.com/intlayer-org/benchmark-bloom). Chạy nó của bạn.
|
|
308
|
+
|
|
309
|
+
Tham khảo tài liệu ['Tại sao Intlayer?'](https://intlayer.org/doc/why) để biết thêm chi tiết.
|