Bernt Christian Egeland 1 месяц назад
Родитель
Сommit
e589ffd4f1
100 измененных файлов с 5894 добавлено и 108 удалено
  1. 13 1
      Dockerfile
  2. 15 32
      README.md
  3. 28 1
      messages/de/audit.json
  4. 6 2
      messages/de/common.json
  5. 12 1
      messages/de/dashboard.json
  6. 10 0
      messages/de/featureHints.json
  7. 1 0
      messages/de/inspections.json
  8. 8 2
      messages/de/navigation.json
  9. 91 0
      messages/de/onboarding.json
  10. 27 0
      messages/de/permissions.json
  11. 17 2
      messages/de/portal.json
  12. 9 0
      messages/de/search.json
  13. 47 1
      messages/de/settings.json
  14. 473 0
      messages/de/tireHotel.json
  15. 28 1
      messages/en/audit.json
  16. 6 2
      messages/en/common.json
  17. 12 1
      messages/en/dashboard.json
  18. 10 0
      messages/en/featureHints.json
  19. 1 0
      messages/en/inspections.json
  20. 8 2
      messages/en/navigation.json
  21. 91 0
      messages/en/onboarding.json
  22. 27 0
      messages/en/permissions.json
  23. 17 2
      messages/en/portal.json
  24. 9 0
      messages/en/search.json
  25. 47 1
      messages/en/settings.json
  26. 473 0
      messages/en/tireHotel.json
  27. 28 1
      messages/es/audit.json
  28. 6 2
      messages/es/common.json
  29. 12 1
      messages/es/dashboard.json
  30. 10 0
      messages/es/featureHints.json
  31. 1 0
      messages/es/inspections.json
  32. 8 2
      messages/es/navigation.json
  33. 91 0
      messages/es/onboarding.json
  34. 27 0
      messages/es/permissions.json
  35. 17 2
      messages/es/portal.json
  36. 9 0
      messages/es/search.json
  37. 47 1
      messages/es/settings.json
  38. 473 0
      messages/es/tireHotel.json
  39. 28 1
      messages/fr/audit.json
  40. 6 2
      messages/fr/common.json
  41. 12 1
      messages/fr/dashboard.json
  42. 10 0
      messages/fr/featureHints.json
  43. 1 0
      messages/fr/inspections.json
  44. 8 2
      messages/fr/navigation.json
  45. 91 0
      messages/fr/onboarding.json
  46. 27 0
      messages/fr/permissions.json
  47. 17 2
      messages/fr/portal.json
  48. 9 0
      messages/fr/search.json
  49. 47 1
      messages/fr/settings.json
  50. 473 0
      messages/fr/tireHotel.json
  51. 28 1
      messages/it/audit.json
  52. 6 2
      messages/it/common.json
  53. 12 1
      messages/it/dashboard.json
  54. 10 0
      messages/it/featureHints.json
  55. 1 0
      messages/it/inspections.json
  56. 8 2
      messages/it/navigation.json
  57. 91 0
      messages/it/onboarding.json
  58. 27 0
      messages/it/permissions.json
  59. 17 2
      messages/it/portal.json
  60. 9 0
      messages/it/search.json
  61. 47 1
      messages/it/settings.json
  62. 473 0
      messages/it/tireHotel.json
  63. 28 1
      messages/lt/audit.json
  64. 6 2
      messages/lt/common.json
  65. 12 1
      messages/lt/dashboard.json
  66. 10 0
      messages/lt/featureHints.json
  67. 1 0
      messages/lt/inspections.json
  68. 8 2
      messages/lt/navigation.json
  69. 91 0
      messages/lt/onboarding.json
  70. 27 0
      messages/lt/permissions.json
  71. 17 2
      messages/lt/portal.json
  72. 9 0
      messages/lt/search.json
  73. 47 1
      messages/lt/settings.json
  74. 473 0
      messages/lt/tireHotel.json
  75. 28 1
      messages/nb/audit.json
  76. 6 2
      messages/nb/common.json
  77. 12 1
      messages/nb/dashboard.json
  78. 10 0
      messages/nb/featureHints.json
  79. 1 0
      messages/nb/inspections.json
  80. 8 2
      messages/nb/navigation.json
  81. 91 0
      messages/nb/onboarding.json
  82. 27 0
      messages/nb/permissions.json
  83. 17 2
      messages/nb/portal.json
  84. 9 0
      messages/nb/search.json
  85. 47 1
      messages/nb/settings.json
  86. 473 0
      messages/nb/tireHotel.json
  87. 28 1
      messages/nl/audit.json
  88. 6 2
      messages/nl/common.json
  89. 12 1
      messages/nl/dashboard.json
  90. 10 0
      messages/nl/featureHints.json
  91. 1 0
      messages/nl/inspections.json
  92. 8 2
      messages/nl/navigation.json
  93. 91 0
      messages/nl/onboarding.json
  94. 27 0
      messages/nl/permissions.json
  95. 17 2
      messages/nl/portal.json
  96. 9 0
      messages/nl/search.json
  97. 47 1
      messages/nl/settings.json
  98. 473 0
      messages/nl/tireHotel.json
  99. 28 1
      messages/pl/audit.json
  100. 6 2
      messages/pl/common.json

+ 13 - 1
Dockerfile

@@ -71,15 +71,27 @@ RUN rm -rf \
       /app/node_modules/prisma \
       /app/node_modules/prisma \
       /app/node_modules/@prisma \
       /app/node_modules/@prisma \
       /app/node_modules/.prisma \
       /app/node_modules/.prisma \
-    && npm install prisma@7.6.0 @prisma/client@7.6.0 @prisma/adapter-pg@7.6.0 pg dotenv tsx
+    && npm install prisma@7.6.0 @prisma/client@7.6.0 @prisma/adapter-pg@7.6.0 pg dotenv tsx sharp
 
 
 # The npm install above re-resolves the dependency tree and replaces the
 # The npm install above re-resolves the dependency tree and replaces the
 # standalone build's PATCHED next package with a fresh unpatched copy from the
 # standalone build's PATCHED next package with a fresh unpatched copy from the
 # registry, which silently breaks all WebSocket routes (live updates, work
 # registry, which silently breaks all WebSocket routes (live updates, work
 # board sync). Re-apply the next-ws patch so the runtime server can accept
 # board sync). Re-apply the next-ws patch so the runtime server can accept
 # WebSocket upgrades. next-ws itself is already in the standalone bundle.
 # WebSocket upgrades. next-ws itself is already in the standalone bundle.
+#
+# sharp is named in that install for the same reason. It is the only native
+# module in the tree, its binary lives in a platform-specific optional
+# dependency (@img/sharp-linuxmusl-x64 on this image), and re-resolving the
+# tree can leave the traced copy without one. Installing it by name makes npm
+# resolve the binary against the platform the image will actually run on.
 RUN npx next-ws patch --yes
 RUN npx next-ws patch --yes
 
 
+# Fail the build here rather than at the first certificate download: a native
+# module that cannot be loaded throws while the route module is being
+# evaluated, which reaches the browser as an empty HTTP 500 with nothing in it
+# to explain itself.
+RUN node -e "require('sharp'); console.log('sharp loads')"
+
 # Copy init script
 # Copy init script
 COPY --chown=nextjs:nodejs init-db.sh ./init-db.sh
 COPY --chown=nextjs:nodejs init-db.sh ./init-db.sh
 RUN chmod +x ./init-db.sh
 RUN chmod +x ./init-db.sh

+ 15 - 32
README.md

@@ -1,6 +1,6 @@
 <br />
 <br />
 <p align="center">
 <p align="center">
-	<a href="https://github.com/sinamics/torqvoice">
+	<a href="https://github.com/Torqvoice/torqvoice">
 		<img src="assets/logo/torqvoice_app_logo_padding.png" alt="Torqvoice Logo" width="120" height="120">
 		<img src="assets/logo/torqvoice_app_logo_padding.png" alt="Torqvoice Logo" width="120" height="120">
 	</a>
 	</a>
 
 
@@ -8,49 +8,32 @@
    	Torqvoice, modern workshop and service management.
    	Torqvoice, modern workshop and service management.
    	<br />
    	<br />
    	<br />
    	<br />
-   	<a href="https://github.com/sinamics/torqvoice/issues/new?labels=bug&template=bug_template.yml&title=%5BBug%5D%3A+">Bug Report</a>
-   	·
-   	<a href="https://github.com/sinamics/torqvoice/issues/new?labels=enhancement&template=feature_request.yml&title=%5BFeature+Request%5D%3A+">Feature Request</a>
+   	<a href="https://torqvoice.com/docs"><strong>Documentation</strong></a>
    	·
    	·
    	<a href="https://demo.torqvoice.com/auth/sign-in?demo=1"><strong>Live Demo</strong></a>
    	<a href="https://demo.torqvoice.com/auth/sign-in?demo=1"><strong>Live Demo</strong></a>
+   	·
+   	<a href="https://github.com/Torqvoice/torqvoice/issues/new?labels=bug&template=bug_template.yml&title=%5BBug%5D%3A+">Bug Report</a>
+   	·
+   	<a href="https://github.com/Torqvoice/torqvoice/issues/new?labels=enhancement&template=feature_request.yml&title=%5BFeature+Request%5D%3A+">Feature Request</a>
    </p>
    </p>
 
 
-   <div align="center">
-
-<!-- [![CI](https://github.com/sinamics/torqvoice/actions/workflows/ci.yml/badge.svg)](https://github.com/sinamics/torqvoice/actions)
-[![Release](https://img.shields.io/github/v/release/sinamics/torqvoice.svg)](https://github.com/sinamics/torqvoice/releases/latest)
-[![License](https://img.shields.io/github/license/sinamics/torqvoice.svg)](LICENSE) -->
-
-   </div>
-
 </p>
 </p>
 <br />
 <br />
 
 
-Torqvoice is a self-hosted workshop management platform built for automotive service businesses. It replaces scattered tools with a single place to manage customers, vehicles, service records, quotes, invoicing, inventory, billing all with a clean modern UI.
+Torqvoice is a self-hosted workshop management platform for automotive service businesses. It replaces scattered tools with one place to run customers, vehicles, work orders, quotes, invoicing and inventory.
+
+## Documentation
 
 
-## Try the Demo
+**[torqvoice.com/docs](https://torqvoice.com/docs)** covers what the app does, how to configure it, and how to run it yourself.
 
 
-👉 **[demo.torqvoice.com](https://demo.torqvoice.com/auth/sign-in?demo=1)** — one-click sign-in, populated with realistic data. No signup required. Data resets every 3 hours.
+## Try it
 
 
-## Quick Start
+**[demo.torqvoice.com](https://demo.torqvoice.com/auth/sign-in?demo=1)**. One-click sign-in, populated with realistic data, no signup. Resets every 3 hours.
 
 
-See the [Quick Start guide](https://torqvoice.com/docs/installation#quick-start) to get up and running with Docker Compose in minutes.
+## Run it
 
 
-## Features
-- **Multi Company Support** — manage multiple workshops with a single login
-- **Vehicles & Customers** — full service history per vehicle, linked to customer profiles
-- **Work Orders** — status-driven workflow (pending, in progress, waiting parts, ready, completed)
-- **Quotes & Invoicing** — PDF generation, customizable templates, public shareable invoice links
-- **Inventory** — parts stock tracking with suppliers and low-stock alerts
-- **Billing & Payments** — payment tracking with paid/partial/unpaid status
-- **Reports** — revenue, service, customer, and inventory analytics with charts and CSV export
-- **Email** — send quotes and invoices with PDF attachments
-- **Custom Fields** — extend service records and quotes with your own data fields
-- **Team Management** — organization-based roles (owner, admin, member)
-- **Company Branding** — logo, invoice colors, fonts, and layout customization
-- **Global Search** — search across all entities via command palette
-- **Dark Mode** — full dark mode support
+Docker Compose, in minutes. See the [Quick Start](https://torqvoice.com/docs/installation#quick-start).
 
 
 ## License
 ## License
 
 
-[Elastic License 2.0 (ELv2)](LICENSE)
+[Elastic License 2.0 (ELv2)](LICENSE)

+ 28 - 1
messages/de/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Geplante Nachricht gesendet",
     "scheduled_message_send_now": "Geplante Nachricht gesendet",
     "settings_applyTaxRate": "Steuersatz angewendet",
     "settings_applyTaxRate": "Steuersatz angewendet",
     "settings_convertToExclusive": "Preise auf netto umgestellt",
     "settings_convertToExclusive": "Preise auf netto umgestellt",
-    "settings_convertToInclusive": "Preise auf brutto umgestellt"
+    "settings_convertToInclusive": "Preise auf brutto umgestellt",
+    "organization_sampleDataRemove": "Beispieldaten entfernt",
+    "tire_warehouse_create": "Reifenlagerort erstellt",
+    "tire_warehouse_update": "Reifenlagerort aktualisiert",
+    "tire_warehouse_delete": "Reifenlagerort gelöscht",
+    "tire_warehouse_archive": "Reifenlagerort archiviert",
+    "tire_location_create": "Reifenregal erstellt",
+    "tire_location_bulk_create": "Reifenregale erstellt",
+    "tire_location_update": "Reifenregal aktualisiert",
+    "tire_location_delete": "Reifenregal gelöscht",
+    "tire_set_check_in": "Reifensatz eingelagert",
+    "tire_set_check_out": "Reifensatz ausgelagert",
+    "tire_set_relocate": "Reifensatz umgelagert",
+    "tire_set_update": "Reifensatz aktualisiert",
+    "tire_set_delete": "Reifensatz gelöscht",
+    "tire_measurement_create": "Reifenmessungen erfasst",
+    "tire_treatment_update": "Vorbereitung aktualisiert",
+    "tire_treatment_mark": "Vorbereitung markiert",
+    "tire_treatment_complete_all": "Vorbereitung abgeschlossen",
+    "tire_set_message_customer": "Kunde zu einem Reifensatz benachrichtigt",
+    "tire_set_create_quote": "Angebot aus einem Reifensatz erstellt",
+    "tire_set_create_work_order": "Auftrag aus einem Reifensatz erstellt",
+    "tire_set_add_to_work_order": "Reifensatz zu einem Auftrag hinzugefügt",
+    "tire_set_unlink_work_order": "Reifensatz von einem Auftrag gelöst",
+    "tire_set_return": "Satz erneut eingelagert",
+    "tire_set_dispose": "Reifensatz ausgemustert",
+    "tire_set_attach": "Dateien zu einem Reifensatz hinzugefügt",
+    "tire_set_detach": "Datei von einem Reifensatz entfernt"
   }
   }
 }
 }

+ 6 - 2
messages/de/common.json

@@ -10,7 +10,8 @@
     "confirm": "Bestätigen",
     "confirm": "Bestätigen",
     "saveChanges": "Änderungen speichern",
     "saveChanges": "Änderungen speichern",
     "edit": "Bearbeiten",
     "edit": "Bearbeiten",
-    "add": "Hinzufügen"
+    "add": "Hinzufügen",
+    "gotIt": "Verstanden"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Tipp: Rechtsklick auf eine Zeile für Aktionen",
     "hint": "Tipp: Rechtsklick auf eine Zeile für Aktionen",
@@ -75,6 +76,9 @@
     "enterManually": "Manuell eingeben",
     "enterManually": "Manuell eingeben",
     "enterBarcode": "Barcode eingeben…",
     "enterBarcode": "Barcode eingeben…",
     "scanHint": "Richten Sie die Kamera auf einen Barcode oder geben Sie ihn manuell ein",
     "scanHint": "Richten Sie die Kamera auf einen Barcode oder geben Sie ihn manuell ein",
-    "iosInstallHint": "Auf Teilen > Zum Home-Bildschirm tippen"
+    "iosInstallHint": "Auf Teilen > Zum Home-Bildschirm tippen",
+    "installDescription": "Zum Home-Bildschirm hinzufügen für schnellen Zugriff",
+    "enterFullscreen": "Vollbild",
+    "exitFullscreen": "Vollbild beenden"
   }
   }
 }
 }

+ 12 - 1
messages/de/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Kürzlich abgeschlossen",
     "recentCompleted": "Kürzlich abgeschlossen",
     "activeJobs": "Aktive Aufträge",
     "activeJobs": "Aktive Aufträge",
     "recentObservations": "Aktuelle Beobachtungen",
     "recentObservations": "Aktuelle Beobachtungen",
-    "recentActivity": "Letzte Aktivitäten"
+    "recentActivity": "Letzte Aktivitäten",
+    "gettingStarted": "Erste Schritte",
+    "tireHotel": "Reifenhotel"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Voraussichtlich fällige Wartung",
     "title": "Voraussichtlich fällige Wartung",
@@ -233,5 +235,14 @@
       "expired": "Abgelaufen",
       "expired": "Abgelaufen",
       "converted": "Umgewandelt"
       "converted": "Umgewandelt"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Reifenhotel",
+    "viewAll": "Reifenhotel ansehen",
+    "stored": "{count} Sätze eingelagert",
+    "capacity": "{used} / {capacity} Reifen",
+    "roomLeft": "Platz für {count} weitere",
+    "needsPrep": "Vorbereitung offen",
+    "unbilled": "Nicht berechnet ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/de/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "Reifenhotel ist aktiv",
+    "body": "Saisonreifen Ihrer Kunden einlagern. Erst Regale anlegen, dann einlagern."
+  },
+  "telegram": {
+    "title": "Telegram ist aktiv",
+    "body": "Kundengespräche landen hier. Antworten Sie, ohne die App zu verlassen."
+  }
+}

+ 1 - 0
messages/de/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Fahrzeug",
     "vehicle": "Fahrzeug",
     "template": "Vorlage",
     "template": "Vorlage",
     "progress": "Fortschritt",
     "progress": "Fortschritt",
+    "progressTooltip": "{graded} von {total} Prüfpunkten bewertet",
     "status": "Status",
     "status": "Status",
     "date": "Datum",
     "date": "Datum",
     "statusInProgress": "Läuft",
     "statusInProgress": "Läuft",

+ 8 - 2
messages/de/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Alle Erinnerungen",
     "allReminders": "Alle Erinnerungen",
     "auditLog": "Aktivitätsprotokoll",
     "auditLog": "Aktivitätsprotokoll",
     "aiAssistant": "KI-Assistent",
     "aiAssistant": "KI-Assistent",
-    "observations": "Beobachtungen"
+    "observations": "Beobachtungen",
+    "tireHotel": "Reifenhotel",
+    "allTireSets": "Alle Reifensätze",
+    "tireStorage": "Lagerstruktur",
+    "tireSetDetails": "Reifensatz",
+    "tireForecast": "Nächste Saison"
   },
   },
   "search": "Suchen...",
   "search": "Suchen...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Abmelden",
     "signOut": "Abmelden",
     "more": "Mehr",
     "more": "Mehr",
     "auditLog": "Aktivitätsprotokoll",
     "auditLog": "Aktivitätsprotokoll",
-    "aiAssistant": "KI-Assistent"
+    "aiAssistant": "KI-Assistent",
+    "tireHotel": "Reifenhotel"
   }
   }
 }
 }

+ 91 - 0
messages/de/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Richten Sie Ihre Werkstatt ein",
+    "subtitle": "Geben Sie den Namen Ihrer Werkstatt ein, um loszulegen",
+    "workshopName": "Name der Werkstatt",
+    "workshopNamePlaceholder": "Meine Autowerkstatt",
+    "submit": "Loslegen",
+    "sampleDataLabel": "Beispieldaten laden",
+    "sampleDataDescription": "Fügt einige Beispielkunden, Fahrzeuge und Aufträge hinzu, damit Sie sich umsehen können. Sie können sie mit einem Klick wieder entfernen.",
+    "setupFailed": "Einrichtung fehlgeschlagen",
+    "couldNotCreate": "Werkstatt konnte nicht erstellt werden",
+    "unexpectedError": "Ein unerwarteter Fehler ist aufgetreten"
+  },
+  "sample": {
+    "prefix": "Beispiel:",
+    "oilService": "Öl- und Filterwechsel",
+    "brakeJob": "Bremsbeläge vorne erneuern",
+    "annualService": "Jahresinspektion",
+    "quoteTitle": "Fahrwerk vorne überholen",
+    "inspectionNote": "Reifen vorne links ist nahe an der Verschleißgrenze",
+    "parts": {
+      "oilFilter": "Ölfilter",
+      "engineOil": "Motoröl 5W-30",
+      "brakePads": "Bremsbelagsatz vorne",
+      "shockAbsorber": "Stoßdämpfer"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Ölwechsel",
+      "description": "Motoröl und Filter wechseln"
+    },
+    "frontBrakePads": {
+      "name": "Bremsbeläge vorne",
+      "description": "Bremsbeläge vorne erneuern"
+    },
+    "tireSwap": {
+      "name": "Radwechsel",
+      "description": "Saisonaler Radwechsel mit Drehmomentkontrolle"
+    },
+    "batteryReplacement": {
+      "name": "Batteriewechsel",
+      "description": "Starterbatterie ersetzen und anlernen"
+    },
+    "diagnostics": {
+      "name": "Diagnose",
+      "description": "Fehlerspeicher auslesen und Erstdiagnose"
+    },
+    "annualService": {
+      "name": "Jahresinspektion",
+      "description": "Komplette Jahresinspektion nach Plan"
+    }
+  },
+  "checklist": {
+    "title": "Erste Schritte",
+    "description": "Vier Schritte, um Ihre Werkstatt zum Laufen zu bringen",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Kunden anlegen",
+        "description": "Legen Sie Ihren ersten Kunden an"
+      },
+      "vehicle": {
+        "title": "Fahrzeug anlegen",
+        "description": "Erfassen Sie ein Fahrzeug für einen Kunden"
+      },
+      "workOrder": {
+        "title": "Auftrag erstellen",
+        "description": "Eröffnen Sie Ihren ersten Auftrag"
+      },
+      "invoice": {
+        "title": "Rechnung senden oder herunterladen",
+        "description": "Teilen Sie eine Rechnung mit einem Kunden oder laden Sie sie als PDF herunter"
+      }
+    },
+    "allDoneTitle": "Alles erledigt",
+    "allDoneDescription": "Alle Schritte sind abgeschlossen und Ihre Werkstatt ist startklar.",
+    "dismiss": "Karte ausblenden",
+    "dismissAriaLabel": "Diese Checkliste ausblenden"
+  },
+  "sampleData": {
+    "title": "Beispieldaten",
+    "description": "Ihre Werkstatt enthält noch die Beispielkunden, Fahrzeuge und Aufträge aus der Einrichtung. Beim Entfernen werden genau diese Datensätze gelöscht und sonst nichts.",
+    "footerNote": "Beispieldaten sind geladen",
+    "remove": "Beispieldaten entfernen",
+    "removeConfirmTitle": "Beispieldaten entfernen?",
+    "removeConfirmDescription": "Damit werden die bei der Einrichtung erstellten Beispielkunden, Fahrzeuge, Aufträge, das Angebot und die Inspektion gelöscht. Ihre eigenen Daten bleiben unberührt.",
+    "removed": "Beispieldaten entfernt",
+    "removeFailed": "Beispieldaten konnten nicht entfernt werden"
+  }
+}

+ 27 - 0
messages/de/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Dashboard",
+    "vehicles": "Fahrzeuge",
+    "customers": "Kunden",
+    "work_orders": "Aufträge",
+    "quotes": "Angebote",
+    "services": "Serviceeinträge",
+    "billing": "Abrechnung",
+    "inventory": "Lager",
+    "labor_presets": "Arbeitsvorlagen",
+    "inspections": "Prüfungen",
+    "tire_hotel": "Reifenhotel",
+    "reports": "Berichte",
+    "work_board": "Auftragstafel",
+    "ai_assistant": "KI-Assistent",
+    "settings": "Einstellungen"
+  },
+  "actions": {
+    "create": "Anlegen",
+    "read": "Ansehen",
+    "update": "Bearbeiten",
+    "delete": "Löschen",
+    "manage": "Verwalten",
+    "manage_storage": "Lagerstruktur verwalten"
+  }
+}

+ 17 - 2
messages/de/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} gut",
     "good": "{count} gut",
     "fair": "{count} akzeptabel",
     "fair": "{count} akzeptabel",
     "poor": "{count} schlecht",
     "poor": "{count} schlecht",
-    "view": "Ansehen"
+    "view": "Ansehen",
+    "serviceHistoryTitle": "Servicehistorie",
+    "inspectionsTitle": "Inspektionen",
+    "columnVehicle": "Fahrzeug",
+    "columnPlate": "Kennzeichen",
+    "columnServices": "Services",
+    "columnInspections": "Inspektionen"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Service anfordern",
     "title": "Service anfordern",
@@ -174,6 +180,15 @@
     "yourRequests": "Ihre Serviceanfragen",
     "yourRequests": "Ihre Serviceanfragen",
     "failedToLoad": "Daten konnten nicht geladen werden.",
     "failedToLoad": "Daten konnten nicht geladen werden.",
     "staffNotes": "Mitarbeiterhinweise: {notes}",
     "staffNotes": "Mitarbeiterhinweise: {notes}",
-    "preferred": "Bevorzugt: {date}"
+    "preferred": "Bevorzugt: {date}",
+    "notListedOption": "Mein Fahrzeug ist nicht aufgeführt",
+    "newVehicleHint": "Beschreiben Sie das Fahrzeug, wir fügen es Ihrem Profil hinzu.",
+    "fillVehicleDetails": "Bitte Marke und Modell des Fahrzeugs angeben",
+    "makeLabel": "Marke",
+    "makePlaceholder": "z. B. Ford",
+    "modelLabel": "Modell",
+    "modelPlaceholder": "z. B. Kuga",
+    "yearLabel": "Baujahr",
+    "plateLabel": "Kennzeichen"
   }
   }
 }
 }

+ 9 - 0
messages/de/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Suche",
+  "description": "Fahrzeuge, Kunden, Aufträge und Einstellungen durchsuchen",
+  "placeholder": "Nach Name, Kennzeichen, Telefon, FIN, Rechnung suchen…",
+  "noResults": "Keine Treffer.",
+  "recentCustomers": "Zuletzt genutzte Kunden",
+  "services": "Serviceeinträge",
+  "toClose": "zum Schließen"
+}

+ 47 - 1
messages/de/settings.json

@@ -103,6 +103,10 @@
         "title": "Daten",
         "title": "Daten",
         "description": "Export & Import-Backup"
         "description": "Export & Import-Backup"
       },
       },
+      "install": {
+        "title": "App installieren",
+        "description": "Torqvoice auf diesem Gerät hinzufügen"
+      },
       "license": {
       "license": {
         "title": "Lizenz",
         "title": "Lizenz",
         "description": "White-Label-Lizenz"
         "description": "White-Label-Lizenz"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Support",
         "title": "Support",
         "description": "Support kontaktieren und die Support-Schaltfläche verwalten"
         "description": "Support kontaktieren und die Support-Schaltfläche verwalten"
+      },
+      "tireHotel": {
+        "title": "Reifenhotel",
+        "description": "Saisonale Reifeneinlagerung"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Letztes Backup:",
       "lastBackup": "Letztes Backup:",
       "supportPrompt": "Bei Fragen zum Schutz Ihrer Daten",
       "supportPrompt": "Bei Fragen zum Schutz Ihrer Daten",
       "supportAction": "kontaktieren Sie unser Support-Team."
       "supportAction": "kontaktieren Sie unser Support-Team."
-    }
+    },
+    "optTireHotel": "Reifenhotel",
+    "optTireHotelDesc": "Eingelagerte Reifensätze, Regale, Vorbereitung und Lagervereinbarungen"
   },
   },
   "license": {
   "license": {
     "title": "White-Label-Lizenz",
     "title": "White-Label-Lizenz",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Support kontaktieren",
     "contactTitle": "Support kontaktieren",
     "contactHint": "Öffnet das Supportformular direkt.",
     "contactHint": "Öffnet das Supportformular direkt.",
     "contactAction": "Formular öffnen"
     "contactAction": "Formular öffnen"
+  },
+  "install": {
+    "title": "Torqvoice installieren",
+    "description": "Installieren Sie Torqvoice auf diesem Gerät, um es im Vollbild vom Home-Bildschirm aus zu öffnen, ohne Browserleiste. Es bleibt dieselbe App mit denselben Daten.",
+    "installed": "Torqvoice ist auf diesem Gerät installiert.",
+    "iosHint": "Tippen Sie in Safari auf Teilen und dann auf „Zum Home-Bildschirm“.",
+    "unavailable": "Dieser Browser hat keine Installation angeboten. Öffnen Sie das Browsermenü und suchen Sie nach „App installieren“ oder „Zum Home-Bildschirm“.",
+    "bannerHidden": "Das Installations-Banner ist auf diesem Gerät ausgeblendet.",
+    "showBanner": "Banner wieder anzeigen"
+  },
+  "tireHotel": {
+    "title": "Reifenhotel",
+    "description": "Lagern Sie die Saisonreifen Ihrer Kunden ein und behalten Sie den Überblick, wo jeder Satz steht.",
+    "cardTitle": "Reifeneinlagerung",
+    "enable": "Reifenhotel aktivieren",
+    "enableHint": "Fügt die Reifeneinlagerung zur Navigation hinzu. Später abschalten blendet sie wieder aus, ohne etwas zu löschen.",
+    "defaultCapacity": "Standardreifen pro Fach",
+    "defaultCapacityHint": "Wird beim Anlegen neuer Fächer vorausgefüllt. Pro Fach änderbar.",
+    "warnPercent": "Warnen bei Auslastung (%)",
+    "warnPercentHint": "Markiert die Lagerübersicht, sobald dieser Anteil der Gesamtkapazität belegt ist.",
+    "treadTitle": "Ersatzgrenzwerte",
+    "treadHint": "Gesetzliche Mindestwerte unterscheiden sich je Land, setzen Sie also die Grenzwerte Ihres Marktes. Profiltiefe unter dem Grenzwert wird zum Ersatz markiert.",
+    "summerLimit": "Sommergrenze ({unit})",
+    "winterLimit": "Wintergrenze ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "Richten Sie als Nächstes Ihre Regale ein.",
+    "setupLink": "Lagerstruktur öffnen",
+    "save": "Reifenhotel-Einstellungen speichern",
+    "saved": "Reifenhotel-Einstellungen gespeichert",
+    "billingTitle": "Lagerabrechnung",
+    "billingHint": "Wird beim Abrechnen eines Satzes auf der Lagerposition vorgeschlagen und bleibt änderbar.",
+    "seasonalPrice": "Standard-Lagerpreis",
+    "prepTitle": "Preise für Vorbereitung",
+    "prepHint": "Was jede Art von Vorbereitung kostet. Leer bedeutet, sie wird nie berechnet, was zu Betrieben passt, die das Waschen in der Lagergebühr enthalten haben. Bepreiste werden automatisch auf Angebot und Auftrag gesetzt.",
+    "prepNotCharged": "nicht berechnet"
   }
   }
 }
 }

+ 473 - 0
messages/de/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Reifenhotel",
+  "description": "Saisonale Reifeneinlagerung für Ihre Kunden",
+  "common": {
+    "cancel": "Abbrechen",
+    "save": "Speichern",
+    "create": "Erstellen",
+    "delete": "Löschen",
+    "edit": "Bearbeiten"
+  },
+  "seasons": {
+    "summer": "Sommer",
+    "winter": "Winter",
+    "all_season": "Ganzjahr",
+    "other": "Sonstige"
+  },
+  "statuses": {
+    "stored": "Eingelagert",
+    "released": "Zurückgegeben",
+    "disposed": "Entsorgt"
+  },
+  "conditions": {
+    "good": "Gut",
+    "fair": "Beobachten",
+    "replace": "Ersetzen"
+  },
+  "positions": {
+    "front_left": "Vorne links",
+    "front_right": "Vorne rechts",
+    "rear_left": "Hinten links",
+    "rear_right": "Hinten rechts",
+    "spare": "Reserverad",
+    "unspecified": "Nicht angegeben"
+  },
+  "capacity": {
+    "used": "{used} von {capacity} Reifen",
+    "free": "Platz für {count}",
+    "freeShort": "{count} frei",
+    "over": "{count} über Kapazität",
+    "meterLabel": "{used} von {capacity} Reifen eingelagert"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32stel",
+    "inputLabel": "Profiltiefe, {position}",
+    "lastTime": "war {value}",
+    "worn": "{value} abgefahren"
+  },
+  "list": {
+    "tabAll": "Alle",
+    "tabStored": "Eingelagert",
+    "tabReleased": "Zurückgegeben",
+    "search": "Nach Kennzeichen, Kunde, Marke oder Regal suchen",
+    "checkIn": "Reifen einlagern",
+    "empty": "Noch keine Reifensätze. Lagern Sie einen Satz ein, um zu starten.",
+    "unassigned": "Nicht zugeordnet",
+    "reference": "Ref",
+    "vehicle": "Fahrzeug",
+    "tires": "Reifen",
+    "location": "Regal",
+    "condition": "Zustand",
+    "status": "Status",
+    "storedSince": "Seit",
+    "tireCount": "{count, plural, one {# Reifen} other {# Reifen}}",
+    "studded": "Spikes",
+    "roomLeft": "Platz für {count}",
+    "prep": "Vorb.",
+    "tabNeedsPrep": "Vorbereitung offen",
+    "setUpShelves": "Regale einrichten",
+    "emptyNoShelves": "Noch keine Regale. Richten Sie Ihr Lager ein, bevor Sie Reifen einlagern.",
+    "tabDisposed": "Ausgemustert"
+  },
+  "checkIn": {
+    "title": "Reifen einlagern",
+    "description": "Erfassen Sie, was angekommen ist, und wählen Sie den Lagerplatz.",
+    "customer": "Kunde",
+    "selectCustomer": "Kunde auswählen",
+    "noCustomer": "Kein Kunde",
+    "vehicle": "Fahrzeug",
+    "selectVehicle": "Fahrzeug auswählen",
+    "season": "Saison",
+    "quantity": "Anzahl Reifen",
+    "quantityHint": "Einzeln gezählt, damit auch ein Paar oder fünf Räder passen.",
+    "brand": "Marke",
+    "brandPlaceholder": "Michelin, Bridgestone, Continental",
+    "model": "Modell",
+    "size": "Größe",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "DOT-Nummer",
+    "dotPlaceholder": "3623",
+    "dotHint": "Woche und Jahr der Herstellung.",
+    "withRims": "Auf Felgen",
+    "hasTpms": "RDKS-Sensoren",
+    "studded": "Spikes",
+    "location": "Lagerplatz",
+    "choosePlace": "Regal wählen",
+    "searchLocations": "Regale durchsuchen",
+    "noLocations": "Keine Regale gefunden",
+    "tooFull": "Nicht genug Platz",
+    "pickLocationFirst": "Wählen Sie ein Regal, bevor Sie den Satz einlagern",
+    "roomSummary": "{locations} Regale passen für diesen Satz, {tires} Reifenplätze frei",
+    "nothingFits": "Kein Regal hat Platz für {quantity} Reifen. Schaffen Sie Platz oder legen Sie ein Regal an.",
+    "treadTitle": "Profiltiefe bei Anlieferung",
+    "treadHint": "Optional, klärt aber später jede Diskussion über den Zustand.",
+    "notes": "Notizen",
+    "submit": "Einlagern",
+    "success": "Satz {reference} auf {code} eingelagert",
+    "failed": "Einlagerung fehlgeschlagen",
+    "noVehiclesForCustomer": "Dieser Kunde hat noch keine Fahrzeuge. Legen Sie zuerst eines auf der Kundenseite an.",
+    "treatmentsHint": "Was mit diesem Satz vor der Einlagerung oder Montage geschehen soll.",
+    "storedBefore": "Schon einmal eingelagert",
+    "storedBeforeHint": "Kommen dieselben Reifen zurück? Satz wählen, dann gehört die Saison zur Historie.",
+    "sameSet": "Derselbe Satz wie zuvor",
+    "pieces": "{count} Stk.",
+    "lastOut": "Abgeholt {date}",
+    "notStored": "Derzeit nicht eingelagert",
+    "returned": "Satz {reference} liegt wieder auf {code}",
+    "submitReturn": "Erneut einlagern",
+    "ownerFromVehicle": "Vom Fahrzeug übernommen",
+    "vehicleOwner": "Der Fahrzeughalter",
+    "clearVehicle": "Fahrzeug entfernen"
+  },
+  "storage": {
+    "title": "Regale",
+    "description": "Lager einmal einrichten, dann auf einen Blick sehen, was wohin passt.",
+    "addWarehouse": "Lagerort hinzufügen",
+    "addShelves": "Regale hinzufügen",
+    "warehouseName": "Name",
+    "warehouseNamePlaceholder": "Hauptgebäude, Container 2, Nebenraum",
+    "warehouseAddress": "Adresse",
+    "warehouseCreated": "Lagerort hinzugefügt",
+    "warehouseDeleted": "{name} gelöscht",
+    "warehouseArchived": "{name} archiviert",
+    "deleteWarehouse": "Lagerort löschen",
+    "deleteWarehouseTitle": "{name} löschen?",
+    "deleteWarehouseBody": "Damit werden der Lagerort und alle seine Regale entfernt.",
+    "deleteWarehouseBodyOccupied": "Dieser Lagerort enthält noch {count} Reifen und wird deshalb archiviert statt gelöscht.",
+    "totalCapacity": "Gesamtkapazität",
+    "totalStored": "Aktuell eingelagert",
+    "totalFree": "Freier Platz",
+    "totalLocations": "Regale",
+    "locationSummary": "{occupied} von {total} Regalen belegt",
+    "noLocations": "Noch keine Regale. Legen Sie ein Regal an, um hier Reifen zu lagern.",
+    "emptyTitle": "Noch kein Lager eingerichtet",
+    "emptyBody": "Legen Sie Ihr erstes Gebäude oder Zimmer an und füllen Sie es mit Regalen.",
+    "default": "Standard",
+    "addLocation": "Regale hinzufügen",
+    "editLocation": "Regal bearbeiten",
+    "createLocation": "Regal anlegen",
+    "createShelves": "Regale anlegen",
+    "locationDialogDescription": "Lagerplatz zu {warehouse} hinzufügen.",
+    "tabBulk": "Ein Regalsystem",
+    "tabSingle": "Ein Regal",
+    "zone": "Zone",
+    "zonePlaceholder": "A, Raum 1, Nord",
+    "rack": "Regalreihe",
+    "rackPlaceholder": "04",
+    "shelf": "Fach",
+    "shelfPlaceholder": "2",
+    "position": "Position",
+    "positionPlaceholder": "Links",
+    "shelfFrom": "Fächer von",
+    "shelfTo": "bis",
+    "code": "Code",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Steht auf dem Etikett. Wird für Sie ausgefüllt, wenn Sie es leer lassen.",
+    "capacity": "Reifen pro Fach",
+    "capacityHint": "Wie viele einzelne Reifen hier hineinpassen.",
+    "notes": "Notizen",
+    "bulkPreview": "Erstellt {count} Fächer, {first} bis {last}",
+    "bulkPreviewCapacity": "Platz für insgesamt {tires} Reifen",
+    "bulkCreated": "{count} Fächer erstellt",
+    "bulkCreatedWithSkips": "{created} Fächer erstellt, {skipped} bereits vorhandene übersprungen",
+    "bulkNothingCreated": "Diese Fächer existieren bereits",
+    "locationCreated": "Regal hinzugefügt",
+    "locationUpdated": "Regal aktualisiert",
+    "locationDeleted": "{code} gelöscht",
+    "deleteLocationTitle": "{code} löschen?",
+    "deleteLocationBody": "Das Regal muss zuerst leer sein.",
+    "saveFailed": "Speichern fehlgeschlagen",
+    "deleteFailed": "Löschen fehlgeschlagen"
+  },
+  "movements": {
+    "check_in": "Eingelagert",
+    "check_out": "Ausgelagert",
+    "relocate": "Umgelagert",
+    "dispose": "Entsorgt",
+    "measure": "Gemessen"
+  },
+  "detail": {
+    "move": "Umlagern",
+    "checkOut": "Auslagern",
+    "conditionTitle": "Zustand",
+    "noMeasurements": "Noch keine Messwerte erfasst.",
+    "historyTitle": "Verlauf",
+    "storageTitle": "Lagerung",
+    "shelf": "Fach",
+    "warehouse": "Lagerort",
+    "notStored": "Derzeit nicht eingelagert.",
+    "checkedIn": "Eingelagert",
+    "checkedOut": "Ausgelagert",
+    "tiresTitle": "Reifen",
+    "loadSpeed": "Last- und Geschwindigkeitsindex",
+    "ownerTitle": "Besitzer",
+    "phone": "Telefon",
+    "deleteTitle": "Diesen Reifensatz löschen?",
+    "deleteBody": "Der Satz und sein gesamter Verlauf werden entfernt. Das lässt sich nicht rückgängig machen.",
+    "deleted": "Reifensatz gelöscht",
+    "deleteFailed": "Satz konnte nicht gelöscht werden",
+    "belowLimit": "{count} Reifen auf oder unter Ihrer Ersatzgrenze.",
+    "belowLimitHint": "Bieten Sie dem Kunden einen Preis an, solange die Reifen noch hier sind.",
+    "dispose": "Ausmustern",
+    "disposeTitle": "Diesen Satz ausmustern?",
+    "disposeBody": "Der Eintrag und seine Historie bleiben, der Regalplatz wird frei. Er wird nächste Saison nicht mehr als dieselben Reifen vorgeschlagen.",
+    "disposed": "Satz ausgemustert",
+    "disposeFailed": "Satz konnte nicht ausgemustert werden"
+  },
+  "checkOut": {
+    "title": "Reifen auslagern",
+    "description": "Der Satz wird von {code} genommen.",
+    "descriptionNoLocation": "Dieser Satz geht an den Kunden zurück.",
+    "treadTitle": "Profiltiefe bei der Ausgabe",
+    "note": "Notiz",
+    "submit": "Auslagern",
+    "success": "Satz {reference} zurückgegeben",
+    "failed": "Auslagern fehlgeschlagen",
+    "prepPending": "{count} Vorbereitungsarbeit(en) offen",
+    "conditionTitle": "Zustand bei Anlieferung",
+    "conditionLead": {
+      "replace": "Mindestens ein Reifen liegt auf oder unter Ihrer Ersatzgrenze. Sagen Sie es dem Kunden, bevor er losfährt.",
+      "fair": "Mindestens ein Reifen nähert sich Ihrer Ersatzgrenze. Vor der nächsten Saison ansprechen."
+    },
+    "treadHintUnmeasured": "Bei der Anlieferung wurde nicht gemessen. Dies ist die letzte Gelegenheit dafür."
+  },
+  "relocate": {
+    "title": "Reifen umlagern",
+    "description": "Wählen Sie das Fach, in das der Satz umzieht.",
+    "newLocation": "Neues Fach",
+    "note": "Notiz",
+    "submit": "Umlagern",
+    "success": "Satz {reference} umgelagert",
+    "failed": "Umlagern fehlgeschlagen"
+  },
+  "edit": {
+    "title": "Reifensatz bearbeiten",
+    "description": "Korrigieren Sie, was bei der Einlagerung erfasst wurde. Das Fach ändern Sie über Umlagern.",
+    "quantityHint": "Eine Erhöhung wird gegen das Fach geprüft, in dem der Satz liegt.",
+    "rimType": "Felgentyp",
+    "rimTypePlaceholder": "Alu, Stahl",
+    "success": "Reifensatz aktualisiert",
+    "failed": "Änderungen konnten nicht gespeichert werden",
+    "treadHint": "Eine geänderte Messung wird als neue Messung in der Historie festgehalten, statt die alte zu überschreiben."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Reifen waschen",
+      "wash_rims": "Felgen waschen",
+      "balance": "Wuchten",
+      "tpms_service": "RDKS-Service",
+      "new_valves": "Neue Ventile",
+      "repair": "Reparatur"
+    },
+    "statuses": {
+      "pending": "Offen",
+      "done": "Erledigt",
+      "skipped": "Übersprungen"
+    },
+    "title": "Vorbereitung",
+    "none": "An diesem Satz ist nichts zu tun.",
+    "add": "Vorbereitung hinzufügen",
+    "pickHint": "Haken Sie an, was dieser Satz braucht, bevor er zurück ans Fahrzeug geht.",
+    "pendingCount": "{count} offen",
+    "allComplete": "Alles erledigt",
+    "ready": "Bereit",
+    "markDone": "Erledigt",
+    "completeAll": "Alle {count} als erledigt markieren",
+    "allDone": "Vorbereitung abgeschlossen",
+    "undo": "Zurück auf die Liste",
+    "saveFailed": "Speichern fehlgeschlagen",
+    "skip": "Überspringen"
+  },
+  "messaging": {
+    "action": "Nachricht",
+    "title": "Kunden benachrichtigen",
+    "description": "An {name}.",
+    "noCustomer": "Kein Kunde an diesem Satz.",
+    "noCustomerBody": "Verknüpfen Sie einen Kunden mit dem Satz, bevor Sie schreiben.",
+    "channel": "Senden per",
+    "channels": {
+      "email": "E-Mail",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "keine Adresse hinterlegt",
+    "recipient": "Senden an",
+    "recipientPlaceholder": "Telefonnummer",
+    "subject": "Betreff",
+    "message": "Nachricht",
+    "send": "Senden",
+    "sent": "Nachricht gesendet",
+    "failed": "Nachricht konnte nicht gesendet werden",
+    "smsLong": "Lange Nachrichten werden eventuell auf mehrere SMS verteilt.",
+    "subjects": {
+      "low_tread": "Ihre {season}reifen",
+      "damage": "Ihre {season}reifen",
+      "stored": "Ihre {season}reifen sind eingelagert",
+      "custom": "Ihre {season}reifen"
+    },
+    "bodies": {
+      "low_tread": "Hallo {customer_name}, Ihre {season}reifen liegen bei uns bei {shop_name}. Bei der Annahme hatten {positions} noch {tread}, das ist unter der Grenze. Eine weitere Saison halten sie nicht durch. Sollen wir Ihnen einen Preis für einen neuen Satz machen?",
+      "damage": "Hallo {customer_name}, Ihre {season}reifen liegen bei uns bei {shop_name}. Bei der Annahme haben wir an {positions} eine Beschädigung gefunden. Rufen Sie uns an, wenn es passt, dann gehen wir es zusammen durch.",
+      "stored": "Hallo {customer_name}, Ihre {season}reifen liegen jetzt bei uns bei {shop_name}. Wir melden uns, wenn der Wechsel ansteht.",
+      "custom": "Hallo {customer_name}, zu Ihren bei {shop_name} eingelagerten {season}reifen:"
+    },
+    "enableInSettings": "Noch nicht eingerichtet. In den Einstellungen aktivieren."
+  },
+  "job": {
+    "cardTitle": "Angebote und Aufträge",
+    "none": "Für diesen Satz wurde noch nichts angelegt.",
+    "quote": "Angebot",
+    "workOrder": "Auftrag",
+    "converted": "umgewandelt",
+    "quoteTitle": "Neuen Satz anbieten",
+    "workOrderTitle": "Diesen Satz abrechnen",
+    "loadingSet": "Satz wird geladen",
+    "tires": "Reifen",
+    "inStock": "{count} auf Lager",
+    "shortStock": "nur {count} auf Lager",
+    "noMatch": "Nichts auf Lager passt zu {size}. Die Position wird mit Ihrem Preis angelegt.",
+    "noSize": "Für diesen Satz ist keine Größe erfasst, daher kein Lagerabgleich. Die Position wird mit Ihrem Preis angelegt.",
+    "unitPrice": "Preis pro Reifen",
+    "createQuote": "Angebot erstellen",
+    "createWorkOrder": "Auftrag erstellen",
+    "quoteCreated": "Angebot {number} erstellt",
+    "workOrderCreated": "Auftrag erstellt",
+    "failed": "Konnte nicht angelegt werden",
+    "bannerTitle": "Holen von",
+    "openSet": "Reifensatz öffnen",
+    "prepPending": "Vor der Montage offen: {jobs}",
+    "notOnShelf": "Dieser Satz ist nicht mehr eingelagert.",
+    "targetNew": "Neuer Auftrag",
+    "targetNewHint": "Einen Auftrag nur für diese Reifen anlegen.",
+    "targetExisting": "Oder zu einem bereits offenen Auftrag für dieses Fahrzeug",
+    "addToWorkOrder": "Zum Auftrag hinzufügen",
+    "workOrderUpdated": "Zum Auftrag hinzugefügt",
+    "unlink": "Verknüpfung lösen",
+    "unlinkTitle": "Diese Reifen vom Auftrag lösen?",
+    "unlinkBody": "Der Auftrag verweist nicht mehr auf den Reifensatz. Bereits erfasste Teile und Arbeitspositionen bleiben stehen; entfernen Sie sie separat, wenn sie nicht berechnet werden sollen.",
+    "unlinked": "Reifen gelöst",
+    "unlinkFailed": "Verknüpfung konnte nicht gelöst werden",
+    "prep": "Vorbereitung",
+    "tiresSkipped": "Keine Reifenposition wird hinzugefügt.",
+    "total": "Gesamt",
+    "searchStock": "Lager nach Marke, Größe oder Artikelnummer durchsuchen",
+    "noSearchHits": "Nichts im Lager passt zu \"{query}\".",
+    "otherSize": "Andere Größe",
+    "inSize": "{count} in {size}",
+    "quantity": "Anz.",
+    "noStockItem": "Kein Lagerartikel, Preis von Hand",
+    "storeTires": "Reifen einlagern",
+    "storage": "Lagerung",
+    "storageFee": "Gebühr",
+    "storagePeriod": "Zeitraum",
+    "destination": "Wohin es geht",
+    "targetInvoice": "Neue Rechnung",
+    "targetInvoiceHint": "Direkt an den Kunden, ohne Auftrag an einem Fahrzeug.",
+    "createInvoice": "Rechnung erstellen",
+    "needsCustomer": "Verknüpfen Sie einen Kunden oder ein Fahrzeug, bevor Sie abrechnen.",
+    "storagePeriodEnd": "Abgeholt",
+    "storageOpenEnded": "Offen",
+    "storagePeriodHint": "Erscheint auf der Rechnungsposition, damit der Kunde sieht, wofür die Gebühr gilt. Enddatum offen lassen, bis der Abholtermin feststeht.",
+    "pricesHint": "Die Preise für die Vorbereitung stammen aus den Einstellungen. Jede Position lässt sich danach noch ändern.",
+    "files": "Fotos und Dateien",
+    "filesCount": "{count} auf der Rechnung"
+  },
+  "label": {
+    "action": "Etiketten",
+    "title": "Reifenetiketten drucken",
+    "description": "Aufkleber für jeden Reifen, mit QR-Code zu diesem Satz.",
+    "format": "Etikettengröße",
+    "formats": {
+      "dymo_small": "Kleine Rolle",
+      "dymo_standard": "Standardrolle",
+      "thermal_large": "Große Rolle",
+      "sheet_a4": "A4-Bogen"
+    },
+    "perSheet": "{count} pro Bogen",
+    "copies": "Wie viele",
+    "copiesHint": "einer pro Reifen ({count})",
+    "fewer": "Einer weniger",
+    "more": "Einer mehr",
+    "print": "Drucken",
+    "shelfNote": "Das Fach steht bewusst nicht auf dem Etikett. Sätze werden umgelagert, und ein gedrucktes Fach wäre dann falsch. Der QR-Code zeigt immer den aktuellen Platz.",
+    "reference": "Ref",
+    "quantity": "{count, plural, one {# Reifen} other {# Reifen}}",
+    "withRims": "Auf Felgen",
+    "tpms": "RDKS",
+    "studded": "Spikes",
+    "unassigned": "Nicht zugeordnet",
+    "preview": "Vorschau",
+    "previewScale": "In Druckgröße dargestellt. Der Code lässt sich auch vom Bildschirm scannen.",
+    "previewFailed": "Vorschau nicht möglich"
+  },
+  "vehicle": {
+    "oneOnShelf": "auf {code}",
+    "inStorage": "im Reifenhotel"
+  },
+  "wear": {
+    "summary": "{value} abgefahren zwischen {from} und {to}",
+    "perSeason": "Etwa {value} pro Saison",
+    "seasonsLeft": "{count, plural, =0 {Jetzt an der Grenze} one {Noch etwa eine Saison} other {Noch etwa # Saisons}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "In den Einstellungen ändern",
+    "prepPrices": "Preise für Vorbereitung",
+    "storagePrices": "Lagerpreise in den Einstellungen",
+    "treadLimits": "Grenzwert ändern",
+    "defaultCapacity": "Standardkapazität"
+  },
+  "forecast": {
+    "title": "Nächste Saison",
+    "subtitle": "Was die eingelagerten Sätze laut Messung brauchen, nach Größe.",
+    "backToList": "Zurück zu den Sätzen",
+    "tires": "Erwartete Reifen",
+    "atLimit": "Bereits an der Grenze",
+    "expected": "Innerhalb einer Saison erwartet",
+    "orderTitle": "Nach Größe",
+    "sizes": "{count} Größen",
+    "empty": "Noch nichts nahe der Grenze gemessen. Ein Satz braucht zwei Besuche, bevor sich eine Rate ergibt.",
+    "nowCount": "{count, plural, one {# Satz an der Grenze} other {# Sätze an der Grenze}}",
+    "nextCount": "{count, plural, one {# Satz innerhalb einer Saison} other {# Sätze innerhalb einer Saison}}",
+    "tiresShort": "Reifen",
+    "lowest": "niedrigster {value}",
+    "now": "An der Grenze",
+    "next": "Innerhalb einer Saison",
+    "rate": "{value} pro Saison",
+    "caveat": "Sätze an der Grenze sind gemessen. Der Rest ist aus dem Verschleiß eines Kunden über ein Intervall hochgerechnet: gut zur Planung, nicht als Bestellung.",
+    "unmeasured": "{count, plural, one {# Satz hat zu wenig Historie und wird nicht gezählt} other {# Sätze haben zu wenig Historie und werden nicht gezählt}}",
+    "capped": "Die {shown} neuesten von {total} Sätzen ausgewertet."
+  },
+  "invoiceLine": {
+    "storage": "Reifeneinlagerung",
+    "pieces": "Stk.",
+    "fromDate": "ab {date}"
+  },
+  "setNames": {
+    "winter": "Winterreifen",
+    "summer": "Sommerreifen",
+    "all_season": "Ganzjahresreifen",
+    "other": "Reifen"
+  },
+  "jobNotes": {
+    "set": "Reifensatz {reference}",
+    "shelf": "Regal: {shelf}",
+    "notOnShelf": "nicht im Regal",
+    "onRims": "Auf Felgen",
+    "tiresOnly": "Nur Reifen",
+    "tpms": "Mit RDKS-Sensoren"
+  },
+  "files": {
+    "title": "Fotos und Dateien",
+    "add": "Dateien hinzufügen",
+    "none": "Noch nichts angehängt. Fotografieren Sie eine beschädigte Felge, solange die Reifen vor Ihnen liegen.",
+    "captionPlaceholder": "Was ist darauf zu sehen?",
+    "onInvoice": "Auf der Rechnung",
+    "invoiceHint": "Für die Rechnung markierte Dateien werden beim Abrechnen auf den Auftrag oder die Rechnung kopiert.",
+    "deleteTitle": "Diese Datei entfernen?",
+    "deleteBody": "{name} wird vom Satz entfernt. Bereits auf eine Rechnung kopierte Dateien bleiben dort.",
+    "failed": "Konnte nicht gespeichert werden",
+    "someFailed": "{count, plural, one {# Datei konnte nicht hochgeladen werden} other {# Dateien konnten nicht hochgeladen werden}}"
+  }
+}

+ 28 - 1
messages/en/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Sent Scheduled Message",
     "scheduled_message_send_now": "Sent Scheduled Message",
     "settings_applyTaxRate": "Applied Tax Rate",
     "settings_applyTaxRate": "Applied Tax Rate",
     "settings_convertToExclusive": "Converted Prices to Tax Exclusive",
     "settings_convertToExclusive": "Converted Prices to Tax Exclusive",
-    "settings_convertToInclusive": "Converted Prices to Tax Inclusive"
+    "settings_convertToInclusive": "Converted Prices to Tax Inclusive",
+    "organization_sampleDataRemove": "Removed Sample Data",
+    "tire_warehouse_create": "Created tire storage location",
+    "tire_warehouse_update": "Updated tire storage location",
+    "tire_warehouse_delete": "Deleted tire storage location",
+    "tire_warehouse_archive": "Archived tire storage location",
+    "tire_location_create": "Created tire shelf",
+    "tire_location_bulk_create": "Created tire shelves",
+    "tire_location_update": "Updated tire shelf",
+    "tire_location_delete": "Deleted tire shelf",
+    "tire_set_check_in": "Checked in tire set",
+    "tire_set_check_out": "Checked out tire set",
+    "tire_set_relocate": "Moved tire set",
+    "tire_set_update": "Updated tire set",
+    "tire_set_delete": "Deleted tire set",
+    "tire_measurement_create": "Recorded tire measurements",
+    "tire_treatment_update": "Updated prep work",
+    "tire_treatment_mark": "Marked prep work",
+    "tire_treatment_complete_all": "Completed prep work",
+    "tire_set_message_customer": "Messaged a customer about a tire set",
+    "tire_set_create_quote": "Created a quote from a tire set",
+    "tire_set_create_work_order": "Created a work order from a tire set",
+    "tire_set_add_to_work_order": "Added a tire set to a work order",
+    "tire_set_unlink_work_order": "Unlinked a tire set from a work order",
+    "tire_set_return": "Stored a set again",
+    "tire_set_dispose": "Wrote off a tire set",
+    "tire_set_attach": "Added files to a tire set",
+    "tire_set_detach": "Removed a file from a tire set"
   }
   }
 }
 }

+ 6 - 2
messages/en/common.json

@@ -10,7 +10,8 @@
     "back": "Back",
     "back": "Back",
     "confirm": "Confirm",
     "confirm": "Confirm",
     "edit": "Edit",
     "edit": "Edit",
-    "add": "Add"
+    "add": "Add",
+    "gotIt": "Got it"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Tip: right-click a row for actions",
     "hint": "Tip: right-click a row for actions",
@@ -75,6 +76,9 @@
     "enterManually": "Enter manually",
     "enterManually": "Enter manually",
     "enterBarcode": "Enter barcode...",
     "enterBarcode": "Enter barcode...",
     "scanHint": "Point your camera at a barcode or enter it manually",
     "scanHint": "Point your camera at a barcode or enter it manually",
-    "iosInstallHint": "Tap Share > Add to Home Screen"
+    "iosInstallHint": "Tap Share > Add to Home Screen",
+    "installDescription": "Add to your home screen for quick access",
+    "enterFullscreen": "Fullscreen",
+    "exitFullscreen": "Exit fullscreen"
   }
   }
 }
 }

+ 12 - 1
messages/en/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Recent Completed",
     "recentCompleted": "Recent Completed",
     "activeJobs": "Active Jobs",
     "activeJobs": "Active Jobs",
     "recentObservations": "Recent Observations",
     "recentObservations": "Recent Observations",
-    "recentActivity": "Recent Activity"
+    "recentActivity": "Recent Activity",
+    "gettingStarted": "Getting Started",
+    "tireHotel": "Tire Hotel"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Predicted Maintenance Due",
     "title": "Predicted Maintenance Due",
@@ -233,5 +235,14 @@
       "expired": "Expired",
       "expired": "Expired",
       "converted": "Converted"
       "converted": "Converted"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Tire Hotel",
+    "viewAll": "View tire hotel",
+    "stored": "{count} sets stored",
+    "capacity": "{used} / {capacity} tires",
+    "roomLeft": "Room for {count} more",
+    "needsPrep": "Needs prep",
+    "unbilled": "Unbilled ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/en/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "Tire hotel is on",
+    "body": "Store customers’ seasonal tires. Set up shelves, then check a set in."
+  },
+  "telegram": {
+    "title": "Telegram is on",
+    "body": "Customer conversations land here. Reply without leaving the app."
+  }
+}

+ 1 - 0
messages/en/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Vehicle",
     "vehicle": "Vehicle",
     "template": "Template",
     "template": "Template",
     "progress": "Progress",
     "progress": "Progress",
+    "progressTooltip": "{graded} of {total} checks graded",
     "status": "Status",
     "status": "Status",
     "date": "Date",
     "date": "Date",
     "statusInProgress": "In Progress",
     "statusInProgress": "In Progress",

+ 8 - 2
messages/en/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "All Reminders",
     "allReminders": "All Reminders",
     "auditLog": "Audit Log",
     "auditLog": "Audit Log",
     "aiAssistant": "AI Assistant",
     "aiAssistant": "AI Assistant",
-    "observations": "Observations"
+    "observations": "Observations",
+    "tireHotel": "Tire Hotel",
+    "allTireSets": "All Tire Sets",
+    "tireStorage": "Storage Layout",
+    "tireSetDetails": "Tire Set",
+    "tireForecast": "Next season"
   },
   },
   "search": "Search...",
   "search": "Search...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Sign Out",
     "signOut": "Sign Out",
     "more": "More",
     "more": "More",
     "auditLog": "Audit Log",
     "auditLog": "Audit Log",
-    "aiAssistant": "AI Assistant"
+    "aiAssistant": "AI Assistant",
+    "tireHotel": "Tire hotel"
   }
   }
 }
 }

+ 91 - 0
messages/en/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Set up your workshop",
+    "subtitle": "Enter your workshop name to get started",
+    "workshopName": "Workshop name",
+    "workshopNamePlaceholder": "My Auto Workshop",
+    "submit": "Get started",
+    "sampleDataLabel": "Load sample data",
+    "sampleDataDescription": "Adds a few sample customers, vehicles and jobs so you can look around. You can remove them again with one click.",
+    "setupFailed": "Setup failed",
+    "couldNotCreate": "Could not create workshop",
+    "unexpectedError": "An unexpected error occurred"
+  },
+  "sample": {
+    "prefix": "Sample:",
+    "oilService": "Oil and filter change",
+    "brakeJob": "Front brake pads replacement",
+    "annualService": "Annual service",
+    "quoteTitle": "Front suspension refresh",
+    "inspectionNote": "Front left tyre is close to the wear limit",
+    "parts": {
+      "oilFilter": "Oil filter",
+      "engineOil": "Engine oil 5W-30",
+      "brakePads": "Front brake pad set",
+      "shockAbsorber": "Shock absorber"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Oil change",
+      "description": "Engine oil and filter change"
+    },
+    "frontBrakePads": {
+      "name": "Front brake pads",
+      "description": "Replace front brake pads"
+    },
+    "tireSwap": {
+      "name": "Tire swap",
+      "description": "Seasonal wheel change with torque check"
+    },
+    "batteryReplacement": {
+      "name": "Battery replacement",
+      "description": "Replace and register the starter battery"
+    },
+    "diagnostics": {
+      "name": "Diagnostics",
+      "description": "Fault code readout and initial diagnosis"
+    },
+    "annualService": {
+      "name": "Annual service",
+      "description": "Full annual service according to schedule"
+    }
+  },
+  "checklist": {
+    "title": "Getting started",
+    "description": "Four steps to get your workshop rolling",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Add a customer",
+        "description": "Create your first customer record"
+      },
+      "vehicle": {
+        "title": "Add a vehicle",
+        "description": "Register a vehicle for a customer"
+      },
+      "workOrder": {
+        "title": "Create a work order",
+        "description": "Open your first job"
+      },
+      "invoice": {
+        "title": "Send or download an invoice",
+        "description": "Share an invoice with a customer or download it as a PDF"
+      }
+    },
+    "allDoneTitle": "You are all set",
+    "allDoneDescription": "All steps are done and your workshop is up and running.",
+    "dismiss": "Hide card",
+    "dismissAriaLabel": "Hide this checklist"
+  },
+  "sampleData": {
+    "title": "Sample data",
+    "description": "Your workshop still contains the sample customers, vehicles and jobs from setup. Removing them deletes exactly those records and nothing else.",
+    "footerNote": "Sample data is loaded",
+    "remove": "Remove sample data",
+    "removeConfirmTitle": "Remove sample data?",
+    "removeConfirmDescription": "This deletes the sample customers, vehicles, work orders, quote and inspection created during setup. Your own data is not affected.",
+    "removed": "Sample data removed",
+    "removeFailed": "Could not remove sample data"
+  }
+}

+ 27 - 0
messages/en/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Dashboard",
+    "vehicles": "Vehicles",
+    "customers": "Customers",
+    "work_orders": "Work orders",
+    "quotes": "Quotes",
+    "services": "Services",
+    "billing": "Billing",
+    "inventory": "Inventory",
+    "labor_presets": "Labor presets",
+    "inspections": "Inspections",
+    "tire_hotel": "Tire hotel",
+    "reports": "Reports",
+    "work_board": "Work board",
+    "ai_assistant": "AI assistant",
+    "settings": "Settings"
+  },
+  "actions": {
+    "create": "Create",
+    "read": "View",
+    "update": "Edit",
+    "delete": "Delete",
+    "manage": "Manage",
+    "manage_storage": "Manage storage layout"
+  }
+}

+ 17 - 2
messages/en/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} good",
     "good": "{count} good",
     "fair": "{count} fair",
     "fair": "{count} fair",
     "poor": "{count} poor",
     "poor": "{count} poor",
-    "view": "View"
+    "view": "View",
+    "serviceHistoryTitle": "Service history",
+    "inspectionsTitle": "Inspections",
+    "columnVehicle": "Vehicle",
+    "columnPlate": "Plate",
+    "columnServices": "Services",
+    "columnInspections": "Inspections"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Request Service",
     "title": "Request Service",
@@ -174,6 +180,15 @@
     "yourRequests": "Your Service Requests",
     "yourRequests": "Your Service Requests",
     "failedToLoad": "Failed to load data.",
     "failedToLoad": "Failed to load data.",
     "staffNotes": "Staff notes: {notes}",
     "staffNotes": "Staff notes: {notes}",
-    "preferred": "Preferred: {date}"
+    "preferred": "Preferred: {date}",
+    "notListedOption": "My vehicle isn't listed",
+    "newVehicleHint": "Tell us about the vehicle and we'll add it to your profile.",
+    "fillVehicleDetails": "Please enter the vehicle's make and model",
+    "makeLabel": "Make",
+    "makePlaceholder": "e.g. Ford",
+    "modelLabel": "Model",
+    "modelPlaceholder": "e.g. Kuga",
+    "yearLabel": "Year",
+    "plateLabel": "License plate"
   }
   }
 }
 }

+ 9 - 0
messages/en/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Search",
+  "description": "Search for vehicles, customers, services and settings",
+  "placeholder": "Search by name, plate, phone, VIN, invoice…",
+  "noResults": "No results found.",
+  "recentCustomers": "Recent customers",
+  "services": "Service records",
+  "toClose": "to close"
+}

+ 47 - 1
messages/en/settings.json

@@ -103,6 +103,10 @@
         "title": "Data",
         "title": "Data",
         "description": "Export & import backup"
         "description": "Export & import backup"
       },
       },
+      "install": {
+        "title": "Install app",
+        "description": "Add Torqvoice to this device"
+      },
       "license": {
       "license": {
         "title": "License",
         "title": "License",
         "description": "White-label license"
         "description": "White-label license"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Support",
         "title": "Support",
         "description": "Contact support and manage the support button"
         "description": "Contact support and manage the support button"
+      },
+      "tireHotel": {
+        "title": "Tire hotel",
+        "description": "Seasonal tire storage"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Last backup:",
       "lastBackup": "Last backup:",
       "supportPrompt": "If you have any questions about how your data is protected,",
       "supportPrompt": "If you have any questions about how your data is protected,",
       "supportAction": "contact our support team."
       "supportAction": "contact our support team."
-    }
+    },
+    "optTireHotel": "Tire hotel",
+    "optTireHotelDesc": "Stored tire sets, shelves, prep work and storage agreements"
   },
   },
   "license": {
   "license": {
     "title": "White-Label License",
     "title": "White-Label License",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Contact support",
     "contactTitle": "Contact support",
     "contactHint": "Open the support form straight away.",
     "contactHint": "Open the support form straight away.",
     "contactAction": "Open form"
     "contactAction": "Open form"
+  },
+  "install": {
+    "title": "Install Torqvoice",
+    "description": "Install Torqvoice on this device to open it from the home screen, full screen and without the browser bar. It stays the same app and the same data.",
+    "installed": "Torqvoice is installed on this device.",
+    "iosHint": "In Safari, tap the Share button, then \"Add to Home Screen\".",
+    "unavailable": "This browser has not offered an install prompt. Open the browser menu and look for \"Install app\" or \"Add to Home Screen\".",
+    "bannerHidden": "The install banner is hidden on this device.",
+    "showBanner": "Show banner again"
+  },
+  "tireHotel": {
+    "title": "Tire hotel",
+    "description": "Store your customers’ seasonal tires and keep track of where every set sits.",
+    "cardTitle": "Tire storage",
+    "enable": "Enable tire hotel",
+    "enableHint": "Adds tire storage to the sidebar. Turning it off later hides it again without deleting anything.",
+    "defaultCapacity": "Default tires per shelf",
+    "defaultCapacityHint": "Prefilled when you add new shelves. You can change it per shelf.",
+    "warnPercent": "Warn when full (%)",
+    "warnPercentHint": "Flags the storage overview once this share of total capacity is in use.",
+    "treadTitle": "Replacement thresholds",
+    "treadHint": "Legal minimums differ by country, so set the limits your market uses. Tread below the limit is flagged for replacement.",
+    "summerLimit": "Summer limit ({unit})",
+    "winterLimit": "Winter limit ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "32nds",
+    "setupHint": "Next, set up your shelves.",
+    "setupLink": "Open storage layout",
+    "save": "Save tire hotel settings",
+    "saved": "Tire hotel settings saved",
+    "billingTitle": "Storage billing",
+    "billingHint": "Prefilled on the storage line when you bill a set. Editable on the bill.",
+    "seasonalPrice": "Default storage price",
+    "prepTitle": "Prep pricing",
+    "prepHint": "What each kind of prep costs. Leave one blank and it is never billed, which is what a shop that includes washing in the storage fee wants. Priced ones are added to a quote or work order automatically.",
+    "prepNotCharged": "not charged"
   }
   }
 }
 }

+ 473 - 0
messages/en/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Tire hotel",
+  "description": "Seasonal tire storage for your customers",
+  "common": {
+    "cancel": "Cancel",
+    "save": "Save",
+    "create": "Create",
+    "delete": "Delete",
+    "edit": "Edit"
+  },
+  "seasons": {
+    "summer": "Summer",
+    "winter": "Winter",
+    "all_season": "All season",
+    "other": "Other"
+  },
+  "statuses": {
+    "stored": "In storage",
+    "released": "Returned",
+    "disposed": "Disposed"
+  },
+  "conditions": {
+    "good": "Good",
+    "fair": "Watch",
+    "replace": "Replace"
+  },
+  "positions": {
+    "front_left": "Front left",
+    "front_right": "Front right",
+    "rear_left": "Rear left",
+    "rear_right": "Rear right",
+    "spare": "Spare",
+    "unspecified": "Unspecified"
+  },
+  "capacity": {
+    "used": "{used} of {capacity} tires",
+    "free": "Room for {count}",
+    "freeShort": "{count} free",
+    "over": "{count} over capacity",
+    "meterLabel": "{used} of {capacity} tires stored"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32nds",
+    "inputLabel": "Tread depth, {position}",
+    "lastTime": "was {value}",
+    "worn": "{value} worn"
+  },
+  "list": {
+    "tabAll": "All",
+    "tabStored": "In storage",
+    "tabReleased": "Returned",
+    "search": "Search by plate, customer, brand or shelf",
+    "checkIn": "Check in tires",
+    "empty": "No tire sets yet. Check in a set to get started.",
+    "unassigned": "Unassigned",
+    "reference": "Ref",
+    "vehicle": "Vehicle",
+    "tires": "Tires",
+    "location": "Shelf",
+    "condition": "Condition",
+    "status": "Status",
+    "storedSince": "Since",
+    "tireCount": "{count, plural, one {# tire} other {# tires}}",
+    "studded": "Studded",
+    "roomLeft": "Room for {count}",
+    "prep": "Prep",
+    "tabNeedsPrep": "Needs prep",
+    "setUpShelves": "Set up shelves",
+    "emptyNoShelves": "No shelves yet. Set up your storage layout before checking tires in.",
+    "tabDisposed": "Written off"
+  },
+  "checkIn": {
+    "title": "Check in tires",
+    "description": "Record what arrived, then choose where it goes.",
+    "customer": "Customer",
+    "selectCustomer": "Select customer",
+    "noCustomer": "No customer",
+    "vehicle": "Vehicle",
+    "selectVehicle": "Select vehicle",
+    "season": "Season",
+    "quantity": "Number of tires",
+    "quantityHint": "Counted individually, so a pair or a five-wheel set works too.",
+    "brand": "Brand",
+    "brandPlaceholder": "Michelin, Bridgestone, Nokian",
+    "model": "Model",
+    "size": "Size",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "DOT / TIN code",
+    "dotPlaceholder": "3623",
+    "dotHint": "Week and year the tire was made.",
+    "withRims": "On rims",
+    "hasTpms": "TPMS sensors",
+    "studded": "Studded",
+    "location": "Where the tires go",
+    "choosePlace": "Choose a shelf",
+    "searchLocations": "Search shelves",
+    "noLocations": "No shelves found",
+    "tooFull": "Not enough room",
+    "pickLocationFirst": "Choose a shelf before checking the set in",
+    "roomSummary": "{locations} shelves fit this set, {tires} tire slots free",
+    "nothingFits": "No shelf has room for {quantity} tires. Free up space or add another shelf.",
+    "treadTitle": "Tread depth on arrival",
+    "treadHint": "Optional, but it settles any dispute about condition later.",
+    "notes": "Notes",
+    "submit": "Check in",
+    "success": "Set {reference} stored on {code}",
+    "failed": "Could not check the set in",
+    "noVehiclesForCustomer": "This customer has no vehicles yet. Add one from their customer page first.",
+    "treatmentsHint": "What should be done to this set before it is stored or refitted.",
+    "storedBefore": "Stored here before",
+    "storedBeforeHint": "Bringing the same tires back? Pick the set so this season joins its history.",
+    "sameSet": "Same set as before",
+    "pieces": "{count} pcs",
+    "lastOut": "Left {date}",
+    "notStored": "Not currently stored",
+    "returned": "Set {reference} is back on {code}",
+    "submitReturn": "Store again",
+    "ownerFromVehicle": "Taken from the vehicle",
+    "vehicleOwner": "The vehicle owner",
+    "clearVehicle": "Clear the vehicle"
+  },
+  "storage": {
+    "title": "Shelves",
+    "description": "Set up your storage once, then see at a glance what fits where.",
+    "addWarehouse": "Add location",
+    "addShelves": "Add shelves",
+    "warehouseName": "Name",
+    "warehouseNamePlaceholder": "Main building, Container 2, Back room",
+    "warehouseAddress": "Address",
+    "warehouseCreated": "Storage location added",
+    "warehouseDeleted": "Deleted {name}",
+    "warehouseArchived": "Archived {name}",
+    "deleteWarehouse": "Delete storage location",
+    "deleteWarehouseTitle": "Delete {name}?",
+    "deleteWarehouseBody": "This removes the location and all its shelves.",
+    "deleteWarehouseBodyOccupied": "This location still holds {count} tires, so it will be archived rather than deleted.",
+    "totalCapacity": "Total capacity",
+    "totalStored": "Stored now",
+    "totalFree": "Room left",
+    "totalLocations": "Shelves",
+    "locationSummary": "{occupied} of {total} shelves in use",
+    "noLocations": "No shelves yet. Add a rack to start storing tires here.",
+    "emptyTitle": "No storage set up yet",
+    "emptyBody": "Add your first building or room, then fill it with shelves.",
+    "default": "Default",
+    "addLocation": "Add shelves",
+    "editLocation": "Edit shelf",
+    "createLocation": "Create shelf",
+    "createShelves": "Create shelves",
+    "locationDialogDescription": "Adding storage to {warehouse}.",
+    "tabBulk": "A rack of shelves",
+    "tabSingle": "One shelf",
+    "zone": "Zone",
+    "zonePlaceholder": "A, Room 1, North",
+    "rack": "Rack",
+    "rackPlaceholder": "04",
+    "shelf": "Shelf",
+    "shelfPlaceholder": "2",
+    "position": "Position",
+    "positionPlaceholder": "Left",
+    "shelfFrom": "Shelves from",
+    "shelfTo": "to",
+    "code": "Code",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Written on the label. Filled in for you if you leave it blank.",
+    "capacity": "Tires per shelf",
+    "capacityHint": "How many individual tires fit here.",
+    "notes": "Notes",
+    "bulkPreview": "Creates {count} shelves, {first} through {last}",
+    "bulkPreviewCapacity": "Room for {tires} tires in total",
+    "bulkCreated": "Created {count} shelves",
+    "bulkCreatedWithSkips": "Created {created} shelves, skipped {skipped} that already existed",
+    "bulkNothingCreated": "Those shelves already exist",
+    "locationCreated": "Shelf added",
+    "locationUpdated": "Shelf updated",
+    "locationDeleted": "Deleted {code}",
+    "deleteLocationTitle": "Delete {code}?",
+    "deleteLocationBody": "The shelf has to be empty first.",
+    "saveFailed": "Could not save",
+    "deleteFailed": "Could not delete"
+  },
+  "movements": {
+    "check_in": "Checked in",
+    "check_out": "Checked out",
+    "relocate": "Moved",
+    "dispose": "Disposed",
+    "measure": "Measured"
+  },
+  "detail": {
+    "move": "Move",
+    "checkOut": "Check out",
+    "conditionTitle": "Condition",
+    "noMeasurements": "No readings recorded yet.",
+    "historyTitle": "History",
+    "storageTitle": "Storage",
+    "shelf": "Shelf",
+    "warehouse": "Location",
+    "notStored": "Not currently in storage.",
+    "checkedIn": "Checked in",
+    "checkedOut": "Checked out",
+    "tiresTitle": "Tires",
+    "loadSpeed": "Load and speed",
+    "ownerTitle": "Owner",
+    "phone": "Phone",
+    "deleteTitle": "Delete this tire set?",
+    "deleteBody": "The set and its full history are removed. This cannot be undone.",
+    "deleted": "Tire set deleted",
+    "deleteFailed": "Could not delete the set",
+    "belowLimit": "{count} tire(s) at or below your replacement limit.",
+    "belowLimitHint": "Worth offering the customer a price while the tires are still here.",
+    "dispose": "Write off",
+    "disposeTitle": "Write this set off?",
+    "disposeBody": "The record and its history stay, and the shelf is freed. It stops being offered as the same tires next season.",
+    "disposed": "Set written off",
+    "disposeFailed": "Could not write the set off"
+  },
+  "checkOut": {
+    "title": "Check out tires",
+    "description": "Taking the set off {code}.",
+    "descriptionNoLocation": "Returning this set to the customer.",
+    "treadTitle": "Tread depth on the way out",
+    "note": "Note",
+    "submit": "Check out",
+    "success": "Set {reference} returned",
+    "failed": "Could not check the set out",
+    "prepPending": "{count} prep job(s) still outstanding",
+    "conditionTitle": "Condition on arrival",
+    "conditionLead": {
+      "replace": "One or more tires are at or below your replacement limit. Worth telling the customer before they drive off.",
+      "fair": "One or more tires are close to your replacement limit. Worth a word before next season."
+    },
+    "treadHintUnmeasured": "No reading was taken when these came in. This is the last chance to record one."
+  },
+  "relocate": {
+    "title": "Move tires",
+    "description": "Pick the shelf this set moves to.",
+    "newLocation": "New shelf",
+    "note": "Note",
+    "submit": "Move",
+    "success": "Set {reference} moved",
+    "failed": "Could not move the set"
+  },
+  "edit": {
+    "title": "Edit tire set",
+    "description": "Correct what was recorded at check-in. Use Move to change the shelf.",
+    "quantityHint": "Raising this is checked against the shelf the set sits on.",
+    "rimType": "Rim type",
+    "rimTypePlaceholder": "Alloy, steel",
+    "success": "Tire set updated",
+    "failed": "Could not save the changes",
+    "treadHint": "Changing a reading records a new measurement in the history rather than rewriting the old one."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Wash tires",
+      "wash_rims": "Wash rims",
+      "balance": "Balance",
+      "tpms_service": "TPMS service",
+      "new_valves": "New valves",
+      "repair": "Repair"
+    },
+    "statuses": {
+      "pending": "To do",
+      "done": "Done",
+      "skipped": "Skipped"
+    },
+    "title": "Prep work",
+    "none": "Nothing to do on this set.",
+    "add": "Add prep work",
+    "pickHint": "Tick what this set needs before it goes back on the car.",
+    "pendingCount": "{count} to do",
+    "allComplete": "All done",
+    "ready": "Ready",
+    "markDone": "Done",
+    "completeAll": "Mark all {count} done",
+    "allDone": "All prep work done",
+    "undo": "Put back on the list",
+    "saveFailed": "Could not save",
+    "skip": "Skip"
+  },
+  "messaging": {
+    "action": "Message",
+    "title": "Message the customer",
+    "description": "To {name}.",
+    "noCustomer": "No customer on this set.",
+    "noCustomerBody": "Link a customer to the set before messaging them.",
+    "channel": "Send by",
+    "channels": {
+      "email": "Email",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "no address on file",
+    "recipient": "Send to",
+    "recipientPlaceholder": "Phone number",
+    "subject": "Subject",
+    "message": "Message",
+    "send": "Send",
+    "sent": "Message sent",
+    "failed": "Could not send the message",
+    "smsLong": "Long messages may be split into several texts.",
+    "subjects": {
+      "low_tread": "Your {season} tires",
+      "damage": "Your {season} tires",
+      "stored": "Your {season} tires are stored",
+      "custom": "Your {season} tires"
+    },
+    "bodies": {
+      "low_tread": "Hi {customer_name}, your {season} tires are stored with us at {shop_name}. When we checked them in, the {positions} measured {tread}, which is under the limit. They won't get through another season. Want a price on a new set?",
+      "damage": "Hi {customer_name}, your {season} tires are stored with us at {shop_name}. We found damage on the {positions} when we checked them in. Give us a call when you get a chance and we'll go through it with you.",
+      "stored": "Hi {customer_name}, your {season} tires are now in storage with us at {shop_name}. We'll let you know when it's time to swap them back.",
+      "custom": "Hi {customer_name}, about your {season} tires stored with us at {shop_name}:"
+    },
+    "enableInSettings": "Not set up yet. Turn it on in Settings."
+  },
+  "job": {
+    "cardTitle": "Quotes and jobs",
+    "none": "Nothing raised for this set yet.",
+    "quote": "Quote",
+    "workOrder": "Work order",
+    "converted": "converted",
+    "quoteTitle": "Quote a new set",
+    "workOrderTitle": "Bill this set",
+    "loadingSet": "Loading the set",
+    "tires": "Tires",
+    "inStock": "{count} in stock",
+    "shortStock": "only {count} in stock",
+    "noMatch": "Nothing in stock matches {size}. The line is added at the price you set.",
+    "noSize": "This set has no size recorded, so stock cannot be matched. The line is added at the price you set.",
+    "unitPrice": "Price per tire",
+    "createQuote": "Create quote",
+    "createWorkOrder": "Create work order",
+    "quoteCreated": "Quote {number} created",
+    "workOrderCreated": "Work order created",
+    "failed": "Could not create that",
+    "bannerTitle": "Fetch from",
+    "openSet": "Open tire set",
+    "prepPending": "Still to do before fitting: {jobs}",
+    "notOnShelf": "This set is no longer in storage.",
+    "targetNew": "New work order",
+    "targetNewHint": "Raise a job just for these tires.",
+    "targetExisting": "Or add to a job already open for this vehicle",
+    "addToWorkOrder": "Add to work order",
+    "workOrderUpdated": "Added to the work order",
+    "unlink": "Unlink",
+    "unlinkTitle": "Unlink these tires from the job?",
+    "unlinkBody": "The job stops pointing at the tire set. The parts and labour lines already on it stay, so remove them separately if they should not be billed.",
+    "unlinked": "Tires unlinked",
+    "unlinkFailed": "Could not unlink the tires",
+    "prep": "Prep work",
+    "tiresSkipped": "No tire line added.",
+    "total": "Total",
+    "searchStock": "Search inventory by brand, size or part number",
+    "noSearchHits": "Nothing in inventory matches \"{query}\".",
+    "otherSize": "Other size",
+    "inSize": "{count} in {size}",
+    "quantity": "Qty",
+    "noStockItem": "No stock item, priced by hand",
+    "storeTires": "Store tires",
+    "storage": "Storage",
+    "storageFee": "Fee",
+    "storagePeriod": "Period",
+    "destination": "Where it goes",
+    "targetInvoice": "New invoice",
+    "targetInvoiceHint": "Bill the customer directly, with no job on a vehicle.",
+    "createInvoice": "Create invoice",
+    "needsCustomer": "Link a customer or a vehicle to this set before billing it.",
+    "storagePeriodEnd": "Collected",
+    "storageOpenEnded": "Open",
+    "storagePeriodHint": "Prints on the invoice line so the customer can see what the fee covers. Leave the end blank until you know when they are collecting.",
+    "pricesHint": "Prep prices come from settings. Every line can still be edited after this is created.",
+    "files": "Photos and files",
+    "filesCount": "{count} on the invoice"
+  },
+  "label": {
+    "action": "Labels",
+    "title": "Print tire labels",
+    "description": "Stickers for each tire, with a QR that opens this set.",
+    "format": "Label size",
+    "formats": {
+      "dymo_small": "Small roll",
+      "dymo_standard": "Standard roll",
+      "thermal_large": "Large roll",
+      "sheet_a4": "A4 sheet"
+    },
+    "perSheet": "{count} per sheet",
+    "copies": "How many",
+    "copiesHint": "one per tire ({count})",
+    "fewer": "One fewer",
+    "more": "One more",
+    "print": "Print",
+    "shelfNote": "The shelf is not printed on purpose. Sets get moved, and a printed shelf would then be wrong. Scanning the QR always shows where it is now.",
+    "reference": "Ref",
+    "quantity": "{count, plural, one {# tire} other {# tires}}",
+    "withRims": "On rims",
+    "tpms": "TPMS",
+    "studded": "Studded",
+    "unassigned": "Unassigned",
+    "preview": "Preview",
+    "previewScale": "Shown at print size. Scanning the code on screen works too.",
+    "previewFailed": "Could not build a preview"
+  },
+  "vehicle": {
+    "oneOnShelf": "on {code}",
+    "inStorage": "in tire hotel"
+  },
+  "wear": {
+    "summary": "Worn {value} between {from} and {to}",
+    "perSeason": "About {value} a season",
+    "seasonsLeft": "{count, plural, =0 {At the limit now} one {Roughly one more season} other {Roughly # more seasons}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Change in settings",
+    "prepPrices": "Prep prices in settings",
+    "storagePrices": "Storage prices in settings",
+    "treadLimits": "Change the limit",
+    "defaultCapacity": "Default capacity"
+  },
+  "forecast": {
+    "title": "Next season",
+    "subtitle": "What your stored sets are measured to need, by size.",
+    "backToList": "Back to sets",
+    "tires": "Tires expected",
+    "atLimit": "Already at the limit",
+    "expected": "Expected within a season",
+    "orderTitle": "By size",
+    "sizes": "{count} sizes",
+    "empty": "Nothing is measured close to its limit yet. Sets need two visits before a rate can be worked out.",
+    "nowCount": "{count, plural, one {# set at the limit} other {# sets at the limit}}",
+    "nextCount": "{count, plural, one {# set within a season} other {# sets within a season}}",
+    "tiresShort": "tires",
+    "lowest": "lowest {value}",
+    "now": "At the limit",
+    "next": "Within a season",
+    "rate": "{value} a season",
+    "caveat": "Sets already at the limit are measured. The rest is extrapolated from one customer’s wear over one interval, so treat it as demand to plan around, not an order to place.",
+    "unmeasured": "{count, plural, one {# set has too little history to judge and is not counted} other {# sets have too little history to judge and are not counted}}",
+    "capped": "Read the {shown} most recent sets of {total}."
+  },
+  "invoiceLine": {
+    "storage": "Tire storage",
+    "pieces": "pcs",
+    "fromDate": "from {date}"
+  },
+  "setNames": {
+    "winter": "winter tires",
+    "summer": "summer tires",
+    "all_season": "all-season tires",
+    "other": "tires"
+  },
+  "jobNotes": {
+    "set": "Tire set {reference}",
+    "shelf": "Shelf: {shelf}",
+    "notOnShelf": "not on a shelf",
+    "onRims": "On rims",
+    "tiresOnly": "Tires only",
+    "tpms": "Has TPMS sensors"
+  },
+  "files": {
+    "title": "Photos and files",
+    "add": "Add files",
+    "none": "Nothing attached yet. Photograph a kerbed rim or damage while the tires are in front of you.",
+    "captionPlaceholder": "What does this show?",
+    "onInvoice": "On the invoice",
+    "invoiceHint": "Files marked for the invoice are copied onto the work order or invoice when this set is billed.",
+    "deleteTitle": "Remove this file?",
+    "deleteBody": "{name} is removed from this set. Anything already copied onto an invoice stays there.",
+    "failed": "Could not save that",
+    "someFailed": "{count, plural, one {# file could not be uploaded} other {# files could not be uploaded}}"
+  }
+}

+ 28 - 1
messages/es/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Mensaje programado enviado",
     "scheduled_message_send_now": "Mensaje programado enviado",
     "settings_applyTaxRate": "Tipo impositivo aplicado",
     "settings_applyTaxRate": "Tipo impositivo aplicado",
     "settings_convertToExclusive": "Precios convertidos a sin impuestos",
     "settings_convertToExclusive": "Precios convertidos a sin impuestos",
-    "settings_convertToInclusive": "Precios convertidos a con impuestos"
+    "settings_convertToInclusive": "Precios convertidos a con impuestos",
+    "organization_sampleDataRemove": "Datos de ejemplo eliminados",
+    "tire_warehouse_create": "Ubicación de almacén de neumáticos creada",
+    "tire_warehouse_update": "Ubicación de almacén de neumáticos actualizada",
+    "tire_warehouse_delete": "Ubicación de almacén de neumáticos eliminada",
+    "tire_warehouse_archive": "Ubicación de almacén de neumáticos archivada",
+    "tire_location_create": "Estante de neumáticos creado",
+    "tire_location_bulk_create": "Estantes de neumáticos creados",
+    "tire_location_update": "Estante de neumáticos actualizado",
+    "tire_location_delete": "Estante de neumáticos eliminado",
+    "tire_set_check_in": "Juego de neumáticos recibido",
+    "tire_set_check_out": "Juego de neumáticos entregado",
+    "tire_set_relocate": "Juego de neumáticos movido",
+    "tire_set_update": "Juego de neumáticos actualizado",
+    "tire_set_delete": "Juego de neumáticos eliminado",
+    "tire_measurement_create": "Mediciones de neumáticos registradas",
+    "tire_treatment_update": "Preparación actualizada",
+    "tire_treatment_mark": "Preparación marcada",
+    "tire_treatment_complete_all": "Preparación completada",
+    "tire_set_message_customer": "Mensaje enviado al cliente sobre un juego de neumáticos",
+    "tire_set_create_quote": "Presupuesto creado desde un juego de neumáticos",
+    "tire_set_create_work_order": "Orden creada desde un juego de neumáticos",
+    "tire_set_add_to_work_order": "Juego de neumáticos añadido a una orden",
+    "tire_set_unlink_work_order": "Juego de neumáticos desvinculado de una orden",
+    "tire_set_return": "Juego almacenado de nuevo",
+    "tire_set_dispose": "Juego de neumáticos dado de baja",
+    "tire_set_attach": "Archivos añadidos a un juego de neumáticos",
+    "tire_set_detach": "Archivo quitado de un juego de neumáticos"
   }
   }
 }
 }

+ 6 - 2
messages/es/common.json

@@ -10,7 +10,8 @@
     "confirm": "Confirmar",
     "confirm": "Confirmar",
     "saveChanges": "Guardar cambios",
     "saveChanges": "Guardar cambios",
     "edit": "Editar",
     "edit": "Editar",
-    "add": "Agregar"
+    "add": "Agregar",
+    "gotIt": "Entendido"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Consejo: clic derecho en una fila para ver acciones",
     "hint": "Consejo: clic derecho en una fila para ver acciones",
@@ -75,6 +76,9 @@
     "enterManually": "Introducir manualmente",
     "enterManually": "Introducir manualmente",
     "enterBarcode": "Introducir código de barras…",
     "enterBarcode": "Introducir código de barras…",
     "scanHint": "Apunte la cámara a un código de barras o introdúzcalo manualmente",
     "scanHint": "Apunte la cámara a un código de barras o introdúzcalo manualmente",
-    "iosInstallHint": "Toque Compartir > Añadir a inicio"
+    "iosInstallHint": "Toque Compartir > Añadir a inicio",
+    "installDescription": "Añádela a tu pantalla de inicio para acceder rápido",
+    "enterFullscreen": "Pantalla completa",
+    "exitFullscreen": "Salir de pantalla completa"
   }
   }
 }
 }

+ 12 - 1
messages/es/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Completados recientemente",
     "recentCompleted": "Completados recientemente",
     "activeJobs": "Trabajos activos",
     "activeJobs": "Trabajos activos",
     "recentObservations": "Observaciones recientes",
     "recentObservations": "Observaciones recientes",
-    "recentActivity": "Actividad reciente"
+    "recentActivity": "Actividad reciente",
+    "gettingStarted": "Primeros pasos",
+    "tireHotel": "Hotel de neumáticos"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Mantenimiento previsto pendiente",
     "title": "Mantenimiento previsto pendiente",
@@ -233,5 +235,14 @@
       "expired": "Caducado",
       "expired": "Caducado",
       "converted": "Convertido"
       "converted": "Convertido"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Hotel de neumáticos",
+    "viewAll": "Ver hotel de neumáticos",
+    "stored": "{count} juegos guardados",
+    "capacity": "{used} / {capacity} neumáticos",
+    "roomLeft": "Sitio para {count} más",
+    "needsPrep": "Pendiente de preparar",
+    "unbilled": "Sin facturar ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/es/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "El hotel de neumáticos está activo",
+    "body": "Guarda neumáticos de temporada. Crea los estantes y recibe un juego."
+  },
+  "telegram": {
+    "title": "Telegram está activo",
+    "body": "Las conversaciones con clientes llegan aquí. Responde sin salir de la app."
+  }
+}

+ 1 - 0
messages/es/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Vehículo",
     "vehicle": "Vehículo",
     "template": "Plantilla",
     "template": "Plantilla",
     "progress": "Avance",
     "progress": "Avance",
+    "progressTooltip": "{graded} de {total} puntos calificados",
     "status": "Estado",
     "status": "Estado",
     "date": "Fecha",
     "date": "Fecha",
     "statusInProgress": "En curso",
     "statusInProgress": "En curso",

+ 8 - 2
messages/es/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Todos los recordatorios",
     "allReminders": "Todos los recordatorios",
     "auditLog": "Registro de auditoría",
     "auditLog": "Registro de auditoría",
     "aiAssistant": "Asistente IA",
     "aiAssistant": "Asistente IA",
-    "observations": "Observaciones"
+    "observations": "Observaciones",
+    "tireHotel": "Hotel de neumáticos",
+    "allTireSets": "Todos los juegos",
+    "tireStorage": "Distribución del almacén",
+    "tireSetDetails": "Juego de neumáticos",
+    "tireForecast": "La próxima temporada"
   },
   },
   "search": "Buscar...",
   "search": "Buscar...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Cerrar sesión",
     "signOut": "Cerrar sesión",
     "more": "Más",
     "more": "Más",
     "auditLog": "Registro de auditoría",
     "auditLog": "Registro de auditoría",
-    "aiAssistant": "Asistente IA"
+    "aiAssistant": "Asistente IA",
+    "tireHotel": "Hotel de neumáticos"
   }
   }
 }
 }

+ 91 - 0
messages/es/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Configure su taller",
+    "subtitle": "Ingrese el nombre de su taller para comenzar",
+    "workshopName": "Nombre del taller",
+    "workshopNamePlaceholder": "Mi Taller Mecánico",
+    "submit": "Comenzar",
+    "sampleDataLabel": "Cargar datos de ejemplo",
+    "sampleDataDescription": "Añade algunos clientes, vehículos y trabajos de ejemplo para que pueda explorar. Puede eliminarlos de nuevo con un clic.",
+    "setupFailed": "Error en la configuración",
+    "couldNotCreate": "No se pudo crear el taller",
+    "unexpectedError": "Ocurrió un error inesperado"
+  },
+  "sample": {
+    "prefix": "Ejemplo:",
+    "oilService": "Cambio de aceite y filtro",
+    "brakeJob": "Sustitución de pastillas de freno delanteras",
+    "annualService": "Revisión anual",
+    "quoteTitle": "Renovación de la suspensión delantera",
+    "inspectionNote": "El neumático delantero izquierdo está cerca del límite de desgaste",
+    "parts": {
+      "oilFilter": "Filtro de aceite",
+      "engineOil": "Aceite de motor 5W-30",
+      "brakePads": "Juego de pastillas de freno delanteras",
+      "shockAbsorber": "Amortiguador"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Cambio de aceite",
+      "description": "Cambio de aceite de motor y filtro"
+    },
+    "frontBrakePads": {
+      "name": "Pastillas de freno delanteras",
+      "description": "Sustituir las pastillas de freno delanteras"
+    },
+    "tireSwap": {
+      "name": "Cambio de ruedas",
+      "description": "Cambio de ruedas de temporada con control de apriete"
+    },
+    "batteryReplacement": {
+      "name": "Sustitución de batería",
+      "description": "Sustituir y registrar la batería de arranque"
+    },
+    "diagnostics": {
+      "name": "Diagnóstico",
+      "description": "Lectura de códigos de avería y diagnóstico inicial"
+    },
+    "annualService": {
+      "name": "Revisión anual",
+      "description": "Revisión anual completa según el programa"
+    }
+  },
+  "checklist": {
+    "title": "Primeros pasos",
+    "description": "Cuatro pasos para poner su taller en marcha",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Añadir un cliente",
+        "description": "Cree su primera ficha de cliente"
+      },
+      "vehicle": {
+        "title": "Añadir un vehículo",
+        "description": "Registre un vehículo para un cliente"
+      },
+      "workOrder": {
+        "title": "Crear una orden de trabajo",
+        "description": "Abra su primer trabajo"
+      },
+      "invoice": {
+        "title": "Enviar o descargar una factura",
+        "description": "Comparta una factura con un cliente o descárguela en PDF"
+      }
+    },
+    "allDoneTitle": "Todo listo",
+    "allDoneDescription": "Todos los pasos están completados y su taller ya está en marcha.",
+    "dismiss": "Ocultar tarjeta",
+    "dismissAriaLabel": "Ocultar esta lista"
+  },
+  "sampleData": {
+    "title": "Datos de ejemplo",
+    "description": "Su taller todavía contiene los clientes, vehículos y trabajos de ejemplo de la configuración. Al eliminarlos se borran exactamente esos registros y nada más.",
+    "footerNote": "Hay datos de ejemplo cargados",
+    "remove": "Eliminar datos de ejemplo",
+    "removeConfirmTitle": "¿Eliminar los datos de ejemplo?",
+    "removeConfirmDescription": "Esto elimina los clientes, vehículos, órdenes de trabajo, el presupuesto y la inspección de ejemplo creados durante la configuración. Sus propios datos no se ven afectados.",
+    "removed": "Datos de ejemplo eliminados",
+    "removeFailed": "No se pudieron eliminar los datos de ejemplo"
+  }
+}

+ 27 - 0
messages/es/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Panel",
+    "vehicles": "Vehículos",
+    "customers": "Clientes",
+    "work_orders": "Órdenes de trabajo",
+    "quotes": "Presupuestos",
+    "services": "Historial de servicio",
+    "billing": "Facturación",
+    "inventory": "Inventario",
+    "labor_presets": "Plantillas de mano de obra",
+    "inspections": "Inspecciones",
+    "tire_hotel": "Hotel de neumáticos",
+    "reports": "Informes",
+    "work_board": "Tablero de trabajo",
+    "ai_assistant": "Asistente de IA",
+    "settings": "Ajustes"
+  },
+  "actions": {
+    "create": "Crear",
+    "read": "Ver",
+    "update": "Editar",
+    "delete": "Eliminar",
+    "manage": "Gestionar",
+    "manage_storage": "Gestionar la distribución del almacén"
+  }
+}

+ 17 - 2
messages/es/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} bueno",
     "good": "{count} bueno",
     "fair": "{count} regular",
     "fair": "{count} regular",
     "poor": "{count} malo",
     "poor": "{count} malo",
-    "view": "Ver"
+    "view": "Ver",
+    "serviceHistoryTitle": "Historial de servicio",
+    "inspectionsTitle": "Inspecciones",
+    "columnVehicle": "Vehículo",
+    "columnPlate": "Matrícula",
+    "columnServices": "Servicios",
+    "columnInspections": "Inspecciones"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Solicitar servicio",
     "title": "Solicitar servicio",
@@ -174,6 +180,15 @@
     "yourRequests": "Sus solicitudes de servicio",
     "yourRequests": "Sus solicitudes de servicio",
     "failedToLoad": "Error al cargar los datos.",
     "failedToLoad": "Error al cargar los datos.",
     "staffNotes": "Notas del personal: {notes}",
     "staffNotes": "Notas del personal: {notes}",
-    "preferred": "Preferido: {date}"
+    "preferred": "Preferido: {date}",
+    "notListedOption": "Mi vehículo no está en la lista",
+    "newVehicleHint": "Cuéntanos sobre el vehículo y lo añadiremos a tu perfil.",
+    "fillVehicleDetails": "Introduce la marca y el modelo del vehículo",
+    "makeLabel": "Marca",
+    "makePlaceholder": "p. ej. Ford",
+    "modelLabel": "Modelo",
+    "modelPlaceholder": "p. ej. Kuga",
+    "yearLabel": "Año",
+    "plateLabel": "Matrícula"
   }
   }
 }
 }

+ 9 - 0
messages/es/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Buscar",
+  "description": "Busca vehículos, clientes, trabajos y ajustes",
+  "placeholder": "Buscar por nombre, matrícula, teléfono, VIN, factura…",
+  "noResults": "Sin resultados.",
+  "recentCustomers": "Clientes recientes",
+  "services": "Historial de servicio",
+  "toClose": "para cerrar"
+}

+ 47 - 1
messages/es/settings.json

@@ -103,6 +103,10 @@
         "title": "Datos",
         "title": "Datos",
         "description": "Exportar e importar respaldo"
         "description": "Exportar e importar respaldo"
       },
       },
+      "install": {
+        "title": "Instalar app",
+        "description": "Añade Torqvoice a este dispositivo"
+      },
       "license": {
       "license": {
         "title": "Licencia",
         "title": "Licencia",
         "description": "Licencia marca blanca"
         "description": "Licencia marca blanca"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Soporte",
         "title": "Soporte",
         "description": "Contactar con soporte y gestionar el botón"
         "description": "Contactar con soporte y gestionar el botón"
+      },
+      "tireHotel": {
+        "title": "Hotel de neumáticos",
+        "description": "Almacenamiento estacional de neumáticos"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Última copia de seguridad:",
       "lastBackup": "Última copia de seguridad:",
       "supportPrompt": "Si tiene alguna pregunta sobre cómo se protegen sus datos,",
       "supportPrompt": "Si tiene alguna pregunta sobre cómo se protegen sus datos,",
       "supportAction": "contacte con nuestro equipo de soporte."
       "supportAction": "contacte con nuestro equipo de soporte."
-    }
+    },
+    "optTireHotel": "Hotel de neumáticos",
+    "optTireHotelDesc": "Juegos guardados, estantes, preparación y contratos de almacenaje"
   },
   },
   "license": {
   "license": {
     "title": "Licencia marca blanca",
     "title": "Licencia marca blanca",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Contactar con soporte",
     "contactTitle": "Contactar con soporte",
     "contactHint": "Abrir el formulario de soporte directamente.",
     "contactHint": "Abrir el formulario de soporte directamente.",
     "contactAction": "Abrir formulario"
     "contactAction": "Abrir formulario"
+  },
+  "install": {
+    "title": "Instalar Torqvoice",
+    "description": "Instala Torqvoice en este dispositivo para abrirlo desde la pantalla de inicio, a pantalla completa y sin la barra del navegador. Es la misma app y los mismos datos.",
+    "installed": "Torqvoice está instalada en este dispositivo.",
+    "iosHint": "En Safari, toca el botón Compartir y luego «Añadir a pantalla de inicio».",
+    "unavailable": "Este navegador no ha ofrecido la instalación. Abre el menú del navegador y busca «Instalar app» o «Añadir a pantalla de inicio».",
+    "bannerHidden": "El aviso de instalación está oculto en este dispositivo.",
+    "showBanner": "Mostrar el aviso de nuevo"
+  },
+  "tireHotel": {
+    "title": "Hotel de neumáticos",
+    "description": "Guarda los neumáticos de temporada de tus clientes y controla dónde está cada juego.",
+    "cardTitle": "Almacenamiento de neumáticos",
+    "enable": "Activar hotel de neumáticos",
+    "enableHint": "Añade el almacenamiento de neumáticos al menú. Desactivarlo después lo oculta sin borrar nada.",
+    "defaultCapacity": "Neumáticos por balda por defecto",
+    "defaultCapacityHint": "Se rellena al crear baldas nuevas. Se puede cambiar en cada balda.",
+    "warnPercent": "Avisar al llenarse (%)",
+    "warnPercentHint": "Marca el resumen del almacén cuando se ocupa esta parte de la capacidad total.",
+    "treadTitle": "Límites de sustitución",
+    "treadHint": "Los mínimos legales cambian según el país, así que fija los límites de tu mercado. El dibujo por debajo del límite se marca para sustituir.",
+    "summerLimit": "Límite verano ({unit})",
+    "winterLimit": "Límite invierno ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "A continuación, configura tus estantes.",
+    "setupLink": "Abrir distribución del almacén",
+    "save": "Guardar ajustes del hotel de neumáticos",
+    "saved": "Ajustes del hotel de neumáticos guardados",
+    "billingTitle": "Facturación del almacenaje",
+    "billingHint": "Se rellena en la línea de almacenaje al facturar un juego. Editable en la factura.",
+    "seasonalPrice": "Precio de almacenaje por defecto",
+    "prepTitle": "Precios de preparación",
+    "prepHint": "Lo que cuesta cada tipo de preparación. Déjalo en blanco y no se factura nunca, que es lo que quiere un taller que incluye el lavado en el almacenaje. Las que tienen precio se añaden solas al presupuesto y a la orden.",
+    "prepNotCharged": "no se cobra"
   }
   }
 }
 }

+ 473 - 0
messages/es/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Hotel de neumáticos",
+  "description": "Almacenamiento estacional de neumáticos para tus clientes",
+  "common": {
+    "cancel": "Cancelar",
+    "save": "Guardar",
+    "create": "Crear",
+    "delete": "Eliminar",
+    "edit": "Editar"
+  },
+  "seasons": {
+    "summer": "Verano",
+    "winter": "Invierno",
+    "all_season": "Todo tiempo",
+    "other": "Otro"
+  },
+  "statuses": {
+    "stored": "En almacén",
+    "released": "Devuelto",
+    "disposed": "Desechado"
+  },
+  "conditions": {
+    "good": "Bueno",
+    "fair": "Vigilar",
+    "replace": "Sustituir"
+  },
+  "positions": {
+    "front_left": "Delantero izquierdo",
+    "front_right": "Delantero derecho",
+    "rear_left": "Trasero izquierdo",
+    "rear_right": "Trasero derecho",
+    "spare": "Rueda de repuesto",
+    "unspecified": "Sin especificar"
+  },
+  "capacity": {
+    "used": "{used} de {capacity} neumáticos",
+    "free": "Sitio para {count}",
+    "freeShort": "{count} libres",
+    "over": "{count} por encima de la capacidad",
+    "meterLabel": "{used} de {capacity} neumáticos almacenados"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32avos",
+    "inputLabel": "Profundidad del dibujo, {position}",
+    "lastTime": "era {value}",
+    "worn": "{value} de desgaste"
+  },
+  "list": {
+    "tabAll": "Todos",
+    "tabStored": "En almacén",
+    "tabReleased": "Devueltos",
+    "search": "Buscar por matrícula, cliente, marca o estante",
+    "checkIn": "Recibir neumáticos",
+    "empty": "Aún no hay juegos de neumáticos. Recibe un juego para empezar.",
+    "unassigned": "Sin asignar",
+    "reference": "Ref",
+    "vehicle": "Vehículo",
+    "tires": "Neumáticos",
+    "location": "Estante",
+    "condition": "Estado",
+    "status": "Situación",
+    "storedSince": "Desde",
+    "tireCount": "{count, plural, one {# neumático} other {# neumáticos}}",
+    "studded": "Con clavos",
+    "roomLeft": "Sitio para {count}",
+    "prep": "Prep.",
+    "tabNeedsPrep": "Pendiente de preparar",
+    "setUpShelves": "Configurar estantes",
+    "emptyNoShelves": "Aún no hay estantes. Configura el almacén antes de recibir neumáticos.",
+    "tabDisposed": "Dados de baja"
+  },
+  "checkIn": {
+    "title": "Recibir neumáticos",
+    "description": "Registra lo que llega y elige dónde se guarda.",
+    "customer": "Cliente",
+    "selectCustomer": "Seleccionar cliente",
+    "noCustomer": "Sin cliente",
+    "vehicle": "Vehículo",
+    "selectVehicle": "Seleccionar vehículo",
+    "season": "Temporada",
+    "quantity": "Número de neumáticos",
+    "quantityHint": "Se cuentan de uno en uno, así que un par o cinco ruedas también valen.",
+    "brand": "Marca",
+    "brandPlaceholder": "Michelin, Bridgestone, Continental",
+    "model": "Modelo",
+    "size": "Medida",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "Código DOT",
+    "dotPlaceholder": "3623",
+    "dotHint": "Semana y año de fabricación del neumático.",
+    "withRims": "Con llantas",
+    "hasTpms": "Sensores TPMS",
+    "studded": "Con clavos",
+    "location": "Dónde van los neumáticos",
+    "choosePlace": "Elegir estante",
+    "searchLocations": "Buscar estantes",
+    "noLocations": "No se han encontrado estantes",
+    "tooFull": "Sin sitio suficiente",
+    "pickLocationFirst": "Elige un estante antes de recibir el juego",
+    "roomSummary": "{locations} estantes admiten este juego, {tires} huecos libres",
+    "nothingFits": "Ningún estante tiene sitio para {quantity} neumáticos. Libera espacio o añade un estante.",
+    "treadTitle": "Profundidad del dibujo a la entrada",
+    "treadHint": "Opcional, pero resuelve cualquier discusión posterior sobre el estado.",
+    "notes": "Notas",
+    "submit": "Recibir",
+    "success": "Juego {reference} guardado en {code}",
+    "failed": "No se ha podido recibir el juego",
+    "noVehiclesForCustomer": "Este cliente aún no tiene vehículos. Añade uno desde su ficha de cliente.",
+    "treatmentsHint": "Qué hay que hacerle a este juego antes de guardarlo o montarlo.",
+    "storedBefore": "Ya almacenado aquí",
+    "storedBeforeHint": "¿Vuelven los mismos neumáticos? Elige el juego y la temporada se sumará a su historial.",
+    "sameSet": "El mismo juego que antes",
+    "pieces": "{count} uds",
+    "lastOut": "Retirado el {date}",
+    "notStored": "Ahora no está almacenado",
+    "returned": "El juego {reference} vuelve a estar en {code}",
+    "submitReturn": "Almacenar de nuevo",
+    "ownerFromVehicle": "Tomado del vehículo",
+    "vehicleOwner": "El propietario del vehículo",
+    "clearVehicle": "Quitar el vehículo"
+  },
+  "storage": {
+    "title": "Estantes",
+    "description": "Configura el almacén una vez y ve de un vistazo qué cabe dónde.",
+    "addWarehouse": "Añadir ubicación",
+    "addShelves": "Añadir estantes",
+    "warehouseName": "Nombre",
+    "warehouseNamePlaceholder": "Nave principal, Contenedor 2, Trastero",
+    "warehouseAddress": "Dirección",
+    "warehouseCreated": "Ubicación de almacén añadida",
+    "warehouseDeleted": "{name} eliminada",
+    "warehouseArchived": "{name} archivada",
+    "deleteWarehouse": "Eliminar ubicación",
+    "deleteWarehouseTitle": "¿Eliminar {name}?",
+    "deleteWarehouseBody": "Esto elimina la ubicación y todos sus estantes.",
+    "deleteWarehouseBodyOccupied": "Esta ubicación todavía contiene {count} neumáticos, así que se archivará en lugar de eliminarse.",
+    "totalCapacity": "Capacidad total",
+    "totalStored": "Almacenados ahora",
+    "totalFree": "Sitio libre",
+    "totalLocations": "Estantes",
+    "locationSummary": "{occupied} de {total} estantes en uso",
+    "noLocations": "Aún no hay estantes. Añade una estantería para guardar neumáticos aquí.",
+    "emptyTitle": "Todavía no hay almacén configurado",
+    "emptyBody": "Añade tu primera nave o sala y llénala de estantes.",
+    "default": "Predeterminada",
+    "addLocation": "Añadir estantes",
+    "editLocation": "Editar estante",
+    "createLocation": "Crear estante",
+    "createShelves": "Crear estantes",
+    "locationDialogDescription": "Añadiendo almacenamiento a {warehouse}.",
+    "tabBulk": "Una estantería completa",
+    "tabSingle": "Un estante",
+    "zone": "Zona",
+    "zonePlaceholder": "A, Sala 1, Norte",
+    "rack": "Estantería",
+    "rackPlaceholder": "04",
+    "shelf": "Balda",
+    "shelfPlaceholder": "2",
+    "position": "Posición",
+    "positionPlaceholder": "Izquierda",
+    "shelfFrom": "Baldas de la",
+    "shelfTo": "a la",
+    "code": "Código",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Aparece en la etiqueta. Se rellena solo si lo dejas en blanco.",
+    "capacity": "Neumáticos por balda",
+    "capacityHint": "Cuántos neumáticos sueltos caben aquí.",
+    "notes": "Notas",
+    "bulkPreview": "Crea {count} baldas, de {first} a {last}",
+    "bulkPreviewCapacity": "Sitio para {tires} neumáticos en total",
+    "bulkCreated": "{count} baldas creadas",
+    "bulkCreatedWithSkips": "{created} baldas creadas, {skipped} omitidas por existir ya",
+    "bulkNothingCreated": "Esas baldas ya existen",
+    "locationCreated": "Estante añadido",
+    "locationUpdated": "Estante actualizado",
+    "locationDeleted": "{code} eliminado",
+    "deleteLocationTitle": "¿Eliminar {code}?",
+    "deleteLocationBody": "El estante tiene que estar vacío antes.",
+    "saveFailed": "No se ha podido guardar",
+    "deleteFailed": "No se ha podido eliminar"
+  },
+  "movements": {
+    "check_in": "Recibido",
+    "check_out": "Entregado",
+    "relocate": "Movido",
+    "dispose": "Desechado",
+    "measure": "Medido"
+  },
+  "detail": {
+    "move": "Mover",
+    "checkOut": "Entregar",
+    "conditionTitle": "Estado",
+    "noMeasurements": "Aún no hay mediciones registradas.",
+    "historyTitle": "Historial",
+    "storageTitle": "Almacenamiento",
+    "shelf": "Estante",
+    "warehouse": "Ubicación",
+    "notStored": "Ahora mismo no está en almacén.",
+    "checkedIn": "Recibido",
+    "checkedOut": "Entregado",
+    "tiresTitle": "Neumáticos",
+    "loadSpeed": "Índices de carga y velocidad",
+    "ownerTitle": "Propietario",
+    "phone": "Teléfono",
+    "deleteTitle": "¿Eliminar este juego de neumáticos?",
+    "deleteBody": "Se elimina el juego y todo su historial. Esto no se puede deshacer.",
+    "deleted": "Juego de neumáticos eliminado",
+    "deleteFailed": "No se ha podido eliminar el juego",
+    "belowLimit": "{count} neumático(s) en tu límite de sustitución o por debajo.",
+    "belowLimitHint": "Conviene ofrecer precio al cliente mientras los neumáticos siguen aquí.",
+    "dispose": "Dar de baja",
+    "disposeTitle": "¿Dar de baja este juego?",
+    "disposeBody": "La ficha y su historial se mantienen y se libera el hueco. Deja de ofrecerse como los mismos neumáticos la próxima temporada.",
+    "disposed": "Juego dado de baja",
+    "disposeFailed": "No se pudo dar de baja el juego"
+  },
+  "checkOut": {
+    "title": "Entregar neumáticos",
+    "description": "Se retira el juego de {code}.",
+    "descriptionNoLocation": "Este juego vuelve al cliente.",
+    "treadTitle": "Profundidad del dibujo a la salida",
+    "note": "Nota",
+    "submit": "Entregar",
+    "success": "Juego {reference} entregado",
+    "failed": "No se ha podido entregar el juego",
+    "prepPending": "{count} tarea(s) de preparación pendiente(s)",
+    "conditionTitle": "Estado a la entrada",
+    "conditionLead": {
+      "replace": "Uno o más neumáticos están en tu límite de sustitución o por debajo. Conviene decírselo al cliente antes de que se vaya.",
+      "fair": "Uno o más neumáticos se acercan a tu límite de sustitución. Conviene comentarlo antes de la próxima temporada."
+    },
+    "treadHintUnmeasured": "No se midió cuando entraron. Esta es la última oportunidad de registrarlo."
+  },
+  "relocate": {
+    "title": "Mover neumáticos",
+    "description": "Elige el estante al que se mueve este juego.",
+    "newLocation": "Nuevo estante",
+    "note": "Nota",
+    "submit": "Mover",
+    "success": "Juego {reference} movido",
+    "failed": "No se ha podido mover el juego"
+  },
+  "edit": {
+    "title": "Editar juego de neumáticos",
+    "description": "Corrige lo que se registró en la recepción. Usa Mover para cambiar de estante.",
+    "quantityHint": "Aumentarlo se comprueba contra el estante donde está el juego.",
+    "rimType": "Tipo de llanta",
+    "rimTypePlaceholder": "Aleación, acero",
+    "success": "Juego de neumáticos actualizado",
+    "failed": "No se han podido guardar los cambios",
+    "treadHint": "Cambiar una medición la registra como una nueva lectura en el historial en lugar de sobrescribir la anterior."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Lavar neumáticos",
+      "wash_rims": "Lavar llantas",
+      "balance": "Equilibrar",
+      "tpms_service": "Servicio TPMS",
+      "new_valves": "Válvulas nuevas",
+      "repair": "Reparación"
+    },
+    "statuses": {
+      "pending": "Por hacer",
+      "done": "Hecho",
+      "skipped": "Omitido"
+    },
+    "title": "Preparación",
+    "none": "No hay nada que hacer en este juego.",
+    "add": "Añadir preparación",
+    "pickHint": "Marca lo que necesita este juego antes de volver al coche.",
+    "pendingCount": "{count} por hacer",
+    "allComplete": "Todo hecho",
+    "ready": "Listo",
+    "markDone": "Hecho",
+    "completeAll": "Marcar los {count} como hechos",
+    "allDone": "Preparación terminada",
+    "undo": "Volver a la lista",
+    "saveFailed": "No se ha podido guardar",
+    "skip": "Omitir"
+  },
+  "messaging": {
+    "action": "Mensaje",
+    "title": "Escribir al cliente",
+    "description": "Para {name}.",
+    "noCustomer": "Este juego no tiene cliente.",
+    "noCustomerBody": "Vincula un cliente al juego antes de escribirle.",
+    "channel": "Enviar por",
+    "channels": {
+      "email": "Correo",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "sin dirección registrada",
+    "recipient": "Enviar a",
+    "recipientPlaceholder": "Número de teléfono",
+    "subject": "Asunto",
+    "message": "Mensaje",
+    "send": "Enviar",
+    "sent": "Mensaje enviado",
+    "failed": "No se ha podido enviar el mensaje",
+    "smsLong": "Los mensajes largos pueden dividirse en varios SMS.",
+    "subjects": {
+      "low_tread": "Tus neumáticos de {season}",
+      "damage": "Tus neumáticos de {season}",
+      "stored": "Tus neumáticos de {season} están guardados",
+      "custom": "Tus neumáticos de {season}"
+    },
+    "bodies": {
+      "low_tread": "Hola {customer_name}, tus neumáticos de {season} están guardados con nosotros en {shop_name}. Al recibirlos, {positions} marcaban {tread}, que está por debajo del límite. No aguantan otra temporada. ¿Te preparamos precio de un juego nuevo?",
+      "damage": "Hola {customer_name}, tus neumáticos de {season} están guardados con nosotros en {shop_name}. Al recibirlos vimos daños en {positions}. Llámanos cuando puedas y lo vemos juntos.",
+      "stored": "Hola {customer_name}, tus neumáticos de {season} ya están guardados con nosotros en {shop_name}. Te avisamos cuando toque el cambio.",
+      "custom": "Hola {customer_name}, sobre tus neumáticos de {season} guardados con nosotros en {shop_name}:"
+    },
+    "enableInSettings": "Aún no está configurado. Actívalo en Ajustes."
+  },
+  "job": {
+    "cardTitle": "Presupuestos y órdenes",
+    "none": "Aún no se ha creado nada para este juego.",
+    "quote": "Presupuesto",
+    "workOrder": "Orden",
+    "converted": "convertido",
+    "quoteTitle": "Presupuestar un juego nuevo",
+    "workOrderTitle": "Facturar este juego",
+    "loadingSet": "Cargando el juego",
+    "tires": "Neumáticos",
+    "inStock": "{count} en stock",
+    "shortStock": "solo {count} en stock",
+    "noMatch": "Nada en stock coincide con {size}. La línea se añade con el precio que pongas.",
+    "noSize": "Este juego no tiene medida, así que no se puede cruzar con el stock. La línea se añade con tu precio.",
+    "unitPrice": "Precio por neumático",
+    "createQuote": "Crear presupuesto",
+    "createWorkOrder": "Crear orden",
+    "quoteCreated": "Presupuesto {number} creado",
+    "workOrderCreated": "Orden creada",
+    "failed": "No se ha podido crear",
+    "bannerTitle": "Recoger en",
+    "openSet": "Abrir juego",
+    "prepPending": "Pendiente antes de montar: {jobs}",
+    "notOnShelf": "Este juego ya no está guardado.",
+    "targetNew": "Nueva orden",
+    "targetNewHint": "Crear una orden solo para estos neumáticos.",
+    "targetExisting": "O añadir a una orden ya abierta para este vehículo",
+    "addToWorkOrder": "Añadir a la orden",
+    "workOrderUpdated": "Añadido a la orden",
+    "unlink": "Desvincular",
+    "unlinkTitle": "¿Desvincular estos neumáticos de la orden?",
+    "unlinkBody": "La orden deja de apuntar al juego de neumáticos. Las piezas y la mano de obra que ya están se quedan; quítalas aparte si no se deben cobrar.",
+    "unlinked": "Neumáticos desvinculados",
+    "unlinkFailed": "No se han podido desvincular",
+    "prep": "Preparación",
+    "tiresSkipped": "No se añadirá ninguna línea de neumáticos.",
+    "total": "Total",
+    "searchStock": "Buscar en el inventario por marca, medida o número de pieza",
+    "noSearchHits": "Nada en el inventario coincide con \"{query}\".",
+    "otherSize": "Otra medida",
+    "inSize": "{count} en {size}",
+    "quantity": "Cant.",
+    "noStockItem": "Sin artículo de inventario, precio manual",
+    "storeTires": "Guardar neumáticos",
+    "storage": "Almacenaje",
+    "storageFee": "Importe",
+    "storagePeriod": "Periodo",
+    "destination": "Dónde va",
+    "targetInvoice": "Nueva factura",
+    "targetInvoiceHint": "Factura al cliente directamente, sin orden sobre un vehículo.",
+    "createInvoice": "Crear factura",
+    "needsCustomer": "Vincula un cliente o un vehículo al juego antes de facturar.",
+    "storagePeriodEnd": "Retirado",
+    "storageOpenEnded": "Abierto",
+    "storagePeriodHint": "Aparece en la línea de la factura para que el cliente vea qué cubre el importe. Deja el final en blanco hasta saber cuándo lo retiran.",
+    "pricesHint": "Los precios de preparación vienen de los ajustes. Cada línea se puede editar después de crearlo.",
+    "files": "Fotos y archivos",
+    "filesCount": "{count} en la factura"
+  },
+  "label": {
+    "action": "Etiquetas",
+    "title": "Imprimir etiquetas",
+    "description": "Una pegatina por neumático, con un QR que abre este juego.",
+    "format": "Tamaño",
+    "formats": {
+      "dymo_small": "Rollo pequeño",
+      "dymo_standard": "Rollo estándar",
+      "thermal_large": "Rollo grande",
+      "sheet_a4": "Hoja A4"
+    },
+    "perSheet": "{count} por hoja",
+    "copies": "Cuántas",
+    "copiesHint": "una por neumático ({count})",
+    "fewer": "Una menos",
+    "more": "Una más",
+    "print": "Imprimir",
+    "shelfNote": "El estante no se imprime a propósito. Los juegos se mueven, y un estante impreso quedaría mal. El QR siempre muestra dónde está ahora.",
+    "reference": "Ref",
+    "quantity": "{count, plural, one {# neumático} other {# neumáticos}}",
+    "withRims": "Con llantas",
+    "tpms": "TPMS",
+    "studded": "Con clavos",
+    "unassigned": "Sin asignar",
+    "preview": "Vista previa",
+    "previewScale": "Mostrado a tamaño de impresión. El código también se escanea desde la pantalla.",
+    "previewFailed": "No se ha podido generar la vista previa"
+  },
+  "vehicle": {
+    "oneOnShelf": "en {code}",
+    "inStorage": "en el hotel"
+  },
+  "wear": {
+    "summary": "{value} de desgaste entre {from} y {to}",
+    "perSeason": "Unos {value} por temporada",
+    "seasonsLeft": "{count, plural, =0 {Ya en el límite} one {Alrededor de una temporada más} other {Alrededor de # temporadas más}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Cambiar en ajustes",
+    "prepPrices": "Precios de preparación",
+    "storagePrices": "Precios de almacenaje",
+    "treadLimits": "Cambiar el límite",
+    "defaultCapacity": "Capacidad por defecto"
+  },
+  "forecast": {
+    "title": "La próxima temporada",
+    "subtitle": "Lo que los juegos almacenados necesitan según las mediciones, por medida.",
+    "backToList": "Volver a los juegos",
+    "tires": "Neumáticos previstos",
+    "atLimit": "Ya en el límite",
+    "expected": "Previsto en una temporada",
+    "orderTitle": "Por medida",
+    "sizes": "{count} medidas",
+    "empty": "Nada medido cerca del límite todavía. Un juego necesita dos visitas para calcular un ritmo.",
+    "nowCount": "{count, plural, one {# juego en el límite} other {# juegos en el límite}}",
+    "nextCount": "{count, plural, one {# juego en una temporada} other {# juegos en una temporada}}",
+    "tiresShort": "neumáticos",
+    "lowest": "mínimo {value}",
+    "now": "En el límite",
+    "next": "En una temporada",
+    "rate": "{value} por temporada",
+    "caveat": "Los juegos en el límite están medidos. El resto se extrapola del desgaste de un cliente en un intervalo: sirve para planificar, no para pedir.",
+    "unmeasured": "{count, plural, one {# juego tiene poco historial y no se cuenta} other {# juegos tienen poco historial y no se cuentan}}",
+    "capped": "Se han leído los {shown} juegos más recientes de {total}."
+  },
+  "invoiceLine": {
+    "storage": "Almacenaje de neumáticos",
+    "pieces": "uds",
+    "fromDate": "desde {date}"
+  },
+  "setNames": {
+    "winter": "neumáticos de invierno",
+    "summer": "neumáticos de verano",
+    "all_season": "neumáticos all season",
+    "other": "neumáticos"
+  },
+  "jobNotes": {
+    "set": "Juego de neumáticos {reference}",
+    "shelf": "Estante: {shelf}",
+    "notOnShelf": "sin estante",
+    "onRims": "Con llantas",
+    "tiresOnly": "Solo neumáticos",
+    "tpms": "Con sensores TPMS"
+  },
+  "files": {
+    "title": "Fotos y archivos",
+    "add": "Añadir archivos",
+    "none": "Todavía no hay nada adjunto. Fotografía una llanta rozada mientras tienes los neumáticos delante.",
+    "captionPlaceholder": "¿Qué se ve aquí?",
+    "onInvoice": "En la factura",
+    "invoiceHint": "Los archivos marcados para la factura se copian a la orden o a la factura al facturar el juego.",
+    "deleteTitle": "¿Quitar este archivo?",
+    "deleteBody": "{name} se quita del juego. Lo que ya se copió a una factura se mantiene.",
+    "failed": "No se pudo guardar",
+    "someFailed": "{count, plural, one {no se pudo subir # archivo} other {no se pudieron subir # archivos}}"
+  }
+}

+ 28 - 1
messages/fr/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Message planifié envoyé",
     "scheduled_message_send_now": "Message planifié envoyé",
     "settings_applyTaxRate": "Taux de taxe appliqué",
     "settings_applyTaxRate": "Taux de taxe appliqué",
     "settings_convertToExclusive": "Prix convertis hors taxes",
     "settings_convertToExclusive": "Prix convertis hors taxes",
-    "settings_convertToInclusive": "Prix convertis toutes taxes comprises"
+    "settings_convertToInclusive": "Prix convertis toutes taxes comprises",
+    "organization_sampleDataRemove": "Données d'exemple supprimées",
+    "tire_warehouse_create": "Site de stockage de pneus créé",
+    "tire_warehouse_update": "Site de stockage de pneus modifié",
+    "tire_warehouse_delete": "Site de stockage de pneus supprimé",
+    "tire_warehouse_archive": "Site de stockage de pneus archivé",
+    "tire_location_create": "Étagère à pneus créée",
+    "tire_location_bulk_create": "Étagères à pneus créées",
+    "tire_location_update": "Étagère à pneus modifiée",
+    "tire_location_delete": "Étagère à pneus supprimée",
+    "tire_set_check_in": "Jeu de pneus réceptionné",
+    "tire_set_check_out": "Jeu de pneus restitué",
+    "tire_set_relocate": "Jeu de pneus déplacé",
+    "tire_set_update": "Jeu de pneus modifié",
+    "tire_set_delete": "Jeu de pneus supprimé",
+    "tire_measurement_create": "Mesures de pneus enregistrées",
+    "tire_treatment_update": "Préparation modifiée",
+    "tire_treatment_mark": "Préparation marquée",
+    "tire_treatment_complete_all": "Préparation terminée",
+    "tire_set_message_customer": "Message envoyé au client au sujet d’un jeu de pneus",
+    "tire_set_create_quote": "Devis créé depuis un jeu de pneus",
+    "tire_set_create_work_order": "Ordre créé depuis un jeu de pneus",
+    "tire_set_add_to_work_order": "Jeu de pneus ajouté à un ordre",
+    "tire_set_unlink_work_order": "Jeu de pneus dissocié d’un ordre",
+    "tire_set_return": "Jeu de nouveau stocké",
+    "tire_set_dispose": "Jeu de pneus mis au rebut",
+    "tire_set_attach": "Fichiers ajoutés à un jeu de pneus",
+    "tire_set_detach": "Fichier retiré d’un jeu de pneus"
   }
   }
 }
 }

+ 6 - 2
messages/fr/common.json

@@ -10,7 +10,8 @@
     "confirm": "Confirmer",
     "confirm": "Confirmer",
     "saveChanges": "Enregistrer les modifications",
     "saveChanges": "Enregistrer les modifications",
     "edit": "Modifier",
     "edit": "Modifier",
-    "add": "Ajouter"
+    "add": "Ajouter",
+    "gotIt": "J’ai compris"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Astuce : clic droit sur une ligne pour les actions",
     "hint": "Astuce : clic droit sur une ligne pour les actions",
@@ -75,6 +76,9 @@
     "enterManually": "Saisir manuellement",
     "enterManually": "Saisir manuellement",
     "enterBarcode": "Saisir un code-barres…",
     "enterBarcode": "Saisir un code-barres…",
     "scanHint": "Pointez la caméra vers un code-barres ou saisissez-le manuellement",
     "scanHint": "Pointez la caméra vers un code-barres ou saisissez-le manuellement",
-    "iosInstallHint": "Touchez Partager > Sur l'écran d'accueil"
+    "iosInstallHint": "Touchez Partager > Sur l'écran d'accueil",
+    "installDescription": "Ajoutez-la à votre écran d'accueil pour un accès rapide",
+    "enterFullscreen": "Plein écran",
+    "exitFullscreen": "Quitter le plein écran"
   }
   }
 }
 }

+ 12 - 1
messages/fr/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Terminés récemment",
     "recentCompleted": "Terminés récemment",
     "activeJobs": "Travaux en cours",
     "activeJobs": "Travaux en cours",
     "recentObservations": "Observations récentes",
     "recentObservations": "Observations récentes",
-    "recentActivity": "Activité récente"
+    "recentActivity": "Activité récente",
+    "gettingStarted": "Premiers pas",
+    "tireHotel": "Hôtel à pneus"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Maintenance prévue à effectuer",
     "title": "Maintenance prévue à effectuer",
@@ -233,5 +235,14 @@
       "expired": "Expiré",
       "expired": "Expiré",
       "converted": "Converti"
       "converted": "Converti"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Hôtel à pneus",
+    "viewAll": "Voir l’hôtel à pneus",
+    "stored": "{count} jeux stockés",
+    "capacity": "{used} / {capacity} pneus",
+    "roomLeft": "Place pour {count} de plus",
+    "needsPrep": "À préparer",
+    "unbilled": "Non facturé ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/fr/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "L’hôtel à pneus est actif",
+    "body": "Stockez les pneus saisonniers. Créez vos étagères, puis réceptionnez un jeu."
+  },
+  "telegram": {
+    "title": "Telegram est activé",
+    "body": "Les conversations clients arrivent ici. Répondez sans quitter l'application."
+  }
+}

+ 1 - 0
messages/fr/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Véhicule",
     "vehicle": "Véhicule",
     "template": "Modèle",
     "template": "Modèle",
     "progress": "Avancement",
     "progress": "Avancement",
+    "progressTooltip": "{graded} points sur {total} évalués",
     "status": "Statut",
     "status": "Statut",
     "date": "Date",
     "date": "Date",
     "statusInProgress": "En cours",
     "statusInProgress": "En cours",

+ 8 - 2
messages/fr/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Tous les rappels",
     "allReminders": "Tous les rappels",
     "auditLog": "Journal d'audit",
     "auditLog": "Journal d'audit",
     "aiAssistant": "Assistant IA",
     "aiAssistant": "Assistant IA",
-    "observations": "Observations"
+    "observations": "Observations",
+    "tireHotel": "Hôtel à pneus",
+    "allTireSets": "Tous les jeux de pneus",
+    "tireStorage": "Organisation du stockage",
+    "tireSetDetails": "Jeu de pneus",
+    "tireForecast": "La saison prochaine"
   },
   },
   "search": "Rechercher…",
   "search": "Rechercher…",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Se déconnecter",
     "signOut": "Se déconnecter",
     "more": "Plus",
     "more": "Plus",
     "auditLog": "Journal d'audit",
     "auditLog": "Journal d'audit",
-    "aiAssistant": "Assistant IA"
+    "aiAssistant": "Assistant IA",
+    "tireHotel": "Hôtel à pneus"
   }
   }
 }
 }

+ 91 - 0
messages/fr/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Configurez votre atelier",
+    "subtitle": "Saisissez le nom de votre atelier pour commencer",
+    "workshopName": "Nom de l'atelier",
+    "workshopNamePlaceholder": "Mon Garage Auto",
+    "submit": "Commencer",
+    "sampleDataLabel": "Charger des données d'exemple",
+    "sampleDataDescription": "Ajoute quelques clients, véhicules et interventions d'exemple pour découvrir l'application. Vous pourrez les supprimer en un clic.",
+    "setupFailed": "Échec de la configuration",
+    "couldNotCreate": "Impossible de créer l'atelier",
+    "unexpectedError": "Une erreur inattendue est survenue"
+  },
+  "sample": {
+    "prefix": "Exemple :",
+    "oilService": "Vidange et remplacement du filtre",
+    "brakeJob": "Remplacement des plaquettes de frein avant",
+    "annualService": "Révision annuelle",
+    "quoteTitle": "Remise en état de la suspension avant",
+    "inspectionNote": "Le pneu avant gauche approche de la limite d'usure",
+    "parts": {
+      "oilFilter": "Filtre à huile",
+      "engineOil": "Huile moteur 5W-30",
+      "brakePads": "Jeu de plaquettes de frein avant",
+      "shockAbsorber": "Amortisseur"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Vidange",
+      "description": "Vidange de l'huile moteur et remplacement du filtre"
+    },
+    "frontBrakePads": {
+      "name": "Plaquettes de frein avant",
+      "description": "Remplacer les plaquettes de frein avant"
+    },
+    "tireSwap": {
+      "name": "Permutation des pneus",
+      "description": "Changement de roues saisonnier avec contrôle du serrage"
+    },
+    "batteryReplacement": {
+      "name": "Remplacement de la batterie",
+      "description": "Remplacer et enregistrer la batterie de démarrage"
+    },
+    "diagnostics": {
+      "name": "Diagnostic",
+      "description": "Lecture des codes défauts et premier diagnostic"
+    },
+    "annualService": {
+      "name": "Révision annuelle",
+      "description": "Révision annuelle complète selon le carnet d'entretien"
+    }
+  },
+  "checklist": {
+    "title": "Premiers pas",
+    "description": "Quatre étapes pour lancer votre atelier",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Ajouter un client",
+        "description": "Créez votre première fiche client"
+      },
+      "vehicle": {
+        "title": "Ajouter un véhicule",
+        "description": "Enregistrez un véhicule pour un client"
+      },
+      "workOrder": {
+        "title": "Créer un ordre de réparation",
+        "description": "Ouvrez votre première intervention"
+      },
+      "invoice": {
+        "title": "Envoyer ou télécharger une facture",
+        "description": "Partagez une facture avec un client ou téléchargez-la en PDF"
+      }
+    },
+    "allDoneTitle": "Tout est prêt",
+    "allDoneDescription": "Toutes les étapes sont terminées et votre atelier est opérationnel.",
+    "dismiss": "Masquer la carte",
+    "dismissAriaLabel": "Masquer cette liste"
+  },
+  "sampleData": {
+    "title": "Données d'exemple",
+    "description": "Votre atelier contient encore les clients, véhicules et interventions d'exemple de la configuration. Leur suppression efface exactement ces enregistrements et rien d'autre.",
+    "footerNote": "Des données d'exemple sont chargées",
+    "remove": "Supprimer les données d'exemple",
+    "removeConfirmTitle": "Supprimer les données d'exemple ?",
+    "removeConfirmDescription": "Cette action supprime les clients, véhicules, ordres de réparation, le devis et l'inspection d'exemple créés lors de la configuration. Vos propres données ne sont pas concernées.",
+    "removed": "Données d'exemple supprimées",
+    "removeFailed": "Impossible de supprimer les données d'exemple"
+  }
+}

+ 27 - 0
messages/fr/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Tableau de bord",
+    "vehicles": "Véhicules",
+    "customers": "Clients",
+    "work_orders": "Ordres de travail",
+    "quotes": "Devis",
+    "services": "Interventions",
+    "billing": "Facturation",
+    "inventory": "Stock",
+    "labor_presets": "Modèles de main-d’œuvre",
+    "inspections": "Contrôles",
+    "tire_hotel": "Hôtel à pneus",
+    "reports": "Rapports",
+    "work_board": "Tableau d’atelier",
+    "ai_assistant": "Assistant IA",
+    "settings": "Paramètres"
+  },
+  "actions": {
+    "create": "Créer",
+    "read": "Consulter",
+    "update": "Modifier",
+    "delete": "Supprimer",
+    "manage": "Gérer",
+    "manage_storage": "Gérer l’organisation du stockage"
+  }
+}

+ 17 - 2
messages/fr/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} bon(s)",
     "good": "{count} bon(s)",
     "fair": "{count} passable(s)",
     "fair": "{count} passable(s)",
     "poor": "{count} mauvais",
     "poor": "{count} mauvais",
-    "view": "Voir"
+    "view": "Voir",
+    "serviceHistoryTitle": "Historique d'entretien",
+    "inspectionsTitle": "Inspections",
+    "columnVehicle": "Véhicule",
+    "columnPlate": "Plaque",
+    "columnServices": "Services",
+    "columnInspections": "Inspections"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Demander un service",
     "title": "Demander un service",
@@ -174,6 +180,15 @@
     "yourRequests": "Vos demandes de service",
     "yourRequests": "Vos demandes de service",
     "failedToLoad": "Échec du chargement des données.",
     "failedToLoad": "Échec du chargement des données.",
     "staffNotes": "Notes du personnel : {notes}",
     "staffNotes": "Notes du personnel : {notes}",
-    "preferred": "Souhaité : {date}"
+    "preferred": "Souhaité : {date}",
+    "notListedOption": "Mon véhicule n'est pas dans la liste",
+    "newVehicleHint": "Décrivez le véhicule et nous l'ajouterons à votre profil.",
+    "fillVehicleDetails": "Veuillez saisir la marque et le modèle du véhicule",
+    "makeLabel": "Marque",
+    "makePlaceholder": "p. ex. Ford",
+    "modelLabel": "Modèle",
+    "modelPlaceholder": "p. ex. Kuga",
+    "yearLabel": "Année",
+    "plateLabel": "Plaque d'immatriculation"
   }
   }
 }
 }

+ 9 - 0
messages/fr/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Recherche",
+  "description": "Rechercher véhicules, clients, interventions et paramètres",
+  "placeholder": "Nom, plaque, téléphone, VIN, facture…",
+  "noResults": "Aucun résultat.",
+  "recentCustomers": "Clients récents",
+  "services": "Interventions",
+  "toClose": "pour fermer"
+}

+ 47 - 1
messages/fr/settings.json

@@ -103,6 +103,10 @@
         "title": "Données",
         "title": "Données",
         "description": "Exporter et importer une sauvegarde"
         "description": "Exporter et importer une sauvegarde"
       },
       },
+      "install": {
+        "title": "Installer l'application",
+        "description": "Ajoutez Torqvoice à cet appareil"
+      },
       "license": {
       "license": {
         "title": "Licence",
         "title": "Licence",
         "description": "Licence marque blanche"
         "description": "Licence marque blanche"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Assistance",
         "title": "Assistance",
         "description": "Contacter l'assistance et gérer le bouton"
         "description": "Contacter l'assistance et gérer le bouton"
+      },
+      "tireHotel": {
+        "title": "Hôtel à pneus",
+        "description": "Stockage saisonnier des pneus"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Dernière sauvegarde :",
       "lastBackup": "Dernière sauvegarde :",
       "supportPrompt": "Pour toute question sur la protection de vos données,",
       "supportPrompt": "Pour toute question sur la protection de vos données,",
       "supportAction": "contactez notre équipe d'assistance."
       "supportAction": "contactez notre équipe d'assistance."
-    }
+    },
+    "optTireHotel": "Hôtel à pneus",
+    "optTireHotelDesc": "Jeux stockés, étagères, préparation et contrats de stockage"
   },
   },
   "license": {
   "license": {
     "title": "Licence marque blanche",
     "title": "Licence marque blanche",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Contacter l'assistance",
     "contactTitle": "Contacter l'assistance",
     "contactHint": "Ouvrir directement le formulaire d'assistance.",
     "contactHint": "Ouvrir directement le formulaire d'assistance.",
     "contactAction": "Ouvrir le formulaire"
     "contactAction": "Ouvrir le formulaire"
+  },
+  "install": {
+    "title": "Installer Torqvoice",
+    "description": "Installez Torqvoice sur cet appareil pour l'ouvrir depuis l'écran d'accueil, en plein écran et sans la barre du navigateur. C'est la même application et les mêmes données.",
+    "installed": "Torqvoice est installée sur cet appareil.",
+    "iosHint": "Dans Safari, touchez le bouton Partager, puis « Sur l'écran d'accueil ».",
+    "unavailable": "Ce navigateur n'a pas proposé d'installation. Ouvrez le menu du navigateur et cherchez « Installer l'application » ou « Sur l'écran d'accueil ».",
+    "bannerHidden": "La bannière d'installation est masquée sur cet appareil.",
+    "showBanner": "Afficher à nouveau la bannière"
+  },
+  "tireHotel": {
+    "title": "Hôtel à pneus",
+    "description": "Stockez les pneus saisonniers de vos clients et sachez où se trouve chaque jeu.",
+    "cardTitle": "Stockage des pneus",
+    "enable": "Activer l’hôtel à pneus",
+    "enableHint": "Ajoute le stockage des pneus au menu. Le désactiver plus tard le masque sans rien supprimer.",
+    "defaultCapacity": "Pneus par niveau par défaut",
+    "defaultCapacityHint": "Prérempli à la création de nouveaux niveaux. Modifiable niveau par niveau.",
+    "warnPercent": "Alerter à partir de (%)",
+    "warnPercentHint": "Signale la vue du stockage dès que cette part de la capacité totale est occupée.",
+    "treadTitle": "Seuils de remplacement",
+    "treadHint": "Les minimums légaux varient selon les pays, réglez donc les seuils de votre marché. Une sculpture sous le seuil est signalée à remplacer.",
+    "summerLimit": "Seuil été ({unit})",
+    "winterLimit": "Seuil hiver ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "Configurez ensuite vos étagères.",
+    "setupLink": "Ouvrir l’organisation du stockage",
+    "save": "Enregistrer les réglages",
+    "saved": "Réglages de l’hôtel à pneus enregistrés",
+    "billingTitle": "Facturation du stockage",
+    "billingHint": "Prérempli sur la ligne de stockage lors de la facturation d’un jeu. Modifiable sur la facture.",
+    "seasonalPrice": "Tarif de stockage par défaut",
+    "prepTitle": "Prix de la préparation",
+    "prepHint": "Ce que coûte chaque type de préparation. Laissez vide et rien n’est facturé, ce qui convient aux ateliers qui incluent le lavage dans le stockage. Les prestations chiffrées sont ajoutées automatiquement au devis et à l’ordre.",
+    "prepNotCharged": "non facturé"
   }
   }
 }
 }

+ 473 - 0
messages/fr/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Hôtel à pneus",
+  "description": "Stockage saisonnier des pneus de vos clients",
+  "common": {
+    "cancel": "Annuler",
+    "save": "Enregistrer",
+    "create": "Créer",
+    "delete": "Supprimer",
+    "edit": "Modifier"
+  },
+  "seasons": {
+    "summer": "Été",
+    "winter": "Hiver",
+    "all_season": "Toutes saisons",
+    "other": "Autre"
+  },
+  "statuses": {
+    "stored": "En stock",
+    "released": "Restitué",
+    "disposed": "Mis au rebut"
+  },
+  "conditions": {
+    "good": "Bon",
+    "fair": "À surveiller",
+    "replace": "À remplacer"
+  },
+  "positions": {
+    "front_left": "Avant gauche",
+    "front_right": "Avant droit",
+    "rear_left": "Arrière gauche",
+    "rear_right": "Arrière droit",
+    "spare": "Roue de secours",
+    "unspecified": "Non précisé"
+  },
+  "capacity": {
+    "used": "{used} sur {capacity} pneus",
+    "free": "Place pour {count}",
+    "freeShort": "{count} libres",
+    "over": "{count} au-delà de la capacité",
+    "meterLabel": "{used} sur {capacity} pneus stockés"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32e",
+    "inputLabel": "Profondeur de sculpture, {position}",
+    "lastTime": "était {value}",
+    "worn": "{value} d’usure"
+  },
+  "list": {
+    "tabAll": "Tous",
+    "tabStored": "En stock",
+    "tabReleased": "Restitués",
+    "search": "Rechercher par plaque, client, marque ou étagère",
+    "checkIn": "Réceptionner des pneus",
+    "empty": "Aucun jeu de pneus pour l'instant. Réceptionnez un jeu pour commencer.",
+    "unassigned": "Non attribué",
+    "reference": "Réf",
+    "vehicle": "Véhicule",
+    "tires": "Pneus",
+    "location": "Étagère",
+    "condition": "État",
+    "status": "Statut",
+    "storedSince": "Depuis",
+    "tireCount": "{count, plural, one {# pneu} other {# pneus}}",
+    "studded": "Cloutés",
+    "roomLeft": "Place pour {count}",
+    "prep": "Prép.",
+    "tabNeedsPrep": "À préparer",
+    "setUpShelves": "Configurer les étagères",
+    "emptyNoShelves": "Aucune étagère. Configurez votre stockage avant de réceptionner des pneus.",
+    "tabDisposed": "Au rebut"
+  },
+  "checkIn": {
+    "title": "Réceptionner des pneus",
+    "description": "Enregistrez ce qui arrive, puis choisissez où le ranger.",
+    "customer": "Client",
+    "selectCustomer": "Choisir un client",
+    "noCustomer": "Aucun client",
+    "vehicle": "Véhicule",
+    "selectVehicle": "Choisir un véhicule",
+    "season": "Saison",
+    "quantity": "Nombre de pneus",
+    "quantityHint": "Comptés à l'unité, donc une paire ou cinq roues fonctionnent aussi.",
+    "brand": "Marque",
+    "brandPlaceholder": "Michelin, Bridgestone, Continental",
+    "model": "Modèle",
+    "size": "Dimension",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "Code DOT",
+    "dotPlaceholder": "3623",
+    "dotHint": "Semaine et année de fabrication du pneu.",
+    "withRims": "Sur jantes",
+    "hasTpms": "Capteurs TPMS",
+    "studded": "Cloutés",
+    "location": "Emplacement de stockage",
+    "choosePlace": "Choisir une étagère",
+    "searchLocations": "Rechercher des étagères",
+    "noLocations": "Aucune étagère trouvée",
+    "tooFull": "Place insuffisante",
+    "pickLocationFirst": "Choisissez une étagère avant de réceptionner le jeu",
+    "roomSummary": "{locations} étagères conviennent, {tires} emplacements libres",
+    "nothingFits": "Aucune étagère n'a la place pour {quantity} pneus. Libérez de l'espace ou ajoutez une étagère.",
+    "treadTitle": "Profondeur de sculpture à la réception",
+    "treadHint": "Facultatif, mais cela tranche toute contestation ultérieure sur l'état.",
+    "notes": "Notes",
+    "submit": "Réceptionner",
+    "success": "Jeu {reference} rangé en {code}",
+    "failed": "Impossible de réceptionner le jeu",
+    "noVehiclesForCustomer": "Ce client n’a pas encore de véhicule. Ajoutez-en un depuis sa fiche client.",
+    "treatmentsHint": "Ce qu’il faut faire sur ce jeu avant stockage ou remontage.",
+    "storedBefore": "Déjà stocké ici",
+    "storedBeforeHint": "Ce sont les mêmes pneus ? Choisissez le jeu, la saison rejoindra son historique.",
+    "sameSet": "Même jeu que la dernière fois",
+    "pieces": "{count} pcs",
+    "lastOut": "Repris le {date}",
+    "notStored": "Pas en stock actuellement",
+    "returned": "Le jeu {reference} est de nouveau en {code}",
+    "submitReturn": "Stocker à nouveau",
+    "ownerFromVehicle": "Repris du véhicule",
+    "vehicleOwner": "Le propriétaire du véhicule",
+    "clearVehicle": "Retirer le véhicule"
+  },
+  "storage": {
+    "title": "Étagères",
+    "description": "Configurez votre stockage une fois, puis voyez d'un coup d'œil ce qui rentre où.",
+    "addWarehouse": "Ajouter un site",
+    "addShelves": "Ajouter des étagères",
+    "warehouseName": "Nom",
+    "warehouseNamePlaceholder": "Bâtiment principal, Conteneur 2, Arrière-salle",
+    "warehouseAddress": "Adresse",
+    "warehouseCreated": "Site de stockage ajouté",
+    "warehouseDeleted": "{name} supprimé",
+    "warehouseArchived": "{name} archivé",
+    "deleteWarehouse": "Supprimer le site",
+    "deleteWarehouseTitle": "Supprimer {name} ?",
+    "deleteWarehouseBody": "Cela supprime le site et toutes ses étagères.",
+    "deleteWarehouseBodyOccupied": "Ce site contient encore {count} pneus, il sera donc archivé plutôt que supprimé.",
+    "totalCapacity": "Capacité totale",
+    "totalStored": "Stockés actuellement",
+    "totalFree": "Place restante",
+    "totalLocations": "Étagères",
+    "locationSummary": "{occupied} étagères sur {total} utilisées",
+    "noLocations": "Aucune étagère. Ajoutez un rack pour stocker des pneus ici.",
+    "emptyTitle": "Aucun stockage configuré",
+    "emptyBody": "Ajoutez votre premier bâtiment ou local, puis remplissez-le d'étagères.",
+    "default": "Par défaut",
+    "addLocation": "Ajouter des étagères",
+    "editLocation": "Modifier l'étagère",
+    "createLocation": "Créer l'étagère",
+    "createShelves": "Créer les étagères",
+    "locationDialogDescription": "Ajout de stockage à {warehouse}.",
+    "tabBulk": "Un rack d'étagères",
+    "tabSingle": "Une étagère",
+    "zone": "Zone",
+    "zonePlaceholder": "A, Local 1, Nord",
+    "rack": "Rack",
+    "rackPlaceholder": "04",
+    "shelf": "Niveau",
+    "shelfPlaceholder": "2",
+    "position": "Position",
+    "positionPlaceholder": "Gauche",
+    "shelfFrom": "Niveaux de",
+    "shelfTo": "à",
+    "code": "Code",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Figure sur l'étiquette. Rempli automatiquement si vous le laissez vide.",
+    "capacity": "Pneus par niveau",
+    "capacityHint": "Combien de pneus individuels tiennent ici.",
+    "notes": "Notes",
+    "bulkPreview": "Crée {count} niveaux, de {first} à {last}",
+    "bulkPreviewCapacity": "Place pour {tires} pneus au total",
+    "bulkCreated": "{count} niveaux créés",
+    "bulkCreatedWithSkips": "{created} niveaux créés, {skipped} déjà existants ignorés",
+    "bulkNothingCreated": "Ces niveaux existent déjà",
+    "locationCreated": "Étagère ajoutée",
+    "locationUpdated": "Étagère mise à jour",
+    "locationDeleted": "{code} supprimé",
+    "deleteLocationTitle": "Supprimer {code} ?",
+    "deleteLocationBody": "L'étagère doit d'abord être vide.",
+    "saveFailed": "Enregistrement impossible",
+    "deleteFailed": "Suppression impossible"
+  },
+  "movements": {
+    "check_in": "Réceptionné",
+    "check_out": "Restitué",
+    "relocate": "Déplacé",
+    "dispose": "Mis au rebut",
+    "measure": "Mesuré"
+  },
+  "detail": {
+    "move": "Déplacer",
+    "checkOut": "Restituer",
+    "conditionTitle": "État",
+    "noMeasurements": "Aucun relevé enregistré.",
+    "historyTitle": "Historique",
+    "storageTitle": "Stockage",
+    "shelf": "Étagère",
+    "warehouse": "Site",
+    "notStored": "Pas en stock actuellement.",
+    "checkedIn": "Réceptionné",
+    "checkedOut": "Restitué",
+    "tiresTitle": "Pneus",
+    "loadSpeed": "Indices de charge et vitesse",
+    "ownerTitle": "Propriétaire",
+    "phone": "Téléphone",
+    "deleteTitle": "Supprimer ce jeu de pneus ?",
+    "deleteBody": "Le jeu et tout son historique seront supprimés. Action irréversible.",
+    "deleted": "Jeu de pneus supprimé",
+    "deleteFailed": "Impossible de supprimer le jeu",
+    "belowLimit": "{count} pneu(s) à votre limite de remplacement ou en dessous.",
+    "belowLimitHint": "À proposer au client tant que les pneus sont encore ici.",
+    "dispose": "Mettre au rebut",
+    "disposeTitle": "Mettre ce jeu au rebut ?",
+    "disposeBody": "La fiche et son historique restent, et la place est libérée. Le jeu ne sera plus proposé la saison prochaine.",
+    "disposed": "Jeu mis au rebut",
+    "disposeFailed": "Impossible de mettre le jeu au rebut"
+  },
+  "checkOut": {
+    "title": "Restituer les pneus",
+    "description": "Le jeu est retiré de {code}.",
+    "descriptionNoLocation": "Ce jeu repart chez le client.",
+    "treadTitle": "Profondeur de sculpture à la sortie",
+    "note": "Note",
+    "submit": "Restituer",
+    "success": "Jeu {reference} restitué",
+    "failed": "Impossible de restituer le jeu",
+    "prepPending": "{count} tâche(s) de préparation en attente",
+    "conditionTitle": "État à la réception",
+    "conditionLead": {
+      "replace": "Au moins un pneu est à votre limite de remplacement ou en dessous. À signaler au client avant son départ.",
+      "fair": "Au moins un pneu approche de votre limite de remplacement. À mentionner avant la saison prochaine."
+    },
+    "treadHintUnmeasured": "Aucun relevé n’a été fait à la réception. C’est la dernière occasion d’en faire un."
+  },
+  "relocate": {
+    "title": "Déplacer les pneus",
+    "description": "Choisissez l’étagère de destination.",
+    "newLocation": "Nouvelle étagère",
+    "note": "Note",
+    "submit": "Déplacer",
+    "success": "Jeu {reference} déplacé",
+    "failed": "Impossible de déplacer le jeu"
+  },
+  "edit": {
+    "title": "Modifier le jeu de pneus",
+    "description": "Corrigez ce qui a été saisi à la réception. Utilisez Déplacer pour changer d’étagère.",
+    "quantityHint": "Une augmentation est vérifiée par rapport à l’étagère où se trouve le jeu.",
+    "rimType": "Type de jante",
+    "rimTypePlaceholder": "Alliage, tôle",
+    "success": "Jeu de pneus mis à jour",
+    "failed": "Impossible d’enregistrer les modifications",
+    "treadHint": "Une valeur modifiée est enregistrée comme un nouveau relevé dans l’historique, sans écraser l’ancien."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Laver les pneus",
+      "wash_rims": "Laver les jantes",
+      "balance": "Équilibrer",
+      "tpms_service": "Service TPMS",
+      "new_valves": "Valves neuves",
+      "repair": "Réparation"
+    },
+    "statuses": {
+      "pending": "À faire",
+      "done": "Fait",
+      "skipped": "Ignoré"
+    },
+    "title": "Préparation",
+    "none": "Rien à faire sur ce jeu.",
+    "add": "Ajouter une préparation",
+    "pickHint": "Cochez ce dont ce jeu a besoin avant de retourner sur le véhicule.",
+    "pendingCount": "{count} à faire",
+    "allComplete": "Tout est fait",
+    "ready": "Prêt",
+    "markDone": "Fait",
+    "completeAll": "Tout marquer fait ({count})",
+    "allDone": "Préparation terminée",
+    "undo": "Remettre dans la liste",
+    "saveFailed": "Enregistrement impossible",
+    "skip": "Ignorer"
+  },
+  "messaging": {
+    "action": "Message",
+    "title": "Écrire au client",
+    "description": "À {name}.",
+    "noCustomer": "Aucun client sur ce jeu.",
+    "noCustomerBody": "Associez un client au jeu avant de lui écrire.",
+    "channel": "Envoyer par",
+    "channels": {
+      "email": "E-mail",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "aucune adresse enregistrée",
+    "recipient": "Envoyer à",
+    "recipientPlaceholder": "Numéro de téléphone",
+    "subject": "Objet",
+    "message": "Message",
+    "send": "Envoyer",
+    "sent": "Message envoyé",
+    "failed": "Impossible d’envoyer le message",
+    "smsLong": "Les messages longs peuvent être découpés en plusieurs SMS.",
+    "subjects": {
+      "low_tread": "Vos pneus {season}",
+      "damage": "Vos pneus {season}",
+      "stored": "Vos pneus {season} sont stockés",
+      "custom": "Vos pneus {season}"
+    },
+    "bodies": {
+      "low_tread": "Bonjour {customer_name}, vos pneus {season} sont stockés chez nous, {shop_name}. À la réception, {positions} mesuraient {tread}, ce qui est sous la limite. Ils ne tiendront pas une saison de plus. On vous fait un prix pour un jeu neuf ?",
+      "damage": "Bonjour {customer_name}, vos pneus {season} sont stockés chez nous, {shop_name}. À la réception, nous avons trouvé un dommage sur {positions}. Appelez-nous quand vous pouvez, on regarde ça ensemble.",
+      "stored": "Bonjour {customer_name}, vos pneus {season} sont maintenant stockés chez nous, {shop_name}. On vous prévient quand ce sera le moment de les remonter.",
+      "custom": "Bonjour {customer_name}, au sujet de vos pneus {season} stockés chez nous, {shop_name} :"
+    },
+    "enableInSettings": "Pas encore configuré. Activez-le dans les Paramètres."
+  },
+  "job": {
+    "cardTitle": "Devis et ordres",
+    "none": "Rien de créé pour ce jeu.",
+    "quote": "Devis",
+    "workOrder": "Ordre",
+    "converted": "converti",
+    "quoteTitle": "Chiffrer un jeu neuf",
+    "workOrderTitle": "Facturer ce jeu",
+    "loadingSet": "Chargement du jeu",
+    "tires": "Pneus",
+    "inStock": "{count} en stock",
+    "shortStock": "seulement {count} en stock",
+    "noMatch": "Rien en stock ne correspond à {size}. La ligne est ajoutée au prix que vous saisissez.",
+    "noSize": "Ce jeu n’a pas de dimension, le stock ne peut pas être comparé. La ligne est ajoutée à votre prix.",
+    "unitPrice": "Prix par pneu",
+    "createQuote": "Créer le devis",
+    "createWorkOrder": "Créer l’ordre",
+    "quoteCreated": "Devis {number} créé",
+    "workOrderCreated": "Ordre créé",
+    "failed": "Création impossible",
+    "bannerTitle": "À récupérer en",
+    "openSet": "Ouvrir le jeu",
+    "prepPending": "À faire avant montage : {jobs}",
+    "notOnShelf": "Ce jeu n’est plus en stockage.",
+    "targetNew": "Nouvel ordre",
+    "targetNewHint": "Créer un ordre uniquement pour ces pneus.",
+    "targetExisting": "Ou ajouter à un ordre déjà ouvert pour ce véhicule",
+    "addToWorkOrder": "Ajouter à l’ordre",
+    "workOrderUpdated": "Ajouté à l’ordre",
+    "unlink": "Dissocier",
+    "unlinkTitle": "Dissocier ces pneus de l’ordre ?",
+    "unlinkBody": "L’ordre ne renvoie plus au jeu de pneus. Les pièces et la main-d’œuvre déjà saisies restent ; retirez-les séparément si elles ne doivent pas être facturées.",
+    "unlinked": "Pneus dissociés",
+    "unlinkFailed": "Impossible de dissocier les pneus",
+    "prep": "Préparation",
+    "tiresSkipped": "Aucune ligne de pneus ne sera ajoutée.",
+    "total": "Total",
+    "searchStock": "Rechercher dans le stock par marque, dimension ou référence",
+    "noSearchHits": "Rien dans le stock ne correspond à \"{query}\".",
+    "otherSize": "Autre dimension",
+    "inSize": "{count} en {size}",
+    "quantity": "Qté",
+    "noStockItem": "Pas d’article en stock, prix saisi à la main",
+    "storeTires": "Stocker les pneus",
+    "storage": "Stockage",
+    "storageFee": "Frais",
+    "storagePeriod": "Période",
+    "destination": "Destination",
+    "targetInvoice": "Nouvelle facture",
+    "targetInvoiceHint": "Facturer le client directement, sans ordre sur un véhicule.",
+    "createInvoice": "Créer la facture",
+    "needsCustomer": "Associez un client ou un véhicule à ce jeu avant de facturer.",
+    "storagePeriodEnd": "Repris",
+    "storageOpenEnded": "Ouvert",
+    "storagePeriodHint": "Apparaît sur la ligne de facture pour que le client voie ce que couvrent les frais. Laissez la fin vide tant que la date de reprise est inconnue.",
+    "pricesHint": "Les tarifs de préparation viennent des paramètres. Chaque ligne reste modifiable une fois le document créé.",
+    "files": "Photos et fichiers",
+    "filesCount": "{count} sur la facture"
+  },
+  "label": {
+    "action": "Étiquettes",
+    "title": "Imprimer les étiquettes",
+    "description": "Une étiquette par pneu, avec un QR qui ouvre ce jeu.",
+    "format": "Format",
+    "formats": {
+      "dymo_small": "Petit rouleau",
+      "dymo_standard": "Rouleau standard",
+      "thermal_large": "Grand rouleau",
+      "sheet_a4": "Planche A4"
+    },
+    "perSheet": "{count} par planche",
+    "copies": "Combien",
+    "copiesHint": "une par pneu ({count})",
+    "fewer": "Une de moins",
+    "more": "Une de plus",
+    "print": "Imprimer",
+    "shelfNote": "L’étagère n’est volontairement pas imprimée. Les jeux sont déplacés, et une étagère imprimée serait alors fausse. Le QR indique toujours l’emplacement actuel.",
+    "reference": "Réf",
+    "quantity": "{count, plural, one {# pneu} other {# pneus}}",
+    "withRims": "Sur jantes",
+    "tpms": "TPMS",
+    "studded": "Cloutés",
+    "unassigned": "Non attribué",
+    "preview": "Aperçu",
+    "previewScale": "Affiché à la taille d’impression. Le code se scanne aussi depuis l’écran.",
+    "previewFailed": "Aperçu impossible"
+  },
+  "vehicle": {
+    "oneOnShelf": "en {code}",
+    "inStorage": "à l’hôtel à pneus"
+  },
+  "wear": {
+    "summary": "{value} d’usure entre le {from} et le {to}",
+    "perSeason": "Environ {value} par saison",
+    "seasonsLeft": "{count, plural, =0 {À la limite} one {Encore une saison environ} other {Encore # saisons environ}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Modifier dans les paramètres",
+    "prepPrices": "Tarifs de préparation",
+    "storagePrices": "Tarifs de stockage",
+    "treadLimits": "Modifier la limite",
+    "defaultCapacity": "Capacité par défaut"
+  },
+  "forecast": {
+    "title": "La saison prochaine",
+    "subtitle": "Ce que les jeux stockés sont mesurés comme devant remplacer, par dimension.",
+    "backToList": "Retour aux jeux",
+    "tires": "Pneus attendus",
+    "atLimit": "Déjà à la limite",
+    "expected": "Attendu sous une saison",
+    "orderTitle": "Par dimension",
+    "sizes": "{count} dimensions",
+    "empty": "Rien de mesuré près de la limite pour l’instant. Il faut deux passages pour établir un rythme d’usure.",
+    "nowCount": "{count, plural, one {# jeu à la limite} other {# jeux à la limite}}",
+    "nextCount": "{count, plural, one {# jeu sous une saison} other {# jeux sous une saison}}",
+    "tiresShort": "pneus",
+    "lowest": "plus faible {value}",
+    "now": "À la limite",
+    "next": "Sous une saison",
+    "rate": "{value} par saison",
+    "caveat": "Les jeux déjà à la limite sont mesurés. Le reste est extrapolé de l’usure d’un client sur un intervalle : à utiliser pour planifier, pas pour commander.",
+    "unmeasured": "{count, plural, one {# jeu manque d’historique et n’est pas compté} other {# jeux manquent d’historique et ne sont pas comptés}}",
+    "capped": "Les {shown} jeux les plus récents sur {total} ont été lus."
+  },
+  "invoiceLine": {
+    "storage": "Stockage de pneus",
+    "pieces": "pcs",
+    "fromDate": "depuis le {date}"
+  },
+  "setNames": {
+    "winter": "pneus hiver",
+    "summer": "pneus été",
+    "all_season": "pneus 4 saisons",
+    "other": "pneus"
+  },
+  "jobNotes": {
+    "set": "Jeu de pneus {reference}",
+    "shelf": "Étagère : {shelf}",
+    "notOnShelf": "pas en rayon",
+    "onRims": "Sur jantes",
+    "tiresOnly": "Pneus seuls",
+    "tpms": "Capteurs TPMS"
+  },
+  "files": {
+    "title": "Photos et fichiers",
+    "add": "Ajouter des fichiers",
+    "none": "Rien de joint pour l’instant. Photographiez une jante frottée pendant que les pneus sont devant vous.",
+    "captionPlaceholder": "Que montre cette image ?",
+    "onInvoice": "Sur la facture",
+    "invoiceHint": "Les fichiers marqués pour la facture sont copiés sur l’ordre ou la facture au moment de facturer.",
+    "deleteTitle": "Retirer ce fichier ?",
+    "deleteBody": "{name} est retiré du jeu. Ce qui a déjà été copié sur une facture y reste.",
+    "failed": "Impossible d’enregistrer",
+    "someFailed": "{count, plural, one {# fichier n’a pas pu être envoyé} other {# fichiers n’ont pas pu être envoyés}}"
+  }
+}

+ 28 - 1
messages/it/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Messaggio pianificato inviato",
     "scheduled_message_send_now": "Messaggio pianificato inviato",
     "settings_applyTaxRate": "Aliquota applicata",
     "settings_applyTaxRate": "Aliquota applicata",
     "settings_convertToExclusive": "Prezzi convertiti in imponibile",
     "settings_convertToExclusive": "Prezzi convertiti in imponibile",
-    "settings_convertToInclusive": "Prezzi convertiti in ivato"
+    "settings_convertToInclusive": "Prezzi convertiti in ivato",
+    "organization_sampleDataRemove": "Dati di esempio rimossi",
+    "tire_warehouse_create": "Sede di deposito pneumatici creata",
+    "tire_warehouse_update": "Sede di deposito pneumatici aggiornata",
+    "tire_warehouse_delete": "Sede di deposito pneumatici eliminata",
+    "tire_warehouse_archive": "Sede di deposito pneumatici archiviata",
+    "tire_location_create": "Scaffale pneumatici creato",
+    "tire_location_bulk_create": "Scaffali pneumatici creati",
+    "tire_location_update": "Scaffale pneumatici aggiornato",
+    "tire_location_delete": "Scaffale pneumatici eliminato",
+    "tire_set_check_in": "Treno di pneumatici accettato",
+    "tire_set_check_out": "Treno di pneumatici restituito",
+    "tire_set_relocate": "Treno di pneumatici spostato",
+    "tire_set_update": "Treno di pneumatici aggiornato",
+    "tire_set_delete": "Treno di pneumatici eliminato",
+    "tire_measurement_create": "Misurazioni pneumatici registrate",
+    "tire_treatment_update": "Preparazione aggiornata",
+    "tire_treatment_mark": "Preparazione segnata",
+    "tire_treatment_complete_all": "Preparazione completata",
+    "tire_set_message_customer": "Messaggio inviato al cliente su un treno di pneumatici",
+    "tire_set_create_quote": "Preventivo creato da un treno di pneumatici",
+    "tire_set_create_work_order": "Ordine creato da un treno di pneumatici",
+    "tire_set_add_to_work_order": "Treno di pneumatici aggiunto a un ordine",
+    "tire_set_unlink_work_order": "Treno di pneumatici scollegato da un ordine",
+    "tire_set_return": "Treno di nuovo in deposito",
+    "tire_set_dispose": "Treno di pneumatici dismesso",
+    "tire_set_attach": "File aggiunti a un treno di pneumatici",
+    "tire_set_detach": "File rimosso da un treno di pneumatici"
   }
   }
 }
 }

+ 6 - 2
messages/it/common.json

@@ -10,7 +10,8 @@
     "confirm": "Conferma",
     "confirm": "Conferma",
     "saveChanges": "Salva modifiche",
     "saveChanges": "Salva modifiche",
     "edit": "Modifica",
     "edit": "Modifica",
-    "add": "Aggiungi"
+    "add": "Aggiungi",
+    "gotIt": "Ho capito"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Suggerimento: clic destro su una riga per le azioni",
     "hint": "Suggerimento: clic destro su una riga per le azioni",
@@ -75,6 +76,9 @@
     "enterManually": "Inserisci manualmente",
     "enterManually": "Inserisci manualmente",
     "enterBarcode": "Inserisci codice a barre…",
     "enterBarcode": "Inserisci codice a barre…",
     "scanHint": "Punta la fotocamera su un codice a barre oppure inseriscilo manualmente",
     "scanHint": "Punta la fotocamera su un codice a barre oppure inseriscilo manualmente",
-    "iosInstallHint": "Tocca Condividi > Aggiungi a Home"
+    "iosInstallHint": "Tocca Condividi > Aggiungi a Home",
+    "installDescription": "Aggiungila alla schermata Home per un accesso rapido",
+    "enterFullscreen": "Schermo intero",
+    "exitFullscreen": "Esci da schermo intero"
   }
   }
 }
 }

+ 12 - 1
messages/it/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Completati di recente",
     "recentCompleted": "Completati di recente",
     "activeJobs": "Lavori attivi",
     "activeJobs": "Lavori attivi",
     "recentObservations": "Osservazioni recenti",
     "recentObservations": "Osservazioni recenti",
-    "recentActivity": "Attività recente"
+    "recentActivity": "Attività recente",
+    "gettingStarted": "Per iniziare",
+    "tireHotel": "Hotel pneumatici"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Manutenzione prevista in scadenza",
     "title": "Manutenzione prevista in scadenza",
@@ -233,5 +235,14 @@
       "expired": "Scaduto",
       "expired": "Scaduto",
       "converted": "Convertito"
       "converted": "Convertito"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Hotel pneumatici",
+    "viewAll": "Vedi hotel pneumatici",
+    "stored": "{count} treni in deposito",
+    "capacity": "{used} / {capacity} pneumatici",
+    "roomLeft": "Spazio per altri {count}",
+    "needsPrep": "Da preparare",
+    "unbilled": "Non fatturato ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/it/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "L’hotel pneumatici è attivo",
+    "body": "Conserva gli pneumatici stagionali. Crea gli scaffali, poi accetta un treno."
+  },
+  "telegram": {
+    "title": "Telegram è attivo",
+    "body": "Le conversazioni con i clienti arrivano qui. Rispondi senza uscire dall'app."
+  }
+}

+ 1 - 0
messages/it/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Veicolo",
     "vehicle": "Veicolo",
     "template": "Modello",
     "template": "Modello",
     "progress": "Avanzamento",
     "progress": "Avanzamento",
+    "progressTooltip": "{graded} punti su {total} valutati",
     "status": "Stato",
     "status": "Stato",
     "date": "Data",
     "date": "Data",
     "statusInProgress": "In corso",
     "statusInProgress": "In corso",

+ 8 - 2
messages/it/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Tutti i promemoria",
     "allReminders": "Tutti i promemoria",
     "auditLog": "Registro attività",
     "auditLog": "Registro attività",
     "aiAssistant": "Assistente IA",
     "aiAssistant": "Assistente IA",
-    "observations": "Osservazioni"
+    "observations": "Osservazioni",
+    "tireHotel": "Hotel pneumatici",
+    "allTireSets": "Tutti i treni",
+    "tireStorage": "Disposizione del deposito",
+    "tireSetDetails": "Treno di pneumatici",
+    "tireForecast": "La prossima stagione"
   },
   },
   "search": "Cerca...",
   "search": "Cerca...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Esci",
     "signOut": "Esci",
     "more": "Altro",
     "more": "Altro",
     "auditLog": "Registro attività",
     "auditLog": "Registro attività",
-    "aiAssistant": "Assistente IA"
+    "aiAssistant": "Assistente IA",
+    "tireHotel": "Hotel pneumatici"
   }
   }
 }
 }

+ 91 - 0
messages/it/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Configuri la Sua officina",
+    "subtitle": "Inserisca il nome della Sua officina per iniziare",
+    "workshopName": "Nome dell'officina",
+    "workshopNamePlaceholder": "La Mia Officina",
+    "submit": "Inizia",
+    "sampleDataLabel": "Carica dati di esempio",
+    "sampleDataDescription": "Aggiunge alcuni clienti, veicoli e lavori di esempio per farsi un'idea. Potrà rimuoverli di nuovo con un clic.",
+    "setupFailed": "Configurazione non riuscita",
+    "couldNotCreate": "Impossibile creare l'officina",
+    "unexpectedError": "Si è verificato un errore imprevisto"
+  },
+  "sample": {
+    "prefix": "Esempio:",
+    "oilService": "Cambio olio e filtro",
+    "brakeJob": "Sostituzione pastiglie freni anteriori",
+    "annualService": "Tagliando annuale",
+    "quoteTitle": "Revisione sospensione anteriore",
+    "inspectionNote": "Lo pneumatico anteriore sinistro è vicino al limite di usura",
+    "parts": {
+      "oilFilter": "Filtro olio",
+      "engineOil": "Olio motore 5W-30",
+      "brakePads": "Kit pastiglie freni anteriori",
+      "shockAbsorber": "Ammortizzatore"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Cambio olio",
+      "description": "Sostituzione olio motore e filtro"
+    },
+    "frontBrakePads": {
+      "name": "Pastiglie freni anteriori",
+      "description": "Sostituire le pastiglie dei freni anteriori"
+    },
+    "tireSwap": {
+      "name": "Cambio gomme",
+      "description": "Cambio stagionale delle ruote con controllo della coppia"
+    },
+    "batteryReplacement": {
+      "name": "Sostituzione batteria",
+      "description": "Sostituire e registrare la batteria di avviamento"
+    },
+    "diagnostics": {
+      "name": "Diagnosi",
+      "description": "Lettura dei codici di errore e prima diagnosi"
+    },
+    "annualService": {
+      "name": "Tagliando annuale",
+      "description": "Tagliando annuale completo secondo il programma"
+    }
+  },
+  "checklist": {
+    "title": "Per iniziare",
+    "description": "Quattro passaggi per avviare la Sua officina",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Aggiungere un cliente",
+        "description": "Crei la Sua prima scheda cliente"
+      },
+      "vehicle": {
+        "title": "Aggiungere un veicolo",
+        "description": "Registri un veicolo per un cliente"
+      },
+      "workOrder": {
+        "title": "Creare un ordine di lavoro",
+        "description": "Apra il Suo primo lavoro"
+      },
+      "invoice": {
+        "title": "Inviare o scaricare una fattura",
+        "description": "Condivida una fattura con un cliente o la scarichi in PDF"
+      }
+    },
+    "allDoneTitle": "Tutto pronto",
+    "allDoneDescription": "Tutti i passaggi sono completati e la Sua officina è operativa.",
+    "dismiss": "Nascondi scheda",
+    "dismissAriaLabel": "Nascondi questa lista"
+  },
+  "sampleData": {
+    "title": "Dati di esempio",
+    "description": "La Sua officina contiene ancora i clienti, i veicoli e i lavori di esempio della configurazione. La rimozione elimina esattamente quei record e nient'altro.",
+    "footerNote": "Sono caricati dati di esempio",
+    "remove": "Rimuovi dati di esempio",
+    "removeConfirmTitle": "Rimuovere i dati di esempio?",
+    "removeConfirmDescription": "Verranno eliminati i clienti, i veicoli, gli ordini di lavoro, il preventivo e l'ispezione di esempio creati durante la configurazione. I Suoi dati non vengono toccati.",
+    "removed": "Dati di esempio rimossi",
+    "removeFailed": "Impossibile rimuovere i dati di esempio"
+  }
+}

+ 27 - 0
messages/it/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Dashboard",
+    "vehicles": "Veicoli",
+    "customers": "Clienti",
+    "work_orders": "Ordini di lavoro",
+    "quotes": "Preventivi",
+    "services": "Interventi",
+    "billing": "Fatturazione",
+    "inventory": "Magazzino",
+    "labor_presets": "Preset di manodopera",
+    "inspections": "Ispezioni",
+    "tire_hotel": "Hotel pneumatici",
+    "reports": "Report",
+    "work_board": "Bacheca lavori",
+    "ai_assistant": "Assistente IA",
+    "settings": "Impostazioni"
+  },
+  "actions": {
+    "create": "Creare",
+    "read": "Vedere",
+    "update": "Modificare",
+    "delete": "Eliminare",
+    "manage": "Gestire",
+    "manage_storage": "Gestire la disposizione del deposito"
+  }
+}

+ 17 - 2
messages/it/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} buono",
     "good": "{count} buono",
     "fair": "{count} discreto",
     "fair": "{count} discreto",
     "poor": "{count} scarso",
     "poor": "{count} scarso",
-    "view": "Visualizza"
+    "view": "Visualizza",
+    "serviceHistoryTitle": "Storico interventi",
+    "inspectionsTitle": "Ispezioni",
+    "columnVehicle": "Veicolo",
+    "columnPlate": "Targa",
+    "columnServices": "Interventi",
+    "columnInspections": "Ispezioni"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Richiedi Servizio",
     "title": "Richiedi Servizio",
@@ -174,6 +180,15 @@
     "yourRequests": "Le Tue Richieste di Servizio",
     "yourRequests": "Le Tue Richieste di Servizio",
     "failedToLoad": "Caricamento dei dati non riuscito.",
     "failedToLoad": "Caricamento dei dati non riuscito.",
     "staffNotes": "Note del personale: {notes}",
     "staffNotes": "Note del personale: {notes}",
-    "preferred": "Preferita: {date}"
+    "preferred": "Preferita: {date}",
+    "notListedOption": "Il mio veicolo non è in elenco",
+    "newVehicleHint": "Descrivi il veicolo e lo aggiungeremo al tuo profilo.",
+    "fillVehicleDetails": "Inserisci marca e modello del veicolo",
+    "makeLabel": "Marca",
+    "makePlaceholder": "es. Ford",
+    "modelLabel": "Modello",
+    "modelPlaceholder": "es. Kuga",
+    "yearLabel": "Anno",
+    "plateLabel": "Targa"
   }
   }
 }
 }

+ 9 - 0
messages/it/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Cerca",
+  "description": "Cerca veicoli, clienti, interventi e impostazioni",
+  "placeholder": "Cerca per nome, targa, telefono, telaio, fattura…",
+  "noResults": "Nessun risultato.",
+  "recentCustomers": "Clienti recenti",
+  "services": "Interventi",
+  "toClose": "per chiudere"
+}

+ 47 - 1
messages/it/settings.json

@@ -103,6 +103,10 @@
         "title": "Dati",
         "title": "Dati",
         "description": "Esportazione e importazione backup"
         "description": "Esportazione e importazione backup"
       },
       },
+      "install": {
+        "title": "Installa app",
+        "description": "Aggiungi Torqvoice a questo dispositivo"
+      },
       "license": {
       "license": {
         "title": "Licenza",
         "title": "Licenza",
         "description": "Licenza white-label"
         "description": "Licenza white-label"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Assistenza",
         "title": "Assistenza",
         "description": "Contatta l'assistenza e gestisci il pulsante"
         "description": "Contatta l'assistenza e gestisci il pulsante"
+      },
+      "tireHotel": {
+        "title": "Hotel pneumatici",
+        "description": "Deposito stagionale degli pneumatici"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Ultimo backup:",
       "lastBackup": "Ultimo backup:",
       "supportPrompt": "Per qualsiasi domanda sulla protezione dei tuoi dati,",
       "supportPrompt": "Per qualsiasi domanda sulla protezione dei tuoi dati,",
       "supportAction": "contatta il nostro team di supporto."
       "supportAction": "contatta il nostro team di supporto."
-    }
+    },
+    "optTireHotel": "Hotel pneumatici",
+    "optTireHotelDesc": "Treni in deposito, scaffali, preparazione e contratti di deposito"
   },
   },
   "license": {
   "license": {
     "title": "Licenza White-Label",
     "title": "Licenza White-Label",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Contatta l'assistenza",
     "contactTitle": "Contatta l'assistenza",
     "contactHint": "Apri subito il modulo di assistenza.",
     "contactHint": "Apri subito il modulo di assistenza.",
     "contactAction": "Apri il modulo"
     "contactAction": "Apri il modulo"
+  },
+  "install": {
+    "title": "Installa Torqvoice",
+    "description": "Installa Torqvoice su questo dispositivo per aprirla dalla schermata Home, a schermo intero e senza la barra del browser. Restano la stessa app e gli stessi dati.",
+    "installed": "Torqvoice è installata su questo dispositivo.",
+    "iosHint": "In Safari, tocca il pulsante Condividi e poi «Aggiungi a Home».",
+    "unavailable": "Questo browser non ha proposto l'installazione. Apri il menu del browser e cerca «Installa app» o «Aggiungi a Home».",
+    "bannerHidden": "Il banner di installazione è nascosto su questo dispositivo.",
+    "showBanner": "Mostra di nuovo il banner"
+  },
+  "tireHotel": {
+    "title": "Hotel pneumatici",
+    "description": "Custodisci gli pneumatici stagionali dei tuoi clienti e sai sempre dove si trova ogni treno.",
+    "cardTitle": "Deposito pneumatici",
+    "enable": "Attiva hotel pneumatici",
+    "enableHint": "Aggiunge il deposito pneumatici al menu. Disattivandolo in seguito viene nascosto senza cancellare nulla.",
+    "defaultCapacity": "Pneumatici per ripiano predefiniti",
+    "defaultCapacityHint": "Precompilato quando crei nuovi ripiani. Modificabile per ogni ripiano.",
+    "warnPercent": "Avvisa quando pieno (%)",
+    "warnPercentHint": "Segnala la panoramica del deposito quando questa quota della capacità totale è occupata.",
+    "treadTitle": "Soglie di sostituzione",
+    "treadHint": "I minimi di legge variano da paese a paese, quindi imposta le soglie del tuo mercato. Un battistrada sotto la soglia viene segnalato per la sostituzione.",
+    "summerLimit": "Soglia estiva ({unit})",
+    "winterLimit": "Soglia invernale ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "Poi configura i tuoi scaffali.",
+    "setupLink": "Apri la disposizione del deposito",
+    "save": "Salva impostazioni hotel pneumatici",
+    "saved": "Impostazioni hotel pneumatici salvate",
+    "billingTitle": "Fatturazione del deposito",
+    "billingHint": "Precompilato sulla riga di deposito quando fatturi un treno. Modificabile sulla fattura.",
+    "seasonalPrice": "Prezzo di deposito predefinito",
+    "prepTitle": "Prezzi della preparazione",
+    "prepHint": "Quanto costa ogni tipo di preparazione. Lasciandolo vuoto non viene mai fatturata, come serve a chi include il lavaggio nel deposito. Quelle con un prezzo finiscono da sole su preventivo e ordine.",
+    "prepNotCharged": "non addebitato"
   }
   }
 }
 }

+ 473 - 0
messages/it/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Hotel pneumatici",
+  "description": "Deposito stagionale degli pneumatici dei tuoi clienti",
+  "common": {
+    "cancel": "Annulla",
+    "save": "Salva",
+    "create": "Crea",
+    "delete": "Elimina",
+    "edit": "Modifica"
+  },
+  "seasons": {
+    "summer": "Estivi",
+    "winter": "Invernali",
+    "all_season": "Quattro stagioni",
+    "other": "Altro"
+  },
+  "statuses": {
+    "stored": "In deposito",
+    "released": "Restituiti",
+    "disposed": "Smaltiti"
+  },
+  "conditions": {
+    "good": "Buono",
+    "fair": "Da tenere d'occhio",
+    "replace": "Da sostituire"
+  },
+  "positions": {
+    "front_left": "Anteriore sinistro",
+    "front_right": "Anteriore destro",
+    "rear_left": "Posteriore sinistro",
+    "rear_right": "Posteriore destro",
+    "spare": "Ruota di scorta",
+    "unspecified": "Non specificato"
+  },
+  "capacity": {
+    "used": "{used} di {capacity} pneumatici",
+    "free": "Spazio per {count}",
+    "freeShort": "{count} liberi",
+    "over": "{count} oltre la capacità",
+    "meterLabel": "{used} di {capacity} pneumatici in deposito"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32esimi",
+    "inputLabel": "Profondità del battistrada, {position}",
+    "lastTime": "era {value}",
+    "worn": "{value} di usura"
+  },
+  "list": {
+    "tabAll": "Tutti",
+    "tabStored": "In deposito",
+    "tabReleased": "Restituiti",
+    "search": "Cerca per targa, cliente, marca o scaffale",
+    "checkIn": "Accettare pneumatici",
+    "empty": "Nessun treno di pneumatici. Accetta un treno per iniziare.",
+    "unassigned": "Non assegnato",
+    "reference": "Rif",
+    "vehicle": "Veicolo",
+    "tires": "Pneumatici",
+    "location": "Scaffale",
+    "condition": "Condizione",
+    "status": "Stato",
+    "storedSince": "Dal",
+    "tireCount": "{count, plural, one {# pneumatico} other {# pneumatici}}",
+    "studded": "Chiodati",
+    "roomLeft": "Spazio per {count}",
+    "prep": "Prep.",
+    "tabNeedsPrep": "Da preparare",
+    "setUpShelves": "Configura gli scaffali",
+    "emptyNoShelves": "Nessuno scaffale. Configura il deposito prima di accettare pneumatici.",
+    "tabDisposed": "Dismessi"
+  },
+  "checkIn": {
+    "title": "Accettare pneumatici",
+    "description": "Registra ciò che è arrivato, poi scegli dove riporlo.",
+    "customer": "Cliente",
+    "selectCustomer": "Seleziona cliente",
+    "noCustomer": "Nessun cliente",
+    "vehicle": "Veicolo",
+    "selectVehicle": "Seleziona veicolo",
+    "season": "Stagione",
+    "quantity": "Numero di pneumatici",
+    "quantityHint": "Contati singolarmente, così vanno bene anche due o cinque ruote.",
+    "brand": "Marca",
+    "brandPlaceholder": "Michelin, Bridgestone, Pirelli",
+    "model": "Modello",
+    "size": "Misura",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "Codice DOT",
+    "dotPlaceholder": "3623",
+    "dotHint": "Settimana e anno di produzione dello pneumatico.",
+    "withRims": "Su cerchi",
+    "hasTpms": "Sensori TPMS",
+    "studded": "Chiodati",
+    "location": "Dove vanno gli pneumatici",
+    "choosePlace": "Scegli uno scaffale",
+    "searchLocations": "Cerca scaffali",
+    "noLocations": "Nessuno scaffale trovato",
+    "tooFull": "Spazio insufficiente",
+    "pickLocationFirst": "Scegli uno scaffale prima di accettare il treno",
+    "roomSummary": "{locations} scaffali adatti a questo treno, {tires} posti liberi",
+    "nothingFits": "Nessuno scaffale ha spazio per {quantity} pneumatici. Libera spazio o aggiungi uno scaffale.",
+    "treadTitle": "Profondità del battistrada all'arrivo",
+    "treadHint": "Facoltativo, ma risolve ogni futura discussione sulle condizioni.",
+    "notes": "Note",
+    "submit": "Accetta",
+    "success": "Treno {reference} riposto in {code}",
+    "failed": "Impossibile accettare il treno",
+    "noVehiclesForCustomer": "Questo cliente non ha ancora veicoli. Aggiungine uno dalla sua scheda cliente.",
+    "treatmentsHint": "Cosa fare su questo treno prima del deposito o del rimontaggio.",
+    "storedBefore": "Già in deposito qui",
+    "storedBeforeHint": "Tornano gli stessi pneumatici? Scegli il treno e la stagione entra nel suo storico.",
+    "sameSet": "Stesso treno di prima",
+    "pieces": "{count} pz",
+    "lastOut": "Ritirato il {date}",
+    "notStored": "Al momento non in deposito",
+    "returned": "Il treno {reference} è di nuovo su {code}",
+    "submitReturn": "Metti di nuovo in deposito",
+    "ownerFromVehicle": "Preso dal veicolo",
+    "vehicleOwner": "Il proprietario del veicolo",
+    "clearVehicle": "Rimuovi il veicolo"
+  },
+  "storage": {
+    "title": "Scaffali",
+    "description": "Configura il deposito una volta e vedi a colpo d'occhio cosa entra dove.",
+    "addWarehouse": "Aggiungi sede",
+    "addShelves": "Aggiungi scaffali",
+    "warehouseName": "Nome",
+    "warehouseNamePlaceholder": "Capannone principale, Container 2, Retro",
+    "warehouseAddress": "Indirizzo",
+    "warehouseCreated": "Sede di deposito aggiunta",
+    "warehouseDeleted": "{name} eliminata",
+    "warehouseArchived": "{name} archiviata",
+    "deleteWarehouse": "Elimina sede",
+    "deleteWarehouseTitle": "Eliminare {name}?",
+    "deleteWarehouseBody": "Questo rimuove la sede e tutti i suoi scaffali.",
+    "deleteWarehouseBodyOccupied": "Questa sede contiene ancora {count} pneumatici, quindi verrà archiviata anziché eliminata.",
+    "totalCapacity": "Capacità totale",
+    "totalStored": "In deposito ora",
+    "totalFree": "Spazio libero",
+    "totalLocations": "Scaffali",
+    "locationSummary": "{occupied} di {total} scaffali in uso",
+    "noLocations": "Nessuno scaffale. Aggiungi una scaffalatura per depositare pneumatici qui.",
+    "emptyTitle": "Nessun deposito configurato",
+    "emptyBody": "Aggiungi il primo capannone o locale e riempilo di scaffali.",
+    "default": "Predefinita",
+    "addLocation": "Aggiungi scaffali",
+    "editLocation": "Modifica scaffale",
+    "createLocation": "Crea scaffale",
+    "createShelves": "Crea scaffali",
+    "locationDialogDescription": "Aggiunta di spazio a {warehouse}.",
+    "tabBulk": "Una scaffalatura",
+    "tabSingle": "Un ripiano",
+    "zone": "Zona",
+    "zonePlaceholder": "A, Locale 1, Nord",
+    "rack": "Scaffalatura",
+    "rackPlaceholder": "04",
+    "shelf": "Ripiano",
+    "shelfPlaceholder": "2",
+    "position": "Posizione",
+    "positionPlaceholder": "Sinistra",
+    "shelfFrom": "Ripiani da",
+    "shelfTo": "a",
+    "code": "Codice",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Compare sull'etichetta. Viene compilato in automatico se lo lasci vuoto.",
+    "capacity": "Pneumatici per ripiano",
+    "capacityHint": "Quanti singoli pneumatici ci stanno qui.",
+    "notes": "Note",
+    "bulkPreview": "Crea {count} ripiani, da {first} a {last}",
+    "bulkPreviewCapacity": "Spazio per {tires} pneumatici in totale",
+    "bulkCreated": "{count} ripiani creati",
+    "bulkCreatedWithSkips": "{created} ripiani creati, {skipped} già esistenti saltati",
+    "bulkNothingCreated": "Quei ripiani esistono già",
+    "locationCreated": "Scaffale aggiunto",
+    "locationUpdated": "Scaffale aggiornato",
+    "locationDeleted": "{code} eliminato",
+    "deleteLocationTitle": "Eliminare {code}?",
+    "deleteLocationBody": "Lo scaffale deve prima essere vuoto.",
+    "saveFailed": "Salvataggio non riuscito",
+    "deleteFailed": "Eliminazione non riuscita"
+  },
+  "movements": {
+    "check_in": "Accettato",
+    "check_out": "Restituito",
+    "relocate": "Spostato",
+    "dispose": "Smaltito",
+    "measure": "Misurato"
+  },
+  "detail": {
+    "move": "Sposta",
+    "checkOut": "Restituisci",
+    "conditionTitle": "Condizione",
+    "noMeasurements": "Nessuna misurazione registrata.",
+    "historyTitle": "Cronologia",
+    "storageTitle": "Deposito",
+    "shelf": "Scaffale",
+    "warehouse": "Sede",
+    "notStored": "Al momento non in deposito.",
+    "checkedIn": "Accettato",
+    "checkedOut": "Restituito",
+    "tiresTitle": "Pneumatici",
+    "loadSpeed": "Indici di carico e velocità",
+    "ownerTitle": "Proprietario",
+    "phone": "Telefono",
+    "deleteTitle": "Eliminare questo treno di pneumatici?",
+    "deleteBody": "Il treno e tutta la sua cronologia vengono rimossi. L’operazione non è reversibile.",
+    "deleted": "Treno di pneumatici eliminato",
+    "deleteFailed": "Impossibile eliminare il treno",
+    "belowLimit": "{count} pneumatico/i al limite di sostituzione o sotto.",
+    "belowLimitHint": "Vale la pena proporre un prezzo finché gli pneumatici sono ancora qui.",
+    "dispose": "Dismetti",
+    "disposeTitle": "Dismettere questo treno?",
+    "disposeBody": "La scheda e lo storico restano e il posto si libera. Non verrà più proposto come lo stesso treno la prossima stagione.",
+    "disposed": "Treno dismesso",
+    "disposeFailed": "Impossibile dismettere il treno"
+  },
+  "checkOut": {
+    "title": "Restituire gli pneumatici",
+    "description": "Il treno viene tolto da {code}.",
+    "descriptionNoLocation": "Questo treno torna al cliente.",
+    "treadTitle": "Battistrada alla riconsegna",
+    "note": "Nota",
+    "submit": "Restituisci",
+    "success": "Treno {reference} restituito",
+    "failed": "Impossibile restituire il treno",
+    "prepPending": "{count} lavoro/i di preparazione in sospeso",
+    "conditionTitle": "Condizione all’accettazione",
+    "conditionLead": {
+      "replace": "Uno o più pneumatici sono al limite di sostituzione o sotto. Vale la pena dirlo al cliente prima che riparta.",
+      "fair": "Uno o più pneumatici si avvicinano al limite di sostituzione. Da segnalare prima della prossima stagione."
+    },
+    "treadHintUnmeasured": "Non è stata fatta alcuna misurazione all’accettazione. Questa è l’ultima occasione."
+  },
+  "relocate": {
+    "title": "Sposta gli pneumatici",
+    "description": "Scegli lo scaffale di destinazione.",
+    "newLocation": "Nuovo scaffale",
+    "note": "Nota",
+    "submit": "Sposta",
+    "success": "Treno {reference} spostato",
+    "failed": "Impossibile spostare il treno"
+  },
+  "edit": {
+    "title": "Modifica treno di pneumatici",
+    "description": "Correggi quanto registrato in accettazione. Usa Sposta per cambiare scaffale.",
+    "quantityHint": "L’aumento viene verificato rispetto allo scaffale su cui si trova il treno.",
+    "rimType": "Tipo di cerchio",
+    "rimTypePlaceholder": "Lega, acciaio",
+    "success": "Treno di pneumatici aggiornato",
+    "failed": "Impossibile salvare le modifiche",
+    "treadHint": "Una misura modificata viene registrata come nuova rilevazione nello storico, senza sovrascrivere la precedente."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Lavare pneumatici",
+      "wash_rims": "Lavare cerchi",
+      "balance": "Equilibrare",
+      "tpms_service": "Servizio TPMS",
+      "new_valves": "Valvole nuove",
+      "repair": "Riparazione"
+    },
+    "statuses": {
+      "pending": "Da fare",
+      "done": "Fatto",
+      "skipped": "Saltato"
+    },
+    "title": "Preparazione",
+    "none": "Su questo treno non c’è nulla da fare.",
+    "add": "Aggiungi preparazione",
+    "pickHint": "Spunta ciò che serve a questo treno prima di tornare sulla vettura.",
+    "pendingCount": "{count} da fare",
+    "allComplete": "Tutto fatto",
+    "ready": "Pronto",
+    "markDone": "Fatto",
+    "completeAll": "Segna tutti e {count} come fatti",
+    "allDone": "Preparazione completata",
+    "undo": "Rimetti in elenco",
+    "saveFailed": "Impossibile salvare",
+    "skip": "Salta"
+  },
+  "messaging": {
+    "action": "Messaggio",
+    "title": "Scrivi al cliente",
+    "description": "A {name}.",
+    "noCustomer": "Nessun cliente su questo treno.",
+    "noCustomerBody": "Collega un cliente al treno prima di scrivergli.",
+    "channel": "Invia via",
+    "channels": {
+      "email": "E-mail",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "nessun recapito registrato",
+    "recipient": "Invia a",
+    "recipientPlaceholder": "Numero di telefono",
+    "subject": "Oggetto",
+    "message": "Messaggio",
+    "send": "Invia",
+    "sent": "Messaggio inviato",
+    "failed": "Impossibile inviare il messaggio",
+    "smsLong": "I messaggi lunghi possono essere divisi in più SMS.",
+    "subjects": {
+      "low_tread": "I tuoi pneumatici {season}",
+      "damage": "I tuoi pneumatici {season}",
+      "stored": "I tuoi pneumatici {season} sono in deposito",
+      "custom": "I tuoi pneumatici {season}"
+    },
+    "bodies": {
+      "low_tread": "Ciao {customer_name}, i tuoi pneumatici {season} sono in deposito da noi, {shop_name}. All'accettazione {positions} misuravano {tread}, sotto il limite. Un'altra stagione non la reggono. Ti facciamo un prezzo per un treno nuovo?",
+      "damage": "Ciao {customer_name}, i tuoi pneumatici {season} sono in deposito da noi, {shop_name}. All'accettazione abbiamo trovato un danno su {positions}. Chiamaci quando puoi e lo vediamo insieme.",
+      "stored": "Ciao {customer_name}, i tuoi pneumatici {season} sono ora in deposito da noi, {shop_name}. Ti avvisiamo quando sarà il momento del cambio.",
+      "custom": "Ciao {customer_name}, riguardo ai tuoi pneumatici {season} in deposito da noi, {shop_name}:"
+    },
+    "enableInSettings": "Non ancora configurato. Attivalo nelle Impostazioni."
+  },
+  "job": {
+    "cardTitle": "Preventivi e ordini",
+    "none": "Per questo treno non è stato creato nulla.",
+    "quote": "Preventivo",
+    "workOrder": "Ordine",
+    "converted": "convertito",
+    "quoteTitle": "Preventiva un treno nuovo",
+    "workOrderTitle": "Fattura questo treno",
+    "loadingSet": "Caricamento del treno",
+    "tires": "Pneumatici",
+    "inStock": "{count} disponibili",
+    "shortStock": "solo {count} disponibili",
+    "noMatch": "Niente a magazzino corrisponde a {size}. La riga viene aggiunta al prezzo che indichi.",
+    "noSize": "Questo treno non ha misura, quindi il magazzino non si può confrontare. La riga usa il tuo prezzo.",
+    "unitPrice": "Prezzo per pneumatico",
+    "createQuote": "Crea preventivo",
+    "createWorkOrder": "Crea ordine",
+    "quoteCreated": "Preventivo {number} creato",
+    "workOrderCreated": "Ordine creato",
+    "failed": "Creazione non riuscita",
+    "bannerTitle": "Da prelevare in",
+    "openSet": "Apri il treno",
+    "prepPending": "Da fare prima del montaggio: {jobs}",
+    "notOnShelf": "Questo treno non è più in deposito.",
+    "targetNew": "Nuovo ordine",
+    "targetNewHint": "Crea un ordine solo per questi pneumatici.",
+    "targetExisting": "Oppure aggiungi a un ordine già aperto per questo veicolo",
+    "addToWorkOrder": "Aggiungi all’ordine",
+    "workOrderUpdated": "Aggiunto all’ordine",
+    "unlink": "Scollega",
+    "unlinkTitle": "Scollegare questi pneumatici dall’ordine?",
+    "unlinkBody": "L’ordine non punta più al treno di pneumatici. Ricambi e manodopera già inseriti restano: rimuovili separatamente se non vanno fatturati.",
+    "unlinked": "Pneumatici scollegati",
+    "unlinkFailed": "Impossibile scollegare gli pneumatici",
+    "prep": "Preparazione",
+    "tiresSkipped": "Nessuna riga pneumatici verrà aggiunta.",
+    "total": "Totale",
+    "searchStock": "Cerca in magazzino per marca, misura o codice articolo",
+    "noSearchHits": "Niente in magazzino corrisponde a \"{query}\".",
+    "otherSize": "Altra misura",
+    "inSize": "{count} in {size}",
+    "quantity": "Qtà",
+    "noStockItem": "Nessun articolo a magazzino, prezzo manuale",
+    "storeTires": "Deposita gli pneumatici",
+    "storage": "Deposito",
+    "storageFee": "Importo",
+    "storagePeriod": "Periodo",
+    "destination": "Dove finisce",
+    "targetInvoice": "Nuova fattura",
+    "targetInvoiceHint": "Fattura il cliente direttamente, senza un ordine su un veicolo.",
+    "createInvoice": "Crea fattura",
+    "needsCustomer": "Collega un cliente o un veicolo al treno prima di fatturare.",
+    "storagePeriodEnd": "Ritirato",
+    "storageOpenEnded": "Aperto",
+    "storagePeriodHint": "Compare sulla riga della fattura, così il cliente vede cosa copre l’importo. Lascia la fine vuota finché non sai quando ritirano.",
+    "pricesHint": "I prezzi della preparazione vengono dalle impostazioni. Ogni riga resta modificabile dopo la creazione.",
+    "files": "Foto e file",
+    "filesCount": "{count} in fattura"
+  },
+  "label": {
+    "action": "Etichette",
+    "title": "Stampa le etichette",
+    "description": "Un adesivo per pneumatico, con QR che apre questo treno.",
+    "format": "Formato",
+    "formats": {
+      "dymo_small": "Rotolo piccolo",
+      "dymo_standard": "Rotolo standard",
+      "thermal_large": "Rotolo grande",
+      "sheet_a4": "Foglio A4"
+    },
+    "perSheet": "{count} per foglio",
+    "copies": "Quante",
+    "copiesHint": "una per pneumatico ({count})",
+    "fewer": "Una in meno",
+    "more": "Una in più",
+    "print": "Stampa",
+    "shelfNote": "Lo scaffale non viene stampato di proposito. I treni vengono spostati, e uno scaffale stampato risulterebbe sbagliato. Il QR mostra sempre la posizione attuale.",
+    "reference": "Rif",
+    "quantity": "{count, plural, one {# pneumatico} other {# pneumatici}}",
+    "withRims": "Su cerchi",
+    "tpms": "TPMS",
+    "studded": "Chiodati",
+    "unassigned": "Non assegnato",
+    "preview": "Anteprima",
+    "previewScale": "Mostrata a dimensione di stampa. Il codice si scansiona anche dallo schermo.",
+    "previewFailed": "Impossibile creare l’anteprima"
+  },
+  "vehicle": {
+    "oneOnShelf": "in {code}",
+    "inStorage": "in hotel"
+  },
+  "wear": {
+    "summary": "{value} di usura tra il {from} e il {to}",
+    "perSeason": "Circa {value} a stagione",
+    "seasonsLeft": "{count, plural, =0 {Già al limite} one {Circa una stagione ancora} other {Circa # stagioni ancora}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Modifica nelle impostazioni",
+    "prepPrices": "Prezzi della preparazione",
+    "storagePrices": "Prezzi del deposito",
+    "treadLimits": "Modifica il limite",
+    "defaultCapacity": "Capacità predefinita"
+  },
+  "forecast": {
+    "title": "La prossima stagione",
+    "subtitle": "Ciò che i treni in deposito risultano dover sostituire, per misura.",
+    "backToList": "Torna ai treni",
+    "tires": "Pneumatici previsti",
+    "atLimit": "Già al limite",
+    "expected": "Previsto entro una stagione",
+    "orderTitle": "Per misura",
+    "sizes": "{count} misure",
+    "empty": "Nulla misurato vicino al limite finora. Servono due visite per ricavare un ritmo di usura.",
+    "nowCount": "{count, plural, one {# treno al limite} other {# treni al limite}}",
+    "nextCount": "{count, plural, one {# treno entro una stagione} other {# treni entro una stagione}}",
+    "tiresShort": "pneumatici",
+    "lowest": "minimo {value}",
+    "now": "Al limite",
+    "next": "Entro una stagione",
+    "rate": "{value} a stagione",
+    "caveat": "I treni al limite sono misurati. Il resto è estrapolato dall’usura di un cliente su un intervallo: utile per pianificare, non per ordinare.",
+    "unmeasured": "{count, plural, one {# treno ha poco storico e non viene conteggiato} other {# treni hanno poco storico e non vengono conteggiati}}",
+    "capped": "Letti i {shown} treni più recenti su {total}."
+  },
+  "invoiceLine": {
+    "storage": "Deposito pneumatici",
+    "pieces": "pz",
+    "fromDate": "dal {date}"
+  },
+  "setNames": {
+    "winter": "pneumatici invernali",
+    "summer": "pneumatici estivi",
+    "all_season": "pneumatici quattro stagioni",
+    "other": "pneumatici"
+  },
+  "jobNotes": {
+    "set": "Treno di pneumatici {reference}",
+    "shelf": "Scaffale: {shelf}",
+    "notOnShelf": "non a scaffale",
+    "onRims": "Su cerchi",
+    "tiresOnly": "Solo pneumatici",
+    "tpms": "Con sensori TPMS"
+  },
+  "files": {
+    "title": "Foto e file",
+    "add": "Aggiungi file",
+    "none": "Ancora nulla in allegato. Fotografa un cerchio segnato mentre hai gli pneumatici davanti.",
+    "captionPlaceholder": "Che cosa mostra?",
+    "onInvoice": "In fattura",
+    "invoiceHint": "I file marcati per la fattura vengono copiati sull’ordine o sulla fattura quando il treno viene fatturato.",
+    "deleteTitle": "Rimuovere questo file?",
+    "deleteBody": "{name} viene rimosso dal treno. Ciò che è già stato copiato su una fattura resta lì.",
+    "failed": "Impossibile salvare",
+    "someFailed": "{count, plural, one {# file non è stato caricato} other {# file non sono stati caricati}}"
+  }
+}

+ 28 - 1
messages/lt/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Išsiųsta suplanuota žinutė",
     "scheduled_message_send_now": "Išsiųsta suplanuota žinutė",
     "settings_applyTaxRate": "Pritaikytas mokesčio tarifas",
     "settings_applyTaxRate": "Pritaikytas mokesčio tarifas",
     "settings_convertToExclusive": "Kainos konvertuotos be mokesčio",
     "settings_convertToExclusive": "Kainos konvertuotos be mokesčio",
-    "settings_convertToInclusive": "Kainos konvertuotos su mokesčiu"
+    "settings_convertToInclusive": "Kainos konvertuotos su mokesčiu",
+    "organization_sampleDataRemove": "Pašalinti pavyzdiniai duomenys",
+    "tire_warehouse_create": "Sukurta padangų saugykla",
+    "tire_warehouse_update": "Atnaujinta padangų saugykla",
+    "tire_warehouse_delete": "Ištrinta padangų saugykla",
+    "tire_warehouse_archive": "Archyvuota padangų saugykla",
+    "tire_location_create": "Sukurta padangų lentyna",
+    "tire_location_bulk_create": "Sukurtos padangų lentynos",
+    "tire_location_update": "Atnaujinta padangų lentyna",
+    "tire_location_delete": "Ištrinta padangų lentyna",
+    "tire_set_check_in": "Priimtas padangų komplektas",
+    "tire_set_check_out": "Atiduotas padangų komplektas",
+    "tire_set_relocate": "Perkeltas padangų komplektas",
+    "tire_set_update": "Atnaujintas padangų komplektas",
+    "tire_set_delete": "Ištrintas padangų komplektas",
+    "tire_measurement_create": "Užfiksuoti padangų matavimai",
+    "tire_treatment_update": "Atnaujintas paruošimas",
+    "tire_treatment_mark": "Pažymėtas paruošimas",
+    "tire_treatment_complete_all": "Paruošimas baigtas",
+    "tire_set_message_customer": "Klientui išsiųsta žinutė apie padangų komplektą",
+    "tire_set_create_quote": "Sukurtas pasiūlymas iš padangų komplekto",
+    "tire_set_create_work_order": "Sukurtas darbo užsakymas iš padangų komplekto",
+    "tire_set_add_to_work_order": "Padangų komplektas pridėtas prie darbo užsakymo",
+    "tire_set_unlink_work_order": "Padangų komplektas atsietas nuo darbo užsakymo",
+    "tire_set_return": "Komplektas vėl saugomas",
+    "tire_set_dispose": "Padangų komplektas nurašytas",
+    "tire_set_attach": "Prie padangų komplekto pridėta failų",
+    "tire_set_detach": "Failas pašalintas iš padangų komplekto"
   }
   }
 }
 }

+ 6 - 2
messages/lt/common.json

@@ -10,7 +10,8 @@
     "back": "Atgal",
     "back": "Atgal",
     "confirm": "Patvirtinti",
     "confirm": "Patvirtinti",
     "edit": "Redaguoti",
     "edit": "Redaguoti",
-    "add": "Pridėti"
+    "add": "Pridėti",
+    "gotIt": "Supratau"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Patarimas: spustelėkite eilutę dešiniuoju mygtuku veiksmams",
     "hint": "Patarimas: spustelėkite eilutę dešiniuoju mygtuku veiksmams",
@@ -75,6 +76,9 @@
     "enterManually": "Įvesti ranka",
     "enterManually": "Įvesti ranka",
     "enterBarcode": "Įveskite brūkšninį kodą…",
     "enterBarcode": "Įveskite brūkšninį kodą…",
     "scanHint": "Nukreipkite kamerą į brūkšninį kodą arba įveskite jį ranka",
     "scanHint": "Nukreipkite kamerą į brūkšninį kodą arba įveskite jį ranka",
-    "iosInstallHint": "Palieskite Bendrinti > Įtraukti į pradžios ekraną"
+    "iosInstallHint": "Palieskite Bendrinti > Įtraukti į pradžios ekraną",
+    "installDescription": "Įtraukite į pradžios ekraną, kad pasiektumėte greičiau",
+    "enterFullscreen": "Visas ekranas",
+    "exitFullscreen": "Išjungti visą ekraną"
   }
   }
 }
 }

+ 12 - 1
messages/lt/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Neseniai užbaigti",
     "recentCompleted": "Neseniai užbaigti",
     "activeJobs": "Aktyvūs darbai",
     "activeJobs": "Aktyvūs darbai",
     "recentObservations": "Naujausi pastebėjimai",
     "recentObservations": "Naujausi pastebėjimai",
-    "recentActivity": "Naujausia veikla"
+    "recentActivity": "Naujausia veikla",
+    "gettingStarted": "Pradžia",
+    "tireHotel": "Padangų viešbutis"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Numatoma priežiūra",
     "title": "Numatoma priežiūra",
@@ -233,5 +235,14 @@
       "expired": "Nebegalioja",
       "expired": "Nebegalioja",
       "converted": "Konvertuota"
       "converted": "Konvertuota"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Padangų viešbutis",
+    "viewAll": "Žiūrėti padangų viešbutį",
+    "stored": "Saugoma komplektų: {count}",
+    "capacity": "{used} / {capacity} padangų",
+    "roomLeft": "Vietos dar {count}",
+    "needsPrep": "Reikia paruošti",
+    "unbilled": "Neapmokestinta ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/lt/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "Padangų viešbutis įjungtas",
+    "body": "Saugokite sezonines padangas. Sukurkite lentynas ir priimkite komplektą."
+  },
+  "telegram": {
+    "title": "Telegram įjungtas",
+    "body": "Pokalbiai su klientais atsiduria čia. Atsakykite neišeidami iš programos."
+  }
+}

+ 1 - 0
messages/lt/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Transporto priemonė",
     "vehicle": "Transporto priemonė",
     "template": "Šablonas",
     "template": "Šablonas",
     "progress": "Eiga",
     "progress": "Eiga",
+    "progressTooltip": "Įvertinta {graded} iš {total} punktų",
     "status": "Būsena",
     "status": "Būsena",
     "date": "Data",
     "date": "Data",
     "statusInProgress": "Vyksta",
     "statusInProgress": "Vyksta",

+ 8 - 2
messages/lt/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Visi priminimai",
     "allReminders": "Visi priminimai",
     "auditLog": "Audito žurnalas",
     "auditLog": "Audito žurnalas",
     "aiAssistant": "DI asistentas",
     "aiAssistant": "DI asistentas",
-    "observations": "Pastebėjimai"
+    "observations": "Pastebėjimai",
+    "tireHotel": "Padangų viešbutis",
+    "allTireSets": "Visi komplektai",
+    "tireStorage": "Saugyklos išdėstymas",
+    "tireSetDetails": "Padangų komplektas",
+    "tireForecast": "Kitas sezonas"
   },
   },
   "search": "Ieškoti...",
   "search": "Ieškoti...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Atsijungti",
     "signOut": "Atsijungti",
     "more": "Daugiau",
     "more": "Daugiau",
     "auditLog": "Audito žurnalas",
     "auditLog": "Audito žurnalas",
-    "aiAssistant": "DI asistentas"
+    "aiAssistant": "DI asistentas",
+    "tireHotel": "Padangų viešbutis"
   }
   }
 }
 }

+ 91 - 0
messages/lt/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Sukurkite savo dirbtuves",
+    "subtitle": "Įveskite dirbtuvių pavadinimą, kad pradėtumėte",
+    "workshopName": "Dirbtuvių pavadinimas",
+    "workshopNamePlaceholder": "Mano autoservisas",
+    "submit": "Pradėti",
+    "sampleDataLabel": "Įkelti pavyzdinius duomenis",
+    "sampleDataDescription": "Prideda kelis pavyzdinius klientus, automobilius ir darbus, kad galėtumėte apsižvalgyti. Juos galėsite pašalinti vienu paspaudimu.",
+    "setupFailed": "Nustatymas nepavyko",
+    "couldNotCreate": "Nepavyko sukurti dirbtuvių",
+    "unexpectedError": "Įvyko netikėta klaida"
+  },
+  "sample": {
+    "prefix": "Pavyzdys:",
+    "oilService": "Alyvos ir filtro keitimas",
+    "brakeJob": "Priekinių stabdžių kaladėlių keitimas",
+    "annualService": "Metinė techninė priežiūra",
+    "quoteTitle": "Priekinės pakabos atnaujinimas",
+    "inspectionNote": "Kairė priekinė padanga arti nusidėvėjimo ribos",
+    "parts": {
+      "oilFilter": "Alyvos filtras",
+      "engineOil": "Variklio alyva 5W-30",
+      "brakePads": "Priekinių stabdžių kaladėlių komplektas",
+      "shockAbsorber": "Amortizatorius"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Alyvos keitimas",
+      "description": "Variklio alyvos ir filtro keitimas"
+    },
+    "frontBrakePads": {
+      "name": "Priekinės stabdžių kaladėlės",
+      "description": "Pakeisti priekines stabdžių kaladėles"
+    },
+    "tireSwap": {
+      "name": "Ratų keitimas",
+      "description": "Sezoninis ratų keitimas su užveržimo momento patikra"
+    },
+    "batteryReplacement": {
+      "name": "Akumuliatoriaus keitimas",
+      "description": "Pakeisti ir užregistruoti paleidimo akumuliatorių"
+    },
+    "diagnostics": {
+      "name": "Diagnostika",
+      "description": "Klaidų kodų nuskaitymas ir pirminė diagnozė"
+    },
+    "annualService": {
+      "name": "Metinė techninė priežiūra",
+      "description": "Pilna metinė priežiūra pagal grafiką"
+    }
+  },
+  "checklist": {
+    "title": "Pradžia",
+    "description": "Keturi žingsniai, kad dirbtuvės pradėtų veikti",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Pridėkite klientą",
+        "description": "Sukurkite pirmą kliento kortelę"
+      },
+      "vehicle": {
+        "title": "Pridėkite automobilį",
+        "description": "Užregistruokite kliento automobilį"
+      },
+      "workOrder": {
+        "title": "Sukurkite darbo užsakymą",
+        "description": "Atidarykite pirmą savo darbą"
+      },
+      "invoice": {
+        "title": "Išsiųskite arba atsisiųskite sąskaitą",
+        "description": "Pasidalinkite sąskaita su klientu arba atsisiųskite ją PDF formatu"
+      }
+    },
+    "allDoneTitle": "Viskas paruošta",
+    "allDoneDescription": "Visi žingsniai atlikti ir jūsų dirbtuvės veikia.",
+    "dismiss": "Slėpti kortelę",
+    "dismissAriaLabel": "Slėpti šį sąrašą"
+  },
+  "sampleData": {
+    "title": "Pavyzdiniai duomenys",
+    "description": "Jūsų dirbtuvėse vis dar yra pavyzdiniai klientai, automobiliai ir darbai iš pradinio nustatymo. Pašalinus ištrinami būtent šie įrašai ir nieko daugiau.",
+    "footerNote": "Įkelti pavyzdiniai duomenys",
+    "remove": "Pašalinti pavyzdinius duomenis",
+    "removeConfirmTitle": "Pašalinti pavyzdinius duomenis?",
+    "removeConfirmDescription": "Bus ištrinti pavyzdiniai klientai, automobiliai, darbo užsakymai, pasiūlymas ir apžiūra, sukurti per pradinį nustatymą. Jūsų pačių duomenys nebus paliesti.",
+    "removed": "Pavyzdiniai duomenys pašalinti",
+    "removeFailed": "Nepavyko pašalinti pavyzdinių duomenų"
+  }
+}

+ 27 - 0
messages/lt/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Skydelis",
+    "vehicles": "Transporto priemonės",
+    "customers": "Klientai",
+    "work_orders": "Darbo užsakymai",
+    "quotes": "Pasiūlymai",
+    "services": "Serviso įrašai",
+    "billing": "Atsiskaitymai",
+    "inventory": "Sandėlis",
+    "labor_presets": "Darbų šablonai",
+    "inspections": "Patikros",
+    "tire_hotel": "Padangų viešbutis",
+    "reports": "Ataskaitos",
+    "work_board": "Darbų lenta",
+    "ai_assistant": "DI asistentas",
+    "settings": "Nustatymai"
+  },
+  "actions": {
+    "create": "Kurti",
+    "read": "Peržiūrėti",
+    "update": "Redaguoti",
+    "delete": "Šalinti",
+    "manage": "Valdyti",
+    "manage_storage": "Valdyti saugyklos išdėstymą"
+  }
+}

+ 17 - 2
messages/lt/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} gerai",
     "good": "{count} gerai",
     "fair": "{count} vidutiniškai",
     "fair": "{count} vidutiniškai",
     "poor": "{count} blogai",
     "poor": "{count} blogai",
-    "view": "Peržiūrėti"
+    "view": "Peržiūrėti",
+    "serviceHistoryTitle": "Aptarnavimo istorija",
+    "inspectionsTitle": "Apžiūros",
+    "columnVehicle": "Transporto priemonė",
+    "columnPlate": "Valst. nr.",
+    "columnServices": "Aptarnavimai",
+    "columnInspections": "Apžiūros"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Užsakyti paslaugą",
     "title": "Užsakyti paslaugą",
@@ -174,6 +180,15 @@
     "yourRequests": "Jūsų paslaugų užklausos",
     "yourRequests": "Jūsų paslaugų užklausos",
     "failedToLoad": "Nepavyko įkelti duomenų.",
     "failedToLoad": "Nepavyko įkelti duomenų.",
     "staffNotes": "Personalo pastabos: {notes}",
     "staffNotes": "Personalo pastabos: {notes}",
-    "preferred": "Pageidaujama: {date}"
+    "preferred": "Pageidaujama: {date}",
+    "notListedOption": "Mano transporto priemonės sąraše nėra",
+    "newVehicleHint": "Papasakokite apie transporto priemonę – pridėsime ją prie jūsų profilio.",
+    "fillVehicleDetails": "Įveskite transporto priemonės markę ir modelį",
+    "makeLabel": "Markė",
+    "makePlaceholder": "pvz., Ford",
+    "modelLabel": "Modelis",
+    "modelPlaceholder": "pvz., Kuga",
+    "yearLabel": "Metai",
+    "plateLabel": "Valstybinis numeris"
   }
   }
 }
 }

+ 9 - 0
messages/lt/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Paieška",
+  "description": "Ieškokite transporto priemonių, klientų, darbų ir nustatymų",
+  "placeholder": "Ieškoti pagal vardą, numerį, telefoną, VIN, sąskaitą…",
+  "noResults": "Rezultatų nėra.",
+  "recentCustomers": "Naujausi klientai",
+  "services": "Serviso įrašai",
+  "toClose": "kad uždarytumėte"
+}

+ 47 - 1
messages/lt/settings.json

@@ -103,6 +103,10 @@
         "title": "Duomenys",
         "title": "Duomenys",
         "description": "Eksportas ir importas atsarginei kopijai"
         "description": "Eksportas ir importas atsarginei kopijai"
       },
       },
+      "install": {
+        "title": "Įdiegti programą",
+        "description": "Pridėkite „Torqvoice“ į šį įrenginį"
+      },
       "license": {
       "license": {
         "title": "Licencija",
         "title": "Licencija",
         "description": "\"White-label\" licencija"
         "description": "\"White-label\" licencija"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Pagalba",
         "title": "Pagalba",
         "description": "Susisiekti su pagalba ir tvarkyti mygtuką"
         "description": "Susisiekti su pagalba ir tvarkyti mygtuką"
+      },
+      "tireHotel": {
+        "title": "Padangų viešbutis",
+        "description": "Sezoninis padangų saugojimas"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Paskutinė atsarginė kopija:",
       "lastBackup": "Paskutinė atsarginė kopija:",
       "supportPrompt": "Jei turite klausimų apie jūsų duomenų apsaugą,",
       "supportPrompt": "Jei turite klausimų apie jūsų duomenų apsaugą,",
       "supportAction": "susisiekite su mūsų pagalbos komanda."
       "supportAction": "susisiekite su mūsų pagalbos komanda."
-    }
+    },
+    "optTireHotel": "Padangų viešbutis",
+    "optTireHotelDesc": "Saugomi komplektai, lentynos, paruošimas ir saugojimo sutartys"
   },
   },
   "license": {
   "license": {
     "title": "\"White-Label\" licencija",
     "title": "\"White-Label\" licencija",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Susisiekti su pagalba",
     "contactTitle": "Susisiekti su pagalba",
     "contactHint": "Iš karto atidaryti pagalbos formą.",
     "contactHint": "Iš karto atidaryti pagalbos formą.",
     "contactAction": "Atidaryti formą"
     "contactAction": "Atidaryti formą"
+  },
+  "install": {
+    "title": "Įdiegti „Torqvoice“",
+    "description": "Įdiekite „Torqvoice“ šiame įrenginyje, kad atidarytumėte ją iš pradžios ekrano visame ekrane ir be naršyklės juostos. Tai ta pati programa ir tie patys duomenys.",
+    "installed": "„Torqvoice“ įdiegta šiame įrenginyje.",
+    "iosHint": "„Safari“ palieskite bendrinimo mygtuką, tada „Į pradžios ekraną“.",
+    "unavailable": "Ši naršyklė nepasiūlė įdiegimo. Atidarykite naršyklės meniu ir ieškokite „Įdiegti programą“ arba „Į pradžios ekraną“.",
+    "bannerHidden": "Įdiegimo juosta šiame įrenginyje paslėpta.",
+    "showBanner": "Vėl rodyti juostą"
+  },
+  "tireHotel": {
+    "title": "Padangų viešbutis",
+    "description": "Saugokite klientų sezonines padangas ir žinokite, kur guli kiekvienas komplektas.",
+    "cardTitle": "Padangų saugojimas",
+    "enable": "Įjungti padangų viešbutį",
+    "enableHint": "Prideda padangų saugojimą į meniu. Vėliau išjungus, jis paslepiamas nieko neištrinant.",
+    "defaultCapacity": "Numatytas padangų skaičius lentynoje",
+    "defaultCapacityHint": "Užpildoma kuriant naujas lentynas. Galima keisti kiekvienai lentynai.",
+    "warnPercent": "Įspėti užsipildžius (%)",
+    "warnPercentHint": "Pažymi saugyklos apžvalgą, kai užimta tokia bendros talpos dalis.",
+    "treadTitle": "Keitimo ribos",
+    "treadHint": "Teisiniai minimumai skiriasi pagal šalį, tad nustatykite savo rinkos ribas. Protektorius žemiau ribos žymimas keitimui.",
+    "summerLimit": "Vasaros riba ({unit})",
+    "winterLimit": "Žiemos riba ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "Toliau sutvarkykite lentynas.",
+    "setupLink": "Atverti saugyklos išdėstymą",
+    "save": "Išsaugoti padangų viešbučio nustatymus",
+    "saved": "Padangų viešbučio nustatymai išsaugoti",
+    "billingTitle": "Saugojimo apmokėjimas",
+    "billingHint": "Įrašoma į saugojimo eilutę apmokestinant komplektą. Sąskaitoje galima keisti.",
+    "seasonalPrice": "Numatytoji saugojimo kaina",
+    "prepTitle": "Paruošimo kainos",
+    "prepHint": "Kiek kainuoja kiekvienas paruošimo darbas. Palikus tuščią, jis niekada neapmokestinamas, o to ir reikia dirbtuvėms, kurios plovimą įskaičiuoja į saugojimą. Turintys kainą patys atsiranda pasiūlyme ir darbo užsakyme.",
+    "prepNotCharged": "neapmokestinama"
   }
   }
 }
 }

+ 473 - 0
messages/lt/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Padangų viešbutis",
+  "description": "Sezoninis klientų padangų saugojimas",
+  "common": {
+    "cancel": "Atšaukti",
+    "save": "Išsaugoti",
+    "create": "Sukurti",
+    "delete": "Ištrinti",
+    "edit": "Redaguoti"
+  },
+  "seasons": {
+    "summer": "Vasarinės",
+    "winter": "Žieminės",
+    "all_season": "Visasezonės",
+    "other": "Kita"
+  },
+  "statuses": {
+    "stored": "Saugykloje",
+    "released": "Atiduotos",
+    "disposed": "Utilizuotos"
+  },
+  "conditions": {
+    "good": "Gera",
+    "fair": "Stebėti",
+    "replace": "Keisti"
+  },
+  "positions": {
+    "front_left": "Priekinė kairė",
+    "front_right": "Priekinė dešinė",
+    "rear_left": "Galinė kairė",
+    "rear_right": "Galinė dešinė",
+    "spare": "Atsarginis ratas",
+    "unspecified": "Nenurodyta"
+  },
+  "capacity": {
+    "used": "{used} iš {capacity} padangų",
+    "free": "Vietos {count}",
+    "freeShort": "{count} laisvos",
+    "over": "{count} virš talpos",
+    "meterLabel": "Saugoma {used} iš {capacity} padangų"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "1/32 colio",
+    "inputLabel": "Protektoriaus gylis, {position}",
+    "lastTime": "buvo {value}",
+    "worn": "nusidėvėjo {value}"
+  },
+  "list": {
+    "tabAll": "Visos",
+    "tabStored": "Saugykloje",
+    "tabReleased": "Atiduotos",
+    "search": "Ieškoti pagal numerį, klientą, markę ar lentyną",
+    "checkIn": "Priimti padangas",
+    "empty": "Padangų komplektų dar nėra. Priimkite komplektą, kad pradėtumėte.",
+    "unassigned": "Nepriskirta",
+    "reference": "Nr.",
+    "vehicle": "Transporto priemonė",
+    "tires": "Padangos",
+    "location": "Lentyna",
+    "condition": "Būklė",
+    "status": "Statusas",
+    "storedSince": "Nuo",
+    "tireCount": "{count, plural, one {# padanga} few {# padangos} many {# padangos} other {# padangų}}",
+    "studded": "Su dygliais",
+    "roomLeft": "Vietos {count}",
+    "prep": "Paruoš.",
+    "tabNeedsPrep": "Reikia paruošti",
+    "setUpShelves": "Sukurti lentynas",
+    "emptyNoShelves": "Lentynų dar nėra. Sutvarkykite saugyklą prieš priimdami padangas.",
+    "tabDisposed": "Nurašyti"
+  },
+  "checkIn": {
+    "title": "Priimti padangas",
+    "description": "Užfiksuokite, kas atvyko, ir pasirinkite, kur padėti.",
+    "customer": "Klientas",
+    "selectCustomer": "Pasirinkti klientą",
+    "noCustomer": "Be kliento",
+    "vehicle": "Transporto priemonė",
+    "selectVehicle": "Pasirinkti transporto priemonę",
+    "season": "Sezonas",
+    "quantity": "Padangų skaičius",
+    "quantityHint": "Skaičiuojamos po vieną, tad tinka ir pora, ir penki ratai.",
+    "brand": "Markė",
+    "brandPlaceholder": "Michelin, Bridgestone, Nokian",
+    "model": "Modelis",
+    "size": "Matmenys",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "DOT kodas",
+    "dotPlaceholder": "3623",
+    "dotHint": "Padangos pagaminimo savaitė ir metai.",
+    "withRims": "Ant ratlankių",
+    "hasTpms": "TPMS jutikliai",
+    "studded": "Su dygliais",
+    "location": "Kur bus padėtos padangos",
+    "choosePlace": "Pasirinkite lentyną",
+    "searchLocations": "Ieškoti lentynų",
+    "noLocations": "Lentynų nerasta",
+    "tooFull": "Nepakanka vietos",
+    "pickLocationFirst": "Prieš priimdami komplektą pasirinkite lentyną",
+    "roomSummary": "Tinka {locations} lentynų, laisva {tires} vietų",
+    "nothingFits": "Nė vienoje lentynoje nėra vietos {quantity} padangoms. Atlaisvinkite vietos arba pridėkite lentyną.",
+    "treadTitle": "Protektoriaus gylis priimant",
+    "treadHint": "Neprivaloma, bet vėliau išsprendžia bet kokį ginčą dėl būklės.",
+    "notes": "Pastabos",
+    "submit": "Priimti",
+    "success": "Komplektas {reference} padėtas į {code}",
+    "failed": "Nepavyko priimti komplekto",
+    "noVehiclesForCustomer": "Šis klientas dar neturi transporto priemonių. Pirmiausia pridėkite ją kliento puslapyje.",
+    "treatmentsHint": "Ką reikia padaryti su šiuo komplektu prieš saugojimą ar montavimą.",
+    "storedBefore": "Jau buvo saugoma čia",
+    "storedBeforeHint": "Grįžta tos pačios padangos? Pasirinkite komplektą, kad sezonas prisidėtų prie jo istorijos.",
+    "sameSet": "Tas pats komplektas kaip anksčiau",
+    "pieces": "{count} vnt.",
+    "lastOut": "Atsiimta {date}",
+    "notStored": "Šiuo metu nesaugoma",
+    "returned": "Komplektas {reference} vėl ant {code}",
+    "submitReturn": "Saugoti vėl",
+    "ownerFromVehicle": "Paimta iš transporto priemonės",
+    "vehicleOwner": "Transporto priemonės savininkas",
+    "clearVehicle": "Išvalyti transporto priemonę"
+  },
+  "storage": {
+    "title": "Lentynos",
+    "description": "Sutvarkykite saugyklą kartą ir iškart matykite, kas kur telpa.",
+    "addWarehouse": "Pridėti saugyklą",
+    "addShelves": "Pridėti lentynų",
+    "warehouseName": "Pavadinimas",
+    "warehouseNamePlaceholder": "Pagrindinis pastatas, Konteineris 2, Sandėliukas",
+    "warehouseAddress": "Adresas",
+    "warehouseCreated": "Saugykla pridėta",
+    "warehouseDeleted": "{name} ištrinta",
+    "warehouseArchived": "{name} archyvuota",
+    "deleteWarehouse": "Ištrinti saugyklą",
+    "deleteWarehouseTitle": "Ištrinti {name}?",
+    "deleteWarehouseBody": "Bus pašalinta saugykla ir visos jos lentynos.",
+    "deleteWarehouseBodyOccupied": "Šioje saugykloje tebėra {count} padangų, todėl ji bus archyvuota, o ne ištrinta.",
+    "totalCapacity": "Bendra talpa",
+    "totalStored": "Dabar saugoma",
+    "totalFree": "Laisva vieta",
+    "totalLocations": "Lentynos",
+    "locationSummary": "Naudojama {occupied} iš {total} lentynų",
+    "noLocations": "Lentynų dar nėra. Pridėkite stelažą, kad čia saugotumėte padangas.",
+    "emptyTitle": "Saugykla dar nesukonfigūruota",
+    "emptyBody": "Pridėkite pirmą pastatą ar patalpą ir užpildykite ją lentynomis.",
+    "default": "Numatytoji",
+    "addLocation": "Pridėti lentynų",
+    "editLocation": "Redaguoti lentyną",
+    "createLocation": "Sukurti lentyną",
+    "createShelves": "Sukurti lentynas",
+    "locationDialogDescription": "Pridedama vietos į {warehouse}.",
+    "tabBulk": "Visas stelažas",
+    "tabSingle": "Viena lentyna",
+    "zone": "Zona",
+    "zonePlaceholder": "A, Patalpa 1, Šiaurė",
+    "rack": "Stelažas",
+    "rackPlaceholder": "04",
+    "shelf": "Lentyna",
+    "shelfPlaceholder": "2",
+    "position": "Pozicija",
+    "positionPlaceholder": "Kairė",
+    "shelfFrom": "Lentynos nuo",
+    "shelfTo": "iki",
+    "code": "Kodas",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Nurodomas etiketėje. Užpildomas automatiškai, jei paliksite tuščią.",
+    "capacity": "Padangų lentynoje",
+    "capacityHint": "Kiek atskirų padangų čia telpa.",
+    "notes": "Pastabos",
+    "bulkPreview": "Sukurs {count} lentynų, nuo {first} iki {last}",
+    "bulkPreviewCapacity": "Iš viso vietos {tires} padangoms",
+    "bulkCreated": "Sukurta {count} lentynų",
+    "bulkCreatedWithSkips": "Sukurta {created} lentynų, praleista {skipped} jau esamų",
+    "bulkNothingCreated": "Tokios lentynos jau yra",
+    "locationCreated": "Lentyna pridėta",
+    "locationUpdated": "Lentyna atnaujinta",
+    "locationDeleted": "{code} ištrinta",
+    "deleteLocationTitle": "Ištrinti {code}?",
+    "deleteLocationBody": "Pirmiausia lentyna turi būti tuščia.",
+    "saveFailed": "Nepavyko išsaugoti",
+    "deleteFailed": "Nepavyko ištrinti"
+  },
+  "movements": {
+    "check_in": "Priimta",
+    "check_out": "Atiduota",
+    "relocate": "Perkelta",
+    "dispose": "Utilizuota",
+    "measure": "Išmatuota"
+  },
+  "detail": {
+    "move": "Perkelti",
+    "checkOut": "Atiduoti",
+    "conditionTitle": "Būklė",
+    "noMeasurements": "Matavimų dar nėra.",
+    "historyTitle": "Istorija",
+    "storageTitle": "Saugojimas",
+    "shelf": "Lentyna",
+    "warehouse": "Saugykla",
+    "notStored": "Šiuo metu nesaugoma.",
+    "checkedIn": "Priimta",
+    "checkedOut": "Atiduota",
+    "tiresTitle": "Padangos",
+    "loadSpeed": "Apkrovos ir greičio indeksai",
+    "ownerTitle": "Savininkas",
+    "phone": "Telefonas",
+    "deleteTitle": "Ištrinti šį padangų komplektą?",
+    "deleteBody": "Komplektas ir visa jo istorija bus pašalinti. Atšaukti negalėsite.",
+    "deleted": "Padangų komplektas ištrintas",
+    "deleteFailed": "Nepavyko ištrinti komplekto",
+    "belowLimit": "{count} padanga (-os) ties keitimo riba arba žemiau.",
+    "belowLimitHint": "Verta pasiūlyti klientui kainą, kol padangos dar čia.",
+    "dispose": "Nurašyti",
+    "disposeTitle": "Nurašyti šį komplektą?",
+    "disposeBody": "Įrašas ir istorija lieka, vieta lentynoje atlaisvinama. Kitą sezoną jis nebebus siūlomas kaip tos pačios padangos.",
+    "disposed": "Komplektas nurašytas",
+    "disposeFailed": "Nepavyko nurašyti komplekto"
+  },
+  "checkOut": {
+    "title": "Atiduoti padangas",
+    "description": "Komplektas nuimamas nuo {code}.",
+    "descriptionNoLocation": "Šis komplektas grąžinamas klientui.",
+    "treadTitle": "Protektoriaus gylis atiduodant",
+    "note": "Pastaba",
+    "submit": "Atiduoti",
+    "success": "Komplektas {reference} atiduotas",
+    "failed": "Nepavyko atiduoti komplekto",
+    "prepPending": "Liko {count} paruošimo darbas (-ai)",
+    "conditionTitle": "Būklė priimant",
+    "conditionLead": {
+      "replace": "Viena ar daugiau padangų yra ties keitimo riba arba žemiau. Verta pasakyti klientui, kol jis neišvažiavo.",
+      "fair": "Viena ar daugiau padangų artėja prie keitimo ribos. Verta paminėti prieš kitą sezoną."
+    },
+    "treadHintUnmeasured": "Priimant matavimas nebuvo atliktas. Tai paskutinė proga jį užfiksuoti."
+  },
+  "relocate": {
+    "title": "Perkelti padangas",
+    "description": "Pasirinkite lentyną, į kurią perkeliama.",
+    "newLocation": "Nauja lentyna",
+    "note": "Pastaba",
+    "submit": "Perkelti",
+    "success": "Komplektas {reference} perkeltas",
+    "failed": "Nepavyko perkelti komplekto"
+  },
+  "edit": {
+    "title": "Redaguoti padangų komplektą",
+    "description": "Pataisykite tai, kas užfiksuota priimant. Lentyną keiskite per „Perkelti“.",
+    "quantityHint": "Padidinimas tikrinamas pagal lentyną, kurioje guli komplektas.",
+    "rimType": "Ratlankio tipas",
+    "rimTypePlaceholder": "Lydinio, plieno",
+    "success": "Padangų komplektas atnaujintas",
+    "failed": "Nepavyko išsaugoti pakeitimų",
+    "treadHint": "Pakeistas matavimas įrašomas į istoriją kaip naujas, o ne perrašo ankstesnį."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Nuplauti padangas",
+      "wash_rims": "Nuplauti ratlankius",
+      "balance": "Balansuoti",
+      "tpms_service": "TPMS aptarnavimas",
+      "new_valves": "Nauji ventiliai",
+      "repair": "Remontas"
+    },
+    "statuses": {
+      "pending": "Reikia atlikti",
+      "done": "Atlikta",
+      "skipped": "Praleista"
+    },
+    "title": "Paruošimas",
+    "none": "Su šiuo komplektu nieko daryti nereikia.",
+    "add": "Pridėti paruošimo darbą",
+    "pickHint": "Pažymėkite, ko šiam komplektui reikia prieš grąžinant ant automobilio.",
+    "pendingCount": "Liko {count}",
+    "allComplete": "Viskas atlikta",
+    "ready": "Paruošta",
+    "markDone": "Atlikta",
+    "completeAll": "Pažymėti visus {count} kaip atliktus",
+    "allDone": "Paruošimas baigtas",
+    "undo": "Grąžinti į sąrašą",
+    "saveFailed": "Nepavyko išsaugoti",
+    "skip": "Praleisti"
+  },
+  "messaging": {
+    "action": "Žinutė",
+    "title": "Parašyti klientui",
+    "description": "Kam: {name}.",
+    "noCustomer": "Šis komplektas neturi kliento.",
+    "noCustomerBody": "Prieš rašydami susiekite komplektą su klientu.",
+    "channel": "Siųsti per",
+    "channels": {
+      "email": "El. paštas",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "adreso nėra",
+    "recipient": "Siųsti į",
+    "recipientPlaceholder": "Telefono numeris",
+    "subject": "Tema",
+    "message": "Žinutė",
+    "send": "Siųsti",
+    "sent": "Žinutė išsiųsta",
+    "failed": "Nepavyko išsiųsti žinutės",
+    "smsLong": "Ilgos žinutės gali būti suskaidytos į kelias SMS.",
+    "subjects": {
+      "low_tread": "Jūsų {season} padangos",
+      "damage": "Jūsų {season} padangos",
+      "stored": "Jūsų {season} padangos saugomos",
+      "custom": "Jūsų {season} padangos"
+    },
+    "bodies": {
+      "low_tread": "Sveiki, {customer_name}. Jūsų {season} padangos saugomos pas mus, {shop_name}. Priimant {positions} rodė {tread}, tai žemiau ribos. Dar vieno sezono jos neištvers. Paskaičiuoti naujo komplekto kainą?",
+      "damage": "Sveiki, {customer_name}. Jūsų {season} padangos saugomos pas mus, {shop_name}. Priimdami radome pažeidimą ties {positions}. Paskambinkite, kai bus patogu, viską kartu apžiūrėsime.",
+      "stored": "Sveiki, {customer_name}. Jūsų {season} padangos jau saugomos pas mus, {shop_name}. Pranešime, kai ateis laikas keisti.",
+      "custom": "Sveiki, {customer_name}. Dėl jūsų {season} padangų, saugomų pas mus, {shop_name}:"
+    },
+    "enableInSettings": "Dar nesukonfigūruota. Įjunkite nustatymuose."
+  },
+  "job": {
+    "cardTitle": "Pasiūlymai ir darbai",
+    "none": "Šiam komplektui dar nieko nesukurta.",
+    "quote": "Pasiūlymas",
+    "workOrder": "Darbo užsakymas",
+    "converted": "konvertuota",
+    "quoteTitle": "Pasiūlymas naujam komplektui",
+    "workOrderTitle": "Apmokestinti šį komplektą",
+    "loadingSet": "Kraunamas komplektas",
+    "tires": "Padangos",
+    "inStock": "{count} sandėlyje",
+    "shortStock": "sandėlyje tik {count}",
+    "noMatch": "Sandėlyje nieko nėra pagal {size}. Eilutė bus pridėta su jūsų nurodyta kaina.",
+    "noSize": "Šiam komplektui nenurodytas dydis, todėl sandėlio sugretinti negalima. Eilutė naudos jūsų kainą.",
+    "unitPrice": "Kaina už padangą",
+    "createQuote": "Sukurti pasiūlymą",
+    "createWorkOrder": "Sukurti darbo užsakymą",
+    "quoteCreated": "Sukurtas pasiūlymas {number}",
+    "workOrderCreated": "Sukurtas darbo užsakymas",
+    "failed": "Nepavyko sukurti",
+    "bannerTitle": "Paimti iš",
+    "openSet": "Atverti komplektą",
+    "prepPending": "Prieš montuojant liko: {jobs}",
+    "notOnShelf": "Šis komplektas nebėra saugykloje.",
+    "targetNew": "Naujas darbo užsakymas",
+    "targetNewHint": "Sukurti darbą tik šioms padangoms.",
+    "targetExisting": "Arba pridėti prie jau atviro šios transporto priemonės darbo",
+    "addToWorkOrder": "Pridėti prie darbo užsakymo",
+    "workOrderUpdated": "Pridėta prie darbo užsakymo",
+    "unlink": "Atsieti",
+    "unlinkTitle": "Atsieti šias padangas nuo darbo?",
+    "unlinkBody": "Darbas neberodys į padangų komplektą. Jau įtrauktos dalių ir darbo eilutės lieka, tad pašalinkite jas atskirai, jei jų apmokestinti nereikia.",
+    "unlinked": "Padangos atsietos",
+    "unlinkFailed": "Nepavyko atsieti padangų",
+    "prep": "Paruošimas",
+    "tiresSkipped": "Padangų eilutė nebus pridėta.",
+    "total": "Iš viso",
+    "searchStock": "Ieškoti sandėlyje pagal markę, dydį ar dalies numerį",
+    "noSearchHits": "Sandėlyje nieko neatitinka \"{query}\".",
+    "otherSize": "Kitas dydis",
+    "inSize": "{count} dydžio {size}",
+    "quantity": "Kiekis",
+    "noStockItem": "Nėra sandėlio prekės, kaina įvedama ranka",
+    "storeTires": "Padėti padangas saugoti",
+    "storage": "Saugojimas",
+    "storageFee": "Mokestis",
+    "storagePeriod": "Laikotarpis",
+    "destination": "Kur pateks",
+    "targetInvoice": "Nauja sąskaita",
+    "targetInvoiceHint": "Apmokestinti klientą tiesiogiai, be darbo užsakymo transporto priemonei.",
+    "createInvoice": "Sukurti sąskaitą",
+    "needsCustomer": "Prieš apmokestindami susiekite komplektą su klientu arba transporto priemone.",
+    "storagePeriodEnd": "Atsiimta",
+    "storageOpenEnded": "Atvira",
+    "storagePeriodHint": "Rodoma sąskaitos eilutėje, kad klientas matytų, už ką mokestis. Palikite pabaigą tuščią, kol nežinote, kada atsiims.",
+    "pricesHint": "Paruošimo kainos imamos iš nustatymų. Kiekvieną eilutę galima redaguoti ir sukūrus dokumentą.",
+    "files": "Nuotraukos ir failai",
+    "filesCount": "{count} sąskaitoje"
+  },
+  "label": {
+    "action": "Etiketės",
+    "title": "Spausdinti etiketes",
+    "description": "Lipdukas kiekvienai padangai su QR, atveriančiu šį komplektą.",
+    "format": "Etiketės dydis",
+    "formats": {
+      "dymo_small": "Maža ritė",
+      "dymo_standard": "Standartinė ritė",
+      "thermal_large": "Didelė ritė",
+      "sheet_a4": "A4 lapas"
+    },
+    "perSheet": "{count} lape",
+    "copies": "Kiek",
+    "copiesHint": "po vieną padangai ({count})",
+    "fewer": "Viena mažiau",
+    "more": "Viena daugiau",
+    "print": "Spausdinti",
+    "shelfNote": "Lentyna sąmoningai nespausdinama. Komplektai perkeliami, o atspausdinta lentyna taptų neteisinga. QR visada rodo dabartinę vietą.",
+    "reference": "Nr.",
+    "quantity": "{count, plural, one {# padanga} few {# padangos} many {# padangos} other {# padangų}}",
+    "withRims": "Ant ratlankių",
+    "tpms": "TPMS",
+    "studded": "Su dygliais",
+    "unassigned": "Nepriskirta",
+    "preview": "Peržiūra",
+    "previewScale": "Rodoma spausdinimo dydžiu. Kodą galima nuskaityti ir nuo ekrano.",
+    "previewFailed": "Nepavyko sukurti peržiūros"
+  },
+  "vehicle": {
+    "oneOnShelf": "{code} lentynoje",
+    "inStorage": "padangų viešbutyje"
+  },
+  "wear": {
+    "summary": "Nusidėvėjo {value} nuo {from} iki {to}",
+    "perSeason": "Maždaug {value} per sezoną",
+    "seasonsLeft": "{count, plural, =0 {Jau ties riba} one {Maždaug dar vienas sezonas} other {Maždaug dar # sezonai}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Keisti nustatymuose",
+    "prepPrices": "Paruošimo kainos",
+    "storagePrices": "Saugojimo kainos",
+    "treadLimits": "Keisti ribą",
+    "defaultCapacity": "Numatytoji talpa"
+  },
+  "forecast": {
+    "title": "Kitas sezonas",
+    "subtitle": "Ko, pagal matavimus, prireiks saugomiems komplektams, pagal dydį.",
+    "backToList": "Atgal į komplektus",
+    "tires": "Laukiama padangų",
+    "atLimit": "Jau ties riba",
+    "expected": "Laukiama per sezoną",
+    "orderTitle": "Pagal dydį",
+    "sizes": "{count} dydžiai",
+    "empty": "Kol kas nieko nepamatuota arti ribos. Komplektui reikia dviejų vizitų, kad būtų galima apskaičiuoti tempą.",
+    "nowCount": "{count, plural, one {# komplektas ties riba} other {# komplektai ties riba}}",
+    "nextCount": "{count, plural, one {# komplektas per sezoną} other {# komplektai per sezoną}}",
+    "tiresShort": "padangos",
+    "lowest": "mažiausia {value}",
+    "now": "Ties riba",
+    "next": "Per sezoną",
+    "rate": "{value} per sezoną",
+    "caveat": "Komplektai ties riba yra išmatuoti. Likusi dalis apskaičiuota pagal vieno kliento nusidėvėjimą per vieną intervalą: planavimui, ne užsakymui.",
+    "unmeasured": "{count, plural, one {# komplektas turi per mažai istorijos ir neįskaičiuotas} other {# komplektai turi per mažai istorijos ir neįskaičiuoti}}",
+    "capped": "Perskaityti {shown} naujausi komplektai iš {total}."
+  },
+  "invoiceLine": {
+    "storage": "Padangų saugojimas",
+    "pieces": "vnt.",
+    "fromDate": "nuo {date}"
+  },
+  "setNames": {
+    "winter": "žieminės padangos",
+    "summer": "vasarinės padangos",
+    "all_season": "visasezonės padangos",
+    "other": "padangos"
+  },
+  "jobNotes": {
+    "set": "Padangų komplektas {reference}",
+    "shelf": "Lentyna: {shelf}",
+    "notOnShelf": "ne lentynoje",
+    "onRims": "Su ratlankiais",
+    "tiresOnly": "Tik padangos",
+    "tpms": "Su TPMS jutikliais"
+  },
+  "files": {
+    "title": "Nuotraukos ir failai",
+    "add": "Pridėti failų",
+    "none": "Kol kas nieko nepridėta. Nufotografuokite subraižytą ratlankį, kol padangos prieš jus.",
+    "captionPlaceholder": "Kas čia matyti?",
+    "onInvoice": "Sąskaitoje",
+    "invoiceHint": "Sąskaitai pažymėti failai nukopijuojami į darbo užsakymą ar sąskaitą apmokestinant komplektą.",
+    "deleteTitle": "Pašalinti šį failą?",
+    "deleteBody": "{name} pašalinamas iš komplekto. Tai, kas jau nukopijuota į sąskaitą, lieka.",
+    "failed": "Nepavyko išsaugoti",
+    "someFailed": "{count, plural, one {nepavyko įkelti # failo} other {nepavyko įkelti # failų}}"
+  }
+}

+ 28 - 1
messages/nb/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Sendte planlagt melding",
     "scheduled_message_send_now": "Sendte planlagt melding",
     "settings_applyTaxRate": "Brukte avgiftssats",
     "settings_applyTaxRate": "Brukte avgiftssats",
     "settings_convertToExclusive": "Konverterte priser til eks. avgift",
     "settings_convertToExclusive": "Konverterte priser til eks. avgift",
-    "settings_convertToInclusive": "Konverterte priser til inkl. avgift"
+    "settings_convertToInclusive": "Konverterte priser til inkl. avgift",
+    "organization_sampleDataRemove": "Fjernet eksempeldata",
+    "tire_warehouse_create": "Opprettet lagersted for dekk",
+    "tire_warehouse_update": "Oppdaterte lagersted for dekk",
+    "tire_warehouse_delete": "Slettet lagersted for dekk",
+    "tire_warehouse_archive": "Arkiverte lagersted for dekk",
+    "tire_location_create": "Opprettet dekkhylle",
+    "tire_location_bulk_create": "Opprettet dekkhyller",
+    "tire_location_update": "Oppdaterte dekkhylle",
+    "tire_location_delete": "Slettet dekkhylle",
+    "tire_set_check_in": "Tok inn dekksett",
+    "tire_set_check_out": "Leverte ut dekksett",
+    "tire_set_relocate": "Flyttet dekksett",
+    "tire_set_update": "Oppdaterte dekksett",
+    "tire_set_delete": "Slettet dekksett",
+    "tire_measurement_create": "Registrerte dekkmålinger",
+    "tire_treatment_update": "Oppdaterte klargjøring",
+    "tire_treatment_mark": "Merket klargjøring",
+    "tire_treatment_complete_all": "Fullførte klargjøring",
+    "tire_set_message_customer": "Sendte melding til kunde om et dekksett",
+    "tire_set_create_quote": "Opprettet tilbud fra et dekksett",
+    "tire_set_create_work_order": "Opprettet arbeidsordre fra et dekksett",
+    "tire_set_add_to_work_order": "La et dekksett til en arbeidsordre",
+    "tire_set_unlink_work_order": "Koblet et dekksett fra en arbeidsordre",
+    "tire_set_return": "Lagret et sett på nytt",
+    "tire_set_dispose": "Kasserte et dekksett",
+    "tire_set_attach": "La til filer på et dekksett",
+    "tire_set_detach": "Fjernet en fil fra et dekksett"
   }
   }
 }
 }

+ 6 - 2
messages/nb/common.json

@@ -10,7 +10,8 @@
     "back": "Tilbake",
     "back": "Tilbake",
     "confirm": "Bekreft",
     "confirm": "Bekreft",
     "edit": "Rediger",
     "edit": "Rediger",
-    "add": "Legg til"
+    "add": "Legg til",
+    "gotIt": "Skjønner"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Tips: høyreklikk på en rad for handlinger",
     "hint": "Tips: høyreklikk på en rad for handlinger",
@@ -75,6 +76,9 @@
     "enterManually": "Skriv inn manuelt",
     "enterManually": "Skriv inn manuelt",
     "enterBarcode": "Skriv inn strekkode…",
     "enterBarcode": "Skriv inn strekkode…",
     "scanHint": "Rett kameraet mot en strekkode, eller skriv den inn manuelt",
     "scanHint": "Rett kameraet mot en strekkode, eller skriv den inn manuelt",
-    "iosInstallHint": "Trykk Del > Legg til på Hjem-skjerm"
+    "iosInstallHint": "Trykk Del > Legg til på Hjem-skjerm",
+    "installDescription": "Legg den til på hjem-skjermen for rask tilgang",
+    "enterFullscreen": "Fullskjerm",
+    "exitFullscreen": "Avslutt fullskjerm"
   }
   }
 }
 }

+ 12 - 1
messages/nb/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Nylig fullført",
     "recentCompleted": "Nylig fullført",
     "activeJobs": "Aktive jobber",
     "activeJobs": "Aktive jobber",
     "recentObservations": "Nylige observasjoner",
     "recentObservations": "Nylige observasjoner",
-    "recentActivity": "Nylig aktivitet"
+    "recentActivity": "Nylig aktivitet",
+    "gettingStarted": "Kom i gang",
+    "tireHotel": "Dekkhotell"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Forventet vedlikehold",
     "title": "Forventet vedlikehold",
@@ -233,5 +235,14 @@
       "expired": "Utløpt",
       "expired": "Utløpt",
       "converted": "Konvertert"
       "converted": "Konvertert"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Dekkhotell",
+    "viewAll": "Vis dekkhotell",
+    "stored": "{count} sett lagret",
+    "capacity": "{used} / {capacity} dekk",
+    "roomLeft": "Plass til {count} til",
+    "needsPrep": "Trenger klargjøring",
+    "unbilled": "Ufakturert ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/nb/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "Dekkhotellet er på",
+    "body": "Lagre kundenes sesongdekk. Sett opp hyller, så tar du inn et sett."
+  },
+  "telegram": {
+    "title": "Telegram er på",
+    "body": "Kundesamtaler havner her. Svar uten å forlate appen."
+  }
+}

+ 1 - 0
messages/nb/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Kjøretøy",
     "vehicle": "Kjøretøy",
     "template": "Mal",
     "template": "Mal",
     "progress": "Fremdrift",
     "progress": "Fremdrift",
+    "progressTooltip": "{graded} av {total} punkter vurdert",
     "status": "Status",
     "status": "Status",
     "date": "Dato",
     "date": "Dato",
     "statusInProgress": "Pågår",
     "statusInProgress": "Pågår",

+ 8 - 2
messages/nb/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Alle påminnelser",
     "allReminders": "Alle påminnelser",
     "auditLog": "Aktivitetslogg",
     "auditLog": "Aktivitetslogg",
     "aiAssistant": "AI-assistent",
     "aiAssistant": "AI-assistent",
-    "observations": "Observasjoner"
+    "observations": "Observasjoner",
+    "tireHotel": "Dekkhotell",
+    "allTireSets": "Alle dekksett",
+    "tireStorage": "Lageroppsett",
+    "tireSetDetails": "Dekksett",
+    "tireForecast": "Neste sesong"
   },
   },
   "search": "Søk...",
   "search": "Søk...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Logg ut",
     "signOut": "Logg ut",
     "more": "Mer",
     "more": "Mer",
     "auditLog": "Aktivitetslogg",
     "auditLog": "Aktivitetslogg",
-    "aiAssistant": "AI-assistent"
+    "aiAssistant": "AI-assistent",
+    "tireHotel": "Dekkhotell"
   }
   }
 }
 }

+ 91 - 0
messages/nb/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Sett opp verkstedet ditt",
+    "subtitle": "Skriv inn navnet på verkstedet for å komme i gang",
+    "workshopName": "Verkstedets navn",
+    "workshopNamePlaceholder": "Mitt Bilverksted",
+    "submit": "Kom i gang",
+    "sampleDataLabel": "Last inn eksempeldata",
+    "sampleDataDescription": "Legger til noen eksempelkunder, kjøretøy og jobber slik at du kan se deg rundt. Du kan fjerne dem igjen med ett klikk.",
+    "setupFailed": "Oppsettet mislyktes",
+    "couldNotCreate": "Kunne ikke opprette verkstedet",
+    "unexpectedError": "Det oppstod en uventet feil"
+  },
+  "sample": {
+    "prefix": "Eksempel:",
+    "oilService": "Olje- og filterskift",
+    "brakeJob": "Skifte av bremseklosser foran",
+    "annualService": "Årsservice",
+    "quoteTitle": "Overhaling av forstilling",
+    "inspectionNote": "Venstre framdekk er nær slitasjegrensen",
+    "parts": {
+      "oilFilter": "Oljefilter",
+      "engineOil": "Motorolje 5W-30",
+      "brakePads": "Bremseklossett foran",
+      "shockAbsorber": "Støtdemper"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Oljeskift",
+      "description": "Skift av motorolje og filter"
+    },
+    "frontBrakePads": {
+      "name": "Bremseklosser foran",
+      "description": "Skifte bremseklosser foran"
+    },
+    "tireSwap": {
+      "name": "Hjulskift",
+      "description": "Sesonghjulskift med momentkontroll"
+    },
+    "batteryReplacement": {
+      "name": "Batteribytte",
+      "description": "Bytte og registrere startbatteri"
+    },
+    "diagnostics": {
+      "name": "Diagnose",
+      "description": "Avlesing av feilkoder og førstediagnose"
+    },
+    "annualService": {
+      "name": "Årsservice",
+      "description": "Full årsservice etter serviceplan"
+    }
+  },
+  "checklist": {
+    "title": "Kom i gang",
+    "description": "Fire steg for å få verkstedet i gang",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Legg til en kunde",
+        "description": "Opprett din første kunde"
+      },
+      "vehicle": {
+        "title": "Legg til et kjøretøy",
+        "description": "Registrer et kjøretøy på en kunde"
+      },
+      "workOrder": {
+        "title": "Opprett en arbeidsordre",
+        "description": "Åpne din første jobb"
+      },
+      "invoice": {
+        "title": "Send eller last ned en faktura",
+        "description": "Del en faktura med en kunde eller last den ned som PDF"
+      }
+    },
+    "allDoneTitle": "Alt er klart",
+    "allDoneDescription": "Alle stegene er fullført og verkstedet ditt er i gang.",
+    "dismiss": "Skjul kortet",
+    "dismissAriaLabel": "Skjul denne sjekklisten"
+  },
+  "sampleData": {
+    "title": "Eksempeldata",
+    "description": "Verkstedet ditt inneholder fortsatt eksempelkundene, kjøretøyene og jobbene fra oppsettet. Fjerning sletter nøyaktig disse oppføringene og ingenting annet.",
+    "footerNote": "Eksempeldata er lastet inn",
+    "remove": "Fjern eksempeldata",
+    "removeConfirmTitle": "Fjerne eksempeldata?",
+    "removeConfirmDescription": "Dette sletter eksempelkundene, kjøretøyene, arbeidsordrene, tilbudet og kontrollen som ble opprettet under oppsettet. Dine egne data berøres ikke.",
+    "removed": "Eksempeldata fjernet",
+    "removeFailed": "Kunne ikke fjerne eksempeldata"
+  }
+}

+ 27 - 0
messages/nb/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Dashbord",
+    "vehicles": "Kjøretøy",
+    "customers": "Kunder",
+    "work_orders": "Arbeidsordrer",
+    "quotes": "Tilbud",
+    "services": "Servicehistorikk",
+    "billing": "Fakturering",
+    "inventory": "Lager",
+    "labor_presets": "Arbeidsmaler",
+    "inspections": "Kontroller",
+    "tire_hotel": "Dekkhotell",
+    "reports": "Rapporter",
+    "work_board": "Arbeidstavle",
+    "ai_assistant": "AI-assistent",
+    "settings": "Innstillinger"
+  },
+  "actions": {
+    "create": "Opprette",
+    "read": "Se",
+    "update": "Endre",
+    "delete": "Slette",
+    "manage": "Administrere",
+    "manage_storage": "Endre lageroppsettet"
+  }
+}

+ 17 - 2
messages/nb/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} bra",
     "good": "{count} bra",
     "fair": "{count} middels",
     "fair": "{count} middels",
     "poor": "{count} dårlig",
     "poor": "{count} dårlig",
-    "view": "Se"
+    "view": "Se",
+    "serviceHistoryTitle": "Servicehistorikk",
+    "inspectionsTitle": "Inspeksjoner",
+    "columnVehicle": "Kjøretøy",
+    "columnPlate": "Reg.nr.",
+    "columnServices": "Tjenester",
+    "columnInspections": "Kontroller"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Be om service",
     "title": "Be om service",
@@ -174,6 +180,15 @@
     "yourRequests": "Dine serviceforespørsler",
     "yourRequests": "Dine serviceforespørsler",
     "failedToLoad": "Kunne ikke laste inn data.",
     "failedToLoad": "Kunne ikke laste inn data.",
     "staffNotes": "Personalnotater: {notes}",
     "staffNotes": "Personalnotater: {notes}",
-    "preferred": "Ønsket: {date}"
+    "preferred": "Ønsket: {date}",
+    "notListedOption": "Kjøretøyet mitt står ikke i listen",
+    "newVehicleHint": "Fortell oss om kjøretøyet, så legger vi det til i din profil.",
+    "fillVehicleDetails": "Fyll inn kjøretøyets merke og modell",
+    "makeLabel": "Merke",
+    "makePlaceholder": "f.eks. Ford",
+    "modelLabel": "Modell",
+    "modelPlaceholder": "f.eks. Kuga",
+    "yearLabel": "Årsmodell",
+    "plateLabel": "Registreringsnummer"
   }
   }
 }
 }

+ 9 - 0
messages/nb/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Søk",
+  "description": "Søk etter kjøretøy, kunder, jobber og innstillinger",
+  "placeholder": "Søk på navn, skilt, telefon, VIN, faktura…",
+  "noResults": "Ingen treff.",
+  "recentCustomers": "Nylige kunder",
+  "services": "Servicehistorikk",
+  "toClose": "for å lukke"
+}

+ 47 - 1
messages/nb/settings.json

@@ -103,6 +103,10 @@
         "title": "Data",
         "title": "Data",
         "description": "Eksporter og importer sikkerhetskopi"
         "description": "Eksporter og importer sikkerhetskopi"
       },
       },
+      "install": {
+        "title": "Installer app",
+        "description": "Legg Torqvoice til på denne enheten"
+      },
       "license": {
       "license": {
         "title": "Lisens",
         "title": "Lisens",
         "description": "White-label-lisens"
         "description": "White-label-lisens"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Support",
         "title": "Support",
         "description": "Kontakt support og styr supportknappen"
         "description": "Kontakt support og styr supportknappen"
+      },
+      "tireHotel": {
+        "title": "Dekkhotell",
+        "description": "Sesonglagring av dekk"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Siste sikkerhetskopi:",
       "lastBackup": "Siste sikkerhetskopi:",
       "supportPrompt": "Har du spørsmål om hvordan dataene dine beskyttes,",
       "supportPrompt": "Har du spørsmål om hvordan dataene dine beskyttes,",
       "supportAction": "kontakt supportteamet vårt."
       "supportAction": "kontakt supportteamet vårt."
-    }
+    },
+    "optTireHotel": "Dekkhotell",
+    "optTireHotelDesc": "Lagrede dekksett, hyller, klargjøring og lagringsavtaler"
   },
   },
   "license": {
   "license": {
     "title": "White-label-lisens",
     "title": "White-label-lisens",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Kontakt support",
     "contactTitle": "Kontakt support",
     "contactHint": "Åpne supportskjemaet med en gang.",
     "contactHint": "Åpne supportskjemaet med en gang.",
     "contactAction": "Åpne skjema"
     "contactAction": "Åpne skjema"
+  },
+  "install": {
+    "title": "Installer Torqvoice",
+    "description": "Installer Torqvoice på denne enheten for å åpne den fra hjem-skjermen, i fullskjerm og uten nettleserlinjen. Det er samme app og samme data.",
+    "installed": "Torqvoice er installert på denne enheten.",
+    "iosHint": "I Safari, trykk Del-knappen og velg «Legg til på Hjem-skjerm».",
+    "unavailable": "Denne nettleseren har ikke tilbudt installasjon. Åpne nettlesermenyen og se etter «Installer app» eller «Legg til på Hjem-skjerm».",
+    "bannerHidden": "Installasjonsbanneret er skjult på denne enheten.",
+    "showBanner": "Vis banner igjen"
+  },
+  "tireHotel": {
+    "title": "Dekkhotell",
+    "description": "Lagre kundenes sesongdekk og hold styr på hvor hvert sett står.",
+    "cardTitle": "Dekklagring",
+    "enable": "Aktiver dekkhotell",
+    "enableHint": "Legger dekklagring i menyen. Slår du det av igjen, skjules det uten at noe slettes.",
+    "defaultCapacity": "Standard dekk per hylle",
+    "defaultCapacityHint": "Fylles inn når du legger til nye hyller. Kan endres per hylle.",
+    "warnPercent": "Varsle når fullt (%)",
+    "warnPercentHint": "Markerer lageroversikten når denne andelen av total kapasitet er i bruk.",
+    "treadTitle": "Grenser for bytte",
+    "treadHint": "Lovkrav varierer mellom land, så sett grensene som gjelder i ditt marked. Mønsterdybde under grensen markeres for bytte.",
+    "summerLimit": "Sommergrense ({unit})",
+    "winterLimit": "Vintergrense ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "Sett så opp hyllene dine.",
+    "setupLink": "Åpne lageroppsett",
+    "save": "Lagre innstillinger for dekkhotell",
+    "saved": "Innstillinger for dekkhotell lagret",
+    "billingTitle": "Lagerfakturering",
+    "billingHint": "Fylles inn på lagringslinjen når du fakturerer et sett. Kan endres på fakturaen.",
+    "seasonalPrice": "Standard lagringspris",
+    "prepTitle": "Priser for klargjøring",
+    "prepHint": "Hva hver type klargjøring koster. La en stå tom, så faktureres den aldri, som passer for verksteder som har vask inkludert i lagerprisen. De med pris legges automatisk på tilbud og arbeidsordre.",
+    "prepNotCharged": "ikke fakturert"
   }
   }
 }
 }

+ 473 - 0
messages/nb/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Dekkhotell",
+  "description": "Sesonglagring av dekk for kundene dine",
+  "common": {
+    "cancel": "Avbryt",
+    "save": "Lagre",
+    "create": "Opprett",
+    "delete": "Slett",
+    "edit": "Rediger"
+  },
+  "seasons": {
+    "summer": "Sommer",
+    "winter": "Vinter",
+    "all_season": "Helår",
+    "other": "Annet"
+  },
+  "statuses": {
+    "stored": "På lager",
+    "released": "Utlevert",
+    "disposed": "Kassert"
+  },
+  "conditions": {
+    "good": "God",
+    "fair": "Følg med",
+    "replace": "Bytt"
+  },
+  "positions": {
+    "front_left": "Foran venstre",
+    "front_right": "Foran høyre",
+    "rear_left": "Bak venstre",
+    "rear_right": "Bak høyre",
+    "spare": "Reservehjul",
+    "unspecified": "Uspesifisert"
+  },
+  "capacity": {
+    "used": "{used} av {capacity} dekk",
+    "free": "Plass til {count}",
+    "freeShort": "{count} ledig",
+    "over": "{count} over kapasitet",
+    "meterLabel": "{used} av {capacity} dekk lagret"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32-deler",
+    "inputLabel": "Mønsterdybde, {position}",
+    "lastTime": "var {value}",
+    "worn": "{value} slitt"
+  },
+  "list": {
+    "tabAll": "Alle",
+    "tabStored": "På lager",
+    "tabReleased": "Utlevert",
+    "search": "Søk på skilt, kunde, merke eller hylle",
+    "checkIn": "Ta inn dekk",
+    "empty": "Ingen dekksett ennå. Ta inn et sett for å komme i gang.",
+    "unassigned": "Uten eier",
+    "reference": "Ref",
+    "vehicle": "Kjøretøy",
+    "tires": "Dekk",
+    "location": "Hylle",
+    "condition": "Tilstand",
+    "status": "Status",
+    "storedSince": "Siden",
+    "tireCount": "{count, plural, one {# dekk} other {# dekk}}",
+    "studded": "Piggdekk",
+    "roomLeft": "Plass til {count}",
+    "prep": "Klargjør",
+    "tabNeedsPrep": "Trenger klargjøring",
+    "setUpShelves": "Sett opp hyller",
+    "emptyNoShelves": "Ingen hyller ennå. Sett opp lageret før du tar inn dekk.",
+    "tabDisposed": "Kassert"
+  },
+  "checkIn": {
+    "title": "Ta inn dekk",
+    "description": "Registrer hva som kom inn, og velg hvor det skal stå.",
+    "customer": "Kunde",
+    "selectCustomer": "Velg kunde",
+    "noCustomer": "Ingen kunde",
+    "vehicle": "Kjøretøy",
+    "selectVehicle": "Velg kjøretøy",
+    "season": "Sesong",
+    "quantity": "Antall dekk",
+    "quantityHint": "Telles enkeltvis, så et par eller fem hjul fungerer også.",
+    "brand": "Merke",
+    "brandPlaceholder": "Michelin, Bridgestone, Nokian",
+    "model": "Modell",
+    "size": "Dimensjon",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "DOT-kode",
+    "dotPlaceholder": "3623",
+    "dotHint": "Uke og år dekket ble produsert.",
+    "withRims": "På felg",
+    "hasTpms": "TPMS-sensorer",
+    "studded": "Piggdekk",
+    "location": "Hvor dekkene skal stå",
+    "choosePlace": "Velg hylle",
+    "searchLocations": "Søk i hyller",
+    "noLocations": "Fant ingen hyller",
+    "tooFull": "Ikke nok plass",
+    "pickLocationFirst": "Velg en hylle før du tar inn settet",
+    "roomSummary": "{locations} hyller passer dette settet, {tires} dekkplasser ledige",
+    "nothingFits": "Ingen hylle har plass til {quantity} dekk. Frigjør plass eller legg til en hylle.",
+    "treadTitle": "Mønsterdybde ved innlevering",
+    "treadHint": "Valgfritt, men avgjør enhver diskusjon om tilstand senere.",
+    "notes": "Notater",
+    "submit": "Ta inn",
+    "success": "Sett {reference} lagret på {code}",
+    "failed": "Kunne ikke ta inn settet",
+    "noVehiclesForCustomer": "Denne kunden har ingen kjøretøy ennå. Legg til ett på kundesiden først.",
+    "treatmentsHint": "Hva som skal gjøres med dette settet før lagring eller montering.",
+    "storedBefore": "Lagret her før",
+    "storedBeforeHint": "Kommer de samme dekkene tilbake? Velg settet, så føyes sesongen til historikken.",
+    "sameSet": "Samme sett som før",
+    "pieces": "{count} stk",
+    "lastOut": "Hentet {date}",
+    "notStored": "Ikke på lager nå",
+    "returned": "Sett {reference} står på {code} igjen",
+    "submitReturn": "Lagre igjen",
+    "ownerFromVehicle": "Hentet fra kjøretøyet",
+    "vehicleOwner": "Eieren av kjøretøyet",
+    "clearVehicle": "Fjern kjøretøyet"
+  },
+  "storage": {
+    "title": "Hyller",
+    "description": "Sett opp lageret én gang, og se med én gang hva som får plass hvor.",
+    "addWarehouse": "Legg til lagersted",
+    "addShelves": "Legg til hyller",
+    "warehouseName": "Navn",
+    "warehouseNamePlaceholder": "Hovedbygg, Container 2, Bakrom",
+    "warehouseAddress": "Adresse",
+    "warehouseCreated": "Lagersted lagt til",
+    "warehouseDeleted": "Slettet {name}",
+    "warehouseArchived": "Arkiverte {name}",
+    "deleteWarehouse": "Slett lagersted",
+    "deleteWarehouseTitle": "Slette {name}?",
+    "deleteWarehouseBody": "Dette fjerner lagerstedet og alle hyllene der.",
+    "deleteWarehouseBodyOccupied": "Dette lagerstedet har fortsatt {count} dekk, så det blir arkivert i stedet for slettet.",
+    "totalCapacity": "Total kapasitet",
+    "totalStored": "Lagret nå",
+    "totalFree": "Ledig plass",
+    "totalLocations": "Hyller",
+    "locationSummary": "{occupied} av {total} hyller i bruk",
+    "noLocations": "Ingen hyller ennå. Legg til en reol for å lagre dekk her.",
+    "emptyTitle": "Ingen lagring satt opp ennå",
+    "emptyBody": "Legg til første bygg eller rom, og fyll det med hyller.",
+    "default": "Standard",
+    "addLocation": "Legg til hyller",
+    "editLocation": "Rediger hylle",
+    "createLocation": "Opprett hylle",
+    "createShelves": "Opprett hyller",
+    "locationDialogDescription": "Legger til lagring i {warehouse}.",
+    "tabBulk": "En reol med hyller",
+    "tabSingle": "Én hylle",
+    "zone": "Sone",
+    "zonePlaceholder": "A, Rom 1, Nord",
+    "rack": "Reol",
+    "rackPlaceholder": "04",
+    "shelf": "Hylle",
+    "shelfPlaceholder": "2",
+    "position": "Plass",
+    "positionPlaceholder": "Venstre",
+    "shelfFrom": "Hyller fra",
+    "shelfTo": "til",
+    "code": "Kode",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Står på etiketten. Fylles ut for deg hvis du lar den stå tom.",
+    "capacity": "Dekk per hylle",
+    "capacityHint": "Hvor mange enkeltdekk det er plass til her.",
+    "notes": "Notater",
+    "bulkPreview": "Oppretter {count} hyller, {first} til {last}",
+    "bulkPreviewCapacity": "Plass til {tires} dekk totalt",
+    "bulkCreated": "Opprettet {count} hyller",
+    "bulkCreatedWithSkips": "Opprettet {created} hyller, hoppet over {skipped} som allerede fantes",
+    "bulkNothingCreated": "Disse hyllene finnes allerede",
+    "locationCreated": "Hylle lagt til",
+    "locationUpdated": "Hylle oppdatert",
+    "locationDeleted": "Slettet {code}",
+    "deleteLocationTitle": "Slette {code}?",
+    "deleteLocationBody": "Hyllen må være tom først.",
+    "saveFailed": "Kunne ikke lagre",
+    "deleteFailed": "Kunne ikke slette"
+  },
+  "movements": {
+    "check_in": "Tatt inn",
+    "check_out": "Levert ut",
+    "relocate": "Flyttet",
+    "dispose": "Kassert",
+    "measure": "Målt"
+  },
+  "detail": {
+    "move": "Flytt",
+    "checkOut": "Lever ut",
+    "conditionTitle": "Tilstand",
+    "noMeasurements": "Ingen målinger registrert ennå.",
+    "historyTitle": "Historikk",
+    "storageTitle": "Lagring",
+    "shelf": "Hylle",
+    "warehouse": "Lagersted",
+    "notStored": "Ikke på lager nå.",
+    "checkedIn": "Tatt inn",
+    "checkedOut": "Levert ut",
+    "tiresTitle": "Dekk",
+    "loadSpeed": "Last og hastighet",
+    "ownerTitle": "Eier",
+    "phone": "Telefon",
+    "deleteTitle": "Slette dette dekksettet?",
+    "deleteBody": "Settet og hele historikken fjernes. Dette kan ikke angres.",
+    "deleted": "Dekksett slettet",
+    "deleteFailed": "Kunne ikke slette settet",
+    "belowLimit": "{count} dekk på eller under byttegrensen.",
+    "belowLimitHint": "Verdt å tilby kunden pris mens dekkene fortsatt står her.",
+    "dispose": "Kasser",
+    "disposeTitle": "Kassere dette settet?",
+    "disposeBody": "Oppføringen og historikken blir stående, og hyllen frigjøres. Det foreslås ikke som samme dekk neste sesong.",
+    "disposed": "Settet er kassert",
+    "disposeFailed": "Kunne ikke kassere settet"
+  },
+  "checkOut": {
+    "title": "Lever ut dekk",
+    "description": "Tar settet av {code}.",
+    "descriptionNoLocation": "Leverer dette settet tilbake til kunden.",
+    "treadTitle": "Mønsterdybde ved utlevering",
+    "note": "Notat",
+    "submit": "Lever ut",
+    "success": "Sett {reference} levert ut",
+    "failed": "Kunne ikke levere ut settet",
+    "prepPending": "{count} klargjøringsjobb(er) gjenstår",
+    "conditionTitle": "Tilstand ved innlevering",
+    "conditionLead": {
+      "replace": "Ett eller flere dekk er på eller under bytte­grensen. Verdt å si fra til kunden før de kjører.",
+      "fair": "Ett eller flere dekk nærmer seg byttegrensen. Verdt en kommentar før neste sesong."
+    },
+    "treadHintUnmeasured": "Det ble ikke tatt måling da disse kom inn. Dette er siste sjanse til å registrere en."
+  },
+  "relocate": {
+    "title": "Flytt dekk",
+    "description": "Velg hyllen settet skal flyttes til.",
+    "newLocation": "Ny hylle",
+    "note": "Notat",
+    "submit": "Flytt",
+    "success": "Sett {reference} flyttet",
+    "failed": "Kunne ikke flytte settet"
+  },
+  "edit": {
+    "title": "Rediger dekksett",
+    "description": "Rett opp det som ble registrert ved innlevering. Bruk Flytt for å endre hylle.",
+    "quantityHint": "Økning kontrolleres mot hyllen settet står på.",
+    "rimType": "Felgtype",
+    "rimTypePlaceholder": "Alu, stål",
+    "success": "Dekksett oppdatert",
+    "failed": "Kunne ikke lagre endringene",
+    "treadHint": "Endrer du en måling, registreres den som en ny måling i historikken i stedet for å overskrive den gamle."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Vask dekk",
+      "wash_rims": "Vask felger",
+      "balance": "Balanser",
+      "tpms_service": "TPMS-service",
+      "new_valves": "Nye ventiler",
+      "repair": "Reparasjon"
+    },
+    "statuses": {
+      "pending": "Skal gjøres",
+      "done": "Ferdig",
+      "skipped": "Hoppet over"
+    },
+    "title": "Klargjøring",
+    "none": "Ingenting å gjøre på dette settet.",
+    "add": "Legg til klargjøring",
+    "pickHint": "Kryss av for hva dette settet trenger før det skal på bilen igjen.",
+    "pendingCount": "{count} igjen",
+    "allComplete": "Alt ferdig",
+    "ready": "Klar",
+    "markDone": "Ferdig",
+    "completeAll": "Merk alle {count} som ferdig",
+    "allDone": "All klargjøring ferdig",
+    "undo": "Legg tilbake på lista",
+    "saveFailed": "Kunne ikke lagre",
+    "skip": "Hopp over"
+  },
+  "messaging": {
+    "action": "Send melding",
+    "title": "Send melding til kunden",
+    "description": "Til {name}.",
+    "noCustomer": "Ingen kunde på dette settet.",
+    "noCustomerBody": "Knytt en kunde til settet før du sender melding.",
+    "channel": "Send via",
+    "channels": {
+      "email": "E-post",
+      "sms": "SMS",
+      "telegram": "Telegram"
+    },
+    "noAddress": "ingen adresse registrert",
+    "recipient": "Send til",
+    "recipientPlaceholder": "Telefonnummer",
+    "subject": "Emne",
+    "message": "Melding",
+    "send": "Send",
+    "sent": "Melding sendt",
+    "failed": "Kunne ikke sende meldingen",
+    "smsLong": "Lange meldinger kan bli delt i flere SMS.",
+    "subjects": {
+      "low_tread": "Dekkene dine ({season})",
+      "damage": "Dekkene dine ({season})",
+      "stored": "{season}dekkene dine er lagret",
+      "custom": "Dekkene dine ({season})"
+    },
+    "bodies": {
+      "low_tread": "Hei {customer_name}, {season}dekkene dine står lagret hos oss på {shop_name}. Da vi tok dem inn målte {positions} {tread}, som er under grensen. De klarer ikke en sesong til. Vil du ha pris på et nytt sett?",
+      "damage": "Hei {customer_name}, {season}dekkene dine står lagret hos oss på {shop_name}. Vi fant skade på {positions} da vi tok dem inn. Ring oss når du har anledning, så går vi gjennom det sammen.",
+      "stored": "Hei {customer_name}, {season}dekkene dine står nå lagret hos oss på {shop_name}. Vi sier fra når det er tid for å bytte tilbake.",
+      "custom": "Hei {customer_name}, angående {season}dekkene dine som står lagret hos oss på {shop_name}:"
+    },
+    "enableInSettings": "Ikke satt opp ennå. Slå det på i Innstillinger."
+  },
+  "job": {
+    "cardTitle": "Tilbud og jobber",
+    "none": "Ingenting opprettet for dette settet ennå.",
+    "quote": "Tilbud",
+    "workOrder": "Arbeidsordre",
+    "converted": "konvertert",
+    "quoteTitle": "Gi pris på nytt sett",
+    "workOrderTitle": "Fakturer dette settet",
+    "loadingSet": "Henter settet",
+    "tires": "Dekk",
+    "inStock": "{count} på lager",
+    "shortStock": "bare {count} på lager",
+    "noMatch": "Ingenting på lager passer {size}. Linjen legges inn med prisen du setter.",
+    "noSize": "Dette settet mangler dimensjon, så lageret kan ikke matches. Linjen legges inn med prisen du setter.",
+    "unitPrice": "Pris per dekk",
+    "createQuote": "Opprett tilbud",
+    "createWorkOrder": "Opprett arbeidsordre",
+    "quoteCreated": "Tilbud {number} opprettet",
+    "workOrderCreated": "Arbeidsordre opprettet",
+    "failed": "Kunne ikke opprette",
+    "bannerTitle": "Hentes fra",
+    "openSet": "Åpne dekksett",
+    "prepPending": "Gjenstår før montering: {jobs}",
+    "notOnShelf": "Dette settet står ikke lenger på lager.",
+    "targetNew": "Ny arbeidsordre",
+    "targetNewHint": "Opprett en jobb bare for disse dekkene.",
+    "targetExisting": "Eller legg til på en jobb som allerede er åpen for dette kjøretøyet",
+    "addToWorkOrder": "Legg til på arbeidsordre",
+    "workOrderUpdated": "Lagt til på arbeidsordren",
+    "unlink": "Koble fra",
+    "unlinkTitle": "Koble disse dekkene fra jobben?",
+    "unlinkBody": "Jobben peker ikke lenger på dekksettet. Deler og arbeidslinjer som allerede ligger der blir stående, så fjern dem separat hvis de ikke skal faktureres.",
+    "unlinked": "Dekkene koblet fra",
+    "unlinkFailed": "Kunne ikke koble fra dekkene",
+    "prep": "Klargjøring",
+    "tiresSkipped": "Ingen dekklinje legges til.",
+    "total": "Totalt",
+    "searchStock": "Søk i lageret på merke, dimensjon eller varenummer",
+    "noSearchHits": "Ingenting på lageret passer \"{query}\".",
+    "otherSize": "Annen dimensjon",
+    "inSize": "{count} i {size}",
+    "quantity": "Ant.",
+    "noStockItem": "Ingen lagervare, pris settes manuelt",
+    "storeTires": "Legg dekk på hotell",
+    "storage": "Lagring",
+    "storageFee": "Gebyr",
+    "storagePeriod": "Periode",
+    "destination": "Hvor det havner",
+    "targetInvoice": "Ny faktura",
+    "targetInvoiceHint": "Fakturer kunden direkte, uten jobb på et kjøretøy.",
+    "createInvoice": "Opprett faktura",
+    "needsCustomer": "Koble en kunde eller et kjøretøy til settet før du fakturerer.",
+    "storagePeriodEnd": "Hentet",
+    "storageOpenEnded": "Åpen",
+    "storagePeriodHint": "Står på fakturalinjen så kunden ser hva gebyret dekker. La sluttdatoen stå tom til du vet når de henter.",
+    "pricesHint": "Prisene for klargjøring hentes fra innstillingene. Alle linjer kan endres etter at dette er opprettet.",
+    "files": "Bilder og filer",
+    "filesCount": "{count} på fakturaen"
+  },
+  "label": {
+    "action": "Etiketter",
+    "title": "Skriv ut dekketiketter",
+    "description": "Klistremerker til hvert dekk, med QR som åpner dette settet.",
+    "format": "Etikettstørrelse",
+    "formats": {
+      "dymo_small": "Liten rull",
+      "dymo_standard": "Standard rull",
+      "thermal_large": "Stor rull",
+      "sheet_a4": "A4-ark"
+    },
+    "perSheet": "{count} per ark",
+    "copies": "Hvor mange",
+    "copiesHint": "ett per dekk ({count})",
+    "fewer": "Én færre",
+    "more": "Én til",
+    "print": "Skriv ut",
+    "shelfNote": "Hyllen står med vilje ikke på etiketten. Sett blir flyttet, og en trykt hylle ville da vært feil. QR-koden viser alltid hvor settet står nå.",
+    "reference": "Ref",
+    "quantity": "{count, plural, one {# dekk} other {# dekk}}",
+    "withRims": "På felg",
+    "tpms": "TPMS",
+    "studded": "Pigg",
+    "unassigned": "Uten eier",
+    "preview": "Forhåndsvisning",
+    "previewScale": "Vist i utskriftsstørrelse. Koden kan skannes rett fra skjermen.",
+    "previewFailed": "Kunne ikke lage forhåndsvisning"
+  },
+  "vehicle": {
+    "oneOnShelf": "på {code}",
+    "inStorage": "på dekkhotell"
+  },
+  "wear": {
+    "summary": "Slitt {value} mellom {from} og {to}",
+    "perSeason": "Rundt {value} per sesong",
+    "seasonsLeft": "{count, plural, =0 {På grensen nå} one {Omtrent én sesong igjen} other {Omtrent # sesonger igjen}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Endre i innstillinger",
+    "prepPrices": "Priser for klargjøring",
+    "storagePrices": "Lagringspriser i innstillinger",
+    "treadLimits": "Endre grensen",
+    "defaultCapacity": "Standard kapasitet"
+  },
+  "forecast": {
+    "title": "Neste sesong",
+    "subtitle": "Hva de lagrede settene er målt til å trenge, per dimensjon.",
+    "backToList": "Tilbake til sett",
+    "tires": "Forventede dekk",
+    "atLimit": "Allerede på grensen",
+    "expected": "Ventet innen en sesong",
+    "orderTitle": "Per dimensjon",
+    "sizes": "{count} dimensjoner",
+    "empty": "Ingenting er målt nær grensen ennå. Sett trenger to besøk før en slitasjerate kan regnes ut.",
+    "nowCount": "{count, plural, one {# sett på grensen} other {# sett på grensen}}",
+    "nextCount": "{count, plural, one {# sett innen en sesong} other {# sett innen en sesong}}",
+    "tiresShort": "dekk",
+    "lowest": "laveste {value}",
+    "now": "På grensen",
+    "next": "Innen en sesong",
+    "rate": "{value} per sesong",
+    "caveat": "Sett som allerede er på grensen, er målt. Resten er beregnet ut fra én kundes slitasje over ett intervall, så bruk det til å planlegge, ikke til å bestille direkte.",
+    "unmeasured": "{count, plural, one {# sett har for lite historikk til å vurderes og telles ikke med} other {# sett har for lite historikk til å vurderes og telles ikke med}}",
+    "capped": "Leste de {shown} nyeste settene av {total}."
+  },
+  "invoiceLine": {
+    "storage": "Dekklagring",
+    "pieces": "stk",
+    "fromDate": "fra {date}"
+  },
+  "setNames": {
+    "winter": "vinterdekk",
+    "summer": "sommerdekk",
+    "all_season": "helårsdekk",
+    "other": "dekk"
+  },
+  "jobNotes": {
+    "set": "Dekksett {reference}",
+    "shelf": "Hylle: {shelf}",
+    "notOnShelf": "ikke på hylle",
+    "onRims": "På felg",
+    "tiresOnly": "Kun dekk",
+    "tpms": "Har TPMS-sensorer"
+  },
+  "files": {
+    "title": "Bilder og filer",
+    "add": "Legg til filer",
+    "none": "Ingenting lagt ved ennå. Ta bilde av en skadet felg mens dekkene står foran deg.",
+    "captionPlaceholder": "Hva viser dette?",
+    "onInvoice": "På fakturaen",
+    "invoiceHint": "Filer merket for faktura kopieres til arbeidsordren eller fakturaen når settet faktureres.",
+    "deleteTitle": "Fjerne denne filen?",
+    "deleteBody": "{name} fjernes fra settet. Det som allerede er kopiert til en faktura, blir stående.",
+    "failed": "Kunne ikke lagre",
+    "someFailed": "{count, plural, one {# fil kunne ikke lastes opp} other {# filer kunne ikke lastes opp}}"
+  }
+}

+ 28 - 1
messages/nl/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Gepland bericht verzonden",
     "scheduled_message_send_now": "Gepland bericht verzonden",
     "settings_applyTaxRate": "Btw-tarief toegepast",
     "settings_applyTaxRate": "Btw-tarief toegepast",
     "settings_convertToExclusive": "Prijzen omgezet naar exclusief btw",
     "settings_convertToExclusive": "Prijzen omgezet naar exclusief btw",
-    "settings_convertToInclusive": "Prijzen omgezet naar inclusief btw"
+    "settings_convertToInclusive": "Prijzen omgezet naar inclusief btw",
+    "organization_sampleDataRemove": "Voorbeeldgegevens verwijderd",
+    "tire_warehouse_create": "Bandenopslaglocatie aangemaakt",
+    "tire_warehouse_update": "Bandenopslaglocatie bijgewerkt",
+    "tire_warehouse_delete": "Bandenopslaglocatie verwijderd",
+    "tire_warehouse_archive": "Bandenopslaglocatie gearchiveerd",
+    "tire_location_create": "Bandenstelling aangemaakt",
+    "tire_location_bulk_create": "Bandenstellingen aangemaakt",
+    "tire_location_update": "Bandenstelling bijgewerkt",
+    "tire_location_delete": "Bandenstelling verwijderd",
+    "tire_set_check_in": "Bandenset ingenomen",
+    "tire_set_check_out": "Bandenset teruggegeven",
+    "tire_set_relocate": "Bandenset verplaatst",
+    "tire_set_update": "Bandenset bijgewerkt",
+    "tire_set_delete": "Bandenset verwijderd",
+    "tire_measurement_create": "Bandenmetingen vastgelegd",
+    "tire_treatment_update": "Voorbereiding bijgewerkt",
+    "tire_treatment_mark": "Voorbereiding gemarkeerd",
+    "tire_treatment_complete_all": "Voorbereiding afgerond",
+    "tire_set_message_customer": "Klant bericht gestuurd over een bandenset",
+    "tire_set_create_quote": "Offerte aangemaakt vanuit een bandenset",
+    "tire_set_create_work_order": "Werkorder aangemaakt vanuit een bandenset",
+    "tire_set_add_to_work_order": "Bandenset aan een werkorder toegevoegd",
+    "tire_set_unlink_work_order": "Bandenset van een werkorder ontkoppeld",
+    "tire_set_return": "Set opnieuw opgeslagen",
+    "tire_set_dispose": "Bandenset afgeschreven",
+    "tire_set_attach": "Bestanden aan een bandenset toegevoegd",
+    "tire_set_detach": "Bestand van een bandenset verwijderd"
   }
   }
 }
 }

+ 6 - 2
messages/nl/common.json

@@ -10,7 +10,8 @@
     "confirm": "Bevestigen",
     "confirm": "Bevestigen",
     "saveChanges": "Wijzigingen opslaan",
     "saveChanges": "Wijzigingen opslaan",
     "edit": "Bewerken",
     "edit": "Bewerken",
-    "add": "Toevoegen"
+    "add": "Toevoegen",
+    "gotIt": "Duidelijk"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Tip: rechtsklik op een rij voor acties",
     "hint": "Tip: rechtsklik op een rij voor acties",
@@ -75,6 +76,9 @@
     "enterManually": "Handmatig invoeren",
     "enterManually": "Handmatig invoeren",
     "enterBarcode": "Barcode invoeren…",
     "enterBarcode": "Barcode invoeren…",
     "scanHint": "Richt uw camera op een barcode of voer hem handmatig in",
     "scanHint": "Richt uw camera op een barcode of voer hem handmatig in",
-    "iosInstallHint": "Tik op Deel > Zet op beginscherm"
+    "iosInstallHint": "Tik op Deel > Zet op beginscherm",
+    "installDescription": "Zet de app op je beginscherm voor snelle toegang",
+    "enterFullscreen": "Volledig scherm",
+    "exitFullscreen": "Volledig scherm afsluiten"
   }
   }
 }
 }

+ 12 - 1
messages/nl/dashboard.json

@@ -53,7 +53,9 @@
     "recentCompleted": "Recent afgerond",
     "recentCompleted": "Recent afgerond",
     "activeJobs": "Actieve opdrachten",
     "activeJobs": "Actieve opdrachten",
     "recentObservations": "Recente waarnemingen",
     "recentObservations": "Recente waarnemingen",
-    "recentActivity": "Recente activiteit"
+    "recentActivity": "Recente activiteit",
+    "gettingStarted": "Aan de slag",
+    "tireHotel": "Bandenhotel"
   },
   },
   "maintenance": {
   "maintenance": {
     "title": "Verwacht onderhoud gepland",
     "title": "Verwacht onderhoud gepland",
@@ -233,5 +235,14 @@
       "expired": "Verlopen",
       "expired": "Verlopen",
       "converted": "Omgezet"
       "converted": "Omgezet"
     }
     }
+  },
+  "tireHotel": {
+    "title": "Bandenhotel",
+    "viewAll": "Bandenhotel bekijken",
+    "stored": "{count} sets opgeslagen",
+    "capacity": "{used} / {capacity} banden",
+    "roomLeft": "Ruimte voor {count} meer",
+    "needsPrep": "Voorbereiding nodig",
+    "unbilled": "Niet gefactureerd ({count})"
   }
   }
 }
 }

+ 10 - 0
messages/nl/featureHints.json

@@ -0,0 +1,10 @@
+{
+  "tire-hotel": {
+    "title": "Bandenhotel staat aan",
+    "body": "Bewaar seizoensbanden van klanten. Zet stellingen op en neem een set in."
+  },
+  "telegram": {
+    "title": "Telegram staat aan",
+    "body": "Klantgesprekken komen hier binnen. Beantwoord ze zonder de app te verlaten."
+  }
+}

+ 1 - 0
messages/nl/inspections.json

@@ -366,6 +366,7 @@
     "vehicle": "Voertuig",
     "vehicle": "Voertuig",
     "template": "Sjabloon",
     "template": "Sjabloon",
     "progress": "Voortgang",
     "progress": "Voortgang",
+    "progressTooltip": "{graded} van {total} punten beoordeeld",
     "status": "Status",
     "status": "Status",
     "date": "Datum",
     "date": "Datum",
     "statusInProgress": "Loopt",
     "statusInProgress": "Loopt",

+ 8 - 2
messages/nl/navigation.json

@@ -53,7 +53,12 @@
     "allReminders": "Alle herinneringen",
     "allReminders": "Alle herinneringen",
     "auditLog": "Activiteitenlog",
     "auditLog": "Activiteitenlog",
     "aiAssistant": "AI-assistent",
     "aiAssistant": "AI-assistent",
-    "observations": "Waarnemingen"
+    "observations": "Waarnemingen",
+    "tireHotel": "Bandenhotel",
+    "allTireSets": "Alle bandensets",
+    "tireStorage": "Opslagindeling",
+    "tireSetDetails": "Bandenset",
+    "tireForecast": "Volgend seizoen"
   },
   },
   "search": "Zoeken...",
   "search": "Zoeken...",
   "shortcut": "Ctrl+K",
   "shortcut": "Ctrl+K",
@@ -107,6 +112,7 @@
     "signOut": "Uitloggen",
     "signOut": "Uitloggen",
     "more": "Meer",
     "more": "Meer",
     "auditLog": "Activiteitenlog",
     "auditLog": "Activiteitenlog",
-    "aiAssistant": "AI-assistent"
+    "aiAssistant": "AI-assistent",
+    "tireHotel": "Bandenhotel"
   }
   }
 }
 }

+ 91 - 0
messages/nl/onboarding.json

@@ -0,0 +1,91 @@
+{
+  "form": {
+    "title": "Stel uw werkplaats in",
+    "subtitle": "Voer de naam van uw werkplaats in om te beginnen",
+    "workshopName": "Naam van de werkplaats",
+    "workshopNamePlaceholder": "Mijn Autogarage",
+    "submit": "Aan de slag",
+    "sampleDataLabel": "Voorbeeldgegevens laden",
+    "sampleDataDescription": "Voegt enkele voorbeeldklanten, voertuigen en klussen toe zodat u kunt rondkijken. U kunt ze met één klik weer verwijderen.",
+    "setupFailed": "Instellen mislukt",
+    "couldNotCreate": "Kon de werkplaats niet aanmaken",
+    "unexpectedError": "Er is een onverwachte fout opgetreden"
+  },
+  "sample": {
+    "prefix": "Voorbeeld:",
+    "oilService": "Olie en filter vervangen",
+    "brakeJob": "Remblokken voor vervangen",
+    "annualService": "Jaarlijkse onderhoudsbeurt",
+    "quoteTitle": "Voorwielophanging reviseren",
+    "inspectionNote": "De linkervoorband zit dicht bij de slijtagegrens",
+    "parts": {
+      "oilFilter": "Oliefilter",
+      "engineOil": "Motorolie 5W-30",
+      "brakePads": "Set remblokken voor",
+      "shockAbsorber": "Schokdemper"
+    }
+  },
+  "presets": {
+    "oilChange": {
+      "name": "Olie verversen",
+      "description": "Motorolie en filter vervangen"
+    },
+    "frontBrakePads": {
+      "name": "Remblokken voor",
+      "description": "Remblokken voor vervangen"
+    },
+    "tireSwap": {
+      "name": "Wielen wisselen",
+      "description": "Seizoenswissel van de wielen met momentcontrole"
+    },
+    "batteryReplacement": {
+      "name": "Accu vervangen",
+      "description": "Startaccu vervangen en registreren"
+    },
+    "diagnostics": {
+      "name": "Diagnose",
+      "description": "Foutcodes uitlezen en eerste diagnose"
+    },
+    "annualService": {
+      "name": "Jaarlijkse onderhoudsbeurt",
+      "description": "Volledige jaarlijkse beurt volgens schema"
+    }
+  },
+  "checklist": {
+    "title": "Aan de slag",
+    "description": "Vier stappen om uw werkplaats op gang te brengen",
+    "progress": "{done}/{total}",
+    "steps": {
+      "customer": {
+        "title": "Voeg een klant toe",
+        "description": "Maak uw eerste klantkaart aan"
+      },
+      "vehicle": {
+        "title": "Voeg een voertuig toe",
+        "description": "Registreer een voertuig voor een klant"
+      },
+      "workOrder": {
+        "title": "Maak een werkorder aan",
+        "description": "Open uw eerste klus"
+      },
+      "invoice": {
+        "title": "Verstuur of download een factuur",
+        "description": "Deel een factuur met een klant of download deze als PDF"
+      }
+    },
+    "allDoneTitle": "Alles staat klaar",
+    "allDoneDescription": "Alle stappen zijn afgerond en uw werkplaats draait.",
+    "dismiss": "Kaart verbergen",
+    "dismissAriaLabel": "Deze checklist verbergen"
+  },
+  "sampleData": {
+    "title": "Voorbeeldgegevens",
+    "description": "Uw werkplaats bevat nog de voorbeeldklanten, voertuigen en klussen uit de installatie. Bij verwijderen worden precies die records gewist en niets anders.",
+    "footerNote": "Er zijn voorbeeldgegevens geladen",
+    "remove": "Voorbeeldgegevens verwijderen",
+    "removeConfirmTitle": "Voorbeeldgegevens verwijderen?",
+    "removeConfirmDescription": "Hiermee verwijdert u de voorbeeldklanten, voertuigen, werkorders, de offerte en de inspectie die bij de installatie zijn aangemaakt. Uw eigen gegevens blijven onaangetast.",
+    "removed": "Voorbeeldgegevens verwijderd",
+    "removeFailed": "Kon de voorbeeldgegevens niet verwijderen"
+  }
+}

+ 27 - 0
messages/nl/permissions.json

@@ -0,0 +1,27 @@
+{
+  "subjects": {
+    "dashboard": "Dashboard",
+    "vehicles": "Voertuigen",
+    "customers": "Klanten",
+    "work_orders": "Werkorders",
+    "quotes": "Offertes",
+    "services": "Serviceregistraties",
+    "billing": "Facturatie",
+    "inventory": "Voorraad",
+    "labor_presets": "Werksjablonen",
+    "inspections": "Inspecties",
+    "tire_hotel": "Bandenhotel",
+    "reports": "Rapporten",
+    "work_board": "Werkbord",
+    "ai_assistant": "AI-assistent",
+    "settings": "Instellingen"
+  },
+  "actions": {
+    "create": "Aanmaken",
+    "read": "Bekijken",
+    "update": "Bewerken",
+    "delete": "Verwijderen",
+    "manage": "Beheren",
+    "manage_storage": "Opslagindeling beheren"
+  }
+}

+ 17 - 2
messages/nl/portal.json

@@ -152,7 +152,13 @@
     "good": "{count} goed",
     "good": "{count} goed",
     "fair": "{count} redelijk",
     "fair": "{count} redelijk",
     "poor": "{count} slecht",
     "poor": "{count} slecht",
-    "view": "Bekijken"
+    "view": "Bekijken",
+    "serviceHistoryTitle": "Servicegeschiedenis",
+    "inspectionsTitle": "Inspecties",
+    "columnVehicle": "Voertuig",
+    "columnPlate": "Kenteken",
+    "columnServices": "Services",
+    "columnInspections": "Inspecties"
   },
   },
   "requestService": {
   "requestService": {
     "title": "Service aanvragen",
     "title": "Service aanvragen",
@@ -174,6 +180,15 @@
     "yourRequests": "Uw serviceaanvragen",
     "yourRequests": "Uw serviceaanvragen",
     "failedToLoad": "Gegevens laden mislukt.",
     "failedToLoad": "Gegevens laden mislukt.",
     "staffNotes": "Medewerkernotities: {notes}",
     "staffNotes": "Medewerkernotities: {notes}",
-    "preferred": "Gewenst: {date}"
+    "preferred": "Gewenst: {date}",
+    "notListedOption": "Mijn voertuig staat er niet bij",
+    "newVehicleHint": "Vertel ons over het voertuig, dan voegen we het toe aan je profiel.",
+    "fillVehicleDetails": "Vul het merk en model van het voertuig in",
+    "makeLabel": "Merk",
+    "makePlaceholder": "bijv. Ford",
+    "modelLabel": "Model",
+    "modelPlaceholder": "bijv. Kuga",
+    "yearLabel": "Bouwjaar",
+    "plateLabel": "Kenteken"
   }
   }
 }
 }

+ 9 - 0
messages/nl/search.json

@@ -0,0 +1,9 @@
+{
+  "title": "Zoeken",
+  "description": "Zoek naar voertuigen, klanten, werkorders en instellingen",
+  "placeholder": "Zoek op naam, kenteken, telefoon, VIN, factuur…",
+  "noResults": "Geen resultaten.",
+  "recentCustomers": "Recente klanten",
+  "services": "Serviceregistraties",
+  "toClose": "om te sluiten"
+}

+ 47 - 1
messages/nl/settings.json

@@ -103,6 +103,10 @@
         "title": "Gegevens",
         "title": "Gegevens",
         "description": "Exporteer en importeer back-up"
         "description": "Exporteer en importeer back-up"
       },
       },
+      "install": {
+        "title": "App installeren",
+        "description": "Voeg Torqvoice toe aan dit apparaat"
+      },
       "license": {
       "license": {
         "title": "Licentie",
         "title": "Licentie",
         "description": "White-label licentie"
         "description": "White-label licentie"
@@ -118,6 +122,10 @@
       "support": {
       "support": {
         "title": "Support",
         "title": "Support",
         "description": "Contact opnemen met support en de knop beheren"
         "description": "Contact opnemen met support en de knop beheren"
+      },
+      "tireHotel": {
+        "title": "Bandenhotel",
+        "description": "Seizoensopslag van banden"
       }
       }
     },
     },
     "pro": "PRO"
     "pro": "PRO"
@@ -970,7 +978,9 @@
       "lastBackup": "Laatste back-up:",
       "lastBackup": "Laatste back-up:",
       "supportPrompt": "Heeft u vragen over hoe uw gegevens worden beschermd,",
       "supportPrompt": "Heeft u vragen over hoe uw gegevens worden beschermd,",
       "supportAction": "neem contact op met ons supportteam."
       "supportAction": "neem contact op met ons supportteam."
-    }
+    },
+    "optTireHotel": "Bandenhotel",
+    "optTireHotelDesc": "Opgeslagen bandensets, stellingen, voorbereiding en opslagovereenkomsten"
   },
   },
   "license": {
   "license": {
     "title": "White-label licentie",
     "title": "White-label licentie",
@@ -1441,5 +1451,41 @@
     "contactTitle": "Contact met support",
     "contactTitle": "Contact met support",
     "contactHint": "Open het supportformulier meteen.",
     "contactHint": "Open het supportformulier meteen.",
     "contactAction": "Formulier openen"
     "contactAction": "Formulier openen"
+  },
+  "install": {
+    "title": "Torqvoice installeren",
+    "description": "Installeer Torqvoice op dit apparaat om de app vanaf het beginscherm te openen, schermvullend en zonder browserbalk. Het blijft dezelfde app met dezelfde gegevens.",
+    "installed": "Torqvoice is op dit apparaat geïnstalleerd.",
+    "iosHint": "Tik in Safari op Deel en daarna op 'Zet op beginscherm'.",
+    "unavailable": "Deze browser heeft geen installatie aangeboden. Open het browsermenu en zoek naar 'App installeren' of 'Zet op beginscherm'.",
+    "bannerHidden": "De installatiebanner is verborgen op dit apparaat.",
+    "showBanner": "Banner weer tonen"
+  },
+  "tireHotel": {
+    "title": "Bandenhotel",
+    "description": "Sla de seizoensbanden van uw klanten op en houd bij waar elke set staat.",
+    "cardTitle": "Bandenopslag",
+    "enable": "Bandenhotel inschakelen",
+    "enableHint": "Voegt bandenopslag toe aan de navigatie. Later uitzetten verbergt het weer zonder iets te verwijderen.",
+    "defaultCapacity": "Standaard banden per plank",
+    "defaultCapacityHint": "Vooraf ingevuld bij nieuwe planken. Per plank aanpasbaar.",
+    "warnPercent": "Waarschuwen bij vol (%)",
+    "warnPercentHint": "Markeert het opslagoverzicht zodra dit deel van de totale capaciteit in gebruik is.",
+    "treadTitle": "Vervangingsgrenzen",
+    "treadHint": "Wettelijke minima verschillen per land, stel dus de grenzen van uw markt in. Profiel onder de grens wordt gemarkeerd voor vervanging.",
+    "summerLimit": "Zomergrens ({unit})",
+    "winterLimit": "Wintergrens ({unit})",
+    "unitMm": "mm",
+    "unit32nds": "1/32\"",
+    "setupHint": "Stel daarna uw stellingen in.",
+    "setupLink": "Opslagindeling openen",
+    "save": "Bandenhotelinstellingen opslaan",
+    "saved": "Bandenhotelinstellingen opgeslagen",
+    "billingTitle": "Opslagfacturatie",
+    "billingHint": "Wordt ingevuld op de opslagregel bij het factureren van een set. Blijft aanpasbaar.",
+    "seasonalPrice": "Standaard opslagprijs",
+    "prepTitle": "Prijzen voorbereiding",
+    "prepHint": "Wat elk soort voorbereiding kost. Laat er een leeg en die wordt nooit gefactureerd, wat past bij een bedrijf dat wassen in de opslagprijs meeneemt. Beprijsde komen automatisch op offerte en werkorder.",
+    "prepNotCharged": "niet in rekening"
   }
   }
 }
 }

+ 473 - 0
messages/nl/tireHotel.json

@@ -0,0 +1,473 @@
+{
+  "title": "Bandenhotel",
+  "description": "Seizoensopslag van banden voor uw klanten",
+  "common": {
+    "cancel": "Annuleren",
+    "save": "Opslaan",
+    "create": "Aanmaken",
+    "delete": "Verwijderen",
+    "edit": "Bewerken"
+  },
+  "seasons": {
+    "summer": "Zomer",
+    "winter": "Winter",
+    "all_season": "Vierseizoenen",
+    "other": "Overig"
+  },
+  "statuses": {
+    "stored": "In opslag",
+    "released": "Teruggegeven",
+    "disposed": "Afgevoerd"
+  },
+  "conditions": {
+    "good": "Goed",
+    "fair": "Let op",
+    "replace": "Vervangen"
+  },
+  "positions": {
+    "front_left": "Linksvoor",
+    "front_right": "Rechtsvoor",
+    "rear_left": "Linksachter",
+    "rear_right": "Rechtsachter",
+    "spare": "Reservewiel",
+    "unspecified": "Niet opgegeven"
+  },
+  "capacity": {
+    "used": "{used} van {capacity} banden",
+    "free": "Ruimte voor {count}",
+    "freeShort": "{count} vrij",
+    "over": "{count} boven capaciteit",
+    "meterLabel": "{used} van {capacity} banden opgeslagen"
+  },
+  "tread": {
+    "placeholderMm": "mm",
+    "placeholder32": "32sten",
+    "inputLabel": "Profieldiepte, {position}",
+    "lastTime": "was {value}",
+    "worn": "{value} versleten"
+  },
+  "list": {
+    "tabAll": "Alle",
+    "tabStored": "In opslag",
+    "tabReleased": "Teruggegeven",
+    "search": "Zoek op kenteken, klant, merk of stelling",
+    "checkIn": "Banden innemen",
+    "empty": "Nog geen bandensets. Neem een set in om te beginnen.",
+    "unassigned": "Niet toegewezen",
+    "reference": "Ref",
+    "vehicle": "Voertuig",
+    "tires": "Banden",
+    "location": "Stelling",
+    "condition": "Staat",
+    "status": "Status",
+    "storedSince": "Sinds",
+    "tireCount": "{count, plural, one {# band} other {# banden}}",
+    "studded": "Spijkerbanden",
+    "roomLeft": "Ruimte voor {count}",
+    "prep": "Voorb.",
+    "tabNeedsPrep": "Voorbereiding nodig",
+    "setUpShelves": "Stellingen instellen",
+    "emptyNoShelves": "Nog geen stellingen. Richt uw opslag in voordat u banden inneemt.",
+    "tabDisposed": "Afgeschreven"
+  },
+  "checkIn": {
+    "title": "Banden innemen",
+    "description": "Leg vast wat er binnenkwam en kies waar het komt te staan.",
+    "customer": "Klant",
+    "selectCustomer": "Klant kiezen",
+    "noCustomer": "Geen klant",
+    "vehicle": "Voertuig",
+    "selectVehicle": "Voertuig kiezen",
+    "season": "Seizoen",
+    "quantity": "Aantal banden",
+    "quantityHint": "Stuk voor stuk geteld, dus een paar of vijf wielen kan ook.",
+    "brand": "Merk",
+    "brandPlaceholder": "Michelin, Bridgestone, Vredestein",
+    "model": "Model",
+    "size": "Maat",
+    "sizePlaceholder": "225/45R17",
+    "dotCode": "DOT-code",
+    "dotPlaceholder": "3623",
+    "dotHint": "Week en jaar waarin de band is gemaakt.",
+    "withRims": "Op velgen",
+    "hasTpms": "TPMS-sensoren",
+    "studded": "Spijkerbanden",
+    "location": "Waar de banden komen",
+    "choosePlace": "Kies een stelling",
+    "searchLocations": "Zoek stellingen",
+    "noLocations": "Geen stellingen gevonden",
+    "tooFull": "Niet genoeg ruimte",
+    "pickLocationFirst": "Kies een stelling voordat u de set inneemt",
+    "roomSummary": "{locations} stellingen passen bij deze set, {tires} bandenplaatsen vrij",
+    "nothingFits": "Geen stelling heeft ruimte voor {quantity} banden. Maak plaats of voeg een stelling toe.",
+    "treadTitle": "Profieldiepte bij binnenkomst",
+    "treadHint": "Optioneel, maar beslecht later elke discussie over de staat.",
+    "notes": "Notities",
+    "submit": "Innemen",
+    "success": "Set {reference} opgeslagen op {code}",
+    "failed": "Innemen mislukt",
+    "noVehiclesForCustomer": "Deze klant heeft nog geen voertuigen. Voeg er eerst een toe op de klantpagina.",
+    "treatmentsHint": "Wat er met deze set moet gebeuren voor opslag of montage.",
+    "storedBefore": "Eerder hier opgeslagen",
+    "storedBeforeHint": "Komen dezelfde banden terug? Kies de set, dan hoort dit seizoen bij de historie.",
+    "sameSet": "Dezelfde set als eerder",
+    "pieces": "{count} st.",
+    "lastOut": "Opgehaald {date}",
+    "notStored": "Nu niet opgeslagen",
+    "returned": "Set {reference} ligt weer op {code}",
+    "submitReturn": "Opnieuw opslaan",
+    "ownerFromVehicle": "Overgenomen van het voertuig",
+    "vehicleOwner": "De eigenaar van het voertuig",
+    "clearVehicle": "Voertuig wissen"
+  },
+  "storage": {
+    "title": "Stellingen",
+    "description": "Richt uw opslag één keer in en zie meteen wat waar past.",
+    "addWarehouse": "Opslaglocatie toevoegen",
+    "addShelves": "Stellingen toevoegen",
+    "warehouseName": "Naam",
+    "warehouseNamePlaceholder": "Hoofdgebouw, Container 2, Achterruimte",
+    "warehouseAddress": "Adres",
+    "warehouseCreated": "Opslaglocatie toegevoegd",
+    "warehouseDeleted": "{name} verwijderd",
+    "warehouseArchived": "{name} gearchiveerd",
+    "deleteWarehouse": "Opslaglocatie verwijderen",
+    "deleteWarehouseTitle": "{name} verwijderen?",
+    "deleteWarehouseBody": "Dit verwijdert de locatie en al haar stellingen.",
+    "deleteWarehouseBodyOccupied": "Deze locatie bevat nog {count} banden en wordt daarom gearchiveerd in plaats van verwijderd.",
+    "totalCapacity": "Totale capaciteit",
+    "totalStored": "Nu opgeslagen",
+    "totalFree": "Vrije ruimte",
+    "totalLocations": "Stellingen",
+    "locationSummary": "{occupied} van {total} stellingen in gebruik",
+    "noLocations": "Nog geen stellingen. Voeg een rek toe om hier banden op te slaan.",
+    "emptyTitle": "Nog geen opslag ingericht",
+    "emptyBody": "Voeg uw eerste gebouw of ruimte toe en vul het met stellingen.",
+    "default": "Standaard",
+    "addLocation": "Stellingen toevoegen",
+    "editLocation": "Stelling bewerken",
+    "createLocation": "Stelling aanmaken",
+    "createShelves": "Stellingen aanmaken",
+    "locationDialogDescription": "Opslag toevoegen aan {warehouse}.",
+    "tabBulk": "Een rek met stellingen",
+    "tabSingle": "Eén stelling",
+    "zone": "Zone",
+    "zonePlaceholder": "A, Ruimte 1, Noord",
+    "rack": "Rek",
+    "rackPlaceholder": "04",
+    "shelf": "Plank",
+    "shelfPlaceholder": "2",
+    "position": "Positie",
+    "positionPlaceholder": "Links",
+    "shelfFrom": "Planken van",
+    "shelfTo": "tot",
+    "code": "Code",
+    "codePlaceholder": "A-04-2",
+    "codeHint": "Staat op het label. Wordt voor u ingevuld als u dit leeg laat.",
+    "capacity": "Banden per plank",
+    "capacityHint": "Hoeveel losse banden hier passen.",
+    "notes": "Notities",
+    "bulkPreview": "Maakt {count} planken aan, {first} tot en met {last}",
+    "bulkPreviewCapacity": "Ruimte voor {tires} banden in totaal",
+    "bulkCreated": "{count} planken aangemaakt",
+    "bulkCreatedWithSkips": "{created} planken aangemaakt, {skipped} bestaande overgeslagen",
+    "bulkNothingCreated": "Die planken bestaan al",
+    "locationCreated": "Stelling toegevoegd",
+    "locationUpdated": "Stelling bijgewerkt",
+    "locationDeleted": "{code} verwijderd",
+    "deleteLocationTitle": "{code} verwijderen?",
+    "deleteLocationBody": "De stelling moet eerst leeg zijn.",
+    "saveFailed": "Opslaan mislukt",
+    "deleteFailed": "Verwijderen mislukt"
+  },
+  "movements": {
+    "check_in": "Ingenomen",
+    "check_out": "Teruggegeven",
+    "relocate": "Verplaatst",
+    "dispose": "Afgevoerd",
+    "measure": "Gemeten"
+  },
+  "detail": {
+    "move": "Verplaatsen",
+    "checkOut": "Teruggeven",
+    "conditionTitle": "Staat",
+    "noMeasurements": "Nog geen metingen vastgelegd.",
+    "historyTitle": "Geschiedenis",
+    "storageTitle": "Opslag",
+    "shelf": "Stelling",
+    "warehouse": "Locatie",
+    "notStored": "Momenteel niet in opslag.",
+    "checkedIn": "Ingenomen",
+    "checkedOut": "Teruggegeven",
+    "tiresTitle": "Banden",
+    "loadSpeed": "Draagvermogen en snelheid",
+    "ownerTitle": "Eigenaar",
+    "phone": "Telefoon",
+    "deleteTitle": "Deze bandenset verwijderen?",
+    "deleteBody": "De set en de volledige geschiedenis worden verwijderd. Dit kan niet ongedaan worden gemaakt.",
+    "deleted": "Bandenset verwijderd",
+    "deleteFailed": "Kon de set niet verwijderen",
+    "belowLimit": "{count} band(en) op of onder uw vervangingsgrens.",
+    "belowLimitHint": "Bied de klant een prijs aan nu de banden hier nog liggen.",
+    "dispose": "Afschrijven",
+    "disposeTitle": "Deze set afschrijven?",
+    "disposeBody": "Het record en de historie blijven staan en de plek komt vrij. Volgend seizoen wordt de set niet meer voorgesteld.",
+    "disposed": "Set afgeschreven",
+    "disposeFailed": "Kon de set niet afschrijven"
+  },
+  "checkOut": {
+    "title": "Banden teruggeven",
+    "description": "De set wordt van {code} gehaald.",
+    "descriptionNoLocation": "Deze set gaat terug naar de klant.",
+    "treadTitle": "Profieldiepte bij teruggave",
+    "note": "Notitie",
+    "submit": "Teruggeven",
+    "success": "Set {reference} teruggegeven",
+    "failed": "Teruggeven mislukt",
+    "prepPending": "{count} voorbereidingsklus(sen) open",
+    "conditionTitle": "Staat bij binnenkomst",
+    "conditionLead": {
+      "replace": "Een of meer banden zitten op of onder uw vervangingsgrens. Meld het de klant voordat hij wegrijdt.",
+      "fair": "Een of meer banden naderen uw vervangingsgrens. Voor het volgende seizoen even benoemen."
+    },
+    "treadHintUnmeasured": "Bij binnenkomst is niet gemeten. Dit is de laatste kans om dat te doen."
+  },
+  "relocate": {
+    "title": "Banden verplaatsen",
+    "description": "Kies de stelling waar deze set heen gaat.",
+    "newLocation": "Nieuwe stelling",
+    "note": "Notitie",
+    "submit": "Verplaatsen",
+    "success": "Set {reference} verplaatst",
+    "failed": "Verplaatsen mislukt"
+  },
+  "edit": {
+    "title": "Bandenset bewerken",
+    "description": "Corrigeer wat bij het innemen is vastgelegd. Gebruik Verplaatsen om de stelling te wijzigen.",
+    "quantityHint": "Verhogen wordt getoetst aan de stelling waar de set op staat.",
+    "rimType": "Velgtype",
+    "rimTypePlaceholder": "Lichtmetaal, staal",
+    "success": "Bandenset bijgewerkt",
+    "failed": "Kon de wijzigingen niet opslaan",
+    "treadHint": "Een gewijzigde meting wordt als nieuwe meting in de historie vastgelegd in plaats van de oude te overschrijven."
+  },
+  "treatments": {
+    "types": {
+      "wash_tires": "Banden wassen",
+      "wash_rims": "Velgen wassen",
+      "balance": "Balanceren",
+      "tpms_service": "TPMS-service",
+      "new_valves": "Nieuwe ventielen",
+      "repair": "Reparatie"
+    },
+    "statuses": {
+      "pending": "Te doen",
+      "done": "Klaar",
+      "skipped": "Overgeslagen"
+    },
+    "title": "Voorbereiding",
+    "none": "Aan deze set hoeft niets te gebeuren.",
+    "add": "Voorbereiding toevoegen",
+    "pickHint": "Vink aan wat deze set nodig heeft voordat hij terug op de auto gaat.",
+    "pendingCount": "{count} te doen",
+    "allComplete": "Alles klaar",
+    "ready": "Klaar",
+    "markDone": "Klaar",
+    "completeAll": "Alle {count} als klaar markeren",
+    "allDone": "Alle voorbereiding klaar",
+    "undo": "Terug op de lijst",
+    "saveFailed": "Kon niet opslaan",
+    "skip": "Overslaan"
+  },
+  "messaging": {
+    "action": "Bericht",
+    "title": "Klant een bericht sturen",
+    "description": "Aan {name}.",
+    "noCustomer": "Geen klant aan deze set.",
+    "noCustomerBody": "Koppel eerst een klant aan de set.",
+    "channel": "Versturen via",
+    "channels": {
+      "email": "E-mail",
+      "sms": "Sms",
+      "telegram": "Telegram"
+    },
+    "noAddress": "geen adres bekend",
+    "recipient": "Versturen naar",
+    "recipientPlaceholder": "Telefoonnummer",
+    "subject": "Onderwerp",
+    "message": "Bericht",
+    "send": "Versturen",
+    "sent": "Bericht verstuurd",
+    "failed": "Kon het bericht niet versturen",
+    "smsLong": "Lange berichten worden mogelijk over meerdere sms’jes verdeeld.",
+    "subjects": {
+      "low_tread": "Uw {season}banden",
+      "damage": "Uw {season}banden",
+      "stored": "Uw {season}banden staan opgeslagen",
+      "custom": "Uw {season}banden"
+    },
+    "bodies": {
+      "low_tread": "Hallo {customer_name}, uw {season}banden liggen bij ons opgeslagen bij {shop_name}. Bij het innemen maten {positions} nog {tread}, dat is onder de grens. Ze halen geen seizoen meer. Zullen we een prijs maken voor een nieuwe set?",
+      "damage": "Hallo {customer_name}, uw {season}banden liggen bij ons opgeslagen bij {shop_name}. Bij het innemen vonden we schade op {positions}. Bel ons wanneer het uitkomt, dan lopen we het samen door.",
+      "stored": "Hallo {customer_name}, uw {season}banden liggen nu bij ons opgeslagen bij {shop_name}. We laten het weten wanneer het tijd is om te wisselen.",
+      "custom": "Hallo {customer_name}, over uw {season}banden die bij ons liggen bij {shop_name}:"
+    },
+    "enableInSettings": "Nog niet ingesteld. Schakel het in bij Instellingen."
+  },
+  "job": {
+    "cardTitle": "Offertes en opdrachten",
+    "none": "Voor deze set is nog niets aangemaakt.",
+    "quote": "Offerte",
+    "workOrder": "Werkorder",
+    "converted": "omgezet",
+    "quoteTitle": "Nieuwe set offreren",
+    "workOrderTitle": "Deze set factureren",
+    "loadingSet": "Set wordt geladen",
+    "tires": "Banden",
+    "inStock": "{count} op voorraad",
+    "shortStock": "maar {count} op voorraad",
+    "noMatch": "Niets op voorraad past bij {size}. De regel komt erin met de prijs die u invult.",
+    "noSize": "Deze set heeft geen maat, dus voorraad kan niet gematcht worden. De regel komt erin met uw prijs.",
+    "unitPrice": "Prijs per band",
+    "createQuote": "Offerte aanmaken",
+    "createWorkOrder": "Werkorder aanmaken",
+    "quoteCreated": "Offerte {number} aangemaakt",
+    "workOrderCreated": "Werkorder aangemaakt",
+    "failed": "Kon dit niet aanmaken",
+    "bannerTitle": "Ophalen van",
+    "openSet": "Bandenset openen",
+    "prepPending": "Nog te doen voor montage: {jobs}",
+    "notOnShelf": "Deze set ligt niet meer in opslag.",
+    "targetNew": "Nieuwe werkorder",
+    "targetNewHint": "Een opdracht alleen voor deze banden.",
+    "targetExisting": "Of toevoegen aan een opdracht die al open staat voor dit voertuig",
+    "addToWorkOrder": "Aan werkorder toevoegen",
+    "workOrderUpdated": "Toegevoegd aan de werkorder",
+    "unlink": "Ontkoppelen",
+    "unlinkTitle": "Deze banden van de opdracht ontkoppelen?",
+    "unlinkBody": "De opdracht verwijst niet langer naar de bandenset. Onderdelen en arbeidsregels die er al op staan blijven staan; verwijder ze apart als ze niet gefactureerd moeten worden.",
+    "unlinked": "Banden ontkoppeld",
+    "unlinkFailed": "Kon de banden niet ontkoppelen",
+    "prep": "Voorbereiding",
+    "tiresSkipped": "Er wordt geen bandenregel toegevoegd.",
+    "total": "Totaal",
+    "searchStock": "Zoek in voorraad op merk, maat of artikelnummer",
+    "noSearchHits": "Niets in de voorraad komt overeen met \"{query}\".",
+    "otherSize": "Andere maat",
+    "inSize": "{count} in {size}",
+    "quantity": "Aantal",
+    "noStockItem": "Geen voorraadartikel, prijs handmatig",
+    "storeTires": "Banden opslaan",
+    "storage": "Opslag",
+    "storageFee": "Kosten",
+    "storagePeriod": "Periode",
+    "destination": "Waar het heen gaat",
+    "targetInvoice": "Nieuwe factuur",
+    "targetInvoiceHint": "Factureer de klant direct, zonder werkorder op een voertuig.",
+    "createInvoice": "Factuur aanmaken",
+    "needsCustomer": "Koppel een klant of voertuig aan deze set voordat u factureert.",
+    "storagePeriodEnd": "Opgehaald",
+    "storageOpenEnded": "Open",
+    "storagePeriodHint": "Staat op de factuurregel zodat de klant ziet waarvoor de kosten gelden. Laat het einde leeg tot u weet wanneer ze ophalen.",
+    "pricesHint": "Prijzen voor voorbereiding komen uit de instellingen. Elke regel is achteraf nog aan te passen.",
+    "files": "Foto’s en bestanden",
+    "filesCount": "{count} op de factuur"
+  },
+  "label": {
+    "action": "Labels",
+    "title": "Bandenlabels printen",
+    "description": "Stickers voor elke band, met een QR die deze set opent.",
+    "format": "Labelformaat",
+    "formats": {
+      "dymo_small": "Kleine rol",
+      "dymo_standard": "Standaardrol",
+      "thermal_large": "Grote rol",
+      "sheet_a4": "A4-vel"
+    },
+    "perSheet": "{count} per vel",
+    "copies": "Hoeveel",
+    "copiesHint": "één per band ({count})",
+    "fewer": "Eén minder",
+    "more": "Eén meer",
+    "print": "Printen",
+    "shelfNote": "De stelling staat er bewust niet op. Sets worden verplaatst en een geprinte stelling klopt dan niet meer. De QR laat altijd de actuele plek zien.",
+    "reference": "Ref",
+    "quantity": "{count, plural, one {# band} other {# banden}}",
+    "withRims": "Op velgen",
+    "tpms": "TPMS",
+    "studded": "Spijkers",
+    "unassigned": "Niet toegewezen",
+    "preview": "Voorbeeld",
+    "previewScale": "Op printformaat weergegeven. De code scant ook vanaf het scherm.",
+    "previewFailed": "Kon geen voorbeeld maken"
+  },
+  "vehicle": {
+    "oneOnShelf": "op {code}",
+    "inStorage": "in bandenhotel"
+  },
+  "wear": {
+    "summary": "{value} versleten tussen {from} en {to}",
+    "perSeason": "Ongeveer {value} per seizoen",
+    "seasonsLeft": "{count, plural, =0 {Nu op de grens} one {Nog ongeveer één seizoen} other {Nog ongeveer # seizoenen}}",
+    "since": "-{value}"
+  },
+  "settings": {
+    "change": "Wijzigen in instellingen",
+    "prepPrices": "Prijzen voor voorbereiding",
+    "storagePrices": "Opslagprijzen in instellingen",
+    "treadLimits": "Grens wijzigen",
+    "defaultCapacity": "Standaardcapaciteit"
+  },
+  "forecast": {
+    "title": "Volgend seizoen",
+    "subtitle": "Wat de opgeslagen sets volgens meting nodig hebben, per maat.",
+    "backToList": "Terug naar sets",
+    "tires": "Verwachte banden",
+    "atLimit": "Al op de grens",
+    "expected": "Binnen een seizoen verwacht",
+    "orderTitle": "Per maat",
+    "sizes": "{count} maten",
+    "empty": "Nog niets gemeten dicht bij de grens. Een set heeft twee bezoeken nodig voor een slijtagetempo.",
+    "nowCount": "{count, plural, one {# set op de grens} other {# sets op de grens}}",
+    "nextCount": "{count, plural, one {# set binnen een seizoen} other {# sets binnen een seizoen}}",
+    "tiresShort": "banden",
+    "lowest": "laagste {value}",
+    "now": "Op de grens",
+    "next": "Binnen een seizoen",
+    "rate": "{value} per seizoen",
+    "caveat": "Sets op de grens zijn gemeten. De rest is doorgerekend vanuit de slijtage van één klant over één periode: bruikbaar om te plannen, niet om op te bestellen.",
+    "unmeasured": "{count, plural, one {# set heeft te weinig historie en telt niet mee} other {# sets hebben te weinig historie en tellen niet mee}}",
+    "capped": "De {shown} nieuwste van {total} sets gelezen."
+  },
+  "invoiceLine": {
+    "storage": "Bandenopslag",
+    "pieces": "st.",
+    "fromDate": "vanaf {date}"
+  },
+  "setNames": {
+    "winter": "winterbanden",
+    "summer": "zomerbanden",
+    "all_season": "vierseizoenenbanden",
+    "other": "banden"
+  },
+  "jobNotes": {
+    "set": "Bandenset {reference}",
+    "shelf": "Stelling: {shelf}",
+    "notOnShelf": "niet op een stelling",
+    "onRims": "Op velgen",
+    "tiresOnly": "Alleen banden",
+    "tpms": "Heeft TPMS-sensoren"
+  },
+  "files": {
+    "title": "Foto’s en bestanden",
+    "add": "Bestanden toevoegen",
+    "none": "Nog niets toegevoegd. Fotografeer een beschadigde velg terwijl de banden voor u liggen.",
+    "captionPlaceholder": "Wat is hierop te zien?",
+    "onInvoice": "Op de factuur",
+    "invoiceHint": "Bestanden gemarkeerd voor de factuur worden bij het factureren naar de werkorder of factuur gekopieerd.",
+    "deleteTitle": "Dit bestand verwijderen?",
+    "deleteBody": "{name} wordt van de set verwijderd. Wat al naar een factuur is gekopieerd, blijft staan.",
+    "failed": "Kon dat niet opslaan",
+    "someFailed": "{count, plural, one {# bestand kon niet worden geüpload} other {# bestanden konden niet worden geüpload}}"
+  }
+}

+ 28 - 1
messages/pl/audit.json

@@ -108,6 +108,33 @@
     "scheduled_message_send_now": "Wysłano zaplanowaną wiadomość",
     "scheduled_message_send_now": "Wysłano zaplanowaną wiadomość",
     "settings_applyTaxRate": "Zastosowano stawkę podatku",
     "settings_applyTaxRate": "Zastosowano stawkę podatku",
     "settings_convertToExclusive": "Przeliczono ceny na netto",
     "settings_convertToExclusive": "Przeliczono ceny na netto",
-    "settings_convertToInclusive": "Przeliczono ceny na brutto"
+    "settings_convertToInclusive": "Przeliczono ceny na brutto",
+    "organization_sampleDataRemove": "Usunięto dane przykładowe",
+    "tire_warehouse_create": "Utworzono lokalizację magazynu opon",
+    "tire_warehouse_update": "Zaktualizowano lokalizację magazynu opon",
+    "tire_warehouse_delete": "Usunięto lokalizację magazynu opon",
+    "tire_warehouse_archive": "Zarchiwizowano lokalizację magazynu opon",
+    "tire_location_create": "Utworzono półkę na opony",
+    "tire_location_bulk_create": "Utworzono półki na opony",
+    "tire_location_update": "Zaktualizowano półkę na opony",
+    "tire_location_delete": "Usunięto półkę na opony",
+    "tire_set_check_in": "Przyjęto komplet opon",
+    "tire_set_check_out": "Wydano komplet opon",
+    "tire_set_relocate": "Przeniesiono komplet opon",
+    "tire_set_update": "Zaktualizowano komplet opon",
+    "tire_set_delete": "Usunięto komplet opon",
+    "tire_measurement_create": "Zapisano pomiary opon",
+    "tire_treatment_update": "Zaktualizowano przygotowanie",
+    "tire_treatment_mark": "Oznaczono przygotowanie",
+    "tire_treatment_complete_all": "Ukończono przygotowanie",
+    "tire_set_message_customer": "Wysłano wiadomość do klienta o komplecie opon",
+    "tire_set_create_quote": "Utworzono wycenę z kompletu opon",
+    "tire_set_create_work_order": "Utworzono zlecenie z kompletu opon",
+    "tire_set_add_to_work_order": "Dodano komplet opon do zlecenia",
+    "tire_set_unlink_work_order": "Odłączono komplet opon od zlecenia",
+    "tire_set_return": "Ponownie przyjęto komplet",
+    "tire_set_dispose": "Spisano komplet opon",
+    "tire_set_attach": "Dodano pliki do kompletu opon",
+    "tire_set_detach": "Usunięto plik z kompletu opon"
   }
   }
 }
 }

+ 6 - 2
messages/pl/common.json

@@ -10,7 +10,8 @@
     "confirm": "Potwierdź",
     "confirm": "Potwierdź",
     "saveChanges": "Zapisz zmiany",
     "saveChanges": "Zapisz zmiany",
     "edit": "Edytuj",
     "edit": "Edytuj",
-    "add": "Dodaj"
+    "add": "Dodaj",
+    "gotIt": "Rozumiem"
   },
   },
   "contextMenu": {
   "contextMenu": {
     "hint": "Wskazówka: kliknij wiersz prawym przyciskiem, aby zobaczyć akcje",
     "hint": "Wskazówka: kliknij wiersz prawym przyciskiem, aby zobaczyć akcje",
@@ -75,6 +76,9 @@
     "enterManually": "Wprowadź ręcznie",
     "enterManually": "Wprowadź ręcznie",
     "enterBarcode": "Wprowadź kod kreskowy…",
     "enterBarcode": "Wprowadź kod kreskowy…",
     "scanHint": "Skieruj aparat na kod kreskowy albo wprowadź go ręcznie",
     "scanHint": "Skieruj aparat na kod kreskowy albo wprowadź go ręcznie",
-    "iosInstallHint": "Dotknij Udostępnij > Do ekranu początkowego"
+    "iosInstallHint": "Dotknij Udostępnij > Do ekranu początkowego",
+    "installDescription": "Dodaj do ekranu głównego, aby mieć szybki dostęp",
+    "enterFullscreen": "Pełny ekran",
+    "exitFullscreen": "Zamknij pełny ekran"
   }
   }
 }
 }

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