Translate TrackPage
This commit is contained in:
parent
ab6cc6f6d0
commit
6f7c8d54f2
18
frontend/src/components/Visibility.tsx
Normal file
18
frontend/src/components/Visibility.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import { Icon } from "semantic-ui-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function Visibility({ public: public_ }: { public: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
const icon = public_ ? (
|
||||
<Icon color="blue" name="eye" fitted />
|
||||
) : (
|
||||
<Icon name="eye slash" fitted />
|
||||
);
|
||||
const text = public_ ? t("general.public") : t("general.private");
|
||||
return (
|
||||
<>
|
||||
{icon} {text}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -9,3 +9,4 @@ export {default as Page} from './Page'
|
|||
export {default as Stats} from './Stats'
|
||||
export {default as StripMarkdown} from './StripMarkdown'
|
||||
export {default as Chart} from './Chart'
|
||||
export {default as Visibility} from './Visibility'
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
import React from 'react'
|
||||
import {Link} from 'react-router-dom'
|
||||
import {Icon, Popup, Button, Dropdown} from 'semantic-ui-react'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function TrackActions({slug, isAuthor, onDownload}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
{isAuthor && (
|
||||
<Link to={`/tracks/${slug}/edit`}>
|
||||
<Button primary>Edit track</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Dropdown text="Download" button upward>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item text="Original" onClick={() => onDownload('original.csv')} disabled={!isAuthor} />
|
||||
<Dropdown.Item text="Track (GPX)" onClick={() => onDownload('track.gpx')} />
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
|
||||
<Popup
|
||||
trigger={<Icon name="info circle" />}
|
||||
offset={[12, 0]}
|
||||
content={
|
||||
isAuthor ? (
|
||||
<>
|
||||
<p>Only you, the author of this track, can download the original file.</p>
|
||||
<p>
|
||||
This is the file as it was uploaded to the server, without modifications, and it can be used with other
|
||||
tools.
|
||||
</p>
|
||||
<p>{t('TrackPage.actions.hintAuthorOnly')}</p>
|
||||
<p>{t('TrackPage.actions.hintOriginal')}</p>
|
||||
</>
|
||||
) : (
|
||||
<p>Only the author of this track can download the original file.</p>
|
||||
<p>{t('TrackPage.actions.hintAuthorOnlyOthers')}</p>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<Dropdown text={t('TrackPage.actions.download')} button>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item text={t('TrackPage.actions.original')}onClick={() => onDownload('original.csv')} disabled={!isAuthor} />
|
||||
<Dropdown.Item text={t('TrackPage.actions.gpx')} onClick={() => onDownload('track.gpx')} />
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
|
||||
{isAuthor && (
|
||||
<Link to={`/tracks/${slug}/edit`}>
|
||||
<Button primary>{t('TrackPage.actions.edit')}</Button>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,31 +1,57 @@
|
|||
import React from 'react'
|
||||
import {Message, Segment, Form, Button, Loader, Header, Comment} from 'semantic-ui-react'
|
||||
import Markdown from 'react-markdown'
|
||||
import React from "react";
|
||||
import {
|
||||
Message,
|
||||
Segment,
|
||||
Form,
|
||||
Button,
|
||||
Loader,
|
||||
Header,
|
||||
Comment,
|
||||
} from "semantic-ui-react";
|
||||
import Markdown from "react-markdown";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import {Avatar, FormattedDate} from 'components'
|
||||
import { Avatar, FormattedDate } from "components";
|
||||
|
||||
function CommentForm({ onSubmit }) {
|
||||
const [body, setBody] = React.useState('')
|
||||
const { t } = useTranslation();
|
||||
const [body, setBody] = React.useState("");
|
||||
|
||||
const onSubmitComment = React.useCallback(() => {
|
||||
onSubmit({body})
|
||||
setBody('')
|
||||
}, [onSubmit, body])
|
||||
onSubmit({ body });
|
||||
setBody("");
|
||||
}, [onSubmit, body]);
|
||||
|
||||
return (
|
||||
<Form reply onSubmit={onSubmitComment}>
|
||||
<Form.TextArea rows={4} value={body} onChange={(e) => setBody(e.target.value)} />
|
||||
<Button content="Post comment" labelPosition="left" icon="edit" primary />
|
||||
<Form.TextArea
|
||||
rows={4}
|
||||
value={body}
|
||||
onChange={(e) => setBody(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
content={t("TrackPage.comments.post")}
|
||||
labelPosition="left"
|
||||
icon="edit"
|
||||
primary
|
||||
/>
|
||||
</Form>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default function TrackComments({comments, onSubmit, onDelete, login, hideLoader}) {
|
||||
export default function TrackComments({
|
||||
comments,
|
||||
onSubmit,
|
||||
onDelete,
|
||||
login,
|
||||
hideLoader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Comment.Group>
|
||||
<Header as="h2" dividing>
|
||||
Comments
|
||||
{t("TrackPage.comments.title")}
|
||||
</Header>
|
||||
|
||||
<Loader active={!hideLoader && comments == null} inline />
|
||||
|
@ -47,11 +73,11 @@ export default function TrackComments({comments, onSubmit, onDelete, login, hide
|
|||
<Comment.Actions>
|
||||
<Comment.Action
|
||||
onClick={(e) => {
|
||||
onDelete(comment.id)
|
||||
e.preventDefault()
|
||||
onDelete(comment.id);
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{t('general.delete')}
|
||||
</Comment.Action>
|
||||
</Comment.Actions>
|
||||
)}
|
||||
|
@ -59,10 +85,12 @@ export default function TrackComments({comments, onSubmit, onDelete, login, hide
|
|||
</Comment>
|
||||
))}
|
||||
|
||||
{comments != null && !comments.length && <Message>Nobody commented... yet</Message>}
|
||||
{comments != null && !comments.length && (
|
||||
<Message>{t("TrackPage.comments.empty")}</Message>
|
||||
)}
|
||||
|
||||
{login && comments != null && <CommentForm onSubmit={onSubmit} />}
|
||||
</Comment.Group>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,80 +1,85 @@
|
|||
import React from 'react'
|
||||
import {List} from 'semantic-ui-react'
|
||||
import {Duration} from 'luxon'
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { List, Header, Grid } from "semantic-ui-react";
|
||||
import { Duration } from "luxon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import {FormattedDate} from 'components'
|
||||
import { FormattedDate, Visibility } from "components";
|
||||
|
||||
function formatDuration(seconds) {
|
||||
return Duration.fromMillis((seconds ?? 0) * 1000).toFormat("h'h' mm'm'")
|
||||
return Duration.fromMillis((seconds ?? 0) * 1000).toFormat("h'h' mm'm'");
|
||||
}
|
||||
|
||||
export default function TrackDetails({ track, isAuthor }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const items = [
|
||||
track.public != null &&
|
||||
isAuthor && [
|
||||
t("TrackPage.details.visibility"),
|
||||
<Visibility public={track.public} />,
|
||||
],
|
||||
|
||||
track.uploadedByUserAgent != null && [
|
||||
t("TrackPage.details.uploadedWith"),
|
||||
track.uploadedByUserAgent,
|
||||
],
|
||||
|
||||
track.duration != null && [
|
||||
t("TrackPage.details.duration"),
|
||||
formatDuration(track.duration),
|
||||
],
|
||||
|
||||
track.createdAt != null && [
|
||||
t("TrackPage.details.uploadedDate"),
|
||||
<FormattedDate date={track.createdAt} />,
|
||||
],
|
||||
|
||||
track?.recordedAt != null && [
|
||||
t("TrackPage.details.recordedDate"),
|
||||
<FormattedDate date={track?.recordedAt} />,
|
||||
],
|
||||
|
||||
track?.numEvents != null && [
|
||||
t("TrackPage.details.numEvents"),
|
||||
track?.numEvents,
|
||||
],
|
||||
|
||||
track?.length != null && [
|
||||
t("TrackPage.details.length"),
|
||||
`${(track?.length / 1000).toFixed(2)} km`,
|
||||
],
|
||||
|
||||
track?.processingStatus != null &&
|
||||
track?.processingStatus != "error" && [
|
||||
t("TrackPage.details.processingStatus"),
|
||||
track.processingStatus,
|
||||
],
|
||||
|
||||
track.originalFileName != null && [
|
||||
t("TrackPage.details.originalFileName"),
|
||||
<code>{track.originalFileName}</code>,
|
||||
],
|
||||
].filter(Boolean);
|
||||
|
||||
const COLUMNS = 4;
|
||||
const chunkSize = Math.ceil(items.length / COLUMNS)
|
||||
return (
|
||||
<List horizontal relaxed>
|
||||
{track.public != null && isAuthor && (
|
||||
<List.Item>
|
||||
<List.Header>Visibility</List.Header>
|
||||
{track.public ? 'Public' : 'Private'}
|
||||
</List.Item>
|
||||
)}
|
||||
<Grid>
|
||||
<Grid.Row columns={COLUMNS}>
|
||||
{_.chunk(items, chunkSize).map((chunkItems, idx) => (
|
||||
<Grid.Column key={idx}>
|
||||
|
||||
{track.originalFileName != null && (
|
||||
<List.Item>
|
||||
{isAuthor && <div style={{float: 'right'}}></div>}
|
||||
|
||||
<List.Header>Original Filename</List.Header>
|
||||
<code>{track.originalFileName}</code>
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track.uploadedByUserAgent != null && (
|
||||
<List.Item>
|
||||
<List.Header>Uploaded with</List.Header>
|
||||
{track.uploadedByUserAgent}
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track.duration != null && (
|
||||
<List.Item>
|
||||
<List.Header>Duration</List.Header>
|
||||
{formatDuration(track.duration)}
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track.createdAt != null && (
|
||||
<List.Item>
|
||||
<List.Header>Uploaded on</List.Header>
|
||||
<FormattedDate date={track.createdAt} />
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track?.recordedAt != null && (
|
||||
<List.Item>
|
||||
<List.Header>Recorded on</List.Header>
|
||||
<FormattedDate date={track?.recordedAt} />
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track?.numEvents != null && (
|
||||
<List.Item>
|
||||
<List.Header>Confirmed events</List.Header>
|
||||
{track?.numEvents}
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track?.length != null && (
|
||||
<List.Item>
|
||||
<List.Header>Length</List.Header>
|
||||
{(track?.length / 1000).toFixed(2)} km
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
{track?.processingStatus != null && track?.processingStatus != 'error' && (
|
||||
<List.Item>
|
||||
<List.Header>Processing</List.Header>
|
||||
{track.processingStatus}
|
||||
</List.Item>
|
||||
)}
|
||||
<List>
|
||||
{chunkItems.map(([title, value]) => (
|
||||
<List.Item key={title}>
|
||||
<List.Header>{title}</List.Header>
|
||||
<List.Description>{value}</List.Description>
|
||||
</List.Item>))}
|
||||
</List>
|
||||
)
|
||||
</Grid.Column>
|
||||
))}
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
} from "rxjs/operators";
|
||||
import { useObservable } from "rxjs-hooks";
|
||||
import Markdown from "react-markdown";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import api from "api";
|
||||
import { Page } from "components";
|
||||
|
@ -52,16 +53,17 @@ function TrackMapSettings({
|
|||
side,
|
||||
setSide,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Header as="h4">Map settings</Header>
|
||||
<Header as="h4">{t("TrackPage.mapSettings.title")}</Header>
|
||||
<List>
|
||||
<List.Item>
|
||||
<Checkbox
|
||||
checked={showTrack}
|
||||
onChange={(e, d) => setShowTrack(d.checked)}
|
||||
/>{" "}
|
||||
Show track
|
||||
{t("TrackPage.mapSettings.showTrack")}
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<span
|
||||
style={{
|
||||
|
@ -73,7 +75,7 @@ function TrackMapSettings({
|
|||
marginRight: 4,
|
||||
}}
|
||||
/>
|
||||
GPS track
|
||||
{t("TrackPage.mapSettings.gpsTrack")}
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
|
@ -86,11 +88,11 @@ function TrackMapSettings({
|
|||
marginRight: 4,
|
||||
}}
|
||||
/>
|
||||
Snapped to road
|
||||
{t("TrackPage.mapSettings.snappedTrack")}
|
||||
</div>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Header>Points</List.Header>
|
||||
<List.Header> {t("TrackPage.mapSettings.points")} </List.Header>
|
||||
<Dropdown
|
||||
selection
|
||||
value={pointsMode}
|
||||
|
@ -100,18 +102,18 @@ function TrackMapSettings({
|
|||
{
|
||||
key: "overtakingEvents",
|
||||
value: "overtakingEvents",
|
||||
text: "Confirmed",
|
||||
text: t("TrackPage.mapSettings.confirmedPoints"),
|
||||
},
|
||||
{
|
||||
key: "measurements",
|
||||
value: "measurements",
|
||||
text: "All measurements",
|
||||
text: t("TrackPage.mapSettings.allPoints"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Header>Side (for color)</List.Header>
|
||||
<List.Header>{t("TrackPage.mapSettings.side")}</List.Header>
|
||||
<Dropdown
|
||||
selection
|
||||
value={side}
|
||||
|
@ -120,12 +122,12 @@ function TrackMapSettings({
|
|||
{
|
||||
key: "overtaker",
|
||||
value: "overtaker",
|
||||
text: "Overtaker (Left)",
|
||||
text: t("TrackPage.mapSettings.overtakerSide"),
|
||||
},
|
||||
{
|
||||
key: "stationary",
|
||||
value: "stationary",
|
||||
text: "Stationary (Right)",
|
||||
text: t("TrackPage.mapSettings.stationarySide"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
@ -138,6 +140,7 @@ function TrackMapSettings({
|
|||
const TrackPage = connect((state) => ({ login: state.login }))(
|
||||
function TrackPage({ login }) {
|
||||
const { slug } = useParams();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [reloadComments, reloadComments$] = useTriggerSubject();
|
||||
const history = useHistory();
|
||||
|
@ -234,9 +237,7 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
await api.downloadFile(`/tracks/${slug}/download/${filename}`);
|
||||
} catch (err) {
|
||||
if (/Failed to fetch/.test(String(err))) {
|
||||
setDownloadError(
|
||||
"The track probably has not been imported correctly or recently enough. Please ask your administrator for assistance."
|
||||
);
|
||||
setDownloadError(t("TrackPage.downloadError"));
|
||||
} else {
|
||||
setDownloadError(String(err));
|
||||
}
|
||||
|
@ -259,7 +260,7 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
const [pointsMode, setPointsMode] = React.useState("overtakingEvents"); // none|overtakingEvents|measurements
|
||||
const [side, setSide] = React.useState("overtaker"); // overtaker|stationary
|
||||
|
||||
const title = track ? track.title || "Unnamed track" : null;
|
||||
const title = track ? track.title || t("general.unnamedTrack") : null;
|
||||
return (
|
||||
<Page
|
||||
title={title}
|
||||
|
@ -268,9 +269,16 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
<Container>
|
||||
{track && (
|
||||
<Segment basic>
|
||||
<div style={{display: 'flex', alignItems: 'baseline', marginBlockStart: 32, marginBlockEnd: 16}}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "baseline",
|
||||
marginBlockStart: 32,
|
||||
marginBlockEnd: 16,
|
||||
}}
|
||||
>
|
||||
<Header as="h1">{title}</Header>
|
||||
<div style={{marginLeft: 'auto'}}>
|
||||
<div style={{ marginLeft: "auto" }}>
|
||||
<TrackActions {...{ isAuthor, onDownload, slug }} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -307,8 +315,7 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
{processing && (
|
||||
<Message warning>
|
||||
<Message.Content>
|
||||
Track data is still being processed, please reload page in
|
||||
a while.
|
||||
{t("TrackPage.processing")}
|
||||
</Message.Content>
|
||||
</Message>
|
||||
)}
|
||||
|
@ -316,8 +323,7 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
{error && (
|
||||
<Message error>
|
||||
<Message.Content>
|
||||
The processing of this track failed, please ask your site
|
||||
administrator for help in debugging the issue.
|
||||
{t("TrackPage.processingError")}
|
||||
</Message.Content>
|
||||
</Message>
|
||||
)}
|
||||
|
@ -328,7 +334,7 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
{track?.description && (
|
||||
<>
|
||||
<Header as="h2" dividing>
|
||||
Description
|
||||
{t("TrackPage.description")}
|
||||
</Header>
|
||||
<Markdown>{track.description}</Markdown>
|
||||
</>
|
||||
|
@ -347,7 +353,7 @@ const TrackPage = connect((state) => ({ login: state.login }))(
|
|||
open={downloadError != null}
|
||||
cancelButton={false}
|
||||
onConfirm={hideDownloadError}
|
||||
header="Download failed"
|
||||
header={t("TrackPage.downloadFailed")}
|
||||
content={String(downloadError)}
|
||||
/>
|
||||
</Page>
|
||||
|
|
|
@ -114,15 +114,7 @@ export function TrackListItem({track, privateTracks = false}) {
|
|||
</Item.Description>
|
||||
{privateTracks && (
|
||||
<Item.Extra>
|
||||
{track.public ? (
|
||||
<>
|
||||
<Icon color="blue" name="eye" fitted /> {t('general.public')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon name="eye slash" fitted /> {t('general.private')}
|
||||
</>
|
||||
)}
|
||||
<Visibility public={track.public} />
|
||||
|
||||
<span style={{marginLeft: '1em'}}>
|
||||
<Icon color={COLOR_BY_STATUS[track.processingStatus]} name="bolt" fitted />
|
||||
|
|
|
@ -7,6 +7,8 @@ general:
|
|||
private: Privat
|
||||
show: Anzeigen
|
||||
edit: Bearbeiten
|
||||
save: Speichern
|
||||
delete: Löschen
|
||||
|
||||
App:
|
||||
footer:
|
||||
|
@ -221,3 +223,49 @@ SettingsPage:
|
|||
|
||||
generate: Neuen API-Schlüssel erstellen
|
||||
|
||||
TrackPage:
|
||||
downloadFailed: Download fehlgeschlagen
|
||||
downloadError: Diese Fahrt wurde vermutlich nicht korrekt importiert, oder in letzter Zeit nicht aktualisiert. Bitte frage den Administrator um Hilfe mit diesem Problem.
|
||||
processing: Diese Fahrt wird gerade importiert, bitte lade die Seite später neu.
|
||||
processingError: Beim Import dieser Fahrt ist ein Fehler aufgetreten, bitte frage den Administrator um Hilfe mit diesem Problem.
|
||||
description: Beschreibung
|
||||
|
||||
mapSettings:
|
||||
title: Karteneinstellungen
|
||||
showTrack: Route anzeigen
|
||||
gpsTrack: GPS-Route
|
||||
snappedTrack: Erkannte Straßenroute
|
||||
|
||||
points: Punkte
|
||||
confirmedPoints: Bestätigte Überholungen
|
||||
allPoints: Alle Messungen
|
||||
|
||||
side: Seite für Einfärbung
|
||||
overtakerSide: Überholung (links)
|
||||
stationarySide: Ruhender Verkehr (rechts)
|
||||
|
||||
details:
|
||||
visibility: Sichtbarkeit
|
||||
originalFileName: Original Dateiname
|
||||
uploadedWith: Hochgeladen mit
|
||||
duration: Dauer
|
||||
uploadedDate: Hochgeladen am
|
||||
recordedDate: Aufgezeichnet am
|
||||
numEvents: Bestätigte Überholungen
|
||||
length: Länge
|
||||
processingStatus: Verarbeitung
|
||||
|
||||
actions:
|
||||
edit: Fahrt bearbeiten
|
||||
|
||||
download: Herunterladen
|
||||
original: Original
|
||||
gpx: GPX-Track
|
||||
hintAuthorOnly: Nur du, als Autor:in dieser Fahrt, kannst die Originaldatei herunterladen.
|
||||
hintOriginal: Dies ist die Originaldatei, wie sie auf den Server hochgeladen wurde, und kann mit anderen Werkzeugen verwendet werden.
|
||||
hintAuthorOnlyOthers: Nur der:die Autor:in dieser Fahrt kann die Originaldatei herunterladen.
|
||||
|
||||
comments:
|
||||
title: Kommentare
|
||||
post: Kommentar abschicken
|
||||
empty: Bisher hat niemand diese Fahrt kommentiert.
|
||||
|
|
|
@ -12,6 +12,7 @@ general:
|
|||
show: Show
|
||||
edit: Edit
|
||||
save: Save
|
||||
delete: Delete
|
||||
|
||||
copied: Copied.
|
||||
copyError: Failed to copy.
|
||||
|
@ -226,3 +227,51 @@ SettingsPage:
|
|||
|
||||
generate: Generate new API key
|
||||
|
||||
|
||||
TrackPage:
|
||||
downloadFailed: Download failed
|
||||
downloadError: The track probably has not been imported correctly or recently enough. Please ask your administrator for assistance.
|
||||
processing: Track data is still being processed, please reload page in a while.
|
||||
processingError: The processing of this track failed, please ask your site administrator for help in debugging the issue.
|
||||
description: Description
|
||||
|
||||
|
||||
mapSettings:
|
||||
title: Map settings
|
||||
showTrack: Show track
|
||||
gpsTrack: GPS track
|
||||
snappedTrack: Snapped to road
|
||||
|
||||
points: Points
|
||||
confirmedPoints: Confirmed
|
||||
allPoints: All measurements
|
||||
|
||||
side: Side (for color)
|
||||
overtakerSide: Overtaker (Left)
|
||||
stationarySide: Stationary (Right)
|
||||
|
||||
details:
|
||||
visibility: Visibility
|
||||
originalFileName: Original Filename
|
||||
uploadedWith: Uploaded with
|
||||
duration: Duration
|
||||
uploadedDate: Uploaded on
|
||||
recordedDate: Recorded on
|
||||
numEvents: Confirmed events
|
||||
length: Length
|
||||
processingStatus: Processing
|
||||
|
||||
actions:
|
||||
edit: Edit track
|
||||
|
||||
download: Download
|
||||
original: Original
|
||||
gpx: Track (GPX)
|
||||
hintAuthorOnly: Only you, the author of this track, can download the original file.
|
||||
hintOriginal: This is the file as it was uploaded to the server, without modifications, and it can be used with other tools.
|
||||
hintAuthorOnlyOthers: Only the author of this track can download the original file.
|
||||
|
||||
comments:
|
||||
title: Comments
|
||||
post: Post comment
|
||||
empty: Nobody commented... yet
|
||||
|
|
Loading…
Reference in a new issue