trails/packages/i18n/src/locales/de.ts
Ullrich Schäfer 20b91ef511
Fix Wahoo import bugs, add Import All, update spec
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>
2026-04-05 17:47:48 +02:00

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;