Give the media upload preview a descriptive VoiceOver title

The QLPreviewItem used the media's file name as its title, which VoiceOver
read as cryptic alt text. Provide a descriptive "Photo preview" / "Video
preview" title based on the media type instead.

Addresses #5240, #5241.
This commit is contained in:
Mauro Romito
2026-06-19 12:52:19 +02:00
committed by Mauro
parent 17da5ff3b7
commit 8a4e4d1903
4 changed files with 22 additions and 1 deletions
@@ -21,6 +21,7 @@
"a11y_page_n" = "Page %1$d";
"a11y_pause" = "Pause";
"a11y_paused_voice_message" = "Voice message, duration: %1$@, current position: %2$@";
"a11y_photo_preview" = "Photo preview";
"a11y_pin_field" = "PIN field";
"a11y_pinned_location" = "Pinned location";
"a11y_play" = "Play";
@@ -48,6 +49,7 @@
"a11y_start_voice_call" = "Start a voice call";
"a11y_tombstoned_room" = "Tombstoned room";
"a11y_user_menu" = "User menu";
"a11y_video_preview" = "Video preview";
"a11y_view_avatar" = "View avatar";
"a11y_view_details" = "View details";
"a11y_voice_message" = "Voice message, duration: %1$@";
@@ -21,6 +21,7 @@
"a11y_page_n" = "Page %1$d";
"a11y_pause" = "Pause";
"a11y_paused_voice_message" = "Voice message, duration: %1$@, current position: %2$@";
"a11y_photo_preview" = "Photo preview";
"a11y_pin_field" = "PIN field";
"a11y_pinned_location" = "Pinned location";
"a11y_play" = "Play";
@@ -48,6 +49,7 @@
"a11y_start_voice_call" = "Start a voice call";
"a11y_tombstoned_room" = "Tombstoned room";
"a11y_user_menu" = "User menu";
"a11y_video_preview" = "Video preview";
"a11y_view_avatar" = "View avatar";
"a11y_view_details" = "View details";
"a11y_voice_message" = "Voice message, duration: %1$@";
+8
View File
@@ -70,6 +70,8 @@ internal nonisolated enum L10n {
internal static func a11yPausedVoiceMessage(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "a11y_paused_voice_message", String(describing: p1), String(describing: p2))
}
/// Photo preview
internal static var a11yPhotoPreview: String { return L10n.tr("Localizable", "a11y_photo_preview") }
/// PIN field
internal static var a11yPinField: String { return L10n.tr("Localizable", "a11y_pin_field") }
/// Pinned location
@@ -144,6 +146,8 @@ internal nonisolated enum L10n {
internal static var a11yTombstonedRoom: String { return L10n.tr("Localizable", "a11y_tombstoned_room") }
/// User menu
internal static var a11yUserMenu: String { return L10n.tr("Localizable", "a11y_user_menu") }
/// Video preview
internal static var a11yVideoPreview: String { return L10n.tr("Localizable", "a11y_video_preview") }
/// View avatar
internal static var a11yViewAvatar: String { return L10n.tr("Localizable", "a11y_view_avatar") }
/// View details
@@ -3553,6 +3557,10 @@ internal nonisolated enum L10n {
}
/// On the map
internal static var screenStaticLocationSheetTitle: String { return L10n.tr("Localizable", "screen_static_location_sheet_title") }
/// Plural format key: "%#@COUNT@"
internal static func screenTimelineActiveCallExtraJoinedCount(_ p1: Int) -> String {
return L10n.tr("Localizable", "screen_timeline_active_call_extra_joined_count", p1)
}
/// Message not sent because %1$@’s verified digital identity was reset.
internal static func screenTimelineItemMenuSendFailureChangedIdentity(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_timeline_item_menu_send_failure_changed_identity", String(describing: p1))
@@ -264,7 +264,16 @@ private struct PreviewView: UIViewControllerRepresentable {
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
PreviewItem(previewItemURL: view.mediaURLs[index], previewItemTitle: view.title)
let url = view.mediaURLs[index]
// Use a descriptive title instead of the file name so VoiceOver doesn't read a cryptic name.
return PreviewItem(previewItemURL: url, previewItemTitle: previewItemTitle(for: url))
}
private func previewItemTitle(for url: URL) -> String? {
guard let type = UTType(filenameExtension: url.pathExtension) else { return view.title }
if type.conforms(to: .image) { return L10n.a11yPhotoPreview }
if type.conforms(to: .movie) || type.conforms(to: .video) { return L10n.a11yVideoPreview }
return view.title
}
// MARK: - QLPreviewControllerDelegate