@miragon/mcp-toolkit-ui 2.6.0 → 2.7.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 (348) hide show
  1. package/README.md +7 -1
  2. package/dist/app/builder/BuilderBanners.d.ts +7 -2
  3. package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
  4. package/dist/app/builder/BuilderBanners.js +8 -3
  5. package/dist/app/builder/BuilderBanners.js.map +1 -1
  6. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  7. package/dist/app/builder/CanvasRow.js +12 -14
  8. package/dist/app/builder/CanvasRow.js.map +1 -1
  9. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  10. package/dist/app/builder/CatalogueSheet.js +6 -5
  11. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  12. package/dist/app/builder/EmptyHint.d.ts +4 -0
  13. package/dist/app/builder/EmptyHint.d.ts.map +1 -1
  14. package/dist/app/builder/EmptyHint.js +6 -1
  15. package/dist/app/builder/EmptyHint.js.map +1 -1
  16. package/dist/app/builder/LayoutSurface.js +3 -3
  17. package/dist/app/builder/LayoutSurface.js.map +1 -1
  18. package/dist/app/builder/PipelineStrip.d.ts +3 -1
  19. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  20. package/dist/app/builder/PipelineStrip.js +11 -6
  21. package/dist/app/builder/PipelineStrip.js.map +1 -1
  22. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  23. package/dist/app/builder/SaveDialog.js +2 -2
  24. package/dist/app/builder/SaveDialog.js.map +1 -1
  25. package/dist/app/builder/Toolbar.d.ts +3 -1
  26. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  27. package/dist/app/builder/Toolbar.js +9 -3
  28. package/dist/app/builder/Toolbar.js.map +1 -1
  29. package/dist/app/builder/Workspace.js +2 -2
  30. package/dist/app/builder/Workspace.js.map +1 -1
  31. package/dist/app/builder/builder-model.d.ts +3 -2
  32. package/dist/app/builder/builder-model.d.ts.map +1 -1
  33. package/dist/app/builder/builder-model.js +5 -4
  34. package/dist/app/builder/builder-model.js.map +1 -1
  35. package/dist/app/builder/labels.d.ts +13 -62
  36. package/dist/app/builder/labels.d.ts.map +1 -1
  37. package/dist/app/builder/labels.js +9 -58
  38. package/dist/app/builder/labels.js.map +1 -1
  39. package/dist/app/builder/status-chip.d.ts.map +1 -1
  40. package/dist/app/builder/status-chip.js +3 -2
  41. package/dist/app/builder/status-chip.js.map +1 -1
  42. package/dist/app/builder/use-builder-catalogue.d.ts +1 -0
  43. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  44. package/dist/app/builder/use-builder-catalogue.js +6 -0
  45. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  46. package/dist/app/builder/use-save-dashboard.d.ts +3 -1
  47. package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
  48. package/dist/app/builder/use-save-dashboard.js +3 -1
  49. package/dist/app/builder/use-save-dashboard.js.map +1 -1
  50. package/dist/app/index.d.ts +1 -1
  51. package/dist/app/index.d.ts.map +1 -1
  52. package/dist/app/index.js.map +1 -1
  53. package/dist/app/layout-builder.d.ts +7 -2
  54. package/dist/app/layout-builder.d.ts.map +1 -1
  55. package/dist/app/layout-builder.js +17 -5
  56. package/dist/app/layout-builder.js.map +1 -1
  57. package/dist/app/mcp-app-view-chrome.d.ts +94 -20
  58. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  59. package/dist/app/mcp-app-view-chrome.js +109 -40
  60. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  61. package/dist/app/mcp-app-view-states.d.ts +15 -0
  62. package/dist/app/mcp-app-view-states.d.ts.map +1 -0
  63. package/dist/app/mcp-app-view-states.js +34 -0
  64. package/dist/app/mcp-app-view-states.js.map +1 -0
  65. package/dist/app/mcp-app-view.d.ts +40 -5
  66. package/dist/app/mcp-app-view.d.ts.map +1 -1
  67. package/dist/app/mcp-app-view.js +174 -128
  68. package/dist/app/mcp-app-view.js.map +1 -1
  69. package/dist/app/mcp-toolkit-app.d.ts +2 -1
  70. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  71. package/dist/app/mcp-toolkit-app.js +25 -3
  72. package/dist/app/mcp-toolkit-app.js.map +1 -1
  73. package/dist/app/use-view-lifecycle.d.ts +38 -0
  74. package/dist/app/use-view-lifecycle.d.ts.map +1 -0
  75. package/dist/app/use-view-lifecycle.js +79 -0
  76. package/dist/app/use-view-lifecycle.js.map +1 -0
  77. package/dist/app/view-lifecycle.d.ts +109 -0
  78. package/dist/app/view-lifecycle.d.ts.map +1 -0
  79. package/dist/app/view-lifecycle.js +120 -0
  80. package/dist/app/view-lifecycle.js.map +1 -0
  81. package/dist/app/widget-props-sheet.d.ts +3 -9
  82. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  83. package/dist/app/widget-props-sheet.js +6 -13
  84. package/dist/app/widget-props-sheet.js.map +1 -1
  85. package/dist/app/widget-renderer.d.ts +41 -1
  86. package/dist/app/widget-renderer.d.ts.map +1 -1
  87. package/dist/app/widget-renderer.js +82 -31
  88. package/dist/app/widget-renderer.js.map +1 -1
  89. package/dist/components/DrillButton.d.ts.map +1 -1
  90. package/dist/components/DrillButton.js +2 -1
  91. package/dist/components/DrillButton.js.map +1 -1
  92. package/dist/components/EmptyState.d.ts +33 -0
  93. package/dist/components/EmptyState.d.ts.map +1 -0
  94. package/dist/components/EmptyState.js +17 -0
  95. package/dist/components/EmptyState.js.map +1 -0
  96. package/dist/components/ErrorState.d.ts +50 -0
  97. package/dist/components/ErrorState.d.ts.map +1 -0
  98. package/dist/components/ErrorState.js +60 -0
  99. package/dist/components/ErrorState.js.map +1 -0
  100. package/dist/components/FilterBar.d.ts +1 -1
  101. package/dist/components/FilterBar.d.ts.map +1 -1
  102. package/dist/components/FilterBar.js +7 -4
  103. package/dist/components/FilterBar.js.map +1 -1
  104. package/dist/components/GroupCard.d.ts.map +1 -1
  105. package/dist/components/GroupCard.js +2 -1
  106. package/dist/components/GroupCard.js.map +1 -1
  107. package/dist/components/KpiGrid.d.ts +10 -3
  108. package/dist/components/KpiGrid.d.ts.map +1 -1
  109. package/dist/components/KpiGrid.js +31 -9
  110. package/dist/components/KpiGrid.js.map +1 -1
  111. package/dist/components/ListFooter.d.ts +3 -2
  112. package/dist/components/ListFooter.d.ts.map +1 -1
  113. package/dist/components/ListFooter.js +14 -4
  114. package/dist/components/ListFooter.js.map +1 -1
  115. package/dist/components/Pills.d.ts +10 -6
  116. package/dist/components/Pills.d.ts.map +1 -1
  117. package/dist/components/Pills.js +12 -6
  118. package/dist/components/Pills.js.map +1 -1
  119. package/dist/components/Toast.d.ts +37 -0
  120. package/dist/components/Toast.d.ts.map +1 -0
  121. package/dist/components/Toast.js +91 -0
  122. package/dist/components/Toast.js.map +1 -0
  123. package/dist/components/WidgetHeader.js +1 -1
  124. package/dist/hooks/use-tool-result-recovery.d.ts +23 -1
  125. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  126. package/dist/hooks/use-tool-result-recovery.js +21 -7
  127. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  128. package/dist/hooks/use-view-data.d.ts +6 -0
  129. package/dist/hooks/use-view-data.d.ts.map +1 -1
  130. package/dist/hooks/use-view-data.js +6 -1
  131. package/dist/hooks/use-view-data.js.map +1 -1
  132. package/dist/index.d.ts +12 -5
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +14 -4
  135. package/dist/index.js.map +1 -1
  136. package/dist/lib/focus-ring.d.ts +18 -0
  137. package/dist/lib/focus-ring.d.ts.map +1 -0
  138. package/dist/lib/focus-ring.js +18 -0
  139. package/dist/lib/focus-ring.js.map +1 -0
  140. package/dist/lib/icon-stroke.d.ts +12 -0
  141. package/dist/lib/icon-stroke.d.ts.map +1 -0
  142. package/dist/lib/icon-stroke.js +12 -0
  143. package/dist/lib/icon-stroke.js.map +1 -0
  144. package/dist/lib/toast-model.d.ts +46 -0
  145. package/dist/lib/toast-model.d.ts.map +1 -0
  146. package/dist/lib/toast-model.js +36 -0
  147. package/dist/lib/toast-model.js.map +1 -0
  148. package/dist/lib/tone-utils.d.ts +43 -9
  149. package/dist/lib/tone-utils.d.ts.map +1 -1
  150. package/dist/lib/tone-utils.js +36 -7
  151. package/dist/lib/tone-utils.js.map +1 -1
  152. package/dist/locale/index.d.ts +3 -1
  153. package/dist/locale/index.d.ts.map +1 -1
  154. package/dist/locale/index.js +2 -1
  155. package/dist/locale/index.js.map +1 -1
  156. package/dist/locale/locale-provider.d.ts +42 -1
  157. package/dist/locale/locale-provider.d.ts.map +1 -1
  158. package/dist/locale/locale-provider.js +44 -3
  159. package/dist/locale/locale-provider.js.map +1 -1
  160. package/dist/locale/message-types.d.ts +223 -0
  161. package/dist/locale/message-types.d.ts.map +1 -0
  162. package/dist/locale/message-types.js +7 -0
  163. package/dist/locale/message-types.js.map +1 -0
  164. package/dist/locale/messages-de.d.ts +9 -0
  165. package/dist/locale/messages-de.d.ts.map +1 -0
  166. package/dist/locale/messages-de.js +151 -0
  167. package/dist/locale/messages-de.js.map +1 -0
  168. package/dist/locale/messages-en.d.ts +8 -0
  169. package/dist/locale/messages-en.d.ts.map +1 -0
  170. package/dist/locale/messages-en.js +150 -0
  171. package/dist/locale/messages-en.js.map +1 -0
  172. package/dist/locale/toolkit-messages.d.ts +39 -0
  173. package/dist/locale/toolkit-messages.d.ts.map +1 -0
  174. package/dist/locale/toolkit-messages.js +50 -0
  175. package/dist/locale/toolkit-messages.js.map +1 -0
  176. package/dist/primitives/alert.d.ts.map +1 -1
  177. package/dist/primitives/alert.js +4 -2
  178. package/dist/primitives/alert.js.map +1 -1
  179. package/dist/primitives/badge.d.ts +1 -1
  180. package/dist/primitives/badge.d.ts.map +1 -1
  181. package/dist/primitives/badge.js +14 -6
  182. package/dist/primitives/badge.js.map +1 -1
  183. package/dist/primitives/button.d.ts.map +1 -1
  184. package/dist/primitives/button.js +12 -8
  185. package/dist/primitives/button.js.map +1 -1
  186. package/dist/primitives/card.js +4 -4
  187. package/dist/primitives/card.js.map +1 -1
  188. package/dist/primitives/dialog.d.ts +7 -3
  189. package/dist/primitives/dialog.d.ts.map +1 -1
  190. package/dist/primitives/dialog.js +14 -7
  191. package/dist/primitives/dialog.js.map +1 -1
  192. package/dist/primitives/input.d.ts.map +1 -1
  193. package/dist/primitives/input.js +4 -1
  194. package/dist/primitives/input.js.map +1 -1
  195. package/dist/primitives/scroll-area.js +1 -1
  196. package/dist/primitives/scroll-area.js.map +1 -1
  197. package/dist/primitives/select.d.ts.map +1 -1
  198. package/dist/primitives/select.js +5 -2
  199. package/dist/primitives/select.js.map +1 -1
  200. package/dist/primitives/sheet.d.ts +3 -1
  201. package/dist/primitives/sheet.d.ts.map +1 -1
  202. package/dist/primitives/sheet.js +12 -6
  203. package/dist/primitives/sheet.js.map +1 -1
  204. package/dist/primitives/skeleton.js +1 -1
  205. package/dist/primitives/skeleton.js.map +1 -1
  206. package/dist/primitives/switch.d.ts.map +1 -1
  207. package/dist/primitives/switch.js +2 -1
  208. package/dist/primitives/switch.js.map +1 -1
  209. package/dist/primitives/tabs.d.ts.map +1 -1
  210. package/dist/primitives/tabs.js +5 -3
  211. package/dist/primitives/tabs.js.map +1 -1
  212. package/dist/render-cases.d.ts.map +1 -1
  213. package/dist/render-cases.js +20 -2
  214. package/dist/render-cases.js.map +1 -1
  215. package/dist/render-scan.d.ts +25 -0
  216. package/dist/render-scan.d.ts.map +1 -0
  217. package/dist/render-scan.js +66 -0
  218. package/dist/render-scan.js.map +1 -0
  219. package/dist/theme/cd-tokens.d.ts +66 -0
  220. package/dist/theme/cd-tokens.d.ts.map +1 -0
  221. package/dist/theme/cd-tokens.js +64 -0
  222. package/dist/theme/cd-tokens.js.map +1 -0
  223. package/dist/theme/create-theme.d.ts +196 -39
  224. package/dist/theme/create-theme.d.ts.map +1 -1
  225. package/dist/theme/create-theme.js +189 -13
  226. package/dist/theme/create-theme.js.map +1 -1
  227. package/dist/theme/fonts.d.ts +38 -0
  228. package/dist/theme/fonts.d.ts.map +1 -0
  229. package/dist/theme/fonts.js +56 -0
  230. package/dist/theme/fonts.js.map +1 -0
  231. package/dist/theme/host-fonts.d.ts +39 -0
  232. package/dist/theme/host-fonts.d.ts.map +1 -0
  233. package/dist/theme/host-fonts.js +44 -0
  234. package/dist/theme/host-fonts.js.map +1 -0
  235. package/dist/theme/index.d.ts +8 -1
  236. package/dist/theme/index.d.ts.map +1 -1
  237. package/dist/theme/index.js +5 -1
  238. package/dist/theme/index.js.map +1 -1
  239. package/dist/theme/portal-container.d.ts +16 -0
  240. package/dist/theme/portal-container.d.ts.map +1 -0
  241. package/dist/theme/portal-container.js +19 -0
  242. package/dist/theme/portal-container.js.map +1 -0
  243. package/dist/theme/presets.d.ts +24 -9
  244. package/dist/theme/presets.d.ts.map +1 -1
  245. package/dist/theme/presets.js +44 -11
  246. package/dist/theme/presets.js.map +1 -1
  247. package/dist/theme/theme-provider.d.ts +27 -9
  248. package/dist/theme/theme-provider.d.ts.map +1 -1
  249. package/dist/theme/theme-provider.js +42 -20
  250. package/dist/theme/theme-provider.js.map +1 -1
  251. package/package.json +9 -4
  252. package/src/app/builder/BuilderBanners.tsx +26 -13
  253. package/src/app/builder/CanvasRow.tsx +22 -21
  254. package/src/app/builder/CatalogueSheet.tsx +16 -14
  255. package/src/app/builder/EmptyHint.tsx +16 -11
  256. package/src/app/builder/LayoutSurface.tsx +7 -7
  257. package/src/app/builder/PipelineStrip.tsx +26 -17
  258. package/src/app/builder/SaveDialog.tsx +11 -12
  259. package/src/app/builder/Toolbar.tsx +19 -7
  260. package/src/app/builder/Workspace.tsx +5 -3
  261. package/src/app/builder/builder-feedback.test.tsx +75 -0
  262. package/src/app/builder/builder-model.test.ts +5 -0
  263. package/src/app/builder/builder-model.ts +5 -4
  264. package/src/app/builder/labels.ts +15 -121
  265. package/src/app/builder/status-chip.tsx +8 -4
  266. package/src/app/builder/use-builder-catalogue.ts +6 -0
  267. package/src/app/builder/use-save-dashboard.ts +5 -0
  268. package/src/app/index.ts +8 -1
  269. package/src/app/layout-builder.tsx +23 -3
  270. package/src/app/mcp-app-view-chrome.test.tsx +347 -14
  271. package/src/app/mcp-app-view-chrome.tsx +278 -100
  272. package/src/app/mcp-app-view-states.tsx +100 -0
  273. package/src/app/mcp-app-view.test.tsx +258 -0
  274. package/src/app/mcp-app-view.tsx +292 -185
  275. package/src/app/mcp-toolkit-app.tsx +31 -7
  276. package/src/app/use-view-lifecycle.ts +116 -0
  277. package/src/app/view-lifecycle.test.ts +227 -0
  278. package/src/app/view-lifecycle.ts +174 -0
  279. package/src/app/widget-props-sheet.tsx +13 -26
  280. package/src/app/widget-renderer.render.test.tsx +131 -0
  281. package/src/app/widget-renderer.tsx +136 -38
  282. package/src/components/DrillButton.tsx +2 -1
  283. package/src/components/EmptyState.tsx +81 -0
  284. package/src/components/ErrorState.tsx +182 -0
  285. package/src/components/FilterBar.tsx +9 -6
  286. package/src/components/GroupCard.tsx +3 -2
  287. package/src/components/KpiGrid.tsx +75 -44
  288. package/src/components/ListFooter.tsx +17 -6
  289. package/src/components/Pills.tsx +20 -11
  290. package/src/components/Toast.tsx +188 -0
  291. package/src/components/WidgetHeader.tsx +1 -1
  292. package/src/components/feedback-states.test.tsx +225 -0
  293. package/src/components/tone-figures.test.tsx +54 -0
  294. package/src/fonts.css +19 -0
  295. package/src/globals.css +9 -165
  296. package/src/hooks/use-tool-result-recovery.test.ts +25 -1
  297. package/src/hooks/use-tool-result-recovery.ts +34 -6
  298. package/src/hooks/use-view-data.test.tsx +60 -0
  299. package/src/hooks/use-view-data.ts +12 -1
  300. package/src/i18n-coverage.test.tsx +323 -0
  301. package/src/index.ts +57 -5
  302. package/src/lib/focus-ring.ts +20 -0
  303. package/src/lib/icon-stroke.test.ts +59 -0
  304. package/src/lib/icon-stroke.ts +13 -0
  305. package/src/lib/toast-model.test.ts +68 -0
  306. package/src/lib/toast-model.ts +63 -0
  307. package/src/lib/tone-utils.test.ts +47 -14
  308. package/src/lib/tone-utils.ts +61 -13
  309. package/src/locale/index.ts +13 -1
  310. package/src/locale/locale-provider.test.tsx +87 -0
  311. package/src/locale/locale-provider.tsx +90 -3
  312. package/src/locale/message-types.ts +221 -0
  313. package/src/locale/messages-de.ts +159 -0
  314. package/src/locale/messages-en.ts +157 -0
  315. package/src/locale/toolkit-messages.test.ts +216 -0
  316. package/src/locale/toolkit-messages.ts +82 -0
  317. package/src/no-hardcoded-copy.test.ts +432 -0
  318. package/src/primitives/alert.tsx +4 -2
  319. package/src/primitives/badge.tsx +14 -7
  320. package/src/primitives/button.tsx +13 -8
  321. package/src/primitives/card.tsx +4 -4
  322. package/src/primitives/dialog.tsx +32 -7
  323. package/src/primitives/input.tsx +5 -3
  324. package/src/primitives/scroll-area.tsx +1 -1
  325. package/src/primitives/select.tsx +6 -3
  326. package/src/primitives/sheet.tsx +26 -6
  327. package/src/primitives/skeleton.tsx +1 -1
  328. package/src/primitives/switch.tsx +4 -2
  329. package/src/primitives/tabs.tsx +9 -6
  330. package/src/render-cases.tsx +45 -1
  331. package/src/render-scan.ts +75 -0
  332. package/src/theme/cd-tokens.generated.css +48 -0
  333. package/src/theme/cd-tokens.test.ts +107 -0
  334. package/src/theme/cd-tokens.ts +67 -0
  335. package/src/theme/create-theme.test.ts +243 -5
  336. package/src/theme/create-theme.ts +313 -41
  337. package/src/theme/fonts.test.ts +85 -0
  338. package/src/theme/fonts.ts +72 -0
  339. package/src/theme/host-fonts.test.ts +135 -0
  340. package/src/theme/host-fonts.ts +52 -0
  341. package/src/theme/index.ts +15 -1
  342. package/src/theme/portal-container.ts +20 -0
  343. package/src/theme/presets.ts +44 -11
  344. package/src/theme/theme-css.test.ts +568 -0
  345. package/src/theme/theme-provider.test.tsx +82 -0
  346. package/src/theme/theme-provider.tsx +60 -24
  347. package/src/theme.css +314 -0
  348. package/ui-catalog.json +344 -17
@@ -0,0 +1,159 @@
1
+ import type { ToolkitMessages } from "./message-types.js"
2
+
3
+ /**
4
+ * German catalog. du-Ansprache, aktive Sätze, normale Groß- und
5
+ * Kleinschreibung, „…“ als Auslassung, kein Gedankenstrich als Verbinder.
6
+ * Fehlertexte sagen, was passiert ist und was du tun kannst. Guarded by
7
+ * `toolkit-messages.test.ts`.
8
+ */
9
+ export const de: ToolkitMessages = {
10
+ common: {
11
+ close: "Schließen",
12
+ retry: "Erneut versuchen",
13
+ details: "Details",
14
+ dismiss: "Hinweis schließen",
15
+ notifications: "Hinweise",
16
+ },
17
+ view: {
18
+ loading: "Ansicht wird geladen…",
19
+ cancelled: "Abgebrochen. Frag im Chat noch mal nach dieser Ansicht.",
20
+ refresh: "Aktualisieren",
21
+ refreshing: "Wird aktualisiert…",
22
+ enterFullscreen: "Vollbild",
23
+ exitFullscreen: "Vollbild verlassen",
24
+ build: "Ansicht bearbeiten",
25
+ slowHint: "Das dauert länger als üblich. Wenn nichts passiert, frag im Chat noch mal nach.",
26
+ errorTitle: "Die Ansicht konnte nicht geladen werden",
27
+ errorHint: "Versuch es noch mal. Klappt es wieder nicht, frag im Chat nach.",
28
+ errorHintNoRetry: "Frag im Chat noch mal nach dieser Ansicht.",
29
+ retry: "Erneut versuchen",
30
+ errorDetails: "Details",
31
+ stepErrorsTitle: "Ein Teil der Daten konnte nicht geladen werden",
32
+ stepErrorsHint: "Versuch es noch mal, um die fehlenden Daten zu laden.",
33
+ refreshFailed: "Aktualisieren hat nicht geklappt.",
34
+ refreshFailedHint: "Du siehst den zuletzt geladenen Stand. Versuch es gleich noch mal.",
35
+ fullscreenFailed: "Die Darstellung ließ sich nicht umschalten.",
36
+ fullscreenFailedHint: "Die Ansicht bleibt, wie sie ist. Versuch es gleich noch mal.",
37
+ },
38
+ listFooter: {
39
+ showing: ({ shown, total, noun }) => `Zeigt ${shown} von ${total} ${noun}`.trimEnd(),
40
+ defaultNoun: "Zeilen",
41
+ loadMore: "Mehr laden",
42
+ loadingMore: "Wird geladen…",
43
+ },
44
+ filterBar: { searchPlaceholder: "Filtern…" },
45
+ pills: { live: "Live" },
46
+ renderer: {
47
+ widgetUnavailable: "Dieses Element ist in dieser Version nicht verfügbar.",
48
+ widgetCrashed: "Dieses Element konnte nicht angezeigt werden.",
49
+ noDataYet: "Noch keine Daten. Nach dem nächsten Aktualisieren sind sie da.",
50
+ },
51
+ builder: {
52
+ title: "Ansicht bearbeiten",
53
+ builderBadge: "Bearbeitung",
54
+ previewBadge: "Vorschau",
55
+ catalogue: "Katalog",
56
+ save: "Speichern",
57
+ done: "Fertig",
58
+ viewLayoutTab: "Layout",
59
+ viewPipelineTab: "Datenquellen",
60
+ viewPreviewTab: "Vorschau",
61
+ saveDialogTitle: "Dashboard speichern",
62
+ saveDialogDescription: "Speichert das Layout zusammen mit den Datenquellen, die es füllen.",
63
+ saveNameLabel: "Name",
64
+ saveDescriptionLabel: "Beschreibung",
65
+ saveConfirm: "Dashboard speichern",
66
+ cancel: "Abbrechen",
67
+ pipelineHeader: "Datenquellen",
68
+ keysHeader: "Schlüssel",
69
+ stepsHeader: "Schritte",
70
+ keyName: "Schlüssel",
71
+ keyValue: 'Wert, "Text", true, {…}',
72
+ stepContextId: "Schritt-ID",
73
+ stepPickerLabel: "Schritt wählen",
74
+ addKey: "Schlüssel hinzufügen",
75
+ addStep: "Schritt hinzufügen",
76
+ emptyKeys: "Noch keine Schlüssel.",
77
+ emptySteps: "Noch keine Schritte.",
78
+ statusUpToDate: "Aktuell",
79
+ statusPending: "Wird gleich übernommen…",
80
+ statusRefreshing: "Wird übernommen…",
81
+ paletteHeader: "Elemente",
82
+ paletteEmpty: "Noch keine Elemente verfügbar. Füg einen Schlüssel oder einen Schritt hinzu.",
83
+ canvasEmpty: "Zieh ein Element hierher oder klick eins in der Liste an.",
84
+ canvasDropHint: "Zum Hinzufügen loslassen",
85
+ addRow: "Zeile hinzufügen",
86
+ addTab: "Tab hinzufügen",
87
+ removeRow: "Zeile entfernen",
88
+ renameTab: "Tab umbenennen",
89
+ removeTab: "Tab entfernen",
90
+ tabsLabel: "Tabs",
91
+ catalogueDescription:
92
+ "Alle Schlüssel und Elemente, die der Server kennt, ob gerade verfügbar oder nicht.",
93
+ catalogueKeys: "Schlüssel",
94
+ catalogueSteps: "Schritte",
95
+ catalogueUnreachable: "Nicht verfügbare Elemente",
96
+ catalogueUnreachableEmpty: "Alle Elemente sind verfügbar.",
97
+ catalogueProducedBy: "erzeugt von",
98
+ catalogueConsumedBy: "genutzt von",
99
+ catalogueNeedsKeys: "braucht",
100
+ cataloguePickKey: "Als Schlüssel hinzufügen",
101
+ catalogueAddProducingStep: "Erzeugenden Schritt hinzufügen",
102
+ catalogueLive: "vorhanden",
103
+ catalogueMissing: "fehlt",
104
+ catalogueError: "Der Katalog ließ sich nicht aktualisieren",
105
+ catalogueErrorHint:
106
+ "Die angezeigten Elemente und Datenquellen sind vielleicht nicht aktuell. Versuch es noch mal. Klappt es wieder nicht, frag im Chat nach.",
107
+ validationWarning: "So lässt sich die Ansicht nicht darstellen",
108
+ saveError: "Das Dashboard ließ sich nicht speichern",
109
+ saveErrorHint:
110
+ "Deine Änderungen sind noch da. Versuch gleich noch mal zu speichern. Klappt es wieder nicht, frag im Chat nach.",
111
+ renameDuplicate: "Einen Tab mit diesem Namen gibt es schon.",
112
+ statsLive: "vorhanden",
113
+ statsReachable: "verfügbar",
114
+ statsUnreachable: "nicht verfügbar",
115
+ emptyHintTitle: "Noch keine Elemente verfügbar.",
116
+ emptyHintBody:
117
+ "Füg unter Datenquellen einen Schlüssel oder einen Schritt hinzu. Elemente erscheinen, sobald ihre Daten da sind.",
118
+ openDataSources: "Datenquellen öffnen",
119
+ saveNamePlaceholder: "Name des Dashboards",
120
+ saveDescriptionPlaceholder: "Optional",
121
+ saved: (name) => `Dashboard „${name}“ gespeichert.`,
122
+ renameHint: "Doppelklick auf einen Tab benennt ihn um.",
123
+ renameTitle: "Doppelklicken zum Umbenennen",
124
+ autoApplies: "Änderungen gelten sofort",
125
+ noMoreKeys: "Keine weiteren Schlüssel verfügbar.",
126
+ parsedType: "Erkannter Typ",
127
+ valueType: (code) =>
128
+ ({ str: "Text", num: "Zahl", bool: "Ja/Nein", arr: "Liste", obj: "Objekt", null: "leer" })[
129
+ code
130
+ ] ?? code,
131
+ removeKey: "Schlüssel entfernen",
132
+ removeStep: "Schritt entfernen",
133
+ noInputs: "keine Eingaben",
134
+ noOutputs: "keine Ausgaben",
135
+ rowLabel: (index) => `Zeile ${index}`,
136
+ elementCount: (count) => (count === 1 ? "1 Element" : `${count} Elemente`),
137
+ moveRowUp: "Zeile nach oben schieben",
138
+ moveRowDown: "Zeile nach unten schieben",
139
+ columnSpan: "Spaltenbreite",
140
+ configureElement: "Element konfigurieren",
141
+ configureElementSet: (count) => `Element konfigurieren (${count} gesetzt)`,
142
+ removeElement: "Element entfernen",
143
+ resizeElement: "Elementbreite ändern",
144
+ noKeysRegistered: "Keine Schlüssel registriert.",
145
+ noStepsRegistered: "Keine Schritte registriert.",
146
+ catalogueStepPrefix: "Schritt",
147
+ catalogueElementPrefix: "Element",
148
+ },
149
+ propsSheet: {
150
+ title: "Element konfigurieren",
151
+ description: "Einstellungen für dieses Element.",
152
+ apply: "Übernehmen",
153
+ cancel: "Abbrechen",
154
+ reset: "Zurücksetzen",
155
+ noProps: "Dieses Element hat keine Einstellungen.",
156
+ unsetOption: "Nicht gesetzt",
157
+ unsupportedType: (type) => `Diesen Feldtyp (${type}) kannst du hier nicht bearbeiten.`,
158
+ },
159
+ }
@@ -0,0 +1,157 @@
1
+ import type { ToolkitMessages } from "./message-types.js"
2
+
3
+ /**
4
+ * English catalog — the fallback for every locale the toolkit does not ship.
5
+ * Sentence case, active voice, one "…" character for ellipses, no dashes as
6
+ * connectors; guarded by `toolkit-messages.test.ts`.
7
+ */
8
+ export const en: ToolkitMessages = {
9
+ common: {
10
+ close: "Close",
11
+ retry: "Try again",
12
+ details: "Details",
13
+ dismiss: "Close notification",
14
+ notifications: "Notifications",
15
+ },
16
+ view: {
17
+ loading: "Loading view…",
18
+ cancelled: "Cancelled. Ask in the chat to open this view again.",
19
+ refresh: "Refresh",
20
+ refreshing: "Refreshing…",
21
+ enterFullscreen: "Fullscreen",
22
+ exitFullscreen: "Exit fullscreen",
23
+ build: "Edit view",
24
+ slowHint: "This is taking longer than usual. If nothing happens, ask in the chat again.",
25
+ errorTitle: "This view couldn't be loaded",
26
+ errorHint: "Try again. If it fails again, ask in the chat.",
27
+ errorHintNoRetry: "Ask in the chat to open this view again.",
28
+ retry: "Try again",
29
+ errorDetails: "Details",
30
+ stepErrorsTitle: "Some data couldn't be loaded",
31
+ stepErrorsHint: "Try again to load the missing data.",
32
+ refreshFailed: "Refresh didn't work.",
33
+ refreshFailedHint: "You're seeing the last loaded data. Try again in a moment.",
34
+ fullscreenFailed: "Couldn't switch the display mode.",
35
+ fullscreenFailedHint: "The view stays as it is. Try again in a moment.",
36
+ },
37
+ listFooter: {
38
+ showing: ({ shown, total, noun }) => `Showing ${shown} of ${total} ${noun}`.trimEnd(),
39
+ defaultNoun: "rows",
40
+ loadMore: "Load more",
41
+ loadingMore: "Loading…",
42
+ },
43
+ filterBar: { searchPlaceholder: "Filter…" },
44
+ pills: { live: "Live" },
45
+ renderer: {
46
+ widgetUnavailable: "This element isn't available in this version.",
47
+ widgetCrashed: "This element couldn't be displayed.",
48
+ noDataYet: "No data yet. It appears after the next refresh.",
49
+ },
50
+ builder: {
51
+ title: "Edit view",
52
+ builderBadge: "Editing",
53
+ previewBadge: "Preview",
54
+ catalogue: "Catalogue",
55
+ save: "Save",
56
+ done: "Done",
57
+ viewLayoutTab: "Layout",
58
+ viewPipelineTab: "Data sources",
59
+ viewPreviewTab: "Preview",
60
+ saveDialogTitle: "Save dashboard",
61
+ saveDialogDescription: "Saves the layout together with the data sources that fill it.",
62
+ saveNameLabel: "Name",
63
+ saveDescriptionLabel: "Description",
64
+ saveConfirm: "Save dashboard",
65
+ cancel: "Cancel",
66
+ pipelineHeader: "Data sources",
67
+ keysHeader: "Keys",
68
+ stepsHeader: "Steps",
69
+ keyName: "Key",
70
+ keyValue: 'Value, "text", true, {…}',
71
+ stepContextId: "Step ID",
72
+ stepPickerLabel: "Pick a step",
73
+ addKey: "Add key",
74
+ addStep: "Add step",
75
+ emptyKeys: "No keys yet.",
76
+ emptySteps: "No steps yet.",
77
+ statusUpToDate: "Up to date",
78
+ statusPending: "Applying soon…",
79
+ statusRefreshing: "Applying…",
80
+ paletteHeader: "Elements",
81
+ paletteEmpty: "No elements available yet. Add a key or a step.",
82
+ canvasEmpty: "Drag an element here or click one in the list.",
83
+ canvasDropHint: "Drop to add",
84
+ addRow: "Add row",
85
+ addTab: "Add tab",
86
+ removeRow: "Remove row",
87
+ renameTab: "Rename tab",
88
+ removeTab: "Remove tab",
89
+ tabsLabel: "Tabs",
90
+ catalogueDescription: "Every key and element the server knows, available or not.",
91
+ catalogueKeys: "Keys",
92
+ catalogueSteps: "Steps",
93
+ catalogueUnreachable: "Unavailable elements",
94
+ catalogueUnreachableEmpty: "Every element is available.",
95
+ catalogueProducedBy: "produced by",
96
+ catalogueConsumedBy: "used by",
97
+ catalogueNeedsKeys: "needs",
98
+ cataloguePickKey: "Add as key",
99
+ catalogueAddProducingStep: "Add producing step",
100
+ catalogueLive: "live",
101
+ catalogueMissing: "missing",
102
+ catalogueError: "Couldn't refresh the catalogue",
103
+ catalogueErrorHint:
104
+ "The elements and data sources shown may be out of date. Try again. If it fails again, ask in the chat.",
105
+ validationWarning: "This view won't render as configured",
106
+ saveError: "The dashboard couldn't be saved",
107
+ saveErrorHint:
108
+ "Your changes are still here. Try saving again in a moment. If it fails again, ask in the chat.",
109
+ renameDuplicate: "A tab with that name already exists.",
110
+ statsLive: "live",
111
+ statsReachable: "available",
112
+ statsUnreachable: "unavailable",
113
+ emptyHintTitle: "No elements available yet.",
114
+ emptyHintBody:
115
+ "Add a key or a step under data sources. Elements appear as soon as their data is there.",
116
+ openDataSources: "Open data sources",
117
+ saveNamePlaceholder: "Name of the dashboard",
118
+ saveDescriptionPlaceholder: "Optional",
119
+ saved: (name) => `Dashboard “${name}” saved.`,
120
+ renameHint: "Double-click a tab to rename it.",
121
+ renameTitle: "Double-click to rename",
122
+ autoApplies: "Changes apply right away",
123
+ noMoreKeys: "No more keys available.",
124
+ parsedType: "Detected type",
125
+ valueType: (code) =>
126
+ ({ str: "text", num: "number", bool: "yes/no", arr: "list", obj: "object", null: "empty" })[
127
+ code
128
+ ] ?? code,
129
+ removeKey: "Remove key",
130
+ removeStep: "Remove step",
131
+ noInputs: "no inputs",
132
+ noOutputs: "no outputs",
133
+ rowLabel: (index) => `Row ${index}`,
134
+ elementCount: (count) => (count === 1 ? "1 element" : `${count} elements`),
135
+ moveRowUp: "Move row up",
136
+ moveRowDown: "Move row down",
137
+ columnSpan: "Column span",
138
+ configureElement: "Configure element",
139
+ configureElementSet: (count) => `Configure element (${count} set)`,
140
+ removeElement: "Remove element",
141
+ resizeElement: "Resize element",
142
+ noKeysRegistered: "No keys registered.",
143
+ noStepsRegistered: "No steps registered.",
144
+ catalogueStepPrefix: "step",
145
+ catalogueElementPrefix: "element",
146
+ },
147
+ propsSheet: {
148
+ title: "Configure element",
149
+ description: "Settings for this element.",
150
+ apply: "Apply",
151
+ cancel: "Cancel",
152
+ reset: "Reset",
153
+ noProps: "This element has no settings.",
154
+ unsetOption: "Not set",
155
+ unsupportedType: (type) => `This field type (${type}) can't be edited here.`,
156
+ },
157
+ }
@@ -0,0 +1,216 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import {
3
+ formatCount,
4
+ resolveToolkitLocale,
5
+ resolveToolkitMessages,
6
+ TOOLKIT_LOCALES,
7
+ TOOLKIT_MESSAGES,
8
+ type ToolkitMessages,
9
+ } from "./toolkit-messages.js"
10
+
11
+ /**
12
+ * The catalog guard: every toolkit string follows the house copy rules in
13
+ * BOTH locales (brand-tone: no dash as a connector, one "…" character, no
14
+ * framework jargon; German with du, AI not KI, "CIB seven"; English in
15
+ * sentence case), and the German catalog is a real translation, not a copy.
16
+ */
17
+
18
+ /** Sample arguments for the function-valued messages. */
19
+ const SAMPLES: Record<string, unknown[]> = {
20
+ showing: [{ shown: "8", total: "42", noun: "tasks" }],
21
+ saved: ["weekly ops"],
22
+ rowLabel: [3],
23
+ elementCount: [2],
24
+ configureElementSet: [2],
25
+ unsupportedType: ["array"],
26
+ valueType: ["num"],
27
+ }
28
+
29
+ /** Every message of a catalog as `[path, rendered text]`. */
30
+ function flatten(messages: ToolkitMessages): [string, string][] {
31
+ const out: [string, string][] = []
32
+ for (const [section, entries] of Object.entries(messages)) {
33
+ for (const [key, value] of Object.entries(entries as Record<string, unknown>)) {
34
+ const text =
35
+ typeof value === "function"
36
+ ? (value as (...args: unknown[]) => string)(...(SAMPLES[key] ?? []))
37
+ : value
38
+ out.push([`${section}.${key}`, String(text)])
39
+ }
40
+ }
41
+ return out
42
+ }
43
+
44
+ const shape = (messages: ToolkitMessages) =>
45
+ Object.fromEntries(
46
+ Object.entries(messages).map(([section, entries]) => [
47
+ section,
48
+ Object.fromEntries(
49
+ Object.entries(entries as Record<string, unknown>).map(([k, v]) => [k, typeof v]),
50
+ ),
51
+ ]),
52
+ )
53
+
54
+ describe("toolkit catalogs", () => {
55
+ it("ship en and de with the identical section/key shape and value kinds", () => {
56
+ expect(TOOLKIT_LOCALES).toEqual(["en", "de"])
57
+ expect(shape(TOOLKIT_MESSAGES.de)).toEqual(shape(TOOLKIT_MESSAGES.en))
58
+ })
59
+
60
+ for (const locale of TOOLKIT_LOCALES) {
61
+ describe(`${locale} copy rules`, () => {
62
+ const entries = flatten(TOOLKIT_MESSAGES[locale])
63
+
64
+ it("has no empty or padded message", () => {
65
+ for (const [path, text] of entries) {
66
+ expect(text.trim().length, path).toBeGreaterThan(0)
67
+ expect(text, path).toBe(text.trim())
68
+ }
69
+ })
70
+
71
+ it("uses no dash as a connector (A2) and the ellipsis character, never three dots", () => {
72
+ for (const [path, text] of entries) {
73
+ expect(text, path).not.toMatch(/—| – | - /)
74
+ expect(text, path).not.toContain("...")
75
+ }
76
+ })
77
+
78
+ it("speaks no framework jargon to users (no 'pipeline')", () => {
79
+ for (const [path, text] of entries) expect(text, path).not.toMatch(/pipeline/i)
80
+ })
81
+
82
+ it("writes the brand spellings (AI, CIB seven)", () => {
83
+ for (const [path, text] of entries) {
84
+ expect(text, path).not.toMatch(/\bKI\b/)
85
+ expect(text, path).not.toMatch(/CIB Seven|CIB SEVEN/)
86
+ }
87
+ })
88
+ })
89
+ }
90
+
91
+ it("de addresses the reader with du — never Sie/Ihnen/Ihr or an impersonal 'man'", () => {
92
+ for (const [path, text] of flatten(TOOLKIT_MESSAGES.de)) {
93
+ expect(text, path).not.toMatch(/\b(Sie|Ihnen|Ihr|Ihre|Ihren|Ihrem|Ihrer)\b/)
94
+ expect(text, path).not.toMatch(/\bman\b/)
95
+ }
96
+ })
97
+
98
+ it("en is in sentence case — no two capitalised words in a row within a sentence", () => {
99
+ const capitalised = (word: string) => /^[A-Z][a-z]/.test(word)
100
+ for (const [path, text] of flatten(TOOLKIT_MESSAGES.en)) {
101
+ const words = text.split(/\s+/)
102
+ for (let i = 1; i < words.length; i++) {
103
+ const prev = words[i - 1] ?? ""
104
+ const word = words[i] ?? ""
105
+ const titleCasePair = capitalised(prev) && !/[.!?:]$/.test(prev) && capitalised(word)
106
+ expect(titleCasePair, `${path}: "${prev} ${word}"`).toBe(false)
107
+ }
108
+ }
109
+ })
110
+
111
+ it("de is a translation: only loanwords may equal the English text", () => {
112
+ const en = new Map(flatten(TOOLKIT_MESSAGES.en))
113
+ const identical = flatten(TOOLKIT_MESSAGES.de)
114
+ .filter(([path, text]) => en.get(path) === text)
115
+ .map(([, text]) => text)
116
+ expect([...new Set(identical)].sort()).toEqual([
117
+ "Details",
118
+ "Layout",
119
+ "Live",
120
+ "Name",
121
+ "Optional",
122
+ "Tabs",
123
+ ])
124
+ })
125
+
126
+ it("renders counts and names into the parametrised messages", () => {
127
+ const { en, de } = TOOLKIT_MESSAGES
128
+ expect(en.listFooter.showing({ shown: "8", total: "42", noun: "tasks" })).toBe(
129
+ "Showing 8 of 42 tasks",
130
+ )
131
+ expect(de.listFooter.showing({ shown: "8", total: "42", noun: "Aufgaben" })).toBe(
132
+ "Zeigt 8 von 42 Aufgaben",
133
+ )
134
+ // An empty noun leaves no trailing blank.
135
+ expect(en.listFooter.showing({ shown: "1", total: "2", noun: "" })).toBe("Showing 1 of 2")
136
+ expect(de.listFooter.showing({ shown: "1", total: "2", noun: "" })).toBe("Zeigt 1 von 2")
137
+ expect(en.builder.saved("Ops")).toBe("Dashboard “Ops” saved.")
138
+ expect(de.builder.saved("Ops")).toBe("Dashboard „Ops“ gespeichert.")
139
+ expect(en.builder.elementCount(1)).toBe("1 element")
140
+ expect(en.builder.elementCount(3)).toBe("3 elements")
141
+ expect(de.builder.elementCount(1)).toBe("1 Element")
142
+ expect(de.builder.elementCount(0)).toBe("0 Elemente")
143
+ expect(en.builder.rowLabel(2)).toBe("Row 2")
144
+ expect(de.builder.rowLabel(2)).toBe("Zeile 2")
145
+ expect(en.builder.configureElementSet(2)).toBe("Configure element (2 set)")
146
+ expect(de.builder.configureElementSet(2)).toBe("Element konfigurieren (2 gesetzt)")
147
+ expect(en.builder.valueType("bool")).toBe("yes/no")
148
+ expect(de.builder.valueType("bool")).toBe("Ja/Nein")
149
+ expect(de.builder.valueType("arr")).toBe("Liste")
150
+ expect(en.builder.valueType("future")).toBe("future")
151
+ expect(de.builder.valueType("future")).toBe("future")
152
+ expect(en.propsSheet.unsupportedType("array")).toBe(
153
+ "This field type (array) can't be edited here.",
154
+ )
155
+ expect(de.propsSheet.unsupportedType("array")).toBe(
156
+ "Diesen Feldtyp (array) kannst du hier nicht bearbeiten.",
157
+ )
158
+ })
159
+ })
160
+
161
+ describe("resolveToolkitLocale", () => {
162
+ it("maps a BCP 47 tag onto its shipped primary language", () => {
163
+ expect(resolveToolkitLocale("de")).toBe("de")
164
+ expect(resolveToolkitLocale("de-DE")).toBe("de")
165
+ expect(resolveToolkitLocale("de_AT")).toBe("de")
166
+ expect(resolveToolkitLocale(" DE-ch ")).toBe("de")
167
+ expect(resolveToolkitLocale("en-US")).toBe("en")
168
+ })
169
+
170
+ it("falls back to English for any other or missing locale", () => {
171
+ expect(resolveToolkitLocale("fr-FR")).toBe("en")
172
+ expect(resolveToolkitLocale("")).toBe("en")
173
+ expect(resolveToolkitLocale(undefined)).toBe("en")
174
+ expect(resolveToolkitLocale("deu")).toBe("en")
175
+ })
176
+ })
177
+
178
+ describe("resolveToolkitMessages", () => {
179
+ it("returns the shipped catalog itself without an override (stable identity)", () => {
180
+ expect(resolveToolkitMessages("de-DE")).toBe(TOOLKIT_MESSAGES.de)
181
+ expect(resolveToolkitMessages("es")).toBe(TOOLKIT_MESSAGES.en)
182
+ })
183
+
184
+ it("merges an override per section and keeps every other string", () => {
185
+ const merged = resolveToolkitMessages("de", {
186
+ listFooter: { loadMore: "Weitere laden" },
187
+ view: { refresh: "Neu laden" },
188
+ })
189
+ expect(merged.listFooter.loadMore).toBe("Weitere laden")
190
+ expect(merged.listFooter.loadingMore).toBe(TOOLKIT_MESSAGES.de.listFooter.loadingMore)
191
+ expect(merged.view.refresh).toBe("Neu laden")
192
+ expect(merged.view.retry).toBe(TOOLKIT_MESSAGES.de.view.retry)
193
+ for (const section of [
194
+ "common",
195
+ "filterBar",
196
+ "pills",
197
+ "renderer",
198
+ "builder",
199
+ "propsSheet",
200
+ ] as const) {
201
+ expect(merged[section]).toEqual(TOOLKIT_MESSAGES.de[section])
202
+ }
203
+ })
204
+ })
205
+
206
+ describe("formatCount", () => {
207
+ it("groups digits per locale", () => {
208
+ expect(formatCount(1234567, "en")).toBe("1,234,567")
209
+ expect(formatCount(1234567, "de-DE")).toBe("1.234.567")
210
+ expect(formatCount(7, "de")).toBe("7")
211
+ })
212
+
213
+ it("falls back to English for an invalid locale tag instead of throwing", () => {
214
+ expect(formatCount(1234, "not a locale!")).toBe("1,234")
215
+ })
216
+ })
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The toolkit's own UI strings — every caption, label, placeholder and
3
+ * accessible name a toolkit component renders — as one typed catalog per
4
+ * locale. Components read the active locale's catalog through
5
+ * `useToolkitMessages()` (the nearest `LocaleProvider`), so a consumer only
6
+ * passes a locale; nothing in the toolkit hard-codes English any more.
7
+ *
8
+ * Pure data + functions (no React, no `mcp-use/react`): browser-safe and
9
+ * shared by the root barrel and the `./app` shell alike.
10
+ */
11
+ import type { ToolkitMessages } from "./message-types.js"
12
+ import { de } from "./messages-de.js"
13
+ import { en } from "./messages-en.js"
14
+
15
+ export type {
16
+ BuilderMessages,
17
+ CommonMessages,
18
+ ListFooterMessages,
19
+ PropsSheetMessages,
20
+ RendererMessages,
21
+ ToolkitMessages,
22
+ ViewMessages,
23
+ } from "./message-types.js"
24
+
25
+ /** A partial override of a {@link ToolkitMessages} catalog, per section. */
26
+ export type ToolkitMessagesOverride = {
27
+ [K in keyof ToolkitMessages]?: Partial<ToolkitMessages[K]>
28
+ }
29
+
30
+ /** The locales the toolkit ships a catalog for. English is the fallback. */
31
+ export const TOOLKIT_LOCALES = ["en", "de"] as const
32
+ export type ToolkitLocale = (typeof TOOLKIT_LOCALES)[number]
33
+
34
+ /** The shipped catalogs, keyed by {@link ToolkitLocale}. */
35
+ export const TOOLKIT_MESSAGES: Readonly<Record<ToolkitLocale, ToolkitMessages>> = { en, de }
36
+
37
+ /**
38
+ * Map any BCP 47 tag (`"de-DE"`, `"de_AT"`, `"EN"`) onto a shipped catalog:
39
+ * its primary language subtag when the toolkit ships it, English otherwise.
40
+ */
41
+ export function resolveToolkitLocale(locale: string | undefined): ToolkitLocale {
42
+ const primary = (locale ?? "").trim().toLowerCase().split(/[-_]/)[0]
43
+ return (TOOLKIT_LOCALES as readonly string[]).includes(primary ?? "")
44
+ ? (primary as ToolkitLocale)
45
+ : "en"
46
+ }
47
+
48
+ /**
49
+ * The catalog for `locale`, with `override` merged over it section by
50
+ * section. Without an override the shipped catalog object itself is returned
51
+ * (referentially stable — safe as a memo dependency).
52
+ */
53
+ export function resolveToolkitMessages(
54
+ locale: string | undefined,
55
+ override?: ToolkitMessagesOverride,
56
+ ): ToolkitMessages {
57
+ const base = TOOLKIT_MESSAGES[resolveToolkitLocale(locale)]
58
+ if (!override) return base
59
+ return {
60
+ common: { ...base.common, ...override.common },
61
+ view: { ...base.view, ...override.view },
62
+ listFooter: { ...base.listFooter, ...override.listFooter },
63
+ filterBar: { ...base.filterBar, ...override.filterBar },
64
+ pills: { ...base.pills, ...override.pills },
65
+ renderer: { ...base.renderer, ...override.renderer },
66
+ builder: { ...base.builder, ...override.builder },
67
+ propsSheet: { ...base.propsSheet, ...override.propsSheet },
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Format a count for display in `locale` (grouping separators per locale:
73
+ * `1,234` / `1.234`). An invalid tag falls back to English instead of
74
+ * throwing inside a render.
75
+ */
76
+ export function formatCount(value: number, locale: string | undefined): string {
77
+ try {
78
+ return new Intl.NumberFormat(locale).format(value)
79
+ } catch {
80
+ return new Intl.NumberFormat("en").format(value)
81
+ }
82
+ }