// English (en). Mirrors the key structure of sq.ts (the default/fallback). Any key // missing here falls back to Albanian. See wiki/concepts/i18n.md. import type { Catalog } from "./sq.js"; export const en: Catalog = { common: { loading: "Loading…", logout: "Log out", cancel: "Cancel", close: "Close", save: "Save", none: "—", themeDark: "dark", themeLight: "light", theme: "Theme", fontSmaller: "Smaller text", fontLarger: "Larger text", fontSize: "Text size", today: "Today", yesterday: "Yesterday", months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ], }, auth: { title: "Parking System", username: "Username", password: "Password", signIn: "Sign in", signingIn: "Signing in…", }, update: { available: "Update available", prompt: "Version {{version}} is available. Install now and restart?", }, nav: { booth: "Booth", shift: "Shift", setup: "Setup", devices: "Devices", tariff: "Tariff", tariffLab: "Tariff Lab", subscriptions: "Subscriptions", plans: "Plans", site: "Site", users: "Users", roles: "Roles", shifts: "Shifts", reports: "Reports", recycleBin: "Recycle bin", logs: "Logs", backup: "Backup", profile: "Profile", }, profile: { title: "My profile", accountSection: "Account", fullName: "Full name", fullNamePh: "First and last name", email: "Email", emailPh: "you@example.com", username: "Username", role: "Role", saveProfile: "Save profile", profileSaved: "Profile saved.", passwordSection: "Change password", currentPassword: "Current password", newPassword: "New password", confirmPassword: "Confirm password", changePassword: "Change password", passwordChanged: "Password changed.", passwordsDontMatch: "Passwords don't match.", passwordTooShort: "Password must be at least {{min}} characters.", }, status: { live: "LIVE", connecting: "CONNECTING", offline: "OFFLINE", }, devices: { footerTitle: "Devices", none: "No devices configured.", catAccess: "Relay", catReader: "Reader", catCamera: "Camera", catPrinter: "Printer", catVision: "ANPR", // Role/direction suffixes for the chip label (e.g. "Reader entry"). role: { entry: "entry", exit: "exit", both: "entry/exit", mixed: "entry/exit", lane: "at lane", booth: "at booth", }, state: { ready: "ready", degraded: "degraded", offline: "offline", }, allOk: "all ready", issuesCount: "{{count}} with issues", issuesTitle: "Device issues", clickForIssues: "Click for details", checkedAt: "checked {{time}}", }, booth: { processTicket: "Process ticket", scanPlaceholder: "Scan or type ticket number…", laneEntry: "Entry", laneExit: "Exit", openTicket: "Read", occupancy: "Occupancy", occUnavailable: "occupancy unavailable", inside: "inside", of: "of", uncapped: "uncapped", free: "free", lotFull: "● lot full", liveFeed: "Live feed", events: "events", noEventsYet: "No events yet — entries and exits will stream here.", activeSessions: "Active sessions", insideCount: "inside", noActiveSessions: "No active sessions.", noMatch: "No sessions match the filter.", // Active-sessions table column headers. colWho: "Ticket / subscriber", colPlate: "Plate", colEntry: "Entry", colElapsed: "Elapsed", badgeOverstay: "overstay", badgeOverstayTitle: "Paid session. The customer failed to exit during the grace period. A new period began.", plateTitle: "Licence plate recognized ANPR.", // filters filterSearchSessions: "Search ticket / subscriber / plate…", filterSearchFeed: "Search event / identity / plate…", filterAll: "All", fKindTransient: "Transient", fKindSubscription: "Subscribers", fSrcBooth: "Booth", fSrcReader: "Reader", fEvtEntry: "Entry", fEvtExit: "Exit", fEvtPay: "Pay", fEvtVoid: "Void", fEvtAnomaly: "Anomaly", openPayExit: "Open pay / exit", openBarrier: "Open barrier", openBarrierTitle: "Human-intervention barrier open (audited)", barrierOpened: "barrier opened", openManually: "open manually", badgeExiting: "exiting", badgePaid: "paid", badgeUnpaid: "unpaid", badgeSubscription: "subscription", evtEntry: "ENTRY", evtExit: "EXIT", evtPay: "PAY", evtVoid: "VOID", evtOpenCmd: "OPEN→", evtOpenObserved: "OPEN✓", evtShiftOpen: "SHIFT+", evtShiftZ: "SHIFT Z", evtCashMovement: "CASH", evtCashIn: "PAY-IN", evtCashOut: "PAY-OUT", evtAnomaly: "ANOMALY", evtRefused: "REFUSED", // live-feed event detail line + classification badges (computed from payload) evtNoReason: "no reason recorded", badgeEntryRefused: "entry refused", badgeExitRefused: "exit refused", badgeLotFull: "lot full", badgeBarrierFailed: "barrier did not open", badgeManualOpen: "manual open", badgeSubRefused: "subscription refused", badgeSubSale: "subscription sale", badgeWindowCharge: "out-of-window — owes fee", badgeNoTicket: "ticket not printed", feedSourceBooth: "booth", feedSourceReader: "reader", // event-detail modal eventDetail: "Event detail", edType: "Type", edTime: "Time", edIndex: "Ledger index", edDirection: "Direction", edSource: "Source", edIdentity: "Identity", edReason: "Reason", edSnapshots: "Snapshots", edPayload: "Signed payload", edChain: "Chain", edSignature: "Signature", edKeyId: "Key", edPrevHash: "Prev hash", edNoPayload: "No payload on this event.", edCopy: "Copy", edCopied: "Copied", edDetails: "Details", edAuditData: "Audit data (signature & chain)", edAmount: "Amount", edTender: "Tender", edSession: "Session", edPlate: "Plate", edCategory: "Category", edOperator: "Operator", edTariffVersion: "Tariff version", edRawPayload: "Raw signed payload", edOccurrence: "Occurrence id", subscriber: "Subscriber", edVia: "Entry medium", viaQr: "QR code", viaCard: "RFID card/chip", viaPlate: "Plate", }, // Localized messages for the signed REASON_CODES (see @parking/shared). Keys MUST // match the codes 1:1; {{param}} placeholders are filled from the event's // reasonParams. Legacy events with no code fall back to the signed English `reason`. reason: { "entry.refused.full": "Entry refused — lot full ({{count}}/{{capacity}})", "entry.held.noTicket": "Entry held — ticket not printed: {{detail}}", "exit.refused.closed": "Exit refused — session already closed", "exit.refused.noSession": "Exit refused — unknown ticket", "exit.refused.unpaid": "Exit refused — not paid (take payment first)", "exit.refused.graceExpired": "Exit refused — walk-back grace expired (top-up required)", "exit.open.noBarrier": "Exit recorded, but no exit barrier is configured — open manually", "exit.open.unavailable": "Exit recorded, but the barrier is unavailable — open manually", "exit.open.failed": "Exit recorded, but the barrier did not open — open manually", "exit.freeGrace": "Free entry-grace (no charge)", "exit.manualOpen": "Manual barrier open (human intervention)", "sub.refused.notFound": "Subscription refused — not found", "sub.refused.outOfWindow": "Subscription refused — {{status}}/out-of-window", "sub.refused.noSession": "Subscription exit with no open session (already out / never entered)", "sub.refused.atCapacity": "Subscription refused — at capacity ({{inUse}}/{{max}} cars in)", "sub.refused.unpaidWindow": "Exit refused — out-of-window charge unpaid ({{amount}} {{currency}}); pay at the booth", "void.ticketCancelled": "Ticket cancelled — {{reason}}", }, tariff: { title: "Tariff", noRateCard: "No tariff published yet — the pay station can't charge until you publish one.", activeSince: "Active since {{date}} · {{count}} version(s) in history. Publishing creates a new version; past sessions keep their original pricing.", currency: "Currency", freeEntryGrace: "Free entry grace (min)", billingIncrement: "Billing increment (min)", dailyCap: "Daily cap (blank = none)", dailyCapPh: "e.g. 12.00", lostTicketFee: "Lost-ticket fee", exitGrace: "Exit walk-back grace (min)", rateBlocks: "Rate blocks", rateBlocksHint: "Each band lasts a number of hours and bills at its own price; bands are consumed in order (the first hours, then the next hours). The last band is \"thereafter\" (open-ended) — its price applies once the ladder is exhausted. Price is per billing increment.", bandDuration: "Band duration", hoursUnit: "hours", egHours: "e.g. 2", pricePerIncrement: "Price / increment", thereafter: "thereafter (open-ended)", remove: "Remove", addBlock: "+ Add block", publishNewVersion: "Publish new version", publishing: "Publishing…", publishedOk: "New tariff version published — it's now the active rate.", defaultCard: "Base rate (always active)", defaultCardHint: "The base rate applied when no time/seasonal tier matches. This alone is enough for most car parks.", modeLadder: "Hourly ladder", modeFlat: "Flat price", modeStepped: "By duration (up-to)", steppedHint: "Set the TOTAL price for a stay up to a given time (e.g. up to 3h = 500). The first row whose limit ≥ the duration wins (the limit is inclusive). The last row's total repeats as a per-day price for longer stays.", stepUpTo: "Up to", stepTotal: "Total price", addStep: "+ Add row", steppedTiersConflict: "⚠ Time/seasonal tiers do NOT apply when the base rate is 'By duration (up-to)' — the engine ignores them entirely. Remove the tiers, or switch the base rate to 'Hourly ladder' or 'Flat price'. Publishing is blocked until this is fixed.", tiersAdvanced: "Advanced: time & seasonal tiers", tiersHint: "Optional. Add tiers that apply only at certain hours/days/dates or for a category (e.g. happy hour, night rate, weekend, bus). With no tiers, just the base rate is published.", tierName: "Name", tierPriority: "Priority", tierCategory: "Category", tierCategoryPh: "e.g. bus", tierDays: "Days", tierHours: "Hours", tierDates: "Dates", tierOvernight: "(crosses midnight)", addTier: "+ Add tier", dow1: "Mon", dow2: "Tue", dow3: "Wed", dow4: "Thu", dow5: "Fri", dow6: "Sat", dow0: "Sun", }, setup: { title: "Setup", intro: "Add your barrier controllers first. Then add readers (QR/RF), cameras, printers", catControllers: "Controllers (barriers + entry button)", catReaders: "Readers (QR / RFID)", catCameras: "Cameras (snapshot + plate)", catPrinters: "Printers (tickets / vouchers)", nounController: "controller", nounReader: "reader", nounCamera: "camera", nounPrinter: "printer", add: "+ Add {{noun}}", addAnother: "+ Add another {{noun}}", addTitle: "Add {{noun}}", editTitle: "Edit {{noun}}", needControllerFirst: "Add a controller first — a {{noun}} points at one of its relays.", failedToLoad: "Failed to load setup: {{error}}", loadingCatalog: "Loading device catalog…", dirEntry: "Entry", dirExit: "Exit", dirBoth: "Both (entry + exit)", inherits: "inherits {{direction}}", warnTitle: "⚠ Saved, but action needed:", dismiss: "Dismiss", disabled: "(disabled)", edit: "Edit", remove: "Remove", removing: "Removing…", confirmRemove: "Remove this {{driver}} device?", noRelaysSet: "no relays set", unbound: "unbound", noDrivers: "No drivers registered.", chooseDevice: "Choose a device…", scan: "Scan for controllers", scanning: "Scanning…", noControllersFound: "No controllers found on the LAN.", use: "Use", test: "Test connection", testing: "Testing…", saveConfigure: "Save & configure", saveChanges: "Save changes", saving: "Saving…", cancel: "Cancel", testFailed: "Test failed: {{error}}", saveFailed: "Save failed: {{error}}", deviceLabel: "Device:", preconditionsOk: "● preconditions OK", autoFixedOnSave: "(auto-fixed on save)", backendPushIp: "Backend push IP", chooseAddress: "Choose an address…", onDeviceSubnet: "— on device subnet", noNicOnSubnet: "⚠ no NIC on the device's subnet — the device may not reach the backend", backendIpHint: "The address this device will POST input events to.", relaysTitle: "Relays on this controller", relaysHint: "Each relay opens one barrier. Set its direction; for transient entry, set which input terminal the entry button is wired to.", outputsTitle: "Outputs — relays (barriers + lamp)", outputsHint: "Relays are OUTPUTS: each opens a barrier (or drives the button lamp). Set the relay number and direction. The input terminals (button, sensor) are in the Inputs section below.", pulseOpenMs: "Pulse open (ms)", pulseOpenHint: "How long a barrier relay is held open (jog). Applies to all barrier relays.", inputsTitle: "Inputs — terminals (button, sensor)", inputsHint: "Inputs are TERMINALS the host READS: the entry button and the presence/radar sensor. Each belongs to an entry barrier — it triggers or gates that relay.", inputsIdleHigh: "Inputs idle HIGH", inputsIdleHighHint: "This board idles inputs HIGH (status 1111); a press pulls LOW.", relay: "Relay", // Generic input rows: terminal + role + the relay it serves. inputTerminal: "Terminal", inputServesRelay: "Serves relay", roleButton: "Entry button", rolePresenceLoop: "Presence (loop)", rolePresenceRadar: "Presence (radar)", roleAlertTrigger: "Alert trigger", addInput: "+ Add input", entryCooldown: "Cooldown after ticket (s)", entryCooldownHint: "When there's no presence sensor: repeat button presses are suppressed for this many seconds after a ticket. A fallback (not a guarantee) — a determined abuser can wait it out.", activeLow: "Active-low", activeLowHint: "Tick if the presence sensor (e.g. a radar) idles HIGH and goes LOW on detection — the opposite of the button. This inverts that terminal's reading so 'present' is read correctly.", eventRadarAlert: "Radar alert (lamp)", triggerInput: "Trigger input", triggerInputHint: "The input terminal (the radar) that starts this relay blinking. Blinks while the trigger is active but the camera doesn't confirm a car; solid on once the camera confirms; off otherwise.", lockLane: "Lock from", lockLaneHint: "Which camera locks the lamp solid: the entry or the exit camera. An exit radar must lock on the EXIT camera.", lockLaneEntry: "Entry camera", lockLaneExit: "Exit camera", blinkOnMs: "Blink on (ms)", blinkOffMs: "Blink off (ms)", addRelay: "+ Add relay", anpr: "Plate recognition (ANPR)", anprHint: "Read plates on this camera: the vision service reads the plate from each snapshot and records it (both entry and exit). Requires the vision service running.", anprAuto: "Auto open/close on subscriber plate", anprAutoHint: "Let THIS camera auto-open the barrier when it recognises a subscriber's plate. Turn OFF on a shared entry/exit lane's exit camera, so a car driving IN isn't auto-EXITed by its back plate (recognition still runs — only the auto-trigger is off).", testAnpr: "Test ANPR", anprTesting: "Testing ANPR…", testAnprHint: "Takes a live snapshot from this camera and tries to read a plate, reporting the result and the time it took. Point a plate at the camera first.", anprOk: "✓ Read plate {{plate}} — {{confidence}}% confidence, {{ms}} ms", anprLowConfidence: "(low confidence — advisory only)", anprTookMs: "{{ms}} ms", "anprFail.vision-disabled": "Vision service is disabled — enable it (VISION_ENABLED) to test ANPR.", "anprFail.snapshot-failed": "Couldn't take a snapshot from the camera (offline or unreachable).", "anprFail.no-plate": "No plate found in the snapshot.", // Printer test slip — pushes a real slip so the admin can confirm it physically prints. testPrint: "Print test slip", printTesting: "Printing…", testPrintHint: "Sends a test slip to the printer now. ‘Connected’ only opens the link — this confirms the printer actually feeds paper.", printOk: "✓ Test slip sent ({{ms}} ms). Check the printer.", "printFail.print-failed": "The printer rejected the job (out of paper, cover open, or the link dropped).", // Reveal/hide toggle for a secret field (e.g. the device web password). revealSecret: "Show password", hideSecret: "Hide password", alarmUrlTitle: "Alarm Server settings (enter these in the camera)", alarmUrlHint: "Enter these in the camera at Configuration → Event → … → Alarm Settings (or Notify Surveillance Center). The camera POSTs every event here — no polling.", alarmUrlCopy: "Copy all", alarmUrlCopied: "Copied ✓", alarmUrlSaveFirst: "Save the camera first — the address is generated once the device has an ID. Re-open it for editing to see it.", alarmUrlTestFirst: "Click “Test connection” first — that resolves this host's IP on the camera's network (so the camera can reach it).", alarmFieldHost: "Destination IP / Host", alarmFieldUrl: "URL", alarmFieldProtocol: "Protocol", alarmFieldPort: "Port", whichBarrier: "Which barrier does this device serve?", controller: "Controller", choose: "Choose…", relayLabel: "Relay {{relay}} ({{direction}})", noRelaysConfigured: "This controller has no relays configured.", }, lab: { title: "Tariff Lab", intro: "Test rates in time (day/night windows, daily caps, overstay) in seconds, with no waiting. Pricing uses the same logic as the booth; nothing is written to the ledger.", loadTicket: "Load from a real ticket", loadTicketPh: "Ticket number / identity", load: "Load", loaded: "Loaded session {{id}}", tariffVersion: "Tariff version", activeVersion: "Active version (current)", entered: "Entered", asOf: "As of (now/exit)", now: "Now", category: "Category", categoryPh: "e.g. bus (blank = car)", payment: "Payment", paid: "paid", graceMin: "grace (min)", price: "Compute price", pricing: "Pricing…", outcome: "Outcome", amountDue: "Amount due", billedPeriod: "Billed period", overstay: "overstay", settled: "settled", periodStart: "Period start", graceExpires: "Grace expires", curve: "Duration curve", curveHint: "Fee from entry at several durations — see where the daily cap flattens or windows shift.", }, subs: { title: "Subscriptions", unnamed: "(unnamed)", unbound: "unbound", car_one: "{{count}} car", car_other: "{{count}} cars", cred: "cred", plates: "{{count}} plate(s)", noPrice: "no price", perDay: "day", perWeek: "week", perMonth: "month", plan: "Plan", quantity: "Cars", quantityHint: "cars covered by this subscription (price ×N)", count: "How many", planNone: "— comp / no charge —", planNoneAvail: "No plans defined — an admin must create one first.", version: "Version", versionCurrent: "current", versionHint: "Move this subscriber to another version of the same plan. The price stays as billed; only the access hours change going forward.", versionOnlyOne: "only one version of this plan", everyDay: "every day", allDay: "24/7 (no window)", quoting: "pricing…", quotePrompt: "pick an end date", quoteLine: "{{periods}} × {{unit}} · {{amount}} {{currency}}", tender: "Paid by", tenderCash: "Cash", tenderCard: "Card", tenderHint: "Recorded as a signed payment (feed, drawer, Z-report).", saleRecorded: "Sale recorded: {{amount}} {{currency}} ({{tender}}).", saleNoShift: "⚠ No shift was open — open one so the takings land in a Z-report.", edit: "Edit", revoke: "Revoke", delete: "Delete", noneYet: "No subscriptions yet.", add: "+ Add subscription", new: "New subscription", editTitle: "Edit subscription", holderName: "Holder name", contact: "Contact", carLimit: "Car limit", limitCarsInAtOnce: "limit cars in at once", validFrom: "Valid from", validTo: "Valid to", validToEnd: "Valid to (end)", isoDateOptional: "ISO date (optional)", boundPlates: "Bound plates", commaSeparatedOptional: "comma-separated (optional)", credentials: "Credentials", credentialsCardQr: "Credentials (card / QR)", rfCardTag: "RF card/tag", rfCardTagSoon: "RF card/tag (soon)", rfPlaceholder: "card number (or read the card)", readCard: "Read card", captureChooseReader: "Choose a reader, then present the card:", captureNoReaders: "No readers configured.", captureWaiting: "Present the card to the reader…", captureTimeout: "Timed out with no card read. Try again.", captured: "Card read: {{value}}", qr: "QR", qrAutoGen: "QR code is auto-generated on save", credentialValue: "credential value", addCredential: "+ credential", needCredentialOrPlate: "A subscription needs at least one credential OR one bound plate.", save: "Save", cancel: "Cancel", saved: "Subscription saved.", savedPrinted: "Subscription saved — QR code printed.", savedPrintFailed: "Subscription saved, but printing failed ({{error}}). Use \"Print code\".", printCode: "Print code", printedOn: "Code printed on {{printer}}.", confirmRevoke: "Revoke subscription for {{name}}? It will be refused at the barrier.", confirmDelete: "Delete subscription for {{name}}? (Past events are kept.)", statusActive: "active", statusSuspended: "suspended", statusRevoked: "revoked", }, plans: { title: "Subscription plans", intro: "Admin-defined plans the operator sells from — the price is looked up, never typed. Editing a plan publishes a new version; past sales keep their recorded price.", add: "+ Add plan", noneYet: "No plans yet. Add one so the booth can sell subscriptions.", colName: "Name", colPrice: "Price", colHours: "Hours", colUsedBy: "Used by", colEffective: "Effective", allHours: "24/7", usedByCount: "{{active}} active / {{total}}", usedByNone: "none", usedByTitle: "Show the subscriptions on this plan", subscribers: "Subscribers", inForce: "in force", retired: "retired", versionCount: "{{count}} versions", newVersion: "New version", retire: "Retire", reactivate: "Reactivate", delete: "Delete", deleteTitle: "Delete this plan permanently (only when no subscription uses it)", reactivated: "“{{name}}” is sellable again.", confirmDelete: "Delete the plan “{{name}}” permanently? This can't be undone.", deleted: "Plan “{{name}}” deleted.", deleteInUse: "Can't delete — subscriptions still use this plan. Retire it instead.", newTitle: "New plan", newVersionTitle: "Publish new version", newVersionHint: "This publishes a NEW version of the plan — existing sales keep their original price.", period: "Period", pricePer: "Price per period", namePlaceholder: "e.g. Hotel daily", needName: "A plan name is required.", needPrice: "Enter a price greater than zero.", saved: "Plan saved.", confirmRetire: "Retire the plan “{{name}}”? It will no longer be sellable (history is kept).", needWindow: "Enter valid window times (HH:MM).", needDays: "Select at least one day for the window.", restrictTimes: "Restrict parking times (charge transient tariff outside the window)", days: "Days", window: "Window", enterAfter: "enter after", exitBefore: "· exit before", grace: "Grace", graceHint: "minutes tolerance around the window edges", timeframesHint: "A scan outside the allowed window is charged the normal transient tariff for the out-of-window minutes (early entry is deferred to exit; late exit is gated until paid).", }, site: { occupancy: "Occupancy:", noCapacitySet: "(no capacity set)", free: "free", full: "FULL", capacityLabel: "Capacity (blank = no limit):", capacityPlaceholder: "e.g. 120", printExitDefault: "Print exit ticket by default", printExitHint: "(booth far from exit → customer self-exits with a voucher)", reserveSubs: "Reserve subscriber spots", reserveSubsHint: "Hold a spot for each active subscriber's car(s) even when they're not parked — transients see 'full' sooner. Off: only cars inside count (handle overflow by valet).", anprEntry: "Auto-open for subscriber plates (ANPR)", anprEntryHint: "When on, a subscriber's plate read by a lane camera opens the barrier through the normal subscription gate. Off: subscribers must use their card/QR. Plate snapshots are still recorded either way.", parkDetails: "Park details (optional — shown on tickets/receipts)", save: "Save", saved: "Saved.", fieldParkName: "Park name", fieldParkNamePh: "e.g. Airport Parking", fieldOperator: "Operator (legal name)", fieldOperatorPh: "operating company", fieldNius: "NIUS", fieldNiusPh: "e.g. L01234567A", fieldAddress: "Address", fieldPhone: "Phone", fieldEmail: "Email", }, users: { title: "Users", add: "+ Add user", new: "New user", none: "No users.", username: "Username", password: "Password", passwordHint: "At least 8 characters.", newPassword: "new password", role: "Role", resetPassword: "Reset password", edit: "Edit", editTitle: "Edit user", save: "Save", delete: "Delete", confirmDelete: "Delete user \"{{name}}\"?", detailsSection: "Details (optional)", fullName: "Full name", phone: "Phone", email: "Email", address: "Address", }, roles: { title: "Roles", add: "+ Add role", new: "New role", editTitle: "Edit role", name: "Name", permissions: "Permissions", builtin: "built-in", edit: "Edit", delete: "Delete", confirmDelete: "Delete role \"{{name}}\"?", permCount_one: "{{count}} permission", permCount_other: "{{count}} permissions", userCount_one: "{{count}} user", userCount_other: "{{count}} users", }, shift: { label: "Shift:", open: "open", notStarted: "not started", since: "since", startShift: "Start shift", starting: "Starting…", endShift: "End shift", ending: "Ending…", endConfirm: "End this shift? A signed Z-report is recorded and printed.", drawer: "Drawer:", openingFloatInherited: "(opening float inherited from the prior shift)", drawerCashAdmin: "Drawer cash (admin) — load or remove the float", drawerVoucher: "Drawer voucher — operator raises, an admin authorizes", amount: "amount", reasonPlaceholder: "reason (e.g. opening float)", load: "Load +", remove: "Remove −", authName: "admin username", authPassword: "admin password", authRequired: "An admin must authorize: enter their username and password.", mandatArketimi: "Receipt (in) +", mandatPagese: "Disbursement (out) −", voucherHint: "A receipt (Mandat Arkëtimi) adds cash; a disbursement (Mandat Pagese) removes it. The float only moves with an admin's sign-off.", voucherRecorded: "Voucher {{no}} recorded. Drawer now {{amount}}.", enterPositive: "Enter a positive amount.", drawerNow: "Drawer now {{amount}}.", zReport: "SHIFT CLOSE", viewTakings: "Takings so far", xReport: "TAKINGS SO FAR", asOf: "as of", xReportHint: "View only — nothing is recorded. These figures are signed when the shift is closed.", payments: "Payments:", cash: "Cash:", card: "Card:", srcTickets: "Tickets:", srcSubscriptions: "Subscriptions:", srcSubWindow: "out-of-window", drawerSection: "— Drawer —", openingFloat: "Opening cash:", cashTaken: "Cash taken:", cashAdded: "Cash added:", cashRemoved: "Cash removed:", expectedDrawer: "Expected drawer:", printedToReceipt: "Printed to booth receipt.", recordedNoPrinter: "Recorded (no printer to print to).", // Header shift control + the booth shift gate. headerNoShift: "No shift", headerOpen: "Open shift", headerClose: "Close shift", headerHeldBy: "Shift open — {{operator}}", headerHeldByShort: "Shift: {{operator}}", gateTitle: "Open a shift to process tickets", gateBody: "No shift is open. Open your shift so payments and exits are recorded against it.", gateOtherTitle: "The open shift belongs to another operator", gateOtherBody: "{{operator}} has an open shift. Only one shift may be open at a time — they must close theirs before you can open yours.", openNow: "Open shift now", opening: "Opening…", }, shifts: { title: "Shift history", myTitle: "My shifts", none: "No closed shifts.", operator: "Operator", started: "Started", ended: "Ended", payments: "Payments", cash: "Cash", card: "Card", srcTickets: "Tickets", srcSubscriptions: "Subscriptions", srcSubWindow: "out-of-window", expectedDrawer: "Expected drawer", filterFrom: "From", filterTo: "To", allOperators: "All operators", apply: "Apply", clear: "Clear", timeframe: "Timeframe", preset_yesterday: "Yesterday", preset_week: "Last week", preset_month: "Last month", preset_all: "All", preset_custom: "Custom", selectAShift: "Select a shift to see its activity log.", noActivity: "No activity in this shift.", current: "current", drawerSection: "Drawer", openingFloat: "Opening cash", cashTaken: "Cash taken", cashAdded: "Cash added", cashRemoved: "Cash removed", loadFailed: "Failed to load shifts.", }, reports: { title: "Reports", groupBy: "Group by", exportCsv: "Export CSV", loadFailed: "Couldn't load the report: {{error}}", noData: "No data in this range.", footnote: "Counts and money are summed from the signed event log. Times shown in {{tz}}.", preset: { today: "Today", "7d": "7 days", "30d": "30 days", "90d": "90 days" }, bucket: { hour: "Hour", day: "Day", month: "Month" }, kpi: { entries: "Entries", exits: "Exits", revenue: "Revenue", payments: "Payments", avgStay: "Avg stay", subscribers: "Subscribers", }, chart: { flow: "Entries & exits over time", revenue: "Revenue ({{currency}})", mix: "Revenue mix", peakHours: "Entries by hour of day", breakdown: "Breakdown", }, mix: { ticket: "Transient", subSales: "Subscriptions", subWindow: "Out-of-window" }, row: { cash: "Cash", card: "Card", closed: "Closed sessions", medianStay: "Median stay", subActive: "Active subscriptions", subCars: "Cars covered", }, }, recycleBin: { title: "Recycle bin", retentionNote: "Deleted items are kept for {{days}} days, then permanently removed.", empty: "Nothing deleted. Items you delete appear here, recoverable until they expire.", col: { type: "Type", item: "Item", deleted: "Deleted", actions: "" }, kind: { user: "User", role: "Role", subscription: "Subscription", plan: "Plan", tariff: "Tariff", }, restore: "Restore", purge: "Purge", purgeConfirmTitle: "Purge permanently?", purgeConfirmBody: "Permanently delete “{{label}}”? It cannot be restored after this.", purgeIrreversible: "This is irreversible.", }, logs: { title: "System logs", refresh: "Refresh", level: "Level", source: "Source", since: "Since", apply: "Apply", clear: "Clear", allLevels: "All levels", allSources: "All sources", frontend: "Frontend", backend: "Backend", time: "Time", message: "Message", status: "Status", path: "Path", empty: "No logs.", }, backup: { title: "Backup", intro: "An encrypted copy of the database (the signed ledger) to an external disk. Runs automatically every day and from the button below.", statusTitle: "Status", configured: "Enabled", notConfigured: "Not configured", notConfiguredHint: "Set BACKUP_TARGET_DIR and BACKUP_KEY on the server to enable backups.", running: "Running…", idle: "Idle", lastSuccess: "Last successful backup", lastError: "Last error", never: "Never", lastFile: "File", size: "Size", pruned: "Pruned", runNow: "Back up now", runSuccess: "Backup complete.", runFailed: "Backup failed.", notConfiguredError: "Backup is not configured.", restoreNote: "Restore is not done here — it's an out-of-band step when provisioning a fresh appliance (needs the backup file + the keys you escrowed offsite).", targetLabel: "Backup location", targetPlaceholder: "e.g. /mnt/backup or /media/usb", targetHint: "An absolute path to a mounted disk (USB/SATA) or a network share (SMB/NFS).", save: "Save", saved: "Saved.", test: "Test target", testOk: "The location is writable.", testEmpty: "Enter a path.", testMissing: "The location does not exist.", testNotDir: "The path is not a directory.", testNotWritable: "The directory is not writable.", keyMissing: "The encryption key (BACKUP_KEY) is missing on the server — set it to enable backups.", }, pay: { ticket: "Ticket", entry: "Entry", now: "Now", duration: "Duration", statusLabel: "Status", paid: "PAID", unpaid: "UNPAID", overstay: "OVERSTAY", overstayHint: "Earlier session paid. The customer failed to exit during the grace period. Payment for the new period is required. The total below is the new period's fee.", topUp: "New period due", total: "Total", noTariff: "no tariff", tender: "Tender", cash: "Cash", card: "Card", printExitVoucher: "Print exit ticket", selfExitHint: "(customer self-exits at the exit)", payAndOpen: "Pay + open barrier", payAndVoucher: "Pay + print voucher", openBarrier: "Open barrier", printVoucher: "Print voucher", takingPayment: "taking payment…", printingVoucher: "printing voucher…", opening: "opening…", noSessionFound: "No session found for this ticket.", alreadyClosed: "This session is already closed (exited {{time}}).", lookingUp: "looking up…", paidBarrierOpened: "Paid — barrier opened. Car may exit.", paidExitRecorded: "Paid and exit recorded, but the barrier did not open: {{reason}}.", subscription: "SUBSCRIPTION", plan: "Plan", prepaid: "PREPAID", subAssistHint: "Prepaid subscription. Open the barrier to assist the exit (faulty reader / missing card). No payment.", assistOpenReveal: "Assist open (faulty reader / lost card)…", payWindowCharge: "Take payment", windowPaidHint: "Window charge paid. Open the barrier to let the subscriber out.", windowCharge: "OUT-OF-WINDOW", windowChargeHint: "This subscriber parked outside their plan's allowed hours. They owe the transient tariff for the out-of-window time — take payment, then open the barrier.", subBarrierOpened: "Barrier opened for the subscriber (intervention recorded).", voucherPrinted: "Exit voucher printed on {{printer}}. Customer self-exits at the exit.", // payment receipt (transparency slip) receiptPrintFailed: "(receipt didn't print — use \"Reprint receipt\".)", receiptReprinted: "Receipt reprinted on {{printer}}.", reprintReceipt: "Reprint receipt", reprinting: "printing…", cancelTicket: "Cancel ticket", cancelTicketTitle: "Cancel this ticket", cancelTicketHint: "Cancels a wrongly-printed ticket. A signed record is kept (operator + reason); the original entry is never deleted.", cancelReason: { misprint: "Misprint", test: "Test", wrongVehicle: "Wrong vehicle", }, cancelReasonPlaceholder: "Reason for cancelling (required)…", confirmCancelTicket: "Confirm cancellation", cancelling: "cancelling…", ticketCancelled: "Ticket cancelled.", noSnapshots: "no snapshots", loadingSnapshots: "loading snapshots…", snapEntry: "entry", snapExit: "exit", snapFailed: "camera unreachable", plate: "Plate", }, };