a11y: localized aria-label on the 3 filter/search inputs

The three filter / search inputs in the always-visible UI surface (VOD filter, sidebar streamer-list filter, Archive search) had only placeholder text as their accessible-name source. Placeholder text is unreliable as a screen-reader name — some implementations announce it, some skip it, and it disappears as soon as the user types so a re-focus during typing leaves the input unannounced.

Added three locale keys (DE+EN):
- vods.filterAria — "VOD-Titel filtern" / "Filter VOD titles"
- static.archiveSearchAria — "Archiv durchsuchen" / "Search archive"
- static.streamerListFilterAria — "Streamer-Liste filtern" / "Filter streamer list"

Wired through renderer-texts via the existing setAriaLabel helper (added in 4.6.91), so each input now has a proper aria-label that survives the placeholder vanishing and reads cleanly in screen-reader navigation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 11:57:11 +02:00
parent 6c56c4e908
commit 7b0e511479
3 changed files with 9 additions and 0 deletions

View File

@ -139,6 +139,7 @@ const UI_TEXT_DE = {
archiveNoMatches: 'Keine Treffer.', archiveNoMatches: 'Keine Treffer.',
archiveNoRoot: 'Download-Ordner nicht gefunden. Setze zuerst einen Download-Pfad in den Einstellungen.', archiveNoRoot: 'Download-Ordner nicht gefunden. Setze zuerst einen Download-Pfad in den Einstellungen.',
archiveSearchPlaceholder: 'Suche...', archiveSearchPlaceholder: 'Suche...',
archiveSearchAria: 'Archiv durchsuchen',
archiveOpen: 'Oeffnen', archiveOpen: 'Oeffnen',
archiveShowInFolder: 'Ordner', archiveShowInFolder: 'Ordner',
archiveViewChat: 'Chat', archiveViewChat: 'Chat',
@ -177,6 +178,7 @@ const UI_TEXT_DE = {
downloadPathNotWritable: 'Download-Ordner ist nicht beschreibbar. Waehle einen anderen Ordner oder pruefe die Schreibrechte.', downloadPathNotWritable: 'Download-Ordner ist nicht beschreibbar. Waehle einen anderen Ordner oder pruefe die Schreibrechte.',
streamerSectionTitle: 'Streamer', streamerSectionTitle: 'Streamer',
streamerListFilterPlaceholder: 'Filtern...', streamerListFilterPlaceholder: 'Filtern...',
streamerListFilterAria: 'Streamer-Liste filtern',
streamerAddAriaLabel: 'Streamer hinzufuegen', streamerAddAriaLabel: 'Streamer hinzufuegen',
streamerBulkRemoveTitle: 'Alle entfernen (oder gefilterte)', streamerBulkRemoveTitle: 'Alle entfernen (oder gefilterte)',
streamerBulkRemoveAll: 'Alle {count} Streamer aus der Liste entfernen?', streamerBulkRemoveAll: 'Alle {count} Streamer aus der Liste entfernen?',
@ -378,6 +380,7 @@ const UI_TEXT_DE = {
addQueue: '+ Warteschlange', addQueue: '+ Warteschlange',
trimButton: 'VOD zuschneiden', trimButton: 'VOD zuschneiden',
filterPlaceholder: 'Nach Titel filtern... (Strg+F)', filterPlaceholder: 'Nach Titel filtern... (Strg+F)',
filterAria: 'VOD-Titel filtern',
filterClearTitle: 'Filter loeschen (Esc)', filterClearTitle: 'Filter loeschen (Esc)',
filterNoMatchTitle: 'Keine Treffer', filterNoMatchTitle: 'Keine Treffer',
filterNoMatchText: 'Keine VODs entsprechen dem aktuellen Filter.', filterNoMatchText: 'Keine VODs entsprechen dem aktuellen Filter.',

View File

@ -140,6 +140,7 @@ const UI_TEXT_EN = {
archiveNoMatches: 'No matches.', archiveNoMatches: 'No matches.',
archiveNoRoot: 'Download folder not found. Set a download path in Settings first.', archiveNoRoot: 'Download folder not found. Set a download path in Settings first.',
archiveSearchPlaceholder: 'Search...', archiveSearchPlaceholder: 'Search...',
archiveSearchAria: 'Search archive',
archiveOpen: 'Open', archiveOpen: 'Open',
archiveShowInFolder: 'Folder', archiveShowInFolder: 'Folder',
archiveViewChat: 'Chat', archiveViewChat: 'Chat',
@ -177,6 +178,7 @@ const UI_TEXT_EN = {
downloadPathNotWritable: 'Download folder is not writable. Pick another folder or grant write permission.', downloadPathNotWritable: 'Download folder is not writable. Pick another folder or grant write permission.',
streamerSectionTitle: 'Streamer', streamerSectionTitle: 'Streamer',
streamerListFilterPlaceholder: 'Filter...', streamerListFilterPlaceholder: 'Filter...',
streamerListFilterAria: 'Filter streamer list',
streamerAddAriaLabel: 'Add streamer', streamerAddAriaLabel: 'Add streamer',
streamerBulkRemoveTitle: 'Remove all (or filtered)', streamerBulkRemoveTitle: 'Remove all (or filtered)',
streamerBulkRemoveAll: 'Remove all {count} streamers from the list?', streamerBulkRemoveAll: 'Remove all {count} streamers from the list?',
@ -378,6 +380,7 @@ const UI_TEXT_EN = {
addQueue: '+ Queue', addQueue: '+ Queue',
trimButton: 'Trim VOD', trimButton: 'Trim VOD',
filterPlaceholder: 'Filter by title... (Ctrl+F)', filterPlaceholder: 'Filter by title... (Ctrl+F)',
filterAria: 'Filter VOD titles',
filterClearTitle: 'Clear filter (Esc)', filterClearTitle: 'Clear filter (Esc)',
filterNoMatchTitle: 'No matches', filterNoMatchTitle: 'No matches',
filterNoMatchText: 'No VODs match the current filter.', filterNoMatchText: 'No VODs match the current filter.',

View File

@ -67,6 +67,7 @@ function applyLanguageToStaticUI(): void {
setText('btnArchiveSearch', UI_TEXT.static.archiveSearchBtn); setText('btnArchiveSearch', UI_TEXT.static.archiveSearchBtn);
const archiveQueryInput = document.getElementById('archiveSearchQuery') as HTMLInputElement | null; const archiveQueryInput = document.getElementById('archiveSearchQuery') as HTMLInputElement | null;
if (archiveQueryInput) archiveQueryInput.placeholder = UI_TEXT.static.archiveSearchPlaceholder; if (archiveQueryInput) archiveQueryInput.placeholder = UI_TEXT.static.archiveSearchPlaceholder;
setAriaLabel('archiveSearchQuery', UI_TEXT.static.archiveSearchAria);
const archiveTypeSelect = document.getElementById('archiveSearchType') as HTMLSelectElement | null; const archiveTypeSelect = document.getElementById('archiveSearchType') as HTMLSelectElement | null;
if (archiveTypeSelect) { if (archiveTypeSelect) {
const opts = archiveTypeSelect.options; const opts = archiveTypeSelect.options;
@ -185,6 +186,7 @@ function applyLanguageToStaticUI(): void {
setText('streamlinkQualityAudio', UI_TEXT.static.streamlinkQualityAudio); setText('streamlinkQualityAudio', UI_TEXT.static.streamlinkQualityAudio);
setText('streamerSectionTitleText', UI_TEXT.static.streamerSectionTitle); setText('streamerSectionTitleText', UI_TEXT.static.streamerSectionTitle);
setPlaceholder('streamerListFilter', UI_TEXT.static.streamerListFilterPlaceholder); setPlaceholder('streamerListFilter', UI_TEXT.static.streamerListFilterPlaceholder);
setAriaLabel('streamerListFilter', UI_TEXT.static.streamerListFilterAria);
setTitle('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle); setTitle('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle);
setAriaLabel('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle); setAriaLabel('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle);
setAriaLabel('btnAddStreamer', UI_TEXT.static.streamerAddAriaLabel); setAriaLabel('btnAddStreamer', UI_TEXT.static.streamerAddAriaLabel);
@ -294,6 +296,7 @@ function applyLanguageToStaticUI(): void {
setText('updateChangelogEmpty', UI_TEXT.updates.noChangelog); setText('updateChangelogEmpty', UI_TEXT.updates.noChangelog);
setPlaceholder('newStreamer', UI_TEXT.static.streamerPlaceholder); setPlaceholder('newStreamer', UI_TEXT.static.streamerPlaceholder);
setPlaceholder('vodFilterInput', UI_TEXT.vods.filterPlaceholder); setPlaceholder('vodFilterInput', UI_TEXT.vods.filterPlaceholder);
setAriaLabel('vodFilterInput', UI_TEXT.vods.filterAria);
setTitle('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle); setTitle('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle);
setAriaLabel('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle); setAriaLabel('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle);
setPlaceholder('chatViewerFilter', UI_TEXT.queue.chatViewerFilterPlaceholder); setPlaceholder('chatViewerFilter', UI_TEXT.queue.chatViewerFilterPlaceholder);