Files
DriverVault/Phone App/assets/i18n/en.json
T
tajniak81andClaude Opus 5 cd16d4383f Orgs: let any user create an organization and become its admin
Organization writes were superadmin-only, so standing up a tenant needed
an out-of-band superadmin. Creating one is now self-service, and an admin
manages the org they belong to.

- POST /api/orgs is open to any authenticated user. A creator who isn't a
  superadmin must have no organization yet (a single-valued membership
  relation means a second one would abandon the first), and is promoted to
  the new org's admin and first member in the same request. If that
  promotion fails the org is rolled back, so it is never left stranded
  with nobody able to administer it. Superadmins still create tenants
  without joining them.
- PATCH/DELETE are manager-gated and scope an admin to their own org. An
  admin deletes theirs only as its sole member: they are detached and
  demoted to a plain user before the record goes, so the org is empty when
  it is removed. Other members still block deletion with a 409.
- /api/me now carries organization + organizationName, which the clients
  need to tell "no org yet" from "org you administer".

The panel, Web App (new OrgManager.vue in Settings) and Phone App (new
_OrganizationSection) all mirror the server's gates rather than
re-deciding them. The Phone App cached its role at login and gates the
Users tab on it, so AuthService.adoptRole refreshes that from the profile
instead of making a freshly promoted admin sign in again.

Covered by orgs_test.go, which drives the real handler + middleware chain
against a stand-in PocketBase: promotion, the already-a-member refusal,
superadmin staying unattached, the rollback, own-org scoping, the
detach-and-demote, and the blocking-member 409.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 16:54:02 +02:00

338 lines
13 KiB
JSON

{
"common": {
"save": "Save",
"saving": "Saving…",
"saved": "Saved ✓",
"cancel": "Cancel",
"remove": "Remove",
"retry": "Retry",
"required": "Required",
"confirm": "Confirm",
"empty": "—"
},
"nav": {
"garage": "Garage",
"charging": "Charging",
"settings": "Settings",
"users": "Users"
},
"login": {
"tagline": "Your car, on track.",
"email": "Email",
"password": "Password",
"showPassword": "Show password",
"hidePassword": "Hide password",
"submit": "Sign in",
"submitting": "Signing in…",
"or": "or",
"signInWithFace": "Sign in with face recognition",
"signInWithFingerprint": "Sign in with fingerprint",
"signInWithBiometrics": "Sign in with biometrics",
"enrollTitle": "Enable biometric sign-in?",
"enrollBody": "Next time you can sign in with {method} instead of typing your password.",
"enrollNotNow": "Not now",
"enrollEnable": "Enable",
"methodFace": "face recognition",
"methodFingerprint": "your fingerprint",
"methodBiometrics": "biometrics",
"bioReasonSignInAs": "Sign in as {email}",
"bioReasonSignIn": "Sign in to DriverVault",
"savedInvalid": "Saved sign-in is no longer valid. Please sign in with your password.",
"serverSettings": "Server settings",
"apiServerUrl": "API server URL",
"leaveBlank": "Leave blank to use the default.",
"reset": "Reset",
"savedNote": "Saved ✓",
"resetNote": "Reset ✓"
},
"lock": {
"title": "DriverVault is locked",
"reason": "Unlock DriverVault",
"unlock": "Unlock",
"unlocking": "Unlocking…",
"usePassword": "Use password instead"
},
"dashboard": {
"eyebrow": "YOUR CARS",
"title": "Garage",
"addCar": "Add car",
"lightMode": "Light mode",
"darkMode": "Dark mode",
"logOut": "Log out",
"empty": "No cars yet.",
"shared": "Shared",
"sharedReadOnly": "Shared · read-only",
"serviceLife": "SERVICE LIFE",
"lastService": "Last service",
"currentOdometer": "Current odometer",
"nextDue": "Next due",
"nextDueKm": "Next due km",
"serviceRecords": {
"one": "{n} service record",
"other": "{n} service records"
}
},
"charging": {
"eyebrow": "CHARGING & MAP",
"title": "Nearby chargers",
"tabs": {
"public": "Public chargers",
"home": "Home chargers"
},
"liveMap": "Live map",
"youAreHere": "You are here",
"session": {
"chargingNow": "Charging now",
"rate": "Rate",
"added": "Added",
"cost": "Cost",
"done": "Done",
"stop": "Stop charging",
"idle": "Not charging",
"idleHint": "Plug in at a station to start a session."
},
"control": {
"title": "Charger control",
"connected": "Connected",
"disconnected": "Offline",
"serialPlaceholder": "Charger serial (e.g. A5191-XXXXXXXX)",
"refresh": "Refresh",
"status": "Connector",
"meter": "Energy",
"start": "Start charging",
"stop": "Stop charging",
"limit": "Current limit",
"applyLimit": "Apply limit",
"clearLimit": "Clear limit",
"reset": "Reset charger",
"resetConfirm": "Reboot this charger now? Any active charging session will be interrupted. Re-enter your password to confirm.",
"resetPassword": "Your account password",
"connectHint": "Enter your charger's serial and refresh. The charger must be connected to DriverVault's OCPP backend (set up in Settings → Integrations)."
},
"stations": {
"heading": "Nearby",
"homeHeading": "Your chargers",
"noControlHint": "Turn on Own or Proxy CSMS in Settings → Integrations to control your home charger here.",
"count": {
"one": "{n} station",
"other": "{n} stations"
},
"free": "{avail} of {total} free",
"full": "Full",
"offPeak": "off-peak",
"homeCharger": "Home charger"
}
},
"settings": {
"title": "Settings",
"tabs": {
"personal": "Personal settings",
"integrations": "Integrations"
},
"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",
"ankerSolix": "Anker Solix (V1 Smart EV Charger)",
"ankerSolixDesc": "Sign in with your Anker account to pull EV charger data from the Anker Solix cloud.",
"ankerEmail": "Anker account email",
"ankerPassword": "Anker account password",
"country": "Country",
"countryHint": "Two-letter country code of your Anker account (e.g. DE, GB, US).",
"controlMode": "Control mode",
"controlModeHint": "How DriverVault controls the charger over OCPP.",
"controlOff": "Off (monitoring only)",
"controlOwn": "Own CSMS (full control)",
"controlProxy": "Proxy CSMS (relay + control)",
"controlTitle": "Charger control (OCPP)",
"controlOwnHint": "The charger connects directly to DriverVault as its Central System. Point the charger's OCPP backend at the endpoint below.",
"controlProxyHint": "DriverVault relays to Anker's cloud and can inject commands. Point the charger's OCPP backend at the endpoint below.",
"controlProvisionSteps": "In the Anker app (or the charger's OCPP settings), set the OCPP backend URL to the endpoint and the authorization key to the token above.",
"chargerSerial": "Charger serial",
"controlCheck": "Check status",
"controlGenerate": "Generate token",
"controlEndpoint": "OCPP endpoint",
"controlToken": "Auth token",
"controlTokenOnce": "Copy this token now — it's shown only once and can't be retrieved later.",
"controlRevoke": "Revoke token",
"controlRevokeConfirm": "Revoke this charger's control token? It will disconnect and can't reconnect until you generate a new one.",
"controlConnected": "Connected to control backend",
"controlDisconnected": "Not connected"
},
"account": {
"title": "Account",
"name": "Name",
"nameSaved": "Name saved.",
"email": "Email",
"verified": "Verified",
"notVerified": "Not verified",
"sending": "Sending…",
"resendVerification": "Resend verification email",
"verificationRequested": "Verification email requested.",
"changePassword": "Change password",
"currentPassword": "Current password",
"newPassword": "New password (min 8)",
"confirmNewPassword": "Confirm new password",
"tooShort": "New password must be at least 8 characters.",
"mismatch": "New password and confirmation don't match.",
"passwordUpdated": "Password updated.",
"updating": "Updating…",
"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.",
"currency": "Currency",
"currencyHint": "Example: {example}. Amounts are display-only — nothing is converted.",
"dateFormat": "Date format",
"dateHint": "Example: {example}",
"fontSize": "Font size",
"fontSmall": "Small",
"fontMedium": "Medium",
"fontLarge": "Large"
},
"profile": {
"title": "Profile",
"working": "Working…",
"uploadPhoto": "Upload photo",
"remove": "Remove",
"bio": "Bio",
"bioHint": "A short note visible to other people in your household.",
"bioSaved": "Bio saved.",
"saveBio": "Save bio"
},
"security": {
"title": "Security",
"checkingDevice": "Checking device…",
"biometricSignIn": "Biometric sign-in",
"biometricAvailable": "Sign in with {method} instead of your password.",
"biometricUnavailable": "No biometrics are enrolled on this device.",
"methodFaceOrFingerprint": "face or fingerprint",
"methodFace": "face recognition",
"methodFingerprint": "fingerprint",
"methodBiometrics": "biometrics",
"turnedOff": "Biometric sign-in turned off",
"enabled": "Biometric sign-in enabled",
"couldNotEnable": "Could not enable: {error}",
"confirmPassword": "Confirm your password",
"password": "Password"
},
"org": {
"title": "Organization",
"titleAll": "Organizations",
"subtitleOwn": "The organization you administer",
"subtitleNone": "Create one to manage your own team",
"subtitleAll": "Tenants users belong to",
"nameLabel": "Organization name",
"namePlaceholder": "Acme Fleet",
"newOrg": "New organization",
"create": "Create organization",
"createHint": "You'll become its admin and can then add and manage users.",
"rename": "Rename",
"delete": "Delete",
"deleteTitle": "Delete organization?",
"empty": "No organizations yet.",
"confirmDelete": "Delete the organization “{name}”? This cannot be undone.",
"confirmDeleteOwn": "Delete your organization “{name}”? You'll be removed from it and become a regular user. This cannot be undone."
},
"privacy": {
"title": "Privacy & security",
"body": "Two-factor authentication isn't available yet. Sessions are held as server-issued tokens that expire on their own, so signing out ends this device's session only. To lock out every device, change your password above.",
"signOut": "Sign out"
},
"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 deletion is final, and you can cancel any time before then.",
"deleteAccount": "Delete my account",
"typeToConfirm": "Type {email} to confirm",
"typeEmailPrompt": "Type your email to confirm.",
"requesting": "Requesting…",
"requestDeletion": "Request deletion",
"requestedOn": "Account deletion requested on {date}. {tail}",
"cooldownPassed": "The cooldown has passed. You can now finalize the deletion.",
"canStillCancel": "You can still cancel — it becomes permanent after the 3-day cooldown.",
"cancelRequest": "Cancel deletion request",
"permanentlyDelete": "Permanently delete",
"finalizeTitle": "Delete account?",
"finalizeBody": "This permanently deletes your account. This cannot be undone.",
"delete": "Delete"
}
},
"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",
"warrantyExpiredAgo": "Warranty expired {days}d ago",
"warrantyEndsIn": "Warranty ends in {days}d",
"underWarranty": "Under warranty · {days}d"
}
}