From 031a3ce2e933f87bdbe7feb338bea19b53c88ddc Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 23:03:48 +0000 Subject: [PATCH] polish(#3220): esc() download label + scrub orphaned media_open i18n keys Opus iter2 SHOULD-FIX (both non-blocking, applied for cleanliness): - esc() the media_download title/aria-label so a future translator's quote can't break out of the attribute (defense-in-depth). - Remove the now-dead media_open key from all 11 locales + the ui.js fallback dict (the Open button was dropped in the clean-image redesign). --- static/i18n.js | 11 ----------- static/ui.js | 5 +++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/static/i18n.js b/static/i18n.js index 12860a37..04ff707d 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -1297,7 +1297,6 @@ const LOCALES = { media_audio_label: 'Audio', media_svg_label: 'Diagram', media_video_label: 'Video', - media_open: 'Open', media_download: 'Download', csv_loading: 'Loading CSV', csv_too_large: 'CSV file too large for inline rendering', @@ -2616,7 +2615,6 @@ const LOCALES = { media_audio_label: 'Audio', media_svg_label: 'Diagramma', media_video_label: 'Video', - media_open: 'Apri', media_download: 'Scarica', csv_loading: 'Caricamento CSV', csv_too_large: 'File CSV troppo grande per il rendering inline', @@ -3940,7 +3938,6 @@ const LOCALES = { media_audio_label: '音声', media_svg_label: '図', media_video_label: '動画', - media_open: '開く', media_download: 'ダウンロード', csv_loading: 'CSV を読み込み中', csv_too_large: 'CSV ファイルが大きすぎてインライン表示できません', @@ -5136,7 +5133,6 @@ const LOCALES = { media_audio_label: 'Аудио', media_svg_label: 'Диаграмма', media_video_label: 'Видео', - media_open: 'Открыть', media_download: 'Скачать', csv_loading: 'Загрузка CSV', csv_too_large: 'CSV-файл слишком большой для встроенного отображения', @@ -6380,7 +6376,6 @@ const LOCALES = { media_audio_label: 'Audio', media_svg_label: 'Diagrama', media_video_label: 'Vídeo', - media_open: 'Abrir', media_download: 'Descargar', csv_loading: 'Cargando CSV', csv_too_large: 'Archivo CSV demasiado grande para mostrar en línea', @@ -7638,7 +7633,6 @@ const LOCALES = { media_audio_label: 'Audio', media_svg_label: 'Diagramm', media_video_label: 'Video', - media_open: 'Öffnen', media_download: 'Herunterladen', csv_loading: 'CSV wird geladen', csv_too_large: 'CSV-Datei zu groß für Inline-Anzeige', @@ -8901,7 +8895,6 @@ const LOCALES = { media_audio_label: '音频', media_svg_label: '图表', media_video_label: '视频', - media_open: '打开', media_download: '下载', csv_loading: '加载 CSV', csv_too_large: 'CSV 文件过大,无法内联渲染', @@ -10217,7 +10210,6 @@ const LOCALES = { media_audio_label: '音訊', media_svg_label: '圖表', media_video_label: '影片', - media_open: '開啟', media_download: '下載', csv_loading: '載入 CSV', csv_too_large: 'CSV 檔案過大,無法內嵌渲染', @@ -12673,7 +12665,6 @@ const LOCALES = { media_audio_label: '오디오', media_svg_label: '다이어그램', media_video_label: '비디오', - media_open: '열기', media_download: '다운로드', csv_loading: 'CSV 로딩 중', csv_too_large: 'CSV 파일이 너무 커서 인라인 렌더링할 수 없습니다', @@ -13895,7 +13886,6 @@ const LOCALES = { media_audio_label: 'Audio', media_svg_label: 'Diagramme', media_video_label: 'Vidéo', - media_open: 'Ouvrir', media_download: 'Télécharger', csv_loading: 'Chargement du fichier CSV', csv_too_large: 'Fichier CSV trop volumineux pour le rendu en ligne', @@ -15245,7 +15235,6 @@ const LOCALES = { media_audio_label: 'Ses', media_svg_label: 'Diyagram', media_video_label: 'Video', - media_open: 'Aç', media_download: 'İndir', csv_loading: 'CSV yükleniyor', csv_too_large: 'CSV dosyası satır içi oluşturma için çok büyük', diff --git a/static/ui.js b/static/ui.js index a8b6ff1c..0b4b260d 100644 --- a/static/ui.js +++ b/static/ui.js @@ -3507,14 +3507,15 @@ function renderMd(raw){ const localArtifactCard=(src,name)=>{ const safeSrc=esc(src); const safeName=esc(name||'image'); - const tt=(typeof t==='function')?t:(key=>({media_open:'Open',media_download:'Download'}[key]||key)); + const tt=(typeof t==='function')?t:(key=>({media_download:'Download'}[key]||key)); // Clean inline image (keeps the existing .msg-media-img lightbox-on-click // behavior) with a hover/focus-revealed Download action overlaid top-right, // matching the ChatGPT/Claude/Gemini pattern. The image stays the hero — // no permanent card chrome. Download is the one affordance the lightbox // (zoom-on-click) doesn't already provide. + const dlLabel=esc(tt('media_download')); const dlSvg=''; - return `${safeName}${dlSvg}`; + return `${safeName}${dlSvg}`; }; if(/^file:\/\//i.test(ref)){ try{