qualia-framework 2.6.0 → 3.2.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 (321) hide show
  1. package/CLAUDE.md +64 -0
  2. package/README.md +103 -30
  3. package/agents/builder.md +110 -0
  4. package/agents/planner.md +134 -0
  5. package/agents/qa-browser.md +186 -0
  6. package/agents/verifier.md +221 -0
  7. package/bin/cli.js +336 -531
  8. package/bin/install.js +570 -0
  9. package/bin/qualia-ui.js +299 -0
  10. package/bin/state.js +630 -0
  11. package/bin/statusline.js +252 -0
  12. package/guide.md +63 -0
  13. package/hooks/auto-update.js +139 -0
  14. package/hooks/branch-guard.js +47 -0
  15. package/hooks/migration-guard.js +60 -0
  16. package/hooks/pre-compact.js +32 -0
  17. package/hooks/pre-deploy-gate.js +110 -0
  18. package/hooks/pre-push.js +33 -0
  19. package/hooks/session-start.js +170 -0
  20. package/package.json +29 -20
  21. package/rules/design-reference.md +179 -0
  22. package/rules/frontend.md +126 -0
  23. package/skills/qualia/SKILL.md +87 -0
  24. package/skills/qualia-build/SKILL.md +97 -0
  25. package/skills/qualia-debug/SKILL.md +87 -0
  26. package/skills/qualia-design/SKILL.md +93 -0
  27. package/skills/qualia-handoff/SKILL.md +66 -0
  28. package/skills/qualia-idk/SKILL.md +8 -0
  29. package/skills/qualia-learn/SKILL.md +88 -0
  30. package/skills/qualia-new/SKILL.md +323 -0
  31. package/{framework/skills → skills}/qualia-optimize/SKILL.md +1 -1
  32. package/skills/qualia-pause/SKILL.md +63 -0
  33. package/skills/qualia-plan/SKILL.md +101 -0
  34. package/skills/qualia-polish/SKILL.md +157 -0
  35. package/skills/qualia-quick/SKILL.md +37 -0
  36. package/skills/qualia-report/SKILL.md +105 -0
  37. package/skills/qualia-resume/SKILL.md +49 -0
  38. package/skills/qualia-review/SKILL.md +76 -0
  39. package/skills/qualia-ship/SKILL.md +90 -0
  40. package/skills/qualia-skill-new/SKILL.md +167 -0
  41. package/skills/qualia-task/SKILL.md +91 -0
  42. package/skills/qualia-verify/SKILL.md +113 -0
  43. package/templates/DESIGN.md +137 -0
  44. package/templates/plan.md +28 -0
  45. package/templates/project.md +22 -0
  46. package/templates/state.md +27 -0
  47. package/templates/tracking.json +20 -0
  48. package/tests/bin.test.sh +673 -0
  49. package/tests/hooks.test.sh +315 -0
  50. package/tests/state.test.sh +535 -0
  51. package/tests/statusline.test.sh +243 -0
  52. package/bin/collect-metrics.sh +0 -62
  53. package/framework/.claudeignore +0 -51
  54. package/framework/CLAUDE.md +0 -51
  55. package/framework/MCP_SETUP.md +0 -229
  56. package/framework/agents/architecture-strategist.md +0 -53
  57. package/framework/agents/backend-agent.md +0 -150
  58. package/framework/agents/code-simplicity-reviewer.md +0 -86
  59. package/framework/agents/frontend-agent.md +0 -111
  60. package/framework/agents/kieran-typescript-reviewer.md +0 -96
  61. package/framework/agents/performance-oracle.md +0 -111
  62. package/framework/agents/qualia-codebase-mapper.md +0 -761
  63. package/framework/agents/qualia-debugger.md +0 -1204
  64. package/framework/agents/qualia-executor.md +0 -882
  65. package/framework/agents/qualia-integration-checker.md +0 -424
  66. package/framework/agents/qualia-phase-researcher.md +0 -457
  67. package/framework/agents/qualia-plan-checker.md +0 -700
  68. package/framework/agents/qualia-planner.md +0 -1245
  69. package/framework/agents/qualia-project-researcher.md +0 -603
  70. package/framework/agents/qualia-research-synthesizer.md +0 -200
  71. package/framework/agents/qualia-roadmapper.md +0 -606
  72. package/framework/agents/qualia-verifier.md +0 -686
  73. package/framework/agents/red-team-qa.md +0 -130
  74. package/framework/agents/security-auditor.md +0 -72
  75. package/framework/agents/team-orchestrator.md +0 -229
  76. package/framework/agents/teams/framework-audit-team.md +0 -66
  77. package/framework/agents/teams/full-stack-team.md +0 -48
  78. package/framework/agents/teams/optimize-team.md +0 -53
  79. package/framework/agents/teams/review-team.md +0 -70
  80. package/framework/agents/teams/ship-team.md +0 -86
  81. package/framework/agents/test-agent.md +0 -182
  82. package/framework/hooks/auto-format.sh +0 -54
  83. package/framework/hooks/block-env-edit.sh +0 -42
  84. package/framework/hooks/branch-guard.sh +0 -43
  85. package/framework/hooks/confirm-delete.sh +0 -59
  86. package/framework/hooks/migration-validate.sh +0 -77
  87. package/framework/hooks/notification-speak.sh +0 -16
  88. package/framework/hooks/pre-commit.sh +0 -100
  89. package/framework/hooks/pre-compact.sh +0 -56
  90. package/framework/hooks/pre-deploy-gate.sh +0 -160
  91. package/framework/hooks/qualia-colors.sh +0 -32
  92. package/framework/hooks/retention-cleanup.sh +0 -62
  93. package/framework/hooks/save-session-state.sh +0 -185
  94. package/framework/hooks/session-context-loader.sh +0 -96
  95. package/framework/hooks/session-learn.sh +0 -32
  96. package/framework/hooks/skill-announce.sh +0 -123
  97. package/framework/hooks/tool-error-announce.sh +0 -27
  98. package/framework/install.ps1 +0 -323
  99. package/framework/install.sh +0 -313
  100. package/framework/qualia-framework/VERSION +0 -1
  101. package/framework/qualia-framework/assets/qualia-logo.png +0 -0
  102. package/framework/qualia-framework/bin/collect-metrics.sh +0 -67
  103. package/framework/qualia-framework/bin/generate-report-docx.py +0 -429
  104. package/framework/qualia-framework/bin/qualia-tools.js +0 -2201
  105. package/framework/qualia-framework/bin/qualia-tools.test.js +0 -1054
  106. package/framework/qualia-framework/references/checkpoints.md +0 -775
  107. package/framework/qualia-framework/references/completion-checklists.md +0 -359
  108. package/framework/qualia-framework/references/continuation-format.md +0 -249
  109. package/framework/qualia-framework/references/continuation-prompt.md +0 -97
  110. package/framework/qualia-framework/references/decimal-phase-calculation.md +0 -65
  111. package/framework/qualia-framework/references/design-quality.md +0 -56
  112. package/framework/qualia-framework/references/employee-guide.md +0 -167
  113. package/framework/qualia-framework/references/git-integration.md +0 -254
  114. package/framework/qualia-framework/references/git-planning-commit.md +0 -50
  115. package/framework/qualia-framework/references/model-profile-resolution.md +0 -32
  116. package/framework/qualia-framework/references/model-profiles.md +0 -73
  117. package/framework/qualia-framework/references/phase-argument-parsing.md +0 -61
  118. package/framework/qualia-framework/references/planning-config.md +0 -195
  119. package/framework/qualia-framework/references/questioning.md +0 -141
  120. package/framework/qualia-framework/references/tdd.md +0 -263
  121. package/framework/qualia-framework/references/ui-brand.md +0 -160
  122. package/framework/qualia-framework/references/verification-patterns.md +0 -612
  123. package/framework/qualia-framework/templates/DEBUG.md +0 -159
  124. package/framework/qualia-framework/templates/DESIGN.md +0 -81
  125. package/framework/qualia-framework/templates/UAT.md +0 -247
  126. package/framework/qualia-framework/templates/codebase/architecture.md +0 -255
  127. package/framework/qualia-framework/templates/codebase/concerns.md +0 -310
  128. package/framework/qualia-framework/templates/codebase/conventions.md +0 -307
  129. package/framework/qualia-framework/templates/codebase/integrations.md +0 -280
  130. package/framework/qualia-framework/templates/codebase/stack.md +0 -186
  131. package/framework/qualia-framework/templates/codebase/structure.md +0 -285
  132. package/framework/qualia-framework/templates/codebase/testing.md +0 -480
  133. package/framework/qualia-framework/templates/config.json +0 -35
  134. package/framework/qualia-framework/templates/context.md +0 -283
  135. package/framework/qualia-framework/templates/continue-here.md +0 -78
  136. package/framework/qualia-framework/templates/debug-subagent-prompt.md +0 -91
  137. package/framework/qualia-framework/templates/discovery.md +0 -146
  138. package/framework/qualia-framework/templates/lab-notes.md +0 -16
  139. package/framework/qualia-framework/templates/milestone-archive.md +0 -123
  140. package/framework/qualia-framework/templates/milestone.md +0 -115
  141. package/framework/qualia-framework/templates/phase-prompt.md +0 -567
  142. package/framework/qualia-framework/templates/planner-subagent-prompt.md +0 -117
  143. package/framework/qualia-framework/templates/project.md +0 -184
  144. package/framework/qualia-framework/templates/projects/ai-agent.md +0 -156
  145. package/framework/qualia-framework/templates/projects/mobile-app.md +0 -181
  146. package/framework/qualia-framework/templates/projects/voice-agent.md +0 -134
  147. package/framework/qualia-framework/templates/projects/website.md +0 -137
  148. package/framework/qualia-framework/templates/requirements.md +0 -231
  149. package/framework/qualia-framework/templates/research-project/ARCHITECTURE.md +0 -204
  150. package/framework/qualia-framework/templates/research-project/FEATURES.md +0 -147
  151. package/framework/qualia-framework/templates/research-project/PITFALLS.md +0 -200
  152. package/framework/qualia-framework/templates/research-project/STACK.md +0 -120
  153. package/framework/qualia-framework/templates/research-project/SUMMARY.md +0 -170
  154. package/framework/qualia-framework/templates/research.md +0 -552
  155. package/framework/qualia-framework/templates/roadmap.md +0 -206
  156. package/framework/qualia-framework/templates/state.md +0 -179
  157. package/framework/qualia-framework/templates/summary-complex.md +0 -59
  158. package/framework/qualia-framework/templates/summary-minimal.md +0 -41
  159. package/framework/qualia-framework/templates/summary-standard.md +0 -48
  160. package/framework/qualia-framework/templates/summary.md +0 -246
  161. package/framework/qualia-framework/templates/user-setup.md +0 -311
  162. package/framework/qualia-framework/templates/verification-report.md +0 -322
  163. package/framework/qualia-framework/workflows/add-phase.md +0 -179
  164. package/framework/qualia-framework/workflows/add-todo.md +0 -157
  165. package/framework/qualia-framework/workflows/audit-milestone.md +0 -241
  166. package/framework/qualia-framework/workflows/check-todos.md +0 -176
  167. package/framework/qualia-framework/workflows/complete-milestone.md +0 -858
  168. package/framework/qualia-framework/workflows/diagnose-issues.md +0 -219
  169. package/framework/qualia-framework/workflows/discovery-phase.md +0 -289
  170. package/framework/qualia-framework/workflows/discuss-phase.md +0 -534
  171. package/framework/qualia-framework/workflows/execute-phase.md +0 -559
  172. package/framework/qualia-framework/workflows/execute-plan.md +0 -438
  173. package/framework/qualia-framework/workflows/help.md +0 -470
  174. package/framework/qualia-framework/workflows/insert-phase.md +0 -220
  175. package/framework/qualia-framework/workflows/list-phase-assumptions.md +0 -178
  176. package/framework/qualia-framework/workflows/map-codebase.md +0 -327
  177. package/framework/qualia-framework/workflows/new-milestone.md +0 -363
  178. package/framework/qualia-framework/workflows/new-project.md +0 -982
  179. package/framework/qualia-framework/workflows/pause-work.md +0 -122
  180. package/framework/qualia-framework/workflows/plan-milestone-gaps.md +0 -256
  181. package/framework/qualia-framework/workflows/plan-phase.md +0 -422
  182. package/framework/qualia-framework/workflows/progress.md +0 -389
  183. package/framework/qualia-framework/workflows/quick.md +0 -252
  184. package/framework/qualia-framework/workflows/remove-phase.md +0 -326
  185. package/framework/qualia-framework/workflows/research-phase.md +0 -74
  186. package/framework/qualia-framework/workflows/resume-project.md +0 -306
  187. package/framework/qualia-framework/workflows/set-profile.md +0 -80
  188. package/framework/qualia-framework/workflows/settings.md +0 -145
  189. package/framework/qualia-framework/workflows/transition.md +0 -556
  190. package/framework/qualia-framework/workflows/update.md +0 -197
  191. package/framework/qualia-framework/workflows/verify-phase.md +0 -195
  192. package/framework/qualia-framework/workflows/verify-work.md +0 -625
  193. package/framework/rules/context7.md +0 -14
  194. package/framework/rules/frontend.md +0 -33
  195. package/framework/rules/speed.md +0 -23
  196. package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
  197. package/framework/scripts/apply-retention.sh +0 -120
  198. package/framework/scripts/bootstrap-pop-os.sh +0 -354
  199. package/framework/scripts/claude-voice +0 -13
  200. package/framework/scripts/cleanup.sh +0 -131
  201. package/framework/scripts/cowork-mode.sh +0 -141
  202. package/framework/scripts/generate-project-claude-md.sh +0 -153
  203. package/framework/scripts/load-test-webhook.js +0 -172
  204. package/framework/scripts/say.py +0 -236
  205. package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +0 -167
  206. package/framework/scripts/showcase-video-recorder/playwright-helpers.js +0 -216
  207. package/framework/scripts/speak.py +0 -55
  208. package/framework/scripts/speak.sh +0 -18
  209. package/framework/scripts/status.sh +0 -138
  210. package/framework/scripts/sync-to-framework.sh +0 -65
  211. package/framework/scripts/voice-hotkey.py +0 -227
  212. package/framework/scripts/voice-input.sh +0 -51
  213. package/framework/skills/animate/SKILL.md +0 -202
  214. package/framework/skills/bolder/SKILL.md +0 -144
  215. package/framework/skills/browser-qa/SKILL.md +0 -536
  216. package/framework/skills/clarify/SKILL.md +0 -179
  217. package/framework/skills/client-handoff/SKILL.md +0 -135
  218. package/framework/skills/collab-onboard/SKILL.md +0 -111
  219. package/framework/skills/colorize/SKILL.md +0 -170
  220. package/framework/skills/critique/SKILL.md +0 -126
  221. package/framework/skills/deep-research/SKILL.md +0 -240
  222. package/framework/skills/delight/SKILL.md +0 -329
  223. package/framework/skills/deploy/SKILL.md +0 -261
  224. package/framework/skills/deploy-verify/SKILL.md +0 -377
  225. package/framework/skills/deploy-verify/scripts/canary-check.sh +0 -206
  226. package/framework/skills/deploy-verify/scripts/check-console-errors.js +0 -147
  227. package/framework/skills/deploy-verify/scripts/check-cwv.js +0 -139
  228. package/framework/skills/deploy-verify/scripts/project-detect.sh +0 -84
  229. package/framework/skills/deploy-verify/scripts/verify.sh +0 -548
  230. package/framework/skills/design-quieter/SKILL.md +0 -130
  231. package/framework/skills/distill/SKILL.md +0 -149
  232. package/framework/skills/docs-lookup/SKILL.md +0 -79
  233. package/framework/skills/fcm-notifications/SKILL.md +0 -125
  234. package/framework/skills/financial-ledger/SKILL.md +0 -1039
  235. package/framework/skills/frontend-master/NOTICE.md +0 -4
  236. package/framework/skills/frontend-master/SKILL.md +0 -127
  237. package/framework/skills/frontend-master/reference/color-and-contrast.md +0 -132
  238. package/framework/skills/frontend-master/reference/interaction-design.md +0 -123
  239. package/framework/skills/frontend-master/reference/motion-design.md +0 -99
  240. package/framework/skills/frontend-master/reference/responsive-design.md +0 -114
  241. package/framework/skills/frontend-master/reference/spatial-design.md +0 -100
  242. package/framework/skills/frontend-master/reference/typography.md +0 -131
  243. package/framework/skills/frontend-master/reference/ux-writing.md +0 -107
  244. package/framework/skills/harden/SKILL.md +0 -357
  245. package/framework/skills/i18n-rtl/SKILL.md +0 -752
  246. package/framework/skills/learn/SKILL.md +0 -95
  247. package/framework/skills/memory/SKILL.md +0 -50
  248. package/framework/skills/mobile-expo/SKILL.md +0 -977
  249. package/framework/skills/mobile-expo/references/store-checklist.md +0 -550
  250. package/framework/skills/nestjs-backend/README.md +0 -73
  251. package/framework/skills/nestjs-backend/SKILL.md +0 -446
  252. package/framework/skills/nestjs-backend/references/templates.md +0 -1173
  253. package/framework/skills/normalize/SKILL.md +0 -79
  254. package/framework/skills/onboard/SKILL.md +0 -242
  255. package/framework/skills/openrouter-agent/SKILL.md +0 -922
  256. package/framework/skills/polish/SKILL.md +0 -209
  257. package/framework/skills/pr/SKILL.md +0 -66
  258. package/framework/skills/qualia/SKILL.md +0 -199
  259. package/framework/skills/qualia-add-todo/SKILL.md +0 -68
  260. package/framework/skills/qualia-audit-milestone/SKILL.md +0 -95
  261. package/framework/skills/qualia-check-todos/SKILL.md +0 -55
  262. package/framework/skills/qualia-complete-milestone/SKILL.md +0 -134
  263. package/framework/skills/qualia-debug/SKILL.md +0 -149
  264. package/framework/skills/qualia-design/SKILL.md +0 -203
  265. package/framework/skills/qualia-discuss-phase/SKILL.md +0 -72
  266. package/framework/skills/qualia-evolve/SKILL.md +0 -200
  267. package/framework/skills/qualia-execute-phase/SKILL.md +0 -89
  268. package/framework/skills/qualia-framework-audit/SKILL.md +0 -604
  269. package/framework/skills/qualia-guide/SKILL.md +0 -32
  270. package/framework/skills/qualia-help/SKILL.md +0 -114
  271. package/framework/skills/qualia-idk/SKILL.md +0 -352
  272. package/framework/skills/qualia-list-phase-assumptions/SKILL.md +0 -67
  273. package/framework/skills/qualia-new-milestone/SKILL.md +0 -72
  274. package/framework/skills/qualia-new-project/SKILL.md +0 -232
  275. package/framework/skills/qualia-pause-work/SKILL.md +0 -96
  276. package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +0 -57
  277. package/framework/skills/qualia-plan-phase/SKILL.md +0 -104
  278. package/framework/skills/qualia-production-check/SKILL.md +0 -0
  279. package/framework/skills/qualia-progress/SKILL.md +0 -53
  280. package/framework/skills/qualia-quick/SKILL.md +0 -89
  281. package/framework/skills/qualia-report/SKILL.md +0 -166
  282. package/framework/skills/qualia-research-phase/SKILL.md +0 -88
  283. package/framework/skills/qualia-resume-work/SKILL.md +0 -62
  284. package/framework/skills/qualia-review/SKILL.md +0 -263
  285. package/framework/skills/qualia-start/SKILL.md +0 -161
  286. package/framework/skills/qualia-verify-work/SKILL.md +0 -132
  287. package/framework/skills/rag/SKILL.md +0 -750
  288. package/framework/skills/responsive/SKILL.md +0 -231
  289. package/framework/skills/retro/SKILL.md +0 -284
  290. package/framework/skills/sakani-conventions/SKILL.md +0 -136
  291. package/framework/skills/sakani-conventions/evals/evals.json +0 -23
  292. package/framework/skills/sakani-conventions/references/entities.md +0 -365
  293. package/framework/skills/sakani-conventions/references/error-codes.md +0 -95
  294. package/framework/skills/seo-master/SKILL.md +0 -490
  295. package/framework/skills/seo-master/references/checklist.md +0 -199
  296. package/framework/skills/seo-master/references/structured-data.md +0 -609
  297. package/framework/skills/ship/SKILL.md +0 -239
  298. package/framework/skills/stack-researcher/SKILL.md +0 -215
  299. package/framework/skills/status/SKILL.md +0 -154
  300. package/framework/skills/status/scripts/health-check.sh +0 -562
  301. package/framework/skills/subscription-payments/SKILL.md +0 -250
  302. package/framework/skills/supabase/SKILL.md +0 -973
  303. package/framework/skills/supabase/references/templates.md +0 -159
  304. package/framework/skills/team/SKILL.md +0 -67
  305. package/framework/skills/test-runner/SKILL.md +0 -202
  306. package/framework/skills/voice-agent/SKILL.md +0 -1312
  307. package/framework/skills/zoho-workflow/SKILL.md +0 -51
  308. package/framework/statusline-command.sh +0 -117
  309. package/framework/teams/default/inboxes/plan-04.json +0 -9
  310. package/framework/teams/review-team.md +0 -75
  311. package/framework/teams/ship-team.md +0 -86
  312. package/profiles/fawzi.json +0 -16
  313. package/profiles/hasan.json +0 -16
  314. package/profiles/moayad.json +0 -16
  315. package/templates/CLAUDE-owner.md +0 -52
  316. package/templates/CLAUDE.md.hbs +0 -58
  317. package/templates/env.claude.template +0 -12
  318. package/templates/settings.json +0 -172
  319. package/uninstall.sh +0 -90
  320. /package/{framework/rules → rules}/deployment.md +0 -0
  321. /package/{framework/rules → rules}/security.md +0 -0
@@ -1,752 +0,0 @@
1
- ---
2
- name: i18n-rtl
3
- description: "Internationalization and RTL (Right-to-Left) layout patterns — Arabic/English bilingual apps, RTL CSS/Tailwind, message key systems, locale switching, numeral handling, date/time formatting, and bidirectional text. Use whenever implementing multi-language support, Arabic UI, RTL layouts, translation systems, or locale-aware formatting in web or mobile apps. Triggers on: i18n, internationalization, RTL, right-to-left, Arabic, translation, locale, language switching, bilingual, message keys, متعدد اللغات."
4
- tags: [i18n, rtl, arabic, localization]
5
- ---
6
-
7
- # Internationalization & RTL (Right-to-Left) Layout Patterns
8
-
9
- This skill covers Arabic/English bilingual application design—message key architectures, RTL layout strategies, locale detection, numeral display, date/time formatting, and bidirectional text handling.
10
-
11
- ## Core Architecture
12
-
13
- ### Message Key System
14
-
15
- All user-facing strings must use message keys. No hardcoded text anywhere—this enables translation, consistency, and independent text updates without code changes.
16
-
17
- **Backend Contract:**
18
- - Backend returns only message keys and structured data
19
- - Never return display text from the server
20
- - Include all context needed for plural/conditional rendering
21
-
22
- **Frontend Resolution:**
23
- - React context or hooks resolve keys → localized strings
24
- - Fallback chain: user locale → default locale (ar) → key itself
25
-
26
- **Key Naming Convention:**
27
-
28
- ```typescript
29
- // In @sakani/shared or dedicated i18n package
30
- // Dot-notation, grouped by domain and entity
31
- const translations = {
32
- ar: {
33
- // Building domain
34
- 'building.name': 'اسم المبنى',
35
- 'building.type.residential': 'سكني',
36
- 'building.type.commercial': 'تجاري',
37
- 'building.label.floorNumber': 'رقم الطابق',
38
-
39
- // Error domain (all error codes → message keys)
40
- 'error.auth.unauthenticated': 'يرجى تسجيل الدخول',
41
- 'error.auth.forbidden': 'ليس لديك صلاحيات كافية',
42
- 'error.validation.required': 'هذا الحقل مطلوب',
43
- 'error.validation.emailInvalid': 'البريد الإلكتروني غير صحيح',
44
-
45
- // Dues domain (reason codes → message keys)
46
- 'dues.status.paid': 'مدفوع',
47
- 'dues.status.unpaid': 'غير مدفوع',
48
- 'dues.status.late': 'متأخر',
49
- 'dues.reason.regularExpense': 'مصروف منتظم',
50
- 'dues.reason.specialAssessment': 'تقييم خاص',
51
-
52
- // Action domain
53
- 'action.create': 'إنشاء',
54
- 'action.update': 'تحديث',
55
- 'action.delete': 'حذف',
56
- 'action.save': 'حفظ',
57
- 'action.cancel': 'إلغاء',
58
- },
59
- en: {
60
- 'building.name': 'Building Name',
61
- 'building.type.residential': 'Residential',
62
- 'building.type.commercial': 'Commercial',
63
- 'building.label.floorNumber': 'Floor Number',
64
-
65
- 'error.auth.unauthenticated': 'Please sign in',
66
- 'error.auth.forbidden': 'You do not have permission',
67
- 'error.validation.required': 'This field is required',
68
- 'error.validation.emailInvalid': 'Email address is invalid',
69
-
70
- 'dues.status.paid': 'Paid',
71
- 'dues.status.unpaid': 'Unpaid',
72
- 'dues.status.late': 'Late',
73
- 'dues.reason.regularExpense': 'Regular Expense',
74
- 'dues.reason.specialAssessment': 'Special Assessment',
75
-
76
- 'action.create': 'Create',
77
- 'action.update': 'Update',
78
- 'action.delete': 'Delete',
79
- 'action.save': 'Save',
80
- 'action.cancel': 'Cancel',
81
- }
82
- };
83
- ```
84
-
85
- **Naming Pattern:**
86
- - `{domain}.{entity}.{property}` — for attributes/labels
87
- - `{domain}.{action}.{context}` — for operations/statuses
88
- - All error codes map 1:1 to a message key
89
- - All reason/status codes are message keys, not magic strings
90
-
91
- ### Locale Detection & Persistence
92
-
93
- **Priority Order:**
94
- 1. User preference (profile setting, synced across devices)
95
- 2. Device locale (from OS settings)
96
- 3. Default: `ar` (Arabic)
97
-
98
- **Persistence:**
99
- - Web: localStorage + server user profile
100
- - Mobile: AsyncStorage + server user profile
101
- - On app load: fetch user preference from profile (source of truth)
102
- - On logout: fall back to device locale
103
- - Offline: use last known preference from device storage
104
-
105
- ## RTL Layout — Web (Tailwind CSS)
106
-
107
- ### Setup
108
-
109
- Install and configure `tailwindcss-rtl` plugin:
110
-
111
- ```bash
112
- npm install tailwindcss-rtl
113
- ```
114
-
115
- ```javascript
116
- // tailwind.config.js
117
- module.exports = {
118
- plugins: [require('tailwindcss-rtl')],
119
- };
120
- ```
121
-
122
- ### Logical Properties (Not Physical)
123
-
124
- Use Tailwind's logical property system—`start`/`end` instead of `left`/`right`, which automatically flip based on text direction.
125
-
126
- **Examples:**
127
-
128
- | Use | Don't Use | When RTL |
129
- |-----|-----------|---------|
130
- | `ms-4` | `ml-4` | becomes `margin-right` |
131
- | `ps-8` | `pl-8` | becomes `padding-right` |
132
- | `text-start` | `text-left` | becomes `text-right` |
133
- | `float-start` | `float-left` | becomes `float-right` |
134
- | `rounded-s-lg` | `rounded-l-lg` | becomes `rounded-r-lg` |
135
- | `border-s-2` | `border-l-2` | becomes `border-r-2` |
136
- | `inset-s-0` | `inset-l-0` | becomes `inset-r-0` |
137
-
138
- **Complete Example:**
139
-
140
- ```jsx
141
- // Header with logo (start) and menu button (end)
142
- <header className="flex items-center justify-between p-4">
143
- <div className="w-10 h-10">Logo</div>
144
- <button>Menu</button>
145
- </header>
146
-
147
- // Sidebar layout — flex-col on mobile, row on desktop
148
- // RTL: flex-row-reverse flips the sidebar to the right
149
- <div className="flex flex-col md:flex-row md:rtl:flex-row-reverse gap-4">
150
- <aside className="w-full md:w-64">Sidebar</aside>
151
- <main className="flex-1">Content</main>
152
- </div>
153
-
154
- // List with icon (left in LTR, right in RTL)
155
- <li className="flex items-center gap-3">
156
- <span className="text-xl">✓</span>
157
- <span>Item text</span>
158
- </li>
159
-
160
- // Input with icon inside
161
- <div className="relative">
162
- <input className="ps-10 pe-4" type="text" />
163
- <span className="absolute inset-s-0 flex items-center ps-3">🔍</span>
164
- </div>
165
- ```
166
-
167
- ### Root Element & Direction
168
-
169
- Set `dir` attribute on the root element:
170
-
171
- ```jsx
172
- // In your layout wrapper or _app.tsx
173
- import { useLocale } from '@/hooks/useLocale';
174
-
175
- export default function App({ Component, pageProps }) {
176
- const { locale } = useLocale();
177
-
178
- return (
179
- <html dir={locale === 'ar' ? 'rtl' : 'ltr'}>
180
- <head>
181
- <meta charSet="utf-8" />
182
- </head>
183
- <body>
184
- <Component {...pageProps} />
185
- </body>
186
- </html>
187
- );
188
- }
189
- ```
190
-
191
- Or with Next.js `next-intl`:
192
-
193
- ```jsx
194
- import { useLocale } from 'next-intl';
195
-
196
- export default function RootLayout({ children }) {
197
- const locale = useLocale();
198
-
199
- return (
200
- <html dir={locale === 'ar' ? 'rtl' : 'ltr'} lang={locale}>
201
- {children}
202
- </html>
203
- );
204
- }
205
- ```
206
-
207
- ### RTL-Specific Overrides
208
-
209
- Use the `rtl:` variant for RTL-only styles:
210
-
211
- ```jsx
212
- // Icon that should point right in LTR, left in RTL
213
- <svg className="w-4 h-4 rtl:rotate-180">
214
- <use href="#arrow-right" />
215
- </svg>
216
-
217
- // Navigation arrow that reverses in RTL
218
- <button className="flex items-center gap-2 rtl:flex-row-reverse">
219
- <span>Next</span>
220
- <ChevronRight className="w-4 h-4" />
221
- </button>
222
-
223
- // Grid that becomes single column in RTL (depends on layout)
224
- <div className="grid grid-cols-3 gap-4 rtl:grid-cols-1">
225
- ...
226
- </div>
227
- ```
228
-
229
- ### Icons & Graphics
230
-
231
- Icons need manual flipping in RTL. Common ones to flip:
232
-
233
- - Chevrons / arrows (right, left)
234
- - Play buttons (play, pause, skip)
235
- - Notifications (bell, envelope)
236
- - Checkmarks / close icons (usually symmetric, no flip)
237
-
238
- **Implementation:**
239
-
240
- ```jsx
241
- import { useLocale } from '@/hooks/useLocale';
242
-
243
- function ChevronRight() {
244
- const { locale } = useLocale();
245
- const isRTL = locale === 'ar';
246
-
247
- return (
248
- <svg className={isRTL ? 'rotate-180' : ''}>
249
- <path d="M9 5l5 7-5 7" />
250
- </svg>
251
- );
252
- }
253
-
254
- // Or use a utility:
255
- function DirectionalIcon({ children, icon }) {
256
- const { locale } = useLocale();
257
- return locale === 'ar' && icon.flipsInRTL ? (
258
- <span className="inline-block rotate-180">{children}</span>
259
- ) : (
260
- children
261
- );
262
- }
263
- ```
264
-
265
- ## RTL Layout — React Native
266
-
267
- ### Forced RTL Mode
268
-
269
- ```typescript
270
- import { I18nManager } from 'react-native';
271
- import { useLocale } from '@/hooks/useLocale';
272
-
273
- // In app initialization / locale context
274
- export function useRTL() {
275
- const { locale } = useLocale();
276
- const isRTL = locale === 'ar';
277
-
278
- useEffect(() => {
279
- I18nManager.forceRTL(isRTL);
280
- // Note: May require app reload on some platforms
281
- }, [isRTL]);
282
-
283
- return isRTL;
284
- }
285
-
286
- // Check current RTL state
287
- const isCurrentlyRTL = I18nManager.isRTL;
288
- ```
289
-
290
- ### Layout Patterns
291
-
292
- ```typescript
293
- import { StyleSheet, View, Text, I18nManager } from 'react-native';
294
-
295
- const styles = StyleSheet.create({
296
- // Use logical properties where possible
297
- container: {
298
- paddingStart: 16,
299
- paddingEnd: 16,
300
- },
301
-
302
- // Flip flex direction for RTL
303
- row: {
304
- flexDirection: 'row',
305
- },
306
- rowReverse: {
307
- flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
308
- },
309
-
310
- // Text alignment
311
- textStart: {
312
- textAlign: I18nManager.isRTL ? 'right' : 'left',
313
- },
314
- textEnd: {
315
- textAlign: I18nManager.isRTL ? 'left' : 'right',
316
- },
317
- });
318
-
319
- // Component with RTL awareness
320
- function Header() {
321
- const isRTL = I18nManager.isRTL;
322
-
323
- return (
324
- <View style={[styles.row, { justifyContent: 'space-between' }]}>
325
- <Text>Title</Text>
326
- <Icon
327
- name={isRTL ? 'chevron-left' : 'chevron-right'}
328
- size={24}
329
- />
330
- </View>
331
- );
332
- }
333
- ```
334
-
335
- ### TextInput & Text Direction
336
-
337
- ```typescript
338
- <TextInput
339
- style={{ textAlign: I18nManager.isRTL ? 'right' : 'left' }}
340
- writingDirection={I18nManager.isRTL ? 'rtl' : 'ltr'}
341
- placeholder="Type here..."
342
- />
343
- ```
344
-
345
- ## Numeral Display
346
-
347
- **Rule: Always display Western Arabic numerals (0–9), never Eastern Arabic (٠–٩).**
348
-
349
- Even in Arabic locale, use `0-9`. This applies to:
350
- - Prices: `1,234.56`
351
- - Quantities: `12`
352
- - Dates: `2025-03-06`
353
- - Phone numbers: `+962 6 123 4567`
354
-
355
- **Implementation:**
356
-
357
- ```typescript
358
- // Correct: Western numerals in Arabic
359
- const formatter = new Intl.NumberFormat('ar-JO');
360
- formatter.format(1234); // → "1,234"
361
-
362
- // Correct: Using date-fns (automatically handles numerals)
363
- import { format } from 'date-fns';
364
- import { ar } from 'date-fns/locale';
365
- format(new Date(), 'd/M/yyyy', { locale: ar }); // → "06/03/2025"
366
-
367
- // Avoid: Eastern Arabic numerals
368
- // ❌ "١٢٣٤" — don't display this in UI
369
- ```
370
-
371
- ## Date & Time Formatting
372
-
373
- ### Storage & Timezone
374
-
375
- - **Store:** Always UTC in the database
376
- - **Display:** Asia/Amman timezone (Jordan local time)
377
- - **Format:** Use `Intl.DateTimeFormat` or `date-fns` with locale
378
-
379
- ### Implementation
380
-
381
- ```typescript
382
- import { format } from 'date-fns';
383
- import { ar, en } from 'date-fns/locale';
384
-
385
- function formatDate(isoDate: string, locale: 'ar' | 'en') {
386
- const date = new Date(isoDate);
387
-
388
- if (locale === 'ar') {
389
- return format(date, 'd MMMM yyyy', { locale: ar });
390
- // → "06 مارس 2025"
391
- }
392
-
393
- return format(date, 'd MMMM yyyy', { locale: en });
394
- // → "6 March 2025"
395
- }
396
-
397
- // Or with Intl.DateTimeFormat
398
- function formatDateIntl(isoDate: string, locale: 'ar' | 'en') {
399
- const date = new Date(isoDate);
400
- return new Intl.DateTimeFormat(locale === 'ar' ? 'ar-JO' : 'en-GB', {
401
- year: 'numeric',
402
- month: 'long',
403
- day: 'numeric',
404
- timeZone: 'Asia/Amman',
405
- }).format(date);
406
- }
407
-
408
- // Time with timezone awareness
409
- function formatTime(isoDate: string, locale: 'ar' | 'en') {
410
- const date = new Date(isoDate);
411
- return new Intl.DateTimeFormat(locale === 'ar' ? 'ar-JO' : 'en-GB', {
412
- hour: '2-digit',
413
- minute: '2-digit',
414
- second: '2-digit',
415
- timeZone: 'Asia/Amman',
416
- }).format(date);
417
- }
418
- ```
419
-
420
- ### Date Formats by Locale
421
-
422
- - **Arabic:** `DD/MM/YYYY` — `06/03/2025`
423
- - **English:** `DD/MM/YYYY` or `MM/DD/YYYY` depending on region
424
- - Always use locale-aware formatters, don't hardcode
425
-
426
- ## Currency Formatting
427
-
428
- ### Jordan Dinar (JOD)
429
-
430
- - **Currency Code:** JOD
431
- - **Decimal Places:** 3
432
- - **Decimal Separator:** `.` (universal) or `٫` (Arabic specific)
433
- - **Symbol Position:** After amount in Arabic, before in English
434
-
435
- ### Implementation
436
-
437
- ```typescript
438
- function formatCurrency(amount: number, locale: 'ar' | 'en') {
439
- const formatter = new Intl.NumberFormat(locale === 'ar' ? 'ar-JO' : 'en-GB', {
440
- style: 'currency',
441
- currency: 'JOD',
442
- minimumFractionDigits: 3,
443
- maximumFractionDigits: 3,
444
- });
445
-
446
- return formatter.format(amount);
447
- // ar: "12٫000 د.أ"
448
- // en: "JOD 12.000"
449
- }
450
-
451
- // Manual formatting with custom formatting
452
- function formatCurrencyCustom(amount: number, locale: 'ar' | 'en') {
453
- const numStr = amount.toFixed(3);
454
- const symbol = 'د.أ'; // JOD symbol
455
-
456
- if (locale === 'ar') {
457
- return `${numStr} ${symbol}`;
458
- }
459
-
460
- return `JOD ${numStr}`;
461
- }
462
- ```
463
-
464
- ## Locale Switching
465
-
466
- ### User Preference Flow
467
-
468
- 1. **On App Load:**
469
- - Fetch user profile (includes locale preference)
470
- - If missing, use device locale
471
- - If not ar/en, default to ar
472
-
473
- 2. **User Changes Language:**
474
- - Update user profile (API call)
475
- - Update local context/state
476
- - Update UI direction (`dir` attribute or `I18nManager.forceRTL`)
477
- - Reload component tree or update i18n library
478
-
479
- 3. **Persistence:**
480
- - Web: localStorage + server (server is source of truth)
481
- - Mobile: AsyncStorage + server (server is source of truth)
482
-
483
- ### Implementation
484
-
485
- ```typescript
486
- // useLocale hook
487
- import { createContext, useContext, useState, useEffect } from 'react';
488
-
489
- const LocaleContext = createContext<{
490
- locale: 'ar' | 'en';
491
- setLocale: (locale: 'ar' | 'en') => Promise<void>;
492
- }>(null);
493
-
494
- export function LocaleProvider({ children }) {
495
- const [locale, setLocaleState] = useState<'ar' | 'en'>('ar');
496
- const [loading, setLoading] = useState(true);
497
-
498
- useEffect(() => {
499
- // Fetch user profile on mount
500
- async function initLocale() {
501
- try {
502
- const profile = await fetchUserProfile();
503
- const userLocale = profile.locale || 'ar';
504
- setLocaleState(userLocale);
505
-
506
- // Apply to DOM/RN
507
- if (typeof document !== 'undefined') {
508
- document.documentElement.dir = userLocale === 'ar' ? 'rtl' : 'ltr';
509
- document.documentElement.lang = userLocale;
510
- }
511
- } catch (err) {
512
- // Fallback to device locale
513
- setLocaleState('ar');
514
- } finally {
515
- setLoading(false);
516
- }
517
- }
518
-
519
- initLocale();
520
- }, []);
521
-
522
- const setLocale = async (newLocale: 'ar' | 'en') => {
523
- try {
524
- // Update server
525
- await updateUserProfile({ locale: newLocale });
526
-
527
- // Update local state
528
- setLocaleState(newLocale);
529
-
530
- // Update DOM
531
- if (typeof document !== 'undefined') {
532
- document.documentElement.dir = newLocale === 'ar' ? 'rtl' : 'ltr';
533
- document.documentElement.lang = newLocale;
534
- }
535
-
536
- // Persist to device storage
537
- if (typeof localStorage !== 'undefined') {
538
- localStorage.setItem('locale', newLocale);
539
- }
540
- } catch (err) {
541
- console.error('Failed to change locale:', err);
542
- }
543
- };
544
-
545
- return (
546
- <LocaleContext.Provider value={{ locale, setLocale }}>
547
- {!loading && children}
548
- </LocaleContext.Provider>
549
- );
550
- }
551
-
552
- export function useLocale() {
553
- const context = useContext(LocaleContext);
554
- if (!context) throw new Error('useLocale must be used within LocaleProvider');
555
- return context;
556
- }
557
- ```
558
-
559
- ## Bidirectional Text
560
-
561
- ### Mixed-Direction Content
562
-
563
- When Arabic and English text mix (e.g., product names, brand names), use Unicode bidirectional formatting to prevent visual corruption.
564
-
565
- **Example Problem:**
566
- ```
567
- Arabic: "اسم المنتج Product Name"
568
- (text-align changes based on first character)
569
- ```
570
-
571
- **Solution: Use `<bdi>` tag or Unicode Isolates**
572
-
573
- ```jsx
574
- // Automatically isolate direction
575
- <p>الشركة: <bdi>Apple Inc.</bdi></p>
576
- // → "الشركة: Apple Inc." (email correct)
577
-
578
- // Or with span and dir attribute
579
- <span dir="auto">مرحبا Hello</span>
580
- // → Direction auto-detected from content
581
-
582
- // Unicode isolates (lowest-level)
583
- <p>{'الشركة: \u2066' + 'Apple Inc.' + '\u2069'}</p>
584
- // \u2066 = LRI (Left-to-Right Isolate)
585
- // \u2069 = PDI (Pop Directional Isolate)
586
- ```
587
-
588
- ### Phone Numbers, Emails, URLs (Always LTR)
589
-
590
- ```jsx
591
- // Phone number in RTL context — always LTR
592
- <p>
593
- الهاتف: <span dir="ltr">+962 6 123 4567</span>
594
- </p>
595
-
596
- // Email address
597
- <a href="mailto:user@example.com" dir="ltr">
598
- user@example.com
599
- </a>
600
-
601
- // URL in Arabic text
602
- <p>
603
- زيارة الموقع: <span dir="ltr">https://example.com</span>
604
- </p>
605
- ```
606
-
607
- ### User-Generated Content
608
-
609
- For text from users (comments, descriptions), use `<bdi>` to auto-detect direction:
610
-
611
- ```jsx
612
- function UserComment({ text, locale }) {
613
- return (
614
- <div className={locale === 'ar' ? 'text-right' : 'text-left'}>
615
- <bdi>{text}</bdi>
616
- {/* Auto-detects whether text is RTL or LTR */}
617
- </div>
618
- );
619
- }
620
- ```
621
-
622
- ## Common Mistakes & How to Avoid
623
-
624
- ### 1. Using Physical Properties Instead of Logical
625
-
626
- ```jsx
627
- // ❌ Wrong: Text will be on the wrong side in RTL
628
- <div className="ml-4 float-left">Content</div>
629
-
630
- // ✓ Correct: Automatically flips in RTL
631
- <div className="ms-4 float-start">Content</div>
632
- ```
633
-
634
- ### 2. Forgetting to Flip Icons
635
-
636
- ```jsx
637
- // ❌ Wrong: Arrow always points right
638
- <button>
639
- Next <ChevronRight />
640
- </button>
641
-
642
- // ✓ Correct: Icon flips based on locale
643
- function NextButton() {
644
- const { locale } = useLocale();
645
- return (
646
- <button className={locale === 'ar' ? 'flex-row-reverse' : ''}>
647
- Next
648
- <ChevronRight className={locale === 'ar' ? 'rotate-180' : ''} />
649
- </button>
650
- );
651
- }
652
- ```
653
-
654
- ### 3. Testing with Latin Placeholders Hides RTL Bugs
655
-
656
- ```
657
- ❌ "Lorem ipsum" in RTL container looks correct
658
- ✓ "مرحبا بك" (Arabic) immediately reveals direction issues
659
- ```
660
-
661
- **Always test with actual Arabic text**, not Lorem Ipsum.
662
-
663
- ### 4. Hardcoding Text Alignment
664
-
665
- ```jsx
666
- // ❌ Wrong: Alignment is fixed regardless of direction
667
- <h1 style={{ textAlign: 'left' }}>Title</h1>
668
-
669
- // ✓ Correct: Flips based on locale
670
- <h1 className="text-start">Title</h1>
671
- // or
672
- <h1 style={{ textAlign: locale === 'ar' ? 'right' : 'left' }}>Title</h1>
673
- ```
674
-
675
- ### 5. Not Isolating Mixed-Direction Content
676
-
677
- ```jsx
678
- // ❌ Wrong: "Product Name" gets pulled to the wrong side
679
- <p>المنتج: Product Name</p>
680
-
681
- // ✓ Correct: Uses bidirectional isolation
682
- <p>المنتج: <bdi>Product Name</bdi></p>
683
- ```
684
-
685
- ### 6. Using Eastern Arabic Numerals (٠–٩)
686
-
687
- ```
688
- ❌ "السعر: ١٢٣٤"
689
- ✓ "السعر: 1234"
690
- ```
691
-
692
- Always use Western numerals (0–9), even in Arabic UI.
693
-
694
- ## Recommended Libraries
695
-
696
- ### Web (Next.js or React)
697
-
698
- - **`next-intl`** — Built for Next.js, excellent RTL support, type-safe message keys
699
- ```bash
700
- npm install next-intl
701
- ```
702
- Configuration in `next.config.js`, message files in `messages/` directory.
703
-
704
- - **`react-i18next`** — Popular, flexible, works with any React setup
705
- ```bash
706
- npm install i18next react-i18next
707
- ```
708
- Share translation files with backend/mobile via `@sakani/shared`.
709
-
710
- ### Mobile (React Native/Expo)
711
-
712
- - **`react-i18next`** — Same library as web, share translation files
713
- - **`expo-localization`** — Get device locale
714
- ```bash
715
- npx expo install expo-localization
716
- ```
717
-
718
- ### Shared (Backend & Frontend)
719
-
720
- - **`i18next`** — Core i18n engine, language-agnostic
721
- - Message files in JSON or TypeScript in shared package (`@sakani/shared/i18n/`)
722
- - Both frontend and backend import from shared package
723
-
724
- ### Date & Time
725
-
726
- - **`date-fns`** — Lightweight, tree-shakeable, excellent locale support
727
- ```bash
728
- npm install date-fns
729
- ```
730
- Import locales: `import { ar } from 'date-fns/locale'`
731
-
732
- - **Native `Intl` API** — Built-in, no dependencies, sufficient for most use cases
733
- ```typescript
734
- new Intl.DateTimeFormat('ar-JO', { ... }).format(date)
735
- ```
736
-
737
- ## Summary Checklist
738
-
739
- - [ ] All UI strings use message keys (`key.domain.entity`)
740
- - [ ] Backend returns message keys, never display text
741
- - [ ] Frontend resolves keys via i18n library (next-intl, react-i18next, etc.)
742
- - [ ] Web: `tailwindcss-rtl` installed, using logical properties (`ms-`, `ps-`, `text-start`)
743
- - [ ] Web: Root `dir="rtl"` set when locale is Arabic
744
- - [ ] Mobile: `I18nManager.forceRTL(isArabic)` on app init
745
- - [ ] Icons tested and flipped for RTL (chevrons, arrows)
746
- - [ ] Date formatting uses `Intl.DateTimeFormat` or `date-fns` with locale
747
- - [ ] All numerals are Western (0–9), never Eastern (٠–٩)
748
- - [ ] Mixed-direction content (Arabic + English) uses `<bdi>` or `dir="auto"`
749
- - [ ] Phone numbers, emails, URLs wrapped in `dir="ltr"`
750
- - [ ] Locale preference stored in user profile (synced) + device storage (offline)
751
- - [ ] Locale switcher tested: changes are persisted and UI updates immediately
752
- - [ ] RTL testing done with **actual Arabic text**, not placeholders