'beacon.hint':'Once a day this installation reports that it exists, so the project can see how many people run it. It sends only the two values below — a random id and the version — and nothing else: no IP is stored, no names, no addresses, nothing about what you use it for. On by default, and you can turn it off here.',
'beacon.collectorHint':'Where the report goes. Leave it unless you run your own collector.',
'beacon.beCollector':'This instance is the collector',
'beacon.receiving':'Receiving anonymous reports — id and version, never an IP.',
'beacon.total':'Total',
'beacon.active7':'Active 7 days',
'beacon.active30':'Active 30 days',
'beacon.byVersion':'By version (30 days):',
'beacon.publish':'Publish the count at /api/v1/beacon/count',
'beacon.publishHint':'No login, cached for a minute. Reports installations seen in the last 30 days — not a running total, which would only ever climb.',
'Reminders and messages from other people, even with the tab closed. The message is encrypted to your browser on the way — the push service cannot read it.',
'push.unsupported':
'This browser does not support it. Safari needs the app added to the home screen; over plain HTTP it only works on localhost.',
'push.blocked':
'The browser has blocked notifications for this site. That can only be changed in the browser’s own settings — a page cannot ask again once the answer has been no.',
'push.on':'On for this device.',
'push.turnOff':'Turn off',
'push.turnOn':'Turn on for this device',
'push.perBrowser':'Applies only to the browser you are in right now.',
'push.recent':'Recent',
'push.none':'Nothing yet.',
'push.open':'Open',
'push.markAllRead':'Mark all as read',
'push.version':'Version',
'push.running':'Running',
'push.seeRelease':'See the release',
// --- settings: API tokens ------------------------------------------------------
'tokens.title':'API tokens',
'tokens.hint':
'What a script, a CalDAV client or an MCP connector signs in with. A token carries exactly your permissions — it can do nothing you cannot.',
'tokens.copyNow':'Copy it now.',
'tokens.onceOnly':'It is shown this once — the server only has its hash.',
'tokens.copy':'Copy',
'tokens.copied':'Copied.',
'tokens.done':'Done',
'tokens.mcpBefore':'Using it as an',
'tokens.mcpLink':'MCP connector',
'tokens.mcpAfter':'in Claude? Then this is the address the dialog wants:',
'tokens.copyAddress':'Copy the address',
'tokens.name':'Name',
'tokens.namePlaceholder':'e.g. my laptop, or the backup script',
'tokens.expires':'Expires',
'tokens.never':'Never',
'tokens.in30':'In 30 days',
'tokens.in90':'In 90 days',
'tokens.inYear':'In a year',
'tokens.expiryHint':
'A calendar client that has been subscribed for years needs “never”. A script you run once does not.',
'tokens.create':'Create a token',
'tokens.none':'No tokens yet.',
'tokens.revoke':'Revoke',
// --- settings: AI --------------------------------------------------------------
'ai.title':'AI',
'ai.hint':
'Used in two places: splitting a task into sub-tasks, and writing a short overview of what is outstanding. The key stays on your server and is only sent to the provider you pick here.',
'ai.provider':'Provider',
'ai.off':'Off',
'ai.model':'Model',
'ai.baseURL':'Address',
'ai.apiKey':'API key',
'ai.save':'Save',
'ai.saved':'Saved.',
'ai.weekly':'Weekly overview',
'ai.weeklyHint':'A short note on what looks most important right now.',
'push.unreadOne':'{n} unread.',
'push.unreadMany':'{n} unread.',
'ai.keyStored':'A key is stored — leave this blank',
'ai.keyBlankHint':
'The field is blank on purpose. The key is never sent back to the browser — a settings page that refills a password field is one that will one day leak a key into a screenshot.',
'ai.keyNewHint':'Stored on the server and never shown again.',
'tokens.lastUsed':'last used {when}',
'tokens.neverUsed':'never used',
'tokens.expired':'expired {when}',
'tokens.expiresOn':'expires {when}',
'tokens.revokeNamed':'Revoke {name}',
// --- settings: people ----------------------------------------------------------
'users.invite':'Invite somebody',
'users.inviteHint':
'There is no open registration. You send a link and the person picks their own name and password — so there is never a password two people know.',
'users.emailAddress':'Email address',
'users.emailPlaceholder':'name@example.com',
'users.mustBeEmail':'Must be an email address.',
'users.sendInvite':'Send invitation',
'users.inviteMade':'The invitation is ready. Send the link to them:',
'users.pending':'Awaiting a reply',
'users.pendingHint':
'Invitations sent and not used yet. Only the link’s hash is stored, so it cannot be shown again — if it went to the wrong person, revoke it.',
'users.revoke':'Revoke',
'users.accounts':'Accounts',
'users.accountsHint':'Everybody on this instance.',
'users.admin':'administrator',
'users.you':'you',
'users.makeAdmin':'Make administrator',
'users.removeAdmin':'Remove administrator',
'users.delete':'Delete',
'users.deleteQuestion':'Delete {name} <{email}> for good?',
'users.deleteTakes':'That deletes {projects} and the {tasks} in them.',
'users.deleteLeaves':'{tasks} in other people’s projects stay, without an author.',
'users.deleteFinal':'It cannot be undone. There is no trash for accounts.',
'users.revokeQuestion':'Revoke the invitation to {email}? The link stops working.',
// --- settings: history and errors -----------------------------------------------
'history.title':'History',
'history.hint':
'Everything that has been done on this server, across every project — including the ones you are not in. A project’s own history panel shows the same rows one project at a time; this is the question those cannot answer. Rows stay when an account is deleted: what was done stops having a name, not having happened.',
'history.person':'Person',
'history.all':'All',
'history.project':'Project',
'history.event':'Event',
'history.clear':'Clear',
'history.more':'Load more',
'history.loading':'Loading…',
'history.noMatch':'Nothing matches the filters.',
'history.nothing':'Nothing has been recorded yet.',
'Every 500 the API has answered, with what the handler was attempting and the error it hit. Written to the database rather than only to the log — a container’s log starts over on every restart, so the explanation is usually gone by the time anybody looks. Kept for 30 days.',
'errors.none':'No errors recorded. That is the right state.',
// --- the views -------------------------------------------------------------------
'view.delegated':'Waiting on others',
'view.delegatedEmpty':'You are not waiting on anybody.',
'view.labelEmpty':'No open tasks with this label.',
'view.filterEmpty':'Nothing matches right now.',
'view.filterFailed':'The filter could not be run.',
'view.upcoming':'Upcoming',
'view.mode':'View',
'view.list':'List',
'view.week':'Week',
'view.month':'Month',
'view.board':'Board',
'view.calendar':'Calendar',
'view.emptySection':'Empty',
'view.addSection':'+ Add a section',
'view.sectionName':'The section’s name',
'view.newSection':'New section',
'view.prevMonth':'Previous month',
'view.nextMonth':'Next month',
'view.prevWeek':'Previous week',
'view.nextWeek':'Next week',
'view.weekNumber':'week {n}',
'view.more':'+{n} more',
// --- how long ago ----------------------------------------------------------------
'when.justNow':'just now',
'when.minutes':'{n} min. ago',
'when.hours':'{n} hours ago',
'when.days':'{n} days ago',
'when.never':'never',
'when.neverSignedIn':'never signed in',
// --- settings: data ---------------------------------------------------------------
'data.importNotes':'Import notes (a zip of Markdown files)',
'data.importNotesHint':'One .md file per note; the first line becomes the title. Pictures beside the files come with them and are attached to the note. The script in tools/ gets your Apple Notes out.',
'data.notesImported':'{n} notes and {files} files brought in.',
'Pick the CSV export of one project. Priorities are converted, not copied: Todoist writes 4 for what its interface calls P1.',
'data.trashHint':
'Deleted projects, with their tasks. They come back exactly as they were — except for tasks you had deleted individually first; there was a reason for those.',
'data.backupsHint':
'The database is copied every night and the most recent fourteen are kept. Counted, not dated: a container that has been off for a month must not come back and delete all of them for being too old.',
'Tokens and mailbox passwords are encrypted inside the copy, and the key is not: it sits in a file beside the database. That is the point — a copy that goes astray does not open your mail. But restore a copy somewhere without that file and the mailboxes must be connected again.',
'Attachments are not included. They are content-addressed and only ever added to, so fourteen identical copies would fill the volume they were meant to protect — take the whole of /data if you want everything.',
'data.restoreHint':
'Putting a copy back is deliberately not a button here: a database cannot be swapped under a server that is writing to it, and a half-finished swap destroys the very thing it was meant to save. Stop the container, put the file in place of verdande.db in the data directory, delete the -wal and -shm beside it, and start it again.',
'data.exportHint':
'Everything, in its raw form. That is the guarantee that you can leave — an export that has been tidied up is an export that has lost something.',
'data.templatesHint':
'A project saved as a shape you can use again. The dates in a template are relative, so they are worked out from the start date you pick.',
'Flag an email in iCloud, Fastmail or any other provider and it becomes a task. One way: removing the flag does nothing to the task. Office 365 cannot be used here — Microsoft has closed IMAP to passwords.',
'int.addMailbox':'Add a mailbox',
'int.host':'IMAP server',
'int.username':'Username',
'int.appPassword':'App password',
'int.appPasswordHint':
'Not your ordinary password. iCloud and most others issue a separate one for applications, which you can withdraw on its own. It is stored encrypted.',
'int.connect':'Connect',
'int.connecting':'Connecting …',
'int.disconnect':'Disconnect',
'int.disconnectMailbox':'Disconnect {name}? The tasks it made will stay.',
'int.mailboxConnected':'The mailbox is connected.',
'Star an email, or give it a label, and it becomes a task. One way: removing the star does nothing to the task.',
'int.connectGmail':'Connect Gmail',
'int.fromEnv':
'The client is set in the environment (VERDANDE_GMAIL_CLIENT_ID), so it cannot be changed here. That is deliberate: a value in the Rune’s manifest is a decision reapplied on every restart, and a form that could overwrite it would make the manifest a lie.',
'Register a client in Google Cloud Console → APIs & Services → Credentials → OAuth client ID → Web application and paste the two values here. The same client serves both Gmail and Google Calendar: enable whichever API you need, and add both redirect URIs — the one below and the calendar\'s, which is shown in the Google Calendar panel. Your own account has to be listed as a test user for as long as the app is unverified with Google.',
'int.secretStored':'Stored — fill this in only to change it',
'int.save':'Save',
'int.saved':'Saved.',
'int.noClient':
'No OAuth client has been registered with Google yet. An administrator has to do that before this button can be used.',
'int.connectedAs':'Connected as',
'int.unknownAccount':'an unknown account',
'int.whatMakesATask':'What makes a task',
'int.starred':'Starred',
'int.aLabel':'A particular label',
'int.both':'Both',
'int.gmailLabelName':'The label’s name in Gmail',
'int.gmailLabelExample':'e.g. Tasks',
'int.fetchNow':'Fetch now',
'int.disconnect':'Disconnect',
'int.feed':'Calendar feed',
'int.feedHint':
'Subscribe in Apple Calendar, Google or Thunderbird. A calendar client cannot sign in, so the key in the address is the whole password — do not share it.',
'int.address':'Address',
'int.copy':'Copy',
'int.newLink':'New link',
'int.newLinkHint':
'A new link breaks every existing subscription immediately. That is the point: it is what you do when the old one has ended up somewhere it should not be.',
'int.mailToTask':'Mail to task',
'int.mailHint':
'Send or forward an email here and the subject line is read as a quick add — “Invoice Anders p1 #Company” works here too.',
'int.noMailServer':
'No mail server is configured on this server, so the address below cannot receive anything yet. Set VERDANDE_SMTP_HOST and the other SMTP settings, and have the mail server deliver post for todo+* to /inbound/mail.',
'int.yourAddress':'Your address',
'int.newAddress':'New address',
'int.caldav':'CalDAV',
'int.caldavHint':
'Two-way, unlike the feed above: Apple Reminders and Thunderbird can write back as well.',
'int.server':'Server',
'int.caldavAuth':
'The username is your email. The password is an API token — not your real password.',
'Read, never written. The events appear in Calendar beside your own tasks with due dates — they cannot be moved or completed from here, because they belong to Google.',
'Uses the same OAuth client as Gmail above. The Google Calendar API has to be enabled, and this address has to be listed as an authorised redirect URI.',
'int.noCalendars':'The account has no calendars to show.',
'int.calendarLastSync':'Last fetched {when}.',
'int.calendarNeverSynced':'Not fetched yet.',
'int.calendarFetchedOne':'{n} event fetched.',
'int.calendarFetchedMany':'{n} events fetched.',
'int.calendarNothingNew':'No events in the window.',
'int.calendarConnected':'Google Calendar is connected.',
'int.calendarState':'Google’s reply did not match the attempt that started here. Try again.',
'int.calendarExpired':'The attempt expired on the way. Try again.',
'int.calendarInvalid':'Google’s reply could not be read. Try again.',
'int.calendarFailed':'Google refused to exchange the code.',
'int.calendarNoRefresh':
'Google sent no refresh token. Remove verdande from your Google account and connect again.',
'int.calendarDenied':'Access was refused at Google.',
'int.calendarOrgInternal':
'The Google app is registered as Internal and only accepts accounts inside the organisation. A private account cannot consent to it, whatever the screen says.',
'A key on a device instead of a secret in a head. The private half never leaves the device, and the signature is bound to the address that asked for it — so a convincing copy of the sign-in page somewhere else gets nothing.',
'passkey.unsupported':'This browser does not support passkeys.',
'passkey.none':'No keys yet.',
'passkey.name':'Name for the key',
'passkey.namePlaceholder':'e.g. my laptop',
'passkey.add':'Add a key',
'passkey.registering':'Waiting for the device…',
'passkey.remove':'Remove',
'passkey.removeQuestion':'Remove "{name}"? That device cannot sign in with it afterwards.',
'passkey.bothFactors':'both factors',
'passkey.possessionOnly':'possession only — the code is still required',
'Passkeys cannot be used here — either this browser does not support them, or the server’s address is an IP rather than a domain name, which no authenticator will accept.',
// --- restarting from the interface ---------------------------------------------
'update.restart':'Restart and pull the latest version',
'update.restarting':'Restarting…',
'update.restartQuestion':
'Restart now? The server is down for about half a minute, and pulls the newest version on the way back up.',
'update.asking':'Asking the panel to restart…',
'update.waiting':'Waiting for the server to come back…',
'update.stillDown':'The server has not come back yet. Have a look in the panel.',
'This instance cannot restart itself: {missing} are not set. A container cannot replace its own image — the panel does the work, and this is only the way to ask.',