4.6 KiB
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_localeinlocalStorage - Sets
document.documentElement.lang - Passes
?locale=ruon catalog API requests - Refetches bootstrap catalog when locale changes
Timeline layout chrome and chart hints are also localised in home.json:
| Key | Where |
|---|---|
layoutHorizontal / layoutVertical / layoutTree |
Header layout switch |
captionClassicTimeline / captionClassicFlow |
Classic era bar + movement streams |
captionVerticalTimeline / captionVerticalFlow |
Vertical rail + streams |
captionTreeFlow |
Tree of Art chart |
Shareable layout URLs (?layout=tree etc.) are language-independent; captions follow the active locale.
Setup (dev)
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:
[
{ "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
- Sign in as curator
- Header → Translations (or Переводы in RU UI)
- Pick entity type (artist / painting / movement)
- Select row → edit Russian fields side-by-side with English canonical
- 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.
Translatable fields (v1)
| entity_type | fields |
|---|---|
era, movement |
name, description |
artist |
name, bio_short, bio_full |
artist_period |
name, description |
painting |
title, description (not curator_notes — English-only for now) |
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 name / title aliases (partial index entity_translations_search_alias_idx; long bio_full text is stored but not btree-indexed).
Prod rollout
npm run dev:migrateon dev; curator review + publish Russian rows on devnpm run devtoprod:release(fullprofile) — includes Step 4 schema migrate + Step 5 restore; ornpm run harmonize:schemathen harmonize/sync if you prefer merge over full promote- After restore, verify locale toggle on https://gallery.mysuperlab.netcraze.pro
entity_translations is included in harmonize-db.js catalog sync.
dev:fetch-artist-bios-ru index limit
Early migrate-i18n.sql indexed all value text; very long Russian bios (e.g. Michelangelo) could fail with index row size … exceeds btree maximum. Current migration uses a partial index on name and title only (char_length(value) <= 512). Re-run npm run dev:migrate (or prod Step 4) after pulling this fix, then re-run npm run dev:fetch-artist-bios-ru for any artists that failed.
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, environments.md, harmonize-dev-prod.md.