create-fate 1.3.3 → 1.5.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 (298) hide show
  1. package/bin/create-fate.mjs +41 -3
  2. package/package.json +1 -1
  3. package/templates/fate/_shared/vue-app/package.json +14 -16
  4. package/templates/fate/cloudflare/README.md +11 -0
  5. package/templates/fate/cloudflare/client/node_modules/.bin/browserslist +22 -0
  6. package/templates/fate/cloudflare/client/node_modules/.bin/cf-wrangler +22 -0
  7. package/templates/fate/cloudflare/client/node_modules/.bin/drizzle-kit +22 -0
  8. package/templates/fate/cloudflare/client/node_modules/.bin/esbuild +22 -0
  9. package/templates/fate/cloudflare/client/node_modules/.bin/fate +22 -0
  10. package/templates/fate/cloudflare/client/node_modules/.bin/fbtee +22 -0
  11. package/templates/fate/cloudflare/client/node_modules/.bin/intent +22 -0
  12. package/templates/fate/cloudflare/client/node_modules/.bin/jiti +22 -0
  13. package/templates/fate/cloudflare/client/node_modules/.bin/oxfmt +22 -0
  14. package/templates/fate/cloudflare/client/node_modules/.bin/oxlint +22 -0
  15. package/templates/fate/cloudflare/client/node_modules/.bin/rolldown +22 -0
  16. package/templates/fate/cloudflare/client/node_modules/.bin/tsc +22 -0
  17. package/templates/fate/cloudflare/client/node_modules/.bin/tsx +22 -0
  18. package/templates/fate/cloudflare/client/node_modules/.bin/vitest +22 -0
  19. package/templates/fate/cloudflare/client/node_modules/.bin/void +22 -0
  20. package/templates/fate/cloudflare/client/node_modules/.bin/vp +22 -0
  21. package/templates/fate/cloudflare/client/node_modules/.bin/vpr +22 -0
  22. package/templates/fate/cloudflare/client/node_modules/.bin/workerd +22 -0
  23. package/templates/fate/cloudflare/client/node_modules/.bin/wrangler +22 -0
  24. package/templates/fate/cloudflare/client/node_modules/.bin/wrangler2 +22 -0
  25. package/templates/fate/cloudflare/client/package.json +26 -21
  26. package/templates/fate/cloudflare/client/pages/layout.tsx +2 -16
  27. package/templates/fate/cloudflare/client/src/lib/AvailableLanguages.tsx +3 -3
  28. package/templates/fate/cloudflare/client/src/lib/LocaleContext.tsx +61 -0
  29. package/templates/fate/cloudflare/client/src/ui/LocaleSwitcher.tsx +26 -44
  30. package/templates/fate/cloudflare/client/start.mjs +11 -0
  31. package/templates/fate/cloudflare/client/translations/{de_DE.json → de-DE.json} +56 -113
  32. package/templates/fate/{graphql/client/translations/ja_JP.json → cloudflare/client/translations/ja-JP.json} +56 -113
  33. package/templates/fate/cloudflare/client/vite.config.ts +4 -8
  34. package/templates/fate/cloudflare/package.json +6 -10
  35. package/templates/fate/cloudflare/pnpm-workspace.yaml +4 -4
  36. package/templates/fate/cloudflare/server/db/migrations/20260508073357_initial_schema.sql +3 -2
  37. package/templates/fate/cloudflare/server/db/migrations/20260508120500_seed_cloudflare_demo.sql +10 -10
  38. package/templates/fate/cloudflare/server/db/migrations/meta/20260508073357_snapshot.json +10 -3
  39. package/templates/fate/cloudflare/server/db/schema.ts +2 -3
  40. package/templates/fate/cloudflare/server/node_modules/.bin/cf-wrangler +22 -0
  41. package/templates/fate/cloudflare/server/node_modules/.bin/drizzle-kit +22 -0
  42. package/templates/fate/cloudflare/server/node_modules/.bin/esbuild +22 -0
  43. package/templates/fate/cloudflare/server/node_modules/.bin/fate +22 -0
  44. package/templates/fate/cloudflare/server/node_modules/.bin/intent +22 -0
  45. package/templates/fate/cloudflare/server/node_modules/.bin/jiti +22 -0
  46. package/templates/fate/cloudflare/server/node_modules/.bin/oxfmt +22 -0
  47. package/templates/fate/cloudflare/server/node_modules/.bin/oxlint +22 -0
  48. package/templates/fate/cloudflare/server/node_modules/.bin/tsc +22 -0
  49. package/templates/fate/cloudflare/server/node_modules/.bin/tsx +22 -0
  50. package/templates/fate/cloudflare/server/node_modules/.bin/vitest +22 -0
  51. package/templates/fate/cloudflare/server/node_modules/.bin/vp +22 -0
  52. package/templates/fate/cloudflare/server/node_modules/.bin/vpr +22 -0
  53. package/templates/fate/cloudflare/server/node_modules/.bin/workerd +22 -0
  54. package/templates/fate/cloudflare/server/node_modules/.bin/wrangler +22 -0
  55. package/templates/fate/cloudflare/server/node_modules/.bin/wrangler2 +22 -0
  56. package/templates/fate/cloudflare/server/package.json +3 -3
  57. package/templates/fate/cloudflare/server/src/fate/server.ts +5 -2
  58. package/templates/fate/cloudflare/server/src/lib/auth.ts +0 -2
  59. package/templates/fate/cloudflare/server/tsconfig.json +1 -1
  60. package/templates/fate/cloudflare/tsconfig.json +2 -11
  61. package/templates/fate/cloudflare/vite.config.ts +3 -0
  62. package/templates/fate/drizzle/README.md +11 -0
  63. package/templates/fate/drizzle/client/node_modules/.bin/browserslist +22 -0
  64. package/templates/fate/drizzle/client/node_modules/.bin/cf-wrangler +22 -0
  65. package/templates/fate/drizzle/client/node_modules/.bin/drizzle-kit +22 -0
  66. package/templates/fate/drizzle/client/node_modules/.bin/esbuild +22 -0
  67. package/templates/fate/drizzle/client/node_modules/.bin/fate +22 -0
  68. package/templates/fate/drizzle/client/node_modules/.bin/fbtee +22 -0
  69. package/templates/fate/drizzle/client/node_modules/.bin/intent +22 -0
  70. package/templates/fate/drizzle/client/node_modules/.bin/jiti +22 -0
  71. package/templates/fate/drizzle/client/node_modules/.bin/oxfmt +22 -0
  72. package/templates/fate/drizzle/client/node_modules/.bin/oxlint +22 -0
  73. package/templates/fate/drizzle/client/node_modules/.bin/rolldown +22 -0
  74. package/templates/fate/drizzle/client/node_modules/.bin/tsc +22 -0
  75. package/templates/fate/drizzle/client/node_modules/.bin/tsx +22 -0
  76. package/templates/fate/drizzle/client/node_modules/.bin/vitest +22 -0
  77. package/templates/fate/drizzle/client/node_modules/.bin/void +22 -0
  78. package/templates/fate/drizzle/client/node_modules/.bin/vp +22 -0
  79. package/templates/fate/drizzle/client/node_modules/.bin/vpr +22 -0
  80. package/templates/fate/drizzle/client/node_modules/.bin/workerd +22 -0
  81. package/templates/fate/drizzle/client/node_modules/.bin/wrangler +22 -0
  82. package/templates/fate/drizzle/client/node_modules/.bin/wrangler2 +22 -0
  83. package/templates/fate/drizzle/client/package.json +28 -24
  84. package/templates/fate/drizzle/client/pages/layout.tsx +2 -16
  85. package/templates/fate/drizzle/client/src/lib/AvailableLanguages.tsx +3 -3
  86. package/templates/fate/drizzle/client/src/lib/LocaleContext.tsx +61 -0
  87. package/templates/fate/drizzle/client/src/ui/LocaleSwitcher.tsx +26 -44
  88. package/templates/fate/drizzle/client/start.mjs +11 -0
  89. package/templates/fate/{graphql/client/translations/de_DE.json → drizzle/client/translations/de-DE.json} +56 -113
  90. package/templates/fate/{cloudflare/client/translations/ja_JP.json → drizzle/client/translations/ja-JP.json} +56 -113
  91. package/templates/fate/drizzle/client/vite.config.ts +4 -8
  92. package/templates/fate/drizzle/package.json +6 -10
  93. package/templates/fate/drizzle/pnpm-workspace.yaml +4 -4
  94. package/templates/fate/drizzle/server/node_modules/.bin/drizzle-kit +22 -0
  95. package/templates/fate/drizzle/server/node_modules/.bin/esbuild +22 -0
  96. package/templates/fate/drizzle/server/node_modules/.bin/fate +22 -0
  97. package/templates/fate/drizzle/server/node_modules/.bin/intent +22 -0
  98. package/templates/fate/drizzle/server/node_modules/.bin/jiti +22 -0
  99. package/templates/fate/drizzle/server/node_modules/.bin/nodemon +22 -0
  100. package/templates/fate/drizzle/server/node_modules/.bin/oxfmt +22 -0
  101. package/templates/fate/drizzle/server/node_modules/.bin/oxlint +22 -0
  102. package/templates/fate/drizzle/server/node_modules/.bin/tsc +22 -0
  103. package/templates/fate/drizzle/server/node_modules/.bin/tsx +22 -0
  104. package/templates/fate/drizzle/server/node_modules/.bin/vitest +22 -0
  105. package/templates/fate/drizzle/server/node_modules/.bin/vp +22 -0
  106. package/templates/fate/drizzle/server/node_modules/.bin/vpr +22 -0
  107. package/templates/fate/drizzle/server/package.json +9 -9
  108. package/templates/fate/drizzle/server/src/app.tsx +1 -1
  109. package/templates/fate/drizzle/server/src/drizzle/schema.ts +2 -3
  110. package/templates/fate/drizzle/server/src/drizzle/seed.tsx +1 -1
  111. package/templates/fate/drizzle/tsconfig.json +1 -10
  112. package/templates/fate/graphql/README.md +11 -0
  113. package/templates/fate/graphql/client/node_modules/.bin/browserslist +22 -0
  114. package/templates/fate/graphql/client/node_modules/.bin/cf-wrangler +22 -0
  115. package/templates/fate/graphql/client/node_modules/.bin/drizzle-kit +22 -0
  116. package/templates/fate/graphql/client/node_modules/.bin/esbuild +22 -0
  117. package/templates/fate/graphql/client/node_modules/.bin/fate +22 -0
  118. package/templates/fate/graphql/client/node_modules/.bin/fbtee +22 -0
  119. package/templates/fate/graphql/client/node_modules/.bin/intent +22 -0
  120. package/templates/fate/graphql/client/node_modules/.bin/jiti +22 -0
  121. package/templates/fate/graphql/client/node_modules/.bin/oxfmt +22 -0
  122. package/templates/fate/graphql/client/node_modules/.bin/oxlint +22 -0
  123. package/templates/fate/graphql/client/node_modules/.bin/rolldown +22 -0
  124. package/templates/fate/graphql/client/node_modules/.bin/tsc +22 -0
  125. package/templates/fate/graphql/client/node_modules/.bin/tsx +22 -0
  126. package/templates/fate/graphql/client/node_modules/.bin/vitest +22 -0
  127. package/templates/fate/graphql/client/node_modules/.bin/void +22 -0
  128. package/templates/fate/graphql/client/node_modules/.bin/vp +22 -0
  129. package/templates/fate/graphql/client/node_modules/.bin/vpr +22 -0
  130. package/templates/fate/graphql/client/node_modules/.bin/workerd +22 -0
  131. package/templates/fate/graphql/client/node_modules/.bin/wrangler +22 -0
  132. package/templates/fate/graphql/client/node_modules/.bin/wrangler2 +22 -0
  133. package/templates/fate/graphql/client/node_modules/.bin/yaml +22 -0
  134. package/templates/fate/graphql/client/package.json +27 -23
  135. package/templates/fate/graphql/client/pages/layout.tsx +2 -16
  136. package/templates/fate/graphql/client/src/lib/AvailableLanguages.tsx +3 -3
  137. package/templates/fate/graphql/client/src/lib/LocaleContext.tsx +61 -0
  138. package/templates/fate/graphql/client/src/ui/LocaleSwitcher.tsx +26 -44
  139. package/templates/fate/graphql/client/src/ui/PostCard.tsx +1 -1
  140. package/templates/fate/graphql/client/start.mjs +11 -0
  141. package/templates/fate/{drizzle/client/translations/de_DE.json → graphql/client/translations/de-DE.json} +56 -113
  142. package/templates/fate/{http/client/translations/ja_JP.json → graphql/client/translations/ja-JP.json} +56 -113
  143. package/templates/fate/graphql/client/vite.config.ts +4 -8
  144. package/templates/fate/graphql/package.json +7 -11
  145. package/templates/fate/graphql/pnpm-workspace.yaml +4 -4
  146. package/templates/fate/graphql/server/package.json +25 -29
  147. package/templates/fate/graphql/server/prisma.config.ts +9 -1
  148. package/templates/fate/graphql/server/scripts/generate-graphql.tsx +1 -1
  149. package/templates/fate/graphql/server/src/index.tsx +1 -1
  150. package/templates/fate/graphql/server/src/prisma/schema.prisma +2 -1
  151. package/templates/fate/graphql/server/src/prisma/seed.tsx +1 -1
  152. package/templates/fate/graphql/server/tsconfig.json +1 -11
  153. package/templates/fate/graphql/tsconfig.json +1 -10
  154. package/templates/fate/graphql-client/README.md +9 -0
  155. package/templates/fate/graphql-client/_gitignore +3 -0
  156. package/templates/fate/graphql-client/package.json +27 -24
  157. package/templates/fate/graphql-client/pages/index.tsx +61 -27
  158. package/templates/fate/graphql-client/pages/layout.tsx +22 -15
  159. package/templates/fate/graphql-client/pnpm-workspace.yaml +6 -4
  160. package/templates/fate/graphql-client/src/lib/AvailableLanguages.tsx +5 -0
  161. package/templates/fate/graphql-client/src/lib/LocaleContext.tsx +61 -0
  162. package/templates/fate/graphql-client/src/ui/LocaleSwitcher.tsx +34 -0
  163. package/templates/fate/graphql-client/translations/de-DE.json +225 -0
  164. package/templates/fate/graphql-client/translations/ja-JP.json +225 -0
  165. package/templates/fate/graphql-client/vite.config.ts +9 -13
  166. package/templates/fate/http/README.md +11 -0
  167. package/templates/fate/http/client/node_modules/.bin/browserslist +22 -0
  168. package/templates/fate/http/client/node_modules/.bin/cf-wrangler +22 -0
  169. package/templates/fate/http/client/node_modules/.bin/drizzle-kit +22 -0
  170. package/templates/fate/http/client/node_modules/.bin/esbuild +22 -0
  171. package/templates/fate/http/client/node_modules/.bin/fate +22 -0
  172. package/templates/fate/http/client/node_modules/.bin/fbtee +22 -0
  173. package/templates/fate/http/client/node_modules/.bin/intent +22 -0
  174. package/templates/fate/http/client/node_modules/.bin/jiti +22 -0
  175. package/templates/fate/http/client/node_modules/.bin/oxfmt +22 -0
  176. package/templates/fate/http/client/node_modules/.bin/oxlint +22 -0
  177. package/templates/fate/http/client/node_modules/.bin/rolldown +22 -0
  178. package/templates/fate/http/client/node_modules/.bin/tsc +22 -0
  179. package/templates/fate/http/client/node_modules/.bin/tsx +22 -0
  180. package/templates/fate/http/client/node_modules/.bin/vitest +22 -0
  181. package/templates/fate/http/client/node_modules/.bin/void +22 -0
  182. package/templates/fate/http/client/node_modules/.bin/vp +22 -0
  183. package/templates/fate/http/client/node_modules/.bin/vpr +22 -0
  184. package/templates/fate/http/client/node_modules/.bin/workerd +22 -0
  185. package/templates/fate/http/client/node_modules/.bin/wrangler +22 -0
  186. package/templates/fate/http/client/node_modules/.bin/wrangler2 +22 -0
  187. package/templates/fate/http/client/package.json +26 -22
  188. package/templates/fate/http/client/pages/layout.tsx +2 -16
  189. package/templates/fate/http/client/src/lib/AvailableLanguages.tsx +3 -3
  190. package/templates/fate/http/client/src/lib/LocaleContext.tsx +61 -0
  191. package/templates/fate/http/client/src/ui/LocaleSwitcher.tsx +26 -44
  192. package/templates/fate/http/client/start.mjs +11 -0
  193. package/templates/fate/http/client/translations/{de_DE.json → de-DE.json} +56 -113
  194. package/templates/fate/{drizzle/client/translations/ja_JP.json → http/client/translations/ja-JP.json} +56 -113
  195. package/templates/fate/http/client/vite.config.ts +4 -8
  196. package/templates/fate/http/package.json +6 -10
  197. package/templates/fate/http/pnpm-workspace.yaml +4 -4
  198. package/templates/fate/http/server/node_modules/.bin/drizzle-kit +22 -0
  199. package/templates/fate/http/server/node_modules/.bin/esbuild +22 -0
  200. package/templates/fate/http/server/node_modules/.bin/fate +22 -0
  201. package/templates/fate/http/server/node_modules/.bin/intent +22 -0
  202. package/templates/fate/http/server/node_modules/.bin/jiti +22 -0
  203. package/templates/fate/http/server/node_modules/.bin/nodemon +22 -0
  204. package/templates/fate/http/server/node_modules/.bin/oxfmt +22 -0
  205. package/templates/fate/http/server/node_modules/.bin/oxlint +22 -0
  206. package/templates/fate/http/server/node_modules/.bin/tsc +22 -0
  207. package/templates/fate/http/server/node_modules/.bin/tsx +22 -0
  208. package/templates/fate/http/server/node_modules/.bin/vitest +22 -0
  209. package/templates/fate/http/server/node_modules/.bin/vp +22 -0
  210. package/templates/fate/http/server/node_modules/.bin/vpr +22 -0
  211. package/templates/fate/http/server/package.json +8 -8
  212. package/templates/fate/http/server/src/app.tsx +1 -1
  213. package/templates/fate/http/server/src/drizzle/schema.ts +2 -3
  214. package/templates/fate/http/server/src/drizzle/seed.tsx +1 -1
  215. package/templates/fate/http/tsconfig.json +1 -10
  216. package/templates/fate/prisma/README.md +11 -0
  217. package/templates/fate/prisma/client/node_modules/.bin/browserslist +22 -0
  218. package/templates/fate/prisma/client/node_modules/.bin/cf-wrangler +22 -0
  219. package/templates/fate/prisma/client/node_modules/.bin/drizzle-kit +22 -0
  220. package/templates/fate/prisma/client/node_modules/.bin/esbuild +22 -0
  221. package/templates/fate/prisma/client/node_modules/.bin/fate +22 -0
  222. package/templates/fate/prisma/client/node_modules/.bin/fbtee +22 -0
  223. package/templates/fate/prisma/client/node_modules/.bin/intent +22 -0
  224. package/templates/fate/prisma/client/node_modules/.bin/jiti +22 -0
  225. package/templates/fate/prisma/client/node_modules/.bin/oxfmt +22 -0
  226. package/templates/fate/prisma/client/node_modules/.bin/oxlint +22 -0
  227. package/templates/fate/prisma/client/node_modules/.bin/rolldown +22 -0
  228. package/templates/fate/prisma/client/node_modules/.bin/tsc +22 -0
  229. package/templates/fate/prisma/client/node_modules/.bin/tsx +22 -0
  230. package/templates/fate/prisma/client/node_modules/.bin/vitest +22 -0
  231. package/templates/fate/prisma/client/node_modules/.bin/void +22 -0
  232. package/templates/fate/prisma/client/node_modules/.bin/vp +22 -0
  233. package/templates/fate/prisma/client/node_modules/.bin/vpr +22 -0
  234. package/templates/fate/prisma/client/node_modules/.bin/workerd +22 -0
  235. package/templates/fate/prisma/client/node_modules/.bin/wrangler +22 -0
  236. package/templates/fate/prisma/client/node_modules/.bin/wrangler2 +22 -0
  237. package/templates/fate/prisma/client/node_modules/.bin/yaml +22 -0
  238. package/templates/fate/prisma/client/package.json +28 -24
  239. package/templates/fate/prisma/client/pages/layout.tsx +2 -16
  240. package/templates/fate/prisma/client/src/lib/AvailableLanguages.tsx +3 -3
  241. package/templates/fate/prisma/client/src/lib/LocaleContext.tsx +61 -0
  242. package/templates/fate/prisma/client/src/ui/LocaleSwitcher.tsx +26 -44
  243. package/templates/fate/prisma/client/start.mjs +11 -0
  244. package/templates/fate/prisma/client/translations/de-DE.json +632 -0
  245. package/templates/fate/prisma/client/translations/ja-JP.json +632 -0
  246. package/templates/fate/prisma/client/vite.config.ts +4 -8
  247. package/templates/fate/prisma/package.json +7 -11
  248. package/templates/fate/prisma/pnpm-workspace.yaml +4 -4
  249. package/templates/fate/prisma/server/node_modules/.bin/esbuild +22 -0
  250. package/templates/fate/prisma/server/node_modules/.bin/fate +22 -0
  251. package/templates/fate/prisma/server/node_modules/.bin/intent +22 -0
  252. package/templates/fate/prisma/server/node_modules/.bin/jiti +22 -0
  253. package/templates/fate/prisma/server/node_modules/.bin/nft +22 -0
  254. package/templates/fate/prisma/server/node_modules/.bin/nodemon +22 -0
  255. package/templates/fate/prisma/server/node_modules/.bin/oxfmt +22 -0
  256. package/templates/fate/prisma/server/node_modules/.bin/oxlint +22 -0
  257. package/templates/fate/prisma/server/node_modules/.bin/prisma +22 -0
  258. package/templates/fate/prisma/server/node_modules/.bin/tsc +22 -0
  259. package/templates/fate/prisma/server/node_modules/.bin/tsx +22 -0
  260. package/templates/fate/prisma/server/node_modules/.bin/vitest +22 -0
  261. package/templates/fate/prisma/server/node_modules/.bin/vp +22 -0
  262. package/templates/fate/prisma/server/node_modules/.bin/vpr +22 -0
  263. package/templates/fate/prisma/server/node_modules/.bin/yaml +22 -0
  264. package/templates/fate/prisma/server/package.json +9 -9
  265. package/templates/fate/prisma/server/prisma.config.ts +2 -1
  266. package/templates/fate/prisma/server/src/app.tsx +1 -1
  267. package/templates/fate/prisma/server/src/prisma/schema.prisma +2 -1
  268. package/templates/fate/prisma/server/src/prisma/seed.tsx +1 -1
  269. package/templates/fate/prisma/tsconfig.json +1 -10
  270. package/templates/fate/void/README.md +9 -0
  271. package/templates/fate/void/_gitignore +3 -0
  272. package/templates/fate/void/db/migrations/20260508073357_initial_schema.sql +3 -2
  273. package/templates/fate/void/db/migrations/20260508120500_seed_void_demo.sql +10 -10
  274. package/templates/fate/void/db/migrations/meta/20260508073357_snapshot.json +10 -3
  275. package/templates/fate/void/db/schema.ts +2 -3
  276. package/templates/fate/void/middleware/01.shared.ts +3 -0
  277. package/templates/fate/void/package.json +30 -25
  278. package/templates/fate/void/pages/index.tsx +23 -10
  279. package/templates/fate/void/pages/layout.tsx +22 -19
  280. package/templates/fate/void/pages/login.tsx +22 -18
  281. package/templates/fate/void/pages/search.tsx +18 -4
  282. package/templates/fate/void/pnpm-workspace.yaml +4 -4
  283. package/templates/fate/void/src/fate/server.ts +2 -1
  284. package/templates/fate/void/src/lib/AvailableLanguages.tsx +5 -0
  285. package/templates/fate/void/src/lib/LocaleContext.tsx +61 -0
  286. package/templates/fate/void/src/ui/CategoryCard.tsx +22 -4
  287. package/templates/fate/void/src/ui/CreatePost.tsx +12 -7
  288. package/templates/fate/void/src/ui/Error.tsx +4 -2
  289. package/templates/fate/void/src/ui/EventCard.tsx +18 -5
  290. package/templates/fate/void/src/ui/Header.tsx +10 -4
  291. package/templates/fate/void/src/ui/LocaleSwitcher.tsx +34 -0
  292. package/templates/fate/void/src/ui/PostCard.tsx +29 -14
  293. package/templates/fate/void/src/ui/UserCard.tsx +15 -8
  294. package/templates/fate/void/translations/de-DE.json +863 -0
  295. package/templates/fate/void/translations/ja-JP.json +863 -0
  296. package/templates/fate/void/vite.config.ts +6 -6
  297. package/templates/fate/prisma/client/translations/de_DE.json +0 -689
  298. package/templates/fate/prisma/client/translations/ja_JP.json +0 -689
@@ -50,6 +50,13 @@
50
50
  "notNull": false,
51
51
  "autoincrement": false
52
52
  },
53
+ "issuer": {
54
+ "name": "issuer",
55
+ "type": "text",
56
+ "primaryKey": false,
57
+ "notNull": true,
58
+ "autoincrement": false
59
+ },
53
60
  "password": {
54
61
  "name": "password",
55
62
  "type": "text",
@@ -102,9 +109,9 @@
102
109
  }
103
110
  },
104
111
  "indexes": {
105
- "account_providerId_accountId_key": {
106
- "name": "account_providerId_accountId_key",
107
- "columns": ["providerId", "accountId"],
112
+ "account_issuer_accountId_key": {
113
+ "name": "account_issuer_accountId_key",
114
+ "columns": ["issuer", "accountId"],
108
115
  "isUnique": true
109
116
  }
110
117
  },
@@ -67,6 +67,7 @@ export const account = sqliteTable(
67
67
  createdAt: timestampColumn(),
68
68
  id: idColumn().primaryKey(),
69
69
  idToken: text(),
70
+ issuer: text().notNull(),
70
71
  password: text(),
71
72
  providerId: text().notNull(),
72
73
  refreshToken: text(),
@@ -77,9 +78,7 @@ export const account = sqliteTable(
77
78
  .notNull()
78
79
  .references(() => user.id, { onDelete: 'cascade' }),
79
80
  },
80
- (table) => [
81
- uniqueIndex('account_providerId_accountId_key').on(table.providerId, table.accountId),
82
- ],
81
+ (table) => [uniqueIndex('account_issuer_accountId_key').on(table.issuer, table.accountId)],
83
82
  );
84
83
 
85
84
  export const category = sqliteTable(
@@ -26,4 +26,7 @@ export default defineMiddleware(async (context, next) => {
26
26
  });
27
27
 
28
28
  await next();
29
+
30
+ // Void auth appends headers after this middleware; asset responses can be immutable.
31
+ context.res = new Response(context.res.body, context.res);
29
32
  });
@@ -10,13 +10,17 @@
10
10
  },
11
11
  "type": "module",
12
12
  "scripts": {
13
- "build": "vp build",
13
+ "build": "vp run fbtee:all && vp build",
14
14
  "db:generate": "void db generate",
15
15
  "db:migrate": "void db migrate",
16
16
  "db:seed": "void db seed --file db/seed.ts",
17
17
  "dev": "vp dev",
18
- "dev:setup": "vp run prepare:void && vp run db:seed && vp run fate:generate",
18
+ "dev:setup": "vp run fbtee:all && vp run prepare:void && vp run db:seed && vp run fate:generate",
19
19
  "fate:generate": "vp exec fate generate",
20
+ "fbtee:all": "vp run fbtee:collect && vp run fbtee:translate",
21
+ "fbtee:collect": "vp exec fbtee collect",
22
+ "fbtee:prepare": "vp run fbtee:collect && vp exec fbtee prepare-translations --source-strings source_strings.json --output-dir translations --locales de-DE ja-JP",
23
+ "fbtee:translate": "vp exec fbtee translate --source-strings source_strings.json --output-dir src/translations --output-locale-style=bcp47",
20
24
  "prepare:void": "void prepare",
21
25
  "test:all": "vp check && vp test"
22
26
  },
@@ -24,38 +28,39 @@
24
28
  "@nkzw/core": "^1.3.1",
25
29
  "@nkzw/fate": "latest",
26
30
  "@nkzw/stack": "^2.3.2",
27
- "@radix-ui/react-slot": "^1.2.4",
28
- "@void/react": "^0.7.13",
29
- "better-auth": "^1.6.9",
31
+ "@radix-ui/react-slot": "^1.3.3",
32
+ "@void/react": "^0.10.12",
33
+ "better-auth": "1.7.2",
30
34
  "class-variance-authority": "^0.7.1",
31
35
  "clsx": "^2.1.1",
32
36
  "drizzle-orm": "^0.45.2",
33
- "lucide-react": "^1.14.0",
34
- "react": "^19.2.5",
35
- "react-dom": "^19.2.5",
36
- "react-error-boundary": "^6.1.1",
37
+ "fbtee": "^4.0.0",
38
+ "lucide-react": "^1.34.0",
39
+ "react": "^19.2.8",
40
+ "react-dom": "^19.2.8",
41
+ "react-error-boundary": "^6.1.3",
37
42
  "react-fate": "latest",
38
- "tailwind-merge": "^3.5.0",
39
- "void": "^0.7.13",
43
+ "tailwind-merge": "^3.6.0",
44
+ "void": "^0.10.12",
40
45
  "void-fate": "latest",
41
46
  "zod": "^4.4.3"
42
47
  },
43
48
  "devDependencies": {
49
+ "@cloudflare/workers-types": "4.20260702.1",
44
50
  "@nkzw/eslint-plugin": "^2.0.0",
45
- "@nkzw/oxlint-config": "^1.2.1",
46
- "@rolldown/plugin-babel": "^0.2.3",
47
- "@tailwindcss/vite": "^4.2.4",
48
- "@types/node": "^25.6.0",
49
- "@types/react": "^19.2.14",
50
- "@types/react-dom": "^19.2.3",
51
- "@typescript/native-preview": "7.0.0-dev.20260505.1",
52
- "@vitejs/plugin-react": "^6.0.1",
53
- "babel-plugin-react-compiler": "^1.0.0",
54
- "eslint-plugin-no-only-tests": "^3.4.0",
55
- "eslint-plugin-perfectionist": "^5.9.0",
56
- "eslint-plugin-react-hooks": "^7.1.1",
57
- "tailwindcss": "^4.2.4",
58
- "ts-node": "^10.9.2",
51
+ "@nkzw/fbtee-cli": "^4.0.0",
52
+ "@nkzw/oxlint-config": "^2.0.1",
53
+ "@nkzw/vite-plugin-fbtee": "^4.0.0",
54
+ "@oxc-node/core": "^0.1.0",
55
+ "@tailwindcss/vite": "^4.3.3",
56
+ "@types/node": "^26.4.0",
57
+ "@types/react": "^19.2.18",
58
+ "@types/react-dom": "^19.2.5",
59
+ "@vitejs/plugin-react": "^6.1.0",
60
+ "oxc-transform-react": "^0.147.0",
61
+ "pg": "8.22.0",
62
+ "tailwindcss": "^4.3.3",
63
+ "typescript": "^7.0.2",
59
64
  "vite": "catalog:",
60
65
  "vite-plus": "catalog:",
61
66
  "vitest": "catalog:"
@@ -38,7 +38,9 @@ const PostFeed = ({
38
38
  return posts.length ? (
39
39
  <VStack gap={16}>
40
40
  <Stack alignCenter between gap={16}>
41
- <H2 className="pl-5">Latest posts</H2>
41
+ <H2 className="pl-5">
42
+ <fbt desc="Latest posts header">Latest posts</fbt>
43
+ </H2>
42
44
  <div>
43
45
  <button
44
46
  className={cx(
@@ -46,6 +48,7 @@ const PostFeed = ({
46
48
  user ? 'active:translate-y-0.5 active:opacity-50' : 'pointer-events-none opacity-0',
47
49
  )}
48
50
  onClick={() => setShowPostEditor((showPostEditor) => !showPostEditor)}
51
+ type="button"
49
52
  >
50
53
  {showPostEditor ? (
51
54
  <CircleMinus className="h-6 w-6" />
@@ -65,7 +68,7 @@ const PostFeed = ({
65
68
  {loadNext ? (
66
69
  <Stack center>
67
70
  <Button onClick={loadNext} variant="ghost">
68
- Load more posts
71
+ <fbt desc="Load more posts button">Load more posts</fbt>{' '}
69
72
  </Button>
70
73
  </Stack>
71
74
  ) : null}
@@ -77,7 +80,9 @@ const PostFeed = ({
77
80
  const CategoryFeed = ({ categories }: { categories: Array<ViewRef<'Category'>> }) =>
78
81
  categories.length ? (
79
82
  <VStack gap={16}>
80
- <H2 className="pl-5">Explore by theme</H2>
83
+ <H2 className="pl-5">
84
+ <fbt desc="index: Explore by theme">Explore by theme</fbt>
85
+ </H2>
81
86
  <VStack gap={24}>
82
87
  {categories.map((category) => (
83
88
  <CategoryCard category={category} key={category.id} />
@@ -89,7 +94,9 @@ const CategoryFeed = ({ categories }: { categories: Array<ViewRef<'Category'>> }
89
94
  const EventFeed = ({ events }: { events: Array<ViewRef<'Event'>> }) =>
90
95
  events.length ? (
91
96
  <VStack gap={16}>
92
- <H2 className="pl-5">Events</H2>
97
+ <H2 className="pl-5">
98
+ <fbt desc="index: Events">Events</fbt>
99
+ </H2>
93
100
  <VStack gap={24}>
94
101
  {events.map((event) => (
95
102
  <EventCard event={event} key={event.id} />
@@ -114,16 +121,20 @@ export default function HomePage() {
114
121
  <Card className="border border-white/20 bg-linear-to-r from-blue-500 to-sky-500 text-white dark:from-blue-600 dark:to-sky-600">
115
122
  <Stack alignCenter gap={12} wrap>
116
123
  <span className="squircle bg-white/20 px-2 py-1 text-xs font-semibold tracking-widest uppercase">
117
- <span className="lowercase italic">fate</span> demo
124
+ <span className="lowercase italic">fate</span> <fbt desc="index: demo">demo</fbt>{' '}
118
125
  </span>
119
126
  </Stack>
120
127
  <div className="space-y-3">
121
128
  <h1 className="text-3xl leading-tight font-semibold text-balance lg:text-4xl">
122
- fate is a modern data client for React inspired by Relay and GraphQL.
129
+ <fbt desc="Description of fate">
130
+ fate is a modern data client for React inspired by Relay and GraphQL.
131
+ </fbt>{' '}
123
132
  </h1>
124
133
  <p className="text-sm text-white/80 lg:text-base">
125
- fate combines view composition, normalized caching, data masking, Async React
126
- features, and type-safe data fetching.
134
+ <fbt desc="index: fate combines view composition, normalized caching, data masking, Async React features, and type-safe data fetching.">
135
+ fate combines view composition, normalized caching, data masking, Async React
136
+ features, and type-safe data fetching.
137
+ </fbt>{' '}
127
138
  </p>
128
139
  </div>
129
140
  <Stack alignCenter gap={12} wrap>
@@ -131,10 +142,12 @@ export default function HomePage() {
131
142
  <>
132
143
  <Button asChild size="sm" variant="secondary">
133
144
  <Link className="squircle px-4 py-2 text-sm font-semibold" href="/login">
134
- Login
145
+ <fbt desc="Login button">Login</fbt>{' '}
135
146
  </Link>
136
147
  </Button>
137
- <span className="text-sm text-white/80">Sign in to post comments.</span>
148
+ <span className="text-sm text-white/80">
149
+ <fbt desc="Login button label">Sign in to post comments.</fbt>
150
+ </span>
138
151
  </>
139
152
  )}
140
153
  </Stack>
@@ -1,9 +1,10 @@
1
- import '../src/App.css';
2
1
  import Stack from '@nkzw/stack';
2
+ import '../src/App.css';
3
3
  import { useShared } from '@void/react';
4
4
  import { ReactNode, Suspense } from 'react';
5
5
  import { ErrorBoundary } from 'react-error-boundary';
6
6
  import { VoidFateClient } from 'void-fate/react';
7
+ import LocaleContext from '../src/lib/LocaleContext.tsx';
7
8
  import type { SharedData } from '../src/lib/shared.ts';
8
9
  import Card from '../src/ui/Card.tsx';
9
10
  import Error from '../src/ui/Error.tsx';
@@ -13,7 +14,7 @@ import Section from '../src/ui/Section.tsx';
13
14
  const Thinking = () => (
14
15
  <Section>
15
16
  <Stack center className="animate-pulse text-gray-500 italic" verticalPadding={48}>
16
- Thinking...
17
+ <fbt desc="Text for thinking/loading screen">Thinking...</fbt>
17
18
  </Stack>
18
19
  </Section>
19
20
  );
@@ -24,23 +25,25 @@ export default function Layout({ children }: { children: ReactNode }) {
24
25
  const origin = typeof window === 'undefined' ? shared.origin : window.location.origin;
25
26
 
26
27
  return (
27
- <VoidFateClient origin={origin} userId={userId}>
28
- <div className="min-h-screen bg-background text-foreground">
29
- <div className="min-h-screen bg-[radial-gradient(circle_at_20%_20%,rgba(59,130,246,0.08),transparent_35%),radial-gradient(circle_at_80%_0,rgba(99,102,241,0.08),transparent_28%)]">
30
- <Header />
31
- <ErrorBoundary
32
- fallbackRender={({ error }) => (
33
- <Section>
34
- <Card>
35
- <Error error={error} />
36
- </Card>
37
- </Section>
38
- )}
39
- >
40
- <Suspense fallback={<Thinking />}>{children}</Suspense>
41
- </ErrorBoundary>
28
+ <LocaleContext>
29
+ <VoidFateClient origin={origin} userId={userId}>
30
+ <div className="min-h-screen bg-background text-foreground">
31
+ <div className="min-h-screen bg-[radial-gradient(circle_at_20%_20%,rgba(59,130,246,0.08),transparent_35%),radial-gradient(circle_at_80%_0,rgba(99,102,241,0.08),transparent_28%)]">
32
+ <Header />
33
+ <ErrorBoundary
34
+ fallbackRender={({ error }) => (
35
+ <Section>
36
+ <Card>
37
+ <Error error={error} />
38
+ </Card>
39
+ </Section>
40
+ )}
41
+ >
42
+ <Suspense fallback={<Thinking />}>{children}</Suspense>
43
+ </ErrorBoundary>
44
+ </div>
42
45
  </div>
43
- </div>
44
- </VoidFateClient>
46
+ </VoidFateClient>
47
+ </LocaleContext>
45
48
  );
46
49
  }
@@ -1,5 +1,6 @@
1
1
  import Stack, { VStack } from '@nkzw/stack';
2
2
  import { useRouter, useShared } from '@void/react';
3
+ import { fbs } from 'fbtee';
3
4
  import { ExternalLinkIcon } from 'lucide-react';
4
5
  import { useActionState, useEffect, useState } from 'react';
5
6
  import type { SharedData } from '../src/lib/shared.ts';
@@ -49,7 +50,9 @@ export default function LoginPage() {
49
50
  return (
50
51
  <Section>
51
52
  <VStack center gap={16}>
52
- <H2 className="pl-5">Sign In</H2>
53
+ <H2 className="pl-5">
54
+ <fbt desc="Sign in button">Sign In</fbt>
55
+ </H2>
53
56
  <Stack gap={32} wrap>
54
57
  <Card className="w-84">
55
58
  <Stack gap vertical>
@@ -58,7 +61,7 @@ export default function LoginPage() {
58
61
  className="w-48"
59
62
  name="email"
60
63
  onChange={(e) => setEmail(e.target.value)}
61
- placeholder="email"
64
+ placeholder={fbs('email', 'login: placeholder')}
62
65
  type="email"
63
66
  value={email}
64
67
  />
@@ -66,13 +69,13 @@ export default function LoginPage() {
66
69
  className="w-48"
67
70
  name="password"
68
71
  onChange={(e) => setPassword(e.target.value)}
69
- placeholder="password"
72
+ placeholder={fbs('password', 'login: placeholder')}
70
73
  type="password"
71
74
  value={password}
72
75
  />
73
76
  <div>
74
77
  <AsyncButton type="submit" variant="outline">
75
- Sign In
78
+ <fbt desc="Sign in button">Sign In</fbt>{' '}
76
79
  </AsyncButton>
77
80
  </div>
78
81
  </VStack>
@@ -80,20 +83,21 @@ export default function LoginPage() {
80
83
  </Card>
81
84
  <Card className="w-84">
82
85
  <p>
83
- Try one of the
84
- <Stack
85
- alignCenter
86
- as="a"
87
- className="inline-flex! px-1 underline hover:no-underline"
88
- gap={4}
89
- href="https://github.com/nkzw-tech/fate/blob/main/packages/create-fate/templates/fate/void/seedData.ts#L1"
90
- rel="noreferrer"
91
- target="_blank"
92
- >
93
- Example Accounts
94
- <ExternalLinkIcon className="h-4 w-4" />
95
- </Stack>{' '}
96
- in the seed data.
86
+ <fbt desc="Example account instructions">
87
+ Try one of the{' '}
88
+ <Stack
89
+ alignCenter
90
+ as="a"
91
+ className="inline-flex! px-1 underline hover:no-underline"
92
+ gap={4}
93
+ href="https://github.com/nkzw-tech/fate/blob/main/packages/create-fate/templates/fate/void/seedData.ts#L1"
94
+ rel="noreferrer"
95
+ target="_blank"
96
+ >
97
+ Example Accounts <ExternalLinkIcon className="h-4 w-4" />
98
+ </Stack>{' '}
99
+ in the seed data.
100
+ </fbt>{' '}
97
101
  </p>
98
102
  </Card>
99
103
  </Stack>
@@ -1,4 +1,5 @@
1
1
  import Stack, { VStack } from '@nkzw/stack';
2
+ import { fbs } from 'fbtee';
2
3
  import { Suspense, useDeferredValue, useState } from 'react';
3
4
  import { ErrorBoundary } from 'react-error-boundary';
4
5
  import { useRequest, useView, view, ViewRef } from 'react-fate';
@@ -39,7 +40,12 @@ const SearchResults = ({ isStale, query }: { isStale: boolean; query: string })
39
40
  if (commentSearch.length === 0) {
40
41
  return (
41
42
  <p>
42
- No matches for <i>&quot;{query}&quot;</i>
43
+ <fbt desc="Empty comment search results">
44
+ No matches for{' '}
45
+ <i>
46
+ &quot;<fbt:param name="query">{query}</fbt:param>&quot;
47
+ </i>
48
+ </fbt>
43
49
  </p>
44
50
  );
45
51
  }
@@ -65,17 +71,25 @@ export default function SearchPage() {
65
71
  <Input
66
72
  className="w-64"
67
73
  onChange={(e) => setQuery(e.target.value)}
68
- placeholder="Search comments..."
74
+ placeholder={fbs('Search comments...', 'search: placeholder')}
69
75
  ref={(ref) => ref?.focus()}
70
76
  value={query}
71
77
  />
72
78
  {isDevelopment ? (
73
- <div className="text-xs text-muted-foreground">500ms artificial slowdown</div>
79
+ <div className="text-xs text-muted-foreground">
80
+ <fbt desc="Slowdown label explanation">500ms artificial slowdown</fbt>
81
+ </div>
74
82
  ) : null}
75
83
  </Stack>
76
84
 
77
85
  <ErrorBoundary FallbackComponent={Error}>
78
- <Suspense fallback={<h2>Thinking…</h2>}>
86
+ <Suspense
87
+ fallback={
88
+ <h2>
89
+ <fbt desc="Text for thinking/loading screen">Thinking…</fbt>
90
+ </h2>
91
+ }
92
+ >
79
93
  {query.trim().length > 0 ? <SearchResults isStale={isStale} query={query} /> : null}
80
94
  </Suspense>
81
95
  </ErrorBoundary>
@@ -8,13 +8,12 @@ allowBuilds:
8
8
  workerd: true
9
9
 
10
10
  catalog:
11
- vite: npm:@voidzero-dev/vite-plus-core@^0.1.23
12
- vite-plus: ^0.1.23
13
- vitest: npm:@voidzero-dev/vite-plus-test@^0.1.23
11
+ vite: npm:@voidzero-dev/vite-plus-core@^0.3.0
12
+ vite-plus: ^0.3.0
13
+ vitest: ^4.1.11
14
14
 
15
15
  minimumReleaseAgeExclude:
16
16
  - '@voidzero-dev/vite-plus-core'
17
- - '@voidzero-dev/vite-plus-test'
18
17
  - vite-plus
19
18
 
20
19
  onlyBuiltDependencies:
@@ -26,6 +25,7 @@ onlyBuiltDependencies:
26
25
  - workerd
27
26
 
28
27
  overrides:
28
+ better-auth: 1.7.2
29
29
  kysely: 0.28.17
30
30
  vite: 'catalog:'
31
31
  vitest: 'catalog:'
@@ -7,8 +7,9 @@ import {
7
7
  hasNestedSelection,
8
8
  } from '@nkzw/fate/server';
9
9
  import { createDrizzleSourceAdapter, type DrizzleQueryExtra } from '@nkzw/fate/server/drizzle';
10
+ import { like } from 'drizzle-orm';
10
11
  import { createVoidFateLive } from 'void-fate/server';
11
- import { db, eq, like } from 'void/db';
12
+ import { db, eq } from 'void/db';
12
13
  import { z } from 'zod';
13
14
  import {
14
15
  type CommentItem,
@@ -0,0 +1,5 @@
1
+ export default new Map([
2
+ ['en-US', 'English'],
3
+ ['de-DE', 'Deutsch (German)'],
4
+ ['ja-JP', '日本語 (Japanese)'],
5
+ ]);
@@ -0,0 +1,61 @@
1
+ /// <reference types="fbtee/ReactTypes.d.ts" />
2
+
3
+ import { createLocaleContext, useLocaleContext } from 'fbtee';
4
+ import { ReactNode, startTransition, useEffect, useState } from 'react';
5
+ import AvailableLanguages from './AvailableLanguages.tsx';
6
+
7
+ const Context = createLocaleContext({
8
+ availableLanguages: AvailableLanguages,
9
+ // Keep the server and first client render identical; restore preferences after hydration.
10
+ clientLocales: ['en-US'],
11
+ fallbackLocale: 'en-US',
12
+ loadLocale: async (locale) => {
13
+ if (locale !== 'en-US' && AvailableLanguages.has(locale)) {
14
+ return (await import(`../translations/${locale}.json`)).default[locale];
15
+ }
16
+ return {};
17
+ },
18
+ });
19
+
20
+ function RestoreLocale({ restored, onRestore }: { restored: boolean; onRestore: () => void }) {
21
+ const { locale, setLocale } = useLocaleContext();
22
+ useEffect(() => {
23
+ if (restored) return;
24
+ onRestore();
25
+ let savedLocale: string | null = null;
26
+ try {
27
+ savedLocale = localStorage.getItem('fbtee:locale');
28
+ } catch {
29
+ // Browser storage can be unavailable.
30
+ }
31
+ const preferred = [savedLocale, ...navigator.languages, navigator.language]
32
+ .filter((value): value is string => !!value)
33
+ .map((value) => value.replaceAll('_', '-'));
34
+ const nextLocale = preferred
35
+ .map((value) =>
36
+ AvailableLanguages.has(value)
37
+ ? value
38
+ : [...AvailableLanguages.keys()].find(
39
+ (available) => available.split('-')[0] === value.split('-')[0],
40
+ ),
41
+ )
42
+ .find(Boolean);
43
+ if (nextLocale && nextLocale !== locale) {
44
+ startTransition(() => setLocale(nextLocale));
45
+ }
46
+ }, [locale, setLocale, restored, onRestore]);
47
+ useEffect(() => {
48
+ document.documentElement.lang = locale;
49
+ }, [locale]);
50
+ return null;
51
+ }
52
+
53
+ export default function LocaleContext({ children }: { children: ReactNode }) {
54
+ const [restored, setRestored] = useState(false);
55
+ return (
56
+ <Context>
57
+ <RestoreLocale restored={restored} onRestore={() => setRestored(true)} />
58
+ {children}
59
+ </Context>
60
+ );
61
+ }
@@ -33,11 +33,23 @@ const CategoryPost = ({ post: postRef }: { post: ViewRef<'Post'> }) => {
33
33
  {post.title}
34
34
  </span>
35
35
  </Link>
36
- <span className="text-xs text-muted-foreground">{post.likes} likes</span>
36
+ <span className="text-xs text-muted-foreground">
37
+ <fbt desc="Post like count">
38
+ <fbt:plural count={post.likes} many="likes" showCount="yes">
39
+ like
40
+ </fbt:plural>
41
+ </fbt>
42
+ </span>
37
43
  </Stack>
38
44
  <Stack alignCenter gap wrap>
39
45
  <span className="text-xs text-muted-foreground">
40
- {author?.name ? `by ${author.name}` : 'By an anonymous collaborator'}
46
+ {author?.name ? (
47
+ <fbt desc="Post author">
48
+ by <fbt:param name="author">{author.name}</fbt:param>
49
+ </fbt>
50
+ ) : (
51
+ <fbt desc="Anonymous post author">By an anonymous collaborator</fbt>
52
+ )}
41
53
  </span>
42
54
  {tags.length ? (
43
55
  <Stack gap wrap>
@@ -82,7 +94,11 @@ export default function CategoryCard({ category: categoryRef }: { category: View
82
94
  <p className="text-sm text-muted-foreground">{category.description}</p>
83
95
  </div>
84
96
  <Badge className="text-nowrap" variant="outline">
85
- {category.postCount} posts
97
+ <fbt desc="Category post count">
98
+ <fbt:plural count={category.postCount} many="posts" showCount="yes">
99
+ post
100
+ </fbt:plural>
101
+ </fbt>{' '}
86
102
  </Badge>
87
103
  </Stack>
88
104
  <VStack gap={12}>
@@ -92,7 +108,9 @@ export default function CategoryCard({ category: categoryRef }: { category: View
92
108
  </VStack>
93
109
  {category.posts?.pagination?.hasNext ? (
94
110
  <span className="text-sm text-muted-foreground">
95
- More posts available in this category...
111
+ <fbt desc="CategoryCard: More posts available in this category...">
112
+ More posts available in this category...
113
+ </fbt>{' '}
96
114
  </span>
97
115
  ) : null}
98
116
  </VStack>
@@ -1,4 +1,5 @@
1
1
  import Stack, { VStack } from '@nkzw/stack';
2
+ import { fbs } from 'fbtee';
2
3
  import { KeyboardEvent, startTransition, useActionState, useState } from 'react';
3
4
  import { useFateClient, useView, ViewRef } from 'react-fate';
4
5
  import { Button } from '../ui/Button.tsx';
@@ -64,13 +65,15 @@ export default function CreatePost({ user: userRef }: { user: ViewRef<'User'> |
64
65
  return (
65
66
  <Card>
66
67
  <VStack action={createPost} as="form" gap={16}>
67
- <H3>Create a Post</H3>
68
+ <H3>
69
+ <fbt desc="CreatePost: Create a Post">Create a Post</fbt>
70
+ </H3>
68
71
  <Input
69
72
  className="w-full"
70
73
  disabled={isPending}
71
74
  onChange={(event) => setTitleValue(event.target.value)}
72
75
  onKeyDown={maybeSubmitPost}
73
- placeholder="Post Title"
76
+ placeholder={fbs('Post Title', 'CreatePost: placeholder')}
74
77
  value={titleValue}
75
78
  />
76
79
  <textarea
@@ -78,18 +81,20 @@ export default function CreatePost({ user: userRef }: { user: ViewRef<'User'> |
78
81
  disabled={isPending}
79
82
  onChange={(event) => setContentValue(event.target.value)}
80
83
  onKeyDown={maybeSubmitPost}
81
- placeholder={'Share your thoughts about fate...'}
84
+ placeholder={fbs('Share your thoughts about fate...', 'CreatePost: placeholder')}
82
85
  value={contentValue}
83
86
  />
84
87
  <Stack alignCenter between className="text-sm" gap={16}>
85
- <span className="font-bold">Mutation Debug Options</span>
88
+ <span className="font-bold">
89
+ <fbt desc="CreatePost: Mutation Debug Options">Mutation Debug Options</fbt>
90
+ </span>
86
91
  <Stack alignCenter as="label" gap>
87
92
  <CheckBox
88
93
  checked={missingMutationSelection}
89
94
  disabled={isPending}
90
95
  onChange={(event) => setMissingMutationSelection(event.target.checked)}
91
96
  />
92
- Missing mutation selection
97
+ <fbt desc="CreatePost: Missing mutation selection">Missing mutation selection</fbt>{' '}
93
98
  </Stack>
94
99
  <Stack alignCenter as="label" gap>
95
100
  <CheckBox
@@ -97,12 +102,12 @@ export default function CreatePost({ user: userRef }: { user: ViewRef<'User'> |
97
102
  disabled={isPending}
98
103
  onChange={(event) => setMissingOptimisticContent(event.target.checked)}
99
104
  />
100
- Missing optimistic content
105
+ <fbt desc="CreatePost: Missing optimistic content">Missing optimistic content</fbt>{' '}
101
106
  </Stack>
102
107
  </Stack>
103
108
  <Stack alignCenter end gap={16}>
104
109
  <Button disabled={postingIsDisabled} size="sm" type="submit" variant="secondary">
105
- Post comment
110
+ <fbt desc="Post comment button">Post comment</fbt>{' '}
106
111
  </Button>
107
112
  </Stack>
108
113
  </VStack>