From b14cc3891927c61097d7ba74267a691d3ea3c2a1 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Thu, 11 Jun 2026 19:23:55 +0200 Subject: [PATCH] UI: render non-play history moves as a dim lowercase tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per owner feedback: pass/exchange/resign/timeout rows in the move history now read as a dim, parenthesised, lowercase tag — e.g. «(обмен)» — so they stand apart from a scored word. The move.* catalog values are lowercased (resign RU → «сдаюсь»); the parentheses and the muted colour (var(--text-muted)) are applied in the view via a .ha.sys modifier. --- ui/src/game/Game.svelte | 7 ++++++- ui/src/lib/i18n.test.ts | 6 +++--- ui/src/lib/i18n/en.ts | 8 ++++---- ui/src/lib/i18n/ru.ts | 8 ++++---- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ui/src/game/Game.svelte b/ui/src/game/Game.svelte index 067127c..7484ae9 100644 --- a/ui/src/game/Game.svelte +++ b/ui/src/game/Game.svelte @@ -750,7 +750,7 @@ {#each moves as m, i (i)}
  • {view.game.seats[m.player]?.displayName ?? m.player} - {m.action === 'play' ? m.words.join(', ') : moveActionLabel(m.action)} + {m.action === 'play' ? m.words.join(', ') : `(${moveActionLabel(m.action)})`} {m.score} ({m.total})
  • {/each} @@ -997,6 +997,11 @@ flex: 1; text-align: center; } + .ha.sys { + /* A non-scoring move (pass/exchange/resign/timeout): dimmer and parenthesised so it + reads as a system action rather than a scored word. */ + color: var(--text-muted); + } .hs { font-variant-numeric: tabular-nums; font-weight: 600; diff --git a/ui/src/lib/i18n.test.ts b/ui/src/lib/i18n.test.ts index 5172699..f61068d 100644 --- a/ui/src/lib/i18n.test.ts +++ b/ui/src/lib/i18n.test.ts @@ -14,9 +14,9 @@ describe('i18n catalog', () => { }); it('localizes move-history action labels', () => { - expect(translate('ru', 'move.exchange')).toBe('Обмен'); - expect(translate('ru', 'move.pass')).toBe('Пас'); - expect(translate('en', 'move.exchange')).toBe('Exchange'); + expect(translate('ru', 'move.exchange')).toBe('обмен'); + expect(translate('ru', 'move.pass')).toBe('пас'); + expect(translate('en', 'move.exchange')).toBe('exchange'); }); it('maps error codes to keys with a generic fallback', () => { diff --git a/ui/src/lib/i18n/en.ts b/ui/src/lib/i18n/en.ts index 23e95e3..0ae7a51 100644 --- a/ui/src/lib/i18n/en.ts +++ b/ui/src/lib/i18n/en.ts @@ -88,10 +88,10 @@ export const en = { 'game.noHintOptions': 'No options with your letters.', 'game.scores': 'Scores: {n}', - 'move.pass': 'Pass', - 'move.exchange': 'Exchange', - 'move.resign': 'Resigned', - 'move.timeout': 'Timed out', + 'move.pass': 'pass', + 'move.exchange': 'exchange', + 'move.resign': 'resign', + 'move.timeout': 'timeout', 'result.victory': 'Victory', 'result.defeat': 'Defeat', diff --git a/ui/src/lib/i18n/ru.ts b/ui/src/lib/i18n/ru.ts index dd7594c..76d46b6 100644 --- a/ui/src/lib/i18n/ru.ts +++ b/ui/src/lib/i18n/ru.ts @@ -89,10 +89,10 @@ export const ru: Record = { 'game.noHintOptions': 'Нет вариантов с вашим набором.', 'game.scores': 'Очков: {n}', - 'move.pass': 'Пас', - 'move.exchange': 'Обмен', - 'move.resign': 'Сдача', - 'move.timeout': 'Тайм-аут', + 'move.pass': 'пас', + 'move.exchange': 'обмен', + 'move.resign': 'сдаюсь', + 'move.timeout': 'тайм-аут', 'result.victory': 'Победа', 'result.defeat': 'Поражение',