Files
baya-monorepo/client/messages/STYLE.md
T
2026-07-19 21:31:59 +03:30

117 lines
7.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Persian (fa) copy style guide
One page, binding for `messages/fa.json`. `scripts/check-copy.mjs` (`npm run lint:copy`, part of
`npm run check`) enforces the banned-variant rules below so these decisions cannot silently regress.
This file does not repeat `en.json` conventions beyond what's noted in §7 — the English catalog is
hand-written and reviewed for idiom, not linted.
## 1. Brand name
**«بالین‌یار» — ZWNJ (``) between بالین and یار, always.** Never a plain space («بالین یار»).
The brand name appears in money/trust copy (login, escrow, refunds) as often as anywhere else — an
unstable brand mark there is the worst place to be inconsistent.
## 2. تأیید — hamza, always
Write **تأیید** (with hamza) and its derived forms — **تأییدشده**, **تأییدیه**, **تأیید کردن** — every
time, never تایید/تاییدشده/تاییدیه (hamza-less). This is the single most frequent word in a
verification product; one spelling, no exceptions, in every namespace (booking, payment, auth,
verification, admin, payouts, bnpl, refunds, legal — all of them).
## 3. جستجو — one form
Standard form: **جستجو** (no ZWNJ, one word). Not «جست‌وجو» / «جست و جو». Applies to the noun and any
compound (`در جستجو`, `نتایج جستجو`).
## 4. ZWNJ (نیم‌فاصله) rules
Use ZWNJ (``) — never a plain space or no separator — in:
- **می‌ + verb stem**: می‌شود، می‌کند، می‌پردازید، می‌ماند (never میشود/می شود).
- **Plural ها**: مراقب‌ها-style compounds keep the ZWNJ before ها when the base ends in a consonant that
would otherwise misread (`شب‌ها` not `شبها`); a plain plural on a word already ending in a vowel/silent-h
takes the ZWNJ too (`بچه‌ها`).
- **Compound past-participle adjectives**: تأییدشده، لغوشده، ردشده، منتشرشده، پرداخت‌شده — the doer/state
compound is one ZWNJ-joined word, not two spaced words («تایید شده») and not fused with no separator.
- Brand name itself (§1) is the other load-bearing ZWNJ case.
## 5. Punctuation & quotes
- Persian text uses «...» guillemets for quoted terms/labels in prose (as this document does), and
Persian «،» / «؛» for commas/semicolons *inside translated sentences* where the surrounding punctuation
is itself Persian prose (most UI strings use plain Latin `,`/`;` today for simplicity in short labels —
don't retrofit existing short strings, but prefer «،»/«؛» in new multi-clause sentences).
- English (`en.json`) uses **straight** apostrophes (`don't`, `couldn't`) throughout — never curly
(``, ``). One admin-namespace holdout (curly `don't`/`couldn't`) is fixed by this phase; don't
reintroduce curly quotes when editing English copy.
## 6. Domain glossary
- **بیمار** — the care recipient, used consistently everywhere except one parenthetical. Do not adopt
«مددجو» — it appeared exactly once (`booking.patient_label`) and has been dropped in favor of the
99%-majority «بیمار».
- **پرستار** — the caregiver, always (never «مراقب» as a noun for the person — «مراقب» only survives as
an adjective/role qualifier, e.g. `booking.gender_label` "جنسیت مراقب" meaning "the caregiver's gender").
- **رزرو** — a confirmed, paid booking. **درخواست رزرو** — a pre-payment request. Never conflate the two;
a `booking_request` is never called «رزرو» before it converts.
- **ویزیت** — one scheduled visit/session within a booking.
- **شبا** — IBAN, always («شماره شبا» for the field label, «شبا» alone elsewhere).
## 7. Shell naming system
One metaphor per audience class, not four:
- **End-user shells** (family, nurse — the apps people book/work through day to day) → **«اپلیکیشن»**:
«اپلیکیشن خانواده», «اپلیکیشن پرستار».
- **Back-office shells** (staff consoles — admin, partner-center) → **«کنسول»**: «کنسول مدیریت»,
«کنسول همکار».
- Never «نما» (view) or «پرتال» (portal) for a whole shell name — those read as one-off inconsistent
metaphors. (`booking.evv_nurse_view` "نمای پرستار" is a different thing — a chip labeling *whose
perspective* a shared booking-detail screen is rendered from, not a shell name; it correctly keeps
«نما» in that narrower sense.)
## 8. Verification pipeline vs. the identity step
**«تأیید صلاحیت»** names the whole 7-step nurse trust pipeline (nav entry, the verification hub's title,
its start/progress/approved states, the admin queue). **«احراز هویت»** stays the name of the one KYC
step inside it (national-ID + civil-registry + liveness selfie) — both on the nurse side
(`verification.step_identity_kyc`) and the admin side (`admin.step_identity_kyc`). A nurse who passed the
KYC step but still sees a pipeline titled «احراز هویت» incomplete in the nav used to read as a
contradiction; they no longer share a name.
## 9. Status vocabulary — one nurse-facing form, one admin-facing form
For "this step/item was rejected/failed" style states that appear on both a nurse-facing screen and an
admin-facing screen for the *same underlying concept* (a verification step's outcome):
- **Nurse-facing**: «رد شد» (`verification.status_failed`) — a short declarative sentence-style status,
matching the register of its sibling `status_passed` ("تأییدشده")/`status_in_review` ("در حال بررسی").
- **Admin-facing**: «ردشده» (`admin.step_failed`, `admin.agg_rejected`, `admin.rstatus_rejected`,
`admin.mstatus_rejected`) — the compound-adjective state form, matching the admin namespace's own
`step_passed`/`agg_approved`/`center_state_verified` ("تأییدشده") pattern.
This does **not** extend to unrelated money-failure vocabulary (`payouts.pstatus_failed`,
`refunds.rstatus_failed`, `admin.batch_status_failed` all legitimately use «ناموفق» — a transfer/payment
*failing* is a different concept from a document being *rejected*, and conflating them would blur a real
distinction).
## 10. Digits policy
Persian digits (۰۱۲۳۴۵۶۷۸۹) everywhere on `/fa` — both hard-coded literals (`"۲۴ ساعت"`) and
interpolated numbers. For an interpolated `{count}`/`{hours}`/… inside an ICU message, use the ICU
`number` sub-format (`{count, number}`) or a plain `#` inside a `plural` block — next-intl formats both
through the active locale (`fa` → Persian digits) automatically. When a raw number is interpolated at a
call site instead of through ICU (e.g. built into a larger string in code, not a message placeholder),
route it through `formatNumber` (`@/utils`) — never template a raw JS number directly into Persian text.
## 11. Register
Formal شما throughout, with polite imperatives (کنید) for actions and instructions. Already consistent
across the whole catalog — this codifies it so a future addition can't drift into informal تو/imperative
stems (نکن, برو).
## 12. Policy numbers
Legally/financially sensitive numbers that the admin config panel can change (the dispute-window hours,
cancellation lead-time hours, refund ETA days) are **never hard-coded into a message string**. The
message key takes a parameter (`{hours}`, `{minDays}`/`{maxDays}`) and the call site interpolates from
`client/src/constants/policy.ts` (single-sourced, REQ-065 tracks the eventual public config-read that
replaces the constants file). A config edit must never again silently make the UI copy lie.