@intlayer/docs 8.9.4-canary.0 → 8.9.5

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.
Files changed (207) hide show
  1. package/dist/cjs/generated/docs.entry.cjs +20 -0
  2. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  3. package/dist/esm/generated/docs.entry.mjs +20 -0
  4. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  5. package/dist/types/generated/docs.entry.d.ts +1 -0
  6. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  7. package/docs/ar/benchmark/index.md +0 -3
  8. package/docs/ar/benchmark/nextjs.md +15 -6
  9. package/docs/ar/benchmark/solid.md +155 -0
  10. package/docs/ar/benchmark/svelte.md +148 -0
  11. package/docs/ar/benchmark/tanstack.md +12 -3
  12. package/docs/ar/benchmark/vue.md +160 -0
  13. package/docs/ar/configuration.md +16 -12
  14. package/docs/ar/dictionary/content_file.md +51 -1
  15. package/docs/ar/mcp_server.md +30 -17
  16. package/docs/ar/plugins/sync-po.md +333 -0
  17. package/docs/bn/configuration.md +16 -12
  18. package/docs/cs/configuration.md +16 -12
  19. package/docs/de/benchmark/index.md +0 -3
  20. package/docs/de/benchmark/nextjs.md +15 -6
  21. package/docs/de/benchmark/solid.md +155 -0
  22. package/docs/de/benchmark/svelte.md +148 -0
  23. package/docs/de/benchmark/tanstack.md +12 -3
  24. package/docs/de/benchmark/vue.md +160 -0
  25. package/docs/de/configuration.md +16 -12
  26. package/docs/de/dictionary/content_file.md +52 -2
  27. package/docs/de/mcp_server.md +29 -16
  28. package/docs/de/plugins/sync-po.md +332 -0
  29. package/docs/en/benchmark/nextjs.md +11 -2
  30. package/docs/en/benchmark/solid.md +22 -4
  31. package/docs/en/benchmark/svelte.md +17 -5
  32. package/docs/en/benchmark/tanstack.md +18 -3
  33. package/docs/en/benchmark/vue.md +17 -11
  34. package/docs/en/configuration.md +16 -13
  35. package/docs/en/dictionary/content_file.md +51 -1
  36. package/docs/en/mcp_server.md +31 -18
  37. package/docs/en/plugins/sync-po.md +333 -0
  38. package/docs/en-GB/benchmark/index.md +0 -3
  39. package/docs/en-GB/benchmark/nextjs.md +15 -6
  40. package/docs/en-GB/benchmark/solid.md +155 -0
  41. package/docs/en-GB/benchmark/svelte.md +148 -0
  42. package/docs/en-GB/benchmark/tanstack.md +12 -3
  43. package/docs/en-GB/benchmark/vue.md +160 -0
  44. package/docs/en-GB/configuration.md +15 -11
  45. package/docs/en-GB/dictionary/content_file.md +51 -1
  46. package/docs/en-GB/mcp_server.md +31 -18
  47. package/docs/en-GB/plugins/sync-po.md +333 -0
  48. package/docs/es/benchmark/index.md +0 -3
  49. package/docs/es/benchmark/nextjs.md +15 -6
  50. package/docs/es/benchmark/solid.md +155 -0
  51. package/docs/es/benchmark/svelte.md +148 -0
  52. package/docs/es/benchmark/tanstack.md +12 -3
  53. package/docs/es/benchmark/vue.md +160 -0
  54. package/docs/es/configuration.md +16 -12
  55. package/docs/es/dictionary/content_file.md +51 -1
  56. package/docs/es/mcp_server.md +30 -17
  57. package/docs/es/plugins/sync-po.md +333 -0
  58. package/docs/fr/benchmark/index.md +0 -3
  59. package/docs/fr/benchmark/nextjs.md +15 -6
  60. package/docs/fr/benchmark/solid.md +155 -0
  61. package/docs/fr/benchmark/svelte.md +148 -0
  62. package/docs/fr/benchmark/tanstack.md +12 -3
  63. package/docs/fr/benchmark/vue.md +160 -0
  64. package/docs/fr/configuration.md +16 -12
  65. package/docs/fr/dictionary/content_file.md +51 -1
  66. package/docs/fr/mcp_server.md +30 -17
  67. package/docs/fr/plugins/sync-po.md +333 -0
  68. package/docs/hi/benchmark/nextjs.md +15 -6
  69. package/docs/hi/benchmark/solid.md +155 -0
  70. package/docs/hi/benchmark/svelte.md +148 -0
  71. package/docs/hi/benchmark/tanstack.md +12 -3
  72. package/docs/hi/benchmark/vue.md +160 -0
  73. package/docs/hi/configuration.md +16 -12
  74. package/docs/hi/dictionary/content_file.md +51 -1
  75. package/docs/hi/mcp_server.md +31 -18
  76. package/docs/hi/plugins/sync-po.md +333 -0
  77. package/docs/id/benchmark/index.md +0 -3
  78. package/docs/id/benchmark/nextjs.md +15 -6
  79. package/docs/id/benchmark/solid.md +155 -0
  80. package/docs/id/benchmark/svelte.md +148 -0
  81. package/docs/id/benchmark/tanstack.md +12 -3
  82. package/docs/id/benchmark/vue.md +160 -0
  83. package/docs/id/configuration.md +16 -12
  84. package/docs/id/dictionary/content_file.md +51 -1
  85. package/docs/id/mcp_server.md +30 -17
  86. package/docs/id/plugins/sync-po.md +333 -0
  87. package/docs/it/benchmark/index.md +1 -4
  88. package/docs/it/benchmark/nextjs.md +15 -6
  89. package/docs/it/benchmark/solid.md +155 -0
  90. package/docs/it/benchmark/svelte.md +148 -0
  91. package/docs/it/benchmark/tanstack.md +12 -3
  92. package/docs/it/benchmark/vue.md +160 -0
  93. package/docs/it/configuration.md +16 -12
  94. package/docs/it/dictionary/content_file.md +51 -1
  95. package/docs/it/mcp_server.md +30 -17
  96. package/docs/it/plugins/sync-po.md +333 -0
  97. package/docs/ja/benchmark/index.md +5 -5
  98. package/docs/ja/benchmark/nextjs.md +15 -6
  99. package/docs/ja/benchmark/solid.md +155 -0
  100. package/docs/ja/benchmark/svelte.md +148 -0
  101. package/docs/ja/benchmark/tanstack.md +12 -3
  102. package/docs/ja/benchmark/vue.md +160 -0
  103. package/docs/ja/configuration.md +16 -12
  104. package/docs/ja/dictionary/content_file.md +50 -2
  105. package/docs/ja/intlayer_with_nextjs_no_locale_path.md +4 -3
  106. package/docs/ja/mcp_server.md +29 -16
  107. package/docs/ja/plugins/sync-po.md +333 -0
  108. package/docs/ko/benchmark/nextjs.md +15 -6
  109. package/docs/ko/benchmark/solid.md +155 -0
  110. package/docs/ko/benchmark/svelte.md +148 -0
  111. package/docs/ko/benchmark/tanstack.md +12 -3
  112. package/docs/ko/benchmark/vue.md +160 -0
  113. package/docs/ko/configuration.md +16 -12
  114. package/docs/ko/dictionary/content_file.md +51 -1
  115. package/docs/ko/intlayer_with_nextjs_no_locale_path.md +3 -2
  116. package/docs/ko/mcp_server.md +31 -18
  117. package/docs/ko/plugins/sync-po.md +333 -0
  118. package/docs/nl/configuration.md +16 -12
  119. package/docs/pl/benchmark/index.md +0 -3
  120. package/docs/pl/benchmark/nextjs.md +15 -6
  121. package/docs/pl/benchmark/solid.md +155 -0
  122. package/docs/pl/benchmark/svelte.md +148 -0
  123. package/docs/pl/benchmark/tanstack.md +12 -3
  124. package/docs/pl/benchmark/vue.md +160 -0
  125. package/docs/pl/configuration.md +16 -12
  126. package/docs/pl/dictionary/content_file.md +51 -1
  127. package/docs/pl/mcp_server.md +30 -17
  128. package/docs/pl/plugins/sync-po.md +333 -0
  129. package/docs/pt/benchmark/index.md +0 -3
  130. package/docs/pt/benchmark/nextjs.md +16 -7
  131. package/docs/pt/benchmark/solid.md +155 -0
  132. package/docs/pt/benchmark/svelte.md +148 -0
  133. package/docs/pt/benchmark/tanstack.md +13 -4
  134. package/docs/pt/benchmark/vue.md +160 -0
  135. package/docs/pt/configuration.md +16 -12
  136. package/docs/pt/dictionary/content_file.md +51 -1
  137. package/docs/pt/mcp_server.md +30 -17
  138. package/docs/pt/plugins/sync-po.md +333 -0
  139. package/docs/ru/benchmark/nextjs.md +15 -6
  140. package/docs/ru/benchmark/solid.md +155 -0
  141. package/docs/ru/benchmark/svelte.md +148 -0
  142. package/docs/ru/benchmark/tanstack.md +12 -3
  143. package/docs/ru/benchmark/vue.md +160 -0
  144. package/docs/ru/configuration.md +16 -12
  145. package/docs/ru/dictionary/content_file.md +52 -2
  146. package/docs/ru/mcp_server.md +30 -17
  147. package/docs/ru/plugins/sync-po.md +333 -0
  148. package/docs/tr/benchmark/index.md +0 -3
  149. package/docs/tr/benchmark/nextjs.md +15 -6
  150. package/docs/tr/benchmark/solid.md +155 -0
  151. package/docs/tr/benchmark/svelte.md +148 -0
  152. package/docs/tr/benchmark/tanstack.md +12 -3
  153. package/docs/tr/benchmark/vue.md +160 -0
  154. package/docs/tr/configuration.md +16 -12
  155. package/docs/tr/dictionary/content_file.md +51 -1
  156. package/docs/tr/mcp_server.md +31 -18
  157. package/docs/tr/plugins/sync-po.md +333 -0
  158. package/docs/uk/benchmark/nextjs.md +15 -6
  159. package/docs/uk/benchmark/solid.md +155 -0
  160. package/docs/uk/benchmark/svelte.md +148 -0
  161. package/docs/uk/benchmark/tanstack.md +12 -3
  162. package/docs/uk/benchmark/vue.md +160 -0
  163. package/docs/uk/configuration.md +16 -12
  164. package/docs/uk/dictionary/content_file.md +51 -1
  165. package/docs/uk/mcp_server.md +29 -16
  166. package/docs/uk/plugins/sync-po.md +333 -0
  167. package/docs/ur/configuration.md +16 -12
  168. package/docs/vi/benchmark/index.md +0 -3
  169. package/docs/vi/benchmark/nextjs.md +15 -6
  170. package/docs/vi/benchmark/solid.md +155 -0
  171. package/docs/vi/benchmark/svelte.md +148 -0
  172. package/docs/vi/benchmark/tanstack.md +12 -3
  173. package/docs/vi/benchmark/vue.md +160 -0
  174. package/docs/vi/configuration.md +16 -12
  175. package/docs/vi/dictionary/content_file.md +51 -1
  176. package/docs/vi/intlayer_with_nextjs_15.md +10 -57
  177. package/docs/vi/mcp_server.md +30 -17
  178. package/docs/vi/plugins/sync-po.md +333 -0
  179. package/docs/zh/benchmark/nextjs.md +15 -6
  180. package/docs/zh/benchmark/solid.md +155 -0
  181. package/docs/zh/benchmark/svelte.md +148 -0
  182. package/docs/zh/benchmark/tanstack.md +12 -3
  183. package/docs/zh/benchmark/vue.md +160 -0
  184. package/docs/zh/configuration.md +16 -12
  185. package/docs/zh/dictionary/content_file.md +51 -3
  186. package/docs/zh/mcp_server.md +31 -18
  187. package/docs/zh/plugins/sync-po.md +333 -0
  188. package/frequent_questions/ar/intlayerNode.md +3 -3
  189. package/frequent_questions/de/intlayerNode.md +3 -3
  190. package/frequent_questions/en/intlayerNode.md +3 -3
  191. package/frequent_questions/en-GB/intlayerNode.md +3 -3
  192. package/frequent_questions/es/intlayerNode.md +3 -3
  193. package/frequent_questions/fr/intlayerNode.md +3 -3
  194. package/frequent_questions/hi/intlayerNode.md +3 -3
  195. package/frequent_questions/id/intlayerNode.md +3 -3
  196. package/frequent_questions/it/intlayerNode.md +3 -3
  197. package/frequent_questions/ja/intlayerNode.md +3 -3
  198. package/frequent_questions/ko/intlayerNode.md +3 -3
  199. package/frequent_questions/pl/intlayerNode.md +3 -3
  200. package/frequent_questions/pt/intlayerNode.md +3 -3
  201. package/frequent_questions/ru/intlayerNode.md +3 -3
  202. package/frequent_questions/tr/intlayerNode.md +3 -3
  203. package/frequent_questions/uk/intlayerNode.md +3 -3
  204. package/frequent_questions/vi/intlayerNode.md +3 -3
  205. package/frequent_questions/zh/intlayerNode.md +3 -3
  206. package/package.json +8 -8
  207. package/src/generated/docs.entry.ts +20 -0
@@ -0,0 +1,148 @@
1
+ ---
2
+ createdAt: 2026-04-20
3
+ updatedAt: 2026-04-21
4
+ title: Best i18n solution for Svelte in 2026 - Benchmark Report
5
+ description: Compare Svelte internationalisation (i18n) libraries like svelte-i18n, Paraglide, and Intlayer. Detailed performance report on bundle size, leakage, and reactivity.
6
+ keywords:
7
+ - benchmark
8
+ - i18n
9
+ - intl
10
+ - svelte
11
+ - performance
12
+ - intlayer
13
+ slugs:
14
+ - doc
15
+ - benchmark
16
+ - svelte
17
+ author: Aymeric PINEAU
18
+ applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-svelte-template
19
+ history:
20
+ - version: 8.7.12
21
+ date: 2026-01-06
22
+ changes: "Init benchmark"
23
+ ---
24
+
25
+ # Svelte i18n Libraries — 2026 Benchmark Report
26
+
27
+ This page is a benchmark report for i18n solutions on Svelte.
28
+
29
+ ## Table of Contents
30
+
31
+ <Toc/>
32
+
33
+ ## Interactive Benchmark
34
+
35
+ <I18nBenchmark framework="vite-svelte" vertical/>
36
+
37
+ ## Results reference:
38
+
39
+ <iframe
40
+ src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_svelte.md"
41
+ width="100%"
42
+ height="600px"
43
+ style="border:none;">
44
+ </iframe>
45
+
46
+ > https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_svelte.md
47
+
48
+ See complete benchmark repository [here](https://github.com/intlayer-org/benchmark-i18n/tree/main).
49
+
50
+ ## Introduction
51
+
52
+ Internationalisation solutions are among the heaviest dependencies in a Svelte app. The main risk is shipping unnecessary content: translations for other pages and other locales in a single route’s bundle.
53
+
54
+ As your app grows, that problem can quickly blow up the JavaScript sent to the client and slow down navigation.
55
+
56
+ In practice, for the least optimised implementations, an internationalised page can end up several times heavier than the version without i18n.
57
+
58
+ The other impact is on developer experience: how you declare content, types, namespace organisation, dynamic loading, and reactivity when the locale changes.
59
+
60
+ ## TL;DR
61
+
62
+ - **Intlayer**: The most performance-efficient choice (v8.7.12) with the smallest footprint.
63
+ - **Paraglide**: Strong contender for tree-shaking but has a more complex developer experience and reactivity overhead.
64
+ - **svelte-i18n**: Comprehensive and standard for Svelte, but carries much larger bundle weight (~7× Intlayer).
65
+
66
+ ## Test your app
67
+
68
+ To quickly spot i18n leakage issues, I set up a free scanner available [here](https://intlayer.org/i18n-seo-scanner).
69
+
70
+ <iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
71
+
72
+ ## The problem
73
+
74
+ Two levers are essential to limit the cost of a multilingual app:
75
+
76
+ - Split content by page / namespace so you do not load whole dictionaries when you do not need them
77
+ - Load the right locale dynamically, only when needed
78
+
79
+ Understanding the technical limitations of these approaches:
80
+
81
+ **Dynamic loading**
82
+
83
+ Without dynamic loading, most solutions keep messages in memory from the first render, which adds significant overhead for apps with many routes and locales.
84
+
85
+ With dynamic loading, you accept a trade-off: less initial JS, but sometimes an extra request when switching language.
86
+
87
+ **Content splitting**
88
+
89
+ Syntaxes built around `t('a.b.c')` are very convenient but often encourage keeping large JSON objects at runtime. That model makes tree-shaking hard unless the library offers a real per-page split strategy.
90
+
91
+ ## Methodology
92
+
93
+ For this benchmark, we compared the following libraries:
94
+
95
+ - `Base App` (No i18n library)
96
+ - `svelte-intlayer` (v8.7.12)
97
+ - `svelte-i18n` (v4.0.1)
98
+ - `@inlang/paraglide-js` (v2.17.0)
99
+
100
+ The framework is `Svelte` with a multilingual app of **10 pages** and **10 languages**.
101
+
102
+ We compared **four loading strategies**:
103
+
104
+ | Strategy | No namespaces (global) | With namespaces (scoped) |
105
+ | :------------------ | :------------------------------------------- | :------------------------------------------------------------------- |
106
+ | **Static loading** | **Static**: Everything in memory at startup. | **Scoped static**: Split by namespace; everything loaded at startup. |
107
+ | **Dynamic loading** | **Dynamic**: On-demand loading per locale. | **Scoped dynamic**: Granular loading per namespace and locale. |
108
+
109
+ ## Strategy summary
110
+
111
+ - **Static**: Simple; no network latency after the initial load. Downside: large bundle size.
112
+ - **Dynamic**: Reduces initial weight (lazy-loading). Ideal when you have many locales.
113
+ - **Scoped static**: Keeps code organised (logical separation) without complex extra network requests.
114
+ - **Scoped dynamic**: Best approach for _code splitting_ and performance. Minimises memory by loading only what the current view and active locale need.
115
+
116
+ ## Results in detail
117
+
118
+ ### 1 — Solutions to avoid
119
+
120
+ > No clear solution to avoid in svelte ecosystem.
121
+
122
+ ### 2 — Acceptable solutions
123
+
124
+ **(Paraglide)** (`@inlang/paraglide-js@2.17.0`):
125
+
126
+ `Paraglide` offers an innovative, well-thought-out approach. In the context of a Vite + Svelte app, the tree-shaking their company advertises worked as expected, which is great.
127
+ But in the case of React + TanStack Start, tree-shaking did not work as expected, same for Next.js. That said, Paraglide's usage in a Svelte and TanStack Start project would be worth a double check.
128
+ The workflow and DX are also more complex than other options.
129
+ Personally I am not a fan of having to regenerate JS files before every push, which creates constant merge conflict risk via PRs. The tool also seems more focused on Vite than Next.js.
130
+ Finally, in comparison with other solutions, Paraglide does not use a store (e.g. Svelte store) to retrieve the current locale to render the content. For each node parsed, it will request the locale from the localStorage / cookie etc. It leads to execution of unnecessary logic that impacts the component reactivity.
131
+
132
+ > Note on paraglide: the solution injects code into your codebase for imports; as a result, the 'lib size' metric in the benchmark report is almost 0. Code generation is a good thing, because the function used will include only the necessary logic (prefix everywhere vs no prefix, cookie vs storage, etc.). In comparison, Intlayer performs this filtering via environment variable injections in the build to force the bundler to tree-shake the content depending on the logic. Thanks to this, paraglide and intlayer end up being 6 to 10 times lighter solutions than i18next or next-intl.
133
+
134
+ **(svelte-i18n)** (`svelte-i18n@3.4.0`):
135
+
136
+ This solution answers all i18n needs in a Svelte project. But as it is the case for i18next or other major i18n solutions, it is a bit heavy (~15.9kb, which is about 7× `svelte-intlayer`).
137
+
138
+ ### 3 — Recommendations
139
+
140
+ **(Intlayer)** (`svelte-intlayer@8.7.12`):
141
+
142
+ I will not personally judge `svelte-intlayer` for objectivity’s sake, since it is my own solution.
143
+
144
+ ### Personal note
145
+
146
+ This note is personal and does not affect the benchmark results. Still, in the i18n world you often see consensus around a pattern like `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>` for translated content.
147
+
148
+ In Svelte apps, injecting a function as a `Slot` is, in my view, an anti-pattern. It also adds avoidable complexity and JavaScript execution overhead (even if it is barely noticeable).
@@ -57,6 +57,13 @@ In practice, for the least optimised implementations, an internationalised page
57
57
 
58
58
  The other impact is on developer experience: how you declare content, types, namespace organisation, dynamic loading, and reactivity when the locale changes.
59
59
 
60
+ ## TL;DR
61
+
62
+ - **Intlayer**: Provides the best performance and smallest bundle size (v8.7.12) for TanStack Start.
63
+ - **react-i18next** & **use-intl**: Mature alternatives with large ecosystems, but significantly heavier and more complex to optimise.
64
+ - **Paraglide**: Innovative tree-shaking idea that does not work in practice. Complex DX and reactivity overhead in TanStack Start.
65
+ - **Avoid**: **General Translation (GT)** and **Lingo.dev** due to severe performance issues, AI quota limits, and vendor lock-in.
66
+
60
67
  ## Test your app
61
68
 
62
69
  To quickly spot i18n leakage issues, I set up a free scanner available [here](https://intlayer.org/i18n-seo-scanner).
@@ -87,12 +94,12 @@ Syntaxes built around `const t = useTranslation()` + `t('a.b.c')` are very conve
87
94
  For this benchmark, we compared the following libraries:
88
95
 
89
96
  - `Base App` (No i18n library)
90
- - `react-intlayer` (v8.7.5-canary.0)
97
+ - `react-intlayer` (v8.7.12)
91
98
  - `react-i18next` (v17.0.2)
92
99
  - `use-intl` (v4.9.1)
93
100
  - `@lingui/core` (v5.3.0)
94
101
  - `@inlang/paraglide-js` (v2.15.1)
95
- - `tolgee` (v7.0.0)
102
+ - `@tolgee/react` (v7.0.0)
96
103
  - `react-intl` (v10.1.1)
97
104
  - `wuchale` (v0.22.11)
98
105
  - `gt-react` (vlatest)
@@ -150,7 +157,9 @@ The idea behind `Wuchale` is interesting but not yet a viable solution. I hit re
150
157
 
151
158
  `Paraglide` offers an innovative, well-thought-out approach. Even so, in this benchmark the tree-shaking their company advertises did not work for my Next.js implementation or for TanStack Start. The workflow and DX are also more complex than other options. Personally I am not a fan of having to regenerate JS files before every push, which creates constant merge conflict risk for developers via PRs.
152
159
 
153
- **(Tolgee)** (`tolgee@7.0.0`):
160
+ > Note on paraglide: the solution injects code into your codebase for imports; as a result, the 'lib size' metric in the benchmark report is almost 0. Code generation is a good thing, because the function used will include only the necessary logic (prefix everywhere vs no prefix, cookie vs storage, etc.). In comparison, Intlayer performs this filtering via environment variable injections in the build to force the bundler to tree-shake the content depending on the logic. Thanks to this, paraglide and intlayer end up being 6 to 10 times lighter solutions than i18next or next-intl.
161
+
162
+ **(Tolgee)** (`@tolgee/react@7.0.0`):
154
163
 
155
164
  `Tolgee` addresses many of the issues mentioned earlier. I found it harder to get started with than other tools with similar approaches. It does not provide type safety, which also makes catching missing keys at compile time much harder. I had to wrap Tolgee’s APIs with my own to add missing-key detection.
156
165
 
@@ -0,0 +1,160 @@
1
+ ---
2
+ createdAt: 2026-04-20
3
+ updatedAt: 2026-04-21
4
+ title: Best i18n solution for Vue in 2026 - Benchmark Report
5
+ description: Compare Vue internationalisation (i18n) libraries like vue-i18n, fluent-vue, and Intlayer. Detailed performance report on bundle size, leakage, and reactivity.
6
+ keywords:
7
+ - benchmark
8
+ - i18n
9
+ - intl
10
+ - vue
11
+ - performance
12
+ - intlayer
13
+ slugs:
14
+ - doc
15
+ - benchmark
16
+ - vue
17
+ author: Aymeric PINEAU
18
+ applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-vue-template
19
+ history:
20
+ - version: 8.7.12
21
+ date: 2026-01-06
22
+ changes: "Init benchmark"
23
+ ---
24
+
25
+ # Vue i18n Libraries — 2026 Benchmark Report
26
+
27
+ This page is a benchmark report for i18n solutions on Vue.
28
+
29
+ ## Table of Contents
30
+
31
+ <Toc/>
32
+
33
+ ## Interactive Benchmark
34
+
35
+ <I18nBenchmark framework="vite-vue" vertical/>
36
+
37
+ ## Results reference:
38
+
39
+ <iframe
40
+ src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_vue.md"
41
+ width="100%"
42
+ height="600px"
43
+ style="border:none;">
44
+ </iframe>
45
+
46
+ > https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_vue.md
47
+
48
+ See complete benchmark repository [here](https://github.com/intlayer-org/benchmark-i18n/tree/main).
49
+
50
+ ## Introduction
51
+
52
+ Internationalisation solutions are among the heaviest dependencies in a Vue app. The main risk is shipping unnecessary content: translations for other pages and other locales in a single route’s bundle.
53
+
54
+ As your app grows, that problem can quickly blow up the JavaScript sent to the client and slow down navigation.
55
+
56
+ In practice, for the least optimised implementations, an internationalised page can end up several times heavier than the version without i18n.
57
+
58
+ The other impact is on developer experience: how you declare content, types, namespace organisation, dynamic loading, and reactivity when the locale changes.
59
+
60
+ ## TL;DR
61
+
62
+ - **Intlayer**: The lightest solution (v8.7.12) with built-in scoping and dynamic loading.
63
+ - **vue-i18n**: The industry standard with a rich ecosystem, but can become significantly heavier and harder to optimise for code-splitting in large applications.
64
+ - **fluent-vue**: Innovative message organisation but lacks type-safety and turns out to be an extremely heavy solution.
65
+
66
+ ## Test your app
67
+
68
+ To quickly spot i18n leakage issues, I set up a free scanner available [here](https://intlayer.org/i18n-seo-scanner).
69
+
70
+ <iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
71
+
72
+ ## The problem
73
+
74
+ Two levers are essential to limit the cost of a multilingual app:
75
+
76
+ - Split content by page / namespace so you do not load whole dictionaries when you do not need them
77
+ - Load the right locale dynamically, only when needed
78
+
79
+ Understanding the technical limitations of these approaches:
80
+
81
+ **Dynamic loading**
82
+
83
+ Without dynamic loading, most solutions keep messages in memory from the first render, which adds significant overhead for apps with many routes and locales.
84
+
85
+ With dynamic loading, you accept a trade-off: less initial JS, but sometimes an extra request when switching language.
86
+
87
+ **Content splitting**
88
+
89
+ Syntaxes built around `const { t } = useI18n()` + `t('a.b.c')` are very convenient but often encourage keeping large JSON objects at runtime. That model makes tree-shaking hard unless the library offers a real per-page split strategy.
90
+
91
+ ## Methodology
92
+
93
+ For this benchmark, we compared the following libraries:
94
+
95
+ - `Base App` (No i18n library)
96
+ - `vue-intlayer` (v8.7.12)
97
+ - `vue-i18n` (v11.4.0)
98
+ - `fluent-vue` (v3.8.2)
99
+
100
+ The framework is `Vue` with a multilingual app of **10 pages** and **10 languages**.
101
+
102
+ We compared **four loading strategies**:
103
+
104
+ | Strategy | No namespaces (global) | With namespaces (scoped) |
105
+ | :------------------ | :------------------------------------------- | :------------------------------------------------------------------- |
106
+ | **Static loading** | **Static**: Everything in memory at startup. | **Scoped static**: Split by namespace; everything loaded at startup. |
107
+ | **Dynamic loading** | **Dynamic**: On-demand loading per locale. | **Scoped dynamic**: Granular loading per namespace and locale. |
108
+
109
+ ## Strategy summary
110
+
111
+ - **Static**: Simple; no network latency after the initial load. Downside: large bundle size.
112
+ - **Dynamic**: Reduces initial weight (lazy-loading). Ideal when you have many locales.
113
+ - **Scoped static**: Keeps code organised (logical separation) without complex extra network requests.
114
+ - **Scoped dynamic**: Best approach for _code splitting_ and performance. Minimises memory by loading only what the current view and active locale need.
115
+
116
+ ### What I measured:
117
+
118
+ I ran the same multilingual app in a real browser for every stack, then wrote down what actually showed up on the wire and how long things took. Sizes are reported **after normal web compression**, because that is closer to what people download than raw source counts.
119
+
120
+ - **Internationalisation library size**: After bundling, tree-shaking and minification, the size of the i18n library is the size of the providers + composables code in an empty component. It does not include the loading of translation files. It answers how expensive the library is before your content enters the picture.
121
+
122
+ - **JavaScript per page**: For each benchmark route, how much script the browser pulls in for that visit, averaged across the pages in the suite (and across locales where the report rolls them up). Heavy pages are slow pages.
123
+
124
+ - **Leakage from other locales**: It's the content of the same page but in another language that would be loaded by mistake in the audited page. This content is unnecessary and should be avoided (e.g. `/fr/about` page content in `/en/about` page bundle).
125
+
126
+ - **Leakage from other routes**: The same idea for **other screens** in the app: whether their copy is riding along when you only opened one page (e.g. `/en/about` page content in `/en/contact` page bundle). A high score hints at weak splitting or over-broad bundles.
127
+
128
+ - **Average component bundle size**: Common UI pieces are measured **one at a time** instead of hiding inside one giant app number. It shows whether internationalisation quietly inflates everyday components. For instance, if your component rerenders, it will load all that data from memory. Attaching a giant JSON to any component is like connecting a big store of unused data that will slow down your components’ performance.
129
+
130
+ - **Language switch responsiveness**: I flip the language using the app’s own control and time how long it takes until the page has clearly switched, what a visitor would notice, not a lab micro-step.
131
+
132
+ - **Rendering work after a language change**: A narrower follow-up: how much effort the interface took to repaint for the new language once the switch is in flight. Useful when the “felt” time and the framework cost diverge.
133
+
134
+ - **Initial page load time**: From navigation to the browser considering the page fully loaded for the scenarios I tested. Good for comparing cold starts.
135
+
136
+ - **Hydration time**: When the app exposes it, how long the client spends turning server HTML into something you can actually click. A dash in the tables means that implementation did not provide a reliable hydration figure in this benchmark.
137
+
138
+ ## Results in detail
139
+
140
+ ### 1 — Solutions to avoid
141
+
142
+ > No clear solution to avoid in vue ecosystem.
143
+
144
+ ### 2 — Acceptable solutions
145
+
146
+ **(vue-i18n)** (`vue-i18n@11.4.0`):
147
+
148
+ - **vue-i18n** is without contestation the most used i18n library for vue, it has a lot of features and a huge ecosystem. But under the hood the solution is quite heavy. Even if vue-i18n integrates lazy loading for messages, it misses a scoping feature. In the case of a classic Vue SPA app there is no issue, but for a nuxt app, using @nuxt/i18n, it leads to including the messages from all pages into a single one. For a big nuxt app including more than 10 pages, it can become really problematic.
149
+
150
+ The package is very heavy (~24.3kb, which is about 9× `vue-intlayer`).
151
+
152
+ **(fluent-vue)** (`fluent-vue@0.5.0`):
153
+
154
+ - **fluent-vue** offer one innovation attempt through the .ftl format. The message organisation is great, easier to get started. But in practice, the lack of typesafety increases the risk of error and can quickly become time consuming to debug. Moreover, that solution load the messages using a vite plugin that force the loading of all the content in all languages into each page. Additionally this is an extremely heavy solution (~92.7kb, which is about 34× `vue-intlayer`).
155
+
156
+ ### 3 — Recommendations
157
+
158
+ **(Intlayer)** (`vue-intlayer@8.7.12`):
159
+
160
+ I will not personally judge `vue-intlayer` for objectivity’s sake, since it is my own solution.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2024-08-13
3
- updatedAt: 2026-04-08
3
+ updatedAt: 2026-05-12
4
4
  title: Configuration
5
5
  description: Learn how to configure Intlayer for your application. Understand the various settings and options available to customize Intlayer to your needs.
6
6
  keywords:
@@ -14,6 +14,9 @@ slugs:
14
14
  - concept
15
15
  - configuration
16
16
  history:
17
+ - version: 8.9.4
18
+ date: 2026-05-12
19
+ changes: "Add support for LM Studio provider"
17
20
  - version: 8.7.0
18
21
  date: 2026-04-08
19
22
  changes: "Add `prune` and `minify` options to the build configuration"
@@ -350,7 +353,7 @@ const config: IntlayerConfig = {
350
353
  ai: {
351
354
  /**
352
355
  * AI provider to use.
353
- * Options: 'openai', 'anthropic', 'mistral', 'deepseek', 'gemini', 'ollama', 'openrouter', 'alibaba', 'fireworks', 'groq', 'huggingface', 'bedrock', 'googlevertex', 'togetherai'
356
+ * Options: 'openai', 'anthropic', 'mistral', 'deepseek', 'gemini', 'ollama', 'openrouter', 'alibaba', 'fireworks', 'groq', 'huggingface', 'bedrock', 'googlevertex', 'togetherai', 'lmstudio'
354
357
  * Default: 'openai'
355
358
  */
356
359
  provider: "openai",
@@ -937,17 +940,18 @@ Intlayer supports multiple AI providers for enhanced flexibility and choice. Cur
937
940
  - **Groq**
938
941
  - **Amazon Bedrock**
939
942
  - **Together.ai**
943
+ - **LM Studio**
940
944
  - **ollama**
941
945
 
942
- | Field | Description | Type | Default | Example | Note |
943
- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
944
- | `provider` | The provider to use for the AI features of Intlayer. | `'openai'` &#124; <br/> `'anthropic'` &#124; <br/> `'mistral'` &#124; <br/> `'deepseek'` &#124; <br/> `'gemini'` &#124; <br/> `'ollama'` &#124; <br/> `'openrouter'` &#124; <br/> `'alibaba'` &#124; <br/> `'fireworks'` &#124; <br/> `'groq'` &#124; <br/> `'huggingface'` &#124; <br/> `'bedrock'` &#124; <br/> `'googleaistudio'` &#124; <br/> `'googlevertex'` &#124; <br/> `'togetherai'` | `undefined` | `'anthropic'` | Different providers require different API keys and have different pricing. |
945
- | `model` | The model to use for AI features. | `string` | None | `'gpt-4o-2024-11-20'` | Specific model varies by provider. |
946
- | `temperature` | Controls the randomness of AI responses. | `number` | None | `0.1` | Higher temperature = more creative and less predictable. |
947
- | `apiKey` | Your API key for the selected provider. | `string` | None | `process.env.OPENAI_API_KEY` | Keep secret; store in environment variables. |
948
- | `applicationContext` | Additional context about your application to help the AI generate more accurate translations (domain, audience, tone, terminology). | `string` | None | `'My application context'` | Can be used to add rules (e.g. `"You should not transform urls"`). |
949
- | `baseURL` | The base URL for the AI API. | `string` | None | `'https://api.openai.com/v1'` <br/> `'http://localhost:5000'` | Can point to a local or custom AI API endpoint. |
950
- | `dataSerialization` | Data serialization format for AI features. | `'json'` &#124; <br/> `'toon'` | `undefined` | `'toon'` | • `'json'`: standard, reliable; uses more tokens.<br/>• `'toon'`: fewer tokens, less consistent.<br/>• Additional parameters are passed to the AI model as context (reasoning effort, verbosity, etc.). |
946
+ | Field | Description | Type | Default | Example | Note |
947
+ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
948
+ | `provider` | The provider to use for the AI features of Intlayer. | `'openai'` &#124; <br/> `'anthropic'` &#124; <br/> `'mistral'` &#124; <br/> `'deepseek'` &#124; <br/> `'gemini'` &#124; <br/> `'ollama'` &#124; <br/> `'openrouter'` &#124; <br/> `'alibaba'` &#124; <br/> `'fireworks'` &#124; <br/> `'groq'` &#124; <br/> `'huggingface'` &#124; <br/> `'bedrock'` &#124; <br/> `'googleaistudio'` &#124; <br/> `'googlevertex'` &#124; <br/> `'togetherai'` &#124; <br/> `'lmstudio'` | `undefined` | `'anthropic'` | Different providers require different API keys and have different pricing. |
949
+ | `model` | The model to use for AI features. | `string` | None | `'gpt-4o-2024-11-20'` | Specific model varies by provider. |
950
+ | `temperature` | Controls the randomness of AI responses. | `number` | None | `0.1` | Higher temperature = more creative and less predictable. |
951
+ | `apiKey` | Your API key for the selected provider. | `string` | None | `process.env.OPENAI_API_KEY` | Keep secret; store in environment variables. |
952
+ | `applicationContext` | Additional context about your application to help the AI generate more accurate translations (domain, audience, tone, terminology). | `string` | None | `'My application context'` | Can be used to add rules (e.g. `"You should not transform urls"`). |
953
+ | `baseURL` | The base URL for the AI API. | `string` | None | `'https://api.openai.com/v1'` <br/> `'http://localhost:5000'` | Can point to a local or custom AI API endpoint. |
954
+ | `dataSerialization` | Data serialization format for AI features. | `'json'` &#124; <br/> `'toon'` | `undefined` | `'toon'` | • `'json'`: standard, reliable; uses more tokens.<br/>• `'toon'`: fewer tokens, less consistent.<br/>• Additional parameters are passed to the AI model as context (reasoning effort, verbosity, etc.). |
951
955
 
952
956
  ### Build Configuration
953
957
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2025-02-07
3
- updatedAt: 2026-01-28
3
+ updatedAt: 2026-05-12
4
4
  title: Content File
5
5
  description: Learn how to customise the extensions for your content declaration files. Follow this documentation to implement conditions efficiently in your project.
6
6
  keywords:
@@ -12,6 +12,9 @@ slugs:
12
12
  - concept
13
13
  - content
14
14
  history:
15
+ - version: 8.9.0
16
+ date: 2026-05-12
17
+ changes: "Add `plural` content node type"
15
18
  - version: 8.0.0
16
19
  date: 2026-01-28
17
20
  changes: "Add `html` content node type"
@@ -63,6 +66,7 @@ import { type ReactNode } from "react";
63
66
  import {
64
67
  t,
65
68
  enu,
69
+ plural,
66
70
  cond,
67
71
  nest,
68
72
  md,
@@ -82,6 +86,7 @@ interface Content {
82
86
  };
83
87
  multilingualContent: string;
84
88
  quantityContent: string;
89
+ pluralContent: string;
85
90
  conditionalContent: string;
86
91
  markdownContent: never;
87
92
  htmlContent: never;
@@ -117,6 +122,10 @@ export default {
117
122
  ">5": "Some cars",
118
123
  ">19": "Many cars",
119
124
  }),
125
+ pluralContent: plural({
126
+ one: "One car",
127
+ other: "{{count}} cars",
128
+ }),
120
129
  conditionalContent: cond({
121
130
  true: "Validation is enabled",
122
131
  false: "Validation is disabled",
@@ -171,6 +180,13 @@ export default {
171
180
  ">5": "Some cars",
172
181
  ">19": "Many cars",
173
182
  },
183
+ "pluralContent": {
184
+ "nodeType": "plural",
185
+ "plural": {
186
+ "one": "One car",
187
+ "other": "{{count}} cars",
188
+ },
189
+ },
174
190
  },
175
191
  "conditionalContent": {
176
192
  "nodeType": "condition",
@@ -218,6 +234,7 @@ Content nodes are the building blocks of dictionary content. They can be:
218
234
  - **Primitive values**: strings, numbers, booleans, null, undefined
219
235
  - **Typed nodes**: Special content types such as translations, conditions, markdown, etc.
220
236
  - **Functions**: Dynamic content that can be evaluated at runtime [see Function Fetching](https://github.com/aymericzip/intlayer/blob/main/docs/docs/{{locale}}/dictionary/function_fetching.md)
237
+ - **Plural Content**: See Plural Content [See Plural Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/plural.md)
221
238
  - **Nested content**: References to other dictionaries
222
239
 
223
240
  #### Content Types
@@ -543,6 +560,8 @@ multilingualContent: t({
543
560
  });
544
561
  ```
545
562
 
563
+ > See [Translation Content (`t`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/translation.md) for more information.
564
+
546
565
  ### Condition Content (`cond`)
547
566
 
548
567
  Content that changes based on boolean conditions:
@@ -556,6 +575,8 @@ conditionalContent: cond({
556
575
  });
557
576
  ```
558
577
 
578
+ > See [Condition Content (`cond`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/condition.md) for more information.
579
+
559
580
  ### Enumeration Content (`enu`)
560
581
 
561
582
  Content that varies based on enumerated values:
@@ -570,6 +591,23 @@ statusContent: enu({
570
591
  });
571
592
  ```
572
593
 
594
+ > See [Enumeration Content (`enu`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/enumeration.md) for more information.
595
+
596
+ ### Plural Content (`plural`)
597
+
598
+ Content that varies based on plural rules:
599
+
600
+ ```typescript
601
+ import { plural } from "intlayer";
602
+
603
+ pluralContent: plural({
604
+ one: "One car",
605
+ other: "{{count}} cars",
606
+ });
607
+ ```
608
+
609
+ > See [Plural Content Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/plural.md) for more information.
610
+
573
611
  ### Insertion Content (`insert`)
574
612
 
575
613
  Content that can be inserted into other content:
@@ -580,6 +618,8 @@ import { insert } from "intlayer";
580
618
  insertionContent: insert("This text can be inserted anywhere");
581
619
  ```
582
620
 
621
+ > See [Insertion Content (`insert`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/insertion.md) for more information.
622
+
583
623
  ### Nested Content (`nest`)
584
624
 
585
625
  References to other dictionaries:
@@ -590,6 +630,8 @@ import { nest } from "intlayer";
590
630
  nestedContent: nest("about-page");
591
631
  ```
592
632
 
633
+ > See [Nested Content (`nest`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/nesting.md) for more information.
634
+
593
635
  ### Markdown Content (`md`)
594
636
 
595
637
  Rich text content in Markdown format:
@@ -602,6 +644,8 @@ markdownContent: md(
602
644
  );
603
645
  ```
604
646
 
647
+ > See [Markdown Content (`md`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/markdown.md) for more information.
648
+
605
649
  ### HTML Content (`html`)
606
650
 
607
651
  Rich HTML content that can use standard tags or custom components:
@@ -619,6 +663,8 @@ localizedHtmlContent: t({
619
663
  });
620
664
  ```
621
665
 
666
+ > See [HTML Content (`html`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/html.md) for more information.
667
+
622
668
  ### Gender Content (`gender`)
623
669
 
624
670
  Content that varies based on gender:
@@ -633,6 +679,8 @@ genderContent: gender({
633
679
  });
634
680
  ```
635
681
 
682
+ > See [Gender Content (`gender`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/gender.md) for more information.
683
+
636
684
  ### File Content (`file`)
637
685
 
638
686
  References to external files:
@@ -643,6 +691,8 @@ import { file } from "intlayer";
643
691
  fileContent: file("./path/to/content.txt");
644
692
  ```
645
693
 
694
+ > See [File Content (`file`) Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en-GB/dictionary/file.md) for more information.
695
+
646
696
  ## Creating Content Files
647
697
 
648
698
  ### Basic Content File Structure