'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.',
'users.projectOne':'project',
'users.projectMany':'projects',
'users.taskOne':'task',
'users.taskMany':'tasks',
'users.never':'never seen',
// --- 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.',
'history.deletedAccount':'A deleted account',
'errors.title':'Server errors',
'errors.hint':
'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.import':'Import from Todoist',
'data.csvFile':'CSV file',
'data.importing':'Importing …',
'data.openProject':'Open the project',
'data.trash':'Trash',
'data.trashEmpty':'The trash is empty.',
'data.restore':'Restore',
'data.backups':'Backups',
'data.backupNow':'Take one now',
'data.backingUp':'Copying…',
'data.backupMade':'Backup written — {size}',
'data.backupFailed':'failed',
'data.download':'Download',
'data.noBackups':'No backup has been taken yet.',
'data.swept':'swept away',
'data.empty':'empty',
'data.export':'Export',
'data.exportAccount':'The whole account as JSON',
'data.exportOne':'One project',
'data.pickProject':'Pick a project',
'data.csv':'CSV',
'data.ics':'Calendar file',
'data.templates':'Templates',
'data.saveAsTemplate':'Save a project as a template',
'data.templateName':'Name for the template',
'data.saveTemplate':'Save as a template',
'data.noTemplates':'No templates yet.',
'data.use':'Use',
'data.delete':'Delete',
'data.newProjectName':'Name for the new project',
'data.startDate':'Start date',
'data.startDateHint':'Day zero for the template’s dates. Blank means today.',
'data.createProject':'Create the project',
// --- a group's own page ------------------------------------------------------------
'group.notFound':'That group does not exist, or it is not yours.',
'group.failed':'The group could not be loaded.',
'group.retry':'Try again',
'group.about':'About this group',
'group.aboutPlaceholder':'What kind of work is this?',
'group.writeAbout':'Write what kind of work this is.',
'group.saving':'Saving…',
'group.projects':'Projects',
'group.projectOne':'1 project',
'group.projectMany':'{n} projects',
'group.noProjects':
'No projects in the group yet. Drag one up here in the sidebar, or pick the group when you create one.',
'group.documents':'Documents',
'group.documentsHint':
'Files that belong to the whole group rather than to one task in it — the contract, the drawing, the agreement everything else rests on.',
'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.',