Full-repo survey of every player-facing German string (seed content, API-computed reputation rank labels, five client-side error-message translation maps, six static Angular label maps, all combat-log generation, ~30 component templates), plus rulings on everything the source spec leaves open: monster keys stay unchanged (only name changes), no migration is needed (content lives in seed-file values, not schema), reputation rank labels move on the API side, combat log templates are freely composed rather than translated 1:1, and the Renown/Reputation German-only "Ansehen"/"Ruf" split carries over as "World Renown"/"Reputation" without the German-specific collision problem that motivated it. A complete glossary gives every implementer the exact English value to use verbatim -- no translation judgment calls left to task execution. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
268 lines
28 KiB
Markdown
268 lines
28 KiB
Markdown
# English Game Content Foundation — Design
|
|
|
|
**Source spec:** `docs/playable-slices/0.6.6-English-Game-Content-Foundation.md`
|
|
**Depends on:** Slice 0.6.5 (Renown & Reputation Foundation), merged to master at `cedd87f`.
|
|
**Classification:** Architectural (touches ~40 files across both apps; binds a large SDD plan), though the change itself is conceptually simple: mechanical content/string replacement plus a small number of terminology decisions the source spec left open. No new subsystem, no new abstraction, no new data flow.
|
|
|
|
## 1. Purpose
|
|
|
|
Convert every player-facing German string in the currently playable loop to English, per the source spec. Development docs and internal `key`/`code` identifiers stay as they are. No localization framework, no language switch, no translation table — this is a one-time content and copy conversion.
|
|
|
|
## 2. What the source spec already decided (binding, not re-litigated here)
|
|
|
|
- Player-facing content is English from this slice on; dev docs may stay German.
|
|
- Technical `key` and error `code` values are stable and do not change because of this slice.
|
|
- No large localization framework. Centralized constants/text maps are fine for static UI text.
|
|
- Persisted content keeps `key` / `name` / `description` shape; only the values change.
|
|
- Domain error `code` stays stable; only the player-facing `message` is English (already true everywhere — see R2).
|
|
- Terminology tables for locations, monsters, core combat actions, navigation, common UI, and danger labels (source spec §5) are used verbatim where they cover a string.
|
|
- Recommended Tier-1 item display names (source spec §6) are used verbatim; do not rename item `key`s just to prettify them.
|
|
|
|
## 3. Survey — current state
|
|
|
|
A full-repo survey (not reproduced here) found German player-facing text in: seed/content files (items, locations, local-location POI/action content, the reputation faction), a client-side reputation-rank label list on the API (dynamic, not a seed row), five web-side error-message translation maps that re-translate already-English API error `code`s back to German for display, six static Angular label maps (`SLOT_LABELS`, `RARITY_LABELS`, `DANGER_LABELS`, `LOCATION_TYPE_LABELS`, and two ad-hoc binary danger labels), all combat-log generation (entirely client-side, template strings keyed off structured event types), and roughly three dozen component templates. It also confirmed several things are **already in scope-compliant English and need no change**: every domain error `message` and `code`, every content `key`/slug, the `ItemRarity`/`DangerRating`/`EquipmentSlot` enum values themselves (only their *display* maps are German), and the API's combat engine (pure structured events, no strings at all — already matches source spec §7's stated preference).
|
|
|
|
## Rulings
|
|
|
|
**R1 — Monster `key`s do not change; only `name` changes.**
|
|
The source spec's suggested keys (`feral-road-hound`, `charred-raider`) don't match the existing seeded keys (`wild-road-dog`, `charred-looter`). Source spec §4 and §6 say not to rename stable keys merely to prettify them, and while that's stated about items, the same reasoning applies with more force to monsters: `LocationMonster`, `HuntEncounter`, and `CombatEvent` rows reference monster identity, and a key rename multiplies blast radius for zero player-visible benefit — only `name` is ever rendered. Existing keys stay; only `MonsterDefinition.name` changes to the source spec's English label.
|
|
**Cost if wrong:** a follow-up key rename touching a few more FK-adjacent rows; cheap, since nothing has executed against a real database yet (see R7).
|
|
|
|
**R2 — Domain error `message` strings need no change.**
|
|
Surveyed all nine `*.errors.ts` files on the API: every `message` is already English, every `code` is already a stable English identifier. This part of the source spec's scope (§9) is already satisfied. What *is* German is a set of **client-side** maps that re-translate the already-English `code` back into German for display (`world.store.ts`, `hunting.store.ts`, `combat.store.ts`, `inventory.store.ts`, `local-location.store.ts`) — those five maps are this slice's actual error-message work, and it is a pure frontend edit.
|
|
**Cost if wrong:** none; this is a factual finding, not a judgment call.
|
|
|
|
**R3 — Reputation rank labels move on the API side, not the frontend.**
|
|
`apps/api/src/reputation/reputation-rank.ts` computes `rankLabel` server-side and returns it as part of `GET /api/reputation` and turn-in results. Unlike the other label maps in scope (which are static Angular constants), this one is API-computed and reaches the client dynamically. English labels are the rank enum keys, humanized — see the Reputation section of the glossary below.
|
|
**Cost if wrong:** touches one file either way; low.
|
|
|
|
**R4 — This is a content edit, not a migration.**
|
|
None of the German strings live in a column definition or a `CREATE TABLE`; they live in row *values* written by `upsert(..., ['key'])` calls in seed files. No `ALTER TABLE` is needed anywhere in this slice — only seed-file literal changes. Per this project's standing practice, no migration runs against any database this session (ownership of the local Postgres container remains unconfirmed); the seed's `upsert` will apply these values the next time someone runs the seed against a real database. This mirrors the source spec §10's "update existing rows by stable key" guidance exactly — an `upsert` keyed on `key` *is* that update, once run.
|
|
**Cost if wrong:** nothing to undo; this is a description of the existing mechanism, not a new decision.
|
|
|
|
**R5 — Combat log templates are freely composed English, not translated 1:1.**
|
|
The source spec (§7) gives *example* output ("You use Shield Bash.", "Road Bandit takes 12 damage.") in a phrasing that doesn't match the current German templates 1:1 (current German is `${attacker} trifft ${defender} für X Schaden` — third-person "attacker hits defender"; the spec's example is second-person "you use X" / "X takes N damage"). Ruling: write new English templates matching the *voice* the source spec's examples establish (second-person for the player's own actions, third-person "takes N damage" for outcomes), not a literal translation of the German construction. Exact wording is in the glossary below.
|
|
**Cost if wrong:** copy-only change, no logic; cheap to revise.
|
|
|
|
**R6 — Terminology not covered by the source spec's tables gets translated here, following existing enum-name conventions where one exists.**
|
|
Several label maps (`SLOT_LABELS`, `RARITY_LABELS`, `LOCATION_TYPE_LABELS`) mirror an existing English enum 1:1 (`EquipmentSlot`, `ItemRarity`, `LocationType`) — for those, the English label is simply the enum value humanized (e.g. `HUNTING_GROUND` → "Hunting Ground"), which is both the lowest-risk choice and keeps the display string legible from the code without a lookup. Where no enum exists (item/location descriptions, POI narrative content, the reputation faction), original English wording is drafted here, matching the tone of the German source. All of it is listed in the glossary below — implementers use these values verbatim, they do not invent their own.
|
|
**Cost if wrong:** copy-only; cheap to revise per string.
|
|
|
|
**R7 — "Renown" is the primary display term; "World Renown" is used once, where the source spec's Common-UI table requires the literal phrase.**
|
|
Slice 0.6.5 used "Ansehen" for Renown in German specifically to avoid colliding with "Ruf" (Reputation) — a German-only disambiguation problem that disappears once both are English. The source spec's Common UI list (§5) includes the exact term "World Renown". Ruling: the Topbar (the most prominent, always-visible renown display) uses "World Renown {{n}}", matching the spec's literal term where the spec asks for it. The location panel's recommended-renown line uses the shorter "Recommended Renown" (parallel to "Empfohlenes Ansehen"'s prior German phrasing, and avoiding "World Renown" twice on one screen). Reputation continues to read "Reputation"/"Rep." in context, never "Renown" — the two systems must stay visually distinct in English exactly as they had to in German (0.6.5 ruling R17's spirit carries over, just without the German-specific term collision).
|
|
**Cost if wrong:** copy-only; both terms already exist in the codebase's vocabulary (the field is literally named `renown`), so a later rename is a find-and-replace, not a redesign.
|
|
|
|
**R8 — The gate-notice POI's over-promise is translated as-is, not fixed.**
|
|
The South Gate's "Aushangtafel" POI text promises "Silber für jeden erlegten Räuber" (silver for every bandit killed) even though slice 0.6.5 zeroed direct monster silver drops — flagged as a known, deliberate non-fix in that slice's final review (N3 in its ledger), on the grounds that it's in-world NPC flavor text and the turn-in mechanism makes the promise *eventually* true. This slice is a language conversion, not a content-accuracy pass; the English translation preserves the same (already-flagged, already-accepted) inaccuracy rather than silently also fixing it. If the user wants it fixed, that is a separate, explicit content decision — not bundled into a translation task.
|
|
**Cost if wrong:** none; this explicitly defers to a standing decision already made and accepted.
|
|
|
|
## 4. Glossary — exact values, used verbatim
|
|
|
|
### Locations (source spec §5, verbatim)
|
|
| Key (unchanged) | German (current) | English |
|
|
|---|---|---|
|
|
| `graufurt` | Graufurt | Graufurt (proper name, unchanged) |
|
|
| `south-gate` | Südtor von Graufurt | Graufurt South Gate |
|
|
| `burned-road` | Verbrannte Straße | Burned Road |
|
|
| — | Verlassener Wachtposten | Abandoned Watchpost (not yet seeded) |
|
|
| — | Aschengrube | Ash Pit (not yet seeded) |
|
|
| `ashen-fields` (regionKey) | Aschenfelder | Ashen Fields |
|
|
|
|
Location `description` (from `vertical-slice.seed.ts`):
|
|
- `south-gate`: "Am schwarzen Südtor endet der Schutz Graufurts. Hinter den Wachtfeuern beginnt die stille Weite der Aschenfelder." → **"At the black South Gate, Graufurt's protection ends. Beyond the watch-fires begins the silent expanse of the Ashen Fields."**
|
|
- `burned-road`: "Die alte Handelsstraße führt durch verkohlte Felder. Zwischen Asche und zerbrochenen Wagen warten die ersten Gefahren." → **"The old trade road cuts through charred fields. Among the ash and broken wagons, the first dangers wait."**
|
|
|
|
`regionTierLabel`: "Gebiet 1" → **"Tier 1"** (both locations; matches the source spec's own "Tier-1 items" vocabulary in §6).
|
|
|
|
### Monsters (source spec §5, verbatim; keys unchanged per R1)
|
|
| Key (unchanged) | German (current `name`) | English `name` |
|
|
|---|---|---|
|
|
| `ash-rat` | Aschenratte | Ash Rat |
|
|
| `wild-road-dog` | Verwilderter Straßenhund | Feral Road Hound |
|
|
| `road-bandit` | Straßenräuber | Road Bandit |
|
|
| `charred-looter` | Verkohlter Plünderer | Charred Raider |
|
|
|
|
Loot table `name` (internal, not currently player-visible, translated anyway for consistency):
|
|
- `ash-rat-loot`: "Aschenratte Beute" → **"Ash Rat Loot"**
|
|
- `road-bandit-loot`: "Straßenräuber Beute" → **"Road Bandit Loot"**
|
|
|
|
### Items (source spec §6 gives names for the 10 listed; descriptions and the 3 unlisted names are new here)
|
|
| Key (unchanged) | English `name` | English `description` |
|
|
|---|---|---|
|
|
| `worn-short-sword` | Worn Shortsword | A recruit's blade, sharpened more often than it's been swung. |
|
|
| `bandit-blade` | Bandit Blade | A roughly serrated blade, forged for quick raids. |
|
|
| `ash-blade` | Ashen Blade | Tempered in the embers of the Ashen Fields; the edge still glows faintly. |
|
|
| `bandit-hood` | Bandit Hood | Scarred leather that hides both the wearer's face and their intent. |
|
|
| `reinforced-leather-jacket` | Reinforced Leather Jacket | Leather sewn with iron plates, heavy and dependable. |
|
|
| `raider-gloves` | Plunderer Gloves | Studded gloves, worn smooth from handling other people's belongings. |
|
|
| `guardsman-legs` | Watchman's Leggings | Leg armor of the Border Watch, patched at the knees. |
|
|
| `ash-boots` | Ashen Boots | Boots that walked through smoldering fields and never quite left them behind. |
|
|
| `borderwatch-sigil` | Mark of the Border Watch | The crest of a watchtower that no longer stands. |
|
|
| `burned-captain-pendant` | Charred Captain's Pendant | A skull cast in slag, its embers never quite gone out. |
|
|
| `small-healing-potion` | Small Healing Potion | A bitter draught that makes wounds forgettable, if only for a breath. |
|
|
| `ash-pelt` | Ash Pelt | Singed pelt, tough as leather and grey with drifting ash. |
|
|
| `bandit-insignia` | Bandit Insignia | A roughly stamped token marking a road bandit as one of their band. |
|
|
|
|
### Reputation (`border-guard` faction, R3, R8)
|
|
- Faction `name`: "Grenzwacht" → **"Border Watch"** (matches `borderwatch-sigil`'s English item name above — same in-world institution).
|
|
- Faction `description`: "Die letzten organisierten Wächter der Aschenfelder, die verbliebene Handelsrouten und Außenposten schützen." → **"The last organized watch of the Ashen Fields, guarding what remains of the trade routes and outposts."**
|
|
- Rank labels (API-computed, `reputation-rank.ts`, R3) — English label is the enum key humanized:
|
|
|
|
| Key | German (current) | English |
|
|
|---|---|---|
|
|
| `ESTEEMED` | Geachtet | Esteemed |
|
|
| `TRUSTED` | Vertraut | Trusted |
|
|
| `RECOGNIZED` | Anerkannt | Recognized |
|
|
| `KNOWN` | Bekannt | Known |
|
|
| `TOLERATED` | Geduldet | Tolerated |
|
|
| `STRANGER` | Fremder | Stranger |
|
|
|
|
### Local-location content (`local-location.content.ts`)
|
|
|
|
**Burned Road:**
|
|
- `localDescription`: "Ein alter Handelsweg, der durch Feuer und Krieg in Asche gelegt wurde. Verbrannte Karren, zerbrochene Waffen und verstummte Schreie säumen den Pfad in die Aschenfelder." → **"An old trade road, burned to ash by fire and war. Charred carts, broken weapons, and silenced cries line the path into the Ashen Fields."**
|
|
- POI `hunt-area`: title "Jagdgebiet" → **"Hunting Ground"**; actionLabel "Jagd beginnen" → **"Begin Hunt"**.
|
|
- POI `inspect-tracks`: title/resultTitle "Verdächtige Spuren" → **"Suspicious Tracks"**; actionLabel "Untersuchen" → **"Investigate"**; resultText "Zwischen Asche und zerbrochenen Steinen erkennst du mehrere frische Stiefelabdrücke. Sie führen nach Osten, in Richtung des verlassenen Wachtpostens." → **"Among the ash and broken stones you make out several fresh bootprints. They lead east, toward the abandoned watchpost."**
|
|
- POI `search-abandoned-wagon`: title/resultTitle "Verlassener Wagen" → **"Abandoned Wagon"**; actionLabel "Durchsuchen" → **"Search"**; resultText "Der Wagen wurde gründlich geplündert. Zwischen verbrannten Brettern findest du nur leere Kisten und Spuren eines hastigen Aufbruchs." → **"The wagon has been thoroughly looted. Among the charred planks you find only empty crates and signs of a hasty departure."**
|
|
- POI `wounded-scout`: title/resultTitle "Verwundeter Kundschafter" → **"Wounded Scout"**; actionLabel "Sprechen" → **"Talk"**; resultText „Die Straße ist nicht mehr sicher. Die Plünderer kommen aus Richtung des alten Wachtpostens. Wenn du weitergehst, halte die Augen offen." → **"The road isn't safe anymore. The raiders are coming from the direction of the old watchpost. If you keep going, keep your eyes open."** (keep the quotation marks around the line; it's spoken dialogue)
|
|
- `localPrimaryActions`: `start-hunt` label "Jagd beginnen" → **"Begin Hunt"**, description "Im Gebiet jagen" → **"Hunt in this area"**. `investigate-tracks` label "Spuren untersuchen" → **"Investigate tracks"**, description "Hinweise finden" → **"Find clues"**. `search-surroundings` label "Umgebung durchsuchen" → **"Search surroundings"**, description "Beute finden" → **"Find loot"**. `open-map` label "Zur Karte" → **"To Map"**, description "Gebiet wechseln" → **"Change area"**.
|
|
- `localRewardPreview`: "Ausrüstung" → **"Equipment"**; "Material" → **"Material"** (already English; no change).
|
|
|
|
**South Gate:**
|
|
- `localDescription`: "Am schwarzen Südtor endet der Schutz Graufurts. Hinter den Wachtfeuern beginnt die stille Weite der Aschenfelder." → **same English as the `LocationDefinition.description` above** ("At the black South Gate, Graufurt's protection ends. Beyond the watch-fires begins the silent expanse of the Ashen Fields.") — the German source repeats the sentence verbatim in both fields; the English does too, for consistency.
|
|
- POI `gate-notice`: title/resultTitle "Aushangtafel" → **"Notice Board"**; actionLabel "Lesen" → **"Read"**; resultText "Verwitterte Anschläge flattern im Wind. Ein frischer Zettel warnt vor Plünderern auf der Verbrannten Straße und verspricht Silber für jeden erlegten Räuber." → **"Weathered notices flutter in the wind. A fresh one warns of raiders on the Burned Road and promises silver for every bandit killed."** (R8: translated as-is, over-promise preserved deliberately)
|
|
- POI `gate-watch`: title/resultTitle "Torwache" → **"Gate Watch"**; actionLabel "Sprechen" → **"Talk"**; resultText „Hinter dem Tor endet Graufurts Schutz. Wer nach Süden geht, geht auf eigene Gefahr — und kommt selten so zurück, wie er gegangen ist." → **"Beyond the gate, Graufurt's protection ends. Whoever heads south does so at their own risk — and rarely comes back the way they left."**
|
|
- POI `south-road`: title "Straße nach Süden" → **"Road South"**; actionLabel "Zur Karte" → **"To Map"**.
|
|
- `localPrimaryActions`: `talk-to-watch` label "Wache ansprechen" → **"Talk to the watch"**, description "Lage erfragen" → **"Ask about the situation"**. `read-notice` label "Aushang lesen" → **"Read the notice"**, description "Hinweise finden" → **"Find clues"**. `open-map` label "Zur Karte" → **"To Map"**, description "Gebiet wechseln" → **"Change area"**.
|
|
|
|
### Combat actions & log (source spec §5 core actions, verbatim; §7 style, R5)
|
|
- Buttons: Angriff → **Attack**; Schwerer Hieb → **Heavy Strike**; Schildstoß → **Shield Bash**; Verteidigen → **Defend**; Trank → **Potion**; (Flee is listed in the spec but not yet implemented in this codebase — no change needed, nothing to translate).
|
|
- Round header: "Runde {{n}}" → **"Round {{n}}"**.
|
|
- Outcome headings: "Sieg" → **"Victory"**; "Niederlage" → **"Defeat"**.
|
|
- Rewards panel: "Belohnungen" → **"Rewards"**; "Beute" → **"Loot"**; "Keine besondere Beute gefunden." → **"No notable loot found."**; "Silber" → **"Silver"**.
|
|
- Post-combat buttons: "Weiter jagen" → **"Keep Hunting"**; "Zum Ort" → **"To Location"**; "Inventar öffnen" → **"Open Inventory"**.
|
|
- Log panel: "Kampfprotokoll"/"Kampflog" → **"Combat Log"**.
|
|
- Loading/error: "Kampf wird geladen…" → **"Loading combat…"**; "Erneut versuchen" → **"Retry"**.
|
|
- Default participant names (fallbacks when a name is unavailable): 'Du' → **'You'**; 'Der Gegner' → **'The enemy'**.
|
|
- Combat log templates (`formatEvent`, per event type) and the standalone intent banner (`monsterIntentLabel`):
|
|
|
|
| Event | New English template |
|
|
|---|---|
|
|
| DAMAGE | `${attacker} hits ${defender} for ${amount} damage.` |
|
|
| HEAL | `${playerName} drinks a potion and heals ${amount} HP.` |
|
|
| DEFEND | `${playerName} braces to defend.` |
|
|
| TELEGRAPH / intent banner | `${monsterName} is winding up a Heavy Strike.` |
|
|
| INTERRUPT | `${playerName} interrupts ${monsterName}'s attack.` |
|
|
| COMBAT_WON | `${monsterName} has been defeated.` |
|
|
| default (COMBAT_LOST) | `${playerName} has been defeated.` |
|
|
|
|
### Navigation (source spec §5, verbatim) & layout
|
|
- Karte → **Map**; Jagd → **Hunt**; Quests → **Quests** (unchanged, already English); Inventar → **Inventory**; Charakter → **Character**; Shop → **Shop** (not yet built; no change needed). The sidebar's "Ort" item (not in the spec's table) → **Location**.
|
|
- Topbar: "Ansehen {{n}}" → **"World Renown {{n}}"** (R7); "Silber" → **Silver**; "Charakterdaten werden geladen" → **"Loading character data"**.
|
|
- Footer: "Verbindung bereit" → **"Connection ready"**; "Aschenfelder" → **Ashen Fields**.
|
|
|
|
### Danger labels (source spec §5, verbatim)
|
|
| Key | German (current) | English |
|
|
|---|---|---|
|
|
| `WEAK` | Schwach | Weak |
|
|
| `MATCH` | Passend | Suitable |
|
|
| `STRONG` | Stark | Strong |
|
|
| `VERY_DANGEROUS` | Sehr gefährlich | Very Dangerous |
|
|
| `DEADLY` | Tödlich | Deadly |
|
|
|
|
Separate binary travel-danger label (`travel-panel.component.ts`, not the 5-tier scale above): Niedrig/Hoch → **Low/High**.
|
|
|
|
### Equipment slots, rarity, location type (R6 — enum names humanized)
|
|
| `SLOT_LABELS` key | German | English |
|
|
|---|---|---|
|
|
| WEAPON | Waffe | Weapon |
|
|
| HEAD | Kopf | Head |
|
|
| CHEST | Brust | Chest |
|
|
| HANDS | Handschuhe | Hands |
|
|
| LEGS | Beine | Legs |
|
|
| FEET | Stiefel | Feet |
|
|
| AMULET | Amulett | Amulet |
|
|
|
|
| `RARITY_LABELS` key | German | English |
|
|
|---|---|---|
|
|
| COMMON | Gewöhnlich | Common |
|
|
| RARE | Selten | Rare |
|
|
| EPIC | Episch | Epic |
|
|
|
|
| `LOCATION_TYPE_LABELS` key | German | English |
|
|
|---|---|---|
|
|
| SAFE_HUB | Zuflucht | Safe Haven |
|
|
| TRANSITION | Übergang | Transition |
|
|
| HUNTING_GROUND | Jagdgebiet | Hunting Ground |
|
|
| QUEST_LOCATION | Questort | Quest Location |
|
|
| OUTPOST | Außenposten | Outpost |
|
|
| ELITE_ZONE | Elitegebiet | Elite Zone |
|
|
| BOSS_LOCATION | Bossort | Boss Location |
|
|
| DUNGEON_ENTRANCE | Verlieseingang | Dungeon Entrance |
|
|
|
|
`typeLabel` fallback (inventory detail panel, hardcoded 'Gegenstand'): → **'Item'**.
|
|
|
|
### Error-message translation maps (R2 — frontend only; `code` unchanged in all cases)
|
|
| Store | `code` | German (current) | English |
|
|
|---|---|---|---|
|
|
| `world.store.ts` | (generic) | Weltzustand konnte nicht geladen werden. | Could not load world state. |
|
|
| | `TRAVEL_ALREADY_ACTIVE` | Du befindest dich bereits auf Reisen. | You're already travelling. |
|
|
| | `INVALID_TRAVEL_TARGET` | Dieses Ziel ist von hier aus nicht erreichbar. | This destination isn't reachable from here. |
|
|
| | `CHARACTER_NOT_FOUND` | Dein Charakter konnte nicht gefunden werden. | Your character could not be found. |
|
|
| | `TRAVEL_STATE_INVALID` | Der Reisezustand ist ungültig. Bitte lade die Seite neu. | Travel state is invalid. Please reload the page. |
|
|
| `hunting.store.ts` | (generic) | (same as above) | Could not load world state. |
|
|
| | `CHARACTER_NOT_FOUND` | (same as above) | Your character could not be found. |
|
|
| | `CHARACTER_TRAVELLING` | Du kannst nicht jagen, während du unterwegs bist. | You can't hunt while travelling. |
|
|
| | `HUNTING_NOT_AVAILABLE` | An diesem Ort gibt es keine Jagdgebiete. | There's no hunting ground at this location. |
|
|
| | `NO_HUNT_ENCOUNTERS_AVAILABLE` | Aktuell sind hier keine Gegner zu finden. | No enemies can currently be found here. |
|
|
| `combat.store.ts` | (generic) | Der Kampf konnte nicht geladen werden. | Could not load the combat. |
|
|
| | `HUNT_ENCOUNTER_NOT_FOUND` | Diese Begegnung wurde nicht gefunden. | This encounter could not be found. |
|
|
| | `HUNT_ENCOUNTER_ALREADY_CONSUMED` | Diese Begegnung wurde bereits genutzt. | This encounter has already been used. |
|
|
| | `INVALID_HUNT_ENCOUNTER` | Diese Begegnung ist nicht mehr gültig. | This encounter is no longer valid. |
|
|
| | `CHARACTER_TRAVELLING` | Du kannst nicht kämpfen, während du unterwegs bist. | You can't fight while travelling. |
|
|
| | `CHARACTER_TOO_WOUNDED` | Du bist zu schwer verwundet, um zu kämpfen. Warte, bis du dich erholt hast. | You're too badly wounded to fight. Wait until you've recovered. |
|
|
| | `COMBAT_ALREADY_ACTIVE` | Du befindest dich bereits in einem Kampf. | You're already in a combat. |
|
|
| | `COMBAT_NOT_FOUND` | Dieser Kampf wurde nicht gefunden. | This combat could not be found. |
|
|
| | `COMBAT_ALREADY_FINISHED` | Dieser Kampf ist bereits beendet. | This combat has already ended. |
|
|
| | `COMBAT_NO_POTIONS_REMAINING` | Du hast keine Tränke mehr. | You have no potions left. |
|
|
| `inventory.store.ts` | (generic) | Inventar konnte nicht geladen werden. | Could not load the inventory. |
|
|
| | `CHARACTER_ITEM_NOT_FOUND` | Dieser Gegenstand konnte nicht gefunden werden. | This item could not be found. |
|
|
| | `ITEM_NOT_OWNED` | Dieser Gegenstand gehört dir nicht. | This item doesn't belong to you. |
|
|
| | `ITEM_NOT_EQUIPPABLE` | Dieser Gegenstand kann nicht ausgerüstet werden. | This item can't be equipped. |
|
|
| | `INVALID_EQUIPMENT_SLOT` | Dieser Ausrüstungsplatz ist ungültig. | This equipment slot is invalid. |
|
|
| | `CHARACTER_IN_COMBAT` | Ausrüstung kann während eines Kampfes nicht geändert werden. | Equipment can't be changed during combat. |
|
|
| `local-location.store.ts` | (generic) | Diese Handlung ist gerade nicht möglich. | This action isn't possible right now. |
|
|
| | `LOCATION_INTERACTION_UNAVAILABLE` | Hier gibt es dazu nichts zu entdecken. | There's nothing to discover here. |
|
|
| | `CHARACTER_NOT_FOUND` | Dein Charakter konnte nicht gefunden werden. | Your character could not be found. |
|
|
|
|
Every row above was read from the actual source files and is pinned exactly; there are no remaining `(verify)` cells.
|
|
|
|
### Remaining component copy (not itemized above — implementer translates in-file, same voice, using the terms already fixed elsewhere in this glossary for anything that overlaps — e.g. "Jagd" is always "Hunt", "Ort" is always "Location")
|
|
Hunt screen, inventory screen, world/travel screens, and their `aria-label`s follow the same direct, plain register as the rest of the UI. No new terms are introduced beyond what's already fixed above; where a screen's copy uses a word already translated elsewhere in this glossary (Jagd, Ort, Silber, Ausrüstung, etc.), use that exact translation for consistency.
|
|
|
|
## 5. Architecture / file scope
|
|
|
|
No new files, no new abstractions. Every change is either a seed-file literal, a component template/TS string, or a small constant map. Grouped by SDD task (task boundaries chosen so each is same-shape, same-layer work — batched per subagent-driven-development's guidance rather than one task per file):
|
|
|
|
1. **API seed content** — `item-content.ts`, `vertical-slice.seed.ts`, `local-location.content.ts`, `reputation-content.ts` and their specs.
|
|
2. **API reputation rank labels** — `reputation-rank.ts` and its spec.
|
|
3. **Web static label maps** — `inventory.labels.ts` (SLOT_LABELS), `item-card.component.ts` (RARITY_LABELS), `danger-badge.component.ts` (DANGER_LABELS), `location-sidebar.component.ts` (LOCATION_TYPE_LABELS), and their specs.
|
|
4. **Web error-message maps** — the five store files in R2's table, and their specs.
|
|
5. **Combat feature** — `combat-page.component.ts` (log templates, intent label) + `.html` (static labels) + `.spec.ts`.
|
|
6. **Inventory feature** — `inventory-page.component.html`, `inventory-detail-panel.component.html/.ts` (typeLabel), and specs.
|
|
7. **World/travel/location feature** — `location-page`, `location-sidebar`, `travel-panel`, `location-interaction-panel`, `location-node`, `world-page` (templates + `travel-panel.component.ts`'s binary danger label) and specs.
|
|
8. **Hunting feature** — `hunt-page.component.html`, `encounter-card.component.html/.ts`, and specs.
|
|
9. **Layout/shared** — `top-bar`, `side-navigation`, `context-panel`, `game-footer`, `reputation-display` static text, and specs.
|
|
10. **Fixture and final sweep** — `current-location.fixture.ts` (the one fixture shared across multiple spec files — updated last, after every task that reads from it has landed, to avoid conflicting edits), plus a full-suite grep for any surviving German string (umlaut/ß scan across `apps/`) as the closing gate.
|
|
|
|
## 6. Testing strategy
|
|
|
|
No new test infrastructure. Every task updates its own component/service specs and fixtures to expect English, in the same commit as the production change (TDD: update the assertion, watch it fail against the still-German code, change the code, watch it pass). The final task (10) runs a repo-wide grep for German-only characters (ä/ö/ü/ß) and remaining known German words as a closing gate — not a substitute for the per-task updates, matching source spec §11's own caveat that a grep "is not a substitute for actual runtime verification."
|
|
|
|
## 7. Self-review
|
|
|
|
- **Placeholder scan:** no TBD/TODO in this document; every glossary row has a concrete value except the explicitly-marked `(verify)` cells, which are a deliberate instruction (read the real string, translate in-voice) rather than an unfilled placeholder.
|
|
- **Internal consistency:** "Hunt" is used consistently for Jagd across navigation, buttons, and location type; "Location" is used consistently for Ort; "Silver" for Silber; "Equipment" for Ausrüstung. Checked for collisions — none found.
|
|
- **Scope check:** matches the source spec's §3 scope list exactly (navigation, locations, monsters, items, combat actions, combat log, hunt/travel/loot screens, inventory, character screen, system messages, danger labels, player-visible errors); nothing extra proposed (no localization framework, per source spec §13).
|
|
- **Ambiguity check:** every place the source spec left a term undecided (item descriptions, location descriptions, POI content, faction content, rank labels, log template wording, the Renown/World Renown split) has an explicit ruling above.
|