fix(timeline): pluralize the zeitstrahl meta-line counts

A count of one rendered "1 Briefe" / "1 Ereignisse". Add _singular
companion keys (de/en/es) and select them when the count is exactly one,
following the project's _singular/_plural convention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-14 12:15:11 +02:00
parent 9665c9c0fc
commit 398babe584
6 changed files with 38 additions and 5 deletions

View File

@@ -1053,6 +1053,8 @@
"timeline_layer_historical_suffix": "historisch", "timeline_layer_historical_suffix": "historisch",
"timeline_strip_density_caption": "Monats-Dichte", "timeline_strip_density_caption": "Monats-Dichte",
"timeline_events_count": "{count} Ereignisse", "timeline_events_count": "{count} Ereignisse",
"timeline_letters_count_singular": "1 Brief",
"timeline_events_count_singular": "1 Ereignis",
"event_editor_new_title": "Neues Ereignis", "event_editor_new_title": "Neues Ereignis",
"event_editor_edit_title": "Ereignis bearbeiten", "event_editor_edit_title": "Ereignis bearbeiten",
"event_editor_section_when": "Wann", "event_editor_section_when": "Wann",

View File

@@ -1053,6 +1053,8 @@
"timeline_layer_historical_suffix": "historical", "timeline_layer_historical_suffix": "historical",
"timeline_strip_density_caption": "Monthly density", "timeline_strip_density_caption": "Monthly density",
"timeline_events_count": "{count} events", "timeline_events_count": "{count} events",
"timeline_letters_count_singular": "1 letter",
"timeline_events_count_singular": "1 event",
"event_editor_new_title": "New event", "event_editor_new_title": "New event",
"event_editor_edit_title": "Edit event", "event_editor_edit_title": "Edit event",
"event_editor_section_when": "When", "event_editor_section_when": "When",

View File

@@ -1053,6 +1053,8 @@
"timeline_layer_historical_suffix": "histórico", "timeline_layer_historical_suffix": "histórico",
"timeline_strip_density_caption": "Densidad mensual", "timeline_strip_density_caption": "Densidad mensual",
"timeline_events_count": "{count} eventos", "timeline_events_count": "{count} eventos",
"timeline_letters_count_singular": "1 carta",
"timeline_events_count_singular": "1 evento",
"event_editor_new_title": "Nuevo evento", "event_editor_new_title": "Nuevo evento",
"event_editor_edit_title": "Editar evento", "event_editor_edit_title": "Editar evento",
"event_editor_section_when": "Cuándo", "event_editor_section_when": "Cuándo",

View File

@@ -80,7 +80,9 @@ describe('message key parity', () => {
'timeline_letter_glyph_label', 'timeline_letter_glyph_label',
'timeline_layer_historical_suffix', 'timeline_layer_historical_suffix',
'timeline_strip_density_caption', 'timeline_strip_density_caption',
'timeline_events_count' 'timeline_events_count',
'timeline_letters_count_singular',
'timeline_events_count_singular'
]; ];
for (const key of requiredKeys) { for (const key of requiredKeys) {
expect(de, `missing key in de: ${key}`).toHaveProperty(key); expect(de, `missing key in de: ${key}`).toHaveProperty(key);

View File

@@ -18,12 +18,21 @@ const metaLine = $derived.by(() => {
if (meta.firstYear !== null && meta.lastYear !== null) { if (meta.firstYear !== null && meta.lastYear !== null) {
segments.push(`${meta.firstYear}${meta.lastYear}`); segments.push(`${meta.firstYear}${meta.lastYear}`);
} }
// A zero-count segment ("0 Briefe") reads as a data error — drop it. // A zero-count segment ("0 Briefe") reads as a data error — drop it; a count
// of one takes the singular key ("1 Brief"), per the project plural convention.
if (meta.letterCount > 0) { if (meta.letterCount > 0) {
segments.push(m.timeline_letters_count({ count: meta.letterCount })); segments.push(
meta.letterCount === 1
? m.timeline_letters_count_singular()
: m.timeline_letters_count({ count: meta.letterCount })
);
} }
if (meta.eventCount > 0) { if (meta.eventCount > 0) {
segments.push(m.timeline_events_count({ count: meta.eventCount })); segments.push(
meta.eventCount === 1
? m.timeline_events_count_singular()
: m.timeline_events_count({ count: meta.eventCount })
);
} }
segments.push(m.timeline_grouping_date()); segments.push(m.timeline_grouping_date());
return segments.join(' · '); return segments.join(' · ');

View File

@@ -68,7 +68,7 @@ describe('/zeitstrahl page', () => {
const sub = document.querySelector('[data-testid="timeline-meta"]'); const sub = document.querySelector('[data-testid="timeline-meta"]');
expect(sub).not.toBeNull(); expect(sub).not.toBeNull();
expect(sub?.textContent).not.toContain(''); expect(sub?.textContent).not.toContain('');
expect(sub?.textContent).toContain(m.timeline_letters_count({ count: 1 })); expect(sub?.textContent).toContain(m.timeline_letters_count_singular());
}); });
it('omits the entire sub-line for an empty timeline (REQ-002)', () => { it('omits the entire sub-line for an empty timeline (REQ-002)', () => {
@@ -94,4 +94,20 @@ describe('/zeitstrahl page', () => {
expect(sub).not.toBeNull(); expect(sub).not.toBeNull();
expect(sub?.textContent).not.toContain(m.timeline_events_count({ count: 0 })); expect(sub?.textContent).not.toContain(m.timeline_events_count({ count: 0 }));
}); });
it('uses singular count labels for exactly one letter and one event (REQ-002)', () => {
render(Page, {
data: pageData(
makeTimelineDTO({
years: [makeYear(1914, [makeEntry({ documentId: 'a' }), event('Geburt')])]
})
)
});
const sub = document.querySelector('[data-testid="timeline-meta"]');
expect(sub?.textContent).toContain(m.timeline_letters_count_singular());
expect(sub?.textContent).toContain(m.timeline_events_count_singular());
// never the "1 Briefe"/"1 Ereignisse" plural forms for a count of one
expect(sub?.textContent).not.toContain(m.timeline_letters_count({ count: 1 }));
expect(sub?.textContent).not.toContain(m.timeline_events_count({ count: 1 }));
});
}); });