Add Russian i18n with DB translations, locale API, and curator review UI.

UI chrome via react-i18next, catalog text in entity_translations with ru.wikipedia seeding, locale-aware search, and Translations page for publish workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-07-15 11:51:46 +03:00
co-authored by Cursor
parent f247b418d8
commit ca58c43648
51 changed files with 2252 additions and 101 deletions
+105
View File
@@ -0,0 +1,105 @@
# 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).