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