## i18n Guide Readest uses a **key-as-content** approach — English strings are the translation keys. The English locale (`en/translation.json`) is empty because keys serve as content. Other locales contain actual translations. ### In React Components ```tsx import { useTranslation } from '@/hooks/useTranslation'; const _ = useTranslation(); _('Progress synced'); ``` ### In Non-React Modules Two-step process: **1. Declaration** — Use `stubTranslation` to mark strings for scanner extraction (returns key as-is, does NOT translate): ```ts import { stubTranslation as _ } from '@/utils/misc'; // These calls only register keys for extraction _('Reveal in Finder'); _('Reveal in Explorer'); ``` **2. Usage** — In the React component that consumes the value, apply the real `_()` from `useTranslation`: ```tsx const _ = useTranslation(); const label = _(getRevealLabel()); // translates at runtime ``` ### Extraction & Translation ```bash pnpm i18n:extract # Scans codebase, adds new keys with __STRING_NOT_TRANSLATED__ ``` - Translation files: `public/locales//translation.json` - Only `_('KEY')` and `_('KEY', options)` patterns are recognized by i18next-scanner ### Rules - `stubTranslation` is for extraction only — always apply `_()` from `useTranslation` in the component for runtime translation. - Fallback: when no translation exists, the English key itself is displayed. - Error messages: register keys with `stubTranslation` in utility modules (e.g. `src/services/errors.ts`), return the English key from helpers, wrap with `_()` in the component.