# Russian localization (i18n) Full Russian support: **UI chrome** via `react-i18next`, **catalog text** via PostgreSQL `entity_translations`, with Cyrillic display aliases where available and English fallback. --- ## Architecture | Layer | English | Russian | |-------|---------|---------| | UI labels, buttons | `client/src/locales/en/*.json` | `client/src/locales/ru/*.json` | | Names / titles (display) | canonical DB columns | `entity_translations` (`name`, `title`) | | Bios, notes, descriptions | canonical DB columns | `entity_translations` (`bio_full`, `body`, `notes`, …) | English remains canonical in main tables. Russian rows use `status`: `draft` → `reviewed` → `published`. Public API returns only **`published`** (unless curator preview). --- ## User-facing locale switch Timeline header: **EN | RU** toggle (`LocaleSwitcher`). - Persists `gallery_locale` in `localStorage` - Sets `document.documentElement.lang` - Passes `?locale=ru` on catalog API requests - Refetches bootstrap catalog when locale changes --- ## Setup (dev) ```powershell npm run dev:migrate # includes migrate-i18n.sql npm run dev:fetch-artist-bios-ru # draft bios + Cyrillic names from ru.wikipedia # Curator: Translations page → review → Publish npm run dev:import-translations -- --file path/to/translations.json --publish ``` Optional manual import JSON: ```json [ { "entity_type": "painting", "entity_id": 42, "field_name": "title", "value": "Джоконда", "status": "published", "source": "manual" } ] ``` CSV: `entity_type,entity_id,field_name,value,status,source` --- ## Curator translation review 1. Sign in as curator 2. Header → **Translations** (or **Переводы** in RU UI) 3. Pick entity type (artist / painting / movement) 4. Select row → edit Russian fields side-by-side with English canonical 5. **Publish** saves and marks rows `published` Coverage stats show artists with `bio_full`, paintings with `title` alias, draft vs published counts. API (curator-only): see [API.md](API.md#translations-curator). --- ## Translatable fields (v1) | entity_type | fields | |-------------|--------| | `era`, `movement` | `name`, `description` | | `artist` | `name`, `bio_short`, `bio_full` | | `artist_period` | `name`, `description` | | `painting` | `title`, `description` | | `annotation` | `label`, `body` | | `influence_source` | `notes`, `aspects`, `quote`, `period_note` | --- ## API locale Public endpoints accept `?locale=ru` or `Accept-Language: ru`. Responses include `"locale": "ru"` on catalog payloads; field names unchanged — values are already resolved. Search matches canonical text **or** published Russian aliases. --- ## Prod rollout 1. `npm run dev:migrate` on dev; prod schema: `npm run harmonize:schema` (dev → prod only) 2. Seed Russian on dev: `npm run dev:fetch-artist-bios-ru` 3. Curator review + publish 4. `npm run harmonize` to sync `entity_translations` to prod (or full promote if preferred) `entity_translations` is included in [`harmonize-db.js`](../scripts/harmonize-db.js) catalog sync. --- ## npm scripts | Script | Purpose | |--------|---------| | `dev:migrate:i18n` | Apply `entity_translations` table only | | `dev:fetch-artist-bios-ru` | Fetch ru.wikipedia bios into translations (draft) | | `dev:import-translations` | Import JSON/CSV translation rows | See also [setup.md](setup.md), [environments.md](environments.md), [harmonize-dev-prod.md](harmonize-dev-prod.md).