trails/packages/i18n/src/locales/de.ts
Ullrich Schäfer b60cd96736
Revoke Wahoo tokens on disconnect; surface OAuth errors
Disconnecting a sync provider now calls the provider's revoke endpoint
(DELETE /v1/permissions for Wahoo) before dropping the local row, so
tokens don't accumulate against Wahoo's per-(app,user) cap. The token
exchange now classifies Wahoo's "Too many unrevoked access tokens" 400
as a distinct OAuthError code, and the connections settings page shows
a localized banner for that and other connect failures instead of a
silent ?error=sync_failed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 10:03:50 +02:00

507 lines
20 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",
exportDays: "Tagesetappen exportieren",
exportDaysDesc: "Eine GPX-Datei pro Tag",
"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",
},
multiDay: {
dayLabel: "Tag {{n}}",
dayCount: "{{count}} Tage",
dayCount_one: "1 Tag",
overnight: "Übernachtung",
markOvernight: "Als Übernachtung markieren",
removeOvernight: "Übernachtung entfernen",
distance: "Distanz",
ascent: "Anstieg",
descent: "Abstieg",
},
poi: {
toggle: "Sehenswürdigkeiten",
title: "Sehenswürdigkeiten",
loading: "Laden...",
zoomIn: "Näher heranzoomen für POIs",
rateLimited: "POI-Daten vorübergehend nicht verfügbar",
error: "POIs konnten nicht geladen werden",
drinkingWater: "Trinkwasser",
shelter: "Unterstand",
camping: "Camping",
food: "Essen & Trinken",
groceries: "Lebensmittel",
bikeInfra: "Fahrrad-Infrastruktur",
accommodation: "Unterkunft",
viewpoints: "Aussichtspunkte",
toilets: "Toiletten",
},
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",
grade: "Steigung",
highway: "Straßentyp",
maxspeed: "Tempolimit",
smoothness: "Fahrbahnqualität",
tracktype: "Wegtyp",
cycleway: "Radweg",
bikeroute: "Radroute",
surfaceLegend: "Farbe nach Straßenbelag",
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",
grade: "Steigungsprofil",
highway: "Straßentypenprofil",
maxspeed: "Tempolimitprofil",
smoothness: "Fahrbahnqualitätsprofil",
tracktype: "Wegtypenprofil",
cycleway: "Radwegprofil",
bikeroute: "Radroutenprofil",
low: "Tief",
high: "Hoch",
},
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: {
imprint: "Impressum",
privacy: "Datenschutz",
terms: "Nutzungsbedingungen",
source: "Quellcode",
builtWith: "Erstellt mit BRouter und OpenStreetMap",
alpha: "Alpha-Version",
},
},
},
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",
home: {
heroTitle: "Föderiertes Outdoor-Tagebuch",
heroSubtitle: "Plane Routen, halte Aktivitäten fest, behalte deine Daten.",
tryPlannerPrefix: "Oder ",
tryPlannerLink: "öffne den Planer ohne Konto",
tryPlannerSuffix: " →",
exploreLink: "Wer ist hier? →",
marketing: {
planner: {
title: "Routen gemeinsam planen",
body: "Skizziere Wander- und Radrouten gemeinsam in Echtzeit. Ohne Konto.",
},
journal: {
title: "Aktivitäten festhalten",
body: "Halte deine Touren, Wanderungen und Spaziergänge fest. Privat, mit Freunden geteilt oder öffentlich.",
},
federation: {
title: "Föderiert by design",
body: "Deine Journal-Instanz spricht via ActivityPub mit anderen trails.cool-Servern. Folge Freundinnen und Freunden überall.",
},
ownership: {
title: "Deine Daten, immer",
body: "Keine Werbung, kein Tracking, kein Datenverkauf. Export als GPX oder JSON jederzeit. MIT-lizenziert und selbst hostbar.",
},
},
feed: {
heading: "Aktuelle Aktivitäten",
empty: "Noch keine öffentlichen Aktivitäten.",
},
dashboardEmpty: "Noch keine Aktivitäten. Erfasse oder importiere deine erste im Aktivitäten-Tab.",
poweredBy: "Powered by trails.cool — über das Projekt",
},
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",
sendToWahoo: "An Wahoo senden",
sendToWahooHelp: "Sendet die Route an Wahoo, damit sie auf deinem Bike-Computer erscheint.",
sentToWahoo: "Am {{date}} an Wahoo gesendet",
sendToWahooFailed: "Letzter Versuch fehlgeschlagen: {{error}}",
sendToWahooSending: "Sende…",
sendToWahooBanner: {
success: "Route an Wahoo gesendet. Schau in der Wahoo-App oder auf deinem Bike-Computer nach.",
needsPermission: "Wir brauchen deine Erlaubnis, Routen an Wahoo zu senden. Bitte neu verbinden.",
noConnection: "Verbinde zuerst dein Wahoo-Konto in den Einstellungen.",
noGeometry: "Diese Route hat noch keine Geometrie — plane zuerst einen Weg.",
validation: "Senden fehlgeschlagen — versuche, Name oder Beschreibung anzupassen.",
rateLimit: "Zu viele Anfragen an Wahoo. Bitte in einer Minute erneut versuchen.",
tokenExpired: "Deine Wahoo-Verbindung ist abgelaufen. Bitte in den Einstellungen neu verbinden.",
generic: "Senden an Wahoo fehlgeschlagen. Bitte später erneut versuchen.",
},
distance: "Strecke",
elevationGain: "Höhenmeter",
dayBreakdown: "Tagesübersicht",
dayLabel: "Tag {{n}}",
noRoutesYet: "Noch keine Routen. Erstelle deine erste Route!",
noMapPreview: "Keine Kartenvorschau",
saveChanges: "Änderungen speichern",
empty: {
heading: "Diese Route ist leer",
bodyOwner: "Du hast noch keine Wegpunkte geplant. Öffne sie im Planer, um eine Strecke zu skizzieren, oder lade eine GPX-Datei hoch.",
bodyViewer: "Diese Route enthält noch keine Wegpunkte.",
openInPlanner: "Im Planer öffnen",
uploadGpx: "oder eine GPX-Datei hochladen",
},
visibility: {
label: "Sichtbarkeit",
private: "Privat",
unlisted: "Nicht gelistet (nur mit Link sichtbar)",
public: "Öffentlich (für alle sichtbar)",
privateHelp: "Nur du kannst diese Route sehen.",
unlistedHelp: "Jede:r mit der URL kann sie ansehen; sie erscheint nicht auf deinem öffentlichen Profil.",
publicHelp: "Sichtbar für alle, auch nicht eingeloggte Besucher:innen; erscheint auf deinem öffentlichen Profil.",
},
},
profile: {
ownNote: "Das ist dein Profil — Besucher:innen sehen nur Inhalte, die du als öffentlich markiert hast.",
privateNote: "Dein Profil ist auf Privat (gesperrt) gestellt. Besucher:innen sehen eine Hinweisseite mit einem Anfrage-Button; nur akzeptierte Follower sehen deine öffentlichen Inhalte.",
goToSettings: "Zu den Einstellungen",
noPublicRoutes: "Noch keine öffentlichen Routen.",
noPublicActivities: "Noch keine öffentlichen Aktivitäten.",
lockedBadge: "Privat",
lockedTitle: "Dieses Profil ist privat. Folge-Anfragen müssen genehmigt werden.",
privateStub: {
heading: "Dieses Profil ist privat",
bodyAuth: "Sende eine Folge-Anfrage, um die öffentlichen Routen und Aktivitäten zu sehen. Die Person muss sie freigeben.",
bodyAnon: "Melde dich an und sende eine Folge-Anfrage, um die öffentlichen Routen und Aktivitäten zu sehen.",
},
},
notifications: {
title: "Benachrichtigungen",
empty: "Noch keine Benachrichtigungen.",
markAllRead: "Alle als gelesen markieren",
loadOlder: "Ältere laden",
someone: "Jemand",
tabs: {
activity: "Aktivität",
requests: "Anfragen",
},
summary: {
followRequestReceived: "{{name}} möchte dir folgen",
followReceived: "{{name}} folgt dir jetzt",
followRequestApproved: "{{name}} hat deine Folge-Anfrage akzeptiert",
activityPublished: "{{owner}} hat „{{activity}}“ gepostet",
},
},
social: {
follow: "Folgen",
unfollow: "Entfolgen",
requestToFollow: "Folgen anfragen",
pendingCancel: "Angefragt",
requests: {
title: "Folge-Anfragen",
empty: "Keine offenen Folge-Anfragen.",
approve: "Annehmen",
reject: "Ablehnen",
receivedAt: "Angefragt am {{date}}",
},
followers: {
label: "Follower",
heading: "Follower von {{user}}",
count: "{{count}} Follower",
count_other: "{{count}} Follower",
empty: "Noch niemand.",
},
following: {
label: "Folgt",
heading: "{{user}} folgt",
count: "Folgt {{count}}",
count_other: "Folgt {{count}}",
empty: "Folgt noch niemandem.",
},
prevPage: "Zurück",
nextPage: "Weiter",
pageOfTotal: "Seite {{page}} von {{totalPages}}",
feed: {
title: "Feed",
heading: "Folge ich",
empty: "Du folgst noch niemandem. Stöbere Profile durch und klicke auf Folgen, um deinen Feed aufzubauen.",
seePublic: "Oder durchstöbere den öffentlichen Feed dieser Instanz →",
toggle: {
followed: "Folge ich",
public: "Öffentlich",
},
public: {
heading: "Öffentlich",
empty: "Noch keine öffentlichen Aktivitäten auf dieser Instanz.",
},
},
},
demo: {
badge: "🐕 Demo-Konto",
},
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}}",
visibility: {
label: "Sichtbarkeit",
private: "Privat",
unlisted: "Nicht gelistet (nur mit Link sichtbar)",
public: "Öffentlich (für alle sichtbar)",
},
},
settings: {
title: "Einstellungen",
nav: {
profile: "Profil",
account: "Konto",
security: "Sicherheit",
connections: "Verbindungen",
},
profile: {
title: "Profil",
displayName: "Anzeigename",
bio: "Bio",
saved: "Profil gespeichert.",
visibility: {
label: "Profil-Sichtbarkeit",
public: "Öffentlich",
publicHelp: "Alle sehen dein Profil und deine öffentlichen Inhalte. Folge-Anfragen werden automatisch akzeptiert.",
private: "Privat (gesperrt)",
privateHelp: "Besucher:innen sehen nur eine Hinweisseite mit Anfrage-Button. Nur akzeptierte Follower sehen deine öffentlichen Inhalte. Anfragen verwaltest du im Reiter „Anfragen“ unter /notifications.",
},
},
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}})",
errors: {
too_many_tokens:
"Für diesen Dienst gibt es zu viele aktive Autorisierungen für dein Konto. Öffne die App des Anbieters, widerrufe dort die trails.cool-Autorisierung und versuche es dann erneut.",
sync_failed: "Verbindung fehlgeschlagen — der Anbieter hat die Anfrage abgelehnt. Bitte versuche es erneut.",
generic: "Verbindung fehlgeschlagen — der Anbieter hat die Anfrage abgelehnt. Bitte versuche es erneut.",
},
},
},
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",
explore: "Entdecken",
login: "Anmelden",
register: "Registrieren",
profile: "Profil",
settings: "Einstellungen",
logout: "Abmelden",
openMenu: "Menü öffnen",
closeMenu: "Menü schließen",
},
explore: {
heading: "Entdecken",
empty: "Auf dieser Instanz hat noch niemand ein öffentliches Profil.",
activeRecently: {
heading: "Kürzlich aktiv",
},
directory: {
heading: "Alle",
},
},
alpha: {
message: "trails.cool befindet sich in früher Entwicklung — deine Daten können zurückgesetzt werden.",
},
footer: {
imprint: "Impressum",
privacy: "Datenschutz",
terms: "Nutzungsbedingungen",
source: "Quellcode",
alpha: "Alpha-Version",
},
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.",
codeHelp: "Oder gib den 6-stelligen Code aus der E-Mail ein:",
verifyCode: "Code bestätigen",
termsBefore: "Ich habe die ",
termsLink: "Nutzungsbedingungen",
termsAfter: " gelesen und akzeptiere sie.",
termsRequired: "Du musst die Nutzungsbedingungen akzeptieren.",
noAccount: "Noch kein Konto?",
alreadyHaveAccount: "Bereits ein Konto?",
handleWillBe: "Dein Handle wird {{handle}} sein",
passkeyNotFound: "Für diese Seite wurde kein Passkey gefunden. Registriere ein neues Konto oder nutze stattdessen einen Magic Link.",
reaccept: {
heading: "Aktualisierte Nutzungsbedingungen",
bodyUpdated: "Die Nutzungsbedingungen wurden aktualisiert (von Version {{from}} auf {{to}}). Bitte lies und akzeptiere sie, um trails.cool weiter zu nutzen.",
bodyNew: "Ab sofort erfassen wir, welche Version der Nutzungsbedingungen du akzeptiert hast. Bitte lies die aktuelle Version ({{version}}) und akzeptiere sie, um trails.cool weiter zu nutzen.",
submit: "Akzeptieren und fortfahren",
logoutInstead: "Stattdessen abmelden",
},
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",
},
},
mobile: {
routes: {
empty: "Noch keine Routen",
emptyHint: "Erstelle deine erste Route im Planner",
retry: "Erneut versuchen",
},
activities: {
empty: "Noch keine Aktivitäten",
emptyHint: "Zeichne deine erste Aktivität auf oder importiere sie",
},
profile: {
logOut: "Abmelden",
switchServer: "Server wechseln",
switchServerConfirm: "Du wirst abgemeldet und lokale Daten werden gelöscht. Fortfahren?",
},
login: {
subtitle: "Bei deinem Journal anmelden",
signIn: "Anmelden",
connectDifferentServer: "Mit anderem Server verbinden",
serverUrl: "Server-URL",
},
},
} as const;