# ISSUE-277 Artifact — shared i18n registry parity report (Django ↔ Angular MVP) Issue: **#277** (`[READY][#175][P3] Shared i18n registry artifact: backend/frontend keyspace parity report`) ## Artifact metadata - `artifact_id`: `issue-277-shared-i18n-parity-report` - `artifact_version`: `1.0` - `generated_at`: `2026-03-13T09:10:14Z` - `catalog_source`: `shared/i18n/lobby.json` - `generator`: `scripts/report_i18n_parity.py` - `delivery_pr`: `#282` - `delivery_head_sha`: `b8a9fbf6d15c1a2c8d40ce6a3273858019b12889` ## Naming/version rules (email-manager-inspired strategy) - **Single canonical artifact per issue**: issue-bundne rapporter navngives `docs/ISSUE---ARTIFACT.md`. - **Stable artifact identity**: `artifact_id` ændres ikke ved tekstlige opdateringer i samme rapporttype; det er den faste reference i review/ops. - **Explicit artifact versioning**: `artifact_version` bumpes, når rapportlogik eller scope ændres, så drift/review kan se forskel på format- vs. dataændringer. - **Shared namespace first**: keys refereres med fulde navnerum (`frontend.ui.*`, `frontend.errors.*`, `backend.error_codes.*`, `backend.errors.*`) i stedet for lokale aliases i artefakter. - **Source-of-truth before consumers**: rapporten afledes fra `shared/i18n/lobby.json`; Django/Angular beskrives som consumers af samme registry og ikke som parallelle kontrakter. ## MVP-critical parity summary - Frontend UI gameplay keys checked: **16** → `OK` - Frontend error keys checked: **7** → `OK` - Backend gameplay/error codes checked: **9** → `OK` - Distinct frontend error keys reached from backend MVP flow: **6** (`join_failed, nickname_invalid, nickname_taken, session_code_required, session_not_found, start_round_failed`) Status: **Shared locale matrix is aligned (`en`, `da`) and backend→frontend error handling is contract-complete for MVP-critical flow.** ## Django ↔ Angular parity matrix (MVP-critical error contract) | Backend code (`backend.error_codes.*`) | Django message key (`backend.errors.*`) | Angular key (`frontend.errors.*`) | Locales `en/da` | Parity note | |---|---|---|---|---| | `session_code_required` | `session_code_required` | `session_code_required` | `OK` | 1:1 | | `nickname_invalid` | `nickname_invalid` | `nickname_invalid` | `OK` | 1:1 | | `session_not_found` | `session_not_found` | `session_not_found` | `OK` | 1:1 | | `session_not_joinable` | `session_not_joinable` | `join_failed` | `OK` | mapped alias | | `nickname_taken` | `nickname_taken` | `nickname_taken` | `OK` | 1:1 | | `category_slug_required` | `category_slug_required` | `start_round_failed` | `OK` | many:1 collapse | | `category_not_found` | `category_not_found` | `start_round_failed` | `OK` | many:1 collapse | | `round_start_invalid_phase` | `round_start_invalid_phase` | `start_round_failed` | `OK` | many:1 collapse | | `round_already_configured` | `round_already_configured` | `start_round_failed` | `OK` | many:1 collapse | ## Scope notes - **Django** consumes backend codes/messages directly from `shared/i18n/lobby.json` via `lobby/i18n.py`. - **Angular** consumes the same registry via `frontend/shared/i18n/lobby-loader.ts` and runtime helpers in `frontend/angular/src/app/lobby-i18n.ts`. - **Parity in MVP** is therefore strongest on the shared error contract and locale matrix; gameplay UI labels are frontend-owned but still live in the same registry. ## Verified MVP gameplay UI keyspace present in the shared registry - `frontend.ui.host.title` - `frontend.ui.player.title` - `frontend.ui.common.session_code` - `frontend.ui.player.nickname` - `frontend.ui.player.join` - `frontend.ui.host.start_round` - `frontend.ui.host.show_question` - `frontend.ui.player.lie_label` - `frontend.ui.player.submit_lie` - `frontend.ui.player.submit_guess` - `frontend.ui.host.mix_answers` - `frontend.ui.host.calculate_scores` - `frontend.ui.host.load_scoreboard` - `frontend.ui.host.final_leaderboard` - `frontend.ui.player.final_leaderboard` - `frontend.ui.common.points_short` ## Concrete deviations / follow-up items 1. **Error granularity collapse remains intentional**: backend codes `category_slug_required, category_not_found, round_start_invalid_phase, round_already_configured` all map to `frontend.errors.start_round_failed`. Follow-up only if product wants case-specific Angular copy instead of one shared host failure message. 2. **Frontend-only fallback copy is not mirrored in Django**: `frontend.errors.unknown` and `frontend.errors.session_fetch_failed` are Angular-side resilience keys, not backend contract keys. Follow-up if API responses should expose stable backend equivalents for these states. 3. **Gameplay UI labels are registry-shared but not backend-rendered**: `frontend.ui.host.*`, `frontend.ui.player.*`, and `frontend.ui.common.*` are available in the shared artifact, but Django currently consumes only the backend error slice. Follow-up only if server-rendered views must guarantee the same UI label surface as Angular. ## Re-run ```bash python3 scripts/check_i18n_drift.py python3 scripts/report_i18n_parity.py ```