diff --git a/frontend/messages/de.json b/frontend/messages/de.json
index e5929a88..2d819091 100644
--- a/frontend/messages/de.json
+++ b/frontend/messages/de.json
@@ -1054,6 +1054,7 @@
"timeline_bucket_show_more": "+ {count} weitere Briefe anzeigen",
"timeline_bucket_show_less": "Weniger anzeigen",
"timeline_letter_glyph_label": "Brief",
+ "timeline_cluster_letter_count": "{count} Briefe",
"timeline_tag_chip_label": "Thema",
"timeline_layer_historical_suffix": "historisch",
"timeline_strip_density_caption": "Monats-Dichte",
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 2388aae4..a9ed5ce8 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -1054,6 +1054,7 @@
"timeline_bucket_show_more": "+ {count} more letters",
"timeline_bucket_show_less": "Show fewer",
"timeline_letter_glyph_label": "Letter",
+ "timeline_cluster_letter_count": "{count} letters",
"timeline_tag_chip_label": "Topic",
"timeline_layer_historical_suffix": "historical",
"timeline_strip_density_caption": "Monthly density",
diff --git a/frontend/messages/es.json b/frontend/messages/es.json
index 5f99f5b6..15239a92 100644
--- a/frontend/messages/es.json
+++ b/frontend/messages/es.json
@@ -1054,6 +1054,7 @@
"timeline_bucket_show_more": "+ {count} cartas más",
"timeline_bucket_show_less": "Mostrar menos",
"timeline_letter_glyph_label": "Carta",
+ "timeline_cluster_letter_count": "{count} cartas",
"timeline_tag_chip_label": "Tema",
"timeline_layer_historical_suffix": "histórico",
"timeline_strip_density_caption": "Densidad mensual",
diff --git a/frontend/src/lib/messages.spec.ts b/frontend/src/lib/messages.spec.ts
index ecfeb9dd..1003bf37 100644
--- a/frontend/src/lib/messages.spec.ts
+++ b/frontend/src/lib/messages.spec.ts
@@ -100,6 +100,14 @@ describe('message key parity', () => {
expect(es).toMatchObject({ timeline_tag_chip_label: 'Tema' });
});
+ // #850 finding #6: the event-card letter count carries an sr-only "{count} Briefe" so the
+ // bare "· 2" never announces to a screen reader without context.
+ it('zeitstrahl cluster letter-count key is present in all locales (#850 finding #6)', () => {
+ expect(de).toHaveProperty('timeline_cluster_letter_count');
+ expect(en).toHaveProperty('timeline_cluster_letter_count');
+ expect(es).toHaveProperty('timeline_cluster_letter_count');
+ });
+
// #780 REQ-010: the layer-filter strings are Paraglide keys in every locale.
// timeline_filter_trigger (0 active) and timeline_filter_trigger_active ({count},
// ≥1 active) are distinct keys so the trigger never reads "Filter (0 aktiv)".
diff --git a/frontend/src/lib/timeline/EventCluster.svelte b/frontend/src/lib/timeline/EventCluster.svelte
index 9fa1f2e8..bbf7da3c 100644
--- a/frontend/src/lib/timeline/EventCluster.svelte
+++ b/frontend/src/lib/timeline/EventCluster.svelte
@@ -86,7 +86,11 @@ const hiddenCount = $derived(letters.length - CLUSTER_PREVIEW);
>{event.title}
- {eventSubtitle} · {count}
+ {eventSubtitle}
+
+ · {count}
+ {m.timeline_cluster_letter_count({ count })}
+
{#if canEdit}
@@ -111,7 +115,10 @@ const hiddenCount = $derived(letters.length - CLUSTER_PREVIEW);
{title}
- · {count}
+
+ · {count}
+ {m.timeline_cluster_letter_count({ count })}
+
{/if}
diff --git a/frontend/src/lib/timeline/EventCluster.svelte.spec.ts b/frontend/src/lib/timeline/EventCluster.svelte.spec.ts
index ee19aa2d..ed8f109b 100644
--- a/frontend/src/lib/timeline/EventCluster.svelte.spec.ts
+++ b/frontend/src/lib/timeline/EventCluster.svelte.spec.ts
@@ -108,6 +108,16 @@ describe('EventCluster — contained event card (#850)', () => {
expect(srOnly?.textContent).toBe(m.timeline_letter_glyph_label());
});
+ it('gives the letter count an sr-only "{count} Briefe" label so "· 2" is not announced bare (finding #6)', () => {
+ render(EventCluster, { letters: letters(2), title: 'Briefe von der Front' });
+ const count = document.querySelector('[data-testid="event-count"]') as HTMLElement;
+ // the visible "· 2" stays aria-hidden; the sr-only sibling carries the meaning
+ expect(count.querySelector('[aria-hidden="true"]')?.textContent).toContain('· 2');
+ expect(count.querySelector('.sr-only')?.textContent).toBe(
+ m.timeline_cluster_letter_count({ count: 2 })
+ );
+ });
+
it('renders an HTML-bearing event title verbatim as text, never as markup (REQ-010)', () => {
render(EventCluster, {
letters: letters(1),