Bugs fixed: - FIT parser returns timestamps as Date objects, not strings — convert to ISO 8601 before passing to generateGpx (caused str.replace crash) - FIT parser already converts semicircles to degrees — remove redundant conversion that produced near-zero coordinates - Wahoo CDN URLs are pre-signed S3 URLs — remove Bearer auth header from download requests (caused 400 "Unsupported Authorization Type") - Filter out third-party workouts (fitness_app_id >= 1000) since Wahoo does not share their data via the API UX improvements: - Individual imports use fetcher (no page refresh), button shows "Importing..." inline - "Import all" button imports all unimported workouts on the page sequentially with progress indicator - Add @vitejs/plugin-basic-ssl for local HTTPS dev (opt-in via HTTPS=1) Also adds unit test for FIT-to-GPX conversion with real fixture file, and updates wahoo-import spec to reflect all behaviors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
228 lines
8.9 KiB
TypeScript
228 lines
8.9 KiB
TypeScript
export default {
|
|
common: {
|
|
save: "Speichern",
|
|
cancel: "Abbrechen",
|
|
delete: "Löschen",
|
|
edit: "Bearbeiten",
|
|
close: "Schließen",
|
|
loading: "Wird geladen...",
|
|
error: "Etwas ist schiefgelaufen",
|
|
goHome: "Zur Startseite",
|
|
pageNotFound: "Seite nicht gefunden",
|
|
serviceUnavailable: "Dienst vorübergehend nicht verfügbar. Bitte versuche es später erneut.",
|
|
},
|
|
planner: {
|
|
title: "trails.cool Planer",
|
|
subtitle: "Gemeinsame Routenplanung",
|
|
home: "Startseite",
|
|
newSession: "Neue Sitzung",
|
|
saveRoute: "Route speichern",
|
|
exportGpx: "GPX exportieren",
|
|
exportRoute: "Route exportieren",
|
|
exportRouteDesc: "GPX-Track für jede App",
|
|
exportPlan: "Plan exportieren",
|
|
exportPlanDesc: "Mit Wegpunkten und Sperrzonen",
|
|
"undo.tooltip": "Rückgängig (Strg+Z)",
|
|
"redo.tooltip": "Wiederholen (Strg+Umschalt+Z)",
|
|
importGpx: "GPX importieren",
|
|
importGpxError: "GPX-Datei konnte nicht gelesen werden. Bitte Dateiformat prüfen.",
|
|
dropGpxHere: "GPX-Datei hier ablegen",
|
|
replaceRouteConfirm: "Aktuelle Route durch importierte GPX ersetzen?",
|
|
profile: "Profil",
|
|
connecting: "Verbinde...",
|
|
loadingMap: "Karte wird geladen...",
|
|
computingRoute: "Route wird berechnet...",
|
|
routeError: "Routenberechnung fehlgeschlagen. Bitte versuche es erneut.",
|
|
noRouteFound: "Keine Route gefunden. Versuche den Wegpunkt auf einen Weg zu verschieben.",
|
|
host: "Host",
|
|
connected: "Verbunden",
|
|
saveToJournal: "Im Journal speichern",
|
|
saving: "Speichert...",
|
|
saved: "Gespeichert!",
|
|
returnToJournal: "Zurück zum Journal",
|
|
profiles: {
|
|
trekking: "Wandern",
|
|
fastbike: "Radfahren (schnell)",
|
|
safety: "Radfahren (sicher)",
|
|
shortest: "Kürzeste",
|
|
car: "Auto",
|
|
},
|
|
participants: {
|
|
you: "(du)",
|
|
host: "Host",
|
|
editName: "Klicke, um deinen Namen zu ändern",
|
|
joined: "{{name}} ist beigetreten",
|
|
left: "{{name}} hat die Sitzung verlassen",
|
|
},
|
|
sidebar: {
|
|
waypoints: "Wegpunkte",
|
|
notes: "Notizen",
|
|
},
|
|
noGoAreas: {
|
|
draw: "Sperrgebiet zeichnen",
|
|
cancel: "Sperrgebiet abbrechen",
|
|
hint: "Rechtsklick auf ein Sperrgebiet zum Löschen",
|
|
},
|
|
notes: {
|
|
placeholder: "Notizen für diese Sitzung hinzufügen...",
|
|
},
|
|
colorMode: {
|
|
label: "Routenfarbe",
|
|
plain: "Standard",
|
|
elevation: "Höhe",
|
|
surface: "Untergrund",
|
|
surfaceUnavailable: "Untergrunddaten für dieses Profil nicht verfügbar",
|
|
},
|
|
rateLimitExceeded: "Zu viele Anfragen. Bitte versuche es später erneut.",
|
|
elevation: {
|
|
gain: "Höhenmeter aufwärts",
|
|
loss: "Höhenmeter abwärts",
|
|
profile: "Höhenprofil",
|
|
},
|
|
landing: {
|
|
startPlanning: "Planung starten",
|
|
heroDescription: "Plane Wander- und Radrouten gemeinsam in Echtzeit. Kein Konto nötig.",
|
|
featureCollaborative: "Echtzeit-Zusammenarbeit",
|
|
featureCollaborativeDesc: "Wegpunkte gemeinsam bearbeiten — Änderungen werden sofort synchronisiert.",
|
|
featureRouting: "Intelligentes Routing",
|
|
featureRoutingDesc: "BRouter berechnet optimale Routen für Wandern, Radfahren oder Autofahren mit Höhenprofil.",
|
|
featureElevation: "Höhenprofil",
|
|
featureElevationDesc: "Das vollständige Höhenprofil deiner Route mit Auf- und Abstiegsstatistiken.",
|
|
featureExport: "GPX-Export",
|
|
featureExportDesc: "Exportiere deine Route als GPX für jedes GPS-Gerät oder jede App.",
|
|
featureNoAccount: "Kein Konto nötig",
|
|
featureNoAccountDesc: "Sofort loslegen. Teile einen Link und jeder kann deiner Sitzung beitreten.",
|
|
saveRoutes: "Routen speichern?",
|
|
saveRoutesDesc: "Erstelle ein kostenloses Konto auf trails.cool, um Routen zu speichern, Aktivitäten zu verfolgen und mehr.",
|
|
goToJournal: "Zu trails.cool",
|
|
footer: {
|
|
privacy: "Datenschutz",
|
|
source: "Quellcode",
|
|
builtWith: "Erstellt mit BRouter und OpenStreetMap",
|
|
},
|
|
},
|
|
},
|
|
journal: {
|
|
title: "trails.cool",
|
|
subtitle: "Dein Outdoor-Aktivitäten-Tagebuch",
|
|
welcome: "Willkommen,",
|
|
addPasskeyPrompt: "Füge einen Passkey hinzu, um dich schneller auf diesem Gerät anzumelden.",
|
|
addPasskey: "Passkey hinzufügen",
|
|
settingUp: "Wird eingerichtet...",
|
|
passkeyAdded: "Passkey hinzugefügt! Du kannst dich jetzt sofort auf diesem Gerät anmelden.",
|
|
passkeyStatus: "{{count}} Passkey registriert",
|
|
passkeyStatus_other: "{{count}} Passkeys registriert",
|
|
routes: {
|
|
title: "Routen",
|
|
myRoutes: "Meine Routen",
|
|
new: "Neue Route",
|
|
edit: "Route bearbeiten",
|
|
editInPlanner: "Im Planer bearbeiten",
|
|
opening: "Öffne...",
|
|
importGpx: "GPX importieren",
|
|
exportGpx: "GPX exportieren",
|
|
delete: "Route löschen",
|
|
distance: "Strecke",
|
|
elevationGain: "Höhenmeter",
|
|
noRoutesYet: "Noch keine Routen. Erstelle deine erste Route!",
|
|
noMapPreview: "Keine Kartenvorschau",
|
|
saveChanges: "Änderungen speichern",
|
|
},
|
|
activities: {
|
|
title: "Aktivitäten",
|
|
new: "Neue Aktivität",
|
|
duration: "Dauer",
|
|
linkToRoute: "Mit Route verknüpfen",
|
|
createRouteFromActivity: "Route aus Aktivität erstellen",
|
|
delete: "Aktivität löschen",
|
|
deleteConfirm: "Möchtest du diese Aktivität wirklich löschen?",
|
|
importedFrom: "Importiert von {{provider}}",
|
|
},
|
|
settings: {
|
|
title: "Einstellungen",
|
|
profile: {
|
|
title: "Profil",
|
|
displayName: "Anzeigename",
|
|
bio: "Bio",
|
|
saved: "Profil gespeichert.",
|
|
},
|
|
security: {
|
|
title: "Sicherheit",
|
|
description: "Verwalte deine Passkeys zum Anmelden.",
|
|
addPasskey: "Passkey hinzufügen",
|
|
passkey: "Passkey",
|
|
thisDevice: "Dieses Gerät",
|
|
securityKey: "Sicherheitsschlüssel",
|
|
bluetooth: "Bluetooth",
|
|
phoneOrTablet: "Telefon oder Tablet",
|
|
addedOn: "Hinzugefügt am {{date}}",
|
|
noPasskeys: "Keine Passkeys registriert. Du kannst dich per Magic Link anmelden.",
|
|
deleteConfirm: "Möchtest du diesen Passkey wirklich löschen?",
|
|
deleteLastWarning: "Dies ist dein einziger Passkey. Du musst Magic Links zum Anmelden verwenden. Trotzdem löschen?",
|
|
},
|
|
account: {
|
|
title: "Konto",
|
|
changeEmail: "Ändern",
|
|
newEmailPlaceholder: "Neue E-Mail-Adresse",
|
|
sendVerification: "Bestätigen",
|
|
verificationSent: "Bestätigungs-E-Mail gesendet. Prüfe deinen Posteingang.",
|
|
dangerZone: "Gefahrenzone",
|
|
deleteDescription: "Lösche dein Konto und alle zugehörigen Daten unwiderruflich.",
|
|
deleteAccount: "Konto löschen",
|
|
deleteConfirmPrompt: "Gib \"{{username}}\" ein, um die Löschung zu bestätigen:",
|
|
confirmDelete: "Endgültig löschen",
|
|
},
|
|
services: {
|
|
title: "Verbundene Dienste",
|
|
connect: "Verbinden",
|
|
disconnect: "Trennen",
|
|
connectedAs: "Verbunden (ID: {{id}})",
|
|
},
|
|
},
|
|
sync: {
|
|
import: "Importieren",
|
|
importFrom: "Import von {{provider}}",
|
|
imported: "Importiert",
|
|
noWorkouts: "Keine Workouts gefunden.",
|
|
noGps: "Kein GPS",
|
|
noGpsTooltip: "{{provider}} stellt für diese Aktivität keine Routendaten bereit",
|
|
importing: "Importiere...",
|
|
importAll: "Alle importieren ({{count}})",
|
|
importingProgress: "Importiere {{current}} von {{total}}...",
|
|
previous: "Zurück",
|
|
next: "Weiter",
|
|
},
|
|
nav: {
|
|
routes: "Routen",
|
|
activities: "Aktivitäten",
|
|
login: "Anmelden",
|
|
register: "Registrieren",
|
|
profile: "Profil",
|
|
settings: "Einstellungen",
|
|
logout: "Abmelden",
|
|
},
|
|
auth: {
|
|
login: "Anmelden",
|
|
register: "Registrieren",
|
|
logout: "Abmelden",
|
|
email: "E-Mail",
|
|
username: "Benutzername",
|
|
signInWithPasskey: "Mit Passkey anmelden",
|
|
authenticating: "Authentifiziere...",
|
|
useMagicLink: "Kein Passkey auf diesem Gerät? Nutze stattdessen einen Magic Link",
|
|
magicLinkHelp: "Wir senden dir einen Anmeldelink per E-Mail.",
|
|
sendMagicLink: "Magic Link senden",
|
|
sending: "Wird gesendet...",
|
|
backToPasskey: "Zurück zur Passkey-Anmeldung",
|
|
checkEmail: "Prüfe deine E-Mails! Wir haben einen Anmeldelink gesendet an",
|
|
linkExpires: "Der Link ist 15 Minuten gültig.",
|
|
noAccount: "Noch kein Konto?",
|
|
registerDescription: "Registriere dich mit einem Passkey — kein Passwort nötig.",
|
|
registerWithPasskey: "Mit Passkey registrieren",
|
|
creatingPasskey: "Erstelle Passkey...",
|
|
passkeyNotSupported: "Dein Browser unterstützt keine Passkeys. Bitte verwende einen anderen Browser zur Registrierung.",
|
|
passkeyNotSupportedRegister: "Dein Browser unterstützt keine Passkeys. Du kannst dich stattdessen per Magic Link registrieren und später einen Passkey hinzufügen.",
|
|
registerWithMagicLink: "Per Magic Link registrieren",
|
|
},
|
|
},
|
|
} as const;
|