# English Game Content Foundation Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Convert every player-facing German string in the currently playable loop (Graufurt South Gate, Burned Road, hunt/combat/inventory/travel screens, navigation, error messages) to English. **Architecture:** No new abstractions. Every task is a same-shape content or copy edit: a seed-file literal, a component template/TS string, or a small display-label constant map. No localization framework is introduced. **Tech Stack:** NestJS + TypeORM (API), Angular 20 signals (web), Jest (API tests), Vitest via `ng test` (web tests). **Spec:** `docs/superpowers/specs/2026-08-21-english-game-content-foundation-design.md` — this plan argues from that spec. It contains a complete glossary of exact English values (§4) that every task below uses verbatim. **Read the glossary section named in each task before touching any file** — do not invent your own translation for a string that already has one there. ## Global Constraints - All player-facing game content becomes English. Development docs stay German unless rendered in-game. - Technical `key` slugs and error `code` values never change in this slice (design doc R1, R2). Only display strings (`name`, `label`, `description`, `message`, template text) change. - No localization framework, no language switch, no translation table. Centralized constants/text maps are fine. - No database migration in this slice (design doc R4) — every change is a seed-file literal or a frontend constant/template string. Do NOT write a migration. Do NOT run `typeorm migration:run` or connect to any Postgres instance — no database's ownership is confirmed for this session. - `apps/api/tsconfig.json` sets `isolatedModules: true`, so `npm run test --workspace=@ashen-realms/api` does NOT type-check. `npm run build --workspace=@ashen-realms/api` does, but `tsconfig.build.json` excludes spec files, so it never type-checks tests either. Pair every full-suite step with the build command and treat a spec holding a stale field as invisible to both. - Angular's `ng test` compiles the whole application bundle before running any spec. A component with a leftover reference anywhere in the bundle fails every test, not just its own — this plan sequences tasks so each is independently compilable and testable (unlike Slice 0.6.5, this plan does not have a designed red-build window: every task should leave both `npm run build --workspace=@ashen-realms/api` and `npm run test --workspace=@ashen-realms/web` green, because nothing here removes a field a sibling task still reads). - Every translation value is pinned in the spec's glossary (§4). If a task step says "use the value in glossary section X," that is the exact string to write — not a prompt to compose your own English. - Monster `key`s, item `key`s, location `key`s, faction `key`s, and every error `code` are unchanged everywhere in this plan (design doc R1). If a step appears to ask you to rename one, stop and treat that as a plan defect rather than doing it. --- ### Task 1: API seed content — items, locations, local-location POI/action content, reputation faction **Files:** - Modify: `apps/api/src/database/seeds/item-content.ts` - Modify: `apps/api/src/database/seeds/vertical-slice.seed.ts` - Modify: `apps/api/src/database/seeds/local-location.content.ts` - Modify: `apps/api/src/database/seeds/reputation-content.ts` - Modify: `apps/api/src/database/seeds/vertical-slice.seed.spec.ts` **Interfaces:** - Consumes: nothing from other tasks in this plan. - Produces: the English `name`/`description`/`label`/`title`/`resultText` values every later web task's fixtures must match when they mirror seed content (Task 10 reconciles `current-location.fixture.ts` against whatever lands here). - [ ] **Step 1: Update `vertical-slice.seed.spec.ts` to expect English monster and item names** Read the spec's glossary "Monsters" and "Items" tables (design doc §4). In `vertical-slice.seed.spec.ts`, every `objectContaining({ key: 'ash-rat', name: 'Aschenratte', ... })`-style assertion (and the equivalent for `wild-road-dog`, `road-bandit`, `charred-looter`, and the `bandit-blade` item name at line ~423) changes only its `name` value to the glossary's English value. Keys are untouched. Example for one row: ```typescript // before expect.objectContaining({ key: 'ash-rat', name: 'Aschenratte', level: 1, ... }), // after expect.objectContaining({ key: 'ash-rat', name: 'Ash Rat', level: 1, ... }), ``` Apply the same pattern to the `wild-road-dog` → "Feral Road Hound", `road-bandit` → "Road Bandit", `charred-looter` → "Charred Raider" rows, and the `bandit-blade` → "Bandit Blade" item row. Also update any assertion on `LocationDefinition.description` for `south-gate`/`burned-road` to the glossary's English "Locations" descriptions. - [ ] **Step 2: Run the spec to see it fail** Run: `npm run test --workspace=@ashen-realms/api -- vertical-slice.seed` Expected: FAIL — the production seed still returns the German names. - [ ] **Step 3: Translate `item-content.ts`** For every entry in `ITEM_DEFINITIONS`, replace `name` and `description` with the glossary's "Items" table values (design doc §4), matching by `key`. Do not touch `key`, `rarity`, `equipmentSlot`, or any numeric field. Also update `LOOT_TABLES`' two `name` fields ("Aschenratte Beute" → "Ash Rat Loot", "Straßenräuber Beute" → "Road Bandit Loot") per the glossary's "Monsters" loot-table row. - [ ] **Step 4: Translate `vertical-slice.seed.ts`** In the `locations` array, update `south-gate` and `burned-road`'s `description` to the glossary's "Locations" English descriptions. In the monster-seeding block, update `name` for `ash-rat`, `wild-road-dog`, `road-bandit`, `charred-looter` to the glossary's "Monsters" English names. Update `regionTierLabel` wherever it's set inline (if any — most of it lives in `local-location.content.ts`, see Step 5) to "Tier 1". - [ ] **Step 5: Translate `local-location.content.ts`** Both `BURNED_ROAD_LOCAL_CONTENT` and `SOUTH_GATE_LOCAL_CONTENT` need every German string replaced per the glossary's "Local-location content" section (design doc §4): `regionName` ("Aschenfelder" → "Ashen Fields"), `regionTierLabel` ("Gebiet 1" → "Tier 1"), `localDescription`, every `localPointsOfInterest[].title`/`actionLabel`/`resultTitle`/`resultText`, every `localPrimaryActions[].label`/`description`, and `localRewardPreview[].label` ("Ausrüstung" → "Equipment"; "Material" is already English, leave it). Use the glossary values verbatim, including the `gate-notice` POI's over-promising text — it is translated as-is per design doc ruling R8, not corrected. Do not touch any `key`, `type`, `iconKey`, `xPercent`, `yPercent`, or `enabled` field. - [ ] **Step 6: Translate `reputation-content.ts`** Update the `border-guard` faction's `name` ("Grenzwacht" → "Border Watch") and `description` per the glossary's "Reputation" section. Do not touch `key`, `id`, `regionKey`, or `enabled`. - [ ] **Step 7: Run the spec to see it pass** Run: `npm run test --workspace=@ashen-realms/api -- vertical-slice.seed` Expected: PASS. - [ ] **Step 8: Run the full API suite and build** Run: `npm run test --workspace=@ashen-realms/api && npm run build --workspace=@ashen-realms/api` Expected: full suite green (no regressions in any other spec that happens to read seed content), build zero errors. If any other spec file breaks (grep the failure for German text), fix that assertion too using the same glossary — do not weaken it. - [ ] **Step 9: Commit** ```bash git add apps/api/src/database/seeds/ git commit -m "feat(content): translate seeded items, locations, and reputation faction to English" ``` --- ### Task 2: API reputation rank labels **Files:** - Modify: `apps/api/src/reputation/reputation-rank.ts` - Modify: `apps/api/src/reputation/reputation-rank.spec.ts` **Interfaces:** - Consumes: nothing. - Produces: `rankLabel` strings that `GET /api/reputation` and turn-in results now return in English — Task 9's reputation-display component (built in the prior slice, still unwired) already renders whatever `rankLabel` the server sends, so no web change is needed for the label text itself to become English. - [ ] **Step 1: Update the spec to expect English rank labels** Read the glossary's "Reputation" rank-label table (design doc §4). In `reputation-rank.spec.ts`, every assertion on a rank's `label` (e.g. `label: 'Geachtet'`) changes to the glossary's English value (`label: 'Esteemed'`), matched by the unchanged `key` (`ESTEEMED`, etc.). All six ranks: ESTEEMED/Esteemed, TRUSTED/Trusted, RECOGNIZED/Recognized, KNOWN/Known, TOLERATED/Tolerated, STRANGER/Stranger. - [ ] **Step 2: Run the spec to see it fail** Run: `npm run test --workspace=@ashen-realms/api -- reputation-rank` Expected: FAIL. - [ ] **Step 3: Translate `reputation-rank.ts`** In the `REPUTATION_RANKS` array, replace each `label` value with the glossary's English label, keyed by the unchanged `key`. Do not touch `key` or `threshold`. - [ ] **Step 4: Run the spec to see it pass** Run: `npm run test --workspace=@ashen-realms/api -- reputation-rank` Expected: PASS. - [ ] **Step 5: Run the full API suite and build** Run: `npm run test --workspace=@ashen-realms/api && npm run build --workspace=@ashen-realms/api` Expected: green, zero errors. Check `reputation.service.spec.ts` and `turn-in.service.spec.ts` for any assertion on a German rank label and fix it the same way if found. - [ ] **Step 6: Commit** ```bash git add apps/api/src/reputation/reputation-rank.ts apps/api/src/reputation/reputation-rank.spec.ts git commit -m "feat(reputation): translate rank labels to English" ``` --- ### Task 3: Web static label maps **Files:** - Modify: `apps/web/src/app/features/inventory/inventory.labels.ts` (SLOT_LABELS) - Modify: `apps/web/src/app/shared/item-card/item-card.component.ts` (RARITY_LABELS) - Modify: `apps/web/src/app/shared/danger-badge/danger-badge.component.ts` (DANGER_LABELS) - Modify: `apps/web/src/app/features/world/location-sidebar/location-sidebar.component.ts` (LOCATION_TYPE_LABELS) - Modify their respective `.spec.ts` files wherever a spec references one of these maps' output text - Modify: `apps/web/src/app/features/inventory/inventory-detail-panel.component.ts` (`typeLabel` hardcoded fallback) **Interfaces:** - Consumes: nothing. - Produces: the label strings that Tasks 6 (inventory), 7 (world/location), and 8 (hunting) component specs will assert on when they render an item, a slot, a danger badge, or a location type. This task lands first among the label-consuming tasks so their specs can assert the final English text directly rather than a transitional value. - [ ] **Step 1: Update each map's own spec (or the nearest consuming component spec if the map has none) to expect English** Read design doc §4's "Equipment slots, rarity, location type" table and the "Danger labels" table. For each of the four maps, find every spec assertion that checks its rendered output text (e.g. `item-card.component.spec.ts` asserting `'Selten'` renders for a RARE item; `danger-badge.component.spec.ts` asserting `'Stark'`) and change the expected string to the glossary's English value, keyed by the same enum value (`RARE`/`STRONG`/etc. unchanged). - [ ] **Step 2: Run each touched spec to see it fail** Run: `cd apps/web && npx ng test --watch=false --include='**/item-card.component.spec.ts' --include='**/danger-badge.component.spec.ts' --include='**/location-sidebar.component.spec.ts' --include='**/inventory-detail-panel.component.spec.ts'` Expected: FAIL (bundle compiles fine here since no field is removed, only string values change — but assertions fail against the still-German source). - [ ] **Step 3: Translate the four label maps** - `inventory.labels.ts`: `SLOT_LABELS` — replace all 7 values per the glossary table (WEAPON→"Weapon", HEAD→"Head", CHEST→"Chest", HANDS→"Hands", LEGS→"Legs", FEET→"Feet", AMULET→"Amulet"). - `item-card.component.ts`: `RARITY_LABELS` — COMMON→"Common", RARE→"Rare", EPIC→"Epic". - `danger-badge.component.ts`: `DANGER_LABELS` — WEAK→"Weak", MATCH→"Suitable", STRONG→"Strong", VERY_DANGEROUS→"Very Dangerous", DEADLY→"Deadly". - `location-sidebar.component.ts`: `LOCATION_TYPE_LABELS` — all 8 values per the glossary table (SAFE_HUB→"Safe Haven", TRANSITION→"Transition", HUNTING_GROUND→"Hunting Ground", QUEST_LOCATION→"Quest Location", OUTPOST→"Outpost", ELITE_ZONE→"Elite Zone", BOSS_LOCATION→"Boss Location", DUNGEON_ENTRANCE→"Dungeon Entrance"). - [ ] **Step 4: Translate the `typeLabel` fallback** In `inventory-detail-panel.component.ts`, the hardcoded `computed(() => 'Gegenstand')` becomes `computed(() => 'Item')`. Update `inventory-detail-panel.component.spec.ts` if it asserts on this value. - [ ] **Step 5: Run the touched specs to see them pass** Run: `cd apps/web && npx ng test --watch=false --include='**/item-card.component.spec.ts' --include='**/danger-badge.component.spec.ts' --include='**/location-sidebar.component.spec.ts' --include='**/inventory-detail-panel.component.spec.ts'` Expected: PASS. - [ ] **Step 6: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green — nothing else in the bundle references these four maps' German values now that the maps themselves changed; if another spec fails on a string sourced from one of these maps, fix that assertion with the same glossary value. - [ ] **Step 7: Commit** ```bash git add apps/web/src/app/features/inventory/inventory.labels.ts \ apps/web/src/app/shared/item-card/item-card.component.ts \ apps/web/src/app/shared/danger-badge/danger-badge.component.ts \ apps/web/src/app/features/world/location-sidebar/location-sidebar.component.ts \ apps/web/src/app/features/inventory/inventory-detail-panel.component.ts git add -u git commit -m "feat(web): translate slot, rarity, danger, and location-type labels to English" ``` --- ### Task 4: Web error-message translation maps **Files:** - Modify: `apps/web/src/app/features/world/world.store.ts` - Modify: `apps/web/src/app/features/hunting/hunting.store.ts` - Modify: `apps/web/src/app/features/combat/combat.store.ts` - Modify: `apps/web/src/app/features/inventory/inventory.store.ts` - Modify: `apps/web/src/app/features/world/local-location.store.ts` - Modify their five `.spec.ts` files **Interfaces:** - Consumes: nothing. - Produces: English error text that components display when a store surfaces an API error — no other task's tests should be reading these values directly (they're store-internal), but grep for German error text in component specs during verification in case a component test hard-codes an expected message. - [ ] **Step 1: Update each store's spec to expect English error messages** Read design doc §4's "Error-message translation maps" table — it is a complete, exact mapping of every `code` to its English message across all five files, including the generic (no-code) fallback for each store. In each `.spec.ts`, every assertion on the store's error signal text (e.g. `expect(store.error()).toBe('Du befindest dich bereits auf Reisen.')`) changes to the glossary's English value for that same `code`. - [ ] **Step 2: Run the five specs to see them fail** Run: `cd apps/web && npx ng test --watch=false --include='**/world.store.spec.ts' --include='**/hunting.store.spec.ts' --include='**/combat.store.spec.ts' --include='**/inventory.store.spec.ts' --include='**/local-location.store.spec.ts'` Expected: FAIL. - [ ] **Step 3: Translate each store's message map** For each of the five files, replace every value in its `*_ERROR_MESSAGES` map and its `GENERIC_ERROR_MESSAGE`/`GENERIC_INTERACTION_ERROR` constant with the glossary's English text for that exact `code`. Do not touch any `code` key — only the German string value. The glossary table gives every one of the ~24 entries across the five files verbatim; use them exactly. - [ ] **Step 4: Run the five specs to see them pass** Run: `cd apps/web && npx ng test --watch=false --include='**/world.store.spec.ts' --include='**/hunting.store.spec.ts' --include='**/combat.store.spec.ts' --include='**/inventory.store.spec.ts' --include='**/local-location.store.spec.ts'` Expected: PASS. - [ ] **Step 5: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green. If a component spec (not one of the five stores) asserts a specific German error string rendered on screen, fix that assertion with the matching glossary value — do not skip it. - [ ] **Step 6: Commit** ```bash git add apps/web/src/app/features/world/world.store.ts \ apps/web/src/app/features/hunting/hunting.store.ts \ apps/web/src/app/features/combat/combat.store.ts \ apps/web/src/app/features/inventory/inventory.store.ts \ apps/web/src/app/features/world/local-location.store.ts git add -u git commit -m "feat(web): translate error messages surfaced from API error codes to English" ``` --- ### Task 5: Combat feature — log templates, intent banner, static labels **Files:** - Modify: `apps/web/src/app/features/combat/combat-page/combat-page.component.ts` - Modify: `apps/web/src/app/features/combat/combat-page/combat-page.component.html` - Modify: `apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts` **Interfaces:** - Consumes: nothing from other tasks. - Produces: nothing other tasks read. - [ ] **Step 1: Update the spec to expect English combat log lines and static labels** Read design doc §4's "Combat actions & log" section — it gives the exact new template for every `CombatEvent` type and every static button/heading string. In `combat-page.component.spec.ts`, every assertion on rendered combat-log text, action button labels, outcome headings ("Sieg"/"Niederlage"), reward-panel text, and default participant-name fallbacks ('Du'/'Der Gegner') changes to the glossary's English equivalent. - [ ] **Step 2: Run the spec to see it fail** Run: `cd apps/web && npx ng test --watch=false --include='**/combat-page.component.spec.ts'` Expected: FAIL. - [ ] **Step 3: Translate `formatEvent()` and `monsterIntentLabel()` in the `.ts` file** Replace each template string per the glossary's "Combat actions & log" table: ```typescript // DAMAGE `${attacker} hits ${defender} for ${event.amount} damage.` // HEAL `${playerName} drinks a potion and heals ${event.amount} HP.` // DEFEND `${playerName} braces to defend.` // TELEGRAPH (and monsterIntentLabel, same wording) `${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.` ``` Also change the default fallback names: `'Du'` → `'You'`, `'Der Gegner'` → `'The enemy'`. - [ ] **Step 4: Translate the static template in `combat-page.component.html`** Per the glossary: round header "Runde {{ combat.round }}" → "Round {{ combat.round }}"; action buttons "Angriff"/"Schwerer Hieb"/"Schildstoß"/"Verteidigen"/"Trank" → "Attack"/"Heavy Strike"/"Shield Bash"/"Defend"/"Potion"; outcome headings "Sieg"/"Niederlage" → "Victory"/"Defeat"; "Belohnungen" → "Rewards"; "Beute" → "Loot"; "Keine besondere Beute gefunden." → "No notable loot found."; "Silber" → "Silver"; post-combat buttons "Weiter jagen"/"Zum Ort"/"Inventar öffnen" → "Keep Hunting"/"To Location"/"Open Inventory"; log panel heading "Kampfprotokoll"/"Kampflog" → "Combat Log"; "Kampf wird geladen…" → "Loading combat…"; "Erneut versuchen" → "Retry"; `aria-label="Kampf"` → `aria-label="Combat"`; `aria-label="Belohnungen"` → `aria-label="Rewards"`; the `aria-label` built from a name (`"Lebenspunkte " + name`) keeps the same construction with "Health points " as the English prefix. - [ ] **Step 5: Run the spec to see it pass** Run: `cd apps/web && npx ng test --watch=false --include='**/combat-page.component.spec.ts'` Expected: PASS. - [ ] **Step 6: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green. - [ ] **Step 7: Commit** ```bash git add apps/web/src/app/features/combat/combat-page/ git commit -m "feat(web): translate combat log, action buttons, and victory/defeat screen to English" ``` --- ### Task 6: Inventory feature templates **Files:** - Modify: `apps/web/src/app/features/inventory/inventory-page.component.html` - Modify: `apps/web/src/app/features/inventory/inventory-detail-panel.component.html` - Modify: `apps/web/src/app/features/inventory/inventory-page.component.spec.ts` - Modify: `apps/web/src/app/features/inventory/inventory-detail-panel.component.spec.ts` **Interfaces:** - Consumes: `SLOT_LABELS` and `RARITY_LABELS` from Task 3 (already English by the time this task runs) — no code change needed here for those, just confirm the specs read the English values Task 3 already produced. - Produces: nothing other tasks read. - [ ] **Step 1: Update both specs to expect English inventory copy** In `inventory-page.component.spec.ts` and `inventory-detail-panel.component.spec.ts`, change every assertion on the following strings to their glossary equivalents: "Inventar" → "Inventory"; "Ausrüstung" → "Equipment"; "Ausgerüsteter Charakter" (aria) → "Equipped Character"; "Werte" → "Stats"; stat labels "Leben"/"Angriff"/"Waffenschaden"/"Rüstung" → "Health"/"Attack"/"Weapon Damage"/"Armor"; "Gegenstände" → "Items"; "Beutel" → "Bag"; "Ausgerüstet" (visually-hidden) → "Equipped"; the bag-count sentence `"{{ bagUsed() }} / {{ bagCapacity() }} Plätze belegt"` → `"{{ bagUsed() }} / {{ bagCapacity() }} slots used"`; "Noch keine Gegenstände gefunden." → "No items found yet."; "Erneut versuchen" → "Retry"; "Leer" (empty slot) → "Empty"; "Gewählter Gegenstand" (heading + aria) → "Selected Item"; "Nicht ausrüstbar" → "Not equippable"; "Ausrüsten" (button) → "Equip"; "Wähle einen Gegenstand aus deinem Inventar." → "Select an item from your inventory." - [ ] **Step 2: Run both specs to see them fail** Run: `cd apps/web && npx ng test --watch=false --include='**/inventory-page.component.spec.ts' --include='**/inventory-detail-panel.component.spec.ts'` Expected: FAIL. - [ ] **Step 3: Translate `inventory-page.component.html`** Apply every string from Step 1 that lives in this file. - [ ] **Step 4: Translate `inventory-detail-panel.component.html`** Apply every string from Step 1 that lives in this file. Note the `typeLabel`/`SLOT_LABELS`/`RARITY_LABELS` values themselves are already English from Task 3 — this file only needs its own literal template text changed, not those bindings. - [ ] **Step 5: Run both specs to see them pass** Run: `cd apps/web && npx ng test --watch=false --include='**/inventory-page.component.spec.ts' --include='**/inventory-detail-panel.component.spec.ts'` Expected: PASS. - [ ] **Step 6: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green. - [ ] **Step 7: Commit** ```bash git add apps/web/src/app/features/inventory/inventory-page.component.html \ apps/web/src/app/features/inventory/inventory-detail-panel.component.html git add -u git commit -m "feat(web): translate inventory and item-detail screen copy to English" ``` --- ### Task 7: World/travel/location feature templates **Files:** - Modify: `apps/web/src/app/features/world/location-page/location-page.component.html` - Modify: `apps/web/src/app/features/world/location-sidebar/location-sidebar.component.html` - Modify: `apps/web/src/app/features/world/travel-panel.component.html` - Modify: `apps/web/src/app/features/world/travel-panel.component.ts` (binary danger label) - Modify: `apps/web/src/app/features/world/location-interaction-panel/location-interaction-panel.component.html` - Modify: `apps/web/src/app/features/world/location-node.component.html` - Modify: `apps/web/src/app/features/world/world-page.component.html` - Modify their `.spec.ts` files **Interfaces:** - Consumes: `LOCATION_TYPE_LABELS` and `DANGER_LABELS` from Task 3 (already English) and the location `name`/`description`/POI content from Task 1 (already English) — this task changes each component's own static template text only. - Produces: nothing other tasks read. - [ ] **Step 1: Update every touched spec to expect English** Per design doc §4: "Empfohlenes Ansehen" → "Recommended Renown" (design doc R7 — note this is deliberately NOT "World Renown" here, only the Topbar in Task 9 uses the full "World Renown" phrase); "Gebiet"/"Ort"/"Ortstyp" → "Region"/"Location"/"Location Type"; "Gefahr"/"Sicher" → "Danger"/"Safe"; "Mögliche Begegnungen" → "Possible Encounters"; "Vorschau — die Jagd würfelt eigenständig." → "Preview — the hunt rolls independently."; "Verfügbare Interaktionen" → "Available Interactions"; "Mögliche Belohnungen" → "Possible Rewards"; "REISE LÄUFT"/"REISEN" → "TRAVELING"/"TRAVEL"; "Reiseziel: {{...}}" → "Destination: {{...}}"; "Ankunft in"/"Ziel"/"Reisezeit"/"Gefahr" → "Arrival in"/"Destination"/"Travel Time"/"Danger"; "Reise läuft" (disabled button) → "Travel in progress"; `"Zur {{...}} reisen?"` → `"Travel to {{...}}?"`; "Reise beginnen" → "Begin Travel"; "Wähle einen erreichbaren Ort auf der Karte." → "Select a reachable location on the map."; travel danger label "Niedrig"/"Hoch" → "Low"/"High"; "Handlung nicht möglich" (aria fallback) → "Action not possible"; "Nichts zu entdecken" → "Nothing to discover"; "Schließen" → "Close"; location-node state labels "Aktueller Ort"/"Ausgewähltes Ziel"/"Erreichbar" → "Current Location"/"Selected Destination"/"Reachable"; `aria-label="Weltansicht"` → `aria-label="World View"`; `"'Weltkarte bei ' + name"` → `"'World map at ' + name"`; "Weltkarte wird vorbereitet." → "Preparing world map."; "Weltzustand wird geladen…" → "Loading world state…"; `aria-label="Ortsansicht"` → `aria-label="Location View"`; `alt="'Ortsansicht: ' + name"` → `alt="'Location view: ' + name"`; "Ortsaktionen" (aria) → "Location Actions"; "Einen Moment…" → "One moment…"; "Ort konnte nicht geladen werden." → "Could not load location."; "Der Ort wird geladen…" → "Loading location…"; every "Erneut versuchen" → "Retry". - [ ] **Step 2: Run all seven touched specs to see them fail** Run: `cd apps/web && npx ng test --watch=false --include='**/location-page.component.spec.ts' --include='**/location-sidebar.component.spec.ts' --include='**/travel-panel.component.spec.ts' --include='**/location-interaction-panel.component.spec.ts' --include='**/location-node.component.spec.ts' --include='**/world-page.component.spec.ts'` Expected: FAIL. - [ ] **Step 3: Translate each template file** Apply every string from Step 1 to its owning `.html` file. In `travel-panel.component.ts`, change the `dangerLabel()` method's two return values from `'Niedrig'`/`'Hoch'` to `'Low'`/`'High'`. - [ ] **Step 4: Run all seven specs to see them pass** Run: `cd apps/web && npx ng test --watch=false --include='**/location-page.component.spec.ts' --include='**/location-sidebar.component.spec.ts' --include='**/travel-panel.component.spec.ts' --include='**/location-interaction-panel.component.spec.ts' --include='**/location-node.component.spec.ts' --include='**/world-page.component.spec.ts'` Expected: PASS. - [ ] **Step 5: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green. - [ ] **Step 6: Commit** ```bash git add apps/web/src/app/features/world/ git commit -m "feat(web): translate location, travel, and world-map screen copy to English" ``` --- ### Task 8: Hunting feature templates **Files:** - Modify: `apps/web/src/app/features/hunting/hunt-page/hunt-page.component.html` - Modify: `apps/web/src/app/features/hunting/encounter-card/encounter-card.component.html` - Modify: `apps/web/src/app/features/hunting/encounter-card/encounter-card.component.ts` - Modify their `.spec.ts` files **Interfaces:** - Consumes: `DANGER_LABELS` from Task 3, monster `name`s from Task 1 — both already English. - Produces: nothing other tasks read. - [ ] **Step 1: Update both specs to expect English** Per design doc §4: `aria-label="Jagd"` → `aria-label="Hunt"`; "Keine Jagd verfügbar" heading → "No Hunt Available"; the explanatory paragraph and "Zurück zum Ort" → "No hunting ground is available here right now." / "Back to Location" (choose plain, direct English matching the register used elsewhere — this is the one paragraph in this task without a pinned glossary row; keep it short and factual, consistent with every other explanatory line already translated in Tasks 6–7); `"{{ location.name }} — Begegnungen"` → `"{{ location.name }} — Encounters"`; "Neu suchen" → "Search Again"; "Du suchst nach Spuren..." → "You search for tracks..."; "Durchsuche die Umgebung nach Spuren und Gegnern, bevor du dich in den Kampf wagst." → "Search the surroundings for tracks and enemies before venturing into combat."; "Jagd beginnen" → "Begin Hunt"; "Jagdgebiet wird vorbereitet." → "Preparing hunting ground."; "Erneut versuchen" → "Retry"; "Schließen" → "Close"; `alt="Besiegt"` → `alt="Defeated"`; `"Stufe {{...}}"` → `"Level {{...}}"`; encounter action label logic "Besiegt"/"Im Kampf"/"Angreifen" → "Defeated"/"In Combat"/"Attack". - [ ] **Step 2: Run both specs to see them fail** Run: `cd apps/web && npx ng test --watch=false --include='**/hunt-page.component.spec.ts' --include='**/encounter-card.component.spec.ts'` Expected: FAIL. - [ ] **Step 3: Translate `hunt-page.component.html`, `encounter-card.component.html`, and `encounter-card.component.ts`** Apply every string from Step 1 to its owning file. - [ ] **Step 4: Run both specs to see them pass** Run: `cd apps/web && npx ng test --watch=false --include='**/hunt-page.component.spec.ts' --include='**/encounter-card.component.spec.ts'` Expected: PASS. - [ ] **Step 5: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green. - [ ] **Step 6: Commit** ```bash git add apps/web/src/app/features/hunting/ git commit -m "feat(web): translate hunt screen and encounter card copy to English" ``` --- ### Task 9: Layout/shared component copy **Files:** - Modify: `apps/web/src/app/layout/top-bar/top-bar.component.html` - Modify: `apps/web/src/app/layout/side-navigation/side-navigation.component.html` - Modify: `apps/web/src/app/layout/context-panel/context-panel.component.html` - Modify: `apps/web/src/app/layout/game-footer/game-footer.component.html` - Modify: `apps/web/src/app/shared/reputation-display/reputation-display.component.html` - Modify their `.spec.ts` files **Interfaces:** - Consumes: nothing new from other tasks (reputation `rankLabel` is already English from Task 2; location `name`/monster names already English from Task 1). - Produces: nothing other tasks read. - [ ] **Step 1: Update every touched spec to expect English** Per design doc §4: Topbar "Ansehen {{ character.renown }}" → "World Renown {{ character.renown }}" (design doc R7 — this is the one place the full phrase is used, matching the source spec's Common UI list); "Silber" → "Silver"; "Charakterdaten werden geladen" → "Loading character data"; side-nav visible labels and `aria-label`s: "Ort" → "Location"; "Karte" → "Map"; "Jagd" → "Hunt"; "Quests" stays "Quests", but its disabled `aria-label="Quests sind noch nicht verfügbar"` → `aria-label="Quests are not yet available"`; "Inventar" → "Inventory"; "Charakter" stays as the nav item label "Character", its disabled `aria-label="Charakter ist noch nicht verfügbar"` → `aria-label="Character is not yet available"`; context-panel `aria-label="Gebietsinformationen"` → `aria-label="Area Information"`; "GEBIETSINFO" eyebrow → "AREA INFO"; "Ausgewähltes Ziel: {{...}}" → "Selected Destination: {{...}}"; `alt="'Ortsansicht: ' + name"` → `alt="'Location view: ' + name"`; "Empfohlenes Ansehen" → "Recommended Renown" (same as Task 7's location-sidebar — kept short here too, not "World Renown"); "Sicherheit"/"Sicherer Ort"/"Gefährliches Gebiet" → "Safety"/"Safe Location"/"Dangerous Area"; "Jagd"/"Jagd möglich"/"Keine Jagd" → "Hunt"/"Hunting Available"/"No Hunting"; "Mögliche Begegnungen" → "Possible Encounters" (the joined monster-name list itself is already English from Task 1, no change needed to the join logic); "Informationen erscheinen mit der Weltansicht." → "Information appears with the world view."; footer "Verbindung bereit" → "Connection ready"; "Aschenfelder" → "Ashen Fields"; reputation-display `aria-label="... Ruf"` → `aria-label="... Reputation"`; "Höchster Rang erreicht" → "Highest Rank Reached". - [ ] **Step 2: Run all five touched specs to see them fail** Run: `cd apps/web && npx ng test --watch=false --include='**/top-bar.component.spec.ts' --include='**/side-navigation.component.spec.ts' --include='**/context-panel.component.spec.ts' --include='**/game-footer.component.spec.ts' --include='**/reputation-display.component.spec.ts'` Expected: FAIL. - [ ] **Step 3: Translate all five template files** Apply every string from Step 1 to its owning `.html` file. - [ ] **Step 4: Run all five specs to see them pass** Run: `cd apps/web && npx ng test --watch=false --include='**/top-bar.component.spec.ts' --include='**/side-navigation.component.spec.ts' --include='**/context-panel.component.spec.ts' --include='**/game-footer.component.spec.ts' --include='**/reputation-display.component.spec.ts'` Expected: PASS. - [ ] **Step 5: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: green. - [ ] **Step 6: Commit** ```bash git add apps/web/src/app/layout/ apps/web/src/app/shared/reputation-display/ git commit -m "feat(web): translate topbar, navigation, context panel, footer, and reputation display to English" ``` --- ### Task 10: Shared fixture reconciliation and final sweep **Files:** - Modify: `apps/web/src/app/features/world/current-location.fixture.ts` - Modify: any `.spec.ts` file the closing grep sweep still finds with German text - Modify: `apps/api/src/app.spec.ts` and any other cross-cutting spec the sweep finds (if any remain outside `apps/web`) **Interfaces:** - Consumes: every English value Tasks 1–9 produced — this task's whole job is making the one shared test fixture (and anything else still standing) match what the rest of the app now actually returns. - Produces: a fully English-passing test suite on both sides. - [ ] **Step 1: Update `current-location.fixture.ts` to match Task 1's seed content** This fixture mirrors the seed data consumed by multiple spec files (`location-sidebar`, `context-panel`, `world.store`, others). Per design doc §4, update its location `name`/`description`, monster `name`s, and any POI/reward-preview `label` it duplicates to match exactly what Task 1 put in `local-location.content.ts` and `vertical-slice.seed.ts`. Do not touch any `key`. - [ ] **Step 2: Run the full web suite** Run: `npm run test --workspace=@ashen-realms/web` Expected: any spec that consumes this fixture and still expects German text now fails visibly — fix each one using the glossary value that matches its `key`, the same pattern used in every prior task. - [ ] **Step 3: Closing grep sweep — web** Run (from the repo root): ```bash grep -rn '[äöüßÄÖÜ]' apps/web/src --include='*.ts' --include='*.html' | grep -v node_modules ``` Every remaining hit is either: (a) a genuine miss — fix it with the matching glossary value and re-run the affected spec; or (b) a legitimate non-content string (a German-language code comment, a CSS class name that happens to contain no umlaut but was flagged by proximity — verify before assuming false-positive). Do not delete or reword comments; this sweep is for player-facing text only. - [ ] **Step 4: Closing grep sweep — API** Run: ```bash grep -rn '[äöüßÄÖÜ]' apps/api/src --include='*.ts' | grep -v node_modules ``` Same triage as Step 3. Expect most remaining hits to be code comments (fine, per source spec's scope — dev docs and internal comments may stay German) or `renown-and-reputation.migration.spec.ts`/similar files whose assertions are on SQL string literals for already-executed migration steps, not player-facing content — leave those alone. - [ ] **Step 5: Run both full suites and the API build one final time** Run: `npm run test --workspace=@ashen-realms/api && npm run build --workspace=@ashen-realms/api && npm run test --workspace=@ashen-realms/web` Expected: all three green/clean. This is the slice's Definition-of-Done gate (source spec §12): the complete playable flow contains no intentional German player-facing text, content IDs are unchanged, and all existing tests pass. - [ ] **Step 6: Commit** ```bash git add apps/web/src/app/features/world/current-location.fixture.ts git add -u git commit -m "test: reconcile the shared location fixture and close out remaining German player-facing text" ``` --- ## Self-Review Notes (for the plan author, not a task) - **Spec coverage:** every scope item in the source spec's §3 list has an owning task: navigation (9), locations (1, 7), monsters (1), items (1), combat actions (5), combat log (5), hunt screen (8), travel screen (7), loot screen (5, "Beute"/"Loot" panel), inventory (6), character screen (6, via inventory's stats panel — there is no separate character screen yet per the survey), system messages (4, 7, 9), danger labels (3), player-visible error messages (4). - **Type consistency:** every task that reads a value another task produces (3→6/7/8, 1→2's unrelated but 1→10, 2→9) is noted in that task's Interfaces block and sequenced after its producer. - **No placeholaders:** every step names the exact file and either quotes the literal before/after string or points at a specific, already-written glossary table in the design doc (never a vague "translate as needed").