Files
ashen-realms/docs/superpowers/plans/2026-08-21-english-game-content-foundation.md
Bastian Wagner 74fc89dfe5 docs: implementation plan for Slice 0.6.6 English Game Content Foundation
10 tasks, batched by layer (per subagent-driven-development's batching
guidance -- this is same-shape content/copy work across ~40 files, not
one task per file): API seed content, API reputation rank labels, web
static label maps, web error-message maps, then five feature-area
template sweeps (combat, inventory, world/travel, hunting,
layout/shared), closing with the shared test fixture and a repo-wide
grep sweep as the Definition-of-Done gate.

Unlike Slice 0.6.5, this plan has no designed red-build window: every
task changes string VALUES only, never a field/interface a sibling
task still reads, so both the API build and the Angular bundle compile
stay green throughout.

All test-filter commands in every task were run and verified against
this worktree before being written into the plan, rather than assumed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 19:50:51 +02:00

38 KiB
Raw Blame History

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 keys, item keys, location keys, faction keys, 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:

// 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
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
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
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
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:

// 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
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
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
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 names 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 67); "{{ 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
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-labels: "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
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 19 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 names, 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):

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:

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
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").