From c1e5cd6c8bb3c185b9f06fd9489b6023a19ac91e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 24 Sep 2026 20:44:46 +0200 Subject: [PATCH] fix: label the remaining settings icon buttons for screen readers The filter rule reorder, delete and remove-condition/action buttons, the filter, Sieve and folder editor close buttons and the attachment keyword remove buttons had no label. The Sieve editor's line count now uses an ICU plural, and the settings i18n test covers every pane. --- locales/rn/en.json | 7 +++ .../__tests__/settings-i18n.test.ts | 46 ++++++++++++++----- src/components/filters/FilterRuleModal.tsx | 12 ++++- src/components/filters/SieveEditorSheet.tsx | 12 ++++- src/components/settings/AboutDataSettings.tsx | 2 +- src/components/settings/ComposingSettings.tsx | 7 ++- src/components/settings/FilterSettings.tsx | 12 ++++- src/components/settings/FolderSettings.tsx | 7 ++- src/components/settings/ReadingSettings.tsx | 2 + 9 files changed, 88 insertions(+), 19 deletions(-) diff --git a/locales/rn/en.json b/locales/rn/en.json index 1338e78e..f3cfb40b 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -640,6 +640,13 @@ "experimental": { "title": "Experimental Feature" }, + "filters": { + "remove_action": "Remove action", + "remove_condition": "Remove condition", + "sieve_editor": { + "line_count": "{count, plural, one {# line} other {# lines}}" + } + }, "folders": { "account": "Account", "cannot_delete": "Cannot delete", diff --git a/src/components/__tests__/settings-i18n.test.ts b/src/components/__tests__/settings-i18n.test.ts index 86208132..5e1fadb3 100644 --- a/src/components/__tests__/settings-i18n.test.ts +++ b/src/components/__tests__/settings-i18n.test.ts @@ -5,27 +5,49 @@ import { describe, it, expect } from 'vitest'; import { join } from 'node:path'; import { findUntranslatedLiterals } from './untranslated-literals'; -// Pane file → texts that stay as they are on purpose. +// Pane file (relative to src/components/) → texts that stay as they are on +// purpose: product names, units, format patterns and sample data. const TRANSLATED_PANES: Record = { - 'ReadingSettings.tsx': [], - 'ContactsSettings.tsx': [], - 'ContentSendersSettings.tsx': [], - 'LayoutSettings.tsx': [], - 'CalendarSettings.tsx': [], - 'IdentitySettings.tsx': [ - // Example signatures in the placeholders: sample data, not UI text. + 'settings/AboutDataSettings.tsx': ['MB', 'GitHub'], + 'settings/AccountSecuritySettings.tsx': ['AES-128', 'AES-256'], + 'settings/AccountSettings.tsx': [], + 'settings/AppearanceSettings.tsx': [], + 'settings/CalendarSettings.tsx': [], + // Sub-address delimiter samples ("user+tag"). + 'settings/ComposingSettings.tsx': ['user tag'], + 'settings/ContactsSettings.tsx': [], + 'settings/ContentSendersSettings.tsx': [], + 'settings/DownloadsSettings.tsx': [], + 'settings/FilesSettings.tsx': [], + 'settings/FilterSettings.tsx': [], + 'settings/FolderSettings.tsx': [], + 'settings/IdentitySettings.tsx': [ + // Example signatures in the placeholders. '-- Jane Doe Bulwark Mail', '

Jane Doe
Bulwark Mail

', ], - 'FilterSettings.tsx': [], - 'SidebarAppsSettings.tsx': ['https://example.com'], - 'TemplateSettings.tsx': ['JSON', '{"templates": [...]}'], + // The raw keyword prefix shown next to a tag id. + 'settings/KeywordSettings.tsx': ['$label:'], + 'settings/LanguageSettings.tsx': [], + 'settings/LayoutSettings.tsx': [], + 'settings/NotificationSettings.tsx': [], + 'settings/PluginsSettings.tsx': [], + 'settings/ReadingSettings.tsx': [], + 'settings/SidebarAppsSettings.tsx': ['https://example.com'], + 'settings/SmimeSettings.tsx': [], + 'settings/TemplateSettings.tsx': ['JSON', '{"templates": [...]}'], + 'settings/ThemesSettings.tsx': [], + 'settings/UpdatesSettings.tsx': [], + 'settings/VacationSettings.tsx': ['YYYY-MM-DD HH:MM'], + 'settings/settings-section.tsx': [], + 'filters/FilterRuleModal.tsx': [], + 'filters/SieveEditorSheet.tsx': [], }; describe('translated settings panes', () => { for (const [file, allow] of Object.entries(TRANSLATED_PANES)) { it(`${file} has no hard-coded English text`, () => { - const literals = findUntranslatedLiterals(join(__dirname, '..', 'settings', file), allow); + const literals = findUntranslatedLiterals(join(__dirname, '..', file), allow); expect(literals).toEqual([]); }); } diff --git a/src/components/filters/FilterRuleModal.tsx b/src/components/filters/FilterRuleModal.tsx index edcdaf9a..7128ac31 100644 --- a/src/components/filters/FilterRuleModal.tsx +++ b/src/components/filters/FilterRuleModal.tsx @@ -229,7 +229,13 @@ export function FilterRuleModal({ visible, rule, mailboxes, onSave, onClose }: F - + @@ -295,6 +301,8 @@ export function FilterRuleModal({ visible, rule, mailboxes, onSave, onClose }: F disabled={conditions.length <= 1} hitSlop={8} style={[styles.removeBtn, conditions.length <= 1 && styles.removeBtnDisabled]} + accessibilityRole="button" + accessibilityLabel={t('settings.filters.remove_condition', 'Remove condition')} > @@ -361,6 +369,8 @@ export function FilterRuleModal({ visible, rule, mailboxes, onSave, onClose }: F disabled={actions.length <= 1} hitSlop={8} style={[styles.removeBtn, actions.length <= 1 && styles.removeBtnDisabled]} + accessibilityRole="button" + accessibilityLabel={t('settings.filters.remove_action', 'Remove action')} > diff --git a/src/components/filters/SieveEditorSheet.tsx b/src/components/filters/SieveEditorSheet.tsx index b9252532..51a0ebd4 100644 --- a/src/components/filters/SieveEditorSheet.tsx +++ b/src/components/filters/SieveEditorSheet.tsx @@ -65,7 +65,13 @@ export function SieveEditorSheet({ visible, content, onSave, onClose, onValidate - + {t('settings.filters.sieve_editor.title', 'Sieve Script Editor')} @@ -104,7 +110,9 @@ export function SieveEditorSheet({ visible, content, onSave, onClose, onValidate /> - {lineCount} {lineCount === 1 ? 'line' : 'lines'} + + {t('settings.filters.sieve_editor.line_count', '{count, plural, one {# line} other {# lines}}', { count: lineCount })} + {validationResult && ( diff --git a/src/components/settings/AboutDataSettings.tsx b/src/components/settings/AboutDataSettings.tsx index 1bbd6a71..24f35691 100644 --- a/src/components/settings/AboutDataSettings.tsx +++ b/src/components/settings/AboutDataSettings.tsx @@ -268,7 +268,7 @@ export function AboutDataSettings() { : sync.phase === 'scanning' ? t('settings.offline.scanning', 'Scanning recent mail…') : sync.phase === 'error' - ? t('settings.offline.last_sync_failed', 'Last sync failed: {message}', { message: sync.message ?? 'unknown error' }) + ? t('settings.offline.last_sync_failed', 'Last sync failed: {message}', { message: sync.message ?? t('identities.validation_errors.unknown_error', 'Unknown error') }) : t('settings.offline.last_sync', 'Last sync {when}', { when: formatRelativeTime(sync.finishedAt) })} {queuedChanges > 0 && ( diff --git a/src/components/settings/ComposingSettings.tsx b/src/components/settings/ComposingSettings.tsx index 540ffd98..8407ae29 100644 --- a/src/components/settings/ComposingSettings.tsx +++ b/src/components/settings/ComposingSettings.tsx @@ -205,7 +205,12 @@ export function ComposingSettings() { {attachmentReminderKeywords.map((kw) => ( {kw} - removeKeyword(kw)} hitSlop={6}> + removeKeyword(kw)} + hitSlop={6} + accessibilityRole="button" + accessibilityLabel={t('settings.email_behavior.attachment_reminder.remove', 'Remove')} + > diff --git a/src/components/settings/FilterSettings.tsx b/src/components/settings/FilterSettings.tsx index 9f972b8b..9e1339c8 100644 --- a/src/components/settings/FilterSettings.tsx +++ b/src/components/settings/FilterSettings.tsx @@ -408,6 +408,8 @@ export function FilterSettings({ onOpenVacation }: FilterSettingsProps = {}) { disabled={editIdx <= 0} hitSlop={6} style={[styles.reorderBtn, editIdx <= 0 && styles.reorderBtnDisabled]} + accessibilityRole="button" + accessibilityLabel={t('settings.appearance.message_list_order.move_up', 'Move up')} > @@ -416,12 +418,20 @@ export function FilterSettings({ onOpenVacation }: FilterSettingsProps = {}) { disabled={editIdx === editableIds.length - 1} hitSlop={6} style={[styles.reorderBtn, editIdx === editableIds.length - 1 && styles.reorderBtnDisabled]} + accessibilityRole="button" + accessibilityLabel={t('settings.appearance.message_list_order.move_down', 'Move down')} > - handleDelete(rule)} hitSlop={6} style={styles.deleteBtn}> + handleDelete(rule)} + hitSlop={6} + style={styles.deleteBtn} + accessibilityRole="button" + accessibilityLabel={t('settings.filters.delete_rule', 'Delete Rule')} + > diff --git a/src/components/settings/FolderSettings.tsx b/src/components/settings/FolderSettings.tsx index 1679ffd4..5951cd22 100644 --- a/src/components/settings/FolderSettings.tsx +++ b/src/components/settings/FolderSettings.tsx @@ -306,7 +306,12 @@ export function FolderSettings() { ? t('settings.folders.edit_folder', `Edit "${editor.mailbox.name}"`, { name: editor.mailbox.name }) : t('settings.folders.new_folder', 'New folder')} - + diff --git a/src/components/settings/ReadingSettings.tsx b/src/components/settings/ReadingSettings.tsx index 0be8f6bc..ef39e666 100644 --- a/src/components/settings/ReadingSettings.tsx +++ b/src/components/settings/ReadingSettings.tsx @@ -232,6 +232,8 @@ export function ReadingSettings() { ) } hitSlop={6} + accessibilityRole="button" + accessibilityLabel={t('settings.email_behavior.attachment_reminder.remove', 'Remove')} >