Просмотр исходного кода

Add i18n support with next-intl (#20)

* next intl

* customers

* locale

* customers and messages

* work-orders

* shared invoice and pdf

* gitignore

* settings page

* settings

* sidebar

* admin menu

* notifications

* qoutation page

* billing age

* work jobs

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

+ 1 - 0
.gitignore

@@ -16,6 +16,7 @@
 # next.js
 /.next/
 /out/
+.claude
 
 # production
 /build

+ 105 - 0
messages/de/admin.json

@@ -0,0 +1,105 @@
+{
+  "nav": {
+    "overview": { "title": "Übersicht", "description": "Plattformstatistiken & Zustand" },
+    "users": { "title": "Benutzer", "description": "Alle Benutzer verwalten" },
+    "organizations": { "title": "Organisationen", "description": "Alle Organisationen verwalten" },
+    "settings": { "title": "Einstellungen", "description": "Plattformkonfiguration" }
+  },
+  "layout": {
+    "title": "Admin-Panel",
+    "description": "Plattformverwaltung für Super-Administratoren"
+  },
+  "overview": {
+    "totalUsers": "Benutzer gesamt",
+    "organizations": "Organisationen",
+    "activeSubscriptions": "Aktive Abonnements",
+    "monthlyRevenue": "Monatlicher Umsatz"
+  },
+  "users": {
+    "searchPlaceholder": "Nach Name oder E-Mail suchen...",
+    "name": "Name",
+    "email": "E-Mail",
+    "role": "Rolle",
+    "orgs": "Orgs",
+    "created": "Erstellt",
+    "lastSeen": "Zuletzt gesehen",
+    "noResults": "Keine Benutzer entsprechen Ihrer Suche.",
+    "noUsers": "Keine Benutzer gefunden.",
+    "superAdmin": "Super-Admin",
+    "user": "Benutzer",
+    "online": "Online",
+    "promoteTitle": "Benutzer befördern",
+    "demoteTitle": "Benutzer herabstufen",
+    "promoteConfirm": "Sind Sie sicher, dass Sie \"{name}\" zum Super-Admin befördern möchten?",
+    "demoteConfirm": "Sind Sie sicher, dass Sie \"{name}\" als Super-Admin herabstufen möchten?",
+    "promote": "Befördern",
+    "demote": "Herabstufen",
+    "promotedSuccess": "Benutzer erfolgreich befördert",
+    "demotedSuccess": "Benutzer erfolgreich herabgestuft",
+    "failedUpdate": "Benutzer konnte nicht aktualisiert werden",
+    "deleteTitle": "Benutzer löschen",
+    "deleteConfirm": "Sind Sie sicher, dass Sie \"{name}\" ({email}) dauerhaft löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
+    "delete": "Löschen",
+    "deletedSuccess": "Benutzer erfolgreich gelöscht",
+    "failedDelete": "Benutzer konnte nicht gelöscht werden",
+    "promoteToSuperAdmin": "Zum Super-Admin befördern",
+    "demoteFromSuperAdmin": "Vom Super-Admin herabstufen",
+    "deleteUser": "Benutzer löschen"
+  },
+  "organizations": {
+    "searchPlaceholder": "Nach Organisationsname suchen...",
+    "name": "Name",
+    "owner": "Eigentümer",
+    "members": "Mitglieder",
+    "subscription": "Abonnement",
+    "created": "Erstellt",
+    "noResults": "Keine Organisationen entsprechen Ihrer Suche.",
+    "noOrganizations": "Keine Organisationen gefunden.",
+    "none": "Keine",
+    "deleteTitle": "Organisation löschen",
+    "deleteConfirm": "Sind Sie sicher, dass Sie \"{name}\" und alle zugehörigen Daten dauerhaft löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
+    "delete": "Löschen",
+    "deletedSuccess": "Organisation erfolgreich gelöscht",
+    "failedDelete": "Organisation konnte nicht gelöscht werden",
+    "deleteOrganization": "Organisation löschen"
+  },
+  "adminSettings": {
+    "platformTitle": "Plattform-Einstellungen",
+    "platformDescription": "Allgemeine Plattformkonfigurationsoptionen",
+    "disableRegistration": "Registrierung deaktivieren",
+    "disableRegistrationHint": "Neue Benutzerregistrierungen auf der Plattform blockieren",
+    "emailTitle": "E-Mail-Einstellungen",
+    "emailDescription": "Plattform-E-Mail-Anbieter — Wird für systemweite E-Mails wie Passwortzurücksetzungen und Teameinladungen verwendet. Dies ist auch der Standard-E-Mail-Anbieter für alle Organisationen. Organisationen können optional ihren eigenen E-Mail-Anbieter unter Einstellungen > E-Mail konfigurieren.",
+    "smtpHost": "SMTP-Host",
+    "smtpPort": "SMTP-Port",
+    "username": "Benutzername",
+    "password": "Passwort",
+    "fromEmail": "Absender-E-Mail",
+    "fromName": "Absendername",
+    "tlsConnection": "TLS-Verbindung (Port 465)",
+    "tlsConnectionHint": "Aktivieren für implizites TLS. Deaktivieren für STARTTLS (Port 587/25).",
+    "verifyTls": "TLS-Zertifikate überprüfen",
+    "verifyTlsHint": "Deaktivieren, um selbstsignierte Zertifikate zuzulassen (nicht empfohlen für Produktion)",
+    "requireTlsUpgrade": "TLS-Upgrade erforderlich",
+    "requireTlsUpgradeHint": "TLS-Upgrade bei STARTTLS-Verbindungen erzwingen",
+    "apiKey": "API-Schlüssel",
+    "serverToken": "Server-Token",
+    "domain": "Domain",
+    "region": "Region",
+    "awsRegion": "AWS-Region",
+    "accessKeyId": "Zugriffsschlüssel-ID",
+    "secretAccessKey": "Geheimer Zugriffsschlüssel",
+    "awsRegionHint": "Die AWS-Region, in der SES konfiguriert ist (z.B. us-east-1, eu-west-1)",
+    "sendTestEmail": "Test-E-Mail senden",
+    "testEmailHint": "Einstellungen zuerst speichern, dann eine Test-E-Mail an Ihr Konto senden",
+    "saveSettings": "Einstellungen speichern",
+    "saved": "Einstellungen erfolgreich gespeichert",
+    "failedSave": "Einstellungen konnten nicht gespeichert werden",
+    "testSentTo": "Test-E-Mail gesendet an {email}",
+    "testFailed": "E-Mail-Test fehlgeschlagen",
+    "resendApiKeyHint": "Holen Sie Ihren API-Schlüssel von resend.com",
+    "postmarkApiKeyHint": "Holen Sie Ihren Server-Token von postmarkapp.com",
+    "mailgunApiKeyHint": "Holen Sie Ihren API-Schlüssel von app.mailgun.com",
+    "sendgridApiKeyHint": "Holen Sie Ihren API-Schlüssel von app.sendgrid.com"
+  }
+}

+ 73 - 0
messages/de/auth.json

@@ -0,0 +1,73 @@
+{
+  "signIn": {
+    "title": "Willkommen zurück",
+    "description": "Melden Sie sich in Ihrer Werkstatt an",
+    "passwordPlaceholder": "Passwort eingeben",
+    "forgotPassword": "Passwort vergessen?",
+    "noAccount": "Noch kein Konto?",
+    "createOne": "Jetzt erstellen",
+    "termsAgreement": "Durch die Nutzung dieses Dienstes stimmen Sie unseren",
+    "errors": {
+      "tooManyAttempts": "Zu viele Versuche, bitte versuchen Sie es später erneut.",
+      "invalidCredentials": "Ungültige E-Mail-Adresse oder ungültiges Passwort"
+    }
+  },
+  "signUp": {
+    "title": "Konto erstellen",
+    "descriptionInvite": "Erstellen Sie Ihr Konto, um dem Team beizutreten",
+    "descriptionDefault": "Richten Sie Ihre Werkstatt ein, um Kundenfahrzeuge zu verwalten",
+    "fullName": "Vollständiger Name",
+    "fullNamePlaceholder": "Max Mustermann",
+    "passwordPlaceholder": "Sicheres Passwort erstellen",
+    "agreeToTerms": "Ich stimme den",
+    "createAccountJoin": "Konto erstellen & Team beitreten",
+    "createAccount": "Konto erstellen",
+    "alreadyHaveAccount": "Bereits ein Konto?",
+    "errors": {
+      "couldNotCreate": "Konto konnte nicht erstellt werden",
+      "registrationDisabled": "Die Registrierung ist derzeit deaktiviert. Bitte wenden Sie sich an Ihren Administrator.",
+      "invitationFailed": "Einladung konnte nicht angenommen werden"
+    }
+  },
+  "forgotPassword": {
+    "title": "Passwort zurücksetzen",
+    "description": "Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen Link zum Zurücksetzen",
+    "sendResetLink": "Link zum Zurücksetzen senden",
+    "backToSignIn": "Zurück zur Anmeldung",
+    "successMessage": "Falls ein Konto mit dieser E-Mail-Adresse existiert, wurde ein Link zum Zurücksetzen gesendet. Überprüfen Sie Ihren Posteingang."
+  },
+  "resetPassword": {
+    "title": "Neues Passwort festlegen",
+    "description": "Geben Sie unten Ihr neues Passwort ein",
+    "newPassword": "Neues Passwort",
+    "newPasswordPlaceholder": "Neues Passwort eingeben",
+    "confirmPassword": "Passwort bestätigen",
+    "confirmPasswordPlaceholder": "Neues Passwort bestätigen",
+    "resetButton": "Passwort zurücksetzen",
+    "backToSignIn": "Zurück zur Anmeldung",
+    "requestNewLink": "Neuen Link anfordern",
+    "invalidToken": "Ungültiger oder fehlender Token zum Zurücksetzen. Bitte fordern Sie einen neuen Link zum Zurücksetzen des Passworts an.",
+    "successMessage": "Ihr Passwort wurde erfolgreich zurückgesetzt. Sie können sich jetzt mit Ihrem neuen Passwort anmelden.",
+    "errors": {
+      "passwordsMismatch": "Die Passwörter stimmen nicht überein.",
+      "passwordTooShort": "Das Passwort muss mindestens 8 Zeichen lang sein.",
+      "resetFailed": "Das Passwort konnte nicht zurückgesetzt werden. Der Link ist möglicherweise abgelaufen."
+    }
+  },
+  "verify2fa": {
+    "title": "Zwei-Faktor-Authentifizierung",
+    "descriptionTotp": "Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein",
+    "descriptionBackup": "Geben Sie einen Ihrer Backup-Codes ein",
+    "authCode": "Authentifizierungscode",
+    "backupCode": "Backup-Code",
+    "backupCodePlaceholder": "Backup-Code eingeben",
+    "totpPlaceholder": "000000",
+    "trustDevice": "Diesem Gerät 30 Tage vertrauen",
+    "verify": "Bestätigen",
+    "useAuthenticator": "Stattdessen Authenticator-App verwenden",
+    "useBackupCode": "Backup-Code verwenden",
+    "errors": {
+      "invalidCode": "Ungültiger Code"
+    }
+  }
+}

+ 83 - 0
messages/de/billing.json

@@ -0,0 +1,83 @@
+{
+  "history": {
+    "title": "Abrechnungsverlauf",
+    "recurring": "Wiederkehrend",
+    "totalRevenue": "Gesamtumsatz",
+    "collected": "Eingezogen",
+    "outstanding": "Ausstehend",
+    "statusAll": "Alle",
+    "statusPaid": "Bezahlt",
+    "statusPartial": "Teilweise",
+    "statusUnpaid": "Unbezahlt",
+    "searchPlaceholder": "Rechnungen suchen...",
+    "search": "Suchen",
+    "columnInvoice": "Rechnung Nr.",
+    "columnTitle": "Titel",
+    "columnVehicle": "Fahrzeug",
+    "columnCustomer": "Kunde",
+    "columnDate": "Datum",
+    "columnTotal": "Gesamt",
+    "columnPaid": "Bezahlt",
+    "columnBalance": "Saldo",
+    "noRecords": "Keine Abrechnungseinträge gefunden."
+  },
+  "recurring": {
+    "title": "Wiederkehrend",
+    "billingHistory": "Abrechnungsverlauf",
+    "processNow": "Jetzt verarbeiten",
+    "newRecurring": "Neue wiederkehrende Rechnung",
+    "noInvoices": "Noch keine wiederkehrenden Rechnungen.",
+    "columnTitle": "Titel",
+    "columnVehicle": "Fahrzeug",
+    "columnCustomer": "Kunde",
+    "columnFrequency": "Häufigkeit",
+    "columnNextRun": "Nächste Ausführung",
+    "columnAmount": "Betrag",
+    "columnStatus": "Status",
+    "columnRuns": "Ausführungen",
+    "statusActive": "Aktiv",
+    "statusPaused": "Pausiert",
+    "frequencyWeekly": "Wöchentlich",
+    "frequencyBiweekly": "Zweiwöchentlich",
+    "frequencyMonthly": "Monatlich",
+    "frequencyQuarterly": "Vierteljährlich",
+    "frequencyYearly": "Jährlich",
+    "serviceTypeMaintenance": "Wartung",
+    "serviceTypeRepair": "Reparatur",
+    "serviceTypeInspection": "Inspektion",
+    "serviceTypeUpgrade": "Aufrüstung",
+    "dialogTitle": "Neue wiederkehrende Rechnung",
+    "titleLabel": "Titel *",
+    "titlePlaceholder": "z. B. Monatlicher Ölwechsel",
+    "vehicleLabel": "Fahrzeug *",
+    "selectVehicle": "Fahrzeug auswählen",
+    "descriptionLabel": "Beschreibung",
+    "frequencyLabel": "Häufigkeit",
+    "startDate": "Startdatum *",
+    "endDate": "Enddatum",
+    "serviceType": "Servicetyp",
+    "baseCost": "Grundkosten",
+    "taxRate": "Steuersatz (%)",
+    "invoiceNotes": "Rechnungsnotizen",
+    "templateParts": "Vorlageteile",
+    "addPart": "Teil hinzufügen",
+    "partName": "Teilename",
+    "partNumber": "Teile-Nr.",
+    "templateLabor": "Vorlagearbeit",
+    "addLabor": "Arbeit hinzufügen",
+    "description": "Beschreibung",
+    "hours": "Stunden",
+    "rate": "Satz",
+    "cancel": "Abbrechen",
+    "create": "Erstellen",
+    "requiredFields": "Titel und Fahrzeug sind erforderlich",
+    "created": "Wiederkehrende Rechnung erstellt",
+    "failedCreate": "Erstellen fehlgeschlagen",
+    "statusUpdated": "Status aktualisiert",
+    "failedUpdate": "Aktualisierung fehlgeschlagen",
+    "deleted": "Wiederkehrende Rechnung gelöscht",
+    "failedDelete": "Löschen fehlgeschlagen",
+    "processed": "{count} Rechnung(en) verarbeitet",
+    "failedProcess": "Verarbeitung fehlgeschlagen"
+  }
+}

+ 61 - 0
messages/de/calendar.json

@@ -0,0 +1,61 @@
+{
+  "weekdays": {
+    "sun": "So",
+    "mon": "Mo",
+    "tue": "Di",
+    "wed": "Mi",
+    "thu": "Do",
+    "fri": "Fr",
+    "sat": "Sa"
+  },
+  "months": {
+    "january": "Januar",
+    "february": "Februar",
+    "march": "M\u00e4rz",
+    "april": "April",
+    "may": "Mai",
+    "june": "Juni",
+    "july": "Juli",
+    "august": "August",
+    "september": "September",
+    "october": "Oktober",
+    "november": "November",
+    "december": "Dezember"
+  },
+  "stats": {
+    "services": "Auftr\u00e4ge",
+    "overdue": "\u00dcberf\u00e4llig",
+    "pendingQuotes": "Offene Angebote",
+    "revenue": "Umsatz"
+  },
+  "today": "Heute",
+  "workOrder": "Arbeitsauftrag",
+  "filters": {
+    "services": "Auftr\u00e4ge",
+    "reminders": "Erinnerungen",
+    "quotes": "Angebote"
+  },
+  "selectVehicle": "Fahrzeug f\u00fcr Arbeitsauftrag ausw\u00e4hlen",
+  "more": "+ {count} weitere",
+  "events": {
+    "status": {
+      "completed": "Abgeschlossen",
+      "inProgress": "In Bearbeitung",
+      "waitingParts": "Warte auf Teile",
+      "pending": "Ausstehend",
+      "sent": "Gesendet",
+      "approved": "Genehmigt",
+      "draft": "Entwurf",
+      "done": "Erledigt",
+      "overdue": "\u00dcberf\u00e4llig",
+      "upcoming": "Bevorstehend"
+    },
+    "type": {
+      "service": "Service",
+      "reminder": "Erinnerung",
+      "quote": "Angebot"
+    },
+    "noEvents": "Keine Ereignisse",
+    "selectDay": "W\u00e4hlen Sie einen Tag mit Ereignissen oder erstellen Sie einen neuen Arbeitsauftrag."
+  }
+}

+ 43 - 0
messages/de/common.json

@@ -0,0 +1,43 @@
+{
+  "brandName": "Torqvoice",
+  "buttons": {
+    "signIn": "Anmelden",
+    "save": "Speichern",
+    "cancel": "Abbrechen",
+    "delete": "Löschen",
+    "submit": "Absenden",
+    "back": "Zurück",
+    "confirm": "Bestätigen",
+    "saveChanges": "Änderungen speichern",
+    "edit": "Bearbeiten",
+    "add": "Hinzufügen"
+  },
+  "form": {
+    "email": "E-Mail",
+    "password": "Passwort",
+    "emailPlaceholder": "you@example.com",
+    "name": "Name",
+    "phone": "Telefon",
+    "company": "Firma",
+    "address": "Adresse",
+    "notes": "Notizen"
+  },
+  "errors": {
+    "unexpected": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
+    "error": "Fehler"
+  },
+  "terms": {
+    "termsOfService": "Nutzungsbedingungen"
+  },
+  "errorPage": {
+    "title": "Etwas ist schiefgelaufen",
+    "description": "Der Motor ist unerwartet abgewürgt. Unsere Mechaniker wurden benachrichtigt und arbeiten bereits daran.",
+    "tryAgain": "Erneut versuchen",
+    "backToDashboard": "Zurück zum Dashboard"
+  },
+  "notFoundPage": {
+    "title": "Seite nicht gefunden",
+    "description": "Sieht aus, als wäre dieses Teil noch in der Werkstatt. Die Seite, die Sie suchen, existiert nicht oder wurde verschoben.",
+    "backToDashboard": "Zurück zum Dashboard"
+  }
+}

+ 76 - 0
messages/de/customers.json

@@ -0,0 +1,76 @@
+{
+  "list": {
+    "addCustomer": "Kunde hinzufügen",
+    "searchPlaceholder": "Kunden suchen...",
+    "table": {
+      "name": "Name",
+      "company": "Unternehmen",
+      "phone": "Telefon",
+      "email": "E-Mail",
+      "vehicles": "Fahrzeuge"
+    },
+    "emptySearch": "Keine Kunden entsprechen Ihrer Suche.",
+    "empty": "Noch keine Kunden vorhanden.",
+    "deleteTitle": "Kunde löschen",
+    "deleteDescription": "{name} löschen? Die zugehörigen Fahrzeuge werden dabei entknüpft.",
+    "deleteError": "Kunde konnte nicht gelöscht werden",
+    "error": "Kunden konnten nicht geladen werden"
+  },
+  "detail": {
+    "backToCustomers": "Zurück zur Kundenliste",
+    "editCustomer": "Kunde bearbeiten",
+    "notFound": "Kunde nicht gefunden",
+    "emailCopied": "E-Mail in die Zwischenablage kopiert",
+    "phoneCopied": "Telefonnummer in die Zwischenablage kopiert",
+    "tabs": {
+      "vehicles": "Fahrzeuge ({count})",
+      "messages": "Nachrichten",
+      "requests": "Anfragen ({count})"
+    },
+    "vehicleTable": {
+      "plate": "Kennzeichen",
+      "vehicle": "Fahrzeug",
+      "mileage": "Kilometerstand",
+      "services": "Wartungen"
+    },
+    "noVehicles": "Diesem Kunden sind keine Fahrzeuge zugewiesen"
+  },
+  "form": {
+    "addTitle": "Neuen Kunden anlegen",
+    "editTitle": "Kunde bearbeiten",
+    "nameRequired": "Name *",
+    "namePlaceholder": "Max Mustermann",
+    "emailPlaceholder": "max@beispiel.de",
+    "phonePlaceholder": "+49 151 12345678",
+    "companyPlaceholder": "Firmenname",
+    "addressPlaceholder": "Musterstraße 1, 12345 Musterstadt",
+    "notesPlaceholder": "Weitere Anmerkungen...",
+    "customerUpdated": "Kunde aktualisiert",
+    "customerCreated": "Kunde angelegt",
+    "saveError": "Kunde konnte nicht gespeichert werden"
+  },
+  "serviceRequests": {
+    "statusNew": "Neu",
+    "statusConverted": "Arbeitsauftrag erstellt",
+    "statusDismissed": "Abgelehnt",
+    "createWorkOrder": "Arbeitsauftrag erstellen",
+    "contactCustomer": "Kunde kontaktieren",
+    "dismiss": "Ablehnen",
+    "viewWorkOrder": "Arbeitsauftrag anzeigen",
+    "notes": "Notizen",
+    "hideNotes": "Notizen ausblenden",
+    "staffNotes": "Mitarbeiternotizen:",
+    "notesPlaceholder": "Interne Notizen hinzufügen...",
+    "saveNotes": "Notizen speichern",
+    "submitted": "Eingereicht am {date}",
+    "preferred": "Gewünscht: {date}",
+    "workOrderCreated": "Arbeitsauftrag erstellt",
+    "workOrderError": "Arbeitsauftrag konnte nicht erstellt werden",
+    "requestDismissed": "Anfrage abgelehnt",
+    "dismissError": "Anfrage konnte nicht abgelehnt werden",
+    "notesSaved": "Notizen gespeichert",
+    "notesSaveError": "Notizen konnten nicht gespeichert werden",
+    "smsLabel": "Serviceanfrage-Nachricht",
+    "smsDefault": "Hallo {name}, bezüglich Ihrer Serviceanfrage für Ihr Fahrzeug {vehicle}: "
+  }
+}

+ 89 - 0
messages/de/dashboard.json

@@ -0,0 +1,89 @@
+{
+  "stats": {
+    "activeJobs": "Aktive Aufträge",
+    "pending": "Ausstehend",
+    "todaysRevenue": "Heutiger Umsatz",
+    "totalCustomers": "Kunden gesamt"
+  },
+  "customize": "Anpassen",
+  "showCards": "Karten anzeigen",
+  "cards": {
+    "maintenance": "Voraussichtliche Wartung",
+    "reminders": "Anstehende Erinnerungen",
+    "inspections": "Inspektionen",
+    "quoteRequests": "Angebotsanfragen",
+    "quoteResponses": "Angebotsantworten",
+    "sms": "Letzte Nachrichten",
+    "notifications": "Letzte Benachrichtigungen",
+    "recentCompleted": "Kürzlich abgeschlossen",
+    "activeJobs": "Aktive Aufträge"
+  },
+  "maintenance": {
+    "title": "Voraussichtlich fällige Wartung",
+    "description": "Basierend auf der Servicehistorie und der geschätzten täglichen Fahrleistung",
+    "overdue": "Überfällig",
+    "approaching": "Bevorstehend",
+    "estimated": "Gesch. {mileage} {unit}",
+    "sinceLastService": "{mileage} {unit} seit letztem Service",
+    "certainty": "{percent}% Sicherheit"
+  },
+  "reminders": {
+    "title": "Anstehende Erinnerungen",
+    "overdue": "Überfällig",
+    "dueSoon": "Bald fällig"
+  },
+  "messages": {
+    "title": "Letzte Nachrichten",
+    "viewAll": "Alle anzeigen",
+    "you": "Sie: "
+  },
+  "notifications": {
+    "title": "Letzte Benachrichtigungen"
+  },
+  "inspections": {
+    "title": "Inspektionen",
+    "inProgress": "In Bearbeitung",
+    "completed": "Abgeschlossen",
+    "itemCount": "{inspected}/{total} Positionen"
+  },
+  "quoteRequests": {
+    "title": "Angebotsanfragen",
+    "description": "Kunden, die Angebote aus geteilten Inspektionen anfordern",
+    "pending": "Ausstehend",
+    "createQuote": "Angebot erstellen",
+    "itemsRequested": "{count, plural, one {# Position angefragt} other {# Positionen angefragt}}"
+  },
+  "quoteResponses": {
+    "title": "Angebotsantworten der Kunden",
+    "description": "Angebote, die von Kunden angenommen oder bei denen Änderungen gewünscht wurden",
+    "accepted": "Angenommen",
+    "changesRequested": "Änderungen gewünscht",
+    "convertToWorkOrder": "In Arbeitsauftrag umwandeln",
+    "viewQuote": "Angebot anzeigen"
+  },
+  "recentCompleted": {
+    "title": "Kürzlich abgeschlossen",
+    "date": "Datum",
+    "vehicle": "Fahrzeug",
+    "customer": "Kunde",
+    "serviceTitle": "Bezeichnung",
+    "total": "Gesamt",
+    "empty": "Noch keine abgeschlossenen Services"
+  },
+  "activeJobsTable": {
+    "title": "Aktive Aufträge",
+    "vehicle": "Fahrzeug",
+    "customer": "Kunde",
+    "serviceTitle": "Bezeichnung",
+    "status": "Status",
+    "tech": "Techniker",
+    "empty": "Keine aktiven Aufträge"
+  },
+  "relativeTime": {
+    "now": "jetzt",
+    "minutes": "{count}m",
+    "hours": "{count}h",
+    "days": "{count}T"
+  },
+  "error": "Dashboard konnte nicht geladen werden"
+}

+ 87 - 0
messages/de/inventory.json

@@ -0,0 +1,87 @@
+{
+  "searchPlaceholder": "Teile suchen...",
+  "categoryPlaceholder": "Kategorie",
+  "allCategories": "Alle Kategorien",
+  "applyMarkup": "Aufschlag anwenden",
+  "addPart": "Teil hinzufügen",
+  "table": {
+    "partNumber": "Teilenr.",
+    "name": "Name",
+    "category": "Kategorie",
+    "inStock": "Auf Lager",
+    "unitCost": "Stückkosten",
+    "sellPrice": "Verkaufspreis",
+    "supplier": "Lieferant",
+    "location": "Lagerort",
+    "low": "Niedrig"
+  },
+  "empty": {
+    "noMatch": "Keine Teile entsprechen Ihrer Suche.",
+    "noParts": "Noch keine Teile im Lager."
+  },
+  "actions": {
+    "edit": "Bearbeiten",
+    "delete": "Löschen"
+  },
+  "deletePart": {
+    "title": "Teil löschen",
+    "description": "\"{name}\" aus dem Lager löschen? Dies kann nicht rückgängig gemacht werden.",
+    "confirm": "Löschen"
+  },
+  "markup": {
+    "title": "Aufschlag auf alle Teile anwenden",
+    "multiplierLabel": "Aufschlagsfaktor",
+    "description": "Der Verkaufspreis wird auf Kosten x {multiplier} für alle {count} Teile gesetzt.",
+    "percentage": "({percent}% Aufschlag)",
+    "cancel": "Abbrechen",
+    "apply": "Anwenden"
+  },
+  "errors": {
+    "multiplierPositive": "Der Multiplikator muss größer als 0 sein",
+    "deleteFailed": "Teil konnte nicht gelöscht werden",
+    "applyFailed": "Aufschlag konnte nicht angewendet werden",
+    "saveFailed": "Teil konnte nicht gespeichert werden",
+    "error": "Fehler"
+  },
+  "form": {
+    "editPart": "Teil bearbeiten",
+    "addNewPart": "Neues Teil hinzufügen",
+    "supplierLink": "Lieferantenlink",
+    "supplierLinkPlaceholder": "https://supplier.com/product/...",
+    "fetch": "Abrufen",
+    "fetchFailed": "Abruf fehlgeschlagen",
+    "fetchError": "Metadaten konnten nicht von der URL abgerufen werden",
+    "image": "Bild",
+    "dropOrClick": "Bild ablegen oder klicken",
+    "replace": "Ersetzen",
+    "upload": "Hochladen",
+    "imageUploading": "Bild wird hochgeladen...",
+    "imageUploaded": "Bild hochgeladen",
+    "imageUploadFailed": "Bild konnte nicht hochgeladen werden",
+    "imageTypeError": "Nur JPEG-, PNG-, WebP- und AVIF-Bilder sind erlaubt",
+    "imageSizeError": "Das Bild darf maximal 5 MB groß sein",
+    "partNumber": "Teilenummer",
+    "partNumberPlaceholder": "BRK-001",
+    "nameLabel": "Name *",
+    "namePlaceholder": "Bremsbelagsatz",
+    "category": "Kategorie",
+    "categoryPlaceholder": "Bremsen",
+    "location": "Lagerort",
+    "locationPlaceholder": "Regal A-3",
+    "quantity": "Menge",
+    "minQty": "Mindestmenge",
+    "unitCost": "Stückkosten",
+    "sellPrice": "Verkaufspreis",
+    "supplier": "Lieferant",
+    "supplierPlaceholder": "AutoParts GmbH",
+    "supplierPhone": "Lieferanten-Telefon",
+    "supplierPhonePlaceholder": "(555) 123-4567",
+    "supplierEmail": "Lieferanten-E-Mail",
+    "supplierEmailPlaceholder": "orders@autoparts.com",
+    "description": "Beschreibung",
+    "descriptionPlaceholder": "Teilebeschreibung...",
+    "cancel": "Abbrechen",
+    "saveChanges": "Änderungen speichern",
+    "addPart": "Teil hinzufügen"
+  }
+}

+ 45 - 0
messages/de/messages.json

@@ -0,0 +1,45 @@
+{
+  "page": {
+    "featureName": "SMS-Nachrichten",
+    "featureDescription": "Senden und empfangen Sie SMS-Nachrichten mit Ihren Kunden direkt über Torqvoice."
+  },
+  "notConfigured": {
+    "title": "SMS nicht konfiguriert",
+    "description": "Richten Sie einen SMS-Anbieter ein, um Nachrichten mit Ihren Kunden zu senden und zu empfangen. Unterstützt Twilio, Vonage und Telnyx.",
+    "goToSettings": "Zu den SMS-Einstellungen"
+  },
+  "threads": {
+    "title": "Unterhaltungen",
+    "empty": "Noch keine Unterhaltungen. Senden Sie eine SMS von einer Kundenseite, um zu beginnen.",
+    "loadMore": "Mehr laden",
+    "selectConversation": "Unterhaltung auswählen",
+    "you": "Sie: ",
+    "deleteConversation": "Unterhaltung löschen",
+    "deleteTitle": "Unterhaltung löschen?",
+    "deleteDescription": "Alle Nachrichten mit {name} werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
+    "deleted": "Unterhaltung gelöscht",
+    "deleteError": "Unterhaltung konnte nicht gelöscht werden"
+  },
+  "conversation": {
+    "noPhone": "Keine Telefonnummer für {name} hinterlegt. Fügen Sie eine Telefonnummer hinzu, um Nachrichten zu senden.",
+    "loadOlder": "Ältere laden",
+    "empty": "Noch keine Nachrichten. Senden Sie die erste Nachricht an {name}.",
+    "placeholder": "Nachricht an {name}...",
+    "deleteMessage": "Nachricht löschen",
+    "deleteTitle": "Nachricht löschen?",
+    "deleteDescription": "Diese Nachricht wird dauerhaft aus der Unterhaltung gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
+    "messageDeleted": "Nachricht gelöscht",
+    "deleteError": "Nachricht konnte nicht gelöscht werden",
+    "sendError": "SMS konnte nicht gesendet werden"
+  },
+  "sendDialog": {
+    "send": "{label} senden",
+    "message": "Nachricht",
+    "placeholder": "Nachricht eingeben...",
+    "characters": "{count} / 1600 Zeichen",
+    "sendSms": "SMS senden",
+    "smsSent": "SMS gesendet",
+    "sentTo": "{label} wurde an {name} ({phone}) gesendet",
+    "sendError": "SMS konnte nicht gesendet werden"
+  }
+}

+ 85 - 0
messages/de/navigation.json

@@ -0,0 +1,85 @@
+{
+  "breadcrumbs": {
+    "dashboard": "Dashboard",
+    "vehicles": "Fahrzeuge",
+    "allVehicles": "Alle Fahrzeuge",
+    "vehicleDetails": "Fahrzeugdetails",
+    "customers": "Kunden",
+    "allCustomers": "Alle Kunden",
+    "customerDetails": "Kundendetails",
+    "workOrders": "Arbeitsaufträge",
+    "allWorkOrders": "Alle Arbeitsaufträge",
+    "quotes": "Angebote",
+    "allQuotes": "Alle Angebote",
+    "newQuote": "Neues Angebot",
+    "quoteDetails": "Angebotsdetails",
+    "edit": "Bearbeiten",
+    "billing": "Abrechnung",
+    "billingHistory": "Abrechnungsverlauf",
+    "inventory": "Lagerbestand",
+    "allParts": "Alle Teile",
+    "reports": "Berichte",
+    "workBoard": "Auftragsboard",
+    "presenter": "Präsentation",
+    "adminOverview": "Admin-Übersicht",
+    "admin": "Admin",
+    "users": "Benutzer",
+    "organizations": "Organisationen",
+    "settings": "Einstellungen",
+    "company": "Unternehmen",
+    "account": "Konto",
+    "customFields": "Benutzerdefinierte Felder",
+    "templates": "Vorlagen",
+    "team": "Team",
+    "invoice": "Rechnung",
+    "payment": "Zahlung",
+    "currency": "Währung",
+    "workshop": "Werkstatt",
+    "appearance": "Erscheinungsbild",
+    "email": "E-Mail",
+    "sms": "SMS",
+    "about": "Über",
+    "home": "Startseite",
+    "newServiceRecord": "Neuer Serviceeintrag",
+    "serviceDetails": "Servicedetails",
+    "data": "Daten",
+    "license": "Lizenz",
+    "subscription": "Abonnement",
+    "maintenance": "Wartung",
+    "customerPortal": "Kundenportal"
+  },
+  "search": "Suchen...",
+  "shortcut": "Ctrl+K",
+  "newWorkOrder": "Neuer Arbeitsauftrag",
+  "purchaseWhiteLabel": "White-Label erwerben",
+  "sidebar": {
+    "dashboard": "Dashboard",
+    "clients": "Kunden",
+    "customers": "Kunden",
+    "messages": "Nachrichten",
+    "workshop": "Werkstatt",
+    "vehicles": "Fahrzeuge",
+    "workOrders": "Arbeitsaufträge",
+    "inspections": "Inspektionen",
+    "calendar": "Kalender",
+    "workBoard": "Auftragsboard",
+    "business": "Geschäft",
+    "quotes": "Angebote",
+    "billing": "Abrechnung",
+    "inventory": "Lagerbestand",
+    "reports": "Berichte",
+    "settings": "Einstellungen",
+    "superAdmin": "Super Admin",
+    "adminPanel": "Admin-Panel",
+    "organizations": "Organisationen",
+    "noOrganization": "Keine Organisation",
+    "addNewCompany": "Neues Unternehmen hinzufügen",
+    "companyName": "Unternehmensname",
+    "companyPlaceholder": "z.B. Müllers Autowerkstatt",
+    "cancel": "Abbrechen",
+    "createCompany": "Unternehmen erstellen",
+    "lightMode": "Heller Modus",
+    "darkMode": "Dunkler Modus",
+    "signOut": "Abmelden"
+  }
+}

+ 10 - 0
messages/de/notifications.json

@@ -0,0 +1,10 @@
+{
+  "panel": {
+    "title": "Benachrichtigungen",
+    "markAllRead": "Alle als gelesen markieren",
+    "empty": "Noch keine Benachrichtigungen",
+    "confirmDelete": "Sind Sie sicher?",
+    "delete": "Löschen",
+    "cancel": "Abbrechen"
+  }
+}

+ 99 - 0
messages/de/pdf.json

@@ -0,0 +1,99 @@
+{
+  "invoice": {
+    "title": "RECHNUNG",
+    "due": "Fällig: {date}",
+    "billTo": "Rechnungsempfänger",
+    "vehicle": "Fahrzeug",
+    "vin": "VIN: {vin}",
+    "plate": "Kennzeichen: {plate}",
+    "mileage": "Kilometerstand: {mileage}",
+    "km": "km",
+    "mi": "mi",
+    "service": "Service",
+    "type": "Typ: {type}",
+    "tech": "Techniker: {tech}",
+    "parts": "Teile",
+    "partNumber": "Teilenr.",
+    "description": "Beschreibung",
+    "qty": "Menge",
+    "unitPrice": "Einzelpreis",
+    "total": "Gesamt",
+    "labor": "Arbeitsleistung",
+    "hours": "Stunden",
+    "rate": "Stundensatz",
+    "ratePerHour": "{rate}/Std.",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "discountPercent": "Rabatt ({percent}%)",
+    "tax": "MwSt. ({rate}%)",
+    "paymentsReceived": "Eingegangene Zahlungen",
+    "paidInFull": "VOLLSTÄNDIG BEZAHLT",
+    "amountDue": "Fälliger Betrag",
+    "notes": "Hinweise",
+    "bankAccount": "Til Konto / Bankkonto",
+    "diagnosticNotes": "Diagnosehinweise",
+    "attachedDocuments": "Beigefügte Dokumente",
+    "seeAppendedPages": "{name} (siehe angehängte Seiten)",
+    "serviceImages": "Service-Bilder",
+    "paymentTerms": "Zahlungsbedingungen: {terms}",
+    "viewPortal": "Ihr Portal ansehen: {url}",
+    "professionalWorkshop": "Professionelle Werkstattverwaltung",
+    "tel": "Tel: {phone}",
+    "org": "Org: {org}"
+  },
+  "quote": {
+    "title": "ANGEBOT",
+    "to": "An",
+    "vehicle": "Fahrzeug",
+    "vin": "VIN: {vin}",
+    "plate": "Kennzeichen: {plate}",
+    "quoteDetails": "Angebotsdetails",
+    "parts": "Teile",
+    "partNumber": "Teilenr.",
+    "description": "Beschreibung",
+    "qty": "Menge",
+    "unitPrice": "Einzelpreis",
+    "total": "Gesamt",
+    "labor": "Arbeitsleistung",
+    "hours": "Stunden",
+    "rate": "Stundensatz",
+    "ratePerHour": "{rate}/Std.",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "discountPercent": "Rabatt ({percent}%)",
+    "tax": "MwSt. ({rate}%)",
+    "validUntil": "Gültig bis: {date}",
+    "attachedDocuments": "Beigefügte Dokumente",
+    "attached": "{name} (beigefügt)",
+    "quoteImages": "Angebotsbilder",
+    "validityFooterUntil": "Dieses Angebot ist gültig bis {date}",
+    "validityFooter30": "Dieses Angebot ist 30 Tage gültig",
+    "viewPortal": "Ihr Portal ansehen: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "FAHRZEUGINSPEKTION",
+    "vehicle": "Fahrzeug",
+    "vin": "VIN: {vin}",
+    "plate": "Kennzeichen: {plate}",
+    "mileage": "Kilometerstand: {mileage}",
+    "customer": "Kunde",
+    "inspectionDetails": "Inspektionsdetails",
+    "statusCompleted": "Abgeschlossen",
+    "statusInProgress": "In Bearbeitung",
+    "status": "Status: {status}",
+    "inspected": "Geprüft",
+    "pass": "Bestanden",
+    "attention": "Achtung",
+    "fail": "Nicht bestanden",
+    "item": "Position",
+    "statusColumn": "Status",
+    "notesColumn": "Hinweise",
+    "footerText": "Fahrzeuginspektion — {shopName}",
+    "viewPortal": "Ihr Portal ansehen: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "common": {
+    "poweredBy": "Bereitgestellt von"
+  }
+}

+ 133 - 0
messages/de/portal.json

@@ -0,0 +1,133 @@
+{
+  "login": {
+    "signInTitle": "Melden Sie sich bei Ihrem Kundenportal an",
+    "emailLabel": "E-Mail-Adresse",
+    "emailPlaceholder": "sie@beispiel.de",
+    "sendLink": "Anmeldelink senden",
+    "checkEmail": "Prüfen Sie Ihre E-Mails",
+    "checkEmailDescription": "Falls ein Konto für diese E-Mail-Adresse existiert, haben wir Ihnen einen Anmeldelink gesendet. Der Link läuft in 15 Minuten ab.",
+    "tryDifferentEmail": "Andere E-Mail-Adresse verwenden",
+    "notFound": "Nicht gefunden",
+    "portalNotExist": "Dieses Portal existiert nicht.",
+    "portalNotAvailable": "Portal nicht verfügbar",
+    "portalNotEnabled": "Das Kundenportal ist für diese Organisation nicht aktiviert."
+  },
+  "nav": {
+    "dashboard": "Dashboard",
+    "vehicles": "Fahrzeuge",
+    "invoices": "Rechnungen",
+    "quotes": "Angebote",
+    "inspections": "Inspektionen",
+    "requestService": "Service anfordern"
+  },
+  "header": {
+    "signOut": "Abmelden"
+  },
+  "shell": {
+    "notFound": "Nicht gefunden",
+    "portalNotExist": "Dieses Portal existiert nicht.",
+    "portalNotAvailable": "Portal nicht verfügbar",
+    "portalNotEnabled": "Das Kundenportal ist für diese Organisation nicht aktiviert."
+  },
+  "dashboard": {
+    "welcome": "Willkommen, {name}",
+    "overview": "Hier ist eine Übersicht Ihres Kontos.",
+    "vehicles": "Fahrzeuge",
+    "openInvoices": "Offene Rechnungen",
+    "pendingQuotes": "Ausstehende Angebote",
+    "recentInvoices": "Aktuelle Rechnungen",
+    "recentQuotes": "Aktuelle Angebote",
+    "pendingServiceRequests": "Ausstehende Serviceanfragen",
+    "viewAll": "Alle anzeigen",
+    "newRequest": "Neue Anfrage",
+    "noInvoicesYet": "Noch keine Rechnungen.",
+    "noQuotesYet": "Noch keine Angebote.",
+    "view": "Ansehen",
+    "pending": "Ausstehend",
+    "failedToLoad": "Dashboard konnte nicht geladen werden."
+  },
+  "invoices": {
+    "title": "Rechnungen",
+    "description": "Alle Ihre Service-Rechnungen.",
+    "noInvoices": "Noch keine Rechnungen.",
+    "failedToLoad": "Rechnungen konnten nicht geladen werden.",
+    "invoice": "Rechnung",
+    "vehicle": "Fahrzeug",
+    "date": "Datum",
+    "amount": "Betrag",
+    "payment": "Zahlung",
+    "view": "Ansehen",
+    "paid": "bezahlt",
+    "partial": "teilweise",
+    "unpaid": "unbezahlt"
+  },
+  "quotes": {
+    "title": "Angebote",
+    "description": "Angebote und Kostenvoranschläge für Ihre Fahrzeuge.",
+    "noQuotes": "Noch keine Angebote.",
+    "failedToLoad": "Angebote konnten nicht geladen werden.",
+    "quote": "Angebot",
+    "vehicle": "Fahrzeug",
+    "status": "Status",
+    "amount": "Betrag",
+    "validUntil": "Gültig bis",
+    "view": "Ansehen"
+  },
+  "inspections": {
+    "title": "Inspektionen",
+    "description": "Fahrzeuginspektionsberichte.",
+    "noInspections": "Noch keine Inspektionen.",
+    "failedToLoad": "Inspektionen konnten nicht geladen werden.",
+    "vehicle": "Fahrzeug",
+    "template": "Vorlage",
+    "status": "Status",
+    "date": "Datum",
+    "summary": "Zusammenfassung",
+    "view": "Ansehen",
+    "good": "{count} gut",
+    "fair": "{count} akzeptabel",
+    "poor": "{count} schlecht"
+  },
+  "vehicles": {
+    "title": "Fahrzeuge",
+    "description": "Ihre registrierten Fahrzeuge.",
+    "noVehicles": "Keine Fahrzeuge gefunden.",
+    "failedToLoad": "Fahrzeuge konnten nicht geladen werden.",
+    "plate": "Kennzeichen: {plate}",
+    "vin": "VIN: {vin}",
+    "serviceCount": "{count, plural, one {# Service} other {# Services}}",
+    "inspectionCount": "{count, plural, one {# Inspektion} other {# Inspektionen}}",
+    "serviceHistory": "Serviceverlauf ({count})",
+    "inspectionsTab": "Inspektionen ({count})",
+    "noServiceRecords": "Noch keine Serviceaufzeichnungen.",
+    "noInspections": "Noch keine Inspektionen.",
+    "vehicleNotFound": "Fahrzeug nicht gefunden.",
+    "completed": "Abgeschlossen {date}",
+    "good": "{count} gut",
+    "fair": "{count} akzeptabel",
+    "poor": "{count} schlecht",
+    "view": "Ansehen"
+  },
+  "requestService": {
+    "title": "Service anfordern",
+    "description": "Reichen Sie eine Serviceanfrage für eines Ihrer Fahrzeuge ein.",
+    "newServiceRequest": "Neue Serviceanfrage",
+    "formDescription": "Beschreiben Sie das Problem und wir melden uns bei Ihnen.",
+    "vehicleLabel": "Fahrzeug",
+    "vehiclePlaceholder": "Fahrzeug auswählen",
+    "descriptionLabel": "Problem beschreiben",
+    "descriptionPlaceholder": "Was stimmt mit Ihrem Fahrzeug nicht?",
+    "preferredDate": "Gewünschtes Datum",
+    "preferredDateOptional": "(optional)",
+    "submitRequest": "Anfrage senden",
+    "selectVehicle": "Bitte wählen Sie ein Fahrzeug aus",
+    "describeIssue": "Bitte beschreiben Sie das Problem",
+    "submitSuccess": "Serviceanfrage eingereicht",
+    "submitError": "Anfrage konnte nicht eingereicht werden",
+    "noVehicles": "Sie haben keine registrierten Fahrzeuge. Bitte kontaktieren Sie uns, um zuerst ein Fahrzeug hinzuzufügen.",
+    "yourRequests": "Ihre Serviceanfragen",
+    "failedToLoad": "Daten konnten nicht geladen werden.",
+    "staffNotes": "Mitarbeiterhinweise: {notes}",
+    "preferred": "Bevorzugt: {date}"
+  }
+}

+ 173 - 0
messages/de/quotes.json

@@ -0,0 +1,173 @@
+{
+  "list": {
+    "statusAll": "Alle",
+    "statusDraft": "Entwurf",
+    "statusSent": "Gesendet",
+    "statusAccepted": "Akzeptiert",
+    "statusRejected": "Abgelehnt",
+    "statusConverted": "Umgewandelt",
+    "searchPlaceholder": "Angebote suchen...",
+    "newQuote": "Neues Angebot",
+    "columnQuoteNumber": "Angebot Nr.",
+    "columnTitle": "Titel",
+    "columnCustomer": "Kunde",
+    "columnVehicle": "Fahrzeug",
+    "columnStatus": "Status",
+    "columnDate": "Datum",
+    "columnTotal": "Gesamt",
+    "noQuotes": "Keine Angebote gefunden."
+  },
+  "page": {
+    "quote": "Angebot",
+    "save": "Speichern",
+    "pdf": "PDF",
+    "email": "E-Mail",
+    "share": "Teilen",
+    "delete": "Löschen",
+    "deleteTitle": "Angebot löschen",
+    "deleteDescription": "Dieses Angebot wird dauerhaft gelöscht.",
+    "saved": "Angebot gespeichert",
+    "failedSave": "Angebot konnte nicht gespeichert werden",
+    "failedDelete": "Löschen fehlgeschlagen",
+    "failedPdf": "PDF konnte nicht erstellt werden",
+    "selectVehicle": "Bitte wählen Sie ein Fahrzeug",
+    "convertTitle": "Angebot in Arbeitsauftrag umwandeln",
+    "convertDescription": "Es wird ein neuer Serviceauftrag (Arbeitsauftrag) aus diesem Angebot erstellt. Wählen Sie ein Fahrzeug für den Arbeitsauftrag.",
+    "convert": "Umwandeln",
+    "convertToWorkOrder": "In Arbeitsauftrag umwandeln",
+    "cancel": "Abbrechen",
+    "failedConvert": "Umwandlung fehlgeschlagen",
+    "changesRequested": "Änderungen angefordert",
+    "quoteAccepted": "Angebot akzeptiert",
+    "markResolved": "Als erledigt markieren",
+    "responseResolved": "Kundenantwort erledigt",
+    "entityLabel": "Angebot",
+    "tabs": {
+      "details": "Details",
+      "images": "Bilder",
+      "documents": "Dokumente"
+    }
+  },
+  "form": {
+    "newQuote": "Neues Angebot",
+    "editQuote": "Angebot bearbeiten",
+    "cancel": "Abbrechen",
+    "createQuote": "Angebot erstellen",
+    "updateQuote": "Angebot aktualisieren",
+    "created": "Angebot erstellt",
+    "updated": "Angebot aktualisiert",
+    "failedCreate": "Angebot konnte nicht erstellt werden",
+    "failedUpdate": "Angebot konnte nicht aktualisiert werden"
+  },
+  "parts": {
+    "title": "Teile",
+    "addPart": "Teil hinzufügen",
+    "partNumber": "Teilenr.",
+    "name": "Name",
+    "namePlaceholder": "Name *",
+    "qty": "Menge",
+    "unitPrice": "Stückpreis",
+    "total": "Gesamt",
+    "subtotal": "Teile Zwischensumme: {amount}"
+  },
+  "labor": {
+    "title": "Arbeitszeit",
+    "addLabor": "Arbeitszeit hinzufügen",
+    "description": "Beschreibung",
+    "descriptionPlaceholder": "Beschreibung *",
+    "hours": "Stunden",
+    "rate": "Satz ({currency}/Std.)",
+    "total": "Gesamt",
+    "subtotal": "Arbeitszeit Zwischensumme: {amount}"
+  },
+  "notes": {
+    "title": "Notizen",
+    "public": "Öffentlich",
+    "internal": "Intern",
+    "publicPlaceholder": "Angebotsbeschreibung für den Kunden...",
+    "internalPlaceholder": "Interne Notizen (nicht für den Kunden sichtbar)...",
+    "publicHelper": "Wird auf dem Angebotsdokument angezeigt",
+    "internalHelper": "Nur für Ihr Team sichtbar"
+  },
+  "details": {
+    "title": "Angebotsdetails",
+    "titleLabel": "Titel *",
+    "titlePlaceholder": "Kostenvoranschlag Fahrzeugreparatur",
+    "status": "Status",
+    "statusDraft": "Entwurf",
+    "statusSent": "Gesendet",
+    "statusAccepted": "Akzeptiert",
+    "statusRejected": "Abgelehnt",
+    "validUntil": "Gültig bis",
+    "viewInspection": "Verknüpfte Inspektion anzeigen",
+    "vehicle": "Fahrzeug",
+    "customer": "Kunde",
+    "selectVehicle": "Fahrzeug auswählen...",
+    "selectCustomer": "Kunden auswählen...",
+    "none": "Keine"
+  },
+  "totals": {
+    "title": "Summen",
+    "parts": "Teile",
+    "labor": "Arbeitszeit",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "discountNone": "Kein",
+    "discountPercentage": "Prozentsatz",
+    "discountFixed": "Festbetrag",
+    "tax": "Steuer",
+    "total": "Gesamt"
+  },
+  "shareDialog": {
+    "title": "Angebot teilen",
+    "description": "Erstellen Sie einen öffentlichen Link, über den jeder dieses Angebot ansehen und herunterladen kann, ohne sich anzumelden.",
+    "notifyTitle": "{name} benachrichtigen",
+    "email": "E-Mail",
+    "noEmail": "(keine E-Mail hinterlegt)",
+    "sms": "SMS",
+    "noPhone": "(keine Telefonnummer hinterlegt)",
+    "sendNotification": "Benachrichtigung senden",
+    "revokeLink": "Link widerrufen",
+    "generateLink": "Öffentlichen Link erstellen",
+    "emailSent": "E-Mail gesendet",
+    "failedEmail": "E-Mail konnte nicht gesendet werden",
+    "smsSent": "SMS gesendet",
+    "failedSms": "SMS konnte nicht gesendet werden"
+  },
+  "images": {
+    "title": "Angebotsbilder",
+    "dropzone": "Bilder hier ablegen oder klicken zum Durchsuchen",
+    "uploading": "Wird hochgeladen...",
+    "formats": "JPG, PNG, WebP — max. 10 MB pro Bild",
+    "uploadingImages": "Bilder werden hochgeladen...",
+    "limitReached": "Bilderlimit erreicht ({count}/{max})",
+    "upgradePrompt": "Upgraden Sie Ihren Plan, um mehr Bilder hochzuladen.",
+    "onlyAllowed": "Nur JPG, PNG und WebP Bilder sind erlaubt. {names} übersprungen.",
+    "onlyUploading": "Es werden nur {count} Bild(er) hochgeladen, um das Limit einzuhalten.",
+    "uploadingCount": "{count} Bild(er) werden hochgeladen...",
+    "uploadedCount": "{count} Bild(er) hochgeladen",
+    "uploadFailed": "Hochladen fehlgeschlagen",
+    "deleted": "Bild gelöscht",
+    "failedDelete": "Bild konnte nicht gelöscht werden",
+    "failedUpdate": "Aktualisierung fehlgeschlagen",
+    "description": "Beschreibung...",
+    "pdf": "PDF"
+  },
+  "documents": {
+    "title": "Dokumente",
+    "dropzone": "Dateien hier ablegen oder klicken zum Durchsuchen",
+    "uploading": "Wird hochgeladen...",
+    "formats": "PDF, CSV, TXT — max. 10 MB pro Datei",
+    "uploadingDocuments": "Dokumente werden hochgeladen...",
+    "limitReached": "Dokumentenlimit erreicht ({count}/{max})",
+    "upgradePrompt": "Upgraden Sie Ihren Plan, um mehr hochzuladen.",
+    "onlyUploading": "Es werden nur {count} Datei(en) hochgeladen, um das Limit einzuhalten.",
+    "uploadingCount": "{count} Datei(en) werden hochgeladen...",
+    "uploadedCount": "{count} Datei(en) hochgeladen",
+    "uploadFailed": "Hochladen fehlgeschlagen",
+    "deleted": "Datei gelöscht",
+    "failedDelete": "Datei konnte nicht gelöscht werden",
+    "failedUpdate": "Aktualisierung fehlgeschlagen",
+    "pdf": "PDF"
+  }
+}

+ 199 - 0
messages/de/reports.json

@@ -0,0 +1,199 @@
+{
+  "title": "Berichte & Analysen",
+  "tabs": {
+    "revenue": "Umsatz",
+    "services": "Dienstleistungen",
+    "customers": "Kunden",
+    "inventory": "Lagerbestand",
+    "technicians": "Techniker",
+    "parts": "Teile",
+    "jobAnalytics": "Auftragsanalyse",
+    "retention": "Kundenbindung",
+    "tax": "Steuer"
+  },
+  "dateRange": {
+    "to": "bis"
+  },
+  "actions": {
+    "refresh": "Aktualisieren",
+    "csv": "CSV"
+  },
+  "revenue": {
+    "revenue": "Umsatz",
+    "collected": "Eingezogen",
+    "outstanding": "Ausstehend",
+    "services": "Dienstleistungen",
+    "monthlyRevenue": "Monatlicher Umsatz",
+    "revenueByType": "Umsatz nach Typ",
+    "monthlyBreakdown": "Monatliche Aufschlüsselung",
+    "tableHeaders": {
+      "month": "Monat",
+      "revenue": "Umsatz",
+      "collected": "Eingezogen",
+      "count": "Anzahl"
+    }
+  },
+  "services": {
+    "totalServices": "Gesamtleistungen",
+    "byStatus": "Nach Status",
+    "byType": "Nach Typ"
+  },
+  "customers": {
+    "total": "Gesamt",
+    "active": "Aktiv",
+    "topCustomersBySpend": "Top-Kunden nach Ausgaben",
+    "topCustomers": "Top-Kunden",
+    "tableHeaders": {
+      "name": "Name",
+      "company": "Unternehmen",
+      "services": "Dienstleistungen",
+      "totalSpent": "Gesamtausgaben"
+    }
+  },
+  "inventory": {
+    "parts": "Teile",
+    "items": "Artikel",
+    "costValue": "Kostenwert",
+    "sellValue": "Verkaufswert",
+    "potentialMargin": "Potenzielle Marge",
+    "lowStock": "Niedriger Bestand",
+    "tableHeaders": {
+      "name": "Name",
+      "partNumber": "Teilenr.",
+      "qty": "Menge",
+      "minQty": "Min. Menge"
+    }
+  },
+  "technicians": {
+    "totalJobs": "Aufträge gesamt",
+    "totalRevenue": "Gesamtumsatz",
+    "revenueByTechnician": "Umsatz nach Techniker",
+    "technicianBreakdown": "Techniker-Aufschlüsselung",
+    "tableHeaders": {
+      "technician": "Techniker",
+      "jobs": "Aufträge",
+      "revenue": "Umsatz",
+      "avgRevenue": "Ø Umsatz",
+      "totalHours": "Gesamtstunden",
+      "avgHours": "Ø Stunden"
+    }
+  },
+  "parts": {
+    "partsUsed": "Verwendete Teile",
+    "partsRevenue": "Teileumsatz",
+    "topPartsByUsage": "Top-Teile nach Verwendung",
+    "partsUsage": "Teileverwendung",
+    "tableHeaders": {
+      "partName": "Teilename",
+      "partNumber": "Teilenr.",
+      "usageCount": "Verwendungsanzahl",
+      "totalQty": "Gesamtmenge",
+      "revenue": "Umsatz"
+    }
+  },
+  "jobAnalytics": {
+    "totalJobs": "Aufträge gesamt",
+    "avgJobValue": "Ø Auftragswert",
+    "jobsByDayOfWeek": "Aufträge nach Wochentag",
+    "serviceTypes": "Dienstleistungstypen",
+    "monthlyTrend": "Monatlicher Trend",
+    "serviceTypeBreakdown": "Dienstleistungstyp-Aufschlüsselung",
+    "tableHeaders": {
+      "type": "Typ",
+      "count": "Anzahl",
+      "avgValue": "Ø Wert",
+      "avgHours": "Ø Stunden"
+    }
+  },
+  "retention": {
+    "returning": "Wiederkehrend",
+    "new": "Neu",
+    "totalActive": "Gesamt aktiv",
+    "avgDaysBetween": "Ø Tage dazwischen",
+    "topReturningCustomers": "Top wiederkehrende Kunden",
+    "returningCustomers": "Wiederkehrende Kunden",
+    "tableHeaders": {
+      "customer": "Kunde",
+      "company": "Unternehmen",
+      "visits": "Besuche",
+      "totalSpent": "Gesamtausgaben",
+      "avgDaysBetween": "Ø Tage dazwischen"
+    }
+  },
+  "tax": {
+    "totalTaxCollected": "Gesamtsteuer eingezogen",
+    "taxableRevenue": "Steuerpflichtiger Umsatz",
+    "invoicesWithTax": "Rechnungen mit Steuer",
+    "monthlyTaxCollected": "Monatlich eingezogene Steuer",
+    "taxByRate": "Steuer nach Satz",
+    "monthlyBreakdown": "Monatliche Aufschlüsselung",
+    "tableHeaders": {
+      "taxRate": "Steuersatz",
+      "taxCollected": "Eingezogene Steuer",
+      "invoices": "Rechnungen",
+      "month": "Monat",
+      "taxableAmount": "Steuerpflichtiger Betrag"
+    }
+  },
+  "charts": {
+    "revenue": "Umsatz",
+    "collected": "Eingezogen",
+    "count": "Anzahl",
+    "totalSpent": "Gesamtausgaben",
+    "jobs": "Aufträge",
+    "visits": "Besuche",
+    "taxCollected": "Eingezogene Steuer"
+  },
+  "days": {
+    "sunday": "Sonntag",
+    "monday": "Montag",
+    "tuesday": "Dienstag",
+    "wednesday": "Mittwoch",
+    "thursday": "Donnerstag",
+    "friday": "Freitag",
+    "saturday": "Samstag"
+  },
+  "daysShort": {
+    "sun": "So",
+    "mon": "Mo",
+    "tue": "Di",
+    "wed": "Mi",
+    "thu": "Do",
+    "fri": "Fr",
+    "sat": "Sa"
+  },
+  "csvHeaders": {
+    "month": "Monat",
+    "revenue": "Umsatz",
+    "collected": "Eingezogen",
+    "count": "Anzahl",
+    "category": "Kategorie",
+    "label": "Bezeichnung",
+    "name": "Name",
+    "company": "Unternehmen",
+    "services": "Dienstleistungen",
+    "totalSpent": "Gesamtausgaben",
+    "partNumber": "Teilenr.",
+    "quantity": "Menge",
+    "minQuantity": "Min. Menge",
+    "unitCost": "Stückkosten",
+    "technician": "Techniker",
+    "jobs": "Aufträge",
+    "totalRevenue": "Gesamtumsatz",
+    "avgRevenue": "Ø Umsatz",
+    "totalHours": "Gesamtstunden",
+    "avgHours": "Ø Stunden",
+    "partName": "Teilename",
+    "usageCount": "Verwendungsanzahl",
+    "totalQty": "Gesamtmenge",
+    "serviceType": "Dienstleistungstyp",
+    "avgValue": "Ø Wert",
+    "customer": "Kunde",
+    "visits": "Besuche",
+    "avgDaysBetweenVisits": "Ø Tage zwischen Besuchen",
+    "taxCollected": "Eingezogene Steuer",
+    "taxableAmount": "Steuerpflichtiger Betrag",
+    "invoiceCount": "Rechnungsanzahl"
+  },
+  "empty": "Noch keine Daten geladen. Klicken Sie auf Aktualisieren, um den Bericht zu laden."
+}

+ 240 - 0
messages/de/service.json

@@ -0,0 +1,240 @@
+{
+  "page": {
+    "notFound": "Serviceeintrag nicht gefunden",
+    "updated": "Serviceeintrag aktualisiert",
+    "failedUpdate": "Serviceeintrag konnte nicht aktualisiert werden",
+    "deleteTitle": "Serviceeintrag löschen",
+    "deleteDescription": "Dies löscht diesen Serviceeintrag und alle zugehörigen Daten dauerhaft. Dies kann nicht rückgängig gemacht werden.",
+    "deleted": "Serviceeintrag gelöscht",
+    "failedDelete": "Löschen fehlgeschlagen"
+  },
+  "header": {
+    "tabs": {
+      "details": "Details",
+      "images": "Bilder",
+      "video": "Video",
+      "documents": "Dokumente"
+    },
+    "save": "Speichern",
+    "pdf": "PDF",
+    "email": "E-Mail",
+    "share": "Teilen",
+    "delete": "Löschen",
+    "unpaid": "Unbezahlt"
+  },
+  "basicInfo": {
+    "title": "Grundlegende Informationen",
+    "vehicle": "Fahrzeug",
+    "open": "Öffnen",
+    "customer": "Kunde",
+    "titleLabel": "Titel *",
+    "titlePlaceholder": "Ölwechsel",
+    "date": "Datum",
+    "type": "Typ",
+    "mileage": "Kilometerstand",
+    "status": "Status",
+    "technician": "Techniker",
+    "invoiceNumber": "Rechnungsnummer",
+    "typeOptions": {
+      "maintenance": "Wartung",
+      "repair": "Reparatur",
+      "upgrade": "Verbesserung",
+      "inspection": "Überprüfung"
+    },
+    "statusOptions": {
+      "pending": "Ausstehend",
+      "in_progress": "In Bearbeitung",
+      "waiting_parts": "Warten auf Teile",
+      "completed": "Abgeschlossen"
+    },
+    "searchVehicles": "Fahrzeuge durchsuchen...",
+    "noVehicleFound": "Kein Fahrzeug gefunden.",
+    "techPicker": {
+      "placeholder": "Wählen oder geben Sie einen Namen ein...",
+      "searchPlaceholder": "Durchsuchen oder Namen eingeben...",
+      "useCustom": "\"{search}\" verwenden",
+      "typePrompt": "Geben Sie einen Namen ein...",
+      "boardTechnicians": "Board-Techniker",
+      "teamMembers": "Teamitglieder"
+    }
+  },
+  "parts": {
+    "title": "Teile",
+    "fromInventory": "Aus Bestand",
+    "addPart": "Teil hinzufügen",
+    "partNumber": "Teil #",
+    "name": "Name",
+    "qty": "Menge",
+    "unitPrice": "Stückpreis",
+    "total": "Gesamt",
+    "namePlaceholder": "Name *",
+    "subtotal": "Teilesumme: {amount}",
+    "inventoryTitle": "Teil aus Bestand auswählen",
+    "searchInventory": "Bestand durchsuchen...",
+    "inStock": "{quantity} auf Lager",
+    "noInventory": "Keine Bestandteile gefunden."
+  },
+  "labor": {
+    "title": "Arbeit",
+    "addLabor": "Arbeit hinzufügen",
+    "description": "Beschreibung",
+    "hours": "Stunden",
+    "rate": "Satz ({currency}/Std.)",
+    "total": "Gesamt",
+    "descriptionPlaceholder": "Beschreibung *",
+    "subtotal": "Arbeitssumme: {amount}"
+  },
+  "totals": {
+    "title": "Summen",
+    "parts": "Teile",
+    "labor": "Arbeit",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "discountNone": "Keine",
+    "discountPercentage": "Prozentsatz",
+    "discountFixed": "Festbetrag",
+    "tax": "Steuern",
+    "total": "Gesamt"
+  },
+  "notes": {
+    "title": "Notizen",
+    "public": "Öffentlich",
+    "internal": "Intern",
+    "publicPlaceholder": "Notizen sichtbar auf der Rechnung...",
+    "internalPlaceholder": "Interne Notizen (nicht auf Rechnung angezeigt)...",
+    "publicHelper": "Auf der Rechnung und in gemeinsamen Dokumenten angezeigt",
+    "internalHelper": "Nur für Ihr Team sichtbar"
+  },
+  "invoice": {
+    "title": "Rechnungszusammenfassung",
+    "parts": "Teile",
+    "labor": "Arbeit",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "tax": "Steuern ({rate}%)",
+    "total": "Gesamt",
+    "paid": "Bezahlt",
+    "balanceDue": "Ausstehend",
+    "paidBadge": "BEZAHLT",
+    "entityLabel": "Rechnung",
+    "emailSubject": "Zahlungsbestätigung"
+  },
+  "payments": {
+    "title": "Zahlungen",
+    "markUnpaid": "Als unbezahlt kennzeichnen",
+    "markPaid": "Als bezahlt kennzeichnen",
+    "recordPayment": "Zahlung aufzeichnen",
+    "totalPaid": "Insgesamt bezahlt",
+    "amount": "Betrag",
+    "date": "Datum",
+    "method": "Methode",
+    "note": "Notiz",
+    "notePlaceholder": "Optionale Notiz",
+    "savePayment": "Zahlung speichern",
+    "methodOptions": {
+      "cash": "Bargeld",
+      "card": "Karte",
+      "transfer": "Überweisung",
+      "other": "Sonstige"
+    },
+    "recorded": "Zahlung aufgezeichnet",
+    "markedPaid": "Als bezahlt gekennzeichnet",
+    "markedUnpaid": "Als unbezahlt gekennzeichnet",
+    "deleted": "Zahlung gelöscht",
+    "failedRecord": "Zahlung konnte nicht aufgezeichnet werden",
+    "failedStatus": "Zahlungsstatus konnte nicht aktualisiert werden",
+    "failedDelete": "Zahlung konnte nicht gelöscht werden",
+    "deleteTitle": "Zahlung löschen",
+    "deleteDescription": "Dies entfernt diesen Zahlungseintrag.",
+    "failedPdf": "PDF-Rechnung konnte nicht erstellt werden"
+  },
+  "attachments": {
+    "deleteTitle": "Anlage löschen",
+    "deleteDescription": "Dies löscht diese Anlage dauerhaft. Dies kann nicht rückgängig gemacht werden.",
+    "deleted": "Anlage gelöscht",
+    "failedDelete": "Löschen fehlgeschlagen",
+    "images": "Bilder ({count})",
+    "videos": "Videos ({count})",
+    "diagnostics": "Diagnoseberichte ({count})",
+    "documents": "Dokumente ({count})"
+  },
+  "share": {
+    "title": "Rechnung teilen",
+    "description": "Generieren Sie einen öffentlichen Link, der es jedem ermöglicht, diese Rechnung anzusehen und herunterzuladen, ohne sich anzumelden.",
+    "notifyTitle": "{name} benachrichtigen",
+    "email": "E-Mail",
+    "noEmail": "(keine E-Mail vorhanden)",
+    "sms": "SMS",
+    "noPhone": "(keine Telefonnummer vorhanden)",
+    "sendNotification": "Benachrichtigung senden",
+    "revokeLink": "Link widerrufen",
+    "generateLink": "Öffentlichen Link generieren",
+    "emailSent": "E-Mail gesendet",
+    "failedEmail": "E-Mail konnte nicht gesendet werden",
+    "smsSent": "SMS gesendet",
+    "failedSms": "SMS konnte nicht gesendet werden"
+  },
+  "sidebar": {
+    "vehicle": "Fahrzeug",
+    "customer": "Kunde",
+    "vin": "FIN: {vin}",
+    "mileageAtService": "Kilometerstand bei Service: {mileage} {unit}"
+  },
+  "images": {
+    "title": "Service-Bilder",
+    "dropzone": "Bilder hierher ziehen oder zum Durchsuchen klicken",
+    "uploading": "Wird hochgeladen...",
+    "formats": "JPG, PNG, WebP — max. 10 MB pro Bild",
+    "uploadingImages": "Bilder werden hochgeladen...",
+    "limitReached": "Bildlimit erreicht ({count}/{max})",
+    "upgradePrompt": "Upgrade Ihren Plan, um mehr Bilder hochzuladen.",
+    "onlyAllowed": "Nur JPG, PNG und WebP Bilder sind erlaubt. {names} übersprungen.",
+    "onlyUploading": "Es werden nur {count} Bild(er) hochgeladen, um das Limit einzuhalten.",
+    "uploadingCount": "{count} Bild(er) werden hochgeladen...",
+    "uploadedCount": "{count} Bild(er) hochgeladen",
+    "uploadFailed": "Hochladen fehlgeschlagen",
+    "deleted": "Bild gelöscht",
+    "failedDelete": "Bild konnte nicht gelöscht werden",
+    "failedUpdate": "Aktualisierung fehlgeschlagen",
+    "description": "Beschreibung...",
+    "invoice": "Rechnung"
+  },
+  "videos": {
+    "title": "Service-Videos",
+    "dropzone": "Videos hierher ziehen oder zum Durchsuchen klicken",
+    "uploading": "Wird hochgeladen...",
+    "formats": "MP4, WebM, MOV — max. 10 MB pro Video",
+    "uploadingVideos": "Videos werden hochgeladen...",
+    "uploadingCount": "{count} Video(s) werden hochgeladen...",
+    "uploadedCount": "{count} Video(s) hochgeladen",
+    "uploadFailed": "Hochladen fehlgeschlagen",
+    "deleted": "Video gelöscht",
+    "failedDelete": "Video konnte nicht gelöscht werden",
+    "failedUpdate": "Aktualisierung fehlgeschlagen",
+    "invoice": "Rechnung"
+  },
+  "documents": {
+    "diagnosticTitle": "Diagnoseberichte",
+    "documentsTitle": "Dokumente",
+    "dropzone": "Dateien hierher ziehen oder zum Durchsuchen klicken",
+    "uploading": "Wird hochgeladen...",
+    "diagnosticFormats": "PDF, CSV, TXT — max. 10 MB pro Datei",
+    "documentFormats": "PDF, DOCX, XLSX, CSV, TXT — max. 10 MB pro Datei",
+    "uploadingReports": "Berichte werden hochgeladen...",
+    "uploadingDocuments": "Dokumente werden hochgeladen...",
+    "diagnosticLimitReached": "Diagnosebericht-Limit erreicht ({count}/{max})",
+    "documentLimitReached": "Dokumenten-Limit erreicht ({count}/{max})",
+    "upgradePrompt": "Upgrade Ihren Plan, um mehr hochzuladen.",
+    "diagnosticLabel": "Diagnosebericht",
+    "documentLabel": "Dokument",
+    "limitReachedToast": "{label}-Limit erreicht ({count}/{max}). Upgrade Ihren Plan für mehr.",
+    "onlyUploading": "Es werden nur {count} Datei(en) hochgeladen, um das Limit einzuhalten.",
+    "uploadingCount": "{count} Datei(en) werden hochgeladen...",
+    "uploadedCount": "{count} Datei(en) hochgeladen",
+    "uploadFailed": "Hochladen fehlgeschlagen",
+    "deleted": "Datei gelöscht",
+    "failedDelete": "Datei konnte nicht gelöscht werden",
+    "failedUpdate": "Aktualisierung fehlgeschlagen",
+    "invoice": "Rechnung"
+  }
+}

+ 700 - 0
messages/de/settings.json

@@ -0,0 +1,700 @@
+{
+  "title": "Einstellungen",
+  "subtitle": "Verwalten Sie Ihre Werkstatt, Rechnungen und Einstellungen",
+  "nav": {
+    "categories": {
+      "general": "Allgemein",
+      "billing": "Abrechnung & Dokumente",
+      "communications": "Kommunikation",
+      "workshop": "Werkstatt",
+      "system": "System"
+    },
+    "items": {
+      "company": { "title": "Unternehmen", "description": "Firmendetails & Branding" },
+      "subscription": { "title": "Abonnement", "description": "Tarif & Abrechnung" },
+      "account": { "title": "Konto", "description": "Name, E-Mail & Passwort" },
+      "team": { "title": "Team", "description": "Mitglieder & Rollen" },
+      "invoice": { "title": "Rechnung", "description": "Rechnungslayout & Felder" },
+      "templates": { "title": "Vorlagen", "description": "Rechnungs-, Angebots- & Inspektionsvorlagen" },
+      "payment": { "title": "Zahlung", "description": "Zahlungsbedingungen & Konten" },
+      "currency": { "title": "Wahrung", "description": "Wahrungs- & Steuerstandards" },
+      "customFields": { "title": "Benutzerdefinierte Felder", "description": "Eigene Datenfelder definieren" },
+      "email": { "title": "E-Mail", "description": "E-Mail-Anbieter & Versand" },
+      "sms": { "title": "SMS", "description": "SMS-Anbieter & Nachrichten" },
+      "customerPortal": { "title": "Kundenportal", "description": "Kunden-Self-Service-Portal" },
+      "workshop": { "title": "Werkstatt", "description": "Techniker- & Arbeitsstandards" },
+      "maintenance": { "title": "Wartung", "description": "Vorhergesagte Serviceintervalle" },
+      "appearance": { "title": "Darstellung", "description": "Design & Anzeige" },
+      "data": { "title": "Daten", "description": "Export & Import-Backup" },
+      "license": { "title": "Lizenz", "description": "White-Label-Lizenz" },
+      "about": { "title": "Info", "description": "Version & Informationen" }
+    },
+    "pro": "PRO"
+  },
+  "readOnly": {
+    "banner": "Sie haben nur Lesezugriff auf die Einstellungen. Kontaktieren Sie einen Administrator, um Anderungen vorzunehmen."
+  },
+  "featureLocked": {
+    "requiresPro": "Erfordert eine Pro-Lizenz",
+    "upgradePlan": "Tarif upgraden",
+    "activateLicense": "Lizenz aktivieren"
+  },
+  "company": {
+    "logoTitle": "Firmenlogo",
+    "logoAlt": "Firmenlogo",
+    "upload": "Hochladen",
+    "remove": "Entfernen",
+    "logoHint": "JPG, PNG, WebP, SVG — max. 2MB",
+    "uploadingLogo": "Logo wird hochgeladen...",
+    "logoUpdated": "Logo aktualisiert",
+    "failedUploadLogo": "Logo-Upload fehlgeschlagen",
+    "detailsTitle": "Firmendetails",
+    "shopName": "Firmen-/Werkstattname",
+    "shopNamePlaceholder": "Meiers Autowerkstatt",
+    "orgNumber": "Organisationsnummer",
+    "orgNumberPlaceholder": "123 456 789",
+    "phone": "Telefon",
+    "phonePlaceholder": "(555) 123-4567",
+    "email": "E-Mail",
+    "emailPlaceholder": "service@beispiel.de",
+    "address": "Adresse",
+    "addressPlaceholder": "Hauptstrasse 123\nEtage 1\n12345 Stadt",
+    "saveCompany": "Firmendetails speichern",
+    "addNewCompany": "Neue Firma hinzufugen",
+    "companyName": "Firmenname",
+    "companyNamePlaceholder": "z.B. Meiers Autowerkstatt",
+    "cancel": "Abbrechen",
+    "createCompany": "Firma erstellen",
+    "settingsSaved": "Firmeneinstellungen gespeichert",
+    "failedSave": "Einstellungen konnten nicht gespeichert werden",
+    "companyCreated": "Firma erfolgreich erstellt"
+  },
+  "account": {
+    "profileTitle": "Profil",
+    "profileDescription": "Aktualisieren Sie Ihren Anzeigenamen und Ihre E-Mail-Adresse.",
+    "name": "Name",
+    "namePlaceholder": "Ihr Name",
+    "emailLabel": "E-Mail",
+    "emailPlaceholder": "Ihre E-Mail",
+    "saveProfile": "Profil speichern",
+    "profileUpdated": "Profil aktualisiert",
+    "failedUpdateProfile": "Profil konnte nicht aktualisiert werden",
+    "failedUpdateEmail": "E-Mail konnte nicht aktualisiert werden",
+    "changePasswordTitle": "Passwort andern",
+    "changePasswordDescription": "Geben Sie Ihr aktuelles Passwort ein und wahlen Sie ein neues.",
+    "currentPassword": "Aktuelles Passwort",
+    "newPassword": "Neues Passwort",
+    "confirmNewPassword": "Neues Passwort bestatigen",
+    "changePassword": "Passwort andern",
+    "passwordChanged": "Passwort geandert",
+    "passwordsNoMatch": "Passworter stimmen nicht uberein",
+    "passwordMinLength": "Passwort muss mindestens 8 Zeichen lang sein",
+    "failedChangePassword": "Passwort konnte nicht geandert werden",
+    "twoFactorTitle": "Zwei-Faktor-Authentifizierung",
+    "twoFactorEnabled": "Zwei-Faktor-Authentifizierung ist aktiviert",
+    "twoFactorDisablePrompt": "Geben Sie Ihr Passwort ein, um die Zwei-Faktor-Authentifizierung zu deaktivieren.",
+    "twoFactorEnablePrompt": "Fugen Sie eine zusatzliche Sicherheitsebene zu Ihrem Konto hinzu, indem Sie die Zwei-Faktor-Authentifizierung mit einer Authenticator-App aktivieren.",
+    "password": "Passwort",
+    "enterPassword": "Passwort eingeben",
+    "disable2FA": "2FA deaktivieren",
+    "enable2FA": "2FA aktivieren",
+    "twoFactorDisabled": "Zwei-Faktor-Authentifizierung deaktiviert",
+    "failedEnable2FA": "2FA konnte nicht aktiviert werden",
+    "failedDisable2FA": "2FA konnte nicht deaktiviert werden",
+    "enterPasswordError": "Bitte geben Sie Ihr Passwort ein",
+    "setupDialogTitle": "Zwei-Faktor-Authentifizierung aktivieren",
+    "setupDialogDescription": "Geben Sie Ihr Passwort ein, um die Einrichtung von 2FA mit einer Authenticator-App zu starten.",
+    "continue": "Weiter",
+    "scanQRTitle": "QR-Code scannen",
+    "scanQRDescription": "Scannen Sie diesen QR-Code mit Ihrer Authenticator-App (Google Authenticator, Authy, usw.)",
+    "verifyCodeTitle": "Code verifizieren",
+    "verifyCodeDescription": "Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein, um die korrekte Einrichtung zu bestatigen.",
+    "authCode": "Authentifizierungscode",
+    "authCodePlaceholder": "000000",
+    "back": "Zuruck",
+    "verify": "Verifizieren",
+    "enterCodeError": "Bitte geben Sie den Code aus Ihrer Authenticator-App ein",
+    "invalidCode": "Ungueltiger Code. Bitte versuchen Sie es erneut.",
+    "verificationFailed": "Verifizierung fehlgeschlagen",
+    "backupCodesTitle": "Backup-Codes speichern",
+    "backupCodesDescription": "Bewahren Sie diese Codes an einem sicheren Ort auf. Sie konnen sie verwenden, um sich anzumelden, wenn Sie den Zugang zu Ihrer Authenticator-App verlieren. Jeder Code kann nur einmal verwendet werden.",
+    "copyCodes": "Codes kopieren",
+    "copied": "Kopiert",
+    "backupCodesCopied": "Backup-Codes in die Zwischenablage kopiert",
+    "savedBackupCodes": "Ich habe meine Backup-Codes gespeichert",
+    "twoFactorEnabledSuccess": "Zwei-Faktor-Authentifizierung aktiviert",
+    "dangerZone": "Gefahrenzone",
+    "deleteContentTitle": "Inhalte loschen",
+    "deleteContentDescription": "Organisationsdaten wie Fahrzeuge, Kunden, Angebote oder Inventar selektiv loschen. Ihr Konto und Ihre Einstellungen bleiben erhalten.",
+    "deleteContentButton": "Inhalte loschen",
+    "deleteAccountTitle": "Konto loschen",
+    "deleteAccountDescription": "Loschen Sie Ihr Konto und alle zugehorigen Daten dauerhaft, einschliesslich Fahrzeuge, Servicedaten, Arbeitsauftrage, Rechnungen, Kunden, Dateien und Zahlungen. Diese Aktion kann nicht ruckgangig gemacht werden.",
+    "deleteAccountButton": "Konto loschen",
+    "deleteAccountDialogTitle": "Konto loschen",
+    "deleteAccountDialogDescription": "Dies loscht Ihr Konto und alle zugehorigen Daten dauerhaft. Diese Aktion ist unwiderruflich. Alle Ihre Fahrzeuge, Servicedaten, Arbeitsauftrage, Angebote, Kunden, Dateien und Zahlungshistorie werden dauerhaft entfernt.",
+    "deleteAccountConfirmPhrase": "delete me",
+    "deleteAccountConfirmPrompt": "Geben Sie <bold>delete me</bold> zur Bestatigung ein",
+    "permanentlyDelete": "Dauerhaft loschen",
+    "deleting": "Wird geloscht...",
+    "failedDeleteAccount": "Konto konnte nicht geloscht werden",
+    "deleteContentDialogTitle": "Inhalte loschen",
+    "deleteContentDialogDescription": "Wahlen Sie die Inhalte aus, die Sie dauerhaft loschen mochten. Diese Aktion kann nicht ruckgangig gemacht werden.",
+    "contentVehicles": "Fahrzeuge",
+    "contentVehiclesDescription": "Alle Fahrzeuge und deren Servicedaten, Notizen, Erinnerungen, Kraftstoffprotokolle und wiederkehrende Rechnungen",
+    "contentCustomers": "Kunden",
+    "contentCustomersDescription": "Alle Kundendaten. Fahrzeuge, die mit geloschten Kunden verknupft sind, werden getrennt.",
+    "contentQuotes": "Angebote",
+    "contentQuotesDescription": "Alle Angebote und deren Positionen",
+    "contentInventory": "Inventar",
+    "contentInventoryDescription": "Alle Inventarteile und deren Bilder",
+    "deleteContentConfirmPhrase": "delete my data",
+    "deleteContentConfirmPrompt": "Geben Sie <bold>delete my data</bold> zur Bestatigung ein",
+    "deleteSelected": "{count} ausgewahlte loschen",
+    "contentDeleted": "Ausgewahlte Inhalte wurden dauerhaft geloscht",
+    "failedDeleteContent": "Inhalte konnten nicht geloscht werden"
+  },
+  "team": {
+    "title": "Team",
+    "noOrgDescription": "Erstellen Sie ein Team, um Daten mit anderen Benutzern zu teilen.",
+    "orgDescription": "Verwalten Sie Ihre Organisationsmitglieder und deren Rollen.",
+    "createOrg": "Organisation erstellen",
+    "createOrgDescription": "Erstellen Sie eine Organisation, um Teammitglieder einzuladen. Sie werden der Eigentumer.",
+    "orgName": "Organisationsname",
+    "orgNamePlaceholder": "Meine Werkstatt",
+    "create": "Erstellen",
+    "orgCreated": "Organisation erstellt",
+    "failedCreateOrg": "Organisation konnte nicht erstellt werden",
+    "memberCount": "{count} Mitglied",
+    "memberCountPlural": "{count} Mitglieder",
+    "admin": "Administrator",
+    "member": "Mitglied",
+    "inviteByEmail": "Per E-Mail einladen",
+    "inviteEmailPlaceholder": "benutzer@beispiel.de",
+    "invite": "Einladen",
+    "invited": "Mitglied erfolgreich eingeladen.",
+    "userNotFoundTitle": "Benutzer nicht gefunden",
+    "userNotFoundDescription": "Kein Konto fur \"{email}\" gefunden. Eine Einladungs-E-Mail senden, um sich anzumelden und Ihrem Team beizutreten?",
+    "sendInvitation": "Einladung senden",
+    "invitationSentTitle": "Einladung gesendet",
+    "invitationSentDescription": "Eine Einladungs-E-Mail wurde an {email} gesendet.",
+    "failedInvite": "Mitglied konnte nicht eingeladen werden",
+    "failedSendInvitation": "Einladung konnte nicht gesendet werden",
+    "pendingInvitations": "Ausstehende Einladungen",
+    "invitedAs": "Eingeladen als {role}",
+    "expires": "Lauft ab {date}",
+    "pending": "Ausstehend",
+    "copyInviteLink": "Einladungslink kopieren",
+    "inviteLinkCopied": "Einladungslink in die Zwischenablage kopiert",
+    "cancelInvitation": "Einladung stornieren",
+    "cancelInvitationDescription": "Einladung an {email} stornieren? Der Link kann dann nicht mehr verwendet werden.",
+    "invitationCancelled": "Einladung storniert",
+    "failedCancelInvitation": "Einladung konnte nicht storniert werden",
+    "customRoles": "Benutzerdefinierte Rollen",
+    "newRole": "Neue Rolle",
+    "roleName": "Rollenname",
+    "roleNamePlaceholder": "z.B. Techniker",
+    "fullAdminAccess": "Voller Admin-Zugriff (umgeht alle Berechtigungsprufungen)",
+    "permissions": "Berechtigungen",
+    "createRole": "Rolle erstellen",
+    "updateRole": "Rolle aktualisieren",
+    "roleCreated": "Rolle erstellt",
+    "roleUpdated": "Rolle aktualisiert",
+    "failedSaveRole": "Rolle konnte nicht gespeichert werden",
+    "deleteRole": "Rolle loschen",
+    "deleteRoleDescription": "Die Rolle \"{name}\" loschen? Mitglieder mit dieser Rolle verlieren ihre benutzerdefinierten Berechtigungen.",
+    "roleDeleted": "Rolle geloscht",
+    "failedDeleteRole": "Rolle konnte nicht geloscht werden",
+    "fullAccess": "Voller Zugriff",
+    "permissionCount": "{count} Berechtigung",
+    "permissionCountPlural": "{count} Berechtigungen",
+    "noCustomRoles": "Noch keine benutzerdefinierten Rollen. Erstellen Sie eine, um granulare Berechtigungen fur Teammitglieder zu definieren.",
+    "builtInRoles": "Integrierte Rollen",
+    "ownerLabel": "Eigentumer",
+    "ownerDescription": "Voller Zugriff. Kann Mitglieder verwalten, Rollen andern und die Organisation loschen.",
+    "adminLabel": "Administrator",
+    "adminDescription": "Kann Mitglieder einladen/entfernen und gemeinsame Daten verwalten.",
+    "memberLabel": "Mitglied",
+    "memberDescription": "Berechtigungen werden durch die zugewiesene benutzerdefinierte Rolle bestimmt. Ohne benutzerdefinierte Rolle nur Lesezugriff.",
+    "roleAssigned": "Rolle aktualisiert",
+    "failedAssignRole": "Rolle konnte nicht aktualisiert werden",
+    "removeMemberTitle": "Mitglied entfernen",
+    "removeMemberDescription": "{name} aus dem Team entfernen? Der Zugriff auf gemeinsame Daten geht verloren.",
+    "removeButton": "Entfernen",
+    "memberRemoved": "Mitglied entfernt",
+    "failedRemoveMember": "Mitglied konnte nicht entfernt werden",
+    "memberRoleUpdated": "Mitgliedsrolle aktualisiert",
+    "failedUpdateRole": "Rolle konnte nicht aktualisiert werden"
+  },
+  "invoice": {
+    "title": "Rechnungslayout",
+    "description": "Konfigurieren Sie, was auf generierten PDF-Rechnungen erscheint.",
+    "invoiceNumberFormat": "Rechnungsnummernformat",
+    "invoiceNumberFormatHint": "Verwenden Sie <code>{year}</code> fur das aktuelle Jahr. Vorschau: <bold>{preview}</bold>",
+    "nextInvoiceNumber": "Nachste Rechnungsnummer",
+    "nextInvoiceNumberPlaceholder": "z.B. 94",
+    "nextInvoiceNumberHint": "Die nachste Rechnung verwendet diese Nummer (z.B. {example})",
+    "dueDays": "Falligkeitstage",
+    "dueDaysHint": "Anzahl der Tage bis zur Falligkeit der Rechnung",
+    "visibilityTitle": "Sichtbarkeit auf der Rechnung",
+    "companyLogo": "Firmenlogo",
+    "companyLogoHint": "Firmenlogo im Rechnungskopf anzeigen",
+    "companyName": "Firmenname",
+    "companyNameHint": "Firmennamentext im Rechnungskopf anzeigen",
+    "bankAccount": "Bankkonto",
+    "bankAccountHint": "Bankkonto/IBAN auf der Rechnung anzeigen",
+    "organizationNumber": "Organisationsnummer",
+    "organizationNumberHint": "Org.-Nr. im Rechnungskopf anzeigen",
+    "customFooter": "Benutzerdefinierte Rechnungsfusszeile",
+    "footerPlaceholder": "Vielen Dank fur Ihren Auftrag!",
+    "footerHint": "Dieser Text erscheint am Ende jeder Rechnung",
+    "saveInvoice": "Rechnungseinstellungen speichern",
+    "saved": "Rechnungseinstellungen gespeichert"
+  },
+  "templates": {
+    "title": "Vorlagen",
+    "invoiceDescription": "Passen Sie das Erscheinungsbild Ihrer PDF-Rechnungen und Angebote an.",
+    "inspectionsDescription": "Verwalten Sie Inspektionschecklisten und Mehrpunkt-Vorlagen.",
+    "smsDescription": "Passen Sie die SMS-Nachrichten an Kunden an.",
+    "tabs": {
+      "invoice": "Rechnung",
+      "quotation": "Angebot",
+      "inspections": "Inspektionen",
+      "sms": "SMS"
+    },
+    "presets": "Vorlagen-Presets",
+    "primaryColor": "Primarfarbe",
+    "fontAndLayout": "Schrift & Layout",
+    "fontFamily": "Schriftfamilie",
+    "helveticaDefault": "Helvetica (Standard)",
+    "timesRoman": "Times Roman",
+    "courier": "Courier",
+    "headerStyle": "Kopfzeilenstil",
+    "standard": "Standard",
+    "compact": "Kompakt",
+    "modern": "Modern",
+    "preview": "{name} Vorschau",
+    "billTo": "Rechnungsempfanger",
+    "preparedFor": "Erstellt fur",
+    "vehicle": "Fahrzeug",
+    "part": "Teil",
+    "qty": "Menge",
+    "price": "Preis",
+    "total": "Gesamt",
+    "labor": "Arbeitszeit",
+    "hours": "Stunden",
+    "rate": "Satz",
+    "subtotal": "Zwischensumme",
+    "tax": "Steuer ({rate}%)",
+    "saveInvoiceTemplate": "Rechnungsvorlage speichern",
+    "saveQuotationTemplate": "Angebotsvorlage speichern",
+    "invoiceTemplateSaved": "Rechnungsvorlageneinstellungen gespeichert",
+    "quotationTemplateSaved": "Angebotsvorlageneinstellungen gespeichert",
+    "failedSave": "Einstellungen konnten nicht gespeichert werden",
+    "smsTemplates": "SMS-Nachrichtenvorlagen",
+    "smsTemplatesDescription": "Passen Sie die Textnachrichten an Kunden an. Verwenden Sie Variablen wie <code>{companyName}</code>, um dynamische Werte einzufugen.",
+    "reset": "Zurucksetzen",
+    "invoiceReady": "Rechnung bereit",
+    "invoiceReadyDescription": "Wird gesendet, wenn eine Rechnung mit einem Kunden geteilt wird.",
+    "quoteReady": "Angebot bereit",
+    "quoteReadyDescription": "Wird gesendet, wenn ein Angebot mit einem Kunden geteilt wird.",
+    "inspectionReady": "Inspektion bereit",
+    "inspectionReadyDescription": "Wird gesendet, wenn ein Inspektionsbericht mit einem Kunden geteilt wird.",
+    "statusInProgress": "Status: In Bearbeitung",
+    "statusInProgressDescription": "Wird gesendet, wenn der Arbeitsauftragsstatus auf In Bearbeitung wechselt.",
+    "statusWaitingParts": "Status: Warten auf Teile",
+    "statusWaitingPartsDescription": "Wird gesendet, wenn der Arbeitsauftragsstatus auf Warten auf Teile wechselt.",
+    "statusReady": "Status: Abholbereit",
+    "statusReadyDescription": "Wird gesendet, wenn der Arbeitsauftragsstatus auf Bereit wechselt.",
+    "statusCompleted": "Status: Abgeschlossen",
+    "statusCompletedDescription": "Wird gesendet, wenn der Arbeitsauftragsstatus auf Abgeschlossen wechselt.",
+    "paymentReceived": "Zahlung eingegangen",
+    "paymentReceivedDescription": "Wird gesendet, wenn eine Zahlung fur eine Rechnung verbucht wird.",
+    "saveSmsTemplates": "SMS-Vorlagen speichern",
+    "smsTemplateSaved": "SMS-Vorlageneinstellungen gespeichert",
+    "smsDefaults": {
+      "invoiceReady": "Ihre Rechnung ist fertig. Hier ansehen: {share_link}",
+      "quoteReady": "Ihr Angebot steht zur Prüfung bereit. Hier ansehen: {share_link}",
+      "inspectionReady": "Ihr Fahrzeuginspektionsbericht ist fertig. Hier ansehen: {share_link}",
+      "statusInProgress": "Die Arbeit an Ihrem Fahrzeug hat begonnen. Wir halten Sie auf dem Laufenden.",
+      "statusWaitingParts": "Ihr Fahrzeug wartet auf Teile. Wir benachrichtigen Sie, wenn die Arbeit fortgesetzt wird.",
+      "statusReady": "Ihr Fahrzeug ist zur Abholung bereit!",
+      "statusCompleted": "Ihr Service ist abgeschlossen. Vielen Dank für Ihren Auftrag!",
+      "paymentReceived": "Zahlung von {amount} für Rechnung {invoice_number} erhalten. Vielen Dank!"
+    },
+    "colorPresets": {
+      "amber": "Bernstein",
+      "blue": "Blau",
+      "emerald": "Smaragd",
+      "red": "Rot",
+      "purple": "Lila",
+      "slate": "Schiefer",
+      "rose": "Rose",
+      "indigo": "Indigo"
+    }
+  },
+  "payment": {
+    "detailsTitle": "Zahlungsdetails",
+    "detailsDescription": "Bankkonto und Zahlungsbedingungen auf Rechnungen.",
+    "bankAccount": "Bankkonto (IBAN / Til Konto)",
+    "bankAccountPlaceholder": "NO93 8601 1117 947",
+    "bankAccountHint": "Ihre Bankkontonummer oder IBAN fur den Zahlungsempfang",
+    "paymentTerms": "Zahlungsbedingungen",
+    "paymentTermsPlaceholder": "Netto 14 Tage",
+    "paymentTermsHint": "Zahlungsbedingungen auf Rechnungen (z.B. \"Netto 14 Tage\", \"Zahlbar bei Erhalt\")",
+    "onlinePayments": "Online-Zahlungen",
+    "onlinePaymentsDescription": "Ermoglichen Sie Kunden, Rechnungen online uber geteilte Rechnungslinks zu bezahlen.",
+    "stripe": "Stripe",
+    "stripeDescription": "Kredit-/Debitkartenzahlungen akzeptieren",
+    "secretKey": "Geheimer Schlussel",
+    "publishableKey": "Offentlicher Schlussel",
+    "webhookSecret": "Webhook-Geheimnis",
+    "webhookUrl": "Webhook-URL",
+    "stripeWebhookHint": "Fugen Sie diese URL in Ihrem Stripe-Dashboard unter Webhooks hinzu. Abonnieren Sie <code>checkout.session.completed</code>.",
+    "vipps": "Vipps",
+    "vippsDescription": "Vipps-Mobilzahlungen akzeptieren (Norwegen)",
+    "clientId": "Client-ID",
+    "clientSecret": "Client-Geheimnis",
+    "subscriptionKey": "Abonnement-Schlussel",
+    "merchantSerialNumber": "Handler-Seriennummer",
+    "testMode": "Testmodus",
+    "callbackUrl": "Callback-URL",
+    "termsOfSale": "Verkaufsbedingungen (Salgsvilkar)",
+    "termsOfSaleDescription": "Von Vipps vorgeschrieben. Kunden konnen diese auf der Rechnungsseite einsehen.",
+    "externalTermsUrl": "Externe Bedingungs-URL (optional)",
+    "externalTermsUrlHint": "Link zu einer externen Seite mit Ihren Bedingungen. Falls gesetzt, verlinkt die Rechnung hierhin statt zur integrierten Bedingungsseite.",
+    "publicTermsUrl": "Offentliche Bedingungs-URL",
+    "publicTermsUrlHint": "Teilen Sie diese URL mit Vipps oder verlinken Sie sie auf Ihrer Website.",
+    "paypal": "PayPal",
+    "paypalDescription": "PayPal-Zahlungen akzeptieren",
+    "sandboxMode": "Sandbox-Modus",
+    "paypalWebhookHint": "Fugen Sie diese URL in Ihrem PayPal-Entwickler-Dashboard unter Webhooks hinzu. Abonnieren Sie <code>PAYMENT.CAPTURE.COMPLETED</code>.",
+    "savePayment": "Zahlungseinstellungen speichern",
+    "saved": "Zahlungseinstellungen gespeichert"
+  },
+  "currency": {
+    "title": "Wahrung & Steuer",
+    "description": "Standardwahrung und Steuersatz fur Rechnungen und Servicedaten.",
+    "currencyLabel": "Wahrung",
+    "previewLabel": "Vorschau: {value}",
+    "enableTax": "Steuer aktivieren",
+    "enableTaxHint": "Steuerberechnung auf Rechnungen und Servicedaten anzeigen",
+    "defaultTaxRate": "Standard-Steuersatz (%)",
+    "defaultTaxRateHint": "Wird automatisch beim Erstellen neuer Servicedaten eingetragen",
+    "saveSettings": "Einstellungen speichern",
+    "saved": "Einstellungen gespeichert",
+    "currencies": {
+      "USD": "US-Dollar",
+      "EUR": "Euro",
+      "GBP": "Britisches Pfund",
+      "NOK": "Norwegische Krone",
+      "SEK": "Schwedische Krone",
+      "DKK": "Danische Krone",
+      "CHF": "Schweizer Franken",
+      "CAD": "Kanadischer Dollar",
+      "AUD": "Australischer Dollar",
+      "NZD": "Neuseelandischer Dollar",
+      "JPY": "Japanischer Yen",
+      "CNY": "Chinesischer Yuan",
+      "INR": "Indische Rupie",
+      "BRL": "Brasilianischer Real",
+      "MXN": "Mexikanischer Peso",
+      "PLN": "Polnischer Zloty",
+      "CZK": "Tschechische Krone",
+      "HUF": "Ungarischer Forint",
+      "TRY": "Turkische Lira",
+      "ZAR": "Sudafrikanischer Rand",
+      "KRW": "Sudkoreanischer Won",
+      "SGD": "Singapur-Dollar",
+      "HKD": "Hongkong-Dollar",
+      "THB": "Thailandischer Baht",
+      "ISK": "Islandische Krone",
+      "RON": "Rumanischer Leu",
+      "ILS": "Israelischer Schekel",
+      "PHP": "Philippinischer Peso",
+      "IDR": "Indonesische Rupiah",
+      "MYR": "Malaysischer Ringgit",
+      "AED": "VAE-Dirham",
+      "SAR": "Saudi-Riyal"
+    }
+  },
+  "customFields": {
+    "title": "Benutzerdefinierte Felder",
+    "description": "Definieren Sie benutzerdefinierte Felder, die auf Serviceberichten und Angeboten erscheinen.",
+    "addField": "Feld hinzufügen",
+    "filterAll": "Alle",
+    "serviceRecord": "Servicebericht",
+    "quote": "Angebot",
+    "emptyState": "Noch keine benutzerdefinierten Felder definiert. Klicken Sie auf \"Feld hinzufügen\", um eines zu erstellen.",
+    "required": "Erforderlich",
+    "inactive": "Inaktiv",
+    "editField": "Feld bearbeiten",
+    "newField": "Neues benutzerdefiniertes Feld",
+    "label": "Bezeichnung",
+    "labelPlaceholder": "z.B. Garantieablauf",
+    "fieldName": "Feldname (ID)",
+    "fieldNamePlaceholder": "garantieablauf",
+    "fieldNameHint": "Nur Kleinbuchstaben, Zahlen und Unterstriche.",
+    "fieldType": "Feldtyp",
+    "entityType": "Entitätstyp",
+    "fieldTypes": {
+      "text": "Text",
+      "number": "Zahl",
+      "date": "Datum",
+      "select": "Auswahl (Dropdown)",
+      "checkbox": "Kontrollkästchen",
+      "textarea": "Textbereich"
+    },
+    "optionsLabel": "Optionen (kommagetrennt)",
+    "optionsPlaceholder": "Option 1, Option 2, Option 3",
+    "active": "Aktiv",
+    "cancel": "Abbrechen",
+    "update": "Aktualisieren",
+    "create": "Erstellen",
+    "deleteTitle": "Benutzerdefiniertes Feld löschen",
+    "deleteDescription": "Dies löscht \"{label}\" und alle gespeicherten Werte dauerhaft. Dies kann nicht rückgängig gemacht werden.",
+    "fieldCreated": "Benutzerdefiniertes Feld erstellt",
+    "fieldUpdated": "Benutzerdefiniertes Feld aktualisiert",
+    "fieldDeleted": "Benutzerdefiniertes Feld gelöscht",
+    "failedSave": "Feld konnte nicht gespeichert werden",
+    "failedDelete": "Konnte nicht gelöscht werden"
+  },
+  "workshop": {
+    "title": "Werkstatt-Standards",
+    "description": "Standardwerte beim Erstellen neuer Servicedaten.",
+    "defaultTechnician": "Standard-Technikername",
+    "technicianPlaceholder": "Max Mustermann",
+    "defaultLaborRate": "Standard-Stundensatz",
+    "laborRatePlaceholder": "75,00",
+    "workingHours": "Arbeitszeiten",
+    "workingHoursPlaceholder": "Mo-Fr 8:00 - 17:00",
+    "unitsTitle": "Einheiten",
+    "unitsDescription": "Wahlen Sie zwischen metrisch (km, Liter) und imperial (Meilen, Gallonen).",
+    "unitSystem": "Einheitensystem",
+    "metric": "Metrisch (km, Liter)",
+    "imperial": "Imperial (Meilen, Gallonen)",
+    "distanceLabel": "Entfernung",
+    "volumeLabel": "Volumen",
+    "kilometers": "Kilometer (km)",
+    "liters": "Liter (L)",
+    "miles": "Meilen (mi)",
+    "gallons": "Gallonen (gal)",
+    "saveWorkshop": "Werkstatteinstellungen speichern",
+    "saved": "Werkstatteinstellungen gespeichert"
+  },
+  "maintenance": {
+    "title": "Vorhergesagte Wartung",
+    "description": "Schatzen Sie den aktuellen Kilometerstand jedes Fahrzeugs anhand seiner Servicehistorie und zeigen Sie Fahrzeuge an, die ihren nachsten Service benotigen oder sich diesem nahern.",
+    "enablePredicted": "Vorhergesagte Wartung aktivieren",
+    "enablePredictedHint": "Vorhersagen auf Fahrzeugdetails und Dashboard anzeigen",
+    "serviceInterval": "Serviceintervall ({unit})",
+    "serviceIntervalHint": "Entfernung zwischen empfohlenen Services",
+    "approachingThreshold": "Annaherungsschwelle ({unit})",
+    "approachingThresholdHint": "Warnung anzeigen, wenn innerhalb dieser Entfernung zum nachsten Service",
+    "saveMaintenance": "Wartungseinstellungen speichern",
+    "saved": "Wartungseinstellungen gespeichert"
+  },
+  "appearance": {
+    "title": "Darstellung",
+    "description": "Passen Sie das Erscheinungsbild von Torqvoice an.",
+    "darkMode": "Dunkler Modus",
+    "darkModeHint": "Zwischen hellem und dunklem Design wechseln",
+    "dateTimeTitle": "Datum & Uhrzeit",
+    "dateTimeDescription": "Konfigurieren Sie, wie Datum und Uhrzeit in der Anwendung angezeigt werden.",
+    "dateFormat": "Datumsformat",
+    "timeFormat": "Zeitformat",
+    "time12h": "12-Stunden (2:30 PM)",
+    "time24h": "24-Stunden (14:30)",
+    "timezone": "Zeitzone",
+    "timezoneAuto": "Automatisch erkennen (Browser)",
+    "timezoneHint": "Bei automatischer Erkennung sieht jeder Benutzer Daten in der Zeitzone seines Browsers.",
+    "previewLabel": "Vorschau",
+    "dateLabel": "Datum",
+    "dateTimeLabel": "Datum & Uhrzeit",
+    "saveSettings": "Einstellungen speichern",
+    "saved": "Einstellungen gespeichert"
+  },
+  "data": {
+    "title": "Datenverwaltung",
+    "description": "Exportieren Sie Ihre Daten als Backup oder importieren Sie eine zuvor exportierte Datei.",
+    "exportTitle": "Daten exportieren",
+    "exportDescription": "Laden Sie Ihre Daten als ZIP-Datei herunter. Wahlen Sie die Kategorien fur das Backup aus.",
+    "includeInBackup": "Im Backup enthalten:",
+    "selectAll": "Alle auswahlen",
+    "deselectAll": "Alle abwahlen",
+    "optSettings": "Einstellungen",
+    "optSettingsDesc": "Werkstattinfo, Rechnungskonfiguration, Zahlungsanbieter",
+    "optCustomers": "Kunden",
+    "optCustomersDesc": "Kundendaten und Kontaktinformationen",
+    "optVehicles": "Fahrzeuge & Servicedaten",
+    "optVehiclesDesc": "Fahrzeuge, Notizen, Kraftstoffprotokolle, Erinnerungen, Servicedaten",
+    "optQuotes": "Angebote",
+    "optQuotesDesc": "Angebote mit Teilen und Arbeitszeit",
+    "optInventory": "Inventar",
+    "optInventoryDesc": "Inventarteile und Lagerbestande",
+    "optCustomFields": "Benutzerdefinierte Felder",
+    "optCustomFieldsDesc": "Benutzerdefinierte Felddefinitionen und Werte",
+    "optFiles": "Hochgeladene Dateien",
+    "optFilesDesc": "Bilder, Service-Anhange, Logo",
+    "filesWarning": "Enthalt hochgeladene Dateien — Backup kann grosser sein",
+    "downloadBackup": "Backup herunterladen",
+    "exportComplete": "Export abgeschlossen",
+    "exportCompleteMessage": "Ihre Backup-Datei wurde heruntergeladen.",
+    "exportFailed": "Export fehlgeschlagen",
+    "exportFailedMessage": "Ihre Daten konnten nicht exportiert werden. Bitte versuchen Sie es erneut.",
+    "importTitle": "Daten importieren",
+    "importWarning": "Der Import wird <bold>alle vorhandenen Daten ersetzen</bold> durch den Inhalt der Backup-Datei. Diese Aktion kann nicht ruckgangig gemacht werden.",
+    "importFileHint": "Unterstutzt sowohl .zip (v2) als auch .json (v1) Backup-Dateien.",
+    "uploadRestore": "Hochladen & Wiederherstellen",
+    "importComplete": "Import abgeschlossen",
+    "importCompleteMessage": "Alle Daten wurden aus der Backup-Datei wiederhergestellt.",
+    "importFailed": "Import fehlgeschlagen",
+    "importFromOther": "Von anderen Diensten importieren",
+    "importFromOtherDescription": "Migrieren Sie Ihre Daten von einer anderen Plattform. Wahlen Sie einen Dienst, um zu beginnen.",
+    "lubelogTitle": "Von LubeLog importieren",
+    "lubelogExportSteps": "So exportieren Sie Ihre LubeLog-Daten:",
+    "lubelogStep1": "Offnen Sie Ihre LubeLog-Instanz",
+    "lubelogStep2": "Gehen Sie zu <bold>Settings</bold>",
+    "lubelogStep3": "Klicken Sie auf <bold>Make Backup</bold>, um eine ZIP-Datei herunterzuladen",
+    "lubelogStep4": "Laden Sie die heruntergeladene ZIP-Datei unten hoch",
+    "lubelogAddNote": "Dies wird <bold>Daten hinzufugen</bold> neben Ihren bestehenden Datensatzen — nichts wird geloscht.",
+    "lubelogImported": "Was importiert wird:",
+    "lubelogVehicles": "Fahrzeuge",
+    "lubelogServiceRecords": "Servicedaten mit Anhangen",
+    "lubelogNotes": "Notizen",
+    "lubelogFileHint": "Dateien bis 200MB unterstutzt.",
+    "lubelogImportComplete": "LubeLog-Import abgeschlossen",
+    "cancel": "Abbrechen",
+    "import": "Importieren",
+    "invoiceNinjaTitle": "Von Invoice Ninja importieren",
+    "invoiceNinjaExportSteps": "So exportieren Sie Ihre Invoice Ninja-Daten:",
+    "invoiceNinjaStep1": "Offnen Sie Ihre Invoice Ninja-Instanz",
+    "invoiceNinjaStep2": "Gehen Sie zu <bold>Settings > Import | Export</bold>",
+    "invoiceNinjaStep3": "Klicken Sie auf <bold>Export</bold>, um eine ZIP-Datei herunterzuladen",
+    "invoiceNinjaStep4": "Laden Sie die heruntergeladene ZIP-Datei unten hoch",
+    "invoiceNinjaAddNote": "Dies wird <bold>Daten hinzufugen</bold> neben Ihren bestehenden Datensatzen — nichts wird geloscht.",
+    "invoiceNinjaImported": "Was importiert wird:",
+    "invoiceNinjaCustomers": "Kunden (Clients)",
+    "invoiceNinjaProducts": "Produkte (Inventar)",
+    "invoiceNinjaInvoices": "Rechnungen als Servicedaten (Teile & Arbeitszeit)",
+    "invoiceNinjaDocuments": "Dokumente & Anhange",
+    "invoiceNinjaPayments": "Zahlungen",
+    "invoiceNinjaFileHint": "Laden Sie eine Invoice Ninja ZIP-Exportdatei hoch. Dateien bis 200MB unterstutzt.",
+    "invoiceNinjaImportComplete": "Invoice Ninja-Import abgeschlossen"
+  },
+  "license": {
+    "title": "White-Label-Lizenz",
+    "description": "Aktivieren Sie eine White-Label-Lizenz, um das Torqvoice-Branding von Rechnungen und der Anwendung zu entfernen.",
+    "purchaseWhiteLabel": "White-Label kaufen",
+    "status": "Status:",
+    "active": "Aktiv",
+    "inactive": "Inaktiv",
+    "lastChecked": "Zuletzt gepruft: {date}",
+    "enterLicenseKey": "Lizenzschlussel eingeben",
+    "validate": "Validieren",
+    "validated": "Lizenz erfolgreich validiert",
+    "invalid": "Ungueltiger Lizenzschlussel",
+    "failedValidate": "Lizenz konnte nicht validiert werden",
+    "keyHint": "Jeder Lizenzschlussel ist an diese Organisation gebunden und kann nicht fur mehrere Organisationen verwendet werden."
+  },
+  "about": {
+    "title": "Uber Torqvoice",
+    "description": "Torqvoice ist Ihre komplette Losung fur die Fahrzeug-Serviceverwaltung. Verfolgen Sie Servicehistorie, Reparaturen und mehr.",
+    "version": "Version",
+    "website": "Website",
+    "documentation": "Dokumentation",
+    "changelog": "Anderungsprotokoll"
+  },
+  "portal": {
+    "title": "Kundenportal",
+    "description": "Ermoglichen Sie Ihren Kunden, sich anzumelden und ihre Fahrzeuge, Rechnungen, Angebote und Inspektionen einzusehen. Kunden konnen auch Serviceanfragen stellen.",
+    "enablePortal": "Kundenportal aktivieren",
+    "enablePortalHint": "Kunden mit einer E-Mail-Adresse konnen sich per Magic Link anmelden",
+    "enabled": "Kundenportal aktiviert",
+    "disabled": "Kundenportal deaktiviert",
+    "failedUpdate": "Einstellung konnte nicht aktualisiert werden",
+    "customSlug": "Benutzerdefinierter Portal-Slug",
+    "save": "Speichern",
+    "slugSaved": "Portal-Slug gespeichert",
+    "slugRemoved": "Portal-Slug entfernt",
+    "failedUpdateSlug": "Slug konnte nicht aktualisiert werden",
+    "slugHint": "Kleinbuchstaben, Zahlen, Bindestriche und Unterstriche. 3-48 Zeichen. Leer lassen, um die Standard-URL zu verwenden.",
+    "portalUrl": "Portal-URL",
+    "urlCopied": "Portal-URL kopiert",
+    "shareHint": "Teilen Sie diesen Link mit Ihren Kunden, damit sie sich in ihr Portal einloggen konnen."
+  },
+  "email": {
+    "title": "E-Mail-Anbieter",
+    "description": "Konfigurieren Sie den E-Mail-Anbieter Ihrer Organisation zum Versenden von Rechnungen und Angeboten. Ohne Konfiguration wird der Standard-E-Mail-Anbieter der Plattform verwendet.",
+    "useCustomLabel": "Eigenen E-Mail-Anbieter verwenden",
+    "useCustomHint": "Aktivieren, um Ihren eigenen E-Mail-Anbieter anstelle des Plattform-Standards zu konfigurieren",
+    "platformDefaultInfo": "Ihre Organisation verwendet den Standard-E-Mail-Anbieter der Plattform. Rechnungen und Angebote werden mit den plattformkonfigurierten E-Mail-Einstellungen versendet.",
+    "smtpHost": "SMTP-Host",
+    "smtpHostPlaceholder": "smtp.example.com",
+    "smtpPort": "SMTP-Port",
+    "smtpPortPlaceholder": "587",
+    "username": "Benutzername",
+    "usernamePlaceholder": "benutzer@example.com",
+    "password": "Passwort",
+    "fromEmail": "Absender-E-Mail",
+    "fromEmailPlaceholder": "noreply@example.com",
+    "fromEmailDomainPlaceholder": "noreply@ihredomain.com",
+    "fromName": "Absendername",
+    "fromNamePlaceholder": "Ihre Werkstatt",
+    "tlsConnection": "TLS-Verbindung (Port 465)",
+    "tlsConnectionHint": "Aktivieren fur implizites TLS. Deaktivieren fur STARTTLS (Port 587/25).",
+    "verifyTlsCerts": "TLS-Zertifikate verifizieren",
+    "verifyTlsCertsHint": "Deaktivieren, um selbstsignierte Zertifikate zuzulassen (nicht fur Produktion empfohlen)",
+    "requireTlsUpgrade": "TLS-Upgrade erzwingen",
+    "requireTlsUpgradeHint": "TLS-Upgrade bei STARTTLS-Verbindungen erzwingen",
+    "apiKey": "API-Schlussel",
+    "apiKeyHintResend": "Holen Sie sich Ihren API-Schlussel von",
+    "serverToken": "Server-Token",
+    "serverTokenHintPostmark": "Holen Sie sich Ihr Server-Token von",
+    "apiKeyHintMailgun": "Holen Sie sich Ihren API-Schlussel von",
+    "domain": "Domain",
+    "domainPlaceholder": "mg.ihredomain.com",
+    "region": "Region",
+    "apiKeyHintSendGrid": "Holen Sie sich Ihren API-Schlussel von",
+    "accessKeyId": "Zugriffsschlussel-ID",
+    "secretAccessKey": "Geheimer Zugriffsschlussel",
+    "awsRegion": "AWS-Region",
+    "awsRegionPlaceholder": "us-east-1",
+    "awsRegionHint": "Die AWS-Region, in der SES konfiguriert ist (z.B. us-east-1, eu-west-1)",
+    "sendTestEmail": "Test-E-Mail senden",
+    "testEmailHint": "Speichern Sie zuerst die Einstellungen und senden Sie dann eine Test-E-Mail an Ihr Konto",
+    "saveSettings": "Einstellungen speichern",
+    "savedDefault": "E-Mail-Einstellungen gespeichert — Plattform-Standard wird verwendet",
+    "saved": "E-Mail-Einstellungen gespeichert",
+    "failedSave": "Einstellungen konnten nicht gespeichert werden",
+    "testSentTo": "Test-E-Mail gesendet an {email}",
+    "testFailed": "E-Mail-Test fehlgeschlagen"
+  },
+  "sms": {
+    "title": "SMS-Anbieter",
+    "description": "Konfigurieren Sie Ihren SMS-Anbieter fur bidirektionale Nachrichten mit Kunden. Jede Organisation verwendet ihren eigenen SMS-Anbieter und ihre eigene Telefonnummer.",
+    "enableLabel": "SMS-Nachrichten aktivieren",
+    "enableHint": "SMS-Nachrichten mit Kunden senden und empfangen",
+    "disabledInfo": "SMS-Nachrichten sind deaktiviert. Aktivieren Sie sie, um Textnachrichten mit Ihren Kunden zu senden und zu empfangen.",
+    "phoneNumber": "Telefonnummer",
+    "phoneNumberPlaceholder": "+4915123456789",
+    "phoneNumberHint": "Ihre SMS-fahige Telefonnummer im E.164-Format (z.B. +4915123456789)",
+    "accountSid": "Konto-SID",
+    "authToken": "Auth-Token",
+    "apiKey": "API-Schlussel",
+    "apiSecret": "API-Geheimnis",
+    "webhookUrl": "Webhook-URL",
+    "webhookUrlHint": "Konfigurieren Sie diese URL in Ihrem {provider}-Dashboard, um eingehende SMS-Nachrichten zu empfangen.",
+    "webhookCopied": "Webhook-URL kopiert",
+    "testPhoneNumber": "Test-Telefonnummer",
+    "testPhonePlaceholder": "+4915198765432",
+    "sendTestSms": "Test-SMS senden",
+    "testSmsHint": "Speichern Sie zuerst die Einstellungen und geben Sie dann eine Empfangernummer ein, um eine Test-SMS zu senden",
+    "enterTestPhone": "Geben Sie eine Telefonnummer ein, um die Test-SMS zu senden",
+    "saveSettings": "Einstellungen speichern",
+    "savedDisabled": "SMS-Einstellungen gespeichert — SMS deaktiviert",
+    "saved": "SMS-Einstellungen gespeichert",
+    "failedSave": "Einstellungen konnten nicht gespeichert werden",
+    "testSentTo": "Test-SMS gesendet an {phone}",
+    "testFailed": "SMS-Test fehlgeschlagen"
+  }
+}

+ 156 - 0
messages/de/share.json

@@ -0,0 +1,156 @@
+{
+  "invoice": {
+    "title": "Rechnung",
+    "downloadPdf": "PDF herunterladen",
+    "paymentReceived": "Zahlung eingegangen!",
+    "paymentApplied": "{amount} wurde auf diese Rechnung angewendet.",
+    "verifyingPayment": "Zahlung wird überprüft...",
+    "balanceDue": "Offener Betrag",
+    "fullAmount": "Vollständiger Betrag",
+    "partialPayment": "Teilzahlung",
+    "enterAmount": "Betrag eingeben",
+    "payWithCard": "{amount} mit Karte bezahlen",
+    "payWithVipps": "{amount} mit Vipps bezahlen",
+    "payWithPaypal": "{amount} mit PayPal bezahlen",
+    "errorInvalidAmount": "Bitte geben Sie einen gültigen Betrag ein.",
+    "errorExceedsBalance": "Betrag darf {amount} nicht überschreiten.",
+    "errorVerifyFailed": "Zahlung konnte nicht verifiziert werden. Sie wird möglicherweise noch verarbeitet.",
+    "errorVerifyRequest": "Zahlungsstatus konnte nicht überprüft werden.",
+    "errorCheckoutFailed": "Checkout konnte nicht gestartet werden.",
+    "errorPaymentFailed": "Zahlung konnte nicht eingeleitet werden. Bitte versuchen Sie es erneut.",
+    "billTo": "Rechnungsempfänger",
+    "vehicle": "Fahrzeug",
+    "vin": "VIN: {vin}",
+    "plate": "Kennzeichen: {plate}",
+    "service": "Service",
+    "type": "Typ: {type}",
+    "tech": "Techniker: {tech}",
+    "parts": "Teile",
+    "partNumber": "Teilenr.",
+    "description": "Beschreibung",
+    "qty": "Menge",
+    "unitPrice": "Einzelpreis",
+    "total": "Gesamt",
+    "labor": "Arbeitsleistung",
+    "hours": "Stunden",
+    "rate": "Stundensatz",
+    "ratePerHour": "{rate}/Std.",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "discountPercent": "Rabatt ({percent}%)",
+    "tax": "MwSt. ({rate}%)",
+    "paid": "Bezahlt",
+    "paidInFull": "VOLLSTÄNDIG BEZAHLT",
+    "amountDue": "Fälliger Betrag",
+    "paymentInformation": "Zahlungsinformationen",
+    "bankAccount": "Bankkonto",
+    "orgNumber": "Org.-Nummer",
+    "paymentTerms": "Zahlungsbedingungen",
+    "dueDate": "Fälligkeitsdatum",
+    "serviceImages": "Service-Bilder ({count})",
+    "serviceVideos": "Service-Videos ({count})",
+    "diagnosticReports": "Diagnoseberichte ({count})",
+    "notes": "Hinweise",
+    "diagnosticNotes": "Diagnosehinweise",
+    "tel": "Tel: {phone}"
+  },
+  "quote": {
+    "title": "Angebot",
+    "downloadPdf": "PDF herunterladen",
+    "preparedFor": "Erstellt für",
+    "vehicle": "Fahrzeug",
+    "vin": "VIN: {vin}",
+    "plate": "Kennzeichen: {plate}",
+    "quoteDetails": "Angebotsdetails",
+    "description": "Beschreibung",
+    "parts": "Teile",
+    "partNumber": "Teilenr.",
+    "partDescription": "Beschreibung",
+    "qty": "Menge",
+    "unitPrice": "Einzelpreis",
+    "total": "Gesamt",
+    "labor": "Arbeitsleistung",
+    "laborDescription": "Beschreibung",
+    "hours": "Stunden",
+    "rate": "Stundensatz",
+    "ratePerHour": "{rate}/Std.",
+    "subtotal": "Zwischensumme",
+    "discount": "Rabatt",
+    "discountPercent": "Rabatt ({percent}%)",
+    "tax": "MwSt. ({rate}%)",
+    "images": "Bilder ({count})",
+    "documents": "Dokumente",
+    "requestChanges": "Änderungen anfordern",
+    "acceptQuote": "Angebot annehmen",
+    "whatChanges": "Welche Änderungen wünschen Sie?",
+    "changePlaceholder": "Beschreiben Sie die gewünschten Änderungen...",
+    "cancel": "Abbrechen",
+    "submitRequest": "Anfrage senden",
+    "quoteAccepted": "Angebot angenommen",
+    "quoteAcceptedMessage": "Vielen Dank! Dieses Angebot wurde angenommen. Die Werkstatt wird sich in Kürze bei Ihnen melden.",
+    "changesRequested": "Änderungen angefordert",
+    "changesRequestedMessage": "Ihre Änderungsanfrage wurde eingereicht. Die Werkstatt wird sie prüfen und sich bei Ihnen melden.",
+    "validUntil": "Gültig bis: {date}",
+    "status": {
+      "draft": "Entwurf",
+      "sent": "Gesendet",
+      "accepted": "Angenommen",
+      "rejected": "Abgelehnt",
+      "expired": "Abgelaufen",
+      "converted": "Umgewandelt",
+      "changes_requested": "Änderungen angefordert"
+    },
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "Fahrzeuginspektion",
+    "vehicle": "Fahrzeug",
+    "customer": "Kunde",
+    "vin": "VIN: {vin}",
+    "plate": "Kennzeichen: {plate}",
+    "mileage": "Kilometerstand: {mileage}",
+    "inspected": "geprüft",
+    "pass": "Bestanden",
+    "fail": "Nicht bestanden",
+    "attention": "Achtung",
+    "notInspected": "Nicht geprüft",
+    "quoteAvailable": "Ein Angebot wurde für diese Inspektion vorbereitet",
+    "quoteAvailableDescription": "Prüfen Sie das Angebot, um Preise zu sehen und die empfohlenen Arbeiten zu genehmigen.",
+    "viewQuote": "Angebot ansehen",
+    "quoteRequested": "Angebot angefordert",
+    "quoteRequestedCancel": "— Abbrechen",
+    "requestQuote": "Angebot anfordern",
+    "downloadPdf": "PDF herunterladen",
+    "notes": "Hinweise",
+    "quoteCancelled": "Angebotsanfrage storniert",
+    "cancelFailed": "Anfrage konnte nicht storniert werden"
+  },
+  "terms": {
+    "title": "Verkaufsbedingungen (Salgsvilkår)",
+    "contact": "Kontakt",
+    "tel": "Tel: {phone}"
+  },
+  "quoteRequest": {
+    "title": "Angebot anfordern",
+    "description": "Wählen Sie die Positionen aus, für die Sie ein Reparaturangebot wünschen.",
+    "selectAll": "Alle auswählen",
+    "deselectAll": "Alle abwählen",
+    "selectedCount": "{selected} von {total} ausgewählt",
+    "fail": "Nicht bestanden",
+    "attention": "Achtung",
+    "message": "Nachricht",
+    "messageOptional": "(optional)",
+    "messagePlaceholder": "Weitere Details oder Fragen...",
+    "cancel": "Abbrechen",
+    "submitRequest": "Anfrage senden",
+    "selectAtLeastOne": "Wählen Sie mindestens eine Position aus",
+    "submitSuccess": "Angebotsanfrage eingereicht!",
+    "submitError": "Anfrage konnte nicht eingereicht werden"
+  },
+  "common": {
+    "poweredBy": "Bereitgestellt von",
+    "termsOfSale": "Verkaufsbedingungen",
+    "portalMessage": "Sehen Sie Ihre Fahrzeuge, Rechnungen und mehr in Ihrem",
+    "customerPortal": "Kundenportal"
+  }
+}

+ 195 - 0
messages/de/vehicles.json

@@ -0,0 +1,195 @@
+{
+  "list": {
+    "active": "Aktiv",
+    "archived": "Archiviert",
+    "searchPlaceholder": "Suche nach Kennzeichen, Marke, Modell, FIN...",
+    "addVehicle": "Fahrzeug hinzuf\u00fcgen",
+    "emptySearch": "Keine Fahrzeuge entsprechen Ihrer Suche.",
+    "emptyArchived": "Keine archivierten Fahrzeuge.",
+    "empty": "Noch keine Fahrzeuge.",
+    "table": {
+      "plate": "Kennzeichen",
+      "vehicle": "Fahrzeug",
+      "customer": "Kunde",
+      "mileage": "Kilometerstand",
+      "services": "Services"
+    },
+    "edit": "Bearbeiten",
+    "archive": "Archivieren",
+    "unarchive": "Wiederherstellen",
+    "delete": "L\u00f6schen",
+    "deleteTitle": "Fahrzeug l\u00f6schen",
+    "deleteDescription": "{name} l\u00f6schen? Dadurch werden alle zugeh\u00f6rigen Serviceeintr\u00e4ge und Notizen entfernt.",
+    "unarchiveTitle": "Fahrzeug wiederherstellen",
+    "unarchiveDescription": "{name} in die aktive Fahrzeugliste zur\u00fcckholen?",
+    "vehicleUnarchived": "Fahrzeug wiederhergestellt",
+    "unarchiveError": "Fehler beim Wiederherstellen des Fahrzeugs",
+    "deleteError": "Fehler beim L\u00f6schen des Fahrzeugs",
+    "error": "Fehler beim Laden der Fahrzeuge"
+  },
+  "detail": {
+    "vehicleArchived": "Dieses Fahrzeug ist archiviert",
+    "backToVehicles": "Zur\u00fcck zu Fahrzeugen",
+    "newWorkOrder": "Neuer Arbeitsauftrag",
+    "editVehicle": "Fahrzeug bearbeiten",
+    "unarchive": "Wiederherstellen",
+    "archive": "Archivieren",
+    "delete": "L\u00f6schen",
+    "notFound": "Fahrzeug nicht gefunden",
+    "predictedKm": "Prognostizierte Km",
+    "predictedMileage": "Prognostizierter Kilometerstand",
+    "dismissed": "Verworfen",
+    "undo": "R\u00fcckg\u00e4ngig",
+    "serviceOverdue": "Service \u00fcberf\u00e4llig",
+    "serviceSoon": "Service bald f\u00e4llig",
+    "services": "Services",
+    "purchased": "Gekauft am {date}",
+    "tabs": {
+      "services": "Services",
+      "inspections": "Inspektionen",
+      "notes": "Notizen",
+      "reminders": "Erinnerungen"
+    },
+    "noteDeleted": "Notiz gel\u00f6scht",
+    "noteDeleteError": "Fehler beim L\u00f6schen",
+    "reminderDeleted": "Erinnerung gel\u00f6scht",
+    "reminderDeleteError": "Fehler beim L\u00f6schen",
+    "vehicleUnarchived": "Fahrzeug wiederhergestellt",
+    "unarchiveError": "Fehler beim Wiederherstellen",
+    "vehicleDeleted": "Fahrzeug gel\u00f6scht",
+    "vehicleDeleteError": "Fehler beim L\u00f6schen des Fahrzeugs",
+    "deleteTitle": "Fahrzeug l\u00f6schen?",
+    "deleteDescription": "Dadurch werden {name} und alle zugeh\u00f6rigen Serviceeintr\u00e4ge, Notizen und Erinnerungen dauerhaft gel\u00f6scht. Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
+    "edit": "Bearbeiten"
+  },
+  "inspections": {
+    "newInspection": "Neue Inspektion",
+    "empty": "Noch keine Inspektionen",
+    "template": "Vorlage",
+    "progress": "Fortschritt",
+    "status": "Status",
+    "date": "Datum",
+    "completed": "Abgeschlossen",
+    "inProgress": "In Bearbeitung"
+  },
+  "reminders": {
+    "active": "Aktiv",
+    "completed": "Abgeschlossen",
+    "all": "Alle",
+    "addReminder": "Erinnerung hinzuf\u00fcgen",
+    "emptyActive": "Keine aktiven Erinnerungen",
+    "emptyCompleted": "Keine abgeschlossenen Erinnerungen",
+    "empty": "Noch keine Erinnerungen",
+    "overdue": "\u00dcberf\u00e4llig",
+    "dueSoon": "Bald f\u00e4llig",
+    "due": "F\u00e4llig am {date}",
+    "dueAt": "F\u00e4llig bei {mileage} {unit}",
+    "editTitle": "Erinnerung bearbeiten",
+    "addTitle": "Erinnerung hinzuf\u00fcgen",
+    "titleLabel": "Titel *",
+    "titlePlaceholder": "z.B. \u00d6lwechsel",
+    "descriptionLabel": "Beschreibung",
+    "descriptionPlaceholder": "Optionale Details...",
+    "dueDateLabel": "F\u00e4lligkeitsdatum",
+    "dueMileageLabel": "F\u00e4lligkeitskilometerstand",
+    "dueMileagePlaceholder": "z.B. 50000",
+    "reminderUpdated": "Erinnerung aktualisiert",
+    "reminderCreated": "Erinnerung erstellt",
+    "saveError": "Fehler beim {action} der Erinnerung"
+  },
+  "services": {
+    "searchPlaceholder": "Eintr\u00e4ge durchsuchen...",
+    "allTypes": "Alle Typen",
+    "maintenance": "Wartung",
+    "repair": "Reparatur",
+    "upgrade": "Ausr\u00fcstung",
+    "inspection": "Inspektion",
+    "newWorkOrder": "Neuer Arbeitsauftrag",
+    "table": {
+      "date": "Datum",
+      "title": "Titel",
+      "type": "Typ",
+      "status": "Status",
+      "mileage": "Kilometerstand",
+      "technician": "Techniker",
+      "files": "Dateien",
+      "total": "Gesamt"
+    },
+    "emptyFiltered": "Keine Serviceeintr\u00e4ge entsprechen Ihren Filtern.",
+    "empty": "Noch keine Serviceeintr\u00e4ge.",
+    "showing": "Zeige {start}-{end} von {total}",
+    "perPage": "pro Seite",
+    "page": "Seite {page} von {totalPages}"
+  },
+  "notes": {
+    "addNote": "Notiz hinzuf\u00fcgen",
+    "table": {
+      "title": "Titel",
+      "content": "Inhalt",
+      "date": "Datum"
+    },
+    "empty": "Noch keine Notizen.",
+    "pin": "Anheften",
+    "unpin": "L\u00f6sen",
+    "delete": "L\u00f6schen",
+    "showing": "Zeige {start}-{end} von {total}",
+    "perPage": "pro Seite",
+    "page": "Seite {page} von {totalPages}",
+    "editTitle": "Notiz bearbeiten",
+    "addTitle": "Notiz hinzuf\u00fcgen",
+    "titleLabel": "Titel *",
+    "titlePlaceholder": "Titel der Notiz",
+    "contentLabel": "Inhalt *",
+    "contentPlaceholder": "Schreiben Sie Ihre Notiz...",
+    "noteUpdated": "Notiz aktualisiert",
+    "noteCreated": "Notiz erstellt",
+    "titleRequired": "Titel und Inhalt sind erforderlich",
+    "saveError": "Fehler beim {action} der Notiz"
+  },
+  "form": {
+    "editTitle": "Fahrzeug bearbeiten",
+    "addTitle": "Neues Fahrzeug hinzuf\u00fcgen",
+    "photo": "Foto",
+    "clickToUpload": "Klicken Sie, um ein Foto hochzuladen",
+    "imageFormats": "JPEG, PNG, WebP bis zu 5MB",
+    "imageTooLarge": "Bild zu gro\u00df",
+    "maxFileSize": "Maximale Dateigr\u00f6\u00dfe ist 5MB",
+    "uploadingImage": "Bild wird hochgeladen...",
+    "uploadFailed": "Hochladen fehlgeschlagen",
+    "imageUploaded": "Bild hochgeladen",
+    "customer": "Kunde",
+    "searchCustomers": "Kunden suchen...",
+    "noCustomerFound": "Kein Kunde gefunden.",
+    "noCustomer": "Kein Kunde",
+    "addCustomer": "Kunde hinzuf\u00fcgen",
+    "make": "Marke *",
+    "model": "Modell *",
+    "year": "Baujahr *",
+    "mileage": "Kilometerstand",
+    "vin": "FIN",
+    "licensePlate": "Kennzeichen",
+    "color": "Farbe",
+    "fuelType": "Kraftstoffart",
+    "gasoline": "Benzin",
+    "diesel": "Diesel",
+    "electric": "Elektrisch",
+    "hybrid": "Hybrid",
+    "other": "Sonstige",
+    "transmission": "Getriebe",
+    "automatic": "Automatik",
+    "manual": "Schaltgetriebe",
+    "cvt": "CVT",
+    "engineSize": "Hubraum",
+    "vehicleUpdated": "Fahrzeug aktualisiert",
+    "vehicleAdded": "Fahrzeug hinzugef\u00fcgt",
+    "saveError": "Fehler beim Speichern des Fahrzeugs"
+  },
+  "archiveDialog": {
+    "title": "Fahrzeug archivieren",
+    "description": "<span>{name}</span> archivieren? Es wird aus der Hauptliste ausgeblendet, aber der gesamte Serviceverlauf bleibt erhalten.",
+    "reasonLabel": "Grund (optional)",
+    "reasonPlaceholder": "z.B. Verkauft, Totalschaden, au\u00dfer Betrieb genommen...",
+    "archived": "{name} archiviert",
+    "archiveError": "Fehler beim Archivieren des Fahrzeugs"
+  }
+}

+ 67 - 0
messages/de/workBoard.json

@@ -0,0 +1,67 @@
+{
+  "page": {
+    "error": "Arbeitsboard konnte nicht geladen werden"
+  },
+  "toolbar": {
+    "today": "Heute",
+    "presenter": "Präsentator",
+    "addTechnician": "Techniker hinzufügen"
+  },
+  "board": {
+    "days": {
+      "mon": "Mo",
+      "tue": "Di",
+      "wed": "Mi",
+      "thu": "Do",
+      "fri": "Fr",
+      "sat": "Sa",
+      "sun": "So"
+    },
+    "noTechnicians": "Noch keine Techniker",
+    "noTechniciansDescription": "Fügen Sie einen Techniker hinzu, um mit der Planung von Arbeiten im Board zu beginnen.",
+    "failedMove": "Zuweisung konnte nicht verschoben werden",
+    "failedAssign": "Auftrag konnte nicht zugewiesen werden",
+    "failedRemove": "Zuweisung konnte nicht entfernt werden"
+  },
+  "unassigned": {
+    "title": "Nicht zugewiesene Aufträge ({count})",
+    "searchPlaceholder": "Aufträge durchsuchen...",
+    "empty": "Keine nicht zugewiesenen Aufträge"
+  },
+  "technician": {
+    "editTitle": "Techniker bearbeiten",
+    "addTitle": "Techniker hinzufügen",
+    "name": "Name",
+    "namePlaceholder": "z.B. Max Mustermann",
+    "color": "Farbe",
+    "linkMember": "Mit Organisationsmitglied verlinken (optional)",
+    "standalone": "Unabhängiger Techniker",
+    "deleteTitle": "Techniker löschen",
+    "deleteDescription": "\"{name}\" von der Arbeitsplatine entfernen? Ihre bestehenden Zuweisungen werden ebenfalls entfernt.",
+    "addTechnician": "Techniker hinzufügen"
+  },
+  "jobDetail": {
+    "viewDetails": "Details anzeigen",
+    "remove": "Entfernen",
+    "date": "Datum: {date}"
+  },
+  "presenter": {
+    "title": "Arbeitsboard",
+    "day": "Tag",
+    "status": "Status",
+    "week": "Woche",
+    "today": "Heute",
+    "live": "Live",
+    "disconnected": "Live-Updates unterbrochen",
+    "noTechnicians": "Keine Techniker konfiguriert",
+    "noJobsToday": "Keine Aufträge heute",
+    "job": "{count} Auftrag",
+    "jobs": "{count} Aufträge",
+    "statusLabels": {
+      "pending": "Ausstehend",
+      "inProgress": "In Bearbeitung",
+      "waitingParts": "Warten auf Teile",
+      "completed": "Abgeschlossen"
+    }
+  }
+}

+ 80 - 0
messages/de/workOrders.json

@@ -0,0 +1,80 @@
+{
+  "page": {
+    "error": "Arbeitsaufträge konnten nicht geladen werden"
+  },
+  "list": {
+    "searchPlaceholder": "Arbeitsaufträge durchsuchen...",
+    "newWorkOrder": "Neuer Arbeitsauftrag",
+    "selectVehicle": "Fahrzeug auswählen",
+    "empty": "Keine Arbeitsaufträge gefunden.",
+    "statusUpdated": "Status aktualisiert",
+    "statusTabs": {
+      "all": "Alle",
+      "active": "Aktiv",
+      "pending": "Ausstehend",
+      "inProgress": "In Bearbeitung",
+      "waitingParts": "Warten auf Teile",
+      "completed": "Abgeschlossen"
+    },
+    "statusActions": {
+      "startWork": "Arbeit starten",
+      "waitingParts": "Warten auf Teile",
+      "complete": "Abschließen",
+      "resumeWork": "Arbeit fortsetzen",
+      "reopen": "Erneut öffnen"
+    },
+    "table": {
+      "invoice": "Rechnung#",
+      "vehicle": "Fahrzeug",
+      "customer": "Kunde",
+      "title": "Titel",
+      "status": "Status",
+      "tech": "Techniker",
+      "date": "Datum",
+      "total": "Gesamt"
+    },
+    "emailSubject": "Fahrzeugstatusupdate: {status}"
+  },
+  "vehiclePicker": {
+    "title": "Fahrzeug für Arbeitsauftrag auswählen",
+    "searchPlaceholder": "Fahrzeuge durchsuchen...",
+    "empty": "Keine Fahrzeuge gefunden.",
+    "emptySearch": "Keine Fahrzeuge entsprechen Ihrer Suche.",
+    "addNewVehicle": "Neues Fahrzeug hinzufügen",
+    "noPlate": "Kein Kennzeichen",
+    "make": "Hersteller *",
+    "model": "Modell *",
+    "year": "Jahr *",
+    "licensePlate": "Kennzeichen",
+    "customer": "Kunde",
+    "selectCustomer": "Wählen Sie einen Kunden (optional)",
+    "createNewCustomer": "Neuen Kunden erstellen",
+    "newCustomer": "Neuer Kunde",
+    "customerName": "Name *",
+    "customerNamePlaceholder": "Kundenname",
+    "phone": "Telefon",
+    "phonePlaceholder": "Telefonnummer",
+    "creating": "Wird erstellt...",
+    "createAndContinue": "Erstellen & Fortfahren",
+    "invalidYear": "Bitte geben Sie ein gültiges Jahr ein",
+    "customerNameRequired": "Kundenname ist erforderlich",
+    "failedCreateCustomer": "Kunde konnte nicht erstellt werden",
+    "failedCreateVehicle": "Fahrzeug konnte nicht erstellt werden",
+    "somethingWentWrong": "Etwas ist schief gelaufen"
+  },
+  "notify": {
+    "title": "{name} benachrichtigen",
+    "description": "Senden Sie eine Benachrichtigung an den Kunden über dieses Update.",
+    "sms": "SMS",
+    "email": "E-Mail",
+    "noPhone": "(keine Telefonnummer vorhanden)",
+    "noEmail": "(keine E-Mail vorhanden)",
+    "notAvailable": "(nicht verfügbar)",
+    "skip": "Überspringen",
+    "send": "Senden",
+    "smsSent": "SMS gesendet",
+    "emailSent": "E-Mail gesendet",
+    "failedSms": "SMS konnte nicht gesendet werden",
+    "failedEmail": "E-Mail konnte nicht gesendet werden"
+  }
+}

+ 105 - 0
messages/en/admin.json

@@ -0,0 +1,105 @@
+{
+  "nav": {
+    "overview": { "title": "Overview", "description": "Platform stats & health" },
+    "users": { "title": "Users", "description": "Manage all users" },
+    "organizations": { "title": "Organizations", "description": "Manage all organizations" },
+    "settings": { "title": "Settings", "description": "Platform configuration" }
+  },
+  "layout": {
+    "title": "Admin Panel",
+    "description": "Platform management for super administrators"
+  },
+  "overview": {
+    "totalUsers": "Total Users",
+    "organizations": "Organizations",
+    "activeSubscriptions": "Active Subscriptions",
+    "monthlyRevenue": "Monthly Revenue"
+  },
+  "users": {
+    "searchPlaceholder": "Search by name or email...",
+    "name": "Name",
+    "email": "Email",
+    "role": "Role",
+    "orgs": "Orgs",
+    "created": "Created",
+    "lastSeen": "Last Seen",
+    "noResults": "No users match your search.",
+    "noUsers": "No users found.",
+    "superAdmin": "Super Admin",
+    "user": "User",
+    "online": "Online",
+    "promoteTitle": "Promote User",
+    "demoteTitle": "Demote User",
+    "promoteConfirm": "Are you sure you want to promote \"{name}\" to super admin?",
+    "demoteConfirm": "Are you sure you want to demote \"{name}\" from super admin?",
+    "promote": "Promote",
+    "demote": "Demote",
+    "promotedSuccess": "User promoted successfully",
+    "demotedSuccess": "User demoted successfully",
+    "failedUpdate": "Failed to update user",
+    "deleteTitle": "Delete User",
+    "deleteConfirm": "Are you sure you want to permanently delete \"{name}\" ({email})? This action cannot be undone.",
+    "delete": "Delete",
+    "deletedSuccess": "User deleted successfully",
+    "failedDelete": "Failed to delete user",
+    "promoteToSuperAdmin": "Promote to Super Admin",
+    "demoteFromSuperAdmin": "Demote from Super Admin",
+    "deleteUser": "Delete User"
+  },
+  "organizations": {
+    "searchPlaceholder": "Search by organization name...",
+    "name": "Name",
+    "owner": "Owner",
+    "members": "Members",
+    "subscription": "Subscription",
+    "created": "Created",
+    "noResults": "No organizations match your search.",
+    "noOrganizations": "No organizations found.",
+    "none": "None",
+    "deleteTitle": "Delete Organization",
+    "deleteConfirm": "Are you sure you want to permanently delete \"{name}\" and all its data? This action cannot be undone.",
+    "delete": "Delete",
+    "deletedSuccess": "Organization deleted successfully",
+    "failedDelete": "Failed to delete organization",
+    "deleteOrganization": "Delete Organization"
+  },
+  "adminSettings": {
+    "platformTitle": "Platform Settings",
+    "platformDescription": "General platform configuration options",
+    "disableRegistration": "Disable Registration",
+    "disableRegistrationHint": "Block new user sign-ups on the platform",
+    "emailTitle": "Email Settings",
+    "emailDescription": "Platform Email Provider — Used for system-level emails such as password resets and team invitations. This is also the default email provider for all organizations. Organizations can optionally configure their own email provider in Settings > Email.",
+    "smtpHost": "SMTP Host",
+    "smtpPort": "SMTP Port",
+    "username": "Username",
+    "password": "Password",
+    "fromEmail": "From Email",
+    "fromName": "From Name",
+    "tlsConnection": "TLS Connection (Port 465)",
+    "tlsConnectionHint": "Enable for implicit TLS. Disable for STARTTLS (port 587/25).",
+    "verifyTls": "Verify TLS Certificates",
+    "verifyTlsHint": "Disable to allow self-signed certificates (not recommended for production)",
+    "requireTlsUpgrade": "Require TLS Upgrade",
+    "requireTlsUpgradeHint": "Force TLS upgrade on STARTTLS connections",
+    "apiKey": "API Key",
+    "serverToken": "Server Token",
+    "domain": "Domain",
+    "region": "Region",
+    "awsRegion": "AWS Region",
+    "accessKeyId": "Access Key ID",
+    "secretAccessKey": "Secret Access Key",
+    "awsRegionHint": "The AWS region where SES is configured (e.g. us-east-1, eu-west-1)",
+    "sendTestEmail": "Send Test Email",
+    "testEmailHint": "Save settings first, then send a test email to your account",
+    "saveSettings": "Save Settings",
+    "saved": "Settings saved successfully",
+    "failedSave": "Failed to save settings",
+    "testSentTo": "Test email sent to {email}",
+    "testFailed": "Email test failed",
+    "resendApiKeyHint": "Get your API key from resend.com",
+    "postmarkApiKeyHint": "Get your Server Token from postmarkapp.com",
+    "mailgunApiKeyHint": "Get your API key from app.mailgun.com",
+    "sendgridApiKeyHint": "Get your API key from app.sendgrid.com"
+  }
+}

+ 73 - 0
messages/en/auth.json

@@ -0,0 +1,73 @@
+{
+  "signIn": {
+    "title": "Welcome back",
+    "description": "Sign in to your workshop",
+    "passwordPlaceholder": "Enter your password",
+    "forgotPassword": "Forgot password?",
+    "noAccount": "Don't have an account?",
+    "createOne": "Create one",
+    "termsAgreement": "By using this service you agree to our",
+    "errors": {
+      "tooManyAttempts": "Too many attempts, try again later.",
+      "invalidCredentials": "Invalid email or password"
+    }
+  },
+  "signUp": {
+    "title": "Create an account",
+    "descriptionInvite": "Create your account to join the team",
+    "descriptionDefault": "Set up your workshop to manage customer vehicles",
+    "fullName": "Full Name",
+    "fullNamePlaceholder": "John Doe",
+    "passwordPlaceholder": "Create a strong password",
+    "agreeToTerms": "I agree to the",
+    "createAccountJoin": "Create Account & Join Team",
+    "createAccount": "Create Account",
+    "alreadyHaveAccount": "Already have an account?",
+    "errors": {
+      "couldNotCreate": "Could not create account",
+      "registrationDisabled": "Registration is currently disabled. Please contact your administrator.",
+      "invitationFailed": "Failed to accept invitation"
+    }
+  },
+  "forgotPassword": {
+    "title": "Reset your password",
+    "description": "Enter your email and we'll send you a reset link",
+    "sendResetLink": "Send Reset Link",
+    "backToSignIn": "Back to Sign In",
+    "successMessage": "If an account exists with that email, a password reset link has been sent. Check your inbox."
+  },
+  "resetPassword": {
+    "title": "Set new password",
+    "description": "Enter your new password below",
+    "newPassword": "New Password",
+    "newPasswordPlaceholder": "Enter new password",
+    "confirmPassword": "Confirm Password",
+    "confirmPasswordPlaceholder": "Confirm new password",
+    "resetButton": "Reset Password",
+    "backToSignIn": "Back to Sign In",
+    "requestNewLink": "Request New Link",
+    "invalidToken": "Invalid or missing reset token. Please request a new password reset link.",
+    "successMessage": "Your password has been reset successfully. You can now sign in with your new password.",
+    "errors": {
+      "passwordsMismatch": "Passwords do not match.",
+      "passwordTooShort": "Password must be at least 8 characters long.",
+      "resetFailed": "Could not reset password. The link may have expired."
+    }
+  },
+  "verify2fa": {
+    "title": "Two-Factor Authentication",
+    "descriptionTotp": "Enter the 6-digit code from your authenticator app",
+    "descriptionBackup": "Enter one of your backup codes",
+    "authCode": "Authentication Code",
+    "backupCode": "Backup Code",
+    "backupCodePlaceholder": "Enter backup code",
+    "totpPlaceholder": "000000",
+    "trustDevice": "Trust this device for 30 days",
+    "verify": "Verify",
+    "useAuthenticator": "Use authenticator app instead",
+    "useBackupCode": "Use a backup code",
+    "errors": {
+      "invalidCode": "Invalid code"
+    }
+  }
+}

+ 83 - 0
messages/en/billing.json

@@ -0,0 +1,83 @@
+{
+  "history": {
+    "title": "Billing History",
+    "recurring": "Recurring",
+    "totalRevenue": "Total Revenue",
+    "collected": "Collected",
+    "outstanding": "Outstanding",
+    "statusAll": "All",
+    "statusPaid": "Paid",
+    "statusPartial": "Partial",
+    "statusUnpaid": "Unpaid",
+    "searchPlaceholder": "Search invoices...",
+    "search": "Search",
+    "columnInvoice": "Invoice #",
+    "columnTitle": "Title",
+    "columnVehicle": "Vehicle",
+    "columnCustomer": "Customer",
+    "columnDate": "Date",
+    "columnTotal": "Total",
+    "columnPaid": "Paid",
+    "columnBalance": "Balance",
+    "noRecords": "No billing records found."
+  },
+  "recurring": {
+    "title": "Recurring",
+    "billingHistory": "Billing History",
+    "processNow": "Process Now",
+    "newRecurring": "New Recurring Invoice",
+    "noInvoices": "No recurring invoices yet.",
+    "columnTitle": "Title",
+    "columnVehicle": "Vehicle",
+    "columnCustomer": "Customer",
+    "columnFrequency": "Frequency",
+    "columnNextRun": "Next Run",
+    "columnAmount": "Amount",
+    "columnStatus": "Status",
+    "columnRuns": "Runs",
+    "statusActive": "Active",
+    "statusPaused": "Paused",
+    "frequencyWeekly": "Weekly",
+    "frequencyBiweekly": "Bi-weekly",
+    "frequencyMonthly": "Monthly",
+    "frequencyQuarterly": "Quarterly",
+    "frequencyYearly": "Yearly",
+    "serviceTypeMaintenance": "Maintenance",
+    "serviceTypeRepair": "Repair",
+    "serviceTypeInspection": "Inspection",
+    "serviceTypeUpgrade": "Upgrade",
+    "dialogTitle": "New Recurring Invoice",
+    "titleLabel": "Title *",
+    "titlePlaceholder": "e.g. Monthly Oil Change",
+    "vehicleLabel": "Vehicle *",
+    "selectVehicle": "Select vehicle",
+    "descriptionLabel": "Description",
+    "frequencyLabel": "Frequency",
+    "startDate": "Start Date *",
+    "endDate": "End Date",
+    "serviceType": "Service Type",
+    "baseCost": "Base Cost",
+    "taxRate": "Tax Rate (%)",
+    "invoiceNotes": "Invoice Notes",
+    "templateParts": "Template Parts",
+    "addPart": "Add Part",
+    "partName": "Part name",
+    "partNumber": "Part #",
+    "templateLabor": "Template Labor",
+    "addLabor": "Add Labor",
+    "description": "Description",
+    "hours": "Hours",
+    "rate": "Rate",
+    "cancel": "Cancel",
+    "create": "Create",
+    "requiredFields": "Title and vehicle are required",
+    "created": "Recurring invoice created",
+    "failedCreate": "Failed to create",
+    "statusUpdated": "Status updated",
+    "failedUpdate": "Failed to update",
+    "deleted": "Recurring invoice deleted",
+    "failedDelete": "Failed to delete",
+    "processed": "Processed {count} invoice(s)",
+    "failedProcess": "Failed to process"
+  }
+}

+ 61 - 0
messages/en/calendar.json

@@ -0,0 +1,61 @@
+{
+  "weekdays": {
+    "sun": "Sun",
+    "mon": "Mon",
+    "tue": "Tue",
+    "wed": "Wed",
+    "thu": "Thu",
+    "fri": "Fri",
+    "sat": "Sat"
+  },
+  "months": {
+    "january": "January",
+    "february": "February",
+    "march": "March",
+    "april": "April",
+    "may": "May",
+    "june": "June",
+    "july": "July",
+    "august": "August",
+    "september": "September",
+    "october": "October",
+    "november": "November",
+    "december": "December"
+  },
+  "stats": {
+    "services": "Services",
+    "overdue": "Overdue",
+    "pendingQuotes": "Pending Quotes",
+    "revenue": "Revenue"
+  },
+  "today": "Today",
+  "workOrder": "Work Order",
+  "filters": {
+    "services": "Services",
+    "reminders": "Reminders",
+    "quotes": "Quotes"
+  },
+  "selectVehicle": "Select Vehicle for Work Order",
+  "more": "+ {count} more",
+  "events": {
+    "status": {
+      "completed": "Completed",
+      "inProgress": "In Progress",
+      "waitingParts": "Waiting Parts",
+      "pending": "Pending",
+      "sent": "Sent",
+      "approved": "Approved",
+      "draft": "Draft",
+      "done": "Done",
+      "overdue": "Overdue",
+      "upcoming": "Upcoming"
+    },
+    "type": {
+      "service": "Service",
+      "reminder": "Reminder",
+      "quote": "Quote"
+    },
+    "noEvents": "No events",
+    "selectDay": "Select a day with events or create a new work order."
+  }
+}

+ 43 - 0
messages/en/common.json

@@ -0,0 +1,43 @@
+{
+  "brandName": "Torqvoice",
+  "buttons": {
+    "signIn": "Sign In",
+    "save": "Save",
+    "saveChanges": "Save Changes",
+    "cancel": "Cancel",
+    "delete": "Delete",
+    "submit": "Submit",
+    "back": "Back",
+    "confirm": "Confirm",
+    "edit": "Edit",
+    "add": "Add"
+  },
+  "form": {
+    "email": "Email",
+    "password": "Password",
+    "emailPlaceholder": "you@example.com",
+    "name": "Name",
+    "phone": "Phone",
+    "company": "Company",
+    "address": "Address",
+    "notes": "Notes"
+  },
+  "errors": {
+    "unexpected": "An unexpected error occurred. Please try again.",
+    "error": "Error"
+  },
+  "terms": {
+    "termsOfService": "Terms of Service"
+  },
+  "errorPage": {
+    "title": "Something Went Wrong",
+    "description": "The engine stalled unexpectedly. Our mechanics have been notified and are working on it.",
+    "tryAgain": "Try Again",
+    "backToDashboard": "Back to Dashboard"
+  },
+  "notFoundPage": {
+    "title": "Page Not Found",
+    "description": "Looks like this part is still in the shop. The page you're looking for doesn't exist or has been moved.",
+    "backToDashboard": "Back to Dashboard"
+  }
+}

+ 76 - 0
messages/en/customers.json

@@ -0,0 +1,76 @@
+{
+  "list": {
+    "addCustomer": "Add Customer",
+    "searchPlaceholder": "Search customers...",
+    "table": {
+      "name": "Name",
+      "company": "Company",
+      "phone": "Phone",
+      "email": "Email",
+      "vehicles": "Vehicles"
+    },
+    "emptySearch": "No customers match your search.",
+    "empty": "No customers yet.",
+    "deleteTitle": "Delete Customer",
+    "deleteDescription": "Delete {name}? This will unlink their vehicles.",
+    "deleteError": "Failed to delete customer",
+    "error": "Failed to load customers"
+  },
+  "detail": {
+    "backToCustomers": "Back to customers",
+    "editCustomer": "Edit Customer",
+    "notFound": "Customer not found",
+    "emailCopied": "Email copied to clipboard",
+    "phoneCopied": "Phone number copied to clipboard",
+    "tabs": {
+      "vehicles": "Vehicles ({count})",
+      "messages": "Messages",
+      "requests": "Requests ({count})"
+    },
+    "vehicleTable": {
+      "plate": "Plate",
+      "vehicle": "Vehicle",
+      "mileage": "Mileage",
+      "services": "Services"
+    },
+    "noVehicles": "No vehicles assigned to this customer"
+  },
+  "form": {
+    "addTitle": "Add New Customer",
+    "editTitle": "Edit Customer",
+    "nameRequired": "Name *",
+    "namePlaceholder": "John Doe",
+    "emailPlaceholder": "john@example.com",
+    "phonePlaceholder": "(555) 123-4567",
+    "companyPlaceholder": "Company name",
+    "addressPlaceholder": "123 Main St, City, State",
+    "notesPlaceholder": "Additional notes...",
+    "customerUpdated": "Customer updated",
+    "customerCreated": "Customer created",
+    "saveError": "Failed to save customer"
+  },
+  "serviceRequests": {
+    "statusNew": "New",
+    "statusConverted": "Work Order Created",
+    "statusDismissed": "Dismissed",
+    "createWorkOrder": "Create Work Order",
+    "contactCustomer": "Contact Customer",
+    "dismiss": "Dismiss",
+    "viewWorkOrder": "View Work Order",
+    "notes": "Notes",
+    "hideNotes": "Hide Notes",
+    "staffNotes": "Staff notes:",
+    "notesPlaceholder": "Add internal notes...",
+    "saveNotes": "Save Notes",
+    "submitted": "Submitted {date}",
+    "preferred": "Preferred: {date}",
+    "workOrderCreated": "Work order created",
+    "workOrderError": "Failed to create work order",
+    "requestDismissed": "Request dismissed",
+    "dismissError": "Failed to dismiss request",
+    "notesSaved": "Notes saved",
+    "notesSaveError": "Failed to save notes",
+    "smsLabel": "Service Request Message",
+    "smsDefault": "Hi {name}, regarding your service request for your {vehicle}: "
+  }
+}

+ 89 - 0
messages/en/dashboard.json

@@ -0,0 +1,89 @@
+{
+  "stats": {
+    "activeJobs": "Active Jobs",
+    "pending": "Pending",
+    "todaysRevenue": "Today's Revenue",
+    "totalCustomers": "Total Customers"
+  },
+  "customize": "Customize",
+  "showCards": "Show cards",
+  "cards": {
+    "maintenance": "Predicted Maintenance",
+    "reminders": "Upcoming Reminders",
+    "inspections": "Inspections",
+    "quoteRequests": "Quote Requests",
+    "quoteResponses": "Quote Responses",
+    "sms": "Recent Messages",
+    "notifications": "Recent Notifications",
+    "recentCompleted": "Recent Completed",
+    "activeJobs": "Active Jobs"
+  },
+  "maintenance": {
+    "title": "Predicted Maintenance Due",
+    "description": "Based on service history and estimated daily mileage",
+    "overdue": "Overdue",
+    "approaching": "Approaching",
+    "estimated": "Est. {mileage} {unit}",
+    "sinceLastService": "{mileage} {unit} since last service",
+    "certainty": "{percent}% certainty"
+  },
+  "reminders": {
+    "title": "Upcoming Reminders",
+    "overdue": "Overdue",
+    "dueSoon": "Due Soon"
+  },
+  "messages": {
+    "title": "Recent Messages",
+    "viewAll": "View All",
+    "you": "You: "
+  },
+  "notifications": {
+    "title": "Recent Notifications"
+  },
+  "inspections": {
+    "title": "Inspections",
+    "inProgress": "In Progress",
+    "completed": "Completed",
+    "itemCount": "{inspected}/{total} items"
+  },
+  "quoteRequests": {
+    "title": "Quote Requests",
+    "description": "Customers requesting quotes from shared inspections",
+    "pending": "Pending",
+    "createQuote": "Create Quote",
+    "itemsRequested": "{count, plural, one {# item requested} other {# items requested}}"
+  },
+  "quoteResponses": {
+    "title": "Customer Quote Responses",
+    "description": "Quotes that customers have accepted or requested changes on",
+    "accepted": "Accepted",
+    "changesRequested": "Changes Requested",
+    "convertToWorkOrder": "Convert to Work Order",
+    "viewQuote": "View Quote"
+  },
+  "recentCompleted": {
+    "title": "Recent Completed",
+    "date": "Date",
+    "vehicle": "Vehicle",
+    "customer": "Customer",
+    "serviceTitle": "Title",
+    "total": "Total",
+    "empty": "No completed services yet"
+  },
+  "activeJobsTable": {
+    "title": "Active Jobs",
+    "vehicle": "Vehicle",
+    "customer": "Customer",
+    "serviceTitle": "Title",
+    "status": "Status",
+    "tech": "Tech",
+    "empty": "No active jobs"
+  },
+  "relativeTime": {
+    "now": "now",
+    "minutes": "{count}m",
+    "hours": "{count}h",
+    "days": "{count}d"
+  },
+  "error": "Failed to load dashboard"
+}

+ 87 - 0
messages/en/inventory.json

@@ -0,0 +1,87 @@
+{
+  "searchPlaceholder": "Search parts...",
+  "categoryPlaceholder": "Category",
+  "allCategories": "All Categories",
+  "applyMarkup": "Apply Markup",
+  "addPart": "Add Part",
+  "table": {
+    "partNumber": "Part #",
+    "name": "Name",
+    "category": "Category",
+    "inStock": "In Stock",
+    "unitCost": "Unit Cost",
+    "sellPrice": "Sell Price",
+    "supplier": "Supplier",
+    "location": "Location",
+    "low": "Low"
+  },
+  "empty": {
+    "noMatch": "No parts match your search.",
+    "noParts": "No parts in inventory yet."
+  },
+  "actions": {
+    "edit": "Edit",
+    "delete": "Delete"
+  },
+  "deletePart": {
+    "title": "Delete Part",
+    "description": "Delete \"{name}\" from inventory? This cannot be undone.",
+    "confirm": "Delete"
+  },
+  "markup": {
+    "title": "Apply Markup to All Parts",
+    "multiplierLabel": "Markup Multiplier",
+    "description": "This will set sell price = cost x {multiplier} for all {count} parts.",
+    "percentage": "({percent}% markup)",
+    "cancel": "Cancel",
+    "apply": "Apply"
+  },
+  "errors": {
+    "multiplierPositive": "Multiplier must be greater than 0",
+    "deleteFailed": "Failed to delete part",
+    "applyFailed": "Failed to apply markup",
+    "saveFailed": "Failed to save part",
+    "error": "Error"
+  },
+  "form": {
+    "editPart": "Edit Part",
+    "addNewPart": "Add New Part",
+    "supplierLink": "Supplier Link",
+    "supplierLinkPlaceholder": "https://supplier.com/product/...",
+    "fetch": "Fetch",
+    "fetchFailed": "Fetch Failed",
+    "fetchError": "Could not fetch metadata from URL",
+    "image": "Image",
+    "dropOrClick": "Drop image or click",
+    "replace": "Replace",
+    "upload": "Upload",
+    "imageUploading": "Uploading image...",
+    "imageUploaded": "Image uploaded",
+    "imageUploadFailed": "Could not upload image",
+    "imageTypeError": "Only JPEG, PNG, WebP, and AVIF images are allowed",
+    "imageSizeError": "Image must be under 5MB",
+    "partNumber": "Part Number",
+    "partNumberPlaceholder": "BRK-001",
+    "nameLabel": "Name *",
+    "namePlaceholder": "Brake Pad Set",
+    "category": "Category",
+    "categoryPlaceholder": "Brakes",
+    "location": "Location",
+    "locationPlaceholder": "Shelf A-3",
+    "quantity": "Quantity",
+    "minQty": "Min Qty",
+    "unitCost": "Unit Cost",
+    "sellPrice": "Sell Price",
+    "supplier": "Supplier",
+    "supplierPlaceholder": "AutoParts Inc.",
+    "supplierPhone": "Supplier Phone",
+    "supplierPhonePlaceholder": "(555) 123-4567",
+    "supplierEmail": "Supplier Email",
+    "supplierEmailPlaceholder": "orders@autoparts.com",
+    "description": "Description",
+    "descriptionPlaceholder": "Part description...",
+    "cancel": "Cancel",
+    "saveChanges": "Save Changes",
+    "addPart": "Add Part"
+  }
+}

+ 45 - 0
messages/en/messages.json

@@ -0,0 +1,45 @@
+{
+  "page": {
+    "featureName": "SMS Messaging",
+    "featureDescription": "Send and receive SMS messages with your customers directly from Torqvoice."
+  },
+  "notConfigured": {
+    "title": "SMS Not Configured",
+    "description": "Set up an SMS provider to send and receive messages with your customers. Supports Twilio, Vonage, and Telnyx.",
+    "goToSettings": "Go to SMS Settings"
+  },
+  "threads": {
+    "title": "Conversations",
+    "empty": "No conversations yet. Send an SMS from a customer page to start.",
+    "loadMore": "Load more",
+    "selectConversation": "Select a conversation",
+    "you": "You: ",
+    "deleteConversation": "Delete conversation",
+    "deleteTitle": "Delete conversation?",
+    "deleteDescription": "This will permanently delete all messages with {name}. This action cannot be undone.",
+    "deleted": "Conversation deleted",
+    "deleteError": "Failed to delete conversation"
+  },
+  "conversation": {
+    "noPhone": "No phone number on file for {name}. Add a phone number to start messaging.",
+    "loadOlder": "Load older",
+    "empty": "No messages yet. Send the first message to {name}.",
+    "placeholder": "Message {name}...",
+    "deleteMessage": "Delete message",
+    "deleteTitle": "Delete message?",
+    "deleteDescription": "This will permanently delete this message from the conversation. This action cannot be undone.",
+    "messageDeleted": "Message deleted",
+    "deleteError": "Failed to delete message",
+    "sendError": "Failed to send SMS"
+  },
+  "sendDialog": {
+    "send": "Send {label}",
+    "message": "Message",
+    "placeholder": "Type your message...",
+    "characters": "{count} / 1600 characters",
+    "sendSms": "Send SMS",
+    "smsSent": "SMS Sent",
+    "sentTo": "{label} has been sent to {name} ({phone})",
+    "sendError": "Failed to send SMS"
+  }
+}

+ 85 - 0
messages/en/navigation.json

@@ -0,0 +1,85 @@
+{
+  "breadcrumbs": {
+    "dashboard": "Dashboard",
+    "vehicles": "Vehicles",
+    "allVehicles": "All Vehicles",
+    "vehicleDetails": "Vehicle Details",
+    "customers": "Customers",
+    "allCustomers": "All Customers",
+    "customerDetails": "Customer Details",
+    "workOrders": "Work Orders",
+    "allWorkOrders": "All Work Orders",
+    "quotes": "Quotes",
+    "allQuotes": "All Quotes",
+    "newQuote": "New Quote",
+    "quoteDetails": "Quote Details",
+    "edit": "Edit",
+    "billing": "Billing",
+    "billingHistory": "Billing History",
+    "inventory": "Inventory",
+    "allParts": "All Parts",
+    "reports": "Reports",
+    "workBoard": "Work Board",
+    "presenter": "Presenter",
+    "adminOverview": "Admin Overview",
+    "admin": "Admin",
+    "users": "Users",
+    "organizations": "Organizations",
+    "settings": "Settings",
+    "company": "Company",
+    "account": "Account",
+    "customFields": "Custom Fields",
+    "templates": "Templates",
+    "team": "Team",
+    "invoice": "Invoice",
+    "payment": "Payment",
+    "currency": "Currency",
+    "workshop": "Workshop",
+    "appearance": "Appearance",
+    "email": "Email",
+    "sms": "SMS",
+    "about": "About",
+    "home": "Home",
+    "newServiceRecord": "New Service Record",
+    "serviceDetails": "Service Details",
+    "data": "Data",
+    "license": "License",
+    "subscription": "Subscription",
+    "maintenance": "Maintenance",
+    "customerPortal": "Customer Portal"
+  },
+  "search": "Search...",
+  "shortcut": "Ctrl+K",
+  "newWorkOrder": "New Work Order",
+  "purchaseWhiteLabel": "Purchase White-Label",
+  "sidebar": {
+    "dashboard": "Dashboard",
+    "clients": "Clients",
+    "customers": "Customers",
+    "messages": "Messages",
+    "workshop": "Workshop",
+    "vehicles": "Vehicles",
+    "workOrders": "Work Orders",
+    "inspections": "Inspections",
+    "calendar": "Calendar",
+    "workBoard": "Work Board",
+    "business": "Business",
+    "quotes": "Quotes",
+    "billing": "Billing",
+    "inventory": "Inventory",
+    "reports": "Reports",
+    "settings": "Settings",
+    "superAdmin": "Super Admin",
+    "adminPanel": "Admin Panel",
+    "organizations": "Organizations",
+    "noOrganization": "No organization",
+    "addNewCompany": "Add New Company",
+    "companyName": "Company Name",
+    "companyPlaceholder": "e.g. Joe's Auto Repair",
+    "cancel": "Cancel",
+    "createCompany": "Create Company",
+    "lightMode": "Light Mode",
+    "darkMode": "Dark Mode",
+    "signOut": "Sign Out"
+  }
+}

+ 10 - 0
messages/en/notifications.json

@@ -0,0 +1,10 @@
+{
+  "panel": {
+    "title": "Notifications",
+    "markAllRead": "Mark all read",
+    "empty": "No notifications yet",
+    "confirmDelete": "Are you sure?",
+    "delete": "Delete",
+    "cancel": "Cancel"
+  }
+}

+ 99 - 0
messages/en/pdf.json

@@ -0,0 +1,99 @@
+{
+  "invoice": {
+    "title": "INVOICE",
+    "due": "Due: {date}",
+    "billTo": "Bill To",
+    "vehicle": "Vehicle",
+    "vin": "VIN: {vin}",
+    "plate": "Plate: {plate}",
+    "mileage": "Mileage: {mileage}",
+    "km": "km",
+    "mi": "mi",
+    "service": "Service",
+    "type": "Type: {type}",
+    "tech": "Tech: {tech}",
+    "parts": "Parts",
+    "partNumber": "Part #",
+    "description": "Description",
+    "qty": "Qty",
+    "unitPrice": "Unit Price",
+    "total": "Total",
+    "labor": "Labor",
+    "hours": "Hours",
+    "rate": "Rate",
+    "ratePerHour": "{rate}/hr",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "discountPercent": "Discount ({percent}%)",
+    "tax": "Tax ({rate}%)",
+    "paymentsReceived": "Payments Received",
+    "paidInFull": "PAID IN FULL",
+    "amountDue": "Amount Due",
+    "notes": "Notes",
+    "bankAccount": "Til Konto / Bank Account",
+    "diagnosticNotes": "Diagnostic Notes",
+    "attachedDocuments": "Attached Documents",
+    "seeAppendedPages": "{name} (see appended pages)",
+    "serviceImages": "Service Images",
+    "paymentTerms": "Payment Terms: {terms}",
+    "viewPortal": "View your portal: {url}",
+    "professionalWorkshop": "Professional Workshop Management",
+    "tel": "Tel: {phone}",
+    "org": "Org: {org}"
+  },
+  "quote": {
+    "title": "QUOTE",
+    "to": "To",
+    "vehicle": "Vehicle",
+    "vin": "VIN: {vin}",
+    "plate": "Plate: {plate}",
+    "quoteDetails": "Quote Details",
+    "parts": "Parts",
+    "partNumber": "Part #",
+    "description": "Description",
+    "qty": "Qty",
+    "unitPrice": "Unit Price",
+    "total": "Total",
+    "labor": "Labor",
+    "hours": "Hours",
+    "rate": "Rate",
+    "ratePerHour": "{rate}/hr",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "discountPercent": "Discount ({percent}%)",
+    "tax": "Tax ({rate}%)",
+    "validUntil": "Valid until: {date}",
+    "attachedDocuments": "Attached Documents",
+    "attached": "{name} (attached)",
+    "quoteImages": "Quote Images",
+    "validityFooterUntil": "This quote is valid until {date}",
+    "validityFooter30": "This quote is valid for 30 days",
+    "viewPortal": "View your portal: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "VEHICLE INSPECTION",
+    "vehicle": "Vehicle",
+    "vin": "VIN: {vin}",
+    "plate": "Plate: {plate}",
+    "mileage": "Mileage: {mileage}",
+    "customer": "Customer",
+    "inspectionDetails": "Inspection Details",
+    "statusCompleted": "Completed",
+    "statusInProgress": "In Progress",
+    "status": "Status: {status}",
+    "inspected": "Inspected",
+    "pass": "Pass",
+    "attention": "Attention",
+    "fail": "Fail",
+    "item": "Item",
+    "statusColumn": "Status",
+    "notesColumn": "Notes",
+    "footerText": "Vehicle Inspection — {shopName}",
+    "viewPortal": "View your portal: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "common": {
+    "poweredBy": "Powered by"
+  }
+}

+ 133 - 0
messages/en/portal.json

@@ -0,0 +1,133 @@
+{
+  "login": {
+    "signInTitle": "Sign in to your customer portal",
+    "emailLabel": "Email address",
+    "emailPlaceholder": "you@example.com",
+    "sendLink": "Send sign-in link",
+    "checkEmail": "Check your email",
+    "checkEmailDescription": "If an account exists for that email, we've sent a sign-in link. The link expires in 15 minutes.",
+    "tryDifferentEmail": "Try a different email",
+    "notFound": "Not Found",
+    "portalNotExist": "This portal does not exist.",
+    "portalNotAvailable": "Portal Not Available",
+    "portalNotEnabled": "The customer portal is not enabled for this organization."
+  },
+  "nav": {
+    "dashboard": "Dashboard",
+    "vehicles": "Vehicles",
+    "invoices": "Invoices",
+    "quotes": "Quotes",
+    "inspections": "Inspections",
+    "requestService": "Request Service"
+  },
+  "header": {
+    "signOut": "Sign out"
+  },
+  "shell": {
+    "notFound": "Not Found",
+    "portalNotExist": "This portal does not exist.",
+    "portalNotAvailable": "Portal Not Available",
+    "portalNotEnabled": "The customer portal is not enabled for this organization."
+  },
+  "dashboard": {
+    "welcome": "Welcome, {name}",
+    "overview": "Here's an overview of your account.",
+    "vehicles": "Vehicles",
+    "openInvoices": "Open Invoices",
+    "pendingQuotes": "Pending Quotes",
+    "recentInvoices": "Recent Invoices",
+    "recentQuotes": "Recent Quotes",
+    "pendingServiceRequests": "Pending Service Requests",
+    "viewAll": "View all",
+    "newRequest": "New request",
+    "noInvoicesYet": "No invoices yet.",
+    "noQuotesYet": "No quotes yet.",
+    "view": "View",
+    "pending": "Pending",
+    "failedToLoad": "Failed to load dashboard."
+  },
+  "invoices": {
+    "title": "Invoices",
+    "description": "All your service invoices.",
+    "noInvoices": "No invoices yet.",
+    "failedToLoad": "Failed to load invoices.",
+    "invoice": "Invoice",
+    "vehicle": "Vehicle",
+    "date": "Date",
+    "amount": "Amount",
+    "payment": "Payment",
+    "view": "View",
+    "paid": "paid",
+    "partial": "partial",
+    "unpaid": "unpaid"
+  },
+  "quotes": {
+    "title": "Quotes",
+    "description": "Quotes and estimates for your vehicles.",
+    "noQuotes": "No quotes yet.",
+    "failedToLoad": "Failed to load quotes.",
+    "quote": "Quote",
+    "vehicle": "Vehicle",
+    "status": "Status",
+    "amount": "Amount",
+    "validUntil": "Valid Until",
+    "view": "View"
+  },
+  "inspections": {
+    "title": "Inspections",
+    "description": "Vehicle inspection reports.",
+    "noInspections": "No inspections yet.",
+    "failedToLoad": "Failed to load inspections.",
+    "vehicle": "Vehicle",
+    "template": "Template",
+    "status": "Status",
+    "date": "Date",
+    "summary": "Summary",
+    "view": "View",
+    "good": "{count} good",
+    "fair": "{count} fair",
+    "poor": "{count} poor"
+  },
+  "vehicles": {
+    "title": "Vehicles",
+    "description": "Your registered vehicles.",
+    "noVehicles": "No vehicles found.",
+    "failedToLoad": "Failed to load vehicles.",
+    "plate": "Plate: {plate}",
+    "vin": "VIN: {vin}",
+    "serviceCount": "{count, plural, one {# service} other {# services}}",
+    "inspectionCount": "{count, plural, one {# inspection} other {# inspections}}",
+    "serviceHistory": "Service History ({count})",
+    "inspectionsTab": "Inspections ({count})",
+    "noServiceRecords": "No service records yet.",
+    "noInspections": "No inspections yet.",
+    "vehicleNotFound": "Vehicle not found.",
+    "completed": "Completed {date}",
+    "good": "{count} good",
+    "fair": "{count} fair",
+    "poor": "{count} poor",
+    "view": "View"
+  },
+  "requestService": {
+    "title": "Request Service",
+    "description": "Submit a service request for one of your vehicles.",
+    "newServiceRequest": "New Service Request",
+    "formDescription": "Describe the issue and we'll get back to you.",
+    "vehicleLabel": "Vehicle",
+    "vehiclePlaceholder": "Select a vehicle",
+    "descriptionLabel": "Describe the issue",
+    "descriptionPlaceholder": "What's going on with your vehicle?",
+    "preferredDate": "Preferred date",
+    "preferredDateOptional": "(optional)",
+    "submitRequest": "Submit Request",
+    "selectVehicle": "Please select a vehicle",
+    "describeIssue": "Please describe the issue",
+    "submitSuccess": "Service request submitted",
+    "submitError": "Failed to submit request",
+    "noVehicles": "You have no vehicles registered. Please contact us to add a vehicle first.",
+    "yourRequests": "Your Service Requests",
+    "failedToLoad": "Failed to load data.",
+    "staffNotes": "Staff notes: {notes}",
+    "preferred": "Preferred: {date}"
+  }
+}

+ 173 - 0
messages/en/quotes.json

@@ -0,0 +1,173 @@
+{
+  "list": {
+    "statusAll": "All",
+    "statusDraft": "Draft",
+    "statusSent": "Sent",
+    "statusAccepted": "Accepted",
+    "statusRejected": "Rejected",
+    "statusConverted": "Converted",
+    "searchPlaceholder": "Search quotes...",
+    "newQuote": "New Quote",
+    "columnQuoteNumber": "Quote #",
+    "columnTitle": "Title",
+    "columnCustomer": "Customer",
+    "columnVehicle": "Vehicle",
+    "columnStatus": "Status",
+    "columnDate": "Date",
+    "columnTotal": "Total",
+    "noQuotes": "No quotes found."
+  },
+  "page": {
+    "quote": "Quote",
+    "save": "Save",
+    "pdf": "PDF",
+    "email": "Email",
+    "share": "Share",
+    "delete": "Delete",
+    "deleteTitle": "Delete Quote",
+    "deleteDescription": "This will permanently delete this quote.",
+    "saved": "Quote saved",
+    "failedSave": "Failed to save quote",
+    "failedDelete": "Failed to delete",
+    "failedPdf": "Failed to generate PDF",
+    "selectVehicle": "Please select a vehicle",
+    "convertTitle": "Convert Quote to Work Order",
+    "convertDescription": "This will create a new service record (work order) from this quote. Select a vehicle for the work order.",
+    "convert": "Convert",
+    "convertToWorkOrder": "Convert to Work Order",
+    "cancel": "Cancel",
+    "failedConvert": "Failed to convert",
+    "changesRequested": "Changes Requested",
+    "quoteAccepted": "Quote Accepted",
+    "markResolved": "Mark as Resolved",
+    "responseResolved": "Customer response resolved",
+    "entityLabel": "Quote",
+    "tabs": {
+      "details": "Details",
+      "images": "Images",
+      "documents": "Documents"
+    }
+  },
+  "form": {
+    "newQuote": "New Quote",
+    "editQuote": "Edit Quote",
+    "cancel": "Cancel",
+    "createQuote": "Create Quote",
+    "updateQuote": "Update Quote",
+    "created": "Quote created",
+    "updated": "Quote updated",
+    "failedCreate": "Failed to create quote",
+    "failedUpdate": "Failed to update quote"
+  },
+  "parts": {
+    "title": "Parts",
+    "addPart": "Add Part",
+    "partNumber": "Part #",
+    "name": "Name",
+    "namePlaceholder": "Name *",
+    "qty": "Qty",
+    "unitPrice": "Unit Price",
+    "total": "Total",
+    "subtotal": "Parts Subtotal: {amount}"
+  },
+  "labor": {
+    "title": "Labor",
+    "addLabor": "Add Labor",
+    "description": "Description",
+    "descriptionPlaceholder": "Description *",
+    "hours": "Hours",
+    "rate": "Rate ({currency}/hr)",
+    "total": "Total",
+    "subtotal": "Labor Subtotal: {amount}"
+  },
+  "notes": {
+    "title": "Notes",
+    "public": "Public",
+    "internal": "Internal",
+    "publicPlaceholder": "Quote description shown to the customer...",
+    "internalPlaceholder": "Internal notes (not shown to customer)...",
+    "publicHelper": "Shown on the quote document",
+    "internalHelper": "Only visible to your team"
+  },
+  "details": {
+    "title": "Quote Details",
+    "titleLabel": "Title *",
+    "titlePlaceholder": "Vehicle repair estimate",
+    "status": "Status",
+    "statusDraft": "Draft",
+    "statusSent": "Sent",
+    "statusAccepted": "Accepted",
+    "statusRejected": "Rejected",
+    "validUntil": "Valid Until",
+    "viewInspection": "View linked inspection",
+    "vehicle": "Vehicle",
+    "customer": "Customer",
+    "selectVehicle": "Select vehicle...",
+    "selectCustomer": "Select customer...",
+    "none": "None"
+  },
+  "totals": {
+    "title": "Totals",
+    "parts": "Parts",
+    "labor": "Labor",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "discountNone": "None",
+    "discountPercentage": "Percentage",
+    "discountFixed": "Fixed",
+    "tax": "Tax",
+    "total": "Total"
+  },
+  "shareDialog": {
+    "title": "Share Quote",
+    "description": "Generate a public link that allows anyone to view and download this quote without signing in.",
+    "notifyTitle": "Notify {name}",
+    "email": "Email",
+    "noEmail": "(no email on file)",
+    "sms": "SMS",
+    "noPhone": "(no phone on file)",
+    "sendNotification": "Send Notification",
+    "revokeLink": "Revoke Link",
+    "generateLink": "Generate Public Link",
+    "emailSent": "Email sent",
+    "failedEmail": "Failed to send email",
+    "smsSent": "SMS sent",
+    "failedSms": "Failed to send SMS"
+  },
+  "images": {
+    "title": "Quote Images",
+    "dropzone": "Drop images here or click to browse",
+    "uploading": "Uploading...",
+    "formats": "JPG, PNG, WebP — max 10MB each",
+    "uploadingImages": "Uploading images...",
+    "limitReached": "Image limit reached ({count}/{max})",
+    "upgradePrompt": "Upgrade your plan to upload more images.",
+    "onlyAllowed": "Only JPG, PNG, and WebP images are allowed. {names} skipped.",
+    "onlyUploading": "Only uploading {count} image(s) to stay within limit.",
+    "uploadingCount": "Uploading {count} image(s)...",
+    "uploadedCount": "{count} image(s) uploaded",
+    "uploadFailed": "Upload failed",
+    "deleted": "Image deleted",
+    "failedDelete": "Failed to delete image",
+    "failedUpdate": "Failed to update",
+    "description": "Description...",
+    "pdf": "PDF"
+  },
+  "documents": {
+    "title": "Documents",
+    "dropzone": "Drop files here or click to browse",
+    "uploading": "Uploading...",
+    "formats": "PDF, CSV, TXT — max 10MB each",
+    "uploadingDocuments": "Uploading documents...",
+    "limitReached": "Document limit reached ({count}/{max})",
+    "upgradePrompt": "Upgrade your plan to upload more.",
+    "onlyUploading": "Only uploading {count} file(s) to stay within limit.",
+    "uploadingCount": "Uploading {count} file(s)...",
+    "uploadedCount": "{count} file(s) uploaded",
+    "uploadFailed": "Upload failed",
+    "deleted": "File deleted",
+    "failedDelete": "Failed to delete file",
+    "failedUpdate": "Failed to update",
+    "pdf": "PDF"
+  }
+}

+ 199 - 0
messages/en/reports.json

@@ -0,0 +1,199 @@
+{
+  "title": "Reports & Analytics",
+  "tabs": {
+    "revenue": "Revenue",
+    "services": "Services",
+    "customers": "Customers",
+    "inventory": "Inventory",
+    "technicians": "Technicians",
+    "parts": "Parts",
+    "jobAnalytics": "Job Analytics",
+    "retention": "Retention",
+    "tax": "Tax"
+  },
+  "dateRange": {
+    "to": "to"
+  },
+  "actions": {
+    "refresh": "Refresh",
+    "csv": "CSV"
+  },
+  "revenue": {
+    "revenue": "Revenue",
+    "collected": "Collected",
+    "outstanding": "Outstanding",
+    "services": "Services",
+    "monthlyRevenue": "Monthly Revenue",
+    "revenueByType": "Revenue by Type",
+    "monthlyBreakdown": "Monthly Breakdown",
+    "tableHeaders": {
+      "month": "Month",
+      "revenue": "Revenue",
+      "collected": "Collected",
+      "count": "Count"
+    }
+  },
+  "services": {
+    "totalServices": "Total Services",
+    "byStatus": "By Status",
+    "byType": "By Type"
+  },
+  "customers": {
+    "total": "Total",
+    "active": "Active",
+    "topCustomersBySpend": "Top Customers by Spend",
+    "topCustomers": "Top Customers",
+    "tableHeaders": {
+      "name": "Name",
+      "company": "Company",
+      "services": "Services",
+      "totalSpent": "Total Spent"
+    }
+  },
+  "inventory": {
+    "parts": "Parts",
+    "items": "Items",
+    "costValue": "Cost Value",
+    "sellValue": "Sell Value",
+    "potentialMargin": "Potential Margin",
+    "lowStock": "Low Stock",
+    "tableHeaders": {
+      "name": "Name",
+      "partNumber": "Part #",
+      "qty": "Qty",
+      "minQty": "Min Qty"
+    }
+  },
+  "technicians": {
+    "totalJobs": "Total Jobs",
+    "totalRevenue": "Total Revenue",
+    "revenueByTechnician": "Revenue by Technician",
+    "technicianBreakdown": "Technician Breakdown",
+    "tableHeaders": {
+      "technician": "Technician",
+      "jobs": "Jobs",
+      "revenue": "Revenue",
+      "avgRevenue": "Avg Revenue",
+      "totalHours": "Total Hours",
+      "avgHours": "Avg Hours"
+    }
+  },
+  "parts": {
+    "partsUsed": "Parts Used",
+    "partsRevenue": "Parts Revenue",
+    "topPartsByUsage": "Top Parts by Usage",
+    "partsUsage": "Parts Usage",
+    "tableHeaders": {
+      "partName": "Part Name",
+      "partNumber": "Part #",
+      "usageCount": "Usage Count",
+      "totalQty": "Total Qty",
+      "revenue": "Revenue"
+    }
+  },
+  "jobAnalytics": {
+    "totalJobs": "Total Jobs",
+    "avgJobValue": "Avg Job Value",
+    "jobsByDayOfWeek": "Jobs by Day of Week",
+    "serviceTypes": "Service Types",
+    "monthlyTrend": "Monthly Trend",
+    "serviceTypeBreakdown": "Service Type Breakdown",
+    "tableHeaders": {
+      "type": "Type",
+      "count": "Count",
+      "avgValue": "Avg Value",
+      "avgHours": "Avg Hours"
+    }
+  },
+  "retention": {
+    "returning": "Returning",
+    "new": "New",
+    "totalActive": "Total Active",
+    "avgDaysBetween": "Avg Days Between",
+    "topReturningCustomers": "Top Returning Customers",
+    "returningCustomers": "Returning Customers",
+    "tableHeaders": {
+      "customer": "Customer",
+      "company": "Company",
+      "visits": "Visits",
+      "totalSpent": "Total Spent",
+      "avgDaysBetween": "Avg Days Between"
+    }
+  },
+  "tax": {
+    "totalTaxCollected": "Total Tax Collected",
+    "taxableRevenue": "Taxable Revenue",
+    "invoicesWithTax": "Invoices with Tax",
+    "monthlyTaxCollected": "Monthly Tax Collected",
+    "taxByRate": "Tax by Rate",
+    "monthlyBreakdown": "Monthly Breakdown",
+    "tableHeaders": {
+      "taxRate": "Tax Rate",
+      "taxCollected": "Tax Collected",
+      "invoices": "Invoices",
+      "month": "Month",
+      "taxableAmount": "Taxable Amount"
+    }
+  },
+  "charts": {
+    "revenue": "Revenue",
+    "collected": "Collected",
+    "count": "Count",
+    "totalSpent": "Total Spent",
+    "jobs": "Jobs",
+    "visits": "Visits",
+    "taxCollected": "Tax Collected"
+  },
+  "days": {
+    "sunday": "Sunday",
+    "monday": "Monday",
+    "tuesday": "Tuesday",
+    "wednesday": "Wednesday",
+    "thursday": "Thursday",
+    "friday": "Friday",
+    "saturday": "Saturday"
+  },
+  "daysShort": {
+    "sun": "Sun",
+    "mon": "Mon",
+    "tue": "Tue",
+    "wed": "Wed",
+    "thu": "Thu",
+    "fri": "Fri",
+    "sat": "Sat"
+  },
+  "csvHeaders": {
+    "month": "Month",
+    "revenue": "Revenue",
+    "collected": "Collected",
+    "count": "Count",
+    "category": "Category",
+    "label": "Label",
+    "name": "Name",
+    "company": "Company",
+    "services": "Services",
+    "totalSpent": "Total Spent",
+    "partNumber": "Part #",
+    "quantity": "Quantity",
+    "minQuantity": "Min Quantity",
+    "unitCost": "Unit Cost",
+    "technician": "Technician",
+    "jobs": "Jobs",
+    "totalRevenue": "Total Revenue",
+    "avgRevenue": "Avg Revenue",
+    "totalHours": "Total Hours",
+    "avgHours": "Avg Hours",
+    "partName": "Part Name",
+    "usageCount": "Usage Count",
+    "totalQty": "Total Qty",
+    "serviceType": "Service Type",
+    "avgValue": "Avg Value",
+    "customer": "Customer",
+    "visits": "Visits",
+    "avgDaysBetweenVisits": "Avg Days Between Visits",
+    "taxCollected": "Tax Collected",
+    "taxableAmount": "Taxable Amount",
+    "invoiceCount": "Invoice Count"
+  },
+  "empty": "No data loaded yet. Click Refresh to load the report."
+}

+ 240 - 0
messages/en/service.json

@@ -0,0 +1,240 @@
+{
+  "page": {
+    "notFound": "Service record not found",
+    "updated": "Service record updated",
+    "failedUpdate": "Failed to update service record",
+    "deleteTitle": "Delete Service Record",
+    "deleteDescription": "This will permanently delete this service record and all associated data. This cannot be undone.",
+    "deleted": "Service record deleted",
+    "failedDelete": "Failed to delete"
+  },
+  "header": {
+    "tabs": {
+      "details": "Details",
+      "images": "Images",
+      "video": "Video",
+      "documents": "Documents"
+    },
+    "save": "Save",
+    "pdf": "PDF",
+    "email": "Email",
+    "share": "Share",
+    "delete": "Delete",
+    "unpaid": "Unpaid"
+  },
+  "basicInfo": {
+    "title": "Basic Information",
+    "vehicle": "Vehicle",
+    "open": "Open",
+    "customer": "Customer",
+    "titleLabel": "Title *",
+    "titlePlaceholder": "Oil Change",
+    "date": "Date",
+    "type": "Type",
+    "mileage": "Mileage",
+    "status": "Status",
+    "technician": "Technician",
+    "invoiceNumber": "Invoice Number",
+    "typeOptions": {
+      "maintenance": "Maintenance",
+      "repair": "Repair",
+      "upgrade": "Upgrade",
+      "inspection": "Inspection"
+    },
+    "statusOptions": {
+      "pending": "Pending",
+      "in_progress": "In Progress",
+      "waiting_parts": "Waiting Parts",
+      "completed": "Completed"
+    },
+    "searchVehicles": "Search vehicles...",
+    "noVehicleFound": "No vehicle found.",
+    "techPicker": {
+      "placeholder": "Select or type a name...",
+      "searchPlaceholder": "Search or type name...",
+      "useCustom": "Use \"{search}\"",
+      "typePrompt": "Type a name...",
+      "boardTechnicians": "Board Technicians",
+      "teamMembers": "Team Members"
+    }
+  },
+  "parts": {
+    "title": "Parts",
+    "fromInventory": "From Inventory",
+    "addPart": "Add Part",
+    "partNumber": "Part #",
+    "name": "Name",
+    "qty": "Qty",
+    "unitPrice": "Unit Price",
+    "total": "Total",
+    "namePlaceholder": "Name *",
+    "subtotal": "Parts Subtotal: {amount}",
+    "inventoryTitle": "Select Part from Inventory",
+    "searchInventory": "Search inventory...",
+    "inStock": "{quantity} in stock",
+    "noInventory": "No inventory parts found."
+  },
+  "labor": {
+    "title": "Labor",
+    "addLabor": "Add Labor",
+    "description": "Description",
+    "hours": "Hours",
+    "rate": "Rate ({currency}/hr)",
+    "total": "Total",
+    "descriptionPlaceholder": "Description *",
+    "subtotal": "Labor Subtotal: {amount}"
+  },
+  "totals": {
+    "title": "Totals",
+    "parts": "Parts",
+    "labor": "Labor",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "discountNone": "None",
+    "discountPercentage": "Percentage",
+    "discountFixed": "Fixed",
+    "tax": "Tax",
+    "total": "Total"
+  },
+  "notes": {
+    "title": "Notes",
+    "public": "Public",
+    "internal": "Internal",
+    "publicPlaceholder": "Notes visible on the invoice...",
+    "internalPlaceholder": "Internal notes (not shown on invoice)...",
+    "publicHelper": "Shown on the invoice and shared documents",
+    "internalHelper": "Only visible to your team"
+  },
+  "invoice": {
+    "title": "Invoice Summary",
+    "parts": "Parts",
+    "labor": "Labor",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "tax": "Tax ({rate}%)",
+    "total": "Total",
+    "paid": "Paid",
+    "balanceDue": "Balance Due",
+    "paidBadge": "PAID",
+    "entityLabel": "Invoice",
+    "emailSubject": "Payment Confirmation"
+  },
+  "payments": {
+    "title": "Payments",
+    "markUnpaid": "Mark as Unpaid",
+    "markPaid": "Mark as Paid",
+    "recordPayment": "Record Payment",
+    "totalPaid": "Total Paid",
+    "amount": "Amount",
+    "date": "Date",
+    "method": "Method",
+    "note": "Note",
+    "notePlaceholder": "Optional note",
+    "savePayment": "Save Payment",
+    "methodOptions": {
+      "cash": "Cash",
+      "card": "Card",
+      "transfer": "Transfer",
+      "other": "Other"
+    },
+    "recorded": "Payment recorded",
+    "markedPaid": "Marked as paid",
+    "markedUnpaid": "Marked as unpaid",
+    "deleted": "Payment deleted",
+    "failedRecord": "Failed to record payment",
+    "failedStatus": "Failed to update payment status",
+    "failedDelete": "Failed to delete payment",
+    "deleteTitle": "Delete Payment",
+    "deleteDescription": "This will remove this payment record.",
+    "failedPdf": "Failed to generate PDF invoice"
+  },
+  "attachments": {
+    "deleteTitle": "Delete Attachment",
+    "deleteDescription": "This will permanently delete this attachment. This cannot be undone.",
+    "deleted": "Attachment deleted",
+    "failedDelete": "Failed to delete",
+    "images": "Images ({count})",
+    "videos": "Videos ({count})",
+    "diagnostics": "Diagnostic Reports ({count})",
+    "documents": "Documents ({count})"
+  },
+  "share": {
+    "title": "Share Invoice",
+    "description": "Generate a public link that allows anyone to view and download this invoice without signing in.",
+    "notifyTitle": "Notify {name}",
+    "email": "Email",
+    "noEmail": "(no email on file)",
+    "sms": "SMS",
+    "noPhone": "(no phone on file)",
+    "sendNotification": "Send Notification",
+    "revokeLink": "Revoke Link",
+    "generateLink": "Generate Public Link",
+    "emailSent": "Email sent",
+    "failedEmail": "Failed to send email",
+    "smsSent": "SMS sent",
+    "failedSms": "Failed to send SMS"
+  },
+  "sidebar": {
+    "vehicle": "Vehicle",
+    "customer": "Customer",
+    "vin": "VIN: {vin}",
+    "mileageAtService": "Mileage at service: {mileage} {unit}"
+  },
+  "images": {
+    "title": "Service Images",
+    "dropzone": "Drop images here or click to browse",
+    "uploading": "Uploading...",
+    "formats": "JPG, PNG, WebP — max 10MB each",
+    "uploadingImages": "Uploading images...",
+    "limitReached": "Image limit reached ({count}/{max})",
+    "upgradePrompt": "Upgrade your plan to upload more images.",
+    "onlyAllowed": "Only JPG, PNG, and WebP images are allowed. {names} skipped.",
+    "onlyUploading": "Only uploading {count} image(s) to stay within limit.",
+    "uploadingCount": "Uploading {count} image(s)...",
+    "uploadedCount": "{count} image(s) uploaded",
+    "uploadFailed": "Upload failed",
+    "deleted": "Image deleted",
+    "failedDelete": "Failed to delete image",
+    "failedUpdate": "Failed to update",
+    "description": "Description...",
+    "invoice": "Invoice"
+  },
+  "videos": {
+    "title": "Service Videos",
+    "dropzone": "Drop videos here or click to browse",
+    "uploading": "Uploading...",
+    "formats": "MP4, WebM, MOV — max 10MB each",
+    "uploadingVideos": "Uploading videos...",
+    "uploadingCount": "Uploading {count} video(s)...",
+    "uploadedCount": "{count} video(s) uploaded",
+    "uploadFailed": "Upload failed",
+    "deleted": "Video deleted",
+    "failedDelete": "Failed to delete video",
+    "failedUpdate": "Failed to update",
+    "invoice": "Invoice"
+  },
+  "documents": {
+    "diagnosticTitle": "Diagnostic Reports",
+    "documentsTitle": "Documents",
+    "dropzone": "Drop files here or click to browse",
+    "uploading": "Uploading...",
+    "diagnosticFormats": "PDF, CSV, TXT — max 10MB each",
+    "documentFormats": "PDF, DOCX, XLSX, CSV, TXT — max 10MB each",
+    "uploadingReports": "Uploading reports...",
+    "uploadingDocuments": "Uploading documents...",
+    "diagnosticLimitReached": "Diagnostic report limit reached ({count}/{max})",
+    "documentLimitReached": "Document limit reached ({count}/{max})",
+    "upgradePrompt": "Upgrade your plan to upload more.",
+    "diagnosticLabel": "Diagnostic report",
+    "documentLabel": "Document",
+    "limitReachedToast": "{label} limit reached ({count}/{max}). Upgrade your plan for more.",
+    "onlyUploading": "Only uploading {count} file(s) to stay within limit.",
+    "uploadingCount": "Uploading {count} file(s)...",
+    "uploadedCount": "{count} file(s) uploaded",
+    "uploadFailed": "Upload failed",
+    "deleted": "File deleted",
+    "failedDelete": "Failed to delete file",
+    "failedUpdate": "Failed to update",
+    "invoice": "Invoice"
+  }
+}

+ 700 - 0
messages/en/settings.json

@@ -0,0 +1,700 @@
+{
+  "title": "Settings",
+  "subtitle": "Manage your workshop, invoices, and preferences",
+  "nav": {
+    "categories": {
+      "general": "General",
+      "billing": "Billing & Documents",
+      "communications": "Communications",
+      "workshop": "Workshop",
+      "system": "System"
+    },
+    "items": {
+      "company": { "title": "Company", "description": "Business details & branding" },
+      "subscription": { "title": "Subscription", "description": "Plan & billing" },
+      "account": { "title": "Account", "description": "Name, email & password" },
+      "team": { "title": "Team", "description": "Members & roles" },
+      "invoice": { "title": "Invoice", "description": "Invoice layout & fields" },
+      "templates": { "title": "Templates", "description": "Invoice, quote & inspection templates" },
+      "payment": { "title": "Payment", "description": "Payment terms & accounts" },
+      "currency": { "title": "Currency", "description": "Currency & tax defaults" },
+      "customFields": { "title": "Custom Fields", "description": "Define custom data fields" },
+      "email": { "title": "Email", "description": "Email provider & sending" },
+      "sms": { "title": "SMS", "description": "SMS provider & messaging" },
+      "customerPortal": { "title": "Customer Portal", "description": "Customer self-service portal" },
+      "workshop": { "title": "Workshop", "description": "Technician & labor defaults" },
+      "maintenance": { "title": "Maintenance", "description": "Predicted service intervals" },
+      "appearance": { "title": "Appearance", "description": "Theme & display" },
+      "data": { "title": "Data", "description": "Export & import backup" },
+      "license": { "title": "License", "description": "White-label license" },
+      "about": { "title": "About", "description": "Version & info" }
+    },
+    "pro": "PRO"
+  },
+  "readOnly": {
+    "banner": "You have view-only access to settings. Contact an admin to make changes."
+  },
+  "featureLocked": {
+    "requiresPro": "Requires a Pro license",
+    "upgradePlan": "Upgrade Plan",
+    "activateLicense": "Activate License"
+  },
+  "company": {
+    "logoTitle": "Company Logo",
+    "logoAlt": "Company logo",
+    "upload": "Upload",
+    "remove": "Remove",
+    "logoHint": "JPG, PNG, WebP, SVG — max 2MB",
+    "uploadingLogo": "Uploading logo...",
+    "logoUpdated": "Logo updated",
+    "failedUploadLogo": "Failed to upload logo",
+    "detailsTitle": "Company Details",
+    "shopName": "Company / Shop Name",
+    "shopNamePlaceholder": "Joe's Auto Repair",
+    "orgNumber": "Organization Number",
+    "orgNumberPlaceholder": "123 456 789",
+    "phone": "Phone",
+    "phonePlaceholder": "(555) 123-4567",
+    "email": "Email",
+    "emailPlaceholder": "service@example.com",
+    "address": "Address",
+    "addressPlaceholder": "123 Main Street\nSuite 100\nCity, State 12345",
+    "saveCompany": "Save Company Details",
+    "addNewCompany": "Add New Company",
+    "companyName": "Company Name",
+    "companyNamePlaceholder": "e.g. Joe's Auto Repair",
+    "cancel": "Cancel",
+    "createCompany": "Create Company",
+    "settingsSaved": "Company settings saved",
+    "failedSave": "Failed to save settings",
+    "companyCreated": "Company created successfully"
+  },
+  "account": {
+    "profileTitle": "Profile",
+    "profileDescription": "Update your display name and email address.",
+    "name": "Name",
+    "namePlaceholder": "Your name",
+    "emailLabel": "Email",
+    "emailPlaceholder": "Your email",
+    "saveProfile": "Save Profile",
+    "profileUpdated": "Profile updated",
+    "failedUpdateProfile": "Failed to update profile",
+    "failedUpdateEmail": "Failed to update email",
+    "changePasswordTitle": "Change Password",
+    "changePasswordDescription": "Enter your current password and choose a new one.",
+    "currentPassword": "Current Password",
+    "newPassword": "New Password",
+    "confirmNewPassword": "Confirm New Password",
+    "changePassword": "Change Password",
+    "passwordChanged": "Password changed",
+    "passwordsNoMatch": "Passwords do not match",
+    "passwordMinLength": "Password must be at least 8 characters",
+    "failedChangePassword": "Failed to change password",
+    "twoFactorTitle": "Two-Factor Authentication",
+    "twoFactorEnabled": "Two-factor authentication is enabled",
+    "twoFactorDisablePrompt": "Enter your password to disable two-factor authentication.",
+    "twoFactorEnablePrompt": "Add an extra layer of security to your account by enabling two-factor authentication with an authenticator app.",
+    "password": "Password",
+    "enterPassword": "Enter your password",
+    "disable2FA": "Disable 2FA",
+    "enable2FA": "Enable 2FA",
+    "twoFactorDisabled": "Two-factor authentication disabled",
+    "failedEnable2FA": "Failed to enable 2FA",
+    "failedDisable2FA": "Failed to disable 2FA",
+    "enterPasswordError": "Please enter your password",
+    "setupDialogTitle": "Enable Two-Factor Authentication",
+    "setupDialogDescription": "Enter your password to begin setting up 2FA with an authenticator app.",
+    "continue": "Continue",
+    "scanQRTitle": "Scan QR Code",
+    "scanQRDescription": "Scan this QR code with your authenticator app (Google Authenticator, Authy, etc.)",
+    "verifyCodeTitle": "Verify Code",
+    "verifyCodeDescription": "Enter the 6-digit code from your authenticator app to verify it's set up correctly.",
+    "authCode": "Authentication Code",
+    "authCodePlaceholder": "000000",
+    "back": "Back",
+    "verify": "Verify",
+    "enterCodeError": "Please enter the code from your authenticator app",
+    "invalidCode": "Invalid code. Please try again.",
+    "verificationFailed": "Verification failed",
+    "backupCodesTitle": "Save Backup Codes",
+    "backupCodesDescription": "Save these codes in a safe place. You can use them to sign in if you lose access to your authenticator app. Each code can only be used once.",
+    "copyCodes": "Copy Codes",
+    "copied": "Copied",
+    "backupCodesCopied": "Backup codes copied to clipboard",
+    "savedBackupCodes": "I've saved my backup codes",
+    "twoFactorEnabledSuccess": "Two-factor authentication enabled",
+    "dangerZone": "Danger Zone",
+    "deleteContentTitle": "Delete Content",
+    "deleteContentDescription": "Selectively delete organization data such as vehicles, customers, quotes, or inventory. Your account and settings will remain intact.",
+    "deleteContentButton": "Delete Content",
+    "deleteAccountTitle": "Delete Account",
+    "deleteAccountDescription": "Permanently delete your account and all associated data including vehicles, service records, work orders, invoices, customers, files, and payments. This action cannot be undone.",
+    "deleteAccountButton": "Delete Account",
+    "deleteAccountDialogTitle": "Delete Account",
+    "deleteAccountDialogDescription": "This will permanently delete your account and all data associated with it. This action is irreversible. All your vehicles, service records, work orders, quotes, customers, files, and payment history will be permanently removed.",
+    "deleteAccountConfirmPhrase": "delete me",
+    "deleteAccountConfirmPrompt": "Type <bold>delete me</bold> to confirm",
+    "permanentlyDelete": "Permanently Delete",
+    "deleting": "Deleting...",
+    "failedDeleteAccount": "Failed to delete account",
+    "deleteContentDialogTitle": "Delete Content",
+    "deleteContentDialogDescription": "Select the content you want to permanently delete. This action cannot be undone.",
+    "contentVehicles": "Vehicles",
+    "contentVehiclesDescription": "All vehicles and their service records, notes, reminders, fuel logs, and recurring invoices",
+    "contentCustomers": "Customers",
+    "contentCustomersDescription": "All customer records. Vehicles linked to deleted customers will be unlinked.",
+    "contentQuotes": "Quotes",
+    "contentQuotesDescription": "All quotes and their line items",
+    "contentInventory": "Inventory",
+    "contentInventoryDescription": "All inventory parts and their images",
+    "deleteContentConfirmPhrase": "delete my data",
+    "deleteContentConfirmPrompt": "Type <bold>delete my data</bold> to confirm",
+    "deleteSelected": "Delete {count} selected",
+    "contentDeleted": "Selected content has been permanently deleted",
+    "failedDeleteContent": "Failed to delete content"
+  },
+  "team": {
+    "title": "Team",
+    "noOrgDescription": "Create a team to share data with other users.",
+    "orgDescription": "Manage your organization members and their roles.",
+    "createOrg": "Create Organization",
+    "createOrgDescription": "Create an organization to invite team members. You will be the owner.",
+    "orgName": "Organization Name",
+    "orgNamePlaceholder": "My Workshop",
+    "create": "Create",
+    "orgCreated": "Organization created",
+    "failedCreateOrg": "Failed to create organization",
+    "memberCount": "{count} member",
+    "memberCountPlural": "{count} members",
+    "admin": "Admin",
+    "member": "Member",
+    "inviteByEmail": "Invite by Email",
+    "inviteEmailPlaceholder": "user@example.com",
+    "invite": "Invite",
+    "invited": "Member invited successfully.",
+    "userNotFoundTitle": "User Not Found",
+    "userNotFoundDescription": "No account found for \"{email}\". Send them an invitation email to sign up and join your team?",
+    "sendInvitation": "Send Invitation",
+    "invitationSentTitle": "Invitation Sent",
+    "invitationSentDescription": "An invitation email has been sent to {email}.",
+    "failedInvite": "Failed to invite member",
+    "failedSendInvitation": "Failed to send invitation",
+    "pendingInvitations": "Pending Invitations",
+    "invitedAs": "Invited as {role}",
+    "expires": "Expires {date}",
+    "pending": "Pending",
+    "copyInviteLink": "Copy invite link",
+    "inviteLinkCopied": "Invite link copied to clipboard",
+    "cancelInvitation": "Cancel Invitation",
+    "cancelInvitationDescription": "Cancel the invitation to {email}? They will no longer be able to use this link to join.",
+    "invitationCancelled": "Invitation cancelled",
+    "failedCancelInvitation": "Failed to cancel invitation",
+    "customRoles": "Custom Roles",
+    "newRole": "New Role",
+    "roleName": "Role Name",
+    "roleNamePlaceholder": "e.g. Technician",
+    "fullAdminAccess": "Full admin access (bypasses all permission checks)",
+    "permissions": "Permissions",
+    "createRole": "Create Role",
+    "updateRole": "Update Role",
+    "roleCreated": "Role created",
+    "roleUpdated": "Role updated",
+    "failedSaveRole": "Failed to save role",
+    "deleteRole": "Delete Role",
+    "deleteRoleDescription": "Delete the \"{name}\" role? Members with this role will lose their custom permissions.",
+    "roleDeleted": "Role deleted",
+    "failedDeleteRole": "Failed to delete role",
+    "fullAccess": "Full access",
+    "permissionCount": "{count} permission",
+    "permissionCountPlural": "{count} permissions",
+    "noCustomRoles": "No custom roles yet. Create one to define granular permissions for team members.",
+    "builtInRoles": "Built-in Roles",
+    "ownerLabel": "Owner",
+    "ownerDescription": "Full access. Can manage members, change roles, and delete the organization.",
+    "adminLabel": "Admin",
+    "adminDescription": "Can invite/remove members and manage shared data.",
+    "memberLabel": "Member",
+    "memberDescription": "Permissions determined by assigned custom role. No custom role means read-only access.",
+    "roleAssigned": "Role updated",
+    "failedAssignRole": "Failed to update role",
+    "removeMemberTitle": "Remove Member",
+    "removeMemberDescription": "Remove {name} from the team? They will lose access to shared data.",
+    "removeButton": "Remove",
+    "memberRemoved": "Member removed",
+    "failedRemoveMember": "Failed to remove member",
+    "memberRoleUpdated": "Member role updated",
+    "failedUpdateRole": "Failed to update role"
+  },
+  "invoice": {
+    "title": "Invoice Layout",
+    "description": "Configure what appears on generated PDF invoices.",
+    "invoiceNumberFormat": "Invoice Number Format",
+    "invoiceNumberFormatHint": "Use <code>{year}</code> for the current year. Preview: <bold>{preview}</bold>",
+    "nextInvoiceNumber": "Next Invoice Number",
+    "nextInvoiceNumberPlaceholder": "e.g. 94",
+    "nextInvoiceNumberHint": "Next invoice will use this number (e.g. {example})",
+    "dueDays": "Due Days",
+    "dueDaysHint": "Number of days until invoice is due",
+    "visibilityTitle": "Visibility on Invoice",
+    "companyLogo": "Company Logo",
+    "companyLogoHint": "Display company logo in invoice header",
+    "companyName": "Company Name",
+    "companyNameHint": "Display company name text in invoice header",
+    "bankAccount": "Bank Account",
+    "bankAccountHint": "Show bank account / IBAN on invoice",
+    "organizationNumber": "Organization Number",
+    "organizationNumberHint": "Show org number on invoice header",
+    "customFooter": "Custom Invoice Footer",
+    "footerPlaceholder": "Thank you for your business!",
+    "footerHint": "This text appears at the bottom of every invoice",
+    "saveInvoice": "Save Invoice Settings",
+    "saved": "Invoice settings saved"
+  },
+  "templates": {
+    "title": "Templates",
+    "invoiceDescription": "Customize the appearance of your PDF invoices and quotes.",
+    "inspectionsDescription": "Manage inspection checklists and multi-point templates.",
+    "smsDescription": "Customize the SMS messages sent to customers.",
+    "tabs": {
+      "invoice": "Invoice",
+      "quotation": "Quotation",
+      "inspections": "Inspections",
+      "sms": "SMS"
+    },
+    "presets": "Template Presets",
+    "primaryColor": "Primary Color",
+    "fontAndLayout": "Font & Layout",
+    "fontFamily": "Font Family",
+    "helveticaDefault": "Helvetica (Default)",
+    "timesRoman": "Times Roman",
+    "courier": "Courier",
+    "headerStyle": "Header Style",
+    "standard": "Standard",
+    "compact": "Compact",
+    "modern": "Modern",
+    "preview": "{name} Preview",
+    "billTo": "Bill To",
+    "preparedFor": "Prepared For",
+    "vehicle": "Vehicle",
+    "part": "Part",
+    "qty": "Qty",
+    "price": "Price",
+    "total": "Total",
+    "labor": "Labor",
+    "hours": "Hours",
+    "rate": "Rate",
+    "subtotal": "Subtotal",
+    "tax": "Tax ({rate}%)",
+    "saveInvoiceTemplate": "Save Invoice Template",
+    "saveQuotationTemplate": "Save Quotation Template",
+    "invoiceTemplateSaved": "Invoice template settings saved",
+    "quotationTemplateSaved": "Quotation template settings saved",
+    "failedSave": "Failed to save settings",
+    "smsTemplates": "SMS Message Templates",
+    "smsTemplatesDescription": "Customize the text messages sent to customers. Use variables like <code>{companyName}</code> to insert dynamic values.",
+    "reset": "Reset",
+    "invoiceReady": "Invoice Ready",
+    "invoiceReadyDescription": "Sent when sharing an invoice with a customer.",
+    "quoteReady": "Quote Ready",
+    "quoteReadyDescription": "Sent when sharing a quote with a customer.",
+    "inspectionReady": "Inspection Ready",
+    "inspectionReadyDescription": "Sent when sharing an inspection report with a customer.",
+    "statusInProgress": "Status: In Progress",
+    "statusInProgressDescription": "Sent when a work order status changes to In Progress.",
+    "statusWaitingParts": "Status: Waiting for Parts",
+    "statusWaitingPartsDescription": "Sent when a work order status changes to Waiting for Parts.",
+    "statusReady": "Status: Ready for Pickup",
+    "statusReadyDescription": "Sent when a work order status changes to Ready.",
+    "statusCompleted": "Status: Completed",
+    "statusCompletedDescription": "Sent when a work order status changes to Completed.",
+    "paymentReceived": "Payment Received",
+    "paymentReceivedDescription": "Sent when a payment is recorded for an invoice.",
+    "saveSmsTemplates": "Save SMS Templates",
+    "smsTemplateSaved": "SMS template settings saved",
+    "smsDefaults": {
+      "invoiceReady": "Your invoice is ready. View it here: {share_link}",
+      "quoteReady": "Your quote is ready for review. View it here: {share_link}",
+      "inspectionReady": "Your vehicle inspection report is ready. View it here: {share_link}",
+      "statusInProgress": "Work has started on your vehicle. We'll keep you updated.",
+      "statusWaitingParts": "Your vehicle is waiting for parts. We'll notify you when work resumes.",
+      "statusReady": "Your vehicle is ready for pickup!",
+      "statusCompleted": "Your service is complete. Thank you for your business!",
+      "paymentReceived": "Payment of {amount} received for invoice {invoice_number}. Thank you!"
+    },
+    "colorPresets": {
+      "amber": "Amber",
+      "blue": "Blue",
+      "emerald": "Emerald",
+      "red": "Red",
+      "purple": "Purple",
+      "slate": "Slate",
+      "rose": "Rose",
+      "indigo": "Indigo"
+    }
+  },
+  "payment": {
+    "detailsTitle": "Payment Details",
+    "detailsDescription": "Bank account and payment terms shown on invoices.",
+    "bankAccount": "Bank Account (IBAN / Til Konto)",
+    "bankAccountPlaceholder": "NO93 8601 1117 947",
+    "bankAccountHint": "Your bank account number or IBAN for receiving payments",
+    "paymentTerms": "Payment Terms",
+    "paymentTermsPlaceholder": "Net 14 days",
+    "paymentTermsHint": "Payment terms text displayed on invoices (e.g. \"Net 14 days\", \"Due on receipt\")",
+    "onlinePayments": "Online Payments",
+    "onlinePaymentsDescription": "Allow customers to pay invoices online via shared invoice links.",
+    "stripe": "Stripe",
+    "stripeDescription": "Accept credit/debit card payments",
+    "secretKey": "Secret Key",
+    "publishableKey": "Publishable Key",
+    "webhookSecret": "Webhook Secret",
+    "webhookUrl": "Webhook URL",
+    "stripeWebhookHint": "Add this URL in your Stripe Dashboard under Webhooks. Subscribe to <code>checkout.session.completed</code>.",
+    "vipps": "Vipps",
+    "vippsDescription": "Accept Vipps mobile payments (Norway)",
+    "clientId": "Client ID",
+    "clientSecret": "Client Secret",
+    "subscriptionKey": "Subscription Key",
+    "merchantSerialNumber": "Merchant Serial Number",
+    "testMode": "Test Mode",
+    "callbackUrl": "Callback URL",
+    "termsOfSale": "Terms of Sale (Salgsvilkar)",
+    "termsOfSaleDescription": "Required by Vipps. Customers can view these from the invoice page.",
+    "externalTermsUrl": "External Terms URL (optional)",
+    "externalTermsUrlHint": "Link to an external page with your terms. If set, the invoice will link here instead of the built-in terms page.",
+    "publicTermsUrl": "Public Terms URL",
+    "publicTermsUrlHint": "Share this URL with Vipps or link it from your website.",
+    "paypal": "PayPal",
+    "paypalDescription": "Accept PayPal payments",
+    "sandboxMode": "Sandbox Mode",
+    "paypalWebhookHint": "Add this URL in your PayPal Developer Dashboard under Webhooks. Subscribe to <code>PAYMENT.CAPTURE.COMPLETED</code>.",
+    "savePayment": "Save Payment Settings",
+    "saved": "Payment settings saved"
+  },
+  "currency": {
+    "title": "Currency & Tax",
+    "description": "Default currency and tax rate used across invoices and service records.",
+    "currencyLabel": "Currency",
+    "previewLabel": "Preview: {value}",
+    "enableTax": "Enable Tax",
+    "enableTaxHint": "Show tax calculation on invoices and service records",
+    "defaultTaxRate": "Default Tax Rate (%)",
+    "defaultTaxRateHint": "Auto-populated when creating new service records",
+    "saveSettings": "Save Settings",
+    "saved": "Settings saved",
+    "currencies": {
+      "USD": "US Dollar",
+      "EUR": "Euro",
+      "GBP": "British Pound",
+      "NOK": "Norwegian Krone",
+      "SEK": "Swedish Krona",
+      "DKK": "Danish Krone",
+      "CHF": "Swiss Franc",
+      "CAD": "Canadian Dollar",
+      "AUD": "Australian Dollar",
+      "NZD": "New Zealand Dollar",
+      "JPY": "Japanese Yen",
+      "CNY": "Chinese Yuan",
+      "INR": "Indian Rupee",
+      "BRL": "Brazilian Real",
+      "MXN": "Mexican Peso",
+      "PLN": "Polish Zloty",
+      "CZK": "Czech Koruna",
+      "HUF": "Hungarian Forint",
+      "TRY": "Turkish Lira",
+      "ZAR": "South African Rand",
+      "KRW": "South Korean Won",
+      "SGD": "Singapore Dollar",
+      "HKD": "Hong Kong Dollar",
+      "THB": "Thai Baht",
+      "ISK": "Icelandic Krona",
+      "RON": "Romanian Leu",
+      "ILS": "Israeli Shekel",
+      "PHP": "Philippine Peso",
+      "IDR": "Indonesian Rupiah",
+      "MYR": "Malaysian Ringgit",
+      "AED": "UAE Dirham",
+      "SAR": "Saudi Riyal"
+    }
+  },
+  "customFields": {
+    "title": "Custom Fields",
+    "description": "Define custom fields that appear on service records and quotes.",
+    "addField": "Add Field",
+    "filterAll": "All",
+    "serviceRecord": "Service Record",
+    "quote": "Quote",
+    "emptyState": "No custom fields defined yet. Click \"Add Field\" to create one.",
+    "required": "Required",
+    "inactive": "Inactive",
+    "editField": "Edit Field",
+    "newField": "New Custom Field",
+    "label": "Label",
+    "labelPlaceholder": "e.g. Warranty Expiry",
+    "fieldName": "Field Name (ID)",
+    "fieldNamePlaceholder": "warranty_expiry",
+    "fieldNameHint": "Lowercase letters, numbers, and underscores only.",
+    "fieldType": "Field Type",
+    "entityType": "Entity Type",
+    "fieldTypes": {
+      "text": "Text",
+      "number": "Number",
+      "date": "Date",
+      "select": "Select (Dropdown)",
+      "checkbox": "Checkbox",
+      "textarea": "Text Area"
+    },
+    "optionsLabel": "Options (comma-separated)",
+    "optionsPlaceholder": "Option 1, Option 2, Option 3",
+    "active": "Active",
+    "cancel": "Cancel",
+    "update": "Update",
+    "create": "Create",
+    "deleteTitle": "Delete Custom Field",
+    "deleteDescription": "This will permanently delete \"{label}\" and all its saved values. This cannot be undone.",
+    "fieldCreated": "Custom field created",
+    "fieldUpdated": "Custom field updated",
+    "fieldDeleted": "Custom field deleted",
+    "failedSave": "Failed to save field",
+    "failedDelete": "Failed to delete"
+  },
+  "workshop": {
+    "title": "Workshop Defaults",
+    "description": "Default values used when creating new service records.",
+    "defaultTechnician": "Default Technician Name",
+    "technicianPlaceholder": "John Smith",
+    "defaultLaborRate": "Default Labor Rate (per hour)",
+    "laborRatePlaceholder": "75.00",
+    "workingHours": "Working Hours",
+    "workingHoursPlaceholder": "Mon-Fri 8:00 AM - 5:00 PM",
+    "unitsTitle": "Units",
+    "unitsDescription": "Choose between metric (km, liters) and imperial (miles, gallons) units.",
+    "unitSystem": "Unit System",
+    "metric": "Metric (km, liters)",
+    "imperial": "Imperial (miles, gallons)",
+    "distanceLabel": "Distance",
+    "volumeLabel": "Volume",
+    "kilometers": "kilometers (km)",
+    "liters": "liters (L)",
+    "miles": "miles (mi)",
+    "gallons": "gallons (gal)",
+    "saveWorkshop": "Save Workshop Settings",
+    "saved": "Workshop settings saved"
+  },
+  "maintenance": {
+    "title": "Predicted Maintenance",
+    "description": "Predict each vehicle's current mileage from its service history and surface vehicles that are due or approaching their next service.",
+    "enablePredicted": "Enable predicted maintenance",
+    "enablePredictedHint": "Show predictions on vehicle details and dashboard",
+    "serviceInterval": "Service interval ({unit})",
+    "serviceIntervalHint": "Distance between recommended services",
+    "approachingThreshold": "Approaching threshold ({unit})",
+    "approachingThresholdHint": "Show warning when within this distance of next service",
+    "saveMaintenance": "Save Maintenance Settings",
+    "saved": "Maintenance settings saved"
+  },
+  "appearance": {
+    "title": "Appearance",
+    "description": "Customize how Torqvoice looks and feels.",
+    "darkMode": "Dark Mode",
+    "darkModeHint": "Switch between light and dark themes",
+    "dateTimeTitle": "Date & Time",
+    "dateTimeDescription": "Configure how dates and times are displayed across the application.",
+    "dateFormat": "Date Format",
+    "timeFormat": "Time Format",
+    "time12h": "12-hour (2:30 PM)",
+    "time24h": "24-hour (14:30)",
+    "timezone": "Timezone",
+    "timezoneAuto": "Auto-detect (browser)",
+    "timezoneHint": "When set to auto-detect, each user sees dates in their browser's timezone.",
+    "previewLabel": "Preview",
+    "dateLabel": "Date",
+    "dateTimeLabel": "Date & Time",
+    "saveSettings": "Save Settings",
+    "saved": "Settings saved"
+  },
+  "data": {
+    "title": "Data Management",
+    "description": "Export your data as a backup or import a previously exported file.",
+    "exportTitle": "Export Data",
+    "exportDescription": "Download your data as a zip file. Select which categories to include in the backup.",
+    "includeInBackup": "Include in backup:",
+    "selectAll": "Select All",
+    "deselectAll": "Deselect All",
+    "optSettings": "Settings",
+    "optSettingsDesc": "Workshop info, invoice config, payment providers",
+    "optCustomers": "Customers",
+    "optCustomersDesc": "Customer records and contact info",
+    "optVehicles": "Vehicles & Service Records",
+    "optVehiclesDesc": "Vehicles, notes, fuel logs, reminders, service records",
+    "optQuotes": "Quotes",
+    "optQuotesDesc": "Quotes with parts and labor",
+    "optInventory": "Inventory",
+    "optInventoryDesc": "Inventory parts and stock levels",
+    "optCustomFields": "Custom Fields",
+    "optCustomFieldsDesc": "Custom field definitions and values",
+    "optFiles": "Uploaded Files",
+    "optFilesDesc": "Images, service attachments, logo",
+    "filesWarning": "Includes uploaded files — backup may be larger",
+    "downloadBackup": "Download Backup",
+    "exportComplete": "Export Complete",
+    "exportCompleteMessage": "Your backup file has been downloaded.",
+    "exportFailed": "Export Failed",
+    "exportFailedMessage": "Could not export your data. Please try again.",
+    "importTitle": "Import Data",
+    "importWarning": "Importing will <bold>replace all existing data</bold> with the contents of the backup file. This action cannot be undone.",
+    "importFileHint": "Supports both .zip (v2) and .json (v1) backup files.",
+    "uploadRestore": "Upload & Restore",
+    "importComplete": "Import Complete",
+    "importCompleteMessage": "All data has been restored from the backup file.",
+    "importFailed": "Import Failed",
+    "importFromOther": "Import from Other Services",
+    "importFromOtherDescription": "Migrate your data from another platform. Select a service to get started.",
+    "lubelogTitle": "Import from LubeLog",
+    "lubelogExportSteps": "How to export your LubeLog data:",
+    "lubelogStep1": "Open your LubeLog instance",
+    "lubelogStep2": "Go to <bold>Settings</bold>",
+    "lubelogStep3": "Click <bold>Make Backup</bold> to download a zip file",
+    "lubelogStep4": "Upload the downloaded zip file below",
+    "lubelogAddNote": "This will <bold>add data alongside</bold> your existing records — nothing will be deleted.",
+    "lubelogImported": "What will be imported:",
+    "lubelogVehicles": "Vehicles",
+    "lubelogServiceRecords": "Service records with attachments",
+    "lubelogNotes": "Notes",
+    "lubelogFileHint": "Files up to 200MB supported.",
+    "lubelogImportComplete": "LubeLog Import Complete",
+    "cancel": "Cancel",
+    "import": "Import",
+    "invoiceNinjaTitle": "Import from Invoice Ninja",
+    "invoiceNinjaExportSteps": "How to export your Invoice Ninja data:",
+    "invoiceNinjaStep1": "Open your Invoice Ninja instance",
+    "invoiceNinjaStep2": "Go to <bold>Settings > Import | Export</bold>",
+    "invoiceNinjaStep3": "Click <bold>Export</bold> to download a zip file",
+    "invoiceNinjaStep4": "Upload the downloaded zip file below",
+    "invoiceNinjaAddNote": "This will <bold>add data alongside</bold> your existing records — nothing will be deleted.",
+    "invoiceNinjaImported": "What will be imported:",
+    "invoiceNinjaCustomers": "Customers (clients)",
+    "invoiceNinjaProducts": "Products (inventory)",
+    "invoiceNinjaInvoices": "Invoices as service records (parts & labor)",
+    "invoiceNinjaDocuments": "Documents & attachments",
+    "invoiceNinjaPayments": "Payments",
+    "invoiceNinjaFileHint": "Upload an Invoice Ninja zip export file. Files up to 200MB supported.",
+    "invoiceNinjaImportComplete": "Invoice Ninja Import Complete"
+  },
+  "license": {
+    "title": "White-Label License",
+    "description": "Activate a white-label license to remove Torqvoice branding from invoices and the application.",
+    "purchaseWhiteLabel": "Purchase White-Label",
+    "status": "Status:",
+    "active": "Active",
+    "inactive": "Inactive",
+    "lastChecked": "Last checked: {date}",
+    "enterLicenseKey": "Enter license key",
+    "validate": "Validate",
+    "validated": "License validated successfully",
+    "invalid": "Invalid license key",
+    "failedValidate": "Failed to validate license",
+    "keyHint": "Each license key is tied to this organization and cannot be shared across multiple organizations."
+  },
+  "about": {
+    "title": "About Torqvoice",
+    "description": "Torqvoice is your complete vehicle service management solution. Track service history, repairs and more.",
+    "version": "Version",
+    "website": "Website",
+    "documentation": "Documentation",
+    "changelog": "Changelog"
+  },
+  "portal": {
+    "title": "Customer Portal",
+    "description": "Allow your customers to log in and view their vehicles, invoices, quotes, and inspections. Customers can also submit service requests.",
+    "enablePortal": "Enable customer portal",
+    "enablePortalHint": "Customers with an email address can log in via magic link",
+    "enabled": "Customer portal enabled",
+    "disabled": "Customer portal disabled",
+    "failedUpdate": "Failed to update setting",
+    "customSlug": "Custom portal slug",
+    "save": "Save",
+    "slugSaved": "Portal slug saved",
+    "slugRemoved": "Portal slug removed",
+    "failedUpdateSlug": "Failed to update slug",
+    "slugHint": "Lowercase letters, numbers, hyphens, and underscores. 3-48 characters. Leave blank to use the default URL.",
+    "portalUrl": "Portal URL",
+    "urlCopied": "Portal URL copied",
+    "shareHint": "Share this link with your customers so they can log in to their portal."
+  },
+  "email": {
+    "title": "Email Provider",
+    "description": "Configure your organization's email provider for sending invoices and quotes. If not configured, the platform's default email provider will be used.",
+    "useCustomLabel": "Use custom email provider",
+    "useCustomHint": "Enable to configure your own email provider instead of using the platform default",
+    "platformDefaultInfo": "Your organization is using the platform's default email provider. Invoices and quotes will be sent using the platform-configured email settings.",
+    "smtpHost": "SMTP Host",
+    "smtpHostPlaceholder": "smtp.example.com",
+    "smtpPort": "SMTP Port",
+    "smtpPortPlaceholder": "587",
+    "username": "Username",
+    "usernamePlaceholder": "user@example.com",
+    "password": "Password",
+    "fromEmail": "From Email",
+    "fromEmailPlaceholder": "noreply@example.com",
+    "fromEmailDomainPlaceholder": "noreply@yourdomain.com",
+    "fromName": "From Name",
+    "fromNamePlaceholder": "Your Workshop",
+    "tlsConnection": "TLS Connection (Port 465)",
+    "tlsConnectionHint": "Enable for implicit TLS. Disable for STARTTLS (port 587/25).",
+    "verifyTlsCerts": "Verify TLS Certificates",
+    "verifyTlsCertsHint": "Disable to allow self-signed certificates (not recommended for production)",
+    "requireTlsUpgrade": "Require TLS Upgrade",
+    "requireTlsUpgradeHint": "Force TLS upgrade on STARTTLS connections",
+    "apiKey": "API Key",
+    "apiKeyHintResend": "Get your API key from",
+    "serverToken": "Server Token",
+    "serverTokenHintPostmark": "Get your Server Token from",
+    "apiKeyHintMailgun": "Get your API key from",
+    "domain": "Domain",
+    "domainPlaceholder": "mg.yourdomain.com",
+    "region": "Region",
+    "apiKeyHintSendGrid": "Get your API key from",
+    "accessKeyId": "Access Key ID",
+    "secretAccessKey": "Secret Access Key",
+    "awsRegion": "AWS Region",
+    "awsRegionPlaceholder": "us-east-1",
+    "awsRegionHint": "The AWS region where SES is configured (e.g. us-east-1, eu-west-1)",
+    "sendTestEmail": "Send Test Email",
+    "testEmailHint": "Save settings first, then send a test email to your account",
+    "saveSettings": "Save Settings",
+    "savedDefault": "Email settings saved — using platform default",
+    "saved": "Email settings saved",
+    "failedSave": "Failed to save settings",
+    "testSentTo": "Test email sent to {email}",
+    "testFailed": "Email test failed"
+  },
+  "sms": {
+    "title": "SMS Provider",
+    "description": "Configure your SMS provider for two-way messaging with customers. Each organization uses their own SMS vendor and phone number.",
+    "enableLabel": "Enable SMS messaging",
+    "enableHint": "Send and receive SMS messages with customers",
+    "disabledInfo": "SMS messaging is disabled. Enable it to send and receive text messages with your customers.",
+    "phoneNumber": "Phone Number",
+    "phoneNumberPlaceholder": "+15551234567",
+    "phoneNumberHint": "Your SMS-enabled phone number in E.164 format (e.g. +15551234567)",
+    "accountSid": "Account SID",
+    "authToken": "Auth Token",
+    "apiKey": "API Key",
+    "apiSecret": "API Secret",
+    "webhookUrl": "Webhook URL",
+    "webhookUrlHint": "Configure this URL in your {provider} dashboard to receive inbound SMS messages.",
+    "webhookCopied": "Webhook URL copied",
+    "testPhoneNumber": "Test Phone Number",
+    "testPhonePlaceholder": "+15559876543",
+    "sendTestSms": "Send Test SMS",
+    "testSmsHint": "Save settings first, then enter a recipient number to send a test SMS",
+    "enterTestPhone": "Enter a phone number to send the test SMS to",
+    "saveSettings": "Save Settings",
+    "savedDisabled": "SMS settings saved — SMS disabled",
+    "saved": "SMS settings saved",
+    "failedSave": "Failed to save settings",
+    "testSentTo": "Test SMS sent to {phone}",
+    "testFailed": "SMS test failed"
+  }
+}

+ 156 - 0
messages/en/share.json

@@ -0,0 +1,156 @@
+{
+  "invoice": {
+    "title": "Invoice",
+    "downloadPdf": "Download PDF",
+    "paymentReceived": "Payment received!",
+    "paymentApplied": "{amount} has been applied to this invoice.",
+    "verifyingPayment": "Verifying payment...",
+    "balanceDue": "Balance Due",
+    "fullAmount": "Full amount",
+    "partialPayment": "Partial payment",
+    "enterAmount": "Enter amount",
+    "payWithCard": "Pay {amount} with Card",
+    "payWithVipps": "Pay {amount} with Vipps",
+    "payWithPaypal": "Pay {amount} with PayPal",
+    "errorInvalidAmount": "Please enter a valid amount.",
+    "errorExceedsBalance": "Amount cannot exceed {amount}.",
+    "errorVerifyFailed": "Payment could not be verified. It may still be processing.",
+    "errorVerifyRequest": "Failed to verify payment status.",
+    "errorCheckoutFailed": "Failed to start checkout.",
+    "errorPaymentFailed": "Failed to initiate payment. Please try again.",
+    "billTo": "Bill To",
+    "vehicle": "Vehicle",
+    "vin": "VIN: {vin}",
+    "plate": "Plate: {plate}",
+    "service": "Service",
+    "type": "Type: {type}",
+    "tech": "Tech: {tech}",
+    "parts": "Parts",
+    "partNumber": "Part #",
+    "description": "Description",
+    "qty": "Qty",
+    "unitPrice": "Unit Price",
+    "total": "Total",
+    "labor": "Labor",
+    "hours": "Hours",
+    "rate": "Rate",
+    "ratePerHour": "{rate}/hr",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "discountPercent": "Discount ({percent}%)",
+    "tax": "Tax ({rate}%)",
+    "paid": "Paid",
+    "paidInFull": "PAID IN FULL",
+    "amountDue": "Amount Due",
+    "paymentInformation": "Payment Information",
+    "bankAccount": "Bank Account",
+    "orgNumber": "Org. Number",
+    "paymentTerms": "Payment Terms",
+    "dueDate": "Due Date",
+    "serviceImages": "Service Images ({count})",
+    "serviceVideos": "Service Videos ({count})",
+    "diagnosticReports": "Diagnostic Reports ({count})",
+    "notes": "Notes",
+    "diagnosticNotes": "Diagnostic Notes",
+    "tel": "Tel: {phone}"
+  },
+  "quote": {
+    "title": "Quote",
+    "downloadPdf": "Download PDF",
+    "preparedFor": "Prepared For",
+    "vehicle": "Vehicle",
+    "vin": "VIN: {vin}",
+    "plate": "Plate: {plate}",
+    "quoteDetails": "Quote Details",
+    "description": "Description",
+    "parts": "Parts",
+    "partNumber": "Part #",
+    "partDescription": "Description",
+    "qty": "Qty",
+    "unitPrice": "Unit Price",
+    "total": "Total",
+    "labor": "Labor",
+    "laborDescription": "Description",
+    "hours": "Hours",
+    "rate": "Rate",
+    "ratePerHour": "{rate}/hr",
+    "subtotal": "Subtotal",
+    "discount": "Discount",
+    "discountPercent": "Discount ({percent}%)",
+    "tax": "Tax ({rate}%)",
+    "images": "Images ({count})",
+    "documents": "Documents",
+    "requestChanges": "Request Changes",
+    "acceptQuote": "Accept Quote",
+    "whatChanges": "What changes would you like?",
+    "changePlaceholder": "Describe the changes you'd like...",
+    "cancel": "Cancel",
+    "submitRequest": "Submit Request",
+    "quoteAccepted": "Quote Accepted",
+    "quoteAcceptedMessage": "Thank you! This quote has been accepted. The workshop will be in touch shortly.",
+    "changesRequested": "Changes Requested",
+    "changesRequestedMessage": "Your change request has been submitted. The workshop will review and get back to you.",
+    "validUntil": "Valid until: {date}",
+    "status": {
+      "draft": "Draft",
+      "sent": "Sent",
+      "accepted": "Accepted",
+      "rejected": "Rejected",
+      "expired": "Expired",
+      "converted": "Converted",
+      "changes_requested": "Changes Requested"
+    },
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "Vehicle Inspection",
+    "vehicle": "Vehicle",
+    "customer": "Customer",
+    "vin": "VIN: {vin}",
+    "plate": "Plate: {plate}",
+    "mileage": "Mileage: {mileage}",
+    "inspected": "inspected",
+    "pass": "Pass",
+    "fail": "Fail",
+    "attention": "Attention",
+    "notInspected": "Not Inspected",
+    "quoteAvailable": "A quote has been prepared for this inspection",
+    "quoteAvailableDescription": "Review the quote to see pricing and approve the recommended work.",
+    "viewQuote": "View Quote",
+    "quoteRequested": "Quote Requested",
+    "quoteRequestedCancel": "— Cancel",
+    "requestQuote": "Request a Quote",
+    "downloadPdf": "Download PDF",
+    "notes": "Notes",
+    "quoteCancelled": "Quote request cancelled",
+    "cancelFailed": "Failed to cancel request"
+  },
+  "terms": {
+    "title": "Terms of Sale (Salgsvilkår)",
+    "contact": "Contact",
+    "tel": "Tel: {phone}"
+  },
+  "quoteRequest": {
+    "title": "Request a Quote",
+    "description": "Select the items you'd like a repair quote for.",
+    "selectAll": "Select all",
+    "deselectAll": "Deselect all",
+    "selectedCount": "{selected} of {total} selected",
+    "fail": "Fail",
+    "attention": "Attention",
+    "message": "Message",
+    "messageOptional": "(optional)",
+    "messagePlaceholder": "Any additional details or questions...",
+    "cancel": "Cancel",
+    "submitRequest": "Submit Request",
+    "selectAtLeastOne": "Select at least one item",
+    "submitSuccess": "Quote request submitted!",
+    "submitError": "Failed to submit request"
+  },
+  "common": {
+    "poweredBy": "Powered by",
+    "termsOfSale": "Terms of Sale",
+    "portalMessage": "View your vehicles, invoices, and more at your",
+    "customerPortal": "customer portal"
+  }
+}

+ 195 - 0
messages/en/vehicles.json

@@ -0,0 +1,195 @@
+{
+  "list": {
+    "active": "Active",
+    "archived": "Archived",
+    "searchPlaceholder": "Search by plate, make, model, VIN...",
+    "addVehicle": "Add Vehicle",
+    "emptySearch": "No vehicles match your search.",
+    "emptyArchived": "No archived vehicles.",
+    "empty": "No vehicles yet.",
+    "table": {
+      "plate": "Plate",
+      "vehicle": "Vehicle",
+      "customer": "Customer",
+      "mileage": "Mileage",
+      "services": "Services"
+    },
+    "edit": "Edit",
+    "archive": "Archive",
+    "unarchive": "Unarchive",
+    "delete": "Delete",
+    "deleteTitle": "Delete Vehicle",
+    "deleteDescription": "Delete {name}? This will remove all associated service records and notes.",
+    "unarchiveTitle": "Unarchive Vehicle",
+    "unarchiveDescription": "Restore {name} to the active vehicles list?",
+    "vehicleUnarchived": "Vehicle unarchived",
+    "unarchiveError": "Failed to unarchive vehicle",
+    "deleteError": "Failed to delete vehicle",
+    "error": "Failed to load vehicles"
+  },
+  "detail": {
+    "vehicleArchived": "This vehicle is archived",
+    "backToVehicles": "Back to vehicles",
+    "newWorkOrder": "New Work Order",
+    "editVehicle": "Edit Vehicle",
+    "unarchive": "Unarchive",
+    "archive": "Archive",
+    "delete": "Delete",
+    "notFound": "Vehicle not found",
+    "predictedKm": "Predicted Km",
+    "predictedMileage": "Predicted Mileage",
+    "dismissed": "Dismissed",
+    "undo": "Undo",
+    "serviceOverdue": "Service Overdue",
+    "serviceSoon": "Service Soon",
+    "services": "services",
+    "purchased": "Purchased {date}",
+    "tabs": {
+      "services": "Services",
+      "inspections": "Inspections",
+      "notes": "Notes",
+      "reminders": "Reminders"
+    },
+    "noteDeleted": "Note deleted",
+    "noteDeleteError": "Failed to delete",
+    "reminderDeleted": "Reminder deleted",
+    "reminderDeleteError": "Failed to delete",
+    "vehicleUnarchived": "Vehicle unarchived",
+    "unarchiveError": "Failed to unarchive",
+    "vehicleDeleted": "Vehicle deleted",
+    "vehicleDeleteError": "Failed to delete vehicle",
+    "deleteTitle": "Delete vehicle?",
+    "deleteDescription": "This will permanently delete {name} and all associated service records, notes, and reminders. This action cannot be undone.",
+    "edit": "Edit"
+  },
+  "inspections": {
+    "newInspection": "New Inspection",
+    "empty": "No inspections yet",
+    "template": "Template",
+    "progress": "Progress",
+    "status": "Status",
+    "date": "Date",
+    "completed": "Completed",
+    "inProgress": "In Progress"
+  },
+  "reminders": {
+    "active": "Active",
+    "completed": "Completed",
+    "all": "All",
+    "addReminder": "Add Reminder",
+    "emptyActive": "No active reminders",
+    "emptyCompleted": "No completed reminders",
+    "empty": "No reminders yet",
+    "overdue": "Overdue",
+    "dueSoon": "Due Soon",
+    "due": "Due {date}",
+    "dueAt": "Due at {mileage} {unit}",
+    "editTitle": "Edit Reminder",
+    "addTitle": "Add Reminder",
+    "titleLabel": "Title *",
+    "titlePlaceholder": "e.g. Oil Change",
+    "descriptionLabel": "Description",
+    "descriptionPlaceholder": "Optional details...",
+    "dueDateLabel": "Due Date",
+    "dueMileageLabel": "Due Mileage",
+    "dueMileagePlaceholder": "e.g. 50000",
+    "reminderUpdated": "Reminder updated",
+    "reminderCreated": "Reminder created",
+    "saveError": "Failed to {action} reminder"
+  },
+  "services": {
+    "searchPlaceholder": "Search records...",
+    "allTypes": "All types",
+    "maintenance": "Maintenance",
+    "repair": "Repair",
+    "upgrade": "Upgrade",
+    "inspection": "Inspection",
+    "newWorkOrder": "New Work Order",
+    "table": {
+      "date": "Date",
+      "title": "Title",
+      "type": "Type",
+      "status": "Status",
+      "mileage": "Mileage",
+      "technician": "Technician",
+      "files": "Files",
+      "total": "Total"
+    },
+    "emptyFiltered": "No service records match your filters.",
+    "empty": "No service records yet.",
+    "showing": "Showing {start}-{end} of {total}",
+    "perPage": "per page",
+    "page": "Page {page} of {totalPages}"
+  },
+  "notes": {
+    "addNote": "Add Note",
+    "table": {
+      "title": "Title",
+      "content": "Content",
+      "date": "Date"
+    },
+    "empty": "No notes yet.",
+    "pin": "Pin",
+    "unpin": "Unpin",
+    "delete": "Delete",
+    "showing": "Showing {start}-{end} of {total}",
+    "perPage": "per page",
+    "page": "Page {page} of {totalPages}",
+    "editTitle": "Edit Note",
+    "addTitle": "Add Note",
+    "titleLabel": "Title *",
+    "titlePlaceholder": "Note title",
+    "contentLabel": "Content *",
+    "contentPlaceholder": "Write your note...",
+    "noteUpdated": "Note updated",
+    "noteCreated": "Note created",
+    "titleRequired": "Title and content are required",
+    "saveError": "Failed to {action} note"
+  },
+  "form": {
+    "editTitle": "Edit Vehicle",
+    "addTitle": "Add New Vehicle",
+    "photo": "Photo",
+    "clickToUpload": "Click to upload a photo",
+    "imageFormats": "JPEG, PNG, WebP up to 5MB",
+    "imageTooLarge": "Image too large",
+    "maxFileSize": "Max file size is 5MB",
+    "uploadingImage": "Uploading image...",
+    "uploadFailed": "Upload failed",
+    "imageUploaded": "Image uploaded",
+    "customer": "Customer",
+    "searchCustomers": "Search customers...",
+    "noCustomerFound": "No customer found.",
+    "noCustomer": "No customer",
+    "addCustomer": "Add Customer",
+    "make": "Make *",
+    "model": "Model *",
+    "year": "Year *",
+    "mileage": "Mileage",
+    "vin": "VIN",
+    "licensePlate": "License Plate",
+    "color": "Color",
+    "fuelType": "Fuel Type",
+    "gasoline": "Gasoline",
+    "diesel": "Diesel",
+    "electric": "Electric",
+    "hybrid": "Hybrid",
+    "other": "Other",
+    "transmission": "Transmission",
+    "automatic": "Automatic",
+    "manual": "Manual",
+    "cvt": "CVT",
+    "engineSize": "Engine Size",
+    "vehicleUpdated": "Vehicle updated",
+    "vehicleAdded": "Vehicle added",
+    "saveError": "Failed to save vehicle"
+  },
+  "archiveDialog": {
+    "title": "Archive Vehicle",
+    "description": "Archive <span>{name}</span>? It will be hidden from the main list but all service history will be preserved.",
+    "reasonLabel": "Reason (optional)",
+    "reasonPlaceholder": "e.g. Sold, totaled, decommissioned...",
+    "archived": "{name} archived",
+    "archiveError": "Failed to archive vehicle"
+  }
+}

+ 67 - 0
messages/en/workBoard.json

@@ -0,0 +1,67 @@
+{
+  "page": {
+    "error": "Failed to load work board"
+  },
+  "toolbar": {
+    "today": "Today",
+    "presenter": "Presenter",
+    "addTechnician": "Add Technician"
+  },
+  "board": {
+    "days": {
+      "mon": "Mon",
+      "tue": "Tue",
+      "wed": "Wed",
+      "thu": "Thu",
+      "fri": "Fri",
+      "sat": "Sat",
+      "sun": "Sun"
+    },
+    "noTechnicians": "No Technicians Yet",
+    "noTechniciansDescription": "Add a technician to start scheduling work on the board.",
+    "failedMove": "Failed to move assignment",
+    "failedAssign": "Failed to assign job",
+    "failedRemove": "Failed to remove assignment"
+  },
+  "unassigned": {
+    "title": "Unassigned Jobs ({count})",
+    "searchPlaceholder": "Search jobs...",
+    "empty": "No unassigned jobs"
+  },
+  "technician": {
+    "editTitle": "Edit Technician",
+    "addTitle": "Add Technician",
+    "name": "Name",
+    "namePlaceholder": "e.g. John Smith",
+    "color": "Color",
+    "linkMember": "Link to Org Member (optional)",
+    "standalone": "Standalone technician",
+    "deleteTitle": "Delete Technician",
+    "deleteDescription": "Remove \"{name}\" from the work board? Their existing assignments will also be removed.",
+    "addTechnician": "Add Technician"
+  },
+  "jobDetail": {
+    "viewDetails": "View Details",
+    "remove": "Remove",
+    "date": "Date: {date}"
+  },
+  "presenter": {
+    "title": "Work Board",
+    "day": "Day",
+    "status": "Status",
+    "week": "Week",
+    "today": "Today",
+    "live": "Live",
+    "disconnected": "Live updates disconnected",
+    "noTechnicians": "No technicians configured",
+    "noJobsToday": "No jobs today",
+    "job": "{count} job",
+    "jobs": "{count} jobs",
+    "statusLabels": {
+      "pending": "Pending",
+      "inProgress": "In Progress",
+      "waitingParts": "Waiting Parts",
+      "completed": "Completed"
+    }
+  }
+}

+ 80 - 0
messages/en/workOrders.json

@@ -0,0 +1,80 @@
+{
+  "page": {
+    "error": "Failed to load work orders"
+  },
+  "list": {
+    "searchPlaceholder": "Search work orders...",
+    "newWorkOrder": "New Work Order",
+    "selectVehicle": "Select Vehicle",
+    "empty": "No work orders found.",
+    "statusUpdated": "Status updated",
+    "statusTabs": {
+      "all": "All",
+      "active": "Active",
+      "pending": "Pending",
+      "inProgress": "In Progress",
+      "waitingParts": "Waiting Parts",
+      "completed": "Completed"
+    },
+    "statusActions": {
+      "startWork": "Start Work",
+      "waitingParts": "Waiting Parts",
+      "complete": "Complete",
+      "resumeWork": "Resume Work",
+      "reopen": "Reopen"
+    },
+    "table": {
+      "invoice": "Invoice#",
+      "vehicle": "Vehicle",
+      "customer": "Customer",
+      "title": "Title",
+      "status": "Status",
+      "tech": "Tech",
+      "date": "Date",
+      "total": "Total"
+    },
+    "emailSubject": "Vehicle Status Update: {status}"
+  },
+  "vehiclePicker": {
+    "title": "Select Vehicle for Work Order",
+    "searchPlaceholder": "Search vehicles...",
+    "empty": "No vehicles found.",
+    "emptySearch": "No vehicles match your search.",
+    "addNewVehicle": "Add New Vehicle",
+    "noPlate": "No plate",
+    "make": "Make *",
+    "model": "Model *",
+    "year": "Year *",
+    "licensePlate": "License Plate",
+    "customer": "Customer",
+    "selectCustomer": "Select a customer (optional)",
+    "createNewCustomer": "Create new customer",
+    "newCustomer": "New Customer",
+    "customerName": "Name *",
+    "customerNamePlaceholder": "Customer name",
+    "phone": "Phone",
+    "phonePlaceholder": "Phone number",
+    "creating": "Creating...",
+    "createAndContinue": "Create & Continue",
+    "invalidYear": "Please enter a valid year",
+    "customerNameRequired": "Customer name is required",
+    "failedCreateCustomer": "Failed to create customer",
+    "failedCreateVehicle": "Failed to create vehicle",
+    "somethingWentWrong": "Something went wrong"
+  },
+  "notify": {
+    "title": "Notify {name}",
+    "description": "Send a notification to the customer about this update.",
+    "sms": "SMS",
+    "email": "Email",
+    "noPhone": "(no phone on file)",
+    "noEmail": "(no email on file)",
+    "notAvailable": "(not available)",
+    "skip": "Skip",
+    "send": "Send",
+    "smsSent": "SMS sent",
+    "emailSent": "Email sent",
+    "failedSms": "Failed to send SMS",
+    "failedEmail": "Failed to send email"
+  }
+}

+ 105 - 0
messages/es/admin.json

@@ -0,0 +1,105 @@
+{
+  "nav": {
+    "overview": { "title": "Resumen", "description": "Estadísticas y estado de la plataforma" },
+    "users": { "title": "Usuarios", "description": "Gestionar todos los usuarios" },
+    "organizations": { "title": "Organizaciones", "description": "Gestionar todas las organizaciones" },
+    "settings": { "title": "Configuración", "description": "Configuración de la plataforma" }
+  },
+  "layout": {
+    "title": "Panel de Administración",
+    "description": "Gestión de la plataforma para superadministradores"
+  },
+  "overview": {
+    "totalUsers": "Usuarios totales",
+    "organizations": "Organizaciones",
+    "activeSubscriptions": "Suscripciones activas",
+    "monthlyRevenue": "Ingresos mensuales"
+  },
+  "users": {
+    "searchPlaceholder": "Buscar por nombre o correo electrónico...",
+    "name": "Nombre",
+    "email": "Correo electrónico",
+    "role": "Rol",
+    "orgs": "Orgs",
+    "created": "Creado",
+    "lastSeen": "Visto por última vez",
+    "noResults": "Ningún usuario coincide con su búsqueda.",
+    "noUsers": "No se encontraron usuarios.",
+    "superAdmin": "Superadministrador",
+    "user": "Usuario",
+    "online": "En línea",
+    "promoteTitle": "Promover usuario",
+    "demoteTitle": "Degradar usuario",
+    "promoteConfirm": "¿Está seguro de que desea promover a \"{name}\" a superadministrador?",
+    "demoteConfirm": "¿Está seguro de que desea degradar a \"{name}\" de superadministrador?",
+    "promote": "Promover",
+    "demote": "Degradar",
+    "promotedSuccess": "Usuario promovido exitosamente",
+    "demotedSuccess": "Usuario degradado exitosamente",
+    "failedUpdate": "Error al actualizar el usuario",
+    "deleteTitle": "Eliminar usuario",
+    "deleteConfirm": "¿Está seguro de que desea eliminar permanentemente a \"{name}\" ({email})? Esta acción no se puede deshacer.",
+    "delete": "Eliminar",
+    "deletedSuccess": "Usuario eliminado exitosamente",
+    "failedDelete": "Error al eliminar el usuario",
+    "promoteToSuperAdmin": "Promover a superadministrador",
+    "demoteFromSuperAdmin": "Degradar de superadministrador",
+    "deleteUser": "Eliminar usuario"
+  },
+  "organizations": {
+    "searchPlaceholder": "Buscar por nombre de organización...",
+    "name": "Nombre",
+    "owner": "Propietario",
+    "members": "Miembros",
+    "subscription": "Suscripción",
+    "created": "Creado",
+    "noResults": "Ninguna organización coincide con su búsqueda.",
+    "noOrganizations": "No se encontraron organizaciones.",
+    "none": "Ninguna",
+    "deleteTitle": "Eliminar organización",
+    "deleteConfirm": "¿Está seguro de que desea eliminar permanentemente \"{name}\" y todos sus datos? Esta acción no se puede deshacer.",
+    "delete": "Eliminar",
+    "deletedSuccess": "Organización eliminada exitosamente",
+    "failedDelete": "Error al eliminar la organización",
+    "deleteOrganization": "Eliminar organización"
+  },
+  "adminSettings": {
+    "platformTitle": "Configuración de la plataforma",
+    "platformDescription": "Opciones generales de configuración de la plataforma",
+    "disableRegistration": "Desactivar registro",
+    "disableRegistrationHint": "Bloquear nuevos registros de usuarios en la plataforma",
+    "emailTitle": "Configuración de correo electrónico",
+    "emailDescription": "Proveedor de correo electrónico de la plataforma — Se utiliza para correos electrónicos a nivel del sistema, como restablecimiento de contraseñas e invitaciones de equipo. Este es también el proveedor de correo electrónico predeterminado para todas las organizaciones. Las organizaciones pueden configurar opcionalmente su propio proveedor de correo electrónico en Configuración > Correo electrónico.",
+    "smtpHost": "Host SMTP",
+    "smtpPort": "Puerto SMTP",
+    "username": "Nombre de usuario",
+    "password": "Contraseña",
+    "fromEmail": "Correo del remitente",
+    "fromName": "Nombre del remitente",
+    "tlsConnection": "Conexión TLS (Puerto 465)",
+    "tlsConnectionHint": "Activar para TLS implícito. Desactivar para STARTTLS (puerto 587/25).",
+    "verifyTls": "Verificar certificados TLS",
+    "verifyTlsHint": "Desactivar para permitir certificados autofirmados (no recomendado para producción)",
+    "requireTlsUpgrade": "Requerir actualización TLS",
+    "requireTlsUpgradeHint": "Forzar actualización TLS en conexiones STARTTLS",
+    "apiKey": "Clave API",
+    "serverToken": "Token del servidor",
+    "domain": "Dominio",
+    "region": "Región",
+    "awsRegion": "Región AWS",
+    "accessKeyId": "ID de clave de acceso",
+    "secretAccessKey": "Clave de acceso secreta",
+    "awsRegionHint": "La región de AWS donde está configurado SES (ej. us-east-1, eu-west-1)",
+    "sendTestEmail": "Enviar correo de prueba",
+    "testEmailHint": "Guarde la configuración primero, luego envíe un correo de prueba a su cuenta",
+    "saveSettings": "Guardar configuración",
+    "saved": "Configuración guardada exitosamente",
+    "failedSave": "Error al guardar la configuración",
+    "testSentTo": "Correo de prueba enviado a {email}",
+    "testFailed": "La prueba de correo electrónico falló",
+    "resendApiKeyHint": "Obtenga su clave API de resend.com",
+    "postmarkApiKeyHint": "Obtenga su Server Token de postmarkapp.com",
+    "mailgunApiKeyHint": "Obtenga su clave API de app.mailgun.com",
+    "sendgridApiKeyHint": "Obtenga su clave API de app.sendgrid.com"
+  }
+}

+ 73 - 0
messages/es/auth.json

@@ -0,0 +1,73 @@
+{
+  "signIn": {
+    "title": "Bienvenido de nuevo",
+    "description": "Inicie sesion en su taller",
+    "passwordPlaceholder": "Ingrese su contrasena",
+    "forgotPassword": "Olvido su contrasena?",
+    "noAccount": "No tiene una cuenta?",
+    "createOne": "Crear una",
+    "termsAgreement": "Al utilizar este servicio, usted acepta nuestros",
+    "errors": {
+      "tooManyAttempts": "Demasiados intentos, vuelva a intentarlo mas tarde.",
+      "invalidCredentials": "Correo electronico o contrasena no validos"
+    }
+  },
+  "signUp": {
+    "title": "Crear una cuenta",
+    "descriptionInvite": "Cree su cuenta para unirse al equipo",
+    "descriptionDefault": "Configure su taller para gestionar los vehiculos de sus clientes",
+    "fullName": "Nombre completo",
+    "fullNamePlaceholder": "Juan Perez",
+    "passwordPlaceholder": "Cree una contrasena segura",
+    "agreeToTerms": "Acepto los",
+    "createAccountJoin": "Crear cuenta y unirse al equipo",
+    "createAccount": "Crear cuenta",
+    "alreadyHaveAccount": "Ya tiene una cuenta?",
+    "errors": {
+      "couldNotCreate": "No se pudo crear la cuenta",
+      "registrationDisabled": "El registro esta deshabilitado actualmente. Por favor, contacte a su administrador.",
+      "invitationFailed": "No se pudo aceptar la invitacion"
+    }
+  },
+  "forgotPassword": {
+    "title": "Restablecer su contrasena",
+    "description": "Ingrese su correo electronico y le enviaremos un enlace de restablecimiento",
+    "sendResetLink": "Enviar enlace de restablecimiento",
+    "backToSignIn": "Volver a iniciar sesion",
+    "successMessage": "Si existe una cuenta con ese correo electronico, se ha enviado un enlace para restablecer la contrasena. Revise su bandeja de entrada."
+  },
+  "resetPassword": {
+    "title": "Establecer nueva contrasena",
+    "description": "Ingrese su nueva contrasena a continuacion",
+    "newPassword": "Nueva contrasena",
+    "newPasswordPlaceholder": "Ingrese la nueva contrasena",
+    "confirmPassword": "Confirmar contrasena",
+    "confirmPasswordPlaceholder": "Confirme la nueva contrasena",
+    "resetButton": "Restablecer contrasena",
+    "backToSignIn": "Volver a iniciar sesion",
+    "requestNewLink": "Solicitar nuevo enlace",
+    "invalidToken": "Token de restablecimiento no valido o faltante. Por favor, solicite un nuevo enlace de restablecimiento de contrasena.",
+    "successMessage": "Su contrasena se ha restablecido correctamente. Ahora puede iniciar sesion con su nueva contrasena.",
+    "errors": {
+      "passwordsMismatch": "Las contrasenas no coinciden.",
+      "passwordTooShort": "La contrasena debe tener al menos 8 caracteres.",
+      "resetFailed": "No se pudo restablecer la contrasena. Es posible que el enlace haya expirado."
+    }
+  },
+  "verify2fa": {
+    "title": "Autenticacion de dos factores",
+    "descriptionTotp": "Ingrese el codigo de 6 digitos de su aplicacion de autenticacion",
+    "descriptionBackup": "Ingrese uno de sus codigos de respaldo",
+    "authCode": "Codigo de autenticacion",
+    "backupCode": "Codigo de respaldo",
+    "backupCodePlaceholder": "Ingrese el codigo de respaldo",
+    "totpPlaceholder": "000000",
+    "trustDevice": "Confiar en este dispositivo durante 30 dias",
+    "verify": "Verificar",
+    "useAuthenticator": "Usar la aplicacion de autenticacion",
+    "useBackupCode": "Usar un codigo de respaldo",
+    "errors": {
+      "invalidCode": "Codigo no valido"
+    }
+  }
+}

+ 83 - 0
messages/es/billing.json

@@ -0,0 +1,83 @@
+{
+  "history": {
+    "title": "Historial de facturación",
+    "recurring": "Recurrente",
+    "totalRevenue": "Ingresos totales",
+    "collected": "Cobrado",
+    "outstanding": "Pendiente",
+    "statusAll": "Todos",
+    "statusPaid": "Pagado",
+    "statusPartial": "Parcial",
+    "statusUnpaid": "Impago",
+    "searchPlaceholder": "Buscar facturas...",
+    "search": "Buscar",
+    "columnInvoice": "Factura N.°",
+    "columnTitle": "Título",
+    "columnVehicle": "Vehículo",
+    "columnCustomer": "Cliente",
+    "columnDate": "Fecha",
+    "columnTotal": "Total",
+    "columnPaid": "Pagado",
+    "columnBalance": "Saldo",
+    "noRecords": "No se encontraron registros de facturación."
+  },
+  "recurring": {
+    "title": "Recurrente",
+    "billingHistory": "Historial de facturación",
+    "processNow": "Procesar ahora",
+    "newRecurring": "Nueva factura recurrente",
+    "noInvoices": "Aún no hay facturas recurrentes.",
+    "columnTitle": "Título",
+    "columnVehicle": "Vehículo",
+    "columnCustomer": "Cliente",
+    "columnFrequency": "Frecuencia",
+    "columnNextRun": "Próxima ejecución",
+    "columnAmount": "Monto",
+    "columnStatus": "Estado",
+    "columnRuns": "Ejecuciones",
+    "statusActive": "Activo",
+    "statusPaused": "Pausado",
+    "frequencyWeekly": "Semanal",
+    "frequencyBiweekly": "Quincenal",
+    "frequencyMonthly": "Mensual",
+    "frequencyQuarterly": "Trimestral",
+    "frequencyYearly": "Anual",
+    "serviceTypeMaintenance": "Mantenimiento",
+    "serviceTypeRepair": "Reparación",
+    "serviceTypeInspection": "Inspección",
+    "serviceTypeUpgrade": "Mejora",
+    "dialogTitle": "Nueva factura recurrente",
+    "titleLabel": "Título *",
+    "titlePlaceholder": "p. ej. Cambio de aceite mensual",
+    "vehicleLabel": "Vehículo *",
+    "selectVehicle": "Seleccionar vehículo",
+    "descriptionLabel": "Descripción",
+    "frequencyLabel": "Frecuencia",
+    "startDate": "Fecha de inicio *",
+    "endDate": "Fecha de fin",
+    "serviceType": "Tipo de servicio",
+    "baseCost": "Costo base",
+    "taxRate": "Tasa de impuesto (%)",
+    "invoiceNotes": "Notas de factura",
+    "templateParts": "Piezas de plantilla",
+    "addPart": "Agregar pieza",
+    "partName": "Nombre de pieza",
+    "partNumber": "Pieza N.°",
+    "templateLabor": "Mano de obra de plantilla",
+    "addLabor": "Agregar mano de obra",
+    "description": "Descripción",
+    "hours": "Horas",
+    "rate": "Tarifa",
+    "cancel": "Cancelar",
+    "create": "Crear",
+    "requiredFields": "El título y el vehículo son obligatorios",
+    "created": "Factura recurrente creada",
+    "failedCreate": "Error al crear",
+    "statusUpdated": "Estado actualizado",
+    "failedUpdate": "Error al actualizar",
+    "deleted": "Factura recurrente eliminada",
+    "failedDelete": "Error al eliminar",
+    "processed": "{count} factura(s) procesada(s)",
+    "failedProcess": "Error al procesar"
+  }
+}

+ 61 - 0
messages/es/calendar.json

@@ -0,0 +1,61 @@
+{
+  "weekdays": {
+    "sun": "Dom",
+    "mon": "Lun",
+    "tue": "Mar",
+    "wed": "Mi\u00e9",
+    "thu": "Jue",
+    "fri": "Vie",
+    "sat": "S\u00e1b"
+  },
+  "months": {
+    "january": "Enero",
+    "february": "Febrero",
+    "march": "Marzo",
+    "april": "Abril",
+    "may": "Mayo",
+    "june": "Junio",
+    "july": "Julio",
+    "august": "Agosto",
+    "september": "Septiembre",
+    "october": "Octubre",
+    "november": "Noviembre",
+    "december": "Diciembre"
+  },
+  "stats": {
+    "services": "Servicios",
+    "overdue": "Vencidos",
+    "pendingQuotes": "Presupuestos pendientes",
+    "revenue": "Ingresos"
+  },
+  "today": "Hoy",
+  "workOrder": "Orden de trabajo",
+  "filters": {
+    "services": "Servicios",
+    "reminders": "Recordatorios",
+    "quotes": "Presupuestos"
+  },
+  "selectVehicle": "Seleccionar veh\u00edculo para orden de trabajo",
+  "more": "+ {count} m\u00e1s",
+  "events": {
+    "status": {
+      "completed": "Completado",
+      "inProgress": "En progreso",
+      "waitingParts": "Esperando piezas",
+      "pending": "Pendiente",
+      "sent": "Enviado",
+      "approved": "Aprobado",
+      "draft": "Borrador",
+      "done": "Hecho",
+      "overdue": "Vencido",
+      "upcoming": "Pr\u00f3ximo"
+    },
+    "type": {
+      "service": "Servicio",
+      "reminder": "Recordatorio",
+      "quote": "Presupuesto"
+    },
+    "noEvents": "Sin eventos",
+    "selectDay": "Seleccione un d\u00eda con eventos o cree una nueva orden de trabajo."
+  }
+}

+ 43 - 0
messages/es/common.json

@@ -0,0 +1,43 @@
+{
+  "brandName": "Torqvoice",
+  "buttons": {
+    "signIn": "Iniciar sesion",
+    "save": "Guardar",
+    "cancel": "Cancelar",
+    "delete": "Eliminar",
+    "submit": "Enviar",
+    "back": "Volver",
+    "confirm": "Confirmar",
+    "saveChanges": "Guardar cambios",
+    "edit": "Editar",
+    "add": "Agregar"
+  },
+  "form": {
+    "email": "Correo electronico",
+    "password": "Contrasena",
+    "emailPlaceholder": "you@example.com",
+    "name": "Nombre",
+    "phone": "Teléfono",
+    "company": "Empresa",
+    "address": "Dirección",
+    "notes": "Notas"
+  },
+  "errors": {
+    "unexpected": "Ha ocurrido un error inesperado. Por favor, intentelo de nuevo.",
+    "error": "Error"
+  },
+  "terms": {
+    "termsOfService": "Terminos de servicio"
+  },
+  "errorPage": {
+    "title": "Algo salió mal",
+    "description": "El motor se caló inesperadamente. Nuestros mecánicos han sido notificados y están trabajando en ello.",
+    "tryAgain": "Intentar de nuevo",
+    "backToDashboard": "Volver al panel"
+  },
+  "notFoundPage": {
+    "title": "Página no encontrada",
+    "description": "Parece que esta pieza aún está en el taller. La página que buscas no existe o ha sido movida.",
+    "backToDashboard": "Volver al panel"
+  }
+}

+ 76 - 0
messages/es/customers.json

@@ -0,0 +1,76 @@
+{
+  "list": {
+    "addCustomer": "Agregar Cliente",
+    "searchPlaceholder": "Buscar clientes...",
+    "table": {
+      "name": "Nombre",
+      "company": "Empresa",
+      "phone": "Teléfono",
+      "email": "Correo electrónico",
+      "vehicles": "Vehículos"
+    },
+    "emptySearch": "Ningún cliente coincide con tu búsqueda.",
+    "empty": "Aún no hay clientes.",
+    "deleteTitle": "Eliminar Cliente",
+    "deleteDescription": "¿Eliminar a {name}? Esto desvinculará sus vehículos.",
+    "deleteError": "Error al eliminar el cliente",
+    "error": "Error al cargar los clientes"
+  },
+  "detail": {
+    "backToCustomers": "Volver a clientes",
+    "editCustomer": "Editar Cliente",
+    "notFound": "Cliente no encontrado",
+    "emailCopied": "Correo electrónico copiado al portapapeles",
+    "phoneCopied": "Número de teléfono copiado al portapapeles",
+    "tabs": {
+      "vehicles": "Vehículos ({count})",
+      "messages": "Mensajes",
+      "requests": "Solicitudes ({count})"
+    },
+    "vehicleTable": {
+      "plate": "Placa",
+      "vehicle": "Vehículo",
+      "mileage": "Kilometraje",
+      "services": "Servicios"
+    },
+    "noVehicles": "No hay vehículos asignados a este cliente"
+  },
+  "form": {
+    "addTitle": "Agregar Nuevo Cliente",
+    "editTitle": "Editar Cliente",
+    "nameRequired": "Nombre *",
+    "namePlaceholder": "Juan García",
+    "emailPlaceholder": "juan@ejemplo.com",
+    "phonePlaceholder": "(55) 1234-5678",
+    "companyPlaceholder": "Nombre de la empresa",
+    "addressPlaceholder": "Calle Principal 123, Ciudad, Estado",
+    "notesPlaceholder": "Notas adicionales...",
+    "customerUpdated": "Cliente actualizado",
+    "customerCreated": "Cliente creado",
+    "saveError": "Error al guardar el cliente"
+  },
+  "serviceRequests": {
+    "statusNew": "Nuevo",
+    "statusConverted": "Orden de Trabajo Creada",
+    "statusDismissed": "Descartado",
+    "createWorkOrder": "Crear Orden de Trabajo",
+    "contactCustomer": "Contactar Cliente",
+    "dismiss": "Descartar",
+    "viewWorkOrder": "Ver Orden de Trabajo",
+    "notes": "Notas",
+    "hideNotes": "Ocultar Notas",
+    "staffNotes": "Notas del personal:",
+    "notesPlaceholder": "Agregar notas internas...",
+    "saveNotes": "Guardar Notas",
+    "submitted": "Enviado {date}",
+    "preferred": "Preferido: {date}",
+    "workOrderCreated": "Orden de trabajo creada",
+    "workOrderError": "Error al crear la orden de trabajo",
+    "requestDismissed": "Solicitud descartada",
+    "dismissError": "Error al descartar la solicitud",
+    "notesSaved": "Notas guardadas",
+    "notesSaveError": "Error al guardar las notas",
+    "smsLabel": "Mensaje de Solicitud de Servicio",
+    "smsDefault": "Hola {name}, sobre tu solicitud de servicio para tu {vehicle}: "
+  }
+}

+ 89 - 0
messages/es/dashboard.json

@@ -0,0 +1,89 @@
+{
+  "stats": {
+    "activeJobs": "Trabajos activos",
+    "pending": "Pendientes",
+    "todaysRevenue": "Ingresos de hoy",
+    "totalCustomers": "Total de clientes"
+  },
+  "customize": "Personalizar",
+  "showCards": "Mostrar tarjetas",
+  "cards": {
+    "maintenance": "Mantenimiento previsto",
+    "reminders": "Recordatorios pendientes",
+    "inspections": "Inspecciones",
+    "quoteRequests": "Solicitudes de presupuesto",
+    "quoteResponses": "Respuestas de presupuesto",
+    "sms": "Mensajes recientes",
+    "notifications": "Notificaciones recientes",
+    "recentCompleted": "Completados recientemente",
+    "activeJobs": "Trabajos activos"
+  },
+  "maintenance": {
+    "title": "Mantenimiento previsto pendiente",
+    "description": "Basado en el historial de servicio y el kilometraje diario estimado",
+    "overdue": "Vencido",
+    "approaching": "Pr\u00f3ximo",
+    "estimated": "Est. {mileage} {unit}",
+    "sinceLastService": "{mileage} {unit} desde el \u00faltimo servicio",
+    "certainty": "{percent}% de certeza"
+  },
+  "reminders": {
+    "title": "Recordatorios pendientes",
+    "overdue": "Vencidos",
+    "dueSoon": "Pr\u00f3ximamente"
+  },
+  "messages": {
+    "title": "Mensajes recientes",
+    "viewAll": "Ver todos",
+    "you": "Usted: "
+  },
+  "notifications": {
+    "title": "Notificaciones recientes"
+  },
+  "inspections": {
+    "title": "Inspecciones",
+    "inProgress": "En progreso",
+    "completed": "Completadas",
+    "itemCount": "{inspected}/{total} elementos"
+  },
+  "quoteRequests": {
+    "title": "Solicitudes de presupuesto",
+    "description": "Clientes que solicitan presupuestos a partir de inspecciones compartidas",
+    "pending": "Pendientes",
+    "createQuote": "Crear presupuesto",
+    "itemsRequested": "{count, plural, one {# elemento solicitado} other {# elementos solicitados}}"
+  },
+  "quoteResponses": {
+    "title": "Respuestas de presupuesto de clientes",
+    "description": "Presupuestos que los clientes han aceptado o en los que han solicitado cambios",
+    "accepted": "Aceptado",
+    "changesRequested": "Cambios solicitados",
+    "convertToWorkOrder": "Convertir en orden de trabajo",
+    "viewQuote": "Ver presupuesto"
+  },
+  "recentCompleted": {
+    "title": "Completados recientemente",
+    "date": "Fecha",
+    "vehicle": "Veh\u00edculo",
+    "customer": "Cliente",
+    "serviceTitle": "T\u00edtulo",
+    "total": "Total",
+    "empty": "A\u00fan no hay servicios completados"
+  },
+  "activeJobsTable": {
+    "title": "Trabajos activos",
+    "vehicle": "Veh\u00edculo",
+    "customer": "Cliente",
+    "serviceTitle": "T\u00edtulo",
+    "status": "Estado",
+    "tech": "T\u00e9cnico",
+    "empty": "No hay trabajos activos"
+  },
+  "relativeTime": {
+    "now": "ahora",
+    "minutes": "{count}m",
+    "hours": "{count}h",
+    "days": "{count}d"
+  },
+  "error": "Error al cargar el panel de control"
+}

+ 87 - 0
messages/es/inventory.json

@@ -0,0 +1,87 @@
+{
+  "searchPlaceholder": "Buscar piezas...",
+  "categoryPlaceholder": "Categoría",
+  "allCategories": "Todas las categorías",
+  "applyMarkup": "Aplicar margen",
+  "addPart": "Añadir pieza",
+  "table": {
+    "partNumber": "N.º pieza",
+    "name": "Nombre",
+    "category": "Categoría",
+    "inStock": "En stock",
+    "unitCost": "Coste unitario",
+    "sellPrice": "Precio de venta",
+    "supplier": "Proveedor",
+    "location": "Ubicación",
+    "low": "Bajo"
+  },
+  "empty": {
+    "noMatch": "Ninguna pieza coincide con su búsqueda.",
+    "noParts": "Aún no hay piezas en el inventario."
+  },
+  "actions": {
+    "edit": "Editar",
+    "delete": "Eliminar"
+  },
+  "deletePart": {
+    "title": "Eliminar pieza",
+    "description": "¿Eliminar \"{name}\" del inventario? Esta acción no se puede deshacer.",
+    "confirm": "Eliminar"
+  },
+  "markup": {
+    "title": "Aplicar margen a todas las piezas",
+    "multiplierLabel": "Multiplicador de margen",
+    "description": "Esto establecerá el precio de venta = coste x {multiplier} para las {count} piezas.",
+    "percentage": "({percent}% de margen)",
+    "cancel": "Cancelar",
+    "apply": "Aplicar"
+  },
+  "errors": {
+    "multiplierPositive": "El multiplicador debe ser mayor que 0",
+    "deleteFailed": "No se pudo eliminar la pieza",
+    "applyFailed": "No se pudo aplicar el margen",
+    "saveFailed": "No se pudo guardar la pieza",
+    "error": "Error"
+  },
+  "form": {
+    "editPart": "Editar pieza",
+    "addNewPart": "Añadir nueva pieza",
+    "supplierLink": "Enlace del proveedor",
+    "supplierLinkPlaceholder": "https://supplier.com/product/...",
+    "fetch": "Obtener",
+    "fetchFailed": "Error al obtener",
+    "fetchError": "No se pudieron obtener los metadatos de la URL",
+    "image": "Imagen",
+    "dropOrClick": "Arrastre una imagen o haga clic",
+    "replace": "Reemplazar",
+    "upload": "Subir",
+    "imageUploading": "Subiendo imagen...",
+    "imageUploaded": "Imagen subida",
+    "imageUploadFailed": "No se pudo subir la imagen",
+    "imageTypeError": "Solo se permiten imágenes JPEG, PNG, WebP y AVIF",
+    "imageSizeError": "La imagen debe ser menor de 5 MB",
+    "partNumber": "Número de pieza",
+    "partNumberPlaceholder": "BRK-001",
+    "nameLabel": "Nombre *",
+    "namePlaceholder": "Juego de pastillas de freno",
+    "category": "Categoría",
+    "categoryPlaceholder": "Frenos",
+    "location": "Ubicación",
+    "locationPlaceholder": "Estante A-3",
+    "quantity": "Cantidad",
+    "minQty": "Cant. mín.",
+    "unitCost": "Coste unitario",
+    "sellPrice": "Precio de venta",
+    "supplier": "Proveedor",
+    "supplierPlaceholder": "AutoParts S.L.",
+    "supplierPhone": "Teléfono del proveedor",
+    "supplierPhonePlaceholder": "(555) 123-4567",
+    "supplierEmail": "Correo del proveedor",
+    "supplierEmailPlaceholder": "orders@autoparts.com",
+    "description": "Descripción",
+    "descriptionPlaceholder": "Descripción de la pieza...",
+    "cancel": "Cancelar",
+    "saveChanges": "Guardar cambios",
+    "addPart": "Añadir pieza"
+  }
+}

+ 45 - 0
messages/es/messages.json

@@ -0,0 +1,45 @@
+{
+  "page": {
+    "featureName": "Mensajería SMS",
+    "featureDescription": "Envíe y reciba mensajes SMS con sus clientes directamente desde Torqvoice."
+  },
+  "notConfigured": {
+    "title": "SMS no configurado",
+    "description": "Configure un proveedor de SMS para enviar y recibir mensajes con sus clientes. Compatible con Twilio, Vonage y Telnyx.",
+    "goToSettings": "Ir a configuración de SMS"
+  },
+  "threads": {
+    "title": "Conversaciones",
+    "empty": "Aún no hay conversaciones. Envíe un SMS desde la página de un cliente para comenzar.",
+    "loadMore": "Cargar más",
+    "selectConversation": "Seleccione una conversación",
+    "you": "Tú: ",
+    "deleteConversation": "Eliminar conversación",
+    "deleteTitle": "¿Eliminar conversación?",
+    "deleteDescription": "Se eliminarán permanentemente todos los mensajes con {name}. Esta acción no se puede deshacer.",
+    "deleted": "Conversación eliminada",
+    "deleteError": "No se pudo eliminar la conversación"
+  },
+  "conversation": {
+    "noPhone": "No hay número de teléfono registrado para {name}. Agregue un número de teléfono para comenzar a enviar mensajes.",
+    "loadOlder": "Cargar anteriores",
+    "empty": "Aún no hay mensajes. Envíe el primer mensaje a {name}.",
+    "placeholder": "Mensaje para {name}...",
+    "deleteMessage": "Eliminar mensaje",
+    "deleteTitle": "¿Eliminar mensaje?",
+    "deleteDescription": "Este mensaje se eliminará permanentemente de la conversación. Esta acción no se puede deshacer.",
+    "messageDeleted": "Mensaje eliminado",
+    "deleteError": "No se pudo eliminar el mensaje",
+    "sendError": "No se pudo enviar el SMS"
+  },
+  "sendDialog": {
+    "send": "Enviar {label}",
+    "message": "Mensaje",
+    "placeholder": "Escribe tu mensaje...",
+    "characters": "{count} / 1600 caracteres",
+    "sendSms": "Enviar SMS",
+    "smsSent": "SMS enviado",
+    "sentTo": "{label} ha sido enviado a {name} ({phone})",
+    "sendError": "No se pudo enviar el SMS"
+  }
+}

+ 85 - 0
messages/es/navigation.json

@@ -0,0 +1,85 @@
+{
+  "breadcrumbs": {
+    "dashboard": "Panel de control",
+    "vehicles": "Veh\u00edculos",
+    "allVehicles": "Todos los veh\u00edculos",
+    "vehicleDetails": "Detalles del veh\u00edculo",
+    "customers": "Clientes",
+    "allCustomers": "Todos los clientes",
+    "customerDetails": "Detalles del cliente",
+    "workOrders": "\u00d3rdenes de trabajo",
+    "allWorkOrders": "Todas las \u00f3rdenes de trabajo",
+    "quotes": "Presupuestos",
+    "allQuotes": "Todos los presupuestos",
+    "newQuote": "Nuevo presupuesto",
+    "quoteDetails": "Detalles del presupuesto",
+    "edit": "Editar",
+    "billing": "Facturaci\u00f3n",
+    "billingHistory": "Historial de facturaci\u00f3n",
+    "inventory": "Inventario",
+    "allParts": "Todas las piezas",
+    "reports": "Informes",
+    "workBoard": "Tablero de trabajo",
+    "presenter": "Presentador",
+    "adminOverview": "Resumen de administraci\u00f3n",
+    "admin": "Administraci\u00f3n",
+    "users": "Usuarios",
+    "organizations": "Organizaciones",
+    "settings": "Configuraci\u00f3n",
+    "company": "Empresa",
+    "account": "Cuenta",
+    "customFields": "Campos personalizados",
+    "templates": "Plantillas",
+    "team": "Equipo",
+    "invoice": "Factura",
+    "payment": "Pago",
+    "currency": "Moneda",
+    "workshop": "Taller",
+    "appearance": "Apariencia",
+    "email": "Correo electr\u00f3nico",
+    "sms": "SMS",
+    "about": "Acerca de",
+    "home": "Inicio",
+    "newServiceRecord": "Nuevo registro de servicio",
+    "serviceDetails": "Detalles del servicio",
+    "data": "Datos",
+    "license": "Licencia",
+    "subscription": "Suscripci\u00f3n",
+    "maintenance": "Mantenimiento",
+    "customerPortal": "Portal del cliente"
+  },
+  "search": "Buscar...",
+  "shortcut": "Ctrl+K",
+  "newWorkOrder": "Nueva orden de trabajo",
+  "purchaseWhiteLabel": "Adquirir marca blanca",
+  "sidebar": {
+    "dashboard": "Panel de control",
+    "clients": "Clientes",
+    "customers": "Clientes",
+    "messages": "Mensajes",
+    "workshop": "Taller",
+    "vehicles": "Vehículos",
+    "workOrders": "Órdenes de trabajo",
+    "inspections": "Inspecciones",
+    "calendar": "Calendario",
+    "workBoard": "Tablero de trabajo",
+    "business": "Negocio",
+    "quotes": "Presupuestos",
+    "billing": "Facturación",
+    "inventory": "Inventario",
+    "reports": "Informes",
+    "settings": "Configuración",
+    "superAdmin": "Super Admin",
+    "adminPanel": "Panel de administración",
+    "organizations": "Organizaciones",
+    "noOrganization": "Sin organización",
+    "addNewCompany": "Añadir nueva empresa",
+    "companyName": "Nombre de la empresa",
+    "companyPlaceholder": "ej. Taller Mecánico López",
+    "cancel": "Cancelar",
+    "createCompany": "Crear empresa",
+    "lightMode": "Modo claro",
+    "darkMode": "Modo oscuro",
+    "signOut": "Cerrar sesión"
+  }
+}

+ 10 - 0
messages/es/notifications.json

@@ -0,0 +1,10 @@
+{
+  "panel": {
+    "title": "Notificaciones",
+    "markAllRead": "Marcar todo como leído",
+    "empty": "Aún no hay notificaciones",
+    "confirmDelete": "¿Estás seguro?",
+    "delete": "Eliminar",
+    "cancel": "Cancelar"
+  }
+}

+ 99 - 0
messages/es/pdf.json

@@ -0,0 +1,99 @@
+{
+  "invoice": {
+    "title": "FACTURA",
+    "due": "Vence: {date}",
+    "billTo": "Facturar a",
+    "vehicle": "Vehículo",
+    "vin": "VIN: {vin}",
+    "plate": "Placa: {plate}",
+    "mileage": "Kilometraje: {mileage}",
+    "km": "km",
+    "mi": "mi",
+    "service": "Servicio",
+    "type": "Tipo: {type}",
+    "tech": "Técnico: {tech}",
+    "parts": "Piezas",
+    "partNumber": "N.º de pieza",
+    "description": "Descripción",
+    "qty": "Cant.",
+    "unitPrice": "Precio unitario",
+    "total": "Total",
+    "labor": "Mano de obra",
+    "hours": "Horas",
+    "rate": "Tarifa",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "discountPercent": "Descuento ({percent}%)",
+    "tax": "Impuesto ({rate}%)",
+    "paymentsReceived": "Pagos recibidos",
+    "paidInFull": "PAGADO EN SU TOTALIDAD",
+    "amountDue": "Monto adeudado",
+    "notes": "Notas",
+    "bankAccount": "Til Konto / Cuenta bancaria",
+    "diagnosticNotes": "Notas de diagnóstico",
+    "attachedDocuments": "Documentos adjuntos",
+    "seeAppendedPages": "{name} (ver páginas adjuntas)",
+    "serviceImages": "Imágenes del servicio",
+    "paymentTerms": "Condiciones de pago: {terms}",
+    "viewPortal": "Ver su portal: {url}",
+    "professionalWorkshop": "Gestión profesional de talleres",
+    "tel": "Tel: {phone}",
+    "org": "Org: {org}"
+  },
+  "quote": {
+    "title": "PRESUPUESTO",
+    "to": "Para",
+    "vehicle": "Vehículo",
+    "vin": "VIN: {vin}",
+    "plate": "Placa: {plate}",
+    "quoteDetails": "Detalles del presupuesto",
+    "parts": "Piezas",
+    "partNumber": "N.º de pieza",
+    "description": "Descripción",
+    "qty": "Cant.",
+    "unitPrice": "Precio unitario",
+    "total": "Total",
+    "labor": "Mano de obra",
+    "hours": "Horas",
+    "rate": "Tarifa",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "discountPercent": "Descuento ({percent}%)",
+    "tax": "Impuesto ({rate}%)",
+    "validUntil": "Válido hasta: {date}",
+    "attachedDocuments": "Documentos adjuntos",
+    "attached": "{name} (adjunto)",
+    "quoteImages": "Imágenes del presupuesto",
+    "validityFooterUntil": "Este presupuesto es válido hasta el {date}",
+    "validityFooter30": "Este presupuesto es válido por 30 días",
+    "viewPortal": "Ver su portal: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "INSPECCIÓN DE VEHÍCULO",
+    "vehicle": "Vehículo",
+    "vin": "VIN: {vin}",
+    "plate": "Placa: {plate}",
+    "mileage": "Kilometraje: {mileage}",
+    "customer": "Cliente",
+    "inspectionDetails": "Detalles de la inspección",
+    "statusCompleted": "Completado",
+    "statusInProgress": "En progreso",
+    "status": "Estado: {status}",
+    "inspected": "Inspeccionado",
+    "pass": "Aprobado",
+    "attention": "Atención",
+    "fail": "Reprobado",
+    "item": "Elemento",
+    "statusColumn": "Estado",
+    "notesColumn": "Notas",
+    "footerText": "Inspección de vehículo — {shopName}",
+    "viewPortal": "Ver su portal: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "common": {
+    "poweredBy": "Desarrollado por"
+  }
+}

+ 133 - 0
messages/es/portal.json

@@ -0,0 +1,133 @@
+{
+  "login": {
+    "signInTitle": "Inicie sesión en su portal de clientes",
+    "emailLabel": "Dirección de correo electrónico",
+    "emailPlaceholder": "usted@ejemplo.com",
+    "sendLink": "Enviar enlace de acceso",
+    "checkEmail": "Revise su correo electrónico",
+    "checkEmailDescription": "Si existe una cuenta para ese correo, hemos enviado un enlace de acceso. El enlace expira en 15 minutos.",
+    "tryDifferentEmail": "Probar con otro correo electrónico",
+    "notFound": "No encontrado",
+    "portalNotExist": "Este portal no existe.",
+    "portalNotAvailable": "Portal no disponible",
+    "portalNotEnabled": "El portal de clientes no está habilitado para esta organización."
+  },
+  "nav": {
+    "dashboard": "Panel",
+    "vehicles": "Vehículos",
+    "invoices": "Facturas",
+    "quotes": "Presupuestos",
+    "inspections": "Inspecciones",
+    "requestService": "Solicitar servicio"
+  },
+  "header": {
+    "signOut": "Cerrar sesión"
+  },
+  "shell": {
+    "notFound": "No encontrado",
+    "portalNotExist": "Este portal no existe.",
+    "portalNotAvailable": "Portal no disponible",
+    "portalNotEnabled": "El portal de clientes no está habilitado para esta organización."
+  },
+  "dashboard": {
+    "welcome": "Bienvenido, {name}",
+    "overview": "Aquí hay un resumen de su cuenta.",
+    "vehicles": "Vehículos",
+    "openInvoices": "Facturas pendientes",
+    "pendingQuotes": "Presupuestos pendientes",
+    "recentInvoices": "Facturas recientes",
+    "recentQuotes": "Presupuestos recientes",
+    "pendingServiceRequests": "Solicitudes de servicio pendientes",
+    "viewAll": "Ver todo",
+    "newRequest": "Nueva solicitud",
+    "noInvoicesYet": "Aún no hay facturas.",
+    "noQuotesYet": "Aún no hay presupuestos.",
+    "view": "Ver",
+    "pending": "Pendiente",
+    "failedToLoad": "Error al cargar el panel."
+  },
+  "invoices": {
+    "title": "Facturas",
+    "description": "Todas sus facturas de servicio.",
+    "noInvoices": "Aún no hay facturas.",
+    "failedToLoad": "Error al cargar las facturas.",
+    "invoice": "Factura",
+    "vehicle": "Vehículo",
+    "date": "Fecha",
+    "amount": "Monto",
+    "payment": "Pago",
+    "view": "Ver",
+    "paid": "pagado",
+    "partial": "parcial",
+    "unpaid": "sin pagar"
+  },
+  "quotes": {
+    "title": "Presupuestos",
+    "description": "Presupuestos y estimaciones para sus vehículos.",
+    "noQuotes": "Aún no hay presupuestos.",
+    "failedToLoad": "Error al cargar los presupuestos.",
+    "quote": "Presupuesto",
+    "vehicle": "Vehículo",
+    "status": "Estado",
+    "amount": "Monto",
+    "validUntil": "Válido hasta",
+    "view": "Ver"
+  },
+  "inspections": {
+    "title": "Inspecciones",
+    "description": "Informes de inspección de vehículos.",
+    "noInspections": "Aún no hay inspecciones.",
+    "failedToLoad": "Error al cargar las inspecciones.",
+    "vehicle": "Vehículo",
+    "template": "Plantilla",
+    "status": "Estado",
+    "date": "Fecha",
+    "summary": "Resumen",
+    "view": "Ver",
+    "good": "{count} bueno",
+    "fair": "{count} regular",
+    "poor": "{count} malo"
+  },
+  "vehicles": {
+    "title": "Vehículos",
+    "description": "Sus vehículos registrados.",
+    "noVehicles": "No se encontraron vehículos.",
+    "failedToLoad": "Error al cargar los vehículos.",
+    "plate": "Placa: {plate}",
+    "vin": "VIN: {vin}",
+    "serviceCount": "{count, plural, one {# servicio} other {# servicios}}",
+    "inspectionCount": "{count, plural, one {# inspección} other {# inspecciones}}",
+    "serviceHistory": "Historial de servicio ({count})",
+    "inspectionsTab": "Inspecciones ({count})",
+    "noServiceRecords": "Aún no hay registros de servicio.",
+    "noInspections": "Aún no hay inspecciones.",
+    "vehicleNotFound": "Vehículo no encontrado.",
+    "completed": "Completado el {date}",
+    "good": "{count} bueno",
+    "fair": "{count} regular",
+    "poor": "{count} malo",
+    "view": "Ver"
+  },
+  "requestService": {
+    "title": "Solicitar servicio",
+    "description": "Envíe una solicitud de servicio para uno de sus vehículos.",
+    "newServiceRequest": "Nueva solicitud de servicio",
+    "formDescription": "Describa el problema y nos pondremos en contacto con usted.",
+    "vehicleLabel": "Vehículo",
+    "vehiclePlaceholder": "Seleccione un vehículo",
+    "descriptionLabel": "Describa el problema",
+    "descriptionPlaceholder": "¿Qué está pasando con su vehículo?",
+    "preferredDate": "Fecha preferida",
+    "preferredDateOptional": "(opcional)",
+    "submitRequest": "Enviar solicitud",
+    "selectVehicle": "Por favor seleccione un vehículo",
+    "describeIssue": "Por favor describa el problema",
+    "submitSuccess": "Solicitud de servicio enviada",
+    "submitError": "Error al enviar la solicitud",
+    "noVehicles": "No tiene vehículos registrados. Por favor contáctenos para agregar un vehículo primero.",
+    "yourRequests": "Sus solicitudes de servicio",
+    "failedToLoad": "Error al cargar los datos.",
+    "staffNotes": "Notas del personal: {notes}",
+    "preferred": "Preferido: {date}"
+  }
+}

+ 173 - 0
messages/es/quotes.json

@@ -0,0 +1,173 @@
+{
+  "list": {
+    "statusAll": "Todos",
+    "statusDraft": "Borrador",
+    "statusSent": "Enviado",
+    "statusAccepted": "Aceptado",
+    "statusRejected": "Rechazado",
+    "statusConverted": "Convertido",
+    "searchPlaceholder": "Buscar presupuestos...",
+    "newQuote": "Nuevo presupuesto",
+    "columnQuoteNumber": "Presupuesto N.º",
+    "columnTitle": "Título",
+    "columnCustomer": "Cliente",
+    "columnVehicle": "Vehículo",
+    "columnStatus": "Estado",
+    "columnDate": "Fecha",
+    "columnTotal": "Total",
+    "noQuotes": "No se encontraron presupuestos."
+  },
+  "page": {
+    "quote": "Presupuesto",
+    "save": "Guardar",
+    "pdf": "PDF",
+    "email": "Correo electrónico",
+    "share": "Compartir",
+    "delete": "Eliminar",
+    "deleteTitle": "Eliminar presupuesto",
+    "deleteDescription": "Este presupuesto se eliminará permanentemente.",
+    "saved": "Presupuesto guardado",
+    "failedSave": "Error al guardar el presupuesto",
+    "failedDelete": "Error al eliminar",
+    "failedPdf": "Error al generar el PDF",
+    "selectVehicle": "Por favor seleccione un vehículo",
+    "convertTitle": "Convertir presupuesto en orden de trabajo",
+    "convertDescription": "Se creará un nuevo registro de servicio (orden de trabajo) a partir de este presupuesto. Seleccione un vehículo para la orden de trabajo.",
+    "convert": "Convertir",
+    "convertToWorkOrder": "Convertir en orden de trabajo",
+    "cancel": "Cancelar",
+    "failedConvert": "Error al convertir",
+    "changesRequested": "Cambios solicitados",
+    "quoteAccepted": "Presupuesto aceptado",
+    "markResolved": "Marcar como resuelto",
+    "responseResolved": "Respuesta del cliente resuelta",
+    "entityLabel": "Presupuesto",
+    "tabs": {
+      "details": "Detalles",
+      "images": "Imágenes",
+      "documents": "Documentos"
+    }
+  },
+  "form": {
+    "newQuote": "Nuevo presupuesto",
+    "editQuote": "Editar presupuesto",
+    "cancel": "Cancelar",
+    "createQuote": "Crear presupuesto",
+    "updateQuote": "Actualizar presupuesto",
+    "created": "Presupuesto creado",
+    "updated": "Presupuesto actualizado",
+    "failedCreate": "Error al crear el presupuesto",
+    "failedUpdate": "Error al actualizar el presupuesto"
+  },
+  "parts": {
+    "title": "Repuestos",
+    "addPart": "Agregar repuesto",
+    "partNumber": "N.º de repuesto",
+    "name": "Nombre",
+    "namePlaceholder": "Nombre *",
+    "qty": "Cant.",
+    "unitPrice": "Precio unitario",
+    "total": "Total",
+    "subtotal": "Subtotal de repuestos: {amount}"
+  },
+  "labor": {
+    "title": "Mano de obra",
+    "addLabor": "Agregar mano de obra",
+    "description": "Descripción",
+    "descriptionPlaceholder": "Descripción *",
+    "hours": "Horas",
+    "rate": "Tarifa ({currency}/hr)",
+    "total": "Total",
+    "subtotal": "Subtotal de mano de obra: {amount}"
+  },
+  "notes": {
+    "title": "Notas",
+    "public": "Pública",
+    "internal": "Interna",
+    "publicPlaceholder": "Descripción del presupuesto visible para el cliente...",
+    "internalPlaceholder": "Notas internas (no visibles para el cliente)...",
+    "publicHelper": "Se muestra en el documento del presupuesto",
+    "internalHelper": "Solo visible para su equipo"
+  },
+  "details": {
+    "title": "Detalles del presupuesto",
+    "titleLabel": "Título *",
+    "titlePlaceholder": "Estimación de reparación de vehículo",
+    "status": "Estado",
+    "statusDraft": "Borrador",
+    "statusSent": "Enviado",
+    "statusAccepted": "Aceptado",
+    "statusRejected": "Rechazado",
+    "validUntil": "Válido hasta",
+    "viewInspection": "Ver inspección vinculada",
+    "vehicle": "Vehículo",
+    "customer": "Cliente",
+    "selectVehicle": "Seleccionar vehículo...",
+    "selectCustomer": "Seleccionar cliente...",
+    "none": "Ninguno"
+  },
+  "totals": {
+    "title": "Totales",
+    "parts": "Repuestos",
+    "labor": "Mano de obra",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "discountNone": "Ninguno",
+    "discountPercentage": "Porcentaje",
+    "discountFixed": "Fijo",
+    "tax": "Impuesto",
+    "total": "Total"
+  },
+  "shareDialog": {
+    "title": "Compartir presupuesto",
+    "description": "Genere un enlace público que permita a cualquier persona ver y descargar este presupuesto sin iniciar sesión.",
+    "notifyTitle": "Notificar a {name}",
+    "email": "Correo electrónico",
+    "noEmail": "(sin correo registrado)",
+    "sms": "SMS",
+    "noPhone": "(sin teléfono registrado)",
+    "sendNotification": "Enviar notificación",
+    "revokeLink": "Revocar enlace",
+    "generateLink": "Generar enlace público",
+    "emailSent": "Correo enviado",
+    "failedEmail": "Error al enviar el correo",
+    "smsSent": "SMS enviado",
+    "failedSms": "Error al enviar el SMS"
+  },
+  "images": {
+    "title": "Imágenes del presupuesto",
+    "dropzone": "Arrastre imágenes aquí o haga clic para explorar",
+    "uploading": "Subiendo...",
+    "formats": "JPG, PNG, WebP — máx. 10 MB cada una",
+    "uploadingImages": "Subiendo imágenes...",
+    "limitReached": "Límite de imágenes alcanzado ({count}/{max})",
+    "upgradePrompt": "Actualice su plan para subir más imágenes.",
+    "onlyAllowed": "Solo se permiten imágenes JPG, PNG y WebP. {names} omitidas.",
+    "onlyUploading": "Solo se subirán {count} imagen(es) para no exceder el límite.",
+    "uploadingCount": "Subiendo {count} imagen(es)...",
+    "uploadedCount": "{count} imagen(es) subida(s)",
+    "uploadFailed": "Error al subir",
+    "deleted": "Imagen eliminada",
+    "failedDelete": "Error al eliminar la imagen",
+    "failedUpdate": "Error al actualizar",
+    "description": "Descripción...",
+    "pdf": "PDF"
+  },
+  "documents": {
+    "title": "Documentos",
+    "dropzone": "Arrastre archivos aquí o haga clic para explorar",
+    "uploading": "Subiendo...",
+    "formats": "PDF, CSV, TXT — máx. 10 MB cada uno",
+    "uploadingDocuments": "Subiendo documentos...",
+    "limitReached": "Límite de documentos alcanzado ({count}/{max})",
+    "upgradePrompt": "Actualice su plan para subir más.",
+    "onlyUploading": "Solo se subirán {count} archivo(s) para no exceder el límite.",
+    "uploadingCount": "Subiendo {count} archivo(s)...",
+    "uploadedCount": "{count} archivo(s) subido(s)",
+    "uploadFailed": "Error al subir",
+    "deleted": "Archivo eliminado",
+    "failedDelete": "Error al eliminar el archivo",
+    "failedUpdate": "Error al actualizar",
+    "pdf": "PDF"
+  }
+}

+ 199 - 0
messages/es/reports.json

@@ -0,0 +1,199 @@
+{
+  "title": "Informes y Análisis",
+  "tabs": {
+    "revenue": "Ingresos",
+    "services": "Servicios",
+    "customers": "Clientes",
+    "inventory": "Inventario",
+    "technicians": "Técnicos",
+    "parts": "Piezas",
+    "jobAnalytics": "Análisis de Trabajos",
+    "retention": "Retención",
+    "tax": "Impuestos"
+  },
+  "dateRange": {
+    "to": "a"
+  },
+  "actions": {
+    "refresh": "Actualizar",
+    "csv": "CSV"
+  },
+  "revenue": {
+    "revenue": "Ingresos",
+    "collected": "Cobrado",
+    "outstanding": "Pendiente",
+    "services": "Servicios",
+    "monthlyRevenue": "Ingresos Mensuales",
+    "revenueByType": "Ingresos por Tipo",
+    "monthlyBreakdown": "Desglose Mensual",
+    "tableHeaders": {
+      "month": "Mes",
+      "revenue": "Ingresos",
+      "collected": "Cobrado",
+      "count": "Cantidad"
+    }
+  },
+  "services": {
+    "totalServices": "Total de Servicios",
+    "byStatus": "Por Estado",
+    "byType": "Por Tipo"
+  },
+  "customers": {
+    "total": "Total",
+    "active": "Activos",
+    "topCustomersBySpend": "Principales Clientes por Gasto",
+    "topCustomers": "Principales Clientes",
+    "tableHeaders": {
+      "name": "Nombre",
+      "company": "Empresa",
+      "services": "Servicios",
+      "totalSpent": "Gasto Total"
+    }
+  },
+  "inventory": {
+    "parts": "Piezas",
+    "items": "Artículos",
+    "costValue": "Valor de Costo",
+    "sellValue": "Valor de Venta",
+    "potentialMargin": "Margen Potencial",
+    "lowStock": "Stock Bajo",
+    "tableHeaders": {
+      "name": "Nombre",
+      "partNumber": "N.º Pieza",
+      "qty": "Cant.",
+      "minQty": "Cant. Mín."
+    }
+  },
+  "technicians": {
+    "totalJobs": "Total de Trabajos",
+    "totalRevenue": "Ingresos Totales",
+    "revenueByTechnician": "Ingresos por Técnico",
+    "technicianBreakdown": "Desglose por Técnico",
+    "tableHeaders": {
+      "technician": "Técnico",
+      "jobs": "Trabajos",
+      "revenue": "Ingresos",
+      "avgRevenue": "Ingreso Prom.",
+      "totalHours": "Horas Totales",
+      "avgHours": "Horas Prom."
+    }
+  },
+  "parts": {
+    "partsUsed": "Piezas Utilizadas",
+    "partsRevenue": "Ingresos por Piezas",
+    "topPartsByUsage": "Piezas Más Utilizadas",
+    "partsUsage": "Uso de Piezas",
+    "tableHeaders": {
+      "partName": "Nombre de Pieza",
+      "partNumber": "N.º Pieza",
+      "usageCount": "Veces Utilizada",
+      "totalQty": "Cant. Total",
+      "revenue": "Ingresos"
+    }
+  },
+  "jobAnalytics": {
+    "totalJobs": "Total de Trabajos",
+    "avgJobValue": "Valor Prom. de Trabajo",
+    "jobsByDayOfWeek": "Trabajos por Día de la Semana",
+    "serviceTypes": "Tipos de Servicio",
+    "monthlyTrend": "Tendencia Mensual",
+    "serviceTypeBreakdown": "Desglose por Tipo de Servicio",
+    "tableHeaders": {
+      "type": "Tipo",
+      "count": "Cantidad",
+      "avgValue": "Valor Prom.",
+      "avgHours": "Horas Prom."
+    }
+  },
+  "retention": {
+    "returning": "Recurrentes",
+    "new": "Nuevos",
+    "totalActive": "Total Activos",
+    "avgDaysBetween": "Prom. Días Entre",
+    "topReturningCustomers": "Principales Clientes Recurrentes",
+    "returningCustomers": "Clientes Recurrentes",
+    "tableHeaders": {
+      "customer": "Cliente",
+      "company": "Empresa",
+      "visits": "Visitas",
+      "totalSpent": "Gasto Total",
+      "avgDaysBetween": "Prom. Días Entre"
+    }
+  },
+  "tax": {
+    "totalTaxCollected": "Impuesto Total Recaudado",
+    "taxableRevenue": "Ingresos Gravables",
+    "invoicesWithTax": "Facturas con Impuesto",
+    "monthlyTaxCollected": "Impuesto Mensual Recaudado",
+    "taxByRate": "Impuesto por Tasa",
+    "monthlyBreakdown": "Desglose Mensual",
+    "tableHeaders": {
+      "taxRate": "Tasa de Impuesto",
+      "taxCollected": "Impuesto Recaudado",
+      "invoices": "Facturas",
+      "month": "Mes",
+      "taxableAmount": "Monto Gravable"
+    }
+  },
+  "charts": {
+    "revenue": "Ingresos",
+    "collected": "Cobrado",
+    "count": "Cantidad",
+    "totalSpent": "Gasto Total",
+    "jobs": "Trabajos",
+    "visits": "Visitas",
+    "taxCollected": "Impuesto Recaudado"
+  },
+  "days": {
+    "sunday": "Domingo",
+    "monday": "Lunes",
+    "tuesday": "Martes",
+    "wednesday": "Miércoles",
+    "thursday": "Jueves",
+    "friday": "Viernes",
+    "saturday": "Sábado"
+  },
+  "daysShort": {
+    "sun": "Dom",
+    "mon": "Lun",
+    "tue": "Mar",
+    "wed": "Mié",
+    "thu": "Jue",
+    "fri": "Vie",
+    "sat": "Sáb"
+  },
+  "csvHeaders": {
+    "month": "Mes",
+    "revenue": "Ingresos",
+    "collected": "Cobrado",
+    "count": "Cantidad",
+    "category": "Categoría",
+    "label": "Etiqueta",
+    "name": "Nombre",
+    "company": "Empresa",
+    "services": "Servicios",
+    "totalSpent": "Gasto Total",
+    "partNumber": "N.º Pieza",
+    "quantity": "Cantidad",
+    "minQuantity": "Cantidad Mínima",
+    "unitCost": "Costo Unitario",
+    "technician": "Técnico",
+    "jobs": "Trabajos",
+    "totalRevenue": "Ingresos Totales",
+    "avgRevenue": "Ingreso Prom.",
+    "totalHours": "Horas Totales",
+    "avgHours": "Horas Prom.",
+    "partName": "Nombre de Pieza",
+    "usageCount": "Veces Utilizada",
+    "totalQty": "Cant. Total",
+    "serviceType": "Tipo de Servicio",
+    "avgValue": "Valor Prom.",
+    "customer": "Cliente",
+    "visits": "Visitas",
+    "avgDaysBetweenVisits": "Prom. Días Entre Visitas",
+    "taxCollected": "Impuesto Recaudado",
+    "taxableAmount": "Monto Gravable",
+    "invoiceCount": "Cant. de Facturas"
+  },
+  "empty": "Aún no se han cargado datos. Haga clic en Actualizar para cargar el informe."
+}

+ 240 - 0
messages/es/service.json

@@ -0,0 +1,240 @@
+{
+  "page": {
+    "notFound": "Registro de servicio no encontrado",
+    "updated": "Registro de servicio actualizado",
+    "failedUpdate": "Error al actualizar registro de servicio",
+    "deleteTitle": "Eliminar Registro de Servicio",
+    "deleteDescription": "Esto eliminará permanentemente este registro de servicio y todos los datos asociados. Esto no se puede deshacer.",
+    "deleted": "Registro de servicio eliminado",
+    "failedDelete": "Error al eliminar"
+  },
+  "header": {
+    "tabs": {
+      "details": "Detalles",
+      "images": "Imágenes",
+      "video": "Video",
+      "documents": "Documentos"
+    },
+    "save": "Guardar",
+    "pdf": "PDF",
+    "email": "Correo Electrónico",
+    "share": "Compartir",
+    "delete": "Eliminar",
+    "unpaid": "Sin Pagar"
+  },
+  "basicInfo": {
+    "title": "Información Básica",
+    "vehicle": "Vehículo",
+    "open": "Abierto",
+    "customer": "Cliente",
+    "titleLabel": "Título *",
+    "titlePlaceholder": "Cambio de Aceite",
+    "date": "Fecha",
+    "type": "Tipo",
+    "mileage": "Millaje",
+    "status": "Estado",
+    "technician": "Técnico",
+    "invoiceNumber": "Número de Factura",
+    "typeOptions": {
+      "maintenance": "Mantenimiento",
+      "repair": "Reparación",
+      "upgrade": "Mejora",
+      "inspection": "Inspección"
+    },
+    "statusOptions": {
+      "pending": "Pendiente",
+      "in_progress": "En Progreso",
+      "waiting_parts": "Esperando Piezas",
+      "completed": "Completado"
+    },
+    "searchVehicles": "Buscar vehículos...",
+    "noVehicleFound": "Vehículo no encontrado.",
+    "techPicker": {
+      "placeholder": "Seleccionar o escribir un nombre...",
+      "searchPlaceholder": "Buscar o escribir nombre...",
+      "useCustom": "Usar \"{search}\"",
+      "typePrompt": "Escriba un nombre...",
+      "boardTechnicians": "Técnicos del Tablero",
+      "teamMembers": "Miembros del Equipo"
+    }
+  },
+  "parts": {
+    "title": "Piezas",
+    "fromInventory": "Del Inventario",
+    "addPart": "Agregar Pieza",
+    "partNumber": "N° de Pieza",
+    "name": "Nombre",
+    "qty": "Cantidad",
+    "unitPrice": "Precio Unitario",
+    "total": "Total",
+    "namePlaceholder": "Nombre *",
+    "subtotal": "Subtotal de Piezas: {amount}",
+    "inventoryTitle": "Seleccionar Pieza del Inventario",
+    "searchInventory": "Buscar en inventario...",
+    "inStock": "{quantity} en stock",
+    "noInventory": "No se encontraron piezas de inventario."
+  },
+  "labor": {
+    "title": "Mano de Obra",
+    "addLabor": "Agregar Mano de Obra",
+    "description": "Descripción",
+    "hours": "Horas",
+    "rate": "Tarifa ({currency}/hr)",
+    "total": "Total",
+    "descriptionPlaceholder": "Descripción *",
+    "subtotal": "Subtotal de Mano de Obra: {amount}"
+  },
+  "totals": {
+    "title": "Totales",
+    "parts": "Piezas",
+    "labor": "Mano de Obra",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "discountNone": "Ninguno",
+    "discountPercentage": "Porcentaje",
+    "discountFixed": "Fijo",
+    "tax": "Impuesto",
+    "total": "Total"
+  },
+  "notes": {
+    "title": "Notas",
+    "public": "Público",
+    "internal": "Interno",
+    "publicPlaceholder": "Notas visibles en la factura...",
+    "internalPlaceholder": "Notas internas (no mostradas en la factura)...",
+    "publicHelper": "Se muestra en la factura y documentos compartidos",
+    "internalHelper": "Solo visible para su equipo"
+  },
+  "invoice": {
+    "title": "Resumen de Factura",
+    "parts": "Piezas",
+    "labor": "Mano de Obra",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "tax": "Impuesto ({rate}%)",
+    "total": "Total",
+    "paid": "Pagado",
+    "balanceDue": "Saldo Pendiente",
+    "paidBadge": "PAGADO",
+    "entityLabel": "Factura",
+    "emailSubject": "Confirmación de Pago"
+  },
+  "payments": {
+    "title": "Pagos",
+    "markUnpaid": "Marcar como Sin Pagar",
+    "markPaid": "Marcar como Pagado",
+    "recordPayment": "Registrar Pago",
+    "totalPaid": "Total Pagado",
+    "amount": "Cantidad",
+    "date": "Fecha",
+    "method": "Método",
+    "note": "Nota",
+    "notePlaceholder": "Nota opcional",
+    "savePayment": "Guardar Pago",
+    "methodOptions": {
+      "cash": "Efectivo",
+      "card": "Tarjeta",
+      "transfer": "Transferencia",
+      "other": "Otro"
+    },
+    "recorded": "Pago registrado",
+    "markedPaid": "Marcado como pagado",
+    "markedUnpaid": "Marcado como sin pagar",
+    "deleted": "Pago eliminado",
+    "failedRecord": "Error al registrar pago",
+    "failedStatus": "Error al actualizar estado del pago",
+    "failedDelete": "Error al eliminar pago",
+    "deleteTitle": "Eliminar Pago",
+    "deleteDescription": "Esto eliminará este registro de pago.",
+    "failedPdf": "Error al generar factura en PDF"
+  },
+  "attachments": {
+    "deleteTitle": "Eliminar Archivo",
+    "deleteDescription": "Esto eliminará permanentemente este archivo. Esto no se puede deshacer.",
+    "deleted": "Archivo eliminado",
+    "failedDelete": "Error al eliminar",
+    "images": "Imágenes ({count})",
+    "videos": "Videos ({count})",
+    "diagnostics": "Reportes de Diagnóstico ({count})",
+    "documents": "Documentos ({count})"
+  },
+  "share": {
+    "title": "Compartir Factura",
+    "description": "Genere un enlace público que permita a cualquiera ver y descargar esta factura sin iniciar sesión.",
+    "notifyTitle": "Notificar a {name}",
+    "email": "Correo Electrónico",
+    "noEmail": "(sin correo electrónico registrado)",
+    "sms": "SMS",
+    "noPhone": "(sin teléfono registrado)",
+    "sendNotification": "Enviar Notificación",
+    "revokeLink": "Revocar Enlace",
+    "generateLink": "Generar Enlace Público",
+    "emailSent": "Correo electrónico enviado",
+    "failedEmail": "Error al enviar correo electrónico",
+    "smsSent": "SMS enviado",
+    "failedSms": "Error al enviar SMS"
+  },
+  "sidebar": {
+    "vehicle": "Vehículo",
+    "customer": "Cliente",
+    "vin": "VIN: {vin}",
+    "mileageAtService": "Millaje en el servicio: {mileage} {unit}"
+  },
+  "images": {
+    "title": "Imágenes del Servicio",
+    "dropzone": "Arrastra imágenes aquí o haz clic para explorar",
+    "uploading": "Subiendo...",
+    "formats": "JPG, PNG, WebP — máx. 10MB cada una",
+    "uploadingImages": "Subiendo imágenes...",
+    "limitReached": "Límite de imágenes alcanzado ({count}/{max})",
+    "upgradePrompt": "Actualiza tu plan para subir más imágenes.",
+    "onlyAllowed": "Solo se permiten imágenes JPG, PNG y WebP. {names} omitidas.",
+    "onlyUploading": "Solo se subirán {count} imagen(es) para no exceder el límite.",
+    "uploadingCount": "Subiendo {count} imagen(es)...",
+    "uploadedCount": "{count} imagen(es) subida(s)",
+    "uploadFailed": "Error al subir",
+    "deleted": "Imagen eliminada",
+    "failedDelete": "Error al eliminar imagen",
+    "failedUpdate": "Error al actualizar",
+    "description": "Descripción...",
+    "invoice": "Factura"
+  },
+  "videos": {
+    "title": "Videos del Servicio",
+    "dropzone": "Arrastra videos aquí o haz clic para explorar",
+    "uploading": "Subiendo...",
+    "formats": "MP4, WebM, MOV — máx. 10MB cada uno",
+    "uploadingVideos": "Subiendo videos...",
+    "uploadingCount": "Subiendo {count} video(s)...",
+    "uploadedCount": "{count} video(s) subido(s)",
+    "uploadFailed": "Error al subir",
+    "deleted": "Video eliminado",
+    "failedDelete": "Error al eliminar video",
+    "failedUpdate": "Error al actualizar",
+    "invoice": "Factura"
+  },
+  "documents": {
+    "diagnosticTitle": "Reportes de Diagnóstico",
+    "documentsTitle": "Documentos",
+    "dropzone": "Arrastra archivos aquí o haz clic para explorar",
+    "uploading": "Subiendo...",
+    "diagnosticFormats": "PDF, CSV, TXT — máx. 10MB cada uno",
+    "documentFormats": "PDF, DOCX, XLSX, CSV, TXT — máx. 10MB cada uno",
+    "uploadingReports": "Subiendo reportes...",
+    "uploadingDocuments": "Subiendo documentos...",
+    "diagnosticLimitReached": "Límite de reportes de diagnóstico alcanzado ({count}/{max})",
+    "documentLimitReached": "Límite de documentos alcanzado ({count}/{max})",
+    "upgradePrompt": "Actualiza tu plan para subir más.",
+    "diagnosticLabel": "Reporte de diagnóstico",
+    "documentLabel": "Documento",
+    "limitReachedToast": "Límite de {label} alcanzado ({count}/{max}). Actualiza tu plan para más.",
+    "onlyUploading": "Solo se subirán {count} archivo(s) para no exceder el límite.",
+    "uploadingCount": "Subiendo {count} archivo(s)...",
+    "uploadedCount": "{count} archivo(s) subido(s)",
+    "uploadFailed": "Error al subir",
+    "deleted": "Archivo eliminado",
+    "failedDelete": "Error al eliminar archivo",
+    "failedUpdate": "Error al actualizar",
+    "invoice": "Factura"
+  }
+}

+ 700 - 0
messages/es/settings.json

@@ -0,0 +1,700 @@
+{
+  "title": "Configuracion",
+  "subtitle": "Administre su taller, facturas y preferencias",
+  "nav": {
+    "categories": {
+      "general": "General",
+      "billing": "Facturacion y documentos",
+      "communications": "Comunicaciones",
+      "workshop": "Taller",
+      "system": "Sistema"
+    },
+    "items": {
+      "company": { "title": "Empresa", "description": "Datos comerciales y marca" },
+      "subscription": { "title": "Suscripcion", "description": "Plan y facturacion" },
+      "account": { "title": "Cuenta", "description": "Nombre, correo y contrasena" },
+      "team": { "title": "Equipo", "description": "Miembros y roles" },
+      "invoice": { "title": "Factura", "description": "Diseno y campos de factura" },
+      "templates": { "title": "Plantillas", "description": "Plantillas de facturas, cotizaciones e inspecciones" },
+      "payment": { "title": "Pago", "description": "Condiciones de pago y cuentas" },
+      "currency": { "title": "Moneda", "description": "Valores predeterminados de moneda e impuestos" },
+      "customFields": { "title": "Campos personalizados", "description": "Definir campos de datos personalizados" },
+      "email": { "title": "Correo", "description": "Proveedor de correo y envio" },
+      "sms": { "title": "SMS", "description": "Proveedor de SMS y mensajeria" },
+      "customerPortal": { "title": "Portal del cliente", "description": "Portal de autoservicio del cliente" },
+      "workshop": { "title": "Taller", "description": "Valores predeterminados de tecnico y mano de obra" },
+      "maintenance": { "title": "Mantenimiento", "description": "Intervalos de servicio predichos" },
+      "appearance": { "title": "Apariencia", "description": "Tema y visualizacion" },
+      "data": { "title": "Datos", "description": "Exportar e importar respaldo" },
+      "license": { "title": "Licencia", "description": "Licencia marca blanca" },
+      "about": { "title": "Acerca de", "description": "Version e informacion" }
+    },
+    "pro": "PRO"
+  },
+  "readOnly": {
+    "banner": "Tiene acceso de solo lectura a la configuracion. Contacte a un administrador para realizar cambios."
+  },
+  "featureLocked": {
+    "requiresPro": "Requiere una licencia Pro",
+    "upgradePlan": "Mejorar plan",
+    "activateLicense": "Activar licencia"
+  },
+  "company": {
+    "logoTitle": "Logo de la empresa",
+    "logoAlt": "Logo de la empresa",
+    "upload": "Subir",
+    "remove": "Eliminar",
+    "logoHint": "JPG, PNG, WebP, SVG — max 2MB",
+    "uploadingLogo": "Subiendo logo...",
+    "logoUpdated": "Logo actualizado",
+    "failedUploadLogo": "Error al subir el logo",
+    "detailsTitle": "Datos de la empresa",
+    "shopName": "Nombre de la empresa / taller",
+    "shopNamePlaceholder": "Taller Mecanico Lopez",
+    "orgNumber": "Numero de organizacion",
+    "orgNumberPlaceholder": "123 456 789",
+    "phone": "Telefono",
+    "phonePlaceholder": "(555) 123-4567",
+    "email": "Correo electronico",
+    "emailPlaceholder": "servicio@ejemplo.com",
+    "address": "Direccion",
+    "addressPlaceholder": "Calle Principal 123\nPiso 1\n12345 Ciudad",
+    "saveCompany": "Guardar datos de empresa",
+    "addNewCompany": "Agregar nueva empresa",
+    "companyName": "Nombre de la empresa",
+    "companyNamePlaceholder": "Ej. Taller Mecanico Lopez",
+    "cancel": "Cancelar",
+    "createCompany": "Crear empresa",
+    "settingsSaved": "Configuracion de empresa guardada",
+    "failedSave": "Error al guardar la configuracion",
+    "companyCreated": "Empresa creada exitosamente"
+  },
+  "account": {
+    "profileTitle": "Perfil",
+    "profileDescription": "Actualice su nombre para mostrar y direccion de correo electronico.",
+    "name": "Nombre",
+    "namePlaceholder": "Su nombre",
+    "emailLabel": "Correo electronico",
+    "emailPlaceholder": "Su correo electronico",
+    "saveProfile": "Guardar perfil",
+    "profileUpdated": "Perfil actualizado",
+    "failedUpdateProfile": "Error al actualizar el perfil",
+    "failedUpdateEmail": "Error al actualizar el correo electronico",
+    "changePasswordTitle": "Cambiar contrasena",
+    "changePasswordDescription": "Ingrese su contrasena actual y elija una nueva.",
+    "currentPassword": "Contrasena actual",
+    "newPassword": "Nueva contrasena",
+    "confirmNewPassword": "Confirmar nueva contrasena",
+    "changePassword": "Cambiar contrasena",
+    "passwordChanged": "Contrasena cambiada",
+    "passwordsNoMatch": "Las contrasenas no coinciden",
+    "passwordMinLength": "La contrasena debe tener al menos 8 caracteres",
+    "failedChangePassword": "Error al cambiar la contrasena",
+    "twoFactorTitle": "Autenticacion de dos factores",
+    "twoFactorEnabled": "La autenticacion de dos factores esta habilitada",
+    "twoFactorDisablePrompt": "Ingrese su contrasena para deshabilitar la autenticacion de dos factores.",
+    "twoFactorEnablePrompt": "Agregue una capa adicional de seguridad a su cuenta habilitando la autenticacion de dos factores con una aplicacion de autenticacion.",
+    "password": "Contrasena",
+    "enterPassword": "Ingrese su contrasena",
+    "disable2FA": "Deshabilitar 2FA",
+    "enable2FA": "Habilitar 2FA",
+    "twoFactorDisabled": "Autenticacion de dos factores deshabilitada",
+    "failedEnable2FA": "Error al habilitar 2FA",
+    "failedDisable2FA": "Error al deshabilitar 2FA",
+    "enterPasswordError": "Por favor ingrese su contrasena",
+    "setupDialogTitle": "Habilitar autenticacion de dos factores",
+    "setupDialogDescription": "Ingrese su contrasena para comenzar a configurar 2FA con una aplicacion de autenticacion.",
+    "continue": "Continuar",
+    "scanQRTitle": "Escanear codigo QR",
+    "scanQRDescription": "Escanee este codigo QR con su aplicacion de autenticacion (Google Authenticator, Authy, etc.)",
+    "verifyCodeTitle": "Verificar codigo",
+    "verifyCodeDescription": "Ingrese el codigo de 6 digitos de su aplicacion de autenticacion para verificar que esta configurada correctamente.",
+    "authCode": "Codigo de autenticacion",
+    "authCodePlaceholder": "000000",
+    "back": "Atras",
+    "verify": "Verificar",
+    "enterCodeError": "Por favor ingrese el codigo de su aplicacion de autenticacion",
+    "invalidCode": "Codigo invalido. Intente nuevamente.",
+    "verificationFailed": "Verificacion fallida",
+    "backupCodesTitle": "Guardar codigos de respaldo",
+    "backupCodesDescription": "Guarde estos codigos en un lugar seguro. Puede usarlos para iniciar sesion si pierde acceso a su aplicacion de autenticacion. Cada codigo solo puede usarse una vez.",
+    "copyCodes": "Copiar codigos",
+    "copied": "Copiado",
+    "backupCodesCopied": "Codigos de respaldo copiados al portapapeles",
+    "savedBackupCodes": "He guardado mis codigos de respaldo",
+    "twoFactorEnabledSuccess": "Autenticacion de dos factores habilitada",
+    "dangerZone": "Zona de peligro",
+    "deleteContentTitle": "Eliminar contenido",
+    "deleteContentDescription": "Elimine selectivamente datos de la organizacion como vehiculos, clientes, cotizaciones o inventario. Su cuenta y configuracion permaneceran intactas.",
+    "deleteContentButton": "Eliminar contenido",
+    "deleteAccountTitle": "Eliminar cuenta",
+    "deleteAccountDescription": "Elimine permanentemente su cuenta y todos los datos asociados incluyendo vehiculos, registros de servicio, ordenes de trabajo, facturas, clientes, archivos y pagos. Esta accion no se puede deshacer.",
+    "deleteAccountButton": "Eliminar cuenta",
+    "deleteAccountDialogTitle": "Eliminar cuenta",
+    "deleteAccountDialogDescription": "Esto eliminara permanentemente su cuenta y todos los datos asociados. Esta accion es irreversible. Todos sus vehiculos, registros de servicio, ordenes de trabajo, cotizaciones, clientes, archivos e historial de pagos seran eliminados permanentemente.",
+    "deleteAccountConfirmPhrase": "delete me",
+    "deleteAccountConfirmPrompt": "Escriba <bold>delete me</bold> para confirmar",
+    "permanentlyDelete": "Eliminar permanentemente",
+    "deleting": "Eliminando...",
+    "failedDeleteAccount": "Error al eliminar la cuenta",
+    "deleteContentDialogTitle": "Eliminar contenido",
+    "deleteContentDialogDescription": "Seleccione el contenido que desea eliminar permanentemente. Esta accion no se puede deshacer.",
+    "contentVehicles": "Vehiculos",
+    "contentVehiclesDescription": "Todos los vehiculos y sus registros de servicio, notas, recordatorios, registros de combustible y facturas recurrentes",
+    "contentCustomers": "Clientes",
+    "contentCustomersDescription": "Todos los registros de clientes. Los vehiculos vinculados a clientes eliminados seran desvinculados.",
+    "contentQuotes": "Cotizaciones",
+    "contentQuotesDescription": "Todas las cotizaciones y sus partidas",
+    "contentInventory": "Inventario",
+    "contentInventoryDescription": "Todas las piezas de inventario y sus imagenes",
+    "deleteContentConfirmPhrase": "delete my data",
+    "deleteContentConfirmPrompt": "Escriba <bold>delete my data</bold> para confirmar",
+    "deleteSelected": "Eliminar {count} seleccionados",
+    "contentDeleted": "El contenido seleccionado ha sido eliminado permanentemente",
+    "failedDeleteContent": "Error al eliminar el contenido"
+  },
+  "team": {
+    "title": "Equipo",
+    "noOrgDescription": "Cree un equipo para compartir datos con otros usuarios.",
+    "orgDescription": "Administre los miembros de su organizacion y sus roles.",
+    "createOrg": "Crear organizacion",
+    "createOrgDescription": "Cree una organizacion para invitar miembros del equipo. Usted sera el propietario.",
+    "orgName": "Nombre de la organizacion",
+    "orgNamePlaceholder": "Mi taller",
+    "create": "Crear",
+    "orgCreated": "Organizacion creada",
+    "failedCreateOrg": "Error al crear la organizacion",
+    "memberCount": "{count} miembro",
+    "memberCountPlural": "{count} miembros",
+    "admin": "Administrador",
+    "member": "Miembro",
+    "inviteByEmail": "Invitar por correo electronico",
+    "inviteEmailPlaceholder": "usuario@ejemplo.com",
+    "invite": "Invitar",
+    "invited": "Miembro invitado exitosamente.",
+    "userNotFoundTitle": "Usuario no encontrado",
+    "userNotFoundDescription": "No se encontro cuenta para \"{email}\". Enviar un correo de invitacion para registrarse y unirse a su equipo?",
+    "sendInvitation": "Enviar invitacion",
+    "invitationSentTitle": "Invitacion enviada",
+    "invitationSentDescription": "Se ha enviado un correo de invitacion a {email}.",
+    "failedInvite": "Error al invitar al miembro",
+    "failedSendInvitation": "Error al enviar la invitacion",
+    "pendingInvitations": "Invitaciones pendientes",
+    "invitedAs": "Invitado como {role}",
+    "expires": "Expira {date}",
+    "pending": "Pendiente",
+    "copyInviteLink": "Copiar enlace de invitacion",
+    "inviteLinkCopied": "Enlace de invitacion copiado al portapapeles",
+    "cancelInvitation": "Cancelar invitacion",
+    "cancelInvitationDescription": "Cancelar la invitacion a {email}? Ya no podran usar este enlace para unirse.",
+    "invitationCancelled": "Invitacion cancelada",
+    "failedCancelInvitation": "Error al cancelar la invitacion",
+    "customRoles": "Roles personalizados",
+    "newRole": "Nuevo rol",
+    "roleName": "Nombre del rol",
+    "roleNamePlaceholder": "Ej. Tecnico",
+    "fullAdminAccess": "Acceso completo de administrador (omite todas las verificaciones de permisos)",
+    "permissions": "Permisos",
+    "createRole": "Crear rol",
+    "updateRole": "Actualizar rol",
+    "roleCreated": "Rol creado",
+    "roleUpdated": "Rol actualizado",
+    "failedSaveRole": "Error al guardar el rol",
+    "deleteRole": "Eliminar rol",
+    "deleteRoleDescription": "Eliminar el rol \"{name}\"? Los miembros con este rol perderan sus permisos personalizados.",
+    "roleDeleted": "Rol eliminado",
+    "failedDeleteRole": "Error al eliminar el rol",
+    "fullAccess": "Acceso completo",
+    "permissionCount": "{count} permiso",
+    "permissionCountPlural": "{count} permisos",
+    "noCustomRoles": "Aun no hay roles personalizados. Cree uno para definir permisos granulares para los miembros del equipo.",
+    "builtInRoles": "Roles integrados",
+    "ownerLabel": "Propietario",
+    "ownerDescription": "Acceso completo. Puede gestionar miembros, cambiar roles y eliminar la organizacion.",
+    "adminLabel": "Administrador",
+    "adminDescription": "Puede invitar/eliminar miembros y gestionar datos compartidos.",
+    "memberLabel": "Miembro",
+    "memberDescription": "Permisos determinados por el rol personalizado asignado. Sin rol personalizado significa acceso de solo lectura.",
+    "roleAssigned": "Rol actualizado",
+    "failedAssignRole": "Error al actualizar el rol",
+    "removeMemberTitle": "Eliminar miembro",
+    "removeMemberDescription": "Eliminar a {name} del equipo? Perdera acceso a los datos compartidos.",
+    "removeButton": "Eliminar",
+    "memberRemoved": "Miembro eliminado",
+    "failedRemoveMember": "Error al eliminar el miembro",
+    "memberRoleUpdated": "Rol del miembro actualizado",
+    "failedUpdateRole": "Error al actualizar el rol"
+  },
+  "invoice": {
+    "title": "Diseno de factura",
+    "description": "Configure lo que aparece en las facturas PDF generadas.",
+    "invoiceNumberFormat": "Formato de numero de factura",
+    "invoiceNumberFormatHint": "Use <code>{year}</code> para el ano actual. Vista previa: <bold>{preview}</bold>",
+    "nextInvoiceNumber": "Siguiente numero de factura",
+    "nextInvoiceNumberPlaceholder": "Ej. 94",
+    "nextInvoiceNumberHint": "La siguiente factura usara este numero (ej. {example})",
+    "dueDays": "Dias de vencimiento",
+    "dueDaysHint": "Numero de dias hasta el vencimiento de la factura",
+    "visibilityTitle": "Visibilidad en la factura",
+    "companyLogo": "Logo de la empresa",
+    "companyLogoHint": "Mostrar logo de la empresa en el encabezado de la factura",
+    "companyName": "Nombre de la empresa",
+    "companyNameHint": "Mostrar texto del nombre de la empresa en el encabezado",
+    "bankAccount": "Cuenta bancaria",
+    "bankAccountHint": "Mostrar cuenta bancaria / IBAN en la factura",
+    "organizationNumber": "Numero de organizacion",
+    "organizationNumberHint": "Mostrar numero de organizacion en el encabezado",
+    "customFooter": "Pie de pagina personalizado",
+    "footerPlaceholder": "Gracias por su preferencia!",
+    "footerHint": "Este texto aparece al final de cada factura",
+    "saveInvoice": "Guardar configuracion de factura",
+    "saved": "Configuracion de factura guardada"
+  },
+  "templates": {
+    "title": "Plantillas",
+    "invoiceDescription": "Personalice la apariencia de sus facturas y cotizaciones PDF.",
+    "inspectionsDescription": "Administre listas de verificacion de inspecciones y plantillas multipunto.",
+    "smsDescription": "Personalice los mensajes SMS enviados a los clientes.",
+    "tabs": {
+      "invoice": "Factura",
+      "quotation": "Cotizacion",
+      "inspections": "Inspecciones",
+      "sms": "SMS"
+    },
+    "presets": "Presets de plantilla",
+    "primaryColor": "Color primario",
+    "fontAndLayout": "Fuente y diseno",
+    "fontFamily": "Familia tipografica",
+    "helveticaDefault": "Helvetica (Predeterminado)",
+    "timesRoman": "Times Roman",
+    "courier": "Courier",
+    "headerStyle": "Estilo de encabezado",
+    "standard": "Estandar",
+    "compact": "Compacto",
+    "modern": "Moderno",
+    "preview": "Vista previa de {name}",
+    "billTo": "Facturar a",
+    "preparedFor": "Preparado para",
+    "vehicle": "Vehiculo",
+    "part": "Pieza",
+    "qty": "Cant.",
+    "price": "Precio",
+    "total": "Total",
+    "labor": "Mano de obra",
+    "hours": "Horas",
+    "rate": "Tarifa",
+    "subtotal": "Subtotal",
+    "tax": "Impuesto ({rate}%)",
+    "saveInvoiceTemplate": "Guardar plantilla de factura",
+    "saveQuotationTemplate": "Guardar plantilla de cotizacion",
+    "invoiceTemplateSaved": "Configuracion de plantilla de factura guardada",
+    "quotationTemplateSaved": "Configuracion de plantilla de cotizacion guardada",
+    "failedSave": "Error al guardar la configuracion",
+    "smsTemplates": "Plantillas de mensajes SMS",
+    "smsTemplatesDescription": "Personalice los mensajes de texto enviados a los clientes. Use variables como <code>{companyName}</code> para insertar valores dinamicos.",
+    "reset": "Restablecer",
+    "invoiceReady": "Factura lista",
+    "invoiceReadyDescription": "Se envia al compartir una factura con un cliente.",
+    "quoteReady": "Cotizacion lista",
+    "quoteReadyDescription": "Se envia al compartir una cotizacion con un cliente.",
+    "inspectionReady": "Inspeccion lista",
+    "inspectionReadyDescription": "Se envia al compartir un informe de inspeccion con un cliente.",
+    "statusInProgress": "Estado: En progreso",
+    "statusInProgressDescription": "Se envia cuando el estado de una orden de trabajo cambia a En progreso.",
+    "statusWaitingParts": "Estado: Esperando piezas",
+    "statusWaitingPartsDescription": "Se envia cuando el estado cambia a Esperando piezas.",
+    "statusReady": "Estado: Listo para recoger",
+    "statusReadyDescription": "Se envia cuando el estado cambia a Listo.",
+    "statusCompleted": "Estado: Completado",
+    "statusCompletedDescription": "Se envia cuando el estado cambia a Completado.",
+    "paymentReceived": "Pago recibido",
+    "paymentReceivedDescription": "Se envia cuando se registra un pago para una factura.",
+    "saveSmsTemplates": "Guardar plantillas SMS",
+    "smsTemplateSaved": "Configuracion de plantillas SMS guardada",
+    "smsDefaults": {
+      "invoiceReady": "Su factura está lista. Véala aquí: {share_link}",
+      "quoteReady": "Su presupuesto está listo para revisión. Véalo aquí: {share_link}",
+      "inspectionReady": "Su informe de inspección vehicular está listo. Véalo aquí: {share_link}",
+      "statusInProgress": "Se ha comenzado a trabajar en su vehículo. Le mantendremos informado.",
+      "statusWaitingParts": "Su vehículo está esperando repuestos. Le notificaremos cuando se reanude el trabajo.",
+      "statusReady": "¡Su vehículo está listo para recoger!",
+      "statusCompleted": "¡Su servicio está completo. Gracias por su confianza!",
+      "paymentReceived": "Pago de {amount} recibido para la factura {invoice_number}. ¡Gracias!"
+    },
+    "colorPresets": {
+      "amber": "Ambar",
+      "blue": "Azul",
+      "emerald": "Esmeralda",
+      "red": "Rojo",
+      "purple": "Purpura",
+      "slate": "Pizarra",
+      "rose": "Rosa",
+      "indigo": "Indigo"
+    }
+  },
+  "payment": {
+    "detailsTitle": "Detalles de pago",
+    "detailsDescription": "Cuenta bancaria y condiciones de pago en las facturas.",
+    "bankAccount": "Cuenta bancaria (IBAN / Til Konto)",
+    "bankAccountPlaceholder": "NO93 8601 1117 947",
+    "bankAccountHint": "Su numero de cuenta bancaria o IBAN para recibir pagos",
+    "paymentTerms": "Condiciones de pago",
+    "paymentTermsPlaceholder": "Neto 14 dias",
+    "paymentTermsHint": "Texto de condiciones de pago en las facturas (ej. \"Neto 14 dias\", \"Pago al recibir\")",
+    "onlinePayments": "Pagos en linea",
+    "onlinePaymentsDescription": "Permita a los clientes pagar facturas en linea a traves de enlaces de factura compartidos.",
+    "stripe": "Stripe",
+    "stripeDescription": "Aceptar pagos con tarjeta de credito/debito",
+    "secretKey": "Clave secreta",
+    "publishableKey": "Clave publica",
+    "webhookSecret": "Secreto del webhook",
+    "webhookUrl": "URL del webhook",
+    "stripeWebhookHint": "Agregue esta URL en su Panel de Stripe en Webhooks. Suscribase a <code>checkout.session.completed</code>.",
+    "vipps": "Vipps",
+    "vippsDescription": "Aceptar pagos moviles Vipps (Noruega)",
+    "clientId": "ID de cliente",
+    "clientSecret": "Secreto del cliente",
+    "subscriptionKey": "Clave de suscripcion",
+    "merchantSerialNumber": "Numero de serie del comerciante",
+    "testMode": "Modo de prueba",
+    "callbackUrl": "URL de callback",
+    "termsOfSale": "Condiciones de venta (Salgsvilkar)",
+    "termsOfSaleDescription": "Requerido por Vipps. Los clientes pueden verlas desde la pagina de factura.",
+    "externalTermsUrl": "URL de condiciones externas (opcional)",
+    "externalTermsUrlHint": "Enlace a una pagina externa con sus condiciones. Si se establece, la factura enlazara aqui.",
+    "publicTermsUrl": "URL publica de condiciones",
+    "publicTermsUrlHint": "Comparta esta URL con Vipps o enlacela desde su sitio web.",
+    "paypal": "PayPal",
+    "paypalDescription": "Aceptar pagos PayPal",
+    "sandboxMode": "Modo sandbox",
+    "paypalWebhookHint": "Agregue esta URL en su Panel de Desarrollador de PayPal en Webhooks. Suscribase a <code>PAYMENT.CAPTURE.COMPLETED</code>.",
+    "savePayment": "Guardar configuracion de pago",
+    "saved": "Configuracion de pago guardada"
+  },
+  "currency": {
+    "title": "Moneda e impuestos",
+    "description": "Moneda predeterminada y tasa de impuestos para facturas y registros de servicio.",
+    "currencyLabel": "Moneda",
+    "previewLabel": "Vista previa: {value}",
+    "enableTax": "Habilitar impuestos",
+    "enableTaxHint": "Mostrar calculo de impuestos en facturas y registros de servicio",
+    "defaultTaxRate": "Tasa de impuesto predeterminada (%)",
+    "defaultTaxRateHint": "Se completa automaticamente al crear nuevos registros de servicio",
+    "saveSettings": "Guardar configuracion",
+    "saved": "Configuracion guardada",
+    "currencies": {
+      "USD": "Dolar estadounidense",
+      "EUR": "Euro",
+      "GBP": "Libra esterlina",
+      "NOK": "Corona noruega",
+      "SEK": "Corona sueca",
+      "DKK": "Corona danesa",
+      "CHF": "Franco suizo",
+      "CAD": "Dolar canadiense",
+      "AUD": "Dolar australiano",
+      "NZD": "Dolar neozelandes",
+      "JPY": "Yen japones",
+      "CNY": "Yuan chino",
+      "INR": "Rupia india",
+      "BRL": "Real brasileno",
+      "MXN": "Peso mexicano",
+      "PLN": "Zloty polaco",
+      "CZK": "Corona checa",
+      "HUF": "Forinto hungaro",
+      "TRY": "Lira turca",
+      "ZAR": "Rand sudafricano",
+      "KRW": "Won surcoreano",
+      "SGD": "Dolar de Singapur",
+      "HKD": "Dolar de Hong Kong",
+      "THB": "Baht tailandes",
+      "ISK": "Corona islandesa",
+      "RON": "Leu rumano",
+      "ILS": "Shekel israeli",
+      "PHP": "Peso filipino",
+      "IDR": "Rupia indonesia",
+      "MYR": "Ringgit malayo",
+      "AED": "Dirham de los EAU",
+      "SAR": "Riyal saudi"
+    }
+  },
+  "customFields": {
+    "title": "Campos personalizados",
+    "description": "Defina campos personalizados que aparecen en registros de servicio y presupuestos.",
+    "addField": "Agregar campo",
+    "filterAll": "Todos",
+    "serviceRecord": "Registro de servicio",
+    "quote": "Presupuesto",
+    "emptyState": "Aún no se han definido campos personalizados. Haga clic en \"Agregar campo\" para crear uno.",
+    "required": "Obligatorio",
+    "inactive": "Inactivo",
+    "editField": "Editar campo",
+    "newField": "Nuevo campo personalizado",
+    "label": "Etiqueta",
+    "labelPlaceholder": "ej. Vencimiento de garantía",
+    "fieldName": "Nombre del campo (ID)",
+    "fieldNamePlaceholder": "vencimiento_garantia",
+    "fieldNameHint": "Solo letras minúsculas, números y guiones bajos.",
+    "fieldType": "Tipo de campo",
+    "entityType": "Tipo de entidad",
+    "fieldTypes": {
+      "text": "Texto",
+      "number": "Número",
+      "date": "Fecha",
+      "select": "Selección (Desplegable)",
+      "checkbox": "Casilla de verificación",
+      "textarea": "Área de texto"
+    },
+    "optionsLabel": "Opciones (separadas por comas)",
+    "optionsPlaceholder": "Opción 1, Opción 2, Opción 3",
+    "active": "Activo",
+    "cancel": "Cancelar",
+    "update": "Actualizar",
+    "create": "Crear",
+    "deleteTitle": "Eliminar campo personalizado",
+    "deleteDescription": "Esto eliminará permanentemente \"{label}\" y todos sus valores guardados. Esta acción no se puede deshacer.",
+    "fieldCreated": "Campo personalizado creado",
+    "fieldUpdated": "Campo personalizado actualizado",
+    "fieldDeleted": "Campo personalizado eliminado",
+    "failedSave": "No se pudo guardar el campo",
+    "failedDelete": "No se pudo eliminar"
+  },
+  "workshop": {
+    "title": "Valores predeterminados del taller",
+    "description": "Valores predeterminados al crear nuevos registros de servicio.",
+    "defaultTechnician": "Nombre del tecnico predeterminado",
+    "technicianPlaceholder": "Juan Perez",
+    "defaultLaborRate": "Tarifa de mano de obra predeterminada (por hora)",
+    "laborRatePlaceholder": "75.00",
+    "workingHours": "Horario de trabajo",
+    "workingHoursPlaceholder": "Lun-Vie 8:00 AM - 5:00 PM",
+    "unitsTitle": "Unidades",
+    "unitsDescription": "Elija entre metrico (km, litros) e imperial (millas, galones).",
+    "unitSystem": "Sistema de unidades",
+    "metric": "Metrico (km, litros)",
+    "imperial": "Imperial (millas, galones)",
+    "distanceLabel": "Distancia",
+    "volumeLabel": "Volumen",
+    "kilometers": "kilometros (km)",
+    "liters": "litros (L)",
+    "miles": "millas (mi)",
+    "gallons": "galones (gal)",
+    "saveWorkshop": "Guardar configuracion del taller",
+    "saved": "Configuracion del taller guardada"
+  },
+  "maintenance": {
+    "title": "Mantenimiento predictivo",
+    "description": "Prediga el kilometraje actual de cada vehiculo a partir de su historial de servicio y muestre los vehiculos que necesitan o se acercan a su proximo servicio.",
+    "enablePredicted": "Habilitar mantenimiento predictivo",
+    "enablePredictedHint": "Mostrar predicciones en detalles del vehiculo y panel",
+    "serviceInterval": "Intervalo de servicio ({unit})",
+    "serviceIntervalHint": "Distancia entre servicios recomendados",
+    "approachingThreshold": "Umbral de proximidad ({unit})",
+    "approachingThresholdHint": "Mostrar advertencia cuando este dentro de esta distancia del proximo servicio",
+    "saveMaintenance": "Guardar configuracion de mantenimiento",
+    "saved": "Configuracion de mantenimiento guardada"
+  },
+  "appearance": {
+    "title": "Apariencia",
+    "description": "Personalice como se ve y se siente Torqvoice.",
+    "darkMode": "Modo oscuro",
+    "darkModeHint": "Cambiar entre temas claro y oscuro",
+    "dateTimeTitle": "Fecha y hora",
+    "dateTimeDescription": "Configure como se muestran las fechas y horas en la aplicacion.",
+    "dateFormat": "Formato de fecha",
+    "timeFormat": "Formato de hora",
+    "time12h": "12 horas (2:30 PM)",
+    "time24h": "24 horas (14:30)",
+    "timezone": "Zona horaria",
+    "timezoneAuto": "Detectar automaticamente (navegador)",
+    "timezoneHint": "Con deteccion automatica, cada usuario ve las fechas en la zona horaria de su navegador.",
+    "previewLabel": "Vista previa",
+    "dateLabel": "Fecha",
+    "dateTimeLabel": "Fecha y hora",
+    "saveSettings": "Guardar configuracion",
+    "saved": "Configuracion guardada"
+  },
+  "data": {
+    "title": "Gestion de datos",
+    "description": "Exporte sus datos como respaldo o importe un archivo previamente exportado.",
+    "exportTitle": "Exportar datos",
+    "exportDescription": "Descargue sus datos como archivo zip. Seleccione las categorias a incluir en el respaldo.",
+    "includeInBackup": "Incluir en el respaldo:",
+    "selectAll": "Seleccionar todo",
+    "deselectAll": "Deseleccionar todo",
+    "optSettings": "Configuracion",
+    "optSettingsDesc": "Info del taller, configuracion de facturas, proveedores de pago",
+    "optCustomers": "Clientes",
+    "optCustomersDesc": "Registros de clientes e informacion de contacto",
+    "optVehicles": "Vehiculos y registros de servicio",
+    "optVehiclesDesc": "Vehiculos, notas, registros de combustible, recordatorios, registros de servicio",
+    "optQuotes": "Cotizaciones",
+    "optQuotesDesc": "Cotizaciones con piezas y mano de obra",
+    "optInventory": "Inventario",
+    "optInventoryDesc": "Piezas de inventario y niveles de stock",
+    "optCustomFields": "Campos personalizados",
+    "optCustomFieldsDesc": "Definiciones y valores de campos personalizados",
+    "optFiles": "Archivos subidos",
+    "optFilesDesc": "Imagenes, adjuntos de servicio, logo",
+    "filesWarning": "Incluye archivos subidos — el respaldo puede ser mas grande",
+    "downloadBackup": "Descargar respaldo",
+    "exportComplete": "Exportacion completada",
+    "exportCompleteMessage": "Su archivo de respaldo ha sido descargado.",
+    "exportFailed": "Exportacion fallida",
+    "exportFailedMessage": "No se pudieron exportar sus datos. Intente nuevamente.",
+    "importTitle": "Importar datos",
+    "importWarning": "La importacion <bold>reemplazara todos los datos existentes</bold> con el contenido del archivo de respaldo. Esta accion no se puede deshacer.",
+    "importFileHint": "Soporta archivos de respaldo .zip (v2) y .json (v1).",
+    "uploadRestore": "Subir y restaurar",
+    "importComplete": "Importacion completada",
+    "importCompleteMessage": "Todos los datos han sido restaurados del archivo de respaldo.",
+    "importFailed": "Importacion fallida",
+    "importFromOther": "Importar de otros servicios",
+    "importFromOtherDescription": "Migre sus datos desde otra plataforma. Seleccione un servicio para comenzar.",
+    "lubelogTitle": "Importar de LubeLog",
+    "lubelogExportSteps": "Como exportar sus datos de LubeLog:",
+    "lubelogStep1": "Abra su instancia de LubeLog",
+    "lubelogStep2": "Vaya a <bold>Settings</bold>",
+    "lubelogStep3": "Haga clic en <bold>Make Backup</bold> para descargar un archivo zip",
+    "lubelogStep4": "Suba el archivo zip descargado abajo",
+    "lubelogAddNote": "Esto <bold>agregara datos junto a</bold> sus registros existentes — nada sera eliminado.",
+    "lubelogImported": "Que se importara:",
+    "lubelogVehicles": "Vehiculos",
+    "lubelogServiceRecords": "Registros de servicio con adjuntos",
+    "lubelogNotes": "Notas",
+    "lubelogFileHint": "Archivos hasta 200MB soportados.",
+    "lubelogImportComplete": "Importacion de LubeLog completada",
+    "cancel": "Cancelar",
+    "import": "Importar",
+    "invoiceNinjaTitle": "Importar de Invoice Ninja",
+    "invoiceNinjaExportSteps": "Como exportar sus datos de Invoice Ninja:",
+    "invoiceNinjaStep1": "Abra su instancia de Invoice Ninja",
+    "invoiceNinjaStep2": "Vaya a <bold>Settings > Import | Export</bold>",
+    "invoiceNinjaStep3": "Haga clic en <bold>Export</bold> para descargar un archivo zip",
+    "invoiceNinjaStep4": "Suba el archivo zip descargado abajo",
+    "invoiceNinjaAddNote": "Esto <bold>agregara datos junto a</bold> sus registros existentes — nada sera eliminado.",
+    "invoiceNinjaImported": "Que se importara:",
+    "invoiceNinjaCustomers": "Clientes",
+    "invoiceNinjaProducts": "Productos (inventario)",
+    "invoiceNinjaInvoices": "Facturas como registros de servicio (piezas y mano de obra)",
+    "invoiceNinjaDocuments": "Documentos y adjuntos",
+    "invoiceNinjaPayments": "Pagos",
+    "invoiceNinjaFileHint": "Suba un archivo de exportacion zip de Invoice Ninja. Archivos hasta 200MB soportados.",
+    "invoiceNinjaImportComplete": "Importacion de Invoice Ninja completada"
+  },
+  "license": {
+    "title": "Licencia marca blanca",
+    "description": "Active una licencia de marca blanca para eliminar la marca Torqvoice de las facturas y la aplicacion.",
+    "purchaseWhiteLabel": "Comprar marca blanca",
+    "status": "Estado:",
+    "active": "Activa",
+    "inactive": "Inactiva",
+    "lastChecked": "Ultima verificacion: {date}",
+    "enterLicenseKey": "Ingrese clave de licencia",
+    "validate": "Validar",
+    "validated": "Licencia validada exitosamente",
+    "invalid": "Clave de licencia invalida",
+    "failedValidate": "Error al validar la licencia",
+    "keyHint": "Cada clave de licencia esta vinculada a esta organizacion y no puede compartirse entre varias organizaciones."
+  },
+  "about": {
+    "title": "Acerca de Torqvoice",
+    "description": "Torqvoice es su solucion completa de gestion de servicios de vehiculos. Rastree historial de servicio, reparaciones y mas.",
+    "version": "Version",
+    "website": "Sitio web",
+    "documentation": "Documentacion",
+    "changelog": "Registro de cambios"
+  },
+  "portal": {
+    "title": "Portal del cliente",
+    "description": "Permita a sus clientes iniciar sesion y ver sus vehiculos, facturas, cotizaciones e inspecciones. Los clientes tambien pueden enviar solicitudes de servicio.",
+    "enablePortal": "Habilitar portal del cliente",
+    "enablePortalHint": "Los clientes con direccion de correo electronico pueden iniciar sesion con enlace magico",
+    "enabled": "Portal del cliente habilitado",
+    "disabled": "Portal del cliente deshabilitado",
+    "failedUpdate": "Error al actualizar la configuracion",
+    "customSlug": "Slug personalizado del portal",
+    "save": "Guardar",
+    "slugSaved": "Slug del portal guardado",
+    "slugRemoved": "Slug del portal eliminado",
+    "failedUpdateSlug": "Error al actualizar el slug",
+    "slugHint": "Letras minusculas, numeros, guiones y guiones bajos. 3-48 caracteres. Dejar vacio para usar la URL predeterminada.",
+    "portalUrl": "URL del portal",
+    "urlCopied": "URL del portal copiada",
+    "shareHint": "Comparta este enlace con sus clientes para que puedan iniciar sesion en su portal."
+  },
+  "email": {
+    "title": "Proveedor de correo",
+    "description": "Configure el proveedor de correo electronico de su organizacion para enviar facturas y presupuestos. Si no se configura, se utilizara el proveedor de correo predeterminado de la plataforma.",
+    "useCustomLabel": "Usar proveedor de correo personalizado",
+    "useCustomHint": "Active para configurar su propio proveedor de correo en lugar de usar el predeterminado de la plataforma",
+    "platformDefaultInfo": "Su organizacion utiliza el proveedor de correo predeterminado de la plataforma. Las facturas y presupuestos se enviaran con la configuracion de correo de la plataforma.",
+    "smtpHost": "Host SMTP",
+    "smtpHostPlaceholder": "smtp.example.com",
+    "smtpPort": "Puerto SMTP",
+    "smtpPortPlaceholder": "587",
+    "username": "Usuario",
+    "usernamePlaceholder": "usuario@example.com",
+    "password": "Contrasena",
+    "fromEmail": "Correo del remitente",
+    "fromEmailPlaceholder": "noreply@example.com",
+    "fromEmailDomainPlaceholder": "noreply@sudominio.com",
+    "fromName": "Nombre del remitente",
+    "fromNamePlaceholder": "Su Taller",
+    "tlsConnection": "Conexion TLS (Puerto 465)",
+    "tlsConnectionHint": "Active para TLS implicito. Desactive para STARTTLS (puerto 587/25).",
+    "verifyTlsCerts": "Verificar certificados TLS",
+    "verifyTlsCertsHint": "Desactive para permitir certificados autofirmados (no recomendado para produccion)",
+    "requireTlsUpgrade": "Requerir actualizacion TLS",
+    "requireTlsUpgradeHint": "Forzar actualizacion TLS en conexiones STARTTLS",
+    "apiKey": "Clave API",
+    "apiKeyHintResend": "Obtenga su clave API en",
+    "serverToken": "Token de servidor",
+    "serverTokenHintPostmark": "Obtenga su Token de servidor en",
+    "apiKeyHintMailgun": "Obtenga su clave API en",
+    "domain": "Dominio",
+    "domainPlaceholder": "mg.sudominio.com",
+    "region": "Region",
+    "apiKeyHintSendGrid": "Obtenga su clave API en",
+    "accessKeyId": "ID de clave de acceso",
+    "secretAccessKey": "Clave de acceso secreta",
+    "awsRegion": "Region AWS",
+    "awsRegionPlaceholder": "us-east-1",
+    "awsRegionHint": "La region AWS donde esta configurado SES (ej. us-east-1, eu-west-1)",
+    "sendTestEmail": "Enviar correo de prueba",
+    "testEmailHint": "Guarde la configuracion primero, luego envie un correo de prueba a su cuenta",
+    "saveSettings": "Guardar configuracion",
+    "savedDefault": "Configuracion de correo guardada — usando predeterminado de la plataforma",
+    "saved": "Configuracion de correo guardada",
+    "failedSave": "Error al guardar la configuracion",
+    "testSentTo": "Correo de prueba enviado a {email}",
+    "testFailed": "Prueba de correo fallida"
+  },
+  "sms": {
+    "title": "Proveedor SMS",
+    "description": "Configure su proveedor de SMS para mensajeria bidireccional con clientes. Cada organizacion usa su propio proveedor de SMS y numero de telefono.",
+    "enableLabel": "Activar mensajeria SMS",
+    "enableHint": "Enviar y recibir mensajes SMS con clientes",
+    "disabledInfo": "La mensajeria SMS esta desactivada. Activela para enviar y recibir mensajes de texto con sus clientes.",
+    "phoneNumber": "Numero de telefono",
+    "phoneNumberPlaceholder": "+34612345678",
+    "phoneNumberHint": "Su numero de telefono habilitado para SMS en formato E.164 (ej. +34612345678)",
+    "accountSid": "SID de cuenta",
+    "authToken": "Token de autenticacion",
+    "apiKey": "Clave API",
+    "apiSecret": "Secreto API",
+    "webhookUrl": "URL de webhook",
+    "webhookUrlHint": "Configure esta URL en su panel de {provider} para recibir mensajes SMS entrantes.",
+    "webhookCopied": "URL de webhook copiada",
+    "testPhoneNumber": "Numero de telefono de prueba",
+    "testPhonePlaceholder": "+34698765432",
+    "sendTestSms": "Enviar SMS de prueba",
+    "testSmsHint": "Guarde la configuracion primero, luego ingrese un numero de destinatario para enviar un SMS de prueba",
+    "enterTestPhone": "Ingrese un numero de telefono para enviar el SMS de prueba",
+    "saveSettings": "Guardar configuracion",
+    "savedDisabled": "Configuracion SMS guardada — SMS desactivado",
+    "saved": "Configuracion SMS guardada",
+    "failedSave": "Error al guardar la configuracion",
+    "testSentTo": "SMS de prueba enviado a {phone}",
+    "testFailed": "Prueba de SMS fallida"
+  }
+}

+ 156 - 0
messages/es/share.json

@@ -0,0 +1,156 @@
+{
+  "invoice": {
+    "title": "Factura",
+    "downloadPdf": "Descargar PDF",
+    "paymentReceived": "¡Pago recibido!",
+    "paymentApplied": "{amount} ha sido aplicado a esta factura.",
+    "verifyingPayment": "Verificando pago...",
+    "balanceDue": "Saldo pendiente",
+    "fullAmount": "Monto completo",
+    "partialPayment": "Pago parcial",
+    "enterAmount": "Ingresar monto",
+    "payWithCard": "Pagar {amount} con tarjeta",
+    "payWithVipps": "Pagar {amount} con Vipps",
+    "payWithPaypal": "Pagar {amount} con PayPal",
+    "errorInvalidAmount": "Por favor ingrese un monto válido.",
+    "errorExceedsBalance": "El monto no puede superar {amount}.",
+    "errorVerifyFailed": "No se pudo verificar el pago. Es posible que aún esté procesándose.",
+    "errorVerifyRequest": "Error al verificar el estado del pago.",
+    "errorCheckoutFailed": "Error al iniciar el proceso de pago.",
+    "errorPaymentFailed": "Error al iniciar el pago. Por favor intente nuevamente.",
+    "billTo": "Facturar a",
+    "vehicle": "Vehículo",
+    "vin": "VIN: {vin}",
+    "plate": "Placa: {plate}",
+    "service": "Servicio",
+    "type": "Tipo: {type}",
+    "tech": "Técnico: {tech}",
+    "parts": "Piezas",
+    "partNumber": "N.º de pieza",
+    "description": "Descripción",
+    "qty": "Cant.",
+    "unitPrice": "Precio unitario",
+    "total": "Total",
+    "labor": "Mano de obra",
+    "hours": "Horas",
+    "rate": "Tarifa",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "discountPercent": "Descuento ({percent}%)",
+    "tax": "Impuesto ({rate}%)",
+    "paid": "Pagado",
+    "paidInFull": "PAGADO EN SU TOTALIDAD",
+    "amountDue": "Monto adeudado",
+    "paymentInformation": "Información de pago",
+    "bankAccount": "Cuenta bancaria",
+    "orgNumber": "N.º de organización",
+    "paymentTerms": "Condiciones de pago",
+    "dueDate": "Fecha de vencimiento",
+    "serviceImages": "Imágenes del servicio ({count})",
+    "serviceVideos": "Videos del servicio ({count})",
+    "diagnosticReports": "Informes de diagnóstico ({count})",
+    "notes": "Notas",
+    "diagnosticNotes": "Notas de diagnóstico",
+    "tel": "Tel: {phone}"
+  },
+  "quote": {
+    "title": "Presupuesto",
+    "downloadPdf": "Descargar PDF",
+    "preparedFor": "Preparado para",
+    "vehicle": "Vehículo",
+    "vin": "VIN: {vin}",
+    "plate": "Placa: {plate}",
+    "quoteDetails": "Detalles del presupuesto",
+    "description": "Descripción",
+    "parts": "Piezas",
+    "partNumber": "N.º de pieza",
+    "partDescription": "Descripción",
+    "qty": "Cant.",
+    "unitPrice": "Precio unitario",
+    "total": "Total",
+    "labor": "Mano de obra",
+    "laborDescription": "Descripción",
+    "hours": "Horas",
+    "rate": "Tarifa",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Subtotal",
+    "discount": "Descuento",
+    "discountPercent": "Descuento ({percent}%)",
+    "tax": "Impuesto ({rate}%)",
+    "images": "Imágenes ({count})",
+    "documents": "Documentos",
+    "requestChanges": "Solicitar cambios",
+    "acceptQuote": "Aceptar presupuesto",
+    "whatChanges": "¿Qué cambios desea realizar?",
+    "changePlaceholder": "Describa los cambios que desea...",
+    "cancel": "Cancelar",
+    "submitRequest": "Enviar solicitud",
+    "quoteAccepted": "Presupuesto aceptado",
+    "quoteAcceptedMessage": "¡Gracias! Este presupuesto ha sido aceptado. El taller se pondrá en contacto en breve.",
+    "changesRequested": "Cambios solicitados",
+    "changesRequestedMessage": "Su solicitud de cambios ha sido enviada. El taller la revisará y le responderá.",
+    "validUntil": "Válido hasta: {date}",
+    "status": {
+      "draft": "Borrador",
+      "sent": "Enviado",
+      "accepted": "Aceptado",
+      "rejected": "Rechazado",
+      "expired": "Expirado",
+      "converted": "Convertido",
+      "changes_requested": "Cambios solicitados"
+    },
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "Inspección de vehículo",
+    "vehicle": "Vehículo",
+    "customer": "Cliente",
+    "vin": "VIN: {vin}",
+    "plate": "Placa: {plate}",
+    "mileage": "Kilometraje: {mileage}",
+    "inspected": "inspeccionado",
+    "pass": "Aprobado",
+    "fail": "Reprobado",
+    "attention": "Atención",
+    "notInspected": "No inspeccionado",
+    "quoteAvailable": "Se ha preparado un presupuesto para esta inspección",
+    "quoteAvailableDescription": "Revise el presupuesto para ver los precios y aprobar el trabajo recomendado.",
+    "viewQuote": "Ver presupuesto",
+    "quoteRequested": "Presupuesto solicitado",
+    "quoteRequestedCancel": "— Cancelar",
+    "requestQuote": "Solicitar presupuesto",
+    "downloadPdf": "Descargar PDF",
+    "notes": "Notas",
+    "quoteCancelled": "Solicitud de presupuesto cancelada",
+    "cancelFailed": "Error al cancelar la solicitud"
+  },
+  "terms": {
+    "title": "Términos de venta (Salgsvilkår)",
+    "contact": "Contacto",
+    "tel": "Tel: {phone}"
+  },
+  "quoteRequest": {
+    "title": "Solicitar presupuesto",
+    "description": "Seleccione los elementos para los que desea un presupuesto de reparación.",
+    "selectAll": "Seleccionar todo",
+    "deselectAll": "Deseleccionar todo",
+    "selectedCount": "{selected} de {total} seleccionados",
+    "fail": "Reprobado",
+    "attention": "Atención",
+    "message": "Mensaje",
+    "messageOptional": "(opcional)",
+    "messagePlaceholder": "Detalles adicionales o preguntas...",
+    "cancel": "Cancelar",
+    "submitRequest": "Enviar solicitud",
+    "selectAtLeastOne": "Seleccione al menos un elemento",
+    "submitSuccess": "¡Solicitud de presupuesto enviada!",
+    "submitError": "Error al enviar la solicitud"
+  },
+  "common": {
+    "poweredBy": "Desarrollado por",
+    "termsOfSale": "Términos de venta",
+    "portalMessage": "Vea sus vehículos, facturas y más en su",
+    "customerPortal": "portal de clientes"
+  }
+}

+ 195 - 0
messages/es/vehicles.json

@@ -0,0 +1,195 @@
+{
+  "list": {
+    "active": "Activos",
+    "archived": "Archivados",
+    "searchPlaceholder": "Buscar por placa, marca, modelo, VIN...",
+    "addVehicle": "Agregar vehiculo",
+    "emptySearch": "Ningun vehiculo coincide con su busqueda.",
+    "emptyArchived": "No hay vehiculos archivados.",
+    "empty": "Aun no hay vehiculos.",
+    "table": {
+      "plate": "Placa",
+      "vehicle": "Vehiculo",
+      "customer": "Cliente",
+      "mileage": "Kilometraje",
+      "services": "Servicios"
+    },
+    "edit": "Editar",
+    "archive": "Archivar",
+    "unarchive": "Desarchivar",
+    "delete": "Eliminar",
+    "deleteTitle": "Eliminar vehiculo",
+    "deleteDescription": "Eliminar {name}? Esto eliminara todos los registros de servicio y notas asociados.",
+    "unarchiveTitle": "Desarchivar vehiculo",
+    "unarchiveDescription": "Restaurar {name} a la lista de vehiculos activos?",
+    "vehicleUnarchived": "Vehiculo desarchivado",
+    "unarchiveError": "Error al desarchivar vehiculo",
+    "deleteError": "Error al eliminar vehiculo",
+    "error": "Error al cargar vehiculos"
+  },
+  "detail": {
+    "vehicleArchived": "Este vehiculo esta archivado",
+    "backToVehicles": "Volver a vehiculos",
+    "newWorkOrder": "Nueva orden de trabajo",
+    "editVehicle": "Editar vehiculo",
+    "unarchive": "Desarchivar",
+    "archive": "Archivar",
+    "delete": "Eliminar",
+    "notFound": "Vehiculo no encontrado",
+    "predictedKm": "Km predichos",
+    "predictedMileage": "Kilometraje predicho",
+    "dismissed": "Descartado",
+    "undo": "Deshacer",
+    "serviceOverdue": "Servicio vencido",
+    "serviceSoon": "Servicio proximo",
+    "services": "servicios",
+    "purchased": "Comprado el {date}",
+    "tabs": {
+      "services": "Servicios",
+      "inspections": "Inspecciones",
+      "notes": "Notas",
+      "reminders": "Recordatorios"
+    },
+    "noteDeleted": "Nota eliminada",
+    "noteDeleteError": "Error al eliminar",
+    "reminderDeleted": "Recordatorio eliminado",
+    "reminderDeleteError": "Error al eliminar",
+    "vehicleUnarchived": "Vehiculo desarchivado",
+    "unarchiveError": "Error al desarchivar",
+    "vehicleDeleted": "Vehiculo eliminado",
+    "vehicleDeleteError": "Error al eliminar vehiculo",
+    "deleteTitle": "Eliminar vehiculo?",
+    "deleteDescription": "Esto eliminara permanentemente {name} y todos los registros de servicio, notas y recordatorios asociados. Esta accion no se puede deshacer.",
+    "edit": "Editar"
+  },
+  "inspections": {
+    "newInspection": "Nueva inspeccion",
+    "empty": "Aun no hay inspecciones",
+    "template": "Plantilla",
+    "progress": "Progreso",
+    "status": "Estado",
+    "date": "Fecha",
+    "completed": "Completada",
+    "inProgress": "En progreso"
+  },
+  "reminders": {
+    "active": "Activos",
+    "completed": "Completados",
+    "all": "Todos",
+    "addReminder": "Agregar recordatorio",
+    "emptyActive": "No hay recordatorios activos",
+    "emptyCompleted": "No hay recordatorios completados",
+    "empty": "Aun no hay recordatorios",
+    "overdue": "Vencido",
+    "dueSoon": "Vence pronto",
+    "due": "Vence el {date}",
+    "dueAt": "Vence a los {mileage} {unit}",
+    "editTitle": "Editar recordatorio",
+    "addTitle": "Agregar recordatorio",
+    "titleLabel": "Titulo *",
+    "titlePlaceholder": "ej. Cambio de aceite",
+    "descriptionLabel": "Descripcion",
+    "descriptionPlaceholder": "Detalles opcionales...",
+    "dueDateLabel": "Fecha de vencimiento",
+    "dueMileageLabel": "Kilometraje de vencimiento",
+    "dueMileagePlaceholder": "ej. 50000",
+    "reminderUpdated": "Recordatorio actualizado",
+    "reminderCreated": "Recordatorio creado",
+    "saveError": "Error al {action} recordatorio"
+  },
+  "services": {
+    "searchPlaceholder": "Buscar registros...",
+    "allTypes": "Todos los tipos",
+    "maintenance": "Mantenimiento",
+    "repair": "Reparacion",
+    "upgrade": "Mejora",
+    "inspection": "Inspeccion",
+    "newWorkOrder": "Nueva orden de trabajo",
+    "table": {
+      "date": "Fecha",
+      "title": "Titulo",
+      "type": "Tipo",
+      "status": "Estado",
+      "mileage": "Kilometraje",
+      "technician": "Tecnico",
+      "files": "Archivos",
+      "total": "Total"
+    },
+    "emptyFiltered": "Ningun registro de servicio coincide con sus filtros.",
+    "empty": "Aun no hay registros de servicio.",
+    "showing": "Mostrando {start}-{end} de {total}",
+    "perPage": "por pagina",
+    "page": "Pagina {page} de {totalPages}"
+  },
+  "notes": {
+    "addNote": "Agregar nota",
+    "table": {
+      "title": "Titulo",
+      "content": "Contenido",
+      "date": "Fecha"
+    },
+    "empty": "Aun no hay notas.",
+    "pin": "Fijar",
+    "unpin": "Desfijar",
+    "delete": "Eliminar",
+    "showing": "Mostrando {start}-{end} de {total}",
+    "perPage": "por pagina",
+    "page": "Pagina {page} de {totalPages}",
+    "editTitle": "Editar nota",
+    "addTitle": "Agregar nota",
+    "titleLabel": "Titulo *",
+    "titlePlaceholder": "Titulo de la nota",
+    "contentLabel": "Contenido *",
+    "contentPlaceholder": "Escriba su nota...",
+    "noteUpdated": "Nota actualizada",
+    "noteCreated": "Nota creada",
+    "titleRequired": "El titulo y el contenido son obligatorios",
+    "saveError": "Error al {action} nota"
+  },
+  "form": {
+    "editTitle": "Editar vehiculo",
+    "addTitle": "Agregar nuevo vehiculo",
+    "photo": "Foto",
+    "clickToUpload": "Haga clic para subir una foto",
+    "imageFormats": "JPEG, PNG, WebP hasta 5MB",
+    "imageTooLarge": "Imagen demasiado grande",
+    "maxFileSize": "El tamano maximo del archivo es 5MB",
+    "uploadingImage": "Subiendo imagen...",
+    "uploadFailed": "Error al subir",
+    "imageUploaded": "Imagen subida",
+    "customer": "Cliente",
+    "searchCustomers": "Buscar clientes...",
+    "noCustomerFound": "No se encontro ningun cliente.",
+    "noCustomer": "Sin cliente",
+    "addCustomer": "Agregar cliente",
+    "make": "Marca *",
+    "model": "Modelo *",
+    "year": "Ano *",
+    "mileage": "Kilometraje",
+    "vin": "VIN",
+    "licensePlate": "Placa",
+    "color": "Color",
+    "fuelType": "Tipo de combustible",
+    "gasoline": "Gasolina",
+    "diesel": "Diesel",
+    "electric": "Electrico",
+    "hybrid": "Hibrido",
+    "other": "Otro",
+    "transmission": "Transmision",
+    "automatic": "Automatica",
+    "manual": "Manual",
+    "cvt": "CVT",
+    "engineSize": "Tamano del motor",
+    "vehicleUpdated": "Vehiculo actualizado",
+    "vehicleAdded": "Vehiculo agregado",
+    "saveError": "Error al guardar vehiculo"
+  },
+  "archiveDialog": {
+    "title": "Archivar vehiculo",
+    "description": "Archivar <span>{name}</span>? Se ocultara de la lista principal pero todo el historial de servicio se conservara.",
+    "reasonLabel": "Motivo (opcional)",
+    "reasonPlaceholder": "ej. Vendido, siniestrado, dado de baja...",
+    "archived": "{name} archivado",
+    "archiveError": "Error al archivar vehiculo"
+  }
+}

+ 67 - 0
messages/es/workBoard.json

@@ -0,0 +1,67 @@
+{
+  "page": {
+    "error": "Error al cargar el tablero de trabajo"
+  },
+  "toolbar": {
+    "today": "Hoy",
+    "presenter": "Presentador",
+    "addTechnician": "Agregar Técnico"
+  },
+  "board": {
+    "days": {
+      "mon": "Lun",
+      "tue": "Mar",
+      "wed": "Mié",
+      "thu": "Jue",
+      "fri": "Vie",
+      "sat": "Sáb",
+      "sun": "Dom"
+    },
+    "noTechnicians": "Sin Técnicos Aún",
+    "noTechniciansDescription": "Agregue un técnico para comenzar a programar trabajo en el tablero.",
+    "failedMove": "Error al mover asignación",
+    "failedAssign": "Error al asignar trabajo",
+    "failedRemove": "Error al eliminar asignación"
+  },
+  "unassigned": {
+    "title": "Trabajos Sin Asignar ({count})",
+    "searchPlaceholder": "Buscar trabajos...",
+    "empty": "No hay trabajos sin asignar"
+  },
+  "technician": {
+    "editTitle": "Editar Técnico",
+    "addTitle": "Agregar Técnico",
+    "name": "Nombre",
+    "namePlaceholder": "p.ej. Juan García",
+    "color": "Color",
+    "linkMember": "Vincular a Miembro de la Organización (opcional)",
+    "standalone": "Técnico independiente",
+    "deleteTitle": "Eliminar Técnico",
+    "deleteDescription": "¿Desea eliminar a \"{name}\" del tablero de trabajo? Sus asignaciones existentes también se eliminarán.",
+    "addTechnician": "Agregar Técnico"
+  },
+  "jobDetail": {
+    "viewDetails": "Ver Detalles",
+    "remove": "Eliminar",
+    "date": "Fecha: {date}"
+  },
+  "presenter": {
+    "title": "Tablero de Trabajo",
+    "day": "Día",
+    "status": "Estado",
+    "week": "Semana",
+    "today": "Hoy",
+    "live": "En Vivo",
+    "disconnected": "Actualizaciones en vivo desconectadas",
+    "noTechnicians": "Sin técnicos configurados",
+    "noJobsToday": "Sin trabajos hoy",
+    "job": "{count} trabajo",
+    "jobs": "{count} trabajos",
+    "statusLabels": {
+      "pending": "Pendiente",
+      "inProgress": "En Progreso",
+      "waitingParts": "Esperando Piezas",
+      "completed": "Completado"
+    }
+  }
+}

+ 80 - 0
messages/es/workOrders.json

@@ -0,0 +1,80 @@
+{
+  "page": {
+    "error": "Error al cargar órdenes de trabajo"
+  },
+  "list": {
+    "searchPlaceholder": "Buscar órdenes de trabajo...",
+    "newWorkOrder": "Nueva Orden de Trabajo",
+    "selectVehicle": "Seleccionar Vehículo",
+    "empty": "No se encontraron órdenes de trabajo.",
+    "statusUpdated": "Estado actualizado",
+    "statusTabs": {
+      "all": "Todas",
+      "active": "Activas",
+      "pending": "Pendientes",
+      "inProgress": "En Progreso",
+      "waitingParts": "Esperando Piezas",
+      "completed": "Completadas"
+    },
+    "statusActions": {
+      "startWork": "Comenzar Trabajo",
+      "waitingParts": "Esperando Piezas",
+      "complete": "Completar",
+      "resumeWork": "Reanudar Trabajo",
+      "reopen": "Reabierto"
+    },
+    "table": {
+      "invoice": "Factura#",
+      "vehicle": "Vehículo",
+      "customer": "Cliente",
+      "title": "Título",
+      "status": "Estado",
+      "tech": "Técnico",
+      "date": "Fecha",
+      "total": "Total"
+    },
+    "emailSubject": "Actualización de Estado del Vehículo: {status}"
+  },
+  "vehiclePicker": {
+    "title": "Seleccionar Vehículo para Orden de Trabajo",
+    "searchPlaceholder": "Buscar vehículos...",
+    "empty": "No se encontraron vehículos.",
+    "emptySearch": "Ningún vehículo coincide con su búsqueda.",
+    "addNewVehicle": "Agregar Nuevo Vehículo",
+    "noPlate": "Sin placa",
+    "make": "Marca *",
+    "model": "Modelo *",
+    "year": "Año *",
+    "licensePlate": "Placa de Licencia",
+    "customer": "Cliente",
+    "selectCustomer": "Seleccionar un cliente (opcional)",
+    "createNewCustomer": "Crear nuevo cliente",
+    "newCustomer": "Nuevo Cliente",
+    "customerName": "Nombre *",
+    "customerNamePlaceholder": "Nombre del cliente",
+    "phone": "Teléfono",
+    "phonePlaceholder": "Número de teléfono",
+    "creating": "Creando...",
+    "createAndContinue": "Crear y Continuar",
+    "invalidYear": "Por favor ingrese un año válido",
+    "customerNameRequired": "El nombre del cliente es requerido",
+    "failedCreateCustomer": "Error al crear cliente",
+    "failedCreateVehicle": "Error al crear vehículo",
+    "somethingWentWrong": "Algo salió mal"
+  },
+  "notify": {
+    "title": "Notificar a {name}",
+    "description": "Enviar una notificación al cliente sobre esta actualización.",
+    "sms": "SMS",
+    "email": "Correo Electrónico",
+    "noPhone": "(sin teléfono registrado)",
+    "noEmail": "(sin correo electrónico registrado)",
+    "notAvailable": "(no disponible)",
+    "skip": "Omitir",
+    "send": "Enviar",
+    "smsSent": "SMS enviado",
+    "emailSent": "Correo electrónico enviado",
+    "failedSms": "Error al enviar SMS",
+    "failedEmail": "Error al enviar correo electrónico"
+  }
+}

+ 105 - 0
messages/fr/admin.json

@@ -0,0 +1,105 @@
+{
+  "nav": {
+    "overview": { "title": "Vue d'ensemble", "description": "Statistiques et état de la plateforme" },
+    "users": { "title": "Utilisateurs", "description": "Gérer tous les utilisateurs" },
+    "organizations": { "title": "Organisations", "description": "Gérer toutes les organisations" },
+    "settings": { "title": "Paramètres", "description": "Configuration de la plateforme" }
+  },
+  "layout": {
+    "title": "Panneau d'administration",
+    "description": "Gestion de la plateforme pour les super administrateurs"
+  },
+  "overview": {
+    "totalUsers": "Utilisateurs totaux",
+    "organizations": "Organisations",
+    "activeSubscriptions": "Abonnements actifs",
+    "monthlyRevenue": "Revenus mensuels"
+  },
+  "users": {
+    "searchPlaceholder": "Rechercher par nom ou e-mail...",
+    "name": "Nom",
+    "email": "E-mail",
+    "role": "Rôle",
+    "orgs": "Orgs",
+    "created": "Créé",
+    "lastSeen": "Dernière connexion",
+    "noResults": "Aucun utilisateur ne correspond à votre recherche.",
+    "noUsers": "Aucun utilisateur trouvé.",
+    "superAdmin": "Super administrateur",
+    "user": "Utilisateur",
+    "online": "En ligne",
+    "promoteTitle": "Promouvoir l'utilisateur",
+    "demoteTitle": "Rétrograder l'utilisateur",
+    "promoteConfirm": "Êtes-vous sûr de vouloir promouvoir \"{name}\" au rang de super administrateur ?",
+    "demoteConfirm": "Êtes-vous sûr de vouloir rétrograder \"{name}\" du rang de super administrateur ?",
+    "promote": "Promouvoir",
+    "demote": "Rétrograder",
+    "promotedSuccess": "Utilisateur promu avec succès",
+    "demotedSuccess": "Utilisateur rétrogradé avec succès",
+    "failedUpdate": "Échec de la mise à jour de l'utilisateur",
+    "deleteTitle": "Supprimer l'utilisateur",
+    "deleteConfirm": "Êtes-vous sûr de vouloir supprimer définitivement \"{name}\" ({email}) ? Cette action est irréversible.",
+    "delete": "Supprimer",
+    "deletedSuccess": "Utilisateur supprimé avec succès",
+    "failedDelete": "Échec de la suppression de l'utilisateur",
+    "promoteToSuperAdmin": "Promouvoir en super administrateur",
+    "demoteFromSuperAdmin": "Rétrograder du super administrateur",
+    "deleteUser": "Supprimer l'utilisateur"
+  },
+  "organizations": {
+    "searchPlaceholder": "Rechercher par nom d'organisation...",
+    "name": "Nom",
+    "owner": "Propriétaire",
+    "members": "Membres",
+    "subscription": "Abonnement",
+    "created": "Créé",
+    "noResults": "Aucune organisation ne correspond à votre recherche.",
+    "noOrganizations": "Aucune organisation trouvée.",
+    "none": "Aucun",
+    "deleteTitle": "Supprimer l'organisation",
+    "deleteConfirm": "Êtes-vous sûr de vouloir supprimer définitivement \"{name}\" et toutes ses données ? Cette action est irréversible.",
+    "delete": "Supprimer",
+    "deletedSuccess": "Organisation supprimée avec succès",
+    "failedDelete": "Échec de la suppression de l'organisation",
+    "deleteOrganization": "Supprimer l'organisation"
+  },
+  "adminSettings": {
+    "platformTitle": "Paramètres de la plateforme",
+    "platformDescription": "Options générales de configuration de la plateforme",
+    "disableRegistration": "Désactiver l'inscription",
+    "disableRegistrationHint": "Bloquer les nouvelles inscriptions d'utilisateurs sur la plateforme",
+    "emailTitle": "Paramètres e-mail",
+    "emailDescription": "Fournisseur d'e-mail de la plateforme — Utilisé pour les e-mails au niveau du système tels que les réinitialisations de mot de passe et les invitations d'équipe. C'est également le fournisseur d'e-mail par défaut pour toutes les organisations. Les organisations peuvent éventuellement configurer leur propre fournisseur d'e-mail dans Paramètres > E-mail.",
+    "smtpHost": "Hôte SMTP",
+    "smtpPort": "Port SMTP",
+    "username": "Nom d'utilisateur",
+    "password": "Mot de passe",
+    "fromEmail": "E-mail de l'expéditeur",
+    "fromName": "Nom de l'expéditeur",
+    "tlsConnection": "Connexion TLS (Port 465)",
+    "tlsConnectionHint": "Activer pour le TLS implicite. Désactiver pour STARTTLS (port 587/25).",
+    "verifyTls": "Vérifier les certificats TLS",
+    "verifyTlsHint": "Désactiver pour autoriser les certificats auto-signés (non recommandé pour la production)",
+    "requireTlsUpgrade": "Exiger la mise à niveau TLS",
+    "requireTlsUpgradeHint": "Forcer la mise à niveau TLS sur les connexions STARTTLS",
+    "apiKey": "Clé API",
+    "serverToken": "Jeton du serveur",
+    "domain": "Domaine",
+    "region": "Région",
+    "awsRegion": "Région AWS",
+    "accessKeyId": "ID de clé d'accès",
+    "secretAccessKey": "Clé d'accès secrète",
+    "awsRegionHint": "La région AWS où SES est configuré (ex. us-east-1, eu-west-1)",
+    "sendTestEmail": "Envoyer un e-mail de test",
+    "testEmailHint": "Enregistrez d'abord les paramètres, puis envoyez un e-mail de test à votre compte",
+    "saveSettings": "Enregistrer les paramètres",
+    "saved": "Paramètres enregistrés avec succès",
+    "failedSave": "Échec de l'enregistrement des paramètres",
+    "testSentTo": "E-mail de test envoyé à {email}",
+    "testFailed": "Le test d'e-mail a échoué",
+    "resendApiKeyHint": "Obtenez votre clé API sur resend.com",
+    "postmarkApiKeyHint": "Obtenez votre Server Token sur postmarkapp.com",
+    "mailgunApiKeyHint": "Obtenez votre clé API sur app.mailgun.com",
+    "sendgridApiKeyHint": "Obtenez votre clé API sur app.sendgrid.com"
+  }
+}

+ 73 - 0
messages/fr/auth.json

@@ -0,0 +1,73 @@
+{
+  "signIn": {
+    "title": "Bon retour parmi nous",
+    "description": "Connectez-vous à votre atelier",
+    "passwordPlaceholder": "Saisissez votre mot de passe",
+    "forgotPassword": "Mot de passe oublié ?",
+    "noAccount": "Vous n'avez pas de compte ?",
+    "createOne": "Créer un compte",
+    "termsAgreement": "En utilisant ce service, vous acceptez nos",
+    "errors": {
+      "tooManyAttempts": "Trop de tentatives, veuillez réessayer plus tard.",
+      "invalidCredentials": "Adresse e-mail ou mot de passe invalide"
+    }
+  },
+  "signUp": {
+    "title": "Créer un compte",
+    "descriptionInvite": "Créez votre compte pour rejoindre l'équipe",
+    "descriptionDefault": "Configurez votre atelier pour gérer les véhicules de vos clients",
+    "fullName": "Nom complet",
+    "fullNamePlaceholder": "Jean Dupont",
+    "passwordPlaceholder": "Créez un mot de passe sécurisé",
+    "agreeToTerms": "J'accepte les",
+    "createAccountJoin": "Créer un compte et rejoindre l'équipe",
+    "createAccount": "Créer un compte",
+    "alreadyHaveAccount": "Vous avez déjà un compte ?",
+    "errors": {
+      "couldNotCreate": "Impossible de créer le compte",
+      "registrationDisabled": "L'inscription est actuellement désactivée. Veuillez contacter votre administrateur.",
+      "invitationFailed": "L'acceptation de l'invitation a échoué"
+    }
+  },
+  "forgotPassword": {
+    "title": "Réinitialiser votre mot de passe",
+    "description": "Saisissez votre adresse e-mail et nous vous enverrons un lien de réinitialisation",
+    "sendResetLink": "Envoyer le lien de réinitialisation",
+    "backToSignIn": "Retour à la connexion",
+    "successMessage": "Si un compte existe avec cette adresse e-mail, un lien de réinitialisation du mot de passe a été envoyé. Vérifiez votre boîte de réception."
+  },
+  "resetPassword": {
+    "title": "Définir un nouveau mot de passe",
+    "description": "Saisissez votre nouveau mot de passe ci-dessous",
+    "newPassword": "Nouveau mot de passe",
+    "newPasswordPlaceholder": "Saisissez le nouveau mot de passe",
+    "confirmPassword": "Confirmer le mot de passe",
+    "confirmPasswordPlaceholder": "Confirmez le nouveau mot de passe",
+    "resetButton": "Réinitialiser le mot de passe",
+    "backToSignIn": "Retour à la connexion",
+    "requestNewLink": "Demander un nouveau lien",
+    "invalidToken": "Jeton de réinitialisation invalide ou manquant. Veuillez demander un nouveau lien de réinitialisation du mot de passe.",
+    "successMessage": "Votre mot de passe a été réinitialisé avec succès. Vous pouvez maintenant vous connecter avec votre nouveau mot de passe.",
+    "errors": {
+      "passwordsMismatch": "Les mots de passe ne correspondent pas.",
+      "passwordTooShort": "Le mot de passe doit comporter au moins 8 caractères.",
+      "resetFailed": "Impossible de réinitialiser le mot de passe. Le lien a peut-être expiré."
+    }
+  },
+  "verify2fa": {
+    "title": "Authentification à deux facteurs",
+    "descriptionTotp": "Saisissez le code à 6 chiffres de votre application d'authentification",
+    "descriptionBackup": "Saisissez l'un de vos codes de secours",
+    "authCode": "Code d'authentification",
+    "backupCode": "Code de secours",
+    "backupCodePlaceholder": "Saisissez le code de secours",
+    "totpPlaceholder": "000000",
+    "trustDevice": "Faire confiance à cet appareil pendant 30 jours",
+    "verify": "Vérifier",
+    "useAuthenticator": "Utiliser l'application d'authentification",
+    "useBackupCode": "Utiliser un code de secours",
+    "errors": {
+      "invalidCode": "Code invalide"
+    }
+  }
+}

+ 83 - 0
messages/fr/billing.json

@@ -0,0 +1,83 @@
+{
+  "history": {
+    "title": "Historique de facturation",
+    "recurring": "Récurrent",
+    "totalRevenue": "Revenu total",
+    "collected": "Encaissé",
+    "outstanding": "En attente",
+    "statusAll": "Tous",
+    "statusPaid": "Payé",
+    "statusPartial": "Partiel",
+    "statusUnpaid": "Impayé",
+    "searchPlaceholder": "Rechercher des factures...",
+    "search": "Rechercher",
+    "columnInvoice": "Facture N°",
+    "columnTitle": "Titre",
+    "columnVehicle": "Véhicule",
+    "columnCustomer": "Client",
+    "columnDate": "Date",
+    "columnTotal": "Total",
+    "columnPaid": "Payé",
+    "columnBalance": "Solde",
+    "noRecords": "Aucun enregistrement de facturation trouvé."
+  },
+  "recurring": {
+    "title": "Récurrent",
+    "billingHistory": "Historique de facturation",
+    "processNow": "Traiter maintenant",
+    "newRecurring": "Nouvelle facture récurrente",
+    "noInvoices": "Aucune facture récurrente pour le moment.",
+    "columnTitle": "Titre",
+    "columnVehicle": "Véhicule",
+    "columnCustomer": "Client",
+    "columnFrequency": "Fréquence",
+    "columnNextRun": "Prochaine exécution",
+    "columnAmount": "Montant",
+    "columnStatus": "Statut",
+    "columnRuns": "Exécutions",
+    "statusActive": "Actif",
+    "statusPaused": "En pause",
+    "frequencyWeekly": "Hebdomadaire",
+    "frequencyBiweekly": "Bihebdomadaire",
+    "frequencyMonthly": "Mensuel",
+    "frequencyQuarterly": "Trimestriel",
+    "frequencyYearly": "Annuel",
+    "serviceTypeMaintenance": "Entretien",
+    "serviceTypeRepair": "Réparation",
+    "serviceTypeInspection": "Inspection",
+    "serviceTypeUpgrade": "Mise à niveau",
+    "dialogTitle": "Nouvelle facture récurrente",
+    "titleLabel": "Titre *",
+    "titlePlaceholder": "p. ex. Vidange mensuelle",
+    "vehicleLabel": "Véhicule *",
+    "selectVehicle": "Sélectionner un véhicule",
+    "descriptionLabel": "Description",
+    "frequencyLabel": "Fréquence",
+    "startDate": "Date de début *",
+    "endDate": "Date de fin",
+    "serviceType": "Type de service",
+    "baseCost": "Coût de base",
+    "taxRate": "Taux de taxe (%)",
+    "invoiceNotes": "Notes de facture",
+    "templateParts": "Pièces du modèle",
+    "addPart": "Ajouter une pièce",
+    "partName": "Nom de la pièce",
+    "partNumber": "Pièce N°",
+    "templateLabor": "Main-d'œuvre du modèle",
+    "addLabor": "Ajouter de la main-d'œuvre",
+    "description": "Description",
+    "hours": "Heures",
+    "rate": "Tarif",
+    "cancel": "Annuler",
+    "create": "Créer",
+    "requiredFields": "Le titre et le véhicule sont obligatoires",
+    "created": "Facture récurrente créée",
+    "failedCreate": "Échec de la création",
+    "statusUpdated": "Statut mis à jour",
+    "failedUpdate": "Échec de la mise à jour",
+    "deleted": "Facture récurrente supprimée",
+    "failedDelete": "Échec de la suppression",
+    "processed": "{count} facture(s) traitée(s)",
+    "failedProcess": "Échec du traitement"
+  }
+}

+ 61 - 0
messages/fr/calendar.json

@@ -0,0 +1,61 @@
+{
+  "weekdays": {
+    "sun": "Dim",
+    "mon": "Lun",
+    "tue": "Mar",
+    "wed": "Mer",
+    "thu": "Jeu",
+    "fri": "Ven",
+    "sat": "Sam"
+  },
+  "months": {
+    "january": "Janvier",
+    "february": "F\u00e9vrier",
+    "march": "Mars",
+    "april": "Avril",
+    "may": "Mai",
+    "june": "Juin",
+    "july": "Juillet",
+    "august": "Ao\u00fbt",
+    "september": "Septembre",
+    "october": "Octobre",
+    "november": "Novembre",
+    "december": "D\u00e9cembre"
+  },
+  "stats": {
+    "services": "Services",
+    "overdue": "En retard",
+    "pendingQuotes": "Devis en attente",
+    "revenue": "Chiffre d\u2019affaires"
+  },
+  "today": "Aujourd\u2019hui",
+  "workOrder": "Ordre de travail",
+  "filters": {
+    "services": "Services",
+    "reminders": "Rappels",
+    "quotes": "Devis"
+  },
+  "selectVehicle": "S\u00e9lectionner un v\u00e9hicule pour l\u2019ordre de travail",
+  "more": "+ {count} de plus",
+  "events": {
+    "status": {
+      "completed": "Termin\u00e9",
+      "inProgress": "En cours",
+      "waitingParts": "En attente de pi\u00e8ces",
+      "pending": "En attente",
+      "sent": "Envoy\u00e9",
+      "approved": "Approuv\u00e9",
+      "draft": "Brouillon",
+      "done": "Fait",
+      "overdue": "En retard",
+      "upcoming": "\u00c0 venir"
+    },
+    "type": {
+      "service": "Service",
+      "reminder": "Rappel",
+      "quote": "Devis"
+    },
+    "noEvents": "Aucun \u00e9v\u00e9nement",
+    "selectDay": "S\u00e9lectionnez un jour avec des \u00e9v\u00e9nements ou cr\u00e9ez un nouvel ordre de travail."
+  }
+}

+ 43 - 0
messages/fr/common.json

@@ -0,0 +1,43 @@
+{
+  "brandName": "Torqvoice",
+  "buttons": {
+    "signIn": "Se connecter",
+    "save": "Enregistrer",
+    "cancel": "Annuler",
+    "delete": "Supprimer",
+    "submit": "Soumettre",
+    "back": "Retour",
+    "confirm": "Confirmer",
+    "saveChanges": "Enregistrer les modifications",
+    "edit": "Modifier",
+    "add": "Ajouter"
+  },
+  "form": {
+    "email": "Adresse e-mail",
+    "password": "Mot de passe",
+    "emailPlaceholder": "you@example.com",
+    "name": "Nom",
+    "phone": "Téléphone",
+    "company": "Entreprise",
+    "address": "Adresse",
+    "notes": "Notes"
+  },
+  "errors": {
+    "unexpected": "Une erreur inattendue s'est produite. Veuillez réessayer.",
+    "error": "Erreur"
+  },
+  "terms": {
+    "termsOfService": "Conditions d'utilisation"
+  },
+  "errorPage": {
+    "title": "Quelque chose s'est mal passé",
+    "description": "Le moteur a calé de manière inattendue. Nos mécaniciens ont été prévenus et travaillent dessus.",
+    "tryAgain": "Réessayer",
+    "backToDashboard": "Retour au tableau de bord"
+  },
+  "notFoundPage": {
+    "title": "Page introuvable",
+    "description": "On dirait que cette pièce est encore à l'atelier. La page que vous recherchez n'existe pas ou a été déplacée.",
+    "backToDashboard": "Retour au tableau de bord"
+  }
+}

+ 76 - 0
messages/fr/customers.json

@@ -0,0 +1,76 @@
+{
+  "list": {
+    "addCustomer": "Ajouter un client",
+    "searchPlaceholder": "Rechercher des clients...",
+    "table": {
+      "name": "Nom",
+      "company": "Entreprise",
+      "phone": "Téléphone",
+      "email": "E-mail",
+      "vehicles": "Véhicules"
+    },
+    "emptySearch": "Aucun client ne correspond à votre recherche.",
+    "empty": "Aucun client pour le moment.",
+    "deleteTitle": "Supprimer le client",
+    "deleteDescription": "Supprimer {name} ? Cela dissociera ses véhicules.",
+    "deleteError": "Échec de la suppression du client",
+    "error": "Échec du chargement des clients"
+  },
+  "detail": {
+    "backToCustomers": "Retour aux clients",
+    "editCustomer": "Modifier le client",
+    "notFound": "Client introuvable",
+    "emailCopied": "E-mail copié dans le presse-papiers",
+    "phoneCopied": "Numéro de téléphone copié dans le presse-papiers",
+    "tabs": {
+      "vehicles": "Véhicules ({count})",
+      "messages": "Messages",
+      "requests": "Demandes ({count})"
+    },
+    "vehicleTable": {
+      "plate": "Plaque",
+      "vehicle": "Véhicule",
+      "mileage": "Kilométrage",
+      "services": "Services"
+    },
+    "noVehicles": "Aucun véhicule associé à ce client"
+  },
+  "form": {
+    "addTitle": "Ajouter un nouveau client",
+    "editTitle": "Modifier le client",
+    "nameRequired": "Nom *",
+    "namePlaceholder": "Jean Dupont",
+    "emailPlaceholder": "jean@exemple.fr",
+    "phonePlaceholder": "01 23 45 67 89",
+    "companyPlaceholder": "Nom de l'entreprise",
+    "addressPlaceholder": "12 rue de la Paix, 75001 Paris",
+    "notesPlaceholder": "Notes supplémentaires...",
+    "customerUpdated": "Client mis à jour",
+    "customerCreated": "Client créé",
+    "saveError": "Échec de l'enregistrement du client"
+  },
+  "serviceRequests": {
+    "statusNew": "Nouveau",
+    "statusConverted": "Ordre de travail créé",
+    "statusDismissed": "Rejeté",
+    "createWorkOrder": "Créer un ordre de travail",
+    "contactCustomer": "Contacter le client",
+    "dismiss": "Rejeter",
+    "viewWorkOrder": "Voir l'ordre de travail",
+    "notes": "Notes",
+    "hideNotes": "Masquer les notes",
+    "staffNotes": "Notes du personnel :",
+    "notesPlaceholder": "Ajouter des notes internes...",
+    "saveNotes": "Enregistrer les notes",
+    "submitted": "Soumis le {date}",
+    "preferred": "Préféré : {date}",
+    "workOrderCreated": "Ordre de travail créé",
+    "workOrderError": "Échec de la création de l'ordre de travail",
+    "requestDismissed": "Demande rejetée",
+    "dismissError": "Échec du rejet de la demande",
+    "notesSaved": "Notes enregistrées",
+    "notesSaveError": "Échec de l'enregistrement des notes",
+    "smsLabel": "Message de demande de service",
+    "smsDefault": "Bonjour {name}, concernant votre demande de service pour votre {vehicle} : "
+  }
+}

+ 89 - 0
messages/fr/dashboard.json

@@ -0,0 +1,89 @@
+{
+  "stats": {
+    "activeJobs": "Travaux en cours",
+    "pending": "En attente",
+    "todaysRevenue": "Revenus du jour",
+    "totalCustomers": "Total des clients"
+  },
+  "customize": "Personnaliser",
+  "showCards": "Afficher les cartes",
+  "cards": {
+    "maintenance": "Maintenance pr\u00e9vue",
+    "reminders": "Rappels \u00e0 venir",
+    "inspections": "Inspections",
+    "quoteRequests": "Demandes de devis",
+    "quoteResponses": "R\u00e9ponses aux devis",
+    "sms": "Messages r\u00e9cents",
+    "notifications": "Notifications r\u00e9centes",
+    "recentCompleted": "Termin\u00e9s r\u00e9cemment",
+    "activeJobs": "Travaux en cours"
+  },
+  "maintenance": {
+    "title": "Maintenance pr\u00e9vue \u00e0 effectuer",
+    "description": "Bas\u00e9 sur l\u2019historique d\u2019entretien et le kilom\u00e9trage quotidien estim\u00e9",
+    "overdue": "En retard",
+    "approaching": "Imminent",
+    "estimated": "Est. {mileage} {unit}",
+    "sinceLastService": "{mileage} {unit} depuis le dernier entretien",
+    "certainty": "{percent}\u00a0% de certitude"
+  },
+  "reminders": {
+    "title": "Rappels \u00e0 venir",
+    "overdue": "En retard",
+    "dueSoon": "Bient\u00f4t d\u00fb"
+  },
+  "messages": {
+    "title": "Messages r\u00e9cents",
+    "viewAll": "Tout afficher",
+    "you": "Vous\u00a0: "
+  },
+  "notifications": {
+    "title": "Notifications r\u00e9centes"
+  },
+  "inspections": {
+    "title": "Inspections",
+    "inProgress": "En cours",
+    "completed": "Termin\u00e9",
+    "itemCount": "{inspected}/{total} \u00e9l\u00e9ments"
+  },
+  "quoteRequests": {
+    "title": "Demandes de devis",
+    "description": "Clients demandant des devis \u00e0 partir d\u2019inspections partag\u00e9es",
+    "pending": "En attente",
+    "createQuote": "Cr\u00e9er un devis",
+    "itemsRequested": "{count, plural, one {# \u00e9l\u00e9ment demand\u00e9} other {# \u00e9l\u00e9ments demand\u00e9s}}"
+  },
+  "quoteResponses": {
+    "title": "R\u00e9ponses des clients aux devis",
+    "description": "Devis que les clients ont accept\u00e9s ou pour lesquels des modifications ont \u00e9t\u00e9 demand\u00e9es",
+    "accepted": "Accept\u00e9",
+    "changesRequested": "Modifications demand\u00e9es",
+    "convertToWorkOrder": "Convertir en ordre de travail",
+    "viewQuote": "Voir le devis"
+  },
+  "recentCompleted": {
+    "title": "Termin\u00e9s r\u00e9cemment",
+    "date": "Date",
+    "vehicle": "V\u00e9hicule",
+    "customer": "Client",
+    "serviceTitle": "Titre",
+    "total": "Total",
+    "empty": "Aucun entretien termin\u00e9 pour le moment"
+  },
+  "activeJobsTable": {
+    "title": "Travaux en cours",
+    "vehicle": "V\u00e9hicule",
+    "customer": "Client",
+    "serviceTitle": "Titre",
+    "status": "Statut",
+    "tech": "Tech.",
+    "empty": "Aucun travail en cours"
+  },
+  "relativeTime": {
+    "now": "maintenant",
+    "minutes": "{count}\u00a0min",
+    "hours": "{count}\u00a0h",
+    "days": "{count}\u00a0j"
+  },
+  "error": "\u00c9chec du chargement du tableau de bord"
+}

+ 87 - 0
messages/fr/inventory.json

@@ -0,0 +1,87 @@
+{
+  "searchPlaceholder": "Rechercher des pièces...",
+  "categoryPlaceholder": "Catégorie",
+  "allCategories": "Toutes les catégories",
+  "applyMarkup": "Appliquer la marge",
+  "addPart": "Ajouter une pièce",
+  "table": {
+    "partNumber": "N° pièce",
+    "name": "Nom",
+    "category": "Catégorie",
+    "inStock": "En stock",
+    "unitCost": "Coût unitaire",
+    "sellPrice": "Prix de vente",
+    "supplier": "Fournisseur",
+    "location": "Emplacement",
+    "low": "Bas"
+  },
+  "empty": {
+    "noMatch": "Aucune pièce ne correspond à votre recherche.",
+    "noParts": "Aucune pièce dans l'inventaire pour l'instant."
+  },
+  "actions": {
+    "edit": "Modifier",
+    "delete": "Supprimer"
+  },
+  "deletePart": {
+    "title": "Supprimer la pièce",
+    "description": "Supprimer \"{name}\" de l'inventaire ? Cette action est irréversible.",
+    "confirm": "Supprimer"
+  },
+  "markup": {
+    "title": "Appliquer la marge à toutes les pièces",
+    "multiplierLabel": "Multiplicateur de marge",
+    "description": "Le prix de vente sera défini à coût x {multiplier} pour les {count} pièces.",
+    "percentage": "({percent}% de marge)",
+    "cancel": "Annuler",
+    "apply": "Appliquer"
+  },
+  "errors": {
+    "multiplierPositive": "Le multiplicateur doit être supérieur à 0",
+    "deleteFailed": "Impossible de supprimer la pièce",
+    "applyFailed": "Impossible d'appliquer la marge",
+    "saveFailed": "Impossible d'enregistrer la pièce",
+    "error": "Erreur"
+  },
+  "form": {
+    "editPart": "Modifier la pièce",
+    "addNewPart": "Ajouter une nouvelle pièce",
+    "supplierLink": "Lien fournisseur",
+    "supplierLinkPlaceholder": "https://supplier.com/product/...",
+    "fetch": "Récupérer",
+    "fetchFailed": "Échec de la récupération",
+    "fetchError": "Impossible de récupérer les métadonnées de l'URL",
+    "image": "Image",
+    "dropOrClick": "Déposez une image ou cliquez",
+    "replace": "Remplacer",
+    "upload": "Télécharger",
+    "imageUploading": "Téléchargement de l'image...",
+    "imageUploaded": "Image téléchargée",
+    "imageUploadFailed": "Impossible de télécharger l'image",
+    "imageTypeError": "Seules les images JPEG, PNG, WebP et AVIF sont autorisées",
+    "imageSizeError": "L'image doit faire moins de 5 Mo",
+    "partNumber": "Numéro de pièce",
+    "partNumberPlaceholder": "BRK-001",
+    "nameLabel": "Nom *",
+    "namePlaceholder": "Jeu de plaquettes de frein",
+    "category": "Catégorie",
+    "categoryPlaceholder": "Freins",
+    "location": "Emplacement",
+    "locationPlaceholder": "Étagère A-3",
+    "quantity": "Quantité",
+    "minQty": "Qté min.",
+    "unitCost": "Coût unitaire",
+    "sellPrice": "Prix de vente",
+    "supplier": "Fournisseur",
+    "supplierPlaceholder": "AutoParts SARL",
+    "supplierPhone": "Téléphone fournisseur",
+    "supplierPhonePlaceholder": "(555) 123-4567",
+    "supplierEmail": "E-mail fournisseur",
+    "supplierEmailPlaceholder": "orders@autoparts.com",
+    "description": "Description",
+    "descriptionPlaceholder": "Description de la pièce...",
+    "cancel": "Annuler",
+    "saveChanges": "Enregistrer les modifications",
+    "addPart": "Ajouter une pièce"
+  }
+}

+ 45 - 0
messages/fr/messages.json

@@ -0,0 +1,45 @@
+{
+  "page": {
+    "featureName": "Messagerie SMS",
+    "featureDescription": "Envoyez et recevez des SMS avec vos clients directement depuis Torqvoice."
+  },
+  "notConfigured": {
+    "title": "SMS non configuré",
+    "description": "Configurez un fournisseur SMS pour envoyer et recevoir des messages avec vos clients. Compatible avec Twilio, Vonage et Telnyx.",
+    "goToSettings": "Aller aux paramètres SMS"
+  },
+  "threads": {
+    "title": "Conversations",
+    "empty": "Aucune conversation pour le moment. Envoyez un SMS depuis la page d'un client pour commencer.",
+    "loadMore": "Charger plus",
+    "selectConversation": "Sélectionnez une conversation",
+    "you": "Vous : ",
+    "deleteConversation": "Supprimer la conversation",
+    "deleteTitle": "Supprimer la conversation ?",
+    "deleteDescription": "Tous les messages avec {name} seront définitivement supprimés. Cette action est irréversible.",
+    "deleted": "Conversation supprimée",
+    "deleteError": "Impossible de supprimer la conversation"
+  },
+  "conversation": {
+    "noPhone": "Aucun numéro de téléphone enregistré pour {name}. Ajoutez un numéro de téléphone pour commencer à envoyer des messages.",
+    "loadOlder": "Charger les anciens",
+    "empty": "Aucun message pour le moment. Envoyez le premier message à {name}.",
+    "placeholder": "Message à {name}...",
+    "deleteMessage": "Supprimer le message",
+    "deleteTitle": "Supprimer le message ?",
+    "deleteDescription": "Ce message sera définitivement supprimé de la conversation. Cette action est irréversible.",
+    "messageDeleted": "Message supprimé",
+    "deleteError": "Impossible de supprimer le message",
+    "sendError": "Impossible d'envoyer le SMS"
+  },
+  "sendDialog": {
+    "send": "Envoyer {label}",
+    "message": "Message",
+    "placeholder": "Tapez votre message...",
+    "characters": "{count} / 1600 caractères",
+    "sendSms": "Envoyer le SMS",
+    "smsSent": "SMS envoyé",
+    "sentTo": "{label} a été envoyé à {name} ({phone})",
+    "sendError": "Impossible d'envoyer le SMS"
+  }
+}

+ 85 - 0
messages/fr/navigation.json

@@ -0,0 +1,85 @@
+{
+  "breadcrumbs": {
+    "dashboard": "Tableau de bord",
+    "vehicles": "V\u00e9hicules",
+    "allVehicles": "Tous les v\u00e9hicules",
+    "vehicleDetails": "D\u00e9tails du v\u00e9hicule",
+    "customers": "Clients",
+    "allCustomers": "Tous les clients",
+    "customerDetails": "D\u00e9tails du client",
+    "workOrders": "Ordres de travail",
+    "allWorkOrders": "Tous les ordres de travail",
+    "quotes": "Devis",
+    "allQuotes": "Tous les devis",
+    "newQuote": "Nouveau devis",
+    "quoteDetails": "D\u00e9tails du devis",
+    "edit": "Modifier",
+    "billing": "Facturation",
+    "billingHistory": "Historique de facturation",
+    "inventory": "Inventaire",
+    "allParts": "Toutes les pi\u00e8ces",
+    "reports": "Rapports",
+    "workBoard": "Tableau de travail",
+    "presenter": "Pr\u00e9sentateur",
+    "adminOverview": "Vue d\u2019ensemble admin",
+    "admin": "Administration",
+    "users": "Utilisateurs",
+    "organizations": "Organisations",
+    "settings": "Param\u00e8tres",
+    "company": "Entreprise",
+    "account": "Compte",
+    "customFields": "Champs personnalis\u00e9s",
+    "templates": "Mod\u00e8les",
+    "team": "\u00c9quipe",
+    "invoice": "Facture",
+    "payment": "Paiement",
+    "currency": "Devise",
+    "workshop": "Atelier",
+    "appearance": "Apparence",
+    "email": "E-mail",
+    "sms": "SMS",
+    "about": "\u00c0 propos",
+    "home": "Accueil",
+    "newServiceRecord": "Nouvel entretien",
+    "serviceDetails": "D\u00e9tails de l\u2019entretien",
+    "data": "Donn\u00e9es",
+    "license": "Licence",
+    "subscription": "Abonnement",
+    "maintenance": "Maintenance",
+    "customerPortal": "Portail client"
+  },
+  "search": "Rechercher\u2026",
+  "shortcut": "Ctrl+K",
+  "newWorkOrder": "Nouvel ordre de travail",
+  "purchaseWhiteLabel": "Acheter la marque blanche",
+  "sidebar": {
+    "dashboard": "Tableau de bord",
+    "clients": "Clients",
+    "customers": "Clients",
+    "messages": "Messages",
+    "workshop": "Atelier",
+    "vehicles": "Véhicules",
+    "workOrders": "Ordres de travail",
+    "inspections": "Inspections",
+    "calendar": "Calendrier",
+    "workBoard": "Tableau de travail",
+    "business": "Activité",
+    "quotes": "Devis",
+    "billing": "Facturation",
+    "inventory": "Inventaire",
+    "reports": "Rapports",
+    "settings": "Paramètres",
+    "superAdmin": "Super Admin",
+    "adminPanel": "Panneau d'administration",
+    "organizations": "Organisations",
+    "noOrganization": "Aucune organisation",
+    "addNewCompany": "Ajouter une nouvelle entreprise",
+    "companyName": "Nom de l'entreprise",
+    "companyPlaceholder": "ex. Garage Auto Dupont",
+    "cancel": "Annuler",
+    "createCompany": "Créer l'entreprise",
+    "lightMode": "Mode clair",
+    "darkMode": "Mode sombre",
+    "signOut": "Se déconnecter"
+  }
+}

+ 10 - 0
messages/fr/notifications.json

@@ -0,0 +1,10 @@
+{
+  "panel": {
+    "title": "Notifications",
+    "markAllRead": "Tout marquer comme lu",
+    "empty": "Aucune notification pour le moment",
+    "confirmDelete": "Êtes-vous sûr ?",
+    "delete": "Supprimer",
+    "cancel": "Annuler"
+  }
+}

+ 99 - 0
messages/fr/pdf.json

@@ -0,0 +1,99 @@
+{
+  "invoice": {
+    "title": "FACTURE",
+    "due": "Échéance : {date}",
+    "billTo": "Facturer à",
+    "vehicle": "Véhicule",
+    "vin": "VIN : {vin}",
+    "plate": "Plaque : {plate}",
+    "mileage": "Kilométrage : {mileage}",
+    "km": "km",
+    "mi": "mi",
+    "service": "Service",
+    "type": "Type : {type}",
+    "tech": "Technicien : {tech}",
+    "parts": "Pièces",
+    "partNumber": "N° de pièce",
+    "description": "Description",
+    "qty": "Qté",
+    "unitPrice": "Prix unitaire",
+    "total": "Total",
+    "labor": "Main-d'œuvre",
+    "hours": "Heures",
+    "rate": "Taux",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "discountPercent": "Remise ({percent}%)",
+    "tax": "Taxe ({rate}%)",
+    "paymentsReceived": "Paiements reçus",
+    "paidInFull": "ENTIÈREMENT PAYÉ",
+    "amountDue": "Montant dû",
+    "notes": "Notes",
+    "bankAccount": "Til Konto / Compte bancaire",
+    "diagnosticNotes": "Notes de diagnostic",
+    "attachedDocuments": "Documents joints",
+    "seeAppendedPages": "{name} (voir pages annexées)",
+    "serviceImages": "Images de service",
+    "paymentTerms": "Conditions de paiement : {terms}",
+    "viewPortal": "Consultez votre portail : {url}",
+    "professionalWorkshop": "Gestion professionnelle d'atelier",
+    "tel": "Tél. : {phone}",
+    "org": "Org. : {org}"
+  },
+  "quote": {
+    "title": "DEVIS",
+    "to": "À",
+    "vehicle": "Véhicule",
+    "vin": "VIN : {vin}",
+    "plate": "Plaque : {plate}",
+    "quoteDetails": "Détails du devis",
+    "parts": "Pièces",
+    "partNumber": "N° de pièce",
+    "description": "Description",
+    "qty": "Qté",
+    "unitPrice": "Prix unitaire",
+    "total": "Total",
+    "labor": "Main-d'œuvre",
+    "hours": "Heures",
+    "rate": "Taux",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "discountPercent": "Remise ({percent}%)",
+    "tax": "Taxe ({rate}%)",
+    "validUntil": "Valable jusqu'au : {date}",
+    "attachedDocuments": "Documents joints",
+    "attached": "{name} (joint)",
+    "quoteImages": "Images du devis",
+    "validityFooterUntil": "Ce devis est valable jusqu'au {date}",
+    "validityFooter30": "Ce devis est valable 30 jours",
+    "viewPortal": "Consultez votre portail : {url}",
+    "tel": "Tél. : {phone}"
+  },
+  "inspection": {
+    "title": "INSPECTION DU VÉHICULE",
+    "vehicle": "Véhicule",
+    "vin": "VIN : {vin}",
+    "plate": "Plaque : {plate}",
+    "mileage": "Kilométrage : {mileage}",
+    "customer": "Client",
+    "inspectionDetails": "Détails de l'inspection",
+    "statusCompleted": "Terminé",
+    "statusInProgress": "En cours",
+    "status": "Statut : {status}",
+    "inspected": "Inspecté",
+    "pass": "Conforme",
+    "attention": "Attention",
+    "fail": "Non conforme",
+    "item": "Élément",
+    "statusColumn": "Statut",
+    "notesColumn": "Notes",
+    "footerText": "Inspection du véhicule — {shopName}",
+    "viewPortal": "Consultez votre portail : {url}",
+    "tel": "Tél. : {phone}"
+  },
+  "common": {
+    "poweredBy": "Propulsé par"
+  }
+}

+ 133 - 0
messages/fr/portal.json

@@ -0,0 +1,133 @@
+{
+  "login": {
+    "signInTitle": "Connexion à votre portail client",
+    "emailLabel": "Adresse e-mail",
+    "emailPlaceholder": "vous@exemple.com",
+    "sendLink": "Envoyer le lien de connexion",
+    "checkEmail": "Vérifiez votre e-mail",
+    "checkEmailDescription": "Si un compte existe pour cet e-mail, nous avons envoyé un lien de connexion. Le lien expire dans 15 minutes.",
+    "tryDifferentEmail": "Essayer une autre adresse e-mail",
+    "notFound": "Introuvable",
+    "portalNotExist": "Ce portail n'existe pas.",
+    "portalNotAvailable": "Portail indisponible",
+    "portalNotEnabled": "Le portail client n'est pas activé pour cette organisation."
+  },
+  "nav": {
+    "dashboard": "Tableau de bord",
+    "vehicles": "Véhicules",
+    "invoices": "Factures",
+    "quotes": "Devis",
+    "inspections": "Inspections",
+    "requestService": "Demander un service"
+  },
+  "header": {
+    "signOut": "Se déconnecter"
+  },
+  "shell": {
+    "notFound": "Introuvable",
+    "portalNotExist": "Ce portail n'existe pas.",
+    "portalNotAvailable": "Portail indisponible",
+    "portalNotEnabled": "Le portail client n'est pas activé pour cette organisation."
+  },
+  "dashboard": {
+    "welcome": "Bienvenue, {name}",
+    "overview": "Voici un aperçu de votre compte.",
+    "vehicles": "Véhicules",
+    "openInvoices": "Factures ouvertes",
+    "pendingQuotes": "Devis en attente",
+    "recentInvoices": "Factures récentes",
+    "recentQuotes": "Devis récents",
+    "pendingServiceRequests": "Demandes de service en attente",
+    "viewAll": "Tout afficher",
+    "newRequest": "Nouvelle demande",
+    "noInvoicesYet": "Aucune facture pour l'instant.",
+    "noQuotesYet": "Aucun devis pour l'instant.",
+    "view": "Voir",
+    "pending": "En attente",
+    "failedToLoad": "Échec du chargement du tableau de bord."
+  },
+  "invoices": {
+    "title": "Factures",
+    "description": "Toutes vos factures de service.",
+    "noInvoices": "Aucune facture pour l'instant.",
+    "failedToLoad": "Échec du chargement des factures.",
+    "invoice": "Facture",
+    "vehicle": "Véhicule",
+    "date": "Date",
+    "amount": "Montant",
+    "payment": "Paiement",
+    "view": "Voir",
+    "paid": "payé",
+    "partial": "partiel",
+    "unpaid": "impayé"
+  },
+  "quotes": {
+    "title": "Devis",
+    "description": "Devis et estimations pour vos véhicules.",
+    "noQuotes": "Aucun devis pour l'instant.",
+    "failedToLoad": "Échec du chargement des devis.",
+    "quote": "Devis",
+    "vehicle": "Véhicule",
+    "status": "Statut",
+    "amount": "Montant",
+    "validUntil": "Valable jusqu'au",
+    "view": "Voir"
+  },
+  "inspections": {
+    "title": "Inspections",
+    "description": "Rapports d'inspection des véhicules.",
+    "noInspections": "Aucune inspection pour l'instant.",
+    "failedToLoad": "Échec du chargement des inspections.",
+    "vehicle": "Véhicule",
+    "template": "Modèle",
+    "status": "Statut",
+    "date": "Date",
+    "summary": "Résumé",
+    "view": "Voir",
+    "good": "{count} bon(s)",
+    "fair": "{count} passable(s)",
+    "poor": "{count} mauvais"
+  },
+  "vehicles": {
+    "title": "Véhicules",
+    "description": "Vos véhicules enregistrés.",
+    "noVehicles": "Aucun véhicule trouvé.",
+    "failedToLoad": "Échec du chargement des véhicules.",
+    "plate": "Plaque : {plate}",
+    "vin": "VIN : {vin}",
+    "serviceCount": "{count, plural, one {# service} other {# services}}",
+    "inspectionCount": "{count, plural, one {# inspection} other {# inspections}}",
+    "serviceHistory": "Historique des services ({count})",
+    "inspectionsTab": "Inspections ({count})",
+    "noServiceRecords": "Aucun dossier de service pour l'instant.",
+    "noInspections": "Aucune inspection pour l'instant.",
+    "vehicleNotFound": "Véhicule introuvable.",
+    "completed": "Terminé le {date}",
+    "good": "{count} bon(s)",
+    "fair": "{count} passable(s)",
+    "poor": "{count} mauvais",
+    "view": "Voir"
+  },
+  "requestService": {
+    "title": "Demander un service",
+    "description": "Soumettez une demande de service pour l'un de vos véhicules.",
+    "newServiceRequest": "Nouvelle demande de service",
+    "formDescription": "Décrivez le problème et nous vous répondrons.",
+    "vehicleLabel": "Véhicule",
+    "vehiclePlaceholder": "Sélectionner un véhicule",
+    "descriptionLabel": "Décrire le problème",
+    "descriptionPlaceholder": "Qu'est-ce qui se passe avec votre véhicule ?",
+    "preferredDate": "Date souhaitée",
+    "preferredDateOptional": "(optionnel)",
+    "submitRequest": "Soumettre la demande",
+    "selectVehicle": "Veuillez sélectionner un véhicule",
+    "describeIssue": "Veuillez décrire le problème",
+    "submitSuccess": "Demande de service soumise",
+    "submitError": "Échec de la soumission de la demande",
+    "noVehicles": "Vous n'avez aucun véhicule enregistré. Veuillez nous contacter pour en ajouter un d'abord.",
+    "yourRequests": "Vos demandes de service",
+    "failedToLoad": "Échec du chargement des données.",
+    "staffNotes": "Notes du personnel : {notes}",
+    "preferred": "Souhaité : {date}"
+  }
+}

+ 173 - 0
messages/fr/quotes.json

@@ -0,0 +1,173 @@
+{
+  "list": {
+    "statusAll": "Tous",
+    "statusDraft": "Brouillon",
+    "statusSent": "Envoyé",
+    "statusAccepted": "Accepté",
+    "statusRejected": "Rejeté",
+    "statusConverted": "Converti",
+    "searchPlaceholder": "Rechercher des devis...",
+    "newQuote": "Nouveau devis",
+    "columnQuoteNumber": "Devis N°",
+    "columnTitle": "Titre",
+    "columnCustomer": "Client",
+    "columnVehicle": "Véhicule",
+    "columnStatus": "Statut",
+    "columnDate": "Date",
+    "columnTotal": "Total",
+    "noQuotes": "Aucun devis trouvé."
+  },
+  "page": {
+    "quote": "Devis",
+    "save": "Enregistrer",
+    "pdf": "PDF",
+    "email": "E-mail",
+    "share": "Partager",
+    "delete": "Supprimer",
+    "deleteTitle": "Supprimer le devis",
+    "deleteDescription": "Ce devis sera définitivement supprimé.",
+    "saved": "Devis enregistré",
+    "failedSave": "Échec de l'enregistrement du devis",
+    "failedDelete": "Échec de la suppression",
+    "failedPdf": "Échec de la génération du PDF",
+    "selectVehicle": "Veuillez sélectionner un véhicule",
+    "convertTitle": "Convertir le devis en ordre de travail",
+    "convertDescription": "Un nouveau dossier de service (ordre de travail) sera créé à partir de ce devis. Sélectionnez un véhicule pour l'ordre de travail.",
+    "convert": "Convertir",
+    "convertToWorkOrder": "Convertir en ordre de travail",
+    "cancel": "Annuler",
+    "failedConvert": "Échec de la conversion",
+    "changesRequested": "Modifications demandées",
+    "quoteAccepted": "Devis accepté",
+    "markResolved": "Marquer comme résolu",
+    "responseResolved": "Réponse du client résolue",
+    "entityLabel": "Devis",
+    "tabs": {
+      "details": "Détails",
+      "images": "Images",
+      "documents": "Documents"
+    }
+  },
+  "form": {
+    "newQuote": "Nouveau devis",
+    "editQuote": "Modifier le devis",
+    "cancel": "Annuler",
+    "createQuote": "Créer le devis",
+    "updateQuote": "Mettre à jour le devis",
+    "created": "Devis créé",
+    "updated": "Devis mis à jour",
+    "failedCreate": "Échec de la création du devis",
+    "failedUpdate": "Échec de la mise à jour du devis"
+  },
+  "parts": {
+    "title": "Pièces",
+    "addPart": "Ajouter une pièce",
+    "partNumber": "N° de pièce",
+    "name": "Nom",
+    "namePlaceholder": "Nom *",
+    "qty": "Qté",
+    "unitPrice": "Prix unitaire",
+    "total": "Total",
+    "subtotal": "Sous-total pièces : {amount}"
+  },
+  "labor": {
+    "title": "Main-d'œuvre",
+    "addLabor": "Ajouter de la main-d'œuvre",
+    "description": "Description",
+    "descriptionPlaceholder": "Description *",
+    "hours": "Heures",
+    "rate": "Tarif ({currency}/h)",
+    "total": "Total",
+    "subtotal": "Sous-total main-d'œuvre : {amount}"
+  },
+  "notes": {
+    "title": "Notes",
+    "public": "Publique",
+    "internal": "Interne",
+    "publicPlaceholder": "Description du devis affichée au client...",
+    "internalPlaceholder": "Notes internes (non visibles par le client)...",
+    "publicHelper": "Affiché sur le document du devis",
+    "internalHelper": "Visible uniquement par votre équipe"
+  },
+  "details": {
+    "title": "Détails du devis",
+    "titleLabel": "Titre *",
+    "titlePlaceholder": "Estimation de réparation de véhicule",
+    "status": "Statut",
+    "statusDraft": "Brouillon",
+    "statusSent": "Envoyé",
+    "statusAccepted": "Accepté",
+    "statusRejected": "Rejeté",
+    "validUntil": "Valide jusqu'au",
+    "viewInspection": "Voir l'inspection liée",
+    "vehicle": "Véhicule",
+    "customer": "Client",
+    "selectVehicle": "Sélectionner un véhicule...",
+    "selectCustomer": "Sélectionner un client...",
+    "none": "Aucun"
+  },
+  "totals": {
+    "title": "Totaux",
+    "parts": "Pièces",
+    "labor": "Main-d'œuvre",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "discountNone": "Aucune",
+    "discountPercentage": "Pourcentage",
+    "discountFixed": "Fixe",
+    "tax": "Taxe",
+    "total": "Total"
+  },
+  "shareDialog": {
+    "title": "Partager le devis",
+    "description": "Générez un lien public permettant à quiconque de consulter et télécharger ce devis sans se connecter.",
+    "notifyTitle": "Notifier {name}",
+    "email": "E-mail",
+    "noEmail": "(aucun e-mail enregistré)",
+    "sms": "SMS",
+    "noPhone": "(aucun téléphone enregistré)",
+    "sendNotification": "Envoyer la notification",
+    "revokeLink": "Révoquer le lien",
+    "generateLink": "Générer un lien public",
+    "emailSent": "E-mail envoyé",
+    "failedEmail": "Échec de l'envoi de l'e-mail",
+    "smsSent": "SMS envoyé",
+    "failedSms": "Échec de l'envoi du SMS"
+  },
+  "images": {
+    "title": "Images du devis",
+    "dropzone": "Déposez les images ici ou cliquez pour parcourir",
+    "uploading": "Téléversement...",
+    "formats": "JPG, PNG, WebP — 10 Mo max. chacune",
+    "uploadingImages": "Téléversement des images...",
+    "limitReached": "Limite d'images atteinte ({count}/{max})",
+    "upgradePrompt": "Mettez à niveau votre plan pour téléverser plus d'images.",
+    "onlyAllowed": "Seules les images JPG, PNG et WebP sont autorisées. {names} ignorées.",
+    "onlyUploading": "Seules {count} image(s) seront téléversées pour respecter la limite.",
+    "uploadingCount": "Téléversement de {count} image(s)...",
+    "uploadedCount": "{count} image(s) téléversée(s)",
+    "uploadFailed": "Échec du téléversement",
+    "deleted": "Image supprimée",
+    "failedDelete": "Échec de la suppression de l'image",
+    "failedUpdate": "Échec de la mise à jour",
+    "description": "Description...",
+    "pdf": "PDF"
+  },
+  "documents": {
+    "title": "Documents",
+    "dropzone": "Déposez les fichiers ici ou cliquez pour parcourir",
+    "uploading": "Téléversement...",
+    "formats": "PDF, CSV, TXT — 10 Mo max. chacun",
+    "uploadingDocuments": "Téléversement des documents...",
+    "limitReached": "Limite de documents atteinte ({count}/{max})",
+    "upgradePrompt": "Mettez à niveau votre plan pour téléverser davantage.",
+    "onlyUploading": "Seuls {count} fichier(s) seront téléversés pour respecter la limite.",
+    "uploadingCount": "Téléversement de {count} fichier(s)...",
+    "uploadedCount": "{count} fichier(s) téléversé(s)",
+    "uploadFailed": "Échec du téléversement",
+    "deleted": "Fichier supprimé",
+    "failedDelete": "Échec de la suppression du fichier",
+    "failedUpdate": "Échec de la mise à jour",
+    "pdf": "PDF"
+  }
+}

+ 199 - 0
messages/fr/reports.json

@@ -0,0 +1,199 @@
+{
+  "title": "Rapports et Analyses",
+  "tabs": {
+    "revenue": "Revenus",
+    "services": "Services",
+    "customers": "Clients",
+    "inventory": "Inventaire",
+    "technicians": "Techniciens",
+    "parts": "Pièces",
+    "jobAnalytics": "Analyse des Travaux",
+    "retention": "Fidélisation",
+    "tax": "Taxes"
+  },
+  "dateRange": {
+    "to": "au"
+  },
+  "actions": {
+    "refresh": "Actualiser",
+    "csv": "CSV"
+  },
+  "revenue": {
+    "revenue": "Revenus",
+    "collected": "Encaissé",
+    "outstanding": "En attente",
+    "services": "Services",
+    "monthlyRevenue": "Revenus Mensuels",
+    "revenueByType": "Revenus par Type",
+    "monthlyBreakdown": "Répartition Mensuelle",
+    "tableHeaders": {
+      "month": "Mois",
+      "revenue": "Revenus",
+      "collected": "Encaissé",
+      "count": "Nombre"
+    }
+  },
+  "services": {
+    "totalServices": "Total des Services",
+    "byStatus": "Par Statut",
+    "byType": "Par Type"
+  },
+  "customers": {
+    "total": "Total",
+    "active": "Actifs",
+    "topCustomersBySpend": "Meilleurs Clients par Dépenses",
+    "topCustomers": "Meilleurs Clients",
+    "tableHeaders": {
+      "name": "Nom",
+      "company": "Entreprise",
+      "services": "Services",
+      "totalSpent": "Total Dépensé"
+    }
+  },
+  "inventory": {
+    "parts": "Pièces",
+    "items": "Articles",
+    "costValue": "Valeur Coût",
+    "sellValue": "Valeur de Vente",
+    "potentialMargin": "Marge Potentielle",
+    "lowStock": "Stock Bas",
+    "tableHeaders": {
+      "name": "Nom",
+      "partNumber": "N° Pièce",
+      "qty": "Qté",
+      "minQty": "Qté Min."
+    }
+  },
+  "technicians": {
+    "totalJobs": "Total Travaux",
+    "totalRevenue": "Revenus Totaux",
+    "revenueByTechnician": "Revenus par Technicien",
+    "technicianBreakdown": "Répartition par Technicien",
+    "tableHeaders": {
+      "technician": "Technicien",
+      "jobs": "Travaux",
+      "revenue": "Revenus",
+      "avgRevenue": "Revenus Moy.",
+      "totalHours": "Heures Totales",
+      "avgHours": "Heures Moy."
+    }
+  },
+  "parts": {
+    "partsUsed": "Pièces Utilisées",
+    "partsRevenue": "Revenus Pièces",
+    "topPartsByUsage": "Pièces les Plus Utilisées",
+    "partsUsage": "Utilisation des Pièces",
+    "tableHeaders": {
+      "partName": "Nom de Pièce",
+      "partNumber": "N° Pièce",
+      "usageCount": "Nombre d'Utilisations",
+      "totalQty": "Qté Totale",
+      "revenue": "Revenus"
+    }
+  },
+  "jobAnalytics": {
+    "totalJobs": "Total Travaux",
+    "avgJobValue": "Valeur Moy. du Travail",
+    "jobsByDayOfWeek": "Travaux par Jour de la Semaine",
+    "serviceTypes": "Types de Service",
+    "monthlyTrend": "Tendance Mensuelle",
+    "serviceTypeBreakdown": "Répartition par Type de Service",
+    "tableHeaders": {
+      "type": "Type",
+      "count": "Nombre",
+      "avgValue": "Valeur Moy.",
+      "avgHours": "Heures Moy."
+    }
+  },
+  "retention": {
+    "returning": "Récurrents",
+    "new": "Nouveaux",
+    "totalActive": "Total Actifs",
+    "avgDaysBetween": "Moy. Jours Entre",
+    "topReturningCustomers": "Meilleurs Clients Récurrents",
+    "returningCustomers": "Clients Récurrents",
+    "tableHeaders": {
+      "customer": "Client",
+      "company": "Entreprise",
+      "visits": "Visites",
+      "totalSpent": "Total Dépensé",
+      "avgDaysBetween": "Moy. Jours Entre"
+    }
+  },
+  "tax": {
+    "totalTaxCollected": "Total Taxes Perçues",
+    "taxableRevenue": "Revenus Taxables",
+    "invoicesWithTax": "Factures avec Taxe",
+    "monthlyTaxCollected": "Taxes Mensuelles Perçues",
+    "taxByRate": "Taxe par Taux",
+    "monthlyBreakdown": "Répartition Mensuelle",
+    "tableHeaders": {
+      "taxRate": "Taux de Taxe",
+      "taxCollected": "Taxe Perçue",
+      "invoices": "Factures",
+      "month": "Mois",
+      "taxableAmount": "Montant Taxable"
+    }
+  },
+  "charts": {
+    "revenue": "Revenus",
+    "collected": "Encaissé",
+    "count": "Nombre",
+    "totalSpent": "Total Dépensé",
+    "jobs": "Travaux",
+    "visits": "Visites",
+    "taxCollected": "Taxe Perçue"
+  },
+  "days": {
+    "sunday": "Dimanche",
+    "monday": "Lundi",
+    "tuesday": "Mardi",
+    "wednesday": "Mercredi",
+    "thursday": "Jeudi",
+    "friday": "Vendredi",
+    "saturday": "Samedi"
+  },
+  "daysShort": {
+    "sun": "Dim",
+    "mon": "Lun",
+    "tue": "Mar",
+    "wed": "Mer",
+    "thu": "Jeu",
+    "fri": "Ven",
+    "sat": "Sam"
+  },
+  "csvHeaders": {
+    "month": "Mois",
+    "revenue": "Revenus",
+    "collected": "Encaissé",
+    "count": "Nombre",
+    "category": "Catégorie",
+    "label": "Libellé",
+    "name": "Nom",
+    "company": "Entreprise",
+    "services": "Services",
+    "totalSpent": "Total Dépensé",
+    "partNumber": "N° Pièce",
+    "quantity": "Quantité",
+    "minQuantity": "Quantité Min.",
+    "unitCost": "Coût Unitaire",
+    "technician": "Technicien",
+    "jobs": "Travaux",
+    "totalRevenue": "Revenus Totaux",
+    "avgRevenue": "Revenus Moy.",
+    "totalHours": "Heures Totales",
+    "avgHours": "Heures Moy.",
+    "partName": "Nom de Pièce",
+    "usageCount": "Nombre d'Utilisations",
+    "totalQty": "Qté Totale",
+    "serviceType": "Type de Service",
+    "avgValue": "Valeur Moy.",
+    "customer": "Client",
+    "visits": "Visites",
+    "avgDaysBetweenVisits": "Moy. Jours Entre Visites",
+    "taxCollected": "Taxe Perçue",
+    "taxableAmount": "Montant Taxable",
+    "invoiceCount": "Nombre de Factures"
+  },
+  "empty": "Aucune donnée chargée. Cliquez sur Actualiser pour charger le rapport."
+}

+ 240 - 0
messages/fr/service.json

@@ -0,0 +1,240 @@
+{
+  "page": {
+    "notFound": "Dossier de service introuvable",
+    "updated": "Dossier de service mis à jour",
+    "failedUpdate": "Impossible de mettre à jour le dossier de service",
+    "deleteTitle": "Supprimer le Dossier de Service",
+    "deleteDescription": "Cela supprimera définitivement ce dossier de service et toutes les données associées. Cette action ne peut pas être annulée.",
+    "deleted": "Dossier de service supprimé",
+    "failedDelete": "Impossible de supprimer"
+  },
+  "header": {
+    "tabs": {
+      "details": "Détails",
+      "images": "Images",
+      "video": "Vidéo",
+      "documents": "Documents"
+    },
+    "save": "Enregistrer",
+    "pdf": "PDF",
+    "email": "Email",
+    "share": "Partager",
+    "delete": "Supprimer",
+    "unpaid": "Non Payé"
+  },
+  "basicInfo": {
+    "title": "Informations de Base",
+    "vehicle": "Véhicule",
+    "open": "Ouvert",
+    "customer": "Client",
+    "titleLabel": "Titre *",
+    "titlePlaceholder": "Vidange d'Huile",
+    "date": "Date",
+    "type": "Type",
+    "mileage": "Kilométrage",
+    "status": "Statut",
+    "technician": "Technicien",
+    "invoiceNumber": "Numéro de Facture",
+    "typeOptions": {
+      "maintenance": "Maintenance",
+      "repair": "Réparation",
+      "upgrade": "Mise à Niveau",
+      "inspection": "Inspection"
+    },
+    "statusOptions": {
+      "pending": "En Attente",
+      "in_progress": "En Cours",
+      "waiting_parts": "En Attente de Pièces",
+      "completed": "Complété"
+    },
+    "searchVehicles": "Rechercher des véhicules...",
+    "noVehicleFound": "Aucun véhicule trouvé.",
+    "techPicker": {
+      "placeholder": "Sélectionner ou tapez un nom...",
+      "searchPlaceholder": "Rechercher ou tapez un nom...",
+      "useCustom": "Utiliser \"{search}\"",
+      "typePrompt": "Tapez un nom...",
+      "boardTechnicians": "Techniciens du Tableau",
+      "teamMembers": "Membres de l'Équipe"
+    }
+  },
+  "parts": {
+    "title": "Pièces",
+    "fromInventory": "De l'Inventaire",
+    "addPart": "Ajouter une Pièce",
+    "partNumber": "Nº de Pièce",
+    "name": "Nom",
+    "qty": "Qté",
+    "unitPrice": "Prix Unitaire",
+    "total": "Total",
+    "namePlaceholder": "Nom *",
+    "subtotal": "Sous-total Pièces: {amount}",
+    "inventoryTitle": "Sélectionner une Pièce de l'Inventaire",
+    "searchInventory": "Rechercher l'inventaire...",
+    "inStock": "{quantity} en stock",
+    "noInventory": "Aucune pièce d'inventaire trouvée."
+  },
+  "labor": {
+    "title": "Main-d'œuvre",
+    "addLabor": "Ajouter une Main-d'œuvre",
+    "description": "Description",
+    "hours": "Heures",
+    "rate": "Taux ({currency}/h)",
+    "total": "Total",
+    "descriptionPlaceholder": "Description *",
+    "subtotal": "Sous-total Main-d'œuvre: {amount}"
+  },
+  "totals": {
+    "title": "Totaux",
+    "parts": "Pièces",
+    "labor": "Main-d'œuvre",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "discountNone": "Aucune",
+    "discountPercentage": "Pourcentage",
+    "discountFixed": "Fixe",
+    "tax": "Taxe",
+    "total": "Total"
+  },
+  "notes": {
+    "title": "Notes",
+    "public": "Public",
+    "internal": "Interne",
+    "publicPlaceholder": "Notes visibles sur la facture...",
+    "internalPlaceholder": "Notes internes (non affichées sur la facture)...",
+    "publicHelper": "Affichées sur la facture et les documents partagés",
+    "internalHelper": "Visibles uniquement pour votre équipe"
+  },
+  "invoice": {
+    "title": "Résumé de la Facture",
+    "parts": "Pièces",
+    "labor": "Main-d'œuvre",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "tax": "Taxe ({rate}%)",
+    "total": "Total",
+    "paid": "Payé",
+    "balanceDue": "Solde Dû",
+    "paidBadge": "PAYÉ",
+    "entityLabel": "Facture",
+    "emailSubject": "Confirmation de Paiement"
+  },
+  "payments": {
+    "title": "Paiements",
+    "markUnpaid": "Marquer comme Non Payé",
+    "markPaid": "Marquer comme Payé",
+    "recordPayment": "Enregistrer un Paiement",
+    "totalPaid": "Total Payé",
+    "amount": "Montant",
+    "date": "Date",
+    "method": "Méthode",
+    "note": "Note",
+    "notePlaceholder": "Note facultative",
+    "savePayment": "Enregistrer le Paiement",
+    "methodOptions": {
+      "cash": "Comptant",
+      "card": "Carte",
+      "transfer": "Virement",
+      "other": "Autre"
+    },
+    "recorded": "Paiement enregistré",
+    "markedPaid": "Marqué comme payé",
+    "markedUnpaid": "Marqué comme non payé",
+    "deleted": "Paiement supprimé",
+    "failedRecord": "Impossible d'enregistrer le paiement",
+    "failedStatus": "Impossible de mettre à jour le statut du paiement",
+    "failedDelete": "Impossible de supprimer le paiement",
+    "deleteTitle": "Supprimer le Paiement",
+    "deleteDescription": "Cela supprimera ce dossier de paiement.",
+    "failedPdf": "Impossible de générer la facture PDF"
+  },
+  "attachments": {
+    "deleteTitle": "Supprimer la Pièce Jointe",
+    "deleteDescription": "Cela supprimera définitivement cette pièce jointe. Cette action ne peut pas être annulée.",
+    "deleted": "Pièce jointe supprimée",
+    "failedDelete": "Impossible de supprimer",
+    "images": "Images ({count})",
+    "videos": "Vidéos ({count})",
+    "diagnostics": "Rapports Diagnostiques ({count})",
+    "documents": "Documents ({count})"
+  },
+  "share": {
+    "title": "Partager la Facture",
+    "description": "Générer un lien public qui permet à n'importe qui de consulter et télécharger cette facture sans se connecter.",
+    "notifyTitle": "Notifier {name}",
+    "email": "Email",
+    "noEmail": "(pas d'email en dossier)",
+    "sms": "SMS",
+    "noPhone": "(pas de numéro en dossier)",
+    "sendNotification": "Envoyer une Notification",
+    "revokeLink": "Révoquer le Lien",
+    "generateLink": "Générer un Lien Public",
+    "emailSent": "Email envoyé",
+    "failedEmail": "Impossible d'envoyer l'email",
+    "smsSent": "SMS envoyé",
+    "failedSms": "Impossible d'envoyer le SMS"
+  },
+  "sidebar": {
+    "vehicle": "Véhicule",
+    "customer": "Client",
+    "vin": "VIN: {vin}",
+    "mileageAtService": "Kilométrage au service: {mileage} {unit}"
+  },
+  "images": {
+    "title": "Images du Service",
+    "dropzone": "Déposez les images ici ou cliquez pour parcourir",
+    "uploading": "Téléversement...",
+    "formats": "JPG, PNG, WebP — max. 10 Mo chacune",
+    "uploadingImages": "Téléversement des images...",
+    "limitReached": "Limite d'images atteinte ({count}/{max})",
+    "upgradePrompt": "Mettez à niveau votre plan pour téléverser plus d'images.",
+    "onlyAllowed": "Seules les images JPG, PNG et WebP sont autorisées. {names} ignoré(es).",
+    "onlyUploading": "Seules {count} image(s) seront téléversée(s) pour respecter la limite.",
+    "uploadingCount": "Téléversement de {count} image(s)...",
+    "uploadedCount": "{count} image(s) téléversée(s)",
+    "uploadFailed": "Échec du téléversement",
+    "deleted": "Image supprimée",
+    "failedDelete": "Impossible de supprimer l'image",
+    "failedUpdate": "Impossible de mettre à jour",
+    "description": "Description...",
+    "invoice": "Facture"
+  },
+  "videos": {
+    "title": "Vidéos du Service",
+    "dropzone": "Déposez les vidéos ici ou cliquez pour parcourir",
+    "uploading": "Téléversement...",
+    "formats": "MP4, WebM, MOV — max. 10 Mo chacune",
+    "uploadingVideos": "Téléversement des vidéos...",
+    "uploadingCount": "Téléversement de {count} vidéo(s)...",
+    "uploadedCount": "{count} vidéo(s) téléversée(s)",
+    "uploadFailed": "Échec du téléversement",
+    "deleted": "Vidéo supprimée",
+    "failedDelete": "Impossible de supprimer la vidéo",
+    "failedUpdate": "Impossible de mettre à jour",
+    "invoice": "Facture"
+  },
+  "documents": {
+    "diagnosticTitle": "Rapports de Diagnostic",
+    "documentsTitle": "Documents",
+    "dropzone": "Déposez les fichiers ici ou cliquez pour parcourir",
+    "uploading": "Téléversement...",
+    "diagnosticFormats": "PDF, CSV, TXT — max. 10 Mo chacun",
+    "documentFormats": "PDF, DOCX, XLSX, CSV, TXT — max. 10 Mo chacun",
+    "uploadingReports": "Téléversement des rapports...",
+    "uploadingDocuments": "Téléversement des documents...",
+    "diagnosticLimitReached": "Limite de rapports de diagnostic atteinte ({count}/{max})",
+    "documentLimitReached": "Limite de documents atteinte ({count}/{max})",
+    "upgradePrompt": "Mettez à niveau votre plan pour téléverser davantage.",
+    "diagnosticLabel": "Rapport de diagnostic",
+    "documentLabel": "Document",
+    "limitReachedToast": "Limite de {label} atteinte ({count}/{max}). Mettez à niveau votre plan pour plus.",
+    "onlyUploading": "Seuls {count} fichier(s) seront téléversé(s) pour respecter la limite.",
+    "uploadingCount": "Téléversement de {count} fichier(s)...",
+    "uploadedCount": "{count} fichier(s) téléversé(s)",
+    "uploadFailed": "Échec du téléversement",
+    "deleted": "Fichier supprimé",
+    "failedDelete": "Impossible de supprimer le fichier",
+    "failedUpdate": "Impossible de mettre à jour",
+    "invoice": "Facture"
+  }
+}

+ 700 - 0
messages/fr/settings.json

@@ -0,0 +1,700 @@
+{
+  "title": "Parametres",
+  "subtitle": "Gerez votre atelier, factures et preferences",
+  "nav": {
+    "categories": {
+      "general": "General",
+      "billing": "Facturation et documents",
+      "communications": "Communications",
+      "workshop": "Atelier",
+      "system": "Systeme"
+    },
+    "items": {
+      "company": { "title": "Entreprise", "description": "Details commerciaux et image de marque" },
+      "subscription": { "title": "Abonnement", "description": "Plan et facturation" },
+      "account": { "title": "Compte", "description": "Nom, e-mail et mot de passe" },
+      "team": { "title": "Equipe", "description": "Membres et roles" },
+      "invoice": { "title": "Facture", "description": "Mise en page et champs de facture" },
+      "templates": { "title": "Modeles", "description": "Modeles de factures, devis et inspections" },
+      "payment": { "title": "Paiement", "description": "Conditions de paiement et comptes" },
+      "currency": { "title": "Devise", "description": "Devise et taxes par defaut" },
+      "customFields": { "title": "Champs personnalises", "description": "Definir des champs de donnees personnalises" },
+      "email": { "title": "E-mail", "description": "Fournisseur et envoi d'e-mails" },
+      "sms": { "title": "SMS", "description": "Fournisseur SMS et messagerie" },
+      "customerPortal": { "title": "Portail client", "description": "Portail libre-service client" },
+      "workshop": { "title": "Atelier", "description": "Valeurs par defaut technicien et main-d'oeuvre" },
+      "maintenance": { "title": "Maintenance", "description": "Intervalles de service predits" },
+      "appearance": { "title": "Apparence", "description": "Theme et affichage" },
+      "data": { "title": "Donnees", "description": "Exporter et importer une sauvegarde" },
+      "license": { "title": "Licence", "description": "Licence marque blanche" },
+      "about": { "title": "A propos", "description": "Version et informations" }
+    },
+    "pro": "PRO"
+  },
+  "readOnly": {
+    "banner": "Vous avez un acces en lecture seule aux parametres. Contactez un administrateur pour effectuer des modifications."
+  },
+  "featureLocked": {
+    "requiresPro": "Necessite une licence Pro",
+    "upgradePlan": "Mettre a niveau",
+    "activateLicense": "Activer la licence"
+  },
+  "company": {
+    "logoTitle": "Logo de l'entreprise",
+    "logoAlt": "Logo de l'entreprise",
+    "upload": "Telecharger",
+    "remove": "Supprimer",
+    "logoHint": "JPG, PNG, WebP, SVG — max 2 Mo",
+    "uploadingLogo": "Telechargement du logo...",
+    "logoUpdated": "Logo mis a jour",
+    "failedUploadLogo": "Echec du telechargement du logo",
+    "detailsTitle": "Details de l'entreprise",
+    "shopName": "Nom de l'entreprise / atelier",
+    "shopNamePlaceholder": "Garage Auto Martin",
+    "orgNumber": "Numero d'organisation",
+    "orgNumberPlaceholder": "123 456 789",
+    "phone": "Telephone",
+    "phonePlaceholder": "(555) 123-4567",
+    "email": "E-mail",
+    "emailPlaceholder": "service@exemple.com",
+    "address": "Adresse",
+    "addressPlaceholder": "123 Rue Principale\nEtage 1\n75001 Paris",
+    "saveCompany": "Enregistrer les details",
+    "addNewCompany": "Ajouter une entreprise",
+    "companyName": "Nom de l'entreprise",
+    "companyNamePlaceholder": "Ex. Garage Auto Martin",
+    "cancel": "Annuler",
+    "createCompany": "Creer l'entreprise",
+    "settingsSaved": "Parametres de l'entreprise enregistres",
+    "failedSave": "Echec de l'enregistrement des parametres",
+    "companyCreated": "Entreprise creee avec succes"
+  },
+  "account": {
+    "profileTitle": "Profil",
+    "profileDescription": "Mettez a jour votre nom d'affichage et votre adresse e-mail.",
+    "name": "Nom",
+    "namePlaceholder": "Votre nom",
+    "emailLabel": "E-mail",
+    "emailPlaceholder": "Votre e-mail",
+    "saveProfile": "Enregistrer le profil",
+    "profileUpdated": "Profil mis a jour",
+    "failedUpdateProfile": "Echec de la mise a jour du profil",
+    "failedUpdateEmail": "Echec de la mise a jour de l'e-mail",
+    "changePasswordTitle": "Changer le mot de passe",
+    "changePasswordDescription": "Entrez votre mot de passe actuel et choisissez-en un nouveau.",
+    "currentPassword": "Mot de passe actuel",
+    "newPassword": "Nouveau mot de passe",
+    "confirmNewPassword": "Confirmer le nouveau mot de passe",
+    "changePassword": "Changer le mot de passe",
+    "passwordChanged": "Mot de passe change",
+    "passwordsNoMatch": "Les mots de passe ne correspondent pas",
+    "passwordMinLength": "Le mot de passe doit comporter au moins 8 caracteres",
+    "failedChangePassword": "Echec du changement de mot de passe",
+    "twoFactorTitle": "Authentification a deux facteurs",
+    "twoFactorEnabled": "L'authentification a deux facteurs est activee",
+    "twoFactorDisablePrompt": "Entrez votre mot de passe pour desactiver l'authentification a deux facteurs.",
+    "twoFactorEnablePrompt": "Ajoutez une couche de securite supplementaire a votre compte en activant l'authentification a deux facteurs avec une application d'authentification.",
+    "password": "Mot de passe",
+    "enterPassword": "Entrez votre mot de passe",
+    "disable2FA": "Desactiver 2FA",
+    "enable2FA": "Activer 2FA",
+    "twoFactorDisabled": "Authentification a deux facteurs desactivee",
+    "failedEnable2FA": "Echec de l'activation de 2FA",
+    "failedDisable2FA": "Echec de la desactivation de 2FA",
+    "enterPasswordError": "Veuillez entrer votre mot de passe",
+    "setupDialogTitle": "Activer l'authentification a deux facteurs",
+    "setupDialogDescription": "Entrez votre mot de passe pour commencer la configuration de 2FA avec une application d'authentification.",
+    "continue": "Continuer",
+    "scanQRTitle": "Scanner le code QR",
+    "scanQRDescription": "Scannez ce code QR avec votre application d'authentification (Google Authenticator, Authy, etc.)",
+    "verifyCodeTitle": "Verifier le code",
+    "verifyCodeDescription": "Entrez le code a 6 chiffres de votre application d'authentification pour verifier qu'elle est correctement configuree.",
+    "authCode": "Code d'authentification",
+    "authCodePlaceholder": "000000",
+    "back": "Retour",
+    "verify": "Verifier",
+    "enterCodeError": "Veuillez entrer le code de votre application d'authentification",
+    "invalidCode": "Code invalide. Veuillez reessayer.",
+    "verificationFailed": "Verification echouee",
+    "backupCodesTitle": "Sauvegarder les codes de secours",
+    "backupCodesDescription": "Conservez ces codes dans un endroit sur. Vous pouvez les utiliser pour vous connecter si vous perdez l'acces a votre application d'authentification. Chaque code ne peut etre utilise qu'une seule fois.",
+    "copyCodes": "Copier les codes",
+    "copied": "Copie",
+    "backupCodesCopied": "Codes de secours copies dans le presse-papiers",
+    "savedBackupCodes": "J'ai sauvegarde mes codes de secours",
+    "twoFactorEnabledSuccess": "Authentification a deux facteurs activee",
+    "dangerZone": "Zone dangereuse",
+    "deleteContentTitle": "Supprimer le contenu",
+    "deleteContentDescription": "Supprimez selectivement les donnees de l'organisation telles que les vehicules, clients, devis ou inventaire. Votre compte et vos parametres resteront intacts.",
+    "deleteContentButton": "Supprimer le contenu",
+    "deleteAccountTitle": "Supprimer le compte",
+    "deleteAccountDescription": "Supprimez definitivement votre compte et toutes les donnees associees, y compris les vehicules, les dossiers de service, les ordres de travail, les factures, les clients, les fichiers et les paiements. Cette action ne peut pas etre annulee.",
+    "deleteAccountButton": "Supprimer le compte",
+    "deleteAccountDialogTitle": "Supprimer le compte",
+    "deleteAccountDialogDescription": "Cela supprimera definitivement votre compte et toutes les donnees associees. Cette action est irreversible. Tous vos vehicules, dossiers de service, ordres de travail, devis, clients, fichiers et historique de paiements seront definitivement supprimes.",
+    "deleteAccountConfirmPhrase": "delete me",
+    "deleteAccountConfirmPrompt": "Tapez <bold>delete me</bold> pour confirmer",
+    "permanentlyDelete": "Supprimer definitivement",
+    "deleting": "Suppression...",
+    "failedDeleteAccount": "Echec de la suppression du compte",
+    "deleteContentDialogTitle": "Supprimer le contenu",
+    "deleteContentDialogDescription": "Selectionnez le contenu que vous souhaitez supprimer definitivement. Cette action ne peut pas etre annulee.",
+    "contentVehicles": "Vehicules",
+    "contentVehiclesDescription": "Tous les vehicules et leurs dossiers de service, notes, rappels, journaux de carburant et factures recurrentes",
+    "contentCustomers": "Clients",
+    "contentCustomersDescription": "Tous les dossiers clients. Les vehicules lies aux clients supprimes seront dissocies.",
+    "contentQuotes": "Devis",
+    "contentQuotesDescription": "Tous les devis et leurs lignes",
+    "contentInventory": "Inventaire",
+    "contentInventoryDescription": "Toutes les pieces d'inventaire et leurs images",
+    "deleteContentConfirmPhrase": "delete my data",
+    "deleteContentConfirmPrompt": "Tapez <bold>delete my data</bold> pour confirmer",
+    "deleteSelected": "Supprimer {count} selectionnes",
+    "contentDeleted": "Le contenu selectionne a ete definitivement supprime",
+    "failedDeleteContent": "Echec de la suppression du contenu"
+  },
+  "team": {
+    "title": "Equipe",
+    "noOrgDescription": "Creez une equipe pour partager des donnees avec d'autres utilisateurs.",
+    "orgDescription": "Gerez les membres de votre organisation et leurs roles.",
+    "createOrg": "Creer une organisation",
+    "createOrgDescription": "Creez une organisation pour inviter des membres. Vous serez le proprietaire.",
+    "orgName": "Nom de l'organisation",
+    "orgNamePlaceholder": "Mon atelier",
+    "create": "Creer",
+    "orgCreated": "Organisation creee",
+    "failedCreateOrg": "Echec de la creation de l'organisation",
+    "memberCount": "{count} membre",
+    "memberCountPlural": "{count} membres",
+    "admin": "Administrateur",
+    "member": "Membre",
+    "inviteByEmail": "Inviter par e-mail",
+    "inviteEmailPlaceholder": "utilisateur@exemple.com",
+    "invite": "Inviter",
+    "invited": "Membre invite avec succes.",
+    "userNotFoundTitle": "Utilisateur non trouve",
+    "userNotFoundDescription": "Aucun compte trouve pour \"{email}\". Envoyer un e-mail d'invitation pour s'inscrire et rejoindre votre equipe ?",
+    "sendInvitation": "Envoyer l'invitation",
+    "invitationSentTitle": "Invitation envoyee",
+    "invitationSentDescription": "Un e-mail d'invitation a ete envoye a {email}.",
+    "failedInvite": "Echec de l'invitation du membre",
+    "failedSendInvitation": "Echec de l'envoi de l'invitation",
+    "pendingInvitations": "Invitations en attente",
+    "invitedAs": "Invite en tant que {role}",
+    "expires": "Expire le {date}",
+    "pending": "En attente",
+    "copyInviteLink": "Copier le lien d'invitation",
+    "inviteLinkCopied": "Lien d'invitation copie dans le presse-papiers",
+    "cancelInvitation": "Annuler l'invitation",
+    "cancelInvitationDescription": "Annuler l'invitation a {email} ? Ils ne pourront plus utiliser ce lien.",
+    "invitationCancelled": "Invitation annulee",
+    "failedCancelInvitation": "Echec de l'annulation de l'invitation",
+    "customRoles": "Roles personnalises",
+    "newRole": "Nouveau role",
+    "roleName": "Nom du role",
+    "roleNamePlaceholder": "Ex. Technicien",
+    "fullAdminAccess": "Acces administrateur complet (contourne toutes les verifications de permissions)",
+    "permissions": "Permissions",
+    "createRole": "Creer le role",
+    "updateRole": "Mettre a jour le role",
+    "roleCreated": "Role cree",
+    "roleUpdated": "Role mis a jour",
+    "failedSaveRole": "Echec de l'enregistrement du role",
+    "deleteRole": "Supprimer le role",
+    "deleteRoleDescription": "Supprimer le role \"{name}\" ? Les membres avec ce role perdront leurs permissions personnalisees.",
+    "roleDeleted": "Role supprime",
+    "failedDeleteRole": "Echec de la suppression du role",
+    "fullAccess": "Acces complet",
+    "permissionCount": "{count} permission",
+    "permissionCountPlural": "{count} permissions",
+    "noCustomRoles": "Pas encore de roles personnalises. Creez-en un pour definir des permissions granulaires pour les membres de l'equipe.",
+    "builtInRoles": "Roles integres",
+    "ownerLabel": "Proprietaire",
+    "ownerDescription": "Acces complet. Peut gerer les membres, changer les roles et supprimer l'organisation.",
+    "adminLabel": "Administrateur",
+    "adminDescription": "Peut inviter/supprimer des membres et gerer les donnees partagees.",
+    "memberLabel": "Membre",
+    "memberDescription": "Permissions determinees par le role personnalise assigne. Sans role personnalise, acces en lecture seule.",
+    "roleAssigned": "Role mis a jour",
+    "failedAssignRole": "Echec de la mise a jour du role",
+    "removeMemberTitle": "Retirer le membre",
+    "removeMemberDescription": "Retirer {name} de l'equipe ? Il perdra l'acces aux donnees partagees.",
+    "removeButton": "Retirer",
+    "memberRemoved": "Membre retire",
+    "failedRemoveMember": "Echec du retrait du membre",
+    "memberRoleUpdated": "Role du membre mis a jour",
+    "failedUpdateRole": "Echec de la mise a jour du role"
+  },
+  "invoice": {
+    "title": "Mise en page de facture",
+    "description": "Configurez ce qui apparait sur les factures PDF generees.",
+    "invoiceNumberFormat": "Format du numero de facture",
+    "invoiceNumberFormatHint": "Utilisez <code>{year}</code> pour l'annee en cours. Apercu : <bold>{preview}</bold>",
+    "nextInvoiceNumber": "Prochain numero de facture",
+    "nextInvoiceNumberPlaceholder": "Ex. 94",
+    "nextInvoiceNumberHint": "La prochaine facture utilisera ce numero (ex. {example})",
+    "dueDays": "Jours d'echeance",
+    "dueDaysHint": "Nombre de jours avant l'echeance de la facture",
+    "visibilityTitle": "Visibilite sur la facture",
+    "companyLogo": "Logo de l'entreprise",
+    "companyLogoHint": "Afficher le logo dans l'en-tete de la facture",
+    "companyName": "Nom de l'entreprise",
+    "companyNameHint": "Afficher le nom de l'entreprise dans l'en-tete",
+    "bankAccount": "Compte bancaire",
+    "bankAccountHint": "Afficher le compte bancaire / IBAN sur la facture",
+    "organizationNumber": "Numero d'organisation",
+    "organizationNumberHint": "Afficher le numero d'organisation dans l'en-tete",
+    "customFooter": "Pied de page personnalise",
+    "footerPlaceholder": "Merci pour votre confiance !",
+    "footerHint": "Ce texte apparait en bas de chaque facture",
+    "saveInvoice": "Enregistrer les parametres de facture",
+    "saved": "Parametres de facture enregistres"
+  },
+  "templates": {
+    "title": "Modeles",
+    "invoiceDescription": "Personnalisez l'apparence de vos factures et devis PDF.",
+    "inspectionsDescription": "Gerez les listes de controle d'inspection et les modeles multi-points.",
+    "smsDescription": "Personnalisez les messages SMS envoyes aux clients.",
+    "tabs": {
+      "invoice": "Facture",
+      "quotation": "Devis",
+      "inspections": "Inspections",
+      "sms": "SMS"
+    },
+    "presets": "Presets de modele",
+    "primaryColor": "Couleur primaire",
+    "fontAndLayout": "Police et mise en page",
+    "fontFamily": "Famille de police",
+    "helveticaDefault": "Helvetica (Par defaut)",
+    "timesRoman": "Times Roman",
+    "courier": "Courier",
+    "headerStyle": "Style d'en-tete",
+    "standard": "Standard",
+    "compact": "Compact",
+    "modern": "Moderne",
+    "preview": "Apercu {name}",
+    "billTo": "Facturer a",
+    "preparedFor": "Prepare pour",
+    "vehicle": "Vehicule",
+    "part": "Piece",
+    "qty": "Qte",
+    "price": "Prix",
+    "total": "Total",
+    "labor": "Main-d'oeuvre",
+    "hours": "Heures",
+    "rate": "Taux",
+    "subtotal": "Sous-total",
+    "tax": "Taxe ({rate}%)",
+    "saveInvoiceTemplate": "Enregistrer le modele de facture",
+    "saveQuotationTemplate": "Enregistrer le modele de devis",
+    "invoiceTemplateSaved": "Parametres du modele de facture enregistres",
+    "quotationTemplateSaved": "Parametres du modele de devis enregistres",
+    "failedSave": "Echec de l'enregistrement des parametres",
+    "smsTemplates": "Modeles de messages SMS",
+    "smsTemplatesDescription": "Personnalisez les messages texte envoyes aux clients. Utilisez des variables comme <code>{companyName}</code> pour inserer des valeurs dynamiques.",
+    "reset": "Reinitialiser",
+    "invoiceReady": "Facture prete",
+    "invoiceReadyDescription": "Envoye lors du partage d'une facture avec un client.",
+    "quoteReady": "Devis pret",
+    "quoteReadyDescription": "Envoye lors du partage d'un devis avec un client.",
+    "inspectionReady": "Inspection prete",
+    "inspectionReadyDescription": "Envoye lors du partage d'un rapport d'inspection avec un client.",
+    "statusInProgress": "Statut : En cours",
+    "statusInProgressDescription": "Envoye lorsque le statut d'un ordre de travail passe a En cours.",
+    "statusWaitingParts": "Statut : En attente de pieces",
+    "statusWaitingPartsDescription": "Envoye lorsque le statut passe a En attente de pieces.",
+    "statusReady": "Statut : Pret a recuperer",
+    "statusReadyDescription": "Envoye lorsque le statut passe a Pret.",
+    "statusCompleted": "Statut : Termine",
+    "statusCompletedDescription": "Envoye lorsque le statut passe a Termine.",
+    "paymentReceived": "Paiement recu",
+    "paymentReceivedDescription": "Envoye lorsqu'un paiement est enregistre pour une facture.",
+    "saveSmsTemplates": "Enregistrer les modeles SMS",
+    "smsTemplateSaved": "Parametres des modeles SMS enregistres",
+    "smsDefaults": {
+      "invoiceReady": "Votre facture est prête. Consultez-la ici : {share_link}",
+      "quoteReady": "Votre devis est prêt pour examen. Consultez-le ici : {share_link}",
+      "inspectionReady": "Votre rapport d'inspection véhicule est prêt. Consultez-le ici : {share_link}",
+      "statusInProgress": "Les travaux sur votre véhicule ont commencé. Nous vous tiendrons informé.",
+      "statusWaitingParts": "Votre véhicule est en attente de pièces. Nous vous informerons quand les travaux reprendront.",
+      "statusReady": "Votre véhicule est prêt à être récupéré !",
+      "statusCompleted": "Votre service est terminé. Merci pour votre confiance !",
+      "paymentReceived": "Paiement de {amount} reçu pour la facture {invoice_number}. Merci !"
+    },
+    "colorPresets": {
+      "amber": "Ambre",
+      "blue": "Bleu",
+      "emerald": "Emeraude",
+      "red": "Rouge",
+      "purple": "Violet",
+      "slate": "Ardoise",
+      "rose": "Rose",
+      "indigo": "Indigo"
+    }
+  },
+  "payment": {
+    "detailsTitle": "Details de paiement",
+    "detailsDescription": "Compte bancaire et conditions de paiement sur les factures.",
+    "bankAccount": "Compte bancaire (IBAN / Til Konto)",
+    "bankAccountPlaceholder": "NO93 8601 1117 947",
+    "bankAccountHint": "Votre numero de compte bancaire ou IBAN pour recevoir les paiements",
+    "paymentTerms": "Conditions de paiement",
+    "paymentTermsPlaceholder": "Net 14 jours",
+    "paymentTermsHint": "Texte des conditions de paiement sur les factures (ex. \"Net 14 jours\", \"A reception\")",
+    "onlinePayments": "Paiements en ligne",
+    "onlinePaymentsDescription": "Permettez aux clients de payer les factures en ligne via des liens de facture partages.",
+    "stripe": "Stripe",
+    "stripeDescription": "Accepter les paiements par carte de credit/debit",
+    "secretKey": "Cle secrete",
+    "publishableKey": "Cle publique",
+    "webhookSecret": "Secret du webhook",
+    "webhookUrl": "URL du webhook",
+    "stripeWebhookHint": "Ajoutez cette URL dans votre tableau de bord Stripe sous Webhooks. Abonnez-vous a <code>checkout.session.completed</code>.",
+    "vipps": "Vipps",
+    "vippsDescription": "Accepter les paiements mobiles Vipps (Norvege)",
+    "clientId": "ID client",
+    "clientSecret": "Secret client",
+    "subscriptionKey": "Cle d'abonnement",
+    "merchantSerialNumber": "Numero de serie du commercant",
+    "testMode": "Mode test",
+    "callbackUrl": "URL de callback",
+    "termsOfSale": "Conditions de vente (Salgsvilkar)",
+    "termsOfSaleDescription": "Requis par Vipps. Les clients peuvent les consulter depuis la page de facture.",
+    "externalTermsUrl": "URL des conditions externes (optionnel)",
+    "externalTermsUrlHint": "Lien vers une page externe avec vos conditions. Si defini, la facture redirigera ici.",
+    "publicTermsUrl": "URL publique des conditions",
+    "publicTermsUrlHint": "Partagez cette URL avec Vipps ou liez-la depuis votre site web.",
+    "paypal": "PayPal",
+    "paypalDescription": "Accepter les paiements PayPal",
+    "sandboxMode": "Mode sandbox",
+    "paypalWebhookHint": "Ajoutez cette URL dans votre tableau de bord PayPal sous Webhooks. Abonnez-vous a <code>PAYMENT.CAPTURE.COMPLETED</code>.",
+    "savePayment": "Enregistrer les parametres de paiement",
+    "saved": "Parametres de paiement enregistres"
+  },
+  "currency": {
+    "title": "Devise et taxes",
+    "description": "Devise par defaut et taux d'imposition pour les factures et les dossiers de service.",
+    "currencyLabel": "Devise",
+    "previewLabel": "Apercu : {value}",
+    "enableTax": "Activer les taxes",
+    "enableTaxHint": "Afficher le calcul des taxes sur les factures et les dossiers de service",
+    "defaultTaxRate": "Taux d'imposition par defaut (%)",
+    "defaultTaxRateHint": "Rempli automatiquement lors de la creation de nouveaux dossiers de service",
+    "saveSettings": "Enregistrer les parametres",
+    "saved": "Parametres enregistres",
+    "currencies": {
+      "USD": "Dollar americain",
+      "EUR": "Euro",
+      "GBP": "Livre sterling",
+      "NOK": "Couronne norvegienne",
+      "SEK": "Couronne suedoise",
+      "DKK": "Couronne danoise",
+      "CHF": "Franc suisse",
+      "CAD": "Dollar canadien",
+      "AUD": "Dollar australien",
+      "NZD": "Dollar neo-zelandais",
+      "JPY": "Yen japonais",
+      "CNY": "Yuan chinois",
+      "INR": "Roupie indienne",
+      "BRL": "Real bresilien",
+      "MXN": "Peso mexicain",
+      "PLN": "Zloty polonais",
+      "CZK": "Couronne tcheque",
+      "HUF": "Forint hongrois",
+      "TRY": "Livre turque",
+      "ZAR": "Rand sud-africain",
+      "KRW": "Won sud-coreen",
+      "SGD": "Dollar de Singapour",
+      "HKD": "Dollar de Hong Kong",
+      "THB": "Baht thailandais",
+      "ISK": "Couronne islandaise",
+      "RON": "Leu roumain",
+      "ILS": "Shekel israelien",
+      "PHP": "Peso philippin",
+      "IDR": "Roupie indonesienne",
+      "MYR": "Ringgit malaisien",
+      "AED": "Dirham des EAU",
+      "SAR": "Riyal saoudien"
+    }
+  },
+  "customFields": {
+    "title": "Champs personnalisés",
+    "description": "Définissez des champs personnalisés qui apparaissent sur les fiches de service et les devis.",
+    "addField": "Ajouter un champ",
+    "filterAll": "Tous",
+    "serviceRecord": "Fiche de service",
+    "quote": "Devis",
+    "emptyState": "Aucun champ personnalisé défini. Cliquez sur \"Ajouter un champ\" pour en créer un.",
+    "required": "Obligatoire",
+    "inactive": "Inactif",
+    "editField": "Modifier le champ",
+    "newField": "Nouveau champ personnalisé",
+    "label": "Libellé",
+    "labelPlaceholder": "ex. Expiration de garantie",
+    "fieldName": "Nom du champ (ID)",
+    "fieldNamePlaceholder": "expiration_garantie",
+    "fieldNameHint": "Lettres minuscules, chiffres et underscores uniquement.",
+    "fieldType": "Type de champ",
+    "entityType": "Type d'entité",
+    "fieldTypes": {
+      "text": "Texte",
+      "number": "Nombre",
+      "date": "Date",
+      "select": "Sélection (Menu déroulant)",
+      "checkbox": "Case à cocher",
+      "textarea": "Zone de texte"
+    },
+    "optionsLabel": "Options (séparées par des virgules)",
+    "optionsPlaceholder": "Option 1, Option 2, Option 3",
+    "active": "Actif",
+    "cancel": "Annuler",
+    "update": "Mettre à jour",
+    "create": "Créer",
+    "deleteTitle": "Supprimer le champ personnalisé",
+    "deleteDescription": "Cela supprimera définitivement \"{label}\" et toutes ses valeurs enregistrées. Cette action est irréversible.",
+    "fieldCreated": "Champ personnalisé créé",
+    "fieldUpdated": "Champ personnalisé mis à jour",
+    "fieldDeleted": "Champ personnalisé supprimé",
+    "failedSave": "Impossible d'enregistrer le champ",
+    "failedDelete": "Impossible de supprimer"
+  },
+  "workshop": {
+    "title": "Valeurs par defaut de l'atelier",
+    "description": "Valeurs par defaut lors de la creation de nouveaux dossiers de service.",
+    "defaultTechnician": "Nom du technicien par defaut",
+    "technicianPlaceholder": "Jean Dupont",
+    "defaultLaborRate": "Taux horaire par defaut",
+    "laborRatePlaceholder": "75,00",
+    "workingHours": "Heures de travail",
+    "workingHoursPlaceholder": "Lun-Ven 8h00 - 17h00",
+    "unitsTitle": "Unites",
+    "unitsDescription": "Choisissez entre metrique (km, litres) et imperial (miles, gallons).",
+    "unitSystem": "Systeme d'unites",
+    "metric": "Metrique (km, litres)",
+    "imperial": "Imperial (miles, gallons)",
+    "distanceLabel": "Distance",
+    "volumeLabel": "Volume",
+    "kilometers": "kilometres (km)",
+    "liters": "litres (L)",
+    "miles": "miles (mi)",
+    "gallons": "gallons (gal)",
+    "saveWorkshop": "Enregistrer les parametres de l'atelier",
+    "saved": "Parametres de l'atelier enregistres"
+  },
+  "maintenance": {
+    "title": "Maintenance predictive",
+    "description": "Predisez le kilometrage actuel de chaque vehicule a partir de son historique de service et affichez les vehicules necessitant ou approchant leur prochain service.",
+    "enablePredicted": "Activer la maintenance predictive",
+    "enablePredictedHint": "Afficher les predictions sur les details du vehicule et le tableau de bord",
+    "serviceInterval": "Intervalle de service ({unit})",
+    "serviceIntervalHint": "Distance entre les services recommandes",
+    "approachingThreshold": "Seuil d'approche ({unit})",
+    "approachingThresholdHint": "Afficher un avertissement a cette distance du prochain service",
+    "saveMaintenance": "Enregistrer les parametres de maintenance",
+    "saved": "Parametres de maintenance enregistres"
+  },
+  "appearance": {
+    "title": "Apparence",
+    "description": "Personnalisez l'apparence de Torqvoice.",
+    "darkMode": "Mode sombre",
+    "darkModeHint": "Basculer entre les themes clair et sombre",
+    "dateTimeTitle": "Date et heure",
+    "dateTimeDescription": "Configurez l'affichage des dates et heures dans l'application.",
+    "dateFormat": "Format de date",
+    "timeFormat": "Format d'heure",
+    "time12h": "12 heures (2:30 PM)",
+    "time24h": "24 heures (14:30)",
+    "timezone": "Fuseau horaire",
+    "timezoneAuto": "Detection automatique (navigateur)",
+    "timezoneHint": "En mode auto-detection, chaque utilisateur voit les dates dans le fuseau horaire de son navigateur.",
+    "previewLabel": "Apercu",
+    "dateLabel": "Date",
+    "dateTimeLabel": "Date et heure",
+    "saveSettings": "Enregistrer les parametres",
+    "saved": "Parametres enregistres"
+  },
+  "data": {
+    "title": "Gestion des donnees",
+    "description": "Exportez vos donnees comme sauvegarde ou importez un fichier precedemment exporte.",
+    "exportTitle": "Exporter les donnees",
+    "exportDescription": "Telechargez vos donnees sous forme de fichier zip. Selectionnez les categories a inclure.",
+    "includeInBackup": "Inclure dans la sauvegarde :",
+    "selectAll": "Tout selectionner",
+    "deselectAll": "Tout deselectionner",
+    "optSettings": "Parametres",
+    "optSettingsDesc": "Info atelier, config facturation, fournisseurs de paiement",
+    "optCustomers": "Clients",
+    "optCustomersDesc": "Dossiers clients et informations de contact",
+    "optVehicles": "Vehicules et dossiers de service",
+    "optVehiclesDesc": "Vehicules, notes, journaux de carburant, rappels, dossiers de service",
+    "optQuotes": "Devis",
+    "optQuotesDesc": "Devis avec pieces et main-d'oeuvre",
+    "optInventory": "Inventaire",
+    "optInventoryDesc": "Pieces d'inventaire et niveaux de stock",
+    "optCustomFields": "Champs personnalises",
+    "optCustomFieldsDesc": "Definitions et valeurs des champs personnalises",
+    "optFiles": "Fichiers telecharges",
+    "optFilesDesc": "Images, pieces jointes de service, logo",
+    "filesWarning": "Inclut les fichiers telecharges — la sauvegarde peut etre plus volumineuse",
+    "downloadBackup": "Telecharger la sauvegarde",
+    "exportComplete": "Exportation terminee",
+    "exportCompleteMessage": "Votre fichier de sauvegarde a ete telecharge.",
+    "exportFailed": "Exportation echouee",
+    "exportFailedMessage": "Impossible d'exporter vos donnees. Veuillez reessayer.",
+    "importTitle": "Importer des donnees",
+    "importWarning": "L'importation va <bold>remplacer toutes les donnees existantes</bold> par le contenu du fichier de sauvegarde. Cette action ne peut pas etre annulee.",
+    "importFileHint": "Supporte les fichiers de sauvegarde .zip (v2) et .json (v1).",
+    "uploadRestore": "Telecharger et restaurer",
+    "importComplete": "Importation terminee",
+    "importCompleteMessage": "Toutes les donnees ont ete restaurees a partir du fichier de sauvegarde.",
+    "importFailed": "Importation echouee",
+    "importFromOther": "Importer d'autres services",
+    "importFromOtherDescription": "Migrez vos donnees depuis une autre plateforme. Selectionnez un service pour commencer.",
+    "lubelogTitle": "Importer de LubeLog",
+    "lubelogExportSteps": "Comment exporter vos donnees LubeLog :",
+    "lubelogStep1": "Ouvrez votre instance LubeLog",
+    "lubelogStep2": "Allez dans <bold>Settings</bold>",
+    "lubelogStep3": "Cliquez sur <bold>Make Backup</bold> pour telecharger un fichier zip",
+    "lubelogStep4": "Telechargez le fichier zip ci-dessous",
+    "lubelogAddNote": "Cela va <bold>ajouter des donnees</bold> a cote de vos enregistrements existants — rien ne sera supprime.",
+    "lubelogImported": "Ce qui sera importe :",
+    "lubelogVehicles": "Vehicules",
+    "lubelogServiceRecords": "Dossiers de service avec pieces jointes",
+    "lubelogNotes": "Notes",
+    "lubelogFileHint": "Fichiers jusqu'a 200 Mo supportes.",
+    "lubelogImportComplete": "Importation LubeLog terminee",
+    "cancel": "Annuler",
+    "import": "Importer",
+    "invoiceNinjaTitle": "Importer de Invoice Ninja",
+    "invoiceNinjaExportSteps": "Comment exporter vos donnees Invoice Ninja :",
+    "invoiceNinjaStep1": "Ouvrez votre instance Invoice Ninja",
+    "invoiceNinjaStep2": "Allez dans <bold>Settings > Import | Export</bold>",
+    "invoiceNinjaStep3": "Cliquez sur <bold>Export</bold> pour telecharger un fichier zip",
+    "invoiceNinjaStep4": "Telechargez le fichier zip ci-dessous",
+    "invoiceNinjaAddNote": "Cela va <bold>ajouter des donnees</bold> a cote de vos enregistrements existants — rien ne sera supprime.",
+    "invoiceNinjaImported": "Ce qui sera importe :",
+    "invoiceNinjaCustomers": "Clients",
+    "invoiceNinjaProducts": "Produits (inventaire)",
+    "invoiceNinjaInvoices": "Factures comme dossiers de service (pieces et main-d'oeuvre)",
+    "invoiceNinjaDocuments": "Documents et pieces jointes",
+    "invoiceNinjaPayments": "Paiements",
+    "invoiceNinjaFileHint": "Telechargez un fichier d'export zip Invoice Ninja. Fichiers jusqu'a 200 Mo supportes.",
+    "invoiceNinjaImportComplete": "Importation Invoice Ninja terminee"
+  },
+  "license": {
+    "title": "Licence marque blanche",
+    "description": "Activez une licence marque blanche pour supprimer la marque Torqvoice des factures et de l'application.",
+    "purchaseWhiteLabel": "Acheter marque blanche",
+    "status": "Statut :",
+    "active": "Active",
+    "inactive": "Inactive",
+    "lastChecked": "Derniere verification : {date}",
+    "enterLicenseKey": "Entrez la cle de licence",
+    "validate": "Valider",
+    "validated": "Licence validee avec succes",
+    "invalid": "Cle de licence invalide",
+    "failedValidate": "Echec de la validation de la licence",
+    "keyHint": "Chaque cle de licence est liee a cette organisation et ne peut pas etre partagee entre plusieurs organisations."
+  },
+  "about": {
+    "title": "A propos de Torqvoice",
+    "description": "Torqvoice est votre solution complete de gestion de services vehicules. Suivez l'historique de service, les reparations et plus encore.",
+    "version": "Version",
+    "website": "Site web",
+    "documentation": "Documentation",
+    "changelog": "Journal des modifications"
+  },
+  "portal": {
+    "title": "Portail client",
+    "description": "Permettez a vos clients de se connecter et de consulter leurs vehicules, factures, devis et inspections. Les clients peuvent egalement soumettre des demandes de service.",
+    "enablePortal": "Activer le portail client",
+    "enablePortalHint": "Les clients avec une adresse e-mail peuvent se connecter via un lien magique",
+    "enabled": "Portail client active",
+    "disabled": "Portail client desactive",
+    "failedUpdate": "Echec de la mise a jour du parametre",
+    "customSlug": "Slug personnalise du portail",
+    "save": "Enregistrer",
+    "slugSaved": "Slug du portail enregistre",
+    "slugRemoved": "Slug du portail supprime",
+    "failedUpdateSlug": "Echec de la mise a jour du slug",
+    "slugHint": "Lettres minuscules, chiffres, tirets et underscores. 3-48 caracteres. Laissez vide pour utiliser l'URL par defaut.",
+    "portalUrl": "URL du portail",
+    "urlCopied": "URL du portail copiee",
+    "shareHint": "Partagez ce lien avec vos clients pour qu'ils puissent se connecter a leur portail."
+  },
+  "email": {
+    "title": "Fournisseur de messagerie",
+    "description": "Configurez le fournisseur de messagerie de votre organisation pour l'envoi de factures et devis. Sans configuration, le fournisseur de messagerie par defaut de la plateforme sera utilise.",
+    "useCustomLabel": "Utiliser un fournisseur de messagerie personnalise",
+    "useCustomHint": "Activer pour configurer votre propre fournisseur de messagerie au lieu du fournisseur par defaut de la plateforme",
+    "platformDefaultInfo": "Votre organisation utilise le fournisseur de messagerie par defaut de la plateforme. Les factures et devis seront envoyes avec les parametres de messagerie configures par la plateforme.",
+    "smtpHost": "Hote SMTP",
+    "smtpHostPlaceholder": "smtp.example.com",
+    "smtpPort": "Port SMTP",
+    "smtpPortPlaceholder": "587",
+    "username": "Nom d'utilisateur",
+    "usernamePlaceholder": "utilisateur@example.com",
+    "password": "Mot de passe",
+    "fromEmail": "E-mail d'expedition",
+    "fromEmailPlaceholder": "noreply@example.com",
+    "fromEmailDomainPlaceholder": "noreply@votredomaine.com",
+    "fromName": "Nom d'expediteur",
+    "fromNamePlaceholder": "Votre Atelier",
+    "tlsConnection": "Connexion TLS (Port 465)",
+    "tlsConnectionHint": "Activer pour TLS implicite. Desactiver pour STARTTLS (port 587/25).",
+    "verifyTlsCerts": "Verifier les certificats TLS",
+    "verifyTlsCertsHint": "Desactiver pour autoriser les certificats auto-signes (non recommande en production)",
+    "requireTlsUpgrade": "Exiger la mise a niveau TLS",
+    "requireTlsUpgradeHint": "Forcer la mise a niveau TLS sur les connexions STARTTLS",
+    "apiKey": "Cle API",
+    "apiKeyHintResend": "Obtenez votre cle API sur",
+    "serverToken": "Jeton de serveur",
+    "serverTokenHintPostmark": "Obtenez votre jeton de serveur sur",
+    "apiKeyHintMailgun": "Obtenez votre cle API sur",
+    "domain": "Domaine",
+    "domainPlaceholder": "mg.votredomaine.com",
+    "region": "Region",
+    "apiKeyHintSendGrid": "Obtenez votre cle API sur",
+    "accessKeyId": "ID de cle d'acces",
+    "secretAccessKey": "Cle d'acces secrete",
+    "awsRegion": "Region AWS",
+    "awsRegionPlaceholder": "us-east-1",
+    "awsRegionHint": "La region AWS ou SES est configure (ex. us-east-1, eu-west-1)",
+    "sendTestEmail": "Envoyer un e-mail de test",
+    "testEmailHint": "Enregistrez d'abord les parametres, puis envoyez un e-mail de test a votre compte",
+    "saveSettings": "Enregistrer les parametres",
+    "savedDefault": "Parametres de messagerie enregistres — fournisseur par defaut de la plateforme utilise",
+    "saved": "Parametres de messagerie enregistres",
+    "failedSave": "Echec de l'enregistrement des parametres",
+    "testSentTo": "E-mail de test envoye a {email}",
+    "testFailed": "Echec du test de messagerie"
+  },
+  "sms": {
+    "title": "Fournisseur SMS",
+    "description": "Configurez votre fournisseur SMS pour la messagerie bidirectionnelle avec les clients. Chaque organisation utilise son propre fournisseur SMS et numero de telephone.",
+    "enableLabel": "Activer la messagerie SMS",
+    "enableHint": "Envoyer et recevoir des SMS avec les clients",
+    "disabledInfo": "La messagerie SMS est desactivee. Activez-la pour envoyer et recevoir des messages texte avec vos clients.",
+    "phoneNumber": "Numero de telephone",
+    "phoneNumberPlaceholder": "+33612345678",
+    "phoneNumberHint": "Votre numero de telephone compatible SMS au format E.164 (ex. +33612345678)",
+    "accountSid": "SID du compte",
+    "authToken": "Jeton d'authentification",
+    "apiKey": "Cle API",
+    "apiSecret": "Secret API",
+    "webhookUrl": "URL du webhook",
+    "webhookUrlHint": "Configurez cette URL dans votre tableau de bord {provider} pour recevoir les SMS entrants.",
+    "webhookCopied": "URL du webhook copiee",
+    "testPhoneNumber": "Numero de telephone de test",
+    "testPhonePlaceholder": "+33698765432",
+    "sendTestSms": "Envoyer un SMS de test",
+    "testSmsHint": "Enregistrez d'abord les parametres, puis entrez un numero de destinataire pour envoyer un SMS de test",
+    "enterTestPhone": "Entrez un numero de telephone pour envoyer le SMS de test",
+    "saveSettings": "Enregistrer les parametres",
+    "savedDisabled": "Parametres SMS enregistres — SMS desactive",
+    "saved": "Parametres SMS enregistres",
+    "failedSave": "Echec de l'enregistrement des parametres",
+    "testSentTo": "SMS de test envoye a {phone}",
+    "testFailed": "Echec du test SMS"
+  }
+}

+ 156 - 0
messages/fr/share.json

@@ -0,0 +1,156 @@
+{
+  "invoice": {
+    "title": "Facture",
+    "downloadPdf": "Télécharger le PDF",
+    "paymentReceived": "Paiement reçu !",
+    "paymentApplied": "{amount} a été appliqué à cette facture.",
+    "verifyingPayment": "Vérification du paiement...",
+    "balanceDue": "Solde dû",
+    "fullAmount": "Montant total",
+    "partialPayment": "Paiement partiel",
+    "enterAmount": "Saisir le montant",
+    "payWithCard": "Payer {amount} par carte",
+    "payWithVipps": "Payer {amount} avec Vipps",
+    "payWithPaypal": "Payer {amount} avec PayPal",
+    "errorInvalidAmount": "Veuillez saisir un montant valide.",
+    "errorExceedsBalance": "Le montant ne peut pas dépasser {amount}.",
+    "errorVerifyFailed": "Le paiement n'a pas pu être vérifié. Il est peut-être encore en cours de traitement.",
+    "errorVerifyRequest": "Échec de la vérification du statut du paiement.",
+    "errorCheckoutFailed": "Échec du démarrage du paiement.",
+    "errorPaymentFailed": "Échec de l'initiation du paiement. Veuillez réessayer.",
+    "billTo": "Facturer à",
+    "vehicle": "Véhicule",
+    "vin": "VIN : {vin}",
+    "plate": "Plaque : {plate}",
+    "service": "Service",
+    "type": "Type : {type}",
+    "tech": "Technicien : {tech}",
+    "parts": "Pièces",
+    "partNumber": "N° de pièce",
+    "description": "Description",
+    "qty": "Qté",
+    "unitPrice": "Prix unitaire",
+    "total": "Total",
+    "labor": "Main-d'œuvre",
+    "hours": "Heures",
+    "rate": "Taux",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "discountPercent": "Remise ({percent}%)",
+    "tax": "Taxe ({rate}%)",
+    "paid": "Payé",
+    "paidInFull": "ENTIÈREMENT PAYÉ",
+    "amountDue": "Montant dû",
+    "paymentInformation": "Informations de paiement",
+    "bankAccount": "Compte bancaire",
+    "orgNumber": "N° d'organisation",
+    "paymentTerms": "Conditions de paiement",
+    "dueDate": "Date d'échéance",
+    "serviceImages": "Images de service ({count})",
+    "serviceVideos": "Vidéos de service ({count})",
+    "diagnosticReports": "Rapports de diagnostic ({count})",
+    "notes": "Notes",
+    "diagnosticNotes": "Notes de diagnostic",
+    "tel": "Tél. : {phone}"
+  },
+  "quote": {
+    "title": "Devis",
+    "downloadPdf": "Télécharger le PDF",
+    "preparedFor": "Préparé pour",
+    "vehicle": "Véhicule",
+    "vin": "VIN : {vin}",
+    "plate": "Plaque : {plate}",
+    "quoteDetails": "Détails du devis",
+    "description": "Description",
+    "parts": "Pièces",
+    "partNumber": "N° de pièce",
+    "partDescription": "Description",
+    "qty": "Qté",
+    "unitPrice": "Prix unitaire",
+    "total": "Total",
+    "labor": "Main-d'œuvre",
+    "laborDescription": "Description",
+    "hours": "Heures",
+    "rate": "Taux",
+    "ratePerHour": "{rate}/h",
+    "subtotal": "Sous-total",
+    "discount": "Remise",
+    "discountPercent": "Remise ({percent}%)",
+    "tax": "Taxe ({rate}%)",
+    "images": "Images ({count})",
+    "documents": "Documents",
+    "requestChanges": "Demander des modifications",
+    "acceptQuote": "Accepter le devis",
+    "whatChanges": "Quelles modifications souhaitez-vous ?",
+    "changePlaceholder": "Décrivez les modifications souhaitées...",
+    "cancel": "Annuler",
+    "submitRequest": "Soumettre la demande",
+    "quoteAccepted": "Devis accepté",
+    "quoteAcceptedMessage": "Merci ! Ce devis a été accepté. L'atelier vous contactera prochainement.",
+    "changesRequested": "Modifications demandées",
+    "changesRequestedMessage": "Votre demande de modification a été soumise. L'atelier l'examinera et vous répondra.",
+    "validUntil": "Valable jusqu'au : {date}",
+    "status": {
+      "draft": "Brouillon",
+      "sent": "Envoyé",
+      "accepted": "Accepté",
+      "rejected": "Refusé",
+      "expired": "Expiré",
+      "converted": "Converti",
+      "changes_requested": "Modifications demandées"
+    },
+    "tel": "Tél. : {phone}"
+  },
+  "inspection": {
+    "title": "Inspection du véhicule",
+    "vehicle": "Véhicule",
+    "customer": "Client",
+    "vin": "VIN : {vin}",
+    "plate": "Plaque : {plate}",
+    "mileage": "Kilométrage : {mileage}",
+    "inspected": "inspecté",
+    "pass": "Conforme",
+    "fail": "Non conforme",
+    "attention": "Attention",
+    "notInspected": "Non inspecté",
+    "quoteAvailable": "Un devis a été préparé pour cette inspection",
+    "quoteAvailableDescription": "Consultez le devis pour voir les tarifs et approuver les travaux recommandés.",
+    "viewQuote": "Voir le devis",
+    "quoteRequested": "Devis demandé",
+    "quoteRequestedCancel": "— Annuler",
+    "requestQuote": "Demander un devis",
+    "downloadPdf": "Télécharger le PDF",
+    "notes": "Notes",
+    "quoteCancelled": "Demande de devis annulée",
+    "cancelFailed": "Échec de l'annulation de la demande"
+  },
+  "terms": {
+    "title": "Conditions de vente (Salgsvilkår)",
+    "contact": "Contact",
+    "tel": "Tél. : {phone}"
+  },
+  "quoteRequest": {
+    "title": "Demander un devis",
+    "description": "Sélectionnez les éléments pour lesquels vous souhaitez un devis de réparation.",
+    "selectAll": "Tout sélectionner",
+    "deselectAll": "Tout désélectionner",
+    "selectedCount": "{selected} sur {total} sélectionné(s)",
+    "fail": "Non conforme",
+    "attention": "Attention",
+    "message": "Message",
+    "messageOptional": "(optionnel)",
+    "messagePlaceholder": "Tout détail supplémentaire ou question...",
+    "cancel": "Annuler",
+    "submitRequest": "Soumettre la demande",
+    "selectAtLeastOne": "Sélectionnez au moins un élément",
+    "submitSuccess": "Demande de devis soumise !",
+    "submitError": "Échec de la soumission de la demande"
+  },
+  "common": {
+    "poweredBy": "Propulsé par",
+    "termsOfSale": "Conditions de vente",
+    "portalMessage": "Consultez vos véhicules, factures et plus encore sur votre",
+    "customerPortal": "portail client"
+  }
+}

+ 195 - 0
messages/fr/vehicles.json

@@ -0,0 +1,195 @@
+{
+  "list": {
+    "active": "Actifs",
+    "archived": "Archiv\u00e9s",
+    "searchPlaceholder": "Rechercher par plaque, marque, mod\u00e8le, NIV...",
+    "addVehicle": "Ajouter un v\u00e9hicule",
+    "emptySearch": "Aucun v\u00e9hicule ne correspond \u00e0 votre recherche.",
+    "emptyArchived": "Aucun v\u00e9hicule archiv\u00e9.",
+    "empty": "Aucun v\u00e9hicule pour le moment.",
+    "table": {
+      "plate": "Plaque",
+      "vehicle": "V\u00e9hicule",
+      "customer": "Client",
+      "mileage": "Kilom\u00e9trage",
+      "services": "Services"
+    },
+    "edit": "Modifier",
+    "archive": "Archiver",
+    "unarchive": "D\u00e9sarchiver",
+    "delete": "Supprimer",
+    "deleteTitle": "Supprimer le v\u00e9hicule",
+    "deleteDescription": "Supprimer {name}\u00a0? Cela supprimera tous les dossiers de service et notes associ\u00e9s.",
+    "unarchiveTitle": "D\u00e9sarchiver le v\u00e9hicule",
+    "unarchiveDescription": "Restaurer {name} dans la liste des v\u00e9hicules actifs\u00a0?",
+    "vehicleUnarchived": "V\u00e9hicule d\u00e9sarchiv\u00e9",
+    "unarchiveError": "\u00c9chec de la d\u00e9sarchivation du v\u00e9hicule",
+    "deleteError": "\u00c9chec de la suppression du v\u00e9hicule",
+    "error": "\u00c9chec du chargement des v\u00e9hicules"
+  },
+  "detail": {
+    "vehicleArchived": "Ce v\u00e9hicule est archiv\u00e9",
+    "backToVehicles": "Retour aux v\u00e9hicules",
+    "newWorkOrder": "Nouvel ordre de travail",
+    "editVehicle": "Modifier le v\u00e9hicule",
+    "unarchive": "D\u00e9sarchiver",
+    "archive": "Archiver",
+    "delete": "Supprimer",
+    "notFound": "V\u00e9hicule introuvable",
+    "predictedKm": "Km pr\u00e9vus",
+    "predictedMileage": "Kilom\u00e9trage pr\u00e9vu",
+    "dismissed": "Ignor\u00e9",
+    "undo": "Annuler",
+    "serviceOverdue": "Service en retard",
+    "serviceSoon": "Service bient\u00f4t",
+    "services": "services",
+    "purchased": "Achet\u00e9 le {date}",
+    "tabs": {
+      "services": "Services",
+      "inspections": "Inspections",
+      "notes": "Notes",
+      "reminders": "Rappels"
+    },
+    "noteDeleted": "Note supprim\u00e9e",
+    "noteDeleteError": "\u00c9chec de la suppression",
+    "reminderDeleted": "Rappel supprim\u00e9",
+    "reminderDeleteError": "\u00c9chec de la suppression",
+    "vehicleUnarchived": "V\u00e9hicule d\u00e9sarchiv\u00e9",
+    "unarchiveError": "\u00c9chec de la d\u00e9sarchivation",
+    "vehicleDeleted": "V\u00e9hicule supprim\u00e9",
+    "vehicleDeleteError": "\u00c9chec de la suppression du v\u00e9hicule",
+    "deleteTitle": "Supprimer le v\u00e9hicule\u00a0?",
+    "deleteDescription": "Cela supprimera d\u00e9finitivement {name} ainsi que tous les dossiers de service, notes et rappels associ\u00e9s. Cette action est irr\u00e9versible.",
+    "edit": "Modifier"
+  },
+  "inspections": {
+    "newInspection": "Nouvelle inspection",
+    "empty": "Aucune inspection pour le moment",
+    "template": "Mod\u00e8le",
+    "progress": "Progression",
+    "status": "Statut",
+    "date": "Date",
+    "completed": "Termin\u00e9e",
+    "inProgress": "En cours"
+  },
+  "reminders": {
+    "active": "Actifs",
+    "completed": "Termin\u00e9s",
+    "all": "Tous",
+    "addReminder": "Ajouter un rappel",
+    "emptyActive": "Aucun rappel actif",
+    "emptyCompleted": "Aucun rappel termin\u00e9",
+    "empty": "Aucun rappel pour le moment",
+    "overdue": "En retard",
+    "dueSoon": "Bient\u00f4t d\u00fb",
+    "due": "\u00c9ch\u00e9ance le {date}",
+    "dueAt": "\u00c9ch\u00e9ance \u00e0 {mileage} {unit}",
+    "editTitle": "Modifier le rappel",
+    "addTitle": "Ajouter un rappel",
+    "titleLabel": "Titre *",
+    "titlePlaceholder": "ex. Vidange d'huile",
+    "descriptionLabel": "Description",
+    "descriptionPlaceholder": "D\u00e9tails optionnels...",
+    "dueDateLabel": "Date d'\u00e9ch\u00e9ance",
+    "dueMileageLabel": "Kilom\u00e9trage d'\u00e9ch\u00e9ance",
+    "dueMileagePlaceholder": "ex. 50000",
+    "reminderUpdated": "Rappel mis \u00e0 jour",
+    "reminderCreated": "Rappel cr\u00e9\u00e9",
+    "saveError": "\u00c9chec de {action} du rappel"
+  },
+  "services": {
+    "searchPlaceholder": "Rechercher des dossiers...",
+    "allTypes": "Tous les types",
+    "maintenance": "Entretien",
+    "repair": "R\u00e9paration",
+    "upgrade": "Am\u00e9lioration",
+    "inspection": "Inspection",
+    "newWorkOrder": "Nouvel ordre de travail",
+    "table": {
+      "date": "Date",
+      "title": "Titre",
+      "type": "Type",
+      "status": "Statut",
+      "mileage": "Kilom\u00e9trage",
+      "technician": "Technicien",
+      "files": "Fichiers",
+      "total": "Total"
+    },
+    "emptyFiltered": "Aucun dossier de service ne correspond \u00e0 vos filtres.",
+    "empty": "Aucun dossier de service pour le moment.",
+    "showing": "Affichage de {start}-{end} sur {total}",
+    "perPage": "par page",
+    "page": "Page {page} sur {totalPages}"
+  },
+  "notes": {
+    "addNote": "Ajouter une note",
+    "table": {
+      "title": "Titre",
+      "content": "Contenu",
+      "date": "Date"
+    },
+    "empty": "Aucune note pour le moment.",
+    "pin": "\u00c9pingler",
+    "unpin": "D\u00e9s\u00e9pingler",
+    "delete": "Supprimer",
+    "showing": "Affichage de {start}-{end} sur {total}",
+    "perPage": "par page",
+    "page": "Page {page} sur {totalPages}",
+    "editTitle": "Modifier la note",
+    "addTitle": "Ajouter une note",
+    "titleLabel": "Titre *",
+    "titlePlaceholder": "Titre de la note",
+    "contentLabel": "Contenu *",
+    "contentPlaceholder": "\u00c9crivez votre note...",
+    "noteUpdated": "Note mise \u00e0 jour",
+    "noteCreated": "Note cr\u00e9\u00e9e",
+    "titleRequired": "Le titre et le contenu sont obligatoires",
+    "saveError": "\u00c9chec de {action} de la note"
+  },
+  "form": {
+    "editTitle": "Modifier le v\u00e9hicule",
+    "addTitle": "Ajouter un nouveau v\u00e9hicule",
+    "photo": "Photo",
+    "clickToUpload": "Cliquez pour t\u00e9l\u00e9verser une photo",
+    "imageFormats": "JPEG, PNG, WebP jusqu'\u00e0 5 Mo",
+    "imageTooLarge": "Image trop volumineuse",
+    "maxFileSize": "La taille maximale est de 5 Mo",
+    "uploadingImage": "T\u00e9l\u00e9versement de l'image...",
+    "uploadFailed": "\u00c9chec du t\u00e9l\u00e9versement",
+    "imageUploaded": "Image t\u00e9l\u00e9vers\u00e9e",
+    "customer": "Client",
+    "searchCustomers": "Rechercher des clients...",
+    "noCustomerFound": "Aucun client trouv\u00e9.",
+    "noCustomer": "Aucun client",
+    "addCustomer": "Ajouter un client",
+    "make": "Marque *",
+    "model": "Mod\u00e8le *",
+    "year": "Ann\u00e9e *",
+    "mileage": "Kilom\u00e9trage",
+    "vin": "NIV",
+    "licensePlate": "Plaque d'immatriculation",
+    "color": "Couleur",
+    "fuelType": "Type de carburant",
+    "gasoline": "Essence",
+    "diesel": "Diesel",
+    "electric": "\u00c9lectrique",
+    "hybrid": "Hybride",
+    "other": "Autre",
+    "transmission": "Transmission",
+    "automatic": "Automatique",
+    "manual": "Manuelle",
+    "cvt": "CVT",
+    "engineSize": "Cylindr\u00e9e",
+    "vehicleUpdated": "V\u00e9hicule mis \u00e0 jour",
+    "vehicleAdded": "V\u00e9hicule ajout\u00e9",
+    "saveError": "\u00c9chec de l'enregistrement du v\u00e9hicule"
+  },
+  "archiveDialog": {
+    "title": "Archiver le v\u00e9hicule",
+    "description": "Archiver <span>{name}</span>\u00a0? Il sera masqu\u00e9 de la liste principale mais tout l'historique de service sera conserv\u00e9.",
+    "reasonLabel": "Raison (optionnel)",
+    "reasonPlaceholder": "ex. Vendu, accident\u00e9, d\u00e9class\u00e9...",
+    "archived": "{name} archiv\u00e9",
+    "archiveError": "\u00c9chec de l'archivage du v\u00e9hicule"
+  }
+}

+ 67 - 0
messages/fr/workBoard.json

@@ -0,0 +1,67 @@
+{
+  "page": {
+    "error": "Impossible de charger le tableau de bord des travaux"
+  },
+  "toolbar": {
+    "today": "Aujourd'hui",
+    "presenter": "Présentateur",
+    "addTechnician": "Ajouter un Technicien"
+  },
+  "board": {
+    "days": {
+      "mon": "Lun",
+      "tue": "Mar",
+      "wed": "Mer",
+      "thu": "Jeu",
+      "fri": "Ven",
+      "sat": "Sam",
+      "sun": "Dim"
+    },
+    "noTechnicians": "Aucun Technicien Encore",
+    "noTechniciansDescription": "Ajoutez un technicien pour commencer à programmer le travail sur le tableau.",
+    "failedMove": "Impossible de déplacer l'assignation",
+    "failedAssign": "Impossible d'assigner le travail",
+    "failedRemove": "Impossible de supprimer l'assignation"
+  },
+  "unassigned": {
+    "title": "Travaux Non Assignés ({count})",
+    "searchPlaceholder": "Rechercher des travaux...",
+    "empty": "Aucun travail non assigné"
+  },
+  "technician": {
+    "editTitle": "Modifier le Technicien",
+    "addTitle": "Ajouter un Technicien",
+    "name": "Nom",
+    "namePlaceholder": "par ex. Jean Dupont",
+    "color": "Couleur",
+    "linkMember": "Lier à un Membre de l'Organisation (optionnel)",
+    "standalone": "Technicien autonome",
+    "deleteTitle": "Supprimer le Technicien",
+    "deleteDescription": "Supprimer \"{name}\" du tableau de bord des travaux? Ses assignations existantes seront également supprimées.",
+    "addTechnician": "Ajouter un Technicien"
+  },
+  "jobDetail": {
+    "viewDetails": "Voir les Détails",
+    "remove": "Supprimer",
+    "date": "Date: {date}"
+  },
+  "presenter": {
+    "title": "Tableau de Bord des Travaux",
+    "day": "Jour",
+    "status": "Statut",
+    "week": "Semaine",
+    "today": "Aujourd'hui",
+    "live": "En Direct",
+    "disconnected": "Mises à jour en direct déconnectées",
+    "noTechnicians": "Aucun technicien configuré",
+    "noJobsToday": "Aucun travail aujourd'hui",
+    "job": "{count} travail",
+    "jobs": "{count} travaux",
+    "statusLabels": {
+      "pending": "En Attente",
+      "inProgress": "En Cours",
+      "waitingParts": "En Attente de Pièces",
+      "completed": "Complété"
+    }
+  }
+}

+ 80 - 0
messages/fr/workOrders.json

@@ -0,0 +1,80 @@
+{
+  "page": {
+    "error": "Impossible de charger les ordres de travail"
+  },
+  "list": {
+    "searchPlaceholder": "Rechercher des ordres de travail...",
+    "newWorkOrder": "Nouvel Ordre de Travail",
+    "selectVehicle": "Sélectionner un Véhicule",
+    "empty": "Aucun ordre de travail trouvé.",
+    "statusUpdated": "Statut mis à jour",
+    "statusTabs": {
+      "all": "Tous",
+      "active": "Actifs",
+      "pending": "En Attente",
+      "inProgress": "En Cours",
+      "waitingParts": "En Attente de Pièces",
+      "completed": "Complétés"
+    },
+    "statusActions": {
+      "startWork": "Commencer le Travail",
+      "waitingParts": "En Attente de Pièces",
+      "complete": "Terminer",
+      "resumeWork": "Reprendre le Travail",
+      "reopen": "Réouvrir"
+    },
+    "table": {
+      "invoice": "Facture nº",
+      "vehicle": "Véhicule",
+      "customer": "Client",
+      "title": "Titre",
+      "status": "Statut",
+      "tech": "Technicien",
+      "date": "Date",
+      "total": "Total"
+    },
+    "emailSubject": "Mise à jour du Statut du Véhicule: {status}"
+  },
+  "vehiclePicker": {
+    "title": "Sélectionner un Véhicule pour l'Ordre de Travail",
+    "searchPlaceholder": "Rechercher des véhicules...",
+    "empty": "Aucun véhicule trouvé.",
+    "emptySearch": "Aucun véhicule ne correspond à votre recherche.",
+    "addNewVehicle": "Ajouter un Nouveau Véhicule",
+    "noPlate": "Pas de plaque",
+    "make": "Marque *",
+    "model": "Modèle *",
+    "year": "Année *",
+    "licensePlate": "Plaque d'Immatriculation",
+    "customer": "Client",
+    "selectCustomer": "Sélectionner un client (optionnel)",
+    "createNewCustomer": "Créer un nouveau client",
+    "newCustomer": "Nouveau Client",
+    "customerName": "Nom *",
+    "customerNamePlaceholder": "Nom du client",
+    "phone": "Téléphone",
+    "phonePlaceholder": "Numéro de téléphone",
+    "creating": "Création...",
+    "createAndContinue": "Créer et Continuer",
+    "invalidYear": "Veuillez entrer une année valide",
+    "customerNameRequired": "Le nom du client est requis",
+    "failedCreateCustomer": "Impossible de créer le client",
+    "failedCreateVehicle": "Impossible de créer le véhicule",
+    "somethingWentWrong": "Une erreur s'est produite"
+  },
+  "notify": {
+    "title": "Notifier {name}",
+    "description": "Envoyer une notification au client au sujet de cette mise à jour.",
+    "sms": "SMS",
+    "email": "Email",
+    "noPhone": "(pas de numéro en dossier)",
+    "noEmail": "(pas d'email en dossier)",
+    "notAvailable": "(non disponible)",
+    "skip": "Ignorer",
+    "send": "Envoyer",
+    "smsSent": "SMS envoyé",
+    "emailSent": "Email envoyé",
+    "failedSms": "Impossible d'envoyer le SMS",
+    "failedEmail": "Impossible d'envoyer l'email"
+  }
+}

+ 105 - 0
messages/it/admin.json

@@ -0,0 +1,105 @@
+{
+  "nav": {
+    "overview": { "title": "Panoramica", "description": "Statistiche e stato della piattaforma" },
+    "users": { "title": "Utenti", "description": "Gestisci tutti gli utenti" },
+    "organizations": { "title": "Organizzazioni", "description": "Gestisci tutte le organizzazioni" },
+    "settings": { "title": "Impostazioni", "description": "Configurazione della piattaforma" }
+  },
+  "layout": {
+    "title": "Pannello di amministrazione",
+    "description": "Gestione della piattaforma per i super amministratori"
+  },
+  "overview": {
+    "totalUsers": "Utenti totali",
+    "organizations": "Organizzazioni",
+    "activeSubscriptions": "Abbonamenti attivi",
+    "monthlyRevenue": "Fatturato mensile"
+  },
+  "users": {
+    "searchPlaceholder": "Cerca per nome o email...",
+    "name": "Nome",
+    "email": "Email",
+    "role": "Ruolo",
+    "orgs": "Org.",
+    "created": "Creato",
+    "lastSeen": "Ultimo accesso",
+    "noResults": "Nessun utente corrisponde alla ricerca.",
+    "noUsers": "Nessun utente trovato.",
+    "superAdmin": "Super amministratore",
+    "user": "Utente",
+    "online": "Online",
+    "promoteTitle": "Promuovi utente",
+    "demoteTitle": "Retrocedi utente",
+    "promoteConfirm": "Sei sicuro di voler promuovere \"{name}\" a super amministratore?",
+    "demoteConfirm": "Sei sicuro di voler retrocedere \"{name}\" da super amministratore?",
+    "promote": "Promuovi",
+    "demote": "Retrocedi",
+    "promotedSuccess": "Utente promosso con successo",
+    "demotedSuccess": "Utente retrocesso con successo",
+    "failedUpdate": "Aggiornamento utente non riuscito",
+    "deleteTitle": "Elimina utente",
+    "deleteConfirm": "Sei sicuro di voler eliminare permanentemente \"{name}\" ({email})? Questa azione non può essere annullata.",
+    "delete": "Elimina",
+    "deletedSuccess": "Utente eliminato con successo",
+    "failedDelete": "Eliminazione utente non riuscita",
+    "promoteToSuperAdmin": "Promuovi a super amministratore",
+    "demoteFromSuperAdmin": "Retrocedi da super amministratore",
+    "deleteUser": "Elimina utente"
+  },
+  "organizations": {
+    "searchPlaceholder": "Cerca per nome organizzazione...",
+    "name": "Nome",
+    "owner": "Proprietario",
+    "members": "Membri",
+    "subscription": "Abbonamento",
+    "created": "Creato",
+    "noResults": "Nessuna organizzazione corrisponde alla ricerca.",
+    "noOrganizations": "Nessuna organizzazione trovata.",
+    "none": "Nessuno",
+    "deleteTitle": "Elimina organizzazione",
+    "deleteConfirm": "Sei sicuro di voler eliminare permanentemente \"{name}\" e tutti i suoi dati? Questa azione non può essere annullata.",
+    "delete": "Elimina",
+    "deletedSuccess": "Organizzazione eliminata con successo",
+    "failedDelete": "Eliminazione organizzazione non riuscita",
+    "deleteOrganization": "Elimina organizzazione"
+  },
+  "adminSettings": {
+    "platformTitle": "Impostazioni piattaforma",
+    "platformDescription": "Opzioni generali di configurazione della piattaforma",
+    "disableRegistration": "Disabilita registrazione",
+    "disableRegistrationHint": "Blocca le nuove registrazioni degli utenti sulla piattaforma",
+    "emailTitle": "Impostazioni email",
+    "emailDescription": "Provider email della piattaforma — Utilizzato per le email a livello di sistema come il ripristino della password e gli inviti al team. Questo è anche il provider email predefinito per tutte le organizzazioni. Le organizzazioni possono configurare opzionalmente il proprio provider email in Impostazioni > Email.",
+    "smtpHost": "Host SMTP",
+    "smtpPort": "Porta SMTP",
+    "username": "Nome utente",
+    "password": "Password",
+    "fromEmail": "Email mittente",
+    "fromName": "Nome mittente",
+    "tlsConnection": "Connessione TLS (Porta 465)",
+    "tlsConnectionHint": "Abilitare per TLS implicito. Disabilitare per STARTTLS (porta 587/25).",
+    "verifyTls": "Verifica certificati TLS",
+    "verifyTlsHint": "Disabilitare per consentire certificati autofirmati (non consigliato per la produzione)",
+    "requireTlsUpgrade": "Richiedi aggiornamento TLS",
+    "requireTlsUpgradeHint": "Forza l'aggiornamento TLS sulle connessioni STARTTLS",
+    "apiKey": "Chiave API",
+    "serverToken": "Server Token",
+    "domain": "Dominio",
+    "region": "Regione",
+    "awsRegion": "Regione AWS",
+    "accessKeyId": "ID chiave di accesso",
+    "secretAccessKey": "Chiave di accesso segreta",
+    "awsRegionHint": "La regione AWS in cui è configurato SES (es. us-east-1, eu-west-1)",
+    "sendTestEmail": "Invia email di prova",
+    "testEmailHint": "Salva prima le impostazioni, poi invia un'email di prova al tuo account",
+    "saveSettings": "Salva impostazioni",
+    "saved": "Impostazioni salvate con successo",
+    "failedSave": "Salvataggio impostazioni non riuscito",
+    "testSentTo": "Email di prova inviata a {email}",
+    "testFailed": "Test email non riuscito",
+    "resendApiKeyHint": "Ottieni la tua chiave API da resend.com",
+    "postmarkApiKeyHint": "Ottieni il tuo Server Token da postmarkapp.com",
+    "mailgunApiKeyHint": "Ottieni la tua chiave API da app.mailgun.com",
+    "sendgridApiKeyHint": "Ottieni la tua chiave API da app.sendgrid.com"
+  }
+}

+ 73 - 0
messages/it/auth.json

@@ -0,0 +1,73 @@
+{
+  "signIn": {
+    "title": "Bentornato",
+    "description": "Acceda alla Sua officina",
+    "passwordPlaceholder": "Inserisca la Sua password",
+    "forgotPassword": "Password dimenticata?",
+    "noAccount": "Non ha un account?",
+    "createOne": "Ne crei uno",
+    "termsAgreement": "Utilizzando questo servizio accetta i nostri",
+    "errors": {
+      "tooManyAttempts": "Troppi tentativi, riprovi più tardi.",
+      "invalidCredentials": "Email o password non validi"
+    }
+  },
+  "signUp": {
+    "title": "Crei un account",
+    "descriptionInvite": "Crei il Suo account per unirsi al team",
+    "descriptionDefault": "Configuri la Sua officina per gestire i veicoli dei clienti",
+    "fullName": "Nome completo",
+    "fullNamePlaceholder": "Mario Rossi",
+    "passwordPlaceholder": "Crei una password sicura",
+    "agreeToTerms": "Accetto i",
+    "createAccountJoin": "Crea account e unisciti al team",
+    "createAccount": "Crea account",
+    "alreadyHaveAccount": "Ha già un account?",
+    "errors": {
+      "couldNotCreate": "Impossibile creare l'account",
+      "registrationDisabled": "La registrazione è attualmente disabilitata. La preghiamo di contattare l'amministratore.",
+      "invitationFailed": "Impossibile accettare l'invito"
+    }
+  },
+  "forgotPassword": {
+    "title": "Reimposti la Sua password",
+    "description": "Inserisca la Sua email e Le invieremo un link per il ripristino",
+    "sendResetLink": "Invia link di ripristino",
+    "backToSignIn": "Torna all'accesso",
+    "successMessage": "Se esiste un account con quell'indirizzo email, è stato inviato un link per il ripristino della password. Controlli la Sua casella di posta."
+  },
+  "resetPassword": {
+    "title": "Imposti una nuova password",
+    "description": "Inserisca la Sua nuova password qui sotto",
+    "newPassword": "Nuova password",
+    "newPasswordPlaceholder": "Inserisca la nuova password",
+    "confirmPassword": "Conferma password",
+    "confirmPasswordPlaceholder": "Confermi la nuova password",
+    "resetButton": "Reimposta password",
+    "backToSignIn": "Torna all'accesso",
+    "requestNewLink": "Richiedi un nuovo link",
+    "invalidToken": "Token di ripristino non valido o mancante. La preghiamo di richiedere un nuovo link per il ripristino della password.",
+    "successMessage": "La Sua password è stata reimpostata con successo. Ora può accedere con la nuova password.",
+    "errors": {
+      "passwordsMismatch": "Le password non corrispondono.",
+      "passwordTooShort": "La password deve contenere almeno 8 caratteri.",
+      "resetFailed": "Impossibile reimpostare la password. Il link potrebbe essere scaduto."
+    }
+  },
+  "verify2fa": {
+    "title": "Autenticazione a due fattori",
+    "descriptionTotp": "Inserisca il codice a 6 cifre dalla Sua app di autenticazione",
+    "descriptionBackup": "Inserisca uno dei Suoi codici di backup",
+    "authCode": "Codice di autenticazione",
+    "backupCode": "Codice di backup",
+    "backupCodePlaceholder": "Inserisca il codice di backup",
+    "totpPlaceholder": "000000",
+    "trustDevice": "Considera attendibile questo dispositivo per 30 giorni",
+    "verify": "Verifica",
+    "useAuthenticator": "Usa l'app di autenticazione",
+    "useBackupCode": "Usa un codice di backup",
+    "errors": {
+      "invalidCode": "Codice non valido"
+    }
+  }
+}

+ 83 - 0
messages/it/billing.json

@@ -0,0 +1,83 @@
+{
+  "history": {
+    "title": "Cronologia fatturazione",
+    "recurring": "Ricorrente",
+    "totalRevenue": "Ricavi totali",
+    "collected": "Incassato",
+    "outstanding": "In sospeso",
+    "statusAll": "Tutti",
+    "statusPaid": "Pagato",
+    "statusPartial": "Parziale",
+    "statusUnpaid": "Non pagato",
+    "searchPlaceholder": "Cerca fatture...",
+    "search": "Cerca",
+    "columnInvoice": "Fattura N°",
+    "columnTitle": "Titolo",
+    "columnVehicle": "Veicolo",
+    "columnCustomer": "Cliente",
+    "columnDate": "Data",
+    "columnTotal": "Totale",
+    "columnPaid": "Pagato",
+    "columnBalance": "Saldo",
+    "noRecords": "Nessun record di fatturazione trovato."
+  },
+  "recurring": {
+    "title": "Ricorrente",
+    "billingHistory": "Cronologia fatturazione",
+    "processNow": "Elabora ora",
+    "newRecurring": "Nuova fattura ricorrente",
+    "noInvoices": "Nessuna fattura ricorrente ancora.",
+    "columnTitle": "Titolo",
+    "columnVehicle": "Veicolo",
+    "columnCustomer": "Cliente",
+    "columnFrequency": "Frequenza",
+    "columnNextRun": "Prossima esecuzione",
+    "columnAmount": "Importo",
+    "columnStatus": "Stato",
+    "columnRuns": "Esecuzioni",
+    "statusActive": "Attiva",
+    "statusPaused": "In pausa",
+    "frequencyWeekly": "Settimanale",
+    "frequencyBiweekly": "Bisettimanale",
+    "frequencyMonthly": "Mensile",
+    "frequencyQuarterly": "Trimestrale",
+    "frequencyYearly": "Annuale",
+    "serviceTypeMaintenance": "Manutenzione",
+    "serviceTypeRepair": "Riparazione",
+    "serviceTypeInspection": "Ispezione",
+    "serviceTypeUpgrade": "Aggiornamento",
+    "dialogTitle": "Nuova fattura ricorrente",
+    "titleLabel": "Titolo *",
+    "titlePlaceholder": "es. Cambio olio mensile",
+    "vehicleLabel": "Veicolo *",
+    "selectVehicle": "Seleziona veicolo",
+    "descriptionLabel": "Descrizione",
+    "frequencyLabel": "Frequenza",
+    "startDate": "Data di inizio *",
+    "endDate": "Data di fine",
+    "serviceType": "Tipo di servizio",
+    "baseCost": "Costo base",
+    "taxRate": "Aliquota fiscale (%)",
+    "invoiceNotes": "Note fattura",
+    "templateParts": "Parti del modello",
+    "addPart": "Aggiungi parte",
+    "partName": "Nome parte",
+    "partNumber": "Parte N°",
+    "templateLabor": "Manodopera del modello",
+    "addLabor": "Aggiungi manodopera",
+    "description": "Descrizione",
+    "hours": "Ore",
+    "rate": "Tariffa",
+    "cancel": "Annulla",
+    "create": "Crea",
+    "requiredFields": "Titolo e veicolo sono obbligatori",
+    "created": "Fattura ricorrente creata",
+    "failedCreate": "Creazione fallita",
+    "statusUpdated": "Stato aggiornato",
+    "failedUpdate": "Aggiornamento fallito",
+    "deleted": "Fattura ricorrente eliminata",
+    "failedDelete": "Eliminazione fallita",
+    "processed": "{count} fattura/e elaborata/e",
+    "failedProcess": "Elaborazione fallita"
+  }
+}

+ 61 - 0
messages/it/calendar.json

@@ -0,0 +1,61 @@
+{
+  "weekdays": {
+    "sun": "Dom",
+    "mon": "Lun",
+    "tue": "Mar",
+    "wed": "Mer",
+    "thu": "Gio",
+    "fri": "Ven",
+    "sat": "Sab"
+  },
+  "months": {
+    "january": "Gennaio",
+    "february": "Febbraio",
+    "march": "Marzo",
+    "april": "Aprile",
+    "may": "Maggio",
+    "june": "Giugno",
+    "july": "Luglio",
+    "august": "Agosto",
+    "september": "Settembre",
+    "october": "Ottobre",
+    "november": "Novembre",
+    "december": "Dicembre"
+  },
+  "stats": {
+    "services": "Servizi",
+    "overdue": "Scaduti",
+    "pendingQuotes": "Preventivi in attesa",
+    "revenue": "Fatturato"
+  },
+  "today": "Oggi",
+  "workOrder": "Ordine di lavoro",
+  "filters": {
+    "services": "Servizi",
+    "reminders": "Promemoria",
+    "quotes": "Preventivi"
+  },
+  "selectVehicle": "Seleziona veicolo per ordine di lavoro",
+  "more": "+ {count} altri",
+  "events": {
+    "status": {
+      "completed": "Completato",
+      "inProgress": "In corso",
+      "waitingParts": "In attesa di ricambi",
+      "pending": "In attesa",
+      "sent": "Inviato",
+      "approved": "Approvato",
+      "draft": "Bozza",
+      "done": "Fatto",
+      "overdue": "Scaduto",
+      "upcoming": "In arrivo"
+    },
+    "type": {
+      "service": "Servizio",
+      "reminder": "Promemoria",
+      "quote": "Preventivo"
+    },
+    "noEvents": "Nessun evento",
+    "selectDay": "Seleziona un giorno con eventi o crea un nuovo ordine di lavoro."
+  }
+}

+ 43 - 0
messages/it/common.json

@@ -0,0 +1,43 @@
+{
+  "brandName": "Torqvoice",
+  "buttons": {
+    "signIn": "Accedi",
+    "save": "Salva",
+    "cancel": "Annulla",
+    "delete": "Elimina",
+    "submit": "Invia",
+    "back": "Indietro",
+    "confirm": "Conferma",
+    "saveChanges": "Salva modifiche",
+    "edit": "Modifica",
+    "add": "Aggiungi"
+  },
+  "form": {
+    "email": "Email",
+    "password": "Password",
+    "emailPlaceholder": "you@example.com",
+    "name": "Nome",
+    "phone": "Telefono",
+    "company": "Azienda",
+    "address": "Indirizzo",
+    "notes": "Note"
+  },
+  "errors": {
+    "unexpected": "Si è verificato un errore imprevisto. La preghiamo di riprovare.",
+    "error": "Errore"
+  },
+  "terms": {
+    "termsOfService": "Termini di servizio"
+  },
+  "errorPage": {
+    "title": "Qualcosa è andato storto",
+    "description": "Il motore si è spento inaspettatamente. I nostri meccanici sono stati avvisati e stanno lavorando alla riparazione.",
+    "tryAgain": "Riprova",
+    "backToDashboard": "Torna alla dashboard"
+  },
+  "notFoundPage": {
+    "title": "Pagina non trovata",
+    "description": "Sembra che questo pezzo sia ancora in officina. La pagina che stai cercando non esiste o è stata spostata.",
+    "backToDashboard": "Torna alla dashboard"
+  }
+}

+ 76 - 0
messages/it/customers.json

@@ -0,0 +1,76 @@
+{
+  "list": {
+    "addCustomer": "Aggiungi cliente",
+    "searchPlaceholder": "Cerca clienti...",
+    "table": {
+      "name": "Nome",
+      "company": "Azienda",
+      "phone": "Telefono",
+      "email": "Email",
+      "vehicles": "Veicoli"
+    },
+    "emptySearch": "Nessun cliente corrisponde alla ricerca.",
+    "empty": "Nessun cliente presente.",
+    "deleteTitle": "Elimina cliente",
+    "deleteDescription": "Eliminare {name}? I suoi veicoli verranno scollegati.",
+    "deleteError": "Eliminazione del cliente non riuscita",
+    "error": "Caricamento dei clienti non riuscito"
+  },
+  "detail": {
+    "backToCustomers": "Torna ai clienti",
+    "editCustomer": "Modifica cliente",
+    "notFound": "Cliente non trovato",
+    "emailCopied": "Email copiata negli appunti",
+    "phoneCopied": "Numero di telefono copiato negli appunti",
+    "tabs": {
+      "vehicles": "Veicoli ({count})",
+      "messages": "Messaggi",
+      "requests": "Richieste ({count})"
+    },
+    "vehicleTable": {
+      "plate": "Targa",
+      "vehicle": "Veicolo",
+      "mileage": "Chilometraggio",
+      "services": "Servizi"
+    },
+    "noVehicles": "Nessun veicolo assegnato a questo cliente"
+  },
+  "form": {
+    "addTitle": "Aggiungi nuovo cliente",
+    "editTitle": "Modifica cliente",
+    "nameRequired": "Nome *",
+    "namePlaceholder": "Mario Rossi",
+    "emailPlaceholder": "mario@esempio.it",
+    "phonePlaceholder": "333 1234567",
+    "companyPlaceholder": "Nome azienda",
+    "addressPlaceholder": "Via Roma 1, 00100 Roma",
+    "notesPlaceholder": "Note aggiuntive...",
+    "customerUpdated": "Cliente aggiornato",
+    "customerCreated": "Cliente creato",
+    "saveError": "Salvataggio del cliente non riuscito"
+  },
+  "serviceRequests": {
+    "statusNew": "Nuova",
+    "statusConverted": "Ordine di lavoro creato",
+    "statusDismissed": "Archiviata",
+    "createWorkOrder": "Crea ordine di lavoro",
+    "contactCustomer": "Contatta il cliente",
+    "dismiss": "Archivia",
+    "viewWorkOrder": "Visualizza ordine di lavoro",
+    "notes": "Note",
+    "hideNotes": "Nascondi note",
+    "staffNotes": "Note del personale:",
+    "notesPlaceholder": "Aggiungi note interne...",
+    "saveNotes": "Salva note",
+    "submitted": "Inviata il {date}",
+    "preferred": "Preferita: {date}",
+    "workOrderCreated": "Ordine di lavoro creato",
+    "workOrderError": "Creazione dell'ordine di lavoro non riuscita",
+    "requestDismissed": "Richiesta archiviata",
+    "dismissError": "Archiviazione della richiesta non riuscita",
+    "notesSaved": "Note salvate",
+    "notesSaveError": "Salvataggio delle note non riuscito",
+    "smsLabel": "Messaggio di richiesta servizio",
+    "smsDefault": "Salve {name}, riguardo alla sua richiesta di servizio per il suo {vehicle}: "
+  }
+}

+ 89 - 0
messages/it/dashboard.json

@@ -0,0 +1,89 @@
+{
+  "stats": {
+    "activeJobs": "Lavori attivi",
+    "pending": "In attesa",
+    "todaysRevenue": "Fatturato odierno",
+    "totalCustomers": "Clienti totali"
+  },
+  "customize": "Personalizza",
+  "showCards": "Mostra schede",
+  "cards": {
+    "maintenance": "Manutenzione prevista",
+    "reminders": "Promemoria in scadenza",
+    "inspections": "Ispezioni",
+    "quoteRequests": "Richieste di preventivo",
+    "quoteResponses": "Risposte ai preventivi",
+    "sms": "Messaggi recenti",
+    "notifications": "Notifiche recenti",
+    "recentCompleted": "Completati di recente",
+    "activeJobs": "Lavori attivi"
+  },
+  "maintenance": {
+    "title": "Manutenzione prevista in scadenza",
+    "description": "Basato sullo storico degli interventi e sul chilometraggio giornaliero stimato",
+    "overdue": "Scaduto",
+    "approaching": "In avvicinamento",
+    "estimated": "Stima {mileage} {unit}",
+    "sinceLastService": "{mileage} {unit} dall'ultimo intervento",
+    "certainty": "{percent}% di certezza"
+  },
+  "reminders": {
+    "title": "Promemoria in scadenza",
+    "overdue": "Scaduto",
+    "dueSoon": "In scadenza"
+  },
+  "messages": {
+    "title": "Messaggi recenti",
+    "viewAll": "Visualizza tutti",
+    "you": "Lei: "
+  },
+  "notifications": {
+    "title": "Notifiche recenti"
+  },
+  "inspections": {
+    "title": "Ispezioni",
+    "inProgress": "In corso",
+    "completed": "Completato",
+    "itemCount": "{inspected}/{total} elementi"
+  },
+  "quoteRequests": {
+    "title": "Richieste di preventivo",
+    "description": "Clienti che richiedono preventivi dalle ispezioni condivise",
+    "pending": "In attesa",
+    "createQuote": "Crea preventivo",
+    "itemsRequested": "{count, plural, one {# elemento richiesto} other {# elementi richiesti}}"
+  },
+  "quoteResponses": {
+    "title": "Risposte ai preventivi dei clienti",
+    "description": "Preventivi accettati dai clienti o per i quali sono state richieste modifiche",
+    "accepted": "Accettato",
+    "changesRequested": "Modifiche richieste",
+    "convertToWorkOrder": "Converti in ordine di lavoro",
+    "viewQuote": "Visualizza preventivo"
+  },
+  "recentCompleted": {
+    "title": "Completati di recente",
+    "date": "Data",
+    "vehicle": "Veicolo",
+    "customer": "Cliente",
+    "serviceTitle": "Titolo",
+    "total": "Totale",
+    "empty": "Nessun intervento completato"
+  },
+  "activeJobsTable": {
+    "title": "Lavori attivi",
+    "vehicle": "Veicolo",
+    "customer": "Cliente",
+    "serviceTitle": "Titolo",
+    "status": "Stato",
+    "tech": "Tecnico",
+    "empty": "Nessun lavoro attivo"
+  },
+  "relativeTime": {
+    "now": "ora",
+    "minutes": "{count}m",
+    "hours": "{count}h",
+    "days": "{count}g"
+  },
+  "error": "Impossibile caricare la dashboard"
+}

+ 87 - 0
messages/it/inventory.json

@@ -0,0 +1,87 @@
+{
+  "searchPlaceholder": "Cerca ricambi...",
+  "categoryPlaceholder": "Categoria",
+  "allCategories": "Tutte le categorie",
+  "applyMarkup": "Applica ricarico",
+  "addPart": "Aggiungi ricambio",
+  "table": {
+    "partNumber": "N. ricambio",
+    "name": "Nome",
+    "category": "Categoria",
+    "inStock": "Disponibile",
+    "unitCost": "Costo unitario",
+    "sellPrice": "Prezzo di vendita",
+    "supplier": "Fornitore",
+    "location": "Posizione",
+    "low": "Basso"
+  },
+  "empty": {
+    "noMatch": "Nessun ricambio corrisponde alla ricerca.",
+    "noParts": "Nessun ricambio in inventario."
+  },
+  "actions": {
+    "edit": "Modifica",
+    "delete": "Elimina"
+  },
+  "deletePart": {
+    "title": "Elimina ricambio",
+    "description": "Eliminare \"{name}\" dall'inventario? Questa azione non può essere annullata.",
+    "confirm": "Elimina"
+  },
+  "markup": {
+    "title": "Applica ricarico a tutti i ricambi",
+    "multiplierLabel": "Moltiplicatore di ricarico",
+    "description": "Il prezzo di vendita sarà impostato a costo x {multiplier} per tutti i {count} ricambi.",
+    "percentage": "({percent}% di ricarico)",
+    "cancel": "Annulla",
+    "apply": "Applica"
+  },
+  "errors": {
+    "multiplierPositive": "Il moltiplicatore deve essere maggiore di 0",
+    "deleteFailed": "Impossibile eliminare il ricambio",
+    "applyFailed": "Impossibile applicare il ricarico",
+    "saveFailed": "Impossibile salvare il ricambio",
+    "error": "Errore"
+  },
+  "form": {
+    "editPart": "Modifica ricambio",
+    "addNewPart": "Aggiungi nuovo ricambio",
+    "supplierLink": "Link fornitore",
+    "supplierLinkPlaceholder": "https://supplier.com/product/...",
+    "fetch": "Recupera",
+    "fetchFailed": "Recupero non riuscito",
+    "fetchError": "Impossibile recuperare i metadati dall'URL",
+    "image": "Immagine",
+    "dropOrClick": "Trascina un'immagine o clicca",
+    "replace": "Sostituisci",
+    "upload": "Carica",
+    "imageUploading": "Caricamento immagine...",
+    "imageUploaded": "Immagine caricata",
+    "imageUploadFailed": "Impossibile caricare l'immagine",
+    "imageTypeError": "Sono consentite solo immagini JPEG, PNG, WebP e AVIF",
+    "imageSizeError": "L'immagine deve essere inferiore a 5 MB",
+    "partNumber": "Numero ricambio",
+    "partNumberPlaceholder": "BRK-001",
+    "nameLabel": "Nome *",
+    "namePlaceholder": "Kit pastiglie freno",
+    "category": "Categoria",
+    "categoryPlaceholder": "Freni",
+    "location": "Posizione",
+    "locationPlaceholder": "Scaffale A-3",
+    "quantity": "Quantità",
+    "minQty": "Qtà min.",
+    "unitCost": "Costo unitario",
+    "sellPrice": "Prezzo di vendita",
+    "supplier": "Fornitore",
+    "supplierPlaceholder": "AutoParts S.r.l.",
+    "supplierPhone": "Telefono fornitore",
+    "supplierPhonePlaceholder": "(555) 123-4567",
+    "supplierEmail": "E-mail fornitore",
+    "supplierEmailPlaceholder": "orders@autoparts.com",
+    "description": "Descrizione",
+    "descriptionPlaceholder": "Descrizione del ricambio...",
+    "cancel": "Annulla",
+    "saveChanges": "Salva modifiche",
+    "addPart": "Aggiungi ricambio"
+  }
+}

+ 45 - 0
messages/it/messages.json

@@ -0,0 +1,45 @@
+{
+  "page": {
+    "featureName": "Messaggi SMS",
+    "featureDescription": "Invia e ricevi messaggi SMS con i tuoi clienti direttamente da Torqvoice."
+  },
+  "notConfigured": {
+    "title": "SMS non configurato",
+    "description": "Configura un provider SMS per inviare e ricevere messaggi con i tuoi clienti. Supporta Twilio, Vonage e Telnyx.",
+    "goToSettings": "Vai alle impostazioni SMS"
+  },
+  "threads": {
+    "title": "Conversazioni",
+    "empty": "Nessuna conversazione. Invia un SMS dalla pagina di un cliente per iniziare.",
+    "loadMore": "Carica altro",
+    "selectConversation": "Seleziona una conversazione",
+    "you": "Tu: ",
+    "deleteConversation": "Elimina conversazione",
+    "deleteTitle": "Eliminare la conversazione?",
+    "deleteDescription": "Tutti i messaggi con {name} verranno eliminati definitivamente. Questa azione non può essere annullata.",
+    "deleted": "Conversazione eliminata",
+    "deleteError": "Impossibile eliminare la conversazione"
+  },
+  "conversation": {
+    "noPhone": "Nessun numero di telefono registrato per {name}. Aggiungi un numero di telefono per iniziare a inviare messaggi.",
+    "loadOlder": "Carica precedenti",
+    "empty": "Nessun messaggio. Invia il primo messaggio a {name}.",
+    "placeholder": "Messaggio a {name}...",
+    "deleteMessage": "Elimina messaggio",
+    "deleteTitle": "Eliminare il messaggio?",
+    "deleteDescription": "Questo messaggio verrà eliminato definitivamente dalla conversazione. Questa azione non può essere annullata.",
+    "messageDeleted": "Messaggio eliminato",
+    "deleteError": "Impossibile eliminare il messaggio",
+    "sendError": "Impossibile inviare l'SMS"
+  },
+  "sendDialog": {
+    "send": "Invia {label}",
+    "message": "Messaggio",
+    "placeholder": "Scrivi il tuo messaggio...",
+    "characters": "{count} / 1600 caratteri",
+    "sendSms": "Invia SMS",
+    "smsSent": "SMS inviato",
+    "sentTo": "{label} è stato inviato a {name} ({phone})",
+    "sendError": "Impossibile inviare l'SMS"
+  }
+}

+ 85 - 0
messages/it/navigation.json

@@ -0,0 +1,85 @@
+{
+  "breadcrumbs": {
+    "dashboard": "Dashboard",
+    "vehicles": "Veicoli",
+    "allVehicles": "Tutti i veicoli",
+    "vehicleDetails": "Dettagli veicolo",
+    "customers": "Clienti",
+    "allCustomers": "Tutti i clienti",
+    "customerDetails": "Dettagli cliente",
+    "workOrders": "Ordini di lavoro",
+    "allWorkOrders": "Tutti gli ordini di lavoro",
+    "quotes": "Preventivi",
+    "allQuotes": "Tutti i preventivi",
+    "newQuote": "Nuovo preventivo",
+    "quoteDetails": "Dettagli preventivo",
+    "edit": "Modifica",
+    "billing": "Fatturazione",
+    "billingHistory": "Storico fatturazione",
+    "inventory": "Inventario",
+    "allParts": "Tutti i ricambi",
+    "reports": "Report",
+    "workBoard": "Bacheca lavori",
+    "presenter": "Presentatore",
+    "adminOverview": "Panoramica amministrazione",
+    "admin": "Amministrazione",
+    "users": "Utenti",
+    "organizations": "Organizzazioni",
+    "settings": "Impostazioni",
+    "company": "Azienda",
+    "account": "Account",
+    "customFields": "Campi personalizzati",
+    "templates": "Modelli",
+    "team": "Team",
+    "invoice": "Fattura",
+    "payment": "Pagamento",
+    "currency": "Valuta",
+    "workshop": "Officina",
+    "appearance": "Aspetto",
+    "email": "Email",
+    "sms": "SMS",
+    "about": "Informazioni",
+    "home": "Home",
+    "newServiceRecord": "Nuovo intervento di servizio",
+    "serviceDetails": "Dettagli intervento",
+    "data": "Dati",
+    "license": "Licenza",
+    "subscription": "Abbonamento",
+    "maintenance": "Manutenzione",
+    "customerPortal": "Portale clienti"
+  },
+  "search": "Cerca...",
+  "shortcut": "Ctrl+K",
+  "newWorkOrder": "Nuovo ordine di lavoro",
+  "purchaseWhiteLabel": "Acquista White-Label",
+  "sidebar": {
+    "dashboard": "Dashboard",
+    "clients": "Clienti",
+    "customers": "Clienti",
+    "messages": "Messaggi",
+    "workshop": "Officina",
+    "vehicles": "Veicoli",
+    "workOrders": "Ordini di lavoro",
+    "inspections": "Ispezioni",
+    "calendar": "Calendario",
+    "workBoard": "Bacheca lavori",
+    "business": "Attività",
+    "quotes": "Preventivi",
+    "billing": "Fatturazione",
+    "inventory": "Inventario",
+    "reports": "Report",
+    "settings": "Impostazioni",
+    "superAdmin": "Super Admin",
+    "adminPanel": "Pannello di amministrazione",
+    "organizations": "Organizzazioni",
+    "noOrganization": "Nessuna organizzazione",
+    "addNewCompany": "Aggiungi nuova azienda",
+    "companyName": "Nome azienda",
+    "companyPlaceholder": "es. Officina Mario Rossi",
+    "cancel": "Annulla",
+    "createCompany": "Crea azienda",
+    "lightMode": "Modalità chiara",
+    "darkMode": "Modalità scura",
+    "signOut": "Esci"
+  }
+}

+ 10 - 0
messages/it/notifications.json

@@ -0,0 +1,10 @@
+{
+  "panel": {
+    "title": "Notifiche",
+    "markAllRead": "Segna tutto come letto",
+    "empty": "Nessuna notifica ancora",
+    "confirmDelete": "Sei sicuro?",
+    "delete": "Elimina",
+    "cancel": "Annulla"
+  }
+}

+ 99 - 0
messages/it/pdf.json

@@ -0,0 +1,99 @@
+{
+  "invoice": {
+    "title": "FATTURA",
+    "due": "Scadenza: {date}",
+    "billTo": "Fatturare a",
+    "vehicle": "Veicolo",
+    "vin": "VIN: {vin}",
+    "plate": "Targa: {plate}",
+    "mileage": "Chilometraggio: {mileage}",
+    "km": "km",
+    "mi": "mi",
+    "service": "Servizio",
+    "type": "Tipo: {type}",
+    "tech": "Tecnico: {tech}",
+    "parts": "Ricambi",
+    "partNumber": "N. pezzo",
+    "description": "Descrizione",
+    "qty": "Qtà",
+    "unitPrice": "Prezzo unitario",
+    "total": "Totale",
+    "labor": "Manodopera",
+    "hours": "Ore",
+    "rate": "Tariffa",
+    "ratePerHour": "{rate}/ora",
+    "subtotal": "Subtotale",
+    "discount": "Sconto",
+    "discountPercent": "Sconto ({percent}%)",
+    "tax": "IVA ({rate}%)",
+    "paymentsReceived": "Pagamenti Ricevuti",
+    "paidInFull": "SALDATO",
+    "amountDue": "Importo Dovuto",
+    "notes": "Note",
+    "bankAccount": "Til Konto / Conto Bancario",
+    "diagnosticNotes": "Note Diagnostiche",
+    "attachedDocuments": "Documenti Allegati",
+    "seeAppendedPages": "{name} (vedi pagine allegate)",
+    "serviceImages": "Immagini del Servizio",
+    "paymentTerms": "Condizioni di Pagamento: {terms}",
+    "viewPortal": "Visualizza il tuo portale: {url}",
+    "professionalWorkshop": "Gestione Officina Professionale",
+    "tel": "Tel: {phone}",
+    "org": "Org: {org}"
+  },
+  "quote": {
+    "title": "PREVENTIVO",
+    "to": "A",
+    "vehicle": "Veicolo",
+    "vin": "VIN: {vin}",
+    "plate": "Targa: {plate}",
+    "quoteDetails": "Dettagli Preventivo",
+    "parts": "Ricambi",
+    "partNumber": "N. pezzo",
+    "description": "Descrizione",
+    "qty": "Qtà",
+    "unitPrice": "Prezzo unitario",
+    "total": "Totale",
+    "labor": "Manodopera",
+    "hours": "Ore",
+    "rate": "Tariffa",
+    "ratePerHour": "{rate}/ora",
+    "subtotal": "Subtotale",
+    "discount": "Sconto",
+    "discountPercent": "Sconto ({percent}%)",
+    "tax": "IVA ({rate}%)",
+    "validUntil": "Valido fino al: {date}",
+    "attachedDocuments": "Documenti Allegati",
+    "attached": "{name} (allegato)",
+    "quoteImages": "Immagini Preventivo",
+    "validityFooterUntil": "Questo preventivo è valido fino al {date}",
+    "validityFooter30": "Questo preventivo è valido per 30 giorni",
+    "viewPortal": "Visualizza il tuo portale: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "inspection": {
+    "title": "ISPEZIONE VEICOLO",
+    "vehicle": "Veicolo",
+    "vin": "VIN: {vin}",
+    "plate": "Targa: {plate}",
+    "mileage": "Chilometraggio: {mileage}",
+    "customer": "Cliente",
+    "inspectionDetails": "Dettagli Ispezione",
+    "statusCompleted": "Completato",
+    "statusInProgress": "In Corso",
+    "status": "Stato: {status}",
+    "inspected": "Ispezionato",
+    "pass": "Superato",
+    "attention": "Attenzione",
+    "fail": "Non Superato",
+    "item": "Elemento",
+    "statusColumn": "Stato",
+    "notesColumn": "Note",
+    "footerText": "Ispezione Veicolo — {shopName}",
+    "viewPortal": "Visualizza il tuo portale: {url}",
+    "tel": "Tel: {phone}"
+  },
+  "common": {
+    "poweredBy": "Powered by"
+  }
+}

+ 133 - 0
messages/it/portal.json

@@ -0,0 +1,133 @@
+{
+  "login": {
+    "signInTitle": "Accedi al tuo portale clienti",
+    "emailLabel": "Indirizzo email",
+    "emailPlaceholder": "tu@esempio.com",
+    "sendLink": "Invia link di accesso",
+    "checkEmail": "Controlla la tua email",
+    "checkEmailDescription": "Se esiste un account per quell'email, abbiamo inviato un link di accesso. Il link scade in 15 minuti.",
+    "tryDifferentEmail": "Prova con un'altra email",
+    "notFound": "Non Trovato",
+    "portalNotExist": "Questo portale non esiste.",
+    "portalNotAvailable": "Portale Non Disponibile",
+    "portalNotEnabled": "Il portale clienti non è abilitato per questa organizzazione."
+  },
+  "nav": {
+    "dashboard": "Dashboard",
+    "vehicles": "Veicoli",
+    "invoices": "Fatture",
+    "quotes": "Preventivi",
+    "inspections": "Ispezioni",
+    "requestService": "Richiedi Servizio"
+  },
+  "header": {
+    "signOut": "Esci"
+  },
+  "shell": {
+    "notFound": "Non Trovato",
+    "portalNotExist": "Questo portale non esiste.",
+    "portalNotAvailable": "Portale Non Disponibile",
+    "portalNotEnabled": "Il portale clienti non è abilitato per questa organizzazione."
+  },
+  "dashboard": {
+    "welcome": "Benvenuto, {name}",
+    "overview": "Ecco una panoramica del tuo account.",
+    "vehicles": "Veicoli",
+    "openInvoices": "Fatture Aperte",
+    "pendingQuotes": "Preventivi in Attesa",
+    "recentInvoices": "Fatture Recenti",
+    "recentQuotes": "Preventivi Recenti",
+    "pendingServiceRequests": "Richieste di Servizio in Attesa",
+    "viewAll": "Visualizza tutti",
+    "newRequest": "Nuova richiesta",
+    "noInvoicesYet": "Nessuna fattura ancora.",
+    "noQuotesYet": "Nessun preventivo ancora.",
+    "view": "Visualizza",
+    "pending": "In attesa",
+    "failedToLoad": "Caricamento della dashboard non riuscito."
+  },
+  "invoices": {
+    "title": "Fatture",
+    "description": "Tutte le tue fatture di servizio.",
+    "noInvoices": "Nessuna fattura ancora.",
+    "failedToLoad": "Caricamento delle fatture non riuscito.",
+    "invoice": "Fattura",
+    "vehicle": "Veicolo",
+    "date": "Data",
+    "amount": "Importo",
+    "payment": "Pagamento",
+    "view": "Visualizza",
+    "paid": "pagato",
+    "partial": "parziale",
+    "unpaid": "non pagato"
+  },
+  "quotes": {
+    "title": "Preventivi",
+    "description": "Preventivi e stime per i tuoi veicoli.",
+    "noQuotes": "Nessun preventivo ancora.",
+    "failedToLoad": "Caricamento dei preventivi non riuscito.",
+    "quote": "Preventivo",
+    "vehicle": "Veicolo",
+    "status": "Stato",
+    "amount": "Importo",
+    "validUntil": "Valido Fino Al",
+    "view": "Visualizza"
+  },
+  "inspections": {
+    "title": "Ispezioni",
+    "description": "Rapporti di ispezione del veicolo.",
+    "noInspections": "Nessuna ispezione ancora.",
+    "failedToLoad": "Caricamento delle ispezioni non riuscito.",
+    "vehicle": "Veicolo",
+    "template": "Modello",
+    "status": "Stato",
+    "date": "Data",
+    "summary": "Riepilogo",
+    "view": "Visualizza",
+    "good": "{count} buono",
+    "fair": "{count} discreto",
+    "poor": "{count} scarso"
+  },
+  "vehicles": {
+    "title": "Veicoli",
+    "description": "I tuoi veicoli registrati.",
+    "noVehicles": "Nessun veicolo trovato.",
+    "failedToLoad": "Caricamento dei veicoli non riuscito.",
+    "plate": "Targa: {plate}",
+    "vin": "VIN: {vin}",
+    "serviceCount": "{count, plural, one {# servizio} other {# servizi}}",
+    "inspectionCount": "{count, plural, one {# ispezione} other {# ispezioni}}",
+    "serviceHistory": "Storico Servizi ({count})",
+    "inspectionsTab": "Ispezioni ({count})",
+    "noServiceRecords": "Nessun record di servizio ancora.",
+    "noInspections": "Nessuna ispezione ancora.",
+    "vehicleNotFound": "Veicolo non trovato.",
+    "completed": "Completato il {date}",
+    "good": "{count} buono",
+    "fair": "{count} discreto",
+    "poor": "{count} scarso",
+    "view": "Visualizza"
+  },
+  "requestService": {
+    "title": "Richiedi Servizio",
+    "description": "Invia una richiesta di servizio per uno dei tuoi veicoli.",
+    "newServiceRequest": "Nuova Richiesta di Servizio",
+    "formDescription": "Descrivi il problema e ti risponderemo.",
+    "vehicleLabel": "Veicolo",
+    "vehiclePlaceholder": "Seleziona un veicolo",
+    "descriptionLabel": "Descrivi il problema",
+    "descriptionPlaceholder": "Cosa sta succedendo con il tuo veicolo?",
+    "preferredDate": "Data preferita",
+    "preferredDateOptional": "(opzionale)",
+    "submitRequest": "Invia Richiesta",
+    "selectVehicle": "Seleziona un veicolo",
+    "describeIssue": "Descrivi il problema",
+    "submitSuccess": "Richiesta di servizio inviata",
+    "submitError": "Invio della richiesta non riuscito",
+    "noVehicles": "Non hai veicoli registrati. Contattaci per aggiungere prima un veicolo.",
+    "yourRequests": "Le Tue Richieste di Servizio",
+    "failedToLoad": "Caricamento dei dati non riuscito.",
+    "staffNotes": "Note del personale: {notes}",
+    "preferred": "Preferita: {date}"
+  }
+}

+ 173 - 0
messages/it/quotes.json

@@ -0,0 +1,173 @@
+{
+  "list": {
+    "statusAll": "Tutti",
+    "statusDraft": "Bozza",
+    "statusSent": "Inviato",
+    "statusAccepted": "Accettato",
+    "statusRejected": "Rifiutato",
+    "statusConverted": "Convertito",
+    "searchPlaceholder": "Cerca preventivi...",
+    "newQuote": "Nuovo preventivo",
+    "columnQuoteNumber": "Preventivo N.",
+    "columnTitle": "Titolo",
+    "columnCustomer": "Cliente",
+    "columnVehicle": "Veicolo",
+    "columnStatus": "Stato",
+    "columnDate": "Data",
+    "columnTotal": "Totale",
+    "noQuotes": "Nessun preventivo trovato."
+  },
+  "page": {
+    "quote": "Preventivo",
+    "save": "Salva",
+    "pdf": "PDF",
+    "email": "E-mail",
+    "share": "Condividi",
+    "delete": "Elimina",
+    "deleteTitle": "Elimina preventivo",
+    "deleteDescription": "Questo preventivo verrà eliminato definitivamente.",
+    "saved": "Preventivo salvato",
+    "failedSave": "Salvataggio del preventivo non riuscito",
+    "failedDelete": "Eliminazione non riuscita",
+    "failedPdf": "Generazione PDF non riuscita",
+    "selectVehicle": "Seleziona un veicolo",
+    "convertTitle": "Converti preventivo in ordine di lavoro",
+    "convertDescription": "Verrà creato un nuovo record di servizio (ordine di lavoro) da questo preventivo. Seleziona un veicolo per l'ordine di lavoro.",
+    "convert": "Converti",
+    "convertToWorkOrder": "Converti in ordine di lavoro",
+    "cancel": "Annulla",
+    "failedConvert": "Conversione non riuscita",
+    "changesRequested": "Modifiche richieste",
+    "quoteAccepted": "Preventivo accettato",
+    "markResolved": "Segna come risolto",
+    "responseResolved": "Risposta del cliente risolta",
+    "entityLabel": "Preventivo",
+    "tabs": {
+      "details": "Dettagli",
+      "images": "Immagini",
+      "documents": "Documenti"
+    }
+  },
+  "form": {
+    "newQuote": "Nuovo preventivo",
+    "editQuote": "Modifica preventivo",
+    "cancel": "Annulla",
+    "createQuote": "Crea preventivo",
+    "updateQuote": "Aggiorna preventivo",
+    "created": "Preventivo creato",
+    "updated": "Preventivo aggiornato",
+    "failedCreate": "Creazione del preventivo non riuscita",
+    "failedUpdate": "Aggiornamento del preventivo non riuscito"
+  },
+  "parts": {
+    "title": "Ricambi",
+    "addPart": "Aggiungi ricambio",
+    "partNumber": "N. ricambio",
+    "name": "Nome",
+    "namePlaceholder": "Nome *",
+    "qty": "Qtà",
+    "unitPrice": "Prezzo unitario",
+    "total": "Totale",
+    "subtotal": "Subtotale ricambi: {amount}"
+  },
+  "labor": {
+    "title": "Manodopera",
+    "addLabor": "Aggiungi manodopera",
+    "description": "Descrizione",
+    "descriptionPlaceholder": "Descrizione *",
+    "hours": "Ore",
+    "rate": "Tariffa ({currency}/ora)",
+    "total": "Totale",
+    "subtotal": "Subtotale manodopera: {amount}"
+  },
+  "notes": {
+    "title": "Note",
+    "public": "Pubblica",
+    "internal": "Interna",
+    "publicPlaceholder": "Descrizione del preventivo mostrata al cliente...",
+    "internalPlaceholder": "Note interne (non visibili al cliente)...",
+    "publicHelper": "Mostrato sul documento del preventivo",
+    "internalHelper": "Visibile solo al tuo team"
+  },
+  "details": {
+    "title": "Dettagli del preventivo",
+    "titleLabel": "Titolo *",
+    "titlePlaceholder": "Stima riparazione veicolo",
+    "status": "Stato",
+    "statusDraft": "Bozza",
+    "statusSent": "Inviato",
+    "statusAccepted": "Accettato",
+    "statusRejected": "Rifiutato",
+    "validUntil": "Valido fino al",
+    "viewInspection": "Visualizza ispezione collegata",
+    "vehicle": "Veicolo",
+    "customer": "Cliente",
+    "selectVehicle": "Seleziona veicolo...",
+    "selectCustomer": "Seleziona cliente...",
+    "none": "Nessuno"
+  },
+  "totals": {
+    "title": "Totali",
+    "parts": "Ricambi",
+    "labor": "Manodopera",
+    "subtotal": "Subtotale",
+    "discount": "Sconto",
+    "discountNone": "Nessuno",
+    "discountPercentage": "Percentuale",
+    "discountFixed": "Fisso",
+    "tax": "IVA",
+    "total": "Totale"
+  },
+  "shareDialog": {
+    "title": "Condividi preventivo",
+    "description": "Genera un link pubblico che consente a chiunque di visualizzare e scaricare questo preventivo senza effettuare l'accesso.",
+    "notifyTitle": "Notifica {name}",
+    "email": "E-mail",
+    "noEmail": "(nessuna e-mail registrata)",
+    "sms": "SMS",
+    "noPhone": "(nessun telefono registrato)",
+    "sendNotification": "Invia notifica",
+    "revokeLink": "Revoca link",
+    "generateLink": "Genera link pubblico",
+    "emailSent": "E-mail inviata",
+    "failedEmail": "Invio e-mail non riuscito",
+    "smsSent": "SMS inviato",
+    "failedSms": "Invio SMS non riuscito"
+  },
+  "images": {
+    "title": "Immagini del preventivo",
+    "dropzone": "Trascina le immagini qui o clicca per sfogliare",
+    "uploading": "Caricamento...",
+    "formats": "JPG, PNG, WebP — max 10 MB ciascuna",
+    "uploadingImages": "Caricamento immagini...",
+    "limitReached": "Limite immagini raggiunto ({count}/{max})",
+    "upgradePrompt": "Aggiorna il tuo piano per caricare più immagini.",
+    "onlyAllowed": "Sono consentite solo immagini JPG, PNG e WebP. {names} ignorati.",
+    "onlyUploading": "Verranno caricate solo {count} immagine/i per rispettare il limite.",
+    "uploadingCount": "Caricamento di {count} immagine/i...",
+    "uploadedCount": "{count} immagine/i caricata/e",
+    "uploadFailed": "Caricamento non riuscito",
+    "deleted": "Immagine eliminata",
+    "failedDelete": "Eliminazione immagine non riuscita",
+    "failedUpdate": "Aggiornamento non riuscito",
+    "description": "Descrizione...",
+    "pdf": "PDF"
+  },
+  "documents": {
+    "title": "Documenti",
+    "dropzone": "Trascina i file qui o clicca per sfogliare",
+    "uploading": "Caricamento...",
+    "formats": "PDF, CSV, TXT — max 10 MB ciascuno",
+    "uploadingDocuments": "Caricamento documenti...",
+    "limitReached": "Limite documenti raggiunto ({count}/{max})",
+    "upgradePrompt": "Aggiorna il tuo piano per caricare di più.",
+    "onlyUploading": "Verranno caricati solo {count} file per rispettare il limite.",
+    "uploadingCount": "Caricamento di {count} file...",
+    "uploadedCount": "{count} file caricato/i",
+    "uploadFailed": "Caricamento non riuscito",
+    "deleted": "File eliminato",
+    "failedDelete": "Eliminazione file non riuscita",
+    "failedUpdate": "Aggiornamento non riuscito",
+    "pdf": "PDF"
+  }
+}

+ 199 - 0
messages/it/reports.json

@@ -0,0 +1,199 @@
+{
+  "title": "Report e Analisi",
+  "tabs": {
+    "revenue": "Ricavi",
+    "services": "Servizi",
+    "customers": "Clienti",
+    "inventory": "Inventario",
+    "technicians": "Tecnici",
+    "parts": "Ricambi",
+    "jobAnalytics": "Analisi Lavori",
+    "retention": "Fidelizzazione",
+    "tax": "Tasse"
+  },
+  "dateRange": {
+    "to": "a"
+  },
+  "actions": {
+    "refresh": "Aggiorna",
+    "csv": "CSV"
+  },
+  "revenue": {
+    "revenue": "Ricavi",
+    "collected": "Incassato",
+    "outstanding": "In sospeso",
+    "services": "Servizi",
+    "monthlyRevenue": "Ricavi Mensili",
+    "revenueByType": "Ricavi per Tipo",
+    "monthlyBreakdown": "Riepilogo Mensile",
+    "tableHeaders": {
+      "month": "Mese",
+      "revenue": "Ricavi",
+      "collected": "Incassato",
+      "count": "Conteggio"
+    }
+  },
+  "services": {
+    "totalServices": "Totale Servizi",
+    "byStatus": "Per Stato",
+    "byType": "Per Tipo"
+  },
+  "customers": {
+    "total": "Totale",
+    "active": "Attivi",
+    "topCustomersBySpend": "Migliori Clienti per Spesa",
+    "topCustomers": "Migliori Clienti",
+    "tableHeaders": {
+      "name": "Nome",
+      "company": "Azienda",
+      "services": "Servizi",
+      "totalSpent": "Totale Speso"
+    }
+  },
+  "inventory": {
+    "parts": "Ricambi",
+    "items": "Articoli",
+    "costValue": "Valore Costo",
+    "sellValue": "Valore Vendita",
+    "potentialMargin": "Margine Potenziale",
+    "lowStock": "Scorte Basse",
+    "tableHeaders": {
+      "name": "Nome",
+      "partNumber": "N. Ricambio",
+      "qty": "Qtà",
+      "minQty": "Qtà Min."
+    }
+  },
+  "technicians": {
+    "totalJobs": "Totale Lavori",
+    "totalRevenue": "Ricavi Totali",
+    "revenueByTechnician": "Ricavi per Tecnico",
+    "technicianBreakdown": "Riepilogo Tecnici",
+    "tableHeaders": {
+      "technician": "Tecnico",
+      "jobs": "Lavori",
+      "revenue": "Ricavi",
+      "avgRevenue": "Ricavo Medio",
+      "totalHours": "Ore Totali",
+      "avgHours": "Ore Medie"
+    }
+  },
+  "parts": {
+    "partsUsed": "Ricambi Utilizzati",
+    "partsRevenue": "Ricavi Ricambi",
+    "topPartsByUsage": "Ricambi Più Utilizzati",
+    "partsUsage": "Utilizzo Ricambi",
+    "tableHeaders": {
+      "partName": "Nome Ricambio",
+      "partNumber": "N. Ricambio",
+      "usageCount": "Utilizzi",
+      "totalQty": "Qtà Totale",
+      "revenue": "Ricavi"
+    }
+  },
+  "jobAnalytics": {
+    "totalJobs": "Totale Lavori",
+    "avgJobValue": "Valore Medio Lavoro",
+    "jobsByDayOfWeek": "Lavori per Giorno della Settimana",
+    "serviceTypes": "Tipi di Servizio",
+    "monthlyTrend": "Trend Mensile",
+    "serviceTypeBreakdown": "Riepilogo Tipi di Servizio",
+    "tableHeaders": {
+      "type": "Tipo",
+      "count": "Conteggio",
+      "avgValue": "Valore Medio",
+      "avgHours": "Ore Medie"
+    }
+  },
+  "retention": {
+    "returning": "Ricorrenti",
+    "new": "Nuovi",
+    "totalActive": "Totale Attivi",
+    "avgDaysBetween": "Media Giorni Tra",
+    "topReturningCustomers": "Migliori Clienti Ricorrenti",
+    "returningCustomers": "Clienti Ricorrenti",
+    "tableHeaders": {
+      "customer": "Cliente",
+      "company": "Azienda",
+      "visits": "Visite",
+      "totalSpent": "Totale Speso",
+      "avgDaysBetween": "Media Giorni Tra"
+    }
+  },
+  "tax": {
+    "totalTaxCollected": "Totale Tasse Riscosse",
+    "taxableRevenue": "Ricavi Tassabili",
+    "invoicesWithTax": "Fatture con Tassa",
+    "monthlyTaxCollected": "Tasse Mensili Riscosse",
+    "taxByRate": "Tassa per Aliquota",
+    "monthlyBreakdown": "Riepilogo Mensile",
+    "tableHeaders": {
+      "taxRate": "Aliquota Fiscale",
+      "taxCollected": "Tassa Riscossa",
+      "invoices": "Fatture",
+      "month": "Mese",
+      "taxableAmount": "Importo Tassabile"
+    }
+  },
+  "charts": {
+    "revenue": "Ricavi",
+    "collected": "Incassato",
+    "count": "Conteggio",
+    "totalSpent": "Totale Speso",
+    "jobs": "Lavori",
+    "visits": "Visite",
+    "taxCollected": "Tassa Riscossa"
+  },
+  "days": {
+    "sunday": "Domenica",
+    "monday": "Lunedì",
+    "tuesday": "Martedì",
+    "wednesday": "Mercoledì",
+    "thursday": "Giovedì",
+    "friday": "Venerdì",
+    "saturday": "Sabato"
+  },
+  "daysShort": {
+    "sun": "Dom",
+    "mon": "Lun",
+    "tue": "Mar",
+    "wed": "Mer",
+    "thu": "Gio",
+    "fri": "Ven",
+    "sat": "Sab"
+  },
+  "csvHeaders": {
+    "month": "Mese",
+    "revenue": "Ricavi",
+    "collected": "Incassato",
+    "count": "Conteggio",
+    "category": "Categoria",
+    "label": "Etichetta",
+    "name": "Nome",
+    "company": "Azienda",
+    "services": "Servizi",
+    "totalSpent": "Totale Speso",
+    "partNumber": "N. Ricambio",
+    "quantity": "Quantità",
+    "minQuantity": "Quantità Minima",
+    "unitCost": "Costo Unitario",
+    "technician": "Tecnico",
+    "jobs": "Lavori",
+    "totalRevenue": "Ricavi Totali",
+    "avgRevenue": "Ricavo Medio",
+    "totalHours": "Ore Totali",
+    "avgHours": "Ore Medie",
+    "partName": "Nome Ricambio",
+    "usageCount": "Utilizzi",
+    "totalQty": "Qtà Totale",
+    "serviceType": "Tipo di Servizio",
+    "avgValue": "Valore Medio",
+    "customer": "Cliente",
+    "visits": "Visite",
+    "avgDaysBetweenVisits": "Media Giorni Tra Visite",
+    "taxCollected": "Tassa Riscossa",
+    "taxableAmount": "Importo Tassabile",
+    "invoiceCount": "Numero Fatture"
+  },
+  "empty": "Nessun dato caricato. Clicca su Aggiorna per caricare il report."
+}

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