'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.',
'int.oauthClient':'OAuth client at Google',
'int.registerHint':
'Register a client in Google Cloud Console → APIs & Services → Credentials → OAuth client ID → Web application, enable the Gmail API, and paste the two values here. Your own account has to be listed as a test user for as long as the app is unverified with Google.',
'int.redirectURI':'Authorised redirect URI',
'int.clientID':'Client ID',
'int.clientSecret':'Client secret',
'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.',
'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.',