@intlayer/docs 8.9.4 → 8.9.6-canary.0

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 (182) hide show
  1. package/docs/ar/benchmark/index.md +0 -3
  2. package/docs/ar/benchmark/nextjs.md +15 -6
  3. package/docs/ar/benchmark/solid.md +155 -0
  4. package/docs/ar/benchmark/svelte.md +148 -0
  5. package/docs/ar/benchmark/tanstack.md +12 -3
  6. package/docs/ar/benchmark/vue.md +160 -0
  7. package/docs/ar/configuration.md +16 -12
  8. package/docs/ar/dictionary/content_file.md +51 -1
  9. package/docs/ar/plugins/sync-po.md +0 -21
  10. package/docs/bn/configuration.md +16 -12
  11. package/docs/cs/configuration.md +16 -12
  12. package/docs/de/benchmark/index.md +0 -3
  13. package/docs/de/benchmark/nextjs.md +15 -6
  14. package/docs/de/benchmark/solid.md +155 -0
  15. package/docs/de/benchmark/svelte.md +148 -0
  16. package/docs/de/benchmark/tanstack.md +12 -3
  17. package/docs/de/benchmark/vue.md +160 -0
  18. package/docs/de/configuration.md +16 -12
  19. package/docs/de/dictionary/content_file.md +52 -2
  20. package/docs/de/plugins/sync-po.md +0 -22
  21. package/docs/en/benchmark/nextjs.md +11 -2
  22. package/docs/en/benchmark/solid.md +22 -4
  23. package/docs/en/benchmark/svelte.md +17 -5
  24. package/docs/en/benchmark/tanstack.md +18 -3
  25. package/docs/en/benchmark/vue.md +17 -11
  26. package/docs/en/configuration.md +16 -13
  27. package/docs/en/dictionary/content_file.md +51 -1
  28. package/docs/en/plugins/sync-po.md +0 -21
  29. package/docs/en-GB/benchmark/index.md +0 -3
  30. package/docs/en-GB/benchmark/nextjs.md +15 -6
  31. package/docs/en-GB/benchmark/solid.md +155 -0
  32. package/docs/en-GB/benchmark/svelte.md +148 -0
  33. package/docs/en-GB/benchmark/tanstack.md +12 -3
  34. package/docs/en-GB/benchmark/vue.md +160 -0
  35. package/docs/en-GB/configuration.md +15 -11
  36. package/docs/en-GB/dictionary/content_file.md +51 -1
  37. package/docs/en-GB/plugins/sync-po.md +0 -21
  38. package/docs/es/benchmark/index.md +0 -3
  39. package/docs/es/benchmark/nextjs.md +15 -6
  40. package/docs/es/benchmark/solid.md +155 -0
  41. package/docs/es/benchmark/svelte.md +148 -0
  42. package/docs/es/benchmark/tanstack.md +12 -3
  43. package/docs/es/benchmark/vue.md +160 -0
  44. package/docs/es/configuration.md +16 -12
  45. package/docs/es/dictionary/content_file.md +51 -1
  46. package/docs/es/plugins/sync-po.md +0 -21
  47. package/docs/fr/benchmark/index.md +0 -3
  48. package/docs/fr/benchmark/nextjs.md +15 -6
  49. package/docs/fr/benchmark/solid.md +155 -0
  50. package/docs/fr/benchmark/svelte.md +148 -0
  51. package/docs/fr/benchmark/tanstack.md +12 -3
  52. package/docs/fr/benchmark/vue.md +160 -0
  53. package/docs/fr/configuration.md +16 -12
  54. package/docs/fr/dictionary/content_file.md +51 -1
  55. package/docs/fr/plugins/sync-po.md +0 -21
  56. package/docs/hi/benchmark/nextjs.md +15 -6
  57. package/docs/hi/benchmark/solid.md +155 -0
  58. package/docs/hi/benchmark/svelte.md +148 -0
  59. package/docs/hi/benchmark/tanstack.md +12 -3
  60. package/docs/hi/benchmark/vue.md +160 -0
  61. package/docs/hi/configuration.md +16 -12
  62. package/docs/hi/dictionary/content_file.md +51 -1
  63. package/docs/hi/plugins/sync-po.md +0 -21
  64. package/docs/id/benchmark/index.md +0 -3
  65. package/docs/id/benchmark/nextjs.md +15 -6
  66. package/docs/id/benchmark/solid.md +155 -0
  67. package/docs/id/benchmark/svelte.md +148 -0
  68. package/docs/id/benchmark/tanstack.md +12 -3
  69. package/docs/id/benchmark/vue.md +160 -0
  70. package/docs/id/configuration.md +16 -12
  71. package/docs/id/dictionary/content_file.md +51 -1
  72. package/docs/id/plugins/sync-po.md +0 -21
  73. package/docs/it/benchmark/index.md +1 -4
  74. package/docs/it/benchmark/nextjs.md +15 -6
  75. package/docs/it/benchmark/solid.md +155 -0
  76. package/docs/it/benchmark/svelte.md +148 -0
  77. package/docs/it/benchmark/tanstack.md +12 -3
  78. package/docs/it/benchmark/vue.md +160 -0
  79. package/docs/it/configuration.md +16 -12
  80. package/docs/it/dictionary/content_file.md +51 -1
  81. package/docs/it/plugins/sync-po.md +0 -21
  82. package/docs/ja/benchmark/index.md +5 -5
  83. package/docs/ja/benchmark/nextjs.md +15 -6
  84. package/docs/ja/benchmark/solid.md +155 -0
  85. package/docs/ja/benchmark/svelte.md +148 -0
  86. package/docs/ja/benchmark/tanstack.md +12 -3
  87. package/docs/ja/benchmark/vue.md +160 -0
  88. package/docs/ja/configuration.md +16 -12
  89. package/docs/ja/dictionary/content_file.md +50 -2
  90. package/docs/ja/intlayer_with_nextjs_no_locale_path.md +4 -3
  91. package/docs/ja/plugins/sync-po.md +0 -21
  92. package/docs/ko/benchmark/nextjs.md +15 -6
  93. package/docs/ko/benchmark/solid.md +155 -0
  94. package/docs/ko/benchmark/svelte.md +148 -0
  95. package/docs/ko/benchmark/tanstack.md +12 -3
  96. package/docs/ko/benchmark/vue.md +160 -0
  97. package/docs/ko/configuration.md +16 -12
  98. package/docs/ko/dictionary/content_file.md +51 -1
  99. package/docs/ko/intlayer_with_nextjs_no_locale_path.md +3 -2
  100. package/docs/ko/plugins/sync-po.md +0 -21
  101. package/docs/nl/configuration.md +16 -12
  102. package/docs/pl/benchmark/index.md +0 -3
  103. package/docs/pl/benchmark/nextjs.md +15 -6
  104. package/docs/pl/benchmark/solid.md +155 -0
  105. package/docs/pl/benchmark/svelte.md +148 -0
  106. package/docs/pl/benchmark/tanstack.md +12 -3
  107. package/docs/pl/benchmark/vue.md +160 -0
  108. package/docs/pl/configuration.md +16 -12
  109. package/docs/pl/dictionary/content_file.md +51 -1
  110. package/docs/pl/plugins/sync-po.md +0 -21
  111. package/docs/pt/benchmark/index.md +0 -3
  112. package/docs/pt/benchmark/nextjs.md +16 -7
  113. package/docs/pt/benchmark/solid.md +155 -0
  114. package/docs/pt/benchmark/svelte.md +148 -0
  115. package/docs/pt/benchmark/tanstack.md +13 -4
  116. package/docs/pt/benchmark/vue.md +160 -0
  117. package/docs/pt/configuration.md +16 -12
  118. package/docs/pt/dictionary/content_file.md +51 -1
  119. package/docs/pt/plugins/sync-po.md +0 -21
  120. package/docs/ru/benchmark/nextjs.md +15 -6
  121. package/docs/ru/benchmark/solid.md +155 -0
  122. package/docs/ru/benchmark/svelte.md +148 -0
  123. package/docs/ru/benchmark/tanstack.md +12 -3
  124. package/docs/ru/benchmark/vue.md +160 -0
  125. package/docs/ru/configuration.md +16 -12
  126. package/docs/ru/dictionary/content_file.md +52 -2
  127. package/docs/ru/plugins/sync-po.md +0 -21
  128. package/docs/tr/benchmark/index.md +0 -3
  129. package/docs/tr/benchmark/nextjs.md +15 -6
  130. package/docs/tr/benchmark/solid.md +155 -0
  131. package/docs/tr/benchmark/svelte.md +148 -0
  132. package/docs/tr/benchmark/tanstack.md +12 -3
  133. package/docs/tr/benchmark/vue.md +160 -0
  134. package/docs/tr/configuration.md +16 -12
  135. package/docs/tr/dictionary/content_file.md +51 -1
  136. package/docs/tr/plugins/sync-po.md +0 -21
  137. package/docs/uk/benchmark/nextjs.md +15 -6
  138. package/docs/uk/benchmark/solid.md +155 -0
  139. package/docs/uk/benchmark/svelte.md +148 -0
  140. package/docs/uk/benchmark/tanstack.md +12 -3
  141. package/docs/uk/benchmark/vue.md +160 -0
  142. package/docs/uk/configuration.md +16 -12
  143. package/docs/uk/dictionary/content_file.md +51 -1
  144. package/docs/uk/plugins/sync-po.md +0 -21
  145. package/docs/ur/configuration.md +16 -12
  146. package/docs/vi/benchmark/index.md +0 -3
  147. package/docs/vi/benchmark/nextjs.md +15 -6
  148. package/docs/vi/benchmark/solid.md +155 -0
  149. package/docs/vi/benchmark/svelte.md +148 -0
  150. package/docs/vi/benchmark/tanstack.md +12 -3
  151. package/docs/vi/benchmark/vue.md +160 -0
  152. package/docs/vi/configuration.md +16 -12
  153. package/docs/vi/dictionary/content_file.md +51 -1
  154. package/docs/vi/intlayer_with_nextjs_15.md +10 -57
  155. package/docs/vi/plugins/sync-po.md +0 -21
  156. package/docs/zh/benchmark/nextjs.md +15 -6
  157. package/docs/zh/benchmark/solid.md +155 -0
  158. package/docs/zh/benchmark/svelte.md +148 -0
  159. package/docs/zh/benchmark/tanstack.md +12 -3
  160. package/docs/zh/benchmark/vue.md +160 -0
  161. package/docs/zh/configuration.md +16 -12
  162. package/docs/zh/dictionary/content_file.md +51 -3
  163. package/docs/zh/plugins/sync-po.md +0 -21
  164. package/frequent_questions/ar/intlayerNode.md +3 -3
  165. package/frequent_questions/de/intlayerNode.md +3 -3
  166. package/frequent_questions/en/intlayerNode.md +3 -3
  167. package/frequent_questions/en-GB/intlayerNode.md +3 -3
  168. package/frequent_questions/es/intlayerNode.md +3 -3
  169. package/frequent_questions/fr/intlayerNode.md +3 -3
  170. package/frequent_questions/hi/intlayerNode.md +3 -3
  171. package/frequent_questions/id/intlayerNode.md +3 -3
  172. package/frequent_questions/it/intlayerNode.md +3 -3
  173. package/frequent_questions/ja/intlayerNode.md +3 -3
  174. package/frequent_questions/ko/intlayerNode.md +3 -3
  175. package/frequent_questions/pl/intlayerNode.md +3 -3
  176. package/frequent_questions/pt/intlayerNode.md +3 -3
  177. package/frequent_questions/ru/intlayerNode.md +3 -3
  178. package/frequent_questions/tr/intlayerNode.md +3 -3
  179. package/frequent_questions/uk/intlayerNode.md +3 -3
  180. package/frequent_questions/vi/intlayerNode.md +3 -3
  181. package/frequent_questions/zh/intlayerNode.md +3 -3
  182. package/package.json +8 -8
@@ -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 customize 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"
@@ -69,6 +72,7 @@ import { type ReactNode } from "react";
69
72
  import {
70
73
  t,
71
74
  enu,
75
+ plural,
72
76
  cond,
73
77
  nest,
74
78
  md,
@@ -88,6 +92,7 @@ interface Content {
88
92
  };
89
93
  multilingualContent: string;
90
94
  quantityContent: string;
95
+ pluralContent: string;
91
96
  conditionalContent: string;
92
97
  markdownContent: never;
93
98
  htmlContent: never;
@@ -123,6 +128,10 @@ export default {
123
128
  ">5": "Some cars",
124
129
  ">19": "Many cars",
125
130
  }),
131
+ pluralContent: plural({
132
+ one: "One car",
133
+ other: "{{count}} cars",
134
+ }),
126
135
  conditionalContent: cond({
127
136
  true: "Validation is enabled",
128
137
  false: "Validation is disabled",
@@ -178,6 +187,13 @@ export default {
178
187
  ">19": "Many cars",
179
188
  },
180
189
  },
190
+ "pluralContent": {
191
+ "nodeType": "plural",
192
+ "plural": {
193
+ "one": "One car",
194
+ "other": "{{count}} cars",
195
+ },
196
+ },
181
197
  "conditionalContent": {
182
198
  "nodeType": "condition",
183
199
  "condition": {
@@ -233,6 +249,7 @@ Intlayer supports various content types through typed nodes:
233
249
  - **Translation Content**: Multilingual text with locale-specific values [see Translation Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/translation_content.md)
234
250
  - **Condition Content**: Conditional content based on boolean expressions [see Condition Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/condition_content.md)
235
251
  - **Enumeration Content**: Content that varies based on enumerated values [see Enumeration Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/enumeration_content.md)
252
+ - **Plural Content**: Content that varies based on plural rules [see Plural Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/plural.md)
236
253
  - **Insertion Content**: Content that can be inserted into other content [see Insertion Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/insertion_content.md)
237
254
  - **Markdown Content**: Rich text content in Markdown format [see Markdown Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/markdown_content.md)
238
255
  - **HTML Content**: Rich HTML content with optional custom components [see HTML Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/html.md)
@@ -547,6 +564,8 @@ multilingualContent: t({
547
564
  });
548
565
  ```
549
566
 
567
+ > See [Translation Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/translation.md) for more information.
568
+
550
569
  ### Condition Content (`cond`)
551
570
 
552
571
  Content that changes based on boolean conditions:
@@ -560,6 +579,23 @@ conditionalContent: cond({
560
579
  });
561
580
  ```
562
581
 
582
+ > See [Condition Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/condition.md) for more information.
583
+
584
+ ### Plural Content (`plural`)
585
+
586
+ Content that varies based on plural rules:
587
+
588
+ ```typescript
589
+ import { plural } from "intlayer";
590
+
591
+ pluralContent: plural({
592
+ one: "One car",
593
+ other: "{{count}} cars",
594
+ });
595
+ ```
596
+
597
+ > See [Plural Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/plural.md) for more information.
598
+
563
599
  ### Enumeration Content (`enu`)
564
600
 
565
601
  Content that varies based on enumerated values:
@@ -574,6 +610,8 @@ statusContent: enu({
574
610
  });
575
611
  ```
576
612
 
613
+ > See [Enumeration Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/enumeration.md) for more information.
614
+
577
615
  ### Insertion Content (`insert`)
578
616
 
579
617
  Content that can be inserted into other content:
@@ -584,6 +622,8 @@ import { insert } from "intlayer";
584
622
  insertionContent: insert("This text can be inserted anywhere");
585
623
  ```
586
624
 
625
+ > See [Insertion Doc](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/insertion.md) for more information.
626
+
587
627
  ### Nested Content (`nest`)
588
628
 
589
629
  References to other dictionaries:
@@ -594,6 +634,8 @@ import { nest } from "intlayer";
594
634
  nestedContent: nest("about-page");
595
635
  ```
596
636
 
637
+ > See [Nested Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/nesting.md) for more information.
638
+
597
639
  ### Markdown Content (`md`)
598
640
 
599
641
  Rich text content in Markdown format:
@@ -611,6 +653,8 @@ localizedMarkdownContent: t({
611
653
  });
612
654
  ```
613
655
 
656
+ > See [Markdown Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/markdown.md) for more information.
657
+
614
658
  ### HTML Content (`html`)
615
659
 
616
660
  Rich HTML content that can use standard tags or custom components:
@@ -628,6 +672,8 @@ localizedHtmlContent: t({
628
672
  });
629
673
  ```
630
674
 
675
+ > See [HTML Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/html.md) for more information.
676
+
631
677
  ### Gender Content (`gender`)
632
678
 
633
679
  Content that varies based on gender:
@@ -642,6 +688,8 @@ genderContent: gender({
642
688
  });
643
689
  ```
644
690
 
691
+ > See [Gender Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/gender.md) for more information.
692
+
645
693
  ### File Content (`file`)
646
694
 
647
695
  References to external files:
@@ -652,6 +700,8 @@ import { file } from "intlayer";
652
700
  fileContent: file("./path/to/content.txt");
653
701
  ```
654
702
 
703
+ > See [File Content](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/file.md) for more information.
704
+
655
705
  ## Creating Content Files
656
706
 
657
707
  ### Basic Content File Structure
@@ -160,30 +160,9 @@ syncPO({
160
160
  source: ({ key, locale }) => string, // required
161
161
  location?: string, // optional label, default: "sync-po::path/to/source"
162
162
  priority?: number, // optional priority for conflict resolution, default: 0
163
- format?: 'icu' | 'i18next' | 'vue-i18n', // optional, only needed when your msgstr values use a specific interpolation syntax
164
163
  });
165
164
  ```
166
165
 
167
- #### `format` ('icu' | 'i18next' | 'vue-i18n')
168
-
169
- PO files are always Gettext Portable Object files — that is fixed. This option only describes the **interpolation syntax** used inside the `msgstr` values, so Intlayer can convert them to its own format at parse time (via `formatDictionary`) and back when writing output.
170
-
171
- - `undefined` _(default)_: `msgstr` values are treated as plain strings — no transformation. Use this for most PO files.
172
- - `'icu'`: `msgstr` values use ICU message syntax (e.g. `{count, plural, one {# item} other {# items}}`).
173
- - `'i18next'`: `msgstr` values use i18next interpolation syntax (e.g. `{{variable}}`).
174
- - `'vue-i18n'`: `msgstr` values use Vue I18n syntax.
175
-
176
- > Transformation is applied by `@intlayer/chokidar`'s `formatDictionary` on load, and reversed with `formatDictionaryOutput` on write. For complex rules like ICU plurals, round-trip fidelity is not guaranteed.
177
-
178
- **Example — PO files contain i18next-style interpolation:**
179
-
180
- ```ts
181
- syncPO({
182
- source: ({ key, locale }) => `./locales/${locale}/${key}.po`,
183
- format: "i18next",
184
- }),
185
- ```
186
-
187
166
  ### Multiple PO sources and priority
188
167
 
189
168
  You can add multiple `syncPO` plugins to synchronize different PO sources. This is useful when you have multiple translation sources or different PO structures in your project.
@@ -30,6 +30,3 @@ Find the detailed reports and technical documentation for each framework below:
30
30
  - [**Vue Benchmark Report**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/benchmark/vue.md)
31
31
  - [**Solid Benchmark Report**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/benchmark/solid.md)
32
32
  - [**Svelte Benchmark Report**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/benchmark/svelte.md)
33
- - [**Vue Benchmark Report**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/benchmark/vue.md)
34
- - [**Solid Benchmark Report**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/benchmark/solid.md)
35
- - [**Svelte Benchmark Report**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/benchmark/svelte.md)
@@ -61,6 +61,13 @@ Because the problem is hard, many solutions exist—some focused on DX, others o
61
61
 
62
62
  Intlayer tries to optimise across these dimensions.
63
63
 
64
+ ## TL;DR
65
+
66
+ - **Intlayer** & **next-translate**: Best choices for Next.js performance, offering the smallest footprint and best static rendering support.
67
+ - **next-intl**: The trendiest option, but heavy and complex to optimise for large applications.
68
+ - **next-i18next**: Popular and plugin-rich, but carries significant bundle weight (~3× Intlayer).
69
+ - **Avoid**: **gt-next** and **lingo.dev** due to severe performance issues, vendor lock-in, and build-breaking bugs.
70
+
64
71
  ## Test your app
65
72
 
66
73
  To surface these issues, I built a free scanner you can try [here](https://intlayer.org/i18n-seo-scanner).
@@ -99,14 +106,14 @@ Finally, `Intlayer` applies a build-time optimisation so `useIntlayer('my-key')`
99
106
  For this benchmark, we compared the following libraries:
100
107
 
101
108
  - `Base App` (No i18n library)
102
- - `next-intlayer` (v8.7.5)
109
+ - `next-intlayer` (v8.7.12)
103
110
  - `next-i18next` (v16.0.5)
104
111
  - `next-intl` (v4.9.1)
105
112
  - `@lingui/core` (v5.3.0)
106
113
  - `next-translate` (v3.1.2)
107
114
  - `next-international` (v1.3.1)
108
115
  - `@inlang/paraglide-js` (v2.15.1)
109
- - `tolgee` (v7.0.0)
116
+ - `@tolgee/react` (v7.0.0)
110
117
  - `@lingo.dev/compiler` (v0.4.0)
111
118
  - `wuchale` (v0.22.11)
112
119
  - `gt-next` (v6.16.5)
@@ -161,10 +168,10 @@ Issues encountered:
161
168
 
162
169
  **(General Translation)** (`gt-next@6.16.5`):
163
170
 
164
- - For a 110kb app, `gt-react` adds more than 440kb extra.
171
+ - For a 110kb app, `gt-next` adds more than 440kb extra.
165
172
  - `Quota Exceeded, please upgrade your plan` on the very first build with General Translation.
166
173
  - Translations are not rendered; I get the error `Error: <T> used on the client-side outside of <GTProvider>`, which seems to be a bug in the library.
167
- - While implementing **gt-tanstack-start-react**, I also came across an [issue](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) with the library: `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, which was making the application break. After reporting this issue, the maintainer fixed it within 24 hours.
174
+ - While implementing **gt-next**, I also came across an [issue](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) with the library: `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, which was making the application break. After reporting this issue, the maintainer fixed it within 24 hours.
168
175
  - The library blocks static rendering of Next.js pages.
169
176
 
170
177
  **(Lingo.dev)** (`@lingo.dev/compiler@0.4.0`):
@@ -187,9 +194,11 @@ Personally I dislike having to regenerate JS files before every push, which crea
187
194
  Even if in theory the tree-shaking strategy works, it does includes all locales in the bundle anyway. Paraglide offers not way to lazy-load the content. That says your page size will grow up correlated to the number of locales you have.
188
195
  Finally, in comparison of other solutions, Paraglide does not use store (e.g. React context) to retrieve the current locale to render the content. For each node parsed, it will request the locale from the localeStorage / cookie etc. It leads to execution of unnecessary logic that impact the component reactivity.
189
196
 
197
+ > Note on paraglide: the solution injects code into your codebase for import, as a result the 'lib size' metric in the benchmark report is nearly 0. Code generation is a good thing, because the function used will include only the necessary logic (all prefixes vs no prefixes, cookies vs storage, etc.). In comparison, Intlayer performs this filtering via environment variable injections in the build to force the bundler to tree-shake 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.
198
+
190
199
  ### 3 — Acceptable solutions
191
200
 
192
- **(Tolgee)** (`tolgee@7.0.0`):
201
+ **(Tolgee)** (`@tolgee/react@7.0.0`):
193
202
 
194
203
  `Tolgee` addresses many of the issues mentioned earlier. I found it harder to adopt than similar tools. It does not provide type safety, which also makes catching missing keys at compile time harder. I had to wrap Tolgee’s functions with my own to add missing-key detection.
195
204
 
@@ -217,7 +226,7 @@ Message formats also differ: `next-intl` uses ICU MessageFormat, while `i18next`
217
226
 
218
227
  `next-translate` is my main recommendation if you like a `t()`-style API. It is elegant via `next-translate-plugin`, loading namespaces through `getStaticProps` with a Webpack / Turbopack loader. It is also the lightest option here (~2.5kb). For namespacing, defining namespaces per page or route in config is well thought out and easier to maintain than main alternatives like **next-intl** or **next-i18next**. In version `3.1.2`, I noted that static rendering did not work; Next.js fell back to dynamic rendering.
219
228
 
220
- **(Intlayer)** (`next-intlayer@8.7.5`):
229
+ **(Intlayer)** (`next-intlayer@8.7.12`):
221
230
 
222
231
  I will not personally judge `next-intlayer` for objectivity’s sake, since it is my own solution.
223
232
 
@@ -0,0 +1,155 @@
1
+ ---
2
+ createdAt: 2026-04-20
3
+ updatedAt: 2026-04-21
4
+ title: Best i18n solution for Solid in 2026 - Benchmark Report
5
+ description: Compare Solid internationalisation (i18n) libraries like solid-primitives, solid-i18next, and Intlayer. Detailed performance report on bundle size, leakage, and reactivity.
6
+ keywords:
7
+ - benchmark
8
+ - i18n
9
+ - intl
10
+ - solid
11
+ - performance
12
+ - intlayer
13
+ slugs:
14
+ - doc
15
+ - benchmark
16
+ - solid
17
+ author: Aymeric PINEAU
18
+ applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-solid-template
19
+ history:
20
+ - version: 8.7.12
21
+ date: 2026-01-06
22
+ changes: "Init benchmark"
23
+ ---
24
+
25
+ # Solid i18n Libraries — 2026 Benchmark Report
26
+
27
+ This page is a benchmark report for i18n solutions on Solid.
28
+
29
+ ## Table of Contents
30
+
31
+ <Toc/>
32
+
33
+ ## Interactive Benchmark
34
+
35
+ <I18nBenchmark framework="vite-solid" 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_solid.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_solid.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 Solid 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**: Recommended choice for professional Solid applications needing advanced features and optimisation (v8.7.12).
63
+ - **@solid-primitives/i18n**: Excellent lightweight alternative for simple projects, though lacks advanced features like lazy loading.
64
+ - **solid-i18next**: Standard but heavy option (~4.7× Intlayer) with same downsides as React i18next.
65
+ - **Paraglide**: Innovative approach but complex DX and tree-shaking issues in some setups.
66
+
67
+ ## Test your app
68
+
69
+ To quickly spot i18n leakage issues, I set up a free scanner available [here](https://intlayer.org/i18n-seo-scanner).
70
+
71
+ <iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
72
+
73
+ ## The problem
74
+
75
+ Two levers are essential to limit the cost of a multilingual app:
76
+
77
+ - Split content by page / namespace so you do not load whole dictionaries when you do not need them
78
+ - Load the right locale dynamically, only when needed
79
+
80
+ Understanding the technical limitations of these approaches:
81
+
82
+ **Dynamic loading**
83
+
84
+ Without dynamic loading, most solutions keep messages in memory from the first render, which adds significant overhead for apps with many routes and locales.
85
+
86
+ With dynamic loading, you accept a trade-off: less initial JS, but sometimes an extra request when switching language.
87
+
88
+ **Content splitting**
89
+
90
+ 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.
91
+
92
+ ## Methodology
93
+
94
+ For this benchmark, we compared the following libraries:
95
+
96
+ - `Base App` (No i18n library)
97
+ - `solid-intlayer` (v8.7.12)
98
+ - `@solid-primitives/i18n` (v2.2.1)
99
+ - `solid-i18next` (v17.0.2)
100
+ - `@inlang/paraglide-js` (v2.17.0)
101
+
102
+ The framework is `Solid` with a multilingual app of **10 pages** and **10 languages**.
103
+
104
+ We compared **four loading strategies**:
105
+
106
+ | Strategy | No namespaces (global) | With namespaces (scoped) |
107
+ | :------------------ | :------------------------------------------- | :------------------------------------------------------------------- |
108
+ | **Static loading** | **Static**: Everything in memory at startup. | **Scoped static**: Split by namespace; everything loaded at startup. |
109
+ | **Dynamic loading** | **Dynamic**: On-demand loading per locale. | **Scoped dynamic**: Granular loading per namespace and locale. |
110
+
111
+ ## Strategy summary
112
+
113
+ - **Static**: Simple; no network latency after the initial load. Downside: large bundle size.
114
+ - **Dynamic**: Reduces initial weight (lazy-loading). Ideal when you have many locales.
115
+ - **Scoped static**: Keeps code organised (logical separation) without complex extra network requests.
116
+ - **Scoped dynamic**: Best approach for _code splitting_ and performance. Minimises memory by loading only what the current view and active locale need.
117
+
118
+ ## Results in detail
119
+
120
+ ### 1 — Solutions to avoid
121
+
122
+ > No clear solution to avoid in solid ecosystem.
123
+
124
+ ### 2 — Acceptable solutions
125
+
126
+ **(solid-i18next)** (`solid-i18next@17.0.2`):
127
+
128
+ `solid-i18next` is probably the most popular option because it was among the first to serve JavaScript app i18n needs. It also has a wide set of community plugins for specific problems.
129
+
130
+ The package is heavy (~14.6kb, which is about 4.7× `solid-intlayer`).
131
+
132
+ Still, it shares the same major downsides as stacks built on `t('a.b.c')`: optimisations are possible but very time-consuming, and large projects risk bad practices (namespaces + dynamic loading + types).
133
+
134
+ **(@solid-primitives/i18n)** (`@solid-primitives/i18n@2.2.1`):
135
+
136
+ Solid primitive is extremely light and efficient. I recommend that solution for light projects, but it can quickly become lacking features for professional solutions including cookie management, proxy redirection, formatters etc.
137
+ It also misses lazy loading and scoping namespaces for page size optimisation.
138
+
139
+ **(Paraglide)** (`@inlang/paraglide-js@2.17.0`):
140
+
141
+ `Paraglide` offers an innovative, well-thought-out approach. Even so, in this benchmark the tree-shaking their company advertises did not work for my implementation. The workflow and DX are also more complex than other options.
142
+ Personally I dislike having to regenerate JS files before every push, which creates constant merge conflict risk via PRs.
143
+ Finally, in comparison with other solutions, Paraglide does not use a store (e.g. Solid signal) 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.
144
+
145
+ ### 3 — Recommendations
146
+
147
+ **(Intlayer)** (`solid-intlayer@8.7.12`):
148
+
149
+ I will not personally judge `solid-intlayer` for objectivity’s sake, since it is my own solution.
150
+
151
+ ### Personal note
152
+
153
+ 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.
154
+
155
+ In Solid apps, injecting a function as a `JSX.Element` is, in my view, an anti-pattern. It also adds avoidable complexity and JavaScript execution overhead (even if it is barely noticeable).
@@ -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