| name | frontend-translations |
| description | MANDATORY frontend translations with @aexol/dynamite - ALL user-facing text MUST use t(). useDynamite hook, factory patterns for data/schemas, cookie-based locale |
ALL user-visible text MUST use t(). Hardcoded user-facing strings are bugs.
Usage
import { useDynamite } from '@aexol/dynamite';
const { t, locale } = useDynamite();
t('Hello World');
t('Welcome, {{name}}', { name: 'John' });
Factory Pattern (data files & Zod schemas)
Files outside React tree can't call hooks — accept t as parameter:
export const getFeatures = (t: (key: string) => string) => [{ title: t('Feature One') }];
export const createSchema = (t: (key: string) => string) =>
z.object({ name: z.string().min(2, t('Name must be at least 2 characters')) });
export type SchemaValues = z.infer<ReturnType<typeof createSchema>>;
const schema = createSchema(t);
const form = useForm({ resolver: zodResolver(schema) });
Locale (Cookie-Based)
- Locale stored in a
locale cookie — NOT localStorage, NOT Zustand
LanguageSwitcher sets cookie + window.location.reload()
- Server reads cookie → SSR renders correct language
- New locale: add to
SUPPORTED_LOCALES in backend/src/config/cookies.ts, create frontend/public/locales/{code}/common.json, restart dev
What to Translate
Always: buttons, page titles, form labels/placeholders, error messages, toasts, table headers, tab labels, nav items, empty states, confirmation dialogs
Never: brand names, code snippets, URLs, strings in api//lib//stores/ that don't reach UI
Rules
- Key = English text — flat, no nesting
- Factory pattern for all data files and Zod schemas
- Locale via cookie only
- NEVER manually edit non-English locale files — auto-generated by
@aexol/vite-plugin-dev-translate
- Commit generated translation files to git
- Files outside React tree (
api/, lib/) can't call useDynamite() — leave in English
- Date formatting: use
locale from useDynamite() with BCP 47 map: { en: 'en-US', pl: 'pl-PL' }