Bernt Christian Egeland 4 месяцев назад
Родитель
Сommit
7ba8a99250
100 измененных файлов с 1566 добавлено и 351 удалено
  1. 14 0
      .devcontainer/devcontainer-lock.json
  2. 1 8
      README.md
  3. 19 4
      messages/de/admin.json
  4. 2 1
      messages/de/aiChat.json
  5. 5 1
      messages/de/audit.json
  6. 5 1
      messages/de/billing.json
  7. 10 8
      messages/de/calendar.json
  8. 2 1
      messages/de/customers.json
  9. 10 5
      messages/de/dashboard.json
  10. 2 1
      messages/de/inventory.json
  11. 14 3
      messages/de/laborPresets.json
  12. 5 2
      messages/de/messages.json
  13. 2 1
      messages/de/notifications.json
  14. 7 0
      messages/de/pdf.json
  15. 5 0
      messages/de/portal.json
  16. 15 4
      messages/de/quotes.json
  17. 2 1
      messages/de/reminders.json
  18. 5 1
      messages/de/reports.json
  19. 36 4
      messages/de/service.json
  20. 148 6
      messages/de/settings.json
  21. 6 0
      messages/de/share.json
  22. 1 0
      messages/de/statusReport.json
  23. 5 2
      messages/de/telegram.json
  24. 2 1
      messages/de/telegramMessages.json
  25. 84 48
      messages/de/vehicles.json
  26. 8 2
      messages/de/workBoard.json
  27. 2 1
      messages/de/workOrders.json
  28. 19 4
      messages/en/admin.json
  29. 2 1
      messages/en/aiChat.json
  30. 5 1
      messages/en/audit.json
  31. 5 1
      messages/en/billing.json
  32. 3 1
      messages/en/calendar.json
  33. 2 1
      messages/en/customers.json
  34. 10 5
      messages/en/dashboard.json
  35. 2 1
      messages/en/inventory.json
  36. 14 3
      messages/en/laborPresets.json
  37. 5 2
      messages/en/messages.json
  38. 2 1
      messages/en/notifications.json
  39. 7 0
      messages/en/pdf.json
  40. 5 0
      messages/en/portal.json
  41. 16 5
      messages/en/quotes.json
  42. 2 1
      messages/en/reminders.json
  43. 5 2
      messages/en/reports.json
  44. 36 4
      messages/en/service.json
  45. 148 6
      messages/en/settings.json
  46. 6 0
      messages/en/share.json
  47. 1 0
      messages/en/statusReport.json
  48. 5 2
      messages/en/telegram.json
  49. 2 1
      messages/en/telegramMessages.json
  50. 42 6
      messages/en/vehicles.json
  51. 8 2
      messages/en/workBoard.json
  52. 2 1
      messages/en/workOrders.json
  53. 19 4
      messages/es/admin.json
  54. 2 1
      messages/es/aiChat.json
  55. 5 1
      messages/es/audit.json
  56. 5 1
      messages/es/billing.json
  57. 9 7
      messages/es/calendar.json
  58. 2 1
      messages/es/customers.json
  59. 10 5
      messages/es/dashboard.json
  60. 2 1
      messages/es/inventory.json
  61. 14 3
      messages/es/laborPresets.json
  62. 5 2
      messages/es/messages.json
  63. 2 1
      messages/es/notifications.json
  64. 7 0
      messages/es/pdf.json
  65. 5 0
      messages/es/portal.json
  66. 15 4
      messages/es/quotes.json
  67. 2 1
      messages/es/reminders.json
  68. 5 1
      messages/es/reports.json
  69. 36 4
      messages/es/service.json
  70. 148 6
      messages/es/settings.json
  71. 6 0
      messages/es/share.json
  72. 1 0
      messages/es/statusReport.json
  73. 5 2
      messages/es/telegram.json
  74. 2 1
      messages/es/telegramMessages.json
  75. 42 6
      messages/es/vehicles.json
  76. 8 2
      messages/es/workBoard.json
  77. 2 1
      messages/es/workOrders.json
  78. 19 4
      messages/fr/admin.json
  79. 2 1
      messages/fr/aiChat.json
  80. 5 1
      messages/fr/audit.json
  81. 5 1
      messages/fr/billing.json
  82. 16 14
      messages/fr/calendar.json
  83. 2 1
      messages/fr/customers.json
  84. 10 5
      messages/fr/dashboard.json
  85. 2 1
      messages/fr/inventory.json
  86. 14 3
      messages/fr/laborPresets.json
  87. 5 2
      messages/fr/messages.json
  88. 2 1
      messages/fr/notifications.json
  89. 7 0
      messages/fr/pdf.json
  90. 5 0
      messages/fr/portal.json
  91. 15 4
      messages/fr/quotes.json
  92. 2 1
      messages/fr/reminders.json
  93. 5 1
      messages/fr/reports.json
  94. 36 4
      messages/fr/service.json
  95. 148 6
      messages/fr/settings.json
  96. 6 0
      messages/fr/share.json
  97. 1 0
      messages/fr/statusReport.json
  98. 5 2
      messages/fr/telegram.json
  99. 2 1
      messages/fr/telegramMessages.json
  100. 122 86
      messages/fr/vehicles.json

+ 14 - 0
.devcontainer/devcontainer-lock.json

@@ -0,0 +1,14 @@
+{
+  "features": {
+    "ghcr.io/devcontainers/features/git:1": {
+      "version": "1.3.5",
+      "resolved": "ghcr.io/devcontainers/features/git@sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251",
+      "integrity": "sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251"
+    },
+    "ghcr.io/devcontainers/features/github-cli:1": {
+      "version": "1.1.0",
+      "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
+      "integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
+    }
+  }
+}

+ 1 - 8
README.md

@@ -53,11 +53,4 @@ See the [Quick Start guide](https://torqvoice.com/docs/installation#quick-start)
 
 ## License
 
-[Elastic License 2.0 (ELv2)](LICENSE)
-
-- Add a dedicated Vehicles report tab with per-vehicle cost breakdown by service type (repair vs maintenance vs 
-upgrade), monthly cost trends, top parts used, and full service history
-- Vehicle selector with searchable dropdown and infinite scroll to pick any vehicle in the organization         
-- CSV, PDF export support for vehicle reports                                                                   
-- Add date range presets (Grafana-style) to the report date picker: Today, Last 7/30/90 days, This month, This  
-year, Last year, etc.
+[Elastic License 2.0 (ELv2)](LICENSE)

+ 19 - 4
messages/de/admin.json

@@ -1,9 +1,21 @@
 {
   "nav": {
-    "overview": { "title": "Übersicht", "description": "Plattformstatistiken & Zustand" },
-    "users": { "title": "Benutzer", "description": "Alle Benutzer verwalten" },
-    "organizations": { "title": "Organisationen", "description": "Alle Organisationen verwalten" },
-    "settings": { "title": "Einstellungen", "description": "Plattformkonfiguration" }
+    "overview": {
+      "title": "Übersicht",
+      "description": "Plattformstatistiken & Zustand"
+    },
+    "users": {
+      "title": "Benutzer",
+      "description": "Alle Benutzer verwalten"
+    },
+    "organizations": {
+      "title": "Organisationen",
+      "description": "Alle Organisationen verwalten"
+    },
+    "settings": {
+      "title": "Einstellungen",
+      "description": "Plattformkonfiguration"
+    }
   },
   "layout": {
     "title": "Admin-Panel",
@@ -104,5 +116,8 @@
     "postmarkApiKeyHint": "Holen Sie Ihren Server-Token von postmarkapp.com",
     "mailgunApiKeyHint": "Holen Sie Ihren API-Schlüssel von app.mailgun.com",
     "sendgridApiKeyHint": "Holen Sie Ihren API-Schlüssel von app.sendgrid.com"
+  },
+  "common": {
+    "openMenu": "Open menu"
   }
 }

+ 2 - 1
messages/de/aiChat.json

@@ -14,5 +14,6 @@
   "chatsPrivate": "Ihre Chats sind privat und nur für Sie sichtbar.",
   "deleteChat": "Chat löschen",
   "hideSidebar": "Seitenleiste ausblenden",
-  "showSidebar": "Seitenleiste einblenden"
+  "showSidebar": "Seitenleiste einblenden",
+  "sendMessage": "Send message"
 }

+ 5 - 1
messages/de/audit.json

@@ -93,6 +93,10 @@
     "statusReport_create": "Statusbericht erstellt",
     "statusReport_send": "Statusbericht gesendet",
     "statusReport_delete": "Statusbericht gelöscht",
-    "telegram_send": "Telegram-Nachricht gesendet"
+    "telegram_send": "Telegram-Nachricht gesendet",
+    "webhook_create": "Webhook erstellt",
+    "webhook_update": "Webhook aktualisiert",
+    "webhook_delete": "Webhook gelöscht",
+    "webhook_rotateSecret": "Webhook-Schlüssel rotiert"
   }
 }

+ 5 - 1
messages/de/billing.json

@@ -78,6 +78,10 @@
     "deleted": "Wiederkehrende Rechnung gelöscht",
     "failedDelete": "Löschen fehlgeschlagen",
     "processed": "{count} Rechnung(en) verarbeitet",
-    "failedProcess": "Verarbeitung fehlgeschlagen"
+    "failedProcess": "Verarbeitung fehlgeschlagen",
+    "pause": "Pause",
+    "resume": "Resume",
+    "delete": "Delete",
+    "removeRow": "Remove row"
   }
 }

+ 10 - 8
messages/de/calendar.json

@@ -11,7 +11,7 @@
   "months": {
     "january": "Januar",
     "february": "Februar",
-    "march": "M\u00e4rz",
+    "march": "März",
     "april": "April",
     "may": "Mai",
     "june": "Juni",
@@ -23,19 +23,19 @@
     "december": "Dezember"
   },
   "stats": {
-    "services": "Auftr\u00e4ge",
-    "overdue": "\u00dcberf\u00e4llig",
+    "services": "Aufträge",
+    "overdue": "Überfällig",
     "pendingQuotes": "Offene Angebote",
     "revenue": "Umsatz"
   },
   "today": "Heute",
   "workOrder": "Arbeitsauftrag",
   "filters": {
-    "services": "Auftr\u00e4ge",
+    "services": "Aufträge",
     "reminders": "Erinnerungen",
     "quotes": "Angebote"
   },
-  "selectVehicle": "Fahrzeug f\u00fcr Arbeitsauftrag ausw\u00e4hlen",
+  "selectVehicle": "Fahrzeug für Arbeitsauftrag auswählen",
   "dateChoice": {
     "title": "Work Order Date",
     "description": "You have {selectedDate} selected. Which date should this work order be for?",
@@ -53,7 +53,7 @@
       "approved": "Genehmigt",
       "draft": "Entwurf",
       "done": "Erledigt",
-      "overdue": "\u00dcberf\u00e4llig",
+      "overdue": "Überfällig",
       "upcoming": "Bevorstehend"
     },
     "type": {
@@ -62,6 +62,8 @@
       "quote": "Angebot"
     },
     "noEvents": "Keine Ereignisse",
-    "selectDay": "W\u00e4hlen Sie einen Tag mit Ereignissen oder erstellen Sie einen neuen Arbeitsauftrag."
-  }
+    "selectDay": "Wählen Sie einen Tag mit Ereignissen oder erstellen Sie einen neuen Arbeitsauftrag."
+  },
+  "previousMonth": "Previous month",
+  "nextMonth": "Next month"
 }

+ 2 - 1
messages/de/customers.json

@@ -21,7 +21,8 @@
     "batchDelete": "Löschen ({count})",
     "batchDeleteTitle": "Kunden löschen",
     "batchDeleteDescription": "{count} Kunde(n) löschen? Die zugehörigen Fahrzeuge werden dabei entknüpft.",
-    "batchDeleteSuccess": "{count} Kunde(n) gelöscht"
+    "batchDeleteSuccess": "{count} Kunde(n) gelöscht",
+    "openMenu": "Open menu"
   },
   "detail": {
     "backToCustomers": "Zurück zur Kundenliste",

+ 10 - 5
messages/de/dashboard.json

@@ -18,7 +18,8 @@
     "quantity": "Menge",
     "addToJob": "{qty} zum Auftrag hinzufügen",
     "statusReport": "Statusbericht",
-    "report": "Bericht"
+    "report": "Bericht",
+    "observation": "Beobachtung"
   },
   "recentActivity": "Letzte Aktivitäten",
   "noRecentActivity": "Keine aktuellen Aktivitäten",
@@ -64,7 +65,9 @@
     "dismissedTab": "Verworfen",
     "noActive": "Derzeit sind keine Fahrzeuge wartungsfällig.",
     "dismissedOn": "Verworfen am {date}",
-    "restore": "Wiederherstellen"
+    "restore": "Wiederherstellen",
+    "settingsAriaLabel": "Maintenance settings",
+    "dismissAriaLabel": "Dismiss"
   },
   "reminders": {
     "title": "Anstehende Erinnerungen",
@@ -95,7 +98,8 @@
     "pending": "Ausstehend",
     "createQuote": "Angebot erstellen",
     "itemsRequested": "{count, plural, one {# Position angefragt} other {# Positionen angefragt}}",
-    "noData": "Keine ausstehenden Angebotsanfragen."
+    "noData": "Keine ausstehenden Angebotsanfragen.",
+    "dismissAriaLabel": "Dismiss quote request"
   },
   "quoteResponses": {
     "title": "Angebotsantworten der Kunden",
@@ -106,7 +110,8 @@
     "convert": "Convert",
     "viewQuote": "Angebot anzeigen",
     "view": "View",
-    "noData": "Keine Kundenantworten."
+    "noData": "Keine Kundenantworten.",
+    "dismissAriaLabel": "Dismiss response"
   },
   "recentCompleted": {
     "title": "Kürzlich abgeschlossen",
@@ -133,4 +138,4 @@
     "days": "{count}T"
   },
   "error": "Dashboard konnte nicht geladen werden"
-}
+}

+ 2 - 1
messages/de/inventory.json

@@ -118,5 +118,6 @@
     "cancel": "Abbrechen",
     "saveChanges": "Änderungen speichern",
     "addPart": "Teil hinzufügen"
-  }
+  },
+  "openMenu": "Open menu"
 }

+ 14 - 3
messages/de/laborPresets.json

@@ -15,7 +15,8 @@
   },
   "actions": {
     "edit": "Bearbeiten",
-    "delete": "Löschen"
+    "delete": "Löschen",
+    "openMenu": "Open menu"
   },
   "deletePreset": {
     "title": "Arbeitsvorlage löschen",
@@ -45,7 +46,16 @@
     "itemQty": "Qty",
     "itemQtyOrHours": "Unit / Hours",
     "hourlyTag": "HR",
-    "serviceTag": "SVC"
+    "serviceTag": "SVC",
+    "partsTitle": "Teile",
+    "addPart": "Teil hinzufügen",
+    "partName": "Teilname",
+    "partNumber": "Teil-Nr.",
+    "partQty": "Menge",
+    "partPrice": "Stückpreis",
+    "importFromInventory": "Aus Lager importieren",
+    "searchInventory": "Lager durchsuchen...",
+    "noPartsFound": "Keine Teile gefunden"
   },
   "picker": {
     "title": "Arbeitsvorlage auswählen",
@@ -53,7 +63,8 @@
     "noPackages": "Keine Arbeitsvorlagen gefunden.",
     "items": "{count} Positionen",
     "totalHours": "{hours} Std. gesamt",
-    "totalUnits": "{units} unit(s)"
+    "totalUnits": "{units} unit(s)",
+    "totalParts": "{count} Teile"
   },
   "errors": {
     "error": "Fehler",

+ 5 - 2
messages/de/messages.json

@@ -18,7 +18,9 @@
     "deleteTitle": "Unterhaltung löschen?",
     "deleteDescription": "Alle Nachrichten mit {name} werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
     "deleted": "Unterhaltung gelöscht",
-    "deleteError": "Unterhaltung konnte nicht gelöscht werden"
+    "deleteError": "Unterhaltung konnte nicht gelöscht werden",
+    "back": "Back",
+    "conversationMenu": "Conversation menu"
   },
   "conversation": {
     "noPhone": "Keine Telefonnummer für {name} hinterlegt. Fügen Sie eine Telefonnummer hinzu, um Nachrichten zu senden.",
@@ -30,7 +32,8 @@
     "deleteDescription": "Diese Nachricht wird dauerhaft aus der Unterhaltung gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
     "messageDeleted": "Nachricht gelöscht",
     "deleteError": "Nachricht konnte nicht gelöscht werden",
-    "sendError": "SMS konnte nicht gesendet werden"
+    "sendError": "SMS konnte nicht gesendet werden",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "{label} senden",

+ 2 - 1
messages/de/notifications.json

@@ -6,5 +6,6 @@
     "confirmDelete": "Sind Sie sicher?",
     "delete": "Löschen",
     "cancel": "Abbrechen"
-  }
+  },
+  "openNotifications": "Open notifications"
 }

+ 7 - 0
messages/de/pdf.json

@@ -40,7 +40,14 @@
     "paymentInformation": "Zahlungsinformationen",
     "orgNumberLabel": "Org.-Nr.",
     "paymentTermsLabel": "Zahlungsbedingungen",
+    "netDays": "{days} Tage",
     "dueDateLabel": "Fälligkeitsdatum",
+    "warrantyTitle": "Garantie",
+    "warrantyDuration": "Dauer",
+    "warrantyMileage": "Kilometergrenze",
+    "warrantyExpires": "Läuft ab",
+    "warrantyNotes": "Bedingungen",
+    "warrantyMonthsUnit": "Monate",
     "diagnosticNotes": "Diagnosehinweise",
     "findings": "Beobachtungen",
     "findingsDescription": "Die folgenden Punkte wurden bei diesem Service festgestellt und erfordern möglicherweise Aufmerksamkeit.",

+ 5 - 0
messages/de/portal.json

@@ -7,6 +7,11 @@
     "noResults": "Keine Werkstatt entspricht Ihrer Suche.",
     "openPortal": "Portal öffnen"
   },
+  "directLink": {
+    "title": "Kundenportal",
+    "description": "Bitte verwenden Sie den direkten Link Ihrer Werkstatt, um auf das Kundenportal zuzugreifen.",
+    "hint": "Ihre Werkstatt wird Ihnen den Portal-Link per E-Mail oder SMS mitteilen."
+  },
   "landing": {
     "contactTitle": "Kontakt",
     "aboutTitle": "Über uns",

+ 15 - 4
messages/de/quotes.json

@@ -70,7 +70,9 @@
     "qty": "Menge",
     "unitPrice": "Stückpreis",
     "total": "Gesamt",
-    "subtotal": "Teile Zwischensumme: {amount}"
+    "subtotal": "Teile Zwischensumme: {amount}",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "labor": {
     "title": "Arbeitszeit",
@@ -90,7 +92,9 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours 00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "notes": {
     "title": "Notizen",
@@ -127,7 +131,9 @@
     "customer": "Kunde",
     "selectVehicle": "Fahrzeug auswählen...",
     "selectCustomer": "Kunden auswählen...",
-    "none": "Keine"
+    "none": "Keine",
+    "clearVehicle": "Clear vehicle",
+    "clearCustomer": "Clear customer"
   },
   "totals": {
     "title": "Summen",
@@ -158,7 +164,8 @@
     "emailSent": "E-Mail gesendet",
     "failedEmail": "E-Mail konnte nicht gesendet werden",
     "smsSent": "SMS gesendet",
-    "failedSms": "SMS konnte nicht gesendet werden"
+    "failedSms": "SMS konnte nicht gesendet werden",
+    "copyLink": "Copy link"
   },
   "images": {
     "title": "Angebotsbilder",
@@ -173,6 +180,8 @@
     "uploadingCount": "{count} Bild(er) werden hochgeladen...",
     "uploadedCount": "{count} Bild(er) hochgeladen",
     "uploadFailed": "Hochladen fehlgeschlagen",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Bild gelöscht",
     "failedDelete": "Bild konnte nicht gelöscht werden",
     "failedUpdate": "Aktualisierung fehlgeschlagen",
@@ -191,6 +200,8 @@
     "uploadingCount": "{count} Datei(en) werden hochgeladen...",
     "uploadedCount": "{count} Datei(en) hochgeladen",
     "uploadFailed": "Hochladen fehlgeschlagen",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Datei gelöscht",
     "failedDelete": "Datei konnte nicht gelöscht werden",
     "failedUpdate": "Aktualisierung fehlgeschlagen",

+ 2 - 1
messages/de/reminders.json

@@ -7,5 +7,6 @@
   "selectVehicle": "Fahrzeug auswählen...",
   "searchVehicle": "Fahrzeuge suchen...",
   "noVehicle": "Keine Fahrzeuge gefunden.",
-  "pickDate": "Datum auswählen"
+  "pickDate": "Datum auswählen",
+  "openMenu": "Open menu"
 }

+ 5 - 1
messages/de/reports.json

@@ -406,5 +406,9 @@
     "date": "Datum",
     "title": "Titel"
   },
-  "empty": "Noch keine Daten geladen. Klicken Sie auf Aktualisieren, um den Bericht zu laden."
+  "empty": "Noch keine Daten geladen. Klicken Sie auf Aktualisieren, um den Bericht zu laden.",
+  "pagination": {
+    "previousPage": "Previous page",
+    "nextPage": "Next page"
+  }
 }

+ 36 - 4
messages/de/service.json

@@ -95,7 +95,8 @@
     "scanBarcode": "Scannen",
     "scanTitle": "Barcode scannen",
     "partFound": "\"{name}\" aus dem Lager hinzugefügt",
-    "partNotFound": "Kein Teil für Barcode \"{barcode}\" gefunden"
+    "partNotFound": "Kein Teil für Barcode \"{barcode}\" gefunden",
+    "deleteRow": "Delete row"
   },
   "labor": {
     "title": "Arbeit",
@@ -118,7 +119,8 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours 00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row"
   },
   "totals": {
     "title": "Summen",
@@ -209,7 +211,9 @@
     "images": "Bilder ({count})",
     "videos": "Videos ({count})",
     "diagnostics": "Diagnoseberichte ({count})",
-    "documents": "Dokumente ({count})"
+    "documents": "Dokumente ({count})",
+    "download": "Download",
+    "close": "Close"
   },
   "share": {
     "title": "Rechnung teilen",
@@ -225,7 +229,8 @@
     "emailSent": "E-Mail gesendet",
     "failedEmail": "E-Mail konnte nicht gesendet werden",
     "smsSent": "SMS gesendet",
-    "failedSms": "SMS konnte nicht gesendet werden"
+    "failedSms": "SMS konnte nicht gesendet werden",
+    "copyLink": "Copy link"
   },
   "sidebar": {
     "vehicle": "Fahrzeug",
@@ -333,5 +338,32 @@
     "minutesAgo": "vor {count}m",
     "hoursAgo": "vor {count}h",
     "daysAgo": "vor {count}d"
+  },
+  "table": {
+    "warranty": "Garantie"
+  },
+  "warranty": {
+    "title": "Garantie",
+    "months": "Dauer (Monate)",
+    "mileage": "Kilometergrenze",
+    "notes": "Garantiebedingungen",
+    "notesPlaceholder": "z.B. 12 Monate Garantie auf alle Teile und Arbeit...",
+    "expiresAt": "Läuft ab",
+    "presets": {
+      "3months": "3 Mo.",
+      "6months": "6 Mo.",
+      "12months": "12 Mo.",
+      "24months": "24 Mo."
+    },
+    "status": {
+      "active": "Aktiv",
+      "expiring": "Läuft bald ab",
+      "expired": "Abgelaufen",
+      "none": "Keine"
+    },
+    "defaults": {
+      "title": "Standard-Garantieeinstellungen",
+      "description": "Standardgarantiebedingungen für neue Serviceeinträge festlegen"
+    }
   }
 }

+ 148 - 6
messages/de/settings.json

@@ -7,6 +7,7 @@
       "billing": "Abrechnung & Dokumente",
       "communications": "Kommunikation",
       "workshop": "Werkstatt",
+      "integrations": "Integrationen",
       "system": "System"
     },
     "items": {
@@ -90,6 +91,10 @@
         "title": "Berichtsplanung",
         "description": "Automatischer Berichtsversand"
       },
+      "webhooks": {
+        "title": "Webhooks",
+        "description": "Echtzeit-Ereignisbenachrichtigungen"
+      },
       "appearance": {
         "title": "Darstellung",
         "description": "Design & Anzeige"
@@ -353,7 +358,10 @@
     "memberRemoved": "Mitglied entfernt",
     "failedRemoveMember": "Mitglied konnte nicht entfernt werden",
     "memberRoleUpdated": "Mitgliedsrolle aktualisiert",
-    "failedUpdateRole": "Rolle konnte nicht aktualisiert werden"
+    "failedUpdateRole": "Rolle konnte nicht aktualisiert werden",
+    "removeMember": "Remove member",
+    "cancelInvite": "Cancel invitation",
+    "editRole": "Edit role"
   },
   "invoice": {
     "title": "Rechnungslayout",
@@ -662,10 +670,37 @@
       "PHP": "Philippinischer Peso",
       "IDR": "Indonesische Rupiah",
       "MYR": "Malaysischer Ringgit",
+      "LKR": "Sri-Lanka-Rupie",
       "AED": "VAE-Dirham",
       "SAR": "Saudi-Riyal",
-      "RUB": "Russischer Rubel"
-    }
+      "RUB": "Russischer Rubel",
+      "TWD": "Taiwan-Dollar",
+      "VND": "Vietnamesischer Dong",
+      "PKR": "Pakistanische Rupie",
+      "BDT": "Bangladesch-Taka",
+      "NPR": "Nepalesische Rupie",
+      "BGN": "Bulgarischer Lew",
+      "UAH": "Ukrainische Hrywnja",
+      "CLP": "Chilenischer Peso",
+      "COP": "Kolumbianischer Peso",
+      "ARS": "Argentinischer Peso",
+      "PEN": "Peruanischer Sol",
+      "EGP": "Ägyptisches Pfund",
+      "NGN": "Nigerianischer Naira",
+      "KES": "Kenia-Schilling",
+      "MAD": "Marokkanischer Dirham",
+      "GHS": "Ghanaischer Cedi",
+      "QAR": "Katar-Riyal",
+      "KWD": "Kuwait-Dinar",
+      "BHD": "Bahrain-Dinar",
+      "OMR": "Omanischer Rial",
+      "JOD": "Jordanischer Dinar"
+    },
+    "formatTitle": "Währungsformat",
+    "formatSymbolName": "Symbol",
+    "formatCodeName": "Code",
+    "formatSymbolLabel": "Symbol: {value}",
+    "formatCodeLabel": "Code: {value}"
   },
   "localization": {
     "languageTitle": "Sprache",
@@ -718,7 +753,9 @@
     "fieldUpdated": "Benutzerdefiniertes Feld aktualisiert",
     "fieldDeleted": "Benutzerdefiniertes Feld gelöscht",
     "failedSave": "Feld konnte nicht gespeichert werden",
-    "failedDelete": "Konnte nicht gelöscht werden"
+    "failedDelete": "Konnte nicht gelöscht werden",
+    "edit": "Edit",
+    "delete": "Delete"
   },
   "workshop": {
     "title": "Werkstatt-Standards",
@@ -948,7 +985,8 @@
         "slate": "Schiefer",
         "sand": "Sand"
       }
-    }
+    },
+    "copyUrl": "Copy portal URL"
   },
   "email": {
     "title": "E-Mail-Anbieter",
@@ -1023,7 +1061,8 @@
     "saved": "SMS-Einstellungen gespeichert",
     "failedSave": "Einstellungen konnten nicht gespeichert werden",
     "testSentTo": "Test-SMS gesendet an {phone}",
-    "testFailed": "SMS-Test fehlgeschlagen"
+    "testFailed": "SMS-Test fehlgeschlagen",
+    "copyWebhookUrl": "Copy webhook URL"
   },
   "subscription": {
     "title": "Abonnementplan",
@@ -1074,6 +1113,7 @@
     "resumeSuccess": "Abonnement erfolgreich fortgesetzt",
     "resumeError": "Abonnement konnte nicht fortgesetzt werden",
     "billingPortalError": "Abrechnungsportal konnte nicht geöffnet werden",
+    "checkoutError": "Failed to start checkout",
     "upgradeTitle": "Plan upgraden",
     "upgradeDescription": "Schalten Sie weitere Funktionen frei, indem Sie Ihren Plan upgraden.",
     "upgradeToProDescription": "Upgraden Sie, um unbegrenzte Kunden, Teammitglieder, Berichte, SMTP, API-Zugang und mehr freizuschalten.",
@@ -1165,6 +1205,10 @@
     "deleted": "Berichtszeitplan gelöscht",
     "toggledOn": "Berichtszeitplan aktiviert",
     "toggledOff": "Berichtszeitplan deaktiviert",
+    "failedSave": "Failed to save",
+    "failedSend": "Failed to send",
+    "failedToggle": "Failed to toggle",
+    "failedDelete": "Failed to delete",
     "noMembers": "Keine Teammitglieder gefunden",
     "addRecipient": "Empfänger hinzufügen...",
     "searchMembers": "Mitglieder suchen...",
@@ -1189,5 +1233,103 @@
       "retention": "Kundenbindung",
       "inventory": "Inventar"
     }
+  },
+  "webhooks": {
+    "title": "Webhooks",
+    "subtitle": "Senden Sie Echtzeit-Ereignisbenachrichtigungen an Ihre eigenen Systeme über HTTPS. Jedes Ereignis wird mit HMAC-SHA256 signiert, sodass Sie die Authentizität überprüfen können.",
+    "create": "Webhook hinzufügen",
+    "createTitle": "Webhook hinzufügen",
+    "editTitle": "Webhook bearbeiten",
+    "createDescription": "Webhooks werden als JSON per POST gesendet. Fehlgeschlagene Zustellungen werden bis zu 5 Mal mit exponentiellem Backoff wiederholt.",
+    "name": "Name",
+    "namePlaceholder": "z. B. Buchhaltungs-Sync",
+    "url": "Endpunkt-URL",
+    "urlHint": "Muss mit http:// oder https:// beginnen. HTTPS wird dringend empfohlen.",
+    "description": "Beschreibung (optional)",
+    "events": "Ereignisse",
+    "eventsHint": "Wählen Sie aus, welche Ereignisse diesen Webhook auslösen.",
+    "subscribeToAll": "Alle Ereignisse",
+    "selectAtLeastOneEvent": "Wählen Sie mindestens ein Ereignis aus",
+    "groups": {
+      "customers": "Kunden",
+      "vehicles": "Fahrzeuge",
+      "services": "Serviceaufträge",
+      "quotes": "Angebote",
+      "payments": "Zahlungen",
+      "inspections": "Inspektionen",
+      "inventory": "Lagerbestand",
+      "findings": "Befunde"
+    },
+    "save": "Speichern",
+    "cancel": "Abbrechen",
+    "delete": "Löschen",
+    "rotate": "Erneuern",
+    "done": "Fertig",
+    "paused": "Pausiert",
+    "statusOk": "OK",
+    "statusFailing": "{count, plural, one {# kürzlicher Fehler} other {# kürzliche Fehler}}",
+    "lastDelivery": "Letzte Zustellung {when}",
+    "totalDeliveries": "{count, plural, one {# Zustellung} other {# Zustellungen}}",
+    "toggleAria": "Diesen Webhook aktivieren oder deaktivieren",
+    "deliveries": "Zustellungen",
+    "sendTest": "Test senden",
+    "testSent": "Test-Ereignis gesendet",
+    "testFailed": "Test-Ereignis konnte nicht gesendet werden",
+    "rotateSecret": "Signaturschlüssel erneuern",
+    "confirmRotateTitle": "Signaturschlüssel erneuern?",
+    "confirmRotateDescription": "Der aktuelle Schlüssel funktioniert sofort nicht mehr. Aktualisieren Sie Ihren Endpunkt mit dem neuen Schlüssel.",
+    "secretRotated": "Schlüssel erneuert",
+    "secretRotatedDescription": "Speichern Sie diesen Schlüssel jetzt — er kann später nicht wiederhergestellt werden.",
+    "confirmDeleteTitle": "Webhook löschen?",
+    "confirmDeleteDescription": "„{name}“ empfängt sofort keine Ereignisse mehr. Dies kann nicht rückgängig gemacht werden.",
+    "secretShownOnceTitle": "Webhook „{name}“ erstellt",
+    "secretShownOnceDescription": "Kopieren Sie diesen Signaturschlüssel jetzt. Er wird nicht erneut angezeigt.",
+    "secretCopyHint": "Verwenden Sie diesen Schlüssel, um den X-Torqvoice-Signature-Header bei jeder Anfrage zu überprüfen.",
+    "deliveriesTitle": "Zustellungen — {name}",
+    "deliveriesDescription": "Letzte 50 Versuche. Fehlgeschlagene Ereignisse werden automatisch mit exponentiellem Backoff wiederholt.",
+    "deliveriesEmpty": "Noch keine Zustellungen. Senden Sie ein Test-Ereignis, um es auszuprobieren.",
+    "attempt": "Versuch {n}/{max}",
+    "nextRetry": "Nächster Versuch {when}",
+    "retryQueued": "Wiederholung in der Warteschlange",
+    "retryFailed": "Wiederholung fehlgeschlagen",
+    "saveFailed": "Speichern fehlgeschlagen",
+    "deleteFailed": "Löschen fehlgeschlagen",
+    "toggleFailed": "Umschalten fehlgeschlagen",
+    "rotateFailed": "Erneuern fehlgeschlagen",
+    "created": "Webhook erstellt",
+    "updated": "Webhook aktualisiert",
+    "deleted": "Webhook gelöscht",
+    "emptyTitle": "Noch keine Webhooks",
+    "emptyDescription": "Fügen Sie einen Webhook hinzu, um Ereignisse an Ihre eigenen Systeme zu senden — Buchhaltungs-Sync, eigene Dashboards, Zapier, alles was HTTP spricht.",
+    "helpTitle": "Webhook-Signaturen überprüfen",
+    "helpIntro": "Jede Zustellung enthält eine HMAC-SHA256-Signatur, damit Sie bestätigen können, dass die Anfrage von Torqvoice stammt.",
+    "helpHeadersTitle": "Header, die mit jeder Anfrage gesendet werden",
+    "helpVerify": "Setzen Sie den signierten String als `[timestamp].[body]` zusammen und vergleichen Sie zeitsicher:",
+    "tabWebhooks": "Webhooks",
+    "tabReference": "Ereignisreferenz",
+    "autoDisabled": "Automatisch pausiert",
+    "autoDisabledBanner": "Dieser Webhook wurde nach 20 aufeinanderfolgenden Fehlern automatisch pausiert. Aktivieren Sie ihn wieder, um die Zustellung fortzusetzen.",
+    "reEnable": "Wieder aktivieren",
+    "resumed": "Webhook fortgesetzt",
+    "copyUrl": "URL kopieren",
+    "refresh": "Aktualisieren",
+    "noMatches": "Keine Zustellungen entsprechen diesem Filter.",
+    "retry": "Wiederholen",
+    "viewPayload": "Payload anzeigen",
+    "payloadCopied": "Payload kopiert",
+    "loadingPayload": "Payload wird geladen…",
+    "requestPayload": "Anfrage-Payload",
+    "response": "Antwort",
+    "close": "Schließen",
+    "samplePayload": "Beispiel-Envelope",
+    "referenceTitle": "Ereignisreferenz",
+    "referenceIntro": "Jede Zustellung folgt dieser Envelope-Form. Klicken Sie auf ein Ereignis, um ein Beispiel-Payload zu sehen, das Ihr Endpunkt empfangen wird.",
+    "filter": {
+      "all": "Alle",
+      "success": "Erfolgreich",
+      "failed": "Fehlgeschlagen",
+      "retrying": "Wiederholt",
+      "pending": "Ausstehend"
+    }
   }
 }

+ 6 - 0
messages/de/share.json

@@ -52,12 +52,18 @@
     "bankAccount": "Bankkonto",
     "orgNumber": "Org.-Nummer",
     "paymentTerms": "Zahlungsbedingungen",
+    "netDays": "{days} Tage",
     "dueDate": "Fälligkeitsdatum",
     "serviceImages": "Service-Bilder ({count})",
     "serviceVideos": "Service-Videos ({count})",
     "diagnosticReports": "Diagnoseberichte ({count})",
     "notes": "Hinweise",
     "diagnosticNotes": "Diagnosehinweise",
+    "warrantyTitle": "Garantie",
+    "warrantyExpires": "Läuft ab",
+    "warrantyMonth": "Monat",
+    "warrantyMonths": "Monate",
+    "warrantyKm": "km",
     "findings": "Beobachtungen",
     "findingsDescription": "Die folgenden Punkte wurden bei diesem Service festgestellt und erfordern möglicherweise Aufmerksamkeit.",
     "severity": "Schweregrad",

+ 1 - 0
messages/de/statusReport.json

@@ -14,6 +14,7 @@
     "createAndSend": "Erstellen & Senden",
     "cancel": "Abbrechen",
     "uploadFailed": "Video konnte nicht hochgeladen werden",
+    "createReportFailed": "Failed to create status report",
     "created": "Statusbericht erstellt",
     "noVideo": "Bitte laden Sie zuerst ein Video hoch",
     "removeVideo": "Video entfernen",

+ 5 - 2
messages/de/telegram.json

@@ -4,7 +4,9 @@
   "botToken": {
     "label": "Bot-Token",
     "placeholder": "Bot-Token von @BotFather eingeben",
-    "help": "Erstellen Sie einen Bot über @BotFather auf Telegram, um Ihr Token zu erhalten."
+    "help": "Erstellen Sie einen Bot über @BotFather auf Telegram, um Ihr Token zu erhalten.",
+    "show": "Show token",
+    "hide": "Hide token"
   },
   "botUsername": {
     "label": "Bot-Benutzername",
@@ -17,7 +19,8 @@
   "deepLink": {
     "title": "Kunden-Verbindungslink",
     "description": "Teilen Sie diesen Link mit Kunden, damit sie sich mit Ihrem Bot verbinden können. Ersetzen Sie den Kunden-ID-Platzhalter durch die tatsächliche Kunden-ID.",
-    "copied": "Link kopiert!"
+    "copied": "Link kopiert!",
+    "copy": "Copy deep link"
   },
   "test": {
     "title": "Testnachricht",

+ 2 - 1
messages/de/telegramMessages.json

@@ -30,7 +30,8 @@
     "deleteDescription": "Dies löscht diese Nachricht dauerhaft. Diese Aktion kann nicht rückgängig gemacht werden.",
     "messageDeleted": "Nachricht gelöscht",
     "deleteError": "Nachricht konnte nicht gelöscht werden",
-    "sendError": "Nachricht konnte nicht gesendet werden"
+    "sendError": "Nachricht konnte nicht gesendet werden",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "{label} senden",

+ 84 - 48
messages/de/vehicles.json

@@ -3,7 +3,7 @@
     "active": "Aktiv",
     "archived": "Archiviert",
     "searchPlaceholder": "Suche nach Kennzeichen, Marke, Modell, FIN...",
-    "addVehicle": "Fahrzeug hinzuf\u00fcgen",
+    "addVehicle": "Fahrzeug hinzufügen",
     "emptySearch": "Keine Fahrzeuge entsprechen Ihrer Suche.",
     "emptyArchived": "Keine archivierten Fahrzeuge.",
     "empty": "Noch keine Fahrzeuge.",
@@ -18,33 +18,37 @@
     "edit": "Bearbeiten",
     "archive": "Archivieren",
     "unarchive": "Wiederherstellen",
-    "delete": "L\u00f6schen",
-    "deleteTitle": "Fahrzeug l\u00f6schen",
-    "deleteDescription": "{name} l\u00f6schen? Dadurch werden alle zugeh\u00f6rigen Serviceeintr\u00e4ge und Notizen entfernt.",
+    "delete": "Löschen",
+    "deleteTitle": "Fahrzeug löschen",
+    "deleteDescription": "{name} löschen? Dadurch werden alle zugehörigen Serviceeinträge und Notizen entfernt.",
     "unarchiveTitle": "Fahrzeug wiederherstellen",
-    "unarchiveDescription": "{name} in die aktive Fahrzeugliste zur\u00fcckholen?",
+    "unarchiveDescription": "{name} in die aktive Fahrzeugliste zurückholen?",
     "vehicleUnarchived": "Fahrzeug wiederhergestellt",
     "unarchiveError": "Fehler beim Wiederherstellen des Fahrzeugs",
-    "deleteError": "Fehler beim L\u00f6schen des Fahrzeugs",
-    "error": "Fehler beim Laden der Fahrzeuge"
+    "deleteError": "Fehler beim Löschen des Fahrzeugs",
+    "error": "Fehler beim Laden der Fahrzeuge",
+    "viewTable": "Table view",
+    "viewGrid": "Grid view",
+    "viewLargeGrid": "Large grid view",
+    "openMenu": "Open menu"
   },
   "detail": {
     "vehicleArchived": "Dieses Fahrzeug ist archiviert",
-    "backToVehicles": "Zur\u00fcck zu Fahrzeugen",
+    "backToVehicles": "Zurück zu Fahrzeugen",
     "newWorkOrder": "Neuer Arbeitsauftrag",
     "editVehicle": "Fahrzeug bearbeiten",
     "unarchive": "Wiederherstellen",
     "archive": "Archivieren",
-    "delete": "L\u00f6schen",
+    "delete": "Löschen",
     "notFound": "Fahrzeug nicht gefunden",
     "predictedKm": "Prognostizierte Km",
     "predictedMileage": "Prognostizierter Kilometerstand",
     "predictedHours": "Geschätzte Stunden",
     "predictedTooltip": "Geschätzter aktueller Kilometerstand basierend auf der Servicehistorie. Der Prozentsatz zeigt die Zuverlässigkeit der Berechnung.",
     "dismissed": "Verworfen",
-    "undo": "R\u00fcckg\u00e4ngig",
-    "serviceOverdue": "Service \u00fcberf\u00e4llig",
-    "serviceSoon": "Service bald f\u00e4llig",
+    "undo": "Rückgängig",
+    "serviceOverdue": "Service überfällig",
+    "serviceSoon": "Service bald fällig",
     "services": "Services",
     "purchased": "Gekauft am {date}",
     "tabs": {
@@ -55,16 +59,16 @@
       "reminders": "Erinnerungen",
       "findings": "Beobachtungen"
     },
-    "noteDeleted": "Notiz gel\u00f6scht",
-    "noteDeleteError": "Fehler beim L\u00f6schen",
-    "reminderDeleted": "Erinnerung gel\u00f6scht",
-    "reminderDeleteError": "Fehler beim L\u00f6schen",
+    "noteDeleted": "Notiz gelöscht",
+    "noteDeleteError": "Fehler beim Löschen",
+    "reminderDeleted": "Erinnerung gelöscht",
+    "reminderDeleteError": "Fehler beim Löschen",
     "vehicleUnarchived": "Fahrzeug wiederhergestellt",
     "unarchiveError": "Fehler beim Wiederherstellen",
-    "vehicleDeleted": "Fahrzeug gel\u00f6scht",
-    "vehicleDeleteError": "Fehler beim L\u00f6schen des Fahrzeugs",
-    "deleteTitle": "Fahrzeug l\u00f6schen?",
-    "deleteDescription": "Dadurch werden {name} und alle zugeh\u00f6rigen Serviceeintr\u00e4ge, Notizen und Erinnerungen dauerhaft gel\u00f6scht. Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
+    "vehicleDeleted": "Fahrzeug gelöscht",
+    "vehicleDeleteError": "Fehler beim Löschen des Fahrzeugs",
+    "deleteTitle": "Fahrzeug löschen?",
+    "deleteDescription": "Dadurch werden {name} und alle zugehörigen Serviceeinträge, Notizen und Erinnerungen dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
     "edit": "Bearbeiten",
     "aiSummary": "KI-Zusammenfassung",
     "aiSummaryTitle": "Servicehistorie-Zusammenfassung",
@@ -84,7 +88,9 @@
     "severityCommon": "Häufig — oft gemeldetes Problem",
     "severityModerate": "Mäßig — von einigen Besitzern gemeldet",
     "severityUncommon": "Ungewöhnlich — von wenigen Besitzern gemeldet",
-    "severityRare": "Selten — vereinzelte Berichte, aber kritisch wenn es auftritt"
+    "severityRare": "Selten — vereinzelte Berichte, aber kritisch wenn es auftritt",
+    "openMenu": "Open menu",
+    "dismissMaintenance": "Dismiss"
   },
   "quotes": {
     "newQuote": "Neues Angebot",
@@ -109,33 +115,33 @@
     "active": "Aktiv",
     "completed": "Abgeschlossen",
     "all": "Alle",
-    "addReminder": "Erinnerung hinzuf\u00fcgen",
+    "addReminder": "Erinnerung hinzufügen",
     "emptyActive": "Keine aktiven Erinnerungen",
     "emptyCompleted": "Keine abgeschlossenen Erinnerungen",
     "empty": "Noch keine Erinnerungen",
-    "overdue": "\u00dcberf\u00e4llig",
-    "dueSoon": "Bald f\u00e4llig",
-    "due": "F\u00e4llig am {date}",
-    "dueAt": "F\u00e4llig bei {mileage} {unit}",
+    "overdue": "Überfällig",
+    "dueSoon": "Bald fällig",
+    "due": "Fällig am {date}",
+    "dueAt": "Fällig bei {mileage} {unit}",
     "editTitle": "Erinnerung bearbeiten",
-    "addTitle": "Erinnerung hinzuf\u00fcgen",
+    "addTitle": "Erinnerung hinzufügen",
     "titleLabel": "Titel *",
-    "titlePlaceholder": "z.B. \u00d6lwechsel",
+    "titlePlaceholder": "z.B. Ölwechsel",
     "descriptionLabel": "Beschreibung",
     "descriptionPlaceholder": "Optionale Details...",
-    "dueDateLabel": "F\u00e4lligkeitsdatum",
-    "dueMileageLabel": "F\u00e4lligkeitskilometerstand",
+    "dueDateLabel": "Fälligkeitsdatum",
+    "dueMileageLabel": "Fälligkeitskilometerstand",
     "dueMileagePlaceholder": "z.B. 50000",
     "reminderUpdated": "Erinnerung aktualisiert",
     "reminderCreated": "Erinnerung erstellt",
     "saveError": "Fehler beim {action} der Erinnerung"
   },
   "services": {
-    "searchPlaceholder": "Eintr\u00e4ge durchsuchen...",
+    "searchPlaceholder": "Einträge durchsuchen...",
     "allTypes": "Alle Typen",
     "maintenance": "Wartung",
     "repair": "Reparatur",
-    "upgrade": "Ausr\u00fcstung",
+    "upgrade": "Ausrüstung",
     "inspection": "Inspektion",
     "newWorkOrder": "Neuer Arbeitsauftrag",
     "exportServiceHistory": "Historie exportieren",
@@ -148,16 +154,29 @@
       "mileageMarine": "Betriebsstunden",
       "technician": "Techniker",
       "files": "Dateien",
-      "total": "Gesamt"
+      "total": "Gesamt",
+      "warranty": "Garantie"
     },
-    "emptyFiltered": "Keine Serviceeintr\u00e4ge entsprechen Ihren Filtern.",
-    "empty": "Noch keine Serviceeintr\u00e4ge.",
+    "warranty": {
+      "status": {
+        "active": "Aktiv",
+        "expiring": "Auslaufend",
+        "expired": "Abgelaufen",
+        "none": "N/A"
+      }
+    },
+    "emptyFiltered": "Keine Serviceeinträge entsprechen Ihren Filtern.",
+    "empty": "Noch keine Serviceeinträge.",
     "showing": "Zeige {start}-{end} von {total}",
     "perPage": "pro Seite",
-    "page": "Seite {page} von {totalPages}"
+    "page": "Seite {page} von {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "notes": {
-    "addNote": "Notiz hinzuf\u00fcgen",
+    "addNote": "Notiz hinzufügen",
     "table": {
       "title": "Titel",
       "content": "Inhalt",
@@ -165,13 +184,13 @@
     },
     "empty": "Noch keine Notizen.",
     "pin": "Anheften",
-    "unpin": "L\u00f6sen",
-    "delete": "L\u00f6schen",
+    "unpin": "Lösen",
+    "delete": "Löschen",
     "showing": "Zeige {start}-{end} von {total}",
     "perPage": "pro Seite",
     "page": "Seite {page} von {totalPages}",
     "editTitle": "Notiz bearbeiten",
-    "addTitle": "Notiz hinzuf\u00fcgen",
+    "addTitle": "Notiz hinzufügen",
     "titleLabel": "Titel *",
     "titlePlaceholder": "Titel der Notiz",
     "contentLabel": "Inhalt *",
@@ -179,7 +198,12 @@
     "noteUpdated": "Notiz aktualisiert",
     "noteCreated": "Notiz erstellt",
     "titleRequired": "Titel und Inhalt sind erforderlich",
-    "saveError": "Fehler beim {action} der Notiz"
+    "saveError": "Fehler beim {action} der Notiz",
+    "openMenu": "Open menu",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "findings": {
     "sectionTitle": "Beobachtungen",
@@ -227,7 +251,12 @@
     "resolveError": "Beobachtung konnte nicht erledigt werden",
     "showing": "Zeige {start}-{end} von {total}",
     "perPage": "pro Seite",
-    "page": "Seite {page} von {totalPages}"
+    "page": "Seite {page} von {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page",
+    "openMenu": "Open menu"
   },
   "observationsPage": {
     "searchPlaceholder": "Beschreibung, Notizen oder Fahrzeug suchen...",
@@ -245,12 +274,12 @@
   },
   "form": {
     "editTitle": "Fahrzeug bearbeiten",
-    "addTitle": "Neues Fahrzeug hinzuf\u00fcgen",
+    "addTitle": "Neues Fahrzeug hinzufügen",
     "photo": "Foto",
     "clickToUpload": "Klicken Sie, um ein Foto hochzuladen",
     "imageFormats": "JPEG, PNG, WebP bis zu 5MB",
-    "imageTooLarge": "Bild zu gro\u00df",
-    "maxFileSize": "Maximale Dateigr\u00f6\u00dfe ist 5MB",
+    "imageTooLarge": "Bild zu groß",
+    "maxFileSize": "Maximale Dateigröße ist 5MB",
     "uploadingImage": "Bild wird hochgeladen...",
     "uploadFailed": "Hochladen fehlgeschlagen",
     "imageUploaded": "Bild hochgeladen",
@@ -258,7 +287,7 @@
     "searchCustomers": "Kunden suchen...",
     "noCustomerFound": "Kein Kunde gefunden.",
     "noCustomer": "Kein Kunde",
-    "addCustomer": "Kunde hinzuf\u00fcgen",
+    "addCustomer": "Kunde hinzufügen",
     "make": "Marke *",
     "model": "Modell *",
     "year": "Baujahr *",
@@ -277,6 +306,7 @@
     "manual": "Schaltgetriebe",
     "cvt": "CVT",
     "engineSize": "Hubraum",
+    "engineCode": "Motorcode",
     "makeMarine": "Hersteller *",
     "mileageMarine": "Betriebsstunden",
     "transmissionMarine": "Motortyp",
@@ -287,15 +317,21 @@
     "vinMarine": "HIN",
     "licensePlateMarine": "Registrierungsnummer",
     "vehicleUpdated": "Fahrzeug aktualisiert",
-    "vehicleAdded": "Fahrzeug hinzugef\u00fcgt",
+    "vehicleAdded": "Fahrzeug hinzugefügt",
     "saveError": "Fehler beim Speichern des Fahrzeugs"
   },
   "archiveDialog": {
     "title": "Fahrzeug archivieren",
     "description": "<span>{name}</span> archivieren? Es wird aus der Hauptliste ausgeblendet, aber der gesamte Serviceverlauf bleibt erhalten.",
     "reasonLabel": "Grund (optional)",
-    "reasonPlaceholder": "z.B. Verkauft, Totalschaden, au\u00dfer Betrieb genommen...",
+    "reasonPlaceholder": "z.B. Verkauft, Totalschaden, außer Betrieb genommen...",
     "archived": "{name} archiviert",
     "archiveError": "Fehler beim Archivieren des Fahrzeugs"
+  },
+  "serviceRecords": {
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   }
 }

+ 8 - 2
messages/de/workBoard.json

@@ -7,7 +7,11 @@
     "day": "Tag",
     "week": "Woche",
     "presenter": "Präsentator",
-    "addTechnician": "Techniker hinzufügen"
+    "addTechnician": "Techniker hinzufügen",
+    "previousWeek": "Previous week",
+    "nextWeek": "Next week",
+    "previousDay": "Previous day",
+    "nextDay": "Next day"
   },
   "board": {
     "days": {
@@ -70,6 +74,8 @@
       "inProgress": "In Bearbeitung",
       "waitingParts": "Warten auf Teile",
       "completed": "Abgeschlossen"
-    }
+    },
+    "previous": "Previous",
+    "next": "Next"
   }
 }

+ 2 - 1
messages/de/workOrders.json

@@ -33,7 +33,8 @@
       "date": "Datum",
       "total": "Gesamt"
     },
-    "emailSubject": "Fahrzeugstatusupdate: {status}"
+    "emailSubject": "Fahrzeugstatusupdate: {status}",
+    "changeStatus": "Change status"
   },
   "vehiclePicker": {
     "title": "Fahrzeug für Arbeitsauftrag auswählen",

+ 19 - 4
messages/en/admin.json

@@ -1,9 +1,21 @@
 {
   "nav": {
-    "overview": { "title": "Overview", "description": "Platform stats & health" },
-    "users": { "title": "Users", "description": "Manage all users" },
-    "organizations": { "title": "Organizations", "description": "Manage all organizations" },
-    "settings": { "title": "Settings", "description": "Platform configuration" }
+    "overview": {
+      "title": "Overview",
+      "description": "Platform stats & health"
+    },
+    "users": {
+      "title": "Users",
+      "description": "Manage all users"
+    },
+    "organizations": {
+      "title": "Organizations",
+      "description": "Manage all organizations"
+    },
+    "settings": {
+      "title": "Settings",
+      "description": "Platform configuration"
+    }
   },
   "layout": {
     "title": "Admin Panel",
@@ -104,5 +116,8 @@
     "postmarkApiKeyHint": "Get your Server Token from postmarkapp.com",
     "mailgunApiKeyHint": "Get your API key from app.mailgun.com",
     "sendgridApiKeyHint": "Get your API key from app.sendgrid.com"
+  },
+  "common": {
+    "openMenu": "Open menu"
   }
 }

+ 2 - 1
messages/en/aiChat.json

@@ -14,5 +14,6 @@
   "chatsPrivate": "Your chats are private and only visible to you.",
   "deleteChat": "Delete chat",
   "hideSidebar": "Hide sidebar",
-  "showSidebar": "Show sidebar"
+  "showSidebar": "Show sidebar",
+  "sendMessage": "Send message"
 }

+ 5 - 1
messages/en/audit.json

@@ -93,6 +93,10 @@
     "statusReport_create": "Created Status Report",
     "statusReport_send": "Sent Status Report",
     "statusReport_delete": "Deleted Status Report",
-    "telegram_send": "Sent Telegram Message"
+    "telegram_send": "Sent Telegram Message",
+    "webhook_create": "Created Webhook",
+    "webhook_update": "Updated Webhook",
+    "webhook_delete": "Deleted Webhook",
+    "webhook_rotateSecret": "Rotated Webhook Secret"
   }
 }

+ 5 - 1
messages/en/billing.json

@@ -78,6 +78,10 @@
     "deleted": "Recurring invoice deleted",
     "failedDelete": "Failed to delete",
     "processed": "Processed {count} invoice(s)",
-    "failedProcess": "Failed to process"
+    "failedProcess": "Failed to process",
+    "pause": "Pause",
+    "resume": "Resume",
+    "delete": "Delete",
+    "removeRow": "Remove row"
   }
 }

+ 3 - 1
messages/en/calendar.json

@@ -63,5 +63,7 @@
     },
     "noEvents": "No events",
     "selectDay": "Select a day with events or create a new work order."
-  }
+  },
+  "previousMonth": "Previous month",
+  "nextMonth": "Next month"
 }

+ 2 - 1
messages/en/customers.json

@@ -21,7 +21,8 @@
     "batchDelete": "Delete ({count})",
     "batchDeleteTitle": "Delete Customers",
     "batchDeleteDescription": "Delete {count} customer(s)? This will unlink their vehicles.",
-    "batchDeleteSuccess": "{count} customer(s) deleted"
+    "batchDeleteSuccess": "{count} customer(s) deleted",
+    "openMenu": "Open menu"
   },
   "detail": {
     "backToCustomers": "Back to customers",

+ 10 - 5
messages/en/dashboard.json

@@ -18,7 +18,8 @@
     "quantity": "Quantity",
     "addToJob": "Add {qty} to job",
     "statusReport": "Status Report",
-    "report": "Report"
+    "report": "Report",
+    "observation": "Observation"
   },
   "recentActivity": "Recent Activity",
   "noRecentActivity": "No recent activity",
@@ -64,7 +65,9 @@
     "dismissedTab": "Dismissed",
     "noActive": "No vehicles currently due for service.",
     "dismissedOn": "Dismissed {date}",
-    "restore": "Restore"
+    "restore": "Restore",
+    "settingsAriaLabel": "Maintenance settings",
+    "dismissAriaLabel": "Dismiss"
   },
   "reminders": {
     "title": "Upcoming Reminders",
@@ -95,7 +98,8 @@
     "pending": "Pending",
     "createQuote": "Create Quote",
     "itemsRequested": "{count, plural, one {# item requested} other {# items requested}}",
-    "noData": "No pending quote requests."
+    "noData": "No pending quote requests.",
+    "dismissAriaLabel": "Dismiss quote request"
   },
   "quoteResponses": {
     "title": "Customer Quote Responses",
@@ -106,7 +110,8 @@
     "convert": "Convert",
     "viewQuote": "View Quote",
     "view": "View",
-    "noData": "No customer responses."
+    "noData": "No customer responses.",
+    "dismissAriaLabel": "Dismiss response"
   },
   "recentCompleted": {
     "title": "Recent Completed",
@@ -133,4 +138,4 @@
     "days": "{count}d"
   },
   "error": "Failed to load dashboard"
-}
+}

+ 2 - 1
messages/en/inventory.json

@@ -118,5 +118,6 @@
     "cancel": "Cancel",
     "saveChanges": "Save Changes",
     "addPart": "Add Part"
-  }
+  },
+  "openMenu": "Open menu"
 }

+ 14 - 3
messages/en/laborPresets.json

@@ -15,7 +15,8 @@
   },
   "actions": {
     "edit": "Edit",
-    "delete": "Delete"
+    "delete": "Delete",
+    "openMenu": "Open menu"
   },
   "deletePreset": {
     "title": "Delete Labor Package",
@@ -45,7 +46,16 @@
     "create": "Create Package",
     "saveChanges": "Save Changes",
     "hourlyTag": "HR",
-    "serviceTag": "SVC"
+    "serviceTag": "SVC",
+    "partsTitle": "Parts",
+    "addPart": "Add Part",
+    "partName": "Part name",
+    "partNumber": "Part #",
+    "partQty": "Qty",
+    "partPrice": "Unit price",
+    "importFromInventory": "Import from inventory",
+    "searchInventory": "Search inventory...",
+    "noPartsFound": "No parts found"
   },
   "picker": {
     "title": "Select Labor Package",
@@ -53,7 +63,8 @@
     "noPackages": "No labor packages found.",
     "items": "{count} items",
     "totalHours": "{hours}h total",
-    "totalUnits": "{units} unit(s)"
+    "totalUnits": "{units} unit(s)",
+    "totalParts": "{count} parts"
   },
   "errors": {
     "error": "Error",

+ 5 - 2
messages/en/messages.json

@@ -18,7 +18,9 @@
     "deleteTitle": "Delete conversation?",
     "deleteDescription": "This will permanently delete all messages with {name}. This action cannot be undone.",
     "deleted": "Conversation deleted",
-    "deleteError": "Failed to delete conversation"
+    "deleteError": "Failed to delete conversation",
+    "back": "Back",
+    "conversationMenu": "Conversation menu"
   },
   "conversation": {
     "noPhone": "No phone number on file for {name}. Add a phone number to start messaging.",
@@ -30,7 +32,8 @@
     "deleteDescription": "This will permanently delete this message from the conversation. This action cannot be undone.",
     "messageDeleted": "Message deleted",
     "deleteError": "Failed to delete message",
-    "sendError": "Failed to send SMS"
+    "sendError": "Failed to send SMS",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "Send {label}",

+ 2 - 1
messages/en/notifications.json

@@ -6,5 +6,6 @@
     "confirmDelete": "Are you sure?",
     "delete": "Delete",
     "cancel": "Cancel"
-  }
+  },
+  "openNotifications": "Open notifications"
 }

+ 7 - 0
messages/en/pdf.json

@@ -41,7 +41,14 @@
     "paymentInformation": "Payment Information",
     "orgNumberLabel": "Org. Number",
     "paymentTermsLabel": "Payment Terms",
+    "netDays": "{days} Days",
     "dueDateLabel": "Due Date",
+    "warrantyTitle": "Warranty",
+    "warrantyDuration": "Duration",
+    "warrantyMileage": "Mileage Limit",
+    "warrantyExpires": "Expires",
+    "warrantyNotes": "Terms",
+    "warrantyMonthsUnit": "months",
     "diagnosticNotes": "Diagnostic Notes",
     "findings": "Observations",
     "findingsDescription": "The following items were observed during this service and may require attention.",

+ 5 - 0
messages/en/portal.json

@@ -7,6 +7,11 @@
     "noResults": "No workshops match your search.",
     "openPortal": "Open portal"
   },
+  "directLink": {
+    "title": "Customer Portal",
+    "description": "Please use the direct link provided by your workshop to access the customer portal.",
+    "hint": "Your workshop will share the portal link with you via email or SMS."
+  },
   "landing": {
     "contactTitle": "Contact us",
     "aboutTitle": "About us",

+ 16 - 5
messages/en/quotes.json

@@ -70,7 +70,9 @@
     "qty": "Qty",
     "unitPrice": "Unit Price",
     "total": "Total",
-    "subtotal": "Parts Subtotal: {amount}"
+    "subtotal": "Parts Subtotal: {amount}",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "labor": {
     "title": "Labor & Services",
@@ -89,8 +91,10 @@
     "serviceTag": "SVC",
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
-    "switchToHourlyHint": "Hourly: priced by hours worked (hours \u00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToHourlyHint": "Hourly: priced by hours worked (hours × rate/hr). Click to switch.",
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "notes": {
     "title": "Notes",
@@ -127,7 +131,9 @@
     "customer": "Customer",
     "selectVehicle": "Select vehicle...",
     "selectCustomer": "Select customer...",
-    "none": "None"
+    "none": "None",
+    "clearVehicle": "Clear vehicle",
+    "clearCustomer": "Clear customer"
   },
   "totals": {
     "title": "Totals",
@@ -158,7 +164,8 @@
     "emailSent": "Email sent",
     "failedEmail": "Failed to send email",
     "smsSent": "SMS sent",
-    "failedSms": "Failed to send SMS"
+    "failedSms": "Failed to send SMS",
+    "copyLink": "Copy link"
   },
   "images": {
     "title": "Quote Images",
@@ -173,6 +180,8 @@
     "uploadingCount": "Uploading {count} image(s)...",
     "uploadedCount": "{count} image(s) uploaded",
     "uploadFailed": "Upload failed",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Image deleted",
     "failedDelete": "Failed to delete image",
     "failedUpdate": "Failed to update",
@@ -191,6 +200,8 @@
     "uploadingCount": "Uploading {count} file(s)...",
     "uploadedCount": "{count} file(s) uploaded",
     "uploadFailed": "Upload failed",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "File deleted",
     "failedDelete": "Failed to delete file",
     "failedUpdate": "Failed to update",

+ 2 - 1
messages/en/reminders.json

@@ -7,5 +7,6 @@
   "selectVehicle": "Select vehicle...",
   "searchVehicle": "Search vehicles...",
   "noVehicle": "No vehicles found.",
-  "pickDate": "Pick a date"
+  "pickDate": "Pick a date",
+  "openMenu": "Open menu"
 }

+ 5 - 2
messages/en/reports.json

@@ -400,12 +400,15 @@
     "unitCost": "Unit Cost",
     "vehiclesSection": "Vehicle Report",
     "vehicleLabel": "Vehicle",
-    "customer": "Customer",
     "totalCost": "Total Cost",
     "partsCostLabel": "Parts Cost",
     "laborCostLabel": "Labor Cost",
     "date": "Date",
     "title": "Title"
   },
-  "empty": "No data loaded yet. Click Refresh to load the report."
+  "empty": "No data loaded yet. Click Refresh to load the report.",
+  "pagination": {
+    "previousPage": "Previous page",
+    "nextPage": "Next page"
+  }
 }

+ 36 - 4
messages/en/service.json

@@ -95,7 +95,8 @@
     "scanBarcode": "Scan",
     "scanTitle": "Scan Barcode",
     "partFound": "Added \"{name}\" from inventory",
-    "partNotFound": "No part found for barcode \"{barcode}\""
+    "partNotFound": "No part found for barcode \"{barcode}\"",
+    "deleteRow": "Delete row"
   },
   "labor": {
     "title": "Labor & Services",
@@ -118,7 +119,8 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours × rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row"
   },
   "totals": {
     "title": "Totals",
@@ -209,7 +211,9 @@
     "images": "Images ({count})",
     "videos": "Videos ({count})",
     "diagnostics": "Diagnostic Reports ({count})",
-    "documents": "Documents ({count})"
+    "documents": "Documents ({count})",
+    "download": "Download",
+    "close": "Close"
   },
   "share": {
     "title": "Share Invoice",
@@ -225,7 +229,8 @@
     "emailSent": "Email sent",
     "failedEmail": "Failed to send email",
     "smsSent": "SMS sent",
-    "failedSms": "Failed to send SMS"
+    "failedSms": "Failed to send SMS",
+    "copyLink": "Copy link"
   },
   "sidebar": {
     "vehicle": "Vehicle",
@@ -333,5 +338,32 @@
     "minutesAgo": "{count}m ago",
     "hoursAgo": "{count}h ago",
     "daysAgo": "{count}d ago"
+  },
+  "table": {
+    "warranty": "Warranty"
+  },
+  "warranty": {
+    "title": "Warranty",
+    "months": "Duration (months)",
+    "mileage": "Mileage Limit",
+    "notes": "Warranty Terms",
+    "notesPlaceholder": "e.g. 12-month warranty on all parts and labor...",
+    "expiresAt": "Expires",
+    "presets": {
+      "3months": "3 mo",
+      "6months": "6 mo",
+      "12months": "12 mo",
+      "24months": "24 mo"
+    },
+    "status": {
+      "active": "Active",
+      "expiring": "Expiring",
+      "expired": "Expired",
+      "none": "N/A"
+    },
+    "defaults": {
+      "title": "Default Warranty Settings",
+      "description": "Set default warranty terms for new service records"
+    }
   }
 }

+ 148 - 6
messages/en/settings.json

@@ -7,6 +7,7 @@
       "billing": "Billing & Documents",
       "communications": "Communications",
       "workshop": "Workshop",
+      "integrations": "Integrations",
       "system": "System"
     },
     "items": {
@@ -90,6 +91,10 @@
         "title": "Report Schedule",
         "description": "Automated report delivery"
       },
+      "webhooks": {
+        "title": "Webhooks",
+        "description": "Real-time event notifications"
+      },
       "appearance": {
         "title": "Appearance",
         "description": "Theme & display"
@@ -353,7 +358,10 @@
     "memberRemoved": "Member removed",
     "failedRemoveMember": "Failed to remove member",
     "memberRoleUpdated": "Member role updated",
-    "failedUpdateRole": "Failed to update role"
+    "failedUpdateRole": "Failed to update role",
+    "removeMember": "Remove member",
+    "cancelInvite": "Cancel invitation",
+    "editRole": "Edit role"
   },
   "invoice": {
     "title": "Invoice & Quotes",
@@ -662,10 +670,37 @@
       "PHP": "Philippine Peso",
       "IDR": "Indonesian Rupiah",
       "MYR": "Malaysian Ringgit",
+      "LKR": "Sri Lankan Rupee",
       "AED": "UAE Dirham",
       "SAR": "Saudi Riyal",
-      "RUB": "Russian Ruble"
-    }
+      "RUB": "Russian Ruble",
+      "TWD": "Taiwan Dollar",
+      "VND": "Vietnamese Dong",
+      "PKR": "Pakistani Rupee",
+      "BDT": "Bangladeshi Taka",
+      "NPR": "Nepalese Rupee",
+      "BGN": "Bulgarian Lev",
+      "UAH": "Ukrainian Hryvnia",
+      "CLP": "Chilean Peso",
+      "COP": "Colombian Peso",
+      "ARS": "Argentine Peso",
+      "PEN": "Peruvian Sol",
+      "EGP": "Egyptian Pound",
+      "NGN": "Nigerian Naira",
+      "KES": "Kenyan Shilling",
+      "MAD": "Moroccan Dirham",
+      "GHS": "Ghanaian Cedi",
+      "QAR": "Qatari Riyal",
+      "KWD": "Kuwaiti Dinar",
+      "BHD": "Bahraini Dinar",
+      "OMR": "Omani Rial",
+      "JOD": "Jordanian Dinar"
+    },
+    "formatTitle": "Currency Format",
+    "formatSymbolName": "Symbol",
+    "formatCodeName": "Code",
+    "formatSymbolLabel": "Symbol: {value}",
+    "formatCodeLabel": "Code: {value}"
   },
   "localization": {
     "languageTitle": "Language",
@@ -718,7 +753,9 @@
     "fieldUpdated": "Custom field updated",
     "fieldDeleted": "Custom field deleted",
     "failedSave": "Failed to save field",
-    "failedDelete": "Failed to delete"
+    "failedDelete": "Failed to delete",
+    "edit": "Edit",
+    "delete": "Delete"
   },
   "workshop": {
     "title": "Workshop Defaults",
@@ -948,7 +985,8 @@
         "slate": "Slate",
         "sand": "Sand"
       }
-    }
+    },
+    "copyUrl": "Copy portal URL"
   },
   "email": {
     "title": "Email Provider",
@@ -1023,7 +1061,8 @@
     "saved": "SMS settings saved",
     "failedSave": "Failed to save settings",
     "testSentTo": "Test SMS sent to {phone}",
-    "testFailed": "SMS test failed"
+    "testFailed": "SMS test failed",
+    "copyWebhookUrl": "Copy webhook URL"
   },
   "subscription": {
     "title": "Subscription Plan",
@@ -1074,6 +1113,7 @@
     "resumeSuccess": "Subscription resumed successfully",
     "resumeError": "Failed to resume subscription",
     "billingPortalError": "Failed to open billing portal",
+    "checkoutError": "Failed to start checkout",
     "upgradeTitle": "Upgrade Plan",
     "upgradeDescription": "Unlock more features by upgrading your plan.",
     "upgradeToProDescription": "Upgrade to unlock unlimited customers, team members, reports, SMTP, API access, and more.",
@@ -1165,6 +1205,10 @@
     "deleted": "Report schedule deleted",
     "toggledOn": "Report schedule enabled",
     "toggledOff": "Report schedule disabled",
+    "failedSave": "Failed to save",
+    "failedSend": "Failed to send",
+    "failedToggle": "Failed to toggle",
+    "failedDelete": "Failed to delete",
     "noMembers": "No team members found",
     "addRecipient": "Add recipient...",
     "searchMembers": "Search members...",
@@ -1189,5 +1233,103 @@
       "retention": "Customer Retention",
       "inventory": "Inventory"
     }
+  },
+  "webhooks": {
+    "title": "Webhooks",
+    "subtitle": "Send real-time event notifications to your own systems via HTTPS. Each event is signed with HMAC-SHA256 so you can verify authenticity.",
+    "create": "Add webhook",
+    "createTitle": "Add webhook",
+    "editTitle": "Edit webhook",
+    "createDescription": "Webhooks are POSTed as JSON. Failed deliveries are retried with exponential backoff up to 5 times.",
+    "name": "Name",
+    "namePlaceholder": "e.g. Accounting sync",
+    "url": "Endpoint URL",
+    "urlHint": "Must start with http:// or https://. We strongly recommend HTTPS.",
+    "description": "Description (optional)",
+    "events": "Events",
+    "eventsHint": "Choose which events trigger this webhook.",
+    "subscribeToAll": "All events",
+    "selectAtLeastOneEvent": "Select at least one event",
+    "groups": {
+      "customers": "Customers",
+      "vehicles": "Vehicles",
+      "services": "Service Records",
+      "quotes": "Quotes",
+      "payments": "Payments",
+      "inspections": "Inspections",
+      "inventory": "Inventory",
+      "findings": "Findings"
+    },
+    "save": "Save",
+    "cancel": "Cancel",
+    "delete": "Delete",
+    "rotate": "Rotate",
+    "done": "Done",
+    "paused": "Paused",
+    "statusOk": "Healthy",
+    "statusFailing": "{count, plural, one {# recent failure} other {# recent failures}}",
+    "lastDelivery": "Last delivery {when}",
+    "totalDeliveries": "{count, plural, one {# delivery} other {# deliveries}}",
+    "toggleAria": "Enable or disable this webhook",
+    "deliveries": "Deliveries",
+    "sendTest": "Send test",
+    "testSent": "Test event sent",
+    "testFailed": "Failed to send test event",
+    "rotateSecret": "Rotate signing secret",
+    "confirmRotateTitle": "Rotate signing secret?",
+    "confirmRotateDescription": "The current secret will stop working immediately. Update your endpoint with the new secret.",
+    "secretRotated": "Secret rotated",
+    "secretRotatedDescription": "Save this secret now — it cannot be recovered later.",
+    "confirmDeleteTitle": "Delete webhook?",
+    "confirmDeleteDescription": "“{name}” will stop receiving events immediately. This cannot be undone.",
+    "secretShownOnceTitle": "Webhook “{name}” created",
+    "secretShownOnceDescription": "Copy this signing secret now. It will not be shown again.",
+    "secretCopyHint": "Use this secret to verify the X-Torqvoice-Signature header on each request.",
+    "deliveriesTitle": "Deliveries — {name}",
+    "deliveriesDescription": "Last 50 attempts. Failed events are retried automatically with exponential backoff.",
+    "deliveriesEmpty": "No deliveries yet. Send a test event to try it out.",
+    "attempt": "Attempt {n}/{max}",
+    "nextRetry": "Next retry {when}",
+    "retryQueued": "Retry queued",
+    "retryFailed": "Retry failed",
+    "saveFailed": "Save failed",
+    "deleteFailed": "Delete failed",
+    "toggleFailed": "Toggle failed",
+    "rotateFailed": "Rotate failed",
+    "created": "Webhook created",
+    "updated": "Webhook updated",
+    "deleted": "Webhook deleted",
+    "emptyTitle": "No webhooks yet",
+    "emptyDescription": "Add a webhook to push events to your own systems — accounting sync, custom dashboards, Zapier, anything that speaks HTTP.",
+    "helpTitle": "Verifying webhook signatures",
+    "helpIntro": "Each delivery includes an HMAC-SHA256 signature so you can confirm the request came from Torqvoice.",
+    "helpHeadersTitle": "Headers sent with every request",
+    "helpVerify": "Reconstruct the signed string as `[timestamp].[body]` and compare with timing-safe equality:",
+    "tabWebhooks": "Webhooks",
+    "tabReference": "Events Reference",
+    "autoDisabled": "Auto-paused",
+    "autoDisabledBanner": "This webhook was auto-paused after 20 consecutive failures. Re-enable to resume deliveries.",
+    "reEnable": "Re-enable",
+    "resumed": "Webhook resumed",
+    "copyUrl": "Copy URL",
+    "refresh": "Refresh",
+    "noMatches": "No deliveries match this filter.",
+    "retry": "Retry",
+    "viewPayload": "View payload",
+    "payloadCopied": "Payload copied",
+    "loadingPayload": "Loading payload…",
+    "requestPayload": "Request payload",
+    "response": "Response",
+    "close": "Close",
+    "samplePayload": "Sample envelope",
+    "referenceTitle": "Event reference",
+    "referenceIntro": "Every delivery follows this envelope shape. Click an event to see a sample payload your endpoint will receive.",
+    "filter": {
+      "all": "All",
+      "success": "Success",
+      "failed": "Failed",
+      "retrying": "Retrying",
+      "pending": "Pending"
+    }
   }
 }

+ 6 - 0
messages/en/share.json

@@ -52,12 +52,18 @@
     "bankAccount": "Bank Account",
     "orgNumber": "Org. Number",
     "paymentTerms": "Payment Terms",
+    "netDays": "{days} Days",
     "dueDate": "Due Date",
     "serviceImages": "Service Images ({count})",
     "serviceVideos": "Service Videos ({count})",
     "diagnosticReports": "Diagnostic Reports ({count})",
     "notes": "Notes",
     "diagnosticNotes": "Diagnostic Notes",
+    "warrantyTitle": "Warranty",
+    "warrantyExpires": "Expires",
+    "warrantyMonth": "month",
+    "warrantyMonths": "months",
+    "warrantyKm": "km",
     "findings": "Observations",
     "findingsDescription": "The following items were observed during this service and may require attention.",
     "severity": "Severity",

+ 1 - 0
messages/en/statusReport.json

@@ -14,6 +14,7 @@
     "createAndSend": "Create & Send",
     "cancel": "Cancel",
     "uploadFailed": "Failed to upload video",
+    "createReportFailed": "Failed to create status report",
     "created": "Status report created",
     "noVideo": "Please upload a video first",
     "removeVideo": "Remove video",

+ 5 - 2
messages/en/telegram.json

@@ -4,7 +4,9 @@
   "botToken": {
     "label": "Bot Token",
     "placeholder": "Enter your bot token from @BotFather",
-    "help": "Create a bot via @BotFather on Telegram to get your token."
+    "help": "Create a bot via @BotFather on Telegram to get your token.",
+    "show": "Show token",
+    "hide": "Hide token"
   },
   "botUsername": {
     "label": "Bot Username",
@@ -17,7 +19,8 @@
   "deepLink": {
     "title": "Customer Connection Link",
     "description": "Share this link with customers so they can connect to your bot. Replace the customer ID placeholder with the actual customer ID.",
-    "copied": "Link copied!"
+    "copied": "Link copied!",
+    "copy": "Copy deep link"
   },
   "test": {
     "title": "Test Message",

+ 2 - 1
messages/en/telegramMessages.json

@@ -30,7 +30,8 @@
     "deleteDescription": "This will permanently delete this message. This action cannot be undone.",
     "messageDeleted": "Message deleted",
     "deleteError": "Failed to delete message",
-    "sendError": "Failed to send message"
+    "sendError": "Failed to send message",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "Send {label}",

+ 42 - 6
messages/en/vehicles.json

@@ -26,7 +26,11 @@
     "vehicleUnarchived": "Vehicle unarchived",
     "unarchiveError": "Failed to unarchive vehicle",
     "deleteError": "Failed to delete vehicle",
-    "error": "Failed to load vehicles"
+    "error": "Failed to load vehicles",
+    "viewTable": "Table view",
+    "viewGrid": "Grid view",
+    "viewLargeGrid": "Large grid view",
+    "openMenu": "Open menu"
   },
   "detail": {
     "vehicleArchived": "This vehicle is archived",
@@ -84,7 +88,9 @@
     "severityCommon": "Common — frequently reported issue",
     "severityModerate": "Moderate — reported by some owners",
     "severityUncommon": "Uncommon — reported by few owners",
-    "severityRare": "Rare — isolated reports but critical when it occurs"
+    "severityRare": "Rare — isolated reports but critical when it occurs",
+    "openMenu": "Open menu",
+    "dismissMaintenance": "Dismiss"
   },
   "quotes": {
     "newQuote": "New Quote",
@@ -148,13 +154,26 @@
       "mileageMarine": "Engine Hours",
       "technician": "Technician",
       "files": "Files",
-      "total": "Total"
+      "total": "Total",
+      "warranty": "Warranty"
+    },
+    "warranty": {
+      "status": {
+        "active": "Active",
+        "expiring": "Expiring",
+        "expired": "Expired",
+        "none": "N/A"
+      }
     },
     "emptyFiltered": "No service records match your filters.",
     "empty": "No service records yet.",
     "showing": "Showing {start}-{end} of {total}",
     "perPage": "per page",
-    "page": "Page {page} of {totalPages}"
+    "page": "Page {page} of {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "notes": {
     "addNote": "Add Note",
@@ -179,7 +198,12 @@
     "noteUpdated": "Note updated",
     "noteCreated": "Note created",
     "titleRequired": "Title and content are required",
-    "saveError": "Failed to {action} note"
+    "saveError": "Failed to {action} note",
+    "openMenu": "Open menu",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "findings": {
     "sectionTitle": "Observations",
@@ -227,7 +251,12 @@
     "resolveError": "Failed to resolve observation",
     "showing": "Showing {start}-{end} of {total}",
     "perPage": "per page",
-    "page": "Page {page} of {totalPages}"
+    "page": "Page {page} of {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page",
+    "openMenu": "Open menu"
   },
   "observationsPage": {
     "searchPlaceholder": "Search description, notes, or vehicle...",
@@ -277,6 +306,7 @@
     "manual": "Manual",
     "cvt": "CVT",
     "engineSize": "Engine Size",
+    "engineCode": "Engine Code",
     "makeMarine": "Manufacturer *",
     "mileageMarine": "Engine Hours",
     "transmissionMarine": "Engine Type",
@@ -297,5 +327,11 @@
     "reasonPlaceholder": "e.g. Sold, totaled, decommissioned...",
     "archived": "{name} archived",
     "archiveError": "Failed to archive vehicle"
+  },
+  "serviceRecords": {
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   }
 }

+ 8 - 2
messages/en/workBoard.json

@@ -7,7 +7,11 @@
     "day": "Day",
     "week": "Week",
     "presenter": "Presenter",
-    "addTechnician": "Add Technician"
+    "addTechnician": "Add Technician",
+    "previousWeek": "Previous week",
+    "nextWeek": "Next week",
+    "previousDay": "Previous day",
+    "nextDay": "Next day"
   },
   "board": {
     "days": {
@@ -70,6 +74,8 @@
       "inProgress": "In Progress",
       "waitingParts": "Waiting Parts",
       "completed": "Completed"
-    }
+    },
+    "previous": "Previous",
+    "next": "Next"
   }
 }

+ 2 - 1
messages/en/workOrders.json

@@ -33,7 +33,8 @@
       "date": "Date",
       "total": "Total"
     },
-    "emailSubject": "Vehicle Status Update: {status}"
+    "emailSubject": "Vehicle Status Update: {status}",
+    "changeStatus": "Change status"
   },
   "vehiclePicker": {
     "title": "Select Vehicle for Work Order",

+ 19 - 4
messages/es/admin.json

@@ -1,9 +1,21 @@
 {
   "nav": {
-    "overview": { "title": "Resumen", "description": "Estadísticas y estado de la plataforma" },
-    "users": { "title": "Usuarios", "description": "Gestionar todos los usuarios" },
-    "organizations": { "title": "Organizaciones", "description": "Gestionar todas las organizaciones" },
-    "settings": { "title": "Configuración", "description": "Configuración de la plataforma" }
+    "overview": {
+      "title": "Resumen",
+      "description": "Estadísticas y estado de la plataforma"
+    },
+    "users": {
+      "title": "Usuarios",
+      "description": "Gestionar todos los usuarios"
+    },
+    "organizations": {
+      "title": "Organizaciones",
+      "description": "Gestionar todas las organizaciones"
+    },
+    "settings": {
+      "title": "Configuración",
+      "description": "Configuración de la plataforma"
+    }
   },
   "layout": {
     "title": "Panel de Administración",
@@ -104,5 +116,8 @@
     "postmarkApiKeyHint": "Obtenga su Server Token de postmarkapp.com",
     "mailgunApiKeyHint": "Obtenga su clave API de app.mailgun.com",
     "sendgridApiKeyHint": "Obtenga su clave API de app.sendgrid.com"
+  },
+  "common": {
+    "openMenu": "Open menu"
   }
 }

+ 2 - 1
messages/es/aiChat.json

@@ -14,5 +14,6 @@
   "chatsPrivate": "Tus chats son privados y solo visibles para ti.",
   "deleteChat": "Eliminar chat",
   "hideSidebar": "Ocultar panel",
-  "showSidebar": "Mostrar panel"
+  "showSidebar": "Mostrar panel",
+  "sendMessage": "Send message"
 }

+ 5 - 1
messages/es/audit.json

@@ -93,6 +93,10 @@
     "statusReport_create": "Informe de estado creado",
     "statusReport_send": "Informe de estado enviado",
     "statusReport_delete": "Informe de estado eliminado",
-    "telegram_send": "Mensaje de Telegram enviado"
+    "telegram_send": "Mensaje de Telegram enviado",
+    "webhook_create": "Webhook creado",
+    "webhook_update": "Webhook actualizado",
+    "webhook_delete": "Webhook eliminado",
+    "webhook_rotateSecret": "Secreto de webhook rotado"
   }
 }

+ 5 - 1
messages/es/billing.json

@@ -78,6 +78,10 @@
     "deleted": "Factura recurrente eliminada",
     "failedDelete": "Error al eliminar",
     "processed": "{count} factura(s) procesada(s)",
-    "failedProcess": "Error al procesar"
+    "failedProcess": "Error al procesar",
+    "pause": "Pause",
+    "resume": "Resume",
+    "delete": "Delete",
+    "removeRow": "Remove row"
   }
 }

+ 9 - 7
messages/es/calendar.json

@@ -3,10 +3,10 @@
     "sun": "Dom",
     "mon": "Lun",
     "tue": "Mar",
-    "wed": "Mi\u00e9",
+    "wed": "Mié",
     "thu": "Jue",
     "fri": "Vie",
-    "sat": "S\u00e1b"
+    "sat": "Sáb"
   },
   "months": {
     "january": "Enero",
@@ -35,14 +35,14 @@
     "reminders": "Recordatorios",
     "quotes": "Presupuestos"
   },
-  "selectVehicle": "Seleccionar veh\u00edculo para orden de trabajo",
+  "selectVehicle": "Seleccionar vehículo para orden de trabajo",
   "dateChoice": {
     "title": "Work Order Date",
     "description": "You have {selectedDate} selected. Which date should this work order be for?",
     "useSelected": "Use {date}",
     "useToday": "Use today"
   },
-  "more": "+ {count} m\u00e1s",
+  "more": "+ {count} más",
   "events": {
     "status": {
       "completed": "Completado",
@@ -54,7 +54,7 @@
       "draft": "Borrador",
       "done": "Hecho",
       "overdue": "Vencido",
-      "upcoming": "Pr\u00f3ximo"
+      "upcoming": "Próximo"
     },
     "type": {
       "service": "Servicio",
@@ -62,6 +62,8 @@
       "quote": "Presupuesto"
     },
     "noEvents": "Sin eventos",
-    "selectDay": "Seleccione un d\u00eda con eventos o cree una nueva orden de trabajo."
-  }
+    "selectDay": "Seleccione un día con eventos o cree una nueva orden de trabajo."
+  },
+  "previousMonth": "Previous month",
+  "nextMonth": "Next month"
 }

+ 2 - 1
messages/es/customers.json

@@ -21,7 +21,8 @@
     "batchDelete": "Eliminar ({count})",
     "batchDeleteTitle": "Eliminar Clientes",
     "batchDeleteDescription": "¿Eliminar {count} cliente(s)? Esto desvinculará sus vehículos.",
-    "batchDeleteSuccess": "{count} cliente(s) eliminado(s)"
+    "batchDeleteSuccess": "{count} cliente(s) eliminado(s)",
+    "openMenu": "Open menu"
   },
   "detail": {
     "backToCustomers": "Volver a clientes",

+ 10 - 5
messages/es/dashboard.json

@@ -18,7 +18,8 @@
     "quantity": "Cantidad",
     "addToJob": "Añadir {qty} al trabajo",
     "statusReport": "Informe de estado",
-    "report": "Informe"
+    "report": "Informe",
+    "observation": "Observación"
   },
   "recentActivity": "Actividad reciente",
   "noRecentActivity": "Sin actividad reciente",
@@ -64,7 +65,9 @@
     "dismissedTab": "Descartados",
     "noActive": "No hay vehículos que requieran servicio actualmente.",
     "dismissedOn": "Descartado el {date}",
-    "restore": "Restaurar"
+    "restore": "Restaurar",
+    "settingsAriaLabel": "Maintenance settings",
+    "dismissAriaLabel": "Dismiss"
   },
   "reminders": {
     "title": "Recordatorios pendientes",
@@ -95,7 +98,8 @@
     "pending": "Pendientes",
     "createQuote": "Crear presupuesto",
     "itemsRequested": "{count, plural, one {# elemento solicitado} other {# elementos solicitados}}",
-    "noData": "No hay solicitudes de presupuesto pendientes."
+    "noData": "No hay solicitudes de presupuesto pendientes.",
+    "dismissAriaLabel": "Dismiss quote request"
   },
   "quoteResponses": {
     "title": "Respuestas de presupuesto de clientes",
@@ -106,7 +110,8 @@
     "convert": "Convert",
     "viewQuote": "Ver presupuesto",
     "view": "View",
-    "noData": "No hay respuestas de clientes."
+    "noData": "No hay respuestas de clientes.",
+    "dismissAriaLabel": "Dismiss response"
   },
   "recentCompleted": {
     "title": "Completados recientemente",
@@ -133,4 +138,4 @@
     "days": "{count}d"
   },
   "error": "Error al cargar el panel de control"
-}
+}

+ 2 - 1
messages/es/inventory.json

@@ -118,5 +118,6 @@
     "cancel": "Cancelar",
     "saveChanges": "Guardar cambios",
     "addPart": "Añadir pieza"
-  }
+  },
+  "openMenu": "Open menu"
 }

+ 14 - 3
messages/es/laborPresets.json

@@ -15,7 +15,8 @@
   },
   "actions": {
     "edit": "Editar",
-    "delete": "Eliminar"
+    "delete": "Eliminar",
+    "openMenu": "Open menu"
   },
   "deletePreset": {
     "title": "Eliminar plantilla de mano de obra",
@@ -45,7 +46,16 @@
     "itemQty": "Qty",
     "itemQtyOrHours": "Unit / Hours",
     "hourlyTag": "HR",
-    "serviceTag": "SVC"
+    "serviceTag": "SVC",
+    "partsTitle": "Piezas",
+    "addPart": "Añadir pieza",
+    "partName": "Nombre de pieza",
+    "partNumber": "Pieza #",
+    "partQty": "Cant.",
+    "partPrice": "Precio unitario",
+    "importFromInventory": "Importar del inventario",
+    "searchInventory": "Buscar en inventario...",
+    "noPartsFound": "No se encontraron piezas"
   },
   "picker": {
     "title": "Seleccionar plantilla de mano de obra",
@@ -53,7 +63,8 @@
     "noPackages": "No se encontraron plantillas de mano de obra.",
     "items": "{count} elementos",
     "totalHours": "{hours}h en total",
-    "totalUnits": "{units} unit(s)"
+    "totalUnits": "{units} unit(s)",
+    "totalParts": "{count} piezas"
   },
   "errors": {
     "error": "Error",

+ 5 - 2
messages/es/messages.json

@@ -18,7 +18,9 @@
     "deleteTitle": "¿Eliminar conversación?",
     "deleteDescription": "Se eliminarán permanentemente todos los mensajes con {name}. Esta acción no se puede deshacer.",
     "deleted": "Conversación eliminada",
-    "deleteError": "No se pudo eliminar la conversación"
+    "deleteError": "No se pudo eliminar la conversación",
+    "back": "Back",
+    "conversationMenu": "Conversation menu"
   },
   "conversation": {
     "noPhone": "No hay número de teléfono registrado para {name}. Agregue un número de teléfono para comenzar a enviar mensajes.",
@@ -30,7 +32,8 @@
     "deleteDescription": "Este mensaje se eliminará permanentemente de la conversación. Esta acción no se puede deshacer.",
     "messageDeleted": "Mensaje eliminado",
     "deleteError": "No se pudo eliminar el mensaje",
-    "sendError": "No se pudo enviar el SMS"
+    "sendError": "No se pudo enviar el SMS",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "Enviar {label}",

+ 2 - 1
messages/es/notifications.json

@@ -6,5 +6,6 @@
     "confirmDelete": "¿Estás seguro?",
     "delete": "Eliminar",
     "cancel": "Cancelar"
-  }
+  },
+  "openNotifications": "Open notifications"
 }

+ 7 - 0
messages/es/pdf.json

@@ -40,7 +40,14 @@
     "paymentInformation": "Información de pago",
     "orgNumberLabel": "Núm. de org.",
     "paymentTermsLabel": "Condiciones de pago",
+    "netDays": "{days} días",
     "dueDateLabel": "Fecha de vencimiento",
+    "warrantyTitle": "Garantía",
+    "warrantyDuration": "Duración",
+    "warrantyMileage": "Límite de kilometraje",
+    "warrantyExpires": "Vence",
+    "warrantyNotes": "Términos",
+    "warrantyMonthsUnit": "meses",
     "diagnosticNotes": "Notas de diagnóstico",
     "findings": "Observaciones",
     "findingsDescription": "Los siguientes elementos fueron observados durante este servicio y pueden requerir atención.",

+ 5 - 0
messages/es/portal.json

@@ -7,6 +7,11 @@
     "noResults": "Ningún taller coincide con tu búsqueda.",
     "openPortal": "Abrir portal"
   },
+  "directLink": {
+    "title": "Portal de clientes",
+    "description": "Utiliza el enlace directo proporcionado por tu taller para acceder al portal de clientes.",
+    "hint": "Tu taller compartirá el enlace del portal contigo por correo electrónico o SMS."
+  },
   "landing": {
     "contactTitle": "Contáctanos",
     "aboutTitle": "Sobre nosotros",

+ 15 - 4
messages/es/quotes.json

@@ -70,7 +70,9 @@
     "qty": "Cant.",
     "unitPrice": "Precio unitario",
     "total": "Total",
-    "subtotal": "Subtotal de repuestos: {amount}"
+    "subtotal": "Subtotal de repuestos: {amount}",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "labor": {
     "title": "Mano de obra",
@@ -90,7 +92,9 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours 00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "notes": {
     "title": "Notas",
@@ -127,7 +131,9 @@
     "customer": "Cliente",
     "selectVehicle": "Seleccionar vehículo...",
     "selectCustomer": "Seleccionar cliente...",
-    "none": "Ninguno"
+    "none": "Ninguno",
+    "clearVehicle": "Clear vehicle",
+    "clearCustomer": "Clear customer"
   },
   "totals": {
     "title": "Totales",
@@ -158,7 +164,8 @@
     "emailSent": "Correo enviado",
     "failedEmail": "Error al enviar el correo",
     "smsSent": "SMS enviado",
-    "failedSms": "Error al enviar el SMS"
+    "failedSms": "Error al enviar el SMS",
+    "copyLink": "Copy link"
   },
   "images": {
     "title": "Imágenes del presupuesto",
@@ -173,6 +180,8 @@
     "uploadingCount": "Subiendo {count} imagen(es)...",
     "uploadedCount": "{count} imagen(es) subida(s)",
     "uploadFailed": "Error al subir",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Imagen eliminada",
     "failedDelete": "Error al eliminar la imagen",
     "failedUpdate": "Error al actualizar",
@@ -191,6 +200,8 @@
     "uploadingCount": "Subiendo {count} archivo(s)...",
     "uploadedCount": "{count} archivo(s) subido(s)",
     "uploadFailed": "Error al subir",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Archivo eliminado",
     "failedDelete": "Error al eliminar el archivo",
     "failedUpdate": "Error al actualizar",

+ 2 - 1
messages/es/reminders.json

@@ -7,5 +7,6 @@
   "selectVehicle": "Seleccionar vehículo...",
   "searchVehicle": "Buscar vehículos...",
   "noVehicle": "No se encontraron vehículos.",
-  "pickDate": "Seleccionar fecha"
+  "pickDate": "Seleccionar fecha",
+  "openMenu": "Open menu"
 }

+ 5 - 1
messages/es/reports.json

@@ -406,5 +406,9 @@
     "date": "Fecha",
     "title": "Título"
   },
-  "empty": "Aún no se han cargado datos. Haga clic en Actualizar para cargar el informe."
+  "empty": "Aún no se han cargado datos. Haga clic en Actualizar para cargar el informe.",
+  "pagination": {
+    "previousPage": "Previous page",
+    "nextPage": "Next page"
+  }
 }

+ 36 - 4
messages/es/service.json

@@ -95,7 +95,8 @@
     "scanBarcode": "Escanear",
     "scanTitle": "Escanear código de barras",
     "partFound": "\"{name}\" añadido del inventario",
-    "partNotFound": "No se encontró pieza para el código de barras \"{barcode}\""
+    "partNotFound": "No se encontró pieza para el código de barras \"{barcode}\"",
+    "deleteRow": "Delete row"
   },
   "labor": {
     "title": "Mano de Obra",
@@ -118,7 +119,8 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours 00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row"
   },
   "totals": {
     "title": "Totales",
@@ -209,7 +211,9 @@
     "images": "Imágenes ({count})",
     "videos": "Videos ({count})",
     "diagnostics": "Reportes de Diagnóstico ({count})",
-    "documents": "Documentos ({count})"
+    "documents": "Documentos ({count})",
+    "download": "Download",
+    "close": "Close"
   },
   "share": {
     "title": "Compartir Factura",
@@ -225,7 +229,8 @@
     "emailSent": "Correo electrónico enviado",
     "failedEmail": "Error al enviar correo electrónico",
     "smsSent": "SMS enviado",
-    "failedSms": "Error al enviar SMS"
+    "failedSms": "Error al enviar SMS",
+    "copyLink": "Copy link"
   },
   "sidebar": {
     "vehicle": "Vehículo",
@@ -333,5 +338,32 @@
     "minutesAgo": "hace {count}m",
     "hoursAgo": "hace {count}h",
     "daysAgo": "hace {count}d"
+  },
+  "table": {
+    "warranty": "Garantía"
+  },
+  "warranty": {
+    "title": "Garantía",
+    "months": "Duración (meses)",
+    "mileage": "Límite de kilometraje",
+    "notes": "Términos de garantía",
+    "notesPlaceholder": "ej. Garantía de 12 meses en todas las piezas y mano de obra...",
+    "expiresAt": "Vence",
+    "presets": {
+      "3months": "3 m",
+      "6months": "6 m",
+      "12months": "12 m",
+      "24months": "24 m"
+    },
+    "status": {
+      "active": "Activa",
+      "expiring": "Por vencer",
+      "expired": "Vencida",
+      "none": "N/A"
+    },
+    "defaults": {
+      "title": "Configuración de garantía predeterminada",
+      "description": "Establecer términos de garantía predeterminados para nuevos registros de servicio"
+    }
   }
 }

+ 148 - 6
messages/es/settings.json

@@ -7,6 +7,7 @@
       "billing": "Facturacion y documentos",
       "communications": "Comunicaciones",
       "workshop": "Taller",
+      "integrations": "Integraciones",
       "system": "Sistema"
     },
     "items": {
@@ -90,6 +91,10 @@
         "title": "Informes programados",
         "description": "Envío automático de informes"
       },
+      "webhooks": {
+        "title": "Webhooks",
+        "description": "Notificaciones de eventos en tiempo real"
+      },
       "appearance": {
         "title": "Apariencia",
         "description": "Tema y visualizacion"
@@ -353,7 +358,10 @@
     "memberRemoved": "Miembro eliminado",
     "failedRemoveMember": "Error al eliminar el miembro",
     "memberRoleUpdated": "Rol del miembro actualizado",
-    "failedUpdateRole": "Error al actualizar el rol"
+    "failedUpdateRole": "Error al actualizar el rol",
+    "removeMember": "Remove member",
+    "cancelInvite": "Cancel invitation",
+    "editRole": "Edit role"
   },
   "invoice": {
     "title": "Diseno de factura",
@@ -662,10 +670,37 @@
       "PHP": "Peso filipino",
       "IDR": "Rupia indonesia",
       "MYR": "Ringgit malayo",
+      "LKR": "Rupia de Sri Lanka",
       "AED": "Dirham de los EAU",
       "SAR": "Riyal saudi",
-      "RUB": "Rublo ruso"
-    }
+      "RUB": "Rublo ruso",
+      "TWD": "Dolar taiwanes",
+      "VND": "Dong vietnamita",
+      "PKR": "Rupia pakistani",
+      "BDT": "Taka de Bangladesh",
+      "NPR": "Rupia nepali",
+      "BGN": "Lev bulgaro",
+      "UAH": "Grivna ucraniana",
+      "CLP": "Peso chileno",
+      "COP": "Peso colombiano",
+      "ARS": "Peso argentino",
+      "PEN": "Sol peruano",
+      "EGP": "Libra egipcia",
+      "NGN": "Naira nigeriano",
+      "KES": "Chelin keniano",
+      "MAD": "Dirham marroqui",
+      "GHS": "Cedi ghanes",
+      "QAR": "Riyal qatari",
+      "KWD": "Dinar kuwaiti",
+      "BHD": "Dinar bahreini",
+      "OMR": "Rial omani",
+      "JOD": "Dinar jordano"
+    },
+    "formatTitle": "Formato de moneda",
+    "formatSymbolName": "Simbolo",
+    "formatCodeName": "Codigo",
+    "formatSymbolLabel": "Simbolo: {value}",
+    "formatCodeLabel": "Codigo: {value}"
   },
   "localization": {
     "languageTitle": "Idioma",
@@ -718,7 +753,9 @@
     "fieldUpdated": "Campo personalizado actualizado",
     "fieldDeleted": "Campo personalizado eliminado",
     "failedSave": "No se pudo guardar el campo",
-    "failedDelete": "No se pudo eliminar"
+    "failedDelete": "No se pudo eliminar",
+    "edit": "Edit",
+    "delete": "Delete"
   },
   "workshop": {
     "title": "Valores predeterminados del taller",
@@ -948,7 +985,8 @@
         "slate": "Pizarra",
         "sand": "Arena"
       }
-    }
+    },
+    "copyUrl": "Copy portal URL"
   },
   "email": {
     "title": "Proveedor de correo",
@@ -1023,7 +1061,8 @@
     "saved": "Configuracion SMS guardada",
     "failedSave": "Error al guardar la configuracion",
     "testSentTo": "SMS de prueba enviado a {phone}",
-    "testFailed": "Prueba de SMS fallida"
+    "testFailed": "Prueba de SMS fallida",
+    "copyWebhookUrl": "Copy webhook URL"
   },
   "subscription": {
     "title": "Plan de suscripción",
@@ -1074,6 +1113,7 @@
     "resumeSuccess": "Suscripción reanudada con éxito",
     "resumeError": "Error al reanudar la suscripción",
     "billingPortalError": "Error al abrir el portal de facturación",
+    "checkoutError": "Failed to start checkout",
     "upgradeTitle": "Mejorar plan",
     "upgradeDescription": "Desbloquee más funciones mejorando su plan.",
     "upgradeToProDescription": "Mejore para desbloquear clientes ilimitados, miembros del equipo, informes, SMTP, acceso API y más.",
@@ -1165,6 +1205,10 @@
     "deleted": "Programa de informes eliminado",
     "toggledOn": "Programa de informes activado",
     "toggledOff": "Programa de informes desactivado",
+    "failedSave": "Failed to save",
+    "failedSend": "Failed to send",
+    "failedToggle": "Failed to toggle",
+    "failedDelete": "Failed to delete",
     "noMembers": "No se encontraron miembros del equipo",
     "addRecipient": "Agregar destinatario...",
     "searchMembers": "Buscar miembros...",
@@ -1189,5 +1233,103 @@
       "retention": "Retención de clientes",
       "inventory": "Inventario"
     }
+  },
+  "webhooks": {
+    "title": "Webhooks",
+    "subtitle": "Envía notificaciones de eventos en tiempo real a tus propios sistemas mediante HTTPS. Cada evento se firma con HMAC-SHA256 para que puedas verificar su autenticidad.",
+    "create": "Añadir webhook",
+    "createTitle": "Añadir webhook",
+    "editTitle": "Editar webhook",
+    "createDescription": "Los webhooks se envían como JSON mediante POST. Las entregas fallidas se reintentan con retroceso exponencial hasta 5 veces.",
+    "name": "Nombre",
+    "namePlaceholder": "p. ej. Sincronización contable",
+    "url": "URL del endpoint",
+    "urlHint": "Debe empezar con http:// o https://. Recomendamos encarecidamente HTTPS.",
+    "description": "Descripción (opcional)",
+    "events": "Eventos",
+    "eventsHint": "Elige qué eventos activan este webhook.",
+    "subscribeToAll": "Todos los eventos",
+    "selectAtLeastOneEvent": "Selecciona al menos un evento",
+    "groups": {
+      "customers": "Clientes",
+      "vehicles": "Vehículos",
+      "services": "Órdenes de servicio",
+      "quotes": "Presupuestos",
+      "payments": "Pagos",
+      "inspections": "Inspecciones",
+      "inventory": "Inventario",
+      "findings": "Hallazgos"
+    },
+    "save": "Guardar",
+    "cancel": "Cancelar",
+    "delete": "Eliminar",
+    "rotate": "Rotar",
+    "done": "Listo",
+    "paused": "Pausado",
+    "statusOk": "Saludable",
+    "statusFailing": "{count, plural, one {# fallo reciente} other {# fallos recientes}}",
+    "lastDelivery": "Última entrega {when}",
+    "totalDeliveries": "{count, plural, one {# entrega} other {# entregas}}",
+    "toggleAria": "Activar o desactivar este webhook",
+    "deliveries": "Entregas",
+    "sendTest": "Enviar prueba",
+    "testSent": "Evento de prueba enviado",
+    "testFailed": "No se pudo enviar el evento de prueba",
+    "rotateSecret": "Rotar clave de firma",
+    "confirmRotateTitle": "¿Rotar clave de firma?",
+    "confirmRotateDescription": "La clave actual dejará de funcionar inmediatamente. Actualiza tu endpoint con la nueva clave.",
+    "secretRotated": "Clave rotada",
+    "secretRotatedDescription": "Guarda esta clave ahora — no podrá recuperarse después.",
+    "confirmDeleteTitle": "¿Eliminar webhook?",
+    "confirmDeleteDescription": "«{name}» dejará de recibir eventos inmediatamente. Esto no se puede deshacer.",
+    "secretShownOnceTitle": "Webhook «{name}» creado",
+    "secretShownOnceDescription": "Copia esta clave de firma ahora. No volverá a mostrarse.",
+    "secretCopyHint": "Usa esta clave para verificar el encabezado X-Torqvoice-Signature de cada solicitud.",
+    "deliveriesTitle": "Entregas — {name}",
+    "deliveriesDescription": "Últimos 50 intentos. Los eventos fallidos se reintentan automáticamente con retroceso exponencial.",
+    "deliveriesEmpty": "Aún no hay entregas. Envía un evento de prueba para probarlo.",
+    "attempt": "Intento {n}/{max}",
+    "nextRetry": "Próximo reintento {when}",
+    "retryQueued": "Reintento en cola",
+    "retryFailed": "Reintento fallido",
+    "saveFailed": "Error al guardar",
+    "deleteFailed": "Error al eliminar",
+    "toggleFailed": "Error al alternar",
+    "rotateFailed": "Error al rotar",
+    "created": "Webhook creado",
+    "updated": "Webhook actualizado",
+    "deleted": "Webhook eliminado",
+    "emptyTitle": "Aún no hay webhooks",
+    "emptyDescription": "Añade un webhook para enviar eventos a tus propios sistemas — sincronización contable, paneles personalizados, Zapier, cualquier cosa que hable HTTP.",
+    "helpTitle": "Verificar firmas de webhook",
+    "helpIntro": "Cada entrega incluye una firma HMAC-SHA256 para que puedas confirmar que la solicitud proviene de Torqvoice.",
+    "helpHeadersTitle": "Encabezados enviados con cada solicitud",
+    "helpVerify": "Reconstruye la cadena firmada como `[timestamp].[body]` y compara con igualdad de tiempo seguro:",
+    "tabWebhooks": "Webhooks",
+    "tabReference": "Referencia de eventos",
+    "autoDisabled": "Pausado automáticamente",
+    "autoDisabledBanner": "Este webhook se pausó automáticamente tras 20 fallos consecutivos. Reactívalo para reanudar las entregas.",
+    "reEnable": "Reactivar",
+    "resumed": "Webhook reanudado",
+    "copyUrl": "Copiar URL",
+    "refresh": "Actualizar",
+    "noMatches": "Ninguna entrega coincide con este filtro.",
+    "retry": "Reintentar",
+    "viewPayload": "Ver payload",
+    "payloadCopied": "Payload copiado",
+    "loadingPayload": "Cargando payload…",
+    "requestPayload": "Payload de la solicitud",
+    "response": "Respuesta",
+    "close": "Cerrar",
+    "samplePayload": "Envoltorio de ejemplo",
+    "referenceTitle": "Referencia de eventos",
+    "referenceIntro": "Cada entrega sigue esta estructura de envoltorio. Haz clic en un evento para ver un payload de ejemplo que recibirá tu endpoint.",
+    "filter": {
+      "all": "Todas",
+      "success": "Éxito",
+      "failed": "Fallida",
+      "retrying": "Reintentando",
+      "pending": "Pendiente"
+    }
   }
 }

+ 6 - 0
messages/es/share.json

@@ -52,12 +52,18 @@
     "bankAccount": "Cuenta bancaria",
     "orgNumber": "N.º de organización",
     "paymentTerms": "Condiciones de pago",
+    "netDays": "{days} días",
     "dueDate": "Fecha de vencimiento",
     "serviceImages": "Imágenes del servicio ({count})",
     "serviceVideos": "Videos del servicio ({count})",
     "diagnosticReports": "Informes de diagnóstico ({count})",
     "notes": "Notas",
     "diagnosticNotes": "Notas de diagnóstico",
+    "warrantyTitle": "Garantía",
+    "warrantyExpires": "Vence",
+    "warrantyMonth": "mes",
+    "warrantyMonths": "meses",
+    "warrantyKm": "km",
     "findings": "Observaciones",
     "findingsDescription": "Los siguientes elementos fueron observados durante este servicio y pueden requerir atención.",
     "severity": "Gravedad",

+ 1 - 0
messages/es/statusReport.json

@@ -14,6 +14,7 @@
     "createAndSend": "Crear y enviar",
     "cancel": "Cancelar",
     "uploadFailed": "Error al subir el vídeo",
+    "createReportFailed": "Failed to create status report",
     "created": "Informe de estado creado",
     "noVideo": "Por favor, suba un vídeo primero",
     "removeVideo": "Eliminar vídeo",

+ 5 - 2
messages/es/telegram.json

@@ -4,7 +4,9 @@
   "botToken": {
     "label": "Token del bot",
     "placeholder": "Ingresa el token de @BotFather",
-    "help": "Crea un bot a través de @BotFather en Telegram para obtener tu token."
+    "help": "Crea un bot a través de @BotFather en Telegram para obtener tu token.",
+    "show": "Show token",
+    "hide": "Hide token"
   },
   "botUsername": {
     "label": "Nombre de usuario del bot",
@@ -17,7 +19,8 @@
   "deepLink": {
     "title": "Enlace de conexión del cliente",
     "description": "Comparte este enlace con los clientes para que se conecten a tu bot. Reemplaza el marcador de ID del cliente con el ID real.",
-    "copied": "¡Enlace copiado!"
+    "copied": "¡Enlace copiado!",
+    "copy": "Copy deep link"
   },
   "test": {
     "title": "Mensaje de prueba",

+ 2 - 1
messages/es/telegramMessages.json

@@ -30,7 +30,8 @@
     "deleteDescription": "Esto eliminará permanentemente este mensaje. Esta acción no se puede deshacer.",
     "messageDeleted": "Mensaje eliminado",
     "deleteError": "No se pudo eliminar el mensaje",
-    "sendError": "No se pudo enviar el mensaje"
+    "sendError": "No se pudo enviar el mensaje",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "Enviar {label}",

+ 42 - 6
messages/es/vehicles.json

@@ -26,7 +26,11 @@
     "vehicleUnarchived": "Vehiculo desarchivado",
     "unarchiveError": "Error al desarchivar vehiculo",
     "deleteError": "Error al eliminar vehiculo",
-    "error": "Error al cargar vehiculos"
+    "error": "Error al cargar vehiculos",
+    "viewTable": "Table view",
+    "viewGrid": "Grid view",
+    "viewLargeGrid": "Large grid view",
+    "openMenu": "Open menu"
   },
   "detail": {
     "vehicleArchived": "Este vehiculo esta archivado",
@@ -84,7 +88,9 @@
     "severityCommon": "Común — problema reportado frecuentemente",
     "severityModerate": "Moderado — reportado por algunos propietarios",
     "severityUncommon": "Poco común — reportado por pocos propietarios",
-    "severityRare": "Raro — reportes aislados pero crítico cuando ocurre"
+    "severityRare": "Raro — reportes aislados pero crítico cuando ocurre",
+    "openMenu": "Open menu",
+    "dismissMaintenance": "Dismiss"
   },
   "quotes": {
     "newQuote": "Nueva cotización",
@@ -148,13 +154,26 @@
       "mileageMarine": "Horas de Motor",
       "technician": "Tecnico",
       "files": "Archivos",
-      "total": "Total"
+      "total": "Total",
+      "warranty": "Garantia"
+    },
+    "warranty": {
+      "status": {
+        "active": "Activa",
+        "expiring": "Por vencer",
+        "expired": "Vencida",
+        "none": "N/A"
+      }
     },
     "emptyFiltered": "Ningun registro de servicio coincide con sus filtros.",
     "empty": "Aun no hay registros de servicio.",
     "showing": "Mostrando {start}-{end} de {total}",
     "perPage": "por pagina",
-    "page": "Pagina {page} de {totalPages}"
+    "page": "Pagina {page} de {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "notes": {
     "addNote": "Agregar nota",
@@ -179,7 +198,12 @@
     "noteUpdated": "Nota actualizada",
     "noteCreated": "Nota creada",
     "titleRequired": "El titulo y el contenido son obligatorios",
-    "saveError": "Error al {action} nota"
+    "saveError": "Error al {action} nota",
+    "openMenu": "Open menu",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "findings": {
     "sectionTitle": "Observaciones",
@@ -227,7 +251,12 @@
     "resolveError": "Error al resolver observación",
     "showing": "Mostrando {start}-{end} de {total}",
     "perPage": "por página",
-    "page": "Página {page} de {totalPages}"
+    "page": "Página {page} de {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page",
+    "openMenu": "Open menu"
   },
   "observationsPage": {
     "searchPlaceholder": "Buscar descripción, notas o vehículo...",
@@ -277,6 +306,7 @@
     "manual": "Manual",
     "cvt": "CVT",
     "engineSize": "Tamano del motor",
+    "engineCode": "Código del motor",
     "makeMarine": "Fabricante *",
     "mileageMarine": "Horas de Motor",
     "transmissionMarine": "Tipo de Motor",
@@ -297,5 +327,11 @@
     "reasonPlaceholder": "ej. Vendido, siniestrado, dado de baja...",
     "archived": "{name} archivado",
     "archiveError": "Error al archivar vehiculo"
+  },
+  "serviceRecords": {
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   }
 }

+ 8 - 2
messages/es/workBoard.json

@@ -7,7 +7,11 @@
     "day": "Día",
     "week": "Semana",
     "presenter": "Presentador",
-    "addTechnician": "Agregar Técnico"
+    "addTechnician": "Agregar Técnico",
+    "previousWeek": "Previous week",
+    "nextWeek": "Next week",
+    "previousDay": "Previous day",
+    "nextDay": "Next day"
   },
   "board": {
     "days": {
@@ -70,6 +74,8 @@
       "inProgress": "En Progreso",
       "waitingParts": "Esperando Piezas",
       "completed": "Completado"
-    }
+    },
+    "previous": "Previous",
+    "next": "Next"
   }
 }

+ 2 - 1
messages/es/workOrders.json

@@ -33,7 +33,8 @@
       "date": "Fecha",
       "total": "Total"
     },
-    "emailSubject": "Actualización de Estado del Vehículo: {status}"
+    "emailSubject": "Actualización de Estado del Vehículo: {status}",
+    "changeStatus": "Change status"
   },
   "vehiclePicker": {
     "title": "Seleccionar Vehículo para Orden de Trabajo",

+ 19 - 4
messages/fr/admin.json

@@ -1,9 +1,21 @@
 {
   "nav": {
-    "overview": { "title": "Vue d'ensemble", "description": "Statistiques et état de la plateforme" },
-    "users": { "title": "Utilisateurs", "description": "Gérer tous les utilisateurs" },
-    "organizations": { "title": "Organisations", "description": "Gérer toutes les organisations" },
-    "settings": { "title": "Paramètres", "description": "Configuration de la plateforme" }
+    "overview": {
+      "title": "Vue d'ensemble",
+      "description": "Statistiques et état de la plateforme"
+    },
+    "users": {
+      "title": "Utilisateurs",
+      "description": "Gérer tous les utilisateurs"
+    },
+    "organizations": {
+      "title": "Organisations",
+      "description": "Gérer toutes les organisations"
+    },
+    "settings": {
+      "title": "Paramètres",
+      "description": "Configuration de la plateforme"
+    }
   },
   "layout": {
     "title": "Panneau d'administration",
@@ -104,5 +116,8 @@
     "postmarkApiKeyHint": "Obtenez votre Server Token sur postmarkapp.com",
     "mailgunApiKeyHint": "Obtenez votre clé API sur app.mailgun.com",
     "sendgridApiKeyHint": "Obtenez votre clé API sur app.sendgrid.com"
+  },
+  "common": {
+    "openMenu": "Open menu"
   }
 }

+ 2 - 1
messages/fr/aiChat.json

@@ -14,5 +14,6 @@
   "chatsPrivate": "Vos conversations sont privées et visibles uniquement par vous.",
   "deleteChat": "Supprimer le chat",
   "hideSidebar": "Masquer le panneau",
-  "showSidebar": "Afficher le panneau"
+  "showSidebar": "Afficher le panneau",
+  "sendMessage": "Send message"
 }

+ 5 - 1
messages/fr/audit.json

@@ -93,6 +93,10 @@
     "statusReport_create": "Rapport d'état créé",
     "statusReport_send": "Rapport d'état envoyé",
     "statusReport_delete": "Rapport d'état supprimé",
-    "telegram_send": "Message Telegram envoyé"
+    "telegram_send": "Message Telegram envoyé",
+    "webhook_create": "Webhook créé",
+    "webhook_update": "Webhook mis à jour",
+    "webhook_delete": "Webhook supprimé",
+    "webhook_rotateSecret": "Secret du webhook renouvelé"
   }
 }

+ 5 - 1
messages/fr/billing.json

@@ -78,6 +78,10 @@
     "deleted": "Facture récurrente supprimée",
     "failedDelete": "Échec de la suppression",
     "processed": "{count} facture(s) traitée(s)",
-    "failedProcess": "Échec du traitement"
+    "failedProcess": "Échec du traitement",
+    "pause": "Pause",
+    "resume": "Resume",
+    "delete": "Delete",
+    "removeRow": "Remove row"
   }
 }

+ 16 - 14
messages/fr/calendar.json

@@ -10,32 +10,32 @@
   },
   "months": {
     "january": "Janvier",
-    "february": "F\u00e9vrier",
+    "february": "Février",
     "march": "Mars",
     "april": "Avril",
     "may": "Mai",
     "june": "Juin",
     "july": "Juillet",
-    "august": "Ao\u00fbt",
+    "august": "Août",
     "september": "Septembre",
     "october": "Octobre",
     "november": "Novembre",
-    "december": "D\u00e9cembre"
+    "december": "Décembre"
   },
   "stats": {
     "services": "Services",
     "overdue": "En retard",
     "pendingQuotes": "Devis en attente",
-    "revenue": "Chiffre d\u2019affaires"
+    "revenue": "Chiffre d’affaires"
   },
-  "today": "Aujourd\u2019hui",
+  "today": "Aujourd’hui",
   "workOrder": "Ordre de travail",
   "filters": {
     "services": "Services",
     "reminders": "Rappels",
     "quotes": "Devis"
   },
-  "selectVehicle": "S\u00e9lectionner un v\u00e9hicule pour l\u2019ordre de travail",
+  "selectVehicle": "Sélectionner un véhicule pour l’ordre de travail",
   "dateChoice": {
     "title": "Work Order Date",
     "description": "You have {selectedDate} selected. Which date should this work order be for?",
@@ -45,23 +45,25 @@
   "more": "+ {count} de plus",
   "events": {
     "status": {
-      "completed": "Termin\u00e9",
+      "completed": "Terminé",
       "inProgress": "En cours",
-      "waitingParts": "En attente de pi\u00e8ces",
+      "waitingParts": "En attente de pièces",
       "pending": "En attente",
-      "sent": "Envoy\u00e9",
-      "approved": "Approuv\u00e9",
+      "sent": "Envoyé",
+      "approved": "Approuvé",
       "draft": "Brouillon",
       "done": "Fait",
       "overdue": "En retard",
-      "upcoming": "\u00c0 venir"
+      "upcoming": "À venir"
     },
     "type": {
       "service": "Service",
       "reminder": "Rappel",
       "quote": "Devis"
     },
-    "noEvents": "Aucun \u00e9v\u00e9nement",
-    "selectDay": "S\u00e9lectionnez un jour avec des \u00e9v\u00e9nements ou cr\u00e9ez un nouvel ordre de travail."
-  }
+    "noEvents": "Aucun événement",
+    "selectDay": "Sélectionnez un jour avec des événements ou créez un nouvel ordre de travail."
+  },
+  "previousMonth": "Previous month",
+  "nextMonth": "Next month"
 }

+ 2 - 1
messages/fr/customers.json

@@ -21,7 +21,8 @@
     "batchDelete": "Supprimer ({count})",
     "batchDeleteTitle": "Supprimer les clients",
     "batchDeleteDescription": "Supprimer {count} client(s) ? Cela dissociera leurs véhicules.",
-    "batchDeleteSuccess": "{count} client(s) supprimé(s)"
+    "batchDeleteSuccess": "{count} client(s) supprimé(s)",
+    "openMenu": "Open menu"
   },
   "detail": {
     "backToCustomers": "Retour aux clients",

+ 10 - 5
messages/fr/dashboard.json

@@ -18,7 +18,8 @@
     "quantity": "Quantité",
     "addToJob": "Ajouter {qty} au travail",
     "statusReport": "Rapport d'état",
-    "report": "Rapport"
+    "report": "Rapport",
+    "observation": "Observation"
   },
   "recentActivity": "Activité récente",
   "noRecentActivity": "Aucune activité récente",
@@ -64,7 +65,9 @@
     "dismissedTab": "Ignorés",
     "noActive": "Aucun véhicule n'a besoin d'entretien actuellement.",
     "dismissedOn": "Ignoré le {date}",
-    "restore": "Restaurer"
+    "restore": "Restaurer",
+    "settingsAriaLabel": "Maintenance settings",
+    "dismissAriaLabel": "Dismiss"
   },
   "reminders": {
     "title": "Rappels à venir",
@@ -95,7 +98,8 @@
     "pending": "En attente",
     "createQuote": "Créer un devis",
     "itemsRequested": "{count, plural, one {# élément demandé} other {# éléments demandés}}",
-    "noData": "Aucune demande de devis en attente."
+    "noData": "Aucune demande de devis en attente.",
+    "dismissAriaLabel": "Dismiss quote request"
   },
   "quoteResponses": {
     "title": "Réponses des clients aux devis",
@@ -106,7 +110,8 @@
     "convert": "Convert",
     "viewQuote": "Voir le devis",
     "view": "View",
-    "noData": "Aucune réponse client."
+    "noData": "Aucune réponse client.",
+    "dismissAriaLabel": "Dismiss response"
   },
   "recentCompleted": {
     "title": "Terminés récemment",
@@ -133,4 +138,4 @@
     "days": "{count} j"
   },
   "error": "Échec du chargement du tableau de bord"
-}
+}

+ 2 - 1
messages/fr/inventory.json

@@ -118,5 +118,6 @@
     "cancel": "Annuler",
     "saveChanges": "Enregistrer les modifications",
     "addPart": "Ajouter une pièce"
-  }
+  },
+  "openMenu": "Open menu"
 }

+ 14 - 3
messages/fr/laborPresets.json

@@ -15,7 +15,8 @@
   },
   "actions": {
     "edit": "Modifier",
-    "delete": "Supprimer"
+    "delete": "Supprimer",
+    "openMenu": "Open menu"
   },
   "deletePreset": {
     "title": "Supprimer le modèle de main-d'œuvre",
@@ -45,7 +46,16 @@
     "itemQty": "Qty",
     "itemQtyOrHours": "Unit / Hours",
     "hourlyTag": "HR",
-    "serviceTag": "SVC"
+    "serviceTag": "SVC",
+    "partsTitle": "Pièces",
+    "addPart": "Ajouter une pièce",
+    "partName": "Nom de la pièce",
+    "partNumber": "Pièce #",
+    "partQty": "Qté",
+    "partPrice": "Prix unitaire",
+    "importFromInventory": "Importer de l'inventaire",
+    "searchInventory": "Rechercher dans l'inventaire...",
+    "noPartsFound": "Aucune pièce trouvée"
   },
   "picker": {
     "title": "Sélectionner un modèle de main-d'œuvre",
@@ -53,7 +63,8 @@
     "noPackages": "Aucun modèle de main-d'œuvre trouvé.",
     "items": "{count} éléments",
     "totalHours": "{hours}h au total",
-    "totalUnits": "{units} unit(s)"
+    "totalUnits": "{units} unit(s)",
+    "totalParts": "{count} pièces"
   },
   "errors": {
     "error": "Erreur",

+ 5 - 2
messages/fr/messages.json

@@ -18,7 +18,9 @@
     "deleteTitle": "Supprimer la conversation ?",
     "deleteDescription": "Tous les messages avec {name} seront définitivement supprimés. Cette action est irréversible.",
     "deleted": "Conversation supprimée",
-    "deleteError": "Impossible de supprimer la conversation"
+    "deleteError": "Impossible de supprimer la conversation",
+    "back": "Back",
+    "conversationMenu": "Conversation menu"
   },
   "conversation": {
     "noPhone": "Aucun numéro de téléphone enregistré pour {name}. Ajoutez un numéro de téléphone pour commencer à envoyer des messages.",
@@ -30,7 +32,8 @@
     "deleteDescription": "Ce message sera définitivement supprimé de la conversation. Cette action est irréversible.",
     "messageDeleted": "Message supprimé",
     "deleteError": "Impossible de supprimer le message",
-    "sendError": "Impossible d'envoyer le SMS"
+    "sendError": "Impossible d'envoyer le SMS",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "Envoyer {label}",

+ 2 - 1
messages/fr/notifications.json

@@ -6,5 +6,6 @@
     "confirmDelete": "Êtes-vous sûr ?",
     "delete": "Supprimer",
     "cancel": "Annuler"
-  }
+  },
+  "openNotifications": "Open notifications"
 }

+ 7 - 0
messages/fr/pdf.json

@@ -40,7 +40,14 @@
     "paymentInformation": "Informations de paiement",
     "orgNumberLabel": "N° d'org.",
     "paymentTermsLabel": "Conditions de paiement",
+    "netDays": "{days} jours",
     "dueDateLabel": "Date d'échéance",
+    "warrantyTitle": "Garantie",
+    "warrantyDuration": "Durée",
+    "warrantyMileage": "Limite kilométrage",
+    "warrantyExpires": "Expire",
+    "warrantyNotes": "Conditions",
+    "warrantyMonthsUnit": "mois",
     "diagnosticNotes": "Notes de diagnostic",
     "findings": "Observations",
     "findingsDescription": "Les éléments suivants ont été observés lors de ce service et peuvent nécessiter une attention particulière.",

+ 5 - 0
messages/fr/portal.json

@@ -7,6 +7,11 @@
     "noResults": "Aucun garage ne correspond à votre recherche.",
     "openPortal": "Ouvrir le portail"
   },
+  "directLink": {
+    "title": "Portail client",
+    "description": "Veuillez utiliser le lien direct fourni par votre garage pour accéder au portail client.",
+    "hint": "Votre garage vous communiquera le lien du portail par e-mail ou SMS."
+  },
   "landing": {
     "contactTitle": "Nous contacter",
     "aboutTitle": "À propos",

+ 15 - 4
messages/fr/quotes.json

@@ -70,7 +70,9 @@
     "qty": "Qté",
     "unitPrice": "Prix unitaire",
     "total": "Total",
-    "subtotal": "Sous-total pièces : {amount}"
+    "subtotal": "Sous-total pièces : {amount}",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "labor": {
     "title": "Main-d'œuvre",
@@ -90,7 +92,9 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours 00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row",
+    "excludeFromTotal": "Exclude from total"
   },
   "notes": {
     "title": "Notes",
@@ -127,7 +131,9 @@
     "customer": "Client",
     "selectVehicle": "Sélectionner un véhicule...",
     "selectCustomer": "Sélectionner un client...",
-    "none": "Aucun"
+    "none": "Aucun",
+    "clearVehicle": "Clear vehicle",
+    "clearCustomer": "Clear customer"
   },
   "totals": {
     "title": "Totaux",
@@ -158,7 +164,8 @@
     "emailSent": "E-mail envoyé",
     "failedEmail": "Échec de l'envoi de l'e-mail",
     "smsSent": "SMS envoyé",
-    "failedSms": "Échec de l'envoi du SMS"
+    "failedSms": "Échec de l'envoi du SMS",
+    "copyLink": "Copy link"
   },
   "images": {
     "title": "Images du devis",
@@ -173,6 +180,8 @@
     "uploadingCount": "Téléversement de {count} image(s)...",
     "uploadedCount": "{count} image(s) téléversée(s)",
     "uploadFailed": "Échec du téléversement",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Image supprimée",
     "failedDelete": "Échec de la suppression de l'image",
     "failedUpdate": "Échec de la mise à jour",
@@ -191,6 +200,8 @@
     "uploadingCount": "Téléversement de {count} fichier(s)...",
     "uploadedCount": "{count} fichier(s) téléversé(s)",
     "uploadFailed": "Échec du téléversement",
+    "failedUploadFile": "Failed to upload {name}",
+    "failedSaveFile": "Failed to save {name}",
     "deleted": "Fichier supprimé",
     "failedDelete": "Échec de la suppression du fichier",
     "failedUpdate": "Échec de la mise à jour",

+ 2 - 1
messages/fr/reminders.json

@@ -7,5 +7,6 @@
   "selectVehicle": "Sélectionner un véhicule...",
   "searchVehicle": "Rechercher des véhicules...",
   "noVehicle": "Aucun véhicule trouvé.",
-  "pickDate": "Choisir une date"
+  "pickDate": "Choisir une date",
+  "openMenu": "Open menu"
 }

+ 5 - 1
messages/fr/reports.json

@@ -406,5 +406,9 @@
     "date": "Date",
     "title": "Titre"
   },
-  "empty": "Aucune donnée chargée. Cliquez sur Actualiser pour charger le rapport."
+  "empty": "Aucune donnée chargée. Cliquez sur Actualiser pour charger le rapport.",
+  "pagination": {
+    "previousPage": "Previous page",
+    "nextPage": "Next page"
+  }
 }

+ 36 - 4
messages/fr/service.json

@@ -95,7 +95,8 @@
     "scanBarcode": "Scanner",
     "scanTitle": "Scanner le code-barres",
     "partFound": "\"{name}\" ajouté depuis l'inventaire",
-    "partNotFound": "Aucune pièce trouvée pour le code-barres \"{barcode}\""
+    "partNotFound": "Aucune pièce trouvée pour le code-barres \"{barcode}\"",
+    "deleteRow": "Delete row"
   },
   "labor": {
     "title": "Main-d'œuvre",
@@ -118,7 +119,8 @@
     "switchToHourly": "Switch to hourly pricing",
     "switchToService": "Switch to service pricing",
     "switchToHourlyHint": "Hourly: priced by hours worked (hours 00d7 rate/hr). Click to switch.",
-    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch."
+    "switchToServiceHint": "Service: priced as a flat fee per unit. Click to switch.",
+    "deleteRow": "Delete row"
   },
   "totals": {
     "title": "Totaux",
@@ -209,7 +211,9 @@
     "images": "Images ({count})",
     "videos": "Vidéos ({count})",
     "diagnostics": "Rapports Diagnostiques ({count})",
-    "documents": "Documents ({count})"
+    "documents": "Documents ({count})",
+    "download": "Download",
+    "close": "Close"
   },
   "share": {
     "title": "Partager la Facture",
@@ -225,7 +229,8 @@
     "emailSent": "Email envoyé",
     "failedEmail": "Impossible d'envoyer l'email",
     "smsSent": "SMS envoyé",
-    "failedSms": "Impossible d'envoyer le SMS"
+    "failedSms": "Impossible d'envoyer le SMS",
+    "copyLink": "Copy link"
   },
   "sidebar": {
     "vehicle": "Véhicule",
@@ -333,5 +338,32 @@
     "minutesAgo": "il y a {count}m",
     "hoursAgo": "il y a {count}h",
     "daysAgo": "il y a {count}j"
+  },
+  "table": {
+    "warranty": "Garantie"
+  },
+  "warranty": {
+    "title": "Garantie",
+    "months": "Durée (mois)",
+    "mileage": "Limite de kilométrage",
+    "notes": "Conditions de garantie",
+    "notesPlaceholder": "ex. Garantie de 12 mois sur toutes les pièces et la main-d'œuvre...",
+    "expiresAt": "Expire",
+    "presets": {
+      "3months": "3 m",
+      "6months": "6 m",
+      "12months": "12 m",
+      "24months": "24 m"
+    },
+    "status": {
+      "active": "Active",
+      "expiring": "Expire bientôt",
+      "expired": "Expirée",
+      "none": "Aucune"
+    },
+    "defaults": {
+      "title": "Paramètres de garantie par défaut",
+      "description": "Définir les conditions de garantie par défaut pour les nouveaux dossiers de service"
+    }
   }
 }

+ 148 - 6
messages/fr/settings.json

@@ -7,6 +7,7 @@
       "billing": "Facturation et documents",
       "communications": "Communications",
       "workshop": "Atelier",
+      "integrations": "Intégrations",
       "system": "Systeme"
     },
     "items": {
@@ -90,6 +91,10 @@
         "title": "Rapports planifiés",
         "description": "Envoi automatique de rapports"
       },
+      "webhooks": {
+        "title": "Webhooks",
+        "description": "Notifications d'événements en temps réel"
+      },
       "appearance": {
         "title": "Apparence",
         "description": "Theme et affichage"
@@ -353,7 +358,10 @@
     "memberRemoved": "Membre retire",
     "failedRemoveMember": "Echec du retrait du membre",
     "memberRoleUpdated": "Role du membre mis a jour",
-    "failedUpdateRole": "Echec de la mise a jour du role"
+    "failedUpdateRole": "Echec de la mise a jour du role",
+    "removeMember": "Remove member",
+    "cancelInvite": "Cancel invitation",
+    "editRole": "Edit role"
   },
   "invoice": {
     "title": "Mise en page de facture",
@@ -662,10 +670,37 @@
       "PHP": "Peso philippin",
       "IDR": "Roupie indonesienne",
       "MYR": "Ringgit malaisien",
+      "LKR": "Roupie srilankaise",
       "AED": "Dirham des EAU",
       "SAR": "Riyal saoudien",
-      "RUB": "Rouble russe"
-    }
+      "RUB": "Rouble russe",
+      "TWD": "Dollar taiwanais",
+      "VND": "Dong vietnamien",
+      "PKR": "Roupie pakistanaise",
+      "BDT": "Taka bangladais",
+      "NPR": "Roupie nepalaise",
+      "BGN": "Lev bulgare",
+      "UAH": "Hryvnia ukrainienne",
+      "CLP": "Peso chilien",
+      "COP": "Peso colombien",
+      "ARS": "Peso argentin",
+      "PEN": "Sol peruvien",
+      "EGP": "Livre egyptienne",
+      "NGN": "Naira nigerian",
+      "KES": "Shilling kenyan",
+      "MAD": "Dirham marocain",
+      "GHS": "Cedi ghaneen",
+      "QAR": "Riyal qatari",
+      "KWD": "Dinar koweitien",
+      "BHD": "Dinar bahreini",
+      "OMR": "Rial omanais",
+      "JOD": "Dinar jordanien"
+    },
+    "formatTitle": "Format de la monnaie",
+    "formatSymbolName": "Symbole",
+    "formatCodeName": "Code",
+    "formatSymbolLabel": "Symbole: {value}",
+    "formatCodeLabel": "Code: {value}"
   },
   "localization": {
     "languageTitle": "Langue",
@@ -718,7 +753,9 @@
     "fieldUpdated": "Champ personnalisé mis à jour",
     "fieldDeleted": "Champ personnalisé supprimé",
     "failedSave": "Impossible d'enregistrer le champ",
-    "failedDelete": "Impossible de supprimer"
+    "failedDelete": "Impossible de supprimer",
+    "edit": "Edit",
+    "delete": "Delete"
   },
   "workshop": {
     "title": "Valeurs par defaut de l'atelier",
@@ -948,7 +985,8 @@
         "slate": "Ardoise",
         "sand": "Sable"
       }
-    }
+    },
+    "copyUrl": "Copy portal URL"
   },
   "email": {
     "title": "Fournisseur de messagerie",
@@ -1023,7 +1061,8 @@
     "saved": "Parametres SMS enregistres",
     "failedSave": "Echec de l'enregistrement des parametres",
     "testSentTo": "SMS de test envoye a {phone}",
-    "testFailed": "Echec du test SMS"
+    "testFailed": "Echec du test SMS",
+    "copyWebhookUrl": "Copy webhook URL"
   },
   "subscription": {
     "title": "Plan d'abonnement",
@@ -1074,6 +1113,7 @@
     "resumeSuccess": "Abonnement repris avec succès",
     "resumeError": "Échec de la reprise de l'abonnement",
     "billingPortalError": "Impossible d'ouvrir le portail de facturation",
+    "checkoutError": "Failed to start checkout",
     "upgradeTitle": "Améliorer le plan",
     "upgradeDescription": "Débloquez plus de fonctionnalités en améliorant votre plan.",
     "upgradeToProDescription": "Améliorez pour débloquer des clients illimités, des membres d'équipe, des rapports, SMTP, l'accès API et plus encore.",
@@ -1165,6 +1205,10 @@
     "deleted": "Planning de rapports supprimé",
     "toggledOn": "Planning de rapports activé",
     "toggledOff": "Planning de rapports désactivé",
+    "failedSave": "Failed to save",
+    "failedSend": "Failed to send",
+    "failedToggle": "Failed to toggle",
+    "failedDelete": "Failed to delete",
     "noMembers": "Aucun membre trouvé",
     "addRecipient": "Ajouter un destinataire...",
     "searchMembers": "Rechercher des membres...",
@@ -1189,5 +1233,103 @@
       "retention": "Fidélisation clients",
       "inventory": "Inventaire"
     }
+  },
+  "webhooks": {
+    "title": "Webhooks",
+    "subtitle": "Envoyez des notifications d'événements en temps réel vers vos propres systèmes via HTTPS. Chaque événement est signé avec HMAC-SHA256 afin que vous puissiez vérifier son authenticité.",
+    "create": "Ajouter un webhook",
+    "createTitle": "Ajouter un webhook",
+    "editTitle": "Modifier le webhook",
+    "createDescription": "Les webhooks sont envoyés en JSON via POST. Les livraisons échouées sont réessayées avec un délai exponentiel jusqu'à 5 fois.",
+    "name": "Nom",
+    "namePlaceholder": "p. ex. Sync comptable",
+    "url": "URL du point de terminaison",
+    "urlHint": "Doit commencer par http:// ou https://. Nous recommandons fortement HTTPS.",
+    "description": "Description (facultatif)",
+    "events": "Événements",
+    "eventsHint": "Choisissez les événements qui déclenchent ce webhook.",
+    "subscribeToAll": "Tous les événements",
+    "selectAtLeastOneEvent": "Sélectionnez au moins un événement",
+    "groups": {
+      "customers": "Clients",
+      "vehicles": "Véhicules",
+      "services": "Ordres de service",
+      "quotes": "Devis",
+      "payments": "Paiements",
+      "inspections": "Inspections",
+      "inventory": "Inventaire",
+      "findings": "Constatations"
+    },
+    "save": "Enregistrer",
+    "cancel": "Annuler",
+    "delete": "Supprimer",
+    "rotate": "Renouveler",
+    "done": "Terminé",
+    "paused": "En pause",
+    "statusOk": "Sain",
+    "statusFailing": "{count, plural, one {# échec récent} other {# échecs récents}}",
+    "lastDelivery": "Dernière livraison {when}",
+    "totalDeliveries": "{count, plural, one {# livraison} other {# livraisons}}",
+    "toggleAria": "Activer ou désactiver ce webhook",
+    "deliveries": "Livraisons",
+    "sendTest": "Envoyer un test",
+    "testSent": "Événement de test envoyé",
+    "testFailed": "Impossible d'envoyer l'événement de test",
+    "rotateSecret": "Renouveler la clé de signature",
+    "confirmRotateTitle": "Renouveler la clé de signature ?",
+    "confirmRotateDescription": "La clé actuelle cessera immédiatement de fonctionner. Mettez à jour votre endpoint avec la nouvelle clé.",
+    "secretRotated": "Clé renouvelée",
+    "secretRotatedDescription": "Enregistrez cette clé maintenant — elle ne pourra pas être récupérée plus tard.",
+    "confirmDeleteTitle": "Supprimer le webhook ?",
+    "confirmDeleteDescription": "« {name} » cessera immédiatement de recevoir des événements. Cette action est irréversible.",
+    "secretShownOnceTitle": "Webhook « {name} » créé",
+    "secretShownOnceDescription": "Copiez cette clé de signature maintenant. Elle ne sera pas affichée à nouveau.",
+    "secretCopyHint": "Utilisez cette clé pour vérifier l'en-tête X-Torqvoice-Signature de chaque requête.",
+    "deliveriesTitle": "Livraisons — {name}",
+    "deliveriesDescription": "50 dernières tentatives. Les événements échoués sont réessayés automatiquement avec un délai exponentiel.",
+    "deliveriesEmpty": "Aucune livraison pour le moment. Envoyez un événement de test pour essayer.",
+    "attempt": "Tentative {n}/{max}",
+    "nextRetry": "Prochaine tentative {when}",
+    "retryQueued": "Nouvelle tentative en file d'attente",
+    "retryFailed": "Échec de la nouvelle tentative",
+    "saveFailed": "Échec de l'enregistrement",
+    "deleteFailed": "Échec de la suppression",
+    "toggleFailed": "Échec du basculement",
+    "rotateFailed": "Échec du renouvellement",
+    "created": "Webhook créé",
+    "updated": "Webhook mis à jour",
+    "deleted": "Webhook supprimé",
+    "emptyTitle": "Aucun webhook pour le moment",
+    "emptyDescription": "Ajoutez un webhook pour envoyer des événements vers vos propres systèmes — sync comptable, tableaux de bord personnalisés, Zapier, tout ce qui parle HTTP.",
+    "helpTitle": "Vérifier les signatures des webhooks",
+    "helpIntro": "Chaque livraison comprend une signature HMAC-SHA256 pour que vous puissiez confirmer que la requête provient de Torqvoice.",
+    "helpHeadersTitle": "En-têtes envoyés avec chaque requête",
+    "helpVerify": "Reconstruisez la chaîne signée comme `[timestamp].[body]` et comparez avec une égalité à temps constant :",
+    "tabWebhooks": "Webhooks",
+    "tabReference": "Référence des événements",
+    "autoDisabled": "Mis en pause automatiquement",
+    "autoDisabledBanner": "Ce webhook a été mis en pause automatiquement après 20 échecs consécutifs. Réactivez-le pour reprendre les livraisons.",
+    "reEnable": "Réactiver",
+    "resumed": "Webhook réactivé",
+    "copyUrl": "Copier l'URL",
+    "refresh": "Actualiser",
+    "noMatches": "Aucune livraison ne correspond à ce filtre.",
+    "retry": "Réessayer",
+    "viewPayload": "Voir le payload",
+    "payloadCopied": "Payload copié",
+    "loadingPayload": "Chargement du payload…",
+    "requestPayload": "Payload de la requête",
+    "response": "Réponse",
+    "close": "Fermer",
+    "samplePayload": "Enveloppe exemple",
+    "referenceTitle": "Référence des événements",
+    "referenceIntro": "Chaque livraison suit cette enveloppe. Cliquez sur un événement pour voir un exemple de payload que votre endpoint recevra.",
+    "filter": {
+      "all": "Toutes",
+      "success": "Réussie",
+      "failed": "Échouée",
+      "retrying": "Nouvelle tentative",
+      "pending": "En attente"
+    }
   }
 }

+ 6 - 0
messages/fr/share.json

@@ -52,12 +52,18 @@
     "bankAccount": "Compte bancaire",
     "orgNumber": "N° d'organisation",
     "paymentTerms": "Conditions de paiement",
+    "netDays": "{days} jours",
     "dueDate": "Date d'échéance",
     "serviceImages": "Images de service ({count})",
     "serviceVideos": "Vidéos de service ({count})",
     "diagnosticReports": "Rapports de diagnostic ({count})",
     "notes": "Notes",
     "diagnosticNotes": "Notes de diagnostic",
+    "warrantyTitle": "Garantie",
+    "warrantyExpires": "Expire",
+    "warrantyMonth": "mois",
+    "warrantyMonths": "mois",
+    "warrantyKm": "km",
     "findings": "Observations",
     "findingsDescription": "Les éléments suivants ont été observés lors de ce service et peuvent nécessiter une attention particulière.",
     "severity": "Gravité",

+ 1 - 0
messages/fr/statusReport.json

@@ -14,6 +14,7 @@
     "createAndSend": "Créer et envoyer",
     "cancel": "Annuler",
     "uploadFailed": "Échec du téléversement de la vidéo",
+    "createReportFailed": "Failed to create status report",
     "created": "Rapport d'état créé",
     "noVideo": "Veuillez d'abord téléverser une vidéo",
     "removeVideo": "Supprimer la vidéo",

+ 5 - 2
messages/fr/telegram.json

@@ -4,7 +4,9 @@
   "botToken": {
     "label": "Token du bot",
     "placeholder": "Entrez le token de @BotFather",
-    "help": "Créez un bot via @BotFather sur Telegram pour obtenir votre token."
+    "help": "Créez un bot via @BotFather sur Telegram pour obtenir votre token.",
+    "show": "Show token",
+    "hide": "Hide token"
   },
   "botUsername": {
     "label": "Nom d'utilisateur du bot",
@@ -17,7 +19,8 @@
   "deepLink": {
     "title": "Lien de connexion client",
     "description": "Partagez ce lien avec les clients pour qu'ils se connectent à votre bot. Remplacez l'espace réservé de l'ID client par l'ID réel.",
-    "copied": "Lien copié !"
+    "copied": "Lien copié !",
+    "copy": "Copy deep link"
   },
   "test": {
     "title": "Message test",

+ 2 - 1
messages/fr/telegramMessages.json

@@ -30,7 +30,8 @@
     "deleteDescription": "Cela supprimera définitivement ce message. Cette action est irréversible.",
     "messageDeleted": "Message supprimé",
     "deleteError": "Échec de la suppression du message",
-    "sendError": "Échec de l'envoi du message"
+    "sendError": "Échec de l'envoi du message",
+    "sendMessage": "Send message"
   },
   "sendDialog": {
     "send": "Envoyer {label}",

+ 122 - 86
messages/fr/vehicles.json

@@ -1,52 +1,56 @@
 {
   "list": {
     "active": "Actifs",
-    "archived": "Archiv\u00e9s",
-    "searchPlaceholder": "Rechercher par plaque, marque, mod\u00e8le, NIV...",
-    "addVehicle": "Ajouter un v\u00e9hicule",
-    "emptySearch": "Aucun v\u00e9hicule ne correspond \u00e0 votre recherche.",
-    "emptyArchived": "Aucun v\u00e9hicule archiv\u00e9.",
-    "empty": "Aucun v\u00e9hicule pour le moment.",
+    "archived": "Archivés",
+    "searchPlaceholder": "Rechercher par plaque, marque, modèle, NIV...",
+    "addVehicle": "Ajouter un véhicule",
+    "emptySearch": "Aucun véhicule ne correspond à votre recherche.",
+    "emptyArchived": "Aucun véhicule archivé.",
+    "empty": "Aucun véhicule pour le moment.",
     "table": {
       "plate": "Plaque",
-      "vehicle": "V\u00e9hicule",
+      "vehicle": "Véhicule",
       "customer": "Client",
-      "mileage": "Kilom\u00e9trage",
+      "mileage": "Kilométrage",
       "mileageMarine": "Heures Moteur",
       "services": "Services"
     },
     "edit": "Modifier",
     "archive": "Archiver",
-    "unarchive": "D\u00e9sarchiver",
+    "unarchive": "Désarchiver",
     "delete": "Supprimer",
-    "deleteTitle": "Supprimer le v\u00e9hicule",
-    "deleteDescription": "Supprimer {name}\u00a0? Cela supprimera tous les dossiers de service et notes associ\u00e9s.",
-    "unarchiveTitle": "D\u00e9sarchiver le v\u00e9hicule",
-    "unarchiveDescription": "Restaurer {name} dans la liste des v\u00e9hicules actifs\u00a0?",
-    "vehicleUnarchived": "V\u00e9hicule d\u00e9sarchiv\u00e9",
-    "unarchiveError": "\u00c9chec de la d\u00e9sarchivation du v\u00e9hicule",
-    "deleteError": "\u00c9chec de la suppression du v\u00e9hicule",
-    "error": "\u00c9chec du chargement des v\u00e9hicules"
+    "deleteTitle": "Supprimer le véhicule",
+    "deleteDescription": "Supprimer {name} ? Cela supprimera tous les dossiers de service et notes associés.",
+    "unarchiveTitle": "Désarchiver le véhicule",
+    "unarchiveDescription": "Restaurer {name} dans la liste des véhicules actifs ?",
+    "vehicleUnarchived": "Véhicule désarchivé",
+    "unarchiveError": "Échec de la désarchivation du véhicule",
+    "deleteError": "Échec de la suppression du véhicule",
+    "error": "Échec du chargement des véhicules",
+    "viewTable": "Table view",
+    "viewGrid": "Grid view",
+    "viewLargeGrid": "Large grid view",
+    "openMenu": "Open menu"
   },
   "detail": {
-    "vehicleArchived": "Ce v\u00e9hicule est archiv\u00e9",
-    "backToVehicles": "Retour aux v\u00e9hicules",
+    "vehicleArchived": "Ce véhicule est archivé",
+    "backToVehicles": "Retour aux véhicules",
     "newWorkOrder": "Nouvel ordre de travail",
-    "editVehicle": "Modifier le v\u00e9hicule",
-    "unarchive": "D\u00e9sarchiver",
+    "editVehicle": "Modifier le véhicule",
+    "unarchive": "Désarchiver",
     "archive": "Archiver",
     "delete": "Supprimer",
-    "notFound": "V\u00e9hicule introuvable",
-    "predictedKm": "Km pr\u00e9vus",
-    "predictedMileage": "Kilom\u00e9trage pr\u00e9vu",
+    "notFound": "Véhicule introuvable",
+    "predictedKm": "Km prévus",
+    "predictedMileage": "Kilométrage prévu",
     "predictedHours": "Heures Prévues",
-    "predictedTooltip": "Kilom\u00e9trage actuel estim\u00e9 bas\u00e9 sur l'historique d'entretien. Le pourcentage indique la fiabilit\u00e9 de la pr\u00e9diction.",
-    "dismissed": "Ignor\u00e9",
+    "predictedTooltip": "Kilométrage actuel estimé basé sur l'historique d'entretien. Le pourcentage indique la fiabilité de la prédiction.",
+    "dismissed": "Ignoré",
     "undo": "Annuler",
     "serviceOverdue": "Service en retard",
-    "serviceSoon": "Service bient\u00f4t",
+    "serviceSoon": "Service bientôt",
     "services": "services",
-    "purchased": "Achet\u00e9 le {date}",
+    "purchased": "Acheté le {date}",
     "tabs": {
       "services": "Services",
       "quotes": "Devis",
@@ -55,16 +59,16 @@
       "reminders": "Rappels",
       "findings": "Observations"
     },
-    "noteDeleted": "Note supprim\u00e9e",
-    "noteDeleteError": "\u00c9chec de la suppression",
-    "reminderDeleted": "Rappel supprim\u00e9",
-    "reminderDeleteError": "\u00c9chec de la suppression",
-    "vehicleUnarchived": "V\u00e9hicule d\u00e9sarchiv\u00e9",
-    "unarchiveError": "\u00c9chec de la d\u00e9sarchivation",
-    "vehicleDeleted": "V\u00e9hicule supprim\u00e9",
-    "vehicleDeleteError": "\u00c9chec de la suppression du v\u00e9hicule",
-    "deleteTitle": "Supprimer le v\u00e9hicule\u00a0?",
-    "deleteDescription": "Cela supprimera d\u00e9finitivement {name} ainsi que tous les dossiers de service, notes et rappels associ\u00e9s. Cette action est irr\u00e9versible.",
+    "noteDeleted": "Note supprimée",
+    "noteDeleteError": "Échec de la suppression",
+    "reminderDeleted": "Rappel supprimé",
+    "reminderDeleteError": "Échec de la suppression",
+    "vehicleUnarchived": "Véhicule désarchivé",
+    "unarchiveError": "Échec de la désarchivation",
+    "vehicleDeleted": "Véhicule supprimé",
+    "vehicleDeleteError": "Échec de la suppression du véhicule",
+    "deleteTitle": "Supprimer le véhicule ?",
+    "deleteDescription": "Cela supprimera définitivement {name} ainsi que tous les dossiers de service, notes et rappels associés. Cette action est irréversible.",
     "edit": "Modifier",
     "aiSummary": "Résumé IA",
     "aiSummaryTitle": "Résumé de l'historique de service",
@@ -84,7 +88,9 @@
     "severityCommon": "Courant — problème fréquemment signalé",
     "severityModerate": "Modéré — signalé par certains propriétaires",
     "severityUncommon": "Peu courant — signalé par peu de propriétaires",
-    "severityRare": "Rare — signalements isolés mais critique lorsqu'il survient"
+    "severityRare": "Rare — signalements isolés mais critique lorsqu'il survient",
+    "openMenu": "Open menu",
+    "dismissMaintenance": "Dismiss"
   },
   "quotes": {
     "newQuote": "Nouveau devis",
@@ -98,44 +104,44 @@
   "inspections": {
     "newInspection": "Nouvelle inspection",
     "empty": "Aucune inspection pour le moment",
-    "template": "Mod\u00e8le",
+    "template": "Modèle",
     "progress": "Progression",
     "status": "Statut",
     "date": "Date",
-    "completed": "Termin\u00e9e",
+    "completed": "Terminée",
     "inProgress": "En cours"
   },
   "reminders": {
     "active": "Actifs",
-    "completed": "Termin\u00e9s",
+    "completed": "Terminés",
     "all": "Tous",
     "addReminder": "Ajouter un rappel",
     "emptyActive": "Aucun rappel actif",
-    "emptyCompleted": "Aucun rappel termin\u00e9",
+    "emptyCompleted": "Aucun rappel terminé",
     "empty": "Aucun rappel pour le moment",
     "overdue": "En retard",
-    "dueSoon": "Bient\u00f4t d\u00fb",
-    "due": "\u00c9ch\u00e9ance le {date}",
-    "dueAt": "\u00c9ch\u00e9ance \u00e0 {mileage} {unit}",
+    "dueSoon": "Bientôt dû",
+    "due": "Échéance le {date}",
+    "dueAt": "Échéance à {mileage} {unit}",
     "editTitle": "Modifier le rappel",
     "addTitle": "Ajouter un rappel",
     "titleLabel": "Titre *",
     "titlePlaceholder": "ex. Vidange d'huile",
     "descriptionLabel": "Description",
-    "descriptionPlaceholder": "D\u00e9tails optionnels...",
-    "dueDateLabel": "Date d'\u00e9ch\u00e9ance",
-    "dueMileageLabel": "Kilom\u00e9trage d'\u00e9ch\u00e9ance",
+    "descriptionPlaceholder": "Détails optionnels...",
+    "dueDateLabel": "Date d'échéance",
+    "dueMileageLabel": "Kilométrage d'échéance",
     "dueMileagePlaceholder": "ex. 50000",
-    "reminderUpdated": "Rappel mis \u00e0 jour",
-    "reminderCreated": "Rappel cr\u00e9\u00e9",
-    "saveError": "\u00c9chec de {action} du rappel"
+    "reminderUpdated": "Rappel mis à jour",
+    "reminderCreated": "Rappel créé",
+    "saveError": "Échec de {action} du rappel"
   },
   "services": {
     "searchPlaceholder": "Rechercher des dossiers...",
     "allTypes": "Tous les types",
     "maintenance": "Entretien",
-    "repair": "R\u00e9paration",
-    "upgrade": "Am\u00e9lioration",
+    "repair": "Réparation",
+    "upgrade": "Amélioration",
     "inspection": "Inspection",
     "newWorkOrder": "Nouvel ordre de travail",
     "exportServiceHistory": "Exporter l'historique",
@@ -144,17 +150,30 @@
       "title": "Titre",
       "type": "Type",
       "status": "Statut",
-      "mileage": "Kilom\u00e9trage",
+      "mileage": "Kilométrage",
       "mileageMarine": "Heures Moteur",
       "technician": "Technicien",
       "files": "Fichiers",
-      "total": "Total"
+      "total": "Total",
+      "warranty": "Garantie"
+    },
+    "warranty": {
+      "status": {
+        "active": "Active",
+        "expiring": "Expiration proche",
+        "expired": "Expirée",
+        "none": "N/A"
+      }
     },
-    "emptyFiltered": "Aucun dossier de service ne correspond \u00e0 vos filtres.",
+    "emptyFiltered": "Aucun dossier de service ne correspond à vos filtres.",
     "empty": "Aucun dossier de service pour le moment.",
     "showing": "Affichage de {start}-{end} sur {total}",
     "perPage": "par page",
-    "page": "Page {page} sur {totalPages}"
+    "page": "Page {page} sur {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "notes": {
     "addNote": "Ajouter une note",
@@ -164,8 +183,8 @@
       "date": "Date"
     },
     "empty": "Aucune note pour le moment.",
-    "pin": "\u00c9pingler",
-    "unpin": "D\u00e9s\u00e9pingler",
+    "pin": "Épingler",
+    "unpin": "Désépingler",
     "delete": "Supprimer",
     "showing": "Affichage de {start}-{end} sur {total}",
     "perPage": "par page",
@@ -175,11 +194,16 @@
     "titleLabel": "Titre *",
     "titlePlaceholder": "Titre de la note",
     "contentLabel": "Contenu *",
-    "contentPlaceholder": "\u00c9crivez votre note...",
-    "noteUpdated": "Note mise \u00e0 jour",
-    "noteCreated": "Note cr\u00e9\u00e9e",
+    "contentPlaceholder": "Écrivez votre note...",
+    "noteUpdated": "Note mise à jour",
+    "noteCreated": "Note créée",
     "titleRequired": "Le titre et le contenu sont obligatoires",
-    "saveError": "\u00c9chec de {action} de la note"
+    "saveError": "Échec de {action} de la note",
+    "openMenu": "Open menu",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   },
   "findings": {
     "sectionTitle": "Observations",
@@ -227,7 +251,12 @@
     "resolveError": "Échec de résolution",
     "showing": "Affichage de {start}-{end} sur {total}",
     "perPage": "par page",
-    "page": "Page {page} sur {totalPages}"
+    "page": "Page {page} sur {totalPages}",
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page",
+    "openMenu": "Open menu"
   },
   "observationsPage": {
     "searchPlaceholder": "Rechercher une description, des notes ou un véhicule...",
@@ -244,39 +273,40 @@
     }
   },
   "form": {
-    "editTitle": "Modifier le v\u00e9hicule",
-    "addTitle": "Ajouter un nouveau v\u00e9hicule",
+    "editTitle": "Modifier le véhicule",
+    "addTitle": "Ajouter un nouveau véhicule",
     "photo": "Photo",
-    "clickToUpload": "Cliquez pour t\u00e9l\u00e9verser une photo",
-    "imageFormats": "JPEG, PNG, WebP jusqu'\u00e0 5 Mo",
+    "clickToUpload": "Cliquez pour téléverser une photo",
+    "imageFormats": "JPEG, PNG, WebP jusqu'à 5 Mo",
     "imageTooLarge": "Image trop volumineuse",
     "maxFileSize": "La taille maximale est de 5 Mo",
-    "uploadingImage": "T\u00e9l\u00e9versement de l'image...",
-    "uploadFailed": "\u00c9chec du t\u00e9l\u00e9versement",
-    "imageUploaded": "Image t\u00e9l\u00e9vers\u00e9e",
+    "uploadingImage": "Téléversement de l'image...",
+    "uploadFailed": "Échec du téléversement",
+    "imageUploaded": "Image téléversée",
     "customer": "Client",
     "searchCustomers": "Rechercher des clients...",
-    "noCustomerFound": "Aucun client trouv\u00e9.",
+    "noCustomerFound": "Aucun client trouvé.",
     "noCustomer": "Aucun client",
     "addCustomer": "Ajouter un client",
     "make": "Marque *",
-    "model": "Mod\u00e8le *",
-    "year": "Ann\u00e9e *",
-    "mileage": "Kilom\u00e9trage",
+    "model": "Modèle *",
+    "year": "Année *",
+    "mileage": "Kilométrage",
     "vin": "NIV",
     "licensePlate": "Plaque d'immatriculation",
     "color": "Couleur",
     "fuelType": "Type de carburant",
     "gasoline": "Essence",
     "diesel": "Diesel",
-    "electric": "\u00c9lectrique",
+    "electric": "Électrique",
     "hybrid": "Hybride",
     "other": "Autre",
     "transmission": "Transmission",
     "automatic": "Automatique",
     "manual": "Manuelle",
     "cvt": "CVT",
-    "engineSize": "Cylindr\u00e9e",
+    "engineSize": "Cylindrée",
+    "engineCode": "Code moteur",
     "makeMarine": "Fabricant *",
     "mileageMarine": "Heures Moteur",
     "transmissionMarine": "Type de Moteur",
@@ -286,16 +316,22 @@
     "twoStroke": "Deux Temps",
     "vinMarine": "HIN",
     "licensePlateMarine": "Numéro d'Immatriculation",
-    "vehicleUpdated": "V\u00e9hicule mis \u00e0 jour",
-    "vehicleAdded": "V\u00e9hicule ajout\u00e9",
-    "saveError": "\u00c9chec de l'enregistrement du v\u00e9hicule"
+    "vehicleUpdated": "Véhicule mis à jour",
+    "vehicleAdded": "Véhicule ajouté",
+    "saveError": "Échec de l'enregistrement du véhicule"
   },
   "archiveDialog": {
-    "title": "Archiver le v\u00e9hicule",
-    "description": "Archiver <span>{name}</span>\u00a0? Il sera masqu\u00e9 de la liste principale mais tout l'historique de service sera conserv\u00e9.",
+    "title": "Archiver le véhicule",
+    "description": "Archiver <span>{name}</span> ? Il sera masqué de la liste principale mais tout l'historique de service sera conservé.",
     "reasonLabel": "Raison (optionnel)",
-    "reasonPlaceholder": "ex. Vendu, accident\u00e9, d\u00e9class\u00e9...",
-    "archived": "{name} archiv\u00e9",
-    "archiveError": "\u00c9chec de l'archivage du v\u00e9hicule"
+    "reasonPlaceholder": "ex. Vendu, accidenté, déclassé...",
+    "archived": "{name} archivé",
+    "archiveError": "Échec de l'archivage du véhicule"
+  },
+  "serviceRecords": {
+    "firstPage": "First page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page",
+    "lastPage": "Last page"
   }
 }

Некоторые файлы не были показаны из-за большого количества измененных файлов