From 2cff60609267bffdab8f3e8e03d32c03c8e7d760 Mon Sep 17 00:00:00 2001 From: Paul Bienkowski Date: Sun, 24 Jul 2022 18:07:47 +0200 Subject: [PATCH] Translate UploadPage --- frontend/src/components/FileUploadField.tsx | 63 ++++--- frontend/src/pages/UploadPage.tsx | 174 ++++++++++++-------- frontend/src/translations/de.yaml | 18 ++ frontend/src/translations/en.yaml | 18 ++ 4 files changed, 183 insertions(+), 90 deletions(-) diff --git a/frontend/src/components/FileUploadField.tsx b/frontend/src/components/FileUploadField.tsx index f3071e3..ace1423 100644 --- a/frontend/src/components/FileUploadField.tsx +++ b/frontend/src/components/FileUploadField.tsx @@ -1,29 +1,31 @@ -import React from 'react' -import {Icon, Segment, Header, Button} from 'semantic-ui-react' +import React from "react"; +import { Icon, Segment, Header, Button } from "semantic-ui-react"; +import { useTranslation } from "react-i18next"; -import {FileDrop} from 'components' +import { FileDrop } from "components"; -export default function FileUploadField({onSelect: onSelect_, multiple}) { - const labelRef = React.useRef() - const [labelRefState, setLabelRefState] = React.useState() +export default function FileUploadField({ onSelect: onSelect_, multiple }) { + const { t } = useTranslation(); + const labelRef = React.useRef(); + const [labelRefState, setLabelRefState] = React.useState(); - const onSelect = multiple ? onSelect_ : (files) => onSelect_(files?.[0]) + const onSelect = multiple ? onSelect_ : (files) => onSelect_(files?.[0]); React.useLayoutEffect( () => { - setLabelRefState(labelRef.current) + setLabelRefState(labelRef.current); }, // eslint-disable-next-line react-hooks/exhaustive-deps [labelRef.current] - ) + ); function onChangeField(e) { - e.preventDefault?.() + e.preventDefault?.(); if (e.target.files && e.target.files.length) { - onSelect(e.target.files) + onSelect(e.target.files); } - e.target.value = '' // reset the form field for uploading again + e.target.value = ""; // reset the form field for uploading again } return ( @@ -31,7 +33,14 @@ export default function FileUploadField({onSelect: onSelect_, multiple}) { {labelRefState && ( - {({draggingOverFrame, draggingOverTarget, onDragOver, onDragLeave, onDrop, onClick}) => ( + {({ + draggingOverFrame, + draggingOverTarget, + onDragOver, + onDragLeave, + onDrop, + onClick, + }) => (
- Drop file{multiple ? 's' : ''} here or click to select {multiple ? 'them' : 'one'} for upload + {multiple + ? t("FileUploadField.dropOrClickMultiple") + : t("FileUploadField.dropOrClick")}
)}
)} - - ) + + ); } diff --git a/frontend/src/pages/UploadPage.tsx b/frontend/src/pages/UploadPage.tsx index 04ba7a8..a76919c 100644 --- a/frontend/src/pages/UploadPage.tsx +++ b/frontend/src/pages/UploadPage.tsx @@ -1,45 +1,46 @@ -import _ from 'lodash' -import React from 'react' -import {List, Loader, Table, Icon} from 'semantic-ui-react' -import {Link} from 'react-router-dom' +import _ from "lodash"; +import React from "react"; +import { List, Loader, Table, Icon } from "semantic-ui-react"; +import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; -import {FileUploadField, Page} from 'components' -import type {Track} from 'types' -import api from 'api' -import configPromise from 'config' +import { FileUploadField, Page } from "components"; +import type { Track } from "types"; +import api from "api"; +import configPromise from "config"; function isSameFile(a: File, b: File) { - return a.name === b.name && a.size === b.size + return a.name === b.name && a.size === b.size; } function formatFileSize(bytes: number) { if (bytes < 1024) { - return `${bytes} bytes` + return `${bytes} bytes`; } - bytes /= 1024 + bytes /= 1024; if (bytes < 1024) { - return `${bytes.toFixed(1)} KiB` + return `${bytes.toFixed(1)} KiB`; } - bytes /= 1024 + bytes /= 1024; if (bytes < 1024) { - return `${bytes.toFixed(1)} MiB` + return `${bytes.toFixed(1)} MiB`; } - bytes /= 1024 - return `${bytes.toFixed(1)} GiB` + bytes /= 1024; + return `${bytes.toFixed(1)} GiB`; } type FileUploadResult = | { - track: Track + track: Track; } | { - errors: Record - } + errors: Record; + }; export function FileUploadStatus({ id, @@ -47,108 +48,127 @@ export function FileUploadStatus({ onComplete, slug, }: { - id: string - file: File - onComplete: (id: string, result: FileUploadResult) => void - slug?: string + id: string; + file: File; + onComplete: (id: string, result: FileUploadResult) => void; + slug?: string; }) { - const [progress, setProgress] = React.useState(0) + const [progress, setProgress] = React.useState(0); React.useEffect( () => { - let xhr + let xhr; async function _work() { - const formData = new FormData() - formData.append('body', file) + const formData = new FormData(); + formData.append("body", file); - xhr = new XMLHttpRequest() - xhr.withCredentials = true + xhr = new XMLHttpRequest(); + xhr.withCredentials = true; const onProgress = (e) => { - const progress = (e.loaded || 0) / (e.total || 1) - setProgress(progress) - } + const progress = (e.loaded || 0) / (e.total || 1); + setProgress(progress); + }; const onLoad = (e) => { - onComplete(id, xhr.response) - } + onComplete(id, xhr.response); + }; - xhr.responseType = 'json' - xhr.onload = onLoad - xhr.upload.onprogress = onProgress + xhr.responseType = "json"; + xhr.onload = onLoad; + xhr.upload.onprogress = onProgress; - const config = await configPromise + const config = await configPromise; if (slug) { - xhr.open('PUT', `${config.apiUrl}/tracks/${slug}`) + xhr.open("PUT", `${config.apiUrl}/tracks/${slug}`); } else { - xhr.open('POST', `${config.apiUrl}/tracks`) + xhr.open("POST", `${config.apiUrl}/tracks`); } // const accessToken = await api.getValidAccessToken() // xhr.setRequestHeader('Authorization', accessToken) - xhr.send(formData) + xhr.send(formData); } - _work() - return () => xhr.abort() + _work(); + return () => xhr.abort(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [file] - ) + ); + const { t } = useTranslation(); return ( - {' '} - {progress < 1 ? `Uploading ${(progress * 100).toFixed(0)}%` : 'Processing...'} + {" "} + {progress < 1 + ? t("UploadPage.uploadProgress", { + progress: (progress * 100).toFixed(0), + }) + : t("UploadPage.processing")} - ) + ); } type FileEntry = { - id: string - file?: File | null - size: number - name: string - result?: FileUploadResult -} + id: string; + file?: File | null; + size: number; + name: string; + result?: FileUploadResult; +}; export default function UploadPage() { - const [files, setFiles] = React.useState([]) + const [files, setFiles] = React.useState([]); const onCompleteFileUpload = React.useCallback( (id, result) => { - setFiles((files) => files.map((file) => (file.id === id ? {...file, result, file: null} : file))) + setFiles((files) => + files.map((file) => + file.id === id ? { ...file, result, file: null } : file + ) + ); }, [setFiles] - ) + ); function onSelectFiles(fileList) { const newFiles = Array.from(fileList).map((file) => ({ - id: 'file-' + String(Math.floor(Math.random() * 1000000)), + id: "file-" + String(Math.floor(Math.random() * 1000000)), file, name: file.name, size: file.size, - })) - setFiles(files.filter((a) => !newFiles.some((b) => isSameFile(a, b))).concat(newFiles)) + })); + setFiles( + files + .filter((a) => !newFiles.some((b) => isSameFile(a, b))) + .concat(newFiles) + ); } + const { t } = useTranslation(); + return ( {files.length ? ( - Filename - Size - Status / Title + + {t("UploadPage.table.filename")} + + {t("UploadPage.table.size")} + + {t("UploadPage.table.statusTitle")} + - {files.map(({id, name, size, file, result}) => ( + {files.map(({ id, name, size, file, result }) => ( @@ -159,7 +179,9 @@ export default function UploadPage() { {result?.errors ? ( {_.sortBy(Object.entries(result.errors)) - .filter(([field, message]) => typeof message === 'string') + .filter( + ([field, message]) => typeof message === "string" + ) .map(([field, message]) => ( @@ -169,15 +191,29 @@ export default function UploadPage() { ) : result ? ( <> - {result.track?.title || 'Unnamed track'} + {" "} + {result.track?.title || t("general.unnamedTrack")} ) : ( - + )} - {result?.track ? Show : null} - {result?.track ? Edit : null} + {result?.track ? ( + + {t("general.show")} + + ) : null} + + + {result?.track ? ( + + {t("general.edit")} + + ) : null} ))} @@ -187,5 +223,5 @@ export default function UploadPage() { - ) + ); } diff --git a/frontend/src/translations/de.yaml b/frontend/src/translations/de.yaml index 29561aa..32b130c 100644 --- a/frontend/src/translations/de.yaml +++ b/frontend/src/translations/de.yaml @@ -3,6 +3,8 @@ general: unnamedTrack: Unbenannte Fahrt public: Öffentlich private: Privat + show: Anzeigen + edit: Bearbeiten App: footer: @@ -87,3 +89,19 @@ ExportPage: shapefile: Shapefile (ZIP) boundingBox: label: Geografischer Bereich + +UploadPage: + uploadProgress: Lade hoch {progress}% + processing: Verarbeiten... + + table: + filename: Dateiname + size: Größe + statusTitle: Status / Titel + + +FileUploadField: + dropOrClick: Datei hierher ziehen oder klicken, um eine zum Hochladen auszuwählen + dropOrClickMultiple: Dateien hierher ziehen oder klicken, um Dateien zum Hochladen auszuwählen + uploadFile: Datei hochladen + uploadFiles: Dateien hochladen diff --git a/frontend/src/translations/en.yaml b/frontend/src/translations/en.yaml index fe5c91f..622cdeb 100644 --- a/frontend/src/translations/en.yaml +++ b/frontend/src/translations/en.yaml @@ -7,6 +7,8 @@ general: unnamedTrack: Unnamed track public: Public private: Private + show: Show + edit: Edit App: footer: @@ -92,3 +94,19 @@ ExportPage: shapefile: Shapefile (ZIP) boundingBox: label: Bounding Box + +UploadPage: + uploadProgress: Uploading {progress}% + processing: Processing... + + table: + filename: Filename + size: Size + statusTitle: Status / Title + + +FileUploadField: + dropOrClick: Drop file here or click to select one for upload + dropOrClickMultiple: Drop files here or click to select them for upload + uploadFile: Upload file + uploadFiles: Upload files