feat(calendar): localize sync and sharing settings pages

Replace ~70 hardcoded German strings with i18n keys in sync and
sharing settings pages. All 5 languages (DE/EN/FR/ES/IT).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-24 10:28:02 +01:00
parent 1a91bd7cfb
commit 0110bff25d
7 changed files with 784 additions and 88 deletions

View file

@ -124,7 +124,73 @@
"search": "Suchen",
"error": "Fehler",
"success": "Erfolgreich",
"calendar": "Kalender"
"calendar": "Kalender",
"create": "Erstellen"
},
"settings": {
"myCalendars": "Meine Kalender",
"externalCalendars": "Externe Kalender",
"shares": "Kalender-Freigaben",
"appSettings": "App-Einstellungen",
"appSettingsDesc": "Diese Einstellungen werden mit allen Mana Apps synchronisiert",
"calendarView": "Kalender-Ansicht",
"events": "Termine",
"birthdays": "Geburtstage",
"account": "Konto",
"newCalendar": "Neuer Kalender",
"calendarName": "Kalender Name",
"name": "Name",
"color": "Farbe",
"default": "Standard",
"setAsDefault": "Als Standardkalender verwenden",
"currentDefault": "aktueller Standard",
"noCalendars": "Keine Kalender vorhanden",
"calendarCreated": "Kalender erstellt",
"calendarUpdated": "Kalender aktualisiert",
"calendarDeleted": "Kalender gelöscht",
"confirmDeleteCalendar": "Möchten Sie \"{name}\" wirklich löschen?",
"externalCalendarsDesc": "Verbinde Google Calendar, Apple Calendar, CalDAV oder iCal-URLs.",
"manageSync": "Kalender-Sync verwalten",
"sharesDesc": "Teile Kalender mit anderen Nutzern oder verwalte Einladungen.",
"manageShares": "Freigaben verwalten",
"defaultView": "Standard-Ansicht",
"defaultViewDesc": "Ansicht beim Öffnen des Kalenders",
"selectView": "Ansicht wählen",
"viewWeek": "Woche",
"viewMonth": "Monat",
"viewAgenda": "Agenda",
"timeFormat": "Zeitformat",
"timeFormatDesc": "Anzeige der Uhrzeiten",
"weekdaysOnly": "Nur Werktage anzeigen",
"weekdaysOnlyDesc": "Wochenenden in der Kalenderansicht ausblenden",
"showWeekNumbers": "Wochennummern anzeigen",
"showWeekNumbersDesc": "Kalenderwoche (KW) in der Ansicht anzeigen",
"filterHours": "Stunden filtern",
"filterHoursDesc": "Nur bestimmte Stunden in der Tages-/Wochenansicht anzeigen",
"visibleHours": "Sichtbare Stunden",
"visibleHoursDesc": "Zeitbereich der in der Kalenderansicht angezeigt wird",
"hoursFrom": "Von",
"hoursTo": "Bis",
"allDayEvents": "Ganztägige Termine",
"allDayEventsDesc": "Wie sollen ganztägige Termine angezeigt werden?",
"allDayInHeader": "In Kopfzeile",
"allDayAsBlock": "Als Tagesblock",
"defaultDuration": "Standard-Dauer",
"defaultDurationDesc": "Voreingestellte Dauer für neue Termine",
"selectDuration": "Dauer wählen",
"durationMinutes": "{count} Minuten",
"durationHours": "{count} Stunde(n)",
"defaultReminder": "Standard-Erinnerung",
"defaultReminderDesc": "Voreingestellte Erinnerung für neue Termine",
"selectReminder": "Erinnerung wählen",
"reminderNone": "Keine",
"reminderMinutes": "{count} Minuten",
"reminderHour": "1 Stunde",
"reminderDay": "1 Tag",
"showBirthdays": "Geburtstage anzeigen",
"showBirthdaysDesc": "Geburtstage aus Kontakten im Kalender anzeigen",
"showAge": "Alter anzeigen",
"showAgeDesc": "Das Alter der Person bei Geburtstagen anzeigen"
},
"errors": {
"loadEvents": "Termine konnten nicht geladen werden",
@ -154,5 +220,88 @@
"error": {
"notFound": "Seite nicht gefunden",
"backToHome": "Zurück zur Startseite"
},
"sync": {
"pageTitle": "Kalender-Sync - Einstellungen",
"title": "Kalender-Sync",
"back": "Zurück",
"connectCalendar": "Kalender verbinden",
"description": "Verbinde externe Kalender, um Termine zu importieren und zu synchronisieren.",
"emptyState": "Keine externen Kalender verbunden",
"syncNow": "Jetzt synchronisieren",
"disconnect": "Verbindung trennen",
"confirmDisconnect": "\"{name}\" wirklich trennen? Synchronisierte Termine werden gelöscht.",
"neverSynced": "Noch nie",
"directionLabel": "Richtung",
"lastSync": "Letzte Sync",
"statusLabel": "Status",
"autoSync": "Auto-Sync",
"connectCaldav": "CalDAV-Server verbinden",
"connectProvider": "{provider} verbinden",
"searching": "Suche...",
"searchCalendars": "Kalender suchen",
"discoveredCalendars": "Gefundene Kalender:",
"connecting": "Verbinde...",
"connect": "Verbinden",
"direction": {
"import": "Nur Import",
"export": "Nur Export",
"both": "Bidirektional"
},
"status": {
"error": "Fehler",
"active": "Aktiv (alle {interval} Min.)",
"paused": "Pausiert"
},
"providers": {
"icalUrl": "iCal URL",
"icalUrlDesc": "ICS-Link importieren (z.B. Feiertage)",
"caldav": "CalDAV",
"caldavDesc": "CalDAV-Server verbinden",
"google": "Google Calendar",
"googleDesc": "Mit Google Kalender synchronisieren",
"apple": "Apple Calendar",
"appleDesc": "iCloud Kalender verbinden"
},
"form": {
"serverUrl": "Server-URL",
"username": "Benutzername",
"password": "Passwort",
"name": "Name",
"namePlaceholder": "Mein externer Kalender",
"url": "URL",
"syncDirection": "Sync-Richtung"
}
},
"sharing": {
"pageTitle": "Kalender-Freigaben - Einstellungen",
"title": "Freigaben",
"back": "Zurück",
"shareCalendar": "Kalender teilen",
"invitations": "Einladungen ({count})",
"calendarInvitation": "Kalender-Einladung",
"access": "Zugriff",
"accept": "Annehmen",
"sharedWithMe": "Mit mir geteilt",
"sharedCalendar": "Geteilter Kalender",
"shareMyCalendars": "Meine Kalender teilen",
"notSharedYet": "Noch nicht geteilt",
"linkShare": "Link-Freigabe",
"pending": "Ausstehend",
"removeShare": "Freigabe entfernen",
"confirmRemoveShare": "Freigabe wirklich entfernen?",
"addPerson": "Person hinzufügen",
"share": "Teilen",
"sharing": "Teile...",
"permission": {
"read": "Lesen",
"write": "Lesen & Bearbeiten",
"admin": "Administrator"
},
"form": {
"calendar": "Kalender",
"email": "E-Mail-Adresse",
"permission": "Berechtigung"
}
}
}

View file

@ -124,7 +124,73 @@
"search": "Search",
"error": "Error",
"success": "Success",
"calendar": "Calendar"
"calendar": "Calendar",
"create": "Create"
},
"settings": {
"myCalendars": "My Calendars",
"externalCalendars": "External Calendars",
"shares": "Calendar Sharing",
"appSettings": "App Settings",
"appSettingsDesc": "These settings are synced across all Mana apps",
"calendarView": "Calendar View",
"events": "Events",
"birthdays": "Birthdays",
"account": "Account",
"newCalendar": "New Calendar",
"calendarName": "Calendar Name",
"name": "Name",
"color": "Color",
"default": "Default",
"setAsDefault": "Set as default calendar",
"currentDefault": "current default",
"noCalendars": "No calendars available",
"calendarCreated": "Calendar created",
"calendarUpdated": "Calendar updated",
"calendarDeleted": "Calendar deleted",
"confirmDeleteCalendar": "Are you sure you want to delete \"{name}\"?",
"externalCalendarsDesc": "Connect Google Calendar, Apple Calendar, CalDAV or iCal URLs.",
"manageSync": "Manage calendar sync",
"sharesDesc": "Share calendars with other users or manage invitations.",
"manageShares": "Manage sharing",
"defaultView": "Default View",
"defaultViewDesc": "View when opening the calendar",
"selectView": "Select view",
"viewWeek": "Week",
"viewMonth": "Month",
"viewAgenda": "Agenda",
"timeFormat": "Time Format",
"timeFormatDesc": "How times are displayed",
"weekdaysOnly": "Show weekdays only",
"weekdaysOnlyDesc": "Hide weekends in the calendar view",
"showWeekNumbers": "Show week numbers",
"showWeekNumbersDesc": "Display week numbers in the calendar view",
"filterHours": "Filter hours",
"filterHoursDesc": "Only show certain hours in day/week view",
"visibleHours": "Visible hours",
"visibleHoursDesc": "Time range displayed in the calendar view",
"hoursFrom": "From",
"hoursTo": "To",
"allDayEvents": "All-day events",
"allDayEventsDesc": "How should all-day events be displayed?",
"allDayInHeader": "In header",
"allDayAsBlock": "As day block",
"defaultDuration": "Default Duration",
"defaultDurationDesc": "Default duration for new events",
"selectDuration": "Select duration",
"durationMinutes": "{count} minutes",
"durationHours": "{count} hour(s)",
"defaultReminder": "Default Reminder",
"defaultReminderDesc": "Default reminder for new events",
"selectReminder": "Select reminder",
"reminderNone": "None",
"reminderMinutes": "{count} minutes",
"reminderHour": "1 hour",
"reminderDay": "1 day",
"showBirthdays": "Show birthdays",
"showBirthdaysDesc": "Show birthdays from contacts in the calendar",
"showAge": "Show age",
"showAgeDesc": "Display the person's age on birthdays"
},
"errors": {
"loadEvents": "Failed to load events",
@ -154,5 +220,88 @@
"error": {
"notFound": "Page not found",
"backToHome": "Back to home"
},
"sync": {
"pageTitle": "Calendar Sync - Settings",
"title": "Calendar Sync",
"back": "Back",
"connectCalendar": "Connect calendar",
"description": "Connect external calendars to import and synchronize events.",
"emptyState": "No external calendars connected",
"syncNow": "Sync now",
"disconnect": "Disconnect",
"confirmDisconnect": "Really disconnect \"{name}\"? Synchronized events will be deleted.",
"neverSynced": "Never",
"directionLabel": "Direction",
"lastSync": "Last sync",
"statusLabel": "Status",
"autoSync": "Auto-Sync",
"connectCaldav": "Connect CalDAV server",
"connectProvider": "Connect {provider}",
"searching": "Searching...",
"searchCalendars": "Search calendars",
"discoveredCalendars": "Discovered calendars:",
"connecting": "Connecting...",
"connect": "Connect",
"direction": {
"import": "Import only",
"export": "Export only",
"both": "Bidirectional"
},
"status": {
"error": "Error",
"active": "Active (every {interval} min.)",
"paused": "Paused"
},
"providers": {
"icalUrl": "iCal URL",
"icalUrlDesc": "Import ICS link (e.g. holidays)",
"caldav": "CalDAV",
"caldavDesc": "Connect CalDAV server",
"google": "Google Calendar",
"googleDesc": "Sync with Google Calendar",
"apple": "Apple Calendar",
"appleDesc": "Connect iCloud Calendar"
},
"form": {
"serverUrl": "Server URL",
"username": "Username",
"password": "Password",
"name": "Name",
"namePlaceholder": "My external calendar",
"url": "URL",
"syncDirection": "Sync direction"
}
},
"sharing": {
"pageTitle": "Calendar Sharing - Settings",
"title": "Sharing",
"back": "Back",
"shareCalendar": "Share calendar",
"invitations": "Invitations ({count})",
"calendarInvitation": "Calendar invitation",
"access": "access",
"accept": "Accept",
"sharedWithMe": "Shared with me",
"sharedCalendar": "Shared calendar",
"shareMyCalendars": "Share my calendars",
"notSharedYet": "Not shared yet",
"linkShare": "Link share",
"pending": "Pending",
"removeShare": "Remove share",
"confirmRemoveShare": "Really remove this share?",
"addPerson": "Add person",
"share": "Share",
"sharing": "Sharing...",
"permission": {
"read": "Read",
"write": "Read & Edit",
"admin": "Admin"
},
"form": {
"calendar": "Calendar",
"email": "Email address",
"permission": "Permission"
}
}
}

View file

@ -117,10 +117,159 @@
"search": "Buscar",
"error": "Error",
"success": "Éxito",
"calendar": "Calendario"
"calendar": "Calendario",
"create": "Crear"
},
"settings": {
"myCalendars": "Mis calendarios",
"externalCalendars": "Calendarios externos",
"shares": "Compartir calendarios",
"appSettings": "Configuración de la app",
"appSettingsDesc": "Estos ajustes se sincronizan con todas las apps de Mana",
"calendarView": "Vista del calendario",
"events": "Eventos",
"birthdays": "Cumpleaños",
"account": "Cuenta",
"newCalendar": "Nuevo calendario",
"calendarName": "Nombre del calendario",
"name": "Nombre",
"color": "Color",
"default": "Predeterminado",
"setAsDefault": "Establecer como calendario predeterminado",
"currentDefault": "predeterminado actual",
"noCalendars": "No hay calendarios disponibles",
"calendarCreated": "Calendario creado",
"calendarUpdated": "Calendario actualizado",
"calendarDeleted": "Calendario eliminado",
"confirmDeleteCalendar": "¿Seguro que quieres eliminar \"{name}\"?",
"externalCalendarsDesc": "Conecta Google Calendar, Apple Calendar, CalDAV o URLs de iCal.",
"manageSync": "Gestionar sincronización",
"sharesDesc": "Comparte calendarios con otros usuarios o gestiona invitaciones.",
"manageShares": "Gestionar compartidos",
"defaultView": "Vista predeterminada",
"defaultViewDesc": "Vista al abrir el calendario",
"selectView": "Seleccionar vista",
"viewWeek": "Semana",
"viewMonth": "Mes",
"viewAgenda": "Agenda",
"timeFormat": "Formato de hora",
"timeFormatDesc": "Cómo se muestran las horas",
"weekdaysOnly": "Mostrar solo días laborables",
"weekdaysOnlyDesc": "Ocultar fines de semana en la vista del calendario",
"showWeekNumbers": "Mostrar números de semana",
"showWeekNumbersDesc": "Mostrar el número de semana en la vista del calendario",
"filterHours": "Filtrar horas",
"filterHoursDesc": "Mostrar solo ciertas horas en la vista de día/semana",
"visibleHours": "Horas visibles",
"visibleHoursDesc": "Rango horario mostrado en la vista del calendario",
"hoursFrom": "Desde",
"hoursTo": "Hasta",
"allDayEvents": "Eventos de todo el día",
"allDayEventsDesc": "¿Cómo se deben mostrar los eventos de todo el día?",
"allDayInHeader": "En encabezado",
"allDayAsBlock": "Como bloque diario",
"defaultDuration": "Duración predeterminada",
"defaultDurationDesc": "Duración predeterminada para nuevos eventos",
"selectDuration": "Seleccionar duración",
"durationMinutes": "{count} minutos",
"durationHours": "{count} hora(s)",
"defaultReminder": "Recordatorio predeterminado",
"defaultReminderDesc": "Recordatorio predeterminado para nuevos eventos",
"selectReminder": "Seleccionar recordatorio",
"reminderNone": "Ninguno",
"reminderMinutes": "{count} minutos",
"reminderHour": "1 hora",
"reminderDay": "1 día",
"showBirthdays": "Mostrar cumpleaños",
"showBirthdaysDesc": "Mostrar cumpleaños de contactos en el calendario",
"showAge": "Mostrar edad",
"showAgeDesc": "Mostrar la edad de la persona en los cumpleaños"
},
"error": {
"notFound": "Página no encontrada",
"backToHome": "Volver al inicio"
},
"sync": {
"pageTitle": "Sincronización de calendarios - Configuración",
"title": "Sincronización",
"back": "Volver",
"connectCalendar": "Conectar calendario",
"description": "Conecta calendarios externos para importar y sincronizar eventos.",
"emptyState": "No hay calendarios externos conectados",
"syncNow": "Sincronizar ahora",
"disconnect": "Desconectar",
"confirmDisconnect": "¿Realmente desconectar \"{name}\"? Los eventos sincronizados serán eliminados.",
"neverSynced": "Nunca",
"directionLabel": "Dirección",
"lastSync": "Última sincronización",
"statusLabel": "Estado",
"autoSync": "Sincronización automática",
"connectCaldav": "Conectar servidor CalDAV",
"connectProvider": "Conectar {provider}",
"searching": "Buscando...",
"searchCalendars": "Buscar calendarios",
"discoveredCalendars": "Calendarios encontrados:",
"connecting": "Conectando...",
"connect": "Conectar",
"direction": {
"import": "Solo importar",
"export": "Solo exportar",
"both": "Bidireccional"
},
"status": {
"error": "Error",
"active": "Activo (cada {interval} min.)",
"paused": "Pausado"
},
"providers": {
"icalUrl": "URL iCal",
"icalUrlDesc": "Importar enlace ICS (ej. festivos)",
"caldav": "CalDAV",
"caldavDesc": "Conectar servidor CalDAV",
"google": "Google Calendar",
"googleDesc": "Sincronizar con Google Calendar",
"apple": "Apple Calendar",
"appleDesc": "Conectar calendario de iCloud"
},
"form": {
"serverUrl": "URL del servidor",
"username": "Nombre de usuario",
"password": "Contraseña",
"name": "Nombre",
"namePlaceholder": "Mi calendario externo",
"url": "URL",
"syncDirection": "Dirección de sincronización"
}
},
"sharing": {
"pageTitle": "Compartir calendarios - Configuración",
"title": "Compartidos",
"back": "Volver",
"shareCalendar": "Compartir calendario",
"invitations": "Invitaciones ({count})",
"calendarInvitation": "Invitación de calendario",
"access": "acceso",
"accept": "Aceptar",
"sharedWithMe": "Compartidos conmigo",
"sharedCalendar": "Calendario compartido",
"shareMyCalendars": "Compartir mis calendarios",
"notSharedYet": "Aún no compartido",
"linkShare": "Compartir por enlace",
"pending": "Pendiente",
"removeShare": "Eliminar compartido",
"confirmRemoveShare": "¿Realmente eliminar este compartido?",
"addPerson": "Agregar persona",
"share": "Compartir",
"sharing": "Compartiendo...",
"permission": {
"read": "Lectura",
"write": "Lectura y edición",
"admin": "Administrador"
},
"form": {
"calendar": "Calendario",
"email": "Dirección de correo",
"permission": "Permiso"
}
}
}

View file

@ -117,10 +117,159 @@
"search": "Rechercher",
"error": "Erreur",
"success": "Succès",
"calendar": "Calendrier"
"calendar": "Calendrier",
"create": "Créer"
},
"settings": {
"myCalendars": "Mes calendriers",
"externalCalendars": "Calendriers externes",
"shares": "Partage de calendriers",
"appSettings": "Paramètres de l'app",
"appSettingsDesc": "Ces paramètres sont synchronisés avec toutes les apps Mana",
"calendarView": "Vue du calendrier",
"events": "Événements",
"birthdays": "Anniversaires",
"account": "Compte",
"newCalendar": "Nouveau calendrier",
"calendarName": "Nom du calendrier",
"name": "Nom",
"color": "Couleur",
"default": "Par défaut",
"setAsDefault": "Définir comme calendrier par défaut",
"currentDefault": "par défaut actuel",
"noCalendars": "Aucun calendrier disponible",
"calendarCreated": "Calendrier créé",
"calendarUpdated": "Calendrier mis à jour",
"calendarDeleted": "Calendrier supprimé",
"confirmDeleteCalendar": "Voulez-vous vraiment supprimer \"{name}\" ?",
"externalCalendarsDesc": "Connectez Google Calendar, Apple Calendar, CalDAV ou des URLs iCal.",
"manageSync": "Gérer la synchronisation",
"sharesDesc": "Partagez des calendriers avec d'autres utilisateurs ou gérez les invitations.",
"manageShares": "Gérer les partages",
"defaultView": "Vue par défaut",
"defaultViewDesc": "Vue à l'ouverture du calendrier",
"selectView": "Choisir la vue",
"viewWeek": "Semaine",
"viewMonth": "Mois",
"viewAgenda": "Agenda",
"timeFormat": "Format de l'heure",
"timeFormatDesc": "Affichage des heures",
"weekdaysOnly": "Afficher uniquement les jours ouvrables",
"weekdaysOnlyDesc": "Masquer les week-ends dans la vue du calendrier",
"showWeekNumbers": "Afficher les numéros de semaine",
"showWeekNumbersDesc": "Afficher le numéro de semaine dans la vue du calendrier",
"filterHours": "Filtrer les heures",
"filterHoursDesc": "Afficher uniquement certaines heures dans la vue jour/semaine",
"visibleHours": "Heures visibles",
"visibleHoursDesc": "Plage horaire affichée dans la vue du calendrier",
"hoursFrom": "De",
"hoursTo": "À",
"allDayEvents": "Événements sur toute la journée",
"allDayEventsDesc": "Comment afficher les événements sur toute la journée ?",
"allDayInHeader": "Dans l'en-tête",
"allDayAsBlock": "En bloc journalier",
"defaultDuration": "Durée par défaut",
"defaultDurationDesc": "Durée par défaut pour les nouveaux événements",
"selectDuration": "Choisir la durée",
"durationMinutes": "{count} minutes",
"durationHours": "{count} heure(s)",
"defaultReminder": "Rappel par défaut",
"defaultReminderDesc": "Rappel par défaut pour les nouveaux événements",
"selectReminder": "Choisir le rappel",
"reminderNone": "Aucun",
"reminderMinutes": "{count} minutes",
"reminderHour": "1 heure",
"reminderDay": "1 jour",
"showBirthdays": "Afficher les anniversaires",
"showBirthdaysDesc": "Afficher les anniversaires des contacts dans le calendrier",
"showAge": "Afficher l'âge",
"showAgeDesc": "Afficher l'âge de la personne lors des anniversaires"
},
"error": {
"notFound": "Page non trouvée",
"backToHome": "Retour à l'accueil"
},
"sync": {
"pageTitle": "Sync des calendriers - Paramètres",
"title": "Sync des calendriers",
"back": "Retour",
"connectCalendar": "Connecter un calendrier",
"description": "Connectez des calendriers externes pour importer et synchroniser des événements.",
"emptyState": "Aucun calendrier externe connecté",
"syncNow": "Synchroniser maintenant",
"disconnect": "Déconnecter",
"confirmDisconnect": "Vraiment déconnecter \"{name}\" ? Les événements synchronisés seront supprimés.",
"neverSynced": "Jamais",
"directionLabel": "Direction",
"lastSync": "Dernière sync",
"statusLabel": "Statut",
"autoSync": "Sync auto",
"connectCaldav": "Connecter un serveur CalDAV",
"connectProvider": "Connecter {provider}",
"searching": "Recherche...",
"searchCalendars": "Rechercher des calendriers",
"discoveredCalendars": "Calendriers trouvés :",
"connecting": "Connexion...",
"connect": "Connecter",
"direction": {
"import": "Import uniquement",
"export": "Export uniquement",
"both": "Bidirectionnel"
},
"status": {
"error": "Erreur",
"active": "Actif (toutes les {interval} min.)",
"paused": "En pause"
},
"providers": {
"icalUrl": "URL iCal",
"icalUrlDesc": "Importer un lien ICS (ex. jours fériés)",
"caldav": "CalDAV",
"caldavDesc": "Connecter un serveur CalDAV",
"google": "Google Calendar",
"googleDesc": "Synchroniser avec Google Agenda",
"apple": "Apple Calendar",
"appleDesc": "Connecter le calendrier iCloud"
},
"form": {
"serverUrl": "URL du serveur",
"username": "Nom d'utilisateur",
"password": "Mot de passe",
"name": "Nom",
"namePlaceholder": "Mon calendrier externe",
"url": "URL",
"syncDirection": "Direction de sync"
}
},
"sharing": {
"pageTitle": "Partage de calendriers - Paramètres",
"title": "Partages",
"back": "Retour",
"shareCalendar": "Partager le calendrier",
"invitations": "Invitations ({count})",
"calendarInvitation": "Invitation de calendrier",
"access": "accès",
"accept": "Accepter",
"sharedWithMe": "Partagés avec moi",
"sharedCalendar": "Calendrier partagé",
"shareMyCalendars": "Partager mes calendriers",
"notSharedYet": "Pas encore partagé",
"linkShare": "Partage par lien",
"pending": "En attente",
"removeShare": "Supprimer le partage",
"confirmRemoveShare": "Vraiment supprimer ce partage ?",
"addPerson": "Ajouter une personne",
"share": "Partager",
"sharing": "Partage...",
"permission": {
"read": "Lecture",
"write": "Lecture & modification",
"admin": "Administrateur"
},
"form": {
"calendar": "Calendrier",
"email": "Adresse e-mail",
"permission": "Autorisation"
}
}
}

View file

@ -122,5 +122,88 @@
"error": {
"notFound": "Pagina non trovata",
"backToHome": "Torna alla home"
},
"sync": {
"pageTitle": "Sincronizzazione calendari - Impostazioni",
"title": "Sincronizzazione",
"back": "Indietro",
"connectCalendar": "Collega calendario",
"description": "Collega calendari esterni per importare e sincronizzare eventi.",
"emptyState": "Nessun calendario esterno collegato",
"syncNow": "Sincronizza ora",
"disconnect": "Disconnetti",
"confirmDisconnect": "Disconnettere davvero \"{name}\"? Gli eventi sincronizzati verranno eliminati.",
"neverSynced": "Mai",
"directionLabel": "Direzione",
"lastSync": "Ultima sincronizzazione",
"statusLabel": "Stato",
"autoSync": "Sincronizzazione automatica",
"connectCaldav": "Collega server CalDAV",
"connectProvider": "Collega {provider}",
"searching": "Ricerca...",
"searchCalendars": "Cerca calendari",
"discoveredCalendars": "Calendari trovati:",
"connecting": "Collegamento...",
"connect": "Collega",
"direction": {
"import": "Solo importazione",
"export": "Solo esportazione",
"both": "Bidirezionale"
},
"status": {
"error": "Errore",
"active": "Attivo (ogni {interval} min.)",
"paused": "In pausa"
},
"providers": {
"icalUrl": "URL iCal",
"icalUrlDesc": "Importa link ICS (es. festività)",
"caldav": "CalDAV",
"caldavDesc": "Collega server CalDAV",
"google": "Google Calendar",
"googleDesc": "Sincronizza con Google Calendar",
"apple": "Apple Calendar",
"appleDesc": "Collega calendario iCloud"
},
"form": {
"serverUrl": "URL del server",
"username": "Nome utente",
"password": "Password",
"name": "Nome",
"namePlaceholder": "Il mio calendario esterno",
"url": "URL",
"syncDirection": "Direzione di sincronizzazione"
}
},
"sharing": {
"pageTitle": "Condivisione calendari - Impostazioni",
"title": "Condivisioni",
"back": "Indietro",
"shareCalendar": "Condividi calendario",
"invitations": "Inviti ({count})",
"calendarInvitation": "Invito calendario",
"access": "accesso",
"accept": "Accetta",
"sharedWithMe": "Condivisi con me",
"sharedCalendar": "Calendario condiviso",
"shareMyCalendars": "Condividi i miei calendari",
"notSharedYet": "Non ancora condiviso",
"linkShare": "Condivisione tramite link",
"pending": "In attesa",
"removeShare": "Rimuovi condivisione",
"confirmRemoveShare": "Rimuovere davvero questa condivisione?",
"addPerson": "Aggiungi persona",
"share": "Condividi",
"sharing": "Condivisione...",
"permission": {
"read": "Lettura",
"write": "Lettura e modifica",
"admin": "Amministratore"
},
"form": {
"calendar": "Calendario",
"email": "Indirizzo email",
"permission": "Autorizzazione"
}
}
}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { _ } from 'svelte-i18n';
import { authStore } from '$lib/stores/auth.svelte';
import { calendarsStore } from '$lib/stores/calendars.svelte';
import { sharesStore } from '$lib/stores/shares.svelte';
@ -28,12 +29,6 @@
// Active calendar for viewing shares
let viewingCalendarId = $state<string | null>(null);
const PERMISSION_LABELS: Record<SharePermission, string> = {
read: 'Lesen',
write: 'Lesen & Bearbeiten',
admin: 'Administrator',
};
async function handleShare() {
if (!shareEmail.trim() || !selectedCalendarId) return;
@ -45,7 +40,7 @@
}
async function handleRemoveShare(calendarId: string, shareId: string) {
if (!confirm('Freigabe wirklich entfernen?')) return;
if (!confirm($_('sharing.confirmRemoveShare'))) return;
await sharesStore.removeShare(calendarId, shareId);
}
@ -63,16 +58,20 @@
</script>
<svelte:head>
<title>Kalender-Freigaben - Einstellungen</title>
<title>{$_('sharing.pageTitle')}</title>
</svelte:head>
<div class="page-container">
<header class="header">
<a href="/settings" class="back-button" aria-label="Zurück">
<a href="/settings" class="back-button" aria-label={$_('sharing.back')}>
<CaretLeft size={20} weight="bold" />
</a>
<h1 class="title">Freigaben</h1>
<button onclick={() => (showShareForm = true)} class="add-button" aria-label="Kalender teilen">
<h1 class="title">{$_('sharing.title')}</h1>
<button
onclick={() => (showShareForm = true)}
class="add-button"
aria-label={$_('sharing.shareCalendar')}
>
<Plus size={20} weight="bold" />
</button>
</header>
@ -82,14 +81,15 @@
<section class="section">
<h2 class="section-title">
<EnvelopeSimple size={18} />
Einladungen ({sharesStore.invitations.length})
{$_('sharing.invitations', { values: { count: sharesStore.invitations.length } })}
</h2>
{#each sharesStore.invitations as invite (invite.id)}
<div class="share-card">
<div class="share-info">
<span class="share-name">Kalender-Einladung</span>
<span class="share-name">{$_('sharing.calendarInvitation')}</span>
<span class="share-detail">
{PERMISSION_LABELS[invite.permission]} Zugriff
{$_('sharing.permission.' + invite.permission)}
{$_('sharing.access')}
</span>
</div>
<div class="share-actions">
@ -98,7 +98,7 @@
onclick={() => sharesStore.acceptInvitation(invite.id)}
>
<CheckCircle size={14} />
Annehmen
{$_('sharing.accept')}
</button>
<button
class="btn btn-sm btn-ghost"
@ -117,13 +117,13 @@
<section class="section">
<h2 class="section-title">
<Users size={18} />
Mit mir geteilt
{$_('sharing.sharedWithMe')}
</h2>
{#each sharesStore.sharedWithMe as share (share.id)}
<div class="share-card">
<div class="share-info">
<span class="share-name">Geteilter Kalender</span>
<span class="share-detail">{PERMISSION_LABELS[share.permission]}</span>
<span class="share-name">{$_('sharing.sharedCalendar')}</span>
<span class="share-detail">{$_('sharing.permission.' + share.permission)}</span>
</div>
</div>
{/each}
@ -134,7 +134,7 @@
<section class="section">
<h2 class="section-title">
<UserPlus size={18} />
Meine Kalender teilen
{$_('sharing.shareMyCalendars')}
</h2>
{#each calendarsStore.calendars as calendar (calendar.id)}
@ -161,25 +161,25 @@
{@const calShares = sharesStore.getSharesForCalendar(calendar.id)}
<div class="shares-list">
{#if calShares.length === 0}
<p class="empty-text">Noch nicht geteilt</p>
<p class="empty-text">{$_('sharing.notSharedYet')}</p>
{:else}
{#each calShares as share (share.id)}
<div class="share-item">
<div class="share-item-info">
<span class="share-email">
{share.sharedWithEmail || 'Link-Freigabe'}
{share.sharedWithEmail || $_('sharing.linkShare')}
</span>
<span class="share-permission">
{PERMISSION_LABELS[share.permission]}
{$_('sharing.permission.' + share.permission)}
</span>
{#if share.status === 'pending'}
<span class="share-status pending">Ausstehend</span>
<span class="share-status pending">{$_('sharing.pending')}</span>
{/if}
</div>
<button
class="remove-btn"
onclick={() => handleRemoveShare(calendar.id, share.id)}
title="Freigabe entfernen"
title={$_('sharing.removeShare')}
>
<Trash size={14} />
</button>
@ -195,7 +195,7 @@
}}
>
<Plus size={14} />
Person hinzufügen
{$_('sharing.addPerson')}
</button>
</div>
{/if}
@ -208,12 +208,12 @@
<Modal
visible={showShareForm}
onClose={() => (showShareForm = false)}
title="Kalender teilen"
title={$_('sharing.shareCalendar')}
maxWidth="sm"
>
<div class="share-form">
<div class="form-field">
<label>Kalender</label>
<label>{$_('sharing.form.calendar')}</label>
<select bind:value={selectedCalendarId} class="select-input">
{#each calendarsStore.calendars as cal}
<option value={cal.id}>{cal.name}</option>
@ -221,28 +221,30 @@
</select>
</div>
<div class="form-field">
<label>E-Mail-Adresse</label>
<label>{$_('sharing.form.email')}</label>
<Input bind:value={shareEmail} placeholder="name@example.com" />
</div>
<div class="form-field">
<label>Berechtigung</label>
<label>{$_('sharing.form.permission')}</label>
<select bind:value={sharePermission} class="select-input">
<option value="read">{PERMISSION_LABELS.read}</option>
<option value="write">{PERMISSION_LABELS.write}</option>
<option value="admin">{PERMISSION_LABELS.admin}</option>
<option value="read">{$_('sharing.permission.read')}</option>
<option value="write">{$_('sharing.permission.write')}</option>
<option value="admin">{$_('sharing.permission.admin')}</option>
</select>
</div>
</div>
{#snippet footer()}
<div class="modal-footer">
<button class="btn btn-secondary" onclick={() => (showShareForm = false)}>Abbrechen</button>
<button class="btn btn-secondary" onclick={() => (showShareForm = false)}
>{$_('common.cancel')}</button
>
<button
class="btn btn-primary"
onclick={handleShare}
disabled={isSharing || !shareEmail.trim() || !selectedCalendarId}
>
{isSharing ? 'Teile...' : 'Teilen'}
{isSharing ? $_('sharing.sharing') : $_('sharing.share')}
</button>
</div>
{/snippet}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { _ } from 'svelte-i18n';
import { authStore } from '$lib/stores/auth.svelte';
import { externalCalendarsStore } from '$lib/stores/external-calendars.svelte';
import {
@ -38,11 +39,23 @@
let isDiscovering = $state(false);
// Provider selection
const providers: { id: CalendarProvider; label: string; description: string }[] = [
{ id: 'ical_url', label: 'iCal URL', description: 'ICS-Link importieren (z.B. Feiertage)' },
{ id: 'caldav', label: 'CalDAV', description: 'CalDAV-Server verbinden' },
{ id: 'google', label: 'Google Calendar', description: 'Mit Google Kalender synchronisieren' },
{ id: 'apple', label: 'Apple Calendar', description: 'iCloud Kalender verbinden' },
const providers: { id: CalendarProvider; labelKey: string; descriptionKey: string }[] = [
{
id: 'ical_url',
labelKey: 'sync.providers.icalUrl',
descriptionKey: 'sync.providers.icalUrlDesc',
},
{
id: 'caldav',
labelKey: 'sync.providers.caldav',
descriptionKey: 'sync.providers.caldavDesc',
},
{
id: 'google',
labelKey: 'sync.providers.google',
descriptionKey: 'sync.providers.googleDesc',
},
{ id: 'apple', labelKey: 'sync.providers.apple', descriptionKey: 'sync.providers.appleDesc' },
];
function selectProvider(provider: CalendarProvider) {
@ -131,7 +144,7 @@
}
async function handleDisconnect(id: string, name: string) {
if (!confirm(`"${name}" wirklich trennen? Synchronisierte Termine werden gelöscht.`)) return;
if (!confirm($_('sync.confirmDisconnect', { values: { name } }))) return;
await externalCalendarsStore.disconnect(id);
}
@ -144,18 +157,18 @@
}
function formatSyncTime(date: Date | string | null | undefined): string {
if (!date) return 'Noch nie';
if (!date) return $_('sync.neverSynced');
return formatDistanceToNow(new Date(date), { addSuffix: true, locale: de });
}
function getSyncDirectionLabel(direction: SyncDirection): string {
switch (direction) {
case 'import':
return 'Nur Import';
return $_('sync.direction.import');
case 'export':
return 'Nur Export';
return $_('sync.direction.export');
case 'both':
return 'Bidirektional';
return $_('sync.direction.both');
}
}
@ -169,26 +182,26 @@
</script>
<svelte:head>
<title>Kalender-Sync - Einstellungen</title>
<title>{$_('sync.pageTitle')}</title>
</svelte:head>
<div class="page-container">
<header class="header">
<a href="/settings" class="back-button" aria-label="Zurück">
<a href="/settings" class="back-button" aria-label={$_('sync.back')}>
<CaretLeft size={20} weight="bold" />
</a>
<h1 class="title">Kalender-Sync</h1>
<h1 class="title">{$_('sync.title')}</h1>
<button
onclick={() => (showConnectForm = true)}
class="add-button"
aria-label="Kalender verbinden"
aria-label={$_('sync.connectCalendar')}
>
<Plus size={20} weight="bold" />
</button>
</header>
<p class="description">
Verbinde externe Kalender, um Termine zu importieren und zu synchronisieren.
{$_('sync.description')}
</p>
{#if externalCalendarsStore.error}
@ -205,10 +218,10 @@
{:else if externalCalendarsStore.calendars.length === 0}
<div class="empty-state">
<Globe size={48} class="text-muted-foreground" />
<p>Keine externen Kalender verbunden</p>
<p>{$_('sync.emptyState')}</p>
<button class="btn btn-primary" onclick={() => (showConnectForm = true)}>
<Plus size={16} weight="bold" />
Kalender verbinden
{$_('sync.connectCalendar')}
</button>
</div>
{:else}
@ -230,7 +243,7 @@
class="icon-btn"
onclick={() => handleSync(cal.id)}
disabled={externalCalendarsStore.isSyncing(cal.id)}
title="Jetzt synchronisieren"
title={$_('sync.syncNow')}
>
<ArrowsClockwise
size={16}
@ -240,7 +253,7 @@
<button
class="icon-btn icon-btn-danger"
onclick={() => handleDisconnect(cal.id, cal.name)}
title="Verbindung trennen"
title={$_('sync.disconnect')}
>
<Trash size={16} />
</button>
@ -249,7 +262,7 @@
<div class="calendar-details">
<div class="detail-row">
<span class="detail-label">Richtung</span>
<span class="detail-label">{$_('sync.directionLabel')}</span>
<span class="detail-value">
{#if cal.syncDirection === 'import'}
<CloudArrowDown size={14} />
@ -262,26 +275,26 @@
</span>
</div>
<div class="detail-row">
<span class="detail-label">Letzte Sync</span>
<span class="detail-label">{$_('sync.lastSync')}</span>
<span class="detail-value">
{formatSyncTime(cal.lastSyncAt)}
</span>
</div>
<div class="detail-row">
<span class="detail-label">Status</span>
<span class="detail-label">{$_('sync.statusLabel')}</span>
<span class="detail-value">
{#if cal.lastSyncError}
<span class="status-error">
<Warning size={14} />
Fehler
{$_('sync.status.error')}
</span>
{:else if cal.syncEnabled}
<span class="status-ok">
<CheckCircle size={14} />
Aktiv (alle {cal.syncInterval} Min.)
{$_('sync.status.active', { values: { interval: cal.syncInterval } })}
</span>
{:else}
<span class="status-paused">Pausiert</span>
<span class="status-paused">{$_('sync.status.paused')}</span>
{/if}
</span>
</div>
@ -300,7 +313,7 @@
onchange={() => handleToggleSync(cal.id, cal.syncEnabled)}
class="toggle"
/>
<span>Auto-Sync</span>
<span>{$_('sync.autoSync')}</span>
</label>
</div>
</div>
@ -314,10 +327,12 @@
visible={showConnectForm}
onClose={closeConnectForm}
title={connectStep === 'provider'
? 'Kalender verbinden'
? $_('sync.connectCalendar')
: connectStep === 'caldav-discover'
? 'CalDAV-Server verbinden'
: `${PROVIDER_INFO[selectedProvider!]?.label || ''} verbinden`}
? $_('sync.connectCaldav')
: $_('sync.connectProvider', {
values: { provider: PROVIDER_INFO[selectedProvider!]?.label || '' },
})}
maxWidth="md"
>
{#if connectStep === 'provider'}
@ -334,8 +349,8 @@
{/if}
</div>
<div>
<span class="provider-name">{provider.label}</span>
<span class="provider-desc">{provider.description}</span>
<span class="provider-name">{$_(provider.labelKey)}</span>
<span class="provider-desc">{$_(provider.descriptionKey)}</span>
</div>
</button>
{/each}
@ -343,26 +358,26 @@
{:else if connectStep === 'caldav-discover'}
<div class="connect-form">
<div class="form-field">
<label for="caldav-url">Server-URL</label>
<label for="caldav-url">{$_('sync.form.serverUrl')}</label>
<Input bind:value={connectUrl} placeholder="https://caldav.example.com" />
</div>
<div class="form-field">
<label for="caldav-user">Benutzername</label>
<label for="caldav-user">{$_('sync.form.username')}</label>
<Input bind:value={connectUsername} placeholder="user@example.com" />
</div>
<div class="form-field">
<label for="caldav-pass">Passwort</label>
<label for="caldav-pass">{$_('sync.form.password')}</label>
<input
type="password"
bind:value={connectPassword}
placeholder="Passwort"
placeholder={$_('sync.form.password')}
class="password-input"
/>
</div>
{#if discoveredCalendars.length > 0}
<div class="discovered-list">
<h4>Gefundene Kalender:</h4>
<h4>{$_('sync.discoveredCalendars')}</h4>
{#each discoveredCalendars as cal}
<button
class="discovered-item"
@ -380,25 +395,25 @@
{#snippet footer()}
<div class="modal-footer">
<button class="btn btn-secondary" onclick={() => (connectStep = 'provider')}>
Zurück
{$_('sync.back')}
</button>
<button
class="btn btn-primary"
onclick={handleCalDavDiscover}
disabled={isDiscovering || !connectUrl.trim() || !connectUsername.trim()}
>
{isDiscovering ? 'Suche...' : 'Kalender suchen'}
{isDiscovering ? $_('sync.searching') : $_('sync.searchCalendars')}
</button>
</div>
{/snippet}
{:else if connectStep === 'credentials'}
<div class="connect-form">
<div class="form-field">
<label>Name</label>
<Input bind:value={connectName} placeholder="Mein externer Kalender" />
<label>{$_('sync.form.name')}</label>
<Input bind:value={connectName} placeholder={$_('sync.form.namePlaceholder')} />
</div>
<div class="form-field">
<label>URL</label>
<label>{$_('sync.form.url')}</label>
<Input
bind:value={connectUrl}
placeholder={selectedProvider === 'ical_url'
@ -408,26 +423,26 @@
</div>
{#if selectedProvider !== 'ical_url'}
<div class="form-field">
<label>Benutzername</label>
<label>{$_('sync.form.username')}</label>
<Input bind:value={connectUsername} placeholder="user@example.com" />
</div>
<div class="form-field">
<label>Passwort</label>
<label>{$_('sync.form.password')}</label>
<input
type="password"
bind:value={connectPassword}
placeholder="Passwort"
placeholder={$_('sync.form.password')}
class="password-input"
/>
</div>
{/if}
<div class="form-field">
<label>Sync-Richtung</label>
<label>{$_('sync.form.syncDirection')}</label>
<select bind:value={connectDirection} class="select-input">
<option value="import">Nur Import</option>
<option value="import">{$_('sync.direction.import')}</option>
{#if selectedProvider !== 'ical_url'}
<option value="export">Nur Export</option>
<option value="both">Bidirektional</option>
<option value="export">{$_('sync.direction.export')}</option>
<option value="both">{$_('sync.direction.both')}</option>
{/if}
</select>
</div>
@ -436,14 +451,14 @@
{#snippet footer()}
<div class="modal-footer">
<button class="btn btn-secondary" onclick={() => (connectStep = 'provider')}>
Zurück
{$_('sync.back')}
</button>
<button
class="btn btn-primary"
onclick={handleConnect}
disabled={isConnecting || !connectName.trim() || !connectUrl.trim()}
>
{isConnecting ? 'Verbinde...' : 'Verbinden'}
{isConnecting ? $_('sync.connecting') : $_('sync.connect')}
</button>
</div>
{/snippet}