Files
DriverVault/Web App/web/src/i18n/en.json
T
tajniak81andClaude Opus 4.8 5e435c5f77 Add per-user Toyota integration with a settings cascade
Let each user run the Toyota Connected plugin under their own MyToyota
credentials and enable/disable it for themselves in the Web App, while a
superadmin (and, in an organization, an org admin) can impose settings
from above. Resolution is a cascade — top wins, and a lower level only
fills fields the levels above left blank:

  - org user:      API Server (superadmin) -> org admin -> user
  - org-less user: API Server (superadmin) -> user

The MyToyota email + password resolve together as a pair from the highest
layer that supplies an email; brand resolves on its own; enablement is
strictly per-user, gated by the global master switch and the org gate.

API Server:
  - plugins.Manager gains RawConfig / HealthCheckWith / InvokeWith so the
    cascade can read global config and probe/invoke under a per-caller
    resolved config.
  - internal/api/integrations.go resolves the cascade and serves
    GET/PUT /api/integrations/toyota, POST .../health, GET .../vehicles.
    Secrets and inherited usernames are masked before leaving the server.
  - The toyota builtin's credentials are no longer required at the global
    layer, so the master switch can be enabled without global credentials.
  - setup-pocketbase.mjs adds a pluginSettings JSON field to the users and
    organizations collections (the user and org layers of the cascade).

Web App:
  - api.js gains getToyota/saveToyota/testToyota.
  - Settings grows an Integrations section: an enable toggle, credential
    fields with locked / "inherited from" states, a brand select, an
    org-scope switch for admins, and a live test-connection button.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:08:58 +02:00

661 lines
22 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"errors": {
"sessionExpired": "Session expired — please log in again."
},
"common": {
"cancel": "Cancel",
"save": "Save",
"saveChanges": "Save changes",
"saving": "Saving…",
"saved": "Saved ✓",
"loading": "Loading…",
"edit": "Edit",
"remove": "Remove",
"delete": "Delete",
"done": "Done",
"undo": "Undo",
"download": "Download",
"empty": "—",
"yes": "Yes",
"no": "No"
},
"nav": {
"garage": "Garage",
"settings": "Settings",
"users": "Users",
"lightMode": "Light mode",
"darkMode": "Dark mode",
"signedIn": "Signed in",
"logOut": "Log out"
},
"login": {
"title": "Sign in",
"tagline": "Your car, on track.",
"email": "Email",
"password": "Password",
"showPassword": "Show password",
"hidePassword": "Hide password",
"submit": "Sign in",
"submitting": "Signing in…",
"failed": "Login failed",
"serverSettings": "Server settings",
"apiServerUrl": "API server URL",
"leaveBlank": "Leave blank to use the default ({url}).",
"resetToDefault": "Reset to default"
},
"dashboard": {
"eyebrow": "Garage",
"title": "Your cars",
"subtitle": "Maintenance overview and service history.",
"addCar": "Add car",
"empty": "No cars yet. Click {action} to get started.",
"shared": "Shared",
"sharedReadOnly": "Shared · read-only",
"serviceLife": "Service life",
"lastService": "Last service",
"odometer": "Odometer",
"nextDue": "Next due",
"nextDueKm": "Next due km",
"serviceRecords": {
"one": "{n} service record",
"other": "{n} service records"
}
},
"admin": {
"eyebrow": "Admin",
"title": "Users",
"subtitleAll": "Accounts across every organization.",
"subtitleOrg": "Accounts in your organization.",
"subtitleOrgsNote": "Organizations are assigned in the API panel.",
"addUser": "Add user",
"colEmail": "Email",
"colName": "Name",
"colOrganization": "Organization",
"colRole": "Role",
"colCreated": "Created",
"you": "(you)",
"resetPassword": "Reset password",
"confirmDelete": "Delete {name}? This cannot be undone.",
"cantDeleteSelf": "You can't delete your own account.",
"onlySuperadminDeletes": "Only a superadmin can delete a superadmin.",
"cantChangeOwnRole": "You can't change your own role.",
"onlySuperadminEdits": "Only a superadmin can edit a superadmin.",
"createTitle": "Add a user",
"emailRequired": "Email *",
"passwordRequired": "Password *",
"minChars": "(min 8)",
"creating": "Creating…",
"createUser": "Create user",
"resetTitle": "Reset password — {email}",
"newPassword": "New password",
"setPassword": "Set password",
"roles": {
"user": "user",
"admin": "admin",
"superadmin": "superadmin"
}
},
"settings": {
"eyebrow": "Account",
"title": "Settings",
"subtitle": "Manage your account, appearance, and data.",
"account": {
"title": "Account",
"name": "Name",
"email": "Email",
"verified": "Verified",
"notVerified": "Not verified",
"resendVerification": "Resend verification email",
"sending": "Sending…",
"verificationRequested": "Verification email requested.",
"changePassword": "Change password",
"currentPassword": "Current password",
"newPassword": "New password",
"confirmNewPassword": "Confirm new password",
"mismatchYet": "Passwords don't match yet.",
"tooShort": "New password must be at least 8 characters.",
"mismatch": "New password and confirmation don't match.",
"updating": "Updating…",
"passwordUpdated": "Password updated ✓",
"updatePassword": "Update password"
},
"appearance": {
"title": "Appearance",
"theme": "Theme",
"themeLight": "light",
"themeDark": "dark",
"themeSystem": "system",
"language": "Language",
"languageHint": "App text, and the names of months and days.",
"languageFallbackHint": "This language isn't translated yet — the app text stays in English.",
"region": "Region",
"regionHint": "Number and currency layout.",
"dateFormat": "Date format",
"dateExample": "Example: {example}",
"currency": "Currency",
"currencyExample": "Example: {example} — display only, no amounts are converted.",
"fontSize": "Font size",
"fontSmall": "small",
"fontMedium": "medium",
"fontLarge": "large"
},
"profile": {
"title": "Profile",
"avatarAlt": "Avatar",
"uploading": "Uploading…",
"uploadPhoto": "Upload photo",
"bio": "Bio",
"bioPlaceholder": "A short note visible to other people in your household.",
"saveBio": "Save bio"
},
"integrations": {
"title": "Integrations",
"subtitle": "Connect external services to your account.",
"toyota": "Toyota Connected (MyToyota)",
"toyotaDesc": "Sign in with your MyToyota account to pull vehicle data from Toyota Connected Europe. Europe only.",
"enable": "Enable for my account",
"enableOrg": "Enable for the organization",
"unavailable": "This integration is turned off by the administrator.",
"orgDisabled": "This integration is turned off for your organization.",
"scopeMy": "My settings",
"scopeOrg": "Organization",
"scopeHint": "Organization settings apply to everyone in your organization who leaves a field blank.",
"readOnly": "You manage this integration's shared settings in the API Server panel; here it is read-only.",
"email": "MyToyota email",
"password": "MyToyota password",
"brand": "Brand",
"brandToyota": "Toyota",
"brandLexus": "Lexus",
"inherited": "Set above your level",
"inheritedFrom": "Inherited from {source}",
"sourceGlobal": "the administrator",
"sourceOrg": "your organization",
"sourceUser": "you",
"passwordKeep": "Leave blank to keep the saved password.",
"test": "Test connection",
"testing": "Testing…",
"save": "Save",
"saved": "Saved ✓",
"connected": "Connected",
"notConnected": "Not connected"
},
"privacy": {
"title": "Privacy & security",
"signOut": "Sign out",
"body": "Two-factor authentication isn't available yet. Sessions are held as server-issued tokens that expire on their own, so signing out here ends this device's session only — there's no per-device list to revoke from. To lock out every device, change your password above."
},
"advanced": {
"title": "Advanced",
"exportTitle": "Export your data",
"exportBody": "Download your profile and all cars, service records, and parts as JSON.",
"preparing": "Preparing…",
"exportAction": "Export data",
"importTitle": "Import your data",
"importBody": "Add cars from a previously exported JSON file. This creates new records — it doesn't merge with or overwrite anything existing.",
"importing": "Importing…",
"importAction": "Import data",
"notJson": "That file isn't valid JSON.",
"notExport": "That file doesn't look like a DriverVault export (missing a \"cars\" list).",
"confirmImport": "Import {count} car(s) from this file? This adds new records — it does not merge with or overwrite existing cars.",
"imported": "Imported {cars} car(s), {services} service record(s), {parts} part(s)."
},
"danger": {
"title": "Danger zone",
"body": "Deleting your account removes your login and profile. It does not delete your household's shared cars or service history. There's a 3-day cooldown before the deletion is final, and you can cancel any time before then.",
"deleteAccount": "Delete my account",
"typeToConfirm": "Type {email} to confirm",
"requesting": "Requesting…",
"requestDeletion": "Request deletion",
"requestedOn": "Account deletion requested on {date}.",
"canStillCancel": "You can still cancel — it becomes permanent after the 3-day cooldown.",
"cooldownPassed": "The cooldown has passed. You can now finalize the deletion.",
"cancelRequest": "Cancel deletion request",
"finalize": "Permanently delete my account",
"confirmFinalize": "This permanently deletes your account. This cannot be undone. Continue?"
}
},
"car": {
"allCars": "← All cars",
"share": "Share",
"shared": "Shared",
"sharedReadOnly": "Shared · read-only",
"tabs": {
"info": "Information",
"services": "Service history",
"technical": "Technical check history",
"maintenance": "Maintenance",
"fuel": "Fuel",
"documents": "Documents",
"parts": "Parts catalog",
"reminders": "Reminders"
},
"info": {
"oilSpec": "Engine oil spec",
"transmissionOil": "Transmission oil",
"differentialOil": "Differential oil",
"brakeFluid": "Brake fluid",
"coolant": "Coolant",
"odometer": "Odometer",
"serviceInterval": "Service interval",
"nextDue": "Next due",
"registrationPlate": "Registration plate",
"registrationCountry": "Registration country",
"vin": "VIN",
"fuelType": "Fuel type",
"buildDate": "Build date",
"firstRegistration": "First registration"
},
"services": {
"title": "Service history",
"add": "Add service",
"empty": "No service records yet.",
"colDate": "Date",
"colKm": "Km",
"colNextDate": "Next date",
"colNextKm": "Next km",
"colOil": "Oil & Oil filter",
"colEngineFilter": "Engine air filter",
"colCabinFilter": "Cabin air filter",
"colNotes": "Notes",
"colFile": "File",
"confirmDelete": "Delete this service record?"
},
"technical": {
"title": "Technical check history",
"subtitle": "Mandatory roadworthiness inspections. Recurs on time alone, whatever the odometer reads.",
"add": "Add check",
"empty": "No technical checks yet.",
"colDate": "Date",
"colResult": "Result",
"colNextCheck": "Next check",
"colStatus": "Status",
"colStation": "Station",
"colCost": "Cost",
"colNotes": "Notes",
"colFile": "File",
"passed": "Passed",
"failed": "Failed",
"confirmDelete": "Delete this technical check?"
},
"maintenance": {
"title": "Maintenance",
"subtitle": "Workshop visits and repairs. Routine servicing lives under Service history.",
"add": "Log visit",
"empty": "No workshop visits logged yet.",
"colDate": "Date",
"colKm": "Km",
"colType": "Type",
"colWork": "Work done",
"colWorkshop": "Workshop",
"colStatus": "Status",
"colCost": "Cost",
"colFile": "File",
"underWarranty": "Under warranty · {days}d left",
"confirmDelete": "Delete this workshop visit?"
},
"fuel": {
"title": "Fuel",
"subtitle": "Consumption is measured between full tanks.",
"add": "Log refill",
"empty": "No refills logged yet.",
"average": "Average",
"best": "Best",
"worst": "Worst",
"costPerKm": "Cost per km",
"refills": "Refills",
"totalLiters": "Total litres",
"totalSpent": "Total spent",
"trackedDistance": "Tracked distance",
"avgPrice": "Avg. price {price}/L",
"needTwoTanks": "Log at least two full tanks to see consumption figures.",
"colDate": "Date",
"colKm": "Km",
"colLiters": "Litres",
"colCost": "Cost",
"colPerLiter": "Per litre",
"colDistance": "Distance",
"colConsumption": "Consumption",
"colStation": "Station",
"colFile": "File",
"partial": "partial",
"gap": "gap",
"confirmDelete": "Delete this refill?"
},
"documents": {
"title": "Documents",
"subtitle": "Insurance, pollution certificates and other paperwork with renewal dates.",
"add": "Add document",
"empty": "No documents yet.",
"colType": "Type",
"colTitle": "Title",
"colProvider": "Provider",
"colIssued": "Issued",
"colRenewal": "Renewal",
"colStatus": "Status",
"colFile": "File",
"confirmDelete": "Delete this document?"
},
"reminders": {
"title": "Reminders",
"subtitle": "Renewal and service reminders are added automatically from your documents and service history.",
"add": "Add reminder",
"empty": "Nothing to be reminded about yet.",
"automatic": "Automatic",
"repeats": "Repeats",
"at": "at {km}",
"doneRollForward": "Done · roll forward",
"markDone": "Mark done",
"reopen": "Reopen",
"confirmDelete": "Delete this reminder?"
},
"parts": {
"title": "Parts catalog",
"add": "Add part",
"empty": "No parts yet.",
"colPart": "Part",
"colPartNumber": "Part number",
"colNotes": "Notes",
"colFile": "File",
"confirmDelete": "Delete this part?"
},
"delete": {
"title": "Delete this car?",
"body": "This permanently deletes {name} and everything logged against it — {services}, {maintenance}, {fuel}, {documents} and {parts}. This cannot be undone.",
"services": {
"one": "{n} service record",
"other": "{n} service records"
},
"maintenance": {
"one": "{n} workshop visit",
"other": "{n} workshop visits"
},
"fuel": {
"one": "{n} refill",
"other": "{n} refills"
},
"documents": {
"one": "{n} document",
"other": "{n} documents"
},
"parts": {
"one": "{n} part",
"other": "{n} parts"
},
"typeToConfirm": "Type {name} to confirm",
"deleting": "Deleting…",
"confirm": "Delete permanently"
}
},
"attachment": {
"legend": "Attachment",
"hint": "PDF or image, up to 10MB.",
"attached": "Attached: {name}",
"willBeRemoved": "Attachment will be removed on save."
},
"forms": {
"car": {
"addTitle": "Add a car",
"editTitle": "Edit car",
"name": "Name *",
"make": "Make",
"model": "Model",
"year": "Year",
"registration": "Registration",
"registrationCountry": "Registration country",
"registrationCountryPlaceholder": "Poland",
"vin": "VIN",
"vinPlaceholder": "Vehicle Identification Number",
"fuelType": "Fuel type",
"buildDate": "Build date",
"firstRegistration": "First registration",
"oilSpec": "Engine oil spec",
"currentKm": "Current odometer (km)",
"transmissionOilSpec": "Transmission oil spec",
"differentialOilSpec": "Differential oil spec",
"brakeFluidSpec": "Brake fluid spec",
"coolantSpec": "Coolant spec",
"serviceIntervalDays": "Service interval (days)",
"serviceIntervalKm": "Service interval (km)",
"technicalCheckIntervalDays": "Technical check interval (days)",
"technicalCheckHint": "Prefills each check's next-due date. Any check can override it with the date printed on its certificate.",
"submit": "Add car"
},
"service": {
"addTitle": "Add service record",
"editTitle": "Edit service record",
"date": "Date *",
"odometer": "Odometer (km)",
"changedParts": "Changed parts",
"oil": "Oil & Oil filter",
"engineFilter": "Engine air filter",
"cabinFilter": "Cabin air filter",
"attachmentLegend": "Receipt or service-book page",
"notes": "Notes",
"autoHint": "Next service date (+{days}d) and km (+{km}) are computed automatically.",
"submit": "Add service"
},
"technical": {
"addTitle": "Add technical check",
"editTitle": "Edit technical check",
"date": "Check date *",
"result": "Result *",
"passed": "Passed",
"failed": "Failed",
"validUntil": "Valid until",
"failedHint": "A failed check certifies nothing, so no next date is derived from it.",
"derivedHint": "Leave blank to use the car's interval (+{days}d → {date}). Enter the date on the certificate when it differs.",
"cost": "Cost",
"station": "Station",
"stationPlaceholder": "Stacja Kontroli Pojazdów",
"attachmentLegend": "Inspection certificate",
"notes": "Notes",
"submit": "Add check"
},
"part": {
"addTitle": "Add part",
"editTitle": "Edit part",
"name": "Part name *",
"namePlaceholder": "Oil Filter",
"partNumber": "Part number",
"notes": "Notes",
"notesPlaceholder": "Fits 2015–2020 · buy in pairs",
"attachmentLegend": "Photo or spec sheet",
"submit": "Add part"
},
"fuel": {
"addTitle": "Log refill",
"editTitle": "Edit refill",
"date": "Date *",
"odometer": "Odometer (km) *",
"liters": "Litres *",
"cost": "Total cost",
"pricePerLiter": "Price per litre: {price}",
"tank": "Tank",
"fullTank": "Filled to full",
"missedFill": "I missed logging a refill before this one",
"tankHint": "Consumption is measured between full tanks, so partial fills count towards the next full one. Flagging a missed refill leaves that stretch out of the figures instead of reporting it as unrealistically economical.",
"station": "Station",
"notes": "Notes",
"attachmentLegend": "Receipt",
"submit": "Log refill"
},
"maintenance": {
"addTitle": "Log workshop visit",
"editTitle": "Edit workshop visit",
"date": "Date *",
"odometer": "Odometer (km)",
"type": "Type",
"status": "Status",
"description": "What was done *",
"descriptionPlaceholder": "Replaced alternator and drive belt",
"workshop": "Workshop",
"location": "Location",
"partsUsed": "Parts replaced",
"partsUsedPlaceholder": "Alternator 27060-0T010, belt 90916-02660",
"laborCost": "Labour cost",
"partsCost": "Parts cost",
"total": "Total: {total}",
"invoiceNumber": "Invoice number",
"warrantyUntil": "Warranty until",
"attachmentLegend": "Invoice",
"notes": "Notes",
"submit": "Log visit"
},
"document": {
"addTitle": "Add document",
"editTitle": "Edit document",
"type": "Type",
"title": "Title *",
"titlePlaceholder": "Third-party liability 2026",
"provider": "Provider",
"reference": "Policy / certificate no.",
"issued": "Issued",
"renewalDate": "Renewal date",
"renewalHint": "Leave the renewal date blank for a document that never expires. Setting it adds a reminder automatically.",
"cost": "Cost",
"attachmentLegend": "Scan or photo",
"notes": "Notes",
"submit": "Add document"
},
"reminder": {
"addTitle": "Add reminder",
"editTitle": "Edit reminder",
"title": "Title *",
"titlePlaceholder": "Swap to winter tyres",
"type": "Type",
"remindMe": "Remind me",
"onDate": "On date",
"atOdometer": "At odometer (km)",
"triggerHint": "Set either or both — with both, whichever comes first wins.",
"currentKm": "The car is at {km} now.",
"repeat": "Repeat (optional)",
"everyDays": "Every … days",
"everyKm": "Every … km",
"recurringHint": "Marking this done will roll it forward instead of closing it.",
"oneOffHint": "Leave blank for a one-off reminder that closes when you mark it done.",
"notes": "Notes",
"noTrigger": "Set a due date, a due odometer reading, or both.",
"submit": "Add reminder"
},
"share": {
"title": "Share {name}",
"body": "Give another user access to this car. Read-only lets them view; read & write also lets them edit the car and its service records and parts.",
"userEmail": "User email",
"read": "Read-only",
"write": "Read & write",
"submit": "Share",
"peopleWithAccess": "People with access",
"notShared": "Not shared with anyone yet."
}
},
"enums": {
"fuelType": {
"petrol": "Petrol (gasoline)",
"petrol_lpg": "Petrol (gasoline) + LPG",
"diesel": "Diesel",
"diesel_lpg": "Diesel + LPG",
"hybrid": "Hybrid",
"electric": "Electric",
"hydrogen": "Hydrogen"
},
"maintenanceType": {
"repair": "Repair",
"inspection": "Inspection",
"bodywork": "Bodywork",
"tyres": "Tyres",
"diagnostics": "Diagnostics",
"recall": "Recall",
"warranty": "Warranty work",
"other": "Other"
},
"maintenanceStatus": {
"scheduled": "Scheduled",
"in_progress": "In progress",
"completed": "Completed"
},
"documentType": {
"insurance": "Insurance",
"pollution": "Pollution certificate",
"registration": "Registration",
"inspection": "Inspection",
"roadTax": "Road tax",
"warranty": "Warranty",
"other": "Other"
},
"reminderTypeShort": {
"maintenance": "Maintenance",
"document": "Document",
"service": "Service",
"inspection": "Inspection",
"other": "Other"
},
"reminderType": {
"maintenance": "Maintenance",
"document": "Document renewal",
"service": "Service",
"inspection": "Inspection",
"other": "Other"
}
},
"status": {
"noData": "No data",
"serviceOverdueDays": "Service Overdue {days}d",
"dueInDays": "Due in {days}d",
"okDays": "OK · {days}d",
"noKm": "No km",
"serviceOverdueKm": "Service Overdue {km} km",
"inKm": "In {km} km",
"kmLeft": "{km} km left",
"expiredAgo": "Expired {days}d ago",
"expiresToday": "Expires today",
"renewInDays": "Renew in {days}d",
"validDays": "Valid · {days}d",
"noExpiry": "No expiry",
"done": "Done",
"noTrigger": "No trigger",
"overdue": "Overdue",
"overdueBy": "Overdue {parts}",
"dueIn": "Due in {parts}",
"upcoming": "Upcoming",
"today": "today",
"days": "{days}d",
"km": "{km} km"
}
}