diff --git a/lib/locales/cs.js b/lib/locales/cs.js index 4d715482e54..19b3c99562b 100644 --- a/lib/locales/cs.js +++ b/lib/locales/cs.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Obnovit nastavení pohledu', // components/modebar/buttons.js:583 'Reset views': 'Obnovit nastavení pohledů', // components/modebar/buttons.js:529 'Show closest data on hover': 'Zobrazit najbližší hodnotu při najetí myší', // components/modebar/buttons.js:157 - 'Snapshot succeeded': 'Snímek vytvořen', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': 'Omlouváme se, ale došlo k chybě stahování snímku!', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': 'Vytváří se snímek - může zabrat pár vteřin', // components/modebar/buttons.js:57 + 'Image download succeeded': 'Snímek vytvořen', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': 'Omlouváme se, ale došlo k chybě stahování snímku!', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': 'Vytváří se snímek - může zabrat pár vteřin', // components/modebar/buttons.js:57 'Zoom': 'Zvětšení', // components/modebar/buttons.js:85 'Zoom in': 'Zvětšit', // components/modebar/buttons.js:121 'Zoom out': 'Zmenšit', // components/modebar/buttons.js:130 diff --git a/lib/locales/cy.js b/lib/locales/cy.js index f8f792eac4d..475a28134f2 100644 --- a/lib/locales/cy.js +++ b/lib/locales/cy.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Ailosodwch y golwg', // components / modebar / buttons.js: 592 'Reset views': 'Ailosod olygfeydd', // components / modebar / buttons.js: 540 'Show closest data on hover': 'Dangos y data agosaf wrth hofran', // components / modebar / buttons.js: 168 - 'Snapshot succeeded': 'Llwyddodd y Ciplun', // components / modebar / buttons.js: 77 - 'Sorry, there was a problem downloading your snapshot!': 'Mae\'n ddrwg gennym, roedd problem wrth lawrlwytho eich ciplun!', // components / modebar / buttons.js: 80 - 'Taking snapshot - this may take a few seconds': 'Tynnu ciplun - gallai hyn gymryd ychydig o eiliadau', // components / modebar / buttons.js: 62 + 'Image download succeeded': 'Llwyddodd y Ciplun', // components / modebar / buttons.js: 77 + 'Sorry, there was a problem downloading your image!': 'Mae\'n ddrwg gennym, roedd problem wrth lawrlwytho eich ciplun!', // components / modebar / buttons.js: 80 + 'Capturing image - this may take a few seconds': 'Tynnu ciplun - gallai hyn gymryd ychydig o eiliadau', // components / modebar / buttons.js: 62 'Toggle Spike Lines': 'Toglo llinellau pigog', // components / modebar / buttons.js: 559 'Toggle show closest data on hover': 'Toglo dangos y data agosaf wrth hofran', // components / modebar / buttons.js: 364 'Turntable rotation': 'Cylchdroi trofwrdd', // components / modebar / buttons.js: 296 diff --git a/lib/locales/de.js b/lib/locales/de.js index 2be03de0398..1e066990a46 100644 --- a/lib/locales/de.js +++ b/lib/locales/de.js @@ -31,9 +31,9 @@ module.exports = { 'Reset view': 'Ansicht zurücksetzen', // components/modebar/buttons.js:583 'Reset views': 'Ansichten zurücksetzen', // components/modebar/buttons.js:529 'Show closest data on hover': 'Zeige näheste Daten beim Überfahren', // components/modebar/buttons.js:157 - 'Snapshot succeeded': 'Snapshot erfolgreich', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': 'Es gab ein Problem beim Herunterladen des Snapshots', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': 'Erstelle einen Snapshot - dies kann einige Sekunden dauern', // components/modebar/buttons.js:57 + 'Image download succeeded': 'Snapshot erfolgreich', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': 'Es gab ein Problem beim Herunterladen des Snapshots', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': 'Erstelle einen Snapshot - dies kann einige Sekunden dauern', // components/modebar/buttons.js:57 'Zoom': 'Zoom', // components/modebar/buttons.js:85 'Zoom in': 'Hineinzoomen', // components/modebar/buttons.js:121 'Zoom out': 'Herauszoomen', // components/modebar/buttons.js:130 diff --git a/lib/locales/es.js b/lib/locales/es.js index 15a8a41369b..9e27a55c2e3 100644 --- a/lib/locales/es.js +++ b/lib/locales/es.js @@ -33,9 +33,9 @@ module.exports = { 'Reset view': 'Restaurar vista', // components/modebar/buttons.js:582 'Reset views': 'Restaurar vistas', // components/modebar/buttons.js:528 'Show closest data on hover': 'Mostrar el dato más cercano al pasar por encima', // components/modebar/buttons.js:157 - 'Snapshot succeeded': 'La captura de la instantánea finalizó correctamente', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': '¡La descarga de la instantánea falló!', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': 'Capturando una instantánea - podría tardar unos segundos', // components/modebar/buttons.js:57 + 'Image download succeeded': 'La descarga de la imagen finalizó correctamente', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': '¡La descarga de la imagen falló!', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': 'Capturando una imagen - podría tardar unos segundos', // components/modebar/buttons.js:57 'Toggle Spike Lines': 'Mostrar/Ocultar Guías', // components/modebar/buttons.js:547 'Toggle show closest data on hover': 'Activar/Desactivar mostrar el dato más cercano al pasar por encima', // components/modebar/buttons.js:352 'Turntable rotation': 'Rotación plana', // components/modebar/buttons.js:288 diff --git a/lib/locales/fi.js b/lib/locales/fi.js index 82ae56a9ada..5b7cb940cb4 100644 --- a/lib/locales/fi.js +++ b/lib/locales/fi.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Palauta näkymän oletusasetukset', 'Reset views': 'Palauta näkymien oletusasetukset', 'Show closest data on hover': 'Näytä kursoria lähin data', - 'Snapshot succeeded': 'Tilannekuvan ottaminen onnistui', - 'Sorry, there was a problem downloading your snapshot!': 'Pahoittelut, tilannekuvan lataaminen epäonnistui!', - 'Taking snapshot - this may take a few seconds': 'Otetaan tilannekuvaa - odota hetki', + 'Image download succeeded': 'Tilannekuvan ottaminen onnistui', + 'Sorry, there was a problem downloading your image!': 'Pahoittelut, tilannekuvan lataaminen epäonnistui!', + 'Capturing image - this may take a few seconds': 'Otetaan tilannekuvaa - odota hetki', 'Toggle Spike Lines': 'Näytä huiput', 'Toggle show closest data on hover': 'Näytä kursoria lähin data', 'Turntable rotation': 'Tasokierto', diff --git a/lib/locales/fr.js b/lib/locales/fr.js index 4e4653f8eee..dc6e27f2d5c 100644 --- a/lib/locales/fr.js +++ b/lib/locales/fr.js @@ -33,9 +33,9 @@ module.exports = { 'Reset view': 'Réinitialiser', 'Reset views': 'Réinitialiser', 'Show closest data on hover': 'Données les plus proches en survol', - 'Snapshot succeeded': 'Conversion réussie', - 'Sorry, there was a problem downloading your snapshot!': 'Désolé, un problème est survenu lors du téléchargement de votre graphique', - 'Taking snapshot - this may take a few seconds': 'Conversion en cours, ceci peut prendre quelques secondes', + 'Image download succeeded': 'Téléchargement du graphique réussi', + 'Sorry, there was a problem downloading your image!': 'Désolé, un problème est survenu lors du téléchargement de votre graphique', + 'Capturing image - this may take a few seconds': 'Conversion en cours, ceci peut prendre quelques secondes', 'Zoom': 'Zoom', 'Zoom in': 'Zoom intérieur', 'Zoom out': 'Zoom extérieur', diff --git a/lib/locales/hr.js b/lib/locales/hr.js index 061acf8f394..e73d7511fa3 100644 --- a/lib/locales/hr.js +++ b/lib/locales/hr.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Resetirajte pogled', 'Reset views': 'Resetirajte poglede', 'Show closest data on hover': 'Prikaži najbliže podatke pri zadržavanju mišem', - 'Snapshot succeeded': 'Preuzimanje slike uspješno', - 'Sorry, there was a problem downloading your snapshot!': 'Pojavila se greška prilikom preuzimanja slike!', - 'Taking snapshot - this may take a few seconds': 'Preuzimanje slike - ovo može potrajati nekoliko sekundi', + 'Image download succeeded': 'Preuzimanje slike uspješno', + 'Sorry, there was a problem downloading your image!': 'Pojavila se greška prilikom preuzimanja slike!', + 'Capturing image - this may take a few seconds': 'Preuzimanje slike - ovo može potrajati nekoliko sekundi', 'Toggle Spike Lines': 'Postavljanje pomoćnih linija', 'Toggle show closest data on hover': 'Postavljanje prikaza najbližih podataka pri zadržavanju mišem', 'Turntable rotation': 'Turntable rotiranje', diff --git a/lib/locales/it.js b/lib/locales/it.js index 7118f71d620..41b2877fd2d 100644 --- a/lib/locales/it.js +++ b/lib/locales/it.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Reimposta la vista', // components/modebar/buttons.js:583 'Reset views': 'Reimposta le viste', // components/modebar/buttons.js:529 'Show closest data on hover': 'Mostra i dati più vicini al passaggio del mouse', // components/modebar/buttons.js:157 - 'Snapshot succeeded': 'Screenshot creato con successo', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': 'Si è verificato un errore durante la creazione dello screenshot', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': 'Creazione screenshot - potrebbe richiedere qualche secondo', // components/modebar/buttons.js:57 + 'Image download succeeded': 'Screenshot creato con successo', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': 'Si è verificato un errore durante la creazione dello screenshot', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': 'Creazione screenshot - potrebbe richiedere qualche secondo', // components/modebar/buttons.js:57 'Zoom': 'Zoom', // components/modebar/buttons.js:85 'Zoom in': 'Ingrandisci', // components/modebar/buttons.js:121 'Zoom out': 'Riduci', // components/modebar/buttons.js:130 diff --git a/lib/locales/ja.js b/lib/locales/ja.js index e227cf3c139..fb201027411 100644 --- a/lib/locales/ja.js +++ b/lib/locales/ja.js @@ -31,9 +31,9 @@ module.exports = { 'Reset view': 'ビューをリセット', // components/modebar/buttons.js:583 'Reset views': 'ビューをリセット', // components/modebar/buttons.js:529 'Show closest data on hover': 'ホバー時に一番近いデータを表示', // components/modebar/buttons.js:157 - 'Snapshot succeeded': 'スナップショットに成功', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': 'すみません、スナップショットダウンロードでエラーです!', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': 'スナップショットを撮影', // components/modebar/buttons.js:57 + 'Image download succeeded': 'スナップショットに成功', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': 'すみません、スナップショットダウンロードでエラーです!', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': 'スナップショットを撮影', // components/modebar/buttons.js:57 'Zoom': 'ズーム', // components/modebar/buttons.js:85 'Zoom in': '拡大', // components/modebar/buttons.js:121 'Zoom out': '縮小', // components/modebar/buttons.js:130 diff --git a/lib/locales/ko.js b/lib/locales/ko.js index 73d7d34bf2f..920acea272e 100644 --- a/lib/locales/ko.js +++ b/lib/locales/ko.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'view 초기화', 'Reset views': 'views 초기화', 'Show closest data on hover': '마우스를 올리면 근접한 데이터를 보이기', - 'Snapshot succeeded': 'Snapshot 성공', - 'Sorry, there was a problem downloading your snapshot!': '죄송합니다, snapshot을 다운로드 중 문제가 발생했습니다!', - 'Taking snapshot - this may take a few seconds': 'snapshot 찍기 - 수 초가 걸릴 수 있습니다', + 'Image download succeeded': 'Snapshot 성공', + 'Sorry, there was a problem downloading your image!': '죄송합니다, snapshot을 다운로드 중 문제가 발생했습니다!', + 'Capturing image - this may take a few seconds': 'snapshot 찍기 - 수 초가 걸릴 수 있습니다', 'Toggle Spike Lines': '스위치로 Lines을 고정합니다', 'Toggle show closest data on hover': '스위치로 마우스를 올렸을 때 가장 가까운 데이터를 보여줍니다', 'Turntable rotation': 'Turntable 회전', diff --git a/lib/locales/no.js b/lib/locales/no.js index 7956d7c2352..801b48822cb 100644 --- a/lib/locales/no.js +++ b/lib/locales/no.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Nullstille visning', // components/modebar/buttons.js:512 'Reset views': 'Nullstille visninger', // components/modebar/buttons.js:550 'Show closest data on hover': 'Vis nærmeste verdi når musepekeren holdes over', // components/modebar/buttons.js:166 - 'Snapshot succeeded': 'Bilde Laget', // components/modebar/buttons.js:75 - 'Sorry, there was a problem downloading your snapshot!': 'Beklager, noe gikk galt under nedlasting av bildet', // components/modebar/buttons.js:78 - 'Taking snapshot - this may take a few seconds': 'Oppretter bilde - dette kan ta noen sekunder', // components/modebar/buttons.js:60 + 'Image download succeeded': 'Bilde Laget', // components/modebar/buttons.js:75 + 'Sorry, there was a problem downloading your image!': 'Beklager, noe gikk galt under nedlasting av bildet', // components/modebar/buttons.js:78 + 'Capturing image - this may take a few seconds': 'Oppretter bilde - dette kan ta noen sekunder', // components/modebar/buttons.js:60 'Toggle Spike Lines': 'Aktiver / deaktiver topplinjer', // components/modebar/buttons.js:569 'Toggle show closest data on hover': 'Aktiver / deaktiver nærmeste verdi når musepekeren holdes over', // components/modebar/buttons.js:361 'Turntable rotation': 'Flat rotation', // components/modebar/buttons.js:290 diff --git a/lib/locales/pt-br.js b/lib/locales/pt-br.js index 9a85104b062..9a6471222e3 100644 --- a/lib/locales/pt-br.js +++ b/lib/locales/pt-br.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Restaurar visão', 'Reset views': 'Restaurar visões', 'Show closest data on hover': 'Exibir dado mais próximo ao pairar', - 'Snapshot succeeded': 'Captura instantânea completa', - 'Sorry, there was a problem downloading your snapshot!': 'Desculpe, houve um problema no download de sua captura instantânea!', - 'Taking snapshot - this may take a few seconds': 'Efetuando captura instantânea - isso pode levar alguns instantes', + 'Image download succeeded': 'Download da imagem concluído com sucesso', + 'Sorry, there was a problem downloading your image!': 'Desculpe, houve um problema no download de sua imagem!', + 'Capturing image - this may take a few seconds': 'Efetuando captura da imagem - isso pode levar alguns instantes', 'Toggle Spike Lines': 'Habilitar/desabilitar triangulação de linhas', 'Toggle show closest data on hover': 'Habilitar/desabilitar exibição de dado mais próximo ao pairar', 'Turntable rotation': 'Rotação de mesa', diff --git a/lib/locales/pt-pt.js b/lib/locales/pt-pt.js index 042766ed0e1..50cf6feb4a8 100644 --- a/lib/locales/pt-pt.js +++ b/lib/locales/pt-pt.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Restaurar vista', 'Reset views': 'Restaurar vistas', 'Show closest data on hover': 'Exibir dado mais próximo ao pairar', - 'Snapshot succeeded': 'Captura instantânea com sucesso', - 'Sorry, there was a problem downloading your snapshot!': 'Desculpe, houve um problema no download de sua captura instantânea!', - 'Taking snapshot - this may take a few seconds': 'Efetuando captura instantânea - isso pode demorar alguns segundos', + 'Image download succeeded': 'Download da imagem concluído com sucesso', + 'Sorry, there was a problem downloading your image!': 'Desculpe, houve um problema no download da sua imagem!', + 'Capturing image - this may take a few seconds': 'Efetuando captura da imagem - isso pode demorar alguns segundos', 'Toggle Spike Lines': 'Habilitar/desabilitar triangulação de linhas', 'Toggle show closest data on hover': 'Habilitar/desabilitar exibição de dado mais próximo ao pairar', 'Turntable rotation': 'Rodar', diff --git a/lib/locales/ro.js b/lib/locales/ro.js index e77bc2fdd15..3937874a11c 100644 --- a/lib/locales/ro.js +++ b/lib/locales/ro.js @@ -37,9 +37,9 @@ module.exports = { 'Reset view': 'Resetează vizualizarea', 'Reset views': 'Resetează vizualizările', 'Show closest data on hover': 'Afișează cele mai apropiate date la trecerea cu mouse-ul', - 'Snapshot succeeded': 'Crearea capturii de ecran a reușit', - 'Sorry, there was a problem downloading your snapshot!': 'Ne pare rău, a apărut o eroare la descărcarea capturii de ecran!', - 'Taking snapshot - this may take a few seconds': 'Se crează captura de ecran - poate dura câteva secunde', + 'Image download succeeded': 'Crearea capturii de ecran a reușit', + 'Sorry, there was a problem downloading your image!': 'Ne pare rău, a apărut o eroare la descărcarea capturii de ecran!', + 'Capturing image - this may take a few seconds': 'Se crează captura de ecran - poate dura câteva secunde', 'Toggle Spike Lines': 'Comutarea afișării liniilor de vârf', 'Toggle show closest data on hover': 'Comutarea afișării celor mai apropiate date', 'Turntable rotation': 'Rotație pe axă', diff --git a/lib/locales/ru.js b/lib/locales/ru.js index 00fff8a4c49..6350d49066d 100644 --- a/lib/locales/ru.js +++ b/lib/locales/ru.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Сбросить отображение к значениям по умолчанию', 'Reset views': 'Сбросить отображения к значениям по умолчанию', 'Show closest data on hover': 'При наведении показывать ближайшие данные', - 'Snapshot succeeded': 'Снимок успешно создан', - 'Sorry, there was a problem downloading your snapshot!': 'К сожалению, возникла проблема при сохранении снимка', - 'Taking snapshot - this may take a few seconds': 'Делается снимок - это может занять несколько секунд', + 'Image download succeeded': 'Снимок успешно создан', + 'Sorry, there was a problem downloading your image!': 'К сожалению, возникла проблема при сохранении снимка', + 'Capturing image - this may take a few seconds': 'Делается снимок - это может занять несколько секунд', 'Toggle Spike Lines': 'Включить/выключить отображение линий проекций точек', 'Toggle show closest data on hover': 'Включить/выключить показ ближайших данных при наведении', 'Turntable rotation': 'Вращение на поворотном столе', diff --git a/lib/locales/si.js b/lib/locales/si.js index 1289bef58a9..f81667e4f6d 100644 --- a/lib/locales/si.js +++ b/lib/locales/si.js @@ -38,9 +38,9 @@ module.exports = { 'Reset view': 'දැක්ම යළි සකසන්න', // components/modebar/buttons.js:599 'Reset views': 'දැක්ම් යළි සකසන්න', // components/modebar/buttons.js:637 'Show closest data on hover': 'සුනංගු කිරීමේදී ආසන්නම දත්ත පෙන්වන්න', // components/modebar/buttons.js:228 - 'Snapshot succeeded': 'ඡායාරූපය සාර්ථකයි', // components/modebar/buttons.js:67 - 'Sorry, there was a problem downloading your snapshot!': 'සමාවන්න, ඔබගේ ඡායාරූපය බාගැනීමේ ගැටලුවක් ඇත!', // components/modebar/buttons.js:70 - 'Taking snapshot - this may take a few seconds': 'ඡායාරූපය ගැනෙමින් - මෙයට තත්. කිහිපයක් ගතවිය හැකිය', // components/modebar/buttons.js:52 + 'Image download succeeded': 'ඡායාරූපය සාර්ථකයි', // components/modebar/buttons.js:67 + 'Sorry, there was a problem downloading your image!': 'සමාවන්න, ඔබගේ ඡායාරූපය බාගැනීමේ ගැටලුවක් ඇත!', // components/modebar/buttons.js:70 + 'Capturing image - this may take a few seconds': 'ඡායාරූපය ගැනෙමින් - මෙයට තත්. කිහිපයක් ගතවිය හැකිය', // components/modebar/buttons.js:52 'Toggle Spike Lines': 'Toggle Spike Lines', // components/modebar/buttons.js:656 'Toggle show closest data on hover': 'Toggle show closest data on hover', // components/modebar/buttons.js:440 'Turntable rotation': 'බමර කරකැවීම', // components/modebar/buttons.js:351 diff --git a/lib/locales/sk.js b/lib/locales/sk.js index 52360965110..ef5e20c4c16 100644 --- a/lib/locales/sk.js +++ b/lib/locales/sk.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Obnoviť nastavenie pohľadu', // components/modebar/buttons.js:583 'Reset views': 'Obnoviť nastavenie pohľadov', // components/modebar/buttons.js:529 'Show closest data on hover': 'Zobraziť najbližšiu hodnotu při prejdení myšou', // components/modebar/buttons.js:157 - 'Snapshot succeeded': 'Obrázok vytvorený', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': 'Ospravedlňujeme sa, došlo k chybe pri sťahovaní obrázka!', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': 'Snímanie - môže trvať niekoľko sekúnd', // components/modebar/buttons.js:57 + 'Image download succeeded': 'Obrázok vytvorený', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': 'Ospravedlňujeme sa, došlo k chybe pri sťahovaní obrázka!', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': 'Snímanie - môže trvať niekoľko sekúnd', // components/modebar/buttons.js:57 'Zoom': 'Zväčšenie', // components/modebar/buttons.js:85 'Zoom in': 'Zväčšiť', // components/modebar/buttons.js:121 'Zoom out': 'Zmenšiť', // components/modebar/buttons.js:130 diff --git a/lib/locales/sv.js b/lib/locales/sv.js index f3b215a4996..4d9c355f92a 100644 --- a/lib/locales/sv.js +++ b/lib/locales/sv.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Återställ vy', // components/modebar/buttons.js:512 'Reset views': 'Återställ vyer', // components/modebar/buttons.js:550 'Show closest data on hover': 'Visa närmaste värde när muspekaren hålls över', // components/modebar/buttons.js:166 - 'Snapshot succeeded': 'Bild skapad', // components/modebar/buttons.js:75 - 'Sorry, there was a problem downloading your snapshot!': 'Tyvärr gick något fel vid nedladdning av bild', // components/modebar/buttons.js:78 - 'Taking snapshot - this may take a few seconds': 'Skapar bild - detta kan ta några sekunder', // components/modebar/buttons.js:60 + 'Image download succeeded': 'Bild skapad', // components/modebar/buttons.js:75 + 'Sorry, there was a problem downloading your image!': 'Tyvärr gick något fel vid nedladdning av bild', // components/modebar/buttons.js:78 + 'Capturing image - this may take a few seconds': 'Skapar bild - detta kan ta några sekunder', // components/modebar/buttons.js:60 'Toggle Spike Lines': 'Aktivera/Inaktivera topplinjer', // components/modebar/buttons.js:569 'Toggle show closest data on hover': 'Aktivera/Inaktivera visa närmaste värde när muspekaren hålls över', // components/modebar/buttons.js:361 'Turntable rotation': 'Platt rotation', // components/modebar/buttons.js:290 diff --git a/lib/locales/sw.js b/lib/locales/sw.js index 6d173e19b80..70f94eae51a 100644 --- a/lib/locales/sw.js +++ b/lib/locales/sw.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Weka upya mtazamo', 'Reset views': 'Weka upya maoni', 'Show closest data on hover': 'Onyesha data iliyo karibu zaidi kielekezi kinapoelea', - 'Snapshot succeeded': 'Snapshot ilifanikiwa', - 'Sorry, there was a problem downloading your snapshot!': 'Samahani, kulikuwa na shida kupakua picha yako!', - 'Taking snapshot - this may take a few seconds': 'Kuchukua snapshot - hii inaweza kuchukua sekunde chache', + 'Image download succeeded': 'Snapshot ilifanikiwa', + 'Sorry, there was a problem downloading your image!': 'Samahani, kulikuwa na shida kupakua picha yako!', + 'Capturing image - this may take a few seconds': 'Kuchukua snapshot - hii inaweza kuchukua sekunde chache', 'Toggle Spike Lines': 'Badilisha Mistari ya Spike', 'Toggle show closest data on hover': 'Badilisha mabadiliko ya karibu zaidi kwenye hover', 'Turntable rotation': 'Zunguka kwa mhimili wa Z', diff --git a/lib/locales/tr.js b/lib/locales/tr.js index 7accb25bcd5..3bdf679696f 100644 --- a/lib/locales/tr.js +++ b/lib/locales/tr.js @@ -31,9 +31,9 @@ module.exports = { 'Reset view': 'Görünümü sıfırla', 'Reset views': 'Görünümleri sıfırla', 'Show closest data on hover': 'Üzerine gelince en yakın veriyi göster', - 'Snapshot succeeded': 'Anlık görüntü alındı', - 'Sorry, there was a problem downloading your snapshot!': 'Üzgünüz, anlık görüntünüz indirilirken bir sorun oluştu!', - 'Taking snapshot - this may take a few seconds': 'Anlık görüntü alınıyor - bu işlem birkaç saniye sürebilir', + 'Image download succeeded': 'Anlık görüntü alındı', + 'Sorry, there was a problem downloading your image!': 'Üzgünüz, anlık görüntünüz indirilirken bir sorun oluştu!', + 'Capturing image - this may take a few seconds': 'Anlık görüntü alınıyor - bu işlem birkaç saniye sürebilir', 'Zoom': 'Yakınlaştır', 'Zoom in': 'Yakınlaş', 'Zoom out': 'Uzaklaş', diff --git a/lib/locales/uk.js b/lib/locales/uk.js index 635bd202563..c39a8dd15e9 100644 --- a/lib/locales/uk.js +++ b/lib/locales/uk.js @@ -32,9 +32,9 @@ module.exports = { 'Reset view': 'Встановити відображенню значення за замовчуванням', 'Reset views': 'Встановити відображенням значення за замовчуванням', 'Show closest data on hover': 'При наведенні показувати найближчі дані', - 'Snapshot succeeded': 'Знімок успішно створений', - 'Sorry, there was a problem downloading your snapshot!': 'На жаль, виникла проблема при збереженні знімку', - 'Taking snapshot - this may take a few seconds': 'Створюється знімок - це може зайняти кілька секунд', + 'Image download succeeded': 'Знімок успішно створений', + 'Sorry, there was a problem downloading your image!': 'На жаль, виникла проблема при збереженні знімку', + 'Capturing image - this may take a few seconds': 'Створюється знімок - це може зайняти кілька секунд', 'Toggle Spike Lines': 'Увімкнути/вимкнути відображення ліній проекцій точок', 'Toggle show closest data on hover': 'Увімкнути/вимкнути відображення найближчих даних при наведенні', 'Turntable rotation': 'Обертання на поворотному столі', diff --git a/lib/locales/zh-cn.js b/lib/locales/zh-cn.js index 7252e623a03..48348afa5a6 100644 --- a/lib/locales/zh-cn.js +++ b/lib/locales/zh-cn.js @@ -31,9 +31,9 @@ module.exports = { 'Reset view': '重置视图', // components/modebar/buttons.js:583 'Reset views': '重置视图', // components/modebar/buttons.js:529 'Show closest data on hover': '悬停时显示最近的数据', // components/modebar/buttons.js:157 - 'Snapshot succeeded': '生成快照成功', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': '抱歉,下载快照出现问题!', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': '正在生成快照 - 可能需要几秒钟', // components/modebar/buttons.js:57 + 'Image download succeeded': '生成快照成功', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': '抱歉,下载快照出现问题!', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': '正在生成快照 - 可能需要几秒钟', // components/modebar/buttons.js:57 'Zoom': '缩放', // components/modebar/buttons.js:85 'Zoom in': '放大', // components/modebar/buttons.js:121 'Zoom out': '缩小', // components/modebar/buttons.js:130 diff --git a/lib/locales/zh-tw.js b/lib/locales/zh-tw.js index 02a29ffc9c9..81f5d209efd 100644 --- a/lib/locales/zh-tw.js +++ b/lib/locales/zh-tw.js @@ -38,9 +38,9 @@ module.exports = { 'Reset view': '重置視圖', // components/modebar/buttons.js:583 'Reset views': '重置視圖', // components/modebar/buttons.js:529 'Show closest data on hover': '游標停留時顯示最接近的資料', // components/modebar/buttons.js:157 - 'Snapshot succeeded': '快照成功', // components/modebar/buttons.js:66 - 'Sorry, there was a problem downloading your snapshot!': '抱歉,下載快照時發生錯誤!', // components/modebar/buttons.js:69 - 'Taking snapshot - this may take a few seconds': '產生快照中 - 可能需要一點時間', // components/modebar/buttons.js:57 + 'Image download succeeded': '快照成功', // components/modebar/buttons.js:66 + 'Sorry, there was a problem downloading your image!': '抱歉,下載快照時發生錯誤!', // components/modebar/buttons.js:69 + 'Capturing image - this may take a few seconds': '產生快照中 - 可能需要一點時間', // components/modebar/buttons.js:57 'Zoom': '縮放', // components/modebar/buttons.js:85 'Zoom in': '放大', // components/modebar/buttons.js:121 'Zoom out': '縮小', // components/modebar/buttons.js:130 diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 09b0dc0003d..0de11f3e9ec 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -49,7 +49,7 @@ modeBarButtons.toImage = { var toImageButtonOptions = gd._context.toImageButtonOptions; var opts = {format: toImageButtonOptions.format || 'png'}; - Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long', gd); + Lib.notifier(_(gd, 'Capturing image - this may take a few seconds'), 'long', gd); ['filename', 'width', 'height', 'scale'].forEach(function(key) { if(key in toImageButtonOptions) { @@ -59,10 +59,10 @@ modeBarButtons.toImage = { Registry.call('downloadImage', gd, opts) .then(function(filename) { - Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long', gd); + Lib.notifier(_(gd, 'Image download succeeded') + ' - ' + filename, 'long', gd); }) .catch(function() { - Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long', gd); + Lib.notifier(_(gd, 'Sorry, there was a problem downloading your image!'), 'long', gd); }); } }; diff --git a/src/lib/index.js b/src/lib/index.js index d898a526140..7685064bf14 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -208,6 +208,8 @@ lib.increment = require('./increment'); lib.cleanNumber = require('./clean_number'); +lib.slugify = require('./slugify'); + lib.ensureNumber = function ensureNumber(v) { if (!isNumeric(v)) return BADNUM; v = Number(v); diff --git a/src/lib/slugify.js b/src/lib/slugify.js new file mode 100644 index 00000000000..32342629717 --- /dev/null +++ b/src/lib/slugify.js @@ -0,0 +1,56 @@ +'use strict'; + +// precompile for speed +var HTML_TAGS_REGEX = /<[^>]*>/g; // anything contained in < > tags +var FORBIDDEN_CHARS_REGEX = /[\\/:*?"<>|$%&!@#~.^`'(){}[\],]/g; // Characters in the set: \/:*?"<>|$%&!@#~.^`'(){}[], +var CONTROL_CHARS_REGEX = /\p{Cc}/gu; // Unicode control characters + +var UNICODE_REPLACEMENT_CHAR_REGEX = /�/g; // U+FFFD, the Unicode replacement character +var WHITESPACE_REGEX = /\s+/g; + +var WORD_SEP_CHAR = '-'; // character used to separate words (replaces whitespace) +var _WORD_SEP_ESCAPED = WORD_SEP_CHAR.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +var WORD_SEP_CHARS_REGEX = new RegExp(_WORD_SEP_ESCAPED + '{3,}', 'g'); // three or more consecutive word separator chars +var TRAILING_WORD_SEP_CHAR_REGEX = new RegExp(_WORD_SEP_ESCAPED + '$', 'g'); // trailing word separator char + +// Safely under the limit for most filesystems +var DEFAULT_MAX_LEN = 60; + +/** + * Coerce a string to well-formed UTF-16, by replacing any unpaired surrogates + * with the replacement character U+FFFD. + * + * Uses the native String.prototype.toWellFormed (ES2024) when available, and + * otherwise falls back to TextEncoder/TextDecoder, which has the same effect. + */ +function toWellFormed(str) { + if(typeof str.toWellFormed === 'function') return str.toWellFormed(); + return new TextDecoder().decode(new TextEncoder().encode(str)); +} + +/** + * slugify: turn an arbitrary string into a lowercase, hyphenated, + * filesystem-safe token (e.g. for use as a filename). Whitespace is replaced + * with hyphens, and Unicode letters (accents, CJK, etc.) are preserved. + * Returns a valid Unicode string. + * + * @param {string} str + * @param {number} [maxLen] max length in code points (default 60) + * @return {string} + */ +module.exports = function slugify(str, maxLen = DEFAULT_MAX_LEN) { + var slug = toWellFormed(str ?? '') // Guarantee well-formed Unicode text + .replace(UNICODE_REPLACEMENT_CHAR_REGEX, '') // Drop Unicode replacement chars left by previous step + .replace(HTML_TAGS_REGEX, ' ') // Remove < > tags, such as
(replace with space) + .replace(FORBIDDEN_CHARS_REGEX, '') // Remove forbidden filename characters + .toLowerCase() // Lowercase everything + .trim() // Strip leading/trailing whitespace + .replace(WHITESPACE_REGEX, WORD_SEP_CHAR) // Replace any remaining whitespace with the word sep char + .replace(CONTROL_CHARS_REGEX, '') // Remove control characters (after whitespace) + .replace(WORD_SEP_CHARS_REGEX, WORD_SEP_CHAR); // Replace multiple word sep chars with a single one + + if (slug.length <= maxLen) return slug; + // Apply maxLen to the resulting string. Use Array.from().slice() instead of String.prototype.split() + // to avoid splitting in the middle of a surrogate pair. + return Array.from(slug).slice(0, maxLen).join('').replace(TRAILING_WORD_SEP_CHAR_REGEX, ''); +}; diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index 0b37345312b..7850a3c889e 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -13,6 +13,20 @@ var LINE_SPACING = require('../constants/alignment').LINE_SPACING; var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/; +/** +* Checks whether the given string contains LaTeX markup +* (delimited by a pair of $ signs) and returns the match array if so. +* +* @param {string} str: the string to check for tex +* @return {?Array} the regex match array (truthy) if the string contains tex, +* otherwise null (for an empty/missing string or when no tex delimiters are found). +*/ +function matchTex(str) { + if (!str) return null; + return str.match(FIND_TEX); +}; +exports.matchTex = matchTex; + exports.convertToTspans = function(_context, gd, _callback) { var str = _context.text(); @@ -21,7 +35,7 @@ exports.convertToTspans = function(_context, gd, _callback) { var tex = (!_context.attr('data-notex')) && gd && gd._context.typesetMath && (typeof MathJax !== 'undefined') && - str.match(FIND_TEX); + matchTex(str); var parent = d3.select(_context.node().parentNode); if(parent.empty()) return; diff --git a/src/snapshot/download.js b/src/snapshot/download.js index 95c97fe7441..292f7075950 100644 --- a/src/snapshot/download.js +++ b/src/snapshot/download.js @@ -1,6 +1,7 @@ 'use strict'; var Lib = require('../lib'); +var svgTextUtils = require('../lib/svg_text_utils'); var toImage = require('../plot_api/to_image'); @@ -35,7 +36,17 @@ function downloadImage(gd, opts) { if(_gd) _gd._snapshotInProgress = true; var promise = toImage(gd, opts); - var filename = opts.filename || gd.fn || 'newplot'; + var potentialFilename = opts.filename || gd.fn; + if (!potentialFilename) { + const plotTitle = helpers.getPlotTitle(gd); + // Trying to slugify a LaTeX string can result in weird ugly filenames, + // so ignore the title entirely if it contains LaTeX markup + if (!svgTextUtils.matchTex(plotTitle)) { + potentialFilename = Lib.slugify(plotTitle, 40); + } + } + + var filename = potentialFilename || 'plot-image'; filename += '.' + opts.format.replace('-', '.'); promise.then(function(result) { diff --git a/src/snapshot/helpers.js b/src/snapshot/helpers.js index 3f50eb1a2d2..98b5d03c911 100644 --- a/src/snapshot/helpers.js +++ b/src/snapshot/helpers.js @@ -51,6 +51,23 @@ exports.octetStream = function(s) { document.location.href = 'data:application/octet-stream' + s; }; +/** + * Get the resolved plot title to derive a filename from, or undefined if there + * is none. We try to read _fullLayout, which reflects the title after applying + * layout.template, but since title.text falls back to the editable-mode placeholder + * when unset, a value equal to that placeholder is treated as no title. + * For an un-rendered figure object (no _fullLayout) we fall back to the input layout + * (gd.layout). + */ +exports.getPlotTitle = function(gd) { + var fullLayout = gd._fullLayout; + if(fullLayout) { + var title = fullLayout.title?.text; + return title === fullLayout._dfltTitle?.plot ? undefined : title; + } + return gd.layout?.title?.text; +}; + // Taken from https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752 function fixBinary(b) { var len = b.length; diff --git a/test/jasmine/tests/download_test.js b/test/jasmine/tests/download_test.js index 02466695809..a0b7df9f6e9 100644 --- a/test/jasmine/tests/download_test.js +++ b/test/jasmine/tests/download_test.js @@ -86,6 +86,75 @@ describe('Plotly.downloadImage', function() { }) .then(done, done.fail); }, LONG_TIMEOUT_INTERVAL); + + describe('default filename (derived from the plot title)', function() { + // download with no explicit `filename`, so the name is derived from the title + function downloadDefault(layout) { + return Plotly.newPlot(gd, [{y: [1, 2, 1]}], layout).then(function() { + return Plotly.downloadImage(gd, {format: 'png', height: 300, width: 300}); + }); + } + + it('slugifies the plot title into the filename', function(done) { + downloadDefault({title: {text: 'My Awesome Plot'}}) + .then(function(filename) { + expect(filename).toBe('my-awesome-plot.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + + it('uses a title supplied via layout.template', function(done) { + // the template title only appears in _fullLayout, not the input layout + downloadDefault({template: {layout: {title: {text: 'Title From Template'}}}}) + .then(function(filename) { + expect(filename).toBe('title-from-template.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + + it('strips forbidden characters from the title', function(done) { + downloadDefault({title: {text: 'Revenue, Costs & "Profit" (2024)'}}) + .then(function(filename) { + expect(filename).toBe('revenue-costs-profit-2024.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + + it('caps the title-derived name at 40 code points', function(done) { + downloadDefault({title: {text: 'A Very Long Plot Title That Exceeds The Forty Character Maximum Limit'}}) + .then(function(filename) { + expect(filename).toBe('a-very-long-plot-title-that-exceeds-the.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + + it('falls back to plot-image as filename when there is no title', function(done) { + downloadDefault({}) + .then(function(filename) { + expect(filename).toBe('plot-image.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + + it('does not use the editable-mode placeholder as the filename', function(done) { + // _fullLayout.title.text defaults to the "Click to enter Plot title" + // placeholder; the filename must come from the input layout instead + downloadDefault({}) + .then(function(filename) { + expect(gd._fullLayout.title.text).toBe('Click to enter Plot title'); + expect(filename).toBe('plot-image.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + + it('ignores title if it contains LaTeX markup', function(done) { + downloadDefault({title: {text: '$\\alpha$ + $\\beta$'}}) + .then(function(filename) { + expect(filename).toBe('plot-image.png'); + }) + .then(done, done.fail); + }, LONG_TIMEOUT_INTERVAL); + }); }); function downloadTest(gd, format) { diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index fe1f6b71f0c..ecbffad2774 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -1941,6 +1941,59 @@ describe('Test lib.js:', function () { }); }); + describe('slugify', function () { + it('lowercases, trims, and hyphenates whitespace', function () { + expect(Lib.slugify(' Hello World ')).toBe('hello-world'); + expect(Lib.slugify('Multiple Spaces\tand\ntabs')).toBe('multiple-spaces-and-tabs'); + }); + + it('strips html/pseudo-html tags', function () { + expect(Lib.slugify('Revenue by year')).toBe('revenue-by-year'); + }); + + it('removes illegal filename characters', function () { + expect(Lib.slugify('a/b\\c:d*e?f"g|h$i%j&k!l@m#n~o.p^q`r\'s,t')) + .toBe('abcdefghijklmnopqrst'); + expect(Lib.slugify('a>b