Fill frontend rule gaps and broaden v2 tokens
Add the frontend guides the v2 UI kit and compliance-portal need but that the first rework left uncovered: forms, routing, client state, and permission-gated UI. forms.md documents a tiered approach on Base UI Field/Form -- native constraints, then a validate function, then zod parsed in onSubmit, and react-hook-form only for large or dynamic forms -- and drops the custom useFormWithSchema wrapper. routing.md covers @probo/routes, navigation, typed params, URL-as-state, redirects, auth/protected routes, and the folded-in no-outlet-context rule. state-management.md gives a decision order across Relay, URL, local state, context, and zustand. permissions.md gates UI on the canUpdate/canDelete permission(action:) fields without re-encoding authorization in the client. Rename v2-colors.md to v2-tokens.md and add the typography, radius, shadow, and native-spacing scales alongside color. Extend ui.md with user feedback, empty-state, and accessibility sections; standardize toasts on Base UI's Toast (Toast.useToastManager) and retire the legacy useToast across ui.md, forms.md, error-handling.md, and relay.md. Add an Intl formatting section to i18n.md and a non-Relay HTTP / file upload-download section to ts-style.md. Update the AGENTS.md index and the v2-color-scale cursor rule for the new and renamed guides. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -96,6 +96,28 @@ Never assemble translated sentences by concatenation — it breaks word order in
|
||||
t("measures.count", { count });
|
||||
```
|
||||
|
||||
## Formatting dates, numbers, and currency
|
||||
|
||||
Locale-aware formatting is **presentation** and must follow the active locale — never hand-format with string templates or hardcoded separators. Use i18next's [`Intl`-based formatting](https://www.i18next.com/translation-function/formatting) (which wraps `Intl.DateTimeFormat` / `Intl.NumberFormat` with the current language) or `Intl` directly when outside a translation string.
|
||||
|
||||
```tsx
|
||||
// i18next interpolation formatters — locale comes from the active language
|
||||
t("measures.updatedAt", { date: updatedAt, formatParams: { date: { dateStyle: "medium" } } });
|
||||
t("invoice.total", { amount, val: amount, formatParams: { val: { style: "currency", currency: "EUR" } } });
|
||||
|
||||
// Outside a translation string — Intl directly with the active locale
|
||||
const { i18n } = useTranslation();
|
||||
new Intl.NumberFormat(i18n.language, { style: "percent" }).format(ratio);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// Bad — hand-rolled, locale-blind formatting
|
||||
`${(ratio * 100).toFixed(0)}%`;
|
||||
`${day}/${month}/${year}`;
|
||||
```
|
||||
|
||||
Reuse `@probo/helpers` only for its **non-presentational** date utilities — parsing and `<input type="date">` shaping (`parseDate`, `toDateInput`, `todayAsDateInput`). Do not use its v1 `formatDate` / `formatDuration` helpers in v2 apps: they predate i18next and thread the legacy `@probo/i18n` `__` translator. Format for display through i18next / `Intl` instead.
|
||||
|
||||
## Loading catalogs
|
||||
|
||||
Catalogs are loaded into i18next per locale at app startup (or lazily per route). Because each `_locales/` folder maps to a resource segment, catalogs can be code-split alongside the route bundle that needs them — keep a catalog scoped to the feature it serves rather than one global megafile.
|
||||
|
||||
Reference in New Issue
Block a user