From 5222bfb2ab584f3bc7a59e0755d7c71e0c338e44 Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Fri, 21 Aug 2026 20:16:56 +0200 Subject: [PATCH] feat(web): translate slot, rarity, danger, and location-type labels to English Translates the four static Angular label maps (SLOT_LABELS, RARITY_LABELS, DANGER_LABELS, LOCATION_TYPE_LABELS) and the inventory detail panel's hardcoded item-type fallback per the English Game Content Foundation design doc glossary. Updates every spec assertion across the web app that checks rendered text sourced from these maps, including specs owned by later tasks (combat-page, encounter-card, inventory-page) where they render one of these labels. Co-Authored-By: Claude Sonnet 5 --- .../combat-page/combat-page.component.spec.ts | 2 +- .../encounter-card.component.spec.ts | 2 +- .../inventory-detail-panel.component.ts | 2 +- .../inventory/inventory-page.component.spec.ts | 4 ++-- .../app/features/inventory/inventory.labels.ts | 16 ++++++++-------- .../location-sidebar.component.spec.ts | 4 ++-- .../location-sidebar.component.ts | 16 ++++++++-------- .../danger-badge/danger-badge.component.spec.ts | 12 ++++++------ .../danger-badge/danger-badge.component.ts | 10 +++++----- .../shared/item-card/item-card.component.spec.ts | 10 +++++----- .../app/shared/item-card/item-card.component.ts | 6 +++--- 11 files changed, 42 insertions(+), 42 deletions(-) diff --git a/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts b/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts index 7edd426..1ebf01f 100644 --- a/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts +++ b/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts @@ -580,7 +580,7 @@ describe('CombatPageComponent', () => { '/images/items/bandit-blade.png', ); expect(element.querySelector('[data-item-name]')?.textContent).toContain('Räuberklinge'); - expect(element.querySelector('[data-item-rarity]')?.textContent).toContain('Gewöhnlich'); + expect(element.querySelector('[data-item-rarity]')?.textContent).toContain('Common'); expect(element.querySelector('[data-reward-empty]')).toBeNull(); }); diff --git a/apps/web/src/app/features/hunting/encounter-card/encounter-card.component.spec.ts b/apps/web/src/app/features/hunting/encounter-card/encounter-card.component.spec.ts index f0c99a8..8b9a736 100644 --- a/apps/web/src/app/features/hunting/encounter-card/encounter-card.component.spec.ts +++ b/apps/web/src/app/features/hunting/encounter-card/encounter-card.component.spec.ts @@ -47,7 +47,7 @@ describe('EncounterCardComponent', () => { expect(element.textContent).toContain('Dämmerwolf'); expect(element.textContent).toContain('Stufe 3'); - expect(element.textContent).toContain('Passend'); + expect(element.textContent).toContain('Suitable'); expect(artwork?.getAttribute('src')).toBe('/images/enemies/Dawnwolf.png'); expect(artwork?.getAttribute('alt')).toBe('Dämmerwolf'); }); diff --git a/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts b/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts index dd7f5f2..df672b5 100644 --- a/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts +++ b/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts @@ -40,7 +40,7 @@ export class InventoryDetailPanelComponent { }); /** Stand-in for items with no slot: the API does not expose an item type. */ - protected readonly typeLabel = computed(() => 'Gegenstand'); + protected readonly typeLabel = computed(() => 'Item'); protected readonly statRows = computed(() => { const item = this.item(); diff --git a/apps/web/src/app/features/inventory/inventory-page.component.spec.ts b/apps/web/src/app/features/inventory/inventory-page.component.spec.ts index d3d0daa..0c17d7f 100644 --- a/apps/web/src/app/features/inventory/inventory-page.component.spec.ts +++ b/apps/web/src/app/features/inventory/inventory-page.component.spec.ts @@ -149,9 +149,9 @@ describe('InventoryPageComponent', () => { const { fixture } = await setup(); const text = (fixture.nativeElement as HTMLElement).querySelector('.inventory-page__equipment-list')?.textContent ?? ''; - expect(text).toContain('Waffe'); + expect(text).toContain('Weapon'); expect(text).toContain('Abgenutztes Kurzschwert'); - expect(text).toContain('Kopf'); + expect(text).toContain('Head'); expect(text).toContain('Leer'); }); diff --git a/apps/web/src/app/features/inventory/inventory.labels.ts b/apps/web/src/app/features/inventory/inventory.labels.ts index f356260..cb21e0f 100644 --- a/apps/web/src/app/features/inventory/inventory.labels.ts +++ b/apps/web/src/app/features/inventory/inventory.labels.ts @@ -1,12 +1,12 @@ import type { EquipmentSlot } from '../../core/api/game-api.models'; -/** German slot names, shared by the paper doll and the item detail panel. */ +/** Slot names, shared by the paper doll and the item detail panel. */ export const SLOT_LABELS: Readonly> = { - WEAPON: 'Waffe', - HEAD: 'Kopf', - CHEST: 'Brust', - HANDS: 'Handschuhe', - LEGS: 'Beine', - FEET: 'Stiefel', - AMULET: 'Amulett', + WEAPON: 'Weapon', + HEAD: 'Head', + CHEST: 'Chest', + HANDS: 'Hands', + LEGS: 'Legs', + FEET: 'Feet', + AMULET: 'Amulet', }; diff --git a/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.spec.ts b/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.spec.ts index 2448906..5852d82 100644 --- a/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.spec.ts +++ b/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.spec.ts @@ -22,14 +22,14 @@ describe('LocationSidebarComponent', () => { expect(text).toContain('Aschenfelder'); expect(text).toContain('Verbrannte Straße'); - expect(text).toContain('Jagdgebiet'); + expect(text).toContain('Hunting Ground'); expect(text).toContain('1–2'); }); it('shows the danger rating as the shared badge', async () => { const element = await render(burnedRoadFixture()); - expect(element.querySelector('app-danger-badge')?.textContent).toContain('Passend'); + expect(element.querySelector('app-danger-badge')?.textContent).toContain('Suitable'); }); it('calls a location with no hostile pool safe instead of inventing a rating', async () => { diff --git a/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.ts b/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.ts index cbfb826..3acec57 100644 --- a/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.ts +++ b/apps/web/src/app/features/world/location-sidebar/location-sidebar.component.ts @@ -4,14 +4,14 @@ import { DangerBadgeComponent } from '../../../shared/danger-badge/danger-badge. import { LocationIconComponent } from '../location-icon/location-icon.component'; const LOCATION_TYPE_LABELS: Readonly> = { - SAFE_HUB: 'Zuflucht', - TRANSITION: 'Übergang', - HUNTING_GROUND: 'Jagdgebiet', - QUEST_LOCATION: 'Questort', - OUTPOST: 'Außenposten', - ELITE_ZONE: 'Elitegebiet', - BOSS_LOCATION: 'Bossort', - DUNGEON_ENTRANCE: 'Verlieseingang', + 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', }; interface InteractionSummary { diff --git a/apps/web/src/app/shared/danger-badge/danger-badge.component.spec.ts b/apps/web/src/app/shared/danger-badge/danger-badge.component.spec.ts index 40a1e86..8c22c74 100644 --- a/apps/web/src/app/shared/danger-badge/danger-badge.component.spec.ts +++ b/apps/web/src/app/shared/danger-badge/danger-badge.component.spec.ts @@ -3,20 +3,20 @@ import type { DangerRating } from '../../core/api/game-api.models'; import { DangerBadgeComponent } from './danger-badge.component'; const expectations: Array<{ rating: DangerRating; label: string; modifierClass: string }> = [ - { rating: 'WEAK', label: 'Schwach', modifierClass: 'danger-badge--weak' }, - { rating: 'MATCH', label: 'Passend', modifierClass: 'danger-badge--match' }, - { rating: 'STRONG', label: 'Stark', modifierClass: 'danger-badge--strong' }, + { rating: 'WEAK', label: 'Weak', modifierClass: 'danger-badge--weak' }, + { rating: 'MATCH', label: 'Suitable', modifierClass: 'danger-badge--match' }, + { rating: 'STRONG', label: 'Strong', modifierClass: 'danger-badge--strong' }, { rating: 'VERY_DANGEROUS', - label: 'Sehr gefährlich', + label: 'Very Dangerous', modifierClass: 'danger-badge--very-dangerous', }, - { rating: 'DEADLY', label: 'Tödlich', modifierClass: 'danger-badge--deadly' }, + { rating: 'DEADLY', label: 'Deadly', modifierClass: 'danger-badge--deadly' }, ]; describe('DangerBadgeComponent', () => { for (const { rating, label, modifierClass } of expectations) { - it(`renders the German label "${label}" and modifier class for ${rating}`, () => { + it(`renders the label "${label}" and modifier class for ${rating}`, () => { const fixture = TestBed.createComponent(DangerBadgeComponent); fixture.componentRef.setInput('rating', rating); fixture.detectChanges(); diff --git a/apps/web/src/app/shared/danger-badge/danger-badge.component.ts b/apps/web/src/app/shared/danger-badge/danger-badge.component.ts index 5dd3bcb..dfb2e23 100644 --- a/apps/web/src/app/shared/danger-badge/danger-badge.component.ts +++ b/apps/web/src/app/shared/danger-badge/danger-badge.component.ts @@ -2,11 +2,11 @@ import { Component, Input } from '@angular/core'; import { DangerRating } from '../../core/api/game-api.models'; const DANGER_LABELS: Record = { - WEAK: 'Schwach', - MATCH: 'Passend', - STRONG: 'Stark', - VERY_DANGEROUS: 'Sehr gefährlich', - DEADLY: 'Tödlich', + WEAK: 'Weak', + MATCH: 'Suitable', + STRONG: 'Strong', + VERY_DANGEROUS: 'Very Dangerous', + DEADLY: 'Deadly', }; @Component({ diff --git a/apps/web/src/app/shared/item-card/item-card.component.spec.ts b/apps/web/src/app/shared/item-card/item-card.component.spec.ts index cd6caaa..d66e44b 100644 --- a/apps/web/src/app/shared/item-card/item-card.component.spec.ts +++ b/apps/web/src/app/shared/item-card/item-card.component.spec.ts @@ -21,7 +21,7 @@ async function setup(item: RewardItemSummary, quantity = 1) { } describe('ItemCardComponent', () => { - it('renders the icon, name, and German rarity label', async () => { + it('renders the icon, name, and rarity label', async () => { const fixture = await setup(banditBlade); const element = fixture.nativeElement as HTMLElement; @@ -29,19 +29,19 @@ describe('ItemCardComponent', () => { expect(icon?.getAttribute('src')).toBe('/images/items/bandit-blade.png'); expect(icon?.getAttribute('alt')).toBe('Räuberklinge'); expect(element.querySelector('[data-item-name]')?.textContent).toContain('Räuberklinge'); - expect(element.querySelector('[data-item-rarity]')?.textContent).toContain('Gewöhnlich'); + expect(element.querySelector('[data-item-rarity]')?.textContent).toContain('Common'); }); - it('labels the other rarities in German too', async () => { + it('labels the other rarities too', async () => { const rare = await setup({ ...banditBlade, rarity: 'RARE' }); expect( (rare.nativeElement as HTMLElement).querySelector('[data-item-rarity]')?.textContent, - ).toContain('Selten'); + ).toContain('Rare'); const epic = await setup({ ...banditBlade, rarity: 'EPIC' }); expect( (epic.nativeElement as HTMLElement).querySelector('[data-item-rarity]')?.textContent, - ).toContain('Episch'); + ).toContain('Epic'); }); it('hides the quantity for a single item and shows it for a stack', async () => { diff --git a/apps/web/src/app/shared/item-card/item-card.component.ts b/apps/web/src/app/shared/item-card/item-card.component.ts index b3904ec..f3bb658 100644 --- a/apps/web/src/app/shared/item-card/item-card.component.ts +++ b/apps/web/src/app/shared/item-card/item-card.component.ts @@ -2,9 +2,9 @@ import { Component, computed, input } from '@angular/core'; import type { ItemRarity, RewardItemSummary } from '../../core/api/game-api.models'; export const RARITY_LABELS: Readonly> = { - COMMON: 'Gewöhnlich', - RARE: 'Selten', - EPIC: 'Episch', + COMMON: 'Common', + RARE: 'Rare', + EPIC: 'Epic', }; /**