/** * English, translated from `da.js`. * * Danish is the source: it is the language the app was written in, and the one * whose phrasing was argued over. A key missing here falls through to Danish * rather than to the key itself — a Danish sentence in an English screen can be * read, and `settings.notifications.title` cannot. * * Dates are not here. They go through `tag()`, which returns `en-GB` for this * locale: the app is day-month everywhere, weeks start on Monday, and an American * date format inside a European layout reads as a bug. */ export const en = { // --- the shell --------------------------------------------------------------- 'notes.shareWith': 'Share in', 'notes.private': 'Only me', 'notes.shared': "The note is shared with the project's people.", 'notes.unshared': 'The note is yours again.', 'notes.save': 'Save', 'notes.format': 'Format', 'notes.bold': 'Bold', 'notes.italic': 'Italic', 'notes.underline': 'Underline', 'notes.sortBy': 'Order', 'notes.sortUpdated': 'Last touched', 'notes.sortCreated': 'Created', 'notes.sortTitle': 'Name', 'beacon.title': 'Counting installations', '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.send': 'Send an anonymous daily report', 'beacon.exactly': 'Exactly this — nothing more:', 'beacon.lastPing': 'Last sent: {when}', 'beacon.collector': 'Collector address', '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.', 'notes.newHere': 'New note', 'notes.noneInProject': 'No notes yet. A note that mentions #theproject turns up here too.', 'notes.clearSearch': 'Clear the search', 'notes.found': '{count} notes', 'notes.picked': '{count} selected', 'notes.archive': 'Archive', 'notes.unarchive': 'Bring back', 'notes.clearPick': 'Clear selection', 'notes.showArchive': 'Show the archive', 'notes.deleteMany': 'Delete {count} notes?', 'notes.archiveOne': 'Archive this note', 'notes.emptyArchive': 'The archive is empty.', 'notes.copyCode': 'Copy', 'notes.copied': 'Copied', 'notes.copyFailed': 'Could not copy', 'notes.strike': 'Strikethrough', 'notes.showSource': 'Show as code', 'notes.source': 'Note source', 'notes.styleTitle': 'Title', 'notes.styleHeading': 'Heading', 'notes.styleSubheading': 'Subheading', 'notes.styleBody': 'Body', 'notes.styleMono': 'Monospaced', 'notes.styleQuote': 'Block quote', 'notes.styleBullet': 'Bulleted list', 'notes.styleNumbered': 'Numbered list', 'notes.favourite': 'Make a favourite', 'notes.unfavourite': 'Remove from favourites', 'notes.syntaxProject': 'project', 'notes.syntaxNote': 'another note', 'notes.yesterday': 'yesterday', 'notes.title': 'Notes', 'notes.new': 'New note', 'notes.search': 'Search notes', 'notes.body': "The note's text", 'notes.placeholder': 'Write. #project and [[another note]] become references.', 'notes.untitled': 'Untitled', 'notes.noneYet': 'No notes yet.', 'notes.noneFound': 'No notes match.', 'notes.pickOne': 'Pick a note, or write a new one.', 'notes.saving': 'Saving …', 'notes.saved': 'Saved', 'notes.delete': 'Delete', 'notes.deleteNote': 'Delete "{name}"? It can be brought back from the trash.', 'palette.project': 'Project', 'palette.task': 'Task', 'palette.note': 'Note', 'nav.today': 'Today', 'nav.upcoming': 'Upcoming', 'nav.delegated': 'Waiting on others', 'nav.inbox': 'Inbox', 'nav.calendar': 'Calendar', 'nav.settings': 'Settings', 'nav.signOut': 'Sign out', 'nav.main': 'Main menu', 'nav.search': 'Search', 'nav.searchLong': 'Search tasks and projects', 'nav.noResults': 'Nothing found.', 'nav.showSidebar': 'Show the sidebar', 'nav.hideSidebar': 'Hide the sidebar', 'nav.showMenu': 'Show menu', 'nav.sidebarWidth': 'Sidebar width', 'nav.toggleTheme': 'Switch between light and dark', 'nav.offline': 'Offline', 'nav.offlineHint': 'Changes from other people are not showing right now', 'nav.doItToday': 'Do it today', // --- the sidebar's own sections ------------------------------------------------ 'sidebar.projects': 'Projects', 'sidebar.shared': 'Shared with me', 'sidebar.filters': 'Filters', 'sidebar.labels': 'Labels', 'sidebar.newProject': 'New project', 'sidebar.newGroup': 'New group', 'sidebar.projectName': 'Project name', 'sidebar.groupName': "The group's name", 'sidebar.noProjects': 'No projects yet.', 'sidebar.emptyGroup': 'Empty — drag a project up here.', 'sidebar.rename': 'Rename', 'sidebar.delete': 'Delete', 'sidebar.fold': 'Collapse {name}', 'sidebar.unfold': 'Expand {name}', 'sidebar.deleteGroup': 'Delete the group "{name}"?', 'sidebar.deleteGroupOne': 'The project in it stays, and ends up without a group.', 'sidebar.deleteGroupMany': 'The {n} projects stay, and end up without a group.', // --- a task in a list ---------------------------------------------------------- 'task.add': 'Add', 'task.new': 'New task', 'task.syntaxProject': 'project', 'task.syntaxSection': 'section', 'task.syntaxLabel': 'label', 'task.syntaxPriority': 'priority', 'task.syntaxDateMark': 'tomorrow at 10', 'task.syntaxDate': 'due', 'task.placeholder': 'Add a task — try “pay VAT tomorrow at 10 p1 #Company”', 'task.complete': 'Mark as done', 'task.reopen': 'Reopen task', 'task.today': 'Today', 'task.tomorrow': 'Tomorrow', 'task.yesterday': 'Yesterday', 'task.overdue': '{n} days overdue', 'task.repeats': 'Repeats {rule}', 'task.assignee': 'Assigned to {name}', 'task.subtasks': '{done} of {total} sub-tasks are done', 'task.attachments': '{n} attached', 'task.priority': 'Priority {n}', 'task.noPriority': 'No priority', // --- a project group ----------------------------------------------------------- 'group.colorOf': 'Colour of {name}', // --- settings: the frame ------------------------------------------------------- 'settings.title': 'Settings', 'settings.tab.account': 'Account', 'settings.tab.notifications': 'Notifications', 'settings.tab.integrations': 'Integrations', 'settings.tab.ai': 'AI', 'settings.tab.tokens': 'API tokens', 'settings.tab.users': 'People', 'settings.tab.history': 'History', 'settings.tab.errors': 'Errors', 'settings.tab.data': 'Data and templates', // --- settings: the account ----------------------------------------------------- 'account.profile': 'Profile', 'account.profileHint': 'The timezone is not decoration: every date in the app is resolved in it, so “tomorrow at 9” means something else once you change it.', 'account.name': 'Name', 'account.email': 'Email', 'account.emailFixed': 'Cannot be changed here.', 'account.timezone': 'Timezone', 'account.parseLanguage': 'Language', 'account.danish': 'Dansk', 'account.english': 'English', 'account.parseHint': 'Both the interface’s language and the grammar quick add reads a line with — “hver mandag” or “every monday”.', 'account.save': 'Save', 'account.saved': 'Saved.', 'account.appearance': 'Appearance', 'account.appearanceHint': 'Kept in this browser. It is a property of the screen you are sitting at — a laptop in the sun and a monitor in a dark room want different answers.', 'account.look': 'Appearance', 'account.lookHint': 'The theme says how bright. This says how it reads — typeface, air and corners. The two combine freely.', 'look.verdande': 'Verdande', 'look.verdandeNote': 'As you know it.', 'look.rolig': 'Calm', 'look.roligNote': 'Serif, more air. For reading and writing.', 'look.taet': 'Tight', 'look.taetNote': 'More rows on screen, less air.', 'look.terminal': 'Terminal', 'look.terminalNote': 'One width for everything. It all lines up.', 'account.password': 'Password', 'account.passwordHint': 'Changing it signs every other session out — which is usually the reason to do it.', 'account.currentPassword': 'Current password', 'account.newPassword': 'New password', 'account.changePassword': 'Change password', 'account.passwordChanged': 'Changed.', 'account.totp': 'Two-factor', 'account.totpHint': 'A one-time code from an authenticator app, on top of the password.', 'account.writeThemDown': 'Write them down now.', 'account.recoveryHint': 'They are shown this once — the server only has their hash.', 'account.gotThem': 'I have them', 'account.passwordRequired': 'Required for both actions below. Turning two-factor off is exactly what a stranger holding your session would do first.', 'account.newRecoveryCodes': 'New recovery codes', 'account.totpOff': 'Turn two-factor off', 'account.scanHint': 'Scan it in your authenticator app, or type the key in by hand.', 'account.codeFromApp': 'The code from the app', 'account.totpProofHint': 'Two-factor is only on once a code has proved the app really has the key.', 'account.confirm': 'Confirm', 'account.cancel': 'Cancel', 'account.totpOn': 'Turn two-factor on', 'account.devices': 'Devices', 'account.devicesHint': 'Where you are signed in. If you do not recognise one, sign it out and change your password.', 'account.thisDevice': 'this device', 'account.signOutDevice': 'Sign out', 'account.totpEnabled': 'On.', 'account.recoveryLeftOne': '{n} recovery code left.', 'account.recoveryLeftMany': '{n} recovery codes left.', // --- settings: notifications --------------------------------------------------- 'push.title': 'Notifications on this device', 'push.hint': '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', 'users.toProject': 'to {name}', 'users.toInstance': 'to the instance', 'users.invitedBy': 'invited by {name}', // --- 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', // What each recorded event is called. The keys are the server's and live in // events.js; these are the words they become. 'event.projectCreated': 'created the project', 'event.projectUpdated': 'changed the project', 'event.projectDeleted': 'deleted the project', 'event.projectImported': 'imported the project', 'event.sectionCreated': 'created a section', 'event.sectionUpdated': 'changed a section', 'event.sectionDeleted': 'deleted a section', 'event.memberInvited': 'invited', 'event.memberAdded': 'added', 'event.memberRemoved': 'removed', 'event.memberRoleChanged': 'changed the role of', 'event.taskCreated': 'created a task', 'event.taskUpdated': 'changed a task', 'event.taskCompleted': 'closed a task', 'event.taskReopened': 'reopened a task', 'event.taskMoved': 'moved a task', 'event.taskDeleted': 'deleted a task', 'event.taskSplit': 'split a task up', 'event.commentCreated': 'wrote a comment', 'event.attachmentAdded': 'attached a file', '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.doneHint': 'Everything you have closed, newest first. Closed something by mistake and it is here — and can be opened again.', 'data.openDone': 'Open finished tasks', '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.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.', 'data.exportNotes': 'Every note as Markdown files', '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.', 'group.remove': 'Remove', 'group.removeNamed': 'Remove {name}', 'group.removeQuestion': 'Remove "{name}"?', 'group.addFile': '+ Upload a file', 'group.uploading': 'Uploading…', // --- keyboard shortcuts -------------------------------------------------------------- 'keys.title': 'Shortcuts', 'data.importHint': '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.', 'data.backupsSecrets': '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.', 'data.backupsAttachments': '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.', 'keys.foldSidebar': 'fold the sidebar away', 'keys.anyLetter': 'any letter', 'keys.typeToCapture': 'starts a task, with the letter already in the field', 'keys.navigation': 'Navigation', 'keys.jumpToQuickAdd': 'Jump to the “add a task” field', 'keys.thisPage': 'This page', 'keys.inQuickAdd': 'In quick add', 'keys.intoProject': 'Put the task in a project', 'keys.setLabel': 'Set a label', 'keys.priority': 'Priority', 'keys.date': 'A date — Danish and English', 'keys.time': 'A time', 'keys.clear': 'Clear the field', // --- signing in ------------------------------------------------------------------ 'auth.firstAccount': 'Create the first account. It becomes the administrator.', 'auth.name': 'Name', 'auth.nameRequired': 'Required', 'auth.email': 'Email', 'auth.emailInvalid': 'Must be an email address', 'auth.password': 'Password', 'auth.passwordHint': 'At least 10 characters. A sentence is both easier and stronger.', 'auth.createAccount': 'Create account', 'auth.totpPrompt': 'Enter the code from your authenticator.', 'auth.code': 'Code', 'auth.totpRecovery': 'If you have lost your phone, one of your recovery codes works here.', 'auth.continue': 'Continue', 'auth.resetPrompt': 'We will send a link to choose a new password.', 'auth.sendLink': 'Send the link', 'auth.back': 'Back', 'auth.linkBroken': 'That link is incomplete. Open it from the email again, or ask for a new one.', 'auth.toSignIn': 'To sign in', 'auth.signOut': 'Sign out', 'auth.invited': 'You have been invited. Choose a name and a password.', 'auth.pickNew': 'Choose a new password.', 'auth.newPassword': 'New password', 'auth.newPasswordHint': 'At least 10 characters. Every other device is signed out.', 'auth.savePassword': 'Save the password', 'auth.passwordChanged': 'The password has been changed. Sign in with the new one.', 'auth.resetSent': 'If that address has an account, a link is on its way. Check the spam filter too.', 'auth.signInPrompt': 'Sign in to continue.', 'auth.signIn': 'Sign in', 'auth.forgot': 'Forgotten your password?', // --- a task, opened --------------------------------------------------------------- 'detail.notFound': 'That task does not exist, or it is not yours.', 'detail.task': 'Task', 'detail.close': 'Close', 'detail.text': 'The task’s text', 'detail.description': 'Description', 'detail.descriptionPlaceholder': 'Details, links, whatever has to be remembered', 'detail.priority': 'Priority', 'detail.p1': 'P1 — urgent', 'detail.none': 'None', 'detail.due': 'Due', 'detail.labels': 'Labels', 'detail.labelsPlaceholder': 'separated by commas', 'detail.project': 'Project', 'detail.repeats': 'Repeats', 'detail.never': 'Never', 'detail.daily': 'Every day', 'detail.weekly': 'Every week', 'detail.fortnightly': 'Every other week', 'detail.monthly': 'Every month', 'detail.yearly': 'Every year', 'detail.asWritten': 'As written', 'detail.assignee': 'Assigned to', 'detail.subtasks': 'Sub-tasks', 'detail.addSubtask': 'Add a sub-task', 'detail.newSubtask': 'New sub-task', 'detail.files': 'Files', 'detail.delete': 'Delete', 'detail.reminders': 'Reminders', 'detail.newReminder': 'New reminder', 'detail.sent': 'sent', 'detail.add': 'Add', 'detail.comments': 'Comments', 'detail.writeComment': 'Write a comment', 'detail.newComment': 'New comment', 'detail.post': 'Post', 'detail.readAs': 'Read as {what}', 'auth.alreadySignedIn': 'You are signed in as {name}. Sign out first, then open the link again.', 'detail.reopen': 'Reopen', 'detail.uploading': 'Uploading …', 'detail.attachFile': 'Attach a file', // --- settings: integrations -------------------------------------------------------- 'int.mailboxes': 'Mailboxes', 'int.mailboxesHint': '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.', 'int.fetchNow': 'Fetch now', 'int.fetching': 'Fetching …', 'int.gmail': 'Gmail', 'int.gmailHint': '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 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.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.', 'int.calendar': 'Google Calendar', 'int.calendarHint': '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.', 'int.connectCalendar': 'Connect Google Calendar', 'int.calendarConnectedAs': 'Connected as', 'int.calendarSameClient': '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.whichCalendars': 'Which calendars are shown', '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.', 'int.calendarOther': 'Google replied: {what}', 'int.gmailFetchedOne': '{n} task fetched.', 'int.gmailFetchedMany': '{n} tasks fetched.', 'int.gmailNothingNew': 'No new messages.', 'int.gmailConnected': 'Gmail is connected.', 'int.gmailState': 'Google’s reply did not match the attempt that started here. Try again.', 'int.gmailExpired': 'The attempt expired on the way. Try again.', 'int.gmailInvalid': 'Google’s reply could not be read. Try again.', 'int.gmailFailed': 'Google refused to exchange the code.', 'int.gmailNoRefresh': 'Google sent no refresh token. Remove verdande from your Google account and connect again.', 'int.gmailDenied': 'Access was refused at Google.', 'int.gmailOther': 'Gmail replied: {what}', // --- the calendar, with Google’s events laid over ------------------------------------ 'cal.title': 'Calendar', 'cal.notConnected': 'No calendar is connected yet. That is done under Settings → Integrations.', 'cal.settings': 'Settings', 'cal.noneChosen': 'None of the account’s calendars are chosen yet.', 'cal.allDay': 'All day', 'cal.openInGoogle': 'Open in Google Calendar', 'cal.beyondWindow': 'Events are kept from {from} to {to}. Outside that the grid shows only your own tasks.', 'cal.eventCannotMove': '{name} — an event in {calendar}. It cannot be moved from here.', // --- a project ---------------------------------------------------------------------- 'project.name': 'The project’s name', 'project.clickToRename': 'Click to rename', 'project.color': 'The project’s colour', 'project.pickColor': 'Pick a colour', 'project.delete': 'Delete the project', 'project.share': 'Share', 'project.history': 'History', 'project.noHistory': 'Nothing has happened here yet.', 'project.canEdit': 'Can edit', 'project.canView': 'Can only view', 'project.invite': 'Invite', 'project.inviteByEmail': 'Invite by email', 'project.emailAddress': 'email address', 'project.role': 'Role', 'project.sendLinkYourself': 'No mail server configured — send the link yourself:', 'project.noSection': 'No section', 'project.rename': 'Rename', 'project.deleteSection': 'Delete', 'project.readOnly': 'You can see this project, but not change it.', 'project.notFound': 'That project does not exist, or you do not have access to it.', 'project.loadFailed': 'The project could not be loaded. The server did not answer.', 'project.retry': 'Try again', 'project.nothingHere': 'Nothing here yet.', 'project.sharedWith': 'Shared · {n}', // --- the palette --------------------------------------------------------------- 'color.graphite': 'Graphite', 'color.tomato': 'Tomato', 'color.amber': 'Amber', 'color.lime': 'Lime', 'color.green': 'Green', 'color.teal': 'Teal', 'color.blue': 'Blue', 'color.indigo': 'Indigo', 'color.violet': 'Violet', 'color.magenta': 'Magenta', // --- what a failed request says ----------------------------------------------- 'error.unauthorized': 'Wrong email or password.', 'error.totpRequired': 'Enter the code from your authenticator.', 'error.forbidden': 'You do not have access to that.', 'error.notFound': 'Not found.', 'error.conflict': 'That already exists.', 'error.rateLimited': 'Too many attempts. Wait a moment.', 'error.validation': 'Check the fields below.', 'error.tooLarge': 'That is too large.', 'error.internal': 'Something went wrong. Try again.', 'error.badRequest': 'The request could not be read.', 'error.offline': 'No connection to the server.', 'error.gmailNotConfigured': 'Gmail is not set up on this server. An administrator has to register an OAuth client with Google under Settings → Integrations.', 'error.aiNotConfigured': 'No AI provider has been chosen. Set one up under Settings → AI.', 'error.totpNotEnabled': 'Two-factor is not on.', 'error.inboxProtected': 'The Inbox cannot be deleted.', 'error.lastAdmin': 'That is the last administrator. Make somebody else an administrator first — otherwise nobody can get back into this page.', // --- the themes ------------------------------------------------------------------- 'theme.dark': 'Northern lights', 'theme.darkNote': 'Dark, with a green accent.', 'theme.dusk': 'Dusk', 'theme.duskNote': 'Dark and warm, with amber.', 'theme.light': 'Daylight', 'theme.lightNote': 'Light and coolly neutral.', 'theme.paper': 'Paper', 'theme.paperNote': 'Light and warm, like paper.', 'theme.contrast': 'Contrast', 'theme.contrastNote': 'Black on white, for bright light.', // --- push, in the browser's own words ---------------------------------------------- 'push.deniedByBrowser': 'The browser has blocked notifications for this site. It has to be turned on in the browser’s own settings.', 'push.notEnabled': 'Notifications were not turned on.', 'push.noVapidKey': 'The server has no VAPID key.', 'view.done': 'Done', 'view.showDone': 'Show done', 'view.hideDone': 'Hide done', 'task.moveFailed': 'The task could not be moved.', 'push.updatesOff': 'Updates are not being checked for.', 'push.updateOut': '{version} is out.', 'push.upToDate': 'No newer version found.', 'view.nothingMoreToday': 'Nothing more today.', 'view.overdue': 'Overdue', 'project.roleFor': 'Role for {name}', 'project.more': 'More actions', 'project.removeMember': 'Remove {name}', 'nav.closeMenu': 'Close menu', 'view.addTask': '+ Add a task', 'view.addTaskHere': 'Write a task — a date, p1 and @label work here too', 'view.addTaskIn': 'New task in {name}', 'task.completedUndo': '“{what}” is done.', 'task.undo': 'Undo', 'done.title': 'Done', 'done.hint': 'Everything you have closed, newest first. Click the circle to reopen one.', 'done.empty': 'You have not closed anything yet.', 'done.unknownDay': 'No date', // --- passkeys ---------------------------------------------------------------------- 'passkey.title': 'Passkeys', 'passkey.hint': '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', 'passkey.lastUsed': 'last used {when}', 'passkey.neverUsed': 'never used', 'passkey.signIn': 'Sign in with a passkey', 'passkey.signingIn': 'Waiting for the device…', 'passkey.unavailable': '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.', 'update.notConfigured': '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.', 'error.gmailSaid': 'Gmail said: {what}', 'error.calendarRefused': 'Google refused the call. Try connecting the calendar again.', 'error.calendarSaid': 'Google replied: {what}', 'int.disconnectCalendarQuestion': 'Disconnect Google Calendar? The tokens are forgotten and the fetched copy of the events is deleted. Your own tasks are untouched.', 'error.mailboxRefused': 'The mailbox could not be read.', 'error.mailboxSaid': 'The mailbox answered: {what}', 'error.gmailRefused': 'Gmail refused the call. Try connecting the mailbox again.', 'int.newAddressQuestion': 'A new address? Mail to the old one is refused.', 'int.copied': 'Copied.', 'int.copyFailed': 'Could not copy — select the text instead.', 'detail.deleteComment': 'Delete the comment?', 'int.disconnectQuestion': 'Disconnect Gmail? The tokens are forgotten.', 'int.newLinkQuestion': 'A new feed link? Every existing subscription stops working.', 'data.deleteTemplate': 'Delete the template "{name}"?', 'tokens.revokeQuestion': 'Revoke "{name}"? Everything using it stops working.', 'account.signOutThisDevice': 'That is this device. You will be signed out.', 'project.deleteQuestion': 'Delete "{name}" with all its tasks?', 'project.deleteRecoverable': 'It goes to the trash and can be restored under Settings → Data and templates for {days} days.' };