Parcourir la source

Merge branch 'main' into demo

Bernt Christian Egeland il y a 1 mois
Parent
commit
b869b58282
100 fichiers modifiés avec 3503 ajouts et 108 suppressions
  1. 0 1
      .github/workflows/deploy-staging.yml
  2. 122 1
      messages/de/audit.json
  3. 15 0
      messages/de/common.json
  4. 6 2
      messages/de/customers.json
  5. 7 0
      messages/de/dashboard.json
  6. 4 1
      messages/de/messages.json
  7. 26 0
      messages/de/messaging.json
  8. 4 2
      messages/de/navigation.json
  9. 4 2
      messages/de/scheduledMessages.json
  10. 35 3
      messages/de/settings.json
  11. 2 1
      messages/de/statusReport.json
  12. 16 1
      messages/de/vehicles.json
  13. 179 0
      messages/de/whatsapp.json
  14. 122 1
      messages/en/audit.json
  15. 15 0
      messages/en/common.json
  16. 6 2
      messages/en/customers.json
  17. 7 0
      messages/en/dashboard.json
  18. 4 1
      messages/en/messages.json
  19. 26 0
      messages/en/messaging.json
  20. 4 2
      messages/en/navigation.json
  21. 4 2
      messages/en/scheduledMessages.json
  22. 35 3
      messages/en/settings.json
  23. 2 1
      messages/en/statusReport.json
  24. 16 1
      messages/en/vehicles.json
  25. 179 0
      messages/en/whatsapp.json
  26. 122 1
      messages/es/audit.json
  27. 15 0
      messages/es/common.json
  28. 6 2
      messages/es/customers.json
  29. 7 0
      messages/es/dashboard.json
  30. 4 1
      messages/es/messages.json
  31. 26 0
      messages/es/messaging.json
  32. 4 2
      messages/es/navigation.json
  33. 4 2
      messages/es/scheduledMessages.json
  34. 35 3
      messages/es/settings.json
  35. 2 1
      messages/es/statusReport.json
  36. 16 1
      messages/es/vehicles.json
  37. 179 0
      messages/es/whatsapp.json
  38. 122 1
      messages/fr/audit.json
  39. 15 0
      messages/fr/common.json
  40. 6 2
      messages/fr/customers.json
  41. 7 0
      messages/fr/dashboard.json
  42. 4 1
      messages/fr/messages.json
  43. 26 0
      messages/fr/messaging.json
  44. 4 2
      messages/fr/navigation.json
  45. 4 2
      messages/fr/scheduledMessages.json
  46. 35 3
      messages/fr/settings.json
  47. 2 1
      messages/fr/statusReport.json
  48. 16 1
      messages/fr/vehicles.json
  49. 179 0
      messages/fr/whatsapp.json
  50. 122 1
      messages/it/audit.json
  51. 15 0
      messages/it/common.json
  52. 6 2
      messages/it/customers.json
  53. 7 0
      messages/it/dashboard.json
  54. 4 1
      messages/it/messages.json
  55. 26 0
      messages/it/messaging.json
  56. 4 2
      messages/it/navigation.json
  57. 4 2
      messages/it/scheduledMessages.json
  58. 35 3
      messages/it/settings.json
  59. 2 1
      messages/it/statusReport.json
  60. 16 1
      messages/it/vehicles.json
  61. 179 0
      messages/it/whatsapp.json
  62. 122 1
      messages/lt/audit.json
  63. 15 0
      messages/lt/common.json
  64. 6 2
      messages/lt/customers.json
  65. 7 0
      messages/lt/dashboard.json
  66. 4 1
      messages/lt/messages.json
  67. 26 0
      messages/lt/messaging.json
  68. 4 2
      messages/lt/navigation.json
  69. 4 2
      messages/lt/scheduledMessages.json
  70. 35 3
      messages/lt/settings.json
  71. 2 1
      messages/lt/statusReport.json
  72. 16 1
      messages/lt/vehicles.json
  73. 179 0
      messages/lt/whatsapp.json
  74. 122 1
      messages/nb/audit.json
  75. 15 0
      messages/nb/common.json
  76. 6 2
      messages/nb/customers.json
  77. 7 0
      messages/nb/dashboard.json
  78. 4 1
      messages/nb/messages.json
  79. 26 0
      messages/nb/messaging.json
  80. 4 2
      messages/nb/navigation.json
  81. 4 2
      messages/nb/scheduledMessages.json
  82. 35 3
      messages/nb/settings.json
  83. 2 1
      messages/nb/statusReport.json
  84. 16 1
      messages/nb/vehicles.json
  85. 179 0
      messages/nb/whatsapp.json
  86. 122 1
      messages/nl/audit.json
  87. 15 0
      messages/nl/common.json
  88. 6 2
      messages/nl/customers.json
  89. 7 0
      messages/nl/dashboard.json
  90. 4 1
      messages/nl/messages.json
  91. 26 0
      messages/nl/messaging.json
  92. 4 2
      messages/nl/navigation.json
  93. 4 2
      messages/nl/scheduledMessages.json
  94. 35 3
      messages/nl/settings.json
  95. 2 1
      messages/nl/statusReport.json
  96. 16 1
      messages/nl/vehicles.json
  97. 179 0
      messages/nl/whatsapp.json
  98. 122 1
      messages/pl/audit.json
  99. 15 0
      messages/pl/common.json
  100. 6 2
      messages/pl/customers.json

+ 0 - 1
.github/workflows/deploy-staging.yml

@@ -65,7 +65,6 @@ jobs:
                 STRIPE_PRO_PRICE_ID: ${STRIPE_PRO_PRICE_ID}
                 STRIPE_ENTERPRISE_PRICE_ID: ${STRIPE_ENTERPRISE_PRICE_ID}
                 POSTHOG_HOST: ${POSTHOG_HOST}
-                POSTHOG_KEY: ${POSTHOG_KEY}
                 BACKUP_HEARTBEAT_TOKEN: ${BACKUP_HEARTBEAT_TOKEN}
               networks:
                 - proxy

+ 122 - 1
messages/de/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Satz erneut eingelagert",
     "tire_set_dispose": "Reifensatz ausgemustert",
     "tire_set_attach": "Dateien zu einem Reifensatz hinzugefügt",
-    "tire_set_detach": "Datei von einem Reifensatz entfernt"
+    "tire_set_detach": "Datei von einem Reifensatz entfernt",
+    "whatsapp_send": "WhatsApp-Nachricht gesendet"
+  },
+  "summary": {
+    "customField_create": "Benutzerdefiniertes Feld „{name}“ erstellt",
+    "customField_delete": "Benutzerdefiniertes Feld „{name}“ gelöscht",
+    "customField_update": "Benutzerdefiniertes Feld „{name}“ aktualisiert",
+    "customer_backfillNumbers": "Kundennummern an {count, plural, one {# Kunden} other {# Kunden}} vergeben",
+    "customer_create": "Kunde {name} angelegt",
+    "customer_delete": "Kunde {id} gelöscht",
+    "customer_update": "Kunde {id} aktualisiert",
+    "email_sendInspection": "Prüfbericht per E-Mail an {recipient} gesendet",
+    "email_sendInvoice": "Rechnung per E-Mail an {recipient} gesendet",
+    "email_sendQuote": "Angebot per E-Mail an {recipient} gesendet",
+    "finding_create": "Befund „{description}“ am Fahrzeug {vehicleId} angelegt",
+    "finding_delete": "Befund {id} gelöscht",
+    "finding_resolve": "Befund „{description}“ erledigt",
+    "finding_update": "Befund „{description}“ aktualisiert",
+    "inspectionTemplate_create": "Prüfvorlage „{name}“ erstellt",
+    "inspectionTemplate_createFromPreset": "Prüfvorlage „{name}“ aus einer Voreinstellung erstellt",
+    "inspectionTemplate_delete": "Prüfvorlage „{name}“ gelöscht",
+    "inspectionTemplate_duplicate": "Prüfvorlage nach „{name}“ dupliziert",
+    "inspectionTemplate_import": "{count, plural, one {# Prüfvorlage} other {# Prüfvorlagen}} aus einem Paket importiert",
+    "inspectionTemplate_update": "Prüfvorlage „{name}“ aktualisiert",
+    "inspection_complete": "Prüfung {id} abgeschlossen",
+    "inspection_create": "Prüfung {id} angelegt",
+    "inspection_delete": "Prüfung {id} gelöscht",
+    "inspection_reopen": "Prüfung {id} wieder geöffnet",
+    "inventory_bulkDelete": "{count, plural, one {# Lagerteil} other {# Lagerteile}} im Stapel gelöscht",
+    "inventory_create": "Lagerteil „{name}“ angelegt",
+    "inventory_delete": "Lagerteil {id} gelöscht",
+    "inventory_update": "Lagerteil {id} aktualisiert",
+    "labor_preset_create": "Arbeitsvorlage „{name}“ erstellt",
+    "labor_preset_delete": "Arbeitsvorlage {id} gelöscht",
+    "labor_preset_update": "Arbeitsvorlage {id} aktualisiert",
+    "note_create": "Notiz am Fahrzeug {vehicleId} angelegt",
+    "note_delete": "Notiz {id} gelöscht",
+    "organization_create": "Organisation „{name}“ angelegt",
+    "organization_sampleDataRemove": "Beispieldaten aus der Einrichtung entfernt",
+    "payment_create": "Zahlung {amount} zum Auftrag {serviceRecordId} erfasst",
+    "payment_delete": "Zahlung {id} gelöscht",
+    "quoteRequest_update": "Status der Angebotsanfrage geändert auf {status, select, pending {offen} approved {genehmigt} rejected {abgelehnt} completed {abgeschlossen} other {{status}}}",
+    "quote_convert": "Angebot {ref} in Auftrag {serviceRecordId} umgewandelt",
+    "quote_create": "Angebot {ref} erstellt",
+    "quote_delete": "Angebot {ref} gelöscht",
+    "quote_status": "Angebotsstatus geändert auf {status, select, draft {Entwurf} sent {gesendet} accepted {angenommen} rejected {abgelehnt} expired {abgelaufen} converted {umgewandelt} other {{status}}}",
+    "quote_update": "Angebot {ref} aktualisiert",
+    "recurringInvoice_create": "Wiederkehrende Rechnung „{title}“ erstellt",
+    "recurringInvoice_delete": "Wiederkehrende Rechnung {id} gelöscht",
+    "recurringInvoice_update": "Wiederkehrende Rechnung „{title}“ aktualisiert",
+    "reminder_create": "Erinnerung „{title}“ erstellt",
+    "reminder_delete": "Erinnerung {id} gelöscht",
+    "role_create": "Rolle „{name}“ erstellt",
+    "role_delete": "Rolle „{name}“ gelöscht",
+    "role_update": "Rolle „{name}“ aktualisiert",
+    "scheduled_message_cancel": "Geplante Nachricht abgebrochen",
+    "scheduled_message_create": "{channel, select, email {E-Mail} sms {SMS} telegram {Telegram-Nachricht} in_app {Hinweis in der App} other {{channel}}} für {sendAt} geplant",
+    "scheduled_message_delete": "Geplante Nachricht gelöscht",
+    "scheduled_message_send_now": "Geplante {channel, select, email {E-Mail} sms {SMS} telegram {Telegram-Nachricht} in_app {Hinweis in der App} other {{channel}}} vorzeitig gesendet",
+    "scheduled_message_update": "Geplante {channel, select, email {E-Mail} sms {SMS} telegram {Telegram-Nachricht} in_app {Hinweis in der App} other {{channel}}} aktualisiert",
+    "service_create": "Auftrag {ref} angelegt",
+    "service_createCounterSale": "Thekenverkauf {ref} angelegt",
+    "service_createDraft": "Auftragsentwurf {ref} angelegt",
+    "service_createFromInspection": "Arbeitsauftrag {ref} aus einer Prüfung angelegt",
+    "service_delete": "Auftrag {ref} gelöscht",
+    "service_status": "Auftragsstatus geändert auf {status, select, pending {offen} scheduled {geplant} in_progress {in Arbeit} waiting_parts {wartet auf Teile} ready {fertig} completed {abgeschlossen} cancelled {storniert} other {{status}}}",
+    "service_update": "Auftrag {ref} aktualisiert",
+    "settings_applyTaxRate": "{rate} % Steuer auf {records, plural, one {# Auftrag} other {# Aufträge}} und {quotes, plural, one {# Angebot} other {# Angebote}} angewendet",
+    "settings_convertToExclusive": "{records, plural, one {# Auftrag} other {# Aufträge}} und {quotes, plural, one {# Angebot} other {# Angebote}} auf Nettopreise umgestellt",
+    "settings_convertToInclusive": "{records, plural, one {# Auftrag} other {# Aufträge}} und {quotes, plural, one {# Angebot} other {# Angebote}} auf Bruttopreise umgestellt",
+    "settings_deleteContent": "Alle Daten gelöscht: {items}",
+    "settings_updateInvoiceLayout": "Rechnungslayout aktualisiert",
+    "settings_updateQuoteLayout": "Angebotslayout aktualisiert",
+    "sms_send": "SMS an {name} ({phone}) gesendet",
+    "statusReport_create": "Statusbericht zum Auftrag {serviceRecordId} erstellt",
+    "statusReport_delete": "Statusbericht gelöscht",
+    "statusReport_send": "Statusbericht über {channels} gesendet",
+    "subscription_cancel": "Abonnement zum Ende der Laufzeit gekündigt",
+    "subscription_resume": "Abonnement fortgesetzt",
+    "team_cancelInvitation": "Einladung für {email} zurückgezogen",
+    "team_removeMember": "Teammitglied {id} entfernt",
+    "team_sendInvitation": "{email} als {role, select, owner {Inhaber} admin {Administrator} member {Mitglied} other {{role}}} eingeladen",
+    "team_updateRole": "Rolle eines Mitglieds geändert auf {role, select, owner {Inhaber} admin {Administrator} member {Mitglied} other {{role}}}",
+    "technician_create": "Mechaniker „{name}“ angelegt",
+    "technician_delete": "Mechaniker {id} gelöscht",
+    "technician_update": "Mechaniker {id} aktualisiert",
+    "telegram_send": "Telegram-Nachricht an {name} gesendet",
+    "tire_location_bulk_create": "{count, plural, one {# Reifenlagerplatz} other {# Reifenlagerplätze}} angelegt",
+    "tire_location_create": "Reifenlagerplatz {code} angelegt",
+    "tire_location_delete": "Reifenlagerplatz {code} gelöscht",
+    "tire_location_update": "Reifenlagerplatz {code} aktualisiert",
+    "tire_measurement_create": "{count, plural, one {# Messung} other {# Messungen}} am Reifensatz {ref} erfasst",
+    "tire_set_add_to_work_order": "Reifensatz {ref} zum Arbeitsauftrag {jobRef} hinzugefügt",
+    "tire_set_attach": "{count, plural, one {# Datei} other {# Dateien}} zum Reifensatz {ref} hinzugefügt",
+    "tire_set_check_in": "Reifensatz {ref} auf {code} eingelagert",
+    "tire_set_check_out": "Reifensatz {ref} ausgegeben",
+    "tire_set_create_quote": "Angebot {ref} aus einem Reifensatz erstellt",
+    "tire_set_create_work_order": "Arbeitsauftrag {ref} aus einem Reifensatz erstellt",
+    "tire_set_delete": "Reifensatz {ref} gelöscht",
+    "tire_set_detach": "{fileName} von einem Reifensatz entfernt",
+    "tire_set_dispose": "Reifensatz {ref} entsorgt",
+    "tire_set_message_customer": "Kunden über Reifensatz {ref} per {channel, select, email {E-Mail} sms {SMS} telegram {Telegram} in_app {Hinweis in der App} other {{channel}}} benachrichtigt",
+    "tire_set_relocate": "Reifensatz {ref} von {from} nach {to} umgeräumt",
+    "tire_set_relocateUnassigned": "Reifensatz {ref} auf {to} gestellt",
+    "tire_set_return": "Reifensatz {ref} wieder eingelagert, auf {code}",
+    "tire_set_unlink_work_order": "Reifensatz {ref} vom Arbeitsauftrag {jobRef} getrennt",
+    "tire_set_update": "Reifensatz {ref} aktualisiert",
+    "tire_treatment_complete_all": "{count, plural, one {# Vorbereitungsarbeit} other {# Vorbereitungsarbeiten}} am Reifensatz {ref} abgeschlossen",
+    "tire_treatment_mark": "{type, select, wash_tires {Reifenwäsche} wash_rims {Felgenwäsche} balance {Auswuchten} tpms_service {RDKS-Service} new_valves {neue Ventile} repair {Reparatur} other {{type}}} am Reifensatz {ref} als {status, select, done {erledigt} pending {offen} skipped {übersprungen} other {{status}}} markiert",
+    "tire_treatment_update": "Vorbereitung am Reifensatz {ref} aktualisiert",
+    "tire_warehouse_archive": "Reifenlager {name} archiviert",
+    "tire_warehouse_create": "Reifenlager {name} angelegt",
+    "tire_warehouse_delete": "Reifenlager {name} gelöscht",
+    "tire_warehouse_update": "Reifenlager {name} aktualisiert",
+    "vehicle_archive": "Fahrzeug {id} archiviert",
+    "vehicle_create": "Fahrzeug {year} {make} {model} angelegt",
+    "vehicle_delete": "Fahrzeug {name} gelöscht",
+    "vehicle_unarchive": "Fahrzeug {id} aus dem Archiv geholt",
+    "vehicle_update": "Fahrzeug {name} aktualisiert, geändert: {fields}",
+    "vehicle_updateNoChanges": "Fahrzeug {name} aktualisiert, nichts geändert",
+    "webhook_create": "Webhook {name} erstellt",
+    "whatsapp_send": "WhatsApp-Nachricht an {name} gesendet"
   }
 }

+ 15 - 0
messages/de/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Zum Home-Bildschirm hinzufügen für schnellen Zugriff",
     "enterFullscreen": "Vollbild",
     "exitFullscreen": "Vollbild beenden"
+  },
+  "broadcast": {
+    "dismiss": "Schließen",
+    "title": "Hinweis an alle",
+    "description": "Wird oben auf jeder Seite angezeigt, für alle, ob angemeldet oder nicht. Leer lassen für keinen Hinweis.",
+    "message": "Nachricht",
+    "level": "Dringlichkeit",
+    "post": "Hinweis veröffentlichen",
+    "clear": "Hinweis entfernen",
+    "levelInfo": "Information",
+    "levelWarning": "Warnung",
+    "levelCritical": "Kritisch",
+    "placeholder": "z. B. Wir haben Probleme mit unserer Serverinfrastruktur und arbeiten daran.",
+    "posted": "Hinweis veröffentlicht",
+    "cleared": "Hinweis entfernt"
   }
 }

+ 6 - 2
messages/de/customers.json

@@ -38,7 +38,8 @@
       "messages": "Nachrichten",
       "requests": "Anfragen ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Kennzeichen",
@@ -68,7 +69,10 @@
     "customerCreated": "Kunde angelegt",
     "saveError": "Kunde konnte nicht gespeichert werden",
     "customerNumber": "Kundennr.",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Kontakt",
+    "sectionBilling": "Rechnung",
+    "addScannedVehicle": "Fahrzeug {vehicle} anlegen"
   },
   "serviceRequests": {
     "statusNew": "Neu",

+ 7 - 0
messages/de/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Voraussichtliche Wartung",
     "reminders": "Anstehende Erinnerungen",
     "inspections": "Inspektionen",
+    "serviceRequests": "Serviceanfragen",
     "quoteRequests": "Angebotsanfragen",
     "quoteResponses": "Angebotsantworten",
     "sms": "Letzte Nachrichten",
@@ -68,6 +69,7 @@
     "active": "Aktiv",
     "dismissedTab": "Verworfen",
     "noActive": "Derzeit sind keine Fahrzeuge wartungsfällig.",
+    "noDismissed": "Keine ausgeblendeten Fahrzeuge.",
     "dismissedOn": "Verworfen am {date}",
     "restore": "Wiederherstellen",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} Positionen",
     "noData": "Keine Inspektionen."
   },
+  "serviceRequests": {
+    "title": "Serviceanfragen",
+    "description": "Terminanfragen aus dem Kundenportal",
+    "noData": "Keine offenen Serviceanfragen."
+  },
   "quoteRequests": {
     "title": "Angebotsanfragen",
     "description": "Kunden, die Angebote aus geteilten Inspektionen anfordern",

+ 4 - 1
messages/de/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Senden und empfangen Sie SMS-Nachrichten mit Ihren Kunden direkt über Torqvoice.",
     "tabs": {
       "inbox": "Posteingang",
-      "scheduled": "Geplant"
+      "scheduled": "Geplant",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "SMS ist nicht eingerichtet",
     "smsNotConfiguredHint": "Verbinden Sie einen SMS-Anbieter, um hier Nachrichten zu senden und zu empfangen. Geplante E-Mails funktionieren auch ohne.",

+ 26 - 0
messages/de/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Konversationen",
+    "searchPlaceholder": "Konversationen durchsuchen",
+    "noSearchResults": "Keine Konversation passt zur Suche.",
+    "empty": "Noch keine Konversationen.",
+    "selectConversation": "Konversation auswählen",
+    "back": "Zurück",
+    "newMessage": "Neue Nachricht",
+    "unknownContact": "Diese Nummer gehört noch zu keinem Kunden. Legen Sie ihn als Kunden an, um zu antworten.",
+    "noChannelsTitle": "Kein Nachrichtenkanal eingerichtet",
+    "noChannelsHint": "Verbinden Sie SMS, WhatsApp oder Telegram, um von hier aus zu schreiben.",
+    "goToProviders": "Anbieter einrichten",
+    "chooseChannel": {
+      "title": "Über welchen Kanal senden?",
+      "description": "Sie haben mehrere Wege, Ihre Kunden zu erreichen."
+    },
+    "deleteConversation": "Konversation löschen",
+    "deleteTitle": "Diese Konversation löschen?",
+    "deleteDescription": "Entfernt Ihre Kopie der Konversation mit {name}. Beim Kunden bleiben die Nachrichten erhalten.",
+    "deleteFailed": "Konversation konnte nicht gelöscht werden",
+    "delete": "Löschen",
+    "cancel": "Abbrechen",
+    "loadMore": "Mehr laden"
+  }
+}

+ 4 - 2
messages/de/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Alle Reifensätze",
     "tireStorage": "Lagerstruktur",
     "tireSetDetails": "Reifensatz",
-    "tireForecast": "Nächste Saison"
+    "tireForecast": "Nächste Saison",
+    "whatsapp": "WhatsApp"
   },
   "search": "Suchen...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Mehr",
     "auditLog": "Aktivitätsprotokoll",
     "aiAssistant": "KI-Assistent",
-    "tireHotel": "Reifenhotel"
+    "tireHotel": "Reifenhotel",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/de/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "E-Mail",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "Notiz in der App"
+      "in_app": "Notiz in der App",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Wiederholen",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Planen",
     "scheduled": "Nachricht geplant",
     "updated": "Geplante Nachricht aktualisiert",
-    "saveError": "Nachricht konnte nicht gespeichert werden"
+    "saveError": "Nachricht konnte nicht gespeichert werden",
+    "whatsappNeedsTemplate": "Diese Nachricht geht lange nach Ablauf des 24-Stunden-Fensters raus, und es ist keine WhatsApp-Textvorlage eingerichtet, daher würde sie beim Senden scheitern. Richten Sie eine unter Einstellungen > Anbieter > WhatsApp ein."
   },
   "list": {
     "scheduleMessage": "Nachricht planen",

+ 35 - 3
messages/de/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Reifenhotel",
         "description": "Saisonale Reifeneinlagerung"
+      },
+      "providers": {
+        "title": "Anbieter",
+        "description": "E-Mail, SMS, WhatsApp & Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Inspektionsvorlagen, Inspektionen und Ergebnisse",
     "optAuditLogs": "Prüfprotokolle",
     "optAuditLogsDesc": "Aktivitäts- und Änderungsverlauf",
-    "optSmsMessages": "SMS-Nachrichten",
-    "optSmsMessagesDesc": "Gesendete und empfangene SMS-Nachrichten",
+    "optSmsMessages": "Nachrichten",
+    "optSmsMessagesDesc": "Gesendete und empfangene SMS-, WhatsApp- und Telegram-Nachrichten",
     "optScheduledMessages": "Geplante Nachrichten",
     "optScheduledMessagesDesc": "Wartende und wiederkehrende Nachrichten, die noch gesendet werden",
     "optNotifications": "Benachrichtigungen",
@@ -980,7 +984,9 @@
       "supportAction": "kontaktieren Sie unser Support-Team."
     },
     "optTireHotel": "Reifenhotel",
-    "optTireHotelDesc": "Eingelagerte Reifensätze, Regale, Vorbereitung und Lagervereinbarungen"
+    "optTireHotelDesc": "Eingelagerte Reifensätze, Regale, Vorbereitung und Lagervereinbarungen",
+    "optWorkshopConfig": "Werkstatt-Einrichtung",
+    "optWorkshopConfigDesc": "Arbeitsvorlagen, Rollen, Webhooks und geplante Berichte"
   },
   "license": {
     "title": "White-Label-Lizenz",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Preise für Vorbereitung",
     "prepHint": "Was jede Art von Vorbereitung kostet. Leer bedeutet, sie wird nie berechnet, was zu Betrieben passt, die das Waschen in der Lagergebühr enthalten haben. Bepreiste werden automatisch auf Angebot und Auftrag gesetzt.",
     "prepNotCharged": "nicht berechnet"
+  },
+  "providers": {
+    "tabs": {
+      "email": "E-Mail",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "E-Mail-Einstellungen",
+        "description": "SMTP-Versand einrichten, um Rechnungen, Angebote und Benachrichtigungen direkt aus der Werkstatt zu senden."
+      },
+      "sms": {
+        "feature": "SMS-Nachrichten",
+        "description": "SMS mit Ihren Kunden direkt aus Torqvoice senden und empfangen."
+      },
+      "whatsapp": {
+        "feature": "WhatsApp-Nachrichten",
+        "description": "WhatsApp-Nachrichten samt Fotos von Bauteilen direkt aus Torqvoice senden und empfangen."
+      },
+      "telegram": {
+        "feature": "Telegram-Nachrichten",
+        "description": "Telegram-Nachrichten mit Ihren Kunden direkt aus Torqvoice senden und empfangen."
+      }
+    }
   }
 }

+ 2 - 1
messages/de/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Überspringen",
     "send": "Senden",
     "sent": "Statusbericht gesendet",
-    "failed": "Statusbericht konnte nicht gesendet werden"
+    "failed": "Statusbericht konnte nicht gesendet werden",
+    "notSent": "Nicht gesendet"
   },
   "view": {
     "title": "Fahrzeug-Statusbericht",

+ 16 - 1
messages/de/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Registrierungsnummer",
     "vehicleUpdated": "Fahrzeug aktualisiert",
     "vehicleAdded": "Fahrzeug hinzugefügt",
-    "saveError": "Fehler beim Speichern des Fahrzeugs"
+    "saveError": "Fehler beim Speichern des Fahrzeugs",
+    "scanDocument": "Fahrzeugschein scannen",
+    "scanDocumentHint": "Fahrzeugpapiere abfotografieren, die Felder werden automatisch ausgefüllt.",
+    "scanningDocument": "Dokument wird gelesen...",
+    "scanSuccess": "Felder aus dem Dokument übernommen",
+    "scanFailed": "Dokument konnte nicht gelesen werden",
+    "ownerMatchTitle": "Fahrzeug zuordnen",
+    "ownerMatchDescription": "Im Dokument steht {name}. Wählen Sie einen dieser Kunden oder legen Sie einen neuen an.",
+    "ownerMatchScore": "{percent}% Übereinstimmung",
+    "ownerMatchCreateNew": "Neuen Kunden anlegen",
+    "ownerMatchChoose": "Kunde auswählen",
+    "scanUnavailable": "KI in den Einstellungen aktivieren",
+    "sectionAssignment": "Foto und Halter",
+    "sectionDetails": "Details",
+    "addScannedOwner": "Kunde {name} anlegen",
+    "ownerSaveFailed": "Kunde konnte nicht angelegt werden"
   },
   "archiveDialog": {
     "title": "Fahrzeug archivieren",

+ 179 - 0
messages/de/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "WhatsApp-Nachrichten mit Ihren Kunden senden und empfangen.",
+    "helpLink": "{provider} einrichten",
+    "save": "WhatsApp-Einstellungen speichern",
+    "saved": "WhatsApp-Einstellungen gespeichert",
+    "saveError": "WhatsApp-Einstellungen konnten nicht gespeichert werden",
+    "enable": {
+      "label": "WhatsApp aktivieren",
+      "hint": "Einschalten, sobald die Zugangsdaten des Anbieters hinterlegt sind.",
+      "disabledInfo": "Schalten Sie WhatsApp ein, um Anbieter, Nummer und Vorlage einzurichten."
+    },
+    "provider": {
+      "label": "Anbieter",
+      "hint": "Wer Ihre WhatsApp-Nummer bereitstellt. Beim Wechsel bleiben die Zugangsdaten des alten Anbieters erhalten."
+    },
+    "from": {
+      "label": "Ihre WhatsApp-Nummer",
+      "hint": "Die Nummer, die Kunden sehen, im internationalen Format."
+    },
+    "secret": {
+      "show": "Anzeigen",
+      "hide": "Verbergen",
+      "stored": "Gespeichert und verborgen. Neuen Wert eingeben, um ihn zu ersetzen."
+    },
+    "webhook": {
+      "label": "Webhook-URL",
+      "description": "Diese URL beim Anbieter eintragen, damit eingehende Nachrichten in Torqvoice ankommen.",
+      "copy": "Webhook-URL kopieren",
+      "copied": "Webhook-URL kopiert",
+      "saveFirst": "Speichern Sie diese Einstellungen, bevor Ihr Anbieter die URL prüft. Geprüft wird gegen das hier Gespeicherte, ein ungespeichertes Formular fällt durch."
+    },
+    "template": {
+      "title": "Nachrichtenvorlage",
+      "description": "Wird verwendet, wenn Sie einem Kunden schreiben, der sich länger nicht gemeldet hat.",
+      "windowExplainer": "WhatsApp erlaubt freien Text nur 24 Stunden nach der letzten Kundennachricht. Danach muss Ihre Nachricht als vom Anbieter freigegebene Vorlage rausgehen.",
+      "nameLabel": "Name der Vorlage",
+      "nameHint": "Name der freigegebenen Vorlage, bei manchen Anbietern die Content-SID.",
+      "languageLabel": "Sprache der Vorlage",
+      "languageHint": "Sprachcode, unter dem die Vorlage freigegeben wurde, zum Beispiel de.",
+      "variablesLabel": "Werte der Vorlage, in Reihenfolge",
+      "variablesHint": "Wählen Sie die Werte, die die nummerierten Platzhalter Ihrer Vorlage füllen, in derselben Reihenfolge. Leer lassen, wenn die Vorlage keine Platzhalter hat.",
+      "tokens": {
+        "customer": "Kundenname",
+        "vehicle": "Baujahr, Marke und Modell",
+        "plate": "Kennzeichen",
+        "message": "Ihr Text",
+        "workshop": "Name Ihrer Werkstatt",
+        "photo": "das angehängte Foto"
+      },
+      "variablesEmpty": "Noch keine Werte. Unten in der Reihenfolge Ihrer Vorlage hinzufügen.",
+      "variablesRemove": "{name} entfernen",
+      "mediaUrlLabel": "Medien-URL für Ihre Vorlage",
+      "mediaUrlHint": "Fügen Sie dies in das Medienfeld der Vorlage ein und ersetzen Sie n durch die Nummer des Foto-Werts.",
+      "mediaUrlCopy": "Medien-URL kopieren",
+      "mediaUrlCopied": "Medien-URL kopiert",
+      "mediaUrlSample": "Als Beispielwert beim Anbieter verwenden: sample",
+      "textTitle": "Textnachrichten",
+      "textDescription": "Wird verwendet, wenn Sie ohne Foto schreiben: aus Nachrichten, aus dem WhatsApp-Tab beim Kunden oder aus einer geplanten Nachricht.",
+      "mediaTitle": "Nachrichten mit Foto",
+      "mediaDescription": "Wird vom Button „Per WhatsApp senden“ bei den Fotos eines Auftrags verwendet und immer, wenn Sie in einer Konversation ein Foto anhängen.",
+      "textMissing": "Nicht eingerichtet: Sie können Kunden, die sich seit 24 Stunden nicht gemeldet haben, nicht schreiben.",
+      "mediaMissing": "Nicht eingerichtet: Sie können Kunden, die sich seit 24 Stunden nicht gemeldet haben, keine Fotos senden.",
+      "whatIsIt": "Eine Vorlage ist eine feste Nachricht, die Sie einmal schreiben und die {provider} freigibt: Der Wortlaut ändert sich nie, nur die nummerierten Platzhalter füllen Sie. Schreiben Sie sie in deren Konsole und fügen Sie hier die Kennung ein.",
+      "whyTwo": "Zwei sind nötig, weil WhatsApp den Medientyp bei der Freigabe festlegt. Eine mit Foto freigegebene Vorlage kann nur Fotos tragen, eine als Text freigegebene nie eines.",
+      "exampleLabel": "Beispieltext für die Konsole Ihres Anbieters",
+      "exampleTextValues": "Zu füllen mit: customer, message. Schreiben Sie ihn in der Sprache Ihrer Kunden.",
+      "exampleMediaValues": "Zu füllen mit: customer, message und photo für die Medien-URL. Schreiben Sie ihn in der Sprache Ihrer Kunden.",
+      "exampleMediaValuesHeader": "Füllen Sie sie mit: customer, message. Das Foto wird automatisch angehängt und braucht keine eigene Lücke. Schreiben Sie sie in der Sprache Ihrer Kundschaft."
+    },
+    "test": {
+      "send": "Test senden",
+      "sent": "Testnachricht gesendet",
+      "failed": "Testnachricht konnte nicht gesendet werden",
+      "hint": "Schreiben Sie sich zuerst selbst über WhatsApp an, sonst braucht der Test eine freigegebene Vorlage."
+    },
+    "disconnect": {
+      "action": "WhatsApp trennen",
+      "hint": "Entfernt die Zugangsdaten aus Torqvoice. Ihre Konversationen bleiben erhalten.",
+      "done": "WhatsApp getrennt",
+      "title": "Trennen"
+    },
+    "savedIncomplete": "Gespeichert. Zum Senden fehlt noch: {fields}",
+    "fieldStillNeeded": "Zum Senden noch erforderlich",
+    "steps": {
+      "openProvider": "{provider} öffnen",
+      "openNumber": "Absendernummer finden",
+      "openToken": "Dauerhaftes Token erstellen",
+      "consolePath": "Gehen Sie in Meta zu:",
+      "provider": {
+        "title": "Anbieter und Nummer wählen",
+        "description": "Twilio ist schneller eingerichtet und übernimmt Meta für Sie. Die Meta Cloud API kostet weniger, dafür durchlaufen Sie die Unternehmensverifizierung selbst. Tragen Sie die Nummer ein, die Kunden sehen, im internationalen Format."
+      },
+      "credentials": {
+        "title": "Zugangsdaten eintragen",
+        "description": "Kopieren Sie jeden Wert aus {provider}. Jedes Feld unten sagt genau, wo er zu finden ist. Speichern Sie, sobald Sie sie haben; den Rest können Sie später ergänzen."
+      },
+      "webhook": {
+        "title": "Webhook eintragen",
+        "description": "Kopieren Sie diese Adresse zu {provider}, damit Nachrichten von Kunden bei Torqvoice ankommen. Speichern Sie zuerst diese Seite: Die Adresse wird gegen das hier Gespeicherte geprüft.",
+        "open": "Webhook-Einstellungen bei {provider} öffnen",
+        "recheck": "Erneut prüfen",
+        "waiting": "An dieser Adresse ist noch nichts angekommen. Sie wird grün, sobald Ihr Anbieter sie bestätigt oder eine Nachricht zustellt.",
+        "seen": "Ihr Anbieter hat diese Adresse erreicht. Eingehende Nachrichten erscheinen unter Nachrichten."
+      },
+      "templates": {
+        "title": "Vorlagen freigeben lassen",
+        "description": "Nur nötig, um außerhalb des 24-Stunden-Fensters zuerst zu schreiben. Sie können das überspringen und zurückkommen, sobald der Nachrichtenversand läuft.",
+        "open": "Vorlagen bei {provider} öffnen"
+      },
+      "test": {
+        "title": "Testnachricht senden",
+        "description": "Schreiben Sie zuerst vom eigenen Handy an Ihre WhatsApp-Nummer. Das beweist, dass der Webhook funktioniert, und öffnet ein 24-Stunden-Fenster, sodass eine Antwort keine Vorlage braucht."
+      },
+      "back": "Zurück",
+      "continue": "Weiter"
+    },
+    "register": {
+      "title": "Nummer bei Meta registrieren",
+      "description": "Meta verlangt nach der Verifizierung eine gesonderte Registrierung. Wählen Sie eine sechsstellige PIN, oder nehmen Sie die vorhandene PIN der Bestätigung in zwei Schritten. Notieren Sie sie: Sie brauchen sie, um die Nummer später zu verschieben oder neu zu registrieren.",
+      "action": "Nummer registrieren",
+      "done": "Nummer registriert",
+      "failed": "Nummer konnte nicht registriert werden",
+      "limit": "Meta erlaubt 10 Registrierungsversuche pro Nummer in 72 Stunden. Eine falsche PIN zählt mit, prüfen Sie sie also vorher."
+    }
+  },
+  "messages": {
+    "featureName": "WhatsApp-Nachrichten",
+    "featureDescription": "WhatsApp-Nachrichten samt Fotos von Bauteilen direkt aus Torqvoice senden und empfangen.",
+    "noThreads": "Noch keine Konversationen",
+    "noThreadsHint": "Nachrichten erscheinen hier, sobald ein Kunde an Ihre WhatsApp-Nummer schreibt oder Sie ihm schreiben.",
+    "selectThread": "Konversation auswählen",
+    "backToThreads": "Konversationen",
+    "empty": "Hier ist noch nichts.",
+    "placeholder": "Nachricht schreiben",
+    "send": "Senden",
+    "sendFailed": "Nachricht konnte nicht gesendet werden",
+    "attach": "Foto anhängen",
+    "attachFailed": "Foto konnte nicht angehängt werden",
+    "removeAttachment": "Anhang entfernen",
+    "attachment": "Anhang",
+    "delete": "Löschen",
+    "deleteFailed": "Nachricht konnte nicht gelöscht werden",
+    "sentAsTemplate": "Vorlage",
+    "sentAsTemplateNotice": "Als freigegebene Vorlage gesendet, weil der Kunde in den letzten 24 Stunden nicht geschrieben hat.",
+    "windowClosed": "Der Kunde hat in den letzten 24 Stunden nicht geschrieben, daher geht dies als freigegebene Vorlage raus.",
+    "unknownNumber": "Diese Nummer gehört noch zu keinem Kunden. Legen Sie ihn als Kunden an, um zu antworten.",
+    "notConfigured": {
+      "title": "WhatsApp ist nicht verbunden",
+      "description": "Verbinden Sie eine WhatsApp-Nummer, um Nachrichten mit Ihren Kunden auszutauschen.",
+      "action": "WhatsApp einrichten"
+    },
+    "title": "Konversationen",
+    "searchPlaceholder": "Konversationen durchsuchen",
+    "noSearchResults": "Keine Konversation passt zur Suche.",
+    "compose": {
+      "newMessage": "Neue Nachricht",
+      "title": "Neue WhatsApp-Nachricht",
+      "searchPlaceholder": "Kunden suchen",
+      "noMatches": "Kein Kunde passt zur Suche.",
+      "noRecipients": "Noch kein Kunde mit Telefonnummer."
+    },
+    "windowBlocked": "Dieser Kunde hat in den letzten 24 Stunden nicht geschrieben, daher stellt WhatsApp jetzt keine Nachricht zu. Bitten Sie ihn zu schreiben, oder richten Sie eine freigegebene Vorlage ein.",
+    "windowBlockedAction": "Vorlage einrichten",
+    "windowBlockedPlaceholder": "Warten, bis der Kunde zuerst schreibt"
+  },
+  "sendPhoto": {
+    "action": "Per WhatsApp senden",
+    "title": "Foto per WhatsApp senden",
+    "description": "Der Kunde erhält dieses Foto in seinem WhatsApp-Chat.",
+    "captionPlaceholder": "Hinweis ergänzen, zum Beispiel was das Foto zeigt",
+    "send": "Senden",
+    "cancel": "Abbrechen",
+    "sent": "Foto gesendet",
+    "sentAsTemplate": "Foto als freigegebene Vorlage gesendet",
+    "failed": "Foto konnte nicht gesendet werden"
+  }
+}

+ 122 - 1
messages/en/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Stored a set again",
     "tire_set_dispose": "Wrote off a tire set",
     "tire_set_attach": "Added files to a tire set",
-    "tire_set_detach": "Removed a file from a tire set"
+    "tire_set_detach": "Removed a file from a tire set",
+    "whatsapp_send": "Sent WhatsApp Message"
+  },
+  "summary": {
+    "customField_create": "Created custom field \"{name}\"",
+    "customField_delete": "Deleted custom field \"{name}\"",
+    "customField_update": "Updated custom field \"{name}\"",
+    "customer_backfillNumbers": "Assigned customer numbers to {count, plural, one {# customer} other {# customers}}",
+    "customer_create": "Created customer {name}",
+    "customer_delete": "Deleted customer {id}",
+    "customer_update": "Updated customer {id}",
+    "email_sendInspection": "Sent inspection email to {recipient}",
+    "email_sendInvoice": "Sent invoice email to {recipient}",
+    "email_sendQuote": "Sent quote email to {recipient}",
+    "finding_create": "Created finding \"{description}\" on vehicle {vehicleId}",
+    "finding_delete": "Deleted finding {id}",
+    "finding_resolve": "Resolved finding \"{description}\"",
+    "finding_update": "Updated finding \"{description}\"",
+    "inspectionTemplate_create": "Created inspection template \"{name}\"",
+    "inspectionTemplate_createFromPreset": "Created inspection template \"{name}\" from a preset",
+    "inspectionTemplate_delete": "Deleted inspection template \"{name}\"",
+    "inspectionTemplate_duplicate": "Duplicated inspection template into \"{name}\"",
+    "inspectionTemplate_import": "Imported {count, plural, one {# inspection template} other {# inspection templates}} from a package",
+    "inspectionTemplate_update": "Updated inspection template \"{name}\"",
+    "inspection_complete": "Completed inspection {id}",
+    "inspection_create": "Created inspection {id}",
+    "inspection_delete": "Deleted inspection {id}",
+    "inspection_reopen": "Reopened inspection {id}",
+    "inventory_bulkDelete": "Bulk deleted {count, plural, one {# inventory part} other {# inventory parts}}",
+    "inventory_create": "Created inventory part \"{name}\"",
+    "inventory_delete": "Deleted inventory part {id}",
+    "inventory_update": "Updated inventory part {id}",
+    "labor_preset_create": "Created labor preset \"{name}\"",
+    "labor_preset_delete": "Deleted labor preset {id}",
+    "labor_preset_update": "Updated labor preset {id}",
+    "note_create": "Created note on vehicle {vehicleId}",
+    "note_delete": "Deleted note {id}",
+    "organization_create": "Created organization \"{name}\"",
+    "organization_sampleDataRemove": "Removed onboarding sample data",
+    "payment_create": "Recorded payment {amount} for service {serviceRecordId}",
+    "payment_delete": "Deleted payment {id}",
+    "quoteRequest_update": "Updated quote request status to {status, select, pending {pending} approved {approved} rejected {rejected} completed {completed} other {{status}}}",
+    "quote_convert": "Converted quote {ref} to service record {serviceRecordId}",
+    "quote_create": "Created quote {ref}",
+    "quote_delete": "Deleted quote {ref}",
+    "quote_status": "Changed quote status to {status, select, draft {draft} sent {sent} accepted {accepted} rejected {rejected} expired {expired} converted {converted} other {{status}}}",
+    "quote_update": "Updated quote {ref}",
+    "recurringInvoice_create": "Created recurring invoice \"{title}\"",
+    "recurringInvoice_delete": "Deleted recurring invoice {id}",
+    "recurringInvoice_update": "Updated recurring invoice \"{title}\"",
+    "reminder_create": "Created reminder \"{title}\"",
+    "reminder_delete": "Deleted reminder {id}",
+    "role_create": "Created role \"{name}\"",
+    "role_delete": "Deleted role \"{name}\"",
+    "role_update": "Updated role \"{name}\"",
+    "scheduled_message_cancel": "Cancelled a scheduled message",
+    "scheduled_message_create": "Scheduled a {channel, select, email {email} sms {text message} telegram {Telegram message} in_app {in-app note} other {{channel}}} for {sendAt}",
+    "scheduled_message_delete": "Deleted a scheduled message",
+    "scheduled_message_send_now": "Sent a scheduled {channel, select, email {email} sms {text message} telegram {Telegram message} in_app {in-app note} other {{channel}}} ahead of time",
+    "scheduled_message_update": "Updated a scheduled {channel, select, email {email} sms {text message} telegram {Telegram message} in_app {in-app note} other {{channel}}}",
+    "service_create": "Created service record {ref}",
+    "service_createCounterSale": "Created counter sale {ref}",
+    "service_createDraft": "Created draft service record {ref}",
+    "service_createFromInspection": "Created work order {ref} from an inspection",
+    "service_delete": "Deleted service record {ref}",
+    "service_status": "Changed service record status to {status, select, pending {pending} scheduled {scheduled} in_progress {in progress} waiting_parts {waiting for parts} ready {ready} completed {completed} cancelled {cancelled} other {{status}}}",
+    "service_update": "Updated service record {ref}",
+    "settings_applyTaxRate": "Applied {rate}% tax to {records, plural, one {# service record} other {# service records}} and {quotes, plural, one {# quote} other {# quotes}}",
+    "settings_convertToExclusive": "Converted {records, plural, one {# service record} other {# service records}} and {quotes, plural, one {# quote} other {# quotes}} to tax-exclusive prices",
+    "settings_convertToInclusive": "Converted {records, plural, one {# service record} other {# service records}} and {quotes, plural, one {# quote} other {# quotes}} to tax-inclusive prices",
+    "settings_deleteContent": "Deleted all data: {items}",
+    "settings_updateInvoiceLayout": "Updated the invoice layout",
+    "settings_updateQuoteLayout": "Updated the quote layout",
+    "sms_send": "Sent a text message to {name} ({phone})",
+    "statusReport_create": "Created a status report for service {serviceRecordId}",
+    "statusReport_delete": "Deleted a status report",
+    "statusReport_send": "Sent a status report via {channels}",
+    "subscription_cancel": "Cancelled the subscription, effective at the end of the period",
+    "subscription_resume": "Resumed the subscription",
+    "team_cancelInvitation": "Cancelled the invitation for {email}",
+    "team_removeMember": "Removed team member {id}",
+    "team_sendInvitation": "Invited {email} as {role, select, owner {owner} admin {admin} member {member} other {{role}}}",
+    "team_updateRole": "Changed a member's role to {role, select, owner {owner} admin {admin} member {member} other {{role}}}",
+    "technician_create": "Created technician \"{name}\"",
+    "technician_delete": "Deleted technician {id}",
+    "technician_update": "Updated technician {id}",
+    "telegram_send": "Sent a Telegram message to {name}",
+    "tire_location_bulk_create": "Created {count, plural, one {# tire storage location} other {# tire storage locations}}",
+    "tire_location_create": "Created tire storage location {code}",
+    "tire_location_delete": "Deleted tire storage location {code}",
+    "tire_location_update": "Updated tire storage location {code}",
+    "tire_measurement_create": "Recorded {count, plural, one {# reading} other {# readings}} on tire set {ref}",
+    "tire_set_add_to_work_order": "Added tire set {ref} to work order {jobRef}",
+    "tire_set_attach": "Added {count, plural, one {# file} other {# files}} to tire set {ref}",
+    "tire_set_check_in": "Checked in tire set {ref} to {code}",
+    "tire_set_check_out": "Checked out tire set {ref}",
+    "tire_set_create_quote": "Created quote {ref} from a tire set",
+    "tire_set_create_work_order": "Created work order {ref} from a tire set",
+    "tire_set_delete": "Deleted tire set {ref}",
+    "tire_set_detach": "Removed {fileName} from a tire set",
+    "tire_set_dispose": "Wrote off tire set {ref}",
+    "tire_set_message_customer": "Messaged the customer about tire set {ref} by {channel, select, email {email} sms {text message} telegram {Telegram} in_app {in-app note} other {{channel}}}",
+    "tire_set_relocate": "Moved tire set {ref} from {from} to {to}",
+    "tire_set_relocateUnassigned": "Moved tire set {ref} onto {to}",
+    "tire_set_return": "Stored tire set {ref} again, on {code}",
+    "tire_set_unlink_work_order": "Unlinked tire set {ref} from work order {jobRef}",
+    "tire_set_update": "Updated tire set {ref}",
+    "tire_treatment_complete_all": "Completed {count, plural, one {# prep job} other {# prep jobs}} on tire set {ref}",
+    "tire_treatment_mark": "Marked {type, select, wash_tires {the tire wash} wash_rims {the rim wash} balance {balancing} tpms_service {the TPMS service} new_valves {new valves} repair {the repair} other {{type}}} as {status, select, done {done} pending {still to do} skipped {skipped} other {{status}}} on tire set {ref}",
+    "tire_treatment_update": "Updated prep work on tire set {ref}",
+    "tire_warehouse_archive": "Archived tire warehouse {name}",
+    "tire_warehouse_create": "Created tire warehouse {name}",
+    "tire_warehouse_delete": "Deleted tire warehouse {name}",
+    "tire_warehouse_update": "Updated tire warehouse {name}",
+    "vehicle_archive": "Archived vehicle {id}",
+    "vehicle_create": "Created vehicle {year} {make} {model}",
+    "vehicle_delete": "Deleted vehicle {name}",
+    "vehicle_unarchive": "Unarchived vehicle {id}",
+    "vehicle_update": "Updated vehicle {name}, changed: {fields}",
+    "vehicle_updateNoChanges": "Updated vehicle {name}, nothing changed",
+    "webhook_create": "Created webhook {name}",
+    "whatsapp_send": "Sent a WhatsApp message to {name}"
   }
 }

+ 15 - 0
messages/en/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Add to your home screen for quick access",
     "enterFullscreen": "Fullscreen",
     "exitFullscreen": "Exit fullscreen"
+  },
+  "broadcast": {
+    "dismiss": "Dismiss",
+    "title": "Notice to everyone",
+    "description": "Shown at the top of every page, for everyone, signed in or not. Leave it empty for no notice.",
+    "message": "Message",
+    "level": "Severity",
+    "post": "Post notice",
+    "clear": "Clear notice",
+    "levelInfo": "Information",
+    "levelWarning": "Warning",
+    "levelCritical": "Critical",
+    "placeholder": "e.g. We are having trouble with our server infrastructure and are working on it.",
+    "posted": "Notice posted",
+    "cleared": "Notice cleared"
   }
 }

+ 6 - 2
messages/en/customers.json

@@ -38,7 +38,8 @@
       "messages": "Messages",
       "requests": "Requests ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Plate",
@@ -68,7 +69,10 @@
     "customerCreated": "Customer created",
     "saveError": "Failed to save customer",
     "customerNumber": "Customer no.",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Contact",
+    "sectionBilling": "Billing",
+    "addScannedVehicle": "Add vehicle {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "New",

+ 7 - 0
messages/en/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Predicted Maintenance",
     "reminders": "Upcoming Reminders",
     "inspections": "Inspections",
+    "serviceRequests": "Service Requests",
     "quoteRequests": "Quote Requests",
     "quoteResponses": "Quote Responses",
     "sms": "Recent Messages",
@@ -68,6 +69,7 @@
     "active": "Active",
     "dismissedTab": "Dismissed",
     "noActive": "No vehicles currently due for service.",
+    "noDismissed": "No dismissed vehicles.",
     "dismissedOn": "Dismissed {date}",
     "restore": "Restore",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} items",
     "noData": "No inspections."
   },
+  "serviceRequests": {
+    "title": "Service Requests",
+    "description": "Booking requests from the customer portal",
+    "noData": "No pending service requests."
+  },
   "quoteRequests": {
     "title": "Quote Requests",
     "description": "Customers requesting quotes from shared inspections",

+ 4 - 1
messages/en/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Send and receive SMS messages with your customers directly from Torqvoice.",
     "tabs": {
       "inbox": "Inbox",
-      "scheduled": "Scheduled"
+      "scheduled": "Scheduled",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "SMS is not set up",
     "smsNotConfiguredHint": "Connect an SMS provider to send and receive texts here. Scheduled email still works without it.",

+ 26 - 0
messages/en/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Conversations",
+    "searchPlaceholder": "Search conversations",
+    "noSearchResults": "No conversations match your search.",
+    "empty": "No conversations yet.",
+    "selectConversation": "Pick a conversation",
+    "back": "Back",
+    "newMessage": "New message",
+    "unknownContact": "This number belongs to no customer yet. Add them as a customer to reply.",
+    "noChannelsTitle": "No messaging channel set up",
+    "noChannelsHint": "Connect SMS, WhatsApp or Telegram to write to your customers from here.",
+    "goToProviders": "Set up a provider",
+    "chooseChannel": {
+      "title": "Send on which channel?",
+      "description": "You have more than one way to reach your customers."
+    },
+    "deleteConversation": "Delete conversation",
+    "deleteTitle": "Delete this conversation?",
+    "deleteDescription": "Removes your copy of the conversation with {name}. The messages stay on their phone.",
+    "deleteFailed": "Could not delete the conversation",
+    "delete": "Delete",
+    "cancel": "Cancel",
+    "loadMore": "Load more"
+  }
+}

+ 4 - 2
messages/en/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "All Tire Sets",
     "tireStorage": "Storage Layout",
     "tireSetDetails": "Tire Set",
-    "tireForecast": "Next season"
+    "tireForecast": "Next season",
+    "whatsapp": "WhatsApp"
   },
   "search": "Search...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "More",
     "auditLog": "Audit Log",
     "aiAssistant": "AI Assistant",
-    "tireHotel": "Tire hotel"
+    "tireHotel": "Tire hotel",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/en/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "Email",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "In-app note"
+      "in_app": "In-app note",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Repeat",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Schedule",
     "scheduled": "Message scheduled",
     "updated": "Scheduled message updated",
-    "saveError": "Could not save the message"
+    "saveError": "Could not save the message",
+    "whatsappNeedsTemplate": "This will be sent long after the customer's 24 hour window closes, and no WhatsApp text template is set up, so it would fail at send time. Set one up in Settings > Providers > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Schedule message",

+ 35 - 3
messages/en/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Tire hotel",
         "description": "Seasonal tire storage"
+      },
+      "providers": {
+        "title": "Providers",
+        "description": "Email, SMS, WhatsApp & Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Inspection templates, inspections, and results",
     "optAuditLogs": "Audit Logs",
     "optAuditLogsDesc": "Activity and change history",
-    "optSmsMessages": "SMS Messages",
-    "optSmsMessagesDesc": "Sent and received SMS messages",
+    "optSmsMessages": "Messages",
+    "optSmsMessagesDesc": "Sent and received SMS, WhatsApp and Telegram messages",
     "optScheduledMessages": "Scheduled Messages",
     "optScheduledMessagesDesc": "Queued and repeating messages waiting to go out",
     "optNotifications": "Notifications",
@@ -980,7 +984,9 @@
       "supportAction": "contact our support team."
     },
     "optTireHotel": "Tire hotel",
-    "optTireHotelDesc": "Stored tire sets, shelves, prep work and storage agreements"
+    "optTireHotelDesc": "Stored tire sets, shelves, prep work and storage agreements",
+    "optWorkshopConfig": "Workshop setup",
+    "optWorkshopConfigDesc": "Labour presets, roles, webhooks and scheduled reports"
   },
   "license": {
     "title": "White-Label License",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Prep pricing",
     "prepHint": "What each kind of prep costs. Leave one blank and it is never billed, which is what a shop that includes washing in the storage fee wants. Priced ones are added to a quote or work order automatically.",
     "prepNotCharged": "not charged"
+  },
+  "providers": {
+    "tabs": {
+      "email": "Email",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "Email Settings",
+        "description": "Configure SMTP email sending to deliver invoices, quotes, and notifications directly from your workshop."
+      },
+      "sms": {
+        "feature": "SMS Messaging",
+        "description": "Send and receive SMS messages with your customers directly from Torqvoice."
+      },
+      "whatsapp": {
+        "feature": "WhatsApp Messaging",
+        "description": "Send and receive WhatsApp messages, including photos of parts, directly from Torqvoice."
+      },
+      "telegram": {
+        "feature": "Telegram Messaging",
+        "description": "Send and receive Telegram messages with your customers directly from Torqvoice."
+      }
+    }
   }
 }

+ 2 - 1
messages/en/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Skip",
     "send": "Send",
     "sent": "Status report sent",
-    "failed": "Failed to send status report"
+    "failed": "Failed to send status report",
+    "notSent": "Not sent"
   },
   "view": {
     "title": "Vehicle Status Report",

+ 16 - 1
messages/en/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Registration Number",
     "vehicleUpdated": "Vehicle updated",
     "vehicleAdded": "Vehicle added",
-    "saveError": "Failed to save vehicle"
+    "saveError": "Failed to save vehicle",
+    "scanDocument": "Scan registration document",
+    "scanDocumentHint": "Photograph the vehicle papers and the fields fill in automatically.",
+    "scanningDocument": "Reading document...",
+    "scanSuccess": "Fields filled from the document",
+    "scanFailed": "Could not read the document",
+    "ownerMatchTitle": "Assign this vehicle",
+    "ownerMatchDescription": "The document lists {name}. Use one of these customers or create a new one.",
+    "ownerMatchScore": "{percent}% match",
+    "ownerMatchCreateNew": "Create new customer",
+    "ownerMatchChoose": "Choose customer",
+    "scanUnavailable": "Enable AI in settings",
+    "sectionAssignment": "Photo and owner",
+    "sectionDetails": "Details",
+    "addScannedOwner": "Add customer {name}",
+    "ownerSaveFailed": "Could not create the customer"
   },
   "archiveDialog": {
     "title": "Archive Vehicle",

+ 179 - 0
messages/en/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "Send and receive WhatsApp messages with your customers.",
+    "helpLink": "Set up {provider}",
+    "save": "Save WhatsApp settings",
+    "saved": "WhatsApp settings saved",
+    "saveError": "Could not save WhatsApp settings",
+    "enable": {
+      "label": "Enable WhatsApp",
+      "hint": "Turn on once your provider credentials are in place.",
+      "disabledInfo": "Switch WhatsApp on to set up your provider, number and template."
+    },
+    "provider": {
+      "label": "Provider",
+      "hint": "Who supplies your WhatsApp number. Switching keeps the credentials of the old one."
+    },
+    "from": {
+      "label": "Your WhatsApp number",
+      "hint": "The business number customers see, in international format."
+    },
+    "secret": {
+      "show": "Show",
+      "hide": "Hide",
+      "stored": "Saved and hidden. Type a new value to replace it."
+    },
+    "webhook": {
+      "label": "Webhook URL",
+      "description": "Paste this into your provider so incoming messages reach Torqvoice.",
+      "copy": "Copy webhook URL",
+      "copied": "Webhook URL copied",
+      "saveFirst": "Save these settings before your provider verifies the URL. It is checked against what is stored here, so an unsaved form fails the check."
+    },
+    "template": {
+      "title": "Message template",
+      "description": "Used when you write to a customer who has not messaged you recently.",
+      "windowExplainer": "WhatsApp only allows free text for 24 hours after the customer's last message. Outside that window your message has to go out as a template approved by your provider.",
+      "nameLabel": "Template name",
+      "nameHint": "The approved template's name, or its Content SID at providers that use one.",
+      "languageLabel": "Template language",
+      "languageHint": "The language code the template was approved under, for example de.",
+      "variablesLabel": "Template values, in order",
+      "variablesHint": "Pick the values that fill your template's numbered blanks, in the same order. Leave empty if your template has no blanks.",
+      "tokens": {
+        "customer": "customer name",
+        "vehicle": "year, make and model",
+        "plate": "registration",
+        "message": "what you type",
+        "workshop": "your shop name",
+        "photo": "the attached photo"
+      },
+      "variablesEmpty": "No values yet. Add them below, in the order your template expects.",
+      "variablesRemove": "Remove {name}",
+      "mediaUrlLabel": "Media URL for your template",
+      "mediaUrlHint": "Paste this into the template's media field and replace n with the number of the photo value.",
+      "mediaUrlCopy": "Copy media URL",
+      "mediaUrlCopied": "Media URL copied",
+      "mediaUrlSample": "For the provider's sample value, use: sample",
+      "textTitle": "Text messages",
+      "textDescription": "Used when you write with no photo attached: from Messages, from the WhatsApp tab on a customer, or from a scheduled message.",
+      "mediaTitle": "Messages with a photo",
+      "mediaDescription": "Used by the Send on WhatsApp button on a job's photos, and whenever you attach a photo in a conversation.",
+      "textMissing": "Not set up: you cannot write to a customer who has not messaged in the last 24 hours.",
+      "mediaMissing": "Not set up: you cannot send photos to a customer who has not messaged in the last 24 hours.",
+      "whatIsIt": "A template is a fixed message you write once and {provider} has approved: the wording never changes, only the numbered blanks you fill in. Write it in their console, then paste its identifier here.",
+      "whyTwo": "Two are needed because WhatsApp fixes a template's media type when it approves it. One approved with a photo can only ever carry a photo, and one approved as text can never carry one.",
+      "exampleLabel": "Example body to write in your provider's console",
+      "exampleTextValues": "Fill it with: customer, message. Write it in the language your customers read.",
+      "exampleMediaValues": "Fill it with: customer, message, and photo for the media URL. Write it in the language your customers read.",
+      "exampleMediaValuesHeader": "Fill it with: customer, message. The photo is attached automatically, so it needs no blank of its own. Write it in the language your customers read."
+    },
+    "test": {
+      "send": "Send test",
+      "sent": "Test message sent",
+      "failed": "Could not send the test message",
+      "hint": "Message yourself on WhatsApp first, otherwise the test needs an approved template."
+    },
+    "disconnect": {
+      "action": "Disconnect WhatsApp",
+      "hint": "Removes the credentials from Torqvoice. Your conversations are kept.",
+      "done": "WhatsApp disconnected",
+      "title": "Disconnect"
+    },
+    "savedIncomplete": "Saved. Still needed before you can send: {fields}",
+    "fieldStillNeeded": "Still needed before you can send",
+    "steps": {
+      "openProvider": "Open {provider}",
+      "openNumber": "Find your sender number",
+      "openToken": "Create a permanent token",
+      "consolePath": "In Meta, go to:",
+      "provider": {
+        "title": "Choose your provider and number",
+        "description": "Twilio is quicker to start with and handles Meta for you. Meta Cloud API costs less but you complete its business verification yourself. Enter the number customers will see, in international format."
+      },
+      "credentials": {
+        "title": "Enter your credentials",
+        "description": "Copy each value from {provider}. Every field below says exactly where to find it. Save when you have them; you can come back for the rest."
+      },
+      "webhook": {
+        "title": "Register the webhook",
+        "description": "Copy this address into {provider} so messages from customers reach Torqvoice. Save this page first: the address is checked against what is stored here.",
+        "open": "Open {provider} webhook settings",
+        "recheck": "Check again",
+        "waiting": "Nothing has reached this address yet. It turns green the moment your provider verifies it or delivers a message.",
+        "seen": "Your provider has reached this address. Incoming messages will appear in Messages."
+      },
+      "templates": {
+        "title": "Approve your templates",
+        "description": "Only needed to write first, outside the 24 hour window. You can skip this now and come back once messaging works.",
+        "open": "Open {provider} templates"
+      },
+      "test": {
+        "title": "Send a test message",
+        "description": "Message your WhatsApp number from your own phone first. That proves the webhook works and opens a 24 hour window, so a reply needs no template."
+      },
+      "back": "Back",
+      "continue": "Continue"
+    },
+    "register": {
+      "title": "Register the number with Meta",
+      "description": "Meta needs a separate registration once the number is verified. Choose a six-digit PIN, or use the number's existing two-step verification PIN if it has one. Write it down: you need it to move or re-register the number later.",
+      "action": "Register number",
+      "done": "Number registered",
+      "failed": "Could not register the number",
+      "limit": "Meta allows 10 registration attempts per number in 72 hours. A wrong PIN counts, so check it before pressing."
+    }
+  },
+  "messages": {
+    "featureName": "WhatsApp Messaging",
+    "featureDescription": "Send and receive WhatsApp messages, including photos of parts, directly from Torqvoice.",
+    "noThreads": "No conversations yet",
+    "noThreadsHint": "Messages appear here as soon as a customer writes to your WhatsApp number, or you write to them.",
+    "selectThread": "Pick a conversation",
+    "backToThreads": "Conversations",
+    "empty": "Nothing here yet.",
+    "placeholder": "Write a message",
+    "send": "Send",
+    "sendFailed": "Could not send the message",
+    "attach": "Attach a photo",
+    "attachFailed": "Could not attach the photo",
+    "removeAttachment": "Remove attachment",
+    "attachment": "Attachment",
+    "delete": "Delete",
+    "deleteFailed": "Could not delete the message",
+    "sentAsTemplate": "template",
+    "sentAsTemplateNotice": "Sent as your approved template, because the customer has not written in the last 24 hours.",
+    "windowClosed": "The customer has not written in the last 24 hours, so this will go out as your approved template.",
+    "unknownNumber": "This number belongs to no customer yet. Add them as a customer to reply.",
+    "notConfigured": {
+      "title": "WhatsApp is not connected",
+      "description": "Connect a WhatsApp number to send and receive messages with your customers.",
+      "action": "Set up WhatsApp"
+    },
+    "title": "Conversations",
+    "searchPlaceholder": "Search conversations",
+    "noSearchResults": "No conversations match your search.",
+    "compose": {
+      "newMessage": "New message",
+      "title": "New WhatsApp message",
+      "searchPlaceholder": "Search customers",
+      "noMatches": "No customers match your search.",
+      "noRecipients": "No customers with a phone number yet."
+    },
+    "windowBlocked": "This customer has not written in the last 24 hours, so WhatsApp will not deliver a message right now. Ask them to send you one, or set up an approved template.",
+    "windowBlockedAction": "Set up a template",
+    "windowBlockedPlaceholder": "Waiting for the customer to write first"
+  },
+  "sendPhoto": {
+    "action": "Send on WhatsApp",
+    "title": "Send photo on WhatsApp",
+    "description": "The customer gets this photo in their WhatsApp chat.",
+    "captionPlaceholder": "Add a note, for example what the photo shows",
+    "send": "Send",
+    "cancel": "Cancel",
+    "sent": "Photo sent",
+    "sentAsTemplate": "Photo sent as your approved template",
+    "failed": "Could not send the photo"
+  }
+}

+ 122 - 1
messages/es/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Juego almacenado de nuevo",
     "tire_set_dispose": "Juego de neumáticos dado de baja",
     "tire_set_attach": "Archivos añadidos a un juego de neumáticos",
-    "tire_set_detach": "Archivo quitado de un juego de neumáticos"
+    "tire_set_detach": "Archivo quitado de un juego de neumáticos",
+    "whatsapp_send": "Mensaje de WhatsApp enviado"
+  },
+  "summary": {
+    "customField_create": "Campo personalizado «{name}» creado",
+    "customField_delete": "Campo personalizado «{name}» eliminado",
+    "customField_update": "Campo personalizado «{name}» actualizado",
+    "customer_backfillNumbers": "Números de cliente asignados a {count, plural, one {# cliente} other {# clientes}}",
+    "customer_create": "Cliente {name} creado",
+    "customer_delete": "Cliente {id} eliminado",
+    "customer_update": "Cliente {id} actualizado",
+    "email_sendInspection": "Inspección enviada por correo a {recipient}",
+    "email_sendInvoice": "Factura enviada por correo a {recipient}",
+    "email_sendQuote": "Presupuesto enviado por correo a {recipient}",
+    "finding_create": "Hallazgo «{description}» creado en el vehículo {vehicleId}",
+    "finding_delete": "Hallazgo {id} eliminado",
+    "finding_resolve": "Hallazgo «{description}» resuelto",
+    "finding_update": "Hallazgo «{description}» actualizado",
+    "inspectionTemplate_create": "Plantilla de inspección «{name}» creada",
+    "inspectionTemplate_createFromPreset": "Plantilla de inspección «{name}» creada a partir de un preajuste",
+    "inspectionTemplate_delete": "Plantilla de inspección «{name}» eliminada",
+    "inspectionTemplate_duplicate": "Plantilla de inspección duplicada como «{name}»",
+    "inspectionTemplate_import": "{count, plural, one {# plantilla de inspección importada} other {# plantillas de inspección importadas}} desde un paquete",
+    "inspectionTemplate_update": "Plantilla de inspección «{name}» actualizada",
+    "inspection_complete": "Inspección {id} completada",
+    "inspection_create": "Inspección {id} creada",
+    "inspection_delete": "Inspección {id} eliminada",
+    "inspection_reopen": "Inspección {id} reabierta",
+    "inventory_bulkDelete": "{count, plural, one {# pieza de inventario eliminada} other {# piezas de inventario eliminadas}} en bloque",
+    "inventory_create": "Pieza de inventario «{name}» creada",
+    "inventory_delete": "Pieza de inventario {id} eliminada",
+    "inventory_update": "Pieza de inventario {id} actualizada",
+    "labor_preset_create": "Preajuste de mano de obra «{name}» creado",
+    "labor_preset_delete": "Preajuste de mano de obra {id} eliminado",
+    "labor_preset_update": "Preajuste de mano de obra {id} actualizado",
+    "note_create": "Nota creada en el vehículo {vehicleId}",
+    "note_delete": "Nota {id} eliminada",
+    "organization_create": "Organización «{name}» creada",
+    "organization_sampleDataRemove": "Datos de ejemplo de la puesta en marcha eliminados",
+    "payment_create": "Pago {amount} registrado para el servicio {serviceRecordId}",
+    "payment_delete": "Pago {id} eliminado",
+    "quoteRequest_update": "Estado de la solicitud de presupuesto cambiado a {status, select, pending {pendiente} approved {aprobada} rejected {rechazada} completed {completada} other {{status}}}",
+    "quote_convert": "Presupuesto {ref} convertido en servicio {serviceRecordId}",
+    "quote_create": "Presupuesto {ref} creado",
+    "quote_delete": "Presupuesto {ref} eliminado",
+    "quote_status": "Estado del presupuesto cambiado a {status, select, draft {borrador} sent {enviado} accepted {aceptado} rejected {rechazado} expired {caducado} converted {convertido} other {{status}}}",
+    "quote_update": "Presupuesto {ref} actualizado",
+    "recurringInvoice_create": "Factura recurrente «{title}» creada",
+    "recurringInvoice_delete": "Factura recurrente {id} eliminada",
+    "recurringInvoice_update": "Factura recurrente «{title}» actualizada",
+    "reminder_create": "Recordatorio «{title}» creado",
+    "reminder_delete": "Recordatorio {id} eliminado",
+    "role_create": "Rol «{name}» creado",
+    "role_delete": "Rol «{name}» eliminado",
+    "role_update": "Rol «{name}» actualizado",
+    "scheduled_message_cancel": "Mensaje programado cancelado",
+    "scheduled_message_create": "{channel, select, email {Correo} sms {SMS} telegram {Mensaje de Telegram} in_app {Aviso en la app} other {{channel}}} programado para {sendAt}",
+    "scheduled_message_delete": "Mensaje programado eliminado",
+    "scheduled_message_send_now": "{channel, select, email {Correo} sms {SMS} telegram {Mensaje de Telegram} in_app {Aviso en la app} other {{channel}}} programado enviado antes de tiempo",
+    "scheduled_message_update": "{channel, select, email {Correo} sms {SMS} telegram {Mensaje de Telegram} in_app {Aviso en la app} other {{channel}}} programado actualizado",
+    "service_create": "Servicio {ref} creado",
+    "service_createCounterSale": "Venta en mostrador {ref} creada",
+    "service_createDraft": "Borrador de servicio {ref} creado",
+    "service_createFromInspection": "Orden de trabajo {ref} creada a partir de una inspección",
+    "service_delete": "Servicio {ref} eliminado",
+    "service_status": "Estado del servicio cambiado a {status, select, pending {pendiente} scheduled {programado} in_progress {en curso} waiting_parts {esperando piezas} ready {listo} completed {completado} cancelled {cancelado} other {{status}}}",
+    "service_update": "Servicio {ref} actualizado",
+    "settings_applyTaxRate": "{rate}% de impuesto aplicado a {records, plural, one {# servicio} other {# servicios}} y {quotes, plural, one {# presupuesto} other {# presupuestos}}",
+    "settings_convertToExclusive": "{records, plural, one {# servicio convertido} other {# servicios convertidos}} y {quotes, plural, one {# presupuesto convertido} other {# presupuestos convertidos}} a precios sin impuestos",
+    "settings_convertToInclusive": "{records, plural, one {# servicio convertido} other {# servicios convertidos}} y {quotes, plural, one {# presupuesto convertido} other {# presupuestos convertidos}} a precios con impuestos",
+    "settings_deleteContent": "Todos los datos eliminados: {items}",
+    "settings_updateInvoiceLayout": "Diseño de la factura actualizado",
+    "settings_updateQuoteLayout": "Diseño del presupuesto actualizado",
+    "sms_send": "SMS enviado a {name} ({phone})",
+    "statusReport_create": "Informe de estado creado para el servicio {serviceRecordId}",
+    "statusReport_delete": "Informe de estado eliminado",
+    "statusReport_send": "Informe de estado enviado por {channels}",
+    "subscription_cancel": "Suscripción cancelada, efectiva al final del periodo",
+    "subscription_resume": "Suscripción reanudada",
+    "team_cancelInvitation": "Invitación para {email} cancelada",
+    "team_removeMember": "Miembro del equipo {id} eliminado",
+    "team_sendInvitation": "{email} invitado como {role, select, owner {propietario} admin {administrador} member {miembro} other {{role}}}",
+    "team_updateRole": "Rol de un miembro cambiado a {role, select, owner {propietario} admin {administrador} member {miembro} other {{role}}}",
+    "technician_create": "Técnico «{name}» creado",
+    "technician_delete": "Técnico {id} eliminado",
+    "technician_update": "Técnico {id} actualizado",
+    "telegram_send": "Mensaje de Telegram enviado a {name}",
+    "tire_location_bulk_create": "{count, plural, one {# ubicación de neumáticos creada} other {# ubicaciones de neumáticos creadas}}",
+    "tire_location_create": "Ubicación de neumáticos {code} creada",
+    "tire_location_delete": "Ubicación de neumáticos {code} eliminada",
+    "tire_location_update": "Ubicación de neumáticos {code} actualizada",
+    "tire_measurement_create": "{count, plural, one {# medición registrada} other {# mediciones registradas}} en el juego de neumáticos {ref}",
+    "tire_set_add_to_work_order": "Juego de neumáticos {ref} añadido a la orden de trabajo {jobRef}",
+    "tire_set_attach": "{count, plural, one {# archivo añadido} other {# archivos añadidos}} al juego de neumáticos {ref}",
+    "tire_set_check_in": "Juego de neumáticos {ref} guardado en {code}",
+    "tire_set_check_out": "Juego de neumáticos {ref} entregado",
+    "tire_set_create_quote": "Presupuesto {ref} creado a partir de un juego de neumáticos",
+    "tire_set_create_work_order": "Orden de trabajo {ref} creada a partir de un juego de neumáticos",
+    "tire_set_delete": "Juego de neumáticos {ref} eliminado",
+    "tire_set_detach": "{fileName} quitado de un juego de neumáticos",
+    "tire_set_dispose": "Juego de neumáticos {ref} dado de baja",
+    "tire_set_message_customer": "Cliente avisado sobre el juego de neumáticos {ref} por {channel, select, email {correo} sms {SMS} telegram {Telegram} in_app {aviso en la app} other {{channel}}}",
+    "tire_set_relocate": "Juego de neumáticos {ref} movido de {from} a {to}",
+    "tire_set_relocateUnassigned": "Juego de neumáticos {ref} colocado en {to}",
+    "tire_set_return": "Juego de neumáticos {ref} guardado de nuevo, en {code}",
+    "tire_set_unlink_work_order": "Juego de neumáticos {ref} desvinculado de la orden de trabajo {jobRef}",
+    "tire_set_update": "Juego de neumáticos {ref} actualizado",
+    "tire_treatment_complete_all": "{count, plural, one {# tarea de preparación completada} other {# tareas de preparación completadas}} en el juego de neumáticos {ref}",
+    "tire_treatment_mark": "{type, select, wash_tires {Lavado de neumáticos} wash_rims {Lavado de llantas} balance {Equilibrado} tpms_service {Servicio TPMS} new_valves {Válvulas nuevas} repair {Reparación} other {{type}}} marcado como {status, select, done {hecho} pending {pendiente} skipped {omitido} other {{status}}} en el juego de neumáticos {ref}",
+    "tire_treatment_update": "Trabajo de preparación del juego de neumáticos {ref} actualizado",
+    "tire_warehouse_archive": "Almacén de neumáticos {name} archivado",
+    "tire_warehouse_create": "Almacén de neumáticos {name} creado",
+    "tire_warehouse_delete": "Almacén de neumáticos {name} eliminado",
+    "tire_warehouse_update": "Almacén de neumáticos {name} actualizado",
+    "vehicle_archive": "Vehículo {id} archivado",
+    "vehicle_create": "Vehículo {year} {make} {model} creado",
+    "vehicle_delete": "Vehículo {name} eliminado",
+    "vehicle_unarchive": "Vehículo {id} recuperado del archivo",
+    "vehicle_update": "Vehículo {name} actualizado, cambios: {fields}",
+    "vehicle_updateNoChanges": "Vehículo {name} actualizado, sin cambios",
+    "webhook_create": "Webhook {name} creado",
+    "whatsapp_send": "Envió un mensaje de WhatsApp a {name}"
   }
 }

+ 15 - 0
messages/es/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Añádela a tu pantalla de inicio para acceder rápido",
     "enterFullscreen": "Pantalla completa",
     "exitFullscreen": "Salir de pantalla completa"
+  },
+  "broadcast": {
+    "dismiss": "Cerrar",
+    "title": "Aviso para todos",
+    "description": "Se muestra en la parte superior de cada página, para todos, con sesión iniciada o no. Déjalo vacío para no mostrar nada.",
+    "message": "Mensaje",
+    "level": "Gravedad",
+    "post": "Publicar aviso",
+    "clear": "Quitar aviso",
+    "levelInfo": "Información",
+    "levelWarning": "Advertencia",
+    "levelCritical": "Crítico",
+    "placeholder": "p. ej. Tenemos problemas con nuestra infraestructura de servidores y estamos trabajando en ello.",
+    "posted": "Aviso publicado",
+    "cleared": "Aviso retirado"
   }
 }

+ 6 - 2
messages/es/customers.json

@@ -38,7 +38,8 @@
       "messages": "Mensajes",
       "requests": "Solicitudes ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Placa",
@@ -68,7 +69,10 @@
     "customerCreated": "Cliente creado",
     "saveError": "Error al guardar el cliente",
     "customerNumber": "N.º de cliente",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Contacto",
+    "sectionBilling": "Facturación",
+    "addScannedVehicle": "Añadir el vehículo {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "Nuevo",

+ 7 - 0
messages/es/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Mantenimiento previsto",
     "reminders": "Recordatorios pendientes",
     "inspections": "Inspecciones",
+    "serviceRequests": "Solicitudes de servicio",
     "quoteRequests": "Solicitudes de presupuesto",
     "quoteResponses": "Respuestas de presupuesto",
     "sms": "Mensajes recientes",
@@ -68,6 +69,7 @@
     "active": "Activos",
     "dismissedTab": "Descartados",
     "noActive": "No hay vehículos que requieran servicio actualmente.",
+    "noDismissed": "No hay vehículos descartados.",
     "dismissedOn": "Descartado el {date}",
     "restore": "Restaurar",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} elementos",
     "noData": "No hay inspecciones."
   },
+  "serviceRequests": {
+    "title": "Solicitudes de servicio",
+    "description": "Solicitudes de cita desde el portal del cliente",
+    "noData": "No hay solicitudes de servicio pendientes."
+  },
   "quoteRequests": {
     "title": "Solicitudes de presupuesto",
     "description": "Clientes que solicitan presupuestos a partir de inspecciones compartidas",

+ 4 - 1
messages/es/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Envíe y reciba mensajes SMS con sus clientes directamente desde Torqvoice.",
     "tabs": {
       "inbox": "Bandeja",
-      "scheduled": "Programados"
+      "scheduled": "Programados",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "SMS no está configurado",
     "smsNotConfiguredHint": "Conecta un proveedor de SMS para enviar y recibir mensajes aquí. El correo programado funciona igual.",

+ 26 - 0
messages/es/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Conversaciones",
+    "searchPlaceholder": "Buscar conversaciones",
+    "noSearchResults": "Ninguna conversación coincide con tu búsqueda.",
+    "empty": "Aún no hay conversaciones.",
+    "selectConversation": "Elige una conversación",
+    "back": "Atrás",
+    "newMessage": "Nuevo mensaje",
+    "unknownContact": "Este número aún no pertenece a ningún cliente. Añádelo como cliente para responder.",
+    "noChannelsTitle": "Sin canal de mensajería",
+    "noChannelsHint": "Conecta SMS, WhatsApp o Telegram para escribir a tus clientes desde aquí.",
+    "goToProviders": "Configurar un proveedor",
+    "chooseChannel": {
+      "title": "¿Por qué canal quieres enviar?",
+      "description": "Tienes más de una forma de llegar a tus clientes."
+    },
+    "deleteConversation": "Eliminar conversación",
+    "deleteTitle": "¿Eliminar esta conversación?",
+    "deleteDescription": "Elimina tu copia de la conversación con {name}. Los mensajes se quedan en su teléfono.",
+    "deleteFailed": "No se pudo eliminar la conversación",
+    "delete": "Eliminar",
+    "cancel": "Cancelar",
+    "loadMore": "Cargar más"
+  }
+}

+ 4 - 2
messages/es/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Todos los juegos",
     "tireStorage": "Distribución del almacén",
     "tireSetDetails": "Juego de neumáticos",
-    "tireForecast": "La próxima temporada"
+    "tireForecast": "La próxima temporada",
+    "whatsapp": "WhatsApp"
   },
   "search": "Buscar...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Más",
     "auditLog": "Registro de auditoría",
     "aiAssistant": "Asistente IA",
-    "tireHotel": "Hotel de neumáticos"
+    "tireHotel": "Hotel de neumáticos",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/es/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "Correo",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "Nota en la app"
+      "in_app": "Nota en la app",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Repetir",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Programar",
     "scheduled": "Mensaje programado",
     "updated": "Mensaje programado actualizado",
-    "saveError": "No se pudo guardar el mensaje"
+    "saveError": "No se pudo guardar el mensaje",
+    "whatsappNeedsTemplate": "Se enviará mucho después de que se cierre la ventana de 24 horas del cliente y no hay plantilla de texto de WhatsApp configurada, así que fallaría al enviarse. Configura una en Ajustes > Proveedores > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Programar mensaje",

+ 35 - 3
messages/es/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Hotel de neumáticos",
         "description": "Almacenamiento estacional de neumáticos"
+      },
+      "providers": {
+        "title": "Proveedores",
+        "description": "Correo, SMS, WhatsApp y Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Plantillas de inspección, inspecciones y resultados",
     "optAuditLogs": "Registros de auditoría",
     "optAuditLogsDesc": "Historial de actividad y cambios",
-    "optSmsMessages": "Mensajes SMS",
-    "optSmsMessagesDesc": "Mensajes SMS enviados y recibidos",
+    "optSmsMessages": "Mensajes",
+    "optSmsMessagesDesc": "Mensajes SMS, WhatsApp y Telegram enviados y recibidos",
     "optScheduledMessages": "Mensajes programados",
     "optScheduledMessagesDesc": "Mensajes en cola y recurrentes pendientes de envío",
     "optNotifications": "Notificaciones",
@@ -980,7 +984,9 @@
       "supportAction": "contacte con nuestro equipo de soporte."
     },
     "optTireHotel": "Hotel de neumáticos",
-    "optTireHotelDesc": "Juegos guardados, estantes, preparación y contratos de almacenaje"
+    "optTireHotelDesc": "Juegos guardados, estantes, preparación y contratos de almacenaje",
+    "optWorkshopConfig": "Configuración del taller",
+    "optWorkshopConfigDesc": "Plantillas de mano de obra, roles, webhooks e informes programados"
   },
   "license": {
     "title": "Licencia marca blanca",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Precios de preparación",
     "prepHint": "Lo que cuesta cada tipo de preparación. Déjalo en blanco y no se factura nunca, que es lo que quiere un taller que incluye el lavado en el almacenaje. Las que tienen precio se añaden solas al presupuesto y a la orden.",
     "prepNotCharged": "no se cobra"
+  },
+  "providers": {
+    "tabs": {
+      "email": "Correo",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "Ajustes de correo",
+        "description": "Configura el envío SMTP para entregar facturas, presupuestos y avisos desde tu taller."
+      },
+      "sms": {
+        "feature": "Mensajería SMS",
+        "description": "Envía y recibe SMS con tus clientes desde Torqvoice."
+      },
+      "whatsapp": {
+        "feature": "Mensajería WhatsApp",
+        "description": "Envía y recibe mensajes de WhatsApp, incluidas fotos de piezas, desde Torqvoice."
+      },
+      "telegram": {
+        "feature": "Mensajería Telegram",
+        "description": "Envía y recibe mensajes de Telegram con tus clientes desde Torqvoice."
+      }
+    }
   }
 }

+ 2 - 1
messages/es/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Omitir",
     "send": "Enviar",
     "sent": "Informe de estado enviado",
-    "failed": "Error al enviar el informe de estado"
+    "failed": "Error al enviar el informe de estado",
+    "notSent": "No enviado"
   },
   "view": {
     "title": "Informe de estado del vehículo",

+ 16 - 1
messages/es/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Número de Registro",
     "vehicleUpdated": "Vehículo actualizado",
     "vehicleAdded": "Vehículo agregado",
-    "saveError": "Error al guardar vehículo"
+    "saveError": "Error al guardar vehículo",
+    "scanDocument": "Escanear permiso de circulación",
+    "scanDocumentHint": "Fotografía la documentación del vehículo y los campos se rellenan solos.",
+    "scanningDocument": "Leyendo el documento...",
+    "scanSuccess": "Campos rellenados desde el documento",
+    "scanFailed": "No se pudo leer el documento",
+    "ownerMatchTitle": "Asignar este vehículo",
+    "ownerMatchDescription": "El documento indica {name}. Elige uno de estos clientes o crea uno nuevo.",
+    "ownerMatchScore": "{percent}% de coincidencia",
+    "ownerMatchCreateNew": "Crear cliente nuevo",
+    "ownerMatchChoose": "Elegir cliente",
+    "scanUnavailable": "Activa la IA en los ajustes",
+    "sectionAssignment": "Foto y titular",
+    "sectionDetails": "Detalles",
+    "addScannedOwner": "Añadir el cliente {name}",
+    "ownerSaveFailed": "No se pudo crear el cliente"
   },
   "archiveDialog": {
     "title": "Archivar vehículo",

+ 179 - 0
messages/es/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "Envía y recibe mensajes de WhatsApp con tus clientes.",
+    "helpLink": "Configurar {provider}",
+    "save": "Guardar ajustes de WhatsApp",
+    "saved": "Ajustes de WhatsApp guardados",
+    "saveError": "No se pudieron guardar los ajustes de WhatsApp",
+    "enable": {
+      "label": "Activar WhatsApp",
+      "hint": "Actívalo cuando las credenciales del proveedor estén puestas.",
+      "disabledInfo": "Activa WhatsApp para configurar tu proveedor, número y plantilla."
+    },
+    "provider": {
+      "label": "Proveedor",
+      "hint": "Quién te da el número de WhatsApp. Al cambiar se conservan las credenciales del anterior."
+    },
+    "from": {
+      "label": "Tu número de WhatsApp",
+      "hint": "El número que ven los clientes, en formato internacional."
+    },
+    "secret": {
+      "show": "Mostrar",
+      "hide": "Ocultar",
+      "stored": "Guardado y oculto. Escribe un valor nuevo para reemplazarlo."
+    },
+    "webhook": {
+      "label": "URL del webhook",
+      "description": "Pega esto en tu proveedor para que los mensajes entrantes lleguen a Torqvoice.",
+      "copy": "Copiar URL del webhook",
+      "copied": "URL del webhook copiada",
+      "saveFirst": "Guarda estos ajustes antes de que tu proveedor verifique la URL. Se comprueba contra lo guardado aquí, así que un formulario sin guardar falla."
+    },
+    "template": {
+      "title": "Plantilla de mensaje",
+      "description": "Se usa cuando escribes a un cliente que no te ha escrito hace poco.",
+      "windowExplainer": "WhatsApp solo permite texto libre durante 24 horas tras el último mensaje del cliente. Fuera de esa ventana, tu mensaje debe salir como plantilla aprobada por tu proveedor.",
+      "nameLabel": "Nombre de la plantilla",
+      "nameHint": "El nombre de la plantilla aprobada, o su Content SID en proveedores que lo usan.",
+      "languageLabel": "Idioma de la plantilla",
+      "languageHint": "El código de idioma con el que se aprobó, por ejemplo es.",
+      "variablesLabel": "Valores de la plantilla, en orden",
+      "variablesHint": "Elige los valores que rellenan los huecos numerados de tu plantilla, en el mismo orden. Déjalo vacío si tu plantilla no tiene huecos.",
+      "tokens": {
+        "customer": "nombre del cliente",
+        "vehicle": "año, marca y modelo",
+        "plate": "matrícula",
+        "message": "lo que escribas",
+        "workshop": "nombre de tu taller",
+        "photo": "la foto adjunta"
+      },
+      "variablesEmpty": "Aún sin valores. Añádelos abajo, en el orden que espera tu plantilla.",
+      "variablesRemove": "Quitar {name}",
+      "mediaUrlLabel": "URL de medios para tu plantilla",
+      "mediaUrlHint": "Pega esto en el campo de medios de la plantilla y sustituye n por el número del valor de la foto.",
+      "mediaUrlCopy": "Copiar URL de medios",
+      "mediaUrlCopied": "URL de medios copiada",
+      "mediaUrlSample": "Como valor de muestra en el proveedor, usa: sample",
+      "textTitle": "Mensajes de texto",
+      "textDescription": "Se usa cuando escribes sin adjuntar foto: desde Mensajes, desde la pestaña WhatsApp de un cliente o desde un mensaje programado.",
+      "mediaTitle": "Mensajes con foto",
+      "mediaDescription": "Lo usa el botón Enviar por WhatsApp en las fotos de un trabajo, y siempre que adjuntas una foto en una conversación.",
+      "textMissing": "Sin configurar: no puedes escribir a un cliente que no te ha escrito en las últimas 24 horas.",
+      "mediaMissing": "Sin configurar: no puedes enviar fotos a un cliente que no te ha escrito en las últimas 24 horas.",
+      "whatIsIt": "Una plantilla es un mensaje fijo que escribes una vez y que {provider} aprueba: el texto nunca cambia, solo rellenas los huecos numerados. Escríbela en su consola y pega aquí su identificador.",
+      "whyTwo": "Hacen falta dos porque WhatsApp fija el tipo de medio al aprobar la plantilla. Una aprobada con foto solo puede llevar fotos, y una aprobada como texto nunca podrá llevarlas.",
+      "exampleLabel": "Cuerpo de ejemplo para la consola de tu proveedor",
+      "exampleTextValues": "Rellénalo con: customer, message. Escríbelo en el idioma de tus clientes.",
+      "exampleMediaValues": "Rellénalo con: customer, message y photo para la URL de medios. Escríbelo en el idioma de tus clientes.",
+      "exampleMediaValuesHeader": "Rellénela con: customer, message. La foto se adjunta automáticamente, así que no necesita su propio hueco. Escríbala en el idioma que leen sus clientes."
+    },
+    "test": {
+      "send": "Enviar prueba",
+      "sent": "Mensaje de prueba enviado",
+      "failed": "No se pudo enviar el mensaje de prueba",
+      "hint": "Escríbete primero por WhatsApp; si no, la prueba necesita una plantilla aprobada."
+    },
+    "disconnect": {
+      "action": "Desconectar WhatsApp",
+      "hint": "Elimina las credenciales de Torqvoice. Se conservan tus conversaciones.",
+      "done": "WhatsApp desconectado",
+      "title": "Desconectar"
+    },
+    "savedIncomplete": "Guardado. Aún falta para poder enviar: {fields}",
+    "fieldStillNeeded": "Aún necesario para enviar",
+    "steps": {
+      "openProvider": "Abrir {provider}",
+      "openNumber": "Buscar su número de remitente",
+      "openToken": "Crear un token permanente",
+      "consolePath": "En Meta, vaya a:",
+      "provider": {
+        "title": "Elige proveedor y número",
+        "description": "Twilio se pone en marcha antes y gestiona Meta por ti. Meta Cloud API cuesta menos, pero completas tú su verificación de empresa. Escribe el número que verán los clientes, en formato internacional."
+      },
+      "credentials": {
+        "title": "Introduce tus credenciales",
+        "description": "Copia cada valor desde {provider}. Cada campo indica dónde encontrarlo. Guarda cuando los tengas; puedes volver a por el resto."
+      },
+      "webhook": {
+        "title": "Registra el webhook",
+        "description": "Copia esta dirección en {provider} para que los mensajes de clientes lleguen a Torqvoice. Guarda antes esta página: la dirección se comprueba contra lo guardado aquí.",
+        "open": "Abrir ajustes de webhook en {provider}",
+        "recheck": "Comprobar de nuevo",
+        "waiting": "Todavía no ha llegado nada a esta dirección. Se pondrá en verde en cuanto tu proveedor la verifique o entregue un mensaje.",
+        "seen": "Tu proveedor ha alcanzado esta dirección. Los mensajes entrantes aparecerán en Mensajes."
+      },
+      "templates": {
+        "title": "Aprueba tus plantillas",
+        "description": "Solo hace falta para escribir primero, fuera de la ventana de 24 horas. Puedes saltarlo ahora y volver cuando la mensajería funcione.",
+        "open": "Abrir plantillas de {provider}"
+      },
+      "test": {
+        "title": "Envía un mensaje de prueba",
+        "description": "Escribe primero a tu número de WhatsApp desde tu propio teléfono. Eso prueba el webhook y abre una ventana de 24 horas, así la respuesta no necesita plantilla."
+      },
+      "back": "Atrás",
+      "continue": "Continuar"
+    },
+    "register": {
+      "title": "Registra el número en Meta",
+      "description": "Meta requiere un registro aparte una vez verificado el número. Elige un PIN de seis dígitos, o usa el PIN de verificación en dos pasos si ya tiene uno. Anótalo: lo necesitarás para mover o volver a registrar el número.",
+      "action": "Registrar número",
+      "done": "Número registrado",
+      "failed": "No se pudo registrar el número",
+      "limit": "Meta permite 10 intentos de registro por número en 72 horas. Un PIN incorrecto cuenta, así que compruébalo antes."
+    }
+  },
+  "messages": {
+    "featureName": "Mensajería WhatsApp",
+    "featureDescription": "Envía y recibe mensajes de WhatsApp, incluidas fotos de piezas, desde Torqvoice.",
+    "noThreads": "Aún no hay conversaciones",
+    "noThreadsHint": "Los mensajes aparecen aquí en cuanto un cliente escribe a tu número de WhatsApp, o tú le escribes.",
+    "selectThread": "Elige una conversación",
+    "backToThreads": "Conversaciones",
+    "empty": "Aquí todavía no hay nada.",
+    "placeholder": "Escribe un mensaje",
+    "send": "Enviar",
+    "sendFailed": "No se pudo enviar el mensaje",
+    "attach": "Adjuntar una foto",
+    "attachFailed": "No se pudo adjuntar la foto",
+    "removeAttachment": "Quitar adjunto",
+    "attachment": "Adjunto",
+    "delete": "Eliminar",
+    "deleteFailed": "No se pudo eliminar el mensaje",
+    "sentAsTemplate": "plantilla",
+    "sentAsTemplateNotice": "Enviado como tu plantilla aprobada, porque el cliente no ha escrito en las últimas 24 horas.",
+    "windowClosed": "El cliente no ha escrito en las últimas 24 horas, así que esto saldrá como tu plantilla aprobada.",
+    "unknownNumber": "Este número aún no pertenece a ningún cliente. Añádelo como cliente para responder.",
+    "notConfigured": {
+      "title": "WhatsApp no está conectado",
+      "description": "Conecta un número de WhatsApp para enviar y recibir mensajes con tus clientes.",
+      "action": "Configurar WhatsApp"
+    },
+    "title": "Conversaciones",
+    "searchPlaceholder": "Buscar conversaciones",
+    "noSearchResults": "Ninguna conversación coincide con tu búsqueda.",
+    "compose": {
+      "newMessage": "Nuevo mensaje",
+      "title": "Nuevo mensaje de WhatsApp",
+      "searchPlaceholder": "Buscar clientes",
+      "noMatches": "Ningún cliente coincide con tu búsqueda.",
+      "noRecipients": "Aún no hay clientes con teléfono."
+    },
+    "windowBlocked": "Este cliente no ha escrito en las últimas 24 horas, así que WhatsApp no entregará el mensaje ahora. Pídele que te escriba o configura una plantilla aprobada.",
+    "windowBlockedAction": "Configurar una plantilla",
+    "windowBlockedPlaceholder": "Esperando a que el cliente escriba primero"
+  },
+  "sendPhoto": {
+    "action": "Enviar por WhatsApp",
+    "title": "Enviar foto por WhatsApp",
+    "description": "El cliente recibe esta foto en su chat de WhatsApp.",
+    "captionPlaceholder": "Añade una nota, por ejemplo qué muestra la foto",
+    "send": "Enviar",
+    "cancel": "Cancelar",
+    "sent": "Foto enviada",
+    "sentAsTemplate": "Foto enviada como tu plantilla aprobada",
+    "failed": "No se pudo enviar la foto"
+  }
+}

+ 122 - 1
messages/fr/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Jeu de nouveau stocké",
     "tire_set_dispose": "Jeu de pneus mis au rebut",
     "tire_set_attach": "Fichiers ajoutés à un jeu de pneus",
-    "tire_set_detach": "Fichier retiré d’un jeu de pneus"
+    "tire_set_detach": "Fichier retiré d’un jeu de pneus",
+    "whatsapp_send": "Message WhatsApp envoyé"
+  },
+  "summary": {
+    "customField_create": "Champ personnalisé « {name} » créé",
+    "customField_delete": "Champ personnalisé « {name} » supprimé",
+    "customField_update": "Champ personnalisé « {name} » mis à jour",
+    "customer_backfillNumbers": "Numéros de client attribués à {count, plural, one {# client} other {# clients}}",
+    "customer_create": "Client {name} créé",
+    "customer_delete": "Client {id} supprimé",
+    "customer_update": "Client {id} mis à jour",
+    "email_sendInspection": "Contrôle envoyé par e-mail à {recipient}",
+    "email_sendInvoice": "Facture envoyée par e-mail à {recipient}",
+    "email_sendQuote": "Devis envoyé par e-mail à {recipient}",
+    "finding_create": "Constat « {description} » créé sur le véhicule {vehicleId}",
+    "finding_delete": "Constat {id} supprimé",
+    "finding_resolve": "Constat « {description} » résolu",
+    "finding_update": "Constat « {description} » mis à jour",
+    "inspectionTemplate_create": "Modèle de contrôle « {name} » créé",
+    "inspectionTemplate_createFromPreset": "Modèle de contrôle « {name} » créé à partir d'un préréglage",
+    "inspectionTemplate_delete": "Modèle de contrôle « {name} » supprimé",
+    "inspectionTemplate_duplicate": "Modèle de contrôle dupliqué vers « {name} »",
+    "inspectionTemplate_import": "{count, plural, one {# modèle de contrôle importé} other {# modèles de contrôle importés}} depuis un paquet",
+    "inspectionTemplate_update": "Modèle de contrôle « {name} » mis à jour",
+    "inspection_complete": "Contrôle {id} terminé",
+    "inspection_create": "Contrôle {id} créé",
+    "inspection_delete": "Contrôle {id} supprimé",
+    "inspection_reopen": "Contrôle {id} rouvert",
+    "inventory_bulkDelete": "{count, plural, one {# pièce en stock supprimée} other {# pièces en stock supprimées}} en lot",
+    "inventory_create": "Pièce en stock « {name} » créée",
+    "inventory_delete": "Pièce en stock {id} supprimée",
+    "inventory_update": "Pièce en stock {id} mise à jour",
+    "labor_preset_create": "Préréglage de main-d'œuvre « {name} » créé",
+    "labor_preset_delete": "Préréglage de main-d'œuvre {id} supprimé",
+    "labor_preset_update": "Préréglage de main-d'œuvre {id} mis à jour",
+    "note_create": "Note créée sur le véhicule {vehicleId}",
+    "note_delete": "Note {id} supprimée",
+    "organization_create": "Organisation « {name} » créée",
+    "organization_sampleDataRemove": "Données d'exemple de la mise en route supprimées",
+    "payment_create": "Paiement {amount} enregistré pour l'intervention {serviceRecordId}",
+    "payment_delete": "Paiement {id} supprimé",
+    "quoteRequest_update": "Statut de la demande de devis passé à {status, select, pending {en attente} approved {approuvée} rejected {refusée} completed {terminée} other {{status}}}",
+    "quote_convert": "Devis {ref} converti en intervention {serviceRecordId}",
+    "quote_create": "Devis {ref} créé",
+    "quote_delete": "Devis {ref} supprimé",
+    "quote_status": "Statut du devis passé à {status, select, draft {brouillon} sent {envoyé} accepted {accepté} rejected {refusé} expired {expiré} converted {converti} other {{status}}}",
+    "quote_update": "Devis {ref} mis à jour",
+    "recurringInvoice_create": "Facture récurrente « {title} » créée",
+    "recurringInvoice_delete": "Facture récurrente {id} supprimée",
+    "recurringInvoice_update": "Facture récurrente « {title} » mise à jour",
+    "reminder_create": "Rappel « {title} » créé",
+    "reminder_delete": "Rappel {id} supprimé",
+    "role_create": "Rôle « {name} » créé",
+    "role_delete": "Rôle « {name} » supprimé",
+    "role_update": "Rôle « {name} » mis à jour",
+    "scheduled_message_cancel": "Message programmé annulé",
+    "scheduled_message_create": "{channel, select, email {E-mail} sms {SMS} telegram {Message Telegram} in_app {Note dans l'application} other {{channel}}} programmé pour {sendAt}",
+    "scheduled_message_delete": "Message programmé supprimé",
+    "scheduled_message_send_now": "{channel, select, email {E-mail} sms {SMS} telegram {Message Telegram} in_app {Note dans l'application} other {{channel}}} programmé envoyé en avance",
+    "scheduled_message_update": "{channel, select, email {E-mail} sms {SMS} telegram {Message Telegram} in_app {Note dans l'application} other {{channel}}} programmé mis à jour",
+    "service_create": "Intervention {ref} créée",
+    "service_createCounterSale": "Vente au comptoir {ref} créée",
+    "service_createDraft": "Brouillon d'intervention {ref} créé",
+    "service_createFromInspection": "Ordre de travail {ref} créé à partir d'un contrôle",
+    "service_delete": "Intervention {ref} supprimée",
+    "service_status": "Statut de l'intervention passé à {status, select, pending {en attente} scheduled {planifiée} in_progress {en cours} waiting_parts {en attente de pièces} ready {prête} completed {terminée} cancelled {annulée} other {{status}}}",
+    "service_update": "Intervention {ref} mise à jour",
+    "settings_applyTaxRate": "TVA de {rate} % appliquée à {records, plural, one {# intervention} other {# interventions}} et {quotes, plural, one {# devis} other {# devis}}",
+    "settings_convertToExclusive": "{records, plural, one {# intervention convertie} other {# interventions converties}} et {quotes, plural, one {# devis converti} other {# devis convertis}} en prix hors taxes",
+    "settings_convertToInclusive": "{records, plural, one {# intervention convertie} other {# interventions converties}} et {quotes, plural, one {# devis converti} other {# devis convertis}} en prix toutes taxes comprises",
+    "settings_deleteContent": "Toutes les données supprimées : {items}",
+    "settings_updateInvoiceLayout": "Mise en page de la facture mise à jour",
+    "settings_updateQuoteLayout": "Mise en page du devis mise à jour",
+    "sms_send": "SMS envoyé à {name} ({phone})",
+    "statusReport_create": "Rapport d'état créé pour l'intervention {serviceRecordId}",
+    "statusReport_delete": "Rapport d'état supprimé",
+    "statusReport_send": "Rapport d'état envoyé via {channels}",
+    "subscription_cancel": "Abonnement résilié à la fin de la période",
+    "subscription_resume": "Abonnement repris",
+    "team_cancelInvitation": "Invitation de {email} annulée",
+    "team_removeMember": "Membre de l'équipe {id} retiré",
+    "team_sendInvitation": "{email} invité en tant que {role, select, owner {propriétaire} admin {administrateur} member {membre} other {{role}}}",
+    "team_updateRole": "Rôle d'un membre changé en {role, select, owner {propriétaire} admin {administrateur} member {membre} other {{role}}}",
+    "technician_create": "Technicien « {name} » créé",
+    "technician_delete": "Technicien {id} supprimé",
+    "technician_update": "Technicien {id} mis à jour",
+    "telegram_send": "Message Telegram envoyé à {name}",
+    "tire_location_bulk_create": "{count, plural, one {# emplacement de stockage pneus créé} other {# emplacements de stockage pneus créés}}",
+    "tire_location_create": "Emplacement de stockage pneus {code} créé",
+    "tire_location_delete": "Emplacement de stockage pneus {code} supprimé",
+    "tire_location_update": "Emplacement de stockage pneus {code} mis à jour",
+    "tire_measurement_create": "{count, plural, one {# relevé enregistré} other {# relevés enregistrés}} sur le jeu de pneus {ref}",
+    "tire_set_add_to_work_order": "Jeu de pneus {ref} ajouté à l'ordre de travail {jobRef}",
+    "tire_set_attach": "{count, plural, one {# fichier ajouté} other {# fichiers ajoutés}} au jeu de pneus {ref}",
+    "tire_set_check_in": "Jeu de pneus {ref} rentré en {code}",
+    "tire_set_check_out": "Jeu de pneus {ref} restitué",
+    "tire_set_create_quote": "Devis {ref} créé à partir d'un jeu de pneus",
+    "tire_set_create_work_order": "Ordre de travail {ref} créé à partir d'un jeu de pneus",
+    "tire_set_delete": "Jeu de pneus {ref} supprimé",
+    "tire_set_detach": "{fileName} retiré d'un jeu de pneus",
+    "tire_set_dispose": "Jeu de pneus {ref} mis au rebut",
+    "tire_set_message_customer": "Client informé au sujet du jeu de pneus {ref} par {channel, select, email {e-mail} sms {SMS} telegram {Telegram} in_app {note dans l'application} other {{channel}}}",
+    "tire_set_relocate": "Jeu de pneus {ref} déplacé de {from} vers {to}",
+    "tire_set_relocateUnassigned": "Jeu de pneus {ref} placé en {to}",
+    "tire_set_return": "Jeu de pneus {ref} de nouveau stocké, en {code}",
+    "tire_set_unlink_work_order": "Jeu de pneus {ref} dissocié de l'ordre de travail {jobRef}",
+    "tire_set_update": "Jeu de pneus {ref} mis à jour",
+    "tire_treatment_complete_all": "{count, plural, one {# tâche de préparation terminée} other {# tâches de préparation terminées}} sur le jeu de pneus {ref}",
+    "tire_treatment_mark": "{type, select, wash_tires {Lavage des pneus} wash_rims {Lavage des jantes} balance {Équilibrage} tpms_service {Service TPMS} new_valves {Valves neuves} repair {Réparation} other {{type}}} marqué comme {status, select, done {fait} pending {à faire} skipped {ignoré} other {{status}}} sur le jeu de pneus {ref}",
+    "tire_treatment_update": "Préparation du jeu de pneus {ref} mise à jour",
+    "tire_warehouse_archive": "Dépôt de pneus {name} archivé",
+    "tire_warehouse_create": "Dépôt de pneus {name} créé",
+    "tire_warehouse_delete": "Dépôt de pneus {name} supprimé",
+    "tire_warehouse_update": "Dépôt de pneus {name} mis à jour",
+    "vehicle_archive": "Véhicule {id} archivé",
+    "vehicle_create": "Véhicule {year} {make} {model} créé",
+    "vehicle_delete": "Véhicule {name} supprimé",
+    "vehicle_unarchive": "Véhicule {id} sorti des archives",
+    "vehicle_update": "Véhicule {name} mis à jour, modifié : {fields}",
+    "vehicle_updateNoChanges": "Véhicule {name} mis à jour, aucun changement",
+    "webhook_create": "Webhook {name} créé",
+    "whatsapp_send": "Message WhatsApp envoyé à {name}"
   }
 }

+ 15 - 0
messages/fr/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Ajoutez-la à votre écran d'accueil pour un accès rapide",
     "enterFullscreen": "Plein écran",
     "exitFullscreen": "Quitter le plein écran"
+  },
+  "broadcast": {
+    "dismiss": "Fermer",
+    "title": "Message à tous",
+    "description": "Affiché en haut de chaque page, pour tout le monde, connecté ou non. Laissez vide pour ne rien afficher.",
+    "message": "Message",
+    "level": "Gravité",
+    "post": "Publier le message",
+    "clear": "Retirer le message",
+    "levelInfo": "Information",
+    "levelWarning": "Avertissement",
+    "levelCritical": "Critique",
+    "placeholder": "ex. Nous rencontrons des difficultés avec notre infrastructure serveur et y travaillons.",
+    "posted": "Message publié",
+    "cleared": "Message retiré"
   }
 }

+ 6 - 2
messages/fr/customers.json

@@ -38,7 +38,8 @@
       "messages": "Messages",
       "requests": "Demandes ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Plaque",
@@ -68,7 +69,10 @@
     "customerCreated": "Client créé",
     "saveError": "Échec de l'enregistrement du client",
     "customerNumber": "N° client",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Contact",
+    "sectionBilling": "Facturation",
+    "addScannedVehicle": "Ajouter le véhicule {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "Nouveau",

+ 7 - 0
messages/fr/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Maintenance prévue",
     "reminders": "Rappels à venir",
     "inspections": "Inspections",
+    "serviceRequests": "Demandes de service",
     "quoteRequests": "Demandes de devis",
     "quoteResponses": "Réponses aux devis",
     "sms": "Messages récents",
@@ -68,6 +69,7 @@
     "active": "Actifs",
     "dismissedTab": "Ignorés",
     "noActive": "Aucun véhicule n'a besoin d'entretien actuellement.",
+    "noDismissed": "Aucun véhicule ignoré.",
     "dismissedOn": "Ignoré le {date}",
     "restore": "Restaurer",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} éléments",
     "noData": "Aucune inspection."
   },
+  "serviceRequests": {
+    "title": "Demandes de service",
+    "description": "Demandes de rendez-vous depuis le portail client",
+    "noData": "Aucune demande de service en attente."
+  },
   "quoteRequests": {
     "title": "Demandes de devis",
     "description": "Clients demandant des devis à partir d’inspections partagées",

+ 4 - 1
messages/fr/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Envoyez et recevez des SMS avec vos clients directement depuis Torqvoice.",
     "tabs": {
       "inbox": "Boîte de réception",
-      "scheduled": "Programmés"
+      "scheduled": "Programmés",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "Le SMS n'est pas configuré",
     "smsNotConfiguredHint": "Connectez un fournisseur SMS pour envoyer et recevoir des messages ici. Les e-mails programmés fonctionnent sans cela.",

+ 26 - 0
messages/fr/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Conversations",
+    "searchPlaceholder": "Rechercher des conversations",
+    "noSearchResults": "Aucune conversation ne correspond à votre recherche.",
+    "empty": "Aucune conversation pour l'instant.",
+    "selectConversation": "Choisissez une conversation",
+    "back": "Retour",
+    "newMessage": "Nouveau message",
+    "unknownContact": "Ce numéro n'appartient encore à aucun client. Ajoutez-le comme client pour répondre.",
+    "noChannelsTitle": "Aucun canal de messagerie configuré",
+    "noChannelsHint": "Connectez SMS, WhatsApp ou Telegram pour écrire à vos clients d'ici.",
+    "goToProviders": "Configurer un fournisseur",
+    "chooseChannel": {
+      "title": "Envoyer sur quel canal ?",
+      "description": "Vous avez plusieurs façons de joindre vos clients."
+    },
+    "deleteConversation": "Supprimer la conversation",
+    "deleteTitle": "Supprimer cette conversation ?",
+    "deleteDescription": "Supprime votre copie de la conversation avec {name}. Les messages restent sur leur téléphone.",
+    "deleteFailed": "Impossible de supprimer la conversation",
+    "delete": "Supprimer",
+    "cancel": "Annuler",
+    "loadMore": "Charger plus"
+  }
+}

+ 4 - 2
messages/fr/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Tous les jeux de pneus",
     "tireStorage": "Organisation du stockage",
     "tireSetDetails": "Jeu de pneus",
-    "tireForecast": "La saison prochaine"
+    "tireForecast": "La saison prochaine",
+    "whatsapp": "WhatsApp"
   },
   "search": "Rechercher…",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Plus",
     "auditLog": "Journal d'audit",
     "aiAssistant": "Assistant IA",
-    "tireHotel": "Hôtel à pneus"
+    "tireHotel": "Hôtel à pneus",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/fr/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "E-mail",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "Note dans l'app"
+      "in_app": "Note dans l'app",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Répéter",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Programmer",
     "scheduled": "Message programmé",
     "updated": "Message programmé mis à jour",
-    "saveError": "Impossible d'enregistrer le message"
+    "saveError": "Impossible d'enregistrer le message",
+    "whatsappNeedsTemplate": "Ce message partira bien après la fermeture de la fenêtre de 24 heures et aucun modèle texte WhatsApp n'est configuré : il échouerait à l'envoi. Configurez-en un dans Paramètres > Fournisseurs > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Programmer un message",

+ 35 - 3
messages/fr/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Hôtel à pneus",
         "description": "Stockage saisonnier des pneus"
+      },
+      "providers": {
+        "title": "Fournisseurs",
+        "description": "E-mail, SMS, WhatsApp et Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Modèles d'inspection, inspections et résultats",
     "optAuditLogs": "Journaux d'audit",
     "optAuditLogsDesc": "Historique des activités et des modifications",
-    "optSmsMessages": "Messages SMS",
-    "optSmsMessagesDesc": "Messages SMS envoyés et reçus",
+    "optSmsMessages": "Messages",
+    "optSmsMessagesDesc": "Messages SMS, WhatsApp et Telegram envoyés et reçus",
     "optScheduledMessages": "Messages programmés",
     "optScheduledMessagesDesc": "Messages en attente et récurrents qui doivent encore partir",
     "optNotifications": "Notifications",
@@ -980,7 +984,9 @@
       "supportAction": "contactez notre équipe d'assistance."
     },
     "optTireHotel": "Hôtel à pneus",
-    "optTireHotelDesc": "Jeux stockés, étagères, préparation et contrats de stockage"
+    "optTireHotelDesc": "Jeux stockés, étagères, préparation et contrats de stockage",
+    "optWorkshopConfig": "Configuration de l'atelier",
+    "optWorkshopConfigDesc": "Modèles de main-d'œuvre, rôles, webhooks et rapports programmés"
   },
   "license": {
     "title": "Licence marque blanche",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Prix de la préparation",
     "prepHint": "Ce que coûte chaque type de préparation. Laissez vide et rien n’est facturé, ce qui convient aux ateliers qui incluent le lavage dans le stockage. Les prestations chiffrées sont ajoutées automatiquement au devis et à l’ordre.",
     "prepNotCharged": "non facturé"
+  },
+  "providers": {
+    "tabs": {
+      "email": "E-mail",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "Paramètres e-mail",
+        "description": "Configurez l'envoi SMTP pour transmettre factures, devis et notifications depuis votre atelier."
+      },
+      "sms": {
+        "feature": "Messagerie SMS",
+        "description": "Envoyez et recevez des SMS avec vos clients depuis Torqvoice."
+      },
+      "whatsapp": {
+        "feature": "Messagerie WhatsApp",
+        "description": "Envoyez et recevez des messages WhatsApp, photos de pièces comprises, depuis Torqvoice."
+      },
+      "telegram": {
+        "feature": "Messagerie Telegram",
+        "description": "Envoyez et recevez des messages Telegram avec vos clients depuis Torqvoice."
+      }
+    }
   }
 }

+ 2 - 1
messages/fr/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Ignorer",
     "send": "Envoyer",
     "sent": "Rapport d'état envoyé",
-    "failed": "Échec de l'envoi du rapport d'état"
+    "failed": "Échec de l'envoi du rapport d'état",
+    "notSent": "Non envoyé"
   },
   "view": {
     "title": "Rapport d'état du véhicule",

+ 16 - 1
messages/fr/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Numéro d'Immatriculation",
     "vehicleUpdated": "Véhicule mis à jour",
     "vehicleAdded": "Véhicule ajouté",
-    "saveError": "Échec de l'enregistrement du véhicule"
+    "saveError": "Échec de l'enregistrement du véhicule",
+    "scanDocument": "Scanner la carte grise",
+    "scanDocumentHint": "Photographiez les papiers du véhicule, les champs se remplissent automatiquement.",
+    "scanningDocument": "Lecture du document...",
+    "scanSuccess": "Champs remplis à partir du document",
+    "scanFailed": "Impossible de lire le document",
+    "ownerMatchTitle": "Attribuer ce véhicule",
+    "ownerMatchDescription": "Le document indique {name}. Choisissez un de ces clients ou créez-en un nouveau.",
+    "ownerMatchScore": "{percent}% de correspondance",
+    "ownerMatchCreateNew": "Créer un nouveau client",
+    "ownerMatchChoose": "Choisir un client",
+    "scanUnavailable": "Activez l'IA dans les paramètres",
+    "sectionAssignment": "Photo et titulaire",
+    "sectionDetails": "Détails",
+    "addScannedOwner": "Ajouter le client {name}",
+    "ownerSaveFailed": "Impossible de créer le client"
   },
   "archiveDialog": {
     "title": "Archiver le véhicule",

+ 179 - 0
messages/fr/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "Envoyez et recevez des messages WhatsApp avec vos clients.",
+    "helpLink": "Configurer {provider}",
+    "save": "Enregistrer les paramètres WhatsApp",
+    "saved": "Paramètres WhatsApp enregistrés",
+    "saveError": "Impossible d'enregistrer les paramètres WhatsApp",
+    "enable": {
+      "label": "Activer WhatsApp",
+      "hint": "Activez une fois les identifiants du fournisseur renseignés.",
+      "disabledInfo": "Activez WhatsApp pour configurer votre fournisseur, votre numéro et votre modèle."
+    },
+    "provider": {
+      "label": "Fournisseur",
+      "hint": "Qui fournit votre numéro WhatsApp. Un changement conserve les identifiants du précédent."
+    },
+    "from": {
+      "label": "Votre numéro WhatsApp",
+      "hint": "Le numéro que voient les clients, au format international."
+    },
+    "secret": {
+      "show": "Afficher",
+      "hide": "Masquer",
+      "stored": "Enregistré et masqué. Saisissez une nouvelle valeur pour le remplacer."
+    },
+    "webhook": {
+      "label": "URL du webhook",
+      "description": "Collez-la chez votre fournisseur pour que les messages entrants arrivent dans Torqvoice.",
+      "copy": "Copier l'URL du webhook",
+      "copied": "URL du webhook copiée",
+      "saveFirst": "Enregistrez ces paramètres avant que votre fournisseur ne vérifie l'URL. La vérification porte sur ce qui est enregistré ici : un formulaire non enregistré échoue."
+    },
+    "template": {
+      "title": "Modèle de message",
+      "description": "Utilisé quand vous écrivez à un client qui ne vous a pas écrit récemment.",
+      "windowExplainer": "WhatsApp n'autorise le texte libre que pendant 24 heures après le dernier message du client. En dehors, votre message doit partir sous forme de modèle approuvé par votre fournisseur.",
+      "nameLabel": "Nom du modèle",
+      "nameHint": "Le nom du modèle approuvé, ou son Content SID chez les fournisseurs qui en utilisent un.",
+      "languageLabel": "Langue du modèle",
+      "languageHint": "Le code de langue sous lequel le modèle a été approuvé, par exemple fr.",
+      "variablesLabel": "Valeurs du modèle, dans l'ordre",
+      "variablesHint": "Choisissez les valeurs qui remplissent les emplacements numérotés de votre modèle, dans le même ordre. Laissez vide si votre modèle n'en a pas.",
+      "tokens": {
+        "customer": "nom du client",
+        "vehicle": "année, marque et modèle",
+        "plate": "immatriculation",
+        "message": "ce que vous écrivez",
+        "workshop": "nom de votre atelier",
+        "photo": "la photo jointe"
+      },
+      "variablesEmpty": "Aucune valeur pour l'instant. Ajoutez-les ci-dessous, dans l'ordre attendu par votre modèle.",
+      "variablesRemove": "Retirer {name}",
+      "mediaUrlLabel": "URL du média pour votre modèle",
+      "mediaUrlHint": "Collez ceci dans le champ média du modèle et remplacez n par le numéro de la valeur photo.",
+      "mediaUrlCopy": "Copier l'URL du média",
+      "mediaUrlCopied": "URL du média copiée",
+      "mediaUrlSample": "Comme valeur d'exemple chez le fournisseur, utilisez : sample",
+      "textTitle": "Messages texte",
+      "textDescription": "Utilisé quand vous écrivez sans photo : depuis Messages, depuis l'onglet WhatsApp d'un client ou depuis un message programmé.",
+      "mediaTitle": "Messages avec photo",
+      "mediaDescription": "Utilisé par le bouton Envoyer sur WhatsApp sur les photos d'une intervention, et dès que vous joignez une photo dans une conversation.",
+      "textMissing": "Non configuré : vous ne pouvez pas écrire à un client qui n'a pas écrit depuis 24 heures.",
+      "mediaMissing": "Non configuré : vous ne pouvez pas envoyer de photos à un client qui n'a pas écrit depuis 24 heures.",
+      "whatIsIt": "Un modèle est un message figé que vous écrivez une fois et que {provider} approuve : le texte ne change jamais, vous ne remplissez que les emplacements numérotés. Écrivez-le dans leur console, puis collez son identifiant ici.",
+      "whyTwo": "Il en faut deux car WhatsApp fige le type de média à l'approbation. Un modèle approuvé avec une photo ne peut porter que des photos, et un approuvé comme texte n'en portera jamais.",
+      "exampleLabel": "Exemple de corps à écrire dans la console de votre fournisseur",
+      "exampleTextValues": "À remplir avec : customer, message. Rédigez-le dans la langue de vos clients.",
+      "exampleMediaValues": "À remplir avec : customer, message et photo pour l'URL du média. Rédigez-le dans la langue de vos clients.",
+      "exampleMediaValuesHeader": "Remplissez-la avec : customer, message. La photo est jointe automatiquement, elle n'a donc pas besoin de son propre espace. Rédigez-la dans la langue de vos clients."
+    },
+    "test": {
+      "send": "Envoyer un test",
+      "sent": "Message de test envoyé",
+      "failed": "Impossible d'envoyer le message de test",
+      "hint": "Écrivez-vous d'abord sur WhatsApp, sinon le test exige un modèle approuvé."
+    },
+    "disconnect": {
+      "action": "Déconnecter WhatsApp",
+      "hint": "Supprime les identifiants de Torqvoice. Vos conversations sont conservées.",
+      "done": "WhatsApp déconnecté",
+      "title": "Déconnecter"
+    },
+    "savedIncomplete": "Enregistré. Il manque encore pour envoyer : {fields}",
+    "fieldStillNeeded": "Encore nécessaire pour envoyer",
+    "steps": {
+      "openProvider": "Ouvrir {provider}",
+      "openNumber": "Trouver votre numéro d'expéditeur",
+      "openToken": "Créer un jeton permanent",
+      "consolePath": "Dans Meta, allez à :",
+      "provider": {
+        "title": "Choisissez votre fournisseur et votre numéro",
+        "description": "Twilio démarre plus vite et gère Meta pour vous. L'API Cloud de Meta coûte moins cher, mais vous passez sa vérification d'entreprise vous-même. Saisissez le numéro que verront les clients, au format international."
+      },
+      "credentials": {
+        "title": "Saisissez vos identifiants",
+        "description": "Copiez chaque valeur depuis {provider}. Chaque champ indique où la trouver. Enregistrez dès que vous les avez ; vous pourrez revenir pour le reste."
+      },
+      "webhook": {
+        "title": "Enregistrez le webhook",
+        "description": "Copiez cette adresse dans {provider} pour que les messages des clients parviennent à Torqvoice. Enregistrez d'abord cette page : l'adresse est vérifiée par rapport à ce qui est enregistré ici.",
+        "open": "Ouvrir les réglages webhook de {provider}",
+        "recheck": "Vérifier à nouveau",
+        "waiting": "Rien n'est encore arrivé à cette adresse. Elle passe au vert dès que votre fournisseur la vérifie ou livre un message.",
+        "seen": "Votre fournisseur a atteint cette adresse. Les messages entrants apparaîtront dans Messages."
+      },
+      "templates": {
+        "title": "Faites approuver vos modèles",
+        "description": "Nécessaire seulement pour écrire en premier, hors de la fenêtre de 24 heures. Vous pouvez passer et revenir une fois la messagerie en place.",
+        "open": "Ouvrir les modèles {provider}"
+      },
+      "test": {
+        "title": "Envoyez un message de test",
+        "description": "Écrivez d'abord à votre numéro WhatsApp depuis votre propre téléphone. Cela prouve que le webhook fonctionne et ouvre une fenêtre de 24 heures, la réponse n'a donc pas besoin de modèle."
+      },
+      "back": "Retour",
+      "continue": "Continuer"
+    },
+    "register": {
+      "title": "Enregistrer le numéro auprès de Meta",
+      "description": "Meta exige un enregistrement distinct une fois le numéro vérifié. Choisissez un code à six chiffres, ou reprenez le code de vérification en deux étapes s'il en a un. Notez-le : il servira pour déplacer ou réenregistrer le numéro.",
+      "action": "Enregistrer le numéro",
+      "done": "Numéro enregistré",
+      "failed": "Impossible d'enregistrer le numéro",
+      "limit": "Meta autorise 10 tentatives d'enregistrement par numéro sur 72 heures. Un code erroné compte, vérifiez-le avant."
+    }
+  },
+  "messages": {
+    "featureName": "Messagerie WhatsApp",
+    "featureDescription": "Envoyez et recevez des messages WhatsApp, photos de pièces comprises, depuis Torqvoice.",
+    "noThreads": "Aucune conversation pour l'instant",
+    "noThreadsHint": "Les messages apparaissent ici dès qu'un client écrit à votre numéro WhatsApp, ou que vous lui écrivez.",
+    "selectThread": "Choisissez une conversation",
+    "backToThreads": "Conversations",
+    "empty": "Rien pour l'instant.",
+    "placeholder": "Écrire un message",
+    "send": "Envoyer",
+    "sendFailed": "Impossible d'envoyer le message",
+    "attach": "Joindre une photo",
+    "attachFailed": "Impossible de joindre la photo",
+    "removeAttachment": "Retirer la pièce jointe",
+    "attachment": "Pièce jointe",
+    "delete": "Supprimer",
+    "deleteFailed": "Impossible de supprimer le message",
+    "sentAsTemplate": "modèle",
+    "sentAsTemplateNotice": "Envoyé sous forme de modèle approuvé, car le client n'a pas écrit depuis 24 heures.",
+    "windowClosed": "Le client n'a pas écrit depuis 24 heures, ce message partira donc sous forme de modèle approuvé.",
+    "unknownNumber": "Ce numéro n'appartient encore à aucun client. Ajoutez-le comme client pour répondre.",
+    "notConfigured": {
+      "title": "WhatsApp n'est pas connecté",
+      "description": "Connectez un numéro WhatsApp pour échanger des messages avec vos clients.",
+      "action": "Configurer WhatsApp"
+    },
+    "title": "Conversations",
+    "searchPlaceholder": "Rechercher des conversations",
+    "noSearchResults": "Aucune conversation ne correspond à votre recherche.",
+    "compose": {
+      "newMessage": "Nouveau message",
+      "title": "Nouveau message WhatsApp",
+      "searchPlaceholder": "Rechercher des clients",
+      "noMatches": "Aucun client ne correspond à votre recherche.",
+      "noRecipients": "Aucun client avec un numéro pour l'instant."
+    },
+    "windowBlocked": "Ce client n'a pas écrit depuis 24 heures, WhatsApp ne délivrera donc aucun message maintenant. Demandez-lui de vous écrire, ou configurez un modèle approuvé.",
+    "windowBlockedAction": "Configurer un modèle",
+    "windowBlockedPlaceholder": "En attente d'un message du client"
+  },
+  "sendPhoto": {
+    "action": "Envoyer sur WhatsApp",
+    "title": "Envoyer la photo sur WhatsApp",
+    "description": "Le client reçoit cette photo dans sa discussion WhatsApp.",
+    "captionPlaceholder": "Ajoutez une note, par exemple ce que montre la photo",
+    "send": "Envoyer",
+    "cancel": "Annuler",
+    "sent": "Photo envoyée",
+    "sentAsTemplate": "Photo envoyée sous forme de modèle approuvé",
+    "failed": "Impossible d'envoyer la photo"
+  }
+}

+ 122 - 1
messages/it/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Treno di nuovo in deposito",
     "tire_set_dispose": "Treno di pneumatici dismesso",
     "tire_set_attach": "File aggiunti a un treno di pneumatici",
-    "tire_set_detach": "File rimosso da un treno di pneumatici"
+    "tire_set_detach": "File rimosso da un treno di pneumatici",
+    "whatsapp_send": "Messaggio WhatsApp inviato"
+  },
+  "summary": {
+    "customField_create": "Campo personalizzato «{name}» creato",
+    "customField_delete": "Campo personalizzato «{name}» eliminato",
+    "customField_update": "Campo personalizzato «{name}» aggiornato",
+    "customer_backfillNumbers": "Numeri cliente assegnati a {count, plural, one {# cliente} other {# clienti}}",
+    "customer_create": "Cliente {name} creato",
+    "customer_delete": "Cliente {id} eliminato",
+    "customer_update": "Cliente {id} aggiornato",
+    "email_sendInspection": "Controllo inviato per e-mail a {recipient}",
+    "email_sendInvoice": "Fattura inviata per e-mail a {recipient}",
+    "email_sendQuote": "Preventivo inviato per e-mail a {recipient}",
+    "finding_create": "Rilievo «{description}» creato sul veicolo {vehicleId}",
+    "finding_delete": "Rilievo {id} eliminato",
+    "finding_resolve": "Rilievo «{description}» risolto",
+    "finding_update": "Rilievo «{description}» aggiornato",
+    "inspectionTemplate_create": "Modello di controllo «{name}» creato",
+    "inspectionTemplate_createFromPreset": "Modello di controllo «{name}» creato da una preimpostazione",
+    "inspectionTemplate_delete": "Modello di controllo «{name}» eliminato",
+    "inspectionTemplate_duplicate": "Modello di controllo duplicato in «{name}»",
+    "inspectionTemplate_import": "{count, plural, one {# modello di controllo importato} other {# modelli di controllo importati}} da un pacchetto",
+    "inspectionTemplate_update": "Modello di controllo «{name}» aggiornato",
+    "inspection_complete": "Controllo {id} completato",
+    "inspection_create": "Controllo {id} creato",
+    "inspection_delete": "Controllo {id} eliminato",
+    "inspection_reopen": "Controllo {id} riaperto",
+    "inventory_bulkDelete": "{count, plural, one {# ricambio a magazzino eliminato} other {# ricambi a magazzino eliminati}} in blocco",
+    "inventory_create": "Ricambio a magazzino «{name}» creato",
+    "inventory_delete": "Ricambio a magazzino {id} eliminato",
+    "inventory_update": "Ricambio a magazzino {id} aggiornato",
+    "labor_preset_create": "Preimpostazione di manodopera «{name}» creata",
+    "labor_preset_delete": "Preimpostazione di manodopera {id} eliminata",
+    "labor_preset_update": "Preimpostazione di manodopera {id} aggiornata",
+    "note_create": "Nota creata sul veicolo {vehicleId}",
+    "note_delete": "Nota {id} eliminata",
+    "organization_create": "Organizzazione «{name}» creata",
+    "organization_sampleDataRemove": "Dati di esempio dell'avvio rimossi",
+    "payment_create": "Pagamento {amount} registrato per l'intervento {serviceRecordId}",
+    "payment_delete": "Pagamento {id} eliminato",
+    "quoteRequest_update": "Stato della richiesta di preventivo cambiato in {status, select, pending {in attesa} approved {approvata} rejected {rifiutata} completed {completata} other {{status}}}",
+    "quote_convert": "Preventivo {ref} convertito nell'intervento {serviceRecordId}",
+    "quote_create": "Preventivo {ref} creato",
+    "quote_delete": "Preventivo {ref} eliminato",
+    "quote_status": "Stato del preventivo cambiato in {status, select, draft {bozza} sent {inviato} accepted {accettato} rejected {rifiutato} expired {scaduto} converted {convertito} other {{status}}}",
+    "quote_update": "Preventivo {ref} aggiornato",
+    "recurringInvoice_create": "Fattura ricorrente «{title}» creata",
+    "recurringInvoice_delete": "Fattura ricorrente {id} eliminata",
+    "recurringInvoice_update": "Fattura ricorrente «{title}» aggiornata",
+    "reminder_create": "Promemoria «{title}» creato",
+    "reminder_delete": "Promemoria {id} eliminato",
+    "role_create": "Ruolo «{name}» creato",
+    "role_delete": "Ruolo «{name}» eliminato",
+    "role_update": "Ruolo «{name}» aggiornato",
+    "scheduled_message_cancel": "Messaggio programmato annullato",
+    "scheduled_message_create": "{channel, select, email {E-mail} sms {SMS} telegram {Messaggio Telegram} in_app {Avviso nell'app} other {{channel}}} programmato per {sendAt}",
+    "scheduled_message_delete": "Messaggio programmato eliminato",
+    "scheduled_message_send_now": "{channel, select, email {E-mail} sms {SMS} telegram {Messaggio Telegram} in_app {Avviso nell'app} other {{channel}}} programmato inviato in anticipo",
+    "scheduled_message_update": "{channel, select, email {E-mail} sms {SMS} telegram {Messaggio Telegram} in_app {Avviso nell'app} other {{channel}}} programmato aggiornato",
+    "service_create": "Intervento {ref} creato",
+    "service_createCounterSale": "Vendita al banco {ref} creata",
+    "service_createDraft": "Bozza di intervento {ref} creata",
+    "service_createFromInspection": "Ordine di lavoro {ref} creato da un controllo",
+    "service_delete": "Intervento {ref} eliminato",
+    "service_status": "Stato dell'intervento cambiato in {status, select, pending {in attesa} scheduled {programmato} in_progress {in lavorazione} waiting_parts {in attesa di ricambi} ready {pronto} completed {completato} cancelled {annullato} other {{status}}}",
+    "service_update": "Intervento {ref} aggiornato",
+    "settings_applyTaxRate": "IVA al {rate}% applicata a {records, plural, one {# intervento} other {# interventi}} e {quotes, plural, one {# preventivo} other {# preventivi}}",
+    "settings_convertToExclusive": "{records, plural, one {# intervento convertito} other {# interventi convertiti}} e {quotes, plural, one {# preventivo convertito} other {# preventivi convertiti}} a prezzi IVA esclusa",
+    "settings_convertToInclusive": "{records, plural, one {# intervento convertito} other {# interventi convertiti}} e {quotes, plural, one {# preventivo convertito} other {# preventivi convertiti}} a prezzi IVA inclusa",
+    "settings_deleteContent": "Tutti i dati eliminati: {items}",
+    "settings_updateInvoiceLayout": "Layout della fattura aggiornato",
+    "settings_updateQuoteLayout": "Layout del preventivo aggiornato",
+    "sms_send": "SMS inviato a {name} ({phone})",
+    "statusReport_create": "Rapporto di stato creato per l'intervento {serviceRecordId}",
+    "statusReport_delete": "Rapporto di stato eliminato",
+    "statusReport_send": "Rapporto di stato inviato via {channels}",
+    "subscription_cancel": "Abbonamento disdetto con effetto a fine periodo",
+    "subscription_resume": "Abbonamento ripreso",
+    "team_cancelInvitation": "Invito per {email} annullato",
+    "team_removeMember": "Membro del team {id} rimosso",
+    "team_sendInvitation": "{email} invitato come {role, select, owner {titolare} admin {amministratore} member {membro} other {{role}}}",
+    "team_updateRole": "Ruolo di un membro cambiato in {role, select, owner {titolare} admin {amministratore} member {membro} other {{role}}}",
+    "technician_create": "Tecnico «{name}» creato",
+    "technician_delete": "Tecnico {id} eliminato",
+    "technician_update": "Tecnico {id} aggiornato",
+    "telegram_send": "Messaggio Telegram inviato a {name}",
+    "tire_location_bulk_create": "{count, plural, one {# posto pneumatici creato} other {# posti pneumatici creati}}",
+    "tire_location_create": "Posto pneumatici {code} creato",
+    "tire_location_delete": "Posto pneumatici {code} eliminato",
+    "tire_location_update": "Posto pneumatici {code} aggiornato",
+    "tire_measurement_create": "{count, plural, one {# misurazione registrata} other {# misurazioni registrate}} sul treno di pneumatici {ref}",
+    "tire_set_add_to_work_order": "Treno di pneumatici {ref} aggiunto all'ordine di lavoro {jobRef}",
+    "tire_set_attach": "{count, plural, one {# file aggiunto} other {# file aggiunti}} al treno di pneumatici {ref}",
+    "tire_set_check_in": "Treno di pneumatici {ref} depositato in {code}",
+    "tire_set_check_out": "Treno di pneumatici {ref} riconsegnato",
+    "tire_set_create_quote": "Preventivo {ref} creato da un treno di pneumatici",
+    "tire_set_create_work_order": "Ordine di lavoro {ref} creato da un treno di pneumatici",
+    "tire_set_delete": "Treno di pneumatici {ref} eliminato",
+    "tire_set_detach": "{fileName} rimosso da un treno di pneumatici",
+    "tire_set_dispose": "Treno di pneumatici {ref} rottamato",
+    "tire_set_message_customer": "Cliente avvisato del treno di pneumatici {ref} via {channel, select, email {e-mail} sms {SMS} telegram {Telegram} in_app {avviso nell'app} other {{channel}}}",
+    "tire_set_relocate": "Treno di pneumatici {ref} spostato da {from} a {to}",
+    "tire_set_relocateUnassigned": "Treno di pneumatici {ref} sistemato in {to}",
+    "tire_set_return": "Treno di pneumatici {ref} depositato di nuovo, in {code}",
+    "tire_set_unlink_work_order": "Treno di pneumatici {ref} scollegato dall'ordine di lavoro {jobRef}",
+    "tire_set_update": "Treno di pneumatici {ref} aggiornato",
+    "tire_treatment_complete_all": "{count, plural, one {# lavoro di preparazione completato} other {# lavori di preparazione completati}} sul treno di pneumatici {ref}",
+    "tire_treatment_mark": "{type, select, wash_tires {Lavaggio pneumatici} wash_rims {Lavaggio cerchi} balance {Equilibratura} tpms_service {Servizio TPMS} new_valves {Valvole nuove} repair {Riparazione} other {{type}}} segnato come {status, select, done {fatto} pending {da fare} skipped {saltato} other {{status}}} sul treno di pneumatici {ref}",
+    "tire_treatment_update": "Preparazione del treno di pneumatici {ref} aggiornata",
+    "tire_warehouse_archive": "Deposito pneumatici {name} archiviato",
+    "tire_warehouse_create": "Deposito pneumatici {name} creato",
+    "tire_warehouse_delete": "Deposito pneumatici {name} eliminato",
+    "tire_warehouse_update": "Deposito pneumatici {name} aggiornato",
+    "vehicle_archive": "Veicolo {id} archiviato",
+    "vehicle_create": "Veicolo {year} {make} {model} creato",
+    "vehicle_delete": "Veicolo {name} eliminato",
+    "vehicle_unarchive": "Veicolo {id} tolto dall'archivio",
+    "vehicle_update": "Veicolo {name} aggiornato, modifiche: {fields}",
+    "vehicle_updateNoChanges": "Veicolo {name} aggiornato, nessuna modifica",
+    "webhook_create": "Webhook {name} creato",
+    "whatsapp_send": "Inviato un messaggio WhatsApp a {name}"
   }
 }

+ 15 - 0
messages/it/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Aggiungila alla schermata Home per un accesso rapido",
     "enterFullscreen": "Schermo intero",
     "exitFullscreen": "Esci da schermo intero"
+  },
+  "broadcast": {
+    "dismiss": "Chiudi",
+    "title": "Avviso a tutti",
+    "description": "Mostrato in cima a ogni pagina, per tutti, con o senza accesso. Lascia vuoto per non mostrare nulla.",
+    "message": "Messaggio",
+    "level": "Gravità",
+    "post": "Pubblica avviso",
+    "clear": "Rimuovi avviso",
+    "levelInfo": "Informazione",
+    "levelWarning": "Avvertenza",
+    "levelCritical": "Critico",
+    "placeholder": "es. Stiamo riscontrando problemi con la nostra infrastruttura server e ci stiamo lavorando.",
+    "posted": "Avviso pubblicato",
+    "cleared": "Avviso rimosso"
   }
 }

+ 6 - 2
messages/it/customers.json

@@ -38,7 +38,8 @@
       "messages": "Messaggi",
       "requests": "Richieste ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Targa",
@@ -68,7 +69,10 @@
     "customerCreated": "Cliente creato",
     "saveError": "Salvataggio del cliente non riuscito",
     "customerNumber": "N. cliente",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Contatto",
+    "sectionBilling": "Fatturazione",
+    "addScannedVehicle": "Aggiungi il veicolo {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "Nuova",

+ 7 - 0
messages/it/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Manutenzione prevista",
     "reminders": "Promemoria in scadenza",
     "inspections": "Ispezioni",
+    "serviceRequests": "Richieste di servizio",
     "quoteRequests": "Richieste di preventivo",
     "quoteResponses": "Risposte ai preventivi",
     "sms": "Messaggi recenti",
@@ -68,6 +69,7 @@
     "active": "Attivi",
     "dismissedTab": "Ignorati",
     "noActive": "Nessun veicolo attualmente in scadenza per la manutenzione.",
+    "noDismissed": "Nessun veicolo ignorato.",
     "dismissedOn": "Ignorato il {date}",
     "restore": "Ripristina",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} elementi",
     "noData": "Nessuna ispezione."
   },
+  "serviceRequests": {
+    "title": "Richieste di servizio",
+    "description": "Richieste di appuntamento dal portale clienti",
+    "noData": "Nessuna richiesta di servizio in sospeso."
+  },
   "quoteRequests": {
     "title": "Richieste di preventivo",
     "description": "Clienti che richiedono preventivi dalle ispezioni condivise",

+ 4 - 1
messages/it/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Invia e ricevi messaggi SMS con i tuoi clienti direttamente da Torqvoice.",
     "tabs": {
       "inbox": "In arrivo",
-      "scheduled": "Pianificati"
+      "scheduled": "Pianificati",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "L'SMS non è configurato",
     "smsNotConfiguredHint": "Collega un provider SMS per inviare e ricevere messaggi qui. Le email pianificate funzionano comunque.",

+ 26 - 0
messages/it/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Conversazioni",
+    "searchPlaceholder": "Cerca conversazioni",
+    "noSearchResults": "Nessuna conversazione corrisponde alla ricerca.",
+    "empty": "Ancora nessuna conversazione.",
+    "selectConversation": "Scegli una conversazione",
+    "back": "Indietro",
+    "newMessage": "Nuovo messaggio",
+    "unknownContact": "Questo numero non appartiene ancora a nessun cliente. Aggiungilo come cliente per rispondere.",
+    "noChannelsTitle": "Nessun canale di messaggistica",
+    "noChannelsHint": "Collega SMS, WhatsApp o Telegram per scrivere ai clienti da qui.",
+    "goToProviders": "Configura un provider",
+    "chooseChannel": {
+      "title": "Su quale canale inviare?",
+      "description": "Hai più di un modo per raggiungere i clienti."
+    },
+    "deleteConversation": "Elimina conversazione",
+    "deleteTitle": "Eliminare questa conversazione?",
+    "deleteDescription": "Rimuove la tua copia della conversazione con {name}. Sul telefono del cliente resta tutto.",
+    "deleteFailed": "Impossibile eliminare la conversazione",
+    "delete": "Elimina",
+    "cancel": "Annulla",
+    "loadMore": "Carica altro"
+  }
+}

+ 4 - 2
messages/it/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Tutti i treni",
     "tireStorage": "Disposizione del deposito",
     "tireSetDetails": "Treno di pneumatici",
-    "tireForecast": "La prossima stagione"
+    "tireForecast": "La prossima stagione",
+    "whatsapp": "WhatsApp"
   },
   "search": "Cerca...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Altro",
     "auditLog": "Registro attività",
     "aiAssistant": "Assistente IA",
-    "tireHotel": "Hotel pneumatici"
+    "tireHotel": "Hotel pneumatici",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/it/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "Email",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "Nota nell'app"
+      "in_app": "Nota nell'app",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Ripeti",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Pianifica",
     "scheduled": "Messaggio pianificato",
     "updated": "Messaggio pianificato aggiornato",
-    "saveError": "Impossibile salvare il messaggio"
+    "saveError": "Impossibile salvare il messaggio",
+    "whatsappNeedsTemplate": "Partirà molto dopo la chiusura della finestra di 24 ore e non c'è un modello di testo WhatsApp configurato, quindi fallirebbe all'invio. Configuralo in Impostazioni > Provider > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Pianifica messaggio",

+ 35 - 3
messages/it/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Hotel pneumatici",
         "description": "Deposito stagionale degli pneumatici"
+      },
+      "providers": {
+        "title": "Provider",
+        "description": "Email, SMS, WhatsApp e Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Modelli di ispezione, ispezioni e risultati",
     "optAuditLogs": "Registri di audit",
     "optAuditLogsDesc": "Cronologia delle attività e delle modifiche",
-    "optSmsMessages": "Messaggi SMS",
-    "optSmsMessagesDesc": "Messaggi SMS inviati e ricevuti",
+    "optSmsMessages": "Messaggi",
+    "optSmsMessagesDesc": "Messaggi SMS, WhatsApp e Telegram inviati e ricevuti",
     "optScheduledMessages": "Messaggi pianificati",
     "optScheduledMessagesDesc": "Messaggi in coda e ricorrenti in attesa di essere inviati",
     "optNotifications": "Notifiche",
@@ -980,7 +984,9 @@
       "supportAction": "contatta il nostro team di supporto."
     },
     "optTireHotel": "Hotel pneumatici",
-    "optTireHotelDesc": "Treni in deposito, scaffali, preparazione e contratti di deposito"
+    "optTireHotelDesc": "Treni in deposito, scaffali, preparazione e contratti di deposito",
+    "optWorkshopConfig": "Configurazione officina",
+    "optWorkshopConfigDesc": "Preset di manodopera, ruoli, webhook e report programmati"
   },
   "license": {
     "title": "Licenza White-Label",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Prezzi della preparazione",
     "prepHint": "Quanto costa ogni tipo di preparazione. Lasciandolo vuoto non viene mai fatturata, come serve a chi include il lavaggio nel deposito. Quelle con un prezzo finiscono da sole su preventivo e ordine.",
     "prepNotCharged": "non addebitato"
+  },
+  "providers": {
+    "tabs": {
+      "email": "Email",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "Impostazioni email",
+        "description": "Configura l'invio SMTP per recapitare fatture, preventivi e notifiche dalla tua officina."
+      },
+      "sms": {
+        "feature": "Messaggi SMS",
+        "description": "Invia e ricevi SMS con i tuoi clienti da Torqvoice."
+      },
+      "whatsapp": {
+        "feature": "Messaggi WhatsApp",
+        "description": "Invia e ricevi messaggi WhatsApp, foto dei ricambi comprese, da Torqvoice."
+      },
+      "telegram": {
+        "feature": "Messaggi Telegram",
+        "description": "Invia e ricevi messaggi Telegram con i tuoi clienti da Torqvoice."
+      }
+    }
   }
 }

+ 2 - 1
messages/it/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Salta",
     "send": "Invia",
     "sent": "Rapporto di stato inviato",
-    "failed": "Invio del rapporto di stato non riuscito"
+    "failed": "Invio del rapporto di stato non riuscito",
+    "notSent": "Non inviato"
   },
   "view": {
     "title": "Rapporto di stato del veicolo",

+ 16 - 1
messages/it/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Numero di Registrazione",
     "vehicleUpdated": "Veicolo aggiornato",
     "vehicleAdded": "Veicolo aggiunto",
-    "saveError": "Impossibile salvare il veicolo"
+    "saveError": "Impossibile salvare il veicolo",
+    "scanDocument": "Scansiona il libretto di circolazione",
+    "scanDocumentHint": "Fotografa i documenti del veicolo e i campi si compilano da soli.",
+    "scanningDocument": "Lettura del documento...",
+    "scanSuccess": "Campi compilati dal documento",
+    "scanFailed": "Impossibile leggere il documento",
+    "ownerMatchTitle": "Assegna questo veicolo",
+    "ownerMatchDescription": "Il documento riporta {name}. Scegli uno di questi clienti o creane uno nuovo.",
+    "ownerMatchScore": "{percent}% di corrispondenza",
+    "ownerMatchCreateNew": "Crea nuovo cliente",
+    "ownerMatchChoose": "Scegli cliente",
+    "scanUnavailable": "Attiva l'IA nelle impostazioni",
+    "sectionAssignment": "Foto e intestatario",
+    "sectionDetails": "Dettagli",
+    "addScannedOwner": "Aggiungi il cliente {name}",
+    "ownerSaveFailed": "Impossibile creare il cliente"
   },
   "archiveDialog": {
     "title": "Archivia Veicolo",

+ 179 - 0
messages/it/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "Invia e ricevi messaggi WhatsApp con i tuoi clienti.",
+    "helpLink": "Configura {provider}",
+    "save": "Salva impostazioni WhatsApp",
+    "saved": "Impostazioni WhatsApp salvate",
+    "saveError": "Impossibile salvare le impostazioni WhatsApp",
+    "enable": {
+      "label": "Attiva WhatsApp",
+      "hint": "Attiva quando le credenziali del provider sono inserite.",
+      "disabledInfo": "Attiva WhatsApp per configurare provider, numero e modello."
+    },
+    "provider": {
+      "label": "Provider",
+      "hint": "Chi fornisce il tuo numero WhatsApp. Cambiando restano le credenziali del precedente."
+    },
+    "from": {
+      "label": "Il tuo numero WhatsApp",
+      "hint": "Il numero che vedono i clienti, in formato internazionale."
+    },
+    "secret": {
+      "show": "Mostra",
+      "hide": "Nascondi",
+      "stored": "Salvato e nascosto. Scrivi un nuovo valore per sostituirlo."
+    },
+    "webhook": {
+      "label": "URL webhook",
+      "description": "Incollalo nel tuo provider così i messaggi in arrivo raggiungono Torqvoice.",
+      "copy": "Copia URL webhook",
+      "copied": "URL webhook copiato",
+      "saveFirst": "Salva queste impostazioni prima che il provider verifichi l'URL. Il controllo avviene su ciò che è salvato qui, quindi un modulo non salvato fallisce."
+    },
+    "template": {
+      "title": "Modello di messaggio",
+      "description": "Usato quando scrivi a un cliente che non ti ha scritto di recente.",
+      "windowExplainer": "WhatsApp consente testo libero solo per 24 ore dall'ultimo messaggio del cliente. Fuori da quella finestra il messaggio deve partire come modello approvato dal provider.",
+      "nameLabel": "Nome del modello",
+      "nameHint": "Il nome del modello approvato, o il Content SID nei provider che lo usano.",
+      "languageLabel": "Lingua del modello",
+      "languageHint": "Il codice lingua con cui è stato approvato, per esempio it.",
+      "variablesLabel": "Valori del modello, in ordine",
+      "variablesHint": "Scegli i valori che riempiono gli spazi numerati del modello, nello stesso ordine. Lascia vuoto se il modello non ha spazi.",
+      "tokens": {
+        "customer": "nome del cliente",
+        "vehicle": "anno, marca e modello",
+        "plate": "targa",
+        "message": "ciò che scrivi",
+        "workshop": "nome della tua officina",
+        "photo": "la foto allegata"
+      },
+      "variablesEmpty": "Ancora nessun valore. Aggiungili sotto, nell'ordine che il modello si aspetta.",
+      "variablesRemove": "Rimuovi {name}",
+      "mediaUrlLabel": "URL del media per il tuo modello",
+      "mediaUrlHint": "Incolla questo nel campo media del modello e sostituisci n con il numero del valore foto.",
+      "mediaUrlCopy": "Copia URL del media",
+      "mediaUrlCopied": "URL del media copiato",
+      "mediaUrlSample": "Come valore di esempio presso il provider, usa: sample",
+      "textTitle": "Messaggi di testo",
+      "textDescription": "Usato quando scrivi senza foto: da Messaggi, dalla scheda WhatsApp di un cliente o da un messaggio programmato.",
+      "mediaTitle": "Messaggi con foto",
+      "mediaDescription": "Usato dal pulsante Invia su WhatsApp sulle foto di un intervento, e ogni volta che alleghi una foto in una conversazione.",
+      "textMissing": "Non configurato: non puoi scrivere a un cliente che non ti scrive da 24 ore.",
+      "mediaMissing": "Non configurato: non puoi inviare foto a un cliente che non ti scrive da 24 ore.",
+      "whatIsIt": "Un modello è un messaggio fisso che scrivi una volta e che {provider} approva: il testo non cambia mai, riempi solo gli spazi numerati. Scrivilo nella loro console e incolla qui il suo identificatore.",
+      "whyTwo": "Ne servono due perché WhatsApp fissa il tipo di media all'approvazione. Uno approvato con foto può portare solo foto, uno approvato come testo non ne porterà mai.",
+      "exampleLabel": "Corpo di esempio da scrivere nella console del provider",
+      "exampleTextValues": "Riempilo con: customer, message. Scrivilo nella lingua dei tuoi clienti.",
+      "exampleMediaValues": "Riempilo con: customer, message e photo per l'URL del media. Scrivilo nella lingua dei tuoi clienti.",
+      "exampleMediaValuesHeader": "La riempia con: customer, message. La foto viene allegata automaticamente, quindi non serve uno spazio dedicato. La scriva nella lingua dei suoi clienti."
+    },
+    "test": {
+      "send": "Invia prova",
+      "sent": "Messaggio di prova inviato",
+      "failed": "Impossibile inviare il messaggio di prova",
+      "hint": "Scriviti prima su WhatsApp, altrimenti la prova richiede un modello approvato."
+    },
+    "disconnect": {
+      "action": "Disconnetti WhatsApp",
+      "hint": "Rimuove le credenziali da Torqvoice. Le conversazioni restano.",
+      "done": "WhatsApp disconnesso",
+      "title": "Disconnetti"
+    },
+    "savedIncomplete": "Salvato. Per inviare manca ancora: {fields}",
+    "fieldStillNeeded": "Ancora necessario per inviare",
+    "steps": {
+      "openProvider": "Apri {provider}",
+      "openNumber": "Trova il tuo numero mittente",
+      "openToken": "Crea un token permanente",
+      "consolePath": "In Meta, vada a:",
+      "provider": {
+        "title": "Scegli provider e numero",
+        "description": "Twilio parte prima e gestisce Meta per te. Meta Cloud API costa meno, ma la verifica aziendale la completi tu. Inserisci il numero che vedranno i clienti, in formato internazionale."
+      },
+      "credentials": {
+        "title": "Inserisci le credenziali",
+        "description": "Copia ogni valore da {provider}. Ogni campo dice dove trovarlo. Salva quando li hai; puoi tornare per il resto."
+      },
+      "webhook": {
+        "title": "Registra il webhook",
+        "description": "Copia questo indirizzo in {provider} così i messaggi dei clienti arrivano a Torqvoice. Salva prima questa pagina: l'indirizzo viene confrontato con quanto è salvato qui.",
+        "open": "Apri le impostazioni webhook di {provider}",
+        "recheck": "Controlla di nuovo",
+        "waiting": "A questo indirizzo non è ancora arrivato nulla. Diventa verde appena il provider lo verifica o consegna un messaggio.",
+        "seen": "Il tuo provider ha raggiunto questo indirizzo. I messaggi in arrivo compariranno in Messaggi."
+      },
+      "templates": {
+        "title": "Fai approvare i modelli",
+        "description": "Serve solo per scrivere per primo, fuori dalla finestra di 24 ore. Puoi saltarlo e tornarci quando la messaggistica funziona.",
+        "open": "Apri i modelli {provider}"
+      },
+      "test": {
+        "title": "Invia un messaggio di prova",
+        "description": "Scrivi prima al tuo numero WhatsApp dal tuo telefono. Questo dimostra che il webhook funziona e apre una finestra di 24 ore, così la risposta non richiede modelli."
+      },
+      "back": "Indietro",
+      "continue": "Continua"
+    },
+    "register": {
+      "title": "Registra il numero presso Meta",
+      "description": "Meta richiede una registrazione a parte una volta verificato il numero. Scegli un PIN di sei cifre, oppure usa il PIN di verifica in due passaggi se già presente. Annotalo: ti servirà per spostare o registrare di nuovo il numero.",
+      "action": "Registra numero",
+      "done": "Numero registrato",
+      "failed": "Impossibile registrare il numero",
+      "limit": "Meta consente 10 tentativi di registrazione per numero in 72 ore. Un PIN sbagliato conta, quindi controllalo prima."
+    }
+  },
+  "messages": {
+    "featureName": "Messaggi WhatsApp",
+    "featureDescription": "Invia e ricevi messaggi WhatsApp, foto dei ricambi comprese, da Torqvoice.",
+    "noThreads": "Ancora nessuna conversazione",
+    "noThreadsHint": "I messaggi compaiono qui appena un cliente scrive al tuo numero WhatsApp, o tu scrivi a lui.",
+    "selectThread": "Scegli una conversazione",
+    "backToThreads": "Conversazioni",
+    "empty": "Qui non c'è ancora nulla.",
+    "placeholder": "Scrivi un messaggio",
+    "send": "Invia",
+    "sendFailed": "Impossibile inviare il messaggio",
+    "attach": "Allega una foto",
+    "attachFailed": "Impossibile allegare la foto",
+    "removeAttachment": "Rimuovi allegato",
+    "attachment": "Allegato",
+    "delete": "Elimina",
+    "deleteFailed": "Impossibile eliminare il messaggio",
+    "sentAsTemplate": "modello",
+    "sentAsTemplateNotice": "Inviato come modello approvato, perché il cliente non scrive da oltre 24 ore.",
+    "windowClosed": "Il cliente non scrive da oltre 24 ore, quindi questo partirà come modello approvato.",
+    "unknownNumber": "Questo numero non appartiene ancora a nessun cliente. Aggiungilo come cliente per rispondere.",
+    "notConfigured": {
+      "title": "WhatsApp non è collegato",
+      "description": "Collega un numero WhatsApp per scambiare messaggi con i tuoi clienti.",
+      "action": "Configura WhatsApp"
+    },
+    "title": "Conversazioni",
+    "searchPlaceholder": "Cerca conversazioni",
+    "noSearchResults": "Nessuna conversazione corrisponde alla ricerca.",
+    "compose": {
+      "newMessage": "Nuovo messaggio",
+      "title": "Nuovo messaggio WhatsApp",
+      "searchPlaceholder": "Cerca clienti",
+      "noMatches": "Nessun cliente corrisponde alla ricerca.",
+      "noRecipients": "Ancora nessun cliente con un numero di telefono."
+    },
+    "windowBlocked": "Questo cliente non scrive da oltre 24 ore, quindi WhatsApp non consegnerà un messaggio adesso. Chiedigli di scriverti oppure configura un modello approvato.",
+    "windowBlockedAction": "Configura un modello",
+    "windowBlockedPlaceholder": "In attesa che scriva il cliente"
+  },
+  "sendPhoto": {
+    "action": "Invia su WhatsApp",
+    "title": "Invia la foto su WhatsApp",
+    "description": "Il cliente riceve questa foto nella sua chat WhatsApp.",
+    "captionPlaceholder": "Aggiungi una nota, per esempio cosa mostra la foto",
+    "send": "Invia",
+    "cancel": "Annulla",
+    "sent": "Foto inviata",
+    "sentAsTemplate": "Foto inviata come modello approvato",
+    "failed": "Impossibile inviare la foto"
+  }
+}

+ 122 - 1
messages/lt/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Komplektas vėl saugomas",
     "tire_set_dispose": "Padangų komplektas nurašytas",
     "tire_set_attach": "Prie padangų komplekto pridėta failų",
-    "tire_set_detach": "Failas pašalintas iš padangų komplekto"
+    "tire_set_detach": "Failas pašalintas iš padangų komplekto",
+    "whatsapp_send": "Išsiųsta „WhatsApp“ žinutė"
+  },
+  "summary": {
+    "customField_create": "Sukurtas pasirinktinis laukas „{name}“",
+    "customField_delete": "Ištrintas pasirinktinis laukas „{name}“",
+    "customField_update": "Atnaujintas pasirinktinis laukas „{name}“",
+    "customer_backfillNumbers": "Kliento numeriai priskirti {count, plural, one {# klientui} few {# klientams} many {# kliento} other {# klientų}}",
+    "customer_create": "Sukurtas klientas {name}",
+    "customer_delete": "Ištrintas klientas {id}",
+    "customer_update": "Atnaujintas klientas {id}",
+    "email_sendInspection": "Patikra išsiųsta el. paštu adresu {recipient}",
+    "email_sendInvoice": "Sąskaita išsiųsta el. paštu adresu {recipient}",
+    "email_sendQuote": "Pasiūlymas išsiųstas el. paštu adresu {recipient}",
+    "finding_create": "Sukurtas radinys „{description}“ transporto priemonei {vehicleId}",
+    "finding_delete": "Ištrintas radinys {id}",
+    "finding_resolve": "Išspręstas radinys „{description}“",
+    "finding_update": "Atnaujintas radinys „{description}“",
+    "inspectionTemplate_create": "Sukurtas patikros šablonas „{name}“",
+    "inspectionTemplate_createFromPreset": "Patikros šablonas „{name}“ sukurtas iš paruošto rinkinio",
+    "inspectionTemplate_delete": "Ištrintas patikros šablonas „{name}“",
+    "inspectionTemplate_duplicate": "Patikros šablonas nukopijuotas į „{name}“",
+    "inspectionTemplate_import": "Iš paketo importuota {count, plural, one {# patikros šablonas} few {# patikros šablonai} many {# patikros šablono} other {# patikros šablonų}}",
+    "inspectionTemplate_update": "Atnaujintas patikros šablonas „{name}“",
+    "inspection_complete": "Užbaigta patikra {id}",
+    "inspection_create": "Sukurta patikra {id}",
+    "inspection_delete": "Ištrinta patikra {id}",
+    "inspection_reopen": "Patikra {id} atidaryta iš naujo",
+    "inventory_bulkDelete": "Masiškai ištrinta {count, plural, one {# sandėlio dalis} few {# sandėlio dalys} many {# sandėlio dalies} other {# sandėlio dalių}}",
+    "inventory_create": "Sukurta sandėlio dalis „{name}“",
+    "inventory_delete": "Ištrinta sandėlio dalis {id}",
+    "inventory_update": "Atnaujinta sandėlio dalis {id}",
+    "labor_preset_create": "Sukurtas darbų šablonas „{name}“",
+    "labor_preset_delete": "Ištrintas darbų šablonas {id}",
+    "labor_preset_update": "Atnaujintas darbų šablonas {id}",
+    "note_create": "Sukurtas užrašas transporto priemonei {vehicleId}",
+    "note_delete": "Ištrintas užrašas {id}",
+    "organization_create": "Sukurta organizacija „{name}“",
+    "organization_sampleDataRemove": "Pašalinti pradinės sąrankos pavyzdiniai duomenys",
+    "payment_create": "Užregistruotas mokėjimas {amount} už darbą {serviceRecordId}",
+    "payment_delete": "Ištrintas mokėjimas {id}",
+    "quoteRequest_update": "Pasiūlymo užklausos būsena pakeista į „{status, select, pending {laukiama} approved {patvirtinta} rejected {atmesta} completed {užbaigta} other {{status}}}“",
+    "quote_convert": "Pasiūlymas {ref} paverstas darbu {serviceRecordId}",
+    "quote_create": "Sukurtas pasiūlymas {ref}",
+    "quote_delete": "Ištrintas pasiūlymas {ref}",
+    "quote_status": "Pasiūlymo būsena pakeista į „{status, select, draft {juodraštis} sent {išsiųstas} accepted {priimtas} rejected {atmestas} expired {nebegalioja} converted {paverstas} other {{status}}}“",
+    "quote_update": "Atnaujintas pasiūlymas {ref}",
+    "recurringInvoice_create": "Sukurta pasikartojanti sąskaita „{title}“",
+    "recurringInvoice_delete": "Ištrinta pasikartojanti sąskaita {id}",
+    "recurringInvoice_update": "Atnaujinta pasikartojanti sąskaita „{title}“",
+    "reminder_create": "Sukurtas priminimas „{title}“",
+    "reminder_delete": "Ištrintas priminimas {id}",
+    "role_create": "Sukurta rolė „{name}“",
+    "role_delete": "Ištrinta rolė „{name}“",
+    "role_update": "Atnaujinta rolė „{name}“",
+    "scheduled_message_cancel": "Suplanuota žinutė atšaukta",
+    "scheduled_message_create": "Suplanuota {sendAt}: {channel, select, email {el. laiškas} sms {SMS} telegram {Telegram žinutė} in_app {pranešimas programoje} other {{channel}}}",
+    "scheduled_message_delete": "Suplanuota žinutė ištrinta",
+    "scheduled_message_send_now": "Suplanuotas {channel, select, email {el. laiškas} sms {SMS} telegram {Telegram žinutė} in_app {pranešimas programoje} other {{channel}}} išsiųstas anksčiau laiko",
+    "scheduled_message_update": "Atnaujintas suplanuotas {channel, select, email {el. laiškas} sms {SMS} telegram {Telegram žinutė} in_app {pranešimas programoje} other {{channel}}}",
+    "service_create": "Sukurtas darbas {ref}",
+    "service_createCounterSale": "Sukurtas pardavimas prie prekystalio {ref}",
+    "service_createDraft": "Sukurtas darbo juodraštis {ref}",
+    "service_createFromInspection": "Iš patikros sukurtas darbo užsakymas {ref}",
+    "service_delete": "Ištrintas darbas {ref}",
+    "service_status": "Darbo būsena pakeista į „{status, select, pending {laukiama} scheduled {suplanuota} in_progress {vykdoma} waiting_parts {laukiama dalių} ready {paruošta} completed {užbaigta} cancelled {atšaukta} other {{status}}}“",
+    "service_update": "Atnaujintas darbas {ref}",
+    "settings_applyTaxRate": "{rate}% mokestis pritaikytas {records, plural, one {# darbui} few {# darbams} many {# darbo} other {# darbų}} ir {quotes, plural, one {# pasiūlymui} few {# pasiūlymams} many {# pasiūlymo} other {# pasiūlymų}}",
+    "settings_convertToExclusive": "{records, plural, one {# darbas} few {# darbai} many {# darbo} other {# darbų}} ir {quotes, plural, one {# pasiūlymas} few {# pasiūlymai} many {# pasiūlymo} other {# pasiūlymų}} perskaičiuoti į kainas be mokesčių",
+    "settings_convertToInclusive": "{records, plural, one {# darbas} few {# darbai} many {# darbo} other {# darbų}} ir {quotes, plural, one {# pasiūlymas} few {# pasiūlymai} many {# pasiūlymo} other {# pasiūlymų}} perskaičiuoti į kainas su mokesčiais",
+    "settings_deleteContent": "Ištrinti visi duomenys: {items}",
+    "settings_updateInvoiceLayout": "Atnaujintas sąskaitos išdėstymas",
+    "settings_updateQuoteLayout": "Atnaujintas pasiūlymo išdėstymas",
+    "sms_send": "SMS išsiųsta {name} ({phone})",
+    "statusReport_create": "Sukurta būsenos ataskaita darbui {serviceRecordId}",
+    "statusReport_delete": "Ištrinta būsenos ataskaita",
+    "statusReport_send": "Būsenos ataskaita išsiųsta per {channels}",
+    "subscription_cancel": "Prenumerata atšaukta, galios iki laikotarpio pabaigos",
+    "subscription_resume": "Prenumerata atnaujinta",
+    "team_cancelInvitation": "Atšauktas kvietimas {email}",
+    "team_removeMember": "Pašalintas komandos narys {id}",
+    "team_sendInvitation": "{email} pakviestas kaip {role, select, owner {savininkas} admin {administratorius} member {narys} other {{role}}}",
+    "team_updateRole": "Nario rolė pakeista į „{role, select, owner {savininkas} admin {administratorius} member {narys} other {{role}}}“",
+    "technician_create": "Sukurtas meistras „{name}“",
+    "technician_delete": "Ištrintas meistras {id}",
+    "technician_update": "Atnaujintas meistras {id}",
+    "telegram_send": "Telegram žinutė išsiųsta {name}",
+    "tire_location_bulk_create": "Sukurta {count, plural, one {# padangų saugojimo vieta} few {# padangų saugojimo vietos} many {# padangų saugojimo vietos} other {# padangų saugojimo vietų}}",
+    "tire_location_create": "Sukurta padangų saugojimo vieta {code}",
+    "tire_location_delete": "Ištrinta padangų saugojimo vieta {code}",
+    "tire_location_update": "Atnaujinta padangų saugojimo vieta {code}",
+    "tire_measurement_create": "Padangų komplektui {ref} užrašyta {count, plural, one {# matavimas} few {# matavimai} many {# matavimo} other {# matavimų}}",
+    "tire_set_add_to_work_order": "Padangų komplektas {ref} pridėtas prie darbo užsakymo {jobRef}",
+    "tire_set_attach": "Prie padangų komplekto {ref} pridėta {count, plural, one {# failas} few {# failai} many {# failo} other {# failų}}",
+    "tire_set_check_in": "Padangų komplektas {ref} priimtas į {code}",
+    "tire_set_check_out": "Padangų komplektas {ref} atiduotas",
+    "tire_set_create_quote": "Iš padangų komplekto sukurtas pasiūlymas {ref}",
+    "tire_set_create_work_order": "Iš padangų komplekto sukurtas darbo užsakymas {ref}",
+    "tire_set_delete": "Ištrintas padangų komplektas {ref}",
+    "tire_set_detach": "{fileName} pašalintas iš padangų komplekto",
+    "tire_set_dispose": "Padangų komplektas {ref} nurašytas",
+    "tire_set_message_customer": "Klientui pranešta apie padangų komplektą {ref} per {channel, select, email {el. paštą} sms {SMS} telegram {Telegram} in_app {pranešimą programoje} other {{channel}}}",
+    "tire_set_relocate": "Padangų komplektas {ref} perkeltas iš {from} į {to}",
+    "tire_set_relocateUnassigned": "Padangų komplektas {ref} padėtas į {to}",
+    "tire_set_return": "Padangų komplektas {ref} vėl padėtas saugoti, į {code}",
+    "tire_set_unlink_work_order": "Padangų komplektas {ref} atsietas nuo darbo užsakymo {jobRef}",
+    "tire_set_update": "Atnaujintas padangų komplektas {ref}",
+    "tire_treatment_complete_all": "Padangų komplektui {ref} užbaigta {count, plural, one {# paruošimo darbas} few {# paruošimo darbai} many {# paruošimo darbo} other {# paruošimo darbų}}",
+    "tire_treatment_mark": "Padangų komplekto {ref} darbas {type, select, wash_tires {padangų plovimas} wash_rims {ratlankių plovimas} balance {balansavimas} tpms_service {TPMS aptarnavimas} new_valves {nauji ventiliai} repair {remontas} other {{type}}} pažymėtas kaip „{status, select, done {atlikta} pending {dar reikia} skipped {praleista} other {{status}}}“",
+    "tire_treatment_update": "Atnaujinti padangų komplekto {ref} paruošimo darbai",
+    "tire_warehouse_archive": "Padangų sandėlis {name} perkeltas į archyvą",
+    "tire_warehouse_create": "Sukurtas padangų sandėlis {name}",
+    "tire_warehouse_delete": "Ištrintas padangų sandėlis {name}",
+    "tire_warehouse_update": "Atnaujintas padangų sandėlis {name}",
+    "vehicle_archive": "Transporto priemonė {id} perkelta į archyvą",
+    "vehicle_create": "Sukurta transporto priemonė {year} {make} {model}",
+    "vehicle_delete": "Ištrinta transporto priemonė {name}",
+    "vehicle_unarchive": "Transporto priemonė {id} grąžinta iš archyvo",
+    "vehicle_update": "Atnaujinta transporto priemonė {name}, pakeista: {fields}",
+    "vehicle_updateNoChanges": "Atnaujinta transporto priemonė {name}, niekas nepasikeitė",
+    "webhook_create": "Sukurtas webhook {name}",
+    "whatsapp_send": "Klientui {name} išsiųsta „WhatsApp“ žinutė"
   }
 }

+ 15 - 0
messages/lt/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Įtraukite į pradžios ekraną, kad pasiektumėte greičiau",
     "enterFullscreen": "Visas ekranas",
     "exitFullscreen": "Išjungti visą ekraną"
+  },
+  "broadcast": {
+    "dismiss": "Uždaryti",
+    "title": "Pranešimas visiems",
+    "description": "Rodomas kiekvieno puslapio viršuje visiems, prisijungusiems ar ne. Palikite tuščią, jei pranešimo nereikia.",
+    "message": "Pranešimas",
+    "level": "Svarbumas",
+    "post": "Paskelbti pranešimą",
+    "clear": "Pašalinti pranešimą",
+    "levelInfo": "Informacija",
+    "levelWarning": "Įspėjimas",
+    "levelCritical": "Kritinis",
+    "placeholder": "pvz. Turime serverių infrastruktūros problemų ir jas sprendžiame.",
+    "posted": "Pranešimas paskelbtas",
+    "cleared": "Pranešimas pašalintas"
   }
 }

+ 6 - 2
messages/lt/customers.json

@@ -38,7 +38,8 @@
       "messages": "Žinutės",
       "requests": "Užklausos ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "„WhatsApp“"
     },
     "vehicleTable": {
       "plate": "Valst. Nr.",
@@ -68,7 +69,10 @@
     "customerCreated": "Klientas sukurtas",
     "saveError": "Nepavyko išsaugoti kliento",
     "customerNumber": "Kliento nr.",
-    "customerNumberAuto": "Autom."
+    "customerNumberAuto": "Autom.",
+    "sectionContact": "Kontaktai",
+    "sectionBilling": "Atsiskaitymas",
+    "addScannedVehicle": "Pridėti transporto priemonę {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "Nauja",

+ 7 - 0
messages/lt/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Numatoma priežiūra",
     "reminders": "Artėjantys priminimai",
     "inspections": "Apžiūros",
+    "serviceRequests": "Paslaugų užklausos",
     "quoteRequests": "Pasiūlymų užklausos",
     "quoteResponses": "Pasiūlymų atsakymai",
     "sms": "Naujausios žinutės",
@@ -68,6 +69,7 @@
     "active": "Aktyvūs",
     "dismissedTab": "Atmesti",
     "noActive": "Šiuo metu nėra transporto priemonių, kurioms reikia aptarnavimo.",
+    "noDismissed": "Nėra atmestų transporto priemonių.",
     "dismissedOn": "Atmesta {date}",
     "restore": "Atkurti",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} elementų",
     "noData": "Apžiūrų nėra."
   },
+  "serviceRequests": {
+    "title": "Paslaugų užklausos",
+    "description": "Vizitų užklausos iš klientų portalo",
+    "noData": "Nėra laukiančių paslaugų užklausų."
+  },
   "quoteRequests": {
     "title": "Pasiūlymų užklausos",
     "description": "Klientai prašo pasiūlymų iš bendrinamų apžiūrų",

+ 4 - 1
messages/lt/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Siųskite ir gaukite SMS žinutes su klientais tiesiogiai iš Torqvoice.",
     "tabs": {
       "inbox": "Gauta",
-      "scheduled": "Suplanuota"
+      "scheduled": "Suplanuota",
+      "sms": "SMS",
+      "whatsapp": "„WhatsApp“",
+      "telegram": "„Telegram“"
     },
     "smsNotConfiguredTitle": "SMS nesukonfigūruotas",
     "smsNotConfiguredHint": "Prijunkite SMS tiekėją, kad galėtumėte siųsti ir gauti žinutes čia. Suplanuoti el. laiškai veikia ir be to.",

+ 26 - 0
messages/lt/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Pokalbiai",
+    "searchPlaceholder": "Ieškoti pokalbių",
+    "noSearchResults": "Pagal paiešką pokalbių nerasta.",
+    "empty": "Pokalbių dar nėra.",
+    "selectConversation": "Pasirinkite pokalbį",
+    "back": "Atgal",
+    "newMessage": "Nauja žinutė",
+    "unknownContact": "Šis numeris dar nepriskirtas klientui. Norėdami atsakyti, pridėkite jį kaip klientą.",
+    "noChannelsTitle": "Nėra sukonfigūruoto kanalo",
+    "noChannelsHint": "Prijunkite SMS, „WhatsApp“ ar „Telegram“, kad rašytumėte klientams iš čia.",
+    "goToProviders": "Nustatyti tiekėją",
+    "chooseChannel": {
+      "title": "Kuriuo kanalu siųsti?",
+      "description": "Klientus galite pasiekti keliais būdais."
+    },
+    "deleteConversation": "Ištrinti pokalbį",
+    "deleteTitle": "Ištrinti šį pokalbį?",
+    "deleteDescription": "Pašalina jūsų pokalbio su {name} kopiją. Kliento telefone žinutės lieka.",
+    "deleteFailed": "Nepavyko ištrinti pokalbio",
+    "delete": "Ištrinti",
+    "cancel": "Atšaukti",
+    "loadMore": "Įkelti daugiau"
+  }
+}

+ 4 - 2
messages/lt/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Visi komplektai",
     "tireStorage": "Saugyklos išdėstymas",
     "tireSetDetails": "Padangų komplektas",
-    "tireForecast": "Kitas sezonas"
+    "tireForecast": "Kitas sezonas",
+    "whatsapp": "„WhatsApp“"
   },
   "search": "Ieškoti...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Daugiau",
     "auditLog": "Audito žurnalas",
     "aiAssistant": "DI asistentas",
-    "tireHotel": "Padangų viešbutis"
+    "tireHotel": "Padangų viešbutis",
+    "whatsapp": "„WhatsApp“"
   }
 }

+ 4 - 2
messages/lt/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "El. paštas",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "Pastaba programoje"
+      "in_app": "Pastaba programoje",
+      "whatsapp": "„WhatsApp“"
     },
     "repeatLabel": "Kartoti",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Suplanuoti",
     "scheduled": "Žinutė suplanuota",
     "updated": "Suplanuota žinutė atnaujinta",
-    "saveError": "Nepavyko išsaugoti žinutės"
+    "saveError": "Nepavyko išsaugoti žinutės",
+    "whatsappNeedsTemplate": "Ši žinutė iškeliaus gerokai užsidarius 24 valandų langui, o „WhatsApp“ teksto šablonas nenustatytas, todėl siuntimas nepavyktų. Nustatykite jį: Nustatymai > Tiekėjai > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Suplanuoti žinutę",

+ 35 - 3
messages/lt/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Padangų viešbutis",
         "description": "Sezoninis padangų saugojimas"
+      },
+      "providers": {
+        "title": "Tiekėjai",
+        "description": "El. paštas, SMS, „WhatsApp“ ir „Telegram“"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Apžiūrų šablonai, apžiūros ir rezultatai",
     "optAuditLogs": "Audito žurnalai",
     "optAuditLogsDesc": "Veiklos ir pakeitimų istorija",
-    "optSmsMessages": "SMS žinutės",
-    "optSmsMessagesDesc": "Išsiųstos ir gautos SMS žinutės",
+    "optSmsMessages": "Žinutės",
+    "optSmsMessagesDesc": "Išsiųstos ir gautos SMS, WhatsApp ir Telegram žinutės",
     "optScheduledMessages": "Suplanuotos žinutės",
     "optScheduledMessagesDesc": "Eilėje esančios ir pasikartojančios žinutės, laukiančios išsiuntimo",
     "optNotifications": "Pranešimai",
@@ -980,7 +984,9 @@
       "supportAction": "susisiekite su mūsų pagalbos komanda."
     },
     "optTireHotel": "Padangų viešbutis",
-    "optTireHotelDesc": "Saugomi komplektai, lentynos, paruošimas ir saugojimo sutartys"
+    "optTireHotelDesc": "Saugomi komplektai, lentynos, paruošimas ir saugojimo sutartys",
+    "optWorkshopConfig": "Dirbtuvių nustatymai",
+    "optWorkshopConfigDesc": "Darbo šablonai, rolės, webhook'ai ir suplanuotos ataskaitos"
   },
   "license": {
     "title": "\"White-Label\" licencija",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Paruošimo kainos",
     "prepHint": "Kiek kainuoja kiekvienas paruošimo darbas. Palikus tuščią, jis niekada neapmokestinamas, o to ir reikia dirbtuvėms, kurios plovimą įskaičiuoja į saugojimą. Turintys kainą patys atsiranda pasiūlyme ir darbo užsakyme.",
     "prepNotCharged": "neapmokestinama"
+  },
+  "providers": {
+    "tabs": {
+      "email": "El. paštas",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "El. pašto nustatymai",
+        "description": "Nustatykite SMTP siuntimą, kad sąskaitos, pasiūlymai ir pranešimai keliautų tiesiai iš dirbtuvių."
+      },
+      "sms": {
+        "feature": "SMS žinutės",
+        "description": "Siųskite ir gaukite SMS su klientais tiesiai iš „Torqvoice“."
+      },
+      "whatsapp": {
+        "feature": "„WhatsApp“ žinutės",
+        "description": "Siųskite ir gaukite „WhatsApp“ žinutes, įskaitant detalių nuotraukas, tiesiai iš „Torqvoice“."
+      },
+      "telegram": {
+        "feature": "„Telegram“ žinutės",
+        "description": "Siųskite ir gaukite „Telegram“ žinutes su klientais tiesiai iš „Torqvoice“."
+      }
+    }
   }
 }

+ 2 - 1
messages/lt/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Praleisti",
     "send": "Siųsti",
     "sent": "Būsenos ataskaita išsiųsta",
-    "failed": "Nepavyko išsiųsti būsenos ataskaitos"
+    "failed": "Nepavyko išsiųsti būsenos ataskaitos",
+    "notSent": "Neišsiųsta"
   },
   "view": {
     "title": "Transporto priemonės būsenos ataskaita",

+ 16 - 1
messages/lt/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Registracijos numeris",
     "vehicleUpdated": "Transporto priemonė atnaujinta",
     "vehicleAdded": "Transporto priemonė pridėta",
-    "saveError": "Nepavyko išsaugoti transporto priemonės"
+    "saveError": "Nepavyko išsaugoti transporto priemonės",
+    "scanDocument": "Skenuoti registracijos liudijimą",
+    "scanDocumentHint": "Nufotografuokite transporto priemonės dokumentus ir laukai bus užpildyti automatiškai.",
+    "scanningDocument": "Skaitomas dokumentas...",
+    "scanSuccess": "Laukai užpildyti iš dokumento",
+    "scanFailed": "Nepavyko perskaityti dokumento",
+    "ownerMatchTitle": "Priskirti transporto priemonę",
+    "ownerMatchDescription": "Dokumente nurodyta {name}. Pasirinkite vieną iš šių klientų arba sukurkite naują.",
+    "ownerMatchScore": "{percent}% atitikmuo",
+    "ownerMatchCreateNew": "Sukurti naują klientą",
+    "ownerMatchChoose": "Pasirinkti klientą",
+    "scanUnavailable": "Įjunkite DI nustatymuose",
+    "sectionAssignment": "Nuotrauka ir savininkas",
+    "sectionDetails": "Duomenys",
+    "addScannedOwner": "Pridėti klientą {name}",
+    "ownerSaveFailed": "Nepavyko sukurti kliento"
   },
   "archiveDialog": {
     "title": "Archyvuoti transporto priemonę",

+ 179 - 0
messages/lt/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "„WhatsApp“",
+    "description": "Siųskite ir gaukite „WhatsApp“ žinutes su klientais.",
+    "helpLink": "Nustatyti {provider}",
+    "save": "Įrašyti „WhatsApp“ nustatymus",
+    "saved": "„WhatsApp“ nustatymai įrašyti",
+    "saveError": "Nepavyko įrašyti „WhatsApp“ nustatymų",
+    "enable": {
+      "label": "Įjungti „WhatsApp“",
+      "hint": "Įjunkite, kai tiekėjo prisijungimo duomenys suvesti.",
+      "disabledInfo": "Įjunkite „WhatsApp“, kad nustatytumėte tiekėją, numerį ir šabloną."
+    },
+    "provider": {
+      "label": "Tiekėjas",
+      "hint": "Kas suteikia jūsų „WhatsApp“ numerį. Pakeitus senojo duomenys išlieka."
+    },
+    "from": {
+      "label": "Jūsų „WhatsApp“ numeris",
+      "hint": "Numeris, kurį mato klientai, tarptautiniu formatu."
+    },
+    "secret": {
+      "show": "Rodyti",
+      "hide": "Slėpti",
+      "stored": "Įrašyta ir paslėpta. Įveskite naują reikšmę, kad pakeistumėte."
+    },
+    "webhook": {
+      "label": "Webhook URL",
+      "description": "Įklijuokite tai pas tiekėją, kad gaunamos žinutės pasiektų „Torqvoice“.",
+      "copy": "Kopijuoti webhook URL",
+      "copied": "Webhook URL nukopijuotas",
+      "saveFirst": "Įrašykite šiuos nustatymus prieš tiekėjui tikrinant URL. Tikrinama pagal tai, kas čia įrašyta, todėl neįrašyta forma testo neišlaikys."
+    },
+    "template": {
+      "title": "Žinutės šablonas",
+      "description": "Naudojamas, kai rašote klientui, kuris seniai nerašė.",
+      "windowExplainer": "„WhatsApp“ leidžia laisvą tekstą tik 24 valandas nuo paskutinės kliento žinutės. Vėliau žinutė turi būti tiekėjo patvirtintas šablonas.",
+      "nameLabel": "Šablono pavadinimas",
+      "nameHint": "Patvirtinto šablono pavadinimas arba „Content SID“ pas tiekėjus, kurie jį naudoja.",
+      "languageLabel": "Šablono kalba",
+      "languageHint": "Kalbos kodas, su kuriuo šablonas patvirtintas, pavyzdžiui lt.",
+      "variablesLabel": "Šablono reikšmės iš eilės",
+      "variablesHint": "Pasirinkite reikšmes, užpildančias sunumeruotas šablono vietas, ta pačia tvarka. Palikite tuščią, jei šablone vietų nėra.",
+      "tokens": {
+        "customer": "kliento vardas",
+        "vehicle": "metai, markė ir modelis",
+        "plate": "valstybinis numeris",
+        "message": "jūsų tekstas",
+        "workshop": "jūsų dirbtuvių pavadinimas",
+        "photo": "pridėta nuotrauka"
+      },
+      "variablesEmpty": "Reikšmių dar nėra. Pridėkite jas žemiau tokia tvarka, kokios tikisi šablonas.",
+      "variablesRemove": "Pašalinti {name}",
+      "mediaUrlLabel": "Medijos URL jūsų šablonui",
+      "mediaUrlHint": "Įklijuokite tai į šablono medijos lauką ir pakeiskite n nuotraukos reikšmės numeriu.",
+      "mediaUrlCopy": "Kopijuoti medijos URL",
+      "mediaUrlCopied": "Medijos URL nukopijuotas",
+      "mediaUrlSample": "Kaip pavyzdinę reikšmę pas tiekėją naudokite: sample",
+      "textTitle": "Tekstinės žinutės",
+      "textDescription": "Naudojama, kai rašote be nuotraukos: iš Žinučių, iš kliento „WhatsApp“ kortelės arba iš suplanuotos žinutės.",
+      "mediaTitle": "Žinutės su nuotrauka",
+      "mediaDescription": "Naudojama mygtuko „Siųsti per WhatsApp“ prie darbo nuotraukų ir visada, kai pokalbyje pridedate nuotrauką.",
+      "textMissing": "Nenustatyta: negalėsite rašyti klientui, kuris nerašė pastarąsias 24 valandas.",
+      "mediaMissing": "Nenustatyta: negalėsite siųsti nuotraukų klientui, kuris nerašė pastarąsias 24 valandas.",
+      "whatIsIt": "Šablonas – tai fiksuota žinutė, kurią parašote kartą ir kurią patvirtina {provider}: tekstas nekinta, užpildote tik sunumeruotas vietas. Parašykite ją jų konsolėje ir čia įklijuokite identifikatorių.",
+      "whyTwo": "Reikia dviejų, nes „WhatsApp“ medijos tipą nustato patvirtinimo metu. Su nuotrauka patvirtintas šablonas gali nešti tik nuotraukas, o kaip tekstas patvirtintas – niekada.",
+      "exampleLabel": "Pavyzdinis tekstas tiekėjo konsolei",
+      "exampleTextValues": "Užpildykite: customer, message. Rašykite klientų kalba.",
+      "exampleMediaValues": "Užpildykite: customer, message ir photo medijos URL. Rašykite klientų kalba.",
+      "exampleMediaValuesHeader": "Užpildykite: customer, message. Nuotrauka pridedama automatiškai, todėl jai atskiros vietos nereikia. Rašykite ta kalba, kuria skaito jūsų klientai."
+    },
+    "test": {
+      "send": "Siųsti bandymą",
+      "sent": "Bandomoji žinutė išsiųsta",
+      "failed": "Nepavyko išsiųsti bandomosios žinutės",
+      "hint": "Pirma parašykite sau per „WhatsApp“, kitaip bandymui reikės patvirtinto šablono."
+    },
+    "disconnect": {
+      "action": "Atjungti „WhatsApp“",
+      "hint": "Pašalina prisijungimo duomenis iš „Torqvoice“. Pokalbiai išlieka.",
+      "done": "„WhatsApp“ atjungtas",
+      "title": "Atjungti"
+    },
+    "savedIncomplete": "Įrašyta. Kad galėtumėte siųsti, dar trūksta: {fields}",
+    "fieldStillNeeded": "Dar reikia norint siųsti",
+    "steps": {
+      "openProvider": "Atidaryti {provider}",
+      "openNumber": "Rasti siuntėjo numerį",
+      "openToken": "Sukurti nuolatinį prieigos raktą",
+      "consolePath": "Meta aplinkoje eikite į:",
+      "provider": {
+        "title": "Pasirinkite tiekėją ir numerį",
+        "description": "„Twilio“ paleisti greičiau ir jis tvarko „Meta“ už jus. „Meta Cloud API“ kainuoja mažiau, bet verslo patvirtinimą atliekate patys. Įveskite numerį, kurį matys klientai, tarptautiniu formatu."
+      },
+      "credentials": {
+        "title": "Įveskite prisijungimo duomenis",
+        "description": "Nukopijuokite kiekvieną reikšmę iš {provider}. Kiekvienas laukas nurodo, kur ją rasti. Įrašykite, kai turėsite; likusius galėsite pridėti vėliau."
+      },
+      "webhook": {
+        "title": "Užregistruokite webhook",
+        "description": "Nukopijuokite šį adresą į {provider}, kad klientų žinutės pasiektų „Torqvoice“. Pirma įrašykite šį puslapį: adresas tikrinamas pagal tai, kas čia įrašyta.",
+        "open": "Atidaryti {provider} webhook nustatymus",
+        "recheck": "Tikrinti dar kartą",
+        "waiting": "Į šį adresą dar nieko neatėjo. Jis taps žalias, kai tiekėjas jį patvirtins arba pristatys žinutę.",
+        "seen": "Tiekėjas pasiekė šį adresą. Gaunamos žinutės atsiras skiltyje Žinutės."
+      },
+      "templates": {
+        "title": "Patvirtinkite šablonus",
+        "description": "Reikia tik tam, kad rašytumėte pirmi už 24 valandų lango ribų. Galite praleisti ir grįžti, kai susirašinėjimas veiks.",
+        "open": "Atidaryti {provider} šablonus"
+      },
+      "test": {
+        "title": "Išsiųskite bandomąją žinutę",
+        "description": "Pirma parašykite savo „WhatsApp“ numeriu iš savo telefono. Tai įrodo, kad webhook veikia, ir atveria 24 valandų langą, tad atsakymui šablono nereikės."
+      },
+      "back": "Atgal",
+      "continue": "Toliau"
+    },
+    "register": {
+      "title": "Užregistruokite numerį „Meta“",
+      "description": "Patvirtinus numerį, „Meta“ reikalauja atskiros registracijos. Pasirinkite šešių skaitmenų PIN arba naudokite esamą dviejų veiksmų patvirtinimo PIN. Užsirašykite jį: prireiks perkeliant ar iš naujo registruojant numerį.",
+      "action": "Registruoti numerį",
+      "done": "Numeris užregistruotas",
+      "failed": "Nepavyko užregistruoti numerio",
+      "limit": "„Meta“ leidžia 10 registracijos bandymų vienam numeriui per 72 valandas. Neteisingas PIN taip pat skaičiuojamas, tad pasitikrinkite."
+    }
+  },
+  "messages": {
+    "featureName": "„WhatsApp“ žinutės",
+    "featureDescription": "Siųskite ir gaukite „WhatsApp“ žinutes, įskaitant detalių nuotraukas, tiesiai iš „Torqvoice“.",
+    "noThreads": "Pokalbių dar nėra",
+    "noThreadsHint": "Žinutės atsiras čia, kai klientas parašys jūsų „WhatsApp“ numeriu arba jūs jam.",
+    "selectThread": "Pasirinkite pokalbį",
+    "backToThreads": "Pokalbiai",
+    "empty": "Čia dar nieko nėra.",
+    "placeholder": "Rašykite žinutę",
+    "send": "Siųsti",
+    "sendFailed": "Nepavyko išsiųsti žinutės",
+    "attach": "Pridėti nuotrauką",
+    "attachFailed": "Nepavyko pridėti nuotraukos",
+    "removeAttachment": "Pašalinti priedą",
+    "attachment": "Priedas",
+    "delete": "Ištrinti",
+    "deleteFailed": "Nepavyko ištrinti žinutės",
+    "sentAsTemplate": "šablonas",
+    "sentAsTemplateNotice": "Išsiųsta kaip patvirtintas šablonas, nes klientas nerašė pastarąsias 24 valandas.",
+    "windowClosed": "Klientas nerašė pastarąsias 24 valandas, todėl tai iškeliaus kaip patvirtintas šablonas.",
+    "unknownNumber": "Šis numeris dar nepriskirtas klientui. Norėdami atsakyti, pridėkite jį kaip klientą.",
+    "notConfigured": {
+      "title": "„WhatsApp“ neprijungtas",
+      "description": "Prijunkite „WhatsApp“ numerį, kad galėtumėte susirašinėti su klientais.",
+      "action": "Nustatyti „WhatsApp“"
+    },
+    "title": "Pokalbiai",
+    "searchPlaceholder": "Ieškoti pokalbių",
+    "noSearchResults": "Pagal paiešką pokalbių nerasta.",
+    "compose": {
+      "newMessage": "Nauja žinutė",
+      "title": "Nauja „WhatsApp“ žinutė",
+      "searchPlaceholder": "Ieškoti klientų",
+      "noMatches": "Pagal paiešką klientų nerasta.",
+      "noRecipients": "Dar nėra klientų su telefono numeriu."
+    },
+    "windowBlocked": "Šis klientas nerašė pastarąsias 24 valandas, todėl „WhatsApp“ dabar žinutės nepristatys. Paprašykite jo parašyti arba nustatykite patvirtintą šabloną.",
+    "windowBlockedAction": "Nustatyti šabloną",
+    "windowBlockedPlaceholder": "Laukiama, kol klientas parašys pirmas"
+  },
+  "sendPhoto": {
+    "action": "Siųsti per „WhatsApp“",
+    "title": "Siųsti nuotrauką per „WhatsApp“",
+    "description": "Klientas gaus šią nuotrauką savo „WhatsApp“ pokalbyje.",
+    "captionPlaceholder": "Pridėkite pastabą, pavyzdžiui ką rodo nuotrauka",
+    "send": "Siųsti",
+    "cancel": "Atšaukti",
+    "sent": "Nuotrauka išsiųsta",
+    "sentAsTemplate": "Nuotrauka išsiųsta kaip patvirtintas šablonas",
+    "failed": "Nepavyko išsiųsti nuotraukos"
+  }
+}

+ 122 - 1
messages/nb/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Lagret et sett på nytt",
     "tire_set_dispose": "Kasserte et dekksett",
     "tire_set_attach": "La til filer på et dekksett",
-    "tire_set_detach": "Fjernet en fil fra et dekksett"
+    "tire_set_detach": "Fjernet en fil fra et dekksett",
+    "whatsapp_send": "WhatsApp-melding sendt"
+  },
+  "summary": {
+    "customField_create": "Opprettet egendefinert felt «{name}»",
+    "customField_delete": "Slettet egendefinert felt «{name}»",
+    "customField_update": "Oppdaterte egendefinert felt «{name}»",
+    "customer_backfillNumbers": "Tildelte kundenummer til {count, plural, one {# kunde} other {# kunder}}",
+    "customer_create": "Opprettet kunde {name}",
+    "customer_delete": "Slettet kunde {id}",
+    "customer_update": "Oppdaterte kunde {id}",
+    "email_sendInspection": "Sendte kontroll på e-post til {recipient}",
+    "email_sendInvoice": "Sendte faktura på e-post til {recipient}",
+    "email_sendQuote": "Sendte tilbud på e-post til {recipient}",
+    "finding_create": "Opprettet funn «{description}» på kjøretøy {vehicleId}",
+    "finding_delete": "Slettet funn {id}",
+    "finding_resolve": "Lukket funn «{description}»",
+    "finding_update": "Oppdaterte funn «{description}»",
+    "inspectionTemplate_create": "Opprettet kontrollmal «{name}»",
+    "inspectionTemplate_createFromPreset": "Opprettet kontrollmal «{name}» fra en ferdigmal",
+    "inspectionTemplate_delete": "Slettet kontrollmal «{name}»",
+    "inspectionTemplate_duplicate": "Kopierte kontrollmal til «{name}»",
+    "inspectionTemplate_import": "Importerte {count, plural, one {# kontrollmal} other {# kontrollmaler}} fra en pakke",
+    "inspectionTemplate_update": "Oppdaterte kontrollmal «{name}»",
+    "inspection_complete": "Fullførte kontroll {id}",
+    "inspection_create": "Opprettet kontroll {id}",
+    "inspection_delete": "Slettet kontroll {id}",
+    "inspection_reopen": "Gjenåpnet kontroll {id}",
+    "inventory_bulkDelete": "Masseslettet {count, plural, one {# lagerdel} other {# lagerdeler}}",
+    "inventory_create": "Opprettet lagerdel «{name}»",
+    "inventory_delete": "Slettet lagerdel {id}",
+    "inventory_update": "Oppdaterte lagerdel {id}",
+    "labor_preset_create": "Opprettet arbeidsmal «{name}»",
+    "labor_preset_delete": "Slettet arbeidsmal {id}",
+    "labor_preset_update": "Oppdaterte arbeidsmal {id}",
+    "note_create": "Opprettet notat på kjøretøy {vehicleId}",
+    "note_delete": "Slettet notat {id}",
+    "organization_create": "Opprettet organisasjon «{name}»",
+    "organization_sampleDataRemove": "Fjernet eksempeldata fra oppstarten",
+    "payment_create": "Registrerte betaling {amount} for jobb {serviceRecordId}",
+    "payment_delete": "Slettet betaling {id}",
+    "quoteRequest_update": "Endret status på tilbudsforespørsel til {status, select, pending {venter} approved {godkjent} rejected {avslått} completed {fullført} other {{status}}}",
+    "quote_convert": "Gjorde tilbud {ref} om til jobb {serviceRecordId}",
+    "quote_create": "Opprettet tilbud {ref}",
+    "quote_delete": "Slettet tilbud {ref}",
+    "quote_status": "Endret tilbudsstatus til {status, select, draft {utkast} sent {sendt} accepted {godtatt} rejected {avslått} expired {utløpt} converted {konvertert} other {{status}}}",
+    "quote_update": "Oppdaterte tilbud {ref}",
+    "recurringInvoice_create": "Opprettet gjentakende faktura «{title}»",
+    "recurringInvoice_delete": "Slettet gjentakende faktura {id}",
+    "recurringInvoice_update": "Oppdaterte gjentakende faktura «{title}»",
+    "reminder_create": "Opprettet påminnelse «{title}»",
+    "reminder_delete": "Slettet påminnelse {id}",
+    "role_create": "Opprettet rollen «{name}»",
+    "role_delete": "Slettet rollen «{name}»",
+    "role_update": "Oppdaterte rollen «{name}»",
+    "scheduled_message_cancel": "Avbrøt en planlagt melding",
+    "scheduled_message_create": "Planla {channel, select, email {en e-post} sms {en SMS} telegram {en Telegram-melding} in_app {et varsel i appen} other {{channel}}} til {sendAt}",
+    "scheduled_message_delete": "Slettet en planlagt melding",
+    "scheduled_message_send_now": "Sendte {channel, select, email {en planlagt e-post} sms {en planlagt SMS} telegram {en planlagt Telegram-melding} in_app {et planlagt varsel i appen} other {{channel}}} før tiden",
+    "scheduled_message_update": "Oppdaterte {channel, select, email {en planlagt e-post} sms {en planlagt SMS} telegram {en planlagt Telegram-melding} in_app {et planlagt varsel i appen} other {{channel}}}",
+    "service_create": "Opprettet jobb {ref}",
+    "service_createCounterSale": "Opprettet disksalg {ref}",
+    "service_createDraft": "Opprettet utkast til jobb {ref}",
+    "service_createFromInspection": "Opprettet arbeidsordre {ref} fra en kontroll",
+    "service_delete": "Slettet jobb {ref}",
+    "service_status": "Endret jobbstatus til {status, select, pending {venter} scheduled {planlagt} in_progress {under arbeid} waiting_parts {venter på deler} ready {klar} completed {fullført} cancelled {avbrutt} other {{status}}}",
+    "service_update": "Oppdaterte jobb {ref}",
+    "settings_applyTaxRate": "La {rate} % mva på {records, plural, one {# jobb} other {# jobber}} og {quotes, plural, one {# tilbud} other {# tilbud}}",
+    "settings_convertToExclusive": "Gjorde om {records, plural, one {# jobb} other {# jobber}} og {quotes, plural, one {# tilbud} other {# tilbud}} til priser uten mva",
+    "settings_convertToInclusive": "Gjorde om {records, plural, one {# jobb} other {# jobber}} og {quotes, plural, one {# tilbud} other {# tilbud}} til priser med mva",
+    "settings_deleteContent": "Slettet alle data: {items}",
+    "settings_updateInvoiceLayout": "Oppdaterte fakturaoppsettet",
+    "settings_updateQuoteLayout": "Oppdaterte tilbudsoppsettet",
+    "sms_send": "Sendte SMS til {name} ({phone})",
+    "statusReport_create": "Opprettet statusrapport for jobb {serviceRecordId}",
+    "statusReport_delete": "Slettet en statusrapport",
+    "statusReport_send": "Sendte statusrapport via {channels}",
+    "subscription_cancel": "Sa opp abonnementet, med virkning ved periodeslutt",
+    "subscription_resume": "Gjenopptok abonnementet",
+    "team_cancelInvitation": "Trakk tilbake invitasjonen til {email}",
+    "team_removeMember": "Fjernet teammedlem {id}",
+    "team_sendInvitation": "Inviterte {email} som {role, select, owner {eier} admin {administrator} member {medlem} other {{role}}}",
+    "team_updateRole": "Endret rollen til et medlem til {role, select, owner {eier} admin {administrator} member {medlem} other {{role}}}",
+    "technician_create": "Opprettet mekaniker «{name}»",
+    "technician_delete": "Slettet mekaniker {id}",
+    "technician_update": "Oppdaterte mekaniker {id}",
+    "telegram_send": "Sendte Telegram-melding til {name}",
+    "tire_location_bulk_create": "Opprettet {count, plural, one {# dekkplass} other {# dekkplasser}}",
+    "tire_location_create": "Opprettet dekkplass {code}",
+    "tire_location_delete": "Slettet dekkplass {code}",
+    "tire_location_update": "Oppdaterte dekkplass {code}",
+    "tire_measurement_create": "Registrerte {count, plural, one {# måling} other {# målinger}} på dekksett {ref}",
+    "tire_set_add_to_work_order": "La dekksett {ref} til arbeidsordre {jobRef}",
+    "tire_set_attach": "La {count, plural, one {# fil} other {# filer}} til dekksett {ref}",
+    "tire_set_check_in": "Tok inn dekksett {ref} på {code}",
+    "tire_set_check_out": "Leverte ut dekksett {ref}",
+    "tire_set_create_quote": "Opprettet tilbud {ref} fra et dekksett",
+    "tire_set_create_work_order": "Opprettet arbeidsordre {ref} fra et dekksett",
+    "tire_set_delete": "Slettet dekksett {ref}",
+    "tire_set_detach": "Fjernet {fileName} fra et dekksett",
+    "tire_set_dispose": "Kasserte dekksett {ref}",
+    "tire_set_message_customer": "Sendte melding til kunden om dekksett {ref} på {channel, select, email {e-post} sms {SMS} telegram {Telegram} in_app {varsel i appen} other {{channel}}}",
+    "tire_set_relocate": "Flyttet dekksett {ref} fra {from} til {to}",
+    "tire_set_relocateUnassigned": "Flyttet dekksett {ref} til {to}",
+    "tire_set_return": "Lagret dekksett {ref} igjen, på {code}",
+    "tire_set_unlink_work_order": "Koblet dekksett {ref} fra arbeidsordre {jobRef}",
+    "tire_set_update": "Oppdaterte dekksett {ref}",
+    "tire_treatment_complete_all": "Fullførte {count, plural, one {# klargjøringsjobb} other {# klargjøringsjobber}} på dekksett {ref}",
+    "tire_treatment_mark": "Merket {type, select, wash_tires {dekkvask} wash_rims {felgvask} balance {balansering} tpms_service {TPMS-service} new_valves {nye ventiler} repair {reparasjon} other {{type}}} som {status, select, done {utført} pending {gjenstår} skipped {hoppet over} other {{status}}} på dekksett {ref}",
+    "tire_treatment_update": "Oppdaterte klargjøringen på dekksett {ref}",
+    "tire_warehouse_archive": "Arkiverte dekklager {name}",
+    "tire_warehouse_create": "Opprettet dekklager {name}",
+    "tire_warehouse_delete": "Slettet dekklager {name}",
+    "tire_warehouse_update": "Oppdaterte dekklager {name}",
+    "vehicle_archive": "Arkiverte kjøretøy {id}",
+    "vehicle_create": "Opprettet kjøretøy {year} {make} {model}",
+    "vehicle_delete": "Slettet kjøretøy {name}",
+    "vehicle_unarchive": "Hentet kjøretøy {id} ut av arkivet",
+    "vehicle_update": "Oppdaterte kjøretøy {name}, endret: {fields}",
+    "vehicle_updateNoChanges": "Oppdaterte kjøretøy {name}, ingenting endret",
+    "webhook_create": "Opprettet webhook {name}",
+    "whatsapp_send": "Sendte en WhatsApp-melding til {name}"
   }
 }

+ 15 - 0
messages/nb/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Legg den til på hjem-skjermen for rask tilgang",
     "enterFullscreen": "Fullskjerm",
     "exitFullscreen": "Avslutt fullskjerm"
+  },
+  "broadcast": {
+    "dismiss": "Lukk",
+    "title": "Melding til alle",
+    "description": "Vises øverst på hver side, for alle, innlogget eller ikke. La den stå tom for ingen melding.",
+    "message": "Melding",
+    "level": "Alvorlighet",
+    "post": "Publiser melding",
+    "clear": "Fjern melding",
+    "levelInfo": "Informasjon",
+    "levelWarning": "Advarsel",
+    "levelCritical": "Kritisk",
+    "placeholder": "f.eks. Vi har problemer med serverinfrastrukturen vår og jobber med saken.",
+    "posted": "Meldingen er publisert",
+    "cleared": "Meldingen er fjernet"
   }
 }

+ 6 - 2
messages/nb/customers.json

@@ -38,7 +38,8 @@
       "messages": "Meldinger",
       "requests": "Forespørsler ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Registreringsnummer",
@@ -68,7 +69,10 @@
     "customerCreated": "Kunde opprettet",
     "saveError": "Kunne ikke lagre kunde",
     "customerNumber": "Kundenr.",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Kontakt",
+    "sectionBilling": "Fakturering",
+    "addScannedVehicle": "Legg til kjøretøyet {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "Ny",

+ 7 - 0
messages/nb/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Forventet vedlikehold",
     "reminders": "Kommende påminnelser",
     "inspections": "Inspeksjoner",
+    "serviceRequests": "Serviceforespørsler",
     "quoteRequests": "Tilbudsforespørsler",
     "quoteResponses": "Tilbudssvar",
     "sms": "Siste meldinger",
@@ -68,6 +69,7 @@
     "active": "Aktive",
     "dismissedTab": "Avvist",
     "noActive": "Ingen kjøretøy trenger service for øyeblikket.",
+    "noDismissed": "Ingen avviste kjøretøy.",
     "dismissedOn": "Avvist {date}",
     "restore": "Gjenopprett",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} punkter",
     "noData": "Ingen inspeksjoner."
   },
+  "serviceRequests": {
+    "title": "Serviceforespørsler",
+    "description": "Timebestillinger fra kundeportalen",
+    "noData": "Ingen ventende serviceforespørsler."
+  },
   "quoteRequests": {
     "title": "Tilbudsforespørsler",
     "description": "Kunder som ber om tilbud fra delte inspeksjoner",

+ 4 - 1
messages/nb/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Send og motta SMS-meldinger med kundene dine direkte fra Torqvoice.",
     "tabs": {
       "inbox": "Innboks",
-      "scheduled": "Planlagt"
+      "scheduled": "Planlagt",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "SMS er ikke satt opp",
     "smsNotConfiguredHint": "Koble til en SMS-leverandør for å sende og motta meldinger her. Planlagt e-post fungerer uansett.",

+ 26 - 0
messages/nb/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Samtaler",
+    "searchPlaceholder": "Søk i samtaler",
+    "noSearchResults": "Ingen samtaler passer søket.",
+    "empty": "Ingen samtaler ennå.",
+    "selectConversation": "Velg en samtale",
+    "back": "Tilbake",
+    "newMessage": "Ny melding",
+    "unknownContact": "Dette nummeret tilhører ingen kunde ennå. Legg dem til som kunde for å svare.",
+    "noChannelsTitle": "Ingen meldingskanal satt opp",
+    "noChannelsHint": "Koble til SMS, WhatsApp eller Telegram for å skrive til kundene herfra.",
+    "goToProviders": "Sett opp en leverandør",
+    "chooseChannel": {
+      "title": "Hvilken kanal?",
+      "description": "Du har flere måter å nå kundene dine på."
+    },
+    "deleteConversation": "Slett samtale",
+    "deleteTitle": "Slette denne samtalen?",
+    "deleteDescription": "Fjerner din kopi av samtalen med {name}. Meldingene blir liggende på telefonen deres.",
+    "deleteFailed": "Kunne ikke slette samtalen",
+    "delete": "Slett",
+    "cancel": "Avbryt",
+    "loadMore": "Last mer"
+  }
+}

+ 4 - 2
messages/nb/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Alle dekksett",
     "tireStorage": "Lageroppsett",
     "tireSetDetails": "Dekksett",
-    "tireForecast": "Neste sesong"
+    "tireForecast": "Neste sesong",
+    "whatsapp": "WhatsApp"
   },
   "search": "Søk...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Mer",
     "auditLog": "Aktivitetslogg",
     "aiAssistant": "AI-assistent",
-    "tireHotel": "Dekkhotell"
+    "tireHotel": "Dekkhotell",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/nb/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "E-post",
       "sms": "SMS",
       "telegram": "Telegram",
-      "in_app": "Notat i appen"
+      "in_app": "Notat i appen",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Gjenta",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Planlegg",
     "scheduled": "Melding planlagt",
     "updated": "Planlagt melding oppdatert",
-    "saveError": "Kunne ikke lagre meldingen"
+    "saveError": "Kunne ikke lagre meldingen",
+    "whatsappNeedsTemplate": "Denne sendes lenge etter at kundens 24-timersvindu er lukket, og ingen WhatsApp-tekstmal er satt opp, så den ville feile ved sending. Sett opp en under Innstillinger > Leverandører > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Planlegg melding",

+ 35 - 3
messages/nb/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Dekkhotell",
         "description": "Sesonglagring av dekk"
+      },
+      "providers": {
+        "title": "Leverandører",
+        "description": "E-post, SMS, WhatsApp og Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Inspeksjonsmaler, inspeksjoner og resultater",
     "optAuditLogs": "Aktivitetslogg",
     "optAuditLogsDesc": "Aktivitets- og endringshistorikk",
-    "optSmsMessages": "SMS-meldinger",
-    "optSmsMessagesDesc": "Sendte og mottatte SMS-meldinger",
+    "optSmsMessages": "Meldinger",
+    "optSmsMessagesDesc": "Sendte og mottatte SMS-, WhatsApp- og Telegram-meldinger",
     "optScheduledMessages": "Planlagte meldinger",
     "optScheduledMessagesDesc": "Meldinger i kø og gjentakende meldinger som venter på å bli sendt",
     "optNotifications": "Varsler",
@@ -980,7 +984,9 @@
       "supportAction": "kontakt supportteamet vårt."
     },
     "optTireHotel": "Dekkhotell",
-    "optTireHotelDesc": "Lagrede dekksett, hyller, klargjøring og lagringsavtaler"
+    "optTireHotelDesc": "Lagrede dekksett, hyller, klargjøring og lagringsavtaler",
+    "optWorkshopConfig": "Verkstedoppsett",
+    "optWorkshopConfigDesc": "Arbeidsmaler, roller, webhooks og planlagte rapporter"
   },
   "license": {
     "title": "White-label-lisens",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Priser for klargjøring",
     "prepHint": "Hva hver type klargjøring koster. La en stå tom, så faktureres den aldri, som passer for verksteder som har vask inkludert i lagerprisen. De med pris legges automatisk på tilbud og arbeidsordre.",
     "prepNotCharged": "ikke fakturert"
+  },
+  "providers": {
+    "tabs": {
+      "email": "E-post",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "E-postinnstillinger",
+        "description": "Sett opp SMTP-sending slik at fakturaer, tilbud og varsler går direkte fra verkstedet."
+      },
+      "sms": {
+        "feature": "SMS-meldinger",
+        "description": "Send og motta SMS med kundene dine rett fra Torqvoice."
+      },
+      "whatsapp": {
+        "feature": "WhatsApp-meldinger",
+        "description": "Send og motta WhatsApp-meldinger, inkludert bilder av deler, rett fra Torqvoice."
+      },
+      "telegram": {
+        "feature": "Telegram-meldinger",
+        "description": "Send og motta Telegram-meldinger med kundene dine rett fra Torqvoice."
+      }
+    }
   }
 }

+ 2 - 1
messages/nb/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Hopp over",
     "send": "Send",
     "sent": "Statusrapport sendt",
-    "failed": "Kunne ikke sende statusrapport"
+    "failed": "Kunne ikke sende statusrapport",
+    "notSent": "Ikke sendt"
   },
   "view": {
     "title": "Kjøretøyets statusrapport",

+ 16 - 1
messages/nb/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Registreringsnummer",
     "vehicleUpdated": "Kjøretøy oppdatert",
     "vehicleAdded": "Kjøretøy lagt til",
-    "saveError": "Kunne ikke lagre kjøretøy"
+    "saveError": "Kunne ikke lagre kjøretøy",
+    "scanDocument": "Skann vognkortet",
+    "scanDocumentHint": "Ta bilde av vognkortet, så fylles feltene ut automatisk.",
+    "scanningDocument": "Leser dokumentet...",
+    "scanSuccess": "Feltene er fylt ut fra dokumentet",
+    "scanFailed": "Kunne ikke lese dokumentet",
+    "ownerMatchTitle": "Tilordne kjøretøyet",
+    "ownerMatchDescription": "Dokumentet oppgir {name}. Velg en av disse kundene, eller opprett en ny.",
+    "ownerMatchScore": "{percent}% treff",
+    "ownerMatchCreateNew": "Opprett ny kunde",
+    "ownerMatchChoose": "Velg kunde",
+    "scanUnavailable": "Aktiver KI i innstillingene",
+    "sectionAssignment": "Bilde og eier",
+    "sectionDetails": "Detaljer",
+    "addScannedOwner": "Legg til kunden {name}",
+    "ownerSaveFailed": "Kunne ikke opprette kunden"
   },
   "archiveDialog": {
     "title": "Arkiver kjøretøy",

+ 179 - 0
messages/nb/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "Send og motta WhatsApp-meldinger med kundene dine.",
+    "helpLink": "Sett opp {provider}",
+    "save": "Lagre WhatsApp-innstillinger",
+    "saved": "WhatsApp-innstillinger lagret",
+    "saveError": "Kunne ikke lagre WhatsApp-innstillingene",
+    "enable": {
+      "label": "Slå på WhatsApp",
+      "hint": "Slå på når legitimasjonen fra leverandøren er på plass.",
+      "disabledInfo": "Slå på WhatsApp for å sette opp leverandør, nummer og mal."
+    },
+    "provider": {
+      "label": "Leverandør",
+      "hint": "Hvem som leverer WhatsApp-nummeret ditt. Bytter du, beholdes legitimasjonen til den forrige."
+    },
+    "from": {
+      "label": "Ditt WhatsApp-nummer",
+      "hint": "Nummeret kundene ser, i internasjonalt format."
+    },
+    "secret": {
+      "show": "Vis",
+      "hide": "Skjul",
+      "stored": "Lagret og skjult. Skriv inn en ny verdi for å erstatte den."
+    },
+    "webhook": {
+      "label": "Webhook-URL",
+      "description": "Lim denne inn hos leverandøren så innkommende meldinger når Torqvoice.",
+      "copy": "Kopier webhook-URL",
+      "copied": "Webhook-URL kopiert",
+      "saveFirst": "Lagre disse innstillingene før leverandøren verifiserer URL-en. Den sjekkes mot det som er lagret her, så et ulagret skjema feiler."
+    },
+    "template": {
+      "title": "Meldingsmal",
+      "description": "Brukes når du skriver til en kunde som ikke har meldt fra på en stund.",
+      "windowExplainer": "WhatsApp tillater fritekst kun i 24 timer etter kundens siste melding. Utenfor det vinduet må meldingen gå ut som en mal leverandøren har godkjent.",
+      "nameLabel": "Malnavn",
+      "nameHint": "Navnet på den godkjente malen, eller Content SID hos leverandører som bruker det.",
+      "languageLabel": "Malspråk",
+      "languageHint": "Språkkoden malen ble godkjent under, for eksempel nb.",
+      "variablesLabel": "Verdier i malen, i rekkefølge",
+      "variablesHint": "Velg verdiene som fyller de nummererte feltene i malen, i samme rekkefølge. La stå tom hvis malen ikke har felter.",
+      "tokens": {
+        "customer": "kundenavn",
+        "vehicle": "årsmodell, merke og modell",
+        "plate": "registreringsnummer",
+        "message": "det du skriver",
+        "workshop": "navnet på verkstedet",
+        "photo": "bildet du legger ved"
+      },
+      "variablesEmpty": "Ingen verdier ennå. Legg dem til under, i rekkefølgen malen forventer.",
+      "variablesRemove": "Fjern {name}",
+      "mediaUrlLabel": "Medie-URL for malen din",
+      "mediaUrlHint": "Lim dette inn i malens mediefelt og bytt ut n med nummeret til bildeverdien.",
+      "mediaUrlCopy": "Kopier medie-URL",
+      "mediaUrlCopied": "Medie-URL kopiert",
+      "mediaUrlSample": "Som eksempelverdi hos leverandøren, bruk: sample",
+      "textTitle": "Tekstmeldinger",
+      "textDescription": "Brukes når du skriver uten bilde: fra Meldinger, fra WhatsApp-fanen på en kunde, eller fra en planlagt melding.",
+      "mediaTitle": "Meldinger med bilde",
+      "mediaDescription": "Brukes av Send på WhatsApp-knappen på bildene i en jobb, og hver gang du legger ved et bilde i en samtale.",
+      "textMissing": "Ikke satt opp: du kan ikke skrive til en kunde som ikke har meldt fra de siste 24 timene.",
+      "mediaMissing": "Ikke satt opp: du kan ikke sende bilder til en kunde som ikke har meldt fra de siste 24 timene.",
+      "whatIsIt": "En mal er en fast melding du skriver én gang og som {provider} godkjenner: ordlyden endres aldri, du fyller bare de nummererte feltene. Skriv den i deres konsoll og lim inn identifikatoren her.",
+      "whyTwo": "Du trenger to fordi WhatsApp låser medietypen ved godkjenning. En mal godkjent med bilde kan bare bære bilder, og en godkjent som tekst kan aldri bære ett.",
+      "exampleLabel": "Eksempeltekst å skrive i leverandørens konsoll",
+      "exampleTextValues": "Fyll den med: customer, message. Skriv den på språket kundene dine leser.",
+      "exampleMediaValues": "Fyll den med: customer, message og photo for medie-URL-en. Skriv den på språket kundene dine leser.",
+      "exampleMediaValuesHeader": "Fyll den med: customer, message. Bildet legges ved automatisk og trenger ingen egen plass. Skriv den på språket kundene dine leser."
+    },
+    "test": {
+      "send": "Send test",
+      "sent": "Testmelding sendt",
+      "failed": "Kunne ikke sende testmeldingen",
+      "hint": "Send deg selv en melding på WhatsApp først, ellers krever testen en godkjent mal."
+    },
+    "disconnect": {
+      "action": "Koble fra WhatsApp",
+      "hint": "Fjerner legitimasjonen fra Torqvoice. Samtalene beholdes.",
+      "done": "WhatsApp koblet fra",
+      "title": "Koble fra"
+    },
+    "savedIncomplete": "Lagret. Mangler fortsatt før du kan sende: {fields}",
+    "fieldStillNeeded": "Mangler fortsatt før du kan sende",
+    "steps": {
+      "openProvider": "Åpne {provider}",
+      "openNumber": "Finn avsendernummeret",
+      "openToken": "Lag et permanent token",
+      "consolePath": "I Meta går du til:",
+      "provider": {
+        "title": "Velg leverandør og nummer",
+        "description": "Twilio er raskere å komme i gang med og håndterer Meta for deg. Meta Cloud API koster mindre, men du tar bedriftsverifiseringen selv. Skriv inn nummeret kundene ser, i internasjonalt format."
+      },
+      "credentials": {
+        "title": "Legg inn legitimasjonen",
+        "description": "Kopier hver verdi fra {provider}. Hvert felt sier hvor du finner den. Lagre når du har dem; du kan komme tilbake for resten."
+      },
+      "webhook": {
+        "title": "Registrer webhooken",
+        "description": "Kopier denne adressen inn i {provider} slik at meldinger fra kunder når Torqvoice. Lagre denne siden først: adressen sjekkes mot det som er lagret her.",
+        "open": "Åpne webhook-innstillinger hos {provider}",
+        "recheck": "Sjekk på nytt",
+        "waiting": "Ingenting har nådd denne adressen ennå. Den blir grønn straks leverandøren verifiserer den eller leverer en melding.",
+        "seen": "Leverandøren har nådd denne adressen. Innkommende meldinger dukker opp under Meldinger."
+      },
+      "templates": {
+        "title": "Få malene godkjent",
+        "description": "Trengs bare for å skrive først, utenfor 24-timersvinduet. Du kan hoppe over nå og komme tilbake når meldinger virker.",
+        "open": "Åpne maler hos {provider}"
+      },
+      "test": {
+        "title": "Send en testmelding",
+        "description": "Send først en melding til WhatsApp-nummeret ditt fra din egen telefon. Det beviser at webhooken virker og åpner et 24-timersvindu, så svaret trenger ingen mal."
+      },
+      "back": "Tilbake",
+      "continue": "Fortsett"
+    },
+    "register": {
+      "title": "Registrer nummeret hos Meta",
+      "description": "Meta krever en egen registrering når nummeret er verifisert. Velg en sekssifret PIN, eller bruk nummerets eksisterende PIN for totrinnsbekreftelse. Skriv den ned: du trenger den for å flytte eller registrere nummeret på nytt.",
+      "action": "Registrer nummer",
+      "done": "Nummeret er registrert",
+      "failed": "Kunne ikke registrere nummeret",
+      "limit": "Meta tillater 10 registreringsforsøk per nummer på 72 timer. Feil PIN teller med, så sjekk den først."
+    }
+  },
+  "messages": {
+    "featureName": "WhatsApp-meldinger",
+    "featureDescription": "Send og motta WhatsApp-meldinger, inkludert bilder av deler, rett fra Torqvoice.",
+    "noThreads": "Ingen samtaler ennå",
+    "noThreadsHint": "Meldinger dukker opp her så snart en kunde skriver til WhatsApp-nummeret ditt, eller du skriver til dem.",
+    "selectThread": "Velg en samtale",
+    "backToThreads": "Samtaler",
+    "empty": "Ingenting her ennå.",
+    "placeholder": "Skriv en melding",
+    "send": "Send",
+    "sendFailed": "Kunne ikke sende meldingen",
+    "attach": "Legg ved et bilde",
+    "attachFailed": "Kunne ikke legge ved bildet",
+    "removeAttachment": "Fjern vedlegg",
+    "attachment": "Vedlegg",
+    "delete": "Slett",
+    "deleteFailed": "Kunne ikke slette meldingen",
+    "sentAsTemplate": "mal",
+    "sentAsTemplateNotice": "Sendt som den godkjente malen din, fordi kunden ikke har skrevet de siste 24 timene.",
+    "windowClosed": "Kunden har ikke skrevet de siste 24 timene, så dette går ut som den godkjente malen din.",
+    "unknownNumber": "Dette nummeret tilhører ingen kunde ennå. Legg dem til som kunde for å svare.",
+    "notConfigured": {
+      "title": "WhatsApp er ikke koblet til",
+      "description": "Koble til et WhatsApp-nummer for å sende og motta meldinger med kundene dine.",
+      "action": "Sett opp WhatsApp"
+    },
+    "title": "Samtaler",
+    "searchPlaceholder": "Søk i samtaler",
+    "noSearchResults": "Ingen samtaler passer søket.",
+    "compose": {
+      "newMessage": "Ny melding",
+      "title": "Ny WhatsApp-melding",
+      "searchPlaceholder": "Søk etter kunder",
+      "noMatches": "Ingen kunder passer søket.",
+      "noRecipients": "Ingen kunder med telefonnummer ennå."
+    },
+    "windowBlocked": "Denne kunden har ikke skrevet de siste 24 timene, så WhatsApp leverer ingen melding nå. Be dem sende deg en, eller sett opp en godkjent mal.",
+    "windowBlockedAction": "Sett opp en mal",
+    "windowBlockedPlaceholder": "Venter på at kunden skriver først"
+  },
+  "sendPhoto": {
+    "action": "Send på WhatsApp",
+    "title": "Send bilde på WhatsApp",
+    "description": "Kunden får dette bildet i WhatsApp-samtalen sin.",
+    "captionPlaceholder": "Legg til en merknad, for eksempel hva bildet viser",
+    "send": "Send",
+    "cancel": "Avbryt",
+    "sent": "Bilde sendt",
+    "sentAsTemplate": "Bilde sendt som den godkjente malen din",
+    "failed": "Kunne ikke sende bildet"
+  }
+}

+ 122 - 1
messages/nl/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Set opnieuw opgeslagen",
     "tire_set_dispose": "Bandenset afgeschreven",
     "tire_set_attach": "Bestanden aan een bandenset toegevoegd",
-    "tire_set_detach": "Bestand van een bandenset verwijderd"
+    "tire_set_detach": "Bestand van een bandenset verwijderd",
+    "whatsapp_send": "WhatsApp-bericht verzonden"
+  },
+  "summary": {
+    "customField_create": "Aangepast veld \"{name}\" aangemaakt",
+    "customField_delete": "Aangepast veld \"{name}\" verwijderd",
+    "customField_update": "Aangepast veld \"{name}\" bijgewerkt",
+    "customer_backfillNumbers": "Klantnummers toegekend aan {count, plural, one {# klant} other {# klanten}}",
+    "customer_create": "Klant {name} aangemaakt",
+    "customer_delete": "Klant {id} verwijderd",
+    "customer_update": "Klant {id} bijgewerkt",
+    "email_sendInspection": "Keuring per e-mail verstuurd naar {recipient}",
+    "email_sendInvoice": "Factuur per e-mail verstuurd naar {recipient}",
+    "email_sendQuote": "Offerte per e-mail verstuurd naar {recipient}",
+    "finding_create": "Bevinding \"{description}\" aangemaakt op voertuig {vehicleId}",
+    "finding_delete": "Bevinding {id} verwijderd",
+    "finding_resolve": "Bevinding \"{description}\" afgehandeld",
+    "finding_update": "Bevinding \"{description}\" bijgewerkt",
+    "inspectionTemplate_create": "Keuringssjabloon \"{name}\" aangemaakt",
+    "inspectionTemplate_createFromPreset": "Keuringssjabloon \"{name}\" aangemaakt vanuit een voorinstelling",
+    "inspectionTemplate_delete": "Keuringssjabloon \"{name}\" verwijderd",
+    "inspectionTemplate_duplicate": "Keuringssjabloon gekopieerd naar \"{name}\"",
+    "inspectionTemplate_import": "{count, plural, one {# keuringssjabloon} other {# keuringssjablonen}} geïmporteerd uit een pakket",
+    "inspectionTemplate_update": "Keuringssjabloon \"{name}\" bijgewerkt",
+    "inspection_complete": "Keuring {id} afgerond",
+    "inspection_create": "Keuring {id} aangemaakt",
+    "inspection_delete": "Keuring {id} verwijderd",
+    "inspection_reopen": "Keuring {id} heropend",
+    "inventory_bulkDelete": "{count, plural, one {# voorraadonderdeel} other {# voorraadonderdelen}} in bulk verwijderd",
+    "inventory_create": "Voorraadonderdeel \"{name}\" aangemaakt",
+    "inventory_delete": "Voorraadonderdeel {id} verwijderd",
+    "inventory_update": "Voorraadonderdeel {id} bijgewerkt",
+    "labor_preset_create": "Werksjabloon \"{name}\" aangemaakt",
+    "labor_preset_delete": "Werksjabloon {id} verwijderd",
+    "labor_preset_update": "Werksjabloon {id} bijgewerkt",
+    "note_create": "Notitie aangemaakt op voertuig {vehicleId}",
+    "note_delete": "Notitie {id} verwijderd",
+    "organization_create": "Organisatie \"{name}\" aangemaakt",
+    "organization_sampleDataRemove": "Voorbeeldgegevens uit de installatie verwijderd",
+    "payment_create": "Betaling {amount} geregistreerd voor opdracht {serviceRecordId}",
+    "payment_delete": "Betaling {id} verwijderd",
+    "quoteRequest_update": "Status van offerteaanvraag gewijzigd naar {status, select, pending {in behandeling} approved {goedgekeurd} rejected {afgewezen} completed {afgerond} other {{status}}}",
+    "quote_convert": "Offerte {ref} omgezet naar opdracht {serviceRecordId}",
+    "quote_create": "Offerte {ref} aangemaakt",
+    "quote_delete": "Offerte {ref} verwijderd",
+    "quote_status": "Offertestatus gewijzigd naar {status, select, draft {concept} sent {verzonden} accepted {geaccepteerd} rejected {afgewezen} expired {verlopen} converted {omgezet} other {{status}}}",
+    "quote_update": "Offerte {ref} bijgewerkt",
+    "recurringInvoice_create": "Terugkerende factuur \"{title}\" aangemaakt",
+    "recurringInvoice_delete": "Terugkerende factuur {id} verwijderd",
+    "recurringInvoice_update": "Terugkerende factuur \"{title}\" bijgewerkt",
+    "reminder_create": "Herinnering \"{title}\" aangemaakt",
+    "reminder_delete": "Herinnering {id} verwijderd",
+    "role_create": "Rol \"{name}\" aangemaakt",
+    "role_delete": "Rol \"{name}\" verwijderd",
+    "role_update": "Rol \"{name}\" bijgewerkt",
+    "scheduled_message_cancel": "Ingepland bericht geannuleerd",
+    "scheduled_message_create": "{channel, select, email {E-mail} sms {Sms} telegram {Telegram-bericht} in_app {Melding in de app} other {{channel}}} ingepland voor {sendAt}",
+    "scheduled_message_delete": "Ingepland bericht verwijderd",
+    "scheduled_message_send_now": "Ingeplande {channel, select, email {e-mail} sms {sms} telegram {Telegram-bericht} in_app {melding in de app} other {{channel}}} eerder verstuurd",
+    "scheduled_message_update": "Ingeplande {channel, select, email {e-mail} sms {sms} telegram {Telegram-bericht} in_app {melding in de app} other {{channel}}} bijgewerkt",
+    "service_create": "Opdracht {ref} aangemaakt",
+    "service_createCounterSale": "Baliekoop {ref} aangemaakt",
+    "service_createDraft": "Conceptopdracht {ref} aangemaakt",
+    "service_createFromInspection": "Werkorder {ref} aangemaakt vanuit een keuring",
+    "service_delete": "Opdracht {ref} verwijderd",
+    "service_status": "Opdrachtstatus gewijzigd naar {status, select, pending {open} scheduled {ingepland} in_progress {in behandeling} waiting_parts {wacht op onderdelen} ready {klaar} completed {afgerond} cancelled {geannuleerd} other {{status}}}",
+    "service_update": "Opdracht {ref} bijgewerkt",
+    "settings_applyTaxRate": "{rate}% btw toegepast op {records, plural, one {# opdracht} other {# opdrachten}} en {quotes, plural, one {# offerte} other {# offertes}}",
+    "settings_convertToExclusive": "{records, plural, one {# opdracht} other {# opdrachten}} en {quotes, plural, one {# offerte} other {# offertes}} omgezet naar prijzen exclusief btw",
+    "settings_convertToInclusive": "{records, plural, one {# opdracht} other {# opdrachten}} en {quotes, plural, one {# offerte} other {# offertes}} omgezet naar prijzen inclusief btw",
+    "settings_deleteContent": "Alle gegevens verwijderd: {items}",
+    "settings_updateInvoiceLayout": "Factuurindeling bijgewerkt",
+    "settings_updateQuoteLayout": "Offerte-indeling bijgewerkt",
+    "sms_send": "Sms verstuurd naar {name} ({phone})",
+    "statusReport_create": "Statusrapport aangemaakt voor opdracht {serviceRecordId}",
+    "statusReport_delete": "Statusrapport verwijderd",
+    "statusReport_send": "Statusrapport verstuurd via {channels}",
+    "subscription_cancel": "Abonnement opgezegd per einde van de periode",
+    "subscription_resume": "Abonnement hervat",
+    "team_cancelInvitation": "Uitnodiging voor {email} ingetrokken",
+    "team_removeMember": "Teamlid {id} verwijderd",
+    "team_sendInvitation": "{email} uitgenodigd als {role, select, owner {eigenaar} admin {beheerder} member {lid} other {{role}}}",
+    "team_updateRole": "Rol van een lid gewijzigd naar {role, select, owner {eigenaar} admin {beheerder} member {lid} other {{role}}}",
+    "technician_create": "Monteur \"{name}\" aangemaakt",
+    "technician_delete": "Monteur {id} verwijderd",
+    "technician_update": "Monteur {id} bijgewerkt",
+    "telegram_send": "Telegram-bericht verstuurd naar {name}",
+    "tire_location_bulk_create": "{count, plural, one {# bandenplek} other {# bandenplekken}} aangemaakt",
+    "tire_location_create": "Bandenplek {code} aangemaakt",
+    "tire_location_delete": "Bandenplek {code} verwijderd",
+    "tire_location_update": "Bandenplek {code} bijgewerkt",
+    "tire_measurement_create": "{count, plural, one {# meting} other {# metingen}} vastgelegd op bandenset {ref}",
+    "tire_set_add_to_work_order": "Bandenset {ref} toegevoegd aan werkorder {jobRef}",
+    "tire_set_attach": "{count, plural, one {# bestand} other {# bestanden}} toegevoegd aan bandenset {ref}",
+    "tire_set_check_in": "Bandenset {ref} ingenomen op {code}",
+    "tire_set_check_out": "Bandenset {ref} meegegeven",
+    "tire_set_create_quote": "Offerte {ref} aangemaakt vanuit een bandenset",
+    "tire_set_create_work_order": "Werkorder {ref} aangemaakt vanuit een bandenset",
+    "tire_set_delete": "Bandenset {ref} verwijderd",
+    "tire_set_detach": "{fileName} verwijderd van een bandenset",
+    "tire_set_dispose": "Bandenset {ref} afgeschreven",
+    "tire_set_message_customer": "Klant bericht over bandenset {ref} via {channel, select, email {e-mail} sms {sms} telegram {Telegram} in_app {melding in de app} other {{channel}}}",
+    "tire_set_relocate": "Bandenset {ref} verplaatst van {from} naar {to}",
+    "tire_set_relocateUnassigned": "Bandenset {ref} op {to} gezet",
+    "tire_set_return": "Bandenset {ref} opnieuw opgeslagen, op {code}",
+    "tire_set_unlink_work_order": "Bandenset {ref} losgekoppeld van werkorder {jobRef}",
+    "tire_set_update": "Bandenset {ref} bijgewerkt",
+    "tire_treatment_complete_all": "{count, plural, one {# voorbereidende klus} other {# voorbereidende klussen}} afgerond op bandenset {ref}",
+    "tire_treatment_mark": "{type, select, wash_tires {Banden wassen} wash_rims {Velgen wassen} balance {Balanceren} tpms_service {TPMS-service} new_valves {Nieuwe ventielen} repair {Reparatie} other {{type}}} op bandenset {ref} gemarkeerd als {status, select, done {klaar} pending {nog te doen} skipped {overgeslagen} other {{status}}}",
+    "tire_treatment_update": "Voorbereidend werk op bandenset {ref} bijgewerkt",
+    "tire_warehouse_archive": "Bandenopslag {name} gearchiveerd",
+    "tire_warehouse_create": "Bandenopslag {name} aangemaakt",
+    "tire_warehouse_delete": "Bandenopslag {name} verwijderd",
+    "tire_warehouse_update": "Bandenopslag {name} bijgewerkt",
+    "vehicle_archive": "Voertuig {id} gearchiveerd",
+    "vehicle_create": "Voertuig {year} {make} {model} aangemaakt",
+    "vehicle_delete": "Voertuig {name} verwijderd",
+    "vehicle_unarchive": "Voertuig {id} uit het archief gehaald",
+    "vehicle_update": "Voertuig {name} bijgewerkt, gewijzigd: {fields}",
+    "vehicle_updateNoChanges": "Voertuig {name} bijgewerkt, niets gewijzigd",
+    "webhook_create": "Webhook {name} aangemaakt",
+    "whatsapp_send": "WhatsApp-bericht verzonden naar {name}"
   }
 }

+ 15 - 0
messages/nl/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Zet de app op je beginscherm voor snelle toegang",
     "enterFullscreen": "Volledig scherm",
     "exitFullscreen": "Volledig scherm afsluiten"
+  },
+  "broadcast": {
+    "dismiss": "Sluiten",
+    "title": "Bericht aan iedereen",
+    "description": "Wordt bovenaan elke pagina getoond, aan iedereen, ingelogd of niet. Laat leeg voor geen bericht.",
+    "message": "Bericht",
+    "level": "Ernst",
+    "post": "Bericht plaatsen",
+    "clear": "Bericht verwijderen",
+    "levelInfo": "Informatie",
+    "levelWarning": "Waarschuwing",
+    "levelCritical": "Kritiek",
+    "placeholder": "bijv. We hebben problemen met onze serverinfrastructuur en werken eraan.",
+    "posted": "Bericht geplaatst",
+    "cleared": "Bericht verwijderd"
   }
 }

+ 6 - 2
messages/nl/customers.json

@@ -38,7 +38,8 @@
       "messages": "Berichten",
       "requests": "Aanvragen ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Kenteken",
@@ -68,7 +69,10 @@
     "customerCreated": "Klant aangemaakt",
     "saveError": "Opslaan van klant mislukt",
     "customerNumber": "Klantnr.",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Contact",
+    "sectionBilling": "Facturatie",
+    "addScannedVehicle": "Voertuig {vehicle} toevoegen"
   },
   "serviceRequests": {
     "statusNew": "Nieuw",

+ 7 - 0
messages/nl/dashboard.json

@@ -46,6 +46,7 @@
     "maintenance": "Verwacht onderhoud",
     "reminders": "Aankomende herinneringen",
     "inspections": "Inspecties",
+    "serviceRequests": "Serviceaanvragen",
     "quoteRequests": "Offerte-aanvragen",
     "quoteResponses": "Offerte-reacties",
     "sms": "Recente berichten",
@@ -68,6 +69,7 @@
     "active": "Actief",
     "dismissedTab": "Genegeerd",
     "noActive": "Geen voertuigen hebben momenteel onderhoud nodig.",
+    "noDismissed": "Geen genegeerde voertuigen.",
     "dismissedOn": "Genegeerd op {date}",
     "restore": "Herstellen",
     "settingsAriaLabel": "Maintenance settings",
@@ -96,6 +98,11 @@
     "itemCount": "{inspected}/{total} items",
     "noData": "Geen inspecties."
   },
+  "serviceRequests": {
+    "title": "Serviceaanvragen",
+    "description": "Afspraakaanvragen via het klantenportaal",
+    "noData": "Geen openstaande serviceaanvragen."
+  },
   "quoteRequests": {
     "title": "Offerte-aanvragen",
     "description": "Klanten die offertes aanvragen vanuit gedeelde inspecties",

+ 4 - 1
messages/nl/messages.json

@@ -4,7 +4,10 @@
     "featureDescription": "Verzend en ontvang SMS-berichten met uw klanten rechtstreeks vanuit Torqvoice.",
     "tabs": {
       "inbox": "Postvak in",
-      "scheduled": "Gepland"
+      "scheduled": "Gepland",
+      "sms": "Sms",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
     },
     "smsNotConfiguredTitle": "Sms is niet ingesteld",
     "smsNotConfiguredHint": "Koppel een sms-provider om hier berichten te versturen en ontvangen. Geplande e-mail werkt ook zonder.",

+ 26 - 0
messages/nl/messaging.json

@@ -0,0 +1,26 @@
+{
+  "inbox": {
+    "title": "Gesprekken",
+    "searchPlaceholder": "Gesprekken zoeken",
+    "noSearchResults": "Geen gesprek komt overeen met je zoekopdracht.",
+    "empty": "Nog geen gesprekken.",
+    "selectConversation": "Kies een gesprek",
+    "back": "Terug",
+    "newMessage": "Nieuw bericht",
+    "unknownContact": "Dit nummer hoort nog bij geen klant. Voeg ze als klant toe om te antwoorden.",
+    "noChannelsTitle": "Geen berichtenkanaal ingesteld",
+    "noChannelsHint": "Verbind sms, WhatsApp of Telegram om hiervandaan naar klanten te schrijven.",
+    "goToProviders": "Provider instellen",
+    "chooseChannel": {
+      "title": "Via welk kanaal?",
+      "description": "Je kunt je klanten op meerdere manieren bereiken."
+    },
+    "deleteConversation": "Gesprek verwijderen",
+    "deleteTitle": "Dit gesprek verwijderen?",
+    "deleteDescription": "Verwijdert jouw kopie van het gesprek met {name}. Op hun telefoon blijven de berichten staan.",
+    "deleteFailed": "Kon het gesprek niet verwijderen",
+    "delete": "Verwijderen",
+    "cancel": "Annuleren",
+    "loadMore": "Meer laden"
+  }
+}

+ 4 - 2
messages/nl/navigation.json

@@ -58,7 +58,8 @@
     "allTireSets": "Alle bandensets",
     "tireStorage": "Opslagindeling",
     "tireSetDetails": "Bandenset",
-    "tireForecast": "Volgend seizoen"
+    "tireForecast": "Volgend seizoen",
+    "whatsapp": "WhatsApp"
   },
   "search": "Zoeken...",
   "shortcut": "Ctrl+K",
@@ -113,6 +114,7 @@
     "more": "Meer",
     "auditLog": "Activiteitenlog",
     "aiAssistant": "AI-assistent",
-    "tireHotel": "Bandenhotel"
+    "tireHotel": "Bandenhotel",
+    "whatsapp": "WhatsApp"
   }
 }

+ 4 - 2
messages/nl/scheduledMessages.json

@@ -7,7 +7,8 @@
       "email": "E-mail",
       "sms": "Sms",
       "telegram": "Telegram",
-      "in_app": "Notitie in de app"
+      "in_app": "Notitie in de app",
+      "whatsapp": "WhatsApp"
     },
     "repeatLabel": "Herhalen",
     "frequencies": {
@@ -39,7 +40,8 @@
     "schedule": "Plannen",
     "scheduled": "Bericht gepland",
     "updated": "Gepland bericht bijgewerkt",
-    "saveError": "Kan het bericht niet opslaan"
+    "saveError": "Kan het bericht niet opslaan",
+    "whatsappNeedsTemplate": "Dit wordt ruim na het sluiten van het venster van 24 uur verstuurd en er is geen WhatsApp-tekstsjabloon ingesteld, dus het zou mislukken. Stel er een in via Instellingen > Providers > WhatsApp."
   },
   "list": {
     "scheduleMessage": "Bericht plannen",

+ 35 - 3
messages/nl/settings.json

@@ -126,6 +126,10 @@
       "tireHotel": {
         "title": "Bandenhotel",
         "description": "Seizoensopslag van banden"
+      },
+      "providers": {
+        "title": "Providers",
+        "description": "E-mail, sms, WhatsApp en Telegram"
       }
     },
     "pro": "PRO"
@@ -917,8 +921,8 @@
     "optInspectionsDesc": "Inspectiesjablonen, inspecties en resultaten",
     "optAuditLogs": "Auditlogboeken",
     "optAuditLogsDesc": "Activiteiten- en wijzigingsgeschiedenis",
-    "optSmsMessages": "SMS-berichten",
-    "optSmsMessagesDesc": "Verzonden en ontvangen SMS-berichten",
+    "optSmsMessages": "Berichten",
+    "optSmsMessagesDesc": "Verzonden en ontvangen sms-, WhatsApp- en Telegram-berichten",
     "optScheduledMessages": "Geplande berichten",
     "optScheduledMessagesDesc": "Berichten in de wachtrij en herhalende berichten die nog verstuurd worden",
     "optNotifications": "Meldingen",
@@ -980,7 +984,9 @@
       "supportAction": "neem contact op met ons supportteam."
     },
     "optTireHotel": "Bandenhotel",
-    "optTireHotelDesc": "Opgeslagen bandensets, stellingen, voorbereiding en opslagovereenkomsten"
+    "optTireHotelDesc": "Opgeslagen bandensets, stellingen, voorbereiding en opslagovereenkomsten",
+    "optWorkshopConfig": "Werkplaatsinrichting",
+    "optWorkshopConfigDesc": "Arbeidsjablonen, rollen, webhooks en geplande rapporten"
   },
   "license": {
     "title": "White-label licentie",
@@ -1487,5 +1493,31 @@
     "prepTitle": "Prijzen voorbereiding",
     "prepHint": "Wat elk soort voorbereiding kost. Laat er een leeg en die wordt nooit gefactureerd, wat past bij een bedrijf dat wassen in de opslagprijs meeneemt. Beprijsde komen automatisch op offerte en werkorder.",
     "prepNotCharged": "niet in rekening"
+  },
+  "providers": {
+    "tabs": {
+      "email": "E-mail",
+      "sms": "SMS",
+      "whatsapp": "WhatsApp",
+      "telegram": "Telegram"
+    },
+    "locked": {
+      "email": {
+        "feature": "E-mailinstellingen",
+        "description": "Stel SMTP-verzending in om facturen, offertes en meldingen direct vanuit de werkplaats te sturen."
+      },
+      "sms": {
+        "feature": "Sms-berichten",
+        "description": "Verstuur en ontvang sms met je klanten rechtstreeks vanuit Torqvoice."
+      },
+      "whatsapp": {
+        "feature": "WhatsApp-berichten",
+        "description": "Verstuur en ontvang WhatsApp-berichten, inclusief foto's van onderdelen, vanuit Torqvoice."
+      },
+      "telegram": {
+        "feature": "Telegram-berichten",
+        "description": "Verstuur en ontvang Telegram-berichten met je klanten vanuit Torqvoice."
+      }
+    }
   }
 }

+ 2 - 1
messages/nl/statusReport.json

@@ -37,7 +37,8 @@
     "skip": "Overslaan",
     "send": "Verzenden",
     "sent": "Statusrapport verzonden",
-    "failed": "Statusrapport verzenden mislukt"
+    "failed": "Statusrapport verzenden mislukt",
+    "notSent": "Niet verzonden"
   },
   "view": {
     "title": "Statusrapport voertuig",

+ 16 - 1
messages/nl/vehicles.json

@@ -321,7 +321,22 @@
     "licensePlateMarine": "Registratienummer",
     "vehicleUpdated": "Voertuig bijgewerkt",
     "vehicleAdded": "Voertuig toegevoegd",
-    "saveError": "Opslaan van voertuig mislukt"
+    "saveError": "Opslaan van voertuig mislukt",
+    "scanDocument": "Kentekenbewijs scannen",
+    "scanDocumentHint": "Fotografeer de voertuigpapieren, de velden worden automatisch ingevuld.",
+    "scanningDocument": "Document wordt gelezen...",
+    "scanSuccess": "Velden ingevuld vanaf het document",
+    "scanFailed": "Kon het document niet lezen",
+    "ownerMatchTitle": "Voertuig toewijzen",
+    "ownerMatchDescription": "Op het document staat {name}. Kies een van deze klanten of maak een nieuwe aan.",
+    "ownerMatchScore": "{percent}% overeenkomst",
+    "ownerMatchCreateNew": "Nieuwe klant aanmaken",
+    "ownerMatchChoose": "Klant kiezen",
+    "scanUnavailable": "Schakel AI in bij instellingen",
+    "sectionAssignment": "Foto en eigenaar",
+    "sectionDetails": "Details",
+    "addScannedOwner": "Klant {name} toevoegen",
+    "ownerSaveFailed": "Kon de klant niet aanmaken"
   },
   "archiveDialog": {
     "title": "Voertuig archiveren",

+ 179 - 0
messages/nl/whatsapp.json

@@ -0,0 +1,179 @@
+{
+  "settings": {
+    "title": "WhatsApp",
+    "description": "Verstuur en ontvang WhatsApp-berichten met je klanten.",
+    "helpLink": "{provider} instellen",
+    "save": "WhatsApp-instellingen opslaan",
+    "saved": "WhatsApp-instellingen opgeslagen",
+    "saveError": "Kon de WhatsApp-instellingen niet opslaan",
+    "enable": {
+      "label": "WhatsApp inschakelen",
+      "hint": "Zet aan zodra de gegevens van je provider ingevuld zijn.",
+      "disabledInfo": "Schakel WhatsApp in om je provider, nummer en sjabloon in te stellen."
+    },
+    "provider": {
+      "label": "Provider",
+      "hint": "Wie je WhatsApp-nummer levert. Bij wisselen blijven de gegevens van de vorige bewaard."
+    },
+    "from": {
+      "label": "Je WhatsApp-nummer",
+      "hint": "Het nummer dat klanten zien, in internationaal formaat."
+    },
+    "secret": {
+      "show": "Tonen",
+      "hide": "Verbergen",
+      "stored": "Opgeslagen en verborgen. Typ een nieuwe waarde om hem te vervangen."
+    },
+    "webhook": {
+      "label": "Webhook-URL",
+      "description": "Plak dit bij je provider zodat inkomende berichten in Torqvoice aankomen.",
+      "copy": "Webhook-URL kopiëren",
+      "copied": "Webhook-URL gekopieerd",
+      "saveFirst": "Sla deze instellingen op voordat je provider de URL verifieert. Er wordt gecontroleerd tegen wat hier is opgeslagen, dus een niet-opgeslagen formulier faalt."
+    },
+    "template": {
+      "title": "Berichtsjabloon",
+      "description": "Gebruikt wanneer je een klant schrijft die je niet recent iets stuurde.",
+      "windowExplainer": "WhatsApp staat vrije tekst alleen toe binnen 24 uur na het laatste bericht van de klant. Daarbuiten moet je bericht als goedgekeurd sjabloon uitgaan.",
+      "nameLabel": "Naam van het sjabloon",
+      "nameHint": "De naam van het goedgekeurde sjabloon, of de Content SID bij providers die daarmee werken.",
+      "languageLabel": "Taal van het sjabloon",
+      "languageHint": "De taalcode waaronder het sjabloon is goedgekeurd, bijvoorbeeld nl.",
+      "variablesLabel": "Waarden van het sjabloon, op volgorde",
+      "variablesHint": "Kies de waarden die de genummerde plekken in je sjabloon invullen, in dezelfde volgorde. Laat leeg als je sjabloon er geen heeft.",
+      "tokens": {
+        "customer": "klantnaam",
+        "vehicle": "bouwjaar, merk en model",
+        "plate": "kenteken",
+        "message": "wat je typt",
+        "workshop": "naam van je werkplaats",
+        "photo": "de bijgevoegde foto"
+      },
+      "variablesEmpty": "Nog geen waarden. Voeg ze hieronder toe, in de volgorde die je sjabloon verwacht.",
+      "variablesRemove": "{name} verwijderen",
+      "mediaUrlLabel": "Media-URL voor je sjabloon",
+      "mediaUrlHint": "Plak dit in het mediaveld van het sjabloon en vervang n door het nummer van de fotowaarde.",
+      "mediaUrlCopy": "Media-URL kopiëren",
+      "mediaUrlCopied": "Media-URL gekopieerd",
+      "mediaUrlSample": "Gebruik als voorbeeldwaarde bij de provider: sample",
+      "textTitle": "Tekstberichten",
+      "textDescription": "Gebruikt wanneer je zonder foto schrijft: vanuit Berichten, vanuit het WhatsApp-tabblad bij een klant of vanuit een gepland bericht.",
+      "mediaTitle": "Berichten met foto",
+      "mediaDescription": "Gebruikt door de knop Via WhatsApp sturen bij de foto's van een werkorder, en telkens als je een foto toevoegt in een gesprek.",
+      "textMissing": "Niet ingesteld: je kunt geen klant schrijven die de afgelopen 24 uur niets stuurde.",
+      "mediaMissing": "Niet ingesteld: je kunt geen foto's sturen naar een klant die de afgelopen 24 uur niets stuurde.",
+      "whatIsIt": "Een sjabloon is een vast bericht dat je één keer schrijft en dat {provider} goedkeurt: de tekst verandert nooit, je vult alleen de genummerde plekken in. Schrijf het in hun console en plak de identificatie hier.",
+      "whyTwo": "Je hebt er twee nodig omdat WhatsApp het mediatype vastlegt bij goedkeuring. Een sjabloon goedgekeurd met foto kan alleen foto's dragen, een als tekst goedgekeurd sjabloon nooit.",
+      "exampleLabel": "Voorbeeldtekst voor de console van je provider",
+      "exampleTextValues": "Vul het met: customer, message. Schrijf het in de taal die je klanten lezen.",
+      "exampleMediaValues": "Vul het met: customer, message en photo voor de media-URL. Schrijf het in de taal die je klanten lezen.",
+      "exampleMediaValuesHeader": "Vul hem met: customer, message. De foto wordt automatisch meegestuurd en heeft dus geen eigen invulplek nodig. Schrijf hem in de taal die uw klanten lezen."
+    },
+    "test": {
+      "send": "Test versturen",
+      "sent": "Testbericht verstuurd",
+      "failed": "Kon het testbericht niet versturen",
+      "hint": "Stuur jezelf eerst een WhatsApp, anders heeft de test een goedgekeurd sjabloon nodig."
+    },
+    "disconnect": {
+      "action": "WhatsApp loskoppelen",
+      "hint": "Verwijdert de gegevens uit Torqvoice. Je gesprekken blijven bewaard.",
+      "done": "WhatsApp losgekoppeld",
+      "title": "Loskoppelen"
+    },
+    "savedIncomplete": "Opgeslagen. Nog nodig om te kunnen sturen: {fields}",
+    "fieldStillNeeded": "Nog nodig om te kunnen sturen",
+    "steps": {
+      "openProvider": "{provider} openen",
+      "openNumber": "Uw afzendernummer vinden",
+      "openToken": "Een permanent token maken",
+      "consolePath": "Ga in Meta naar:",
+      "provider": {
+        "title": "Kies je provider en nummer",
+        "description": "Twilio is sneller op te zetten en regelt Meta voor je. Meta Cloud API kost minder, maar de bedrijfsverificatie doe je zelf. Vul het nummer in dat klanten zien, in internationaal formaat."
+      },
+      "credentials": {
+        "title": "Vul je gegevens in",
+        "description": "Kopieer elke waarde uit {provider}. Elk veld zegt waar je die vindt. Sla op zodra je ze hebt; voor de rest kom je terug."
+      },
+      "webhook": {
+        "title": "Registreer de webhook",
+        "description": "Plak dit adres in {provider} zodat berichten van klanten bij Torqvoice aankomen. Sla eerst deze pagina op: het adres wordt gecontroleerd tegen wat hier is opgeslagen.",
+        "open": "Webhook-instellingen bij {provider} openen",
+        "recheck": "Opnieuw controleren",
+        "waiting": "Er is nog niets op dit adres binnengekomen. Het wordt groen zodra je provider het verifieert of een bericht aflevert.",
+        "seen": "Je provider heeft dit adres bereikt. Inkomende berichten verschijnen bij Berichten."
+      },
+      "templates": {
+        "title": "Laat je sjablonen goedkeuren",
+        "description": "Alleen nodig om als eerste te schrijven, buiten het venster van 24 uur. Je kunt dit overslaan en terugkomen als berichten werken.",
+        "open": "Sjablonen bij {provider} openen"
+      },
+      "test": {
+        "title": "Stuur een testbericht",
+        "description": "Stuur eerst vanaf je eigen telefoon een bericht naar je WhatsApp-nummer. Dat bewijst dat de webhook werkt en opent een venster van 24 uur, dus een antwoord heeft geen sjabloon nodig."
+      },
+      "back": "Terug",
+      "continue": "Doorgaan"
+    },
+    "register": {
+      "title": "Registreer het nummer bij Meta",
+      "description": "Meta vraagt om een aparte registratie zodra het nummer geverifieerd is. Kies een pincode van zes cijfers, of gebruik de bestaande pincode voor verificatie in twee stappen. Schrijf hem op: je hebt hem nodig om het nummer te verplaatsen of opnieuw te registreren.",
+      "action": "Nummer registreren",
+      "done": "Nummer geregistreerd",
+      "failed": "Kon het nummer niet registreren",
+      "limit": "Meta staat 10 registratiepogingen per nummer per 72 uur toe. Een verkeerde pincode telt mee, dus controleer hem eerst."
+    }
+  },
+  "messages": {
+    "featureName": "WhatsApp-berichten",
+    "featureDescription": "Verstuur en ontvang WhatsApp-berichten, inclusief foto's van onderdelen, vanuit Torqvoice.",
+    "noThreads": "Nog geen gesprekken",
+    "noThreadsHint": "Berichten verschijnen hier zodra een klant naar je WhatsApp-nummer schrijft, of jij naar hen.",
+    "selectThread": "Kies een gesprek",
+    "backToThreads": "Gesprekken",
+    "empty": "Hier is nog niets.",
+    "placeholder": "Schrijf een bericht",
+    "send": "Versturen",
+    "sendFailed": "Kon het bericht niet versturen",
+    "attach": "Foto toevoegen",
+    "attachFailed": "Kon de foto niet toevoegen",
+    "removeAttachment": "Bijlage verwijderen",
+    "attachment": "Bijlage",
+    "delete": "Verwijderen",
+    "deleteFailed": "Kon het bericht niet verwijderen",
+    "sentAsTemplate": "sjabloon",
+    "sentAsTemplateNotice": "Verstuurd als je goedgekeurde sjabloon, omdat de klant de afgelopen 24 uur niets stuurde.",
+    "windowClosed": "De klant schreef de afgelopen 24 uur niets, dus dit gaat als je goedgekeurde sjabloon de deur uit.",
+    "unknownNumber": "Dit nummer hoort nog bij geen klant. Voeg ze als klant toe om te antwoorden.",
+    "notConfigured": {
+      "title": "WhatsApp is niet verbonden",
+      "description": "Verbind een WhatsApp-nummer om berichten met je klanten uit te wisselen.",
+      "action": "WhatsApp instellen"
+    },
+    "title": "Gesprekken",
+    "searchPlaceholder": "Gesprekken zoeken",
+    "noSearchResults": "Geen gesprek komt overeen met je zoekopdracht.",
+    "compose": {
+      "newMessage": "Nieuw bericht",
+      "title": "Nieuw WhatsApp-bericht",
+      "searchPlaceholder": "Klanten zoeken",
+      "noMatches": "Geen klant komt overeen met je zoekopdracht.",
+      "noRecipients": "Nog geen klanten met een telefoonnummer."
+    },
+    "windowBlocked": "Deze klant schreef de afgelopen 24 uur niets, dus WhatsApp levert nu geen bericht af. Vraag ze iets te sturen of stel een goedgekeurd sjabloon in.",
+    "windowBlockedAction": "Sjabloon instellen",
+    "windowBlockedPlaceholder": "Wachten tot de klant eerst schrijft"
+  },
+  "sendPhoto": {
+    "action": "Via WhatsApp sturen",
+    "title": "Foto via WhatsApp sturen",
+    "description": "De klant krijgt deze foto in zijn WhatsApp-gesprek.",
+    "captionPlaceholder": "Voeg een notitie toe, bijvoorbeeld wat de foto laat zien",
+    "send": "Versturen",
+    "cancel": "Annuleren",
+    "sent": "Foto verstuurd",
+    "sentAsTemplate": "Foto verstuurd als je goedgekeurde sjabloon",
+    "failed": "Kon de foto niet versturen"
+  }
+}

+ 122 - 1
messages/pl/audit.json

@@ -135,6 +135,127 @@
     "tire_set_return": "Ponownie przyjęto komplet",
     "tire_set_dispose": "Spisano komplet opon",
     "tire_set_attach": "Dodano pliki do kompletu opon",
-    "tire_set_detach": "Usunięto plik z kompletu opon"
+    "tire_set_detach": "Usunięto plik z kompletu opon",
+    "whatsapp_send": "Wysłano wiadomość WhatsApp"
+  },
+  "summary": {
+    "customField_create": "Utworzono pole niestandardowe „{name}”",
+    "customField_delete": "Usunięto pole niestandardowe „{name}”",
+    "customField_update": "Zaktualizowano pole niestandardowe „{name}”",
+    "customer_backfillNumbers": "Nadano numery klienta {count, plural, one {# klientowi} few {# klientom} many {# klientom} other {# klientom}}",
+    "customer_create": "Utworzono klienta {name}",
+    "customer_delete": "Usunięto klienta {id}",
+    "customer_update": "Zaktualizowano klienta {id}",
+    "email_sendInspection": "Wysłano przegląd e-mailem do {recipient}",
+    "email_sendInvoice": "Wysłano fakturę e-mailem do {recipient}",
+    "email_sendQuote": "Wysłano wycenę e-mailem do {recipient}",
+    "finding_create": "Utworzono usterkę „{description}” w pojeździe {vehicleId}",
+    "finding_delete": "Usunięto usterkę {id}",
+    "finding_resolve": "Rozwiązano usterkę „{description}”",
+    "finding_update": "Zaktualizowano usterkę „{description}”",
+    "inspectionTemplate_create": "Utworzono szablon przeglądu „{name}”",
+    "inspectionTemplate_createFromPreset": "Utworzono szablon przeglądu „{name}” z gotowego zestawu",
+    "inspectionTemplate_delete": "Usunięto szablon przeglądu „{name}”",
+    "inspectionTemplate_duplicate": "Zduplikowano szablon przeglądu jako „{name}”",
+    "inspectionTemplate_import": "Zaimportowano z pakietu {count, plural, one {# szablon przeglądu} few {# szablony przeglądu} many {# szablonów przeglądu} other {# szablonu przeglądu}}",
+    "inspectionTemplate_update": "Zaktualizowano szablon przeglądu „{name}”",
+    "inspection_complete": "Zakończono przegląd {id}",
+    "inspection_create": "Utworzono przegląd {id}",
+    "inspection_delete": "Usunięto przegląd {id}",
+    "inspection_reopen": "Ponownie otwarto przegląd {id}",
+    "inventory_bulkDelete": "Usunięto zbiorczo {count, plural, one {# część magazynową} few {# części magazynowe} many {# części magazynowych} other {# części magazynowej}}",
+    "inventory_create": "Utworzono część magazynową „{name}”",
+    "inventory_delete": "Usunięto część magazynową {id}",
+    "inventory_update": "Zaktualizowano część magazynową {id}",
+    "labor_preset_create": "Utworzono szablon robocizny „{name}”",
+    "labor_preset_delete": "Usunięto szablon robocizny {id}",
+    "labor_preset_update": "Zaktualizowano szablon robocizny {id}",
+    "note_create": "Utworzono notatkę przy pojeździe {vehicleId}",
+    "note_delete": "Usunięto notatkę {id}",
+    "organization_create": "Utworzono organizację „{name}”",
+    "organization_sampleDataRemove": "Usunięto dane przykładowe z konfiguracji startowej",
+    "payment_create": "Zarejestrowano płatność {amount} do zlecenia {serviceRecordId}",
+    "payment_delete": "Usunięto płatność {id}",
+    "quoteRequest_update": "Zmieniono status zapytania o wycenę na {status, select, pending {oczekujące} approved {zatwierdzone} rejected {odrzucone} completed {zakończone} other {{status}}}",
+    "quote_convert": "Przekształcono wycenę {ref} w zlecenie {serviceRecordId}",
+    "quote_create": "Utworzono wycenę {ref}",
+    "quote_delete": "Usunięto wycenę {ref}",
+    "quote_status": "Zmieniono status wyceny na {status, select, draft {szkic} sent {wysłana} accepted {zaakceptowana} rejected {odrzucona} expired {wygasła} converted {przekształcona} other {{status}}}",
+    "quote_update": "Zaktualizowano wycenę {ref}",
+    "recurringInvoice_create": "Utworzono fakturę cykliczną „{title}”",
+    "recurringInvoice_delete": "Usunięto fakturę cykliczną {id}",
+    "recurringInvoice_update": "Zaktualizowano fakturę cykliczną „{title}”",
+    "reminder_create": "Utworzono przypomnienie „{title}”",
+    "reminder_delete": "Usunięto przypomnienie {id}",
+    "role_create": "Utworzono rolę „{name}”",
+    "role_delete": "Usunięto rolę „{name}”",
+    "role_update": "Zaktualizowano rolę „{name}”",
+    "scheduled_message_cancel": "Anulowano zaplanowaną wiadomość",
+    "scheduled_message_create": "Zaplanowano {channel, select, email {e-mail} sms {SMS} telegram {wiadomość Telegram} in_app {powiadomienie w aplikacji} other {{channel}}} na {sendAt}",
+    "scheduled_message_delete": "Usunięto zaplanowaną wiadomość",
+    "scheduled_message_send_now": "Wysłano przed czasem zaplanowany {channel, select, email {e-mail} sms {SMS} telegram {wiadomość Telegram} in_app {powiadomienie w aplikacji} other {{channel}}}",
+    "scheduled_message_update": "Zaktualizowano zaplanowany {channel, select, email {e-mail} sms {SMS} telegram {wiadomość Telegram} in_app {powiadomienie w aplikacji} other {{channel}}}",
+    "service_create": "Utworzono zlecenie {ref}",
+    "service_createCounterSale": "Utworzono sprzedaż z lady {ref}",
+    "service_createDraft": "Utworzono szkic zlecenia {ref}",
+    "service_createFromInspection": "Utworzono zlecenie warsztatowe {ref} z przeglądu",
+    "service_delete": "Usunięto zlecenie {ref}",
+    "service_status": "Zmieniono status zlecenia na {status, select, pending {oczekujące} scheduled {zaplanowane} in_progress {w realizacji} waiting_parts {czeka na części} ready {gotowe} completed {zakończone} cancelled {anulowane} other {{status}}}",
+    "service_update": "Zaktualizowano zlecenie {ref}",
+    "settings_applyTaxRate": "Zastosowano {rate}% podatku do {records, plural, one {# zlecenia} few {# zleceń} many {# zleceń} other {# zlecenia}} i {quotes, plural, one {# wyceny} few {# wycen} many {# wycen} other {# wyceny}}",
+    "settings_convertToExclusive": "Przeliczono {records, plural, one {# zlecenie} few {# zlecenia} many {# zleceń} other {# zlecenia}} i {quotes, plural, one {# wycenę} few {# wyceny} many {# wycen} other {# wyceny}} na ceny netto",
+    "settings_convertToInclusive": "Przeliczono {records, plural, one {# zlecenie} few {# zlecenia} many {# zleceń} other {# zlecenia}} i {quotes, plural, one {# wycenę} few {# wyceny} many {# wycen} other {# wyceny}} na ceny brutto",
+    "settings_deleteContent": "Usunięto wszystkie dane: {items}",
+    "settings_updateInvoiceLayout": "Zaktualizowano układ faktury",
+    "settings_updateQuoteLayout": "Zaktualizowano układ wyceny",
+    "sms_send": "Wysłano SMS do {name} ({phone})",
+    "statusReport_create": "Utworzono raport statusu dla zlecenia {serviceRecordId}",
+    "statusReport_delete": "Usunięto raport statusu",
+    "statusReport_send": "Wysłano raport statusu przez {channels}",
+    "subscription_cancel": "Anulowano abonament ze skutkiem na koniec okresu",
+    "subscription_resume": "Wznowiono abonament",
+    "team_cancelInvitation": "Anulowano zaproszenie dla {email}",
+    "team_removeMember": "Usunięto członka zespołu {id}",
+    "team_sendInvitation": "Zaproszono {email} jako {role, select, owner {właściciela} admin {administratora} member {członka} other {{role}}}",
+    "team_updateRole": "Zmieniono rolę członka na {role, select, owner {właściciel} admin {administrator} member {członek} other {{role}}}",
+    "technician_create": "Utworzono mechanika „{name}”",
+    "technician_delete": "Usunięto mechanika {id}",
+    "technician_update": "Zaktualizowano mechanika {id}",
+    "telegram_send": "Wysłano wiadomość Telegram do {name}",
+    "tire_location_bulk_create": "Utworzono {count, plural, one {# miejsce na opony} few {# miejsca na opony} many {# miejsc na opony} other {# miejsca na opony}}",
+    "tire_location_create": "Utworzono miejsce na opony {code}",
+    "tire_location_delete": "Usunięto miejsce na opony {code}",
+    "tire_location_update": "Zaktualizowano miejsce na opony {code}",
+    "tire_measurement_create": "Zapisano {count, plural, one {# pomiar} few {# pomiary} many {# pomiarów} other {# pomiaru}} na zestawie opon {ref}",
+    "tire_set_add_to_work_order": "Dodano zestaw opon {ref} do zlecenia {jobRef}",
+    "tire_set_attach": "Dodano {count, plural, one {# plik} few {# pliki} many {# plików} other {# pliku}} do zestawu opon {ref}",
+    "tire_set_check_in": "Przyjęto zestaw opon {ref} na {code}",
+    "tire_set_check_out": "Wydano zestaw opon {ref}",
+    "tire_set_create_quote": "Utworzono wycenę {ref} z zestawu opon",
+    "tire_set_create_work_order": "Utworzono zlecenie warsztatowe {ref} z zestawu opon",
+    "tire_set_delete": "Usunięto zestaw opon {ref}",
+    "tire_set_detach": "Usunięto {fileName} z zestawu opon",
+    "tire_set_dispose": "Zezłomowano zestaw opon {ref}",
+    "tire_set_message_customer": "Powiadomiono klienta o zestawie opon {ref} przez {channel, select, email {e-mail} sms {SMS} telegram {Telegram} in_app {powiadomienie w aplikacji} other {{channel}}}",
+    "tire_set_relocate": "Przeniesiono zestaw opon {ref} z {from} na {to}",
+    "tire_set_relocateUnassigned": "Ustawiono zestaw opon {ref} na {to}",
+    "tire_set_return": "Ponownie przyjęto zestaw opon {ref}, na {code}",
+    "tire_set_unlink_work_order": "Odłączono zestaw opon {ref} od zlecenia {jobRef}",
+    "tire_set_update": "Zaktualizowano zestaw opon {ref}",
+    "tire_treatment_complete_all": "Ukończono {count, plural, one {# pracę przygotowawczą} few {# prace przygotowawcze} many {# prac przygotowawczych} other {# pracy przygotowawczej}} przy zestawie opon {ref}",
+    "tire_treatment_mark": "Oznaczono {type, select, wash_tires {mycie opon} wash_rims {mycie felg} balance {wyważanie} tpms_service {serwis TPMS} new_valves {nowe zawory} repair {naprawę} other {{type}}} jako {status, select, done {wykonane} pending {do zrobienia} skipped {pominięte} other {{status}}} przy zestawie opon {ref}",
+    "tire_treatment_update": "Zaktualizowano prace przygotowawcze przy zestawie opon {ref}",
+    "tire_warehouse_archive": "Zarchiwizowano magazyn opon {name}",
+    "tire_warehouse_create": "Utworzono magazyn opon {name}",
+    "tire_warehouse_delete": "Usunięto magazyn opon {name}",
+    "tire_warehouse_update": "Zaktualizowano magazyn opon {name}",
+    "vehicle_archive": "Zarchiwizowano pojazd {id}",
+    "vehicle_create": "Utworzono pojazd {year} {make} {model}",
+    "vehicle_delete": "Usunięto pojazd {name}",
+    "vehicle_unarchive": "Przywrócono pojazd {id} z archiwum",
+    "vehicle_update": "Zaktualizowano pojazd {name}, zmieniono: {fields}",
+    "vehicle_updateNoChanges": "Zaktualizowano pojazd {name}, nic nie zmieniono",
+    "webhook_create": "Utworzono webhook {name}",
+    "whatsapp_send": "Wysłano wiadomość WhatsApp do {name}"
   }
 }

+ 15 - 0
messages/pl/common.json

@@ -80,5 +80,20 @@
     "installDescription": "Dodaj do ekranu głównego, aby mieć szybki dostęp",
     "enterFullscreen": "Pełny ekran",
     "exitFullscreen": "Zamknij pełny ekran"
+  },
+  "broadcast": {
+    "dismiss": "Zamknij",
+    "title": "Komunikat dla wszystkich",
+    "description": "Wyświetlany na górze każdej strony, wszystkim, zalogowanym i nie. Zostaw puste, aby nic nie pokazywać.",
+    "message": "Komunikat",
+    "level": "Waga",
+    "post": "Opublikuj komunikat",
+    "clear": "Usuń komunikat",
+    "levelInfo": "Informacja",
+    "levelWarning": "Ostrzeżenie",
+    "levelCritical": "Krytyczny",
+    "placeholder": "np. Mamy problemy z infrastrukturą serwerową i pracujemy nad nimi.",
+    "posted": "Komunikat opublikowany",
+    "cleared": "Komunikat usunięty"
   }
 }

+ 6 - 2
messages/pl/customers.json

@@ -38,7 +38,8 @@
       "messages": "Wiadomości",
       "requests": "Zlecenia ({count})",
       "sms": "SMS",
-      "telegram": "Telegram"
+      "telegram": "Telegram",
+      "whatsapp": "WhatsApp"
     },
     "vehicleTable": {
       "plate": "Tablica",
@@ -68,7 +69,10 @@
     "customerCreated": "Klient utworzony",
     "saveError": "Nie udało się zapisać klienta",
     "customerNumber": "Nr klienta",
-    "customerNumberAuto": "Auto"
+    "customerNumberAuto": "Auto",
+    "sectionContact": "Kontakt",
+    "sectionBilling": "Rozliczenia",
+    "addScannedVehicle": "Dodaj pojazd {vehicle}"
   },
   "serviceRequests": {
     "statusNew": "Nowe",

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff