From 409d41403557b28560380a9badfb4c576d1df330 Mon Sep 17 00:00:00 2001 From: Luca <102960244+Luca-Timo@users.noreply.github.com> Date: Tue, 26 May 2026 18:19:55 +0200 Subject: [PATCH] feat(crm): i18n EN + DE for CRM, machine fr/nl/pt/ru fallbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ~940 new keys per primary locale covering every CRM surface: quote / invoice / contract editor + list + detail + public response pages, calendar, hours, tax report, deals lineage, reminder emails, feature toggles, settings tabs, error toasts. en.json + de.json are hand-translated by the maintainer and are authoritative. fr / nl / pt / ru received the same key set but machine-derived strings — flagged for native review in the PR description per project policy (see memory feedback_translation_flagging). 3-way merge note: 1 conflict (fr.json) hand-resolved to keep upstream's improved phrasing for previewLayout / livePreview / heroPlaceholderText alongside feat/crm's pdfTypography keys. --- frontend/src/i18n/locales/de.json | 935 ++++++++++++++++++++++++++++- frontend/src/i18n/locales/en.json | 940 +++++++++++++++++++++++++++++- frontend/src/i18n/locales/fr.json | 53 +- frontend/src/i18n/locales/nl.json | 53 +- frontend/src/i18n/locales/pt.json | 53 +- frontend/src/i18n/locales/ru.json | 53 +- 6 files changed, 2041 insertions(+), 46 deletions(-) diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index 6524a6aa..ddbb6612 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -124,7 +124,11 @@ "saveChanges": "Änderungen speichern", "resetChanges": "Änderungen zurücksetzen", "dismiss": "Schließen", - "discard": "Verwerfen" + "discard": "Verwerfen", + "preview": "Vorschau", + "duplicate": "Duplizieren", + "showAll": "Alle anzeigen", + "confirm": "Bestätigen" }, "upload": { "photoCategory": "Fotokategorie", @@ -168,7 +172,8 @@ "cmsPages": "CMS-Seiten", "users": "Benutzer", "calendar": "Kalender", - "clients": "Kunden" + "clients": "CRM", + "betaTag": "Beta" }, "eventTypes": { "title": "Veranstaltungsarten", @@ -738,6 +743,9 @@ "eventName": "Veranstaltungsname", "eventType": "Veranstaltungstyp", "eventDate": "Veranstaltungsdatum", + "fullDay": "Ganztägig", + "eventTimeStart": "Startzeit", + "eventTimeEnd": "Endzeit", "hostEmail": "E-Mail des Kunden", "hostName": "Name des Kunden", "hostNamePlaceholder": "Max Mustermann", @@ -1391,7 +1399,14 @@ "integrations": "Integrationen", "system": "System", "appearance": "Inhalt & Erscheinungsbild", - "communication": "Kommunikation" + "communication": "Kommunikation", + "crm": "CRM-Einstellungen" + }, + "contracts": { + "title": "Verträge" + }, + "reminderTemplates": { + "title": "Erinnerungs-E-Mails" }, "apiTokens": { "title": "API-Tokens", @@ -1466,7 +1481,7 @@ "sales": "Vertrieb", "insights": "Auswertungen & Zugriff", "customers": "Kunden", - "clients": "Kunden" + "clients": "CRM" }, "status": { "stable": "stabil", @@ -1491,9 +1506,14 @@ }, "calendar": { "title": "Kalender", - "description": "Alle anstehenden und vergangenen Veranstaltungen in einer Monats-/Wochenansicht. Optional Buchungen von Kunden annehmen.", + "description": "Admin-Kalender mit Terminen, erfassten Stunden und offenen Angeboten/Verträgen in einer Ansicht. Stunden lassen sich direkt im Kalender per Drag erfassen.", "sidebar": "Kalender" }, + "calendarBooking": { + "title": "Kundenbuchung", + "description": "Kunden sehen freie Slots auf einem öffentlichen Kalender und können direkt buchen. In Kürze verfügbar.", + "sidebar": "Buchung" + }, "quotes": { "title": "Angebote", "description": "Senden Sie Angebote mit Einzelposten an Kunden. Diese können über einen öffentlichen Link angenommen oder abgelehnt werden; Zahlungen werden manuell verfolgt.", @@ -1504,6 +1524,12 @@ "description": "Generieren Sie eine Rechnung aus jedem angenommenen Angebot. Manuell als bezahlt markieren — keine Integration mit Zahlungsanbietern.", "sidebar": "Rechnungen" }, + "taxReport": { + "title": "Steuerliste", + "description": "Periodenbezogene Umsatzliste mit Netto- und MwSt-Aufschlüsselung, gruppiert nach MwSt-Satz. Exportieren Sie als PDF (Querformat, Firmenbriefkopf) oder CSV für Ihre Buchhaltung. Stornierte Rechnungen bleiben für die lückenlose Nummernfolge sichtbar, sind aber nicht in den Summen enthalten.", + "sidebar": "Steuer", + "requiresBills": "Bitte zuerst Rechnungen aktivieren — die Steuerliste liest aus Ihren Rechnungen." + }, "analytics": { "title": "Statistiken", "description": "Speichernutzung, Galerie-Aufrufe, Download-Zahlen und Statistiken pro Veranstaltung." @@ -1514,12 +1540,12 @@ "warning": "Bestehende Benutzerkonten bleiben gültig; die Admin-Oberfläche für deren Verwaltung wird ausgeblendet, bis Sie dies wieder aktivieren." }, "customerPortal": { - "title": "Kunden", - "description": "Persistente Kunden-Logins. Wiederkehrende Kunden sehen alle zugewiesenen Galerien an einem Ort — keine Passwörter pro Event. Kunden melden sich unter /customer/login an, verwaltet werden sie unter Kunden → Konten." + "title": "Kundenkonten", + "description": "Persistente Kunden-Logins. Wiederkehrende Kunden sehen alle zugewiesenen Galerien an einem Ort — keine Passwörter pro Event. Kunden melden sich unter /customer/login an, verwaltet werden sie unter CRM → Konten." }, "clients": { - "title": "Kunden", - "description": "Hauptschalter für den Kunden-Bereich in der Seitenleiste. Aus blendet den Bereich und alle Unterfunktionen aus, unabhängig von deren individuellen Schaltern – beim erneuten Einschalten wird der vorherige Zustand wiederhergestellt." + "title": "CRM", + "description": "Hauptschalter für den CRM-Bereich in der Seitenleiste. Aus blendet den Bereich und alle Unterfunktionen aus, unabhängig von deren individuellen Schaltern – beim erneuten Einschalten wird der vorherige Zustand wiederhergestellt." } }, "customerSurface": { @@ -1691,6 +1717,12 @@ "saveTheme": "Design speichern", "previewLayout": "Vorschau-Layout", "livePreview": "Live-Vorschau", + "pdfTypography": "PDF-Schriftart", + "pdfTypographyHelp": "Wird für Rechnungs- und Angebotsbriefköpfe verwendet. Wähle eine der mitgelieferten Schriftarten aus oder belasse den Standard, um Helvetica zu verwenden.", + "pdfFontFamily": "Fließtext-Schrift", + "pdfFontFamilyDefault": "Helvetica verwenden (Standard)", + "pdfFontFamilyMissing": "nicht verfügbar", + "pdfFontSavedToast": "PDF-Schriftart gespeichert.", "heroPlaceholderText": "Das Hero-Foto Ihrer Veranstaltung wird hier angezeigt", "whiteLabel": "White Label", "hidePoweredBy": "\"Powered by PicPeak\" Branding ausblenden", @@ -1705,6 +1737,11 @@ "logoMaxHeight": "Maximale Höhe (Pixel)", "logoMaxHeightHelp": "Legen Sie eine benutzerdefinierte maximale Höhe für das Logo fest (20-200 Pixel)", "logoPosition": "Logo-Position im Header", + "positionLeft": "Links", + "positionCenter": "Zentriert", + "positionRight": "Rechts", + "positionSidepanel": "Seitenleiste", + "positionSidepanelHelp": "Das Logo erscheint statt im Header in der Admin-Seitenleiste. Bei eingeklappter Seitenleiste wird das Favicon angezeigt.", "logoDisplayMode": "Anzeigemodus", "logoOnly": "Nur Logo", "textOnly": "Nur Firmenname", @@ -2333,6 +2370,7 @@ "customers": "Kunden", "calendar": "Kalender", "quotes": "Angebote", + "contracts": "Verträge", "billing": "Rechnungen" }, "featureOff": "Funktion aus", @@ -2775,7 +2813,8 @@ "quotes": "Angebote", "bills": "Rechnungen", "profile": "Profil", - "soon": "Bald" + "soon": "Bald", + "new": "Neu" }, "comingSoon": { "tag": "Demnächst" @@ -2786,11 +2825,51 @@ }, "quotes": { "title": "Angebote", - "body": "Angebote für kommende Shoots an einem Ort prüfen und annehmen. Wir bauen das noch — bis dahin schickt dir dein Fotograf Angebote wie gewohnt." + "subtitle": "Alle Angebote, die dir dein Fotograf gesendet hat. Klicke auf ein offenes Angebot, um es anzunehmen oder abzulehnen.", + "empty": "Noch keine Angebote.", + "disabled": "Diese Funktion ist für dein Konto aktuell deaktiviert. Bitte wende dich an deinen Fotografen, falls du hier Angebote erwartest.", + "loadError": "Angebote konnten nicht geladen werden.", + "noEventName": "—", + "openToRespond": "Öffnen zum Antworten", + "viewPdf": "PDF anzeigen", + "popupBlocked": "Erlaube Pop-ups für diese Seite, um das Angebots-PDF anzuzeigen." }, "bills": { "title": "Rechnungen", - "body": "Rechnungen und Zahlungshistorie zu deinen Shoots findest du hier. Wir senden eine E-Mail, sobald es live ist." + "subtitle": "Alle Rechnungen, die dir dein Fotograf gesendet hat. Klicke auf „PDF anzeigen“, um sie herunterzuladen.", + "empty": "Noch keine Rechnungen.", + "disabled": "Diese Funktion ist für dein Konto aktuell deaktiviert. Bitte wende dich an deinen Fotografen, falls du hier Rechnungen erwartest.", + "loadError": "Rechnungen konnten nicht geladen werden.", + "viewPdf": "PDF anzeigen", + "popupBlocked": "Erlaube Pop-ups für diese Seite, um das Rechnungs-PDF anzuzeigen.", + "field": { + "issueDate": "Ausgestellt", + "dueDate": "Fällig", + "paidAt": "Bezahlt", + "outstanding": "Offen", + "lateFee": "Mahngebühr" + }, + "filter": { + "outstanding": "Offen (gesendet + überfällig)" + }, + "kind": { + "storno": "Stornorechnung" + }, + "cancelsLabel": "Hebt Rechnung auf:", + "cancelledByLabel": "Aufgehoben durch Stornorechnung" + }, + "sort": { + "label": "Sortieren", + "newest": "Neueste zuerst", + "oldest": "Älteste zuerst", + "priceDesc": "Preis (hoch nach niedrig)", + "priceAsc": "Preis (niedrig nach hoch)" + }, + "filter": { + "label": "Filter", + "all": "Alle", + "countAll": "{{count}} insgesamt", + "countFiltered": "{{visible}} von {{total}}" }, "profile": { "title": "Kundenprofil", @@ -2860,6 +2939,78 @@ } }, "customers": { + "field": { + "featureHoursLogging": "Stundenerfassung", + "hourlyRate": "Standard-Stundensatz", + "hourlyRateHint": "Haupteinheiten (z. B. 150.00 für {{currency}} 150). Leer lassen, um pro Eintrag einen Satz zu verlangen." + }, + "hours": { + "section": "Stunden", + "monthlyHint": "Einträge werden automatisch dem aktuellen monatlichen Entwurf angehängt. Bearbeiten/Löschen möglich, bis der Scheduler den Versand auslöst.", + "perEventHint": "Erfasste Einträge bleiben unverrechnet, bis Sie auf „Rechnungsentwurf erstellen“ klicken — dann wird ein eigenständiger Rechnungsentwurf mit einer Zeile pro Eintrag erzeugt, den Sie vor dem Versand prüfen können.", + "form": { + "title": "Neuen Eintrag erfassen", + "date": "Datum", + "start": "Start", + "end": "Ende", + "duration": "Dauer", + "durationPlaceholder": "1h · 1,5 · 1:30", + "durationHint": "Dauer eingeben, um Ende automatisch zu setzen: 1h, 1.5, 1,5 oder 1:30", + "rateOverride": "Satz-Override", + "note": "Notiz / Beschreibung", + "notePlaceholder": "Was wurde gearbeitet?", + "save": "Eintrag hinzufügen" + }, + "col": { + "date": "Datum", + "range": "Zeit", + "hours": "Stunden", + "rate": "Satz", + "total": "Gesamt", + "note": "Notiz", + "status": "Status" + }, + "status": { + "unbilled": "Offen", + "billed": "Verrechnet", + "billedOn": "Verrechnet: {{number}}" + }, + "billButton": "Rechnungsentwurf erstellen", + "unbilledCount": "{{count}} offene Einträge, total {{total}}", + "empty": "Noch keine Einträge erfasst.", + "confirmDelete": "Diesen Eintrag löschen? Wenn er auf einem Entwurf verrechnet wurde, wird die zugehörige Rechnungsposition ebenfalls entfernt.", + "locked": "Gesperrt: Rechnung bereits zum Versand vorbereitet", + "toast": { + "created": "Eintrag erfasst", + "deleted": "Eintrag gelöscht", + "billed": "Stunden verrechnet" + } + }, + "create": { + "openButton": "Passiven Kunden erstellen", + "openLink": "+ Neuen Kunden erstellen", + "title": "Neuen Kunden erstellen", + "subtitle": "Geben Sie die Daten unten ein. Wählen Sie „Als passiven Kunden speichern“, um einen reinen Admin-Eintrag zu erstellen, oder „Speichern & Portal-Einladung senden“, um dem Kunden zusätzlich eine Einladungs-E-Mail zu schicken.", + "passiveSubtitle": "Erstellt einen reinen Admin-Datensatz. Der Kunde wird nicht benachrichtigt und kann sich erst anmelden, wenn Sie ihm später eine Einladung senden.", + "saveAsPassive": "Als passiven Kunden speichern", + "saveAndInvite": "Speichern & Portal-Einladung senden", + "savedPassiveToast": "Passiver Kunde erstellt.", + "savedActiveToast": "Kunde erstellt und Portal-Einladung gesendet.", + "inviteFailedToast": "Kunde gespeichert (passiv). Einladungs-E-Mail fehlgeschlagen — bitte aus dem Kundendetail erneut versuchen.", + "emailRequired": "Eine gültige E-Mail-Adresse ist erforderlich." + }, + "passive": { + "badge": "Passiv — nur Admin", + "activeLabel": "Portalzugang aktiv", + "sendInvite": "Portal-Einladung senden", + "sendInviteToast": "Portal-Einladung gesendet.", + "alreadyActive": "Der Kunde hat bereits Portalzugang — keine Einladung nötig.", + "detailHint": "Dieser Kunde hat keinen Portalzugang (reiner Admin-Eintrag). Klicken Sie unten, um eine Portal-Einladung per E-Mail zu senden. Bestehende Rechnungen, Angebote und Galerie-Zuweisungen bleiben beim Annehmen der Einladung erhalten.", + "deactivatedHint": "Bitte den Kunden zuerst reaktivieren, bevor die Einladung gesendet wird." + }, + "hoursLoggingDisabled": { + "badge": "Stundenerfassung deaktiviert" + }, "pageTitle": "Kunden", "pageSubtitle": "Wiederkehrende Kundenkonten, die sich unter /customer/login anmelden können.", "unnamed": "Unbenannt", @@ -2935,6 +3086,7 @@ "noEvents": "Noch keinem Event zugewiesen. Fügen Sie diesen Kunden über das Event-Formular hinzu.", "email": "E-Mail", "preferredLanguage": "Bevorzugte Sprache", + "preferredLanguageHint": "Steuert die Portal-Sprache sowie die Sprache von Angebots- und Rechnungs-PDFs. Neue Kunden erben standardmässig die Sprache aus dem Geschäftsprofil ({{lang}}); hier kann pro Kunde überschrieben werden.", "salutation": "Anrede", "salutationNone": "—", "firstName": "Vorname", @@ -2950,6 +3102,7 @@ "city": "Stadt", "state": "Bundesland / Region", "countryCode": "Land (ISO-2)", + "countryName": "Land (vollständiger Name)", "notesHint": "Nur für Administratoren sichtbar. Wird dem Kunden nie gezeigt.", "featuresSection": "Kundenfunktionen", "featuresHint": "Pro-Kunde-Überschreibungen für die Kundenoberflächen-Tabs. Die globalen Schalter in Einstellungen → Kundenoberfläche sind der Master-Schalter — wenn global AUS, sieht niemand den Tab, unabhängig von der Einstellung hier. Standard ist AN, schalte einen Eintrag AUS, um diesen Tab für diesen Kunden auszublenden.", @@ -2961,7 +3114,32 @@ "error": "Passwort-Reset konnte nicht gesendet werden", "inactive": "Aktiviere den Kunden, bevor du einen Reset sendest." }, - "manageEvents": "Galerien verwalten" + "manageEvents": "Galerien verwalten", + "quotesSection": "Angebote", + "noQuotes": "Noch keine Angebote für diesen Kunden.", + "contractsSection": "Verträge", + "noContracts": "Noch keine Verträge für diesen Kunden.", + "billsSection": "Rechnungen", + "noBills": "Noch keine Rechnungen für diesen Kunden." + }, + "billing": { + "section": "Abrechnungsrhythmus", + "hint": "Per-Event (Standard): jede Rechnung wird einzeln versendet. Monatlich: alle Rechnungen einer Periode werden zu einer Sammelrechnung gebündelt, die am konfigurierten Stichtag ausgelöst wird.", + "cadence": "Abrechnungsrhythmus", + "perEvent": "Per Event", + "monthly": "Monatlich", + "quarterly": "Quartalsweise", + "cycleDay": "Stichtag", + "cycleDayHint": "1..28 = Tag im Monat. Negativ -1..-15 für „N Tage vor Monatsende“ (so löst -3 in einem 31-Tage-Monat am 28. aus).", + "triggerNow": "Rechnung jetzt ausstellen", + "triggerConfirm": "Monatsrechnung für diesen Kunden jetzt ausstellen? Der Kunde erhält die E-Mail sofort.", + "triggerHint": "Überspringt den Stichtag und stellt den aktuellen Entwurf sofort aus. Wird abgelehnt, wenn für die aktuelle Periode nichts erfasst wurde.", + "triggered": "Monatsrechnung ausgestellt: {{number}}", + "triggerError": "Monatsrechnung konnte nicht ausgelöst werden.", + "draftPreview": { + "title": "Offen für die Rechnung dieses Monats", + "periodRange": "{{number}} · {{from}} – {{to}}" + } }, "reactivate": { "button": "Reaktivieren", @@ -2997,15 +3175,734 @@ } }, "clients": { - "title": "Kunden", - "subtitle": "Kundenkonten, Termine, Angebote und Rechnungen für wiederkehrende Kunden.", - "navAriaLabel": "Kunden-Navigation", + "title": "CRM", + "subtitle": "Kundenkonten, Termine, Angebote und Rechnungen — alles für wiederkehrende Kunden an einem Ort.", + "navAriaLabel": "CRM-Navigation", "empty": { - "title": "Keine Kunden-Funktionen aktiviert", - "body": "Aktiviere „Konten\" (oder eine andere Kunden-Unterfunktion) unter Einstellungen → Funktionen, um loszulegen." + "title": "Keine CRM-Funktionen aktiviert", + "body": "Aktiviere „Konten\" (oder eine andere CRM-Unterfunktion) unter Einstellungen → Funktionen, um loszulegen." }, "subnav": { - "accounts": "Konten" + "accounts": "Konten", + "quotes": "Angebote", + "contracts": "Verträge", + "hours": "Stunden", + "calendar": "Kalender", + "bills": "Rechnungen", + "taxReport": "Steuer", + "development": "Entwicklung" + } + }, + "calendar": { + "pageTitle": "Kalender", + "subtitle": "Termine, erfasste Stunden und offene Angebote/Verträge in einer Ansicht.", + "viewMonth": "Monat", + "viewWeek": "Woche", + "loading": "Einträge werden geladen…", + "legend": { + "events": "Termine", + "hours": "Stunden", + "pendingQuotes": "Offene Angebote", + "pendingContracts": "Offene Verträge", + "hoursLocked": "Gesperrt (abgerechnet)" + }, + "hourEntry": { + "createTitle": "Stunden erfassen", + "customerLabel": "Kunde", + "customerSearch": "Nach E-Mail oder Firma suchen…", + "descriptionLabel": "Beschreibung (optional)", + "descriptionPlaceholder": "Bearbeitung / Shooting / Anreise…", + "startLabel": "Start", + "endLabel": "Ende", + "submit": "Stunden speichern", + "saving": "Speichern…", + "cancel": "Abbrechen", + "close": "Schließen", + "delete": "Löschen", + "confirmDelete": "Diese erfassten Stunden löschen? Das kann nicht rückgängig gemacht werden.", + "created": "Stunden erfasst.", + "createFailed": "Speichern fehlgeschlagen: {{message}}", + "saved": "Stunden aktualisiert.", + "saveFailed": "Speichern fehlgeschlagen: {{message}}", + "movedToast": "Stunden aktualisiert.", + "moveFailed": "Verschieben fehlgeschlagen: {{message}}", + "resizedToast": "Stunden aktualisiert.", + "resizeFailed": "Größe ändern fehlgeschlagen: {{message}}", + "deleted": "Stunden gelöscht.", + "deleteFailed": "Löschen fehlgeschlagen: {{message}}", + "lockedBadge": "Gesperrt", + "featureOffToast": "Stundenerfassung ist deaktiviert.", + "lockedTooltip": "Bereits abgerechnet — Rechnung stornieren, um zu bearbeiten.", + "noDescription": "Keine Beschreibung.", + "untitledCustomer": "Stunden" + } + }, + "hoursLogging": { + "title": "Stundenerfassung", + "subtitle": "Kunden wählen und abrechenbare Zeit erfassen. Einträge fließen in die nächste Monatsrechnung oder werden für Per-Event-Kunden auf Knopfdruck abgerechnet.", + "pickCustomer": "Kunde", + "pickPlaceholder": "— Kunde wählen —", + "emptyList": "Noch keine Kunden mit aktivierter Stundenerfassung. Aktivieren Sie „Stundenerfassung“ zuerst auf der Kundendetailseite.", + "searchPlaceholder": "Nach E-Mail oder Firma suchen…", + "customerLoggingDisabled": "Bei diesem Kunden ist die Stundenerfassung deaktiviert. Aktiviere sie auf der Kundendetailseite, um Stunden zu erfassen." + }, + "crmDev": { + "title": "CRM-Entwicklung", + "subtitle": "Interne Werkzeuge zur Überprüfung von CRM-Abläufen. Standardmäßig ausgeblendet — Aktivierung über Einstellungen → Funktionen → Entwicklung.", + "warning": "Diese Werkzeuge lösen echte Nebeneffekte aus (E-Mails, Statusänderungen). Nur gegen Testdaten verwenden.", + "envDisabled": { + "title": "Entwicklungs-Tools durch Umgebungsschutz blockiert", + "body": "Setze PICPEAK_ENABLE_DEV_TOOLS=1 in der Backend-Env-Datei und starte den Server neu. Der Funktions-Schalter allein reicht nicht — dies ist eine bewusste Produktiv-Schutzschicht.", + "cardHint": "E-Mail-Vorlagen können nicht geladen werden, solange der Entwicklungs-Tools-Umgebungsschutz nicht geöffnet ist." + }, + "paymentCheck": { + "title": "Zahlungsprüfung-E-Mail testen (echte Rechnung)", + "help": "Sendet die Admin-Zahlungsprüfung-E-Mail für eine echte gesendete/überfällige Rechnung und umgeht dabei die 24-Stunden-Sperre. Die drei Buttons in der E-Mail enthalten echte signierte Tokens — ein Klick verändert den Rechnungsstatus.", + "selectInvoice": "Gesendete oder überfällige Rechnung", + "selectPlaceholder": "— Rechnung auswählen —", + "noneAvailable": "Keine gesendeten oder überfälligen Rechnungen vorhanden.", + "send": "Test-E-Mail senden", + "sending": "Wird eingereiht …", + "sentToast": "Test-E-Mail eingereiht. Bitte das Postfach des Empfängers prüfen." + }, + "templates": { + "title": "Beliebige CRM-E-Mail an mich senden (Mock-Daten)", + "help": "Reiht die gewählte Vorlage mit Platzhalterwerten an die eigene Admin-Adresse ein. Wenn die Installation echte Angebote/Rechnungen enthält, wird das passende PDF angehängt, damit die vollständige Ausgabe geprüft werden kann.", + "notSeeded": "Nicht eingespielt", + "send": "An mich senden", + "sending": "Wird eingereiht …", + "sentToast": "Eingereiht an {{to}}.", + "empty": "Keine CRM-E-Mail-Vorlagen gefunden — bitte Migrationen ausführen.", + "label": { + "quote_sent": { + "title": "Angebot gesendet (an Kunden)", + "description": "Wird beim Versand eines Angebots ausgelöst — enthält das PDF sowie Annehmen-/Ablehnen-Links." + }, + "quote_accepted_customer": { + "title": "Angebot angenommen — Kundenbestätigung", + "description": "Wird an den Kunden gesendet, nachdem er (oder der Admin in seinem Namen) das Angebot angenommen hat. Enthält PDF." + }, + "quote_accepted_admin": { + "title": "Angebot angenommen — Admin-Benachrichtigung", + "description": "Benachrichtigt den Admin, dass ein Angebot vom Kunden angenommen wurde." + }, + "quote_declined_admin": { + "title": "Angebot abgelehnt — Admin-Benachrichtigung", + "description": "Benachrichtigt den Admin, dass ein Angebot vom Kunden abgelehnt wurde." + }, + "invoice_sent": { + "title": "Rechnung gesendet (an Kunden)", + "description": "Wird beim Versand einer Rechnung ausgelöst — enthält das PDF." + }, + "invoice_reminder_first": { + "title": "Zahlungserinnerung — erste", + "description": "Erste Erinnerung an den Kunden. Noch keine Mahngebühr." + }, + "invoice_reminder_second": { + "title": "Zahlungserinnerung — zweite (mit Mahngebühr)", + "description": "Zweite Erinnerung mit zusätzlicher Mahngebühr." + }, + "invoice_payment_check_admin": { + "title": "Zahlungsprüfung (Admin)", + "description": "Admin-E-Mail mit drei Buttons, die signierte Tokens enthalten." + }, + "contract_sent": { + "title": "Vertrag gesendet (an Kunden)", + "description": "Versand Admin → Kunde mit dem unsignierten Vertrags-PDF im Anhang." + }, + "contract_signed_admin_notification": { + "title": "Vertrag unterzeichnet — Admin-Benachrichtigung", + "description": "Rückmeldung an den Admin, nachdem der Kunde unterzeichnet hat. Ohne Anhang." + }, + "contract_fully_signed": { + "title": "Vertrag beidseitig unterzeichnet", + "description": "Beidseitiger Versand, sobald beide Unterschriften vorliegen. Gestempeltes Vertrags-PDF im Anhang." + } + } + } + }, + "taxReport": { + "title": "Steuerliste", + "intro": "Periodenbezogene Umsatzliste mit Netto- und MwSt-Aufschlüsselung, gruppiert nach MwSt-Satz. Stornierte Rechnungen bleiben für die lückenlose Nummernfolge sichtbar, sind aber nicht in den Summen enthalten.", + "filters": { + "period": "Zeitraum", + "thisYear": "Dieses Jahr", + "lastYear": "Letztes Jahr", + "thisQuarter": "Dieses Quartal", + "lastQuarter": "Letztes Quartal", + "custom": "Benutzerdefiniert", + "from": "Von", + "to": "Bis", + "currency": "Währung" + }, + "exportPdf": "PDF exportieren", + "exportCsv": "CSV exportieren", + "exportFailed": "Export fehlgeschlagen. Bitte erneut versuchen.", + "errorTitle": "Steuerliste konnte nicht geladen werden", + "empty": "Keine Rechnungen in diesem Zeitraum.", + "statusCancelled": "Storniert", + "totalsByVatRate": "Summen nach MwSt-Satz", + "grandTotalNet": "Gesamt Netto", + "grandTotalVat": "Gesamt MwSt.", + "grandTotalGross": "Gesamt Brutto", + "cancelledFootnote": "{{count}} stornierte Rechnung(en) — Beträge nicht in den Summen enthalten (für lückenlose Nummernfolge dargestellt).", + "col": { + "date": "Datum", + "invoice": "Rechnung", + "customer": "Kunde", + "event": "Anlass", + "vatRate": "MwSt %", + "net": "Netto", + "vat": "MwSt.", + "total": "Brutto" + } + }, + "quotes": { + "status": { + "draft": "Entwurf", + "sent": "Gesendet", + "accepted": "Angenommen", + "declined": "Abgelehnt", + "expired": "Abgelaufen", + "converted": "Umgewandelt" + }, + "errors": { + "popupBlocked": "Erlauben Sie Pop-ups für diese Seite, um die PDF-Vorschau anzuzeigen.", + "customerRequired": "Bitte zuerst einen Kunden auswählen.", + "customerFeatureDisabled": "Bei diesem Kunden sind Angebote deaktiviert. Aktivieren Sie Angebote zuerst in den Kundendetails." + }, + "confirmSend": "Dieses Angebot jetzt an den Kunden senden?", + "sentToast": "Angebot an Kunden gesendet.", + "confirmConvert": "Dieses angenommene Angebot in einen Anlass + geplante Rechnungen umwandeln?", + "convertedToast": "Angebot in Anlass #{{id}} umgewandelt", + "send": "Senden", + "resend": "Erneut senden", + "convert": "In Anlass umwandeln", + "field": { + "issueDate": "Ausgestellt am", + "validUntil": "Gültig bis", + "eventName": "Anlass", + "eventDate": "Anlassdatum", + "sentAt": "Gesendet am", + "acceptedAt": "Angenommen am", + "declinedAt": "Abgelehnt am", + "responseWindow": "Antwortfrist", + "eventTimeStart": "Startzeit", + "eventTimeEnd": "Endzeit", + "expectedDuration": "Voraussichtliche Dauer (h)", + "vatRate": "MwSt-Satz %", + "shipping": "Versandkosten", + "currency": "Währung", + "selectPaymentTerm": "— Zahlungsbedingungen wählen —", + "paymentNetDays": "Zahlungsziel", + "paymentTiming": "Zahlungsablauf", + "selectNetDays": "— Zahlungsziel wählen —", + "selectTiming": "— Zahlungsablauf wählen —", + "introText": "Einleitungstext", + "outroText": "Schlusstext", + "ccPdfEmail": "PDF-Kopie an (zusätzlicher Empfänger)", + "ccPdfEmailPlaceholder": "name@example.com", + "ccPdfPickFromAdmins": "Aus Admins auswählen:", + "ccPdfCustom": "Eigene E-Mail-Adresse", + "internalNotes": "Interne Notizen (nicht auf der PDF)" + }, + "responseWindowOpen": "Offen bis {{at}}", + "section": { + "lineItems": "Positionen", + "internalNotes": "Interne Notizen", + "customer": "Kunde", + "event": "Anlassdetails", + "payment": "Zahlungsbedingungen", + "extras": "Einleitung / Schluss / Extras" + }, + "savedToast": "Angebot als Entwurf gespeichert.", + "edit": "Angebot bearbeiten", + "new": "Neues Angebot", + "preview": "PDF-Vorschau", + "saveAndSend": "Speichern & senden", + "customerSearch": "Kunde nach E-Mail oder Firma suchen…", + "title": "Angebote", + "subtitle": "Angebote versenden, verfolgen und in Anlässe umwandeln.", + "searchPlaceholder": "Nach Nummer, Kunde, Anlass suchen…", + "sort": { + "newest": "Neueste zuerst", + "oldest": "Älteste zuerst", + "customerAsc": "Kunde A→Z", + "valueAsc": "Betrag aufsteigend", + "valueDesc": "Betrag absteigend" + }, + "empty": "Noch keine Angebote.", + "table": { + "customer": "Kunde", + "event": "Anlass", + "issueDate": "Ausgestellt", + "total": "Gesamt", + "status": "Status" + }, + "pagination": "Seite {{page}} von {{total}} · {{count}} Angebote", + "trigger": { + "quote_accepted": "bei Annahme", + "before_event": "vor dem Anlass", + "after_event": "nach dem Anlass", + "after_delivery": "nach Lieferung", + "fixed_date": "an festem Datum" + } + }, + "crm": { + "lineItems": { + "quantity": "Anzahl", + "description": "Beschreibung", + "unitPrice": "Einzelpreis", + "total": "Summe", + "subtotal": "Zwischensumme", + "vat": "MwSt.", + "position": "Pos.", + "discount": "Rabatt %", + "descriptionPlaceholder": "Beschreibung (mehrere Zeilen möglich)", + "saveAsPreset": "Als Vorlage speichern", + "empty": "Noch keine Positionen — fügen Sie eine hinzu, um zu starten.", + "addRow": "Position hinzufügen", + "addFromPreset": "Aus Vorlage hinzufügen…", + "shipping": "Versand", + "addSubItem": "Unterposition hinzufügen", + "detailsAdd": "+ Notiz / Details hinzufügen", + "detailsFilled": "Details", + "detailsPlaceholder": "Optionale Notizen — Kleingedrucktes, Paketinhalte, Bedingungen…", + "autoTotaledHint": "Summe automatisch aus den Unterpositionen berechnet", + "autoTotaledNote": "= Σ Unterpositionen" + } + }, + "bills": { + "status": { + "scheduled": "Geplant", + "pending_delivery": "Wartet auf Lieferung", + "sent": "Gesendet", + "paid": "Bezahlt", + "overdue": "Überfällig", + "cancelled": "Storniert", + "skipped": "Übersprungen (leerer Monat)" + }, + "field": { + "number": "Rechnung", + "status": "Status", + "dueDate": "Fällig", + "total": "Gesamt", + "sourceQuote": "Vom Angebot", + "issueDate": "Ausgestellt am", + "scheduledSendAt": "Geplanter Versand (optional)", + "installment": "Rate", + "paid": "Bezahlt", + "outstanding": "Offen", + "lateFee": "Mahngebühr", + "qrFormat": "Zahlungs-QR-Format", + "vatRate": "MwSt-Satz %", + "shipping": "Versand", + "currency": "Währung", + "ccPdfEmail": "PDF-Kopie an (zusätzlicher Empfänger)", + "sourceQuote": "Aus Angebot", + "businessBankAccount": "Zahlungskonto", + "bankAccountProfileDefault": "Geschäftsprofil-Standard für {{currency}} verwenden", + "bankAccountDefaultBadge": "(Standard)", + "bankAccountHelp": "Überschreibt den Profil-Standard nur für diese Rechnung. Auf «Standard» belassen, um das passende Konto aus Einstellungen → Geschäftsprofil zu übernehmen.", + "paymentNetDays": "Zahlungsziel", + "paymentTiming": "Zahlungsablauf", + "selectNetDays": "— Zahlungsziel wählen —", + "selectTiming": "— Zahlungsablauf wählen —", + "eventName": "Anlass", + "eventDate": "Anlassdatum", + "eventTimeStart": "Startzeit", + "eventTimeEnd": "Endzeit", + "customer": "Kunde", + "invoiceNumber": "Rechnungsnummer", + "paidAmount": "Bereits bezahlt (optional)", + "pdfFile": "PDF-Datei", + "ccPdfCustom": "Eigene E-Mail-Adresse", + "ccPdfEmailPlaceholder": "name@beispiel.de", + "ccPdfPickFromAdmins": "Aus Admin-Liste wählen:", + "paymentTermHelp": "Zahlungsziel + Skonto für diese Rechnung. Leer lassen, um die Werte vom Quell-Angebot oder den globalen CRM-Standardwerten zu übernehmen.", + "skontoDisabled": "Skonto für diese Rechnung deaktivieren (unterdrückt den Skonto-Block im PDF sowie die „Mit Skonto bezahlt“-Buttons in der Admin-E-Mail / im Zahlung-erfassen-Dialog).", + "sourceContract": "Vom Vertrag" + }, + "import": "Historische Rechnung importieren", + "importTitle": "Historische Rechnung importieren", + "importHelp": "Hänge ein PDF aus einem früheren Abrechnungssystem an. Der Kunde sieht dieses Originaldokument in seinem Portal — picpeak erzeugt es nicht neu.", + "importedToast": "Rechnung importiert.", + "importError": "Import fehlgeschlagen", + "importing": "Importiere…", + "noMatch": "Keine Treffer", + "title": "Rechnungen", + "subtitle": "Rechnungen planen, versenden, Zahlungen erfassen und überfällige Rechnungen mahnen.", + "new": "Neue Rechnung", + "searchPlaceholder": "Nach Nummer oder Kunde suchen…", + "sort": { + "newest": "Neueste zuerst", + "dueAsc": "Fällig zuerst", + "dueDesc": "Fällig zuletzt", + "customerAsc": "Kunde A→Z", + "valueAsc": "Betrag aufsteigend", + "valueDesc": "Betrag absteigend" + }, + "filter": { + "unpaidOnly": "Nur unbezahlt" + }, + "empty": "Noch keine Rechnungen.", + "table": { + "customer": "Kunde", + "event": "Anlass", + "installment": "Rate", + "dueDate": "Fällig", + "total": "Gesamt", + "status": "Status" + }, + "errors": { + "popupBlocked": "Erlauben Sie Pop-ups für diese Seite, um die PDF-Vorschau anzuzeigen.", + "customerRequired": "Bitte zuerst einen Kunden auswählen." + }, + "confirmSend": "Diese Rechnung jetzt an den Kunden senden?", + "sentToast": "Rechnung an Kunden gesendet.", + "confirmRelease": "Fotos jetzt als geliefert markieren und Rechnung an den Kunden senden?", + "releaseForDelivery": "Geliefert markieren & senden", + "releasedToast": "Lieferungs-Rechnung gesendet.", + "monthlyDraftBadge": "Monats-Entwurf — wird am Stichtag des Kunden oder über „Rechnung jetzt ausstellen“ verschickt", + "confirmReminder": "Zahlungserinnerung jetzt senden?", + "reminderToast": "Zahlungserinnerung gesendet.", + "confirmCancel": "Diese Rechnung wirklich stornieren? Sie wird ausgegraut und kann nicht mehr bearbeitet werden.", + "confirmCancelDraft": "Diesen Rechnungsentwurf verwerfen? Es wird kein Dokument an den Kunden gesendet.", + "confirmCancelIssued": "Es wird eine Stornorechnung erzeugt und sofort an den Kunden gesendet. Dies kann nicht rückgängig gemacht werden. Fortfahren?", + "cancelledToast": "Rechnung storniert.", + "cancelledWithStornoToast": "Rechnung storniert — Stornorechnung an den Kunden gesendet.", + "confirmReissue": "Für diese Rechnung wird eine Stornorechnung an den Kunden gesendet, und ein neuer Entwurf mit den gleichen Positionen wird angelegt. Die neue Rechnung verweist auf diese als „Ersetzt R-XXXX“. Fortfahren?", + "confirmReissueCancelled": "Eine neue Rechnung mit denselben Positionen anlegen, verknüpft mit dieser stornierten Rechnung?", + "reissuedToast": "Rechnung neu ausgestellt — Entwurf wird geöffnet.", + "reissuedWithStornoToast": "Stornorechnung erzeugt — Entwurf wird geöffnet.", + "reissue": "Neu ausstellen", + "cancelAndReissue": "Stornieren & neu ausstellen", + "kind": { + "storno": "Stornorechnung", + "reissue": "Neu ausgestellt" + }, + "stornoCancelsLabel": "Diese Stornorechnung hebt Rechnung auf:", + "cancelledByStornoLabel": "Diese Rechnung wurde aufgehoben durch Stornorechnung", + "paymentRecordedToast": "Zahlung erfasst.", + "sendNow": "Jetzt senden", + "resend": "Erneut senden", + "markPaid": "Als bezahlt markieren", + "sendReminder": "Erinnerung senden", + "section": { + "lineItems": "Positionen", + "paymentLog": "Zahlungsverlauf", + "customer": "Kunde", + "event": "Anlassdetails", + "details": "Details", + "payment": "Zahlungsbedingungen" + }, + "noPayments": "Noch keine Zahlungen erfasst.", + "payment": { + "paidAt": "Bezahlt am", + "amount": "Betrag", + "method": "Methode", + "reference": "Referenz", + "notes": "Notizen", + "methodPlaceholder": "Methode wählen…", + "methods": { + "card": "Karte", + "cash": "Bar", + "paypal": "PayPal", + "twint": "TWINT" + }, + "withSkonto": "Mit Skonto bezahlt ({{percent}}% Rabatt)" + }, + "recordPayment": "Zahlung erfassen", + "savedToast": "Rechnung gespeichert.", + "savedToastMulti": "{{n}} Rechnungen erstellt.", + "saveAndSpawn": "{{n}} Rechnungen erstellen", + "edit": "Rechnung bearbeiten", + "customerSearch": "Kunde nach E-Mail oder Firma suchen…", + "qrFormat": { + "none": "Keiner (Override)", + "swiss": "Swiss QR-Bill", + "epc": "EPC QR (SEPA)", + "profileDefault": "Standard aus Geschäftsprofil verwenden" + } + }, + "businessProfile": { + "savedToast": "Geschäftsprofil gespeichert.", + "title": "Geschäftsprofil", + "subtitle": "Briefkopf, Kontaktdaten und Standardwerte für Angebote und Rechnungen.", + "section": { + "company": "Firma", + "contact": "Kontakt", + "defaults": "Standardwerte", + "banks": "Bankverbindungen" + }, + "field": { + "companyName": "Firmenname", + "vatId": "USt-IdNr.", + "taxId": "Steuernummer", + "addressLine1": "Adresszeile 1", + "addressLine2": "Adresszeile 2", + "postalCode": "PLZ", + "city": "Ort", + "state": "Kanton / Bundesland", + "countryCode": "Land", + "phone": "Telefon", + "mobile": "Mobil", + "email": "E-Mail", + "website": "Webseite", + "defaultCurrency": "Standardwährung", + "defaultLocale": "Standardsprache", + "timezone": "Zeitzone (IANA)", + "vatLabel": "MwSt-Bezeichnung", + "vatRateDefault": "Standard-MwSt-Satz %", + "defaultQrFormat": "Standard-QR-Format", + "footerLine": "Fusszeile" + }, + "qrFormat": { + "none": "Kein QR", + "swiss": "Swiss QR-Bill", + "epc": "EPC QR (SEPA)" + }, + "bankCreatedToast": "Bankverbindung hinzugefügt.", + "bankDeletedToast": "Bankverbindung gelöscht.", + "addBank": "Bankverbindung hinzufügen", + "bank": { + "label": "Bezeichnung", + "accountHolder": "Kontoinhaber", + "currency": "Währung", + "isDefault": "Standard", + "makeDefault": "Als Standard", + "confirmDelete": "Diese Bankverbindung wirklich löschen?" + }, + "noBanks": "Noch keine Bankverbindungen hinterlegt." + }, + "crmSettings": { + "savedToast": "CRM-Einstellungen gespeichert.", + "title": "CRM-Einstellungen", + "subtitle": "Standardwerte für Angebote und Rechnungen.", + "emptyState": "Aktuell sind keine CRM-Funktionen aktiv. Aktiviere Angebote, Rechnungen oder Verträge unter Einstellungen → Features, damit die passenden Konfigurationen hier erscheinen.", + "section": { + "quotes": "Angebote", + "invoices": "Rechnungen", + "paymentDefaults": "Standard-Zahlungsbedingungen", + "installmentDefaults": "Standard-Trigger für Teilzahlungen" + }, + "installmentDefaults": { + "help": "Vorbelegung der Trigger für neue Zeilen im Teilzahlungs-Panel. Pro-Dokument-Anpassungen überschreiben; bestehende Dokumente behalten ihren gespeicherten Plan.", + "trigger": { + "quote_accepted": "Bei Unterzeichnung / Erstellung", + "before_event": "Vor dem Anlass", + "after_event": "Nach dem Anlass", + "after_delivery": "Bei Lieferung (manuelle Freigabe)", + "fixed_date": "Festes Datum" + } + }, + "paymentDefaults": { + "help": "Wird bei jeder neuen Rechnung und jedem Angebot vorausgewählt. Im Editor lässt sich pro Dokument eine andere Kombination wählen.", + "none": "— Kein Standard —" + }, + "crm_invoices_default_payment_net_days_template_id": { + "label": "Standard-Zahlungsziel" + }, + "crm_invoices_default_payment_timing_template_id": { + "label": "Standard-Zahlungsablauf" + }, + "crm_quotes_accept_window_minutes": { + "label": "Antwortfrist nach Versand (Minuten)" + }, + "crm_quotes_default_valid_days": { + "label": "Standard-Gültigkeit Angebot (Tage)" + }, + "crm_quotes_number_format": { + "label": "Format Angebotsnummer" + }, + "crm_invoices_reminder_first_days": { + "label": "Erste Mahnung nach (Tagen)" + }, + "crm_invoices_reminder_second_days": { + "label": "Zweite Mahnung nach (Tagen)" + }, + "crm_invoices_late_fee_minor": { + "label": "Mahngebühr (Rappen / Cent)" + }, + "crm_invoices_late_fee_label": { + "label": "Bezeichnung Mahngebühr" + }, + "crm_invoices_skonto_percent_default": { + "label": "Standard-Skonto %" + }, + "crm_invoices_skonto_business_days": { + "label": "Skonto-Frist (Werktage)" + }, + "crm_invoices_installment_trigger_first": { + "label": "Trigger erste Teilzahlung" + }, + "crm_invoices_installment_days_before_event": { + "label": "Tage vor dem Anlass (mittlere Teilzahlung)" + }, + "crm_invoices_installment_days_after_event": { + "label": "Tage nach dem Anlass (letzte Teilzahlung)" + }, + "crm_invoices_number_format": { + "label": "Format Rechnungsnummer" + }, + "crm_quotes_pdf_attachment_enabled": { + "label": "PDF an E-Mail anhängen (Angebote)" + }, + "crm_quotes_skonto_enabled": { + "label": "Skonto auf Angeboten aktivieren" + }, + "crm_invoices_qr_enabled": { + "label": "Zahlungs-QR auf Rechnungen aktivieren" + }, + "crm_invoices_reminders_enabled": { + "label": "Automatische Mahnungen aktivieren" + }, + "crm_invoices_late_fee_enabled": { + "label": "Mahngebühr aktivieren" + } + }, + "quoteResponse": { + "notFound": "Angebot nicht gefunden", + "notFoundBody": "Dieser Angebotslink ist nicht mehr gültig.", + "locked": "Dieses Angebot kann nicht mehr beantwortet werden.", + "title": "Angebot", + "event": "Anlass", + "issueDate": "Ausgestellt am", + "validUntil": "Gültig bis", + "description": "Beschreibung", + "qty": "Anzahl", + "unit": "Einzelpreis", + "total": "Gesamt", + "subtotal": "Zwischensumme", + "vat": "MwSt.", + "acceptedLocked": "Dieses Angebot wurde bereits angenommen.", + "declinedLocked": "Dieses Angebot wurde abgelehnt.", + "cannotRespond": "Es ist nicht mehr möglich, auf dieses Angebot zu antworten.", + "changeWithin": "Sie können Ihre Antwort innerhalb von {{minutes}} Minuten ändern.", + "intro": "Vielen Dank für Ihre Anfrage. Bitte prüfen Sie das folgende Angebot und antworten Sie unten.", + "accept": "Angebot annehmen", + "decline": "Angebot ablehnen" + }, + "reminderTemplates": { + "title": "Erinnerungs-E-Mails vor dem Anlass", + "globalSection": "Globales Verhalten", + "globalHelp": "Standardmässig aus — aktiviere, um Erinnerungen zu senden. Der unten gesetzte Offset ist der Standard; jeder Anlass kann ihn auf der Detailseite überschreiben.", + "enableLabel": "Erinnerungs-E-Mails vor dem Anlass senden", + "daysBeforeLabel": "Tage vor dem Anlass", + "saveSettings": "Globale Einstellungen speichern", + "settingsSaved": "Erinnerungs-Einstellungen gespeichert.", + "settingsSaveError": "Erinnerungs-Einstellungen konnten nicht gespeichert werden.", + "sidebarHeading": "Vorlagen", + "sectionTemplates": "Vorlagen", + "defaultLabel": "Standard (Auffang)", + "usesDefault": "Standard", + "usesDefaultTooltip": "Noch keine eigene Vorlage — dieser Anlass-Typ nutzt die Standard-Vorlage. Bearbeite + speichere hier, um eine eigene zu erstellen.", + "willCreateOnSave": "Dieser Anlass-Typ verwendet die Standard-Vorlage. Die Felder unten sind aus der Standard-Vorlage vorbelegt; beim Speichern wird eine eigene Vorlage für diesen Anlass-Typ erstellt.", + "variablesHint": "Verfügbare Variablen: {{customer_name}}, {{event_name}}, {{event_date}}, {{event_type}}, {{days_before}}, {{business_name}} — werden beim Versand der E-Mail ersetzt.", + "subjectLabel": "Betreff", + "subjectPlaceholder": "Betreff der E-Mail", + "bodyLabel": "Text", + "noTranslation": "Noch keine Übersetzung", + "saveTemplate": "Vorlage speichern", + "saved": "Vorlage gespeichert.", + "saveError": "Vorlage konnte nicht gespeichert werden." + }, + "eventReminderOverride": { + "title": "Erinnerung vor dem Event", + "save": "Override speichern", + "help": "Event-spezifischer Override für die Kundenerinnerung. Globaler An/Aus-Schalter und Standard-Offset liegen unter Einstellungen → Erinnerungs-E-Mails. Was hier leer bleibt, übernimmt die globale Einstellung bzw. die aufgelöste Vorlage.", + "disabledLabel": "Erinnerung für dieses Event deaktivieren (es wird keine E-Mail verschickt)", + "offsetLabel": "Tage vor dem Event (Override) — leer lassen für Standard", + "offsetPlaceholder": "Leer lassen, um den globalen Standard zu verwenden", + "bodyOverrideLabel": "Eigener Text für dieses Event (überschreibt den Vorlagentext)", + "bodyOverridePlaceholder": "Leer lassen, um den Vorlagentext zu verwenden. Variablen wie {{customer_name}}, {{event_name}}, {{event_date}} funktionieren auch hier.", + "saved": "Erinnerungs-Override gespeichert.", + "saveError": "Erinnerungs-Override konnte nicht gespeichert werden.", + "invalidOffset": "Offset muss eine nicht-negative ganze Zahl oder leer sein." + }, + "dealLineage": { + "title": "Verknüpfte Dokumente", + "quotes": "Angebote", + "contracts": "Verträge", + "invoices": "Rechnungen", + "loading": "Verknüpfte Dokumente werden geladen…", + "error": "Verknüpfte Dokumente konnten nicht geladen werden.", + "empty": "Noch keine weiteren Dokumente in diesem Vorgang. Neue Rechnungen, Verträge oder Teilzahlungen erscheinen hier, sobald sie erstellt werden.", + "editPlan": "Plan bearbeiten", + "editPlanModalTitle": "Teilzahlungsplan bearbeiten", + "editPlanHelp": "Den Plan atomar umformen: Prozente, Bezeichnungen, Trigger ändern, Zeilen hinzufügen oder entfernen. Der Plan-Gesamtbetrag bleibt fix; bestehende Rechnungsnummern werden wo möglich beibehalten. Nicht mehr möglich, sobald eine Rechnung versendet wurde.", + "editPlanWarning": "Beim Entfernen von Zeilen werden deren Rechnungsnummern gelöscht (die Nummernfolge kann sie nicht freigeben — §14 UStG-Kontinuitätspflicht). Neue Zeilen vergeben frische Nummern.", + "editPlanSave": "Plan speichern", + "editPlanSuccess": "Teilzahlungsplan aktualisiert.", + "editPlanLocked": "Plan kann nicht mehr bearbeitet werden — mindestens eine Rechnung wurde versendet oder storniert.", + "editPlanPercentSumError": "Prozente müssen sich auf 100 summieren.", + "editPlanGeneralError": "Plan konnte nicht aktualisiert werden.", + "editPlanEmpty": "Plan darf nicht leer sein." + }, + "installments": { + "enableLabel": "In Teilzahlungen aufteilen", + "advancedToggle": "Dynamische Trigger verwenden", + "simpleToggle": "Einfache Datumsauswahl verwenden", + "simpleHint": "Jede Zeile wird an einem festen Datum versendet. Wechsle zu dynamischen Triggern für Pläne, die an das Anlassdatum oder die Lieferung gekoppelt sind.", + "advancedHint": "Wähle einen Trigger (Angebot angenommen, vor/nach Anlass, bei Lieferung, festes Datum) und einen Offset in Tagen. Trigger lösen sich neu auf, wenn sich das Anlassdatum später verschiebt.", + "percent": "%", + "label": "Bezeichnung", + "labelPlaceholder": "Anzahlung / vor Anlass …", + "sendOn": "Versanddatum", + "trigger": "Trigger", + "offsetDays": "Offset (Tage)", + "onDeliveryHint": "Bei Lieferung — Admin gibt manuell frei. Für Änderungen auf erweitert umschalten.", + "addRow": "Teilzahlung hinzufügen", + "removeRow": "Zeile entfernen", + "total": "Summe", + "mustSumTo100": "muss 100 % ergeben", + "firstRowDefaultLabel": "Anzahlung", + "triggerOption": { + "quote_accepted": "Bei Unterzeichnung / Erstellung", + "before_event": "Vor dem Anlass", + "after_event": "Nach dem Anlass", + "after_delivery": "Bei Lieferung (manuelle Freigabe)", + "fixed_date": "Festes Datum" + } + }, + "contracts": { + "title": "Verträge", + "subtitle": "Verträge aus wiederverwendbaren Bausteinen zusammenstellen und durch den Kunden im Browser oder per hochgeladenem, handunterschriebenem PDF unterzeichnen lassen.", + "sections": { + "basics": "Vertragsgrundlagen", + "scope": "Leistungsumfang", + "privacy": "Persönlichkeitsrechte & Datenschutz", + "commercial": "Kaufmännisches", + "nda": "Vertraulichkeit", + "closing": "Schlussbestimmungen" + }, + "status": { + "draft": "Entwurf", + "sent": "Gesendet", + "signed_by_customer": "Kunde unterzeichnet", + "signed_by_admin": "Gegengezeichnet", + "fully_signed": "Beidseitig unterzeichnet", + "cancelled": "Storniert" + }, + "list": { + "new": "Neuer Vertrag" + }, + "detail": { + "integrity": { + "title": "PDF-Integritätsprüfung", + "help": "Berechnet die SHA-256-Hashes der unsignierten und signierten PDFs neu und vergleicht sie mit den beim Ausstellen gespeicherten Werten. Deckt Backup-Beschädigungen oder nachträgliche Änderungen an der Kundenkopie auf.", + "verify": "Prüfen", + "reverify": "Erneut prüfen", + "unsignedTitle": "Unsigniertes PDF", + "signedTitle": "Signiertes PDF", + "match": "Hash stimmt überein", + "mismatch": "Hash weicht ab — Datei verändert", + "missing": "Datei auf dem Server nicht gefunden", + "notIssued": "Noch nicht ausgestellt", + "expected": "erwartet", + "actual": "tatsächlich", + "error": "Integritätsprüfung fehlgeschlagen." + } } } } diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 4a686dd8..3512bb1a 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -124,7 +124,11 @@ "saveChanges": "Save changes", "resetChanges": "Reset Changes", "dismiss": "Dismiss", - "discard": "Discard" + "discard": "Discard", + "preview": "Preview", + "duplicate": "Duplicate", + "showAll": "Show all", + "confirm": "Confirm" }, "upload": { "photoCategory": "Photo Category", @@ -168,7 +172,8 @@ "cmsPages": "CMS Pages", "users": "Users", "calendar": "Calendar", - "clients": "Clients" + "clients": "CRM", + "betaTag": "Beta" }, "archives": { "title": "Archives", @@ -378,6 +383,9 @@ "eventName": "Event Name", "eventType": "Event Type", "eventDate": "Event Date", + "fullDay": "Full day", + "eventTimeStart": "Start time", + "eventTimeEnd": "End time", "hostEmail": "Customer Email", "hostName": "Customer Name", "hostNamePlaceholder": "John Smith", @@ -1030,7 +1038,14 @@ "integrations": "Integrations", "system": "System", "appearance": "Content & Appearance", - "communication": "Communication" + "communication": "Communication", + "crm": "CRM-Settings" + }, + "contracts": { + "title": "Contracts" + }, + "reminderTemplates": { + "title": "Reminder emails" }, "apiTokens": { "title": "API Tokens", @@ -1105,7 +1120,7 @@ "sales": "Sales", "insights": "Insights & Access", "customers": "Customers", - "clients": "Clients" + "clients": "CRM" }, "status": { "stable": "stable", @@ -1130,18 +1145,29 @@ }, "calendar": { "title": "Calendar", - "description": "See all upcoming and past events on a month/week view. Optionally accept new bookings from clients.", + "description": "Admin-only calendar showing events, logged hours, and pending quotes/contracts in one view. Drag-create hours directly on the calendar.", "sidebar": "Calendar" }, + "calendarBooking": { + "title": "Customer booking", + "description": "Let customers see free slots on a public calendar and book directly. Coming soon.", + "sidebar": "Booking" + }, "quotes": { "title": "Quotes", "description": "Send line-itemed quotes to clients. They can accept or decline from a public link; payment is tracked manually.", "sidebar": "Quotes" }, "bills": { - "title": "Bills", - "description": "Generate a bill from any accepted quote. Mark paid manually — no payment processor integration.", - "sidebar": "Bills" + "title": "Invoices", + "description": "Generate an invoice from any accepted quote. Mark paid manually — no payment processor integration.", + "sidebar": "Invoices" + }, + "taxReport": { + "title": "Tax report", + "description": "Period-scoped revenue list with net + VAT breakdown grouped by VAT rate. Export as PDF (landscape, company letterhead) or CSV for your accountant. Cancelled invoices stay visible for a gap-free audit trail but are excluded from totals.", + "sidebar": "Tax", + "requiresBills": "Enable Bills first — the tax report reads from your invoices." }, "analytics": { "title": "Analytics", @@ -1153,12 +1179,12 @@ "warning": "Existing user accounts stay valid; the admin UI for managing them will be hidden until you re-enable this." }, "customerPortal": { - "title": "Clients", - "description": "Persistent customer logins. Recurring clients see all their assigned galleries from one place — no per-event passwords. Customers log in at /customer/login and you manage them under Clients → Accounts." + "title": "Customer Accounts", + "description": "Persistent customer logins. Recurring clients see all their assigned galleries from one place — no per-event passwords. Customers log in at /customer/login and you manage them under CRM → Accounts." }, "clients": { - "title": "Clients", - "description": "Master switch for the Clients sidebar section. Off hides the section and every sub-feature below regardless of their individual toggles — re-enable to restore them to whatever you set last." + "title": "CRM", + "description": "Master switch for the CRM sidebar section. Off hides the section and every sub-feature below regardless of their individual toggles — re-enable to restore them to whatever you set last." } }, "customerSurface": { @@ -1361,6 +1387,12 @@ "saveTheme": "Save Theme", "previewLayout": "Preview Layout", "livePreview": "Live Preview", + "pdfTypography": "PDF typography", + "pdfTypographyHelp": "Used for invoice + quote letterheads. Pick one of the bundled fonts, or leave on default to use Helvetica.", + "pdfFontFamily": "Body font", + "pdfFontFamilyDefault": "Use Helvetica (default)", + "pdfFontFamilyMissing": "missing", + "pdfFontSavedToast": "PDF font saved.", "heroPlaceholderText": "Your event's hero photo will appear here", "whiteLabel": "White Label", "hidePoweredBy": "Hide \"Powered by PicPeak\" branding", @@ -1375,6 +1407,11 @@ "logoMaxHeight": "Maximum Height (pixels)", "logoMaxHeightHelp": "Set a custom maximum height for the logo (20-200 pixels)", "logoPosition": "Logo Position in Header", + "positionLeft": "Left", + "positionCenter": "Center", + "positionRight": "Right", + "positionSidepanel": "Sidebar", + "positionSidepanelHelp": "The logo appears in the admin sidebar instead of the header. When the sidebar is collapsed, the favicon is shown.", "logoDisplayMode": "Display Mode", "logoOnly": "Logo Only", "textOnly": "Company Name Only", @@ -1987,6 +2024,7 @@ "customers": "Customers", "calendar": "Calendar", "quotes": "Quotes", + "contracts": "Contracts", "billing": "Billing" }, "featureOff": "Feature off", @@ -2773,9 +2811,10 @@ "galleries": "Galleries", "calendar": "Calendar", "quotes": "Quotes", - "bills": "Bills", + "bills": "Invoices", "profile": "Profile", - "soon": "Soon" + "soon": "Soon", + "new": "New" }, "comingSoon": { "tag": "Coming soon" @@ -2786,11 +2825,51 @@ }, "quotes": { "title": "Quotes", - "body": "Review and accept quotes for upcoming shoots in one place. We're still building this — for now, your photographer will keep sending quotes the usual way." + "subtitle": "Every quote your photographer has sent you. Click an open quote to accept or decline.", + "empty": "No quotes yet.", + "disabled": "This feature is currently disabled for your account. Please contact your photographer if you expected to see quotes here.", + "loadError": "Could not load quotes.", + "noEventName": "—", + "openToRespond": "Open to respond", + "viewPdf": "View PDF", + "popupBlocked": "Allow pop-ups for this site to view the quote PDF." }, "bills": { - "title": "Bills", - "body": "Invoices and payment history for your sessions will be available here. We'll send you an email when this is live." + "title": "Invoices", + "subtitle": "Every invoice your photographer has sent you. Click \"View PDF\" to download.", + "empty": "No invoices yet.", + "disabled": "This feature is currently disabled for your account. Please contact your photographer if you expected to see invoices here.", + "loadError": "Could not load invoices.", + "viewPdf": "View PDF", + "popupBlocked": "Allow pop-ups for this site to view the invoice PDF.", + "field": { + "issueDate": "Issued", + "dueDate": "Due", + "paidAt": "Paid", + "outstanding": "Outstanding", + "lateFee": "Late fee" + }, + "filter": { + "outstanding": "Outstanding (sent + overdue)" + }, + "kind": { + "storno": "Cancellation invoice" + }, + "cancelsLabel": "Cancels invoice", + "cancelledByLabel": "Cancelled by cancellation invoice" + }, + "sort": { + "label": "Sort", + "newest": "Newest first", + "oldest": "Oldest first", + "priceDesc": "Price (high to low)", + "priceAsc": "Price (low to high)" + }, + "filter": { + "label": "Filter", + "all": "All", + "countAll": "{{count}} total", + "countFiltered": "{{visible}} of {{total}}" }, "profile": { "title": "Customer profile", @@ -2860,6 +2939,78 @@ } }, "customers": { + "field": { + "featureHoursLogging": "Hours logging", + "hourlyRate": "Default hourly rate", + "hourlyRateHint": "Major units (e.g. 150.00 for {{currency}} 150). Leave blank to require a per-entry override on every block." + }, + "hours": { + "section": "Hours", + "monthlyHint": "Entries auto-append to the current monthly draft. Edit / delete remains possible until the scheduler arms the draft for send.", + "perEventHint": "Logged entries stay unbilled until you click \"Create draft invoice\" — a standalone draft invoice is generated with one line per entry, ready for you to review before sending.", + "form": { + "title": "Log new entry", + "date": "Date", + "start": "Start", + "end": "End", + "duration": "Duration", + "durationPlaceholder": "1h · 1.5 · 1:30", + "durationHint": "Type a duration to auto-fill End: 1h, 1.5, 1,5 or 1:30", + "rateOverride": "Rate override", + "note": "Note / description", + "notePlaceholder": "What was worked on?", + "save": "Add entry" + }, + "col": { + "date": "Date", + "range": "Time", + "hours": "Hours", + "rate": "Rate", + "total": "Total", + "note": "Note", + "status": "Status" + }, + "status": { + "unbilled": "Unbilled", + "billed": "Billed", + "billedOn": "Billed: {{number}}" + }, + "billButton": "Create draft invoice", + "unbilledCount": "{{count}} unbilled entries totaling {{total}}", + "empty": "No entries logged yet.", + "confirmDelete": "Delete this entry? If it has been billed onto a draft, the matching invoice line will also be removed.", + "locked": "Locked: invoice already armed for send", + "toast": { + "created": "Entry logged", + "deleted": "Entry deleted", + "billed": "Hours billed" + } + }, + "create": { + "openButton": "Create passive customer", + "openLink": "+ Create new customer", + "title": "Create new customer", + "subtitle": "Fill in the details below. Choose \"Save as passive customer\" to create an admin-only record, or \"Save & send portal invitation\" to also email the customer a sign-up link.", + "passiveSubtitle": "Adds an admin-only customer record. The customer is not notified and cannot log in until you send them an invitation later.", + "saveAsPassive": "Save as passive customer", + "saveAndInvite": "Save & send portal invitation", + "savedPassiveToast": "Passive customer created.", + "savedActiveToast": "Customer created and portal invitation sent.", + "inviteFailedToast": "Customer saved (passive). Invitation email failed — retry from the customer detail page.", + "emailRequired": "A valid email is required." + }, + "passive": { + "badge": "Passive — admin only", + "activeLabel": "Has portal access", + "sendInvite": "Send portal invitation", + "sendInviteToast": "Portal invitation sent.", + "alreadyActive": "Customer already has portal access — no invitation needed.", + "detailHint": "This customer has no portal access (admin-only record). Click below to email them a portal sign-up link. The customer's existing invoices, quotes, and gallery assignments are preserved when they claim the invitation.", + "deactivatedHint": "Reactivate the customer before sending the invitation." + }, + "hoursLoggingDisabled": { + "badge": "Hour logging disabled" + }, "pageTitle": "Customers", "pageSubtitle": "Recurring customer accounts that can log in at /customer/login.", "unnamed": "Unnamed", @@ -2935,6 +3086,7 @@ "noEvents": "Not assigned to any events yet. Add this customer to an event from the event form.", "email": "Email", "preferredLanguage": "Preferred language", + "preferredLanguageHint": "Drives portal UI and quote/invoice PDF locale. New customers default to the business-profile language ({{lang}}); override here per customer.", "salutation": "Salutation", "salutationNone": "—", "firstName": "First name", @@ -2950,6 +3102,7 @@ "city": "City", "state": "State / region", "countryCode": "Country (ISO 2)", + "countryName": "Country (full name)", "notesHint": "Visible only to admins. Never shown to the customer.", "featuresSection": "Customer features", "featuresHint": "Per-customer overrides for the customer-surface tabs. The global toggles in Settings → Customer Surface are the master switch — when global is OFF nobody sees the tab, regardless of what you set here. Defaults are ON, so flip a switch OFF to hide a tab for this specific customer.", @@ -2961,8 +3114,33 @@ "error": "Could not send password reset", "inactive": "Reactivate the customer before sending a reset." }, + "quotesSection": "Quotes", + "noQuotes": "No quotes for this customer yet.", + "contractsSection": "Contracts", + "noContracts": "No contracts for this customer yet.", + "billsSection": "Invoices", + "noBills": "No invoices for this customer yet.", "manageEvents": "Manage galleries" }, + "billing": { + "section": "Billing cadence", + "hint": "Per-event (default): every invoice is sent on its own schedule. Monthly: all invoices issued in the period accumulate into one bill that fires on the configured day.", + "cadence": "Billing cadence", + "perEvent": "Per event", + "monthly": "Monthly", + "quarterly": "Quarterly", + "cycleDay": "Cycle day", + "cycleDayHint": "1..28 = day of month. Use negative -1..-15 for \"N days before month end\" (so -3 fires on the 28th of a 31-day month).", + "triggerNow": "Trigger invoice now", + "triggerConfirm": "Issue this customer's monthly bill now? The customer receives the email immediately.", + "triggerHint": "Bypasses the cadence day and issues the running draft immediately. Refuses when nothing has been queued for the current period.", + "triggered": "Monthly bill issued: {{number}}", + "triggerError": "Could not trigger the monthly bill.", + "draftPreview": { + "title": "Pending in this month's bill", + "periodRange": "{{number}} · {{from}} – {{to}}" + } + }, "reactivate": { "button": "Reactivate", "success": "Customer reactivated", @@ -2997,15 +3175,731 @@ } }, "clients": { - "title": "Clients", - "subtitle": "Customer accounts, scheduling, quotes and billing for recurring clients.", - "navAriaLabel": "Clients navigation", + "title": "CRM", + "subtitle": "Customer accounts, scheduling, quotes and billing — everything for recurring clients in one place.", + "navAriaLabel": "CRM navigation", "empty": { - "title": "No client features enabled", - "body": "Enable Accounts (or another Clients sub-feature) under Settings → Features to get started." + "title": "No CRM features enabled", + "body": "Enable Accounts (or another CRM sub-feature) under Settings → Features to get started." }, "subnav": { - "accounts": "Accounts" + "accounts": "Accounts", + "quotes": "Quotes", + "contracts": "Contracts", + "hours": "Hours", + "calendar": "Calendar", + "bills": "Invoices", + "taxReport": "Tax", + "development": "Development" + } + }, + "calendar": { + "pageTitle": "Calendar", + "subtitle": "Events, logged hours, and pending quotes/contracts in one view.", + "viewMonth": "Month", + "viewWeek": "Week", + "loading": "Loading items…", + "legend": { + "events": "Events", + "hours": "Hours", + "pendingQuotes": "Pending quotes", + "pendingContracts": "Pending contracts", + "hoursLocked": "Locked (billed)" + }, + "hourEntry": { + "createTitle": "Log hours", + "customerLabel": "Customer", + "customerSearch": "Search by email or company…", + "descriptionLabel": "Description (optional)", + "descriptionPlaceholder": "Editing / shoot / travel…", + "startLabel": "Start", + "endLabel": "End", + "submit": "Save hours", + "saving": "Saving…", + "cancel": "Cancel", + "close": "Close", + "delete": "Delete", + "confirmDelete": "Delete these logged hours? This cannot be undone.", + "created": "Hours logged.", + "createFailed": "Couldn't save: {{message}}", + "saved": "Hours updated.", + "saveFailed": "Couldn't save: {{message}}", + "movedToast": "Hours updated.", + "moveFailed": "Couldn't move: {{message}}", + "resizedToast": "Hours updated.", + "resizeFailed": "Couldn't resize: {{message}}", + "deleted": "Hours deleted.", + "deleteFailed": "Couldn't delete: {{message}}", + "lockedBadge": "Locked", + "featureOffToast": "Hour logging is disabled.", + "lockedTooltip": "Already billed — Storno the invoice to edit.", + "noDescription": "No description.", + "untitledCustomer": "Hours" + } + }, + "hoursLogging": { + "title": "Hours logging", + "subtitle": "Pick a customer and log billable time blocks. Entries flow into the next monthly bill or are billed on demand for per-event customers.", + "pickCustomer": "Customer", + "pickPlaceholder": "— Select customer —", + "emptyList": "No customers have hours logging enabled yet. Flip \"Hours logging\" on a customer's detail page first.", + "searchPlaceholder": "Search by email or company…", + "customerLoggingDisabled": "This customer has hour logging disabled. Enable it on the customer's detail page to log hours." + }, + "crmDev": { + "title": "CRM Development", + "subtitle": "Internal tools for verifying CRM flows. Hidden by default — enabled via Settings → Features → Development.", + "warning": "These tools fire real side effects (emails, status changes). Use against test data.", + "envDisabled": { + "title": "Dev tools blocked by env gate", + "body": "Set PICPEAK_ENABLE_DEV_TOOLS=1 in your backend env file and restart the server. The feature flag alone is not enough — this is a deliberate production-safety guard.", + "cardHint": "Email templates can't be listed until the dev-tools env gate is opened." + }, + "paymentCheck": { + "title": "Test payment-check email (real invoice)", + "help": "Fires the admin payment-check email for a real sent/overdue invoice, bypassing the 24h throttle. The three buttons in the email are real signed tokens — clicking them will affect the invoice status.", + "selectInvoice": "Sent or overdue invoice", + "selectPlaceholder": "— Pick an invoice —", + "noneAvailable": "No sent or overdue invoices in the database.", + "send": "Send test email", + "sending": "Queuing…", + "sentToast": "Test email queued. Check the recipient's inbox." + }, + "templates": { + "title": "Send any CRM email to me (mock data)", + "help": "Queues the chosen template to your own admin email with placeholder values. When the install has a real quote / invoice on file, the appropriate PDF is attached so you can verify the full output.", + "notSeeded": "Not seeded", + "send": "Send to me", + "sending": "Queuing…", + "sentToast": "Queued to {{to}}.", + "empty": "No CRM email templates found — run migrations to seed them.", + "label": { + "quote_sent": { + "title": "Quote sent (to customer)", + "description": "Fires when a quote is sent — includes the PDF attachment + accept/decline links." + }, + "quote_accepted_customer": { + "title": "Quote accepted — customer confirmation", + "description": "Sent to the customer after they (or the admin on their behalf) accept the quote. Includes PDF." + }, + "quote_accepted_admin": { + "title": "Quote accepted — admin notification", + "description": "Notifies the admin that a quote was accepted by the customer." + }, + "quote_declined_admin": { + "title": "Quote declined — admin notification", + "description": "Notifies the admin that a quote was declined by the customer." + }, + "invoice_sent": { + "title": "Invoice sent (to customer)", + "description": "Fires when an invoice is sent — includes the PDF attachment." + }, + "invoice_reminder_first": { + "title": "Invoice reminder — first", + "description": "First reminder to the customer. No late fee yet." + }, + "invoice_reminder_second": { + "title": "Invoice reminder — second (with Mahngebühr)", + "description": "Second reminder with the late fee surcharge added." + }, + "invoice_payment_check_admin": { + "title": "Payment-check (admin)", + "description": "Admin email with the three signed-token action buttons." + }, + "contract_sent": { + "title": "Contract sent (to customer)", + "description": "Admin → customer send with the unsigned contract PDF attached." + }, + "contract_signed_admin_notification": { + "title": "Contract signed — admin notification", + "description": "Customer-signed ping back to the admin. No attachment." + }, + "contract_fully_signed": { + "title": "Contract fully signed (dual-party)", + "description": "Dual-party send once both signatures are in. Stamped contract PDF attached." + } + } + } + }, + "taxReport": { + "title": "Tax report", + "intro": "Period-scoped revenue list with net + VAT breakdown grouped by VAT rate. Cancelled invoices stay visible for audit-trail continuity but are excluded from totals.", + "filters": { + "period": "Period", + "thisYear": "This year", + "lastYear": "Last year", + "thisQuarter": "This quarter", + "lastQuarter": "Last quarter", + "custom": "Custom range", + "from": "From", + "to": "To", + "currency": "Currency" + }, + "exportPdf": "Export PDF", + "exportCsv": "Export CSV", + "exportFailed": "Export failed. Please try again.", + "errorTitle": "Could not load tax report", + "empty": "No invoices in this period.", + "statusCancelled": "Cancelled", + "totalsByVatRate": "Totals by VAT rate", + "grandTotalNet": "Total net", + "grandTotalVat": "Total VAT", + "grandTotalGross": "Total gross", + "cancelledFootnote": "{{count}} cancelled invoice(s) — amounts excluded from totals (shown for audit-trail continuity).", + "col": { + "date": "Date", + "invoice": "Invoice", + "customer": "Customer", + "event": "Event", + "vatRate": "VAT %", + "net": "Net", + "vat": "VAT", + "total": "Gross" + } + }, + "quotes": { + "errors": { + "popupBlocked": "Allow pop-ups for this site to preview the PDF.", + "customerRequired": "Pick a customer first.", + "customerFeatureDisabled": "This customer has Quotes disabled. Enable Quotes on the customer detail page first." + }, + "confirmSend": "Send this quote to the customer now?", + "sentToast": "Quote sent to customer.", + "confirmConvert": "Convert this accepted quote into an event + scheduled invoices?", + "convertedToast": "Quote converted to event #{{id}}", + "send": "Send", + "resend": "Resend", + "convert": "Convert to event", + "field": { + "issueDate": "Issued", + "validUntil": "Valid until", + "eventName": "Event", + "eventDate": "Event date", + "sentAt": "Sent at", + "acceptedAt": "Accepted at", + "declinedAt": "Declined at", + "responseWindow": "Response window", + "eventTimeStart": "Start time", + "eventTimeEnd": "End time", + "expectedDuration": "Expected duration (h)", + "vatRate": "VAT rate %", + "shipping": "Shipping amount", + "currency": "Currency", + "selectPaymentTerm": "— Select payment terms —", + "paymentNetDays": "Net days", + "paymentTiming": "Payment schedule", + "selectNetDays": "— Select net days —", + "selectTiming": "— Select schedule —", + "introText": "Intro text", + "outroText": "Outro text", + "ccPdfEmail": "CC PDF to (extra recipient)", + "ccPdfEmailPlaceholder": "name@example.com", + "ccPdfPickFromAdmins": "Pick from admins:", + "ccPdfCustom": "Custom email", + "internalNotes": "Internal notes (not on PDF)" + }, + "responseWindowOpen": "Open until {{at}}", + "section": { + "lineItems": "Line items", + "internalNotes": "Internal notes", + "customer": "Customer", + "event": "Event details", + "payment": "Payment conditions", + "extras": "Intro / outro / extras" + }, + "savedToast": "Quote saved as draft.", + "edit": "Edit quote", + "new": "New quote", + "preview": "Preview PDF", + "saveAndSend": "Save & send", + "customerSearch": "Search customer by email or company…", + "title": "Quotes", + "subtitle": "Send, track and convert quotes into events.", + "searchPlaceholder": "Search by number, customer, event…", + "sort": { + "newest": "Newest first", + "oldest": "Oldest first", + "customerAsc": "Customer A→Z", + "valueAsc": "Value low→high", + "valueDesc": "Value high→low" + }, + "empty": "No quotes yet.", + "table": { + "customer": "Customer", + "event": "Event", + "issueDate": "Issued", + "total": "Total", + "status": "Status" + }, + "pagination": "Page {{page}} of {{total}} · {{count}} quotes", + "status": { + "draft": "Draft", + "sent": "Sent", + "accepted": "Accepted", + "declined": "Declined", + "expired": "Expired", + "converted": "Converted" + }, + "trigger": { + "quote_accepted": "on acceptance", + "before_event": "before event", + "after_event": "after event", + "after_delivery": "after delivery", + "fixed_date": "on fixed date" + } + }, + "crm": { + "lineItems": { + "quantity": "Qty", + "description": "Description", + "unitPrice": "Unit", + "total": "Total", + "subtotal": "Subtotal", + "vat": "VAT", + "position": "Pos.", + "discount": "Rabatt %", + "descriptionPlaceholder": "Description (multi-line OK)", + "saveAsPreset": "Save as preset", + "empty": "No line items yet — add one to get started.", + "addRow": "Add row", + "addFromPreset": "Add from preset…", + "shipping": "Shipping", + "addSubItem": "Add sub-item", + "detailsAdd": "+ Add details / notes", + "detailsFilled": "Details", + "detailsPlaceholder": "Optional notes — fine print, package inclusions, conditions…", + "autoTotaledHint": "Total auto-computed from sub-items below", + "autoTotaledNote": "= sum of sub-items" + } + }, + "bills": { + "title": "Invoices", + "subtitle": "Schedule, send, track payments and chase late invoices.", + "new": "New invoice", + "searchPlaceholder": "Search by number or customer…", + "sort": { + "newest": "Newest first", + "dueAsc": "Due soon first", + "dueDesc": "Due latest first", + "customerAsc": "Customer A→Z", + "valueAsc": "Value low→high", + "valueDesc": "Value high→low" + }, + "filter": { + "unpaidOnly": "Unpaid only" + }, + "empty": "No invoices yet.", + "table": { + "customer": "Customer", + "event": "Event", + "installment": "Installment", + "dueDate": "Due", + "total": "Total", + "status": "Status" + }, + "errors": { + "popupBlocked": "Allow pop-ups for this site to preview the PDF.", + "customerRequired": "Pick a customer first." + }, + "confirmSend": "Send invoice to customer now?", + "sentToast": "Invoice sent.", + "confirmRelease": "Mark the photos as delivered and send this invoice to the customer now?", + "releaseForDelivery": "Mark delivered & send", + "releasedToast": "Delivery invoice sent.", + "monthlyDraftBadge": "Monthly draft — ships via the customer's cadence day or \"Trigger invoice now\"", + "confirmReminder": "Send a reminder now?", + "reminderToast": "Reminder sent.", + "confirmCancel": "Cancel this invoice?", + "confirmCancelDraft": "Cancel this draft invoice? No document goes out.", + "confirmCancelIssued": "A Stornorechnung will be generated and emailed to the customer immediately. This cannot be undone. Continue?", + "cancelledToast": "Invoice cancelled.", + "cancelledWithStornoToast": "Invoice cancelled — Stornorechnung issued to the customer.", + "confirmReissue": "A Stornorechnung will be issued to the customer for this invoice, and a new scheduled draft will be created with the same line items. The new invoice will reference this one as \"Replaces R-XXXX\". Continue?", + "confirmReissueCancelled": "Create a new scheduled invoice with the same line items, linked back to this cancelled one?", + "reissuedToast": "Invoice reissued — opening the new draft.", + "reissuedWithStornoToast": "Stornorechnung issued — opening the new draft.", + "reissue": "Reissue", + "cancelAndReissue": "Cancel & reissue", + "kind": { + "storno": "Stornorechnung", + "reissue": "Reissue" + }, + "stornoCancelsLabel": "This Stornorechnung cancels invoice", + "cancelledByStornoLabel": "This invoice was cancelled by Stornorechnung", + "paymentRecordedToast": "Payment recorded.", + "sendNow": "Send now", + "resend": "Resend", + "markPaid": "Mark paid", + "sendReminder": "Send reminder", + "field": { + "issueDate": "Issued", + "dueDate": "Due", + "scheduledSendAt": "Scheduled send", + "installment": "Installment", + "total": "Total", + "paid": "Paid", + "outstanding": "Outstanding", + "lateFee": "Late fee", + "qrFormat": "Payment QR format", + "vatRate": "VAT rate %", + "shipping": "Shipping", + "currency": "Currency", + "ccPdfEmail": "CC PDF to (extra recipient)", + "sourceQuote": "From quote", + "businessBankAccount": "Payment account", + "bankAccountProfileDefault": "Use business profile default for {{currency}}", + "bankAccountDefaultBadge": "(default)", + "bankAccountHelp": "Overrides the profile default for this invoice only. Leave on default to inherit the currency-matched account from Settings → Business profile.", + "paymentNetDays": "Net days", + "paymentTiming": "Payment schedule", + "selectNetDays": "— Select net days —", + "selectTiming": "— Select schedule —", + "eventName": "Event", + "eventDate": "Event date", + "eventTimeStart": "Start time", + "eventTimeEnd": "End time", + "customer": "Customer", + "invoiceNumber": "Invoice number", + "paidAmount": "Paid amount (optional)", + "pdfFile": "PDF file", + "ccPdfCustom": "Custom email", + "ccPdfEmailPlaceholder": "name@example.com", + "ccPdfPickFromAdmins": "Pick from admins:", + "paymentTermHelp": "Net days + Skonto for this invoice. Leave blank to inherit from the source quote or the global CRM defaults.", + "skontoDisabled": "Disable Skonto for this invoice (suppresses the early-payment-discount block on the PDF and the \"Paid with Skonto\" buttons in the admin email / record-payment dialog).", + "sourceContract": "From contract" + }, + "import": "Import historical", + "importTitle": "Import historical invoice", + "importHelp": "Attach a PDF from a previous billing system. The customer sees this original document in their portal — picpeak does not regenerate it.", + "importedToast": "Invoice imported.", + "importError": "Import failed", + "importing": "Importing…", + "noMatch": "No matches", + "section": { + "lineItems": "Line items", + "paymentLog": "Payment log", + "customer": "Customer", + "event": "Event details", + "details": "Details", + "payment": "Payment conditions" + }, + "noPayments": "No payments recorded yet.", + "payment": { + "paidAt": "Date", + "amount": "Amount", + "method": "Method", + "reference": "Reference", + "notes": "Notes", + "methodPlaceholder": "Select method…", + "methods": { + "card": "Card", + "cash": "Cash", + "paypal": "PayPal", + "twint": "TWINT" + }, + "withSkonto": "Paid with Skonto ({{percent}}% discount)" + }, + "recordPayment": "Record payment", + "savedToast": "Invoice saved.", + "savedToastMulti": "Created {{n}} invoices.", + "saveAndSpawn": "Create {{n}} invoices", + "edit": "Edit invoice", + "customerSearch": "Search by email or company…", + "qrFormat": { + "none": "None", + "swiss": "Swiss QR-bill", + "epc": "EPC QR (SEPA)", + "profileDefault": "Use business profile default" + }, + "status": { + "scheduled": "Scheduled", + "pending_delivery": "Awaiting delivery", + "sent": "Sent", + "paid": "Paid", + "overdue": "Overdue", + "cancelled": "Cancelled", + "skipped": "Skipped (empty month)" + } + }, + "businessProfile": { + "savedToast": "Business profile saved.", + "title": "Business profile", + "subtitle": "Issuer block shown on every quote and invoice PDF.", + "section": { + "company": "Company", + "contact": "Contact", + "defaults": "Defaults", + "banks": "Bank accounts" + }, + "field": { + "companyName": "Company name", + "vatId": "VAT ID (USt-IdNr.)", + "taxId": "Tax number (Steuernummer)", + "addressLine1": "Address line 1", + "addressLine2": "Address line 2", + "postalCode": "Postal code", + "city": "City", + "state": "State / Region", + "countryCode": "Country (ISO 3166)", + "phone": "Phone", + "mobile": "Mobile", + "email": "Email", + "website": "Website", + "defaultCurrency": "Default currency", + "defaultLocale": "Default locale", + "timezone": "Timezone (IANA)", + "vatLabel": "VAT label (e.g. MwSt., VAT)", + "vatRateDefault": "Default VAT rate %", + "defaultQrFormat": "Default invoice QR", + "footerLine": "PDF footer line" + }, + "qrFormat": { + "none": "None", + "swiss": "Swiss QR-bill (CH / LI)", + "epc": "EPC QR (SEPA / EUR)" + }, + "bankCreatedToast": "Bank account added.", + "bankDeletedToast": "Bank account removed.", + "addBank": "Add account", + "bank": { + "label": "Label", + "accountHolder": "Account holder", + "currency": "Currency", + "isDefault": "Default for this currency", + "makeDefault": "Make default", + "confirmDelete": "Remove this bank account?" + }, + "noBanks": "No bank accounts configured yet." + }, + "crmSettings": { + "savedToast": "CRM settings saved.", + "title": "CRM settings", + "subtitle": "Fine-tune quote and invoice behaviour.", + "emptyState": "No CRM features are currently enabled. Turn on Quotes, Invoices, or Contracts in Settings → Features to see the matching configuration here.", + "section": { + "quotes": "Quotes", + "invoices": "Invoices", + "paymentDefaults": "Default payment conditions", + "installmentDefaults": "Default installment triggers" + }, + "paymentDefaults": { + "help": "Pre-filled on every new quote and invoice. The editor still lets you pick a different combination per document.", + "none": "— No default —" + }, + "installmentDefaults": { + "help": "Pre-fill the trigger for fresh rows in the Installments panel. Per-document edits override; existing documents keep their snapshotted plan.", + "trigger": { + "quote_accepted": "At signing / creation", + "before_event": "Before event", + "after_event": "After event", + "after_delivery": "On delivery (manual release)", + "fixed_date": "Fixed date" + } + }, + "crm_invoices_default_payment_net_days_template_id": { + "label": "Default net days" + }, + "crm_invoices_default_payment_timing_template_id": { + "label": "Default payment schedule" + }, + "crm_quotes_accept_window_minutes": { + "label": "Accept window (minutes)" + }, + "crm_quotes_default_valid_days": { + "label": "Default validity (days)" + }, + "crm_quotes_number_format": { + "label": "Quote number format" + }, + "crm_invoices_reminder_first_days": { + "label": "First reminder after (days past due)" + }, + "crm_invoices_reminder_second_days": { + "label": "Second reminder after (days past due)" + }, + "crm_invoices_late_fee_minor": { + "label": "Late fee (minor units / Rappen)" + }, + "crm_invoices_late_fee_label": { + "label": "Late fee label" + }, + "crm_invoices_skonto_percent_default": { + "label": "Skonto rate (default %)" + }, + "crm_invoices_skonto_business_days": { + "label": "Skonto window (business days)" + }, + "crm_invoices_installment_trigger_first": { + "label": "First installment trigger" + }, + "crm_invoices_installment_days_before_event": { + "label": "Days before event (middle installment)" + }, + "crm_invoices_installment_days_after_event": { + "label": "Days after event (final installment)" + }, + "crm_invoices_number_format": { + "label": "Invoice number format" + }, + "crm_quotes_pdf_attachment_enabled": { + "label": "Attach quote PDF to email" + }, + "crm_quotes_skonto_enabled": { + "label": "Allow early-payment discount (Skonto) on quotes" + }, + "crm_invoices_qr_enabled": { + "label": "Render payment QR on invoice PDFs" + }, + "crm_invoices_reminders_enabled": { + "label": "Send automatic reminders for overdue invoices" + }, + "crm_invoices_late_fee_enabled": { + "label": "Add a late fee on the second reminder" + } + }, + "quoteResponse": { + "notFound": "Quote not found", + "notFoundBody": "This link may have expired or been revoked. Please contact the photographer for a new quote.", + "locked": "Your response window has closed and the decision is now final.", + "title": "Quote", + "event": "Event", + "issueDate": "Issued", + "validUntil": "valid until", + "description": "Description", + "qty": "Qty", + "unit": "Unit", + "total": "Total", + "subtotal": "Subtotal", + "vat": "VAT", + "acceptedLocked": "You accepted this quote on {{date}}. The decision is final.", + "declinedLocked": "You declined this quote on {{date}}. The decision is final.", + "cannotRespond": "This quote can no longer be responded to via this link.", + "changeWithin": "You can change your response until {{at}}.", + "intro": "Please accept or decline this quote:", + "accept": "Accept quote", + "decline": "Decline" + }, + "reminderTemplates": { + "title": "Pre-event reminder emails", + "globalSection": "Global behaviour", + "globalHelp": "Off by default — turn on to start sending pre-event reminders. The offset below is the default; each event can override on its detail page.", + "enableLabel": "Send pre-event reminder emails", + "daysBeforeLabel": "Days before the event", + "saveSettings": "Save global settings", + "settingsSaved": "Reminder settings saved.", + "settingsSaveError": "Could not save reminder settings.", + "sidebarHeading": "Templates", + "sectionTemplates": "Templates", + "defaultLabel": "Default (catch-all)", + "usesDefault": "Default", + "usesDefaultTooltip": "No dedicated template yet — this event type falls back to the default. Edit + save here to create one.", + "willCreateOnSave": "This event type uses the default template. The fields below are pre-filled from the default; saving will create a dedicated template for this event type.", + "variablesHint": "Available variables: {{customer_name}}, {{event_name}}, {{event_date}}, {{event_type}}, {{days_before}}, {{business_name}} — substituted when the email is rendered.", + "subjectLabel": "Subject", + "subjectPlaceholder": "Email subject", + "bodyLabel": "Body", + "noTranslation": "No translation yet", + "saveTemplate": "Save template", + "saved": "Template saved.", + "saveError": "Could not save template." + }, + "eventReminderOverride": { + "title": "Pre-event reminder", + "save": "Save override", + "help": "Per-event override for the customer reminder. Global on-off + default offset live under Settings → Reminder emails. Anything left blank here inherits the global setting / resolved template.", + "disabledLabel": "Disable the reminder for this event (no email goes out)", + "offsetLabel": "Days before the event (override) — leave blank to inherit", + "offsetPlaceholder": "Leave blank to use the global default", + "bodyOverrideLabel": "Custom body for this event (overrides the resolved template body)", + "bodyOverridePlaceholder": "Leave blank to use the template body. Variables like {{customer_name}}, {{event_name}}, {{event_date}} still work here.", + "saved": "Reminder override saved.", + "saveError": "Could not save reminder override.", + "invalidOffset": "Offset must be a non-negative integer or blank." + }, + "dealLineage": { + "title": "Related documents", + "quotes": "Quotes", + "contracts": "Contracts", + "invoices": "Invoices", + "loading": "Loading related documents…", + "error": "Could not load related documents.", + "empty": "No other documents share this deal yet. New invoices, contracts, or installments will show up here once created.", + "editPlan": "Edit plan", + "editPlanModalTitle": "Edit installment plan", + "editPlanHelp": "Atomically reshape this plan: change percents, labels, triggers, add or remove rows. The plan total stays fixed; existing invoice numbers are kept where possible. Refused once any invoice has shipped.", + "editPlanWarning": "Trimming rows deletes their invoice numbers (the sequence cannot release them — a §14 UStG continuity rule). Adding rows claims fresh numbers.", + "editPlanSave": "Save plan", + "editPlanSuccess": "Installment plan updated.", + "editPlanLocked": "Plan can no longer be edited — at least one invoice has shipped or been cancelled.", + "editPlanPercentSumError": "Percents must sum to 100.", + "editPlanGeneralError": "Could not update plan.", + "editPlanEmpty": "Plan cannot be empty." + }, + "installments": { + "enableLabel": "Split into installments", + "advancedToggle": "Use dynamic triggers", + "simpleToggle": "Use simple date picker", + "simpleHint": "Each row fires on a specific date. Switch to dynamic triggers for plans tied to event date or delivery.", + "advancedHint": "Pick a trigger (quote accepted, before/after event, on delivery, fixed date) plus offset in days. Triggers re-resolve if the event date later shifts.", + "percent": "%", + "label": "Label", + "labelPlaceholder": "Anzahlung / before event …", + "sendOn": "Send on", + "trigger": "Trigger", + "offsetDays": "Offset (days)", + "onDeliveryHint": "On delivery — admin releases manually. Switch to advanced to change.", + "addRow": "Add installment", + "removeRow": "Remove row", + "total": "Total", + "mustSumTo100": "must sum to 100%", + "firstRowDefaultLabel": "Down payment", + "triggerOption": { + "quote_accepted": "At signing / creation", + "before_event": "Before event", + "after_event": "After event", + "after_delivery": "On delivery (manual release)", + "fixed_date": "Fixed date" + } + }, + "contracts": { + "title": "Contracts", + "subtitle": "Compose contracts from reusable blocks and have customers sign in-browser or upload a wet-signed PDF.", + "sections": { + "basics": "Basics", + "scope": "Scope", + "privacy": "Privacy", + "commercial": "Commercial", + "nda": "Confidentiality", + "closing": "Closing provisions" + }, + "status": { + "draft": "Draft", + "sent": "Sent", + "signed_by_customer": "Signed by customer", + "signed_by_admin": "Counter-signed", + "fully_signed": "Fully signed", + "cancelled": "Cancelled" + }, + "list": { + "new": "New contract" + }, + "detail": { + "integrity": { + "title": "PDF integrity check", + "help": "Re-hashes the unsigned + signed PDFs on disk and compares them to the SHA-256 stored when the document was issued. Catches backup corruption or manual edits since the customer received their copy.", + "verify": "Verify", + "reverify": "Re-verify", + "unsignedTitle": "Unsigned PDF", + "signedTitle": "Signed PDF", + "match": "Hash matches", + "mismatch": "Hash mismatch — file altered", + "missing": "File missing from disk", + "notIssued": "Not yet issued", + "expected": "expected", + "actual": "actual", + "error": "Integrity check failed." + } } } } diff --git a/frontend/src/i18n/locales/fr.json b/frontend/src/i18n/locales/fr.json index 93e9fd4a..9dc1bcaa 100644 --- a/frontend/src/i18n/locales/fr.json +++ b/frontend/src/i18n/locales/fr.json @@ -1361,6 +1361,12 @@ "saveTheme": "Enregistrer le thème", "previewLayout": "Aperçu de la disposition", "livePreview": "Aperçu en direct", + "pdfTypography": "Typographie PDF", + "pdfTypographyHelp": "Utilisée pour les en-têtes de factures et de devis. Choisissez l'une des polices fournies ou laissez la valeur par défaut pour utiliser Helvetica.", + "pdfFontFamily": "Police du corps", + "pdfFontFamilyDefault": "Utiliser Helvetica (par défaut)", + "pdfFontFamilyMissing": "manquante", + "pdfFontSavedToast": "Police PDF enregistrée.", "heroPlaceholderText": "La photo d'en-tête de votre événement apparaîtra ici", "whiteLabel": "Marque blanche", "hidePoweredBy": "Masquer la mention \"Propulsé par PicPeak\"", @@ -1375,6 +1381,11 @@ "logoMaxHeight": "Hauteur maximale (pixels)", "logoMaxHeightHelp": "Définissez une hauteur maximale personnalisée pour le logo (20-200 pixels)", "logoPosition": "Position du logo dans l'en-tête", + "positionLeft": "Gauche", + "positionCenter": "Centre", + "positionRight": "Droite", + "positionSidepanel": "Barre latérale", + "positionSidepanelHelp": "Le logo apparaît dans la barre latérale admin au lieu de l'en-tête. Lorsque la barre latérale est repliée, la favicon est affichée.", "logoDisplayMode": "Mode d'affichage", "logoOnly": "Logo uniquement", "textOnly": "Nom de l'entreprise uniquement", @@ -3005,7 +3016,47 @@ "body": "Activez Comptes (ou une autre sous-fonctionnalité Clients) dans Paramètres → Fonctionnalités pour commencer." }, "subnav": { - "accounts": "Comptes" + "accounts": "Comptes", + "quotes": "Devis", + "bills": "Factures", + "taxReport": "Fiscalité", + "development": "Développement" + } + }, + "taxReport": { + "title": "Rapport fiscal", + "intro": "Liste des revenus sur une période donnée avec ventilation net + TVA groupée par taux de TVA. Les factures annulées restent visibles pour la continuité de la piste d'audit, mais sont exclues des totaux.", + "filters": { + "period": "Période", + "thisYear": "Cette année", + "lastYear": "Année dernière", + "thisQuarter": "Ce trimestre", + "lastQuarter": "Trimestre dernier", + "custom": "Plage personnalisée", + "from": "Du", + "to": "Au", + "currency": "Devise" + }, + "exportPdf": "Exporter en PDF", + "exportCsv": "Exporter en CSV", + "exportFailed": "Échec de l'export. Veuillez réessayer.", + "errorTitle": "Impossible de charger le rapport fiscal", + "empty": "Aucune facture sur cette période.", + "statusCancelled": "Annulée", + "totalsByVatRate": "Totaux par taux de TVA", + "grandTotalNet": "Total net", + "grandTotalVat": "Total TVA", + "grandTotalGross": "Total brut", + "cancelledFootnote": "{{count}} facture(s) annulée(s) — montants exclus des totaux (affichés pour la continuité de la piste d'audit).", + "col": { + "date": "Date", + "invoice": "Facture", + "customer": "Client", + "event": "Événement", + "vatRate": "TVA %", + "net": "Net", + "vat": "TVA", + "total": "Brut" } } } diff --git a/frontend/src/i18n/locales/nl.json b/frontend/src/i18n/locales/nl.json index d4088907..163aa5ce 100644 --- a/frontend/src/i18n/locales/nl.json +++ b/frontend/src/i18n/locales/nl.json @@ -1350,6 +1350,12 @@ "saveTheme": "Thema opslaan", "previewLayout": "Lay-outvoorbeeld", "livePreview": "Live voorbeeld", + "pdfTypography": "PDF-typografie", + "pdfTypographyHelp": "Wordt gebruikt voor briefhoofden van facturen en offertes. Kies een van de meegeleverde lettertypen, of laat de standaardinstelling staan om Helvetica te gebruiken.", + "pdfFontFamily": "Bodylettertype", + "pdfFontFamilyDefault": "Helvetica gebruiken (standaard)", + "pdfFontFamilyMissing": "ontbreekt", + "pdfFontSavedToast": "PDF-lettertype opgeslagen.", "heroPlaceholderText": "De hero-foto van uw evenement verschijnt hier", "whiteLabel": "White Label", "hidePoweredBy": "\"Powered by PicPeak\"-vermelding verbergen", @@ -1364,6 +1370,11 @@ "logoMaxHeight": "Maximale hoogte (pixels)", "logoMaxHeightHelp": "Stel een aangepaste maximale hoogte in voor het logo (20-200 pixels)", "logoPosition": "Logopositie in header", + "positionLeft": "Links", + "positionCenter": "Midden", + "positionRight": "Rechts", + "positionSidepanel": "Zijbalk", + "positionSidepanelHelp": "Het logo verschijnt in de admin-zijbalk in plaats van in de header. Wanneer de zijbalk is ingeklapt, wordt het favicon getoond.", "logoDisplayMode": "Weergavemodus", "logoOnly": "Alleen logo", "textOnly": "Alleen bedrijfsnaam", @@ -2994,7 +3005,47 @@ "body": "Schakel Accounts (of een andere Klanten-subfunctie) in onder Instellingen → Functies om te beginnen." }, "subnav": { - "accounts": "Accounts" + "accounts": "Accounts", + "quotes": "Offertes", + "bills": "Facturen", + "taxReport": "Belasting", + "development": "Ontwikkeling" + } + }, + "taxReport": { + "title": "Belastingrapport", + "intro": "Periodegebonden omzetlijst met netto + btw-uitsplitsing gegroepeerd per btw-tarief. Geannuleerde facturen blijven zichtbaar voor de continuïteit van het audit-spoor, maar zijn uitgesloten van de totalen.", + "filters": { + "period": "Periode", + "thisYear": "Dit jaar", + "lastYear": "Vorig jaar", + "thisQuarter": "Dit kwartaal", + "lastQuarter": "Vorig kwartaal", + "custom": "Aangepast bereik", + "from": "Van", + "to": "Tot", + "currency": "Valuta" + }, + "exportPdf": "PDF exporteren", + "exportCsv": "CSV exporteren", + "exportFailed": "Export mislukt. Probeer het opnieuw.", + "errorTitle": "Belastingrapport kon niet worden geladen", + "empty": "Geen facturen in deze periode.", + "statusCancelled": "Geannuleerd", + "totalsByVatRate": "Totalen per btw-tarief", + "grandTotalNet": "Totaal netto", + "grandTotalVat": "Totaal btw", + "grandTotalGross": "Totaal bruto", + "cancelledFootnote": "{{count}} geannuleerde factu(u)r(en) — bedragen uitgesloten van totalen (getoond voor continuïteit van het audit-spoor).", + "col": { + "date": "Datum", + "invoice": "Factuur", + "customer": "Klant", + "event": "Evenement", + "vatRate": "Btw %", + "net": "Netto", + "vat": "Btw", + "total": "Bruto" } } } diff --git a/frontend/src/i18n/locales/pt.json b/frontend/src/i18n/locales/pt.json index f85dfab8..8d6cabdc 100644 --- a/frontend/src/i18n/locales/pt.json +++ b/frontend/src/i18n/locales/pt.json @@ -1367,6 +1367,12 @@ "saveTheme": "Salvar Tema", "previewLayout": "Visualizar Layout", "livePreview": "Prévia ao Vivo", + "pdfTypography": "Tipografia do PDF", + "pdfTypographyHelp": "Usada nos cabeçalhos de faturas e orçamentos. Escolha uma das fontes incluídas ou mantenha o padrão para usar Helvetica.", + "pdfFontFamily": "Fonte do corpo", + "pdfFontFamilyDefault": "Usar Helvetica (padrão)", + "pdfFontFamilyMissing": "ausente", + "pdfFontSavedToast": "Fonte do PDF salva.", "heroPlaceholderText": "A foto de destaque do seu evento aparecerá aqui", "whiteLabel": "White Label", "hidePoweredBy": "Ocultar selo \"Powered by PicPeak\"", @@ -1381,6 +1387,11 @@ "logoMaxHeight": "Altura Máxima (pixels)", "logoMaxHeightHelp": "Define uma altura máxima para a logo (20-200 px)", "logoPosition": "Posição da Logo no Cabeçalho", + "positionLeft": "Esquerda", + "positionCenter": "Centro", + "positionRight": "Direita", + "positionSidepanel": "Barra lateral", + "positionSidepanelHelp": "A logo aparece na barra lateral do admin em vez do cabeçalho. Quando a barra lateral está recolhida, o favicon é exibido.", "logoDisplayMode": "Modo de Exibição", "logoOnly": "Apenas Logo", "textOnly": "Apenas Nome da Empresa", @@ -3027,7 +3038,47 @@ "body": "Ative Contas (ou outra sub-funcionalidade de Clientes) em Configurações → Recursos para começar." }, "subnav": { - "accounts": "Contas" + "accounts": "Contas", + "quotes": "Orçamentos", + "bills": "Faturas", + "taxReport": "Fiscal", + "development": "Desenvolvimento" + } + }, + "taxReport": { + "title": "Relatório fiscal", + "intro": "Lista de receita por período com discriminação líquido + IVA agrupada por taxa de IVA. Faturas canceladas permanecem visíveis para continuidade do rastro de auditoria, mas são excluídas dos totais.", + "filters": { + "period": "Período", + "thisYear": "Este ano", + "lastYear": "Ano passado", + "thisQuarter": "Este trimestre", + "lastQuarter": "Trimestre passado", + "custom": "Intervalo personalizado", + "from": "De", + "to": "Até", + "currency": "Moeda" + }, + "exportPdf": "Exportar PDF", + "exportCsv": "Exportar CSV", + "exportFailed": "Falha na exportação. Por favor, tente novamente.", + "errorTitle": "Não foi possível carregar o relatório fiscal", + "empty": "Sem faturas neste período.", + "statusCancelled": "Cancelada", + "totalsByVatRate": "Totais por taxa de IVA", + "grandTotalNet": "Total líquido", + "grandTotalVat": "Total IVA", + "grandTotalGross": "Total bruto", + "cancelledFootnote": "{{count}} fatura(s) cancelada(s) — valores excluídos dos totais (apresentados para continuidade do rastro de auditoria).", + "col": { + "date": "Data", + "invoice": "Fatura", + "customer": "Cliente", + "event": "Evento", + "vatRate": "IVA %", + "net": "Líquido", + "vat": "IVA", + "total": "Bruto" } } } diff --git a/frontend/src/i18n/locales/ru.json b/frontend/src/i18n/locales/ru.json index 64daa8ce..006431ca 100644 --- a/frontend/src/i18n/locales/ru.json +++ b/frontend/src/i18n/locales/ru.json @@ -1384,6 +1384,12 @@ "saveTheme": "Сохранить тему", "previewLayout": "Предпросмотр макета", "livePreview": "Живой предпросмотр", + "pdfTypography": "Шрифт PDF", + "pdfTypographyHelp": "Используется для бланков счетов и предложений. Выберите один из встроенных шрифтов или оставьте значение по умолчанию, чтобы использовать Helvetica.", + "pdfFontFamily": "Шрифт текста", + "pdfFontFamilyDefault": "Использовать Helvetica (по умолчанию)", + "pdfFontFamilyMissing": "отсутствует", + "pdfFontSavedToast": "Шрифт PDF сохранён.", "heroPlaceholderText": "Здесь появится главное фото вашего события", "whiteLabel": "Собственный брендинг", "hidePoweredBy": "Скрыть «Powered by PicPeak»", @@ -1398,6 +1404,11 @@ "logoMaxHeight": "Максимальная высота (пикселей)", "logoMaxHeightHelp": "Задайте пользовательскую максимальную высоту логотипа (20–200 пикселей)", "logoPosition": "Положение логотипа в заголовке", + "positionLeft": "Слева", + "positionCenter": "По центру", + "positionRight": "Справа", + "positionSidepanel": "Боковая панель", + "positionSidepanelHelp": "Логотип отображается в боковой панели администратора вместо заголовка. Когда боковая панель свернута, отображается фавикон.", "logoDisplayMode": "Режим отображения", "logoOnly": "Только логотип", "textOnly": "Только название компании", @@ -3060,7 +3071,47 @@ "body": "Включите «Учётные записи» (или другую подфункцию «Клиенты») в Настройки → Функции, чтобы начать." }, "subnav": { - "accounts": "Учётные записи" + "accounts": "Учётные записи", + "quotes": "Предложения", + "bills": "Счета", + "taxReport": "Налоги", + "development": "Разработка" + } + }, + "taxReport": { + "title": "Налоговый отчёт", + "intro": "Список выручки за выбранный период с разбивкой нетто и НДС по ставкам. Аннулированные счета остаются видимыми для непрерывности аудиторской цепочки, но исключаются из итогов.", + "filters": { + "period": "Период", + "thisYear": "Этот год", + "lastYear": "Прошлый год", + "thisQuarter": "Этот квартал", + "lastQuarter": "Прошлый квартал", + "custom": "Произвольный диапазон", + "from": "С", + "to": "По", + "currency": "Валюта" + }, + "exportPdf": "Экспорт в PDF", + "exportCsv": "Экспорт в CSV", + "exportFailed": "Экспорт не выполнен. Пожалуйста, попробуйте ещё раз.", + "errorTitle": "Не удалось загрузить налоговый отчёт", + "empty": "Нет счетов за этот период.", + "statusCancelled": "Аннулирован", + "totalsByVatRate": "Итоги по ставкам НДС", + "grandTotalNet": "Итого нетто", + "grandTotalVat": "Итого НДС", + "grandTotalGross": "Итого брутто", + "cancelledFootnote": "{{count}} аннулированных счёт(а/ов) — суммы исключены из итогов (показаны для непрерывности аудиторской цепочки).", + "col": { + "date": "Дата", + "invoice": "Счёт", + "customer": "Клиент", + "event": "Событие", + "vatRate": "НДС %", + "net": "Нетто", + "vat": "НДС", + "total": "Брутто" } } }