xertica-ui 2.5.0 → 2.5.2

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 (536) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +56 -17
  3. package/assets/xertica-logo.svg +37 -37
  4. package/assets/xertica-x-logo.svg +20 -20
  5. package/bin/cli.ts +14 -2
  6. package/bin/generate-tokens.ts +262 -262
  7. package/bin/language-config.ts +359 -358
  8. package/components/assistant/code-block/CodeBlock.tsx +268 -268
  9. package/components/assistant/formatted-document/FormattedDocument.tsx +147 -147
  10. package/components/assistant/modern-chat-input/ModernChatInput.tsx +564 -564
  11. package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +99 -99
  12. package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -104
  13. package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -81
  14. package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +88 -88
  15. package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +75 -75
  16. package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +564 -564
  17. package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +67 -67
  18. package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +103 -103
  19. package/components/assistant/xertica-assistant/use-assistant.ts +615 -615
  20. package/components/assistant/xertica-assistant/xertica-assistant.tsx +611 -611
  21. package/components/blocks/card-patterns/ActivityCard.tsx +100 -100
  22. package/components/blocks/card-patterns/ActivityCardSkeleton.tsx +56 -56
  23. package/components/blocks/card-patterns/FeatureCardSkeleton.tsx +58 -58
  24. package/components/blocks/card-patterns/NotificationCard.tsx +140 -140
  25. package/components/blocks/card-patterns/NotificationCardSkeleton.tsx +81 -81
  26. package/components/blocks/card-patterns/ProfileCard.tsx +112 -112
  27. package/components/blocks/card-patterns/ProfileCardSkeleton.tsx +69 -69
  28. package/components/blocks/card-patterns/ProjectCard.tsx +123 -123
  29. package/components/blocks/card-patterns/ProjectCardSkeleton.tsx +67 -67
  30. package/components/blocks/card-patterns/QuickActionCardSkeleton.tsx +44 -44
  31. package/components/blocks/card-patterns/card-patterns.stories.tsx +594 -594
  32. package/components/blocks/card-patterns/index.ts +29 -29
  33. package/components/brand/language-selector/LanguageSelector.tsx +102 -102
  34. package/components/brand/language-selector/language-selector.stories.tsx +111 -111
  35. package/components/brand/language-selector/language-selector.test.tsx +101 -101
  36. package/components/brand/theme-toggle/ThemeToggle.tsx +74 -74
  37. package/components/brand/xertica-provider/xertica-provider.mdx +61 -61
  38. package/components/index.ts +86 -86
  39. package/components/layout/sidebar/sidebar.mdx +1 -1
  40. package/components/layout/sidebar/sidebar.stories.tsx +1033 -787
  41. package/components/layout/sidebar/sidebar.tsx +338 -1
  42. package/components/media/FloatingMediaWrapper.tsx +371 -371
  43. package/components/media/audio-player/AudioPlayer.tsx +768 -768
  44. package/components/media/video-player/VideoPlayer.tsx +310 -310
  45. package/components/pages/home-content/HomeContent.tsx +120 -120
  46. package/components/pages/home-content/home-content.mdx +62 -62
  47. package/components/pages/home-page/HomePage.tsx +78 -78
  48. package/components/pages/home-page/home-page.mdx +53 -53
  49. package/components/pages/template-content/TemplateContent.tsx +1354 -1354
  50. package/components/pages/template-content/template-content.mdx +61 -61
  51. package/components/pages/template-page/TemplatePage.stories.tsx +32 -32
  52. package/components/pages/template-page/template-page.mdx +53 -53
  53. package/components/shared/error-boundary.stories.tsx +114 -114
  54. package/components/shared/error-boundary.tsx +150 -150
  55. package/components/shared/error-fallbacks.tsx +222 -222
  56. package/components/ui/accordion/accordion.mdx +8 -8
  57. package/components/ui/alert/alert.mdx +8 -8
  58. package/components/ui/alert-dialog/alert-dialog.mdx +8 -8
  59. package/components/ui/aspect-ratio/aspect-ratio.mdx +8 -8
  60. package/components/ui/assistant-chart/assistant-chart.mdx +8 -8
  61. package/components/ui/avatar/avatar.mdx +8 -8
  62. package/components/ui/badge/badge.mdx +8 -8
  63. package/components/ui/breadcrumb/breadcrumb.mdx +8 -8
  64. package/components/ui/button/button.mdx +8 -8
  65. package/components/ui/calendar/calendar.mdx +8 -8
  66. package/components/ui/card/card.mdx +8 -8
  67. package/components/ui/carousel/carousel.mdx +8 -8
  68. package/components/ui/chart/chart.mdx +8 -8
  69. package/components/ui/chart/chart.test.tsx +178 -178
  70. package/components/ui/chart/chart.tsx +2245 -2239
  71. package/components/ui/checkbox/checkbox.mdx +8 -8
  72. package/components/ui/collapsible/collapsible.mdx +8 -8
  73. package/components/ui/command/command.mdx +8 -8
  74. package/components/ui/context-menu/context-menu.mdx +8 -8
  75. package/components/ui/dialog/dialog.mdx +8 -8
  76. package/components/ui/drawer/drawer.mdx +8 -8
  77. package/components/ui/dropdown-menu/dropdown-menu.mdx +8 -8
  78. package/components/ui/empty/empty.mdx +8 -8
  79. package/components/ui/file-upload/file-upload.mdx +8 -8
  80. package/components/ui/hover-card/hover-card.mdx +8 -8
  81. package/components/ui/input/input.mdx +8 -8
  82. package/components/ui/input-otp/input-otp.mdx +8 -8
  83. package/components/ui/label/label.mdx +8 -8
  84. package/components/ui/map/map.mdx +8 -8
  85. package/components/ui/menubar/menubar.mdx +8 -8
  86. package/components/ui/navigation-menu/navigation-menu.mdx +8 -8
  87. package/components/ui/notification-badge/notification-badge.mdx +8 -8
  88. package/components/ui/pagination/pagination.mdx +8 -8
  89. package/components/ui/popover/popover.mdx +8 -8
  90. package/components/ui/progress/progress.mdx +8 -8
  91. package/components/ui/radio-group/radio-group.mdx +8 -8
  92. package/components/ui/rating/rating.mdx +8 -8
  93. package/components/ui/resizable/resizable.mdx +8 -8
  94. package/components/ui/route-map/route-map.mdx +8 -8
  95. package/components/ui/scroll-area/scroll-area.mdx +8 -8
  96. package/components/ui/search/search.mdx +8 -8
  97. package/components/ui/select/select.mdx +8 -8
  98. package/components/ui/separator/separator.mdx +8 -8
  99. package/components/ui/sheet/sheet.mdx +8 -8
  100. package/components/ui/simple-map/simple-map.mdx +8 -8
  101. package/components/ui/skeleton/skeleton.mdx +8 -8
  102. package/components/ui/slider/slider.mdx +8 -8
  103. package/components/ui/sonner/sonner.mdx +8 -8
  104. package/components/ui/stats-card/index.ts +2 -2
  105. package/components/ui/stats-card/stats-card-skeleton.tsx +60 -60
  106. package/components/ui/stats-card/stats-card.mdx +8 -8
  107. package/components/ui/stats-card/stats-card.tsx +109 -109
  108. package/components/ui/stepper/stepper.mdx +8 -8
  109. package/components/ui/switch/switch.mdx +8 -8
  110. package/components/ui/table/table.mdx +8 -8
  111. package/components/ui/tabs/tabs.mdx +8 -8
  112. package/components/ui/textarea/textarea.mdx +8 -8
  113. package/components/ui/timeline/timeline.mdx +8 -8
  114. package/components/ui/toggle/toggle.mdx +8 -8
  115. package/components/ui/toggle-group/toggle-group.mdx +8 -8
  116. package/components/ui/tooltip/tooltip.mdx +8 -8
  117. package/components/ui/tree-view/tree-view.mdx +8 -8
  118. package/components.json +511 -511
  119. package/contexts/AuthContext.tsx +121 -121
  120. package/contexts/BrandColorsContext.tsx +282 -282
  121. package/contexts/LanguageContext.test.tsx +121 -121
  122. package/contexts/LanguageContext.tsx +250 -250
  123. package/contexts/theme-data.ts +391 -391
  124. package/dist/{AssistantChart-DoZCyS5r.cjs → AssistantChart-9w31gdAb.cjs} +4 -4
  125. package/dist/{AssistantChart-CldVCVDe.cjs → AssistantChart-BAudAfne.cjs} +5 -5
  126. package/dist/{AssistantChart-Bdd44uBn.cjs → AssistantChart-BAx9VQvb.cjs} +127 -388
  127. package/dist/{AssistantChart-Cu3m7RBo.js → AssistantChart-BP8upjMk.js} +5 -5
  128. package/dist/{AssistantChart-CFhDdGyU.js → AssistantChart-CVko2A1W.js} +130 -391
  129. package/dist/{AssistantChart-C_hwFRRr.js → AssistantChart-CVzmmhx4.js} +4 -4
  130. package/dist/{AudioPlayer-IAU5q5T1.cjs → AudioPlayer-1ypwE2Wh.cjs} +1 -1
  131. package/dist/{AudioPlayer-CGRUtUdN.js → AudioPlayer-DuKXrCfy.js} +1 -1
  132. package/dist/{LanguageContext-CS14yCpi.js → LanguageContext-BwhwC3G2.js} +2 -2
  133. package/dist/{LanguageContext-B_KFTCzT.cjs → LanguageContext-DvUt5jBg.cjs} +2 -2
  134. package/dist/{ThemeContext-C2EwAPDt.js → ThemeContext-BbBNoFTG.js} +2 -2
  135. package/dist/{ThemeContext-Bmod0Cg2.cjs → ThemeContext-BblcjQup.cjs} +13 -8
  136. package/dist/{ThemeContext-BWq9ACPo.js → ThemeContext-Bo-W2WZH.js} +13 -8
  137. package/dist/{ThemeContext-j5aGtPky.cjs → ThemeContext-CP3a0jxy.cjs} +193 -262
  138. package/dist/{ThemeContext-vTjumZeM.cjs → ThemeContext-Cmr8Ex8H.cjs} +2 -2
  139. package/dist/ThemeContext-CpqYShLq.cjs +324 -0
  140. package/dist/{ThemeContext-CQSo4Iwc.js → ThemeContext-D3LzacmG.js} +8 -1
  141. package/dist/ThemeContext-Du2nE1PL.js +325 -0
  142. package/dist/ThemeContext-GeEBTJ3q.cjs +1621 -0
  143. package/dist/ThemeContext-JyLK9B1o.js +1622 -0
  144. package/dist/{ThemeContext-CGk3KK0k.cjs → ThemeContext-U4dEYc6C.cjs} +8 -1
  145. package/dist/{ThemeContext-BXjrgUjW.js → ThemeContext-ept8jhXI.js} +200 -261
  146. package/dist/{VerifyEmailPage-CGIwmWrm.js → VerifyEmailPage-B31mCrMc.js} +1 -1
  147. package/dist/{VerifyEmailPage-C0c2e5n0.js → VerifyEmailPage-BE-L9mB7.js} +7 -7
  148. package/dist/{VerifyEmailPage-DSBMRHtl.js → VerifyEmailPage-BIBOKV7Z.js} +41 -36
  149. package/dist/{VerifyEmailPage-DgIid028.js → VerifyEmailPage-BJjAMUTW.js} +4 -4
  150. package/dist/{VerifyEmailPage--1Vurewl.cjs → VerifyEmailPage-BRSP-Pwt.cjs} +3 -3
  151. package/dist/{VerifyEmailPage-Cwi3kbol.cjs → VerifyEmailPage-Bae2cBXT.cjs} +7 -7
  152. package/dist/{VerifyEmailPage-De6bQjrz.cjs → VerifyEmailPage-BiRm7Nh4.cjs} +41 -36
  153. package/dist/{VerifyEmailPage-ByerOcm4.cjs → VerifyEmailPage-Bv8Ah_TK.cjs} +23 -20
  154. package/dist/VerifyEmailPage-Bvfv8HVQ.js +3214 -0
  155. package/dist/{VerifyEmailPage-BComraR7.cjs → VerifyEmailPage-CR7kb5df.cjs} +22 -12
  156. package/dist/{VerifyEmailPage-CpqqpLpo.cjs → VerifyEmailPage-C_Zk6Gen.cjs} +1 -1
  157. package/dist/{VerifyEmailPage-MTD7AG1Z.js → VerifyEmailPage-C_ihbcth.js} +4 -4
  158. package/dist/{VerifyEmailPage-1WwWczAn.js → VerifyEmailPage-CbgjOF0v.js} +22 -12
  159. package/dist/{VerifyEmailPage-DvMLZgFt.js → VerifyEmailPage-CdYPSJoO.js} +1 -1
  160. package/dist/{VerifyEmailPage-By3Jf__L.cjs → VerifyEmailPage-CkBYfsNy.cjs} +4 -4
  161. package/dist/{VerifyEmailPage-CJLz3jrn.js → VerifyEmailPage-Cyl55sJb.js} +23 -20
  162. package/dist/VerifyEmailPage-D-FRj5TU.cjs +3213 -0
  163. package/dist/{VerifyEmailPage-B4peJjAT.cjs → VerifyEmailPage-DF2ilhum.cjs} +334 -356
  164. package/dist/{VerifyEmailPage-CYXtbKi3.cjs → VerifyEmailPage-DMBh4NM9.cjs} +1 -1
  165. package/dist/{VerifyEmailPage-CgMxRb4z.js → VerifyEmailPage-DTtFfC-J.js} +3 -3
  166. package/dist/{VerifyEmailPage-CFLMls1p.cjs → VerifyEmailPage-Dt7zgA4w.cjs} +4 -4
  167. package/dist/{VerifyEmailPage-C5TNQTBa.js → VerifyEmailPage-EhudUdqF.js} +343 -355
  168. package/dist/{VerifyEmailPage-DGhuIqkb.js → VerifyEmailPage-X14vhdyl.js} +4 -4
  169. package/dist/VerifyEmailPage-hdB8JQGv.cjs +3213 -0
  170. package/dist/{VerifyEmailPage-Bp1XXl3H.cjs → VerifyEmailPage-u_Dn7t1U.cjs} +4 -4
  171. package/dist/VerifyEmailPage-vYHbYK3q.js +3214 -0
  172. package/dist/{XerticaProvider-CBGc4EMA.cjs → XerticaProvider-AChwphCO.cjs} +4 -4
  173. package/dist/{XerticaProvider-BIrqfZ-i.cjs → XerticaProvider-AbWlr7Af.cjs} +8 -11
  174. package/dist/{XerticaProvider-D-yNhF94.cjs → XerticaProvider-B8CaV7xu.cjs} +1 -1
  175. package/dist/{XerticaProvider-CEoWMTxu.js → XerticaProvider-BITjgC5p.js} +2 -2
  176. package/dist/{XerticaProvider-CllrbMEJ.cjs → XerticaProvider-By8q3Roe.cjs} +2 -2
  177. package/dist/{XerticaProvider-C1DKnvLh.js → XerticaProvider-CUYJZc32.js} +4 -4
  178. package/dist/{XerticaProvider-ET0ihewn.cjs → XerticaProvider-CW9hpCdF.cjs} +2 -2
  179. package/dist/{XerticaProvider-Dt5HEzbQ.js → XerticaProvider-CWgby5mY.js} +10 -10
  180. package/dist/XerticaProvider-CWs6EwNa.js +49 -0
  181. package/dist/XerticaProvider-CjQAQPcn.cjs +48 -0
  182. package/dist/XerticaProvider-D5lLumH-.js +49 -0
  183. package/dist/{XerticaProvider-DYq4JWtg.js → XerticaProvider-DQtvJU7m.js} +1 -1
  184. package/dist/XerticaProvider-qQUDop71.cjs +48 -0
  185. package/dist/{XerticaProvider-B7EVH-NF.js → XerticaProvider-siSt9uG2.js} +2 -2
  186. package/dist/{XerticaXLogo-Zw2B276b.cjs → XerticaXLogo-8TTzBjHw.cjs} +1 -1
  187. package/dist/{XerticaXLogo-B7xQ5dhi.js → XerticaXLogo-BWaag64t.js} +1 -1
  188. package/dist/{XerticaXLogo-DZbo4vOE.js → XerticaXLogo-CFuIlYFH.js} +12 -12
  189. package/dist/{XerticaXLogo-bvZSgwGF.cjs → XerticaXLogo-CU-U-GP4.cjs} +7 -13
  190. package/dist/XerticaXLogo-ChryA6xj.js +252 -0
  191. package/dist/{XerticaXLogo-CQUUjXoH.cjs → XerticaXLogo-CziKMQil.cjs} +8 -8
  192. package/dist/XerticaXLogo-DHz5SugF.js +252 -0
  193. package/dist/XerticaXLogo-DTee_y8X.cjs +251 -0
  194. package/dist/{XerticaXLogo-Cmsp-Eey.js → XerticaXLogo-DfUvz-lD.js} +9 -9
  195. package/dist/XerticaXLogo-kslQ8Tk_.cjs +251 -0
  196. package/dist/{alert-dialog-s-vmNkJ_.js → alert-dialog-iDe5VE5o.js} +3 -3
  197. package/dist/{alert-dialog-DSKByiKZ.cjs → alert-dialog-yckpaOpy.cjs} +3 -3
  198. package/dist/cli.js +16 -6
  199. package/dist/components/ui/chart/chart.d.ts +7 -5
  200. package/dist/{google-maps-loader-Y-QkD-Li.cjs → google-maps-loader-BqsYL48U.cjs} +0 -5
  201. package/dist/{google-maps-loader-CTYySAun.js → google-maps-loader-t2IlYBzw.js} +0 -4
  202. package/dist/index-CkTUgOwX.js +8 -0
  203. package/dist/{index-COtD8bRW.cjs → index-D3RLKRAs.cjs} +1 -1
  204. package/dist/index.cjs.js +2 -2
  205. package/dist/index.es.js +2 -2
  206. package/dist/index.umd.js +454 -1027
  207. package/dist/layout.cjs.js +1 -1
  208. package/dist/layout.es.js +1 -1
  209. package/dist/pages.cjs.js +1 -1
  210. package/dist/pages.es.js +1 -1
  211. package/dist/{sidebar-DAaY8bRU.cjs → sidebar-B3EYhli0.cjs} +33 -24
  212. package/dist/{sidebar-nzPoVHBQ.cjs → sidebar-B9NR0lCe.cjs} +46 -41
  213. package/dist/{sidebar-CeTMuzOx.cjs → sidebar-BvF5I2Ue.cjs} +47 -128
  214. package/dist/{sidebar-q7P2Godd.cjs → sidebar-C5B_LHek.cjs} +1 -1
  215. package/dist/{sidebar-CrQDDdcz.js → sidebar-CA6_ek3f.js} +33 -24
  216. package/dist/sidebar-CLmIjgNd.cjs +1136 -0
  217. package/dist/{sidebar-BxGXsDAd.cjs → sidebar-CVUGHOS_.cjs} +8 -16
  218. package/dist/{sidebar-BViy8Eeu.js → sidebar-CmvwjnVb.js} +9 -17
  219. package/dist/{sidebar-B6SlKZYN.js → sidebar-CplprZpM.js} +49 -40
  220. package/dist/sidebar-Duermn32.js +1133 -0
  221. package/dist/{sidebar-BbVIQvlP.js → sidebar-Dz7bd3zP.js} +1 -1
  222. package/dist/{sidebar-0ocFLSks.js → sidebar-KIS0C2JH.js} +50 -127
  223. package/dist/sidebar-OTO_up7Z.js +801 -0
  224. package/dist/sidebar-zowjejT2.cjs +800 -0
  225. package/dist/{use-audio-player-nv8ZSGa1.js → use-audio-player-Bkh23vQ3.js} +3 -7
  226. package/dist/{use-audio-player-NKsWyjWu.cjs → use-audio-player-Dn1NR9xN.cjs} +3 -7
  227. package/dist/{xertica-assistant-dyP7KHM5.cjs → xertica-assistant-B1IaHXnB.cjs} +388 -529
  228. package/dist/{xertica-assistant-ciJaWqm1.js → xertica-assistant-BMqdyRVi.js} +10 -28
  229. package/dist/{xertica-assistant-V_IdW4WF.cjs → xertica-assistant-Bj3vBCq_.cjs} +9 -27
  230. package/dist/{xertica-assistant-yX1CFBBo.js → xertica-assistant-DPsESB6t.js} +390 -531
  231. package/dist/{CodeBlock-7TTgmdGG.cjs → xertica-assistant-Qp3ydksa.cjs} +51 -263
  232. package/dist/{CodeBlock-BeSt1h5P.js → xertica-assistant-gnCJdcZY.js} +7 -219
  233. package/dist/xertica-ui.css +2 -2
  234. package/docs/architecture-improvements.md +456 -456
  235. package/docs/architecture.md +312 -312
  236. package/docs/components/assistant.md +428 -428
  237. package/docs/components/branding.md +252 -252
  238. package/docs/components/card-patterns.md +447 -447
  239. package/docs/components/error-boundary.md +201 -201
  240. package/docs/components/hooks.md +432 -432
  241. package/docs/components/language-selector.md +176 -176
  242. package/docs/components/pages.md +323 -323
  243. package/docs/components/sidebar.md +331 -331
  244. package/docs/components/stats-card.md +138 -138
  245. package/docs/doc-audit.md +244 -244
  246. package/docs/getting-started.md +616 -616
  247. package/docs/guidelines.md +330 -330
  248. package/docs/i18n.md +480 -480
  249. package/docs/installation.md +268 -268
  250. package/docs/llms.md +295 -295
  251. package/docs/state-management.md +289 -289
  252. package/guidelines/Guidelines.md +409 -409
  253. package/llms-compact.txt +1 -1
  254. package/llms-full.txt +10688 -10688
  255. package/llms.txt +1 -1
  256. package/package.json +1 -1
  257. package/styles/xertica/base.css +90 -90
  258. package/styles/xertica/tokens.css +240 -240
  259. package/templates/.prettierignore +4 -4
  260. package/templates/.prettierrc +10 -10
  261. package/templates/CLAUDE.md +180 -180
  262. package/templates/package.json +2 -2
  263. package/templates/src/app/App.tsx +46 -46
  264. package/templates/src/app/components/AuthGuard.tsx +131 -131
  265. package/templates/src/features/assistant/data/mock.ts +75 -75
  266. package/templates/src/features/assistant/hooks/useAssistantConfig.ts +20 -20
  267. package/templates/src/features/assistant/index.ts +5 -5
  268. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +70 -70
  269. package/templates/src/features/auth/ui/LoginContent.tsx +92 -92
  270. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +183 -183
  271. package/templates/src/features/auth/ui/SocialLoginButtons.tsx +78 -78
  272. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +80 -80
  273. package/templates/src/features/home/data/mock.ts +41 -41
  274. package/templates/src/features/home/hooks/useFeatureCards.ts +20 -20
  275. package/templates/src/features/home/index.ts +11 -11
  276. package/templates/src/features/home/ui/HomeContent.tsx +117 -117
  277. package/templates/src/features/template/ui/CrudTemplate.tsx +112 -112
  278. package/templates/src/features/template/ui/DashboardTemplate.tsx +110 -110
  279. package/templates/src/features/template/ui/FormTemplate.tsx +117 -117
  280. package/templates/src/features/template/ui/LoginTemplate.tsx +59 -59
  281. package/templates/src/features/template/ui/TemplateContent.tsx +1322 -1322
  282. package/templates/src/i18n.ts +124 -124
  283. package/templates/src/locales/en/common.json +21 -21
  284. package/templates/src/locales/en/components/activityCard.json +10 -10
  285. package/templates/src/locales/en/components/assistant.json +119 -119
  286. package/templates/src/locales/en/components/media.json +29 -29
  287. package/templates/src/locales/en/components/notificationCard.json +5 -5
  288. package/templates/src/locales/en/components/profileCard.json +8 -8
  289. package/templates/src/locales/en/components/projectCard.json +10 -10
  290. package/templates/src/locales/en/components/sidebar.json +14 -14
  291. package/templates/src/locales/en/components/stats.json +8 -8
  292. package/templates/src/locales/en/components/team.json +14 -14
  293. package/templates/src/locales/en/errors.json +9 -9
  294. package/templates/src/locales/en/languageSelector.json +7 -7
  295. package/templates/src/locales/en/nav.json +6 -6
  296. package/templates/src/locales/en/pages/crudTemplate.json +25 -25
  297. package/templates/src/locales/en/pages/dashboardTemplate.json +20 -20
  298. package/templates/src/locales/en/pages/forgotPassword.json +10 -10
  299. package/templates/src/locales/en/pages/formTemplate.json +16 -16
  300. package/templates/src/locales/en/pages/home.json +7 -7
  301. package/templates/src/locales/en/pages/login.json +15 -15
  302. package/templates/src/locales/en/pages/loginTemplate.json +9 -9
  303. package/templates/src/locales/en/pages/resetPassword.json +18 -18
  304. package/templates/src/locales/en/pages/templates.json +317 -317
  305. package/templates/src/locales/en/pages/verifyEmail.json +12 -12
  306. package/templates/src/locales/en/themeToggle.json +6 -6
  307. package/templates/src/locales/es/common.json +21 -21
  308. package/templates/src/locales/es/components/activityCard.json +10 -10
  309. package/templates/src/locales/es/components/assistant.json +119 -119
  310. package/templates/src/locales/es/components/media.json +29 -29
  311. package/templates/src/locales/es/components/notificationCard.json +5 -5
  312. package/templates/src/locales/es/components/profileCard.json +8 -8
  313. package/templates/src/locales/es/components/projectCard.json +10 -10
  314. package/templates/src/locales/es/components/sidebar.json +14 -14
  315. package/templates/src/locales/es/components/stats.json +8 -8
  316. package/templates/src/locales/es/components/team.json +14 -14
  317. package/templates/src/locales/es/errors.json +9 -9
  318. package/templates/src/locales/es/languageSelector.json +7 -7
  319. package/templates/src/locales/es/nav.json +6 -6
  320. package/templates/src/locales/es/pages/crudTemplate.json +25 -25
  321. package/templates/src/locales/es/pages/dashboardTemplate.json +20 -20
  322. package/templates/src/locales/es/pages/forgotPassword.json +10 -10
  323. package/templates/src/locales/es/pages/formTemplate.json +16 -16
  324. package/templates/src/locales/es/pages/home.json +7 -7
  325. package/templates/src/locales/es/pages/login.json +15 -15
  326. package/templates/src/locales/es/pages/loginTemplate.json +9 -9
  327. package/templates/src/locales/es/pages/resetPassword.json +18 -18
  328. package/templates/src/locales/es/pages/templates.json +317 -317
  329. package/templates/src/locales/es/pages/verifyEmail.json +12 -12
  330. package/templates/src/locales/es/themeToggle.json +6 -6
  331. package/templates/src/locales/pt-BR/common.json +21 -21
  332. package/templates/src/locales/pt-BR/components/activityCard.json +10 -10
  333. package/templates/src/locales/pt-BR/components/assistant.json +119 -119
  334. package/templates/src/locales/pt-BR/components/media.json +29 -29
  335. package/templates/src/locales/pt-BR/components/notificationCard.json +5 -5
  336. package/templates/src/locales/pt-BR/components/profileCard.json +8 -8
  337. package/templates/src/locales/pt-BR/components/projectCard.json +10 -10
  338. package/templates/src/locales/pt-BR/components/sidebar.json +14 -14
  339. package/templates/src/locales/pt-BR/components/stats.json +8 -8
  340. package/templates/src/locales/pt-BR/components/team.json +14 -14
  341. package/templates/src/locales/pt-BR/errors.json +9 -9
  342. package/templates/src/locales/pt-BR/languageSelector.json +7 -7
  343. package/templates/src/locales/pt-BR/nav.json +6 -6
  344. package/templates/src/locales/pt-BR/pages/crudTemplate.json +25 -25
  345. package/templates/src/locales/pt-BR/pages/dashboardTemplate.json +20 -20
  346. package/templates/src/locales/pt-BR/pages/forgotPassword.json +10 -10
  347. package/templates/src/locales/pt-BR/pages/formTemplate.json +16 -16
  348. package/templates/src/locales/pt-BR/pages/home.json +7 -7
  349. package/templates/src/locales/pt-BR/pages/login.json +15 -15
  350. package/templates/src/locales/pt-BR/pages/loginTemplate.json +9 -9
  351. package/templates/src/locales/pt-BR/pages/resetPassword.json +18 -18
  352. package/templates/src/locales/pt-BR/pages/templates.json +317 -317
  353. package/templates/src/locales/pt-BR/pages/verifyEmail.json +12 -12
  354. package/templates/src/locales/pt-BR/themeToggle.json +6 -6
  355. package/templates/src/pages/AssistantPage.tsx +470 -470
  356. package/templates/src/pages/HomePage.tsx +53 -53
  357. package/templates/src/shared/error-boundary.tsx +150 -150
  358. package/templates/src/shared/error-fallbacks.tsx +222 -222
  359. package/templates/src/styles/xertica/tokens.css +240 -240
  360. package/templates/vite.config.js +20 -20
  361. package/templates/vite.config.ts +55 -55
  362. package/dist/AssistantChart-BKVtGUKF.js +0 -3383
  363. package/dist/AssistantChart-CxGjH7Qk.js +0 -3477
  364. package/dist/AssistantChart-DIpshm3i.js +0 -4784
  365. package/dist/AssistantChart-D_PTeu8P.cjs +0 -3503
  366. package/dist/AssistantChart-WeycT5Pd.cjs +0 -3551
  367. package/dist/AssistantChart-zjsy2GaZ.cjs +0 -4810
  368. package/dist/AudioPlayer-B1lt5cPl.cjs +0 -989
  369. package/dist/AudioPlayer-BZ7bibzU.cjs +0 -982
  370. package/dist/AudioPlayer-BpRPS4-1.cjs +0 -1277
  371. package/dist/AudioPlayer-C12BjQBV.cjs +0 -997
  372. package/dist/AudioPlayer-CFeV8t-5.cjs +0 -936
  373. package/dist/AudioPlayer-Coly3q5R.js +0 -1278
  374. package/dist/AudioPlayer-CySJIyvL.js +0 -937
  375. package/dist/AudioPlayer-DMcG_c7L.js +0 -990
  376. package/dist/AudioPlayer-DcFKRJE_.js +0 -998
  377. package/dist/AudioPlayer-e8LfNoqO.js +0 -983
  378. package/dist/BrandColorsContext-565dDHd5.js +0 -660
  379. package/dist/BrandColorsContext-BcJbtkqn.cjs +0 -659
  380. package/dist/CodeBlock-BgfYL_rD.cjs +0 -2094
  381. package/dist/CodeBlock-BlcqlA9M.cjs +0 -2094
  382. package/dist/CodeBlock-Bnmeu5ez.cjs +0 -2094
  383. package/dist/CodeBlock-BtfPlbAI.js +0 -2078
  384. package/dist/CodeBlock-CIySIuYr.js +0 -2078
  385. package/dist/CodeBlock-CuPtUM-7.cjs +0 -2094
  386. package/dist/CodeBlock-D6ffWXgc.js +0 -2078
  387. package/dist/CodeBlock-D8dcwbit.cjs +0 -2094
  388. package/dist/CodeBlock-DMZrFnlw.cjs +0 -2094
  389. package/dist/CodeBlock-DlBehYN8.js +0 -2078
  390. package/dist/CodeBlock-DnYNI8rQ.js +0 -2078
  391. package/dist/CodeBlock-DvKWbSnE.cjs +0 -2094
  392. package/dist/CodeBlock-DwMCfkFY.js +0 -2078
  393. package/dist/CodeBlock-Dy6CNYyj.js +0 -2078
  394. package/dist/CodeBlock-U1pPOQI7.cjs +0 -2094
  395. package/dist/CodeBlock-f_GpNhEB.js +0 -2078
  396. package/dist/CodeBlock-oB6u8nI1.js +0 -2078
  397. package/dist/CodeBlock-tZC31B73.cjs +0 -2094
  398. package/dist/FeatureCard-CxC-7C-C.cjs +0 -300
  399. package/dist/FeatureCard-DbHWCb4E.js +0 -301
  400. package/dist/ImageWithFallback-CGtidP6B.cjs +0 -4542
  401. package/dist/ImageWithFallback-lsg3pdFg.js +0 -4508
  402. package/dist/LanguageSelector-B5YfbHra.js +0 -231
  403. package/dist/LanguageSelector-D6uacAIM.cjs +0 -230
  404. package/dist/LayoutContext-B45-e9DI.cjs +0 -93
  405. package/dist/LayoutContext-BAql6ZRY.js +0 -97
  406. package/dist/LayoutContext-Bav3UMEA.js +0 -94
  407. package/dist/LayoutContext-BvK-ggDa.cjs +0 -96
  408. package/dist/ThemeContext-BoH4NLfN.js +0 -734
  409. package/dist/ThemeContext-r69W20Xg.cjs +0 -733
  410. package/dist/VerifyEmailPage-COiyNl1y.js +0 -2825
  411. package/dist/VerifyEmailPage-CqKsR2v8.js +0 -2827
  412. package/dist/VerifyEmailPage-DjQKRlUS.cjs +0 -2824
  413. package/dist/VerifyEmailPage-s-1X3LDJ.cjs +0 -2826
  414. package/dist/XerticaOrbe-KL1RBHzw.cjs +0 -1354
  415. package/dist/XerticaOrbe-zwS1p2a8.js +0 -1355
  416. package/dist/XerticaProvider-6btlAlzc.js +0 -17
  417. package/dist/XerticaProvider-BNoNOxQ5.cjs +0 -16
  418. package/dist/XerticaProvider-BlY2limY.cjs +0 -38
  419. package/dist/XerticaProvider-DDuiIcKo.js +0 -39
  420. package/dist/XerticaProvider-cI9hSs27.cjs +0 -38
  421. package/dist/XerticaProvider-hSwhNQex.js +0 -39
  422. package/dist/alert-dialog-BOje--vD.js +0 -847
  423. package/dist/alert-dialog-BtEuQqrg.cjs +0 -870
  424. package/dist/breadcrumb-CqJ7bHY5.js +0 -161
  425. package/dist/breadcrumb-m9Hb2_XN.cjs +0 -177
  426. package/dist/components/assistant/xertica-assistant/hooks/index.d.ts +0 -6
  427. package/dist/components/assistant/xertica-assistant/hooks/use-assistant-conversations.d.ts +0 -21
  428. package/dist/components/assistant/xertica-assistant/hooks/use-assistant-messages.d.ts +0 -49
  429. package/dist/components/assistant/xertica-assistant/hooks/use-assistant-suggestions.d.ts +0 -16
  430. package/dist/components/blocks/audio-player/AudioPlayer.d.ts +0 -35
  431. package/dist/components/blocks/audio-player/index.d.ts +0 -1
  432. package/dist/components/blocks/document-editor/DocumentEditor.d.ts +0 -26
  433. package/dist/components/blocks/document-editor/index.d.ts +0 -1
  434. package/dist/components/blocks/podcast-player/PodcastPlayer.d.ts +0 -41
  435. package/dist/components/blocks/podcast-player/index.d.ts +0 -1
  436. package/dist/components/ui/chart/parts/chart-dashboard.d.ts +0 -113
  437. package/dist/components/ui/chart/parts/chart-metric.d.ts +0 -118
  438. package/dist/components/ui/chart/parts/chart-primitives.d.ts +0 -101
  439. package/dist/components/ui/chart/parts/chart-shared.d.ts +0 -20
  440. package/dist/components/ui/chart/parts/chart-utils.d.ts +0 -12
  441. package/dist/components/ui/chart/parts/index.d.ts +0 -5
  442. package/dist/dropdown-menu-BDB5CmQs.cjs +0 -247
  443. package/dist/dropdown-menu-DQidbKBD.js +0 -231
  444. package/dist/google-maps-loader-BFWp6VPd.js +0 -287
  445. package/dist/google-maps-loader-BKcdgFbu.cjs +0 -312
  446. package/dist/google-maps-loader-CumCNXeG.js +0 -312
  447. package/dist/google-maps-loader-eS3uQ5TA.cjs +0 -287
  448. package/dist/header-Cgy6vYPk.cjs +0 -731
  449. package/dist/header-DRlT4jgI.js +0 -715
  450. package/dist/header-Dux00SI4.cjs +0 -731
  451. package/dist/header-EkGKXPsD.js +0 -715
  452. package/dist/header-WfEywpyc.cjs +0 -731
  453. package/dist/header-tifNQn2U.js +0 -715
  454. package/dist/index-BhapVLVj.js +0 -8
  455. package/dist/index-D6fxYEY8.cjs +0 -7
  456. package/dist/index-DAIp0_HK.js +0 -8
  457. package/dist/index-DW5tYe26.js +0 -8
  458. package/dist/index-GA__GvnG.cjs +0 -7
  459. package/dist/input-2R4loU86.js +0 -127
  460. package/dist/input-DWANSKGb.cjs +0 -145
  461. package/dist/progress-DPtzoVV8.js +0 -175
  462. package/dist/progress-EeaoqqUs.cjs +0 -191
  463. package/dist/rich-text-editor-0mraWT5y.cjs +0 -2376
  464. package/dist/rich-text-editor-B-IkcPD0.js +0 -2874
  465. package/dist/rich-text-editor-B6jMRLzk.cjs +0 -1939
  466. package/dist/rich-text-editor-B8_oYcIR.js +0 -1730
  467. package/dist/rich-text-editor-B9UbSXNb.js +0 -1203
  468. package/dist/rich-text-editor-BYuRBNBU.js +0 -2373
  469. package/dist/rich-text-editor-Bb9pySTs.cjs +0 -2374
  470. package/dist/rich-text-editor-BcL6L3cm.cjs +0 -2374
  471. package/dist/rich-text-editor-BoVZYtTs.cjs +0 -2391
  472. package/dist/rich-text-editor-Bp3zQqMC.js +0 -2954
  473. package/dist/rich-text-editor-CMgSN_w2.js +0 -1189
  474. package/dist/rich-text-editor-CPV1lEPH.cjs +0 -1748
  475. package/dist/rich-text-editor-CeucBdIv.cjs +0 -2971
  476. package/dist/rich-text-editor-CoKqbCtu.cjs +0 -1799
  477. package/dist/rich-text-editor-Cw56T_mB.js +0 -2356
  478. package/dist/rich-text-editor-Cyt8qs2b.js +0 -1921
  479. package/dist/rich-text-editor-D6H84OcX.cjs +0 -1220
  480. package/dist/rich-text-editor-D76gD-QI.js +0 -2328
  481. package/dist/rich-text-editor-DKkokOnA.js +0 -1781
  482. package/dist/rich-text-editor-DNsdpN64.cjs +0 -2359
  483. package/dist/rich-text-editor-DfG8bCyY.js +0 -2358
  484. package/dist/rich-text-editor-Dxjw31Z4.js +0 -2341
  485. package/dist/rich-text-editor-DzP0Epmb.js +0 -2356
  486. package/dist/rich-text-editor-bRkNoeZY.cjs +0 -2891
  487. package/dist/rich-text-editor-lyYE2ZG5.cjs +0 -1207
  488. package/dist/rich-text-editor-skplNlBM.cjs +0 -2345
  489. package/dist/select-Bkbr0f-Z.cjs +0 -162
  490. package/dist/select-CvIVdX2n.js +0 -145
  491. package/dist/sidebar-CK_0ZQHj.cjs +0 -803
  492. package/dist/sidebar-CUuOvYhK.js +0 -787
  493. package/dist/sidebar-DQj1z3jG.cjs +0 -758
  494. package/dist/sidebar-Djn5syhi.cjs +0 -786
  495. package/dist/sidebar-LluMXfam.js +0 -759
  496. package/dist/sidebar-_rT7rBMk.js +0 -787
  497. package/dist/slider-Bc5Hd0y1.js +0 -56
  498. package/dist/slider-N7hFFj6X.cjs +0 -73
  499. package/dist/tooltip-Ded96neP.cjs +0 -137
  500. package/dist/tooltip-HDOoD2-0.js +0 -120
  501. package/dist/use-audio-player-B31J-aqh.cjs +0 -187
  502. package/dist/use-audio-player-BkmEmj8Q.js +0 -185
  503. package/dist/use-audio-player-CLFTWFW1.cjs +0 -184
  504. package/dist/use-audio-player-CLLn00I6.js +0 -188
  505. package/dist/use-file-upload-BcjEo2S5.js +0 -404
  506. package/dist/use-file-upload-CRJR68Tj.cjs +0 -403
  507. package/dist/use-mobile-B0hNy_Y6.cjs +0 -4303
  508. package/dist/use-mobile-BXuYROXM.js +0 -4202
  509. package/dist/use-mobile-Bbd51ASU.cjs +0 -4392
  510. package/dist/use-mobile-Bk6CX-TC.js +0 -4359
  511. package/dist/use-mobile-BvYdisLP.js +0 -4202
  512. package/dist/use-mobile-BzuxjzNX.cjs +0 -4392
  513. package/dist/use-mobile-CG2-SdXV.cjs +0 -4235
  514. package/dist/use-mobile-CKb5pqTs.js +0 -4269
  515. package/dist/use-mobile-CYuAuGDl.js +0 -4202
  516. package/dist/use-mobile-CaENcqm-.js +0 -4508
  517. package/dist/use-mobile-CbrYgJGJ.js +0 -4203
  518. package/dist/use-mobile-Cd4xPrKq.cjs +0 -46
  519. package/dist/use-mobile-DMOvImGQ.cjs +0 -4542
  520. package/dist/use-mobile-DRB3BQgD.cjs +0 -4235
  521. package/dist/use-mobile-DZvv7QMR.js +0 -4359
  522. package/dist/use-mobile-DdI_TXam.cjs +0 -4235
  523. package/dist/use-mobile-DlceKf8a.js +0 -4359
  524. package/dist/use-mobile-DsOnow1o.cjs +0 -4236
  525. package/dist/use-mobile-Kcj6jSnK.cjs +0 -4392
  526. package/dist/use-mobile-bnKcua_i.js +0 -4202
  527. package/dist/use-mobile-j4w2Jrf1.js +0 -30
  528. package/dist/use-mobile-ncXBeE2z.cjs +0 -4235
  529. package/dist/use-rich-text-editor-DjiddBGv.js +0 -282
  530. package/dist/use-rich-text-editor-lpeswbCs.cjs +0 -281
  531. package/dist/xertica-assistant-BdiZag0h.js +0 -2187
  532. package/dist/xertica-assistant-CrgTb6Hs.cjs +0 -2155
  533. package/dist/xertica-assistant-DCsnQyi5.js +0 -2156
  534. package/dist/xertica-assistant-DUBpmEgo.cjs +0 -2186
  535. package/dist/{rich-text-editor-DgF8s7xW.js → rich-text-editor-BmsjY03B.js} +26 -26
  536. package/dist/{rich-text-editor-mWoaSCE4.cjs → rich-text-editor-GS2kpTAK.cjs} +26 -26
package/docs/i18n.md CHANGED
@@ -1,480 +1,480 @@
1
- # Internationalization (i18n) — Xertica UI
2
-
3
- Xertica UI projects use **`i18next`** with the **`react-i18next`** binding for all UI string translations. The `LanguageSelector` component is wired directly to `i18next` — changing the language immediately updates every `useTranslation()` consumer in the app and invalidates the React Query cache so that data-layer strings (mock API responses) also refresh.
4
-
5
- ---
6
-
7
- ## Setup
8
-
9
- ### 1. Install
10
-
11
- ```bash
12
- npm install i18next react-i18next
13
- ```
14
-
15
- Both packages are listed in `templates/package.json` and are installed automatically by `npx xertica-ui@latest init`.
16
-
17
- ### 2. Create locale files
18
-
19
- Locales are organized as one **folder per language**, split by category:
20
-
21
- ```
22
- src/locales/
23
- ├── .languages.json ← CLI-managed selection ({ "version": 1, "codes": [...] })
24
- ├── pt-BR/ ← default & fallback (only your selected languages are present)
25
- │ ├── common.json ← shared action labels (view, edit, save, cancel…)
26
- │ ├── nav.json ← navigation labels
27
- │ ├── errors.json ← error boundary UI
28
- │ ├── languageSelector.json ← language picker UI
29
- │ ├── themeToggle.json ← theme toggle UI
30
- │ ├── pages/
31
- │ │ ├── home.json
32
- │ │ ├── templates.json
33
- │ │ ├── login.json
34
- │ │ ├── resetPassword.json
35
- │ │ ├── verifyEmail.json
36
- │ │ ├── loginTemplate.json ← starter template pages
37
- │ │ ├── formTemplate.json
38
- │ │ ├── dashboardTemplate.json
39
- │ │ └── crudTemplate.json
40
- │ └── components/
41
- │ ├── assistant.json
42
- │ ├── sidebar.json
43
- │ ├── media.json
44
- │ ├── projectCard.json
45
- │ ├── profileCard.json
46
- │ ├── notificationCard.json
47
- │ ├── activityCard.json
48
- │ ├── stats.json
49
- │ └── team.json
50
- ├── en/ ← same structure
51
- └── es/ ← same structure
52
- ```
53
-
54
- Each JSON file contains only the keys for its category (no top-level wrapper key). For example, `locales/pt-BR/common.json`:
55
-
56
- ```json
57
- { "view": "Visualizar", "edit": "Editar", "loading": "Carregando...", "cancel": "Cancelar" }
58
- ```
59
-
60
- And `locales/pt-BR/pages/home.json`:
61
-
62
- ```json
63
- { "welcome": "Bem-vindo ao Design System!", "subtitle": "...", "templateCliTitle": "Template CLI" }
64
- ```
65
-
66
- > All files under `locales/<lang>/` are automatically discovered by `import.meta.glob` in `i18n.ts`. Adding a new JSON file requires no changes to `i18n.ts` — Vite picks it up on the next build.
67
-
68
- ### 3. Create `src/i18n.ts`
69
-
70
- ```ts
71
- import i18n from 'i18next';
72
- import { initReactI18next } from 'react-i18next';
73
-
74
- // Merges the split JSON files (pages/, components/) into one flat bundle per language.
75
- // The key is the file basename — folder (pages/components) is discarded.
76
- // Adding a new JSON file under locales/<lang>/... is auto-discovered by Vite.
77
- function bundleLang(chunks: Record<string, unknown>): Record<string, unknown> {
78
- const out: Record<string, unknown> = {};
79
- for (const [filePath, value] of Object.entries(chunks)) {
80
- const base = filePath.split('/').pop();
81
- if (!base) continue;
82
- out[base.replace(/\.json$/, '')] = value;
83
- }
84
- return out;
85
- }
86
-
87
- // `import.meta.glob` requires a literal pattern — one call per language.
88
- // `eager: true` inlines the JSON at build time (no runtime fetch).
89
- const ptBR = bundleLang(
90
- import.meta.glob('./locales/pt-BR/**/*.json', { eager: true, import: 'default' })
91
- );
92
- const en = bundleLang(
93
- import.meta.glob('./locales/en/**/*.json', { eager: true, import: 'default' })
94
- );
95
- const es = bundleLang(
96
- import.meta.glob('./locales/es/**/*.json', { eager: true, import: 'default' })
97
- );
98
-
99
- const savedLanguage =
100
- typeof window !== 'undefined' ? (localStorage.getItem('xertica_language') ?? 'pt-BR') : 'pt-BR';
101
-
102
- i18n.use(initReactI18next).init({
103
- resources: {
104
- 'pt-BR': { translation: ptBR },
105
- en: { translation: en },
106
- es: { translation: es },
107
- },
108
- lng: savedLanguage,
109
- fallbackLng: 'pt-BR',
110
- interpolation: { escapeValue: false }, // React escapes already
111
- });
112
-
113
- export default i18n;
114
- ```
115
-
116
- ### 4. Initialize before rendering
117
-
118
- ```ts
119
- // src/main.tsx — BEFORE any component import
120
- import './i18n'; // side-effect: initializes i18next synchronously
121
- import App from './app/App';
122
- ```
123
-
124
- ---
125
-
126
- ## Using Translations in Components
127
-
128
- ```tsx
129
- import { useTranslation } from 'react-i18next';
130
-
131
- function HomeContent() {
132
- const { t } = useTranslation();
133
-
134
- return (
135
- <div>
136
- <h1>{t('home.welcome')}</h1>
137
- <p>{t('home.subtitle')}</p>
138
- <button>{t('common.view')}</button>
139
- </div>
140
- );
141
- }
142
- ```
143
-
144
- ### Interpolation
145
-
146
- ```json
147
- { "team": { "showing": "Exibindo {{count}} de {{total}} usuários" } }
148
- ```
149
-
150
- ```tsx
151
- t('team.showing', { count: 5, total: 127 });
152
- // → "Exibindo 5 de 127 usuários"
153
- ```
154
-
155
- ---
156
-
157
- ## Language Switching
158
-
159
- The `LanguageSelector` component (from `xertica-ui/brand`) handles the full switching flow automatically:
160
-
161
- 1. User selects a language in the dropdown
162
- 2. `setLanguage(lang)` is called on `LanguageContext`
163
- 3. `LanguageContext` writes to `localStorage` (key: `xertica_language`) and calls `i18n.changeLanguage(lang)`
164
- 4. All components using `useTranslation()` re-render with the new locale
165
- 5. `queryClient.invalidateQueries()` is called — React Query refetches any query whose result contains translated strings
166
-
167
- ```tsx
168
- // Manual language change (without LanguageSelector)
169
- import { useLanguage } from 'xertica-ui/hooks';
170
-
171
- const { setLanguage } = useLanguage();
172
- setLanguage('en'); // persists + calls i18n.changeLanguage('en') + invalidates React Query cache
173
- ```
174
-
175
- ### Language codes
176
-
177
- | Code | Display | Stored as |
178
- | --------- | -------------- | --------------------------- |
179
- | `'pt-BR'` | Português (BR) | `'pt-BR'` in `localStorage` |
180
- | `'en'` | English | `'en'` |
181
- | `'es'` | Español | `'es'` |
182
-
183
- ---
184
-
185
- ## Translation Key Namespaces
186
-
187
- The project uses a single `translation` namespace. Each top-level key maps to a separate JSON file under `locales/<lang>/`:
188
-
189
- **Root files** (`locales/<lang>/<key>.json`):
190
-
191
- | Namespace | File | Example keys |
192
- | ------------------ | ----------------------- | ------------------------------------------------------------------------------- |
193
- | `common` | `common.json` | `common.view`, `common.edit`, `common.loading`, `common.close`, `common.copied` |
194
- | `nav` | `nav.json` | `nav.home`, `nav.designSystem`, `nav.settings` |
195
- | `errors` | `errors.json` | `errors.somethingWentWrong`, `errors.tryAgain`, `errors.pageLoadError` |
196
- | `languageSelector` | `languageSelector.json` | `languageSelector.label`, `languageSelector.ptBR` |
197
- | `themeToggle` | `themeToggle.json` | `themeToggle.switchToLight`, `themeToggle.darkMode` |
198
-
199
- **Page files** (`locales/<lang>/pages/<key>.json`):
200
-
201
- | Namespace | File | Example keys |
202
- | ------------------- | ------------------------------ | --------------------------------------------------------------------------------- |
203
- | `home` | `pages/home.json` | `home.welcome`, `home.subtitle`, `home.templateCliTitle` |
204
- | `templates` | `pages/templates.json` | `templates.title`, `templates.alerts.infoTitle`, `templates.forms.firstName` |
205
- | `login` | `pages/login.json` | `login.heading`, `login.submit`, `login.forgotPassword` |
206
- | `resetPassword` | `pages/resetPassword.json` | `resetPassword.heading`, `resetPassword.errorMismatch` |
207
- | `verifyEmail` | `pages/verifyEmail.json` | `verifyEmail.heading`, `verifyEmail.resend` |
208
- | `loginTemplate` | `pages/loginTemplate.json` | `loginTemplate.title`, `loginTemplate.submit` |
209
- | `formTemplate` | `pages/formTemplate.json` | `formTemplate.title`, `formTemplate.save`, `formTemplate.errors.fullNameRequired` |
210
- | `dashboardTemplate` | `pages/dashboardTemplate.json` | `dashboardTemplate.title`, `dashboardTemplate.stats.totalRevenue` |
211
- | `crudTemplate` | `pages/crudTemplate.json` | `crudTemplate.title`, `crudTemplate.actions.editProfile` |
212
-
213
- **Component files** (`locales/<lang>/components/<key>.json`):
214
-
215
- | Namespace | File | Example keys |
216
- | ------------------ | ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
217
- | `assistant` | `components/assistant.json` | `assistant.title`, `assistant.inputPlaceholder`, `assistant.tabs.chat`, `assistant.feedbackDialog.title` |
218
- | `sidebar` | `components/sidebar.json` | `sidebar.collapse`, `sidebar.logout`, `sidebar.moreOptions` |
219
- | `media` | `components/media.json` | `media.play`, `media.pause`, `media.downloadAudio`, `media.floatingMode` |
220
- | `projectCard` | `components/projectCard.json` | `projectCard.progress`, `projectCard.status.active` |
221
- | `profileCard` | `components/profileCard.json` | `profileCard.status.online`, `profileCard.status.busy` |
222
- | `notificationCard` | `components/notificationCard.json` | `notificationCard.title`, `notificationCard.markAllRead` |
223
- | `activityCard` | `components/activityCard.json` | `activityCard.title`, `activityCard.type.create` |
224
- | `stats` | `components/stats.json` | `stats.totalUsers`, `stats.last30Days` |
225
- | `team` | `components/team.json` | `team.name`, `team.roles.Developer`, `team.showing` |
226
-
227
- ---
228
-
229
- ## Translating Mock Data
230
-
231
- Mock data fetch functions use `i18n.t()` (the instance, not the hook) so they respond to the active language when called from a React Query `queryFn`:
232
-
233
- ```ts
234
- // features/home/data/mock.ts
235
- import i18n from '../../../i18n';
236
-
237
- export async function fetchFeatureCards(): Promise<FeatureCard[]> {
238
- return [
239
- {
240
- id: 'template-cli',
241
- title: i18n.t('home.templateCliTitle'), // ← translated at query time
242
- description: i18n.t('home.templateCliDescription'),
243
- },
244
- ];
245
- }
246
- ```
247
-
248
- ### Language-aware React Query Keys
249
-
250
- Every hook that returns translated strings includes the active language in its `queryKey` so that each locale gets its own cache slot and switches instantly without a page reload:
251
-
252
- ```ts
253
- // features/home/hooks/useFeatureCards.ts
254
- import { useLanguage } from 'xertica-ui/hooks';
255
-
256
- export function useFeatureCards() {
257
- const { language } = useLanguage();
258
- return useQuery({
259
- queryKey: ['home', 'feature-cards', language], // ← language as third element
260
- queryFn: fetchFeatureCards,
261
- staleTime: 10 * 60 * 1000,
262
- });
263
- }
264
- ```
265
-
266
- **Why this works:**
267
-
268
- - Switching from `pt-BR` → `en` changes the queryKey to `['home', 'feature-cards', 'en']`
269
- - React Query finds no cache entry for this key → triggers an immediate refetch
270
- - `fetchFeatureCards()` runs again → `i18n.t()` now returns English strings
271
- - Switching back to `pt-BR` → cache hit (Portuguese data still stored) → instant, no refetch
272
-
273
- The `setLanguage()` call in `LanguageContext` also calls `queryClient.invalidateQueries()` as a defensive backstop for any query not yet updated to include `language` in its key.
274
-
275
- ### Fallback factory functions (not frozen constants)
276
-
277
- When you need static fallback data while a query loads, use **factory functions** (not `const` arrays):
278
-
279
- ```ts
280
- // ✅ Correct — evaluated at call time, always returns current language
281
- export function getMockRichSuggestions(): Suggestion[] {
282
- return [{ id: 'rich-1', text: i18n.t('assistant.richSuggestions.viewPerformance') }];
283
- }
284
-
285
- // ❌ Wrong — i18n.t() runs once at module load, frozen in initial language
286
- export const MOCK_RICH_SUGGESTIONS = [
287
- { id: 'rich-1', text: i18n.t('assistant.richSuggestions.viewPerformance') },
288
- ];
289
- ```
290
-
291
- ```tsx
292
- // Usage in component
293
- richSuggestions={assistantConfig?.richSuggestions ?? getMockRichSuggestions()}
294
- ```
295
-
296
- ---
297
-
298
- ## Configuring Available Languages
299
-
300
- The set of languages a project supports is configured **at runtime** via the `availableLanguages` prop on `<XerticaProvider>` (or `<LanguageProvider>`). The library ships with built-in support for `pt-BR`, `en`, and `es` (exposed as `DEFAULT_LANGUAGES`), but the system is fully extensible.
301
-
302
- ### Choosing languages via the CLI
303
-
304
- When you scaffold a new project, the CLI asks you which languages to enable:
305
-
306
- ```bash
307
- $ npx xertica-ui init my-app
308
- ✔ Which pages/templates to include? › Login, Home, Template
309
- ✔ Which languages should the app support? › Português (BR), English, Español
310
- ✔ Select the default color theme: › Xertica
311
- ```
312
-
313
- Pick **all three** (default), **two**, or **just one**:
314
-
315
- - **All three** — the CLI omits the `availableLanguages` prop entirely (the library default already matches).
316
- - **Two or one** — the CLI injects the explicit `availableLanguages` array into `src/app/App.tsx`.
317
- - **Just one (monolingual)** — additionally, the `LanguageSelector` auto-hides because there is nothing to switch between. A header banner comment in `App.tsx` documents this.
318
-
319
- The CLI also:
320
-
321
- - Copies **only** the locale **folders** for the selected languages into `src/locales/` (no orphan files). Each language is a directory tree with split JSON files.
322
- - Generates `src/i18n.ts` with `import.meta.glob` calls for exactly those languages — Vite auto-discovers all JSON files in the folder at build time.
323
- - Persists the selection in `src/locales/.languages.json` so the `update` command can preserve it.
324
-
325
- ### Adding or removing languages later
326
-
327
- Run `npx xertica-ui update` and choose **Languages**:
328
-
329
- ```bash
330
- $ npx xertica-ui update
331
- ✔ What do you want to update? › Languages
332
-
333
- Current languages: Português (BR), English
334
-
335
- ✔ Select the languages this project should support: › Português (BR), English, Español
336
-
337
- + es
338
-
339
- ⚠️ This will regenerate src/app/App.tsx and src/i18n.ts (preserving language-only changes). Continue? › yes
340
- ✔ Languages updated successfully!
341
- Copied: es/
342
- ```
343
-
344
- The command:
345
-
346
- 1. Computes the add/remove diff and shows it to you.
347
- 2. Copies the newly-added locale **folders** from `node_modules/xertica-ui/templates/src/locales/<lang>/`.
348
- 3. Removes the **folders** of unselected languages from your project (also removes any legacy flat `<lang>.json` files if upgrading from pre-2.2.0).
349
- 4. Regenerates `src/i18n.ts` and `src/app/App.tsx` to reflect the new set.
350
- 5. Updates `src/locales/.languages.json`.
351
-
352
- > The `update` → **Project files** flow also reads `.languages.json` and preserves your selection — updating `App.tsx` and `i18n.ts` won't reset your languages to the defaults.
353
-
354
- ### Default — three built-in languages
355
-
356
- When you don't pass `availableLanguages`, the provider uses `DEFAULT_LANGUAGES`:
357
-
358
- ```tsx
359
- import { XerticaProvider } from 'xertica-ui';
360
-
361
- <XerticaProvider>
362
- {/* pt-BR, en, es are all available — selector shows all three */}
363
- </XerticaProvider>;
364
- ```
365
-
366
- ### Monolingual — single language, no selector
367
-
368
- Pass a single-element array to lock the app to one language. The `LanguageSelector` component **auto-hides** because there is nothing to switch to:
369
-
370
- ```tsx
371
- <XerticaProvider availableLanguages={[{ code: 'en', label: 'English' }]}>
372
- {/* App is permanently English. LanguageSelector renders nothing. */}
373
- </XerticaProvider>
374
- ```
375
-
376
- To force the selector to render even when monolingual (e.g. for a future-proofing placeholder), pass `<LanguageSelector showWhenMonolingual />`.
377
-
378
- ### Adding a new language at runtime
379
-
380
- There are two ways to add a language without editing `src/i18n.ts`.
381
-
382
- **Option A — declarative, via `availableLanguages`** (recommended):
383
-
384
- ```tsx
385
- import { XerticaProvider, DEFAULT_LANGUAGES } from 'xertica-ui';
386
- import fr from './locales/fr.json';
387
-
388
- <XerticaProvider
389
- availableLanguages={[
390
- ...DEFAULT_LANGUAGES,
391
- { code: 'fr', label: 'Français', shortLabel: 'FR', resources: fr },
392
- ]}
393
- >
394
- {/* French now appears in the selector; its strings are registered automatically */}
395
- </XerticaProvider>;
396
- ```
397
-
398
- The provider calls `i18n.addResourceBundle()` on mount for any entry that carries `resources`.
399
-
400
- **Option B — imperative, via `registerLanguageResource`**:
401
-
402
- ```ts
403
- import { registerLanguageResource } from 'xertica-ui';
404
- import fr from './locales/fr.json';
405
-
406
- registerLanguageResource('fr', fr);
407
- // Then list it on the provider
408
- <XerticaProvider availableLanguages={[
409
- ...DEFAULT_LANGUAGES,
410
- { code: 'fr', label: 'Français', shortLabel: 'FR' },
411
- ]}>
412
- ```
413
-
414
- ### Removing built-in languages
415
-
416
- Just omit them from `availableLanguages`. The translation bundles registered in `src/i18n.ts` remain loaded (they're cheap) but they're invisible to the UI:
417
-
418
- ```tsx
419
- <XerticaProvider
420
- availableLanguages={[
421
- { code: 'pt-BR', label: 'Português' },
422
- { code: 'en', label: 'English' },
423
- ]}
424
- >
425
- {/* Only Portuguese and English appear — Spanish is hidden */}
426
- </XerticaProvider>
427
- ```
428
-
429
- ### Statically (library-only) — adding a built-in default
430
-
431
- Only used when contributing to the library itself, not for app consumers:
432
-
433
- 1. Create `locales/<code>/` folder mirroring an existing language (copy `locales/en/` as a starting point, then translate all values)
434
- 2. `i18n.ts` uses `import.meta.glob` — **no import changes needed**. Vite auto-discovers the new folder on the next build.
435
- 3. Add a `LanguageDefinition` entry to `DEFAULT_LANGUAGES` in `contexts/LanguageContext.tsx`
436
- 4. Add an entry to `SUPPORTED_LANGUAGES` in `bin/language-config.ts` so the CLI exposes it in its prompts
437
- 5. Mirror the same folder structure under `templates/src/locales/<code>/` for the scaffold template
438
-
439
- ### `LanguageDefinition` shape
440
-
441
- ```ts
442
- interface LanguageDefinition {
443
- /** BCP-47 language code stored in localStorage and passed to i18n.changeLanguage() */
444
- code: string;
445
- /** Full display label shown in the LanguageSelector dropdown */
446
- label: string;
447
- /** Short label shown in `variant="minimal"` (e.g. "PT", "EN"). Defaults to code.slice(0,2).toUpperCase() */
448
- shortLabel?: string;
449
- /** Optional translation JSON. When provided, it is registered with i18next automatically. */
450
- resources?: Record<string, unknown>;
451
- }
452
- ```
453
-
454
- ### `useLanguage()` extras
455
-
456
- ```ts
457
- const {
458
- language, // current locale code
459
- setLanguage, // change locale + persist + invalidate React Query
460
- availableLanguages, // LanguageDefinition[] currently configured
461
- isMonolingual, // true when availableLanguages.length === 1
462
- } = useLanguage();
463
- ```
464
-
465
- ---
466
-
467
- ## AI Rules
468
-
469
- - **ALWAYS import `'./i18n'` in `main.tsx` before any component** — if omitted, `useTranslation()` falls back to returning the raw key string
470
- - **Use `t('namespace.key')` for all user-facing strings** — never hardcode text in JSX
471
- - **Use `i18n.t()` (the instance) in non-component code** (mock fetch functions, utility files) — `useTranslation()` is a hook and can only be called inside React components and custom hooks
472
- - **Include `language` in the `queryKey`** for every React Query hook that returns translated strings — this gives each locale its own cache slot and enables instant switching without reload
473
- - **Use factory functions, not frozen constants**, when you need pre-translated fallback arrays — `const arr = [i18n.t(...)]` evaluates once at module load and is permanently frozen
474
- - **Never hardcode language fallbacks in JSX** — add missing keys to the appropriate file under `locales/<lang>/` instead
475
- - `setLanguage()` from `useLanguage()` is the single point of control — it persists to `localStorage`, calls `i18n.changeLanguage()`, and invalidates the React Query cache
476
- - Import `useLanguage` from `'xertica-ui/hooks'` (or directly from `'xertica-ui'`) — both resolve to the same export. The `xertica-ui/brand` subpath exports only the `LanguageSelector` component and `Language` type, NOT the `useLanguage` hook.
477
- - The `fallbackLng: 'pt-BR'` ensures missing keys in `en.json` or `es.json` display the Portuguese fallback instead of the raw key string
478
- - **The set of available languages is runtime-configurable** — pass `availableLanguages` to `<XerticaProvider>` to add, remove, or restrict to a single locale. Never modify `DEFAULT_LANGUAGES` directly
479
- - **The `LanguageSelector` auto-hides when `isMonolingual` is true** — do not conditionally render it yourself; pass it `showWhenMonolingual` if you want it visible
480
- - **`Language` is typed as `string`** (not a strict union) to support runtime-added locales. Use `LanguageDefinition['code']` from `useLanguage().availableLanguages` for autocomplete
1
+ # Internationalization (i18n) — Xertica UI
2
+
3
+ Xertica UI projects use **`i18next`** with the **`react-i18next`** binding for all UI string translations. The `LanguageSelector` component is wired directly to `i18next` — changing the language immediately updates every `useTranslation()` consumer in the app and invalidates the React Query cache so that data-layer strings (mock API responses) also refresh.
4
+
5
+ ---
6
+
7
+ ## Setup
8
+
9
+ ### 1. Install
10
+
11
+ ```bash
12
+ npm install i18next react-i18next
13
+ ```
14
+
15
+ Both packages are listed in `templates/package.json` and are installed automatically by `npx xertica-ui@latest init`.
16
+
17
+ ### 2. Create locale files
18
+
19
+ Locales are organized as one **folder per language**, split by category:
20
+
21
+ ```
22
+ src/locales/
23
+ ├── .languages.json ← CLI-managed selection ({ "version": 1, "codes": [...] })
24
+ ├── pt-BR/ ← default & fallback (only your selected languages are present)
25
+ │ ├── common.json ← shared action labels (view, edit, save, cancel…)
26
+ │ ├── nav.json ← navigation labels
27
+ │ ├── errors.json ← error boundary UI
28
+ │ ├── languageSelector.json ← language picker UI
29
+ │ ├── themeToggle.json ← theme toggle UI
30
+ │ ├── pages/
31
+ │ │ ├── home.json
32
+ │ │ ├── templates.json
33
+ │ │ ├── login.json
34
+ │ │ ├── resetPassword.json
35
+ │ │ ├── verifyEmail.json
36
+ │ │ ├── loginTemplate.json ← starter template pages
37
+ │ │ ├── formTemplate.json
38
+ │ │ ├── dashboardTemplate.json
39
+ │ │ └── crudTemplate.json
40
+ │ └── components/
41
+ │ ├── assistant.json
42
+ │ ├── sidebar.json
43
+ │ ├── media.json
44
+ │ ├── projectCard.json
45
+ │ ├── profileCard.json
46
+ │ ├── notificationCard.json
47
+ │ ├── activityCard.json
48
+ │ ├── stats.json
49
+ │ └── team.json
50
+ ├── en/ ← same structure
51
+ └── es/ ← same structure
52
+ ```
53
+
54
+ Each JSON file contains only the keys for its category (no top-level wrapper key). For example, `locales/pt-BR/common.json`:
55
+
56
+ ```json
57
+ { "view": "Visualizar", "edit": "Editar", "loading": "Carregando...", "cancel": "Cancelar" }
58
+ ```
59
+
60
+ And `locales/pt-BR/pages/home.json`:
61
+
62
+ ```json
63
+ { "welcome": "Bem-vindo ao Design System!", "subtitle": "...", "templateCliTitle": "Template CLI" }
64
+ ```
65
+
66
+ > All files under `locales/<lang>/` are automatically discovered by `import.meta.glob` in `i18n.ts`. Adding a new JSON file requires no changes to `i18n.ts` — Vite picks it up on the next build.
67
+
68
+ ### 3. Create `src/i18n.ts`
69
+
70
+ ```ts
71
+ import i18n from 'i18next';
72
+ import { initReactI18next } from 'react-i18next';
73
+
74
+ // Merges the split JSON files (pages/, components/) into one flat bundle per language.
75
+ // The key is the file basename — folder (pages/components) is discarded.
76
+ // Adding a new JSON file under locales/<lang>/... is auto-discovered by Vite.
77
+ function bundleLang(chunks: Record<string, unknown>): Record<string, unknown> {
78
+ const out: Record<string, unknown> = {};
79
+ for (const [filePath, value] of Object.entries(chunks)) {
80
+ const base = filePath.split('/').pop();
81
+ if (!base) continue;
82
+ out[base.replace(/\.json$/, '')] = value;
83
+ }
84
+ return out;
85
+ }
86
+
87
+ // `import.meta.glob` requires a literal pattern — one call per language.
88
+ // `eager: true` inlines the JSON at build time (no runtime fetch).
89
+ const ptBR = bundleLang(
90
+ import.meta.glob('./locales/pt-BR/**/*.json', { eager: true, import: 'default' })
91
+ );
92
+ const en = bundleLang(
93
+ import.meta.glob('./locales/en/**/*.json', { eager: true, import: 'default' })
94
+ );
95
+ const es = bundleLang(
96
+ import.meta.glob('./locales/es/**/*.json', { eager: true, import: 'default' })
97
+ );
98
+
99
+ const savedLanguage =
100
+ typeof window !== 'undefined' ? (localStorage.getItem('xertica_language') ?? 'pt-BR') : 'pt-BR';
101
+
102
+ i18n.use(initReactI18next).init({
103
+ resources: {
104
+ 'pt-BR': { translation: ptBR },
105
+ en: { translation: en },
106
+ es: { translation: es },
107
+ },
108
+ lng: savedLanguage,
109
+ fallbackLng: 'pt-BR',
110
+ interpolation: { escapeValue: false }, // React escapes already
111
+ });
112
+
113
+ export default i18n;
114
+ ```
115
+
116
+ ### 4. Initialize before rendering
117
+
118
+ ```ts
119
+ // src/main.tsx — BEFORE any component import
120
+ import './i18n'; // side-effect: initializes i18next synchronously
121
+ import App from './app/App';
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Using Translations in Components
127
+
128
+ ```tsx
129
+ import { useTranslation } from 'react-i18next';
130
+
131
+ function HomeContent() {
132
+ const { t } = useTranslation();
133
+
134
+ return (
135
+ <div>
136
+ <h1>{t('home.welcome')}</h1>
137
+ <p>{t('home.subtitle')}</p>
138
+ <button>{t('common.view')}</button>
139
+ </div>
140
+ );
141
+ }
142
+ ```
143
+
144
+ ### Interpolation
145
+
146
+ ```json
147
+ { "team": { "showing": "Exibindo {{count}} de {{total}} usuários" } }
148
+ ```
149
+
150
+ ```tsx
151
+ t('team.showing', { count: 5, total: 127 });
152
+ // → "Exibindo 5 de 127 usuários"
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Language Switching
158
+
159
+ The `LanguageSelector` component (from `xertica-ui/brand`) handles the full switching flow automatically:
160
+
161
+ 1. User selects a language in the dropdown
162
+ 2. `setLanguage(lang)` is called on `LanguageContext`
163
+ 3. `LanguageContext` writes to `localStorage` (key: `xertica_language`) and calls `i18n.changeLanguage(lang)`
164
+ 4. All components using `useTranslation()` re-render with the new locale
165
+ 5. `queryClient.invalidateQueries()` is called — React Query refetches any query whose result contains translated strings
166
+
167
+ ```tsx
168
+ // Manual language change (without LanguageSelector)
169
+ import { useLanguage } from 'xertica-ui/hooks';
170
+
171
+ const { setLanguage } = useLanguage();
172
+ setLanguage('en'); // persists + calls i18n.changeLanguage('en') + invalidates React Query cache
173
+ ```
174
+
175
+ ### Language codes
176
+
177
+ | Code | Display | Stored as |
178
+ | --------- | -------------- | --------------------------- |
179
+ | `'pt-BR'` | Português (BR) | `'pt-BR'` in `localStorage` |
180
+ | `'en'` | English | `'en'` |
181
+ | `'es'` | Español | `'es'` |
182
+
183
+ ---
184
+
185
+ ## Translation Key Namespaces
186
+
187
+ The project uses a single `translation` namespace. Each top-level key maps to a separate JSON file under `locales/<lang>/`:
188
+
189
+ **Root files** (`locales/<lang>/<key>.json`):
190
+
191
+ | Namespace | File | Example keys |
192
+ | ------------------ | ----------------------- | ------------------------------------------------------------------------------- |
193
+ | `common` | `common.json` | `common.view`, `common.edit`, `common.loading`, `common.close`, `common.copied` |
194
+ | `nav` | `nav.json` | `nav.home`, `nav.designSystem`, `nav.settings` |
195
+ | `errors` | `errors.json` | `errors.somethingWentWrong`, `errors.tryAgain`, `errors.pageLoadError` |
196
+ | `languageSelector` | `languageSelector.json` | `languageSelector.label`, `languageSelector.ptBR` |
197
+ | `themeToggle` | `themeToggle.json` | `themeToggle.switchToLight`, `themeToggle.darkMode` |
198
+
199
+ **Page files** (`locales/<lang>/pages/<key>.json`):
200
+
201
+ | Namespace | File | Example keys |
202
+ | ------------------- | ------------------------------ | --------------------------------------------------------------------------------- |
203
+ | `home` | `pages/home.json` | `home.welcome`, `home.subtitle`, `home.templateCliTitle` |
204
+ | `templates` | `pages/templates.json` | `templates.title`, `templates.alerts.infoTitle`, `templates.forms.firstName` |
205
+ | `login` | `pages/login.json` | `login.heading`, `login.submit`, `login.forgotPassword` |
206
+ | `resetPassword` | `pages/resetPassword.json` | `resetPassword.heading`, `resetPassword.errorMismatch` |
207
+ | `verifyEmail` | `pages/verifyEmail.json` | `verifyEmail.heading`, `verifyEmail.resend` |
208
+ | `loginTemplate` | `pages/loginTemplate.json` | `loginTemplate.title`, `loginTemplate.submit` |
209
+ | `formTemplate` | `pages/formTemplate.json` | `formTemplate.title`, `formTemplate.save`, `formTemplate.errors.fullNameRequired` |
210
+ | `dashboardTemplate` | `pages/dashboardTemplate.json` | `dashboardTemplate.title`, `dashboardTemplate.stats.totalRevenue` |
211
+ | `crudTemplate` | `pages/crudTemplate.json` | `crudTemplate.title`, `crudTemplate.actions.editProfile` |
212
+
213
+ **Component files** (`locales/<lang>/components/<key>.json`):
214
+
215
+ | Namespace | File | Example keys |
216
+ | ------------------ | ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
217
+ | `assistant` | `components/assistant.json` | `assistant.title`, `assistant.inputPlaceholder`, `assistant.tabs.chat`, `assistant.feedbackDialog.title` |
218
+ | `sidebar` | `components/sidebar.json` | `sidebar.collapse`, `sidebar.logout`, `sidebar.moreOptions` |
219
+ | `media` | `components/media.json` | `media.play`, `media.pause`, `media.downloadAudio`, `media.floatingMode` |
220
+ | `projectCard` | `components/projectCard.json` | `projectCard.progress`, `projectCard.status.active` |
221
+ | `profileCard` | `components/profileCard.json` | `profileCard.status.online`, `profileCard.status.busy` |
222
+ | `notificationCard` | `components/notificationCard.json` | `notificationCard.title`, `notificationCard.markAllRead` |
223
+ | `activityCard` | `components/activityCard.json` | `activityCard.title`, `activityCard.type.create` |
224
+ | `stats` | `components/stats.json` | `stats.totalUsers`, `stats.last30Days` |
225
+ | `team` | `components/team.json` | `team.name`, `team.roles.Developer`, `team.showing` |
226
+
227
+ ---
228
+
229
+ ## Translating Mock Data
230
+
231
+ Mock data fetch functions use `i18n.t()` (the instance, not the hook) so they respond to the active language when called from a React Query `queryFn`:
232
+
233
+ ```ts
234
+ // features/home/data/mock.ts
235
+ import i18n from '../../../i18n';
236
+
237
+ export async function fetchFeatureCards(): Promise<FeatureCard[]> {
238
+ return [
239
+ {
240
+ id: 'template-cli',
241
+ title: i18n.t('home.templateCliTitle'), // ← translated at query time
242
+ description: i18n.t('home.templateCliDescription'),
243
+ },
244
+ ];
245
+ }
246
+ ```
247
+
248
+ ### Language-aware React Query Keys
249
+
250
+ Every hook that returns translated strings includes the active language in its `queryKey` so that each locale gets its own cache slot and switches instantly without a page reload:
251
+
252
+ ```ts
253
+ // features/home/hooks/useFeatureCards.ts
254
+ import { useLanguage } from 'xertica-ui/hooks';
255
+
256
+ export function useFeatureCards() {
257
+ const { language } = useLanguage();
258
+ return useQuery({
259
+ queryKey: ['home', 'feature-cards', language], // ← language as third element
260
+ queryFn: fetchFeatureCards,
261
+ staleTime: 10 * 60 * 1000,
262
+ });
263
+ }
264
+ ```
265
+
266
+ **Why this works:**
267
+
268
+ - Switching from `pt-BR` → `en` changes the queryKey to `['home', 'feature-cards', 'en']`
269
+ - React Query finds no cache entry for this key → triggers an immediate refetch
270
+ - `fetchFeatureCards()` runs again → `i18n.t()` now returns English strings
271
+ - Switching back to `pt-BR` → cache hit (Portuguese data still stored) → instant, no refetch
272
+
273
+ The `setLanguage()` call in `LanguageContext` also calls `queryClient.invalidateQueries()` as a defensive backstop for any query not yet updated to include `language` in its key.
274
+
275
+ ### Fallback factory functions (not frozen constants)
276
+
277
+ When you need static fallback data while a query loads, use **factory functions** (not `const` arrays):
278
+
279
+ ```ts
280
+ // ✅ Correct — evaluated at call time, always returns current language
281
+ export function getMockRichSuggestions(): Suggestion[] {
282
+ return [{ id: 'rich-1', text: i18n.t('assistant.richSuggestions.viewPerformance') }];
283
+ }
284
+
285
+ // ❌ Wrong — i18n.t() runs once at module load, frozen in initial language
286
+ export const MOCK_RICH_SUGGESTIONS = [
287
+ { id: 'rich-1', text: i18n.t('assistant.richSuggestions.viewPerformance') },
288
+ ];
289
+ ```
290
+
291
+ ```tsx
292
+ // Usage in component
293
+ richSuggestions={assistantConfig?.richSuggestions ?? getMockRichSuggestions()}
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Configuring Available Languages
299
+
300
+ The set of languages a project supports is configured **at runtime** via the `availableLanguages` prop on `<XerticaProvider>` (or `<LanguageProvider>`). The library ships with built-in support for `pt-BR`, `en`, and `es` (exposed as `DEFAULT_LANGUAGES`), but the system is fully extensible.
301
+
302
+ ### Choosing languages via the CLI
303
+
304
+ When you scaffold a new project, the CLI asks you which languages to enable:
305
+
306
+ ```bash
307
+ $ npx xertica-ui init my-app
308
+ ✔ Which pages/templates to include? › Login, Home, Template
309
+ ✔ Which languages should the app support? › Português (BR), English, Español
310
+ ✔ Select the default color theme: › Xertica
311
+ ```
312
+
313
+ Pick **all three** (default), **two**, or **just one**:
314
+
315
+ - **All three** — the CLI omits the `availableLanguages` prop entirely (the library default already matches).
316
+ - **Two or one** — the CLI injects the explicit `availableLanguages` array into `src/app/App.tsx`.
317
+ - **Just one (monolingual)** — additionally, the `LanguageSelector` auto-hides because there is nothing to switch between. A header banner comment in `App.tsx` documents this.
318
+
319
+ The CLI also:
320
+
321
+ - Copies **only** the locale **folders** for the selected languages into `src/locales/` (no orphan files). Each language is a directory tree with split JSON files.
322
+ - Generates `src/i18n.ts` with `import.meta.glob` calls for exactly those languages — Vite auto-discovers all JSON files in the folder at build time.
323
+ - Persists the selection in `src/locales/.languages.json` so the `update` command can preserve it.
324
+
325
+ ### Adding or removing languages later
326
+
327
+ Run `npx xertica-ui update` and choose **Languages**:
328
+
329
+ ```bash
330
+ $ npx xertica-ui update
331
+ ✔ What do you want to update? › Languages
332
+
333
+ Current languages: Português (BR), English
334
+
335
+ ✔ Select the languages this project should support: › Português (BR), English, Español
336
+
337
+ + es
338
+
339
+ ⚠️ This will regenerate src/app/App.tsx and src/i18n.ts (preserving language-only changes). Continue? › yes
340
+ ✔ Languages updated successfully!
341
+ Copied: es/
342
+ ```
343
+
344
+ The command:
345
+
346
+ 1. Computes the add/remove diff and shows it to you.
347
+ 2. Copies the newly-added locale **folders** from `node_modules/xertica-ui/templates/src/locales/<lang>/`.
348
+ 3. Removes the **folders** of unselected languages from your project (also removes any legacy flat `<lang>.json` files if upgrading from pre-2.2.0).
349
+ 4. Regenerates `src/i18n.ts` and `src/app/App.tsx` to reflect the new set.
350
+ 5. Updates `src/locales/.languages.json`.
351
+
352
+ > The `update` → **Project files** flow also reads `.languages.json` and preserves your selection — updating `App.tsx` and `i18n.ts` won't reset your languages to the defaults.
353
+
354
+ ### Default — three built-in languages
355
+
356
+ When you don't pass `availableLanguages`, the provider uses `DEFAULT_LANGUAGES`:
357
+
358
+ ```tsx
359
+ import { XerticaProvider } from 'xertica-ui';
360
+
361
+ <XerticaProvider>
362
+ {/* pt-BR, en, es are all available — selector shows all three */}
363
+ </XerticaProvider>;
364
+ ```
365
+
366
+ ### Monolingual — single language, no selector
367
+
368
+ Pass a single-element array to lock the app to one language. The `LanguageSelector` component **auto-hides** because there is nothing to switch to:
369
+
370
+ ```tsx
371
+ <XerticaProvider availableLanguages={[{ code: 'en', label: 'English' }]}>
372
+ {/* App is permanently English. LanguageSelector renders nothing. */}
373
+ </XerticaProvider>
374
+ ```
375
+
376
+ To force the selector to render even when monolingual (e.g. for a future-proofing placeholder), pass `<LanguageSelector showWhenMonolingual />`.
377
+
378
+ ### Adding a new language at runtime
379
+
380
+ There are two ways to add a language without editing `src/i18n.ts`.
381
+
382
+ **Option A — declarative, via `availableLanguages`** (recommended):
383
+
384
+ ```tsx
385
+ import { XerticaProvider, DEFAULT_LANGUAGES } from 'xertica-ui';
386
+ import fr from './locales/fr.json';
387
+
388
+ <XerticaProvider
389
+ availableLanguages={[
390
+ ...DEFAULT_LANGUAGES,
391
+ { code: 'fr', label: 'Français', shortLabel: 'FR', resources: fr },
392
+ ]}
393
+ >
394
+ {/* French now appears in the selector; its strings are registered automatically */}
395
+ </XerticaProvider>;
396
+ ```
397
+
398
+ The provider calls `i18n.addResourceBundle()` on mount for any entry that carries `resources`.
399
+
400
+ **Option B — imperative, via `registerLanguageResource`**:
401
+
402
+ ```ts
403
+ import { registerLanguageResource } from 'xertica-ui';
404
+ import fr from './locales/fr.json';
405
+
406
+ registerLanguageResource('fr', fr);
407
+ // Then list it on the provider
408
+ <XerticaProvider availableLanguages={[
409
+ ...DEFAULT_LANGUAGES,
410
+ { code: 'fr', label: 'Français', shortLabel: 'FR' },
411
+ ]}>
412
+ ```
413
+
414
+ ### Removing built-in languages
415
+
416
+ Just omit them from `availableLanguages`. The translation bundles registered in `src/i18n.ts` remain loaded (they're cheap) but they're invisible to the UI:
417
+
418
+ ```tsx
419
+ <XerticaProvider
420
+ availableLanguages={[
421
+ { code: 'pt-BR', label: 'Português' },
422
+ { code: 'en', label: 'English' },
423
+ ]}
424
+ >
425
+ {/* Only Portuguese and English appear — Spanish is hidden */}
426
+ </XerticaProvider>
427
+ ```
428
+
429
+ ### Statically (library-only) — adding a built-in default
430
+
431
+ Only used when contributing to the library itself, not for app consumers:
432
+
433
+ 1. Create `locales/<code>/` folder mirroring an existing language (copy `locales/en/` as a starting point, then translate all values)
434
+ 2. `i18n.ts` uses `import.meta.glob` — **no import changes needed**. Vite auto-discovers the new folder on the next build.
435
+ 3. Add a `LanguageDefinition` entry to `DEFAULT_LANGUAGES` in `contexts/LanguageContext.tsx`
436
+ 4. Add an entry to `SUPPORTED_LANGUAGES` in `bin/language-config.ts` so the CLI exposes it in its prompts
437
+ 5. Mirror the same folder structure under `templates/src/locales/<code>/` for the scaffold template
438
+
439
+ ### `LanguageDefinition` shape
440
+
441
+ ```ts
442
+ interface LanguageDefinition {
443
+ /** BCP-47 language code stored in localStorage and passed to i18n.changeLanguage() */
444
+ code: string;
445
+ /** Full display label shown in the LanguageSelector dropdown */
446
+ label: string;
447
+ /** Short label shown in `variant="minimal"` (e.g. "PT", "EN"). Defaults to code.slice(0,2).toUpperCase() */
448
+ shortLabel?: string;
449
+ /** Optional translation JSON. When provided, it is registered with i18next automatically. */
450
+ resources?: Record<string, unknown>;
451
+ }
452
+ ```
453
+
454
+ ### `useLanguage()` extras
455
+
456
+ ```ts
457
+ const {
458
+ language, // current locale code
459
+ setLanguage, // change locale + persist + invalidate React Query
460
+ availableLanguages, // LanguageDefinition[] currently configured
461
+ isMonolingual, // true when availableLanguages.length === 1
462
+ } = useLanguage();
463
+ ```
464
+
465
+ ---
466
+
467
+ ## AI Rules
468
+
469
+ - **ALWAYS import `'./i18n'` in `main.tsx` before any component** — if omitted, `useTranslation()` falls back to returning the raw key string
470
+ - **Use `t('namespace.key')` for all user-facing strings** — never hardcode text in JSX
471
+ - **Use `i18n.t()` (the instance) in non-component code** (mock fetch functions, utility files) — `useTranslation()` is a hook and can only be called inside React components and custom hooks
472
+ - **Include `language` in the `queryKey`** for every React Query hook that returns translated strings — this gives each locale its own cache slot and enables instant switching without reload
473
+ - **Use factory functions, not frozen constants**, when you need pre-translated fallback arrays — `const arr = [i18n.t(...)]` evaluates once at module load and is permanently frozen
474
+ - **Never hardcode language fallbacks in JSX** — add missing keys to the appropriate file under `locales/<lang>/` instead
475
+ - `setLanguage()` from `useLanguage()` is the single point of control — it persists to `localStorage`, calls `i18n.changeLanguage()`, and invalidates the React Query cache
476
+ - Import `useLanguage` from `'xertica-ui/hooks'` (or directly from `'xertica-ui'`) — both resolve to the same export. The `xertica-ui/brand` subpath exports only the `LanguageSelector` component and `Language` type, NOT the `useLanguage` hook.
477
+ - The `fallbackLng: 'pt-BR'` ensures missing keys in `en.json` or `es.json` display the Portuguese fallback instead of the raw key string
478
+ - **The set of available languages is runtime-configurable** — pass `availableLanguages` to `<XerticaProvider>` to add, remove, or restrict to a single locale. Never modify `DEFAULT_LANGUAGES` directly
479
+ - **The `LanguageSelector` auto-hides when `isMonolingual` is true** — do not conditionally render it yourself; pass it `showWhenMonolingual` if you want it visible
480
+ - **`Language` is typed as `string`** (not a strict union) to support runtime-added locales. Use `LanguageDefinition['code']` from `useLanguage().availableLanguages` for autocomplete