diff --git a/.drone.yml b/.drone.yml
index 4090e6a8f..dc82fa780 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -116,36 +116,15 @@ steps:
YARN_CACHE_FOLDER: .cache/yarn/
CYPRESS_CACHE_FOLDER: .cache/cypress/
CYPRESS_DEFAULT_COMMAND_TIMEOUT: 60000
+ CYPRESS_RECORD_KEY:
+ from_secret: cypress_project_key
commands:
- sed -i 's/localhost/api/g' dist/index.html
- - yarn serve:dist & npx wait-on http://localhost:5000
- - yarn test:frontend --browser chrome
+ - yarn serve:dist & npx wait-on http://localhost:4173
+ - yarn test:frontend --browser chrome --record
depends_on:
- - dependencies
- build-prod
- - name: upload-test-results
- image: plugins/s3
- pull: true
- settings:
- bucket: drone-test-results
- access_key:
- from_secret: test_results_aws_access_key_id
- secret_key:
- from_secret: test_results_aws_secret_access_key
- endpoint: https://s3.fr-par.scw.cloud
- region: fr-par
- path_style: true
- source: cypress/screenshots/**/**/*
- strip_prefix: cypress/screenshots/
- target: /${DRONE_REPO}/${DRONE_PULL_REQUEST}_${DRONE_BRANCH}/${DRONE_BUILD_NUMBER}/
- depends_on:
- - test-frontend
- when:
- status:
- - failure
- - success
-
- name: deploy-preview
image: node:16
pull: true
@@ -157,6 +136,9 @@ steps:
GITEA_TOKEN:
from_secret: gitea_token
commands:
+ - cp -r dist dist-preview
+ # Override the default api url used for preview
+ - sed -i 's|localhost:3456|try.vikunja.io|g' dist-preview/index.html
- shasum -a 384 -c ./scripts/deploy-preview-netlify.js.sha384
- node ./scripts/deploy-preview-netlify.js
depends_on:
@@ -665,6 +647,6 @@ steps:
from_secret: crowdin_key
---
kind: signature
-hmac: 188ee90100c5fc5922a445e531e7a47453121edddb2a64a182eb23ed2bf602de
+hmac: 997e1badebe484ac29557c4af356e63db4d3d57f3d32e92d482f117f8cec64da
...
diff --git a/cypress.json b/cypress.json
index 27f12495c..48eb6ac59 100644
--- a/cypress.json
+++ b/cypress.json
@@ -1,5 +1,5 @@
{
- "baseUrl": "http://localhost:5000",
+ "baseUrl": "http://localhost:4173",
"env": {
"API_URL": "http://localhost:3456/api/v1",
"TEST_SECRET": "averyLongSecretToSe33dtheDB"
@@ -7,5 +7,6 @@
"video": false,
"retries": {
"runMode": 2
- }
+ },
+ "projectId": "181c7x"
}
diff --git a/cypress/factories/bucket.js b/cypress/factories/bucket.js
index be90cca99..8001899b4 100644
--- a/cypress/factories/bucket.js
+++ b/cypress/factories/bucket.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
diff --git a/cypress/factories/labels.js b/cypress/factories/labels.js
index b3f9ab30f..7aac5eb09 100644
--- a/cypress/factories/labels.js
+++ b/cypress/factories/labels.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
diff --git a/cypress/factories/link_sharing.js b/cypress/factories/link_sharing.js
index e2c01dd07..3a406ea22 100644
--- a/cypress/factories/link_sharing.js
+++ b/cypress/factories/link_sharing.js
@@ -1,6 +1,6 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
-import faker from 'faker'
+import faker from '@faker-js/faker'
export class LinkShareFactory extends Factory {
static table = 'link_shares'
diff --git a/cypress/factories/list.js b/cypress/factories/list.js
index f93cdba4c..2ffc31256 100644
--- a/cypress/factories/list.js
+++ b/cypress/factories/list.js
@@ -1,6 +1,6 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
-import faker from 'faker'
+import faker from '@faker-js/faker'
export class ListFactory extends Factory {
static table = 'lists'
diff --git a/cypress/factories/namespace.js b/cypress/factories/namespace.js
index 89096d2dd..203f7159d 100644
--- a/cypress/factories/namespace.js
+++ b/cypress/factories/namespace.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
diff --git a/cypress/factories/task.js b/cypress/factories/task.js
index 6fa8d5b67..5410a25eb 100644
--- a/cypress/factories/task.js
+++ b/cypress/factories/task.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
diff --git a/cypress/factories/task_comment.js b/cypress/factories/task_comment.js
index 74e043f92..7800c0093 100644
--- a/cypress/factories/task_comment.js
+++ b/cypress/factories/task_comment.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
diff --git a/cypress/factories/team.js b/cypress/factories/team.js
index 928b8ce42..33cc37947 100644
--- a/cypress/factories/team.js
+++ b/cypress/factories/team.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
diff --git a/cypress/factories/user.js b/cypress/factories/user.js
index 9e133b552..93971efeb 100644
--- a/cypress/factories/user.js
+++ b/cypress/factories/user.js
@@ -1,4 +1,4 @@
-import faker from 'faker'
+import faker from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
diff --git a/cypress/integration/list/list-history.spec.js b/cypress/integration/list/list-history.spec.js
new file mode 100644
index 000000000..b7633cbda
--- /dev/null
+++ b/cypress/integration/list/list-history.spec.js
@@ -0,0 +1,56 @@
+import {ListFactory} from '../../factories/list'
+
+import '../../support/authenticateUser'
+import {prepareLists} from './prepareLists'
+
+describe('List History', () => {
+ prepareLists()
+
+ it('should show a list history on the home page', () => {
+ cy.intercept(Cypress.env('API_URL') + '/namespaces*').as('loadNamespaces')
+ cy.intercept(Cypress.env('API_URL') + '/lists/*').as('loadList')
+
+ const lists = ListFactory.create(6)
+
+ cy.visit('/')
+ cy.wait('@loadNamespaces')
+ cy.get('body')
+ .should('not.contain', 'Last viewed')
+
+ cy.visit(`/lists/${lists[0].id}`)
+ cy.wait('@loadNamespaces')
+ cy.wait('@loadList')
+ cy.visit(`/lists/${lists[1].id}`)
+ cy.wait('@loadNamespaces')
+ cy.wait('@loadList')
+ cy.visit(`/lists/${lists[2].id}`)
+ cy.wait('@loadNamespaces')
+ cy.wait('@loadList')
+ cy.visit(`/lists/${lists[3].id}`)
+ cy.wait('@loadNamespaces')
+ cy.wait('@loadList')
+ cy.visit(`/lists/${lists[4].id}`)
+ cy.wait('@loadNamespaces')
+ cy.wait('@loadList')
+ cy.visit(`/lists/${lists[5].id}`)
+ cy.wait('@loadNamespaces')
+ cy.wait('@loadList')
+
+ // cy.visit('/')
+ // cy.wait('@loadNamespaces')
+ // Not using cy.visit here to work around the redirect issue fixed in #1337
+ cy.get('nav.menu.top-menu a')
+ .contains('Overview')
+ .click()
+
+ cy.get('body')
+ .should('contain', 'Last viewed')
+ cy.get('.list-cards-wrapper-2-rows')
+ .should('not.contain', lists[0].title)
+ .should('contain', lists[1].title)
+ .should('contain', lists[2].title)
+ .should('contain', lists[3].title)
+ .should('contain', lists[4].title)
+ .should('contain', lists[5].title)
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/list/list-view-gantt.spec.js b/cypress/integration/list/list-view-gantt.spec.js
new file mode 100644
index 000000000..69805a30d
--- /dev/null
+++ b/cypress/integration/list/list-view-gantt.spec.js
@@ -0,0 +1,76 @@
+import {formatISO, format} from 'date-fns'
+import {TaskFactory} from '../../factories/task'
+import {prepareLists} from './prepareLists'
+
+import '../../support/authenticateUser'
+
+describe('List View Gantt', () => {
+ prepareLists()
+
+ it('Hides tasks with no dates', () => {
+ const tasks = TaskFactory.create(1)
+ cy.visit('/lists/1/gantt')
+
+ cy.get('.gantt-chart .tasks')
+ .should('not.contain', tasks[0].title)
+ })
+
+ it('Shows tasks from the current and next month', () => {
+ const now = new Date()
+ const nextMonth = now
+ nextMonth.setDate(1)
+ nextMonth.setMonth(now.getMonth() + 1)
+
+ cy.visit('/lists/1/gantt')
+
+ cy.get('.gantt-chart .months')
+ .should('contain', format(now, 'MMMM'))
+ .should('contain', format(nextMonth, 'MMMM'))
+ })
+
+ it('Shows tasks with dates', () => {
+ const now = new Date()
+ const tasks = TaskFactory.create(1, {
+ start_date: formatISO(now),
+ end_date: formatISO(now.setDate(now.getDate() + 4))
+ })
+ cy.visit('/lists/1/gantt')
+
+ cy.get('.gantt-chart .tasks')
+ .should('not.be.empty')
+ cy.get('.gantt-chart .tasks')
+ .should('contain', tasks[0].title)
+ })
+
+ it('Shows tasks with no dates after enabling them', () => {
+ TaskFactory.create(1, {
+ start_date: null,
+ end_date: null,
+ })
+ cy.visit('/lists/1/gantt')
+
+ cy.get('.gantt-options .fancycheckbox')
+ .contains('Show tasks which don\'t have dates set')
+ .click()
+
+ cy.get('.gantt-chart .tasks')
+ .should('not.be.empty')
+ cy.get('.gantt-chart .tasks .task.nodate')
+ .should('exist')
+ })
+
+ it('Drags a task around', () => {
+ const now = new Date()
+ TaskFactory.create(1, {
+ start_date: formatISO(now),
+ end_date: formatISO(now.setDate(now.getDate() + 4))
+ })
+ cy.visit('/lists/1/gantt')
+
+ cy.get('.gantt-chart .tasks .task')
+ .first()
+ .trigger('mousedown', {which: 1})
+ .trigger('mousemove', {clientX: 500, clientY: 0})
+ .trigger('mouseup', {force: true})
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/list/list-view-kanban.spec.js b/cypress/integration/list/list-view-kanban.spec.js
new file mode 100644
index 000000000..68268304d
--- /dev/null
+++ b/cypress/integration/list/list-view-kanban.spec.js
@@ -0,0 +1,196 @@
+import {BucketFactory} from '../../factories/bucket'
+import {ListFactory} from '../../factories/list'
+import {TaskFactory} from '../../factories/task'
+import {prepareLists} from './prepareLists'
+
+import '../../support/authenticateUser'
+
+describe('List View Kanban', () => {
+ let buckets
+ prepareLists()
+
+ beforeEach(() => {
+ buckets = BucketFactory.create(2)
+ })
+
+ it('Shows all buckets with their tasks', () => {
+ const data = TaskFactory.create(10, {
+ list_id: 1,
+ bucket_id: 1,
+ })
+ cy.visit('/lists/1/kanban')
+
+ cy.get('.kanban .bucket .title')
+ .contains(buckets[0].title)
+ .should('exist')
+ cy.get('.kanban .bucket .title')
+ .contains(buckets[1].title)
+ .should('exist')
+ cy.get('.kanban .bucket')
+ .first()
+ .should('contain', data[0].title)
+ })
+
+ it('Can add a new task to a bucket', () => {
+ TaskFactory.create(2, {
+ list_id: 1,
+ bucket_id: 1,
+ })
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket')
+ .contains(buckets[0].title)
+ .get('.bucket-footer .button')
+ .contains('Add another task')
+ .click()
+ cy.get('.kanban .bucket')
+ .contains(buckets[0].title)
+ .get('.bucket-footer .field .control input.input')
+ .type('New Task{enter}')
+
+ cy.get('.kanban .bucket')
+ .first()
+ .should('contain', 'New Task')
+ })
+
+ it('Can create a new bucket', () => {
+ cy.visit('/lists/1/kanban')
+
+ cy.get('.kanban .bucket.new-bucket .button')
+ .click()
+ cy.get('.kanban .bucket.new-bucket input.input')
+ .type('New Bucket{enter}')
+
+ cy.wait(1000) // Wait for the request to finish
+ cy.get('.kanban .bucket .title')
+ .contains('New Bucket')
+ .should('exist')
+ })
+
+ it('Can set a bucket limit', () => {
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
+ .first()
+ .click()
+ cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
+ .contains('Limit: Not Set')
+ .click()
+ cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
+ .first()
+ .type(3)
+ cy.get('[data-cy="setBucketLimit"]')
+ .first()
+ .click()
+
+ cy.get('.kanban .bucket .bucket-header span.limit')
+ .contains('0/3')
+ .should('exist')
+ })
+
+ it('Can rename a bucket', () => {
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket .bucket-header .title')
+ .first()
+ .type('{selectall}New Bucket Title{enter}')
+ cy.get('.kanban .bucket .bucket-header .title')
+ .first()
+ .should('contain', 'New Bucket Title')
+ })
+
+ it('Can delete a bucket', () => {
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
+ .first()
+ .click()
+ cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
+ .contains('Delete')
+ .click()
+ cy.get('.modal-mask .modal-container .modal-content .header')
+ .should('contain', 'Delete the bucket')
+ cy.get('.modal-mask .modal-container .modal-content .actions .button')
+ .contains('Do it!')
+ .click()
+
+ cy.get('.kanban .bucket .title')
+ .contains(buckets[0].title)
+ .should('not.exist')
+ cy.get('.kanban .bucket .title')
+ .contains(buckets[1].title)
+ .should('exist')
+ })
+
+ it('Can drag tasks around', () => {
+ const tasks = TaskFactory.create(2, {
+ list_id: 1,
+ bucket_id: 1,
+ })
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket .tasks .task')
+ .contains(tasks[0].title)
+ .first()
+ .drag('.kanban .bucket:nth-child(2) .tasks')
+
+ cy.get('.kanban .bucket:nth-child(2) .tasks')
+ .should('contain', tasks[0].title)
+ cy.get('.kanban .bucket:nth-child(1) .tasks')
+ .should('not.contain', tasks[0].title)
+ })
+
+ it('Should navigate to the task when the task card is clicked', () => {
+ const tasks = TaskFactory.create(5, {
+ id: '{increment}',
+ list_id: 1,
+ bucket_id: 1,
+ })
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket .tasks .task')
+ .contains(tasks[0].title)
+ .should('be.visible')
+ .click()
+
+ cy.url()
+ .should('contain', `/tasks/${tasks[0].id}`, { timeout: 1000 })
+ })
+
+ it('Should remove a task from the kanban board when moving it to another list', () => {
+ const lists = ListFactory.create(2)
+ BucketFactory.create(2, {
+ list_id: '{increment}',
+ })
+ const tasks = TaskFactory.create(5, {
+ id: '{increment}',
+ list_id: 1,
+ bucket_id: 1,
+ })
+ const task = tasks[0]
+ cy.visit('/lists/1/kanban')
+
+ cy.getSettled('.kanban .bucket .tasks .task')
+ .contains(task.title)
+ .should('be.visible')
+ .click()
+
+ cy.get('.task-view .action-buttons .button', { timeout: 3000 })
+ .contains('Move')
+ .click()
+ cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
+ .type(`${lists[1].title}{enter}`)
+ // The requests happen with a 200ms timeout. Because of that, the results are not yet there when cypress
+ // presses enter and we can't simulate pressing on enter to select the item.
+ cy.get('.task-view .content.details .field .multiselect.control .search-results')
+ .children()
+ .first()
+ .click()
+
+ cy.get('.global-notification', { timeout: 1000 })
+ .should('contain', 'Success')
+ cy.go('back')
+ cy.get('.kanban .bucket')
+ .should('not.contain', task.title)
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/list/list-view-list.spec.js b/cypress/integration/list/list-view-list.spec.js
new file mode 100644
index 000000000..e1a4a0f69
--- /dev/null
+++ b/cypress/integration/list/list-view-list.spec.js
@@ -0,0 +1,97 @@
+import {UserListFactory} from '../../factories/users_list'
+import {TaskFactory} from '../../factories/task'
+import {UserFactory} from '../../factories/user'
+import {ListFactory} from '../../factories/list'
+import {prepareLists} from './prepareLists'
+
+import '../../support/authenticateUser'
+
+describe('List View List', () => {
+ prepareLists()
+
+ it('Should be an empty list', () => {
+ cy.visit('/lists/1')
+ cy.url()
+ .should('contain', '/lists/1/list')
+ cy.get('.list-title h1')
+ .should('contain', 'First List')
+ cy.get('.list-title .dropdown')
+ .should('exist')
+ cy.get('p')
+ .contains('This list is currently empty.')
+ .should('exist')
+ })
+
+ it('Should navigate to the task when the title is clicked', () => {
+ const tasks = TaskFactory.create(5, {
+ id: '{increment}',
+ list_id: 1,
+ })
+ cy.visit('/lists/1/list')
+
+ cy.get('.tasks .task .tasktext')
+ .contains(tasks[0].title)
+ .first()
+ .click()
+
+ cy.url()
+ .should('contain', `/tasks/${tasks[0].id}`)
+ })
+
+ it('Should not see any elements for a list which is shared read only', () => {
+ UserFactory.create(2)
+ UserListFactory.create(1, {
+ list_id: 2,
+ user_id: 1,
+ right: 0,
+ })
+ const lists = ListFactory.create(2, {
+ owner_id: '{increment}',
+ namespace_id: '{increment}',
+ })
+ cy.visit(`/lists/${lists[1].id}/`)
+
+ cy.get('.list-title a.icon')
+ .should('not.exist')
+ cy.get('input.input[placeholder="Add a new task..."')
+ .should('not.exist')
+ })
+
+ it('Should only show the color of a list in the navigation and not in the list view', () => {
+ const lists = ListFactory.create(1, {
+ hex_color: '00db60',
+ })
+ TaskFactory.create(10, {
+ list_id: lists[0].id,
+ })
+ cy.visit(`/lists/${lists[0].id}/`)
+
+ cy.get('.menu-list li .list-menu-link .color-bubble')
+ .should('have.css', 'background-color', 'rgb(0, 219, 96)')
+ cy.get('.tasks-container .tasks .color-bubble')
+ .should('not.exist')
+ })
+
+ it('Should paginate for > 50 tasks', () => {
+ const tasks = TaskFactory.create(100, {
+ id: '{increment}',
+ title: i => `task${i}`,
+ list_id: 1,
+ })
+ cy.visit('/lists/1/list')
+
+ cy.get('.tasks-container .tasks')
+ .should('contain', tasks[99].title)
+
+ cy.get('.card-content .pagination .pagination-link')
+ .contains('2')
+ .click()
+
+ cy.url()
+ .should('contain', '?page=2')
+ cy.get('.tasks-container .tasks')
+ .should('contain', tasks[1].title)
+ cy.get('.tasks-container .tasks')
+ .should('not.contain', tasks[99].title)
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/list/list-view-table.spec.js b/cypress/integration/list/list-view-table.spec.js
new file mode 100644
index 000000000..e0336efc5
--- /dev/null
+++ b/cypress/integration/list/list-view-table.spec.js
@@ -0,0 +1,52 @@
+import {TaskFactory} from '../../factories/task'
+
+import '../../support/authenticateUser'
+
+describe('List View Table', () => {
+ it('Should show a table with tasks', () => {
+ const tasks = TaskFactory.create(1)
+ cy.visit('/lists/1/table')
+
+ cy.get('.list-table table.table')
+ .should('exist')
+ cy.get('.list-table table.table')
+ .should('contain', tasks[0].title)
+ })
+
+ it('Should have working column switches', () => {
+ TaskFactory.create(1)
+ cy.visit('/lists/1/table')
+
+ cy.get('.list-table .filter-container .items .button')
+ .contains('Columns')
+ .click()
+ cy.get('.list-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
+ .contains('Priority')
+ .click()
+ cy.get('.list-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
+ .contains('Done')
+ .click()
+
+ cy.get('.list-table table.table th')
+ .contains('Priority')
+ .should('exist')
+ cy.get('.list-table table.table th')
+ .contains('Done')
+ .should('not.exist')
+ })
+
+ it('Should navigate to the task when the title is clicked', () => {
+ const tasks = TaskFactory.create(5, {
+ id: '{increment}',
+ list_id: 1,
+ })
+ cy.visit('/lists/1/table')
+
+ cy.get('.list-table table.table')
+ .contains(tasks[0].title)
+ .click()
+
+ cy.url()
+ .should('contain', `/tasks/${tasks[0].id}`)
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/list/list.spec.js b/cypress/integration/list/list.spec.js
index 8b8630fdb..00f5b4f5e 100644
--- a/cypress/integration/list/list.spec.js
+++ b/cypress/integration/list/list.spec.js
@@ -1,25 +1,11 @@
-import {formatISO, format} from 'date-fns'
-
import {TaskFactory} from '../../factories/task'
-import {ListFactory} from '../../factories/list'
-import {UserListFactory} from '../../factories/users_list'
-import {UserFactory} from '../../factories/user'
-import {NamespaceFactory} from '../../factories/namespace'
-import {BucketFactory} from '../../factories/bucket'
+import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('Lists', () => {
let lists
-
- beforeEach(() => {
- UserFactory.create(1)
- NamespaceFactory.create(1)
- lists = ListFactory.create(1, {
- title: 'First List'
- })
- TaskFactory.truncate()
- })
+ prepareLists((newLists) => (lists = newLists))
it('Should create a new list', () => {
cy.visit('/')
@@ -29,7 +15,7 @@ describe('Lists', () => {
.contains('New list')
.click()
cy.url()
- .should('contain', '/namespaces/1/list')
+ .should('contain', '/lists/new/1')
cy.get('.card-header-title')
.contains('New list')
cy.get('input.input')
@@ -56,7 +42,7 @@ describe('Lists', () => {
})
it('Should rename the list in all places', () => {
- const tasks = TaskFactory.create(5, {
+ TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
})
@@ -86,7 +72,7 @@ describe('Lists', () => {
.should('contain', newListName)
.should('not.contain', lists[0].title)
cy.visit('/')
- cy.get('.card-content .tasks')
+ cy.get('.card-content')
.should('contain', newListName)
.should('not.contain', lists[0].title)
})
@@ -112,429 +98,4 @@ describe('Lists', () => {
cy.location('pathname')
.should('equal', '/')
})
-
- describe('List View', () => {
- it('Should be an empty list', () => {
- cy.visit('/lists/1')
- cy.url()
- .should('contain', '/lists/1/list')
- cy.get('.list-title h1')
- .should('contain', 'First List')
- cy.get('.list-title .dropdown')
- .should('exist')
- cy.get('p')
- .contains('This list is currently empty.')
- .should('exist')
- })
-
- it('Should navigate to the task when the title is clicked', () => {
- const tasks = TaskFactory.create(5, {
- id: '{increment}',
- list_id: 1,
- })
- cy.visit('/lists/1/list')
-
- cy.get('.tasks .task .tasktext')
- .contains(tasks[0].title)
- .first()
- .click()
-
- cy.url()
- .should('contain', `/tasks/${tasks[0].id}`)
- })
-
- it('Should not see any elements for a list which is shared read only', () => {
- UserFactory.create(2)
- UserListFactory.create(1, {
- list_id: 2,
- user_id: 1,
- right: 0,
- })
- const lists = ListFactory.create(2, {
- owner_id: '{increment}',
- namespace_id: '{increment}',
- })
- cy.visit(`/lists/${lists[1].id}/`)
-
- cy.get('.list-title a.icon')
- .should('not.exist')
- cy.get('input.input[placeholder="Add a new task..."')
- .should('not.exist')
- })
-
- it('Should only show the color of a list in the navigation and not in the list view', () => {
- const lists = ListFactory.create(1, {
- hex_color: '00db60',
- })
- TaskFactory.create(10, {
- list_id: lists[0].id,
- })
- cy.visit(`/lists/${lists[0].id}/`)
-
- cy.get('.menu-list li .list-menu-link .color-bubble')
- .should('have.css', 'background-color', 'rgb(0, 219, 96)')
- cy.get('.tasks-container .tasks .color-bubble')
- .should('not.exist')
- })
-
- it('Should paginate for > 50 tasks', () => {
- const tasks = TaskFactory.create(100, {
- id: '{increment}',
- title: i => `task${i}`,
- list_id: 1,
- })
- cy.visit('/lists/1/list')
-
- cy.get('.tasks-container .tasks')
- .should('contain', tasks[99].title)
-
- cy.get('.card-content .pagination .pagination-link')
- .contains('2')
- .click()
-
- cy.url()
- .should('contain', '?page=2')
- cy.get('.tasks-container .tasks')
- .should('contain', tasks[1].title)
- cy.get('.tasks-container .tasks')
- .should('not.contain', tasks[99].title)
- })
- })
-
- describe('Table View', () => {
- it('Should show a table with tasks', () => {
- const tasks = TaskFactory.create(1)
- cy.visit('/lists/1/table')
-
- cy.get('.table-view table.table')
- .should('exist')
- cy.get('.table-view table.table')
- .should('contain', tasks[0].title)
- })
-
- it('Should have working column switches', () => {
- TaskFactory.create(1)
- cy.visit('/lists/1/table')
-
- cy.get('.table-view .filter-container .items .button')
- .contains('Columns')
- .click()
- cy.get('.table-view .filter-container .card.columns-filter .card-content .fancycheckbox .check')
- .contains('Priority')
- .click()
- cy.get('.table-view .filter-container .card.columns-filter .card-content .fancycheckbox .check')
- .contains('Done')
- .click()
-
- cy.get('.table-view table.table th')
- .contains('Priority')
- .should('exist')
- cy.get('.table-view table.table th')
- .contains('Done')
- .should('not.exist')
- })
-
- it('Should navigate to the task when the title is clicked', () => {
- const tasks = TaskFactory.create(5, {
- id: '{increment}',
- list_id: 1,
- })
- cy.visit('/lists/1/table')
-
- cy.get('.table-view table.table')
- .contains(tasks[0].title)
- .click()
-
- cy.url()
- .should('contain', `/tasks/${tasks[0].id}`)
- })
- })
-
- describe('Gantt View', () => {
- it('Hides tasks with no dates', () => {
- const tasks = TaskFactory.create(1)
- cy.visit('/lists/1/gantt')
-
- cy.get('.gantt-chart-container .gantt-chart .tasks')
- .should('not.contain', tasks[0].title)
- })
-
- it('Shows tasks from the current and next month', () => {
- const now = new Date()
- const nextMonth = now
- nextMonth.setDate(1)
- nextMonth.setMonth(now.getMonth() + 1)
-
- cy.visit('/lists/1/gantt')
-
- cy.get('.gantt-chart-container .gantt-chart .months')
- .should('contain', format(now, 'MMMM'))
- .should('contain', format(nextMonth, 'MMMM'))
- })
-
- it('Shows tasks with dates', () => {
- const now = new Date()
- const tasks = TaskFactory.create(1, {
- start_date: formatISO(now),
- end_date: formatISO(now.setDate(now.getDate() + 4))
- })
- cy.visit('/lists/1/gantt')
-
- cy.get('.gantt-chart-container .gantt-chart .tasks')
- .should('not.be.empty')
- cy.get('.gantt-chart-container .gantt-chart .tasks')
- .should('contain', tasks[0].title)
- })
-
- it('Shows tasks with no dates after enabling them', () => {
- TaskFactory.create(1, {
- start_date: null,
- end_date: null,
- })
- cy.visit('/lists/1/gantt')
-
- cy.get('.gantt-chart-container .gantt-options .fancycheckbox')
- .contains('Show tasks which don\'t have dates set')
- .click()
-
- cy.get('.gantt-chart-container .gantt-chart .tasks')
- .should('not.be.empty')
- cy.get('.gantt-chart-container .gantt-chart .tasks .task.nodate')
- .should('exist')
- })
-
- it('Drags a task around', () => {
- const now = new Date()
- TaskFactory.create(1, {
- start_date: formatISO(now),
- end_date: formatISO(now.setDate(now.getDate() + 4))
- })
- cy.visit('/lists/1/gantt')
-
- cy.get('.gantt-chart-container .gantt-chart .tasks .task')
- .first()
- .trigger('mousedown', {which: 1})
- .trigger('mousemove', {clientX: 500, clientY: 0})
- .trigger('mouseup', {force: true})
- })
- })
-
- describe('Kanban', () => {
- let buckets
-
- beforeEach(() => {
- buckets = BucketFactory.create(2)
- })
-
- it('Shows all buckets with their tasks', () => {
- const data = TaskFactory.create(10, {
- list_id: 1,
- bucket_id: 1,
- })
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket .title')
- .contains(buckets[0].title)
- .should('exist')
- cy.get('.kanban .bucket .title')
- .contains(buckets[1].title)
- .should('exist')
- cy.get('.kanban .bucket')
- .first()
- .should('contain', data[0].title)
- })
-
- it('Can add a new task to a bucket', () => {
- const data = TaskFactory.create(2, {
- list_id: 1,
- bucket_id: 1,
- })
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket')
- .contains(buckets[0].title)
- .get('.bucket-footer .button')
- .contains('Add another task')
- .click()
- cy.get('.kanban .bucket')
- .contains(buckets[0].title)
- .get('.bucket-footer .field .control input.input')
- .type('New Task{enter}')
-
- cy.get('.kanban .bucket')
- .first()
- .should('contain', 'New Task')
- })
-
- it('Can create a new bucket', () => {
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket.new-bucket .button')
- .click()
- cy.get('.kanban .bucket.new-bucket input.input')
- .type('New Bucket{enter}')
-
- cy.wait(1000) // Wait for the request to finish
- cy.get('.kanban .bucket .title')
- .contains('New Bucket')
- .should('exist')
- })
-
- it('Can set a bucket limit', () => {
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
- .first()
- .click()
- cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
- .contains('Limit: Not Set')
- .click()
- cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
- .first()
- .type(3)
- cy.get('[data-cy="setBucketLimit"]')
- .first()
- .click()
-
- cy.get('.kanban .bucket .bucket-header span.limit')
- .contains('0/3')
- .should('exist')
- })
-
- it('Can rename a bucket', () => {
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket .bucket-header .title')
- .first()
- .type('{selectall}New Bucket Title{enter}')
- cy.get('.kanban .bucket .bucket-header .title')
- .first()
- .should('contain', 'New Bucket Title')
- })
-
- it('Can delete a bucket', () => {
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
- .first()
- .click()
- cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
- .contains('Delete')
- .click()
- cy.get('.modal-mask .modal-container .modal-content .header')
- .should('contain', 'Delete the bucket')
- cy.get('.modal-mask .modal-container .modal-content .actions .button')
- .contains('Do it!')
- .click()
-
- cy.get('.kanban .bucket .title')
- .contains(buckets[0].title)
- .should('not.exist')
- cy.get('.kanban .bucket .title')
- .contains(buckets[1].title)
- .should('exist')
- })
-
- it('Can drag tasks around', () => {
- const tasks = TaskFactory.create(2, {
- list_id: 1,
- bucket_id: 1,
- })
- cy.visit('/lists/1/kanban')
-
- cy.get('.kanban .bucket .tasks .task')
- .contains(tasks[0].title)
- .first()
- .drag('.kanban .bucket:nth-child(2) .tasks .dropper')
-
- cy.get('.kanban .bucket:nth-child(2) .tasks')
- .should('contain', tasks[0].title)
- cy.get('.kanban .bucket:nth-child(1) .tasks')
- .should('not.contain', tasks[0].title)
- })
-
- it('Should navigate to the task when the task card is clicked', () => {
- const tasks = TaskFactory.create(5, {
- id: '{increment}',
- list_id: 1,
- bucket_id: 1,
- })
- cy.visit('/lists/1/kanban')
-
- cy.getSettled('.kanban .bucket .tasks .task')
- .contains(tasks[0].title)
- .should('be.visible')
- .click()
-
- cy.url()
- .should('contain', `/tasks/${tasks[0].id}`)
- })
-
- it('Should remove a task from the kanban board when moving it to another list', () => {
- const lists = ListFactory.create(2)
- BucketFactory.create(2, {
- list_id: '{increment}',
- })
- const tasks = TaskFactory.create(5, {
- id: '{increment}',
- list_id: 1,
- bucket_id: 1,
- })
- const task = tasks[0]
- cy.visit('/lists/1/kanban')
-
- cy.getSettled('.kanban .bucket .tasks .task')
- .contains(task.title)
- .should('be.visible')
- .click()
-
- cy.get('.task-view .action-buttons .button')
- .contains('Move task')
- .click()
- cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
- .type(`${lists[1].title}{enter}`)
- // The requests happen with a 200ms timeout. Because of that, the results are not yet there when cypress
- // presses enter and we can't simulate pressing on enter to select the item.
- cy.get('.task-view .content.details .field .multiselect.control .search-results')
- .children()
- .first()
- .click()
-
- cy.get('.global-notification', { timeout: 1000 })
- .should('contain', 'Success')
- cy.go('back')
- cy.get('.kanban .bucket')
- .should('not.contain', task.title)
- })
- })
-
- describe('List history', () => {
- it('should show a list history on the home page', () => {
- const lists = ListFactory.create(6)
-
- cy.visit('/')
- cy.get('h3')
- .contains('Last viewed')
- .should('not.exist')
-
- cy.visit(`/lists/${lists[0].id}`)
- cy.visit(`/lists/${lists[1].id}`)
- cy.visit(`/lists/${lists[2].id}`)
- cy.visit(`/lists/${lists[3].id}`)
- cy.visit(`/lists/${lists[4].id}`)
- cy.visit(`/lists/${lists[5].id}`)
-
- cy.visit('/')
- cy.get('h3')
- .contains('Last viewed')
- .should('exist')
- cy.get('.list-cards-wrapper-2-rows')
- .should('not.contain', lists[0].title)
- .should('contain', lists[1].title)
- .should('contain', lists[2].title)
- .should('contain', lists[3].title)
- .should('contain', lists[4].title)
- .should('contain', lists[5].title)
- })
- })
})
diff --git a/cypress/integration/list/prepareLists.js b/cypress/integration/list/prepareLists.js
new file mode 100644
index 000000000..afef6ba4f
--- /dev/null
+++ b/cypress/integration/list/prepareLists.js
@@ -0,0 +1,16 @@
+import {ListFactory} from '../../factories/list'
+import {UserFactory} from '../../factories/user'
+import {NamespaceFactory} from '../../factories/namespace'
+import {TaskFactory} from '../../factories/task'
+
+export function prepareLists(setLists = () => {}) {
+ beforeEach(() => {
+ UserFactory.create(1)
+ NamespaceFactory.create(1)
+ const lists = ListFactory.create(1, {
+ title: 'First List'
+ })
+ setLists(lists)
+ TaskFactory.truncate()
+ })
+}
\ No newline at end of file
diff --git a/cypress/integration/task/task.spec.js b/cypress/integration/task/task.spec.js
index 1b85e9921..29ade1d24 100644
--- a/cypress/integration/task/task.spec.js
+++ b/cypress/integration/task/task.spec.js
@@ -116,6 +116,7 @@ describe('Task', () => {
.should('be.visible')
.should('contain', 'Done')
cy.get('.task-view .action-buttons p.created')
+ .scrollIntoView()
.should('be.visible')
.should('contain', 'Done')
})
@@ -209,7 +210,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
- .contains('Move task')
+ .contains('Move')
.click()
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
.type(`${lists[1].title}{enter}`)
@@ -236,7 +237,7 @@ describe('Task', () => {
cy.get('.task-view .action-buttons .button')
.should('be.visible')
- .contains('Delete task')
+ .contains('Delete')
.click()
cy.get('.modal-mask .modal-container .modal-content .header')
.should('contain', 'Delete this task')
@@ -316,7 +317,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
- .contains('Add labels')
+ .contains('Add Labels')
.should('be.visible')
.click()
cy.get('.task-view .details.labels-list .multiselect input')
@@ -343,7 +344,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
- .contains('Add labels')
+ .contains('Add Labels')
.click()
cy.get('.task-view .details.labels-list .multiselect input')
.type(labels[0].title)
@@ -372,13 +373,13 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
- cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
+ cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
.should('be.visible')
.should('contain', labels[0].title)
- cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
+ cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
.children()
.first()
- .get('a.delete')
+ .get('[data-cy="taskDetail.removeLabel"]')
.click()
cy.get('.global-notification')
diff --git a/cypress/integration/user/logout.spec.js b/cypress/integration/user/logout.spec.js
index fbbc7088c..1a22e21ef 100644
--- a/cypress/integration/user/logout.spec.js
+++ b/cypress/integration/user/logout.spec.js
@@ -6,7 +6,7 @@ describe('Log out', () => {
cy.get('.navbar .user .username')
.click()
- cy.get('.navbar .user .dropdown-menu a.dropdown-item')
+ cy.get('.navbar .user .dropdown-menu .dropdown-item')
.contains('Logout')
.click()
diff --git a/cypress/integration/user/registration.spec.js b/cypress/integration/user/registration.spec.js
index fd940aa7e..16e959d7b 100644
--- a/cypress/integration/user/registration.spec.js
+++ b/cypress/integration/user/registration.spec.js
@@ -25,7 +25,6 @@ context('Registration', () => {
cy.get('#username').type(fixture.username)
cy.get('#email').type(fixture.email)
cy.get('#password').type(fixture.password)
- cy.get('#passwordValidation').type(fixture.password)
cy.get('#register-submit').click()
cy.url().should('include', '/')
cy.clock(1625656161057) // 13:00
@@ -43,7 +42,6 @@ context('Registration', () => {
cy.get('#username').type(fixture.username)
cy.get('#email').type(fixture.email)
cy.get('#password').type(fixture.password)
- cy.get('#passwordValidation').type(fixture.password)
cy.get('#register-submit').click()
cy.get('div.message.danger').contains('A user with this username already exists.')
})
diff --git a/cypress/integration/user/settings.spec.js b/cypress/integration/user/settings.spec.js
index c6a645d59..21bd9c1d9 100644
--- a/cypress/integration/user/settings.spec.js
+++ b/cypress/integration/user/settings.spec.js
@@ -8,12 +8,14 @@ describe('User Settings', () => {
})
it('Changes the user avatar', () => {
+ cy.intercept(`${Cypress.env('API_URL')}/user/settings/avatar/upload`).as('uploadAvatar')
+
cy.visit('/user/settings/avatar')
cy.get('input[name=avatarProvider][value=upload]')
.click()
- cy.get('input[type=file]', { timeout: 1000 })
- .attachFile('image.jpg')
+ cy.get('input[type=file]', {timeout: 1000})
+ .selectFile('cypress/fixtures/image.jpg', {force: true}) // The input is not visible, but on purpose
cy.get('.vue-handler-wrapper.vue-handler-wrapper--south .vue-simple-handler.vue-simple-handler--south')
.trigger('mousedown', {which: 1})
.trigger('mousemove', {clientY: 100})
@@ -22,7 +24,7 @@ describe('User Settings', () => {
.contains('Upload Avatar')
.click()
- cy.wait(3000) // Wait for the request to finish
+ cy.wait('@uploadAvatar')
cy.get('.global-notification')
.should('contain', 'Success')
})
diff --git a/cypress/support/index.js b/cypress/support/index.js
index 0c885c654..7b0c56d18 100644
--- a/cypress/support/index.js
+++ b/cypress/support/index.js
@@ -1,6 +1,5 @@
import './commands'
-import 'cypress-file-upload'
import '@4tw/cypress-drag-drop'
// see https://github.com/cypress-io/cypress/issues/702#issuecomment-587127275
diff --git a/netlify.toml b/netlify.toml
index 24ee45e7f..a0bfdfabc 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -1,6 +1,6 @@
[build]
command = "yarn build"
- publish = "dist"
+ publish = "dist-preview"
[[redirects]]
from = "/*"
diff --git a/package.json b/package.json
index eb3ff293f..f543b0f54 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"scripts": {
"serve": "vite",
"serve:dist-dev": "node scripts/serve-dist.js",
- "serve:dist": "vite preview",
+ "serve:dist": "vite preview --port 4173",
"build": "vite build && workbox copyLibraries dist/",
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
"build:dev": "vite build -m development --outDir dist-dev/",
@@ -18,82 +18,80 @@
"browserslist:update": "npx browserslist@latest --update-db"
},
"dependencies": {
- "@github/hotkey": "1.6.1",
+ "@github/hotkey": "2.0.0",
"@kyvg/vue3-notification": "2.3.4",
- "@sentry/tracing": "6.16.1",
- "@sentry/vue": "6.16.1",
+ "@sentry/tracing": "6.19.3",
+ "@sentry/vue": "6.19.3",
"@types/is-touch-device": "1.0.0",
- "@vue/compat": "3.2.27",
- "@vueuse/core": "7.5.2",
- "@vueuse/router": "7.5.3",
+ "@vue/compat": "3.2.31",
+ "@vueuse/core": "8.2.3",
+ "@vueuse/router": "8.2.3",
"blurhash": "^1.1.4",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
- "codemirror": "5.65.0",
+ "codemirror": "5.65.2",
"copy-to-clipboard": "3.3.1",
"date-fns": "2.28.0",
- "dompurify": "2.3.4",
+ "dompurify": "2.3.6",
"easymde": "2.16.1",
- "flatpickr": "4.6.9",
+ "flatpickr": "4.6.11",
"flexsearch": "0.7.21",
- "highlight.js": "11.4.0",
+ "highlight.js": "11.5.0",
"is-touch-device": "1.0.1",
"lodash.clonedeep": "4.5.0",
"lodash.debounce": "4.0.8",
- "marked": "4.0.10",
+ "marked": "4.0.12",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
- "ufo": "0.7.9",
+ "ufo": "0.8.3",
"v-tooltip": "4.0.0-beta.17",
- "vue": "3.2.27",
- "vue-advanced-cropper": "2.7.1",
+ "vue": "3.2.31",
+ "vue-advanced-cropper": "2.8.1",
"vue-drag-resize": "2.0.3",
"vue-flatpickr-component": "9.0.5",
- "vue-i18n": "9.2.0-beta.28",
- "vue-router": "4.0.12",
+ "vue-i18n": "9.2.0-beta.30",
+ "vue-router": "4.0.14",
"vuedraggable": "4.1.0",
"vuex": "4.0.2",
- "workbox-precaching": "6.4.2"
+ "workbox-precaching": "6.5.2"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.1.0",
- "@fortawesome/fontawesome-svg-core": "1.2.36",
- "@fortawesome/free-regular-svg-icons": "5.15.4",
- "@fortawesome/free-solid-svg-icons": "5.15.4",
+ "@faker-js/faker": "6.1.1",
+ "@fortawesome/fontawesome-svg-core": "6.1.1",
+ "@fortawesome/free-regular-svg-icons": "6.1.1",
+ "@fortawesome/free-solid-svg-icons": "6.1.1",
"@fortawesome/vue-fontawesome": "3.0.0-5",
- "@types/flexsearch": "0.7.2",
- "@typescript-eslint/eslint-plugin": "5.9.1",
- "@typescript-eslint/parser": "5.9.1",
- "@vitejs/plugin-legacy": "1.6.4",
- "@vitejs/plugin-vue": "2.0.1",
+ "@types/flexsearch": "0.7.3",
+ "@typescript-eslint/eslint-plugin": "5.17.0",
+ "@typescript-eslint/parser": "5.17.0",
+ "@vitejs/plugin-legacy": "1.8.0",
+ "@vitejs/plugin-vue": "2.3.1",
"@vue/eslint-config-typescript": "10.0.0",
- "autoprefixer": "10.4.2",
- "axios": "0.24.0",
- "browserslist": "4.19.1",
- "caniuse-lite": "1.0.30001299",
- "cypress": "9.2.1",
- "cypress-file-upload": "5.0.8",
- "esbuild": "0.14.11",
- "eslint": "8.7.0",
- "eslint-plugin-vue": "8.3.0",
- "express": "4.17.2",
- "faker": "5.5.3",
- "netlify-cli": "8.8.2",
- "happy-dom": "2.25.2",
- "postcss": "8.4.5",
- "postcss-preset-env": "7.2.3",
- "rollup": "2.64.0",
- "rollup-plugin-visualizer": "5.5.4",
- "sass": "1.48.0",
- "slugify": "1.6.5",
- "typescript": "4.5.4",
- "vite": "2.7.12",
+ "autoprefixer": "10.4.4",
+ "axios": "0.26.1",
+ "browserslist": "4.20.2",
+ "caniuse-lite": "1.0.30001324",
+ "cypress": "9.5.3",
+ "esbuild": "0.14.30",
+ "eslint": "8.12.0",
+ "eslint-plugin-vue": "8.5.0",
+ "express": "4.17.3",
+ "happy-dom": "2.55.0",
+ "netlify-cli": "9.13.5",
+ "postcss": "8.4.12",
+ "postcss-preset-env": "7.4.3",
+ "rollup": "2.70.1",
+ "rollup-plugin-visualizer": "5.6.0",
+ "sass": "1.49.11",
+ "typescript": "4.6.3",
+ "vite": "2.9.1",
"vite-plugin-pwa": "0.11.13",
- "vite-svg-loader": "3.1.2",
- "vitest": "0.1.17",
- "vue-tsc": "0.30.4",
- "wait-on": "6.0.0",
- "workbox-cli": "6.4.2"
+ "vite-svg-loader": "3.2.0",
+ "vitest": "0.8.2",
+ "vue-tsc": "0.33.9",
+ "wait-on": "6.0.1",
+ "workbox-cli": "6.5.2"
},
"eslintConfig": {
"root": true,
@@ -131,7 +129,7 @@
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
- "ecmaVersion": 2021
+ "ecmaVersion": 2022
},
"ignorePatterns": [
"*.test.*",
@@ -147,5 +145,5 @@
}
},
"license": "AGPL-3.0-or-later",
- "packageManager": "yarn@1.22.17"
+ "packageManager": "yarn@1.22.18"
}
diff --git a/scripts/deploy-preview-netlify.js b/scripts/deploy-preview-netlify.js
index b2dd23364..11eac4e3a 100644
--- a/scripts/deploy-preview-netlify.js
+++ b/scripts/deploy-preview-netlify.js
@@ -1,20 +1,24 @@
-const slugify = require('slugify')
const {exec} = require('child_process')
const axios = require('axios')
const BOT_USER_ID = 513
const giteaToken = process.env.GITEA_TOKEN
const siteId = process.env.NETLIFY_SITE_ID
-const branchSlug = slugify(process.env.DRONE_SOURCE_BRANCH)
+const branchSlug = String(process.env.DRONE_SOURCE_BRANCH)
+ .trim()
+ .normalize('NFKD')
+ .toLowerCase()
+ .replace(/[.\s/]/g, '-')
+ .replace(/[^A-Za-z\d-]/g, '')
const prNumber = process.env.DRONE_PULL_REQUEST
const prIssueCommentsUrl = `https://kolaente.dev/api/v1/repos/vikunja/frontend/issues/${prNumber}/comments`
-const alias = `${prNumber}-${branchSlug}`
+const alias = `${prNumber}-${branchSlug}`.substring(0,37)
const fullPreviewUrl = `https://${alias}--vikunja-frontend-preview.netlify.app`
const promiseExec = cmd => {
return new Promise((resolve, reject) => {
- exec(cmd, (error, stdout, stderr) => {
+ exec(cmd, (error, stdout) => {
if (error) {
reject(error)
return
diff --git a/scripts/deploy-preview-netlify.js.sha384 b/scripts/deploy-preview-netlify.js.sha384
index fe5f72f1d..03ac06468 100644
--- a/scripts/deploy-preview-netlify.js.sha384
+++ b/scripts/deploy-preview-netlify.js.sha384
@@ -1 +1 @@
-55ce0faaa2c1919341617ccfaeccbb6029ac12107964ff488985cff13dd952f1a991df3ab0d4b0705deb761e508e6434 ./scripts/deploy-preview-netlify.js
+bb46342a0a08105b340ba7976cff9d80ef89901120ec0639669caa70bb7d2dbc43e78b1f635a7654ab2456e8358c98a4 ./scripts/deploy-preview-netlify.js
diff --git a/scripts/serve-dist.js b/scripts/serve-dist.js
index e0303dd7f..f6e092e5f 100644
--- a/scripts/serve-dist.js
+++ b/scripts/serve-dist.js
@@ -3,7 +3,7 @@ const express = require('express')
const app = express()
const p = path.join(__dirname, '..', 'dist-dev')
-const port = 5000
+const port = 4173
app.use(express.static(p))
// Handle urls set by the frontend
diff --git a/src/App.vue b/src/App.vue
index 06a818506..4080a6cc9 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,7 +1,7 @@
-
+
@@ -27,7 +27,7 @@ import {success} from '@/message'
import Notification from '@/components/misc/notification.vue'
import KeyboardShortcuts from './components/misc/keyboard-shortcuts/index.vue'
-import TopNavigation from './components/home/topNavigation.vue'
+import TheNavigation from '@/components/home/TheNavigation.vue'
import ContentAuth from './components/home/contentAuth.vue'
import ContentLinkShare from './components/home/contentLinkShare.vue'
import NoAuthWrapper from '@/components/misc/no-auth-wrapper.vue'
@@ -42,7 +42,7 @@ import {useBodyClass} from '@/composables/useBodyClass'
const store = useStore()
const router = useRouter()
-useBodyClass('is-touch', isTouchDevice)
+useBodyClass('is-touch', isTouchDevice())
const keyboardShortcutsActive = computed(() => store.state.keyboardShortcutsActive)
const authUser = computed(() => store.getters['auth/authUser'])
diff --git a/src/assets/logo-full-pride.svg b/src/assets/logo-full-pride.svg
index 1ecacb3e9..bff6f8bba 100644
--- a/src/assets/logo-full-pride.svg
+++ b/src/assets/logo-full-pride.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/assets/logo-full.svg b/src/assets/logo-full.svg
index 20b6ae130..db656b85e 100644
--- a/src/assets/logo-full.svg
+++ b/src/assets/logo-full.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/components/base/BaseButton.vue b/src/components/base/BaseButton.vue
index 8096e8a8a..f22dd8288 100644
--- a/src/components/base/BaseButton.vue
+++ b/src/components/base/BaseButton.vue
@@ -69,10 +69,10 @@ watchEffect(() => {
}
// if there is a href we assume the user wants an external link via a link element
- // we also set the attribute rel to "noopener" but make it possible to overwrite this by the user.
+ // we also set a predefined value for the attribute rel, but make it possible to overwrite this by the user.
if ('href' in attrs) {
nodeName = 'a'
- bindings = {rel: 'noopener'}
+ bindings = {rel: 'noreferrer noopener nofollow'}
}
componentNodeName.value = nodeName
diff --git a/src/components/date/dateRanges.ts b/src/components/date/dateRanges.ts
new file mode 100644
index 000000000..648f001b4
--- /dev/null
+++ b/src/components/date/dateRanges.ts
@@ -0,0 +1,21 @@
+export const DATE_RANGES = {
+ // Format:
+ // Key is the title, as a translation string, the first entry of the value array
+ // is the "from" date, the second one is the "to" date.
+ 'today': ['now/d', 'now/d+1d'],
+
+ 'lastWeek': ['now/w-1w', 'now/w-2w'],
+ 'thisWeek': ['now/w', 'now/w+1w'],
+ 'restOfThisWeek': ['now', 'now/w+1w'],
+ 'nextWeek': ['now/w+1w', 'now/w+2w'],
+ 'next7Days': ['now', 'now+7d'],
+
+ 'lastMonth': ['now/M-1M', 'now/M-2M'],
+ 'thisMonth': ['now/M', 'now/M+1M'],
+ 'restOfThisMonth': ['now', 'now/M+1M'],
+ 'nextMonth': ['now/M+1M', 'now/M+2M'],
+ 'next30Days': ['now', 'now+30d'],
+
+ 'thisYear': ['now/y', 'now/y+1y'],
+ 'restOfThisYear': ['now', 'now/y+1y'],
+}
diff --git a/src/components/date/datemathHelp.vue b/src/components/date/datemathHelp.vue
new file mode 100644
index 000000000..f645fdb3e
--- /dev/null
+++ b/src/components/date/datemathHelp.vue
@@ -0,0 +1,131 @@
+
+
+
+ {{ $t('input.datemathHelp.intro') }}
+
+
+
+ now
+ ||
+
+
+
+
+
+ Grafana
+
+
+ Elasticsearch
+
+
+
+ {{ $t('misc.forExample') }}
+
+ +1d
{{ $t('input.datemathHelp.add1Day') }}
+ -1d
{{ $t('input.datemathHelp.minus1Day') }}
+ /d
{{ $t('input.datemathHelp.roundDay') }}
+
+ {{ $t('input.datemathHelp.supportedUnits') }}
+
+
+
+ s
+ {{ $t('input.datemathHelp.units.seconds') }}
+
+
+ m
+ {{ $t('input.datemathHelp.units.minutes') }}
+
+
+ h
+ {{ $t('input.datemathHelp.units.hours') }}
+
+
+ H
+ {{ $t('input.datemathHelp.units.hours') }}
+
+
+ d
+ {{ $t('input.datemathHelp.units.days') }}
+
+
+ w
+ {{ $t('input.datemathHelp.units.weeks') }}
+
+
+ M
+ {{ $t('input.datemathHelp.units.months') }}
+
+
+ y
+ {{ $t('input.datemathHelp.units.years') }}
+
+
+
+
+ {{ $t('input.datemathHelp.someExamples') }}
+
+
+
+ now
+ {{ $t('input.datemathHelp.examples.now') }}
+
+
+ now+24h
+ {{ $t('input.datemathHelp.examples.in24h') }}
+
+
+ now/d
+ {{ $t('input.datemathHelp.examples.today') }}
+
+
+ now/w
+ {{ $t('input.datemathHelp.examples.beginningOfThisWeek') }}
+
+
+ now/w+1w
+ {{ $t('input.datemathHelp.examples.endOfThisWeek') }}
+
+
+ now+30d
+ {{ $t('input.datemathHelp.examples.in30Days') }}
+
+
+ {{ exampleDate }}||+1M/d
+
+
+ {{ exampleDate }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/date/datepickerWithRange.vue b/src/components/date/datepickerWithRange.vue
new file mode 100644
index 000000000..61c21ef53
--- /dev/null
+++ b/src/components/date/datepickerWithRange.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t('misc.custom') }}
+
+
+ {{ $t(`input.datepickerRange.ranges.${text}`) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/home/Logo.vue b/src/components/home/Logo.vue
index 5a5612b46..6b9f12f8f 100644
--- a/src/components/home/Logo.vue
+++ b/src/components/home/Logo.vue
@@ -1,9 +1,12 @@
diff --git a/src/components/home/MenuButton.vue b/src/components/home/MenuButton.vue
index ab012c92b..8d2e0d664 100644
--- a/src/components/home/MenuButton.vue
+++ b/src/components/home/MenuButton.vue
@@ -1,8 +1,7 @@
-
-
\ No newline at end of file
diff --git a/src/components/home/navigation.vue b/src/components/home/navigation.vue
index adf0bff95..142ba5c2d 100644
--- a/src/components/home/navigation.vue
+++ b/src/components/home/navigation.vue
@@ -1,12 +1,12 @@
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/list/list-settings-dropdown.vue b/src/components/list/list-settings-dropdown.vue
index 35fc63567..b8a9082b9 100644
--- a/src/components/list/list-settings-dropdown.vue
+++ b/src/components/list/list-settings-dropdown.vue
@@ -2,21 +2,22 @@
{{ $t('menu.edit') }}
{{ $t('misc.delete') }}
+
{{ $t('menu.unarchive') }}
@@ -24,37 +25,38 @@
{{ $t('menu.edit') }}
{{ $t('menu.setBackground') }}
{{ $t('menu.share') }}
{{ $t('menu.duplicate') }}
{{ $t('menu.archive') }}
subscription = sub"
/>
@@ -73,56 +75,32 @@
-
diff --git a/src/components/list/partials/filter-popup.vue b/src/components/list/partials/filter-popup.vue
index efbfcbe3e..8e156106e 100644
--- a/src/components/list/partials/filter-popup.vue
+++ b/src/components/list/partials/filter-popup.vue
@@ -6,7 +6,7 @@
>
{{ $t('filters.clear') }}
-
+
import Filters from '@/components/list/partials/filters'
-import {getDefaultParams} from '@/components/tasks/mixins/taskList'
import Popup from '@/components/misc/popup'
+import {getDefaultParams} from '@/composables/taskList'
+
export default {
name: 'filter-popup',
components: {
diff --git a/src/components/list/partials/filters.vue b/src/components/list/partials/filters.vue
index cbbb82f14..810391481 100644
--- a/src/components/list/partials/filters.vue
+++ b/src/components/list/partials/filters.vue
@@ -67,49 +67,49 @@
{{ $t('task.attributes.dueDate') }}
-
+ setDateFilter('due_date', values)">
+
+
+ {{ buttonText }}
+
+
+
{{ $t('task.attributes.startDate') }}
-
+ setDateFilter('start_date', values)">
+
+
+ {{ buttonText }}
+
+
+
{{ $t('task.attributes.endDate') }}
-
+ setDateFilter('end_date', values)">
+
+
+ {{ buttonText }}
+
+
+
{{ $t('task.attributes.reminders') }}
-
+ setDateFilter('reminders', values)">
+
+
+ {{ buttonText }}
+
+
+
@@ -175,15 +175,14 @@
-
diff --git a/src/components/list/partials/list-card.vue b/src/components/list/partials/list-card.vue
index c101a3fc9..cfa34fc85 100644
--- a/src/components/list/partials/list-card.vue
+++ b/src/components/list/partials/list-card.vue
@@ -34,13 +34,14 @@
diff --git a/src/components/misc/keyboard-shortcuts/index.vue b/src/components/misc/keyboard-shortcuts/index.vue
index 3164dade1..274c5ff57 100644
--- a/src/components/misc/keyboard-shortcuts/index.vue
+++ b/src/components/misc/keyboard-shortcuts/index.vue
@@ -4,9 +4,15 @@
{{ $t(s.title) }}
-
+
{{
- s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages')
+ typeof s.available === 'undefined' ?
+ $t('keyboardShortcuts.allPages') :
+ (
+ s.available($route)
+ ? $t('keyboardShortcuts.currentPageOnly')
+ : $t('keyboardShortcuts.somePagesOnly')
+ )
}}
@@ -17,7 +23,8 @@
class="shortcut-keys"
is="dd"
:keys="sc.keys"
- :combination="typeof sc.combination !== 'undefined' ? $t(`keyboardShortcuts.${sc.combination}`) : null"/>
+ :combination="sc.combination && $t(`keyboardShortcuts.${sc.combination}`)"
+ />
@@ -25,28 +32,19 @@
-
diff --git a/src/components/misc/keyboard-shortcuts/shortcuts.js b/src/components/misc/keyboard-shortcuts/shortcuts.ts
similarity index 54%
rename from src/components/misc/keyboard-shortcuts/shortcuts.js
rename to src/components/misc/keyboard-shortcuts/shortcuts.ts
index bcc5014b4..e69722f92 100644
--- a/src/components/misc/keyboard-shortcuts/shortcuts.js
+++ b/src/components/misc/keyboard-shortcuts/shortcuts.ts
@@ -1,11 +1,24 @@
+import {RouteLocation} from 'vue-router'
+
import {isAppleDevice} from '@/helpers/isAppleDevice'
const ctrl = isAppleDevice() ? '⌘' : 'ctrl'
-export const KEYBOARD_SHORTCUTS = [
+interface Shortcut {
+ title: string
+ keys: string[]
+ combination?: 'then'
+}
+
+interface ShortcutGroup {
+ title: string
+ available?: (route: RouteLocation) => boolean
+ shortcuts: Shortcut[]
+}
+
+export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
{
title: 'keyboardShortcuts.general',
- available: () => null,
shortcuts: [
{
title: 'keyboardShortcuts.toggleMenu',
@@ -17,6 +30,36 @@ export const KEYBOARD_SHORTCUTS = [
},
],
},
+ {
+ title: 'keyboardShortcuts.navigation.title',
+ shortcuts: [
+ {
+ title: 'keyboardShortcuts.navigation.overview',
+ keys: ['g', 'o'],
+ combination: 'then',
+ },
+ {
+ title: 'keyboardShortcuts.navigation.upcoming',
+ keys: ['g', 'u'],
+ combination: 'then',
+ },
+ {
+ title: 'keyboardShortcuts.navigation.namespaces',
+ keys: ['g', 'n'],
+ combination: 'then',
+ },
+ {
+ title: 'keyboardShortcuts.navigation.labels',
+ keys: ['g', 'a'],
+ combination: 'then',
+ },
+ {
+ title: 'keyboardShortcuts.navigation.teams',
+ keys: ['g', 'm'],
+ combination: 'then',
+ },
+ ],
+ },
{
title: 'list.kanban.title',
available: (route) => route.name === 'list.kanban',
@@ -29,7 +72,7 @@ export const KEYBOARD_SHORTCUTS = [
},
{
title: 'keyboardShortcuts.list.title',
- available: (route) => route.name.startsWith('list.'),
+ available: (route) => (route.name as string)?.startsWith('list.'),
shortcuts: [
{
title: 'keyboardShortcuts.list.switchToListView',
@@ -55,14 +98,12 @@ export const KEYBOARD_SHORTCUTS = [
},
{
title: 'keyboardShortcuts.task.title',
- available: (route) => [
- 'task.detail',
- 'task.list.detail',
- 'task.gantt.detail',
- 'task.kanban.detail',
- 'task.detail',
- ].includes(route.name),
+ available: (route) => route.name === 'task.detail',
shortcuts: [
+ {
+ title: 'keyboardShortcuts.task.done',
+ keys: ['t'],
+ },
{
title: 'keyboardShortcuts.task.assign',
keys: ['a'],
@@ -83,6 +124,14 @@ export const KEYBOARD_SHORTCUTS = [
title: 'keyboardShortcuts.task.related',
keys: ['r'],
},
+ {
+ title: 'keyboardShortcuts.task.move',
+ keys: ['m'],
+ },
+ {
+ title: 'keyboardShortcuts.task.color',
+ keys: ['c'],
+ },
],
},
]
diff --git a/src/components/misc/message.vue b/src/components/misc/message.vue
index df60cc384..7ff84f9fe 100644
--- a/src/components/misc/message.vue
+++ b/src/components/misc/message.vue
@@ -1,18 +1,35 @@
-
-.modal-enter .modal-container,
-.modal-leave-active .modal-container {
- transform: scale(0.9);
+
\ No newline at end of file
diff --git a/src/components/namespace/namespace-search.vue b/src/components/namespace/namespace-search.vue
index 19a5c3b2b..1dd1354eb 100644
--- a/src/components/namespace/namespace-search.vue
+++ b/src/components/namespace/namespace-search.vue
@@ -13,6 +13,7 @@
import {ref, computed} from 'vue'
import {useStore} from 'vuex'
import Multiselect from '@/components/input/multiselect.vue'
+import NamespaceModel from '@/models/namespace'
const emit = defineEmits(['selected'])
@@ -25,7 +26,7 @@ function findNamespaces(newQuery: string) {
query.value = newQuery
}
-function select(namespace) {
+function select(namespace: NamespaceModel) {
emit('selected', namespace)
}
diff --git a/src/components/namespace/namespace-settings-dropdown.vue b/src/components/namespace/namespace-settings-dropdown.vue
index c27525789..3359fed95 100644
--- a/src/components/namespace/namespace-settings-dropdown.vue
+++ b/src/components/namespace/namespace-settings-dropdown.vue
@@ -16,13 +16,13 @@
{{ $t('menu.edit') }}
{{ $t('menu.share') }}
{{ $t('menu.newList') }}
@@ -34,6 +34,7 @@
{{ $t('menu.archive') }}
n.readAt === null).length
},
notifications() {
- return this.allNotifications.filter(n => n.name !== '')
+ return this.allNotifications ? this.allNotifications.filter(n => n.name !== '') : []
},
...mapState({
userInfo: state => state.auth.info,
diff --git a/src/components/sharing/linkSharing.vue b/src/components/sharing/linkSharing.vue
index d07577ded..99da29b98 100644
--- a/src/components/sharing/linkSharing.vue
+++ b/src/components/sharing/linkSharing.vue
@@ -264,4 +264,6 @@ export default {
.sharables-list:not(.card-content) {
overflow-y: auto
}
+
+@include modal-transition();
\ No newline at end of file
diff --git a/src/components/sharing/userTeam.vue b/src/components/sharing/userTeam.vue
index 007cfaa68..7b7debddc 100644
--- a/src/components/sharing/userTeam.vue
+++ b/src/components/sharing/userTeam.vue
@@ -365,3 +365,7 @@ export default {
},
}
+
+
\ No newline at end of file
diff --git a/src/components/tasks/add-task.vue b/src/components/tasks/add-task.vue
index 0ddc61555..c5d00b12f 100644
--- a/src/components/tasks/add-task.vue
+++ b/src/components/tasks/add-task.vue
@@ -5,12 +5,13 @@
@@ -24,15 +25,18 @@
@click="addTask()"
icon="plus"
:loading="taskService.loading"
+ :aria-label="$t('list.list.add')"
>
- {{ $t('list.list.add') }}
+
+ {{ $t('list.list.add') }}
+
{{ errorMessage }}
-
+
@@ -40,7 +44,7 @@
import {ref, watch, unref, shallowReactive} from 'vue'
import {useI18n} from 'vue-i18n'
import {useStore} from 'vuex'
-import { tryOnMounted, debouncedWatch, useWindowSize, MaybeRef } from '@vueuse/core'
+import {tryOnMounted, debouncedWatch, useWindowSize, MaybeRef} from '@vueuse/core'
import TaskService from '@/services/task'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
@@ -54,12 +58,12 @@ function useAutoHeightTextarea(value: MaybeRef) {
const minHeight = ref(0)
// adapted from https://github.com/LeaVerou/stretchy/blob/47f5f065c733029acccb755cae793009645809e2/src/stretchy.js#L34
- function resize(textareaEl: HTMLInputElement|undefined) {
+ function resize(textareaEl: HTMLInputElement | undefined) {
if (!textareaEl) return
let empty
- // the value here is the the attribute value
+ // the value here is the attribute value
if (!textareaEl.value && textareaEl.placeholder) {
empty = true
textareaEl.value = textareaEl.placeholder
@@ -95,12 +99,12 @@ function useAutoHeightTextarea(value: MaybeRef) {
}
})
- const { width: windowWidth } = useWindowSize()
+ const {width: windowWidth} = useWindowSize()
debouncedWatch(
windowWidth,
() => resize(textarea.value),
- { debounce: 200 },
+ {debounce: 200},
)
// It is not possible to get notified of a change of the value attribute of a textarea without workarounds (setTimeout)
@@ -129,12 +133,18 @@ const emit = defineEmits(['taskAdded'])
const newTaskTitle = ref('')
const newTaskInput = useAutoHeightTextarea(newTaskTitle)
-const { t } = useI18n()
+const {t} = useI18n()
const store = useStore()
const taskService = shallowReactive(new TaskService())
const errorMessage = ref('')
+function resetEmptyTitleError() {
+ if (newTaskTitle.value !== '') {
+ errorMessage.value = ''
+ }
+}
+
async function addTask() {
if (newTaskTitle.value === '') {
errorMessage.value = t('list.create.addTitleRequired')
@@ -194,9 +204,26 @@ function handleEnter(e: KeyboardEvent) {
.add-task-button {
height: 2.5rem;
+
+ @media screen and (max-width: $mobile) {
+ .button-text {
+ display: none;
+ }
+
+ :deep(.icon) {
+ margin: 0 !important;
+ }
+ }
}
+
.add-task-textarea {
transition: border-color $transition;
resize: none;
}
+
+// Adding this class when the textarea has no text prevents the textarea from wrapping the placeholder.
+.textarea-empty {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
diff --git a/src/components/tasks/edit-task.vue b/src/components/tasks/edit-task.vue
index e4ada125d..718304c61 100644
--- a/src/components/tasks/edit-task.vue
+++ b/src/components/tasks/edit-task.vue
@@ -67,7 +67,7 @@
{{ $t('task.openDetail') }}
@@ -97,6 +97,15 @@ export default {
taskEditTask: TaskModel,
}
},
+ computed: {
+ taskDetailRoute() {
+ return {
+ name: 'task.detail',
+ params: { id: this.taskEditTask.id },
+ state: { backdropView: this.$router.currentRoute.value.fullPath },
+ }
+ },
+ },
components: {
ColorPicker,
Reminders,
diff --git a/src/components/tasks/mixins/taskList.js b/src/components/tasks/mixins/taskList.js
deleted file mode 100644
index a9b2e5878..000000000
--- a/src/components/tasks/mixins/taskList.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import TaskCollectionService from '@/services/taskCollection'
-
-// FIXME: merge with DEFAULT_PARAMS in filters.vue
-export const getDefaultParams = () => ({
- sort_by: ['position', 'id'],
- order_by: ['asc', 'desc'],
- filter_by: ['done'],
- filter_value: ['false'],
- filter_comparator: ['equals'],
- filter_concat: 'and',
-})
-
-/**
- * This mixin provides a base set of methods and properties to get tasks on a list.
- */
-export default {
- data() {
- return {
- taskCollectionService: new TaskCollectionService(),
- tasks: [],
-
- currentPage: 0,
-
- loadedList: null,
-
- searchTerm: '',
-
- showTaskFilter: false,
- params: {...getDefaultParams()},
- }
- },
- watch: {
- // Only listen for query path changes
- '$route.query': {
- handler: 'loadTasksForPage',
- immediate: true,
- },
- '$route.path': 'loadTasksOnSavedFilter',
- },
- methods: {
- async loadTasks(
- page,
- search = '',
- params = null,
- forceLoading = false,
- ) {
- // Because this function is triggered every time on topNavigation, we're putting a condition here to only load it when we actually want to show tasks
- // FIXME: This is a bit hacky -> Cleanup.
- if (
- this.$route.name !== 'list.list' &&
- this.$route.name !== 'list.table' &&
- !forceLoading
- ) {
- return
- }
-
- if (params === null) {
- params = this.params
- }
-
- if (search !== '') {
- params.s = search
- }
-
- const list = {listId: parseInt(this.$route.params.listId)}
-
- const currentList = {
- id: list.listId,
- params,
- search,
- page,
- }
- if (JSON.stringify(currentList) === JSON.stringify(this.loadedList) && !forceLoading) {
- return
- }
-
- this.tasks = []
- this.tasks = await this.taskCollectionService.getAll(list, params, page)
- this.currentPage = page
- this.loadedList = JSON.parse(JSON.stringify(currentList))
- },
-
- loadTasksForPage(e) {
- // The page parameter can be undefined, in the case where the user loads a new list from the side bar menu
- let page = Number(e.page)
- if (typeof e.page === 'undefined') {
- page = 1
- }
- let search = e.search
- if (typeof e.search === 'undefined') {
- search = ''
- }
- this.initTasks(page, search)
- },
- loadTasksOnSavedFilter() {
- if (typeof this.$route.params.listId !== 'undefined' && parseInt(this.$route.params.listId) < 0) {
- this.loadTasks(1, '', null, true)
- }
- },
- },
-}
\ No newline at end of file
diff --git a/src/components/tasks/partials/attachments.vue b/src/components/tasks/partials/attachments.vue
index a01a62014..7d507e5eb 100644
--- a/src/components/tasks/partials/attachments.vue
+++ b/src/components/tasks/partials/attachments.vue
@@ -34,7 +34,7 @@
>
{{ a.file.name }}
-
+
{{ formatDateSince(a.created) }}
@@ -289,21 +289,6 @@ export default {
content: '·';
padding: 0 .25rem;
}
-
- @media screen and (max-width: $mobile) {
- &.collapses {
- flex-direction: column;
-
- > span:not(:last-child):after,
- > a:not(:last-child):after {
- display: none;
- }
-
- .user .username {
- display: none;
- }
- }
- }
}
}
}
@@ -341,6 +326,10 @@ export default {
height: auto;
text-shadow: var(--shadow-md);
animation: bounce 2s infinite;
+
+ @media (prefers-reduced-motion: reduce) {
+ animation: none;
+ }
}
.hint {
@@ -357,6 +346,35 @@ export default {
}
}
+.attachment-info-meta {
+ display: flex;
+ align-items: center;
+
+ :deep(.user) {
+ display: flex !important;
+ align-items: center;
+ margin: 0 .5rem;
+ }
+
+ @media screen and (max-width: $mobile) {
+ flex-direction: column;
+ align-items: flex-start;
+
+ :deep(.user) {
+ margin: .5rem 0;
+ }
+
+ > span:not(:last-child):after,
+ > a:not(:last-child):after {
+ display: none;
+ }
+
+ .user .username {
+ display: none;
+ }
+ }
+}
+
@keyframes bounce {
from,
20%,
@@ -382,4 +400,6 @@ export default {
transform: translate3d(0, -4px, 0);
}
}
+
+@include modal-transition();
\ No newline at end of file
diff --git a/src/components/tasks/partials/comments.vue b/src/components/tasks/partials/comments.vue
index d5db45c33..23105771b 100644
--- a/src/components/tasks/partials/comments.vue
+++ b/src/components/tasks/partials/comments.vue
@@ -162,7 +162,7 @@ import {mapState} from 'vuex'
export default {
name: 'comments',
components: {
- editor: AsyncEditor,
+ Editor: AsyncEditor,
},
props: {
taskId: {
@@ -339,4 +339,6 @@ export default {
.media-content {
width: calc(100% - 48px - 2rem);
}
+
+@include modal-transition();
\ No newline at end of file
diff --git a/src/components/tasks/partials/createdUpdated.vue b/src/components/tasks/partials/createdUpdated.vue
new file mode 100644
index 000000000..51ef1d31e
--- /dev/null
+++ b/src/components/tasks/partials/createdUpdated.vue
@@ -0,0 +1,47 @@
+
+
+
+
+ {{ formatDateSince(task.created) }}
+ {{ task.createdBy.getDisplayName() }}
+
+
+
+
+
+
+
+ {{ updatedSince }}
+
+
+
+
+
+
+
+ {{ doneSince }}
+
+
+
+
+
+
+
diff --git a/src/components/tasks/partials/date-table-cell.vue b/src/components/tasks/partials/date-table-cell.vue
index e405c0b30..8df948c71 100644
--- a/src/components/tasks/partials/date-table-cell.vue
+++ b/src/components/tasks/partials/date-table-cell.vue
@@ -1,6 +1,8 @@
- {{ +date === 0 ? '-' : formatDateSince(date) }}
+
+ {{ +date === 0 ? '-' : formatDateSince(date) }}
+
diff --git a/src/components/tasks/partials/description.vue b/src/components/tasks/partials/description.vue
index 28fd5a88e..07d136fde 100644
--- a/src/components/tasks/partials/description.vue
+++ b/src/components/tasks/partials/description.vue
@@ -38,7 +38,7 @@ import {mapState} from 'vuex'
export default {
name: 'description',
components: {
- editor: AsyncEditor,
+ Editor: AsyncEditor,
},
data() {
return {
diff --git a/src/components/tasks/partials/editLabels.vue b/src/components/tasks/partials/editLabels.vue
index ddc10dc3b..93ed147c8 100644
--- a/src/components/tasks/partials/editLabels.vue
+++ b/src/components/tasks/partials/editLabels.vue
@@ -19,19 +19,19 @@
:style="{'background': props.item.hexColor, 'color': props.item.textColor}"
class="tag">
{{ props.item.title }}
-
+
+ class="tag search-result">
{{ props.option }}
+ class="tag search-result">
{{ props.option.title }}
@@ -114,23 +114,17 @@ export default {
},
async removeLabel(label) {
- const removeFromState = () => {
- for (const l in this.labels) {
- if (this.labels[l].id === label.id) {
- this.labels.splice(l, 1)
- }
+ if (this.taskId !== 0) {
+ await this.$store.dispatch('tasks/removeLabel', {label: label, taskId: this.taskId})
+ }
+
+ for (const l in this.labels) {
+ if (this.labels[l].id === label.id) {
+ this.labels.splice(l, 1)
}
- this.$emit('update:modelValue', this.labels)
- this.$emit('change', this.labels)
}
-
- if (this.taskId === 0) {
- removeFromState()
- return
- }
-
- await this.$store.dispatch('tasks/removeLabel', {label: label, taskId: this.taskId})
- removeFromState()
+ this.$emit('update:modelValue', this.labels)
+ this.$emit('change', this.labels)
this.$message.success({message: this.$t('task.label.removeSuccess')})
},
@@ -152,6 +146,18 @@ export default {
diff --git a/src/components/tasks/partials/kanban-card.vue b/src/components/tasks/partials/kanban-card.vue
index e19106433..4a3146efe 100644
--- a/src/components/tasks/partials/kanban-card.vue
+++ b/src/components/tasks/partials/kanban-card.vue
@@ -7,8 +7,8 @@
'has-light-text': !colorIsDark(task.hexColor) && task.hexColor !== `#${task.defaultColor}` && task.hexColor !== task.defaultColor,
}"
:style="{'background-color': task.hexColor !== '#' && task.hexColor !== `#${task.defaultColor}` ? task.hexColor : false}"
+ @click.exact="openTaskDetail()"
@click.ctrl="() => toggleTaskDone(task)"
- @click.exact="() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })"
@click.meta="() => toggleTaskDone(task)"
>
@@ -28,9 +28,9 @@
-
+
{{ formatDateSince(task.dueDate) }}
-
+
{{ task.title }}
@@ -131,7 +139,6 @@ $task-background: var(--white);
border: 3px solid transparent;
font-size: .9rem;
- margin: .5rem;
padding: .4rem;
border-radius: $radius;
background: $task-background;
diff --git a/src/components/tasks/partials/relatedTasks.vue b/src/components/tasks/partials/relatedTasks.vue
index 0b2be060b..9fe4d6f15 100644
--- a/src/components/tasks/partials/relatedTasks.vue
+++ b/src/components/tasks/partials/relatedTasks.vue
@@ -1,7 +1,7 @@
{{ rts.title }}
-
+
{
// by doing this here once we can save a lot of duplicate calls in the template
+ const listAndNamespace = this.$store.getters['namespaces/getListAndNamespaceById'](task.listId, true)
const {
list,
namespace,
- } = this.$store.getters['namespaces/getListAndNamespaceById'](task.listId, true)
+ } = listAndNamespace === null ? {list: null, namespace: null} : listAndNamespace
return {
...task,
@@ -364,4 +377,6 @@ export default {
:deep(.multiselect .search-results button) {
padding: 0.5rem;
}
+
+@include modal-transition();
\ No newline at end of file
diff --git a/src/components/tasks/partials/singleTaskInList.vue b/src/components/tasks/partials/singleTaskInList.vue
index 9e955d5c8..29ab00ff8 100644
--- a/src/components/tasks/partials/singleTaskInList.vue
+++ b/src/components/tasks/partials/singleTaskInList.vue
@@ -8,7 +8,7 @@
>
@@ -39,14 +39,17 @@
:user="a"
v-for="(a, i) in task.assignees"
/>
-
- {{ $t('task.detail.due', {at: formatDateSince(task.dueDate)}) }}
-
+
@@ -126,10 +129,6 @@ export default {
type: Boolean,
default: false,
},
- taskDetailRoute: {
- type: String,
- default: 'task.list.detail',
- },
showList: {
type: Boolean,
default: false,
@@ -167,6 +166,14 @@ export default {
title: '',
} : this.$store.state.currentList
},
+ taskDetailRoute() {
+ return {
+ name: 'task.detail',
+ params: { id: this.task.id },
+ // TODO: re-enable opening task detail in modal
+ // state: { backdropView: this.$router.currentRoute.value.fullPath },
+ }
+ },
},
methods: {
async markAsDone(checked) {
@@ -183,10 +190,7 @@ export default {
this.$t('task.undoneSuccess'),
}, [{
title: 'Undo',
- callback() {
- this.task.done = !this.task.done
- this.markAsDone(!checked)
- },
+ callback: () => this.undoDone(checked),
}])
}
@@ -196,6 +200,11 @@ export default {
await updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around
}
},
+
+ undoDone(checked) {
+ this.task.done = !this.task.done
+ this.markAsDone(!checked)
+ },
async toggleFavorite() {
this.task.isFavorite = !this.task.isFavorite
diff --git a/src/components/tasks/partials/sort.vue b/src/components/tasks/partials/sort.vue
index c3581ba59..4eeb5e9f6 100644
--- a/src/components/tasks/partials/sort.vue
+++ b/src/components/tasks/partials/sort.vue
@@ -1,20 +1,21 @@
-
+
-
+
-
+
-
diff --git a/src/composables/taskList.js b/src/composables/taskList.js
new file mode 100644
index 000000000..e80c5cbe1
--- /dev/null
+++ b/src/composables/taskList.js
@@ -0,0 +1,111 @@
+import { ref, shallowReactive, watch, computed } from 'vue'
+import {useRoute} from 'vue-router'
+
+import TaskCollectionService from '@/services/taskCollection'
+
+// FIXME: merge with DEFAULT_PARAMS in filters.vue
+export const getDefaultParams = () => ({
+ sort_by: ['position', 'id'],
+ order_by: ['asc', 'desc'],
+ filter_by: ['done'],
+ filter_value: ['false'],
+ filter_comparator: ['equals'],
+ filter_concat: 'and',
+})
+
+const SORT_BY_DEFAULT = {
+ id: 'desc',
+}
+
+/**
+ * This mixin provides a base set of methods and properties to get tasks on a list.
+ */
+export function useTaskList(listId) {
+ const params = ref({...getDefaultParams()})
+
+ const search = ref('')
+ const page = ref(1)
+
+ const sortBy = ref({ ...SORT_BY_DEFAULT })
+
+
+ // This makes sure an id sort order is always sorted last.
+ // When tasks would be sorted first by id and then by whatever else was specified, the id sort takes
+ // precedence over everything else, making any other sort columns pretty useless.
+ function formatSortOrder(params) {
+ let hasIdFilter = false
+ const sortKeys = Object.keys(sortBy.value)
+ for (const s of sortKeys) {
+ if (s === 'id') {
+ sortKeys.splice(s, 1)
+ hasIdFilter = true
+ break
+ }
+ }
+ if (hasIdFilter) {
+ sortKeys.push('id')
+ }
+ params.sort_by = sortKeys
+ params.order_by = sortKeys.map(s => sortBy.value[s])
+
+ return params
+ }
+
+ const getAllTasksParams = computed(() => {
+ let loadParams = {...params.value}
+
+ if (search.value !== '') {
+ loadParams.s = search.value
+ }
+
+ loadParams = formatSortOrder(loadParams)
+
+ return [
+ {listId: listId.value},
+ loadParams,
+ page.value || 1,
+ ]
+ })
+
+ const taskCollectionService = shallowReactive(new TaskCollectionService())
+ const loading = computed(() => taskCollectionService.loading)
+ const totalPages = computed(() => taskCollectionService.totalPages)
+
+ const tasks = ref([])
+ async function loadTasks() {
+ tasks.value = await taskCollectionService.getAll(...getAllTasksParams.value)
+ return tasks.value
+ }
+
+ const route = useRoute()
+ watch(() => route.query, (query) => {
+ const { page: pageQueryValue, search: searchQuery } = query
+ if (searchQuery !== undefined) {
+ search.value = searchQuery
+ }
+ if (pageQueryValue !== undefined) {
+ page.value = parseInt(pageQueryValue)
+ }
+
+ }, { immediate: true })
+
+
+ // Only listen for query path changes
+ watch(() => JSON.stringify(getAllTasksParams.value), (newParams, oldParams) => {
+ if (oldParams === newParams) {
+ return
+ }
+
+ loadTasks()
+ }, { immediate: true })
+
+ return {
+ tasks,
+ loading,
+ totalPages,
+ currentPage: page,
+ loadTasks,
+ searchTerm: search,
+ params,
+ }
+}
\ No newline at end of file
diff --git a/src/composables/useColorScheme.ts b/src/composables/useColorScheme.ts
index c0fed1218..f9b1cb94f 100644
--- a/src/composables/useColorScheme.ts
+++ b/src/composables/useColorScheme.ts
@@ -1,9 +1,9 @@
import {computed, watch, readonly} from 'vue'
-import {useStorage, createSharedComposable, ColorSchema, usePreferredColorScheme, tryOnMounted} from '@vueuse/core'
+import {useStorage, createSharedComposable, BasicColorSchema, usePreferredColorScheme, tryOnMounted} from '@vueuse/core'
const STORAGE_KEY = 'color-scheme'
-const DEFAULT_COLOR_SCHEME_SETTING: ColorSchema = 'light'
+const DEFAULT_COLOR_SCHEME_SETTING: BasicColorSchema = 'light'
const CLASS_DARK = 'dark'
const CLASS_LIGHT = 'light'
@@ -16,7 +16,7 @@ const CLASS_LIGHT = 'light'
// - value is synced via `createSharedComposable`
// https://github.com/vueuse/vueuse/blob/main/packages/core/useDark/index.ts
export const useColorScheme = createSharedComposable(() => {
- const store = useStorage(STORAGE_KEY, DEFAULT_COLOR_SCHEME_SETTING)
+ const store = useStorage(STORAGE_KEY, DEFAULT_COLOR_SCHEME_SETTING)
const preferredColorScheme = usePreferredColorScheme()
diff --git a/src/composables/useTitle.ts b/src/composables/useTitle.ts
index e35448290..84aaa177f 100644
--- a/src/composables/useTitle.ts
+++ b/src/composables/useTitle.ts
@@ -1,9 +1,9 @@
import { computed, watchEffect } from 'vue'
import { setTitle } from '@/helpers/setTitle'
-import { ComputedGetter, ComputedRef } from '@vue/reactivity'
+import { ComputedGetter } from '@vue/reactivity'
-export function useTitle(titleGetter: ComputedGetter) : ComputedRef {
+export function useTitle(titleGetter: ComputedGetter) {
const titleRef = computed(titleGetter)
watchEffect(() => setTitle(titleRef.value))
diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts
index b073a8caf..0b5d598b7 100644
--- a/src/helpers/auth.ts
+++ b/src/helpers/auth.ts
@@ -1,4 +1,4 @@
-import {HTTPFactory} from '@/http-common'
+import {AuthenticatedHTTPFactory} from '@/http-common'
import {AxiosResponse} from 'axios'
let savedToken: string | null = null
@@ -6,8 +6,6 @@ let savedToken: string | null = null
/**
* Saves a token while optionally saving it to lacal storage. This is used when viewing a link share:
* It enables viewing multiple link shares indipendently from each in multiple tabs other without overriding any other open ones.
- * @param token
- * @param persist
*/
export const saveToken = (token: string, persist: boolean) => {
savedToken = token
@@ -18,7 +16,6 @@ export const saveToken = (token: string, persist: boolean) => {
/**
* Returns a saved token. If there is one saved in memory it will use that before anything else.
- * @returns {string|null}
*/
export const getToken = (): string | null => {
if (savedToken !== null) {
@@ -39,20 +36,16 @@ export const removeToken = () => {
/**
* Refreshes an auth token while ensuring it is updated everywhere.
- * @returns {Promise>}
*/
export async function refreshToken(persist: boolean): Promise {
- const HTTP = HTTPFactory()
+ const HTTP = AuthenticatedHTTPFactory()
try {
- const response = await HTTP.post('user/token', null, {
- headers: {
- Authorization: `Bearer ${getToken()}`,
- },
- })
+ const response = await HTTP.post('user/token')
saveToken(response.data.token, persist)
return response
} catch(e) {
+ // @ts-ignore
throw new Error('Error renewing token: ', { cause: e })
}
}
diff --git a/src/helpers/isEmail.ts b/src/helpers/isEmail.ts
new file mode 100644
index 000000000..08957d0f0
--- /dev/null
+++ b/src/helpers/isEmail.ts
@@ -0,0 +1,6 @@
+export function isEmail(email: string): Boolean {
+ const format = /^.+@.+$/
+ const match = email.match(format)
+
+ return match === null ? false : match.length > 0
+}
diff --git a/src/helpers/playPop.ts b/src/helpers/playPop.ts
index 6f5f7039a..3f4e50c35 100644
--- a/src/helpers/playPop.ts
+++ b/src/helpers/playPop.ts
@@ -3,11 +3,15 @@ import popSoundFile from '@/assets/audio/pop.mp3'
export const playSoundWhenDoneKey = 'playSoundWhenTaskDone'
export function playPop() {
- const enabled = Boolean(localStorage.getItem(playSoundWhenDoneKey))
- if(!enabled) {
+ const enabled = localStorage.getItem(playSoundWhenDoneKey) === 'true'
+ if (!enabled) {
return
}
+ playPopSound()
+}
+
+export function playPopSound() {
const popSound = new Audio(popSoundFile)
popSound.play()
-}
\ No newline at end of file
+}
diff --git a/src/helpers/saveListView.js b/src/helpers/saveListView.js
index b7f735e1e..96be63420 100644
--- a/src/helpers/saveListView.js
+++ b/src/helpers/saveListView.js
@@ -1,3 +1,5 @@
+// Save the current list view to local storage
+// We use local storage and not vuex here to make it persistent across reloads.
export const saveListView = (listId, routeName) => {
if (routeName.includes('settings.')) {
return
diff --git a/src/helpers/setTitle.js b/src/helpers/setTitle.ts
similarity index 65%
rename from src/helpers/setTitle.js
rename to src/helpers/setTitle.ts
index a2f31a452..d7e6b5dd0 100644
--- a/src/helpers/setTitle.js
+++ b/src/helpers/setTitle.ts
@@ -1,4 +1,4 @@
-export function setTitle(title) {
+export function setTitle(title : undefined | string) {
document.title = (typeof title === 'undefined' || title === '')
? 'Vikunja'
: `${title} | Vikunja`
diff --git a/src/helpers/time/formatDate.js b/src/helpers/time/formatDate.js
index c3e8c1b71..afa3f70a3 100644
--- a/src/helpers/time/formatDate.js
+++ b/src/helpers/time/formatDate.js
@@ -1,5 +1,5 @@
import {createDateFromString} from '@/helpers/time/createDateFromString'
-import {format, formatDistanceToNow} from 'date-fns'
+import {format, formatDistanceToNow, formatISO as formatISOfns} from 'date-fns'
import {enGB, de, fr, ru} from 'date-fns/locale'
import {i18n} from '@/i18n'
@@ -44,3 +44,7 @@ export const formatDateSince = (date) => {
addSuffix: true,
})
}
+
+export function formatISO(date) {
+ return date ? formatISOfns(date) : ''
+}
diff --git a/src/helpers/time/getNextWeekDate.ts b/src/helpers/time/getNextWeekDate.ts
new file mode 100644
index 000000000..d0bb303fb
--- /dev/null
+++ b/src/helpers/time/getNextWeekDate.ts
@@ -0,0 +1,3 @@
+export function getNextWeekDate(): Date {
+ return new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
+}
diff --git a/src/helpers/time/parseDate.ts b/src/helpers/time/parseDate.ts
index 34e6ef0f3..3b6634693 100644
--- a/src/helpers/time/parseDate.ts
+++ b/src/helpers/time/parseDate.ts
@@ -288,7 +288,7 @@ const getDateFromWeekday = (text: string): dateFoundResult => {
}
const getDayFromText = (text: string) => {
- const matcher = /(([1-2][0-9])|(3[01])|(0?[1-9]))(st|nd|rd|th|\.)/ig
+ const matcher = /($| )(([1-2][0-9])|(3[01])|(0?[1-9]))(st|nd|rd|th|\.)($| )/ig
const results = matcher.exec(text)
if (results === null) {
return {
@@ -302,17 +302,17 @@ const getDayFromText = (text: string) => {
const day = parseInt(results[0])
date.setDate(day)
- // If the parsed day is the 31st but the next month only has 30 days, setting the day to 31 will "overflow" the
- // date to the next month, but the first.
+ // If the parsed day is the 31st (or 29+ and the next month is february) but the next month only has 30 days,
+ // setting the day to 31 will "overflow" the date to the next month, but the first.
// This would look like a very weired bug. Now, to prevent that, we check if the day is the same as parsed after
// setting it for the first time and set it again if it isn't - that would mean the month overflowed.
- if (day === 31 && date.getDate() !== day) {
- date.setDate(day)
- }
-
- if (date < now) {
+ while (date < now) {
date.setMonth(date.getMonth() + 1)
}
+
+ if (date.getDate() !== day) {
+ date.setDate(day)
+ }
return {
foundText: results[0],
diff --git a/src/helpers/time/parseDateOrString.ts b/src/helpers/time/parseDateOrString.ts
new file mode 100644
index 000000000..1aeeb1153
--- /dev/null
+++ b/src/helpers/time/parseDateOrString.ts
@@ -0,0 +1,12 @@
+export function parseDateOrString(rawValue: string | undefined, fallback: any): string | Date {
+ if (typeof rawValue === 'undefined') {
+ return fallback
+ }
+
+ const d = new Date(rawValue)
+
+ // @ts-ignore if rawValue is an invalid date, isNan will return false.
+ return !isNaN(d)
+ ? d
+ : rawValue
+}
diff --git a/src/http-common/index.js b/src/http-common/index.js
index 66835fc5f..e6c116cd6 100644
--- a/src/http-common/index.js
+++ b/src/http-common/index.js
@@ -1,7 +1,18 @@
import axios from 'axios'
+import {getToken} from '@/helpers/auth'
-export const HTTPFactory = () => {
+export function HTTPFactory() {
return axios.create({
baseURL: window.API_URL,
})
}
+
+export function AuthenticatedHTTPFactory(token = getToken()) {
+ return axios.create({
+ baseURL: window.API_URL,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ })
+}
diff --git a/src/i18n/index.js b/src/i18n/index.js
index 7bbaa3ffd..b201d6f8f 100644
--- a/src/i18n/index.js
+++ b/src/i18n/index.js
@@ -1,4 +1,4 @@
-import { createI18n } from 'vue-i18n'
+import {createI18n} from 'vue-i18n'
import langEN from './lang/en.json'
export const i18n = createI18n({
@@ -19,6 +19,9 @@ export const availableLanguages = {
'vi-VN': 'Tiếng Việt',
'it-IT': 'Italiano',
'cs-CZ': 'Čeština',
+ 'pl-PL': 'Polski',
+ 'nl-NL': 'Nederlands',
+ 'pt-PT': 'Português',
}
const loadedLanguages = ['en'] // our default language that is preloaded
@@ -30,6 +33,10 @@ const setI18nLanguage = lang => {
}
export const loadLanguageAsync = lang => {
+ if (!lang) {
+ return
+ }
+
if (
// If the same language
i18n.global.locale === lang ||
diff --git a/src/i18n/lang/cs-CZ.json b/src/i18n/lang/cs-CZ.json
index 85dac7e14..63b721c49 100644
--- a/src/i18n/lang/cs-CZ.json
+++ b/src/i18n/lang/cs-CZ.json
@@ -31,10 +31,9 @@
"username": "Uživatelské jméno",
"usernameEmail": "Uživatelské jméno nebo e-mail",
"usernamePlaceholder": "např. Jarmil",
- "email": "E-mailová adresa",
+ "email": "Email address",
"emailPlaceholder": "např. jarmil{'@'}vikunja.io",
"password": "Heslo",
- "passwordRepeat": "Zopakovat heslo",
"passwordPlaceholder": "např. • • • • • • • •",
"forgotPassword": "Zapomenuté heslo?",
"resetPassword": "Obnovit heslo",
@@ -45,12 +44,20 @@
"totpTitle": "Kód dvoufaktorového ověření",
"totpPlaceholder": "např. 123456",
"login": "Přihlásit se",
- "register": "Registrovat",
+ "createAccount": "Create account",
"loginWith": "Přihlásit se pomocí {provider}",
"authenticating": "Ověřování…",
"openIdStateError": "Stav neodpovídá, odmítám pokračovat!",
"openIdGeneralError": "Došlo k chybě při ověřování proti třetí straně.",
- "logout": "Odhlásit se"
+ "logout": "Odhlásit se",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Nastavení",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Vaše současné heslo",
"passwordsDontMatch": "Nové heslo se neshoduje s potvrzením hesla.",
"passwordUpdateSuccess": "Heslo bylo úspěšně změněno.",
- "updateEmailTitle": "Aktualizovat Vaši e-mailovou adresu",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "Nová e-mailová adresa",
"updateEmailSuccess": "Vaše e-mailová adresa byla úspěšně aktualizována. Poslali jsme vám odkaz pro její potvrzení.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Neděle",
"weekStartMonday": "Pondělí",
"language": "Jazyk",
- "defaultList": "Výchozí seznam"
+ "defaultList": "Výchozí seznam",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Dvoufaktorové ověření",
@@ -327,6 +335,7 @@
"archiveText": "Nebudete moci upravovat tento jmenný prostor ani vytvářet nové seznamy, dokud jej neodarchivujete. Všechny seznamy v tomto prostoru budou také archivovány.",
"unarchiveText": "Budete moci vytvářet nové úkoly nebo je upravovat.",
"success": "Prostor byl úspěšně archivován.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "Pokud je prostor archivován, nelze vytvořit nové seznamy nebo je upravit."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Zobrazit dokončené úkoly",
"sortAlphabetically": "Řadit podle abecedy",
"enablePriority": "Povolit filtrování podle priority",
- "enablePercentDone": "Povolit filtrování dle dokončenosti",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Rozsah termínu",
"startDateRange": "Začátek období",
"endDateRange": "Konec období",
@@ -476,7 +485,8 @@
"showMenu": "Zobrazit nabídku",
"hideMenu": "Skrýt nabídku",
"forExample": "Například:",
- "welcomeBack": "Vítejte zpět!"
+ "welcomeBack": "Vítejte zpět!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Obnovit barvu",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Vytvořit nový",
"selectPlaceholder": "Kliknutím nebo stisknutím klávesy Enter vyberte"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Aktuální úkoly",
"titleDates": "Úkoly od {from} do {to}",
"noDates": "Zobrazit úkoly bez datumu",
- "current": "Aktuální úkoly",
- "from": "Úkoly od",
- "until": "do",
- "today": "Dnes",
- "nextWeek": "Příští týden",
- "nextMonth": "Příští měsíc",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nic na práci - užij si pěkný den!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "Tímto také odstraníte všechny přílohy, připomenutí a vztahy spojené s tímto úkolem a nelze je vrátit zpět!"
},
"actions": {
- "assign": "Přiřadit uživateli",
- "label": "Přidat štítky",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Nastavit prioritu",
"dueDate": "Nastavit termín",
- "startDate": "Nastavit počáteční datum",
- "endDate": "Nastavit koncové datum",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Nastavit připomenutí",
- "repeatAfter": "Nastavit interval opakování",
- "percentDone": "Nastavit procenta dokončeno",
- "attachments": "Přidat přílohy",
- "relatedTasks": "Přidat vztahy úkolu",
- "moveList": "Přesunout úkol",
- "color": "Nastavit barvu úkolu",
- "delete": "Smazat úkol",
- "favorite": "Uložit jako oblíbené",
- "unfavorite": "Odebrat z oblíbených"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Termín",
"endDate": "Datum ukončení",
"labels": "Štítky",
- "percentDone": "% Hotovo",
+ "percentDone": "Progress",
"priority": "Priorita",
"relatedTasks": "Související úkoly",
"reminders": "Připomínky",
@@ -776,17 +834,20 @@
"general": "Obecné",
"allPages": "Tyto zkratky fungují na všech stránkách.",
"currentPageOnly": "Tyto zkratky fungují pouze na aktuální stránce.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Přepnout nabídku",
"quickSearch": "Otevřít vyhledávání / panel rychlých akcí",
"then": "potom",
"task": {
"title": "Stránka úkolů",
- "done": "Hotovo",
- "assign": "Přiřadit uživateli",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Přidat štítky k tomuto úkolu",
"dueDate": "Změnit termín tohoto úkolu",
"attachment": "Přidat přílohu k tomuto úkolu",
- "related": "Upravit související úkoly tohoto úkolu"
+ "related": "Upravit související úkoly tohoto úkolu",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "Zobrazení seznamů",
@@ -794,6 +855,14 @@
"switchToGanttView": "Přepnout na zobrazení gantt",
"switchToKanbanView": "Přepnout na zobrazení kanbanu",
"switchToTableView": "Přepnout na zobrazení tabulky"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/de-DE.json b/src/i18n/lang/de-DE.json
index 032fe5d87..57f651ec2 100644
--- a/src/i18n/lang/de-DE.json
+++ b/src/i18n/lang/de-DE.json
@@ -7,7 +7,7 @@
"lastViewed": "Zuletzt angesehen",
"list": {
"newText": "Du kannst eine neue Liste für deine neuen Aufgaben erstellen:",
- "new": "New list",
+ "new": "Neue Liste",
"importText": "Oder importiere deine Listen und Aufgaben aus anderen Diensten in Vikunja:",
"import": "Deine Daten in Vikunja importieren"
}
@@ -34,7 +34,6 @@
"email": "E-Mail-Adresse",
"emailPlaceholder": "z.B. frederic{'@'}vikunja.io",
"password": "Passwort",
- "passwordRepeat": "Gib dein Passwort erneut ein",
"passwordPlaceholder": "z.B. •••••••••••",
"forgotPassword": "Passwort vergessen?",
"resetPassword": "Setze dein Passwort zurück",
@@ -45,12 +44,20 @@
"totpTitle": "Zwei-Faktor-Authentifizierungscode",
"totpPlaceholder": "z.B. 123456",
"login": "Anmelden",
- "register": "Registrieren",
+ "createAccount": "Account erstellen",
"loginWith": "Mit {provider} anmelden",
"authenticating": "Authentifizierung…",
"openIdStateError": "Zustand stimmt nicht überein, fahre nicht fort!",
"openIdGeneralError": "Es ist ein Fehler bei der externen Authentisierung aufgetreten.",
- "logout": "Abmelden"
+ "logout": "Abmelden",
+ "emailInvalid": "Bitte gib eine gültige E-Mail-Adresse ein.",
+ "usernameRequired": "Bitte gib einen Anmeldenamen ein.",
+ "passwordRequired": "Bitte gib ein Passwort ein.",
+ "showPassword": "Passwort anzeigen",
+ "hidePassword": "Passwort verbergen",
+ "noAccountYet": "Noch kein Account?",
+ "alreadyHaveAnAccount": "Hast du bereits einen Account?",
+ "remember": "Angemeldet bleiben"
},
"settings": {
"title": "Einstellungen",
@@ -78,7 +85,8 @@
"weekStartSunday": "Sonntag",
"weekStartMonday": "Montag",
"language": "Sprache",
- "defaultList": "Standard-Liste"
+ "defaultList": "Standard-Liste",
+ "timezone": "Zeitzone"
},
"totp": {
"title": "Zwei-Faktor-Authentifizierung",
@@ -157,7 +165,7 @@
"searchSelect": "Klicke auf oder drücke die Eingabetaste, um diese Liste auszuwählen",
"shared": "Geteilte Listen",
"create": {
- "header": "New list",
+ "header": "Neue Liste",
"titlePlaceholder": "Der Titel der Liste steht hier…",
"addTitleRequired": "Bitte gebe einen Namen an.",
"createdSuccess": "Die Liste wurde erfolgreich erstellt.",
@@ -315,7 +323,7 @@
"namespaces": "Namespaces",
"search": "Beginne zu schreiben, um einen Namespace zu suchen…",
"create": {
- "title": "New namespace",
+ "title": "Neuer Namespace",
"titleRequired": "Bitte gebe einen Titel an.",
"explanation": "Ein Namespace ist eine Sammlung von Listen, die du teilen und zur Organisation verwenden kannst. Jede Liste zu einem Namespace.",
"tooltip": "Was ist ein Namespace?",
@@ -327,6 +335,7 @@
"archiveText": "Du kannst diesen Namespace nicht mehr bearbeiten oder neue Listen erstellen, bis du die Archivierung rückgängig machst. Das gilt auch für alle Listen in diesem Namespace.",
"unarchiveText": "Du kannst neue Aufgaben erstellen oder diese bearbeiten.",
"success": "Der Namespace wurde erfolgreich archiviert.",
+ "unarchiveSuccess": "Der Namespace wurde erfolgreich wiederhergestellt.",
"description": "In einem archivierten Namespace können Listen weder angelegt noch editiert werden."
},
"delete": {
@@ -376,14 +385,14 @@
"showDoneTasks": "Erledigte Aufgaben anzeigen",
"sortAlphabetically": "Alphabetisch sortieren",
"enablePriority": "Filter nach Priorität aktivieren",
- "enablePercentDone": "Filter nach % Erledigt aktivieren",
+ "enablePercentDone": "Filter nach Fortschritt aktivieren",
"dueDateRange": "Fälligkeitsbereich",
"startDateRange": "Startdatumsbereich",
"endDateRange": "Enddatumsbereich",
"reminderRange": "Erinnerungs-Datumsbereich"
},
"create": {
- "title": "New Saved Filter",
+ "title": "Neuer gespeicherter Filter",
"description": "Ein gespeicherter Filter ist eine virtuelle Liste, die bei jedem Zugriff aus einem Satz von Filtern errechnet wird. Einmal erstellt, erscheint diese in einem speziellen Namespace.",
"action": "Neuen gespeicherten Filter erstellen"
},
@@ -476,7 +485,8 @@
"showMenu": "Menü anzeigen",
"hideMenu": "Menü ausblenden",
"forExample": "Zum Beispiel:",
- "welcomeBack": "Willkommen zurück!"
+ "welcomeBack": "Willkommen zurück!",
+ "custom": "Benutzerdefiniert"
},
"input": {
"resetColor": "Farbe zurücksetzen",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Neu erstellen",
"selectPlaceholder": "Klicken oder Enter drücken zum Auswählen"
+ },
+ "datepickerRange": {
+ "to": "Bis",
+ "from": "Von",
+ "fromto": "{from} bis {to}",
+ "ranges": {
+ "today": "Heute",
+ "thisWeek": "Diese Woche",
+ "restOfThisWeek": "Der Rest dieser Woche",
+ "nextWeek": "Nächste Woche",
+ "next7Days": "Nächsten 7 Tage",
+ "lastWeek": "Letzte Woche",
+ "thisMonth": "Dieser Monat",
+ "restOfThisMonth": "Der Rest dieses Monats",
+ "nextMonth": "Nächster Monat",
+ "next30Days": "Nächsten 30 Tage",
+ "lastMonth": "Letzter Monat",
+ "thisYear": "Dieses Jahr",
+ "restOfThisYear": "Der Rest des Jahres"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "Du kannst Datumsberechnung verwenden, um nach relativen Daten zu filtern.",
+ "learnhow": "Sieh dir an, wie es funktioniert",
+ "title": "Datumsberechnung",
+ "intro": "Die Datumsberechnung erlaubt es, relative Daten anzugeben, die bei der Anwendung des Filters von Vikunja aufgelöst werden.",
+ "expression": "Jeder Ausdruck der Datumsberechnung beginnt mit einem Datumswert, welcher entweder {0} sein kann oder mit {1} endet. Auf diesen Datumswert kann optional ein oder mehrere mathematische Ausdrücke folgen.",
+ "similar": "Diese Ausdrücke ähneln denen von {0} und {1}.",
+ "add1Day": "Einen Tag hinzufügen",
+ "minus1Day": "Einen Tag abziehen",
+ "roundDay": "Auf den nächsten Tag abrunden",
+ "supportedUnits": "Unterstützte Zeiteinheiten sind:",
+ "someExamples": "Einige Beispiele für Zeitausdrücke:",
+ "units": {
+ "seconds": "Sekunden",
+ "minutes": "Minuten",
+ "hours": "Stunden",
+ "days": "Tage",
+ "weeks": "Wochen",
+ "months": "Monate",
+ "years": "Jahre"
+ },
+ "examples": {
+ "now": "Jetzt",
+ "in24h": "In 24 Stunden",
+ "today": "Heute um 00:00 Uhr",
+ "beginningOfThisWeek": "Der Anfang dieser Woche um 00:00 Uhr",
+ "endOfThisWeek": "Das Ende dieser Woche",
+ "in30Days": "In 30 Tagen",
+ "datePlusMonth": "{0} plus einen Monat um 00:00 des Tages"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Aktuelle Aufgaben",
"titleDates": "Aufgaben von {from} bis {to}",
"noDates": "Aufgaben ohne Datum anzeigen",
- "current": "Aktuelle Aufgaben",
- "from": "Aufgaben von",
- "until": "bis",
- "today": "Heute",
- "nextWeek": "Nächste Woche",
- "nextMonth": "Nächster Monat",
+ "overdue": "Überfällige Aufgaben anzeigen",
+ "fromuntil": "Aufgaben von {from} bis {until}",
+ "select": "Datumsbereich wählen",
"noTasks": "Nichts zu tun – Einen schönen Tag noch!"
},
"detail": {
@@ -545,7 +603,7 @@
"chooseStartDate": "Klicke hier, um ein Startdatum zu setzen",
"chooseEndDate": "Klicke hier, um ein Enddatum zu setzen",
"move": "Aufgabe in eine andere Liste verschieben",
- "done": "Mark task done!",
+ "done": "Als erledigt markieren!",
"undone": "Als nicht erledigt markieren",
"created": "Erstellt {0} von {1}",
"updated": "Aktualisiert {0}",
@@ -569,13 +627,13 @@
"endDate": "Enddatum setzen",
"reminders": "Erinnerungen setzen",
"repeatAfter": "Wiederholung setzen",
- "percentDone": "Prozent erledigt setzen",
+ "percentDone": "Fortschritt einstellen",
"attachments": "Anhänge hinzufügen",
- "relatedTasks": "Aufgabenbeziehungen hinzufügen",
- "moveList": "Aufgabe verschieben",
- "color": "Taskfarbe setzen",
- "delete": "Aufgabe löschen",
- "favorite": "In Favoriten speichern",
+ "relatedTasks": "Beziehung hinzufügen",
+ "moveList": "Verschieben",
+ "color": "Farbe setzen",
+ "delete": "Löschen",
+ "favorite": "Zu Favoriten hinzufügen",
"unfavorite": "Aus Favoriten entfernen"
}
},
@@ -589,7 +647,7 @@
"dueDate": "Fälligkeitsdatum",
"endDate": "Enddatum",
"labels": "Labels",
- "percentDone": "% erledigt",
+ "percentDone": "Fortschritt",
"priority": "Priorität",
"relatedTasks": "Verwandte Aufgaben",
"reminders": "Erinnerungen",
@@ -776,17 +834,20 @@
"general": "Allgemein",
"allPages": "Diese Tastenkürzel funktionieren auf allen Seiten.",
"currentPageOnly": "Diese Tastenkürzel funktionieren nur auf der aktuellen Seite.",
+ "somePagesOnly": "Funktioniert nur auf manchen Seiten.",
"toggleMenu": "Das Menü umschalten",
"quickSearch": "Such-/Schnellaktionsleiste öffnen",
"then": "dann",
"task": {
"title": "Aufgabenseite",
- "done": "Done",
- "assign": "Benutzer:in zuweisen",
+ "done": "Aufgabe als erledigt / unerledigt markieren",
+ "assign": "Diese Aufgabe jemandem zuweisen",
"labels": "Dieser Aufgabe ein Label hinzufügen",
"dueDate": "Ändere das Fälligkeitsdatum dieser Aufgabe",
"attachment": "Einen Anhang dieser Aufgabe hinzufügen",
- "related": "Ändere die Abhängigen Aufgaben dieser Aufgabe"
+ "related": "Ändere die Abhängigen Aufgaben dieser Aufgabe",
+ "color": "Die Farbe dieser Aufgabe ändern",
+ "move": "Diese Aufgabe in eine andere Liste verschieben"
},
"list": {
"title": "Listenansicht",
@@ -794,6 +855,14 @@
"switchToGanttView": "Zur Ganttansicht wechseln",
"switchToKanbanView": "Zur Kanbanansicht wechseln",
"switchToTableView": "Zur Tabellenansicht wechseln"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Die Startseite aufrufen",
+ "upcoming": "Anstehende Aufgaben aufrufen",
+ "namespaces": "Namespaces & Listen aufrufen",
+ "labels": "Labels aufrufen",
+ "teams": "Teams aufrufen"
}
},
"update": {
@@ -899,7 +968,7 @@
"4015": "Dieser Aufgabenkommentar existiert nicht.",
"4016": "Ungültiges Aufgabenfeld.",
"4017": "Ungültiger Aufgabenfilter (Vergleichskriterium).",
- "4018": "Invalid task filter concatenator.",
+ "4018": "Ungültige Verkettung von Aufgabenfiltern.",
"4019": "Ungültiger Aufgabenfilter (Wert).",
"5001": "Dieser Namespace existiert nicht.",
"5003": "Du hast keinen Zugriff auf den Namespace.",
diff --git a/src/i18n/lang/de-swiss.json b/src/i18n/lang/de-swiss.json
index 212b70b92..7c2db857e 100644
--- a/src/i18n/lang/de-swiss.json
+++ b/src/i18n/lang/de-swiss.json
@@ -7,7 +7,7 @@
"lastViewed": "Zletscht ahglueget",
"list": {
"newText": "Du chasch e Liste für dini neue Uufgabe erstelle:",
- "new": "New list",
+ "new": "Neue Liste",
"importText": "Oder importier dini Liste und Uufgabe us anderne Dienst nach Vikunja:",
"import": "Dini Date in Vikunja importiere"
}
@@ -31,10 +31,9 @@
"username": "Benutzernamä",
"usernameEmail": "Benutzernamä oder E-Mail Adrässe",
"usernamePlaceholder": "z.B. Hansruedi",
- "email": "E-Mail Adrässe",
+ "email": "E-Mail-Adresse",
"emailPlaceholder": "z.B. frederic{'@'}vikunja.io",
"password": "Passwort",
- "passwordRepeat": "Gib dis Passwort nomal iih",
"passwordPlaceholder": "z.B. •••••••••••",
"forgotPassword": "Passwort vergessen?",
"resetPassword": "Setz diis Passwort zrugg",
@@ -45,12 +44,20 @@
"totpTitle": "Zweifaktor Authentifizierigs Ziffere",
"totpPlaceholder": "z.B. 123456",
"login": "Iihlogge",
- "register": "Registriere",
+ "createAccount": "Account erstellen",
"loginWith": "Iihlogge mit {provider}",
"authenticating": "Authentifiziere…",
"openIdStateError": "Status stimmt nid überiih, ich verweigerä wiiter zmache!",
"openIdGeneralError": "Es ist ein Fehler bei der externen Authentisierung aufgetreten.",
- "logout": "Uuslogge"
+ "logout": "Uuslogge",
+ "emailInvalid": "Bitte gib eine gültige E-Mail-Adresse ein.",
+ "usernameRequired": "Bitte gib einen Anmeldenamen ein.",
+ "passwordRequired": "Bitte gib ein Passwort ein.",
+ "showPassword": "Passwort anzeigen",
+ "hidePassword": "Passwort verbergen",
+ "noAccountYet": "Noch kein Account?",
+ "alreadyHaveAnAccount": "Hast du bereits einen Account?",
+ "remember": "Angemeldet bleiben"
},
"settings": {
"title": "Iihstellige",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Diis jetzige Passwort",
"passwordsDontMatch": "Dis neue Passwort und siini Bestätigung stimmed nid überiih.",
"passwordUpdateSuccess": "Dis Passwort isch erfolgriich aktualisiert wordä.",
- "updateEmailTitle": "Dini E-Mail Adrässä änderä",
+ "updateEmailTitle": "Aktualisiere deine E-Mail-Adresse",
"updateEmailNew": "Neui E-Mail Adrässä",
"updateEmailSuccess": "Dini E-Mail Adrässä isch erfolgriich gänderet worde. Mir hend dir en Link gschickt, um si zu bestätigä.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunntig",
"weekStartMonday": "Määntig",
"language": "Sproch",
- "defaultList": "Standard Liste"
+ "defaultList": "Standard Liste",
+ "timezone": "Zeitzone"
},
"totp": {
"title": "Zweifaktor Authentifizierig",
@@ -95,9 +103,9 @@
"disableSuccess": "Zweifaktor Authentifizierig isch erfolgriich uusgschalte wore."
},
"caldav": {
- "title": "Caldav",
- "howTo": "Du chasch Vikunja zu Caldav Applikatione verbinde, um dini Uufgabe vo verschidene Gräät zgseh. Gib die Url i dim Client iih:",
- "more": "Meh Informatione über Caldav in Vikunja"
+ "title": "CalDAV",
+ "howTo": "Du chasch Vikunja zu CalDAV Applikatione verbinde, um dini Uufgabe vo verschidene Gräät zgseh. Gib die Url i dim Client iih:",
+ "more": "Meh Informatione über CalDAV in Vikunja"
},
"avatar": {
"title": "Herr Der Elemente",
@@ -157,7 +165,7 @@
"searchSelect": "Druck uf Enter um die Liste uuszwähle",
"shared": "Teilti Liste",
"create": {
- "header": "New list",
+ "header": "Neue Liste",
"titlePlaceholder": "Listetitl da ahgeh…",
"addTitleRequired": "Bitte gib en Titl ah.",
"createdSuccess": "Liste erfolgriich erstellt.",
@@ -315,7 +323,7 @@
"namespaces": "Namensrüüm",
"search": "Schriib, um nachemne Namensruum z'sueche…",
"create": {
- "title": "New namespace",
+ "title": "Neuer Namespace",
"titleRequired": "Bitte gib en Titl ah.",
"explanation": "En Namensruum isch e Gruppe vo Liste, wo du chasch zur Organisation benutze. Tatsächlich sind alli Listene emne Namensruum zuegwise.",
"tooltip": "Was isch en Namensruum?",
@@ -327,6 +335,7 @@
"archiveText": "Du hesch kei möglichkeit meh de Namensruum z'bearbeite oder neui Listene drin z'erstelle, bis du si wider ent-archiviert hesch. Das archiviert au grad alli Liste im Namensruum.",
"unarchiveText": "Du chasch neui Liste erstelle oder bearbeite.",
"success": "De Namensruum isch erfolgriich archiviert worde.",
+ "unarchiveSuccess": "Der Namespace wurde erfolgreich wiederhergestellt.",
"description": "Wenn en Namensruum archiviert isch, chasch du kei neui Liste erstelle oder die bearbeite."
},
"delete": {
@@ -376,14 +385,14 @@
"showDoneTasks": "Zeig die fertige Uufgabe",
"sortAlphabetically": "Alphabetisch sortieren",
"enablePriority": "Filter nach Priorität aktiviere",
- "enablePercentDone": "Filter nach Prozent iihschalte",
+ "enablePercentDone": "Filter nach Fortschritt aktivieren",
"dueDateRange": "Fälligkeitsberiich",
"startDateRange": "Startdatumsbreiich",
"endDateRange": "Enddatumsberiich",
"reminderRange": "Errinnerigs Datumbereich"
},
"create": {
- "title": "New Saved Filter",
+ "title": "Neuer gespeicherter Filter",
"description": "En gspeicherete Filter isch e virtuelli Liste, welche vomene Satz a Filter zemmegsetzt wird, sobald me uf sie zuegriift. Wenn sie mal erstellt worde isch, erhaltet si ihren eigene Namensruum.",
"action": "Neue gspeicherete Filter erstelle"
},
@@ -476,7 +485,8 @@
"showMenu": "Menü anzeigen",
"hideMenu": "Menü ausblenden",
"forExample": "Zum Beispiel:",
- "welcomeBack": "Willkommen zurück!"
+ "welcomeBack": "Willkommen zurück!",
+ "custom": "Benutzerdefiniert"
},
"input": {
"resetColor": "Farb zruggsetze",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Neu erstelle",
"selectPlaceholder": "Druck uf Enter zum uuswähle"
+ },
+ "datepickerRange": {
+ "to": "Bis",
+ "from": "Von",
+ "fromto": "{from} bis {to}",
+ "ranges": {
+ "today": "Heute",
+ "thisWeek": "Diese Woche",
+ "restOfThisWeek": "Der Rest dieser Woche",
+ "nextWeek": "Nächste Woche",
+ "next7Days": "Nächsten 7 Tage",
+ "lastWeek": "Letzte Woche",
+ "thisMonth": "Dieser Monat",
+ "restOfThisMonth": "Der Rest dieses Monats",
+ "nextMonth": "Nächster Monat",
+ "next30Days": "Nächsten 30 Tage",
+ "lastMonth": "Letzter Monat",
+ "thisYear": "Dieses Jahr",
+ "restOfThisYear": "Der Rest des Jahres"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "Du kannst Datumsberechnung verwenden, um nach relativen Daten zu filtern.",
+ "learnhow": "Sieh dir an, wie es funktioniert",
+ "title": "Datumsberechnung",
+ "intro": "Die Datumsberechnung erlaubt es, relative Daten anzugeben, die bei der Anwendung des Filters von Vikunja aufgelöst werden.",
+ "expression": "Jeder Ausdruck der Datumsberechnung beginnt mit einem Datumswert, welcher entweder {0} sein kann oder mit {1} endet. Auf diesen Datumswert kann optional ein oder mehrere mathematische Ausdrücke folgen.",
+ "similar": "Diese Ausdrücke ähneln denen von {0} und {1}.",
+ "add1Day": "Einen Tag hinzufügen",
+ "minus1Day": "Einen Tag abziehen",
+ "roundDay": "Auf den nächsten Tag abrunden",
+ "supportedUnits": "Unterstützte Zeiteinheiten sind:",
+ "someExamples": "Einige Beispiele für Zeitausdrücke:",
+ "units": {
+ "seconds": "Sekunden",
+ "minutes": "Minuten",
+ "hours": "Stunden",
+ "days": "Tage",
+ "weeks": "Wochen",
+ "months": "Monate",
+ "years": "Jahre"
+ },
+ "examples": {
+ "now": "Jetzt",
+ "in24h": "In 24 Stunden",
+ "today": "Heute um 00:00 Uhr",
+ "beginningOfThisWeek": "Der Anfang dieser Woche um 00:00 Uhr",
+ "endOfThisWeek": "Das Ende dieser Woche",
+ "in30Days": "In 30 Tagen",
+ "datePlusMonth": "{0} plus einen Monat um 00:00 des Tages"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Momentani Uufgabe",
"titleDates": "Uufgabe vo {from} bis {to}",
"noDates": "Zeig Uufgabe ohni Datum",
- "current": "Momentani Uufgabe",
- "from": "Uufgabe vo",
- "until": "bis",
- "today": "Hütt",
- "nextWeek": "Negst Wuchä",
- "nextMonth": "Negste Monet",
+ "overdue": "Überfällige Aufgaben anzeigen",
+ "fromuntil": "Aufgaben von {from} bis {until}",
+ "select": "Datumsbereich wählen",
"noTasks": "Nichts zu tun – Einen schönen Tag noch!"
},
"detail": {
@@ -545,7 +603,7 @@
"chooseStartDate": "Druck dah, um es Startdatum z'setze",
"chooseEndDate": "Druck da, um es Enddatum z'setze",
"move": "Schieb die Uufgab in e anderi Liste",
- "done": "Mark task done!",
+ "done": "Als erledigt markieren!",
"undone": "Als unerledigt markierä",
"created": "Erstellt am {0} vo {1}",
"updated": "{0} g'updatet",
@@ -562,20 +620,20 @@
},
"actions": {
"assign": "Benutzer:in zuweisen",
- "label": "Label hinzuefüege",
+ "label": "Label hinzufügen",
"priority": "Priorität setzä",
"dueDate": "Fälligkeitsdatum setze",
- "startDate": "Startdatum setze",
- "endDate": "Enddatum setze",
+ "startDate": "Startdatum setzen",
+ "endDate": "Enddatum setzen",
"reminders": "Errinnerig iihstelle",
- "repeatAfter": "En wiederholende Intervall setze",
- "percentDone": "Prozentuelli Erledigung setze",
- "attachments": "Aahang hinzuefüege",
- "relatedTasks": "Uufgabsbeziehig hinzufüege",
- "moveList": "Uufgab verschiebe",
- "color": "Uufgab Farb setze",
- "delete": "Uufgab chüble",
- "favorite": "In Favoriten speichern",
+ "repeatAfter": "Wiederholung setzen",
+ "percentDone": "Fortschritt einstellen",
+ "attachments": "Anhänge hinzufügen",
+ "relatedTasks": "Beziehung hinzufügen",
+ "moveList": "Verschieben",
+ "color": "Farbe setzen",
+ "delete": "Löschen",
+ "favorite": "Zu Favoriten hinzufügen",
"unfavorite": "Aus Favoriten entfernen"
}
},
@@ -589,7 +647,7 @@
"dueDate": "Fälligkeitsdatum",
"endDate": "Enddatum",
"labels": "Labels",
- "percentDone": "% fertig",
+ "percentDone": "Fortschritt",
"priority": "Priorität",
"relatedTasks": "Verwandti Uufgabe",
"reminders": "Errinnerige",
@@ -776,17 +834,20 @@
"general": "Allgemein",
"allPages": "Die Chürzl funktioniered uf allne Siitene.",
"currentPageOnly": "Die Chürzl funktioniered nur uf de momentane Siite.",
+ "somePagesOnly": "Funktioniert nur auf manchen Seiten.",
"toggleMenu": "Menü umschalte",
"quickSearch": "Suechi und Schnellaktionsliste öffne",
"then": "dann",
"task": {
"title": "Uufgabesiite",
- "done": "Done",
- "assign": "Benutzer:in zuweisen",
+ "done": "Aufgabe als erledigt / unerledigt markieren",
+ "assign": "Diese Aufgabe jemandem zuweisen",
"labels": "Labels ennere Uufgab hinzuefüege",
"dueDate": "S'Fälligkeitsdatum für die Uufgab ändere",
"attachment": "En Aahang dere Uufgab hinzuefüege",
- "related": "Beziehige vo dere Uufgab bearbeite"
+ "related": "Beziehige vo dere Uufgab bearbeite",
+ "color": "Die Farbe dieser Aufgabe ändern",
+ "move": "Diese Aufgabe in eine andere Liste verschieben"
},
"list": {
"title": "Listenansicht",
@@ -794,6 +855,14 @@
"switchToGanttView": "Zur Ganttansicht wechseln",
"switchToKanbanView": "Zur Kanbanansicht wechseln",
"switchToTableView": "Zur Tabellenansicht wechseln"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Die Startseite aufrufen",
+ "upcoming": "Anstehende Aufgaben aufrufen",
+ "namespaces": "Namespaces & Listen aufrufen",
+ "labels": "Labels aufrufen",
+ "teams": "Teams aufrufen"
}
},
"update": {
@@ -899,7 +968,7 @@
"4015": "De Uufgabe Kommentar giz nid.",
"4016": "Ungültigs Uufgabefeld.",
"4017": "Ungültige Uufgabefilter vergliich.",
- "4018": "Invalid task filter concatenator.",
+ "4018": "Ungültige Verkettung von Aufgabenfiltern.",
"4019": "Ungültigi Uufgabe Filter Wert.",
"5001": "De Namensruum existiert nid.",
"5003": "Du hesch kei Zuegriff zu dem Namensruum.",
diff --git a/src/i18n/lang/en.json b/src/i18n/lang/en.json
index 1f03d175b..ca476f47b 100644
--- a/src/i18n/lang/en.json
+++ b/src/i18n/lang/en.json
@@ -31,10 +31,9 @@
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
+ "email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password",
@@ -45,12 +44,20 @@
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
- "register": "Register",
+ "createAccount": "Create account",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Logout",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Settings",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
- "defaultList": "Default List"
+ "defaultList": "Default List",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Two Factor Authentication",
@@ -327,6 +335,7 @@
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Reset Color",
@@ -515,6 +525,60 @@
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +596,9 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +622,22 @@
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +650,7 @@
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
+ "percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
"reminders": "Reminders",
@@ -676,6 +737,7 @@
"deleteText1": "Are you sure you want to delete this task relation?",
"deleteText2": "This cannot be undone!",
"select": "Select a relation kind",
+ "taskRequired": "Please select a task or enter a new task title.",
"kinds": {
"subtask": "Subtask | Subtasks",
"parenttask": "Parent Task | Parent Tasks",
@@ -776,17 +838,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +859,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/es-ES.json b/src/i18n/lang/es-ES.json
index 1f03d175b..86f71fc1e 100644
--- a/src/i18n/lang/es-ES.json
+++ b/src/i18n/lang/es-ES.json
@@ -31,10 +31,9 @@
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
+ "email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password",
@@ -45,12 +44,20 @@
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
- "register": "Register",
+ "createAccount": "Create account",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Logout",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Settings",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
- "defaultList": "Default List"
+ "defaultList": "Default List",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Two Factor Authentication",
@@ -327,6 +335,7 @@
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Reset Color",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
+ "percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
"reminders": "Reminders",
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +855,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/fr-FR.json b/src/i18n/lang/fr-FR.json
index 9ae5ce8f7..75944d48c 100644
--- a/src/i18n/lang/fr-FR.json
+++ b/src/i18n/lang/fr-FR.json
@@ -7,7 +7,7 @@
"lastViewed": "Dernière consultation",
"list": {
"newText": "Tu peux créer une nouvelle liste pour tes nouvelles tâches :",
- "new": "New list",
+ "new": "Nouvelle liste",
"importText": "Ou importe tes listes et tâches d’autres services dans Vikunja :",
"import": "Importer tes données dans Vikunja"
}
@@ -34,9 +34,8 @@
"email": "Adresse courriel",
"emailPlaceholder": "p. ex. frederic{'@'}vikunja.io",
"password": "Mot de passe",
- "passwordRepeat": "Retape ton mot de passe",
"passwordPlaceholder": "p. ex. •••••••••••",
- "forgotPassword": "Forgot your password?",
+ "forgotPassword": "Mot de passe oublié ?",
"resetPassword": "Réinitialiser ton mot de passe",
"resetPasswordAction": "M’envoyer un lien de réinitialisation du mot de passe",
"resetPasswordSuccess": "Vérifie ta boîte de réception ! Tu devrais avoir un courriel contenant les instructions sur la manière de réinitialiser ton mot de passe.",
@@ -45,12 +44,20 @@
"totpTitle": "Code d’authentification à deux facteurs",
"totpPlaceholder": "p. ex. 123456",
"login": "Se connecter",
- "register": "S’inscrire",
+ "createAccount": "Créer un compte",
"loginWith": "Se connecter avec {provider}",
"authenticating": "Authentification…",
"openIdStateError": "L’état ne correspond pas, impossible de continuer !",
"openIdGeneralError": "Une erreur s'est produite lors de l'authentification contre un tiers.",
- "logout": "Se déconnecter"
+ "logout": "Se déconnecter",
+ "emailInvalid": "Veuillez saisir une adresse courriel valide.",
+ "usernameRequired": "Veuillez saisir un nom d'utilisateur.",
+ "passwordRequired": "Veuillez fournir un mot de passe.",
+ "showPassword": "Afficher le mot de passe",
+ "hidePassword": "Masquer le mot de passe",
+ "noAccountYet": "Vous n'avez pas encore de compte ?",
+ "alreadyHaveAnAccount": "Vous avez déjà un compte ?",
+ "remember": "Rester connecté(e)"
},
"settings": {
"title": "Paramètres",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Ton mot de passe actuel",
"passwordsDontMatch": "Le nouveau mot de passe et sa confirmation ne correspondent pas.",
"passwordUpdateSuccess": "Mot de passe mis à jour.",
- "updateEmailTitle": "Mets à jour ton adresse électronique",
+ "updateEmailTitle": "Mettre à jour votre adresse courriel",
"updateEmailNew": "Nouvelle adresse courriel",
"updateEmailSuccess": "Mise à jour de l’adresse électronique. Clique sur le lien dans le courriel qui t’a été envoyé pour le confirmer.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "dimanche",
"weekStartMonday": "lundi",
"language": "Langue",
- "defaultList": "Liste par défaut"
+ "defaultList": "Liste par défaut",
+ "timezone": "Fuseau horaire"
},
"totp": {
"title": "Authentification à deux facteurs",
@@ -103,7 +111,7 @@
"title": "Avatar",
"initials": "Initiales",
"gravatar": "Gravatar",
- "marble": "Marble",
+ "marble": "Bille",
"upload": "Téléverser",
"uploadAvatar": "Téléverser l’avatar",
"statusUpdateSuccess": "Statut de l’avatar mis à jour.",
@@ -116,12 +124,12 @@
"vikunja": "Vikunja"
},
"appearance": {
- "title": "Color Scheme",
- "setSuccess": "Saved change of color scheme to {colorScheme}",
+ "title": "Jeu de couleurs",
+ "setSuccess": "Changement du jeu de couleurs enregistré vers {colorScheme}",
"colorScheme": {
- "light": "Light",
- "system": "System",
- "dark": "Dark"
+ "light": "Clair",
+ "system": "Système",
+ "dark": "Sombre"
}
}
},
@@ -157,7 +165,7 @@
"searchSelect": "Clique ou appuie sur la touche Entrée pour sélectionner cette liste",
"shared": "Listes partagées",
"create": {
- "header": "New list",
+ "header": "Nouvelle liste",
"titlePlaceholder": "Entre le nom de la liste…",
"addTitleRequired": "Indique un nom.",
"createdSuccess": "Liste créée.",
@@ -315,7 +323,7 @@
"namespaces": "Espaces de noms",
"search": "Écris pour rechercher un espace de noms…",
"create": {
- "title": "New namespace",
+ "title": "Nouvel espace de noms",
"titleRequired": "Indique un nom.",
"explanation": "Des collections de listes pour partager et organiser vos listes. En fait, chaque liste appartient à un espace de noms.",
"tooltip": "Qu’est-ce qu’un espace de noms ?",
@@ -327,6 +335,7 @@
"archiveText": "Tu ne pourras pas modifier cet espace de noms ou créer de nouvelles listes tant que tu ne l’auras pas désarchivé. Ceci archivera également toutes les listes de cet espace de noms.",
"unarchiveText": "Tu pourras créer de nouvelles listes ou les modifier.",
"success": "Espace de noms archivé.",
+ "unarchiveSuccess": "Espace de noms archivé.",
"description": "L’archivage d’un espace de noms signifie qu’on ne peut pas créer de nouvelles listes dans cet espace, ni le modifier."
},
"delete": {
@@ -374,16 +383,16 @@
"includeNulls": "Inclure les tâches sans valeurs",
"requireAll": "Exiger tous les filtres pour qu’une tâche s’affiche",
"showDoneTasks": "Afficher les tâches terminées",
- "sortAlphabetically": "Sort Alphabetically",
+ "sortAlphabetically": "Trier par ordre alphabétique",
"enablePriority": "Activer le filtre par priorité",
- "enablePercentDone": "Par % d’achèvement",
+ "enablePercentDone": "Activer le filtre par progression",
"dueDateRange": "Plage de dates d’échéance",
"startDateRange": "Plage de dates de début",
"endDateRange": "Plage de dates de fin",
"reminderRange": "Plage de dates de rappel"
},
"create": {
- "title": "New Saved Filter",
+ "title": "Nouveau filtre enregistré",
"description": "Un filtre enregistré est une liste virtuelle qui est calculée à partir d’un ensemble de filtres à chaque fois qu’on y accède. Une fois créé, il apparaît dans un espace de noms spécial.",
"action": "Créer un nouveau filtre enregistré"
},
@@ -475,8 +484,9 @@
"download": "Télécharger",
"showMenu": "Afficher le menu",
"hideMenu": "Masquer le menu",
- "forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "forExample": "Par exemple :",
+ "welcomeBack": "Heureux de vous revoir !",
+ "custom": "Custom"
},
"input": {
"resetColor": "Réinitialiser la couleur",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Créer un nouveau",
"selectPlaceholder": "Clique ou appuie sur la touche Entrée pour sélectionner"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Tâches actuelles",
"titleDates": "Tâches du {from} au {to}",
"noDates": "Afficher les tâches sans date",
- "current": "Tâches actuelles",
- "from": "Tâches du",
- "until": "au",
- "today": "Aujourd’hui",
- "nextWeek": "La semaine prochaine",
- "nextMonth": "Le mois prochain",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Rien à faire — Passe une bonne journée !"
},
"detail": {
@@ -545,7 +603,7 @@
"chooseStartDate": "Clique ici pour fixer une date de début",
"chooseEndDate": "Clique ici pour fixer une date de fin",
"move": "Déplacer une tâche vers une autre liste",
- "done": "Mark task done!",
+ "done": "Marquer la tâche comme terminée !",
"undone": "Marquer comme inachevé",
"created": "Créé {0} par {1}",
"updated": "Mis à jour {0}",
@@ -561,21 +619,21 @@
"text2": "Ceci supprimera également toutes les pièces jointes, les rappels et les relations associés à cette tâche et ne pourra pas être annulé !"
},
"actions": {
- "assign": "Assign to a user",
+ "assign": "Assigner à l'utilisateur",
"label": "Ajouter des étiquettes",
"priority": "Définir la priorité",
"dueDate": "Définir l’échéance",
- "startDate": "Définir une date de début",
- "endDate": "Fixer une date de fin",
+ "startDate": "Définir la date de début",
+ "endDate": "Définir la date de fin",
"reminders": "Définir des rappels",
"repeatAfter": "Définir un intervalle de répétition",
- "percentDone": "Définir le pourcentage d’achèvement",
+ "percentDone": "Définir la progression",
"attachments": "Ajouter des pièces jointes",
- "relatedTasks": "Ajouter des relations de tâches",
- "moveList": "Déplacer la tâche",
- "color": "Définir la couleur de la tâche",
- "delete": "Supprimer la tâche",
- "favorite": "Enregistrer comme favori",
+ "relatedTasks": "Ajouter une relation",
+ "moveList": "Déplacer",
+ "color": "Définir la couleur",
+ "delete": "Supprimer",
+ "favorite": "Ajouter aux favoris",
"unfavorite": "Retirer des favoris"
}
},
@@ -589,7 +647,7 @@
"dueDate": "Date d’échéance",
"endDate": "Date de fin",
"labels": "Étiquettes",
- "percentDone": "% terminé",
+ "percentDone": "Progression",
"priority": "Priorité",
"relatedTasks": "Tâches connexes",
"reminders": "Rappels",
@@ -726,8 +784,8 @@
"dateCurrentYear": "utilisera l’année en cours",
"dateNth": "utilisera le {day}e du mois en cours",
"dateTime": "Combinez n’importe lequel des formats de date avec « {time} » (ou {timePM}) pour définir une heure.",
- "repeats": "Repeating tasks",
- "repeatsDescription": "To set a task as repeating in an interval, simply add '{suffix}' to the task text. The amount needs to be a number and can be omitted to use just the type (see examples)."
+ "repeats": "Tâches répétitives",
+ "repeatsDescription": "Pour définir une tâche comme répétitive dans un intervalle, il suffit d'ajouter « {suffix} » au texte de la tâche. Le montant doit être un nombre et peut être omis pour utiliser uniquement le type (voir exemples)."
}
},
"team": {
@@ -776,17 +834,20 @@
"general": "Général",
"allPages": "Fonctionne sur toutes les pages.",
"currentPageOnly": "Fonctionnent uniquement sur la page en cours.",
+ "somePagesOnly": "Ces raccourcis fonctionnent seulement sur certaines pages.",
"toggleMenu": "Basculer le menu",
"quickSearch": "Ouvrir la barre de recherche/action rapide",
"then": "puis",
"task": {
"title": "Page de tâche",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assigner cette tâche à un utilisateur",
"labels": "Ajouter des étiquettes à cette tâche",
"dueDate": "Modifier la date d’échéance de cette tâche",
"attachment": "Ajouter une pièce jointe à cette tâche",
- "related": "Modifier les tâches connexes de cette tâche"
+ "related": "Modifier les tâches connexes de cette tâche",
+ "color": "Changer la couleur de cette tâche",
+ "move": "Déplacer cette tâche dans une autre liste"
},
"list": {
"title": "Vues en liste",
@@ -794,6 +855,14 @@
"switchToGanttView": "Passer en vue Gantt",
"switchToKanbanView": "Passer en vue kanban",
"switchToTableView": "Passer en vue tableau"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Accéder à la vue d'ensemble",
+ "upcoming": "Accéder aux prochaines tâches",
+ "namespaces": "Accéder aux espaces de noms et listes",
+ "labels": "Accéder aux étiquettes",
+ "teams": "Accéder aux équipes"
}
},
"update": {
@@ -814,7 +883,7 @@
"url": "URL Vikunja",
"urlPlaceholder": "Par exemple : https://localhost:3456",
"change": "changer",
- "use": "Using Vikunja installation at {0}",
+ "use": "Utiliser l’installation de Vikunja à {0}",
"error": "Impossible de trouver ou d'utiliser l'installation de Vikunja sur « {domain} ». Veuillez essayer une autre URL.",
"success": "Utilisation de l’installation Vikunja à « {domain} ».",
"urlRequired": "Une URL est requise."
@@ -899,7 +968,7 @@
"4015": "Le commentaire de la tâche n’existe pas.",
"4016": "Champ de tâche invalide.",
"4017": "Comparateur de filtre de tâche invalide.",
- "4018": "Invalid task filter concatenator.",
+ "4018": "Concaténateur de filtre de tâche invalide.",
"4019": "Valeur de filtre de tâche invalide.",
"5001": "L’espace de noms n’existe pas.",
"5003": "Tu n’as pas accès à l’espace de noms indiqué.",
@@ -908,7 +977,7 @@
"5010": "Cette équipe n’a pas accès à cet espace de noms.",
"5011": "Cet·e utilisateur·rice a déjà accès à cet espace de noms.",
"5012": "L’espace de noms est archivé et ne peut donc être consulté qu’en lecture seule.",
- "6001": "The team name cannot be empty.",
+ "6001": "Le nom de l'équipe ne peut pas être vide.",
"6002": "L’équipe n’existe pas.",
"6004": "L’équipe a déjà accès à cet espace de noms ou à cette liste.",
"6005": "L’utilisateur·rice est déjà membre de cette équipe.",
diff --git a/src/i18n/lang/it-IT.json b/src/i18n/lang/it-IT.json
index ebdb9158e..4ac4c2a8e 100644
--- a/src/i18n/lang/it-IT.json
+++ b/src/i18n/lang/it-IT.json
@@ -7,7 +7,7 @@
"lastViewed": "Ultima visualizzazione",
"list": {
"newText": "È possibile creare una nuova lista per le nuove attività:",
- "new": "New list",
+ "new": "Nuova lista",
"importText": "O importare le liste e le attività da altri servizi in Vikunja:",
"import": "Importa i tuoi dati in Vikunja"
}
@@ -17,14 +17,14 @@
"text": "La pagina richiesta non esiste."
},
"ready": {
- "loading": "Vikunja is loading…",
- "errorOccured": "An error occured:",
- "checkApiUrl": "Please check if the api url is correct.",
- "noApiUrlConfigured": "No API url was configured. Please set one below:"
+ "loading": "Vikunja sta caricando…",
+ "errorOccured": "Si è verificato un errore:",
+ "checkApiUrl": "Controlla se l'URL API è corretto.",
+ "noApiUrlConfigured": "Nessun URL API configurato. Impostane uno qui sotto:"
},
"offline": {
- "title": "You are offline.",
- "text": "Please check your network connection and try again."
+ "title": "Sei offline.",
+ "text": "Controlla la connessione di rete e riprova."
},
"user": {
"auth": {
@@ -34,9 +34,8 @@
"email": "Indirizzo e-mail",
"emailPlaceholder": "per es. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Digita di nuovo la tua password",
"passwordPlaceholder": "es. ••••••••••••",
- "forgotPassword": "Forgot your password?",
+ "forgotPassword": "Password dimenticata?",
"resetPassword": "Reimposta la tua password",
"resetPasswordAction": "Inviami il link per reimpostare la password",
"resetPasswordSuccess": "Controlla la tua casella di posta! Dovresti avere un'e-mail con le istruzioni su come reimpostare la password.",
@@ -45,12 +44,20 @@
"totpTitle": "Codice di autenticazione a due fattori",
"totpPlaceholder": "es. 123456",
"login": "Accedi",
- "register": "Registrati",
+ "createAccount": "Crea account",
"loginWith": "Accedi con {provider}",
"authenticating": "Autenticazione…",
- "openIdStateError": "State does not match, refusing to continue!",
+ "openIdStateError": "Stato non corrispondente, impossibile continuare!",
"openIdGeneralError": "Si è verificato un errore durante l'autenticazione con terze parti.",
- "logout": "Esci"
+ "logout": "Esci",
+ "emailInvalid": "Inserisci un indirizzo e-mail valido.",
+ "usernameRequired": "Inserisci un nome utente.",
+ "passwordRequired": "Inserisci una password.",
+ "showPassword": "Mostra la password",
+ "hidePassword": "Nascondi la password",
+ "noAccountYet": "Non hai un account?",
+ "alreadyHaveAnAccount": "Hai già un account?",
+ "remember": "Resta connesso"
},
"settings": {
"title": "Impostazioni",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "La tua password attuale",
"passwordsDontMatch": "La nuova password e la conferma non coincidono.",
"passwordUpdateSuccess": "Password aggiornata con successo.",
- "updateEmailTitle": "Inserisci il tuo indirizzo e-mail",
+ "updateEmailTitle": "Aggiorna l'indirizzo e-mail",
"updateEmailNew": "Nuovo indirizzo e-mail",
"updateEmailSuccess": "Il tuo indirizzo e-mail è stato aggiornato correttamente. Ti abbiamo inviato un collegamento per confermarlo.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Domenica",
"weekStartMonday": "Lunedì",
"language": "Lingua",
- "defaultList": "Lista predefinita"
+ "defaultList": "Lista predefinita",
+ "timezone": "Fuso Orario"
},
"totp": {
"title": "Autenticazione a due fattori",
@@ -95,39 +103,39 @@
"disableSuccess": "L'autenticazione a due fattori è stata disattivata."
},
"caldav": {
- "title": "CalDav",
- "howTo": "Puoi connettere Vikunja ai client caldav per visualizzare e gestire tutte le attività da diversi client. Inserisci questo URL nel tuo client:",
- "more": "Ulteriori informazioni su caldav in Vikunja"
+ "title": "CalDAV",
+ "howTo": "Puoi connettere Vikunja ai client CalDAV per visualizzare e gestire tutte le attività da diversi client. Inserisci questo URL nel tuo client:",
+ "more": "Ulteriori informazioni su CalDAV in Vikunja"
},
"avatar": {
"title": "Avatar",
"initials": "Iniziali",
"gravatar": "Gravatar",
- "marble": "Marble",
+ "marble": "Marmo",
"upload": "Carica",
"uploadAvatar": "Carica Avatar",
- "statusUpdateSuccess": "Avatar status was updated successfully!",
+ "statusUpdateSuccess": "Avatar aggiornato!",
"setSuccess": "L'avatar è stato impostato con successo!"
},
"quickAddMagic": {
- "title": "Quick Add Magic Mode",
+ "title": "Modalità Aggiunta Rapida Magica",
"disabled": "Disabilitato",
"todoist": "Todoist",
"vikunja": "Vikunja"
},
"appearance": {
- "title": "Color Scheme",
- "setSuccess": "Saved change of color scheme to {colorScheme}",
+ "title": "Tema",
+ "setSuccess": "Tema cambiato in {colorScheme}",
"colorScheme": {
- "light": "Light",
- "system": "System",
- "dark": "Dark"
+ "light": "Chiaro",
+ "system": "Sistema",
+ "dark": "Scuro"
}
}
},
"deletion": {
- "title": "Delete your Vikunja Account",
- "text1": "The deletion of your account is permanent and cannot be undone. We will delete all your namespaces, lists, tasks and everything associated with it.",
+ "title": "Elimina il tuo Account Vikunja",
+ "text1": "La cancellazione del tuo account è permanente e non può essere annullata. Elimineremo tutti i tuoi namespace, liste, attività e tutto ciò che è ad esso associato.",
"text2": "Per continuare, inserisci la tua password. Riceverai un'e-mail con ulteriori istruzioni.",
"confirm": "Elimina il mio profilo",
"requestSuccess": "Richiesta riuscita. Riceverai un'e-mail con ulteriori istruzioni.",
@@ -141,7 +149,7 @@
},
"export": {
"title": "Esporta i tuoi dati Vikunja",
- "description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
+ "description": "Puoi richiedere una copia di tutti i tuoi dati all'interno di Vikunja. Questo include i Namespace, le Liste, le Attività e tutto ciò che è loro associato. È possibile importare questi dati in qualsiasi istanza Vikunja attraverso la funzione di migrazione.",
"descriptionPasswordRequired": "Inserisci la tua password per procedere:",
"request": "Richiedi una copia dei miei dati Vikunja",
"success": "Hai richiesto con successo i tuoi dati Vikunja! Ti invieremo un'e-mail una volta che saranno pronti da scaricare.",
@@ -157,7 +165,7 @@
"searchSelect": "Fare clic o premere invio per selezionare questa lista",
"shared": "Liste Condivise",
"create": {
- "header": "New list",
+ "header": "Nuova lista",
"titlePlaceholder": "Il titolo della lista va qui…",
"addTitleRequired": "Specifica un titolo.",
"createdSuccess": "La lista è stata creata correttamente.",
@@ -191,7 +199,7 @@
"duplicate": {
"title": "Duplica questa lista",
"label": "Duplica",
- "text": "Select a namespace which should hold the duplicated list:",
+ "text": "Seleziona un namespace che dovrebbe contenere l'elenco duplicato:",
"success": "Lista duplicata."
},
"edit": {
@@ -279,23 +287,23 @@
"title": "Kanban",
"limit": "Limite: {limit}",
"noLimit": "Non Impostato",
- "doneBucket": "Done bucket",
- "doneBucketHint": "All tasks moved into this bucket will automatically marked as done.",
- "doneBucketHintExtended": "All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.",
- "doneBucketSavedSuccess": "The done bucket has been saved successfully.",
- "deleteLast": "You cannot remove the last bucket.",
- "addTaskPlaceholder": "Enter the new task title…",
+ "doneBucket": "Colonna attività completate",
+ "doneBucketHint": "Tutte le attività spostate in questa colonna verranno automaticamente contrassegnate come completate.",
+ "doneBucketHintExtended": "Tutte le attività spostate nella colonna attività completate saranno contrassegnate automaticamente come completate. Tutte le attività contrassegnate come completate altrove verranno anche spostate.",
+ "doneBucketSavedSuccess": "Colonna attività completate salvata.",
+ "deleteLast": "Impossibile eliminare l'ultima colonna.",
+ "addTaskPlaceholder": "Inserisci il nuovo titolo dell'attività…",
"addTask": "Aggiungi un'attività",
"addAnotherTask": "Aggiungi un'altra attività",
- "addBucket": "Create a new bucket",
- "addBucketPlaceholder": "Enter the new bucket title…",
- "deleteHeaderBucket": "Delete the bucket",
- "deleteBucketText1": "Are you sure you want to delete this bucket?",
- "deleteBucketText2": "This will not delete any tasks but move them into the default bucket.",
- "deleteBucketSuccess": "The bucket has been deleted successfully.",
- "bucketTitleSavedSuccess": "The bucket title has been saved successfully.",
- "bucketLimitSavedSuccess": "The bucket limit been saved successfully.",
- "collapse": "Collapse this bucket"
+ "addBucket": "Crea una nuova colonna",
+ "addBucketPlaceholder": "Inserisci il titolo della nuova colonna…",
+ "deleteHeaderBucket": "Elimina la colonna",
+ "deleteBucketText1": "Confermi di voler eliminare questa colonna?",
+ "deleteBucketText2": "Questo non eliminerà nessuna attività, ma la sposterà nel bucket predefinito.",
+ "deleteBucketSuccess": "Colonna eliminata.",
+ "bucketTitleSavedSuccess": "Titolo della colonna salvato.",
+ "bucketLimitSavedSuccess": "Limite della colonna salvato.",
+ "collapse": "Comprimi questa colonna"
},
"pseudo": {
"favorites": {
@@ -304,52 +312,53 @@
}
},
"namespace": {
- "title": "Namespaces & Lists",
+ "title": "Namespace e Liste",
"namespace": "Namespace",
- "showArchived": "Show Archived",
- "noneAvailable": "You don't have any namespaces right now.",
- "unarchive": "Un-Archive",
- "archived": "Archived",
- "noLists": "This namespace does not contain any lists.",
- "createList": "Create a new list in this namespace.",
- "namespaces": "Namespaces",
- "search": "Type to search for a namespace…",
+ "showArchived": "Mostra Archiviati",
+ "noneAvailable": "Non hai alcun namespace in questo momento.",
+ "unarchive": "De-Archivia",
+ "archived": "Archiviato",
+ "noLists": "Questo namespace non contiene alcuna lista.",
+ "createList": "Crea una nuova lista in questo namespace.",
+ "namespaces": "Namespace",
+ "search": "Digita per cercare un namespace…",
"create": {
- "title": "New namespace",
- "titleRequired": "Please specify a title.",
- "explanation": "A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.",
- "tooltip": "What's a namespace?",
- "success": "The namespace was successfully created."
+ "title": "Nuovo namespace",
+ "titleRequired": "Specifica un titolo.",
+ "explanation": "Un namespace è una raccolta di liste che puoi condividere e che puoi usare per organizzare le tue liste. Infatti, ogni lista appartiene a un namespace.",
+ "tooltip": "Che cos'è un namespace?",
+ "success": "Namespace creato."
},
"archive": {
"titleArchive": "Archivia \"{namespace}\"",
- "titleUnarchive": "Un-Archive \"{namespace}\"",
- "archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
- "unarchiveText": "You will be able to create new lists or edit it.",
- "success": "The namespace was successfully archived.",
- "description": "If a namespace is archived, you cannot create new lists or edit it."
+ "titleUnarchive": "Disarchivia \"{namespace}\"",
+ "archiveText": "Non sarà possibile modificare questo namespace o creare nuove liste fino a quando non verrà disarchiviato. Questo archivierà anche tutte le liste in questo namespace.",
+ "unarchiveText": "Potrai creare nuove liste o modificarle.",
+ "success": "Namespace creato.",
+ "unarchiveSuccess": "Namespace estratto dall'archivio.",
+ "description": "Se un namespace è archiviato, non è possibile creare nuove liste o modificarlo."
},
"delete": {
- "title": "Delete \"{namespace}\"",
- "text1": "Are you sure you want to delete this namespace and all of its contents?",
+ "title": "Elimina \"{namespace}\"",
+ "text1": "Sei sicuro di voler rimuovere questo namespace e tutto il relativo contenuto?",
"text2": "Questo include tutte le liste e le attività e NON PUÒ ESSERE RIPRISTINATO!",
- "success": "The namespace was successfully deleted."
+ "success": "Namespace eliminato."
},
"edit": {
"title": "Modifica \"{namespace}\"",
- "success": "The namespace was successfully updated."
+ "success": "Namespace aggiornato."
},
"share": {
"title": "Condividi \"{namespace}\""
},
"attributes": {
- "title": "Namespace Title",
- "titlePlaceholder": "The namespace title goes here…",
+ "title": "Titolo del Namespace",
+ "titlePlaceholder": "Il titolo del namespace va qui…",
"description": "Descrizione",
- "descriptionPlaceholder": "The namespaces description goes here…",
+ "descriptionPlaceholder": "La descrizione del namespace va qui…",
"color": "Colore",
- "archived": "Is Archived",
- "isArchived": "This namespace is archived"
+ "archived": "Archiviato",
+ "isArchived": "Questo namespace è archiviato"
},
"pseudo": {
"sharedLists": {
@@ -365,7 +374,7 @@
},
"filters": {
"title": "Filtri",
- "clear": "Clear Filters",
+ "clear": "Pulisci Filtri",
"attributes": {
"title": "Titolo",
"titlePlaceholder": "Il titolo del filtro salvato va qui…",
@@ -374,17 +383,17 @@
"includeNulls": "Includi attività che non hanno un valore impostato",
"requireAll": "Tutti i filtri devono essere veri affinché l'attività venga mostrata",
"showDoneTasks": "Mostra Attività Fatte",
- "sortAlphabetically": "Sort Alphabetically",
+ "sortAlphabetically": "Ordine alfabetico",
"enablePriority": "Abilita Filtro Per Priorità",
- "enablePercentDone": "Abilitare Filtro Per Percentuale Fatta",
+ "enablePercentDone": "Abilita Filtro Per Progresso",
"dueDateRange": "Intervallo Data Di Scadenza",
"startDateRange": "Intervallo Data Iniziale",
"endDateRange": "Intervallo Data Finale",
- "reminderRange": "Reminder Date Range"
+ "reminderRange": "Intervallo date dei promemoria"
},
"create": {
- "title": "New Saved Filter",
- "description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
+ "title": "Nuovo Filtro Salvato",
+ "description": "Un filtro salvato è una lista virtuale che viene calcolata da un insieme di filtri di volta in volta. Una volta creato, apparirà in un namespace speciale.",
"action": "Crea nuovo filtro salvato"
},
"delete": {
@@ -446,9 +455,9 @@
},
"navigation": {
"overview": "Panoramica",
- "upcoming": "Upcoming",
+ "upcoming": "Prossimamente",
"settings": "Impostazioni",
- "imprint": "Imprint",
+ "imprint": "Informazioni legali",
"privacy": "Politica sulla Privacy"
},
"misc": {
@@ -464,19 +473,20 @@
"searchPlaceholder": "Digita per cercare…",
"previous": "Precedente",
"next": "Successivo",
- "poweredBy": "Powered by Vikunja",
+ "poweredBy": "Creato con Vikunja",
"info": "Info",
- "create": "Create",
+ "create": "Crea",
"doit": "Fallo!",
"saving": "Salvataggio…",
"saved": "Salvato!",
"default": "Predefinito",
"close": "Chiudi",
"download": "Scarica",
- "showMenu": "Show the menu",
- "hideMenu": "Hide the menu",
- "forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "showMenu": "Mostra il menu",
+ "hideMenu": "Nascondi il menù",
+ "forExample": "Ad esempio:",
+ "welcomeBack": "Bentornato!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Ripristina Colore",
@@ -485,9 +495,9 @@
"tomorrow": "Domani",
"nextMonday": "Lunedì Prossimo",
"thisWeekend": "Questo fine settimana",
- "laterThisWeek": "Later This Week",
+ "laterThisWeek": "Alla fine di questa settimana",
"nextWeek": "Prossima Settimana",
- "chooseDate": "Choose a date"
+ "chooseDate": "Seleziona una data"
},
"editor": {
"edit": "Modifica",
@@ -504,17 +514,68 @@
"quote": "Citazione",
"unorderedList": "Elenco puntato",
"orderedList": "Elenco numerato",
- "cleanBlock": "Clean Block",
+ "cleanBlock": "Pulisci Blocco",
"link": "Link",
"image": "Immagine",
"table": "Tabella",
- "horizontalRule": "Horizontal Rule",
- "sideBySide": "Side By Side",
- "guide": "Guide"
+ "horizontalRule": "Divisore Orizzontale",
+ "sideBySide": "Affianca",
+ "guide": "Guida"
},
"multiselect": {
- "createPlaceholder": "Create new",
+ "createPlaceholder": "Crea nuovo",
"selectPlaceholder": "Clicca o premere invio per selezionare"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,20 +593,17 @@
"titleCurrent": "Attività Attuali",
"titleDates": "Attività dal {from} al {to}",
"noDates": "Mostra attività senza date",
- "current": "Attività attuali",
- "from": "Tasks from",
- "until": "until",
- "today": "Oggi",
- "nextWeek": "Settimana Prossima",
- "nextMonth": "Prossimo Mese",
- "noTasks": "Nothing to do — Have a nice day!"
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
+ "noTasks": "Nessuna attività — Buona giornata!"
},
"detail": {
"chooseDueDate": "Clicca qui per impostare una data di scadenza",
"chooseStartDate": "Clicca qui per impostare una data di inizio",
"chooseEndDate": "Clicca qui per impostare una data di fine",
"move": "Sposta attività in un'altra lista",
- "done": "Mark task done!",
+ "done": "Segna attività fatta!",
"undone": "Segna come non completato",
"created": "Creato {0} da {1}",
"updated": "Aggiornato {0}",
@@ -554,29 +612,29 @@
"deleteSuccess": "L'attività è stata eliminata con successo.",
"belongsToList": "Questa attività appartiene alla lista '{list}'",
"due": "Scadenza {at}",
- "closePopup": "Close popup",
+ "closePopup": "Chiudi popup",
"delete": {
"header": "Elimina questa attività",
"text1": "Sei sicuro di voler eliminare questa attività?",
"text2": "Questo rimuoverà anche tutti gli allegati, i promemoria e le relazioni associati a questa attività e non può essere ripristinato!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Aggiungi etichette",
+ "assign": "Assegna all'Utente",
+ "label": "Aggiungi Etichette",
"priority": "Imposta Priorità",
"dueDate": "Imposta data di scadenza",
- "startDate": "Imposta una data di inizio",
- "endDate": "Imposta una data di fine",
+ "startDate": "Imposta Data Inizio",
+ "endDate": "Imposta Data Fine",
"reminders": "Imposta promemoria",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Imposta Percentuale Completata",
- "attachments": "Aggiungi allegati",
- "relatedTasks": "Aggiungi attività collegate",
- "moveList": "Sposta attività",
- "color": "Imposta colore attività",
- "delete": "Elimina attività",
- "favorite": "Salva come preferito",
- "unfavorite": "Rimuovi dai preferiti"
+ "repeatAfter": "Imposta Intervallo Ripetizione",
+ "percentDone": "Imposta Progresso",
+ "attachments": "Aggiungi Allegati",
+ "relatedTasks": "Aggiungi Relazione",
+ "moveList": "Sposta",
+ "color": "Imposta Colore",
+ "delete": "Elimina",
+ "favorite": "Aggiungi ai Preferiti",
+ "unfavorite": "Rimuovi dai Preferiti"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Data di scadenza",
"endDate": "Data di fine",
"labels": "Etichette",
- "percentDone": "% Completata",
+ "percentDone": "Progresso",
"priority": "Priorità",
"relatedTasks": "Attività Collegate",
"reminders": "Promemoria",
@@ -599,13 +657,13 @@
"updated": "Aggiornato"
},
"subscription": {
- "subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
- "subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
- "notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
- "subscribe": "Subscribe",
- "unsubscribe": "Unsubscribe",
- "subscribeSuccess": "You are now subscribed to this {entity}",
- "unsubscribeSuccess": "You are now unsubscribed to this {entity}"
+ "subscribedThroughParent": "Non puoi annullare l'iscrizione qui perché sei iscritto a questo {entity} attraverso il suo {parent}.",
+ "subscribed": "Sei attualmente iscritto a questo {entity} e riceverai notifiche per le modifiche.",
+ "notSubscribed": "Non sei iscritto a questo {entity} e non riceverai notifiche per le modifiche.",
+ "subscribe": "Iscriviti",
+ "unsubscribe": "Disiscriviti",
+ "subscribeSuccess": "Ti sei iscritto a questo {entity}",
+ "unsubscribeSuccess": "Ti sei disiscritto a questo {entity}"
},
"attachment": {
"title": "Allegati",
@@ -623,41 +681,41 @@
"comment": {
"title": "Commenti",
"loading": "Caricamento commenti…",
- "edited": "edited {date}",
+ "edited": "modificato il {date}",
"creating": "Creazione del commento…",
"placeholder": "Aggiungi un commento…",
- "comment": "Comment",
+ "comment": "Commenta",
"delete": "Elimina questo commento",
"deleteText1": "Sei sicuro di voler eliminare questo commento?",
"deleteText2": "Questa azione non può essere annullata!",
"addedSuccess": "Il commento è stato aggiunto correttamente."
},
"deferDueDate": {
- "title": "Defer due date",
+ "title": "Rinvia data di scadenza",
"1day": "1 giorno",
"3days": "3 giorni",
"1week": "1 settimana"
},
"description": {
- "placeholder": "Click here to enter a description…",
- "empty": "No description available yet."
+ "placeholder": "Clicca qui per inserire una descrizione…",
+ "empty": "Nessuna descrizione."
},
"assignee": {
- "placeholder": "Type to assign a user…",
+ "placeholder": "Digita per assegnare un utente…",
"selectPlaceholder": "Assegna questo utente",
- "assignSuccess": "The user has been assigned successfully.",
- "unassignSuccess": "The user has been unassigned successfully."
+ "assignSuccess": "Utente assegnato.",
+ "unassignSuccess": "Utente disassegnato."
},
"label": {
- "placeholder": "Type to add a new label…",
- "createPlaceholder": "Add this as new label",
+ "placeholder": "Digita per aggiungere una nuova etichetta…",
+ "createPlaceholder": "Aggiungila come nuova etichetta",
"addSuccess": "Etichetta aggiunta.",
"createSuccess": "Etichetta creata.",
"removeSuccess": "Etichetta eliminata.",
"addCreateSuccess": "Etichetta creata e aggiunta."
},
"priority": {
- "unset": "Unset",
+ "unset": "Azzera",
"low": "Bassa",
"medium": "Media",
"high": "Alta",
@@ -665,38 +723,38 @@
"doNow": "FARE ORA"
},
"relation": {
- "add": "Add a New Task Relation",
- "new": "New Task Relation",
- "searchPlaceholder": "Type search for a new task to add as related…",
- "createPlaceholder": "Add this as new related task",
- "differentList": "This task belongs to a different list.",
- "differentNamespace": "This task belongs to a different namespace.",
- "noneYet": "No task relations yet.",
- "delete": "Delete Task Relation",
- "deleteText1": "Are you sure you want to delete this task relation?",
+ "add": "Aggiungi Attività Collegata",
+ "new": "Nuova Attività Collegata",
+ "searchPlaceholder": "Digita per cercare un'attività da aggiungere come collegata…",
+ "createPlaceholder": "Aggiungi come attività collegata",
+ "differentList": "Questa attività è di una lista diversa.",
+ "differentNamespace": "Questa attività appartiene ad un namespace diverso.",
+ "noneYet": "Nessuna attività collegata.",
+ "delete": "Elimina Collegamento Attività",
+ "deleteText1": "Confermi di voler eliminare questo collegamento attività?",
"deleteText2": "Questa azione non può essere annullata!",
- "select": "Select a relation kind",
+ "select": "Seleziona un tipo di collegamento",
"kinds": {
- "subtask": "Subtask | Subtasks",
- "parenttask": "Parent Task | Parent Tasks",
- "related": "Related Task | Related Tasks",
+ "subtask": "Sotto-attività | Sotto-attività",
+ "parenttask": "Attività Principale | Attività Principale",
+ "related": "Attività Correlata | Attività Correlata",
"duplicateof": "Duplicato Di | Duplicati Di",
- "duplicates": "Duplicates | Duplicates",
- "blocking": "Blocking | Blocking",
- "blocked": "Blocked By | Blocked By",
- "precedes": "Precedes | Precedes",
- "follows": "Follows | Follows",
- "copiedfrom": "Copied From | Copied From",
- "copiedto": "Copied To | Copied To"
+ "duplicates": "Duplicato | Duplicati",
+ "blocking": "Bloccante | Bloccanti",
+ "blocked": "Bloccato Da | Bloccati Da",
+ "precedes": "Precede | Precede",
+ "follows": "Segue | Segue",
+ "copiedfrom": "Copiata Da | Copiate Da",
+ "copiedto": "Copiata In | Copiate In"
}
},
"repeat": {
"everyDay": "Ogni Giorno",
"everyWeek": "Ogni Settimana",
"everyMonth": "Ogni Mese",
- "mode": "Repeat mode",
+ "mode": "Modalità Ripetizione",
"monthly": "Mensilmente",
- "fromCurrentDate": "From Current Date",
+ "fromCurrentDate": "Dalla Data Attuale",
"each": "Ogni",
"specifyAmount": "Specifica una quantità…",
"hours": "Ore",
@@ -706,32 +764,32 @@
"years": "Anni"
},
"quickAddMagic": {
- "hint": "You can use Quick Add Magic",
+ "hint": "Puoi usare l'Aggiunta Rapida Magica",
"what": "Cosa?",
- "title": "Quick Add Magic",
- "intro": "When creating a task, you can use special keywords to directly add attributes to the newly created task. This allows to add commonly used attributes to tasks much faster.",
+ "title": "Aggiunta Rapida Magica",
+ "intro": "Quando si crea un'attività, è possibile utilizzare parole chiave speciali per aggiungere direttamente attributi all'attività appena creata. Questo permette di aggiungere gli attributi comuni molto più velocemente.",
"multiple": "Puoi usarlo più volte.",
- "label1": "To add a label, simply prefix the name of the label with {prefix}.",
- "label2": "Vikunja will first check if the label already exist and create it if not.",
- "label3": "To use spaces, simply add a \" around the label name.",
- "label4": "For example: {prefix}\"Label with spaces\".",
- "priority1": "To set a task's priority, add a number 1-5, prefixed with a {prefix}.",
- "priority2": "The higher the number, the higher the priority.",
- "assignees": "To directly assign the task to a user, add their username prefixed with {prefix} to the task.",
- "list1": "To set a list for the task to appear in, enter its name prefixed with {prefix}.",
- "list2": "This will return an error if the list does not exist.",
+ "label1": "Per aggiungere un'etichetta, basta aggiungere il nome dell'etichetta preceduto da {prefix}.",
+ "label2": "Vikunja controllerà prima se l'etichetta esiste già e nel caso la creerà.",
+ "label3": "Per usare gli spazi, basta \" prima e dopo del nome dell'etichetta.",
+ "label4": "Per esempio: {prefix}\"Etichetta con spazi\".",
+ "priority1": "Per impostare la priorità di un'attività, aggiungi un numero 1-5, preceduto da {prefix}.",
+ "priority2": "Più alto è il numero, più alta è la priorità.",
+ "assignees": "Per assegnare direttamente l'attività a un utente, aggiungere il suo nome utente preceduto da {prefix} all'attività.",
+ "list1": "Per impostare una lista di appartenenza all'attività, inserisci il suo nome prefisso con {prefix}.",
+ "list2": "Ciò restituirà un errore se la lista non esiste.",
"dateAndTime": "Data e ora",
- "date": "Any date will be used as the due date of the new task. You can use dates in any of these formats:",
- "dateWeekday": "any weekday, will use the next date with that date",
- "dateCurrentYear": "will use the current year",
- "dateNth": "will use the {day}th of the current month",
- "dateTime": "Combine any of the date formats with \"{time}\" (or {timePM}) to set a time.",
- "repeats": "Repeating tasks",
- "repeatsDescription": "To set a task as repeating in an interval, simply add '{suffix}' to the task text. The amount needs to be a number and can be omitted to use just the type (see examples)."
+ "date": "Qualsiasi data verrà utilizzata come data di scadenza della nuova attività. È possibile utilizzare le date in uno qualsiasi di questi formati:",
+ "dateWeekday": "qualsiasi giorno della settimana, userà la data più vicina",
+ "dateCurrentYear": "userà l’anno corrente",
+ "dateNth": "userà il {day} del mese corrente",
+ "dateTime": "Combina uno qualsiasi dei formati di data con \"{time}\" (o {timePM}) per impostare un orario.",
+ "repeats": "Attività ricorrenti",
+ "repeatsDescription": "Per impostare un'attività come ricorrente in un intervallo, basta aggiungere '{suffix}' al testo dell'attività. La quantità deve essere un numero e può essere omesso per usare solo il tipo (vedi esempi)."
}
},
"team": {
- "title": "Teams",
+ "title": "Gruppi",
"noTeams": "Non fai parte di nessun gruppo.",
"create": {
"title": "Crea un nuovo gruppo",
@@ -746,23 +804,23 @@
"makeAdmin": "Rendi Amministratore",
"success": "Gruppo aggiornato.",
"userAddedSuccess": "Membro del gruppo aggiunto.",
- "madeMember": "The team member was successfully made member.",
- "madeAdmin": "The team member was successfully made admin.",
+ "madeMember": "Membro del gruppo reso membro.",
+ "madeAdmin": "Membro del gruppo reso amministratore.",
"delete": {
"header": "Elimina il gruppo",
"text1": "Sei sicuro di voler eliminare questo gruppo e tutti i suoi membri?",
- "text2": "All team members will lose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!",
+ "text2": "Tutti i membri del gruppo perderanno l'accesso alle liste e ai namespace condivisi con questo gruppo. NON PUÒ ESSERE RIPRISTINATO!",
"success": "Gruppo eliminato."
},
"deleteUser": {
"header": "Rimuovi un utente dal gruppo",
"text1": "Confermi di voler rimuovere questo utente dal gruppo?",
- "text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
+ "text2": "Perderanno l'accesso a tutte le liste e i namespace a cui questo gruppo ha accesso. NON PUÒ ESSERE RIPRISTINATO!",
"success": "Utente rimosso dal gruppo."
}
},
"attributes": {
- "name": "Team Name",
+ "name": "Nome Gruppo",
"namePlaceholder": "Il nome del gruppo va qui…",
"nameRequired": "Specifica un nome.",
"description": "Descrizione",
@@ -772,32 +830,43 @@
}
},
"keyboardShortcuts": {
- "title": "Keyboard Shortcuts",
- "general": "General",
+ "title": "Tasti Rapidi",
+ "general": "Generali",
"allPages": "Queste scorciatoie funzionano in tutte le pagine.",
"currentPageOnly": "Queste scorciatoie funzionano solo nella pagina attuale.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Attiva/Disattiva Menu",
"quickSearch": "Apri la barra di ricerca/azione rapida",
- "then": "then",
+ "then": "e dopo",
"task": {
- "title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
- "labels": "Add labels to this task",
- "dueDate": "Change the due date of this task",
- "attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "title": "Pagina Attività",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
+ "labels": "Aggiungi etichette a questa attività",
+ "dueDate": "Modifica la data di scadenza di questa attività",
+ "attachment": "Aggiungi un allegato a questa attività",
+ "related": "Modifica le attività collegate a questa",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
- "title": "List Views",
- "switchToListView": "Switch to list view",
- "switchToGanttView": "Switch to gantt view",
- "switchToKanbanView": "Switch to kanban view",
- "switchToTableView": "Switch to table view"
+ "title": "Viste Liste",
+ "switchToListView": "Passa alla vista Lista",
+ "switchToGanttView": "Passa alla vista Gantt",
+ "switchToKanbanView": "Passa alla vista Kanban",
+ "switchToTableView": "Passa alla vista Tabella"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
- "available": "There is an update for Vikunja available!",
+ "available": "È disponibile un aggiornamento per Vikunja!",
"do": "Aggiorna Adesso"
},
"menu": {
@@ -805,136 +874,136 @@
"archive": "Archivia",
"duplicate": "Duplica",
"delete": "Elimina",
- "unarchive": "Un-Archive",
- "setBackground": "Set background",
+ "unarchive": "Disarchivia",
+ "setBackground": "Imposta sfondo",
"share": "Condividi",
"newList": "Nuova lista"
},
"apiConfig": {
"url": "URL Vikunja",
"urlPlaceholder": "es. http://localhost:8080",
- "change": "change",
- "use": "Using Vikunja installation at {0}",
- "error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
- "success": "Using Vikunja installation at \"{domain}\".",
- "urlRequired": "A url is required."
+ "change": "modifica",
+ "use": "Usa l'installazione di Vikunja a {0}",
+ "error": "Impossibile trovare o usare l'installazione di Vikunja su \"{domain}\". Prova per favore con un altro Url.",
+ "success": "Utilizzando l'installazione di Vikunja su \"{domain}\".",
+ "urlRequired": "L'URL è obbligatorio."
},
"loadingError": {
- "failed": "Loading failed, please {0}. If the error persists, please {1}.",
- "tryAgain": "try again",
- "contact": "contact us"
+ "failed": "Caricamento non riuscito, si prega di {0}. Se l'errore persiste, per favore {1}.",
+ "tryAgain": "riprova",
+ "contact": "Contattaci"
},
"notification": {
- "title": "Notifications",
- "none": "You don't have any notifications. Have a nice day!",
- "explainer": "Notifications will appear here when actions on namespaces, lists or tasks you subscribed to happen."
+ "title": "Notifiche",
+ "none": "Nessuna notifica. Buona giornata!",
+ "explainer": "Le notifiche appariranno qui quando le azioni su Namespace, liste o attività a cui hai sottoscritto la sottoscrizione avvengono."
},
"quickActions": {
- "commands": "Commands",
- "placeholder": "Type a command or search…",
- "hint": "You can use {list} to limit the search to a list. Combine {list} or {label} (labels) with a search query to search for a task with these labels or on that list. Use {assignee} to only search for teams.",
- "tasks": "Tasks",
+ "commands": "Comandi",
+ "placeholder": "Digita un comando o cerca…",
+ "hint": "Puoi usare {list} per limitare la ricerca a una lista. Unisci {list} o {label} (etichette) alla ricerca per trovare un'attività con quelle etichette o in quella lista. Usa {assignee} per cercare solo i gruppi.",
+ "tasks": "Attivitá",
"lists": "Liste",
- "teams": "Teams",
- "newList": "Enter the title of the new list…",
- "newTask": "Enter the title of the new task…",
- "newNamespace": "Enter the title of the new namespace…",
- "newTeam": "Enter the name of the new team…",
- "createTask": "Create a task in the current list ({title})",
- "createList": "Create a list in the current namespace ({title})",
+ "teams": "Gruppi",
+ "newList": "Inserisci il titolo della nuova lista…",
+ "newTask": "Inserisci il titolo della nuova attività…",
+ "newNamespace": "Inserisci il titolo del nuovo namespace…",
+ "newTeam": "Inserisci il nome del nuovo gruppo…",
+ "createTask": "Crea un'attività nella lista attuale ({title})",
+ "createList": "Crea una lista nel namespace attuale ({title})",
"cmds": {
- "newTask": "New task",
- "newList": "New list",
- "newNamespace": "New namespace",
- "newTeam": "New team"
+ "newTask": "Nuova attività",
+ "newList": "Nuova lista",
+ "newNamespace": "Nuovo Namespace",
+ "newTeam": "Nuovo gruppo"
}
},
"date": {
- "locale": "en",
+ "locale": "it",
"altFormatLong": "j M Y H:i",
"altFormatShort": "j M Y"
},
"error": {
"error": "Errore",
- "success": "Success",
+ "success": "Fatto",
"0001": "Non ti è permesso farlo.",
- "1001": "A user with this username already exists.",
+ "1001": "Esiste già un utente con questo nome utente.",
"1002": "Un utente con questo indirizzo e-mail esiste già.",
- "1004": "No username and password specified.",
+ "1004": "Nessun nome utente e password specificati.",
"1005": "L'utente non esiste.",
"1006": "Impossibile ottenere l'id utente.",
- "1008": "No password reset token provided.",
- "1009": "Invalid password reset token.",
+ "1008": "Nessun codice di reimpostazione password fornito.",
+ "1009": "Codice di reimpostazione password non valido.",
"1010": "Token di conferma dell'e-mail non valido.",
- "1011": "Wrong username or password.",
+ "1011": "Nome utente o password errati.",
"1012": "Indirizzo e-mail dell'utente non confermato.",
"1013": "La nuova password è vuota.",
"1014": "La vecchia password è vuota.",
"1015": "Autenticazione TOTP già abilitata per questo utente.",
"1016": "Autenticazione TOTP non abilitata per questo utente.",
"1017": "Codice TOTP non valido.",
- "1018": "The user avatar type setting is invalid.",
+ "1018": "L'impostazione del tipo di avatar utente non è valida.",
"2001": "L'ID non può essere vuoto o 0.",
"2002": "Alcuni dati della richiesta non erano validi.",
"3001": "La lista non esiste.",
- "3004": "You need to have read permissions on that list to perform that action.",
+ "3004": "Devi avere i permessi di lettura su quella lista per eseguire quell'azione.",
"3005": "Il titolo della lista non può essere vuoto.",
- "3006": "The list share does not exist.",
+ "3006": "La condivisione della lista non esiste.",
"3007": "Esiste già una lista con questo identificatore.",
- "3008": "The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list.",
- "4001": "The list task text cannot be empty.",
- "4002": "The list task does not exist.",
+ "3008": "La lista è archiviata e può quindi essere consultata solo in sola lettura. Questo vale anche per tutte le attività associate a questa lista.",
+ "4001": "Il testo delle attività della lista non può essere vuoto.",
+ "4002": "Lista di attività non esistente.",
"4003": "Tutte le attività di modifica in blocco devono appartenere alla stessa lista.",
"4004": "Hai bisogno di almeno un'attività quando si modificano in blocco le attività.",
"4005": "Non hai il permesso di vedere l'attività.",
- "4006": "You can't set a parent task as the task itself.",
- "4007": "You can't create a task relation with an invalid kind of relation.",
- "4008": "You can't create a task relation which already exists.",
- "4009": "The task relation does not exist.",
- "4010": "Cannot relate a task with itself.",
- "4011": "The task attachment does not exist.",
- "4012": "The task attachment is too large.",
- "4013": "The task sort param is invalid.",
- "4014": "The task sort order is invalid.",
- "4015": "The task comment does not exist.",
- "4016": "Invalid task field.",
- "4017": "Invalid task filter comparator.",
- "4018": "Invalid task filter concatenator.",
- "4019": "Invalid task filter value.",
- "5001": "The namespace does not exist.",
- "5003": "You do not have access to the specified namespace.",
- "5006": "The namespace name cannot be empty.",
- "5009": "You need to have namespace read access to perform that action.",
- "5010": "This team does not have access to that namespace.",
- "5011": "This user has already access to that namespace.",
- "5012": "The namespace is archived and can therefore only be accessed read only.",
- "6001": "The team name cannot be empty.",
- "6002": "The team does not exist.",
- "6004": "The team already has access to that namespace or list.",
- "6005": "The user is already a member of that team.",
- "6006": "Cannot delete the last team member.",
- "6007": "The team does not have access to the list to perform that action.",
- "7002": "The user already has access to that list.",
+ "4006": "Non è possibile impostare un'attività principale come l'attività stessa.",
+ "4007": "Non è possibile creare una relazione di attività con un tipo di relazione non valido.",
+ "4008": "Non è possibile creare una relazione di attività già esistente.",
+ "4009": "La relazione di attività non esiste.",
+ "4010": "Non è possibile relazionare un'attività con se stessa.",
+ "4011": "L'allegato dell'attività non esiste.",
+ "4012": "L'allegato dell'attività è troppo grande.",
+ "4013": "Il parametro di ordinamento dei task non è valido.",
+ "4014": "L' ordinamento dei task non è valido.",
+ "4015": "Il commento all'attività non esiste.",
+ "4016": "Campo attività non valido.",
+ "4017": "Comparatore di filtri attività non valido.",
+ "4018": "Concatenatore filtro attività non valido.",
+ "4019": "Filtro attività non valido.",
+ "5001": "Il namespace non esiste.",
+ "5003": "Non hai accesso a questo namespace.",
+ "5006": "Il nome del namespace non può essere vuoto.",
+ "5009": "Devi avere accesso in lettura al namespace per effettuare questa operazione.",
+ "5010": "Il tuo gruppo non ha accesso a questo namespace.",
+ "5011": "Questo utente ha già accesso a quel namespace.",
+ "5012": "Il namespace è archiviato e può quindi essere accessibile solo in sola lettura.",
+ "6001": "Il nome del gruppo non può essere vuoto.",
+ "6002": "Gruppo non esistente.",
+ "6004": "Il team ha già accesso a questo namespace o lista.",
+ "6005": "L'utente è già membro di quel gruppo.",
+ "6006": "Non è possibile eliminare l'ultimo membro del gruppo.",
+ "6007": "Il gruppo non ha accesso alla lista per eseguire quell'azione.",
+ "7002": "L'utente ha già accesso a quella lista.",
"7003": "Non hai accesso a quella lista.",
"8001": "Questa etichetta esiste già in quell'attività.",
"8002": "L'etichetta non esiste.",
"8003": "Non hai accesso a questa etichetta.",
- "9001": "The right is invalid.",
- "10001": "The bucket does not exist.",
- "10002": "The bucket does not belong to that list.",
- "10003": "You cannot remove the last bucket on a list.",
- "10004": "You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold.",
- "10005": "There can be only one done bucket per list.",
- "11001": "The saved filter does not exist.",
- "11002": "Saved filters are not available for link shares.",
- "12001": "The subscription entity type is invalid.",
- "12002": "You are already subscribed to the entity itself or a parent entity.",
- "13001": "This link share requires a password for authentication, but none was provided.",
- "13002": "The provided link share password was invalid."
+ "9001": "Permesso non valido.",
+ "10001": "Colonna non esistente.",
+ "10002": "La colonna non appartiene a quella lista.",
+ "10003": "Non puoi rimuovere l'ultima colonna di una lista.",
+ "10004": "Non puoi aggiungere l'attività a questa colonna perché ha già superato il limite di attività che può contenere.",
+ "10005": "Ci può essere solo una colonna completati per lista.",
+ "11001": "Filtro salvato non esistente.",
+ "11002": "I filtri salvati non sono disponibili per i link di condivisione.",
+ "12001": "Il tipo di entità sottoscritto non è valido.",
+ "12002": "Sei già iscritto all'entità stessa o a un'entità principale.",
+ "13001": "Questa condivisione di link richiede una password per l'autenticazione, ma non è stato inserita.",
+ "13002": "La password inserita per il link di condivisione è valida."
},
"about": {
- "title": "About",
- "frontendVersion": "Frontend Version: {version}",
- "apiVersion": "API Version: {version}"
+ "title": "Informazioni",
+ "frontendVersion": "Versione Frontend: {version}",
+ "apiVersion": "Versione API: {version}"
}
}
diff --git a/src/i18n/lang/nl-NL.json b/src/i18n/lang/nl-NL.json
index 1f03d175b..1b73a0123 100644
--- a/src/i18n/lang/nl-NL.json
+++ b/src/i18n/lang/nl-NL.json
@@ -1,292 +1,300 @@
{
"home": {
- "welcomeNight": "Good Night {username}",
- "welcomeMorning": "Good Morning {username}",
- "welcomeDay": "Hi {username}",
- "welcomeEvening": "Good Evening {username}",
- "lastViewed": "Last viewed",
+ "welcomeNight": "Goede nacht {username}",
+ "welcomeMorning": "Goedemorgen {username}",
+ "welcomeDay": "Hallo {username}",
+ "welcomeEvening": "Goedenavond {username}",
+ "lastViewed": "Laatst bekeken",
"list": {
- "newText": "You can create a new list for your new tasks:",
- "new": "New list",
- "importText": "Or import your lists and tasks from other services into Vikunja:",
- "import": "Import your data into Vikunja"
+ "newText": "Je kan een nieuwe lijst maken voor je nieuwe taken:",
+ "new": "Nieuwe lijst",
+ "importText": "Of importeer je lijsten en taken van andere diensten naar Vikunja:",
+ "import": "Importeer je gegevens in Vikunja"
}
},
"404": {
- "title": "Not found",
- "text": "The page you requested does not exist."
+ "title": "Niet gevonden",
+ "text": "De opgevraagde pagina bestaat niet."
},
"ready": {
- "loading": "Vikunja is loading…",
- "errorOccured": "An error occured:",
- "checkApiUrl": "Please check if the api url is correct.",
- "noApiUrlConfigured": "No API url was configured. Please set one below:"
+ "loading": "Vikunja is aan het laden…",
+ "errorOccured": "Er is een fout opgetreden:",
+ "checkApiUrl": "Controleer of de API URL juist is.",
+ "noApiUrlConfigured": "Er is geen API URL geconfigureerd. Stel er hieronder een in:"
},
"offline": {
- "title": "You are offline.",
- "text": "Please check your network connection and try again."
+ "title": "Je bent offline.",
+ "text": "Controleer je netwerkverbinding en probeer het opnieuw."
},
"user": {
"auth": {
- "username": "Username",
- "usernameEmail": "Username Or Email Address",
- "usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
- "emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
- "password": "Password",
- "passwordRepeat": "Retype your password",
- "passwordPlaceholder": "e.g. •••••••••••",
- "forgotPassword": "Forgot your password?",
- "resetPassword": "Reset your password",
- "resetPasswordAction": "Send me a password reset link",
- "resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
- "passwordsDontMatch": "Passwords don't match",
- "confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
- "totpTitle": "Two Factor Authentication Code",
- "totpPlaceholder": "e.g. 123456",
- "login": "Login",
- "register": "Register",
- "loginWith": "Log in with {provider}",
- "authenticating": "Authenticating…",
+ "username": "Gebruikersnaam",
+ "usernameEmail": "Gebruikersnaam of e-mailadres",
+ "usernamePlaceholder": "bv. frederick",
+ "email": "Email address",
+ "emailPlaceholder": "bijv. frederic@vikunja.io",
+ "password": "Wachtwoord",
+ "passwordPlaceholder": "bv. •••••••••••",
+ "forgotPassword": "Wachtwoord vergeten?",
+ "resetPassword": "Je wachtwoord opnieuw instellen",
+ "resetPasswordAction": "Stuur me een wachtwoord reset link",
+ "resetPasswordSuccess": "Check je inbox! Je zou een e-mail moeten hebben met instructies om je wachtwoord te resetten.",
+ "passwordsDontMatch": "Wachtwoorden komen niet overeen",
+ "confirmEmailSuccess": "Je hebt met succes je e-mailadres bevestigd! Je kunt nu inloggen.",
+ "totpTitle": "Twee-factor-authenticatie code",
+ "totpPlaceholder": "bv. 123456",
+ "login": "Inloggen",
+ "createAccount": "Create account",
+ "loginWith": "Inloggen met {provider}",
+ "authenticating": "Authenticeren…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Uitloggen",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
- "title": "Settings",
- "newPasswordTitle": "Update Your Password",
- "newPassword": "New Password",
- "newPasswordConfirm": "New Password Confirmation",
- "currentPassword": "Current Password",
- "currentPasswordPlaceholder": "Your current password",
- "passwordsDontMatch": "The new password and its confirmation don't match.",
- "passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
- "updateEmailNew": "New Email Address",
- "updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
+ "title": "Instellingen",
+ "newPasswordTitle": "Je wachtwoord bijwerken",
+ "newPassword": "Nieuw wachtwoord",
+ "newPasswordConfirm": "Nieuw wachtwoord bevestigen",
+ "currentPassword": "Huidig wachtwoord",
+ "currentPasswordPlaceholder": "Je huidige wachtwoord",
+ "passwordsDontMatch": "Het nieuwe wachtwoord en de bevestiging ervan komen niet overeen.",
+ "passwordUpdateSuccess": "Het wachtwoord is succesvol bijgewerkt.",
+ "updateEmailTitle": "Update Your Email Address",
+ "updateEmailNew": "Nieuw e-mailadres",
+ "updateEmailSuccess": "Je e-mailadres is succesvol bijgewerkt. We hebben je een link gestuurd om het te bevestigen.",
"general": {
- "title": "General Settings",
- "name": "Name",
- "newName": "The new Name",
- "savedSuccess": "The settings were successfully updated.",
- "emailReminders": "Send me reminders for tasks via Email",
- "overdueReminders": "Send me reminders for overdue undone tasks via email each morning",
- "discoverableByName": "Let other users find me when they search for my name",
- "discoverableByEmail": "Let other users find me when they search for my full email",
- "playSoundWhenDone": "Play a sound when marking tasks as done",
- "weekStart": "Week starts on",
- "weekStartSunday": "Sunday",
- "weekStartMonday": "Monday",
- "language": "Language",
- "defaultList": "Default List"
+ "title": "Algemene instellingen",
+ "name": "Naam",
+ "newName": "De nieuwe naam",
+ "savedSuccess": "De instellingen zijn succesvol bijgewerkt.",
+ "emailReminders": "Stuur me herinneringen voor taken via e-mail",
+ "overdueReminders": "Stuur me iedere ochtend per e-mail herinneringen voor achterstallige taken",
+ "discoverableByName": "Andere gebruikers mogen mij vinden door te zoeken op mijn naam",
+ "discoverableByEmail": "Andere gebruikers mogen mij vinden door te zoeken op mijn volledige e-mailadres",
+ "playSoundWhenDone": "Een geluid afspelen wanneer taken als voltooid gemarkeerd worden",
+ "weekStart": "Week begint op",
+ "weekStartSunday": "Zondag",
+ "weekStartMonday": "Maandag",
+ "language": "Taal",
+ "defaultList": "Standaard lijst",
+ "timezone": "Time Zone"
},
"totp": {
- "title": "Two Factor Authentication",
- "enroll": "Enroll",
- "finishSetupPart1": "To finish your setup, use this secret in your totp app (Google Authenticator or similar):",
- "finishSetupPart2": "After that, enter a code from your app below.",
- "scanQR": "Alternatively you can scan this QR code:",
- "passcode": "Passcode",
- "passcodePlaceholder": "A code generated by your totp application",
- "setupSuccess": "You've sucessfully set up two factor authentication!",
- "enterPassword": "Please Enter Your Password",
- "disable": "Disable two factor authentication",
- "confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
- "disableSuccess": "Two factor authentication was sucessfully disabled."
+ "title": "Twee-factor-authenticatie",
+ "enroll": "Activeren",
+ "finishSetupPart1": "Gebruik dit geheim in je TOTP app (Google Authenticator of vergelijkbaar), om de installatie te voltooien:",
+ "finishSetupPart2": "Daarna voert u hieronder een code van je app in.",
+ "scanQR": "Als alternatief kan je ook deze QR code scannen:",
+ "passcode": "Je toegangscode",
+ "passcodePlaceholder": "Een code gegenereerd door uw TOTP app",
+ "setupSuccess": "Je hebt met succes tweestapsverificatie ingesteld!",
+ "enterPassword": "Voer alsjeblieft je wachtwoord in",
+ "disable": "Tweestapsverificatie uitschakelen",
+ "confirmSuccess": "Je hebt met succes je TOTP setup bevestigd en kan het van nu af aan gebruiken!",
+ "disableSuccess": "Authenticatie in twee stappen is succesvol uitgeschakeld."
},
"caldav": {
"title": "Caldav",
- "howTo": "You can connect Vikunja to caldav clients to view and manage all tasks from different clients. Enter this url into your client:",
- "more": "More information about caldav in Vikunja"
+ "howTo": "Je kan Vikunja verbinden met CalDAVclients om alle taken van verschillende clients te bekijken en beheren. Voer deze url in bij je client:",
+ "more": "Meer informatie over CalDAV in Vikunja"
},
"avatar": {
"title": "Avatar",
- "initials": "Initials",
+ "initials": "Initialen",
"gravatar": "Gravatar",
"marble": "Marble",
- "upload": "Upload",
- "uploadAvatar": "Upload Avatar",
- "statusUpdateSuccess": "Avatar status was updated successfully!",
- "setSuccess": "The avatar has been set successfully!"
+ "upload": "Uploaden",
+ "uploadAvatar": "Upload avatar",
+ "statusUpdateSuccess": "Avatar status is succesvol bijgewerkt!",
+ "setSuccess": "De avatar is succesvol ingesteld!"
},
"quickAddMagic": {
- "title": "Quick Add Magic Mode",
- "disabled": "Disabled",
+ "title": "Magische Modus Snel Toevoegen",
+ "disabled": "Uitgeschakeld",
"todoist": "Todoist",
"vikunja": "Vikunja"
},
"appearance": {
- "title": "Color Scheme",
- "setSuccess": "Saved change of color scheme to {colorScheme}",
+ "title": "Kleurenschema",
+ "setSuccess": "Het kleurenschema is aangepast naar {colorScheme}",
"colorScheme": {
- "light": "Light",
- "system": "System",
- "dark": "Dark"
+ "light": "Licht",
+ "system": "Systeem",
+ "dark": "Donker"
}
}
},
"deletion": {
- "title": "Delete your Vikunja Account",
- "text1": "The deletion of your account is permanent and cannot be undone. We will delete all your namespaces, lists, tasks and everything associated with it.",
- "text2": "To proceed, please enter your password. You will receive an email with further instructions.",
- "confirm": "Delete my account",
- "requestSuccess": "The request was successful. You'll receive an email with further instructions.",
- "passwordRequired": "Please enter your password.",
- "confirmSuccess": "You've successfully confirmed the deletion of your account. We will delete your account in three days.",
- "scheduled": "We will delete your Vikunja account at {date} ({dateSince}).",
- "scheduledCancel": "To cancel the deletion of your account, click here.",
+ "title": "Verwijder je Vikunja account",
+ "text1": "Het verwijderen van je account is permanent en kan niet ongedaan worden gemaakt. We zullen al je namespaces, lijsten, taken en alles wat ermee verbonden is verwijderen.",
+ "text2": "Graag je wachtwoord invullen om verder te gaan. Je zult een e-mail ontvangen met verdere instructies.",
+ "confirm": "Verwijder mijn account",
+ "requestSuccess": "Het verzoek was succesvol. Je ontvangt een e-mail met verdere instructies.",
+ "passwordRequired": "Voer alsjeblieft je wachtwoord in.",
+ "confirmSuccess": "Je hebt de verwijdering van je account met succes bevestigd. We verwijderen je account over drie dagen.",
+ "scheduled": "We zullen je Vikunja account op {date} ({dateSince}) verwijderen.",
+ "scheduledCancel": "Klik hier om de verwijdering van je account te annuleren.",
"scheduledCancelText": "To cancel the deletion of your account, please enter your password below:",
- "scheduledCancelConfirm": "Cancel the deletion of my account",
- "scheduledCancelSuccess": "We will not delete your account."
+ "scheduledCancelConfirm": "Annuleer de verwijdering van mijn account",
+ "scheduledCancelSuccess": "We zullen je account niet verwijderen."
},
"export": {
- "title": "Export your Vikunja data",
+ "title": "Je Vikunja gegevens exporteren",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
- "descriptionPasswordRequired": "Please enter your password to proceed:",
- "request": "Request a copy of my Vikunja Data",
- "success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
- "downloadTitle": "Download your exported Vikunja data"
+ "descriptionPasswordRequired": "Voer je wachtwoord in om door te gaan:",
+ "request": "Een kopie van mijn Vikunja gegevens opvragen",
+ "success": "Je hebt met succes je Vikunja Data aangevraagd! We sturen je een e-mail zodra het klaar is om te downloaden.",
+ "downloadTitle": "Download je geëxporteerde Vikunja gegevens"
}
},
"list": {
"archived": "This list is archived. It is not possible to create new or edit tasks for it.",
- "title": "List Title",
- "color": "Color",
- "lists": "Lists",
- "search": "Type to search for a list…",
- "searchSelect": "Click or press enter to select this list",
- "shared": "Shared Lists",
+ "title": "Lijst titel",
+ "color": "Kleur",
+ "lists": "Lijsten",
+ "search": "Typ om naar een lijst te zoeken…",
+ "searchSelect": "Klik of druk op enter om deze lijst te selecteren",
+ "shared": "Gedeelde lijsten",
"create": {
- "header": "New list",
- "titlePlaceholder": "The list's title goes here…",
- "addTitleRequired": "Please specify a title.",
- "createdSuccess": "The list was successfully created.",
+ "header": "Nieuwe lijst",
+ "titlePlaceholder": "De titel van de lijst komt hier…",
+ "addTitleRequired": "Voer alsjeblieft een titel in.",
+ "createdSuccess": "Lijst is succesvol aangemaakt.",
"addListRequired": "Please specify a list or set a default list in the settings."
},
"archive": {
- "title": "Archive \"{list}\"",
- "archive": "Archive this list",
- "unarchive": "Un-Archive this list",
- "unarchiveText": "You will be able to create new tasks or edit it.",
+ "title": "Archiveer \"{list}\"",
+ "archive": "Archiveer deze lijst",
+ "unarchive": "Archivering van deze lijst opheffen",
+ "unarchiveText": "Je kan nieuwe taken maken of bewerken.",
"archiveText": "You won't be able to edit this list or create new tasks until you un-archive it.",
- "success": "The list was successfully archived."
+ "success": "De lijst is succesvol gearchiveerd."
},
"background": {
- "title": "Set list background",
- "remove": "Remove Background",
- "upload": "Choose a background from your pc",
- "searchPlaceholder": "Search for a background…",
- "poweredByUnsplash": "Powered by Unsplash",
- "loadMore": "Load more photos",
- "success": "The background has been set successfully!",
- "removeSuccess": "The background has been removed successfully!"
+ "title": "Achtergrond van lijst instellen",
+ "remove": "Verwijder achtergrond",
+ "upload": "Kies een achtergrond vanuit je pc",
+ "searchPlaceholder": "Naar een achtergrond zoeken…",
+ "poweredByUnsplash": "Mogelijk gemaakt door Unsplash",
+ "loadMore": "Meer foto's laden",
+ "success": "De achtergrond is succesvol ingesteld!",
+ "removeSuccess": "De achtergrond is succesvol verwijderd!"
},
"delete": {
- "title": "Delete \"{list}\"",
- "header": "Delete this list",
+ "title": "Verwijder \"{list}\"",
+ "header": "Verwijder deze lijst",
"text1": "Are you sure you want to delete this list and all of its contents?",
- "text2": "This includes all tasks and CANNOT BE UNDONE!",
- "success": "The list was successfully deleted."
+ "text2": "Dit omvat alle taken en KAN NIET ONGEDAAN GEMAAKT WORDEN!",
+ "success": "De lijst is succesvol verwijderd."
},
"duplicate": {
- "title": "Duplicate this list",
- "label": "Duplicate",
+ "title": "Deze lijst dupliceren",
+ "label": "Dupliceer",
"text": "Select a namespace which should hold the duplicated list:",
- "success": "The list was successfully duplicated."
+ "success": "De lijst is succesvol gedupliceerd."
},
"edit": {
- "header": "Edit This List",
- "title": "Edit \"{list}\"",
- "titlePlaceholder": "The list title goes here…",
+ "header": "Bewerk deze lijst",
+ "title": "Bewerk \"{list}\"",
+ "titlePlaceholder": "De titel van de lijst komt hier…",
"identifierTooltip": "The list identifier can be used to uniquely identify a task across lists. You can set it to empty to disable it.",
"identifier": "List Identifier",
"identifierPlaceholder": "The list identifier goes here…",
- "description": "Description",
+ "description": "Beschrijving",
"descriptionPlaceholder": "The lists description goes here…",
- "color": "Color",
- "success": "The list was successfully updated."
+ "color": "Kleur",
+ "success": "De lijst is succesvol bijgewerkt."
},
"share": {
- "header": "Share this list",
- "title": "Share \"{list}\"",
- "share": "Share",
+ "header": "Deel deze lijst",
+ "title": "Deel \"{list}\"",
+ "share": "Delen",
"links": {
"title": "Share Links",
"what": "What is a share link?",
"explanation": "Share Links allow you to easily share a list with other users who don't have an account on Vikunja.",
"create": "Create a new link share",
- "name": "Name (optional)",
- "namePlaceholder": "e.g. Lorem Ipsum",
+ "name": "Naam (optioneel)",
+ "namePlaceholder": "bv. Lorem Ipsum",
"nameExplanation": "All actions done by this link share will show up with the name.",
- "password": "Password (optional)",
+ "password": "Wachtwoord (optioneel)",
"passwordExplanation": "When authenticating, the user will be required to enter this password.",
- "noName": "No name set",
+ "noName": "Geen naam ingesteld",
"remove": "Remove a link share",
"removeText": "Are you sure you want to remove this link share? It will no longer be possible to access this list with this link share. This cannot be undone!",
"createSuccess": "The link share was successfully created.",
"deleteSuccess": "The link share was successfully deleted"
},
"userTeam": {
- "typeUser": "user | users",
+ "typeUser": "gebruiker | gebruikers",
"typeTeam": "team | teams",
"shared": "Shared with these {type}",
- "you": "You",
+ "you": "Jij",
"notShared": "Not shared with any {type} yet.",
"removeHeader": "Remove a {type} from the {sharable}",
"removeText": "Are you sure you want to remove this {sharable} from the {type}? This cannot be undone!",
"removeSuccess": "The {sharable} was successfully removed from the {type}.",
- "addedSuccess": "The {type} was successfully added.",
+ "addedSuccess": "Het {type} is succesvol toegevoegd.",
"updatedSuccess": "The {type} was successfully added."
},
"right": {
- "title": "Right",
- "read": "Read only",
- "readWrite": "Read & write",
- "admin": "Admin"
+ "title": "Rechten",
+ "read": "Alleen lezen",
+ "readWrite": "Lezen & schrijven",
+ "admin": "Beheerder"
},
"attributes": {
"link": "Link",
- "name": "Name",
- "sharedBy": "Shared by",
- "right": "Right",
- "delete": "Delete"
+ "name": "Naam",
+ "sharedBy": "Gedeeld door",
+ "right": "Rechten",
+ "delete": "Verwijderen"
}
},
"list": {
- "title": "List",
- "add": "Add",
- "addPlaceholder": "Add a new task…",
- "empty": "This list is currently empty.",
- "newTaskCta": "Create a new task.",
- "editTask": "Edit Task"
+ "title": "Lijst",
+ "add": "Toevoegen",
+ "addPlaceholder": "Voeg een nieuwe taak toe…",
+ "empty": "Deze lijst is momenteel leeg.",
+ "newTaskCta": "Creëer een nieuwe taak.",
+ "editTask": "Taak bewerken"
},
"gantt": {
"title": "Gantt",
- "showTasksWithoutDates": "Show tasks which don't have dates set",
- "size": "Size",
- "default": "Default",
- "month": "Month",
- "day": "Day",
- "from": "From",
- "to": "To",
+ "showTasksWithoutDates": "Toon taken waarvoor geen datums zijn ingesteld",
+ "size": "Grootte",
+ "default": "Standaard",
+ "month": "Maand",
+ "day": "Dag",
+ "from": "Van",
+ "to": "Aan",
"noDates": "This task has no dates set."
},
"table": {
- "title": "Table",
- "columns": "Columns"
+ "title": "Tabel",
+ "columns": "Kolommen"
},
"kanban": {
"title": "Kanban",
- "limit": "Limit: {limit}",
- "noLimit": "Not Set",
+ "limit": "Limiet: {limit}",
+ "noLimit": "Niet ingesteld",
"doneBucket": "Done bucket",
"doneBucketHint": "All tasks moved into this bucket will automatically marked as done.",
"doneBucketHintExtended": "All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.",
"doneBucketSavedSuccess": "The done bucket has been saved successfully.",
"deleteLast": "You cannot remove the last bucket.",
- "addTaskPlaceholder": "Enter the new task title…",
- "addTask": "Add a task",
- "addAnotherTask": "Add another task",
+ "addTaskPlaceholder": "Voer de nieuwe taak titel in…",
+ "addTask": "Taak toevoegen",
+ "addAnotherTask": "Nog een taak toevoegen",
"addBucket": "Create a new bucket",
"addBucketPlaceholder": "Enter the new bucket title…",
"deleteHeaderBucket": "Delete the bucket",
@@ -299,64 +307,65 @@
},
"pseudo": {
"favorites": {
- "title": "Favorites"
+ "title": "Favorieten"
}
}
},
"namespace": {
- "title": "Namespaces & Lists",
+ "title": "Namespaces & lijsten",
"namespace": "Namespace",
- "showArchived": "Show Archived",
- "noneAvailable": "You don't have any namespaces right now.",
- "unarchive": "Un-Archive",
- "archived": "Archived",
- "noLists": "This namespace does not contain any lists.",
- "createList": "Create a new list in this namespace.",
+ "showArchived": "Toon gearchiveerd",
+ "noneAvailable": "Je hebt op dit moment geen Namespaces.",
+ "unarchive": "Archivering opheffen",
+ "archived": "Gearchiveerd",
+ "noLists": "Deze namespace bevat geen lijsten.",
+ "createList": "Maak een nieuwe lijst in deze namespace.",
"namespaces": "Namespaces",
- "search": "Type to search for a namespace…",
+ "search": "Begin met typen om een Namespace te zoeken…",
"create": {
- "title": "New namespace",
- "titleRequired": "Please specify a title.",
- "explanation": "A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.",
- "tooltip": "What's a namespace?",
- "success": "The namespace was successfully created."
+ "title": "Nieuwe namespace",
+ "titleRequired": "Voer een titel in.",
+ "explanation": "Een Namespace is een verzameling lijsten die je kunt delen en gebruiken om je lijsten mee te organiseren. Iedere lijst hoort bij een Namespace.",
+ "tooltip": "Wat is een namespace?",
+ "success": "De Namespace is succesvol gemaakt."
},
"archive": {
- "titleArchive": "Archive \"{namespace}\"",
- "titleUnarchive": "Un-Archive \"{namespace}\"",
+ "titleArchive": "Archiveer \"{namespace}\"",
+ "titleUnarchive": "Archivering \"{namespace}\" ongedaan maken",
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
- "unarchiveText": "You will be able to create new lists or edit it.",
+ "unarchiveText": "Je kan nieuwe lijsten maken of deze bewerken.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
- "title": "Delete \"{namespace}\"",
- "text1": "Are you sure you want to delete this namespace and all of its contents?",
- "text2": "This includes all lists and tasks and CANNOT BE UNDONE!",
- "success": "The namespace was successfully deleted."
+ "title": "\"{namespace}\" verwijderen",
+ "text1": "Weet je zeker dat je deze Namespace en alle inhoud ervan wilt verwijderen?",
+ "text2": "Dit omvat alle lijsten en taken en KAN NIET ONGEDAAN GEMAAKT WORDEN!",
+ "success": "De Namespace is succesvol verwijderd."
},
"edit": {
- "title": "Edit \"{namespace}\"",
+ "title": "Bewerk \"{namespace}\"",
"success": "The namespace was successfully updated."
},
"share": {
- "title": "Share \"{namespace}\""
+ "title": "\"{namespace}\" delen"
},
"attributes": {
- "title": "Namespace Title",
+ "title": "Namespace titel",
"titlePlaceholder": "The namespace title goes here…",
- "description": "Description",
+ "description": "Beschrijving",
"descriptionPlaceholder": "The namespaces description goes here…",
- "color": "Color",
- "archived": "Is Archived",
+ "color": "Kleur",
+ "archived": "Is gearchiveerd",
"isArchived": "This namespace is archived"
},
"pseudo": {
"sharedLists": {
- "title": "Shared Lists"
+ "title": "Gedeelde lijsten"
},
"favorites": {
- "title": "Favorites"
+ "title": "Favorieten"
},
"savedFilters": {
"title": "Filters"
@@ -367,16 +376,16 @@
"title": "Filters",
"clear": "Clear Filters",
"attributes": {
- "title": "Title",
+ "title": "Titel",
"titlePlaceholder": "The saved filter title goes here…",
- "description": "Description",
- "descriptionPlaceholder": "The description goes here…",
+ "description": "Beschrijving",
+ "descriptionPlaceholder": "De beschrijving komt hier…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
- "sortAlphabetically": "Sort Alphabetically",
+ "sortAlphabetically": "Alfabetisch sorteren",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -390,213 +399,262 @@
"delete": {
"header": "Delete this saved filter",
"text": "Are you sure you want to delete this saved filter?",
- "success": "The filter was deleted successfully."
+ "success": "Het filter is succesvol verwijderd."
},
"edit": {
"title": "Edit This Saved Filter",
- "success": "The filter was saved successfully."
+ "success": "Het filter is succesvol opgeslagen."
}
},
"migrate": {
- "title": "Migrate from other services to Vikunja",
- "titleService": "Import your data from {name} into Vikunja",
- "import": "Import your data into Vikunja",
+ "title": "Migreer van andere diensten naar Vikunja",
+ "titleService": "Importeer je gegevens van {name} naar Vikunja",
+ "import": "Importeer je data in Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",
"descriptionDo": "Vikunja will import all lists, tasks, notes, reminders and files you have access to.",
"authorize": "To authorize Vikunja to access your {name} Account, click the button below.",
- "getStarted": "Get Started",
- "inProgress": "Importing in progress…",
+ "getStarted": "Aan de slag",
+ "inProgress": "Bezig met importeren…",
"alreadyMigrated1": "It looks like you've already imported your stuff from {name} at {date}.",
"alreadyMigrated2": "Importing again is possible, but might create duplicates. Are you sure?",
- "confirm": "I am sure, please start migrating now!",
+ "confirm": "Ik weet het zeker, begin nu met migreren!",
"importUpload": "To import data from {name} into Vikunja, click the button below to select a file.",
- "upload": "Upload file"
+ "upload": "Bestand uploaden"
},
"label": {
"title": "Labels",
- "manage": "Manage labels",
+ "manage": "Labels beheren",
"description": "Click on a label to edit it. You can edit all labels you created, you can use all labels which are associated with a task to whose list you have access.",
- "newCTA": "You currently do not have any labels.",
+ "newCTA": "Je hebt momenteel geen labels.",
"search": "Type to search for a label…",
"create": {
- "header": "New label",
- "title": "Create a new label",
+ "header": "Nieuw label",
+ "title": "Nieuwe label maken",
"titleRequired": "Please specify a title.",
"success": "The label was successfully created."
},
"edit": {
- "header": "Edit Label",
+ "header": "Bewerk label",
"forbidden": "You are not allowed to edit this label because you dont own it.",
- "success": "The label was successfully updated."
+ "success": "Het label is succesvol bijgewerkt."
},
- "deleteSuccess": "The label was successfully deleted.",
+ "deleteSuccess": "Het label is succesvol verwijderd.",
"attributes": {
- "title": "Title",
- "titlePlaceholder": "The label title goes here…",
- "description": "Description",
- "descriptionPlaceholder": "Label description",
- "color": "Color"
+ "title": "Titel",
+ "titlePlaceholder": "De labeltitel komt hier…",
+ "description": "Beschrijving",
+ "descriptionPlaceholder": "Beschrijving label",
+ "color": "Kleur"
}
},
"sharing": {
- "authenticating": "Authenticating…",
- "passwordRequired": "This shared list requires a password. Please enter it below:",
- "error": "An error occured.",
- "invalidPassword": "The password is invalid."
+ "authenticating": "Authenticeren…",
+ "passwordRequired": "Deze gedeelde lijst vereist een wachtwoord. Gelieve hieronder in te voeren:",
+ "error": "Er is een fout opgetreden.",
+ "invalidPassword": "Het wachtwoord is ongeldig."
},
"navigation": {
- "overview": "Overview",
- "upcoming": "Upcoming",
- "settings": "Settings",
+ "overview": "Overzicht",
+ "upcoming": "Aankomend",
+ "settings": "Instellingen",
"imprint": "Imprint",
- "privacy": "Privacy Policy"
+ "privacy": "Privacybeleid"
},
"misc": {
- "loading": "Loading…",
- "save": "Save",
- "delete": "Delete",
- "confirm": "Confirm",
- "cancel": "Cancel",
- "refresh": "Refresh",
- "disable": "Disable",
- "copy": "Copy to clipboard",
- "search": "Search",
- "searchPlaceholder": "Type to search…",
- "previous": "Previous",
- "next": "Next",
- "poweredBy": "Powered by Vikunja",
- "info": "Info",
- "create": "Create",
- "doit": "Do it!",
- "saving": "Saving…",
- "saved": "Saved!",
- "default": "Default",
- "close": "Close",
- "download": "Download",
- "showMenu": "Show the menu",
- "hideMenu": "Hide the menu",
- "forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "loading": "Bezig met laden…",
+ "save": "Opslaan",
+ "delete": "Verwijderen",
+ "confirm": "Bevestigen",
+ "cancel": "Annuleren",
+ "refresh": "Vernieuwen",
+ "disable": "Uitschakelen",
+ "copy": "Kopiëren naar klembord",
+ "search": "Zoeken",
+ "searchPlaceholder": "Typ om te zoeken…",
+ "previous": "Vorige",
+ "next": "Volgende",
+ "poweredBy": "Mogelijk gemaakt door Vikunja",
+ "info": "Informatie",
+ "create": "Creëren",
+ "doit": "Doe het!",
+ "saving": "Opslaan…",
+ "saved": "Opgeslagen!",
+ "default": "Standaard",
+ "close": "Sluiten",
+ "download": "Downloaden",
+ "showMenu": "Menu weergeven",
+ "hideMenu": "Menu verbergen",
+ "forExample": "Bijvoorbeeld:",
+ "welcomeBack": "Welkom terug!",
+ "custom": "Custom"
},
"input": {
- "resetColor": "Reset Color",
+ "resetColor": "Kleur resetten",
"datepicker": {
- "today": "Today",
- "tomorrow": "Tomorrow",
- "nextMonday": "Next Monday",
- "thisWeekend": "This Weekend",
- "laterThisWeek": "Later This Week",
- "nextWeek": "Next Week",
- "chooseDate": "Choose a date"
+ "today": "Vandaag",
+ "tomorrow": "Morgen",
+ "nextMonday": "Volgende maandag",
+ "thisWeekend": "Dit weekend",
+ "laterThisWeek": "Later deze week",
+ "nextWeek": "Volgende week",
+ "chooseDate": "Kies een datum"
},
"editor": {
- "edit": "Edit",
- "done": "Done",
- "heading1": "Heading 1",
- "heading2": "Heading 2",
- "heading3": "Heading 3",
+ "edit": "Bewerken",
+ "done": "Voltooid",
+ "heading1": "Kop 1",
+ "heading2": "Kop 2",
+ "heading3": "Kop 3",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
- "bold": "Bold",
- "italic": "Italic",
- "strikethrough": "Strikethrough",
+ "bold": "Dikgedrukt",
+ "italic": "Cursief",
+ "strikethrough": "Doorgestreept",
"code": "Code",
- "quote": "Quote",
- "unorderedList": "Unordered List",
+ "quote": "Citaat",
+ "unorderedList": "Ongesorteerde lijst",
"orderedList": "Ordered List",
"cleanBlock": "Clean Block",
"link": "Link",
- "image": "Image",
- "table": "Table",
- "horizontalRule": "Horizontal Rule",
- "sideBySide": "Side By Side",
- "guide": "Guide"
+ "image": "Afbeelding",
+ "table": "Tabel",
+ "horizontalRule": "Horizontale lijn",
+ "sideBySide": "Naast elkaar",
+ "guide": "Handleiding"
},
"multiselect": {
- "createPlaceholder": "Create new",
- "selectPlaceholder": "Click or press enter to select"
+ "createPlaceholder": "Nieuwe aanmaken",
+ "selectPlaceholder": "Klik of druk op enter om te selecteren"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
- "task": "Task",
- "new": "Create a new task",
- "delete": "Delete this task",
- "createSuccess": "The task was successfully created.",
- "addReminder": "Add a new reminder…",
+ "task": "Taak",
+ "new": "Creëer een nieuwe taak",
+ "delete": "Deze taak verwijderen",
+ "createSuccess": "De taak is succesvol aangemaakt.",
+ "addReminder": "Nieuwe herinnering toevoegen…",
"doneSuccess": "The task was successfully marked as done.",
"undoneSuccess": "The task was successfully un-marked as done.",
"openDetail": "Open task detail view",
- "checklistTotal": "{checked} of {total} tasks",
- "checklistAllDone": "{total} tasks",
+ "checklistTotal": "{checked} van {total} taken",
+ "checklistAllDone": "{total} taken",
"show": {
- "titleCurrent": "Current Tasks",
+ "titleCurrent": "Huidige taken",
"titleDates": "Tasks from {from} until {to}",
- "noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "noDates": "Taken zonder datums tonen",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
- "chooseDueDate": "Click here to set a due date",
- "chooseStartDate": "Click here to set a start date",
- "chooseEndDate": "Click here to set an end date",
- "move": "Move task to a different list",
+ "chooseDueDate": "Klik hier om een vervaldatum in te stellen",
+ "chooseStartDate": "Klik hier om een begindatum in te stellen",
+ "chooseEndDate": "Klik hier om een einddatum in te stellen",
+ "move": "Verplaats taak naar een andere lijst",
"done": "Mark task done!",
- "undone": "Mark as undone",
+ "undone": "Markeer als niet voltooid",
"created": "Created {0} by {1}",
"updated": "Updated {0}",
"doneAt": "Done {0}",
- "updateSuccess": "The task was saved successfully.",
- "deleteSuccess": "The task has been deleted successfully.",
- "belongsToList": "This task belongs to list '{list}'",
- "due": "Due {at}",
+ "updateSuccess": "De taak is succesvol opgeslagen.",
+ "deleteSuccess": "De taak is succesvol verwijderd.",
+ "belongsToList": "Deze taak hoort bij de lijst '{list}'",
+ "due": "Vervalt {at}",
"closePopup": "Close popup",
"delete": {
- "header": "Delete this task",
- "text1": "Are you sure you want to remove this task?",
+ "header": "Verwijder deze taak",
+ "text1": "Weet je zeker dat je deze taak wilt verwijderen?",
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
- "priority": "Set Priority",
- "dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
- "reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "assign": "Assign to User",
+ "label": "Add Labels",
+ "priority": "Prioriteit instellen",
+ "dueDate": "Vervaldatum instellen",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
+ "reminders": "Herinneringen instellen",
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Verwijder",
+ "favorite": "Toevoegen aan favorieten",
+ "unfavorite": "Verwijder uit favorieten"
}
},
"attributes": {
- "assignees": "Assignees",
- "color": "Color",
- "created": "Created",
- "createdBy": "Created By",
- "description": "Description",
- "done": "Done",
+ "assignees": "Toegewezen personen",
+ "color": "Kleur",
+ "created": "Aangemaakt op",
+ "createdBy": "Aangemaakt door",
+ "description": "Beschrijving",
+ "done": "Voltooid",
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
- "priority": "Priority",
- "relatedTasks": "Related Tasks",
- "reminders": "Reminders",
- "repeat": "Repeat",
- "startDate": "Start Date",
- "title": "Title",
- "updated": "Updated"
+ "percentDone": "Progress",
+ "priority": "Prioriteit",
+ "relatedTasks": "Verwante Taken",
+ "reminders": "Herinneringen",
+ "repeat": "Herhalen",
+ "startDate": "Begindatum",
+ "title": "Titel",
+ "updated": "Bijgewerkt"
},
"subscription": {
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
@@ -608,49 +666,49 @@
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
},
"attachment": {
- "title": "Attachments",
+ "title": "Bijlagen",
"createdBy": "created {0} by {1}",
"downloadTooltip": "Download this attachment",
- "upload": "Upload attachment",
- "drop": "Drop files here to upload",
- "delete": "Delete attachment",
- "deleteTooltip": "Delete this attachment",
- "deleteText1": "Are you sure you want to delete the attachment {filename}?",
- "deleteText2": "This cannot be undone!",
- "copyUrl": "Copy URL",
+ "upload": "Upload bijlage",
+ "drop": "Sleep bestanden hierheen om te uploaden",
+ "delete": "Bijlage verwijderen",
+ "deleteTooltip": "Verwijder deze bijlage",
+ "deleteText1": "Weet je zeker dat je de bijlage {filename} wilt verwijderen?",
+ "deleteText2": "Dit kan niet ongedaan worden gemaakt!",
+ "copyUrl": "URL kopiëren",
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
},
"comment": {
- "title": "Comments",
- "loading": "Loading comments…",
+ "title": "Reacties",
+ "loading": "Bezig met laden van reacties…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
- "comment": "Comment",
- "delete": "Delete this comment",
- "deleteText1": "Are you sure you want to delete this comment?",
- "deleteText2": "This cannot be undone!",
- "addedSuccess": "The comment was added successfully."
+ "comment": "Reactie",
+ "delete": "Verwijder deze reactie",
+ "deleteText1": "Weet je zeker dat je deze reactie wilt verwijderen?",
+ "deleteText2": "Dit kan niet ongedaan worden gemaakt!",
+ "addedSuccess": "De reactie is succesvol toegevoegd."
},
"deferDueDate": {
- "title": "Defer due date",
- "1day": "1 day",
- "3days": "3 days",
+ "title": "Vervaldatum uitstellen",
+ "1day": "1 dag",
+ "3days": "3 dagen",
"1week": "1 week"
},
"description": {
- "placeholder": "Click here to enter a description…",
- "empty": "No description available yet."
+ "placeholder": "Klik hier om een beschrijving in te voeren…",
+ "empty": "Nog geen beschrijving beschikbaar."
},
"assignee": {
- "placeholder": "Type to assign a user…",
- "selectPlaceholder": "Assign this user",
- "assignSuccess": "The user has been assigned successfully.",
- "unassignSuccess": "The user has been unassigned successfully."
+ "placeholder": "Typ om een gebruiker toe te wijzen…",
+ "selectPlaceholder": "Deze gebruiker toewijzen",
+ "assignSuccess": "De gebruiker is succesvol toegewezen.",
+ "unassignSuccess": "De toewijzing van de gebruiker is met succes ingetrokken."
},
"label": {
- "placeholder": "Type to add a new label…",
- "createPlaceholder": "Add this as new label",
+ "placeholder": "Typ om een nieuw label toe te voegen…",
+ "createPlaceholder": "Dit toevoegen als nieuw label",
"addSuccess": "The label has been added successfully.",
"createSuccess": "The label has been created successfully.",
"removeSuccess": "The label has been removed successfully.",
@@ -658,24 +716,24 @@
},
"priority": {
"unset": "Unset",
- "low": "Low",
- "medium": "Medium",
- "high": "high",
- "urgent": "Urgent",
- "doNow": "DO NOW"
+ "low": "Laag",
+ "medium": "Gemiddeld",
+ "high": "hoog",
+ "urgent": "Dringend",
+ "doNow": "NU DOEN"
},
"relation": {
"add": "Add a New Task Relation",
- "new": "New Task Relation",
+ "new": "Nieuwe taakrelatie",
"searchPlaceholder": "Type search for a new task to add as related…",
- "createPlaceholder": "Add this as new related task",
- "differentList": "This task belongs to a different list.",
+ "createPlaceholder": "Voeg dit toe als nieuwe gerelateerde taak",
+ "differentList": "Deze taak hoort bij een andere lijst.",
"differentNamespace": "This task belongs to a different namespace.",
- "noneYet": "No task relations yet.",
- "delete": "Delete Task Relation",
- "deleteText1": "Are you sure you want to delete this task relation?",
- "deleteText2": "This cannot be undone!",
- "select": "Select a relation kind",
+ "noneYet": "Nog geen taakrelaties.",
+ "delete": "Taak relatie verwijderen",
+ "deleteText1": "Weet je zeker dat je deze taakrelatie wilt verwijderen?",
+ "deleteText2": "Dit kan niet ongedaan worden gemaakt!",
+ "select": "Selecteer een relatietype",
"kinds": {
"subtask": "Subtask | Subtasks",
"parenttask": "Parent Task | Parent Tasks",
@@ -691,39 +749,39 @@
}
},
"repeat": {
- "everyDay": "Every Day",
- "everyWeek": "Every Week",
- "everyMonth": "Every Month",
- "mode": "Repeat mode",
- "monthly": "Monthly",
- "fromCurrentDate": "From Current Date",
+ "everyDay": "Elke dag",
+ "everyWeek": "Elke week",
+ "everyMonth": "Elke maand",
+ "mode": "Herhaalmodus",
+ "monthly": "Maandelijks",
+ "fromCurrentDate": "Vanaf huidige datum",
"each": "Each",
"specifyAmount": "Specify an amount…",
- "hours": "Hours",
- "days": "Days",
- "weeks": "Weeks",
- "months": "Months",
- "years": "Years"
+ "hours": "Uren",
+ "days": "Dagen",
+ "weeks": "Weken",
+ "months": "Maanden",
+ "years": "Jaren"
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",
- "what": "What?",
+ "what": "Wat?",
"title": "Quick Add Magic",
"intro": "When creating a task, you can use special keywords to directly add attributes to the newly created task. This allows to add commonly used attributes to tasks much faster.",
- "multiple": "You can use this multiple times.",
+ "multiple": "Je kan dit meerdere keren gebruiken.",
"label1": "To add a label, simply prefix the name of the label with {prefix}.",
"label2": "Vikunja will first check if the label already exist and create it if not.",
"label3": "To use spaces, simply add a \" around the label name.",
"label4": "For example: {prefix}\"Label with spaces\".",
- "priority1": "To set a task's priority, add a number 1-5, prefixed with a {prefix}.",
- "priority2": "The higher the number, the higher the priority.",
+ "priority1": "Om prioriteit aan een taak te geven voeg je een nummer tussen 1-5 toe, beginnend met een {prefix}.",
+ "priority2": "Hoe hoger het getal, hoe hoger de prioriteit.",
"assignees": "To directly assign the task to a user, add their username prefixed with {prefix} to the task.",
"list1": "To set a list for the task to appear in, enter its name prefixed with {prefix}.",
- "list2": "This will return an error if the list does not exist.",
- "dateAndTime": "Date and time",
+ "list2": "Dit geeft een foutmelding als de lijst niet bestaat.",
+ "dateAndTime": "Datum en tijd",
"date": "Any date will be used as the due date of the new task. You can use dates in any of these formats:",
"dateWeekday": "any weekday, will use the next date with that date",
- "dateCurrentYear": "will use the current year",
+ "dateCurrentYear": "zal het huidige jaar gebruiken",
"dateNth": "will use the {day}th of the current month",
"dateTime": "Combine any of the date formats with \"{time}\" (or {timePM}) to set a time.",
"repeats": "Repeating tasks",
@@ -732,43 +790,43 @@
},
"team": {
"title": "Teams",
- "noTeams": "You are currently not part of any teams.",
+ "noTeams": "Je maakt momenteel geen deel uit van een team.",
"create": {
- "title": "Create a new team",
- "success": "The team was successfully created."
+ "title": "Maak een nieuw team",
+ "success": "Het team is succesvol aangemaakt."
},
"edit": {
- "title": "Edit Team \"{team}\"",
- "members": "Team Members",
- "search": "Type to search a user…",
- "addUser": "Add to team",
- "makeMember": "Make Member",
- "makeAdmin": "Make Admin",
- "success": "The team was successfully updated.",
- "userAddedSuccess": "The team member was successfully added.",
- "madeMember": "The team member was successfully made member.",
- "madeAdmin": "The team member was successfully made admin.",
+ "title": "Team \"{team}\" bewerken",
+ "members": "Teamleden",
+ "search": "Typ om een gebruiker te zoeken…",
+ "addUser": "Toevoegen aan team",
+ "makeMember": "Maak lid",
+ "makeAdmin": "Beheerder maken",
+ "success": "Het team is succesvol bijgewerkt.",
+ "userAddedSuccess": "Het teamlid is succesvol toegevoegd.",
+ "madeMember": "Het teamlid is succesvol tot lid gemaakt.",
+ "madeAdmin": "Het teamlid is succesvol beheerder gemaakt.",
"delete": {
- "header": "Delete the team",
+ "header": "Verwijder het team",
"text1": "Are you sure you want to delete this team and all of its members?",
"text2": "All team members will lose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!",
"success": "The team was successfully deleted."
},
"deleteUser": {
- "header": "Remove a user from the team",
- "text1": "Are you sure you want to remove this user from the team?",
+ "header": "Verwijder een gebruiker uit het team",
+ "text1": "Weet je zeker dat je deze gebruiker wilt verwijderen uit het team?",
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
"success": "The user was successfully deleted from the team."
}
},
"attributes": {
- "name": "Team Name",
+ "name": "Teamnaam",
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
- "description": "Description",
+ "description": "Beschrijving",
"descriptionPlaceholder": "The teams description goes here…",
- "admin": "Admin",
- "member": "Member"
+ "admin": "Beheerder",
+ "member": "Lid"
}
},
"keyboardShortcuts": {
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,26 +855,34 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
- "available": "There is an update for Vikunja available!",
- "do": "Update Now"
+ "available": "Er is een update voor Vikunja beschikbaar!",
+ "do": "Nu bijwerken"
},
"menu": {
- "edit": "Edit",
- "archive": "Archive",
- "duplicate": "Duplicate",
- "delete": "Delete",
- "unarchive": "Un-Archive",
- "setBackground": "Set background",
- "share": "Share",
- "newList": "New list"
+ "edit": "Bewerken",
+ "archive": "Archiveren",
+ "duplicate": "Dupliceer",
+ "delete": "Verwijderen",
+ "unarchive": "Archivering opheffen",
+ "setBackground": "Achtergrond instellen",
+ "share": "Delen",
+ "newList": "Nieuwe lijst"
},
"apiConfig": {
"url": "Vikunja URL",
- "urlPlaceholder": "eg. https://localhost:3456",
- "change": "change",
+ "urlPlaceholder": "bv. https://localhost:3456",
+ "change": "wijzigen",
"use": "Using Vikunja installation at {0}",
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
"success": "Using Vikunja installation at \"{domain}\".",
@@ -821,20 +890,20 @@
},
"loadingError": {
"failed": "Loading failed, please {0}. If the error persists, please {1}.",
- "tryAgain": "try again",
- "contact": "contact us"
+ "tryAgain": "probeer opnieuw",
+ "contact": "neem contact met ons op"
},
"notification": {
- "title": "Notifications",
- "none": "You don't have any notifications. Have a nice day!",
+ "title": "Notificaties",
+ "none": "Je hebt geen meldingen. Fijne dag!",
"explainer": "Notifications will appear here when actions on namespaces, lists or tasks you subscribed to happen."
},
"quickActions": {
"commands": "Commands",
"placeholder": "Type a command or search…",
"hint": "You can use {list} to limit the search to a list. Combine {list} or {label} (labels) with a search query to search for a task with these labels or on that list. Use {assignee} to only search for teams.",
- "tasks": "Tasks",
- "lists": "Lists",
+ "tasks": "Taken",
+ "lists": "Lijsten",
"teams": "Teams",
"newList": "Enter the title of the new list…",
"newTask": "Enter the title of the new task…",
@@ -843,42 +912,42 @@
"createTask": "Create a task in the current list ({title})",
"createList": "Create a list in the current namespace ({title})",
"cmds": {
- "newTask": "New task",
- "newList": "New list",
- "newNamespace": "New namespace",
- "newTeam": "New team"
+ "newTask": "Nieuwe taak",
+ "newList": "Nieuwe lijst",
+ "newNamespace": "Nieuwe namespace",
+ "newTeam": "Nieuw team"
}
},
"date": {
- "locale": "en",
+ "locale": "nl",
"altFormatLong": "j M Y H:i",
"altFormatShort": "j M Y"
},
"error": {
- "error": "Error",
- "success": "Success",
- "0001": "You're not allowed to do that.",
- "1001": "A user with this username already exists.",
- "1002": "A user with this email address already exists.",
- "1004": "No username and password specified.",
- "1005": "The user does not exist.",
+ "error": "Fout",
+ "success": "Succes",
+ "0001": "Je hebt geen toestemming om dat te doen.",
+ "1001": "Er bestaat al een gebruiker met deze naam.",
+ "1002": "Er bestaat al een gebruiker met dit e-mailadres.",
+ "1004": "Geen gebruikersnaam en wachtwoord opgegeven.",
+ "1005": "De gebruiker bestaat niet.",
"1006": "Could not get the user id.",
"1008": "No password reset token provided.",
"1009": "Invalid password reset token.",
- "1010": "Invalid email confirm token.",
- "1011": "Wrong username or password.",
- "1012": "Email address of the user not confirmed.",
- "1013": "New password is empty.",
- "1014": "Old password is empty.",
+ "1010": "Ongeldig bevestigingstoken voor e-mail.",
+ "1011": "Ongeldige gebruikersnaam of wachtwoord.",
+ "1012": "E-mailadres van de gebruiker is niet bevestigd.",
+ "1013": "Nieuw wachtwoord is leeg.",
+ "1014": "Oude wachtwoord is leeg.",
"1015": "Totp is already enabled for this user.",
"1016": "Totp is not enabled for this user.",
"1017": "The totp passcode is invalid.",
"1018": "The user avatar type setting is invalid.",
"2001": "ID cannot be empty or 0.",
"2002": "Some of the request data was invalid.",
- "3001": "The list does not exist.",
- "3004": "You need to have read permissions on that list to perform that action.",
- "3005": "The list title cannot be empty.",
+ "3001": "De lijst bestaat niet.",
+ "3004": "Je moet leesrechten hebben op die lijst om die actie uit te voeren.",
+ "3005": "De titel van de lijst mag niet leeg zijn.",
"3006": "The list share does not exist.",
"3007": "A list with this identifier already exists.",
"3008": "The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list.",
@@ -890,42 +959,42 @@
"4006": "You can't set a parent task as the task itself.",
"4007": "You can't create a task relation with an invalid kind of relation.",
"4008": "You can't create a task relation which already exists.",
- "4009": "The task relation does not exist.",
- "4010": "Cannot relate a task with itself.",
+ "4009": "Deze taakrelatie bestaat niet.",
+ "4010": "Kan een taak niet met zichzelf relatëren.",
"4011": "The task attachment does not exist.",
"4012": "The task attachment is too large.",
"4013": "The task sort param is invalid.",
- "4014": "The task sort order is invalid.",
+ "4014": "De sorteervolgorde van taken is ongeldig.",
"4015": "The task comment does not exist.",
- "4016": "Invalid task field.",
+ "4016": "Ongeldig taakveld.",
"4017": "Invalid task filter comparator.",
"4018": "Invalid task filter concatenator.",
- "4019": "Invalid task filter value.",
- "5001": "The namespace does not exist.",
- "5003": "You do not have access to the specified namespace.",
- "5006": "The namespace name cannot be empty.",
+ "4019": "Ongeldige taakfilter waarde.",
+ "5001": "Deze Namespace bestaat niet.",
+ "5003": "Je hebt geen toegang tot de opgegeven Namespace.",
+ "5006": "De naam van de namespace mag niet leeg zijn.",
"5009": "You need to have namespace read access to perform that action.",
"5010": "This team does not have access to that namespace.",
"5011": "This user has already access to that namespace.",
"5012": "The namespace is archived and can therefore only be accessed read only.",
"6001": "The team name cannot be empty.",
- "6002": "The team does not exist.",
- "6004": "The team already has access to that namespace or list.",
+ "6002": "Het team bestaat niet.",
+ "6004": "Het team heeft al toegang tot die namespace of lijst.",
"6005": "The user is already a member of that team.",
- "6006": "Cannot delete the last team member.",
- "6007": "The team does not have access to the list to perform that action.",
- "7002": "The user already has access to that list.",
- "7003": "You do not have access to that list.",
- "8001": "This label already exists on that task.",
- "8002": "The label does not exist.",
- "8003": "You do not have access to this label.",
- "9001": "The right is invalid.",
+ "6006": "Kan het laatste teamlid niet verwijderen.",
+ "6007": "Het team heeft geen toegang tot de lijst om die actie uit te voeren.",
+ "7002": "De gebruiker heeft al toegang tot deze lijst.",
+ "7003": "Je hebt geen toegang tot die lijst.",
+ "8001": "Dit label bestaat al voor die taak.",
+ "8002": "Het label bestaat niet.",
+ "8003": "Je hebt geen toegang tot dit label.",
+ "9001": "Ongeldige rechten.",
"10001": "The bucket does not exist.",
"10002": "The bucket does not belong to that list.",
"10003": "You cannot remove the last bucket on a list.",
"10004": "You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold.",
"10005": "There can be only one done bucket per list.",
- "11001": "The saved filter does not exist.",
+ "11001": "Het opgeslagen filter bestaat niet.",
"11002": "Saved filters are not available for link shares.",
"12001": "The subscription entity type is invalid.",
"12002": "You are already subscribed to the entity itself or a parent entity.",
@@ -933,8 +1002,8 @@
"13002": "The provided link share password was invalid."
},
"about": {
- "title": "About",
- "frontendVersion": "Frontend Version: {version}",
- "apiVersion": "API Version: {version}"
+ "title": "Over",
+ "frontendVersion": "Frontend versie: {version}",
+ "apiVersion": "API Versie: {version}"
}
}
diff --git a/src/i18n/lang/pl-PL.json b/src/i18n/lang/pl-PL.json
new file mode 100644
index 000000000..ebca6a1e2
--- /dev/null
+++ b/src/i18n/lang/pl-PL.json
@@ -0,0 +1,1009 @@
+{
+ "home": {
+ "welcomeNight": "Dobrej nocy {username}",
+ "welcomeMorning": "Dzień dobry {username}",
+ "welcomeDay": "Cześć {username}",
+ "welcomeEvening": "Dobry wieczór {username}",
+ "lastViewed": "Ostatnio oglądane",
+ "list": {
+ "newText": "Możesz stworzyć nową listę dla swoich nowych zadań:",
+ "new": "Nowa lista",
+ "importText": "Lub zaimportować swoje listy i zadania z innych usług do Vikunja:",
+ "import": "Zaimportuj swoje dane do Vikunja"
+ }
+ },
+ "404": {
+ "title": "Nie znaleziono",
+ "text": "Żądana strona nie istnieje."
+ },
+ "ready": {
+ "loading": "Vikunja się ładuje…",
+ "errorOccured": "Wystąpił błąd:",
+ "checkApiUrl": "Sprawdź, czy adres URL interfejsu API jest poprawny.",
+ "noApiUrlConfigured": "Nie skonfigurowano adresu URL interfejsu API. Ustaw go poniżej:"
+ },
+ "offline": {
+ "title": "Jesteś offline.",
+ "text": "Sprawdź połączenie internetowe i spróbuj ponownie."
+ },
+ "user": {
+ "auth": {
+ "username": "Nazwa użytkownika",
+ "usernameEmail": "Nazwa użytkownika lub adres e-mail",
+ "usernamePlaceholder": "np. frederick",
+ "email": "Adres e-mail",
+ "emailPlaceholder": "np. frederic{'@'}vikunja.io",
+ "password": "Hasło",
+ "passwordPlaceholder": "np. •••••••••••",
+ "forgotPassword": "Zapomniałeś hasła?",
+ "resetPassword": "Zresetuj swoje hasło",
+ "resetPasswordAction": "Wyślij mi link do resetowania hasła",
+ "resetPasswordSuccess": "Sprawdź swoją skrzynkę odbiorczą! Powinieneś otrzymać e-mail z instrukcją, jak zresetować hasło.",
+ "passwordsDontMatch": "Hasła nie są takie same",
+ "confirmEmailSuccess": "Pomyślnie zatwierdzono e-mail! Możesz się teraz zalogować.",
+ "totpTitle": "Kod uwierzytelniania dwuskładnikowego",
+ "totpPlaceholder": "np. 123456",
+ "login": "Zaloguj sie",
+ "createAccount": "Utwórz konto",
+ "loginWith": "Zaloguj się przez {provider}",
+ "authenticating": "Uwierzytelnianie…",
+ "openIdStateError": "Stan się nie zgadza, odmowa kontynuacji!",
+ "openIdGeneralError": "Wystąpił błąd podczas uwierzytelniania wobec strony trzeciej.",
+ "logout": "Wyloguj",
+ "emailInvalid": "Proszę podać poprawny adres e-mail.",
+ "usernameRequired": "Proszę podać nazwę użytkownika.",
+ "passwordRequired": "Proszę podać hasło.",
+ "showPassword": "Pokaż hasło",
+ "hidePassword": "Ukryj hasło",
+ "noAccountYet": "Nie masz jeszcze konta?",
+ "alreadyHaveAnAccount": "Masz już konto?",
+ "remember": "Pozostań zalogowany"
+ },
+ "settings": {
+ "title": "Ustawienia",
+ "newPasswordTitle": "Zaktualizuj swoje hasło",
+ "newPassword": "Nowe hasło",
+ "newPasswordConfirm": "Potwierdź hasło",
+ "currentPassword": "Aktualne hasło",
+ "currentPasswordPlaceholder": "Twoje aktualne hasło",
+ "passwordsDontMatch": "Nowe hasło i jego potwierdzenie nie są takie same.",
+ "passwordUpdateSuccess": "Hasło zostało pomyślnie zaktualizowane.",
+ "updateEmailTitle": "Zaktualizuj swój adres e-mail",
+ "updateEmailNew": "Nowy adres e-mail",
+ "updateEmailSuccess": "Twój adres e-mail został pomyślnie zaktualizowany. Wysłaliśmy Ci link do potwierdzenia operacji.",
+ "general": {
+ "title": "Ustawienia główne",
+ "name": "Nazwa",
+ "newName": "Nowa nazwa",
+ "savedSuccess": "Ustawienia zostały pomyślnie zaktualizowane.",
+ "emailReminders": "Wysyłaj mi przypomnienia o zadaniach przez e-mail",
+ "overdueReminders": "Codziennie rano wysyłaj mi przypomnienia o zaległych niewykonanych zadaniach e-mailem",
+ "discoverableByName": "Pozwól innym użytkownikom znaleźć mnie, gdy będą szukać mojej nazwy",
+ "discoverableByEmail": "Pozwól innym użytkownikom znaleźć mnie, gdy będą szukać mojego adresu e-mail",
+ "playSoundWhenDone": "Odtwórz dźwięk podczas oznaczania zadań jako ukończonych",
+ "weekStart": "Tydzień zaczyna się od",
+ "weekStartSunday": "niedzieli",
+ "weekStartMonday": "poniedziałku",
+ "language": "Język",
+ "defaultList": "Domyślna lista",
+ "timezone": "Strefa czasowa"
+ },
+ "totp": {
+ "title": "Uwierzytelnianie dwuskładnikowe",
+ "enroll": "Włącz",
+ "finishSetupPart1": "Aby dokończyć, użyj tego kodu konfiguracyjnego w swojej aplikacji TOTP (Google Authenticator lub podobnej):",
+ "finishSetupPart2": "Następnie przepisz poniżej kod autoryzacyjny ze swojej aplikacji.",
+ "scanQR": "Możesz również zeskanować ten kod QR:",
+ "passcode": "Kod autoryzacyjny",
+ "passcodePlaceholder": "Kod wygenerowany przez twoją aplikację TOTP",
+ "setupSuccess": "Udało Ci się skonfigurować uwierzytelnianie dwuskładnikowe!",
+ "enterPassword": "Wprowadź hasło",
+ "disable": "Wyłącz uwierzytelnianie dwuskładnikowe",
+ "confirmSuccess": "Pomyślnie skonfigurowałeś uwierzytelnianie dwuskładnikowe i od teraz możesz z niego korzystać!",
+ "disableSuccess": "Uwierzytelnianie dwuskładnikowe zostało pomyślnie wyłączone."
+ },
+ "caldav": {
+ "title": "Caldav",
+ "howTo": "Możesz połączyć Vikunja z klientami caldav, aby przeglądać i zarządzać wszystkimi zadaniami z różnych klientów. Wprowadź ten adres URL do swojego klienta:",
+ "more": "Więcej informacji o CalDAV w Vikunji"
+ },
+ "avatar": {
+ "title": "Awatar",
+ "initials": "Inicjały",
+ "gravatar": "Gravatar",
+ "marble": "Szklana kulka",
+ "upload": "Prześlij",
+ "uploadAvatar": "Prześlij awatar",
+ "statusUpdateSuccess": "Status awatara został pomyślnie zaktualizowany!",
+ "setSuccess": "Awatar został pomyślnie ustawiony!"
+ },
+ "quickAddMagic": {
+ "title": "Tryb Quick Add Magic",
+ "disabled": "Wyłączony",
+ "todoist": "Todoist",
+ "vikunja": "Vikunja"
+ },
+ "appearance": {
+ "title": "Schemat kolorów",
+ "setSuccess": "Zapisano zmianę schematu kolorów na {colorScheme}",
+ "colorScheme": {
+ "light": "Jasny",
+ "system": "Systemowy",
+ "dark": "Ciemny"
+ }
+ }
+ },
+ "deletion": {
+ "title": "Usuń swoje konto Vikunja",
+ "text1": "Usunięcie Twojego konta jest trwałe i nie można tego cofnąć. Usuniemy wszystkie Twoje sekcje, listy, zadania i wszystko, co z nimi powiązane.",
+ "text2": "Aby kontynuować, wprowadź swoje hasło. Otrzymasz wiadomość e-mail z dalszymi instrukcjami.",
+ "confirm": "Usuń moje konto",
+ "requestSuccess": "Żądanie powiodło się. Otrzymasz wiadomość e-mail z dalszymi instrukcjami.",
+ "passwordRequired": "Wprowadź hasło.",
+ "confirmSuccess": "Pomyślnie potwierdziłeś usunięcie swojego konta. Za trzy dni usuniemy Twoje konto.",
+ "scheduled": "Twoje konto Vikunja zostanie usunięte w dniu {date} ({dateSince}).",
+ "scheduledCancel": "Aby anulować usunięcie konta, kliknij tutaj.",
+ "scheduledCancelText": "Aby anulować usunięcie konta, wprowadź poniżej swoje hasło:",
+ "scheduledCancelConfirm": "Anuluj usunięcie mojego konta",
+ "scheduledCancelSuccess": "Nie usuniemy Twojego konta."
+ },
+ "export": {
+ "title": "Eksportuj swoje dane Vikunja",
+ "description": "Możesz zażądać kopii wszystkich swoich danych Vikunja. Obejmuje to sekcje, listy, zadania i wszystko, co z nimi powiązane. Możesz zaimportować te dane do dowolnej instancji Vikunja za pomocą funkcji migracji.",
+ "descriptionPasswordRequired": "Wprowadź hasło, aby kontynuować:",
+ "request": "Generuj kopię moich danych Vikunja",
+ "success": "Pomyślnie zażądałeś danych Vikunja! Wyślemy Ci e-mail, gdy będą gotowe do pobrania.",
+ "downloadTitle": "Pobierz wyeksportowane dane Vikunja"
+ }
+ },
+ "list": {
+ "archived": "Ta lista jest zarchiwizowana. Nie można w niej tworzyć ani edytować zadań.",
+ "title": "Tytuł listy",
+ "color": "Kolor",
+ "lists": "Listy",
+ "search": "Wpisz, aby wyszukać listę…",
+ "searchSelect": "Kliknij lub naciśnij Enter, aby wybrać tę listę",
+ "shared": "Współdzielone listy",
+ "create": {
+ "header": "Nowa lista",
+ "titlePlaceholder": "Tu wpisz tytuł listy…",
+ "addTitleRequired": "Proszę podaj tytuł.",
+ "createdSuccess": "Lista została pomyślnie utworzona.",
+ "addListRequired": "Proszę, wybierz listę lub ustaw listę domyślną w ustawieniach."
+ },
+ "archive": {
+ "title": "Archiwizuj \"{list}\"",
+ "archive": "Zarchiwizuj tę listę",
+ "unarchive": "Cofnij archiwizację tej listy",
+ "unarchiveText": "Będziesz mógł tworzyć nowe zadania lub je edytować.",
+ "archiveText": "Nie będziesz mógł edytować tej listy ani tworzyć nowych zadań, dopóki nie cofniesz jej archiwizacji.",
+ "success": "Lista została pomyślnie zarchiwizowana."
+ },
+ "background": {
+ "title": "Ustaw tło listy",
+ "remove": "Usuń tło",
+ "upload": "Prześlij tło ze swojego komputera",
+ "searchPlaceholder": "Wyszukiwanie tła…",
+ "poweredByUnsplash": "Wspierane przez Unsplash",
+ "loadMore": "Załaduj więcej zdjęć",
+ "success": "Tło zostało ustawione pomyślnie!",
+ "removeSuccess": "Tło zostało pomyślnie usunięte!"
+ },
+ "delete": {
+ "title": "Usuń \"{list}\"",
+ "header": "Usuń tę listę",
+ "text1": "Czy na pewno chcesz usunąć tę listę i całą jej zawartość?",
+ "text2": "Obejmuje to wszystkie zadania i tego NIE DA SIĘ COFNĄĆ!",
+ "success": "Lista została pomyślnie usunięta."
+ },
+ "duplicate": {
+ "title": "Duplikuj tę listę",
+ "label": "Duplikuj",
+ "text": "Wybierz sekcję, do której powinna trafić zduplikowana lista:",
+ "success": "Lista została pomyślnie zduplikowana."
+ },
+ "edit": {
+ "header": "Edytuj tę listę",
+ "title": "Edytuj \"{list}\"",
+ "titlePlaceholder": "Tu wpisz tytuł listy…",
+ "identifierTooltip": "Identyfikator listy może być użyty do jednoznacznej identyfikacji zadania z różnych list. Możesz ustawić go jako pusty, aby go wyłączyć.",
+ "identifier": "Identyfikator listy",
+ "identifierPlaceholder": "Tu wpisz identyfikator listy…",
+ "description": "Opis",
+ "descriptionPlaceholder": "Tu wpisz opis listy…",
+ "color": "Kolor",
+ "success": "Lista została pomyślnie zaktualizowana."
+ },
+ "share": {
+ "header": "Udostępnij tę listę",
+ "title": "Udostępnij \"{list}\"",
+ "share": "Udostępnij",
+ "links": {
+ "title": "Udostępnij linki",
+ "what": "Co to jest udostępnianie linków?",
+ "explanation": "Udostępnianie linków umożliwia łatwe udostępnianie listy innym użytkownikom, którzy nie mają konta na Vikunja.",
+ "create": "Utwórz nowy link do udostępnienia",
+ "name": "Nazwa: (opcjonalnie)",
+ "namePlaceholder": "np. Lorem Ipsum",
+ "nameExplanation": "Wszystkie działania wykonane przez ten link będą wyświetlane pod tą nazwą.",
+ "password": "Hasło (opcjonalnie)",
+ "passwordExplanation": "Podczas uwierzytelniania użytkownik będzie musiał wprowadzić to hasło.",
+ "noName": "Nie ustawiono nazwy",
+ "remove": "Usuń link",
+ "removeText": "Czy na pewno chcesz usunąć ten link? Dostęp do tej listy z tym linkiem nie będzie już możliwy. Tego nie da się cofnąć!",
+ "createSuccess": "Pomyślnie utworzono udostępniony link.",
+ "deleteSuccess": "Udostępniony link został pomyślnie usunięty"
+ },
+ "userTeam": {
+ "typeUser": "użytkownik(a) | użytkownikom",
+ "typeTeam": "zespół | zespołom",
+ "shared": "Udostępniono tym {type}",
+ "you": "Ty",
+ "notShared": "Nie udostępniono jeszcze żadnym {type}.",
+ "removeHeader": "Usuń {type} z {sharable}",
+ "removeText": "Czy na pewno chcesz usunąć {type} z {sharable}? Tego nie da się cofnąć!",
+ "removeSuccess": "{type} został pomyślnie usunięty z {sharable}.",
+ "addedSuccess": "{type} został pomyślnie dodany.",
+ "updatedSuccess": "{type} został pomyślnie dodany."
+ },
+ "right": {
+ "title": "Uprawnienia",
+ "read": "Tylko do odczytu",
+ "readWrite": "Odczyt i zapis",
+ "admin": "Administrator"
+ },
+ "attributes": {
+ "link": "Link",
+ "name": "Nazwa",
+ "sharedBy": "Udostępniony przez",
+ "right": "Uprawnienia",
+ "delete": "Usuń"
+ }
+ },
+ "list": {
+ "title": "Lista",
+ "add": "Dodaj",
+ "addPlaceholder": "Dodaj nowe zadanie…",
+ "empty": "Ta lista jest obecnie pusta.",
+ "newTaskCta": "Utwórz nowe zadanie.",
+ "editTask": "Edytuj zadanie"
+ },
+ "gantt": {
+ "title": "Gantt",
+ "showTasksWithoutDates": "Pokaż zadania, które nie mają ustawionych dat",
+ "size": "Rozmiar",
+ "default": "Domyślny",
+ "month": "Miesiąc",
+ "day": "Dzień",
+ "from": "Od",
+ "to": "Do",
+ "noDates": "To zadanie nie ma ustawionych dat."
+ },
+ "table": {
+ "title": "Tabela",
+ "columns": "Kolumny"
+ },
+ "kanban": {
+ "title": "Kanban",
+ "limit": "Limit: {limit}",
+ "noLimit": "Nie ustawiony",
+ "doneBucket": "Zasobnik ukończonych zadań",
+ "doneBucketHint": "Wszystkie zadania przeniesione do tego zasobnika zostaną automatycznie oznaczone jako ukończone.",
+ "doneBucketHintExtended": "Wszystkie zadania przeniesione do zasobnika ukończonych zadań zostaną automatycznie oznaczone jako ukończone. Również wszystkie zadania z innych zasobników oznaczone jako ukończone zostaną do niego przeniesione.",
+ "doneBucketSavedSuccess": "Zasobnik ukończonych zadań został pomyślnie zapisany.",
+ "deleteLast": "Nie możesz usunąć ostatniego zasobnika.",
+ "addTaskPlaceholder": "Wpisz tytuł nowego zadania…",
+ "addTask": "Dodaj zadanie",
+ "addAnotherTask": "Dodaj kolejne zadanie",
+ "addBucket": "Utwórz nowy zasobnik",
+ "addBucketPlaceholder": "Wpisz tytuł nowego zasobnika…",
+ "deleteHeaderBucket": "Usuń zasobnik",
+ "deleteBucketText1": "Czy na pewno chcesz usunąć ten zasobnik?",
+ "deleteBucketText2": "Nie spowoduje to usunięcia żadnych zadań, ale przeniesie je do domyślnego zasobnika.",
+ "deleteBucketSuccess": "Zasobnik został pomyślnie usunięty.",
+ "bucketTitleSavedSuccess": "Tytuł zasobnika został pomyślnie zapisany.",
+ "bucketLimitSavedSuccess": "Limit zasobnika został pomyślnie zapisany.",
+ "collapse": "Zwiń ten zasobnik"
+ },
+ "pseudo": {
+ "favorites": {
+ "title": "Ulubione"
+ }
+ }
+ },
+ "namespace": {
+ "title": "Sekcje i Listy",
+ "namespace": "Sekcja",
+ "showArchived": "Pokaż zarchiwizowane",
+ "noneAvailable": "W tej chwili nie masz żadnych sekcji.",
+ "unarchive": "Cofnij archiwizację",
+ "archived": "Zarchiwizowane",
+ "noLists": "Ta sekcja nie zawiera żadnych list.",
+ "createList": "Utwórz nową listę w tej sekcji.",
+ "namespaces": "Sekcje",
+ "search": "Wpisz, aby wyszukać sekcję…",
+ "create": {
+ "title": "Nowa sekcja",
+ "titleRequired": "Proszę, podaj tytuł.",
+ "explanation": "Sekcja to zbiór list, które możesz udostępniać i używać do organizowania list. Każda lista należy do jakiejś sekcji.",
+ "tooltip": "Co to jest sekcja?",
+ "success": "Sekcja została pomyślnie utworzona."
+ },
+ "archive": {
+ "titleArchive": "Archiwizuj \"{namespace}\"",
+ "titleUnarchive": "Cofnij archiwizację \"{namespace}\"",
+ "archiveText": "Nie będziesz mógł tworzyć nowych list ani edytować tej sekcji, dopóki nie cofniesz archiwizacji. Ta operacja zarchiwizuje również wszystkie listy należące do tej sekcji.",
+ "unarchiveText": "Będziesz mógł tworzyć nowe listy i je edytować.",
+ "success": "Sekcja została pomyślnie zarchiwizowana.",
+ "unarchiveSuccess": "Archiwizacja sekcji została pomyślnie cofnięta.",
+ "description": "Jeśli sekcja jest zarchiwizowana, nie można edytować ani tworzyć nowych list."
+ },
+ "delete": {
+ "title": "Usuń \"{namespace}\"",
+ "text1": "Czy na pewno chcesz usunąć tę sekcję i całą jej zawartość?",
+ "text2": "Dotyczy to wszystkich list i zadań i tego NIE DA SIĘ COFNĄĆ!",
+ "success": "Sekcja została pomyślnie usunięta."
+ },
+ "edit": {
+ "title": "Edytuj \"{namespace}\"",
+ "success": "Sekcja została pomyślnie zaktualizowana."
+ },
+ "share": {
+ "title": "Udostępnij \"{namespace}\""
+ },
+ "attributes": {
+ "title": "Tytuł sekcji",
+ "titlePlaceholder": "Tu wpisz tytuł sekcji…",
+ "description": "Opis",
+ "descriptionPlaceholder": "Tu wpisz opis sekcji…",
+ "color": "Kolor",
+ "archived": "Archiwizacja",
+ "isArchived": "Ta sekcja jest zarchiwizowana"
+ },
+ "pseudo": {
+ "sharedLists": {
+ "title": "Współdzielone listy"
+ },
+ "favorites": {
+ "title": "Ulubione"
+ },
+ "savedFilters": {
+ "title": "Filtry"
+ }
+ }
+ },
+ "filters": {
+ "title": "Filtry",
+ "clear": "Wyczyść filtry",
+ "attributes": {
+ "title": "Tytuł",
+ "titlePlaceholder": "Tu wpisz tytuł filtra stałego…",
+ "description": "Opis",
+ "descriptionPlaceholder": "Tu wpisz opis…",
+ "includeNulls": "Uwzględnij zadania, które nie mają ustawionej danej wartości",
+ "requireAll": "Wymagaj, aby wszystkie filtry były spełnione, aby zadanie się pojawiło",
+ "showDoneTasks": "Pokaż ukończone zadania",
+ "sortAlphabetically": "Sortuj alfabetycznie",
+ "enablePriority": "Włącz filtrowanie według priorytetu",
+ "enablePercentDone": "Włącz filtrowanie według postępu",
+ "dueDateRange": "Zakres daty terminu",
+ "startDateRange": "Zakres daty rozpoczęcia",
+ "endDateRange": "Zakres daty zakończenia",
+ "reminderRange": "Zakres daty przypomnienia"
+ },
+ "create": {
+ "title": "Nowy filtr stały",
+ "description": "Filtr stały to wirtualna lista, która jest kalkulowana na podstawie zestawu filtrów przy każdym wejściu w nią. Po utworzeniu pojawi się w specjalnej sekcji.",
+ "action": "Utwórz nowy filtr stały"
+ },
+ "delete": {
+ "header": "Usuń ten filtr stały",
+ "text": "Czy na pewno chcesz usunąć ten filtr stały?",
+ "success": "Filtr został pomyślnie usunięty."
+ },
+ "edit": {
+ "title": "Edytuj ten filtr stały",
+ "success": "Filtr został pomyślnie zapisany."
+ }
+ },
+ "migrate": {
+ "title": "Migruj z innych usług do Vikunja",
+ "titleService": "Zaimportuj swoje dane z {name} do Vikunja",
+ "import": "Zaimportuj swoje dane do Vikunja",
+ "description": "Aby rozpocząć, kliknij logo jednej z usług zewnętrznych.",
+ "descriptionDo": "Vikunja zaimportuje wszystkie listy, zadania, notatki, przypomnienia i pliki, do których masz dostęp.",
+ "authorize": "Aby zezwolić Vikunja na dostęp do Twojego konta {name}, kliknij przycisk poniżej.",
+ "getStarted": "Rozpocznij",
+ "inProgress": "Trwa importowanie…",
+ "alreadyMigrated1": "Wygląda na to, że zaimportowałeś już swoje komponenty z {name} w dniu {date}.",
+ "alreadyMigrated2": "Ponowne importowanie jest możliwe, ale proces ten utworzy duplikaty. Jesteś pewny?",
+ "confirm": "Jestem pewien, proszę rozpocząć migrację!",
+ "importUpload": "Aby zaimportować dane z {name} do Vikunja, kliknij przycisk poniżej, aby wybrać plik.",
+ "upload": "Prześlij plik"
+ },
+ "label": {
+ "title": "Etykiety",
+ "manage": "Zarządzaj etykietami",
+ "description": "Kliknij etykietę, aby ją edytować. Możesz edytować wszystkie etykiety które utworzyłeś. Możesz edytować każdą etykietę powiązaną z zadaniem, do którego masz dostęp.",
+ "newCTA": "Obecnie nie masz żadnych etykiet.",
+ "search": "Wpisz, aby wyszukać etykietę…",
+ "create": {
+ "header": "Nowa etykieta",
+ "title": "Utwórz nową etykietę",
+ "titleRequired": "Proszę, podaj tytuł.",
+ "success": "Etykieta została pomyślnie utworzona."
+ },
+ "edit": {
+ "header": "Edytuj etykietę",
+ "forbidden": "Nie możesz edytować tej etykiety, ponieważ nie jesteś jej właścicielem.",
+ "success": "Etykieta została pomyślnie zaktualizowana."
+ },
+ "deleteSuccess": "Etykieta została pomyślnie usunięta.",
+ "attributes": {
+ "title": "Tytuł",
+ "titlePlaceholder": "Tu wpisz tytuł etykiety…",
+ "description": "Opis",
+ "descriptionPlaceholder": "Opis etykiety",
+ "color": "Kolor"
+ }
+ },
+ "sharing": {
+ "authenticating": "Uwierzytelnianie…",
+ "passwordRequired": "Ta współdzielona lista wymaga hasła. Wpisz je poniżej:",
+ "error": "Wystąpił błąd.",
+ "invalidPassword": "Hasło jest nieprawidłowe."
+ },
+ "navigation": {
+ "overview": "Przegląd",
+ "upcoming": "Nadchodzące",
+ "settings": "Ustawienia",
+ "imprint": "Odcisk",
+ "privacy": "Polityka prywatności"
+ },
+ "misc": {
+ "loading": "Ładowanie…",
+ "save": "Zapisz",
+ "delete": "Usuń",
+ "confirm": "Potwierdź",
+ "cancel": "Anuluj",
+ "refresh": "Odśwież",
+ "disable": "Wyłącz",
+ "copy": "Skopiuj do schowka",
+ "search": "Szukaj",
+ "searchPlaceholder": "Wpisz, aby wyszukać…",
+ "previous": "Poprzedni",
+ "next": "Następny",
+ "poweredBy": "Wspierane przez Vikunja",
+ "info": "Informacje",
+ "create": "Utwórz",
+ "doit": "Zrób to!",
+ "saving": "Zapisywanie…",
+ "saved": "Zapisano!",
+ "default": "Domyślnie",
+ "close": "Zamknij",
+ "download": "Pobierz",
+ "showMenu": "Pokaż menu",
+ "hideMenu": "Ukryj menu",
+ "forExample": "Na przykład:",
+ "welcomeBack": "Witaj ponownie!",
+ "custom": "Custom"
+ },
+ "input": {
+ "resetColor": "Resetuj kolor",
+ "datepicker": {
+ "today": "Dziś",
+ "tomorrow": "Jutro",
+ "nextMonday": "Następny poniedziałek",
+ "thisWeekend": "W ten weekend",
+ "laterThisWeek": "Pod koniec tego tygodnia",
+ "nextWeek": "W następnym tygodniu",
+ "chooseDate": "Wybierz datę"
+ },
+ "editor": {
+ "edit": "Edytuj",
+ "done": "Gotowe",
+ "heading1": "Nagłówek 1",
+ "heading2": "Nagłówek 2",
+ "heading3": "Nagłówek 3",
+ "headingSmaller": "Nagłówek mniejszy",
+ "headingBigger": "Nagłówek większy",
+ "bold": "Pogrubiony",
+ "italic": "Kursywa",
+ "strikethrough": "Przekreślony",
+ "code": "Kod",
+ "quote": "Cytat",
+ "unorderedList": "Lista nieuporządkowana",
+ "orderedList": "Lista uporządkowana",
+ "cleanBlock": "Wyczyść blok",
+ "link": "Link",
+ "image": "Obraz",
+ "table": "Tabela",
+ "horizontalRule": "Linia pozioma",
+ "sideBySide": "Obok siebie",
+ "guide": "Przewodnik"
+ },
+ "multiselect": {
+ "createPlaceholder": "Utwórz nowy",
+ "selectPlaceholder": "Kliknij lub naciśnij Enter, aby wybrać"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
+ }
+ },
+ "task": {
+ "task": "Zadanie",
+ "new": "Utwórz nowe zadanie",
+ "delete": "Usuń to zadanie",
+ "createSuccess": "Zadanie zostało pomyślnie utworzone.",
+ "addReminder": "Dodaj nowe przypomnienie…",
+ "doneSuccess": "Zadanie zostało pomyślnie oznaczone jako ukończone.",
+ "undoneSuccess": "Zadanie zostało pomyślnie otwarte ponownie.",
+ "openDetail": "Otwórz szczegółowy widok zadania",
+ "checklistTotal": "{checked} z {total} zadań",
+ "checklistAllDone": "{total} zadań",
+ "show": {
+ "titleCurrent": "Bieżące zadania",
+ "titleDates": "Zadania od {from} do {to}",
+ "noDates": "Pokaż zadania bez dat",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
+ "noTasks": "Brak zadań do wykonania – miłego dnia!"
+ },
+ "detail": {
+ "chooseDueDate": "Kliknij tutaj, aby ustawić termin",
+ "chooseStartDate": "Kliknij tutaj, aby ustawić datę rozpoczęcia",
+ "chooseEndDate": "Kliknij tutaj, aby ustawić datę zakończenia",
+ "move": "Przenieś zadanie na inną listę",
+ "done": "Oznacz zadanie jako ukończone!",
+ "undone": "Otwórz zadanie ponownie",
+ "created": "Utworzono {0} przez {1}",
+ "updated": "Zaktualizowano {0}",
+ "doneAt": "Ukończone {0}",
+ "updateSuccess": "Zadanie zostało pomyślnie zapisane.",
+ "deleteSuccess": "Zadanie zostało pomyślnie usunięte.",
+ "belongsToList": "To zadanie należy do listy '{list}'",
+ "due": "Termin {at}",
+ "closePopup": "Zamknij okno",
+ "delete": {
+ "header": "Usuń to zadanie",
+ "text1": "Czy na pewno chcesz usunąć to zadanie?",
+ "text2": "Spowoduje to również usunięcie wszystkich załączników, przypomnień i powiązań z tym zadaniem i nie będzie można tego cofnąć!"
+ },
+ "actions": {
+ "assign": "Przypisz do użytkownika",
+ "label": "Dodaj etykiety",
+ "priority": "Ustaw priorytet",
+ "dueDate": "Ustaw termin",
+ "startDate": "Ustaw datę rozpoczęcia",
+ "endDate": "Ustaw datę zakończenia",
+ "reminders": "Ustaw przypomnienia",
+ "repeatAfter": "Ustaw interwał powtarzania",
+ "percentDone": "Ustaw postęp",
+ "attachments": "Dodaj załączniki",
+ "relatedTasks": "Dodaj powiązanie",
+ "moveList": "Przenieś",
+ "color": "Ustaw kolor",
+ "delete": "Usuń",
+ "favorite": "Dodaj do ulubionych",
+ "unfavorite": "Usuń z ulubionych"
+ }
+ },
+ "attributes": {
+ "assignees": "Przypisani",
+ "color": "Kolor",
+ "created": "Utworzone",
+ "createdBy": "Utworzone przez",
+ "description": "Opis",
+ "done": "Ukończone",
+ "dueDate": "Termin",
+ "endDate": "Data zakończenia",
+ "labels": "Etykiety",
+ "percentDone": "Postęp",
+ "priority": "Priorytet",
+ "relatedTasks": "Powiązane zadania",
+ "reminders": "Przypomnienia",
+ "repeat": "Powtarzanie",
+ "startDate": "Data rozpoczęcia",
+ "title": "Tytuł",
+ "updated": "Zaktualizowano"
+ },
+ "subscription": {
+ "subscribedThroughParent": "Nie możesz zrezygnować z subskrypcji, ponieważ subskrybujesz {entity} za pośrednictwem {parent}.",
+ "subscribed": "Obecnie subskrybujesz {entity} i będziesz otrzymywać powiadomienia o zmianach.",
+ "notSubscribed": "Nie subskrybujesz {entity} i nie będziesz otrzymywać powiadomień o zmianach.",
+ "subscribe": "Subskrybuj",
+ "unsubscribe": "Anuluj subskrypcję",
+ "subscribeSuccess": "Od teraz subskrybujesz {entity}",
+ "unsubscribeSuccess": "Już nie subskrybujesz {entity}"
+ },
+ "attachment": {
+ "title": "Załączniki",
+ "createdBy": "utworzony {0} przez {1}",
+ "downloadTooltip": "Pobierz ten załącznik",
+ "upload": "Prześlij załącznik",
+ "drop": "Upuść pliki tutaj, aby przesłać",
+ "delete": "Usuń załącznik",
+ "deleteTooltip": "Usuń ten załącznik",
+ "deleteText1": "Czy na pewno chcesz usunąć załącznik {filename}?",
+ "deleteText2": "Tego nie da się cofnąć!",
+ "copyUrl": "Kopiuj URL",
+ "copyUrlTooltip": "Skopiuj adres URL tego załącznika do użycia w tekście"
+ },
+ "comment": {
+ "title": "Komentarze",
+ "loading": "Ładowanie komentarzy…",
+ "edited": "edytowane {date}",
+ "creating": "Tworzenie komentarza…",
+ "placeholder": "Dodaj swój komentarz…",
+ "comment": "Skomentuj",
+ "delete": "Usuń ten komentarz",
+ "deleteText1": "Czy na pewno chcesz usunąć ten komentarz?",
+ "deleteText2": "Tego nie da się cofnąć!",
+ "addedSuccess": "Komentarz został pomyślnie dodany."
+ },
+ "deferDueDate": {
+ "title": "Odroczenie terminu",
+ "1day": "1 dzień",
+ "3days": "3 dni",
+ "1week": "1 tydzień"
+ },
+ "description": {
+ "placeholder": "Kliknij tutaj, aby wprowadzić opis…",
+ "empty": "Nie ma jeszcze opisu."
+ },
+ "assignee": {
+ "placeholder": "Wpisz, aby przypisać użytkownika…",
+ "selectPlaceholder": "Przypisz tego użytkownika",
+ "assignSuccess": "Użytkownik został pomyślnie przypisany.",
+ "unassignSuccess": "Użytkownik został pomyślnie usunięty."
+ },
+ "label": {
+ "placeholder": "Wpisz, aby dodać nową etykietę…",
+ "createPlaceholder": "Dodaj jako nową etykietę",
+ "addSuccess": "Etykieta została pomyślnie dodana.",
+ "createSuccess": "Etykieta została pomyślnie utworzona.",
+ "removeSuccess": "Etykieta została pomyślnie usunięta.",
+ "addCreateSuccess": "Etykieta została pomyślnie utworzona i dodana."
+ },
+ "priority": {
+ "unset": "Nie ustawiony",
+ "low": "Niski",
+ "medium": "Średni",
+ "high": "Wysoki",
+ "urgent": "Pilny",
+ "doNow": "ZRÓB TO TERAZ"
+ },
+ "relation": {
+ "add": "Dodaj nowe powiązane zadanie",
+ "new": "Nowe powiązane zadanie",
+ "searchPlaceholder": "Wpisz, aby wyszukać zadanie, które chcesz dodać jako powiązane…",
+ "createPlaceholder": "Dodaj jako nowe powiązane zadanie",
+ "differentList": "To zadanie należy do innej listy.",
+ "differentNamespace": "To zadanie należy do innej sekcji.",
+ "noneYet": "Nie ma jeszcze powiązanych zadań.",
+ "delete": "Usuń powiązane zadanie",
+ "deleteText1": "Czy na pewno chcesz usunąć to powiązane zadanie?",
+ "deleteText2": "Tego nie da się cofnąć!",
+ "select": "Wybierz rodzaj powiązanego zadania",
+ "kinds": {
+ "subtask": "Podzadanie | Podzadania",
+ "parenttask": "Zadanie nadrzędne | Zadania nadrzędne",
+ "related": "Powiązane zadanie | Powiązane zadania",
+ "duplicateof": "Duplikat | Duplikaty",
+ "duplicates": "Duplikat | Duplikaty",
+ "blocking": "Blokujący | Blokujące",
+ "blocked": "Blokowany przez | Blokowane przez",
+ "precedes": "Poprzedzający | Poprzedzające",
+ "follows": "Wynikający z | Wynikające z",
+ "copiedfrom": "Skopiowany z | Skopiowane z",
+ "copiedto": "Skopiowany do | Skopiowane do"
+ }
+ },
+ "repeat": {
+ "everyDay": "Codziennie",
+ "everyWeek": "Co tydzień",
+ "everyMonth": "Co miesiąc",
+ "mode": "Tryb powtarzania",
+ "monthly": "Miesięczny",
+ "fromCurrentDate": "Od bieżącej daty",
+ "each": "Co",
+ "specifyAmount": "Określ ilość…",
+ "hours": "Godziny",
+ "days": "Dni",
+ "weeks": "Tygodnie",
+ "months": "Miesiące",
+ "years": "Lata"
+ },
+ "quickAddMagic": {
+ "hint": "Możesz użyć Quick Add Magic",
+ "what": "Co?",
+ "title": "Quick Add Magic",
+ "intro": "Podczas tworzenia zadania możesz użyć specjalnych słów kluczowych, aby bezpośrednio dodać atrybuty do nowo utworzonego zadania. Pozwala to na znacznie szybsze dodawanie powszechnie używanych atrybutów do zadań.",
+ "multiple": "Możesz użyć tego wielokrotnie.",
+ "label1": "Aby dodać etykietę, po prostu poprzedź nazwę etykiety przedrostkiem {prefix}.",
+ "label2": "Vikunja najpierw sprawdzi, czy etykieta już istnieje, a jeśli nie, utworzy ją.",
+ "label3": "Aby użyć spacji, po prostu podaj nazwę etykiety pomiędzy \".",
+ "label4": "Na przykład: {prefix}\"Etykieta ze spacjami\".",
+ "priority1": "Aby ustawić priorytet zadania, dodaj liczbę 1-5 poprzedzoną {prefix}.",
+ "priority2": "Im większa liczba, tym wyższy priorytet.",
+ "assignees": "Aby bezpośrednio przypisać zadanie użytkownikowi, dodaj nazwę użytkownika poprzedzoną przedrostkiem {prefix} do zadania.",
+ "list1": "Aby ustawić listę, do której ma zostać przypisane zadanie, wprowadź jego nazwę z prefiksem {prefix}.",
+ "list2": "Jeśli lista nie istnieje zostanie zwrócony błąd.",
+ "dateAndTime": "Data i godzina",
+ "date": "Dowolna data może być użyta jako termin wykonania nowego zadania. Możesz wykorzystać datę w dowolnym z podanych formatów:",
+ "dateWeekday": "dowolny dzień tygodnia spowoduje wybranie najbliższej daty przypadającej na podany dzień tygodnia",
+ "dateCurrentYear": "spowoduje wybranie bieżącego roku",
+ "dateNth": "spowoduje wybranie {day}-stego dnia bieżącego miesiąca",
+ "dateTime": "Połącz dowolny format daty z \"{time}\" (lub {timePM}), aby ustawić godzinę.",
+ "repeats": "Powtarzające się zadania",
+ "repeatsDescription": "Aby ustawić zadanie jako powtarzające się w odstępie czasu, po prostu dodaj '{suffix}' do tekstu zadania. Ilość musi być liczbą, ale może zostać pominięta, aby użyć tylko typu (patrz przykłady)."
+ }
+ },
+ "team": {
+ "title": "Zespoły",
+ "noTeams": "Obecnie nie należysz do żadnego zespołu.",
+ "create": {
+ "title": "Utwórz nowy zespół",
+ "success": "Zespół został pomyślnie utworzony."
+ },
+ "edit": {
+ "title": "Edytuj zespół \"{team}\"",
+ "members": "Członkowie zespołu",
+ "search": "Wpisz, aby wyszukać użytkownika…",
+ "addUser": "Dodaj do zespołu",
+ "makeMember": "Uczyń członkiem",
+ "makeAdmin": "Uczyń administratorem",
+ "success": "Zespół został pomyślnie zaktualizowany.",
+ "userAddedSuccess": "Członek zespołu został pomyślnie dodany.",
+ "madeMember": "Użytkownik został pomyślnie mianowany członkiem zespołu.",
+ "madeAdmin": "Członek zespołu został pomyślnie mianowany administratorem.",
+ "delete": {
+ "header": "Usuń zespół",
+ "text1": "Czy na pewno chcesz usunąć ten zespół i wszystkich jego członków?",
+ "text2": "Wszyscy członkowie zespołu stracą dostęp do list i sekcji udostępnionych temu zespołowi. Tego NIE DA SIĘ COFNĄĆ!",
+ "success": "Zespół został pomyślnie usunięty."
+ },
+ "deleteUser": {
+ "header": "Usuń użytkownika z zespołu",
+ "text1": "Czy na pewno chcesz usunąć tego użytkownika z zespołu?",
+ "text2": "Utraci on dostęp do wszystkich list i sekcji, do których ma dostęp ten zespół. Tego NIE DA SIĘ COFNĄĆ!",
+ "success": "Użytkownik został pomyślnie usunięty z zespołu."
+ }
+ },
+ "attributes": {
+ "name": "Nazwa zespołu",
+ "namePlaceholder": "Tu wpisz nazwę zespołu…",
+ "nameRequired": "Proszę, podaj nazwę.",
+ "description": "Opis",
+ "descriptionPlaceholder": "Tu wpisz opis drużyny…",
+ "admin": "Administrator",
+ "member": "Członek"
+ }
+ },
+ "keyboardShortcuts": {
+ "title": "Skróty klawiszowe",
+ "general": "Ogólne",
+ "allPages": "Te skróty działają na wszystkich stronach.",
+ "currentPageOnly": "Te skróty działają tylko na bieżącej stronie.",
+ "somePagesOnly": "Te skróty działają tylko na niektórych stronach.",
+ "toggleMenu": "Przełącz menu",
+ "quickSearch": "Otwórz pasek wyszukiwania/szybkiej akcji",
+ "then": "następnie",
+ "task": {
+ "title": "Widok zadań",
+ "done": "Oznacz zadanie jako wykonane / niewykonane",
+ "assign": "Przypisz to zadanie do użytkownika",
+ "labels": "Dodaj etykiety do tego zadania",
+ "dueDate": "Zmień termin wykonania tego zadania",
+ "attachment": "Dodaj załącznik do tego zadania",
+ "related": "Zmodyfikuj zadania powiązane z tym zadaniem",
+ "color": "Zmień kolor tego zadania",
+ "move": "Przenieś to zadanie do innej listy"
+ },
+ "list": {
+ "title": "Widoki listy",
+ "switchToListView": "Przełącz na widok listy",
+ "switchToGanttView": "Przełącz na widok Gantta",
+ "switchToKanbanView": "Przełącz na widok Kanban",
+ "switchToTableView": "Przełącz na widok tabeli"
+ },
+ "navigation": {
+ "title": "Nawigacja",
+ "overview": "Przejdź do przeglądu",
+ "upcoming": "Przejdź do nadchodzących zadań",
+ "namespaces": "Przejdź do sekcji i list",
+ "labels": "Przejdź do etykiet",
+ "teams": "Przejdź do zespołów"
+ }
+ },
+ "update": {
+ "available": "Dostępna jest aktualizacja Vikunji!",
+ "do": "Aktualizuj teraz"
+ },
+ "menu": {
+ "edit": "Edytuj",
+ "archive": "Archiwizuj",
+ "duplicate": "Duplikuj",
+ "delete": "Usuń",
+ "unarchive": "Cofnij archiwizację",
+ "setBackground": "Ustaw tło",
+ "share": "Udostępnij",
+ "newList": "Nowa lista"
+ },
+ "apiConfig": {
+ "url": "URL Vikunji",
+ "urlPlaceholder": "np. https://localhost:3456",
+ "change": "zmień",
+ "use": "Użyj instalacji Vikunji z {0}",
+ "error": "Nie można znaleźć lub użyć instalacji Vikunji z \"{domain}\". Wypróbuj inny adres URL.",
+ "success": "Używasz instalacji Vikunji z \"{domain}\".",
+ "urlRequired": "URL jest wymagany."
+ },
+ "loadingError": {
+ "failed": "Ładowanie nie powiodło się, {0}. Jeśli błąd będzie się powtarzał, {1}.",
+ "tryAgain": "spróbuj ponownie",
+ "contact": "skontaktuj się z nami"
+ },
+ "notification": {
+ "title": "Powiadomienia",
+ "none": "Nie masz żadnych powiadomień. Miłego dnia!",
+ "explainer": "Powiadomienia pojawią się tutaj, gdy będą miały miejsce akcje na sekcjach, listach lub zadaniach, które subskrybujesz."
+ },
+ "quickActions": {
+ "commands": "Polecenia",
+ "placeholder": "Wpisz polecenie lub wyszukiwaną frazę…",
+ "hint": "Możesz użyć {list}, aby ograniczyć wyszukiwanie do listy. Połącz {list} lub {label} (etykiety) z wyszukiwaną frazą, aby wyszukać zadanie z tymi etykietami lub na tej liście. Użyj {assignee}, aby wyszukiwać tylko zespoły.",
+ "tasks": "Zadania",
+ "lists": "Listy",
+ "teams": "Zespoły",
+ "newList": "Wpisz tytuł nowej listy…",
+ "newTask": "Wpisz tytuł nowego zadania…",
+ "newNamespace": "Wpisz tytuł nowej sekcji…",
+ "newTeam": "Wpisz nazwę nowego zespołu…",
+ "createTask": "Utwórz zadanie na bieżącej liście ({title})",
+ "createList": "Utwórz listę w bieżącej sekcji ({title})",
+ "cmds": {
+ "newTask": "Nowe zadanie",
+ "newList": "Nowa lista",
+ "newNamespace": "Nowa sekcja",
+ "newTeam": "Nowy zespół"
+ }
+ },
+ "date": {
+ "locale": "pl",
+ "altFormatLong": "j M Y H:i",
+ "altFormatShort": "j M Y"
+ },
+ "error": {
+ "error": "Błąd",
+ "success": "Sukces",
+ "0001": "Nie jesteś upoważniony, aby to zrobić.",
+ "1001": "Użytkownik o tej nazwie już istnieje.",
+ "1002": "Użytkownik z takim adresem e-mail już istnieje.",
+ "1004": "Nie podano nazwy użytkownika ani hasła.",
+ "1005": "Użytkownik nie istnieje.",
+ "1006": "Nie można uzyskać ID użytkownika.",
+ "1008": "Nie podano tokena resetowania hasła.",
+ "1009": "Nieprawidłowy token resetowania hasła.",
+ "1010": "Nieprawidłowy token potwierdzenia adresu e-mail.",
+ "1011": "Błędna nazwa użytkownika lub hasło.",
+ "1012": "Adres e-mail użytkownika nie został potwierdzony.",
+ "1013": "Nowe hasło jest puste.",
+ "1014": "Stare hasło jest puste.",
+ "1015": "TOTP jest już włączony dla tego użytkownika.",
+ "1016": "TOTP nie jest włączony dla tego użytkownika.",
+ "1017": "Kod autoryzacyjny uwierzytelniania dwuskładnikowego (TOTP) jest nieprawidłowy.",
+ "1018": "Ustawienie typu awatara użytkownika jest nieprawidłowe.",
+ "2001": "ID nie może być puste lub równe 0.",
+ "2002": "Niektóre dane żądania były nieprawidłowe.",
+ "3001": "Lista nie istnieje.",
+ "3004": "Aby wykonać tę akcję, musisz mieć uprawnienia do odczytu tej listy.",
+ "3005": "Tytuł listy nie może być pusty.",
+ "3006": "Współdzielona lista nie istnieje.",
+ "3007": "Lista o tym identyfikatorze już istnieje.",
+ "3008": "Lista jest zarchiwizowana i dlatego jest dostępna w trybie tylko do odczytu. Dotyczy to również wszystkich zadań powiązanych z tą listą.",
+ "4001": "Tekst zadania listy nie może być pusty.",
+ "4002": "Zadanie listy nie istnieje.",
+ "4003": "Wszystkie zadania edycji zbiorczej muszą należeć do tej samej listy.",
+ "4004": "Potrzebujesz co najmniej jednego zadania do edycji zbiorczej zadań.",
+ "4005": "Nie masz uprawnień, aby zobaczyć to zadanie.",
+ "4006": "Zadanie nie może zostać ustawione jako nadrzędne dla samego siebie.",
+ "4007": "Nie możesz utworzyć powiązanego zadania z nieprawidłowym rodzajem relacji.",
+ "4008": "Nie możesz utworzyć powiązanego zadania, które już istnieje.",
+ "4009": "Powiązane zadanie nie istnieje.",
+ "4010": "Nie można powiązać zadania z nim samym.",
+ "4011": "Załącznik do zadania nie istnieje.",
+ "4012": "Załącznik do zadania jest zbyt duży.",
+ "4013": "Parametr sortowania zadań jest nieprawidłowy.",
+ "4014": "Kolejność sortowania zadań jest nieprawidłowa.",
+ "4015": "Komentarz do zadania nie istnieje.",
+ "4016": "Nieprawidłowe pole zadania.",
+ "4017": "Nieprawidłowe porównanie filtra zadań.",
+ "4018": "Nieprawidłowe połączenie filtra zadań.",
+ "4019": "Nieprawidłowa wartość filtra zadań.",
+ "5001": "Sekcja nie istnieje.",
+ "5003": "Nie masz dostępu do określonej sekcji.",
+ "5006": "Nazwa sekcji nie może być pusta.",
+ "5009": "Aby wykonać tę akcję, musisz mieć uprawnienia do odczytu sekcji.",
+ "5010": "Ten zespół nie ma dostępu do tej sekcji.",
+ "5011": "Ten użytkownik ma już dostęp do tej sekcji.",
+ "5012": "Sekcja jest zarchiwizowana, dlatego może być dostępna tylko do odczytu.",
+ "6001": "Nazwa zespołu nie może być pusta.",
+ "6002": "Zespół nie istnieje.",
+ "6004": "Zespół ma już dostęp do tej sekcji lub listy.",
+ "6005": "Użytkownik jest już członkiem tego zespołu.",
+ "6006": "Nie można usunąć ostatniego członka zespołu.",
+ "6007": "Zespół nie ma dostępu do listy, aby wykonać tę akcję.",
+ "7002": "Użytkownik ma już dostęp do tej listy.",
+ "7003": "Nie masz dostępu do tej listy.",
+ "8001": "Ta etykieta już istnieje w tym zadaniu.",
+ "8002": "Etykieta nie istnieje.",
+ "8003": "Nie masz dostępu do tej etykiety.",
+ "9001": "Nieprawidłowe uprawnienia.",
+ "10001": "Zasobnik nie istnieje.",
+ "10002": "Zasobnik nie należy do tej listy.",
+ "10003": "Nie możesz usunąć ostatniego zasobnika z listy.",
+ "10004": "Nie możesz dodać zadania do tego zasobnika, ponieważ przekroczyło już limit zadań, które może pomieścić.",
+ "10005": "Na liście może znajdować się tylko jeden zasobnik ukończonych zadań.",
+ "11001": "Filtr stały nie istnieje.",
+ "11002": "Filtry stałe nie są dostępne dla udostępnionych linków.",
+ "12001": "Typ subskrypcji jest nieprawidłowy.",
+ "12002": "Subskrybujesz już tę jednostkę lub jej jednostkę nadrzędną.",
+ "13001": "Ten udostępniony link wymaga hasła do uwierzytelnienia, ale nie został podany.",
+ "13002": "Podane hasło udostępnionego linku było nieprawidłowe."
+ },
+ "about": {
+ "title": "O aplikacji",
+ "frontendVersion": "Wersja frontendu: {version}",
+ "apiVersion": "Wersja API: {version}"
+ }
+}
diff --git a/src/i18n/lang/pt-BR.json b/src/i18n/lang/pt-BR.json
index 1f03d175b..86f71fc1e 100644
--- a/src/i18n/lang/pt-BR.json
+++ b/src/i18n/lang/pt-BR.json
@@ -31,10 +31,9 @@
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
+ "email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password",
@@ -45,12 +44,20 @@
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
- "register": "Register",
+ "createAccount": "Create account",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Logout",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Settings",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
- "defaultList": "Default List"
+ "defaultList": "Default List",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Two Factor Authentication",
@@ -327,6 +335,7 @@
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Reset Color",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
+ "percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
"reminders": "Reminders",
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +855,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/pt-PT.json b/src/i18n/lang/pt-PT.json
index 1f03d175b..c48b46af0 100644
--- a/src/i18n/lang/pt-PT.json
+++ b/src/i18n/lang/pt-PT.json
@@ -1,940 +1,1009 @@
{
"home": {
- "welcomeNight": "Good Night {username}",
- "welcomeMorning": "Good Morning {username}",
- "welcomeDay": "Hi {username}",
- "welcomeEvening": "Good Evening {username}",
- "lastViewed": "Last viewed",
+ "welcomeNight": "Boa Noite {username}",
+ "welcomeMorning": "Bom Dia {username}",
+ "welcomeDay": "Olá {username}",
+ "welcomeEvening": "Boa Tarde {username}",
+ "lastViewed": "Visto recentemente",
"list": {
- "newText": "You can create a new list for your new tasks:",
- "new": "New list",
- "importText": "Or import your lists and tasks from other services into Vikunja:",
- "import": "Import your data into Vikunja"
+ "newText": "Podes criar uma nova lista para as tuas novas tarefas:",
+ "new": "Nova lista",
+ "importText": "Ou importar as tuas listas e tarefas de outros serviços no Vikunja:",
+ "import": "Importa os teus dados para o Vikunja"
}
},
"404": {
- "title": "Not found",
- "text": "The page you requested does not exist."
+ "title": "Não encontrado",
+ "text": "A página solicitada não existe."
},
"ready": {
- "loading": "Vikunja is loading…",
- "errorOccured": "An error occured:",
- "checkApiUrl": "Please check if the api url is correct.",
- "noApiUrlConfigured": "No API url was configured. Please set one below:"
+ "loading": "Vikunja está a carregar…",
+ "errorOccured": "Ocorreu um erro:",
+ "checkApiUrl": "Por favor, verifica se o url da api está correto.",
+ "noApiUrlConfigured": "Nenhum URL de API foi configurado. Por favor, define um abaixo:"
},
"offline": {
- "title": "You are offline.",
- "text": "Please check your network connection and try again."
+ "title": "Estás offline.",
+ "text": "Por favor, verifica a tua ligação à internet e tenta de novo."
},
"user": {
"auth": {
- "username": "Username",
- "usernameEmail": "Username Or Email Address",
- "usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
- "emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
- "password": "Password",
- "passwordRepeat": "Retype your password",
- "passwordPlaceholder": "e.g. •••••••••••",
- "forgotPassword": "Forgot your password?",
- "resetPassword": "Reset your password",
- "resetPasswordAction": "Send me a password reset link",
- "resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
- "passwordsDontMatch": "Passwords don't match",
- "confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
- "totpTitle": "Two Factor Authentication Code",
- "totpPlaceholder": "e.g. 123456",
- "login": "Login",
- "register": "Register",
- "loginWith": "Log in with {provider}",
- "authenticating": "Authenticating…",
- "openIdStateError": "State does not match, refusing to continue!",
- "openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "username": "Nome de Utilizador",
+ "usernameEmail": "Nome de Utilizador ou E-mail",
+ "usernamePlaceholder": "ex: frederico",
+ "email": "Endereço de e-mail",
+ "emailPlaceholder": "ex. frederico{'@'}vikunja.io",
+ "password": "Palavra-Passe",
+ "passwordPlaceholder": "ex.: •••••••••••••",
+ "forgotPassword": "Esqueceste a tua palavra-passe?",
+ "resetPassword": "Redefine a tua palavra-passe",
+ "resetPasswordAction": "Enviem-me um link para redefinição da palavra-passe",
+ "resetPasswordSuccess": "Verifique a tua caixa de entrada! Deves ter um e-mail com instruções sobre como redefinir a tua palavra-passe.",
+ "passwordsDontMatch": "As palavras-passe não coincidem",
+ "confirmEmailSuccess": "Confirmaste com sucesso o teu e-mail! Podes agora iniciar sessão.",
+ "totpTitle": "Código de autenticação de dois fatores",
+ "totpPlaceholder": "ex.: 123456",
+ "login": "Iniciar Sessão",
+ "createAccount": "Criar conta",
+ "loginWith": "Iniciar sessão com {provider}",
+ "authenticating": "A autenticar…",
+ "openIdStateError": "O estado não coincide, a recusar continuar!",
+ "openIdGeneralError": "Ocorreu um erro na autenticação perante a entidade externa.",
+ "logout": "Terminar Sessão",
+ "emailInvalid": "Por favor, insire um endereço de e-mail válido.",
+ "usernameRequired": "Por favor, fornece um nome de utilizador.",
+ "passwordRequired": "Por favor, fornece uma palavra-passe.",
+ "showPassword": "Mostrar a palavra-passe",
+ "hidePassword": "Esconder a palavra-passe",
+ "noAccountYet": "Ainda não tens uma conta?",
+ "alreadyHaveAnAccount": "Já tens uma conta?",
+ "remember": "Permanecer autenticado"
},
"settings": {
- "title": "Settings",
- "newPasswordTitle": "Update Your Password",
- "newPassword": "New Password",
- "newPasswordConfirm": "New Password Confirmation",
- "currentPassword": "Current Password",
- "currentPasswordPlaceholder": "Your current password",
- "passwordsDontMatch": "The new password and its confirmation don't match.",
- "passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
- "updateEmailNew": "New Email Address",
- "updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
+ "title": "Definições",
+ "newPasswordTitle": "Atualizar Palavra-Passe",
+ "newPassword": "Nova Palavra-Passe",
+ "newPasswordConfirm": "Confirmação de Nova Password",
+ "currentPassword": "Palavra-Passe Atual",
+ "currentPasswordPlaceholder": "A tua palavra-passe atual",
+ "passwordsDontMatch": "A nova palavra-passe e a sua confirmação não correspondem.",
+ "passwordUpdateSuccess": "A sua palavra-passe foi atualizada com sucesso.",
+ "updateEmailTitle": "Atualiza o Teu Endereço de E-mail",
+ "updateEmailNew": "Novo Endereço de E-mail",
+ "updateEmailSuccess": "E-mail atualizado com sucesso. Foi enviado um link de confirmação.",
"general": {
- "title": "General Settings",
- "name": "Name",
- "newName": "The new Name",
- "savedSuccess": "The settings were successfully updated.",
- "emailReminders": "Send me reminders for tasks via Email",
- "overdueReminders": "Send me reminders for overdue undone tasks via email each morning",
- "discoverableByName": "Let other users find me when they search for my name",
- "discoverableByEmail": "Let other users find me when they search for my full email",
- "playSoundWhenDone": "Play a sound when marking tasks as done",
- "weekStart": "Week starts on",
- "weekStartSunday": "Sunday",
- "weekStartMonday": "Monday",
- "language": "Language",
- "defaultList": "Default List"
+ "title": "Definições Gerais",
+ "name": "Nome",
+ "newName": "O novo Nome",
+ "savedSuccess": "As definições foram atualizadas com sucesso.",
+ "emailReminders": "Enviar-me notificações por e-mail",
+ "overdueReminders": "Enviar-me lembretes para tarefas pendentes em atraso, por e-mail, todas as manhãs",
+ "discoverableByName": "Permitir que outros utilizadores me encontrem quando pesquisam o meu nome",
+ "discoverableByEmail": "Permitir que outros utilizadores me encontrem quando pesquisam o meu e-mail completo",
+ "playSoundWhenDone": "Tocar um som quando marcar tarefas como concluídas",
+ "weekStart": "A semana começa com",
+ "weekStartSunday": "Domingo",
+ "weekStartMonday": "Segunda-Feira",
+ "language": "Idioma",
+ "defaultList": "Lista padrão",
+ "timezone": "Fuso Horário"
},
"totp": {
- "title": "Two Factor Authentication",
- "enroll": "Enroll",
- "finishSetupPart1": "To finish your setup, use this secret in your totp app (Google Authenticator or similar):",
- "finishSetupPart2": "After that, enter a code from your app below.",
- "scanQR": "Alternatively you can scan this QR code:",
- "passcode": "Passcode",
- "passcodePlaceholder": "A code generated by your totp application",
- "setupSuccess": "You've sucessfully set up two factor authentication!",
- "enterPassword": "Please Enter Your Password",
- "disable": "Disable two factor authentication",
- "confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
- "disableSuccess": "Two factor authentication was sucessfully disabled."
+ "title": "Autenticação de Dois Fatores",
+ "enroll": "Registar",
+ "finishSetupPart1": "Para concluir a tua configuração, utiliza este código no teu aplicativo totp (Google Authenticator ou similar):",
+ "finishSetupPart2": "Depois disso, insere um código do teu aplicativo abaixo.",
+ "scanQR": "Como alternativa, podes digitalizar este código QR:",
+ "passcode": "Código",
+ "passcodePlaceholder": "Um código gerado pelo teu aplicativo totp",
+ "setupSuccess": "Configuraste com sucesso a autenticação de dois fatores!",
+ "enterPassword": "Por favor, insere a tua palavra-passe",
+ "disable": "Desativar a autenticação de dois fatores",
+ "confirmSuccess": "Confirmaste com sucesso a tua configuração totp e podes utilizá-la de agora em diante!",
+ "disableSuccess": "A autenticação de dois fatores foi desativada com sucesso."
},
"caldav": {
- "title": "Caldav",
- "howTo": "You can connect Vikunja to caldav clients to view and manage all tasks from different clients. Enter this url into your client:",
- "more": "More information about caldav in Vikunja"
+ "title": "CalDAV",
+ "howTo": "Podes conectar o Vikunja a clientes CalDAV para visualizar e gerenciar todas as tarefas através de diferentes aplicativos. Coloca este url no teu aplicativo:",
+ "more": "Mais informações sobre o CalDAV na Vikunja"
},
"avatar": {
"title": "Avatar",
- "initials": "Initials",
+ "initials": "Iniciais",
"gravatar": "Gravatar",
- "marble": "Marble",
- "upload": "Upload",
- "uploadAvatar": "Upload Avatar",
- "statusUpdateSuccess": "Avatar status was updated successfully!",
- "setSuccess": "The avatar has been set successfully!"
+ "marble": "Berlinde",
+ "upload": "Carregar",
+ "uploadAvatar": "Carregar Avatar",
+ "statusUpdateSuccess": "Estado do avatar foi atualizado com sucesso!",
+ "setSuccess": "O avatar foi configurado com sucesso!"
},
"quickAddMagic": {
- "title": "Quick Add Magic Mode",
- "disabled": "Disabled",
+ "title": "Modo de Introdução Mágica Rápida",
+ "disabled": "Desativado",
"todoist": "Todoist",
"vikunja": "Vikunja"
},
"appearance": {
- "title": "Color Scheme",
- "setSuccess": "Saved change of color scheme to {colorScheme}",
+ "title": "Esquema de cores",
+ "setSuccess": "Salva a alteração do esquema de cores para {colorScheme}",
"colorScheme": {
- "light": "Light",
- "system": "System",
- "dark": "Dark"
+ "light": "Claro",
+ "system": "Sistema",
+ "dark": "Escuro"
}
}
},
"deletion": {
- "title": "Delete your Vikunja Account",
- "text1": "The deletion of your account is permanent and cannot be undone. We will delete all your namespaces, lists, tasks and everything associated with it.",
- "text2": "To proceed, please enter your password. You will receive an email with further instructions.",
- "confirm": "Delete my account",
- "requestSuccess": "The request was successful. You'll receive an email with further instructions.",
- "passwordRequired": "Please enter your password.",
- "confirmSuccess": "You've successfully confirmed the deletion of your account. We will delete your account in three days.",
- "scheduled": "We will delete your Vikunja account at {date} ({dateSince}).",
- "scheduledCancel": "To cancel the deletion of your account, click here.",
- "scheduledCancelText": "To cancel the deletion of your account, please enter your password below:",
- "scheduledCancelConfirm": "Cancel the deletion of my account",
- "scheduledCancelSuccess": "We will not delete your account."
+ "title": "Eliminar a tua conta Vikunja",
+ "text1": "A eliminação da tua conta é permanente e não pode ser revertida. Vão ser eliminados todos os teus espaços, listas, tarefas e tudo o que lhes está associado.",
+ "text2": "Para prosseguires, introduz por favor a tua palavra-passe. Receberás um e-mail com mais instruções.",
+ "confirm": "Eliminar a minha conta",
+ "requestSuccess": "A solicitação foi bem sucedida. Receberás um e-mail com mais instruções.",
+ "passwordRequired": "Por favor, insere a tua palavra-passe.",
+ "confirmSuccess": "Confirmaste com sucesso a eliminação da tua conta. Vamos eliminar a tua conta dentro de três dias.",
+ "scheduled": "Vamos eliminar a tua conta no Vikunja em {date} ({dateSince}).",
+ "scheduledCancel": "Para cancelar a eliminação da tua conta, clica aqui.",
+ "scheduledCancelText": "Para cancelares a eliminação da tua conta, introduz a tua palavra-passe abaixo:",
+ "scheduledCancelConfirm": "Cancelar a eliminação da minha conta",
+ "scheduledCancelSuccess": "Não vamos apagar a tua conta."
},
"export": {
- "title": "Export your Vikunja data",
- "description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
- "descriptionPasswordRequired": "Please enter your password to proceed:",
- "request": "Request a copy of my Vikunja Data",
- "success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
- "downloadTitle": "Download your exported Vikunja data"
+ "title": "Exportar os teus dados do Vikunja",
+ "description": "Podes solicitar uma cópia de todos os teus dados do Vikunja. Isso inclui Espaços, Listas, Tarefas e tudo o que lhes está associado. Podes importar esses dados em qualquer instância do Vikunja através da função de migração.",
+ "descriptionPasswordRequired": "Por favor, introduz a tua palavra-passe para continuar:",
+ "request": "Solicitar uma cópia dos meus dados do Vikunja",
+ "success": "Solicitaste com sucesso os teus dados do Vikunja! Enviaremos um e-mail assim que estiverem prontos para download.",
+ "downloadTitle": "Transferir os teus dados exportados"
}
},
"list": {
- "archived": "This list is archived. It is not possible to create new or edit tasks for it.",
- "title": "List Title",
- "color": "Color",
- "lists": "Lists",
- "search": "Type to search for a list…",
- "searchSelect": "Click or press enter to select this list",
- "shared": "Shared Lists",
+ "archived": "Esta lista está arquivada. Não é possível editar ou criar novas tarefas para ela.",
+ "title": "Título da Lista",
+ "color": "Cor",
+ "lists": "Listas",
+ "search": "Escreve para pesquisar por uma lista…",
+ "searchSelect": "Clica ou pressiona Enter para selecionar esta lista",
+ "shared": "Listas Partilhadas",
"create": {
- "header": "New list",
- "titlePlaceholder": "The list's title goes here…",
- "addTitleRequired": "Please specify a title.",
- "createdSuccess": "The list was successfully created.",
- "addListRequired": "Please specify a list or set a default list in the settings."
+ "header": "Nova lista",
+ "titlePlaceholder": "O título da lista será aqui…",
+ "addTitleRequired": "Por favor, especifica um título.",
+ "createdSuccess": "A lista foi criada com sucesso.",
+ "addListRequired": "Por favor, especifica uma lista ou define uma lista padrão nas configurações."
},
"archive": {
- "title": "Archive \"{list}\"",
- "archive": "Archive this list",
- "unarchive": "Un-Archive this list",
- "unarchiveText": "You will be able to create new tasks or edit it.",
- "archiveText": "You won't be able to edit this list or create new tasks until you un-archive it.",
- "success": "The list was successfully archived."
+ "title": "Arquivar \"{list}\"",
+ "archive": "Arquivar esta lista",
+ "unarchive": "Desarquivar esta lista",
+ "unarchiveText": "Vais ser capaz de criar novas tarefas ou editá-las.",
+ "archiveText": "Não poderás editar esta lista ou criar novas tarefas até a desarquivares.",
+ "success": "Esta lista foi arquivada com sucesso."
},
"background": {
- "title": "Set list background",
- "remove": "Remove Background",
- "upload": "Choose a background from your pc",
- "searchPlaceholder": "Search for a background…",
- "poweredByUnsplash": "Powered by Unsplash",
- "loadMore": "Load more photos",
- "success": "The background has been set successfully!",
- "removeSuccess": "The background has been removed successfully!"
+ "title": "Definir o fundo da lista",
+ "remove": "Remover Fundo",
+ "upload": "Escolhe uma imagem de fundo do teu pc",
+ "searchPlaceholder": "Procura por uma imagem de fundo…",
+ "poweredByUnsplash": "Fornecido por Unsplash",
+ "loadMore": "Carregar mais fotos",
+ "success": "A imagem de fundo foi definida com sucesso!",
+ "removeSuccess": "A imagem de fundo foi removida com sucesso!"
},
"delete": {
- "title": "Delete \"{list}\"",
- "header": "Delete this list",
- "text1": "Are you sure you want to delete this list and all of its contents?",
- "text2": "This includes all tasks and CANNOT BE UNDONE!",
- "success": "The list was successfully deleted."
+ "title": "Eliminar \"{list}\"",
+ "header": "Eliminar esta lista",
+ "text1": "Tens a certeza que prentedes apagar esta lista e todo o seu conteúdo?",
+ "text2": "Isto inclui todas as tarefas e NÃO PODE SER REVERTIDO!",
+ "success": "A lista foi eliminada com sucesso."
},
"duplicate": {
- "title": "Duplicate this list",
- "label": "Duplicate",
- "text": "Select a namespace which should hold the duplicated list:",
- "success": "The list was successfully duplicated."
+ "title": "Duplicar esta lista",
+ "label": "Duplicar",
+ "text": "Seleccionar um espaço que deve conter a lista duplicada:",
+ "success": "A lista foi duplicada com sucesso."
},
"edit": {
- "header": "Edit This List",
- "title": "Edit \"{list}\"",
- "titlePlaceholder": "The list title goes here…",
- "identifierTooltip": "The list identifier can be used to uniquely identify a task across lists. You can set it to empty to disable it.",
- "identifier": "List Identifier",
- "identifierPlaceholder": "The list identifier goes here…",
- "description": "Description",
- "descriptionPlaceholder": "The lists description goes here…",
- "color": "Color",
- "success": "The list was successfully updated."
+ "header": "Editar Esta Lista",
+ "title": "Editar \"{list}\"",
+ "titlePlaceholder": "O título da lista será aqui…",
+ "identifierTooltip": "O identificador da lista pode ser utilizado para identificar de forma única uma tarefa entre diferentes listas. Pode defini-lo como vazio para o desactivar.",
+ "identifier": "Identificador da Lista",
+ "identifierPlaceholder": "O identificador da lista será aqui…",
+ "description": "Descrição",
+ "descriptionPlaceholder": "A descrição da lista será aqui…",
+ "color": "Cor",
+ "success": "A lista foi atualizada com sucesso."
},
"share": {
- "header": "Share this list",
- "title": "Share \"{list}\"",
- "share": "Share",
+ "header": "Partilhar esta lista",
+ "title": "Partilhar \"{list}\"",
+ "share": "Partilhar",
"links": {
- "title": "Share Links",
- "what": "What is a share link?",
- "explanation": "Share Links allow you to easily share a list with other users who don't have an account on Vikunja.",
- "create": "Create a new link share",
- "name": "Name (optional)",
- "namePlaceholder": "e.g. Lorem Ipsum",
- "nameExplanation": "All actions done by this link share will show up with the name.",
- "password": "Password (optional)",
- "passwordExplanation": "When authenticating, the user will be required to enter this password.",
- "noName": "No name set",
- "remove": "Remove a link share",
- "removeText": "Are you sure you want to remove this link share? It will no longer be possible to access this list with this link share. This cannot be undone!",
- "createSuccess": "The link share was successfully created.",
- "deleteSuccess": "The link share was successfully deleted"
+ "title": "Links Partilhados",
+ "what": "Como funcionam os links partilhados?",
+ "explanation": "Links Partilhados permite-lhe partilhar facilmente uma lista com outros utilizadores que não têm uma conta no Vikunja.",
+ "create": "Criar um novo link partilhado",
+ "name": "Nome (opcional)",
+ "namePlaceholder": "ex.: Lorem Ipsum",
+ "nameExplanation": "Todas as ações realizadas através deste link partilhado vão aparecer com este nome.",
+ "password": "Palavra-passe (opcional)",
+ "passwordExplanation": "Ao autenticar-se, ao utilizador vai ser requerido introduzir esta palavra-passe.",
+ "noName": "Nenhum nome definido",
+ "remove": "Remover um link partilhado",
+ "removeText": "Tens a certeza que queres remover este link partilhado? Não será mais possível aceder a esta lista utilizando este link partilhado. Isto não pode ser revertido!",
+ "createSuccess": "O link partilhado foi criado com sucesso.",
+ "deleteSuccess": "O link partilhado foi eliminado com sucesso"
},
"userTeam": {
- "typeUser": "user | users",
- "typeTeam": "team | teams",
- "shared": "Shared with these {type}",
- "you": "You",
- "notShared": "Not shared with any {type} yet.",
- "removeHeader": "Remove a {type} from the {sharable}",
- "removeText": "Are you sure you want to remove this {sharable} from the {type}? This cannot be undone!",
- "removeSuccess": "The {sharable} was successfully removed from the {type}.",
- "addedSuccess": "The {type} was successfully added.",
- "updatedSuccess": "The {type} was successfully added."
+ "typeUser": "utilizador | utilizadores",
+ "typeTeam": "equipa | equipas",
+ "shared": "Partilhado com seguintes {type}",
+ "you": "Tu",
+ "notShared": "Ainda não partilhado com {type}.",
+ "removeHeader": "Remover {type} de {sharable}",
+ "removeText": "Tens a certeza que prentendes remover {sharable} de {type}? Isto não pode ser revertido!",
+ "removeSuccess": "{sharable} foi removido com sucesso de {type}.",
+ "addedSuccess": "{type} foi adicionado com sucesso.",
+ "updatedSuccess": "{type} foi adicionado com sucesso."
},
"right": {
- "title": "Right",
- "read": "Read only",
- "readWrite": "Read & write",
- "admin": "Admin"
+ "title": "Permissões",
+ "read": "Apenas de leitura",
+ "readWrite": "Leitura e escrita",
+ "admin": "Administração"
},
"attributes": {
"link": "Link",
- "name": "Name",
- "sharedBy": "Shared by",
- "right": "Right",
- "delete": "Delete"
+ "name": "Nome",
+ "sharedBy": "Partilhado por",
+ "right": "Permissões",
+ "delete": "Eliminar"
}
},
"list": {
- "title": "List",
- "add": "Add",
- "addPlaceholder": "Add a new task…",
- "empty": "This list is currently empty.",
- "newTaskCta": "Create a new task.",
- "editTask": "Edit Task"
+ "title": "Lista",
+ "add": "Adicionar",
+ "addPlaceholder": "Adicionar uma nova tarefa…",
+ "empty": "Esta lista está atualmente vazia.",
+ "newTaskCta": "Cria uma nova tarefa.",
+ "editTask": "Editar Tarefa"
},
"gantt": {
"title": "Gantt",
- "showTasksWithoutDates": "Show tasks which don't have dates set",
- "size": "Size",
- "default": "Default",
- "month": "Month",
- "day": "Day",
- "from": "From",
- "to": "To",
- "noDates": "This task has no dates set."
+ "showTasksWithoutDates": "Mostrar tarefas que não têm datas atríbuidas",
+ "size": "Tamanho",
+ "default": "Padrão",
+ "month": "Mês",
+ "day": "Dia",
+ "from": "De",
+ "to": "Até",
+ "noDates": "Esta tarefa não tem datas definidas."
},
"table": {
- "title": "Table",
- "columns": "Columns"
+ "title": "Tabela",
+ "columns": "Colunas"
},
"kanban": {
"title": "Kanban",
- "limit": "Limit: {limit}",
- "noLimit": "Not Set",
- "doneBucket": "Done bucket",
- "doneBucketHint": "All tasks moved into this bucket will automatically marked as done.",
- "doneBucketHintExtended": "All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.",
- "doneBucketSavedSuccess": "The done bucket has been saved successfully.",
- "deleteLast": "You cannot remove the last bucket.",
- "addTaskPlaceholder": "Enter the new task title…",
- "addTask": "Add a task",
- "addAnotherTask": "Add another task",
- "addBucket": "Create a new bucket",
- "addBucketPlaceholder": "Enter the new bucket title…",
- "deleteHeaderBucket": "Delete the bucket",
- "deleteBucketText1": "Are you sure you want to delete this bucket?",
- "deleteBucketText2": "This will not delete any tasks but move them into the default bucket.",
- "deleteBucketSuccess": "The bucket has been deleted successfully.",
- "bucketTitleSavedSuccess": "The bucket title has been saved successfully.",
- "bucketLimitSavedSuccess": "The bucket limit been saved successfully.",
- "collapse": "Collapse this bucket"
+ "limit": "Limite: {limit}",
+ "noLimit": "Não Definido",
+ "doneBucket": "Conjunto concluído",
+ "doneBucketHint": "Todas as tarefas movidas para este conjunto serão automaticamente marcadas como concluídas.",
+ "doneBucketHintExtended": "Todas as tarefas movidas para o conjunto concluído serão marcadas automaticamente como concluídas. Todas as tarefas marcadas como concluídas em outro lugar também serão movidas.",
+ "doneBucketSavedSuccess": "O conjunto concluído foi salvo com sucesso.",
+ "deleteLast": "Não podes remover o ultimo conjunto.",
+ "addTaskPlaceholder": "Introduz o título da nova tarefa…",
+ "addTask": "Adicionar uma tarefa",
+ "addAnotherTask": "Adicionar outra tarefa",
+ "addBucket": "Criar um novo conjunto",
+ "addBucketPlaceholder": "Introduz o título do novo conjunto…",
+ "deleteHeaderBucket": "Eliminar o conjunto",
+ "deleteBucketText1": "Tens a certeza que pretendes eliminar este conjunto?",
+ "deleteBucketText2": "Isto não vai eliminar nenhum tarefa, mas sim movê-la para o conjunto padrão.",
+ "deleteBucketSuccess": "O conjunto foi eliminado com sucesso.",
+ "bucketTitleSavedSuccess": "O título do conjunto foi salvo com sucesso.",
+ "bucketLimitSavedSuccess": "O limite do conjunto foi salvo com sucesso.",
+ "collapse": "Encolher este conjunto"
},
"pseudo": {
"favorites": {
- "title": "Favorites"
+ "title": "Favoritos"
}
}
},
"namespace": {
- "title": "Namespaces & Lists",
- "namespace": "Namespace",
- "showArchived": "Show Archived",
- "noneAvailable": "You don't have any namespaces right now.",
- "unarchive": "Un-Archive",
- "archived": "Archived",
- "noLists": "This namespace does not contain any lists.",
- "createList": "Create a new list in this namespace.",
- "namespaces": "Namespaces",
- "search": "Type to search for a namespace…",
+ "title": "Espaços e Listas",
+ "namespace": "Espaço",
+ "showArchived": "Mostrar Arquivados",
+ "noneAvailable": "Por enquanto não tens nenhum espaço.",
+ "unarchive": "Desarquivar",
+ "archived": "Arquivado",
+ "noLists": "Este espaço não contém qualquer lista.",
+ "createList": "Criar uma nova lista neste espaço.",
+ "namespaces": "Espaços",
+ "search": "Escreva para pesquisar por um espaço…",
"create": {
- "title": "New namespace",
- "titleRequired": "Please specify a title.",
- "explanation": "A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.",
- "tooltip": "What's a namespace?",
- "success": "The namespace was successfully created."
+ "title": "Novo espaço",
+ "titleRequired": "Por favor, especifica um título.",
+ "explanation": "Um espaço é uma coleção de listas que podes partilhar e utilizar para organizar as tuas listas. De facto, cada lista pertence a um espaço.",
+ "tooltip": "O que é um espaço?",
+ "success": "O espaço foi criado com sucesso."
},
"archive": {
- "titleArchive": "Archive \"{namespace}\"",
- "titleUnarchive": "Un-Archive \"{namespace}\"",
- "archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
- "unarchiveText": "You will be able to create new lists or edit it.",
- "success": "The namespace was successfully archived.",
- "description": "If a namespace is archived, you cannot create new lists or edit it."
+ "titleArchive": "Arquivar \"{namespace}\"",
+ "titleUnarchive": "Desarquivar \"{namespace}\"",
+ "archiveText": "Não será possível editar este espaço nem criar novas listas enquanto não o desarquivares. Isto também irá arquivar todas as listas existentes neste espaço.",
+ "unarchiveText": "Poderás criar novas listas ou editá-las.",
+ "success": "O espaço foi arquivado com sucesso.",
+ "unarchiveSuccess": "O espaço foi desarquivado com sucesso.",
+ "description": "Se um espaço estiver arquivado, não poderás criar novas listas ou editá-las."
},
"delete": {
- "title": "Delete \"{namespace}\"",
- "text1": "Are you sure you want to delete this namespace and all of its contents?",
- "text2": "This includes all lists and tasks and CANNOT BE UNDONE!",
- "success": "The namespace was successfully deleted."
+ "title": "Eliminar \"{namespace}\"",
+ "text1": "Tens a certeza que pretendes eliminar este espaço e todo o seu conteúdo?",
+ "text2": "Isto inclui todas as listas e taregas, e NÃO PODE SER REVERTIDO!",
+ "success": "O espaço foi eliminado com sucesso."
},
"edit": {
- "title": "Edit \"{namespace}\"",
- "success": "The namespace was successfully updated."
+ "title": "Editar \"{namespace}\"",
+ "success": "O espaço foi atualizado com sucesso."
},
"share": {
- "title": "Share \"{namespace}\""
+ "title": "Partilhar \"{namespace}\""
},
"attributes": {
- "title": "Namespace Title",
- "titlePlaceholder": "The namespace title goes here…",
- "description": "Description",
- "descriptionPlaceholder": "The namespaces description goes here…",
- "color": "Color",
- "archived": "Is Archived",
- "isArchived": "This namespace is archived"
+ "title": "Título do Espaço",
+ "titlePlaceholder": "O título do espaço será aqui…",
+ "description": "Descrição",
+ "descriptionPlaceholder": "A descrição do espaço será aqui…",
+ "color": "Cor",
+ "archived": "Arquivado",
+ "isArchived": "O espaço está arquivado"
},
"pseudo": {
"sharedLists": {
- "title": "Shared Lists"
+ "title": "Listas Partilhadas"
},
"favorites": {
- "title": "Favorites"
+ "title": "Favoritos"
},
"savedFilters": {
- "title": "Filters"
+ "title": "Filtros"
}
}
},
"filters": {
- "title": "Filters",
- "clear": "Clear Filters",
+ "title": "Filtros",
+ "clear": "Limpar Filtros",
"attributes": {
- "title": "Title",
- "titlePlaceholder": "The saved filter title goes here…",
- "description": "Description",
- "descriptionPlaceholder": "The description goes here…",
- "includeNulls": "Include Tasks which don't have a value set",
- "requireAll": "Require all filters to be true for a task to show up",
- "showDoneTasks": "Show Done Tasks",
- "sortAlphabetically": "Sort Alphabetically",
- "enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
- "dueDateRange": "Due Date Range",
- "startDateRange": "Start Date Range",
- "endDateRange": "End Date Range",
- "reminderRange": "Reminder Date Range"
+ "title": "Título",
+ "titlePlaceholder": "O título do filtro memorizado será aqui…",
+ "description": "Descrição",
+ "descriptionPlaceholder": "A descrição será aqui…",
+ "includeNulls": "Incluir Tarefas que não têm um valor definido",
+ "requireAll": "Requerer que todos os filtros sejam verdadeiros para que uma tarefa apareça",
+ "showDoneTasks": "Mostrar Tarefas Concluídas",
+ "sortAlphabetically": "Ordenar Alfabeticamente",
+ "enablePriority": "Ativar Filtro por Prioridade",
+ "enablePercentDone": "Ativar Filtro por Progresso",
+ "dueDateRange": "Intervalo de Datas de Vencimento",
+ "startDateRange": "Intervalo de Datas de Início",
+ "endDateRange": "Intervalo de Datas de Fim",
+ "reminderRange": "Intervalo de Datas de Lembrete"
},
"create": {
- "title": "New Saved Filter",
- "description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
- "action": "Create new saved filter"
+ "title": "Novo Filtro Memorizado",
+ "description": "Um filtro memorizado é uma lista virtual que é compilada a partir de um conjunto de filtros de cada vez que é acedido. Uma vez criado, irá aparecer num espaço especial.",
+ "action": "Criar novo filtro memorizado"
},
"delete": {
- "header": "Delete this saved filter",
- "text": "Are you sure you want to delete this saved filter?",
- "success": "The filter was deleted successfully."
+ "header": "Eliminar este filtro memorizado",
+ "text": "Tens a certeza que pretendes eliminar este filtro memorizado?",
+ "success": "O filtro foi eliminado com sucesso."
},
"edit": {
- "title": "Edit This Saved Filter",
- "success": "The filter was saved successfully."
+ "title": "Editar Este Filtro Memorizado",
+ "success": "O filtro foi memorizado com sucesso."
}
},
"migrate": {
- "title": "Migrate from other services to Vikunja",
- "titleService": "Import your data from {name} into Vikunja",
- "import": "Import your data into Vikunja",
- "description": "Click on the logo of one of the third-party services below to get started.",
- "descriptionDo": "Vikunja will import all lists, tasks, notes, reminders and files you have access to.",
- "authorize": "To authorize Vikunja to access your {name} Account, click the button below.",
- "getStarted": "Get Started",
- "inProgress": "Importing in progress…",
- "alreadyMigrated1": "It looks like you've already imported your stuff from {name} at {date}.",
- "alreadyMigrated2": "Importing again is possible, but might create duplicates. Are you sure?",
- "confirm": "I am sure, please start migrating now!",
- "importUpload": "To import data from {name} into Vikunja, click the button below to select a file.",
- "upload": "Upload file"
+ "title": "Migrar de outros serviços para o Vikunja",
+ "titleService": "Importar os teus dados de {name} para o Vikunja",
+ "import": "Importar os teus dados para o Vikunja",
+ "description": "Clica no logótipo de um dos serviços de terceiros abaixo para começar.",
+ "descriptionDo": "O Vikunja vai importar todas as listas, tarefas, notas, lembretes e ficheiros a que tens acesso.",
+ "authorize": "Para autorizares o Vikunja a aceder à tua conta em {name}, clica no botão abaixo.",
+ "getStarted": "Guia de Introdução",
+ "inProgress": "A importar…",
+ "alreadyMigrated1": "Parece que já importaste os teus dados de {name} em {date}.",
+ "alreadyMigrated2": "É possível importar novamente, mas pode criar duplicados. Tens a certeza?",
+ "confirm": "Tenho a certeza, por favor comece a migração agora!",
+ "importUpload": "Para importares dados de {name} para o Vikunja, clica no botão abaixo para selecionar um ficheiro.",
+ "upload": "Carregar ficheiro"
},
"label": {
- "title": "Labels",
- "manage": "Manage labels",
- "description": "Click on a label to edit it. You can edit all labels you created, you can use all labels which are associated with a task to whose list you have access.",
- "newCTA": "You currently do not have any labels.",
- "search": "Type to search for a label…",
+ "title": "Etiquetas",
+ "manage": "Gerir etiquetas",
+ "description": "Clica numa etiqueta para a editares. Podes editar todas as etiquetas que crias-te, podes utilizar todas as etiquetas que estão associadas a uma tarefa a cuja lista tens acesso.",
+ "newCTA": "Atualmente não tens quaisquer etiquetas.",
+ "search": "Escreve para pesquisar uma etiqueta…",
"create": {
- "header": "New label",
- "title": "Create a new label",
- "titleRequired": "Please specify a title.",
- "success": "The label was successfully created."
+ "header": "Nova etiqueta",
+ "title": "Cria uma nova etiqueta",
+ "titleRequired": "Por favor, especifica um título.",
+ "success": "A etiqueta foi criada com sucesso."
},
"edit": {
- "header": "Edit Label",
- "forbidden": "You are not allowed to edit this label because you dont own it.",
- "success": "The label was successfully updated."
+ "header": "Editar Etiqueta",
+ "forbidden": "Não estás autorizado a editar esta etiqueta porque não és o seu proprietário.",
+ "success": "A etiqueta foi atualizada com sucesso."
},
- "deleteSuccess": "The label was successfully deleted.",
+ "deleteSuccess": "A etiqueta foi eliminada com sucesso.",
"attributes": {
- "title": "Title",
- "titlePlaceholder": "The label title goes here…",
- "description": "Description",
- "descriptionPlaceholder": "Label description",
- "color": "Color"
+ "title": "Título",
+ "titlePlaceholder": "O título da etiqueta será aqui…",
+ "description": "Descrição",
+ "descriptionPlaceholder": "Descrição da etiqueta",
+ "color": "Cor"
}
},
"sharing": {
- "authenticating": "Authenticating…",
- "passwordRequired": "This shared list requires a password. Please enter it below:",
- "error": "An error occured.",
- "invalidPassword": "The password is invalid."
+ "authenticating": "A autenticar…",
+ "passwordRequired": "Esta lista partilhada requer uma palavra-passe. Por favor, introduz-a abaixo:",
+ "error": "Ocorreu um erro.",
+ "invalidPassword": "A palavra-passe é inválida."
},
"navigation": {
- "overview": "Overview",
- "upcoming": "Upcoming",
- "settings": "Settings",
- "imprint": "Imprint",
- "privacy": "Privacy Policy"
+ "overview": "Vista geral",
+ "upcoming": "Agendado",
+ "settings": "Definições",
+ "imprint": "Informação legal",
+ "privacy": "Política de Privacidade"
},
"misc": {
- "loading": "Loading…",
- "save": "Save",
- "delete": "Delete",
- "confirm": "Confirm",
- "cancel": "Cancel",
- "refresh": "Refresh",
- "disable": "Disable",
- "copy": "Copy to clipboard",
- "search": "Search",
- "searchPlaceholder": "Type to search…",
- "previous": "Previous",
- "next": "Next",
- "poweredBy": "Powered by Vikunja",
+ "loading": "A carregar…",
+ "save": "Salvar",
+ "delete": "Eliminar",
+ "confirm": "Confirmar",
+ "cancel": "Cancelar",
+ "refresh": "Atualizar",
+ "disable": "Desativar",
+ "copy": "Copiar para a área de trabalho",
+ "search": "Pesquisar",
+ "searchPlaceholder": "Escreva para pesquisar…",
+ "previous": "Anterior",
+ "next": "Seguinte",
+ "poweredBy": "Fornecido por Vikunja",
"info": "Info",
- "create": "Create",
- "doit": "Do it!",
- "saving": "Saving…",
- "saved": "Saved!",
- "default": "Default",
- "close": "Close",
- "download": "Download",
- "showMenu": "Show the menu",
- "hideMenu": "Hide the menu",
- "forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "create": "Criar",
+ "doit": "Fá-lo!",
+ "saving": "A salvar…",
+ "saved": "Salvo!",
+ "default": "Padrão",
+ "close": "Fechar",
+ "download": "Transferir",
+ "showMenu": "Mostra o menu",
+ "hideMenu": "Ocultar o menu",
+ "forExample": "Por exemplo:",
+ "welcomeBack": "Bem-Vindo de Volta!",
+ "custom": "Personalizado"
},
"input": {
- "resetColor": "Reset Color",
+ "resetColor": "Repor cor",
"datepicker": {
- "today": "Today",
- "tomorrow": "Tomorrow",
- "nextMonday": "Next Monday",
- "thisWeekend": "This Weekend",
- "laterThisWeek": "Later This Week",
- "nextWeek": "Next Week",
- "chooseDate": "Choose a date"
+ "today": "Hoje",
+ "tomorrow": "Amanhã",
+ "nextMonday": "Próxima segunda-feira",
+ "thisWeekend": "Este fim de semana",
+ "laterThisWeek": "No Final da Semana",
+ "nextWeek": "Próxima Semana",
+ "chooseDate": "Escolher uma data"
},
"editor": {
- "edit": "Edit",
- "done": "Done",
- "heading1": "Heading 1",
- "heading2": "Heading 2",
- "heading3": "Heading 3",
- "headingSmaller": "Heading Smaller",
- "headingBigger": "Heading Bigger",
- "bold": "Bold",
- "italic": "Italic",
- "strikethrough": "Strikethrough",
- "code": "Code",
- "quote": "Quote",
- "unorderedList": "Unordered List",
- "orderedList": "Ordered List",
- "cleanBlock": "Clean Block",
+ "edit": "Editar",
+ "done": "Concluído",
+ "heading1": "Cabeçalho 1",
+ "heading2": "Cabeçalho 2",
+ "heading3": "Cabeçalho 3",
+ "headingSmaller": "Título Menor",
+ "headingBigger": "Título Maior",
+ "bold": "Negrito",
+ "italic": "Itálico",
+ "strikethrough": "Rasurado",
+ "code": "Código",
+ "quote": "Citação",
+ "unorderedList": "Lista Não Ordenada",
+ "orderedList": "Lista Ordenada",
+ "cleanBlock": "Limpar Formatação",
"link": "Link",
- "image": "Image",
- "table": "Table",
- "horizontalRule": "Horizontal Rule",
- "sideBySide": "Side By Side",
- "guide": "Guide"
+ "image": "Imagem",
+ "table": "Tabela",
+ "horizontalRule": "Linha Horizontal",
+ "sideBySide": "Lado-a-lado",
+ "guide": "Guia"
},
"multiselect": {
- "createPlaceholder": "Create new",
- "selectPlaceholder": "Click or press enter to select"
+ "createPlaceholder": "Criar novo",
+ "selectPlaceholder": "Clica ou pressiona Enter para selecionar"
+ },
+ "datepickerRange": {
+ "to": "Até",
+ "from": "De",
+ "fromto": "{from} até {to}",
+ "ranges": {
+ "today": "Hoje",
+ "thisWeek": "Esta semana",
+ "restOfThisWeek": "O Resto Desta Semana",
+ "nextWeek": "Próxima Semana",
+ "next7Days": "Próximos 7 Dias",
+ "lastWeek": "Semana Passada",
+ "thisMonth": "Este Mês",
+ "restOfThisMonth": "O Resto Deste Mês",
+ "nextMonth": "Próximo Mês",
+ "next30Days": "Próximos 30 Dias",
+ "lastMonth": "Mês Passado",
+ "thisYear": "Este Ano",
+ "restOfThisYear": "O Resto Deste Ano"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "Podes utilizar cálculo de data para filtrar por datas relativas.",
+ "learnhow": "Vê como funciona",
+ "title": "Cálculo de Data",
+ "intro": "O cálculo de data permite especificar datas relativas resolvidas em tempo real pelo Vikunja na aplicação do filtro.",
+ "expression": "Cada expressão de Cálculo de Data inicia com uma data âncora, que tanto pode ser {0}, como uma expressão de data terminada com {1}. Esta data âncora pode ser opcionalmente seguida de uma ou mais expressões matemáticas.",
+ "similar": "Essas expressões são semelhantes às fornecidas por {0} e {1}.",
+ "add1Day": "Adicionar um dia",
+ "minus1Day": "Subtrair um dia",
+ "roundDay": "Arredondar para baixo para o dia mais próximo",
+ "supportedUnits": "As unidades de tempo suportadas são:",
+ "someExamples": "Alguns exemplos de expressões de tempo:",
+ "units": {
+ "seconds": "Segundos",
+ "minutes": "Minutos",
+ "hours": "Horas",
+ "days": "Dias",
+ "weeks": "Semanas",
+ "months": "Meses",
+ "years": "Anos"
+ },
+ "examples": {
+ "now": "Agora",
+ "in24h": "Em 24h",
+ "today": "Hoje às 00:00",
+ "beginningOfThisWeek": "O início desta semana às 00h00",
+ "endOfThisWeek": "O fim desta semana",
+ "in30Days": "Em 30 dias",
+ "datePlusMonth": "{0} mais um mês às 00:00 desse dia"
+ }
}
},
"task": {
- "task": "Task",
- "new": "Create a new task",
- "delete": "Delete this task",
- "createSuccess": "The task was successfully created.",
- "addReminder": "Add a new reminder…",
- "doneSuccess": "The task was successfully marked as done.",
- "undoneSuccess": "The task was successfully un-marked as done.",
- "openDetail": "Open task detail view",
- "checklistTotal": "{checked} of {total} tasks",
- "checklistAllDone": "{total} tasks",
+ "task": "Tarefa",
+ "new": "Criar uma nova tarefa",
+ "delete": "Eliminar esta tarefa",
+ "createSuccess": "A tarefa for criada com sucesso.",
+ "addReminder": "Adicionar um novo lembrete…",
+ "doneSuccess": "A tarefa foi marcada como concluída.",
+ "undoneSuccess": "A tarefa foi desmarcada como concluída.",
+ "openDetail": "Abrir vista detalhada da tarefa",
+ "checklistTotal": "{checked} de {total} tarefas",
+ "checklistAllDone": "{total} tarefas",
"show": {
- "titleCurrent": "Current Tasks",
- "titleDates": "Tasks from {from} until {to}",
- "noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
- "noTasks": "Nothing to do — Have a nice day!"
+ "titleCurrent": "Tarefas Atuais",
+ "titleDates": "Tarefas de {from} até {to}",
+ "noDates": "Mostrar tarefas sem datas",
+ "overdue": "Mostrar tarefas em atraso",
+ "fromuntil": "Tarefas de {from} a {until}",
+ "select": "Escolha um intervalo de datas",
+ "noTasks": "Nada para fazer — Tem um bom dia!"
},
"detail": {
- "chooseDueDate": "Click here to set a due date",
- "chooseStartDate": "Click here to set a start date",
- "chooseEndDate": "Click here to set an end date",
- "move": "Move task to a different list",
- "done": "Mark task done!",
- "undone": "Mark as undone",
- "created": "Created {0} by {1}",
- "updated": "Updated {0}",
- "doneAt": "Done {0}",
- "updateSuccess": "The task was saved successfully.",
- "deleteSuccess": "The task has been deleted successfully.",
- "belongsToList": "This task belongs to list '{list}'",
- "due": "Due {at}",
- "closePopup": "Close popup",
+ "chooseDueDate": "Clica aqui para definir uma data de vencimento",
+ "chooseStartDate": "Clica aqui para definir uma data de início",
+ "chooseEndDate": "Clica aqui para definir uma data de fim",
+ "move": "Mover tarefa para outra lista",
+ "done": "Marcar como concluída!",
+ "undone": "Marcar como não concluída",
+ "created": "Criada {0} por {1}",
+ "updated": "Atualizada {0}",
+ "doneAt": "Concluído {0}",
+ "updateSuccess": "A tarefa foi salva com sucesso.",
+ "deleteSuccess": "A tarefa foi eliminada com sucesso.",
+ "belongsToList": "Esta tarefa pertence à lista '{list}'",
+ "due": "Vence {at}",
+ "closePopup": "Fechar janela",
"delete": {
- "header": "Delete this task",
- "text1": "Are you sure you want to remove this task?",
- "text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
+ "header": "Eliminar esta tarefa",
+ "text1": "Tens a certeza que pretendes eliminar esta tarefa?",
+ "text2": "Isto vai também eliminar todos os anexos, lembretes e relações associadas a esta tarefa, e não pode ser revertido!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
- "priority": "Set Priority",
- "dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
- "reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "assign": "Atribuir a Utilizador",
+ "label": "Adicionar Etiquetas",
+ "priority": "Definir Prioridade",
+ "dueDate": "Definir Data de Vencimento",
+ "startDate": "Definir Data de Início",
+ "endDate": "Definir Data de Fim",
+ "reminders": "Definir Lembretes",
+ "repeatAfter": "Definir Intervalo Recorrente",
+ "percentDone": "Definir Progresso",
+ "attachments": "Adicionar Anexos",
+ "relatedTasks": "Adicionar Relação",
+ "moveList": "Mover",
+ "color": "Definir Cor",
+ "delete": "Eliminar",
+ "favorite": "Adicionar aos Favoritos",
+ "unfavorite": "Remover dos Favoritos"
}
},
"attributes": {
- "assignees": "Assignees",
- "color": "Color",
- "created": "Created",
- "createdBy": "Created By",
- "description": "Description",
- "done": "Done",
- "dueDate": "Due Date",
- "endDate": "End Date",
- "labels": "Labels",
- "percentDone": "% Done",
- "priority": "Priority",
- "relatedTasks": "Related Tasks",
- "reminders": "Reminders",
- "repeat": "Repeat",
- "startDate": "Start Date",
- "title": "Title",
- "updated": "Updated"
+ "assignees": "Responsáveis",
+ "color": "Cor",
+ "created": "Criado",
+ "createdBy": "Criado por",
+ "description": "Descrição",
+ "done": "Concluído",
+ "dueDate": "Data de Vencimento",
+ "endDate": "Data de Fim",
+ "labels": "Etiquetas",
+ "percentDone": "Progresso",
+ "priority": "Prioridade",
+ "relatedTasks": "Tarefas Relacionadas",
+ "reminders": "Lembretes",
+ "repeat": "Repetir",
+ "startDate": "Data de Início",
+ "title": "Título",
+ "updated": "Atualizado"
},
"subscription": {
- "subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
- "subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
- "notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
- "subscribe": "Subscribe",
- "unsubscribe": "Unsubscribe",
- "subscribeSuccess": "You are now subscribed to this {entity}",
- "unsubscribeSuccess": "You are now unsubscribed to this {entity}"
+ "subscribedThroughParent": "Não podes cancelar a tua subscrição aqui porque estás subscrito nesta {entity} através de {parent}.",
+ "subscribed": "Estás atualmente subscrito a esta {entity} e serás notificado de alterações.",
+ "notSubscribed": "Não estás subscrito a esta {entity} e não serás notificado de alterações.",
+ "subscribe": "Subscrever",
+ "unsubscribe": "Remover Subscrição",
+ "subscribeSuccess": "Estás agora subscrito a esta {entity}",
+ "unsubscribeSuccess": "Não estás mais subcrito a esta {entity}"
},
"attachment": {
- "title": "Attachments",
- "createdBy": "created {0} by {1}",
- "downloadTooltip": "Download this attachment",
- "upload": "Upload attachment",
- "drop": "Drop files here to upload",
- "delete": "Delete attachment",
- "deleteTooltip": "Delete this attachment",
- "deleteText1": "Are you sure you want to delete the attachment {filename}?",
- "deleteText2": "This cannot be undone!",
- "copyUrl": "Copy URL",
- "copyUrlTooltip": "Copy the url of this attachment for usage in text"
+ "title": "Anexos",
+ "createdBy": "criado {0} por {1}",
+ "downloadTooltip": "Transferir este anexo",
+ "upload": "Carregar anexo",
+ "drop": "Arrasta para aqui um ficheiro para o carregar",
+ "delete": "Eliminar anexo",
+ "deleteTooltip": "Eliminar este anexo",
+ "deleteText1": "Tens a certeza que pretendes eliminar o anexo {filename}?",
+ "deleteText2": "Isto não pode ser revertido!",
+ "copyUrl": "Copiar URL",
+ "copyUrlTooltip": "Copia o url deste anexo para o utilizar no texto"
},
"comment": {
- "title": "Comments",
- "loading": "Loading comments…",
- "edited": "edited {date}",
- "creating": "Creating comment…",
- "placeholder": "Add your comment…",
- "comment": "Comment",
- "delete": "Delete this comment",
- "deleteText1": "Are you sure you want to delete this comment?",
- "deleteText2": "This cannot be undone!",
- "addedSuccess": "The comment was added successfully."
+ "title": "Comentários",
+ "loading": "A carregar comentários…",
+ "edited": "editado em {date}",
+ "creating": "A criar comentário…",
+ "placeholder": "Adiciona o teu comentário…",
+ "comment": "Comentário",
+ "delete": "Eliminar este comentário",
+ "deleteText1": "Tens a certeza que pretendes eliminar este comentário?",
+ "deleteText2": "Isto não pode ser revertido!",
+ "addedSuccess": "O comentário foi adicionada com sucesso."
},
"deferDueDate": {
- "title": "Defer due date",
- "1day": "1 day",
- "3days": "3 days",
- "1week": "1 week"
+ "title": "Adiar data de vencimento",
+ "1day": "1 dia",
+ "3days": "3 dias",
+ "1week": "1 semana"
},
"description": {
- "placeholder": "Click here to enter a description…",
- "empty": "No description available yet."
+ "placeholder": "Clica aqui para inserir uma descrição…",
+ "empty": "Nenhuma descrição ainda disponível."
},
"assignee": {
- "placeholder": "Type to assign a user…",
- "selectPlaceholder": "Assign this user",
- "assignSuccess": "The user has been assigned successfully.",
- "unassignSuccess": "The user has been unassigned successfully."
+ "placeholder": "Escreve para atribuir um utilizador…",
+ "selectPlaceholder": "Atribuir a este utilizador",
+ "assignSuccess": "O utilizador foi atribuído com sucesso.",
+ "unassignSuccess": "O utilizador foi desatribuido com sucesso."
},
"label": {
- "placeholder": "Type to add a new label…",
- "createPlaceholder": "Add this as new label",
- "addSuccess": "The label has been added successfully.",
- "createSuccess": "The label has been created successfully.",
- "removeSuccess": "The label has been removed successfully.",
- "addCreateSuccess": "The label has been created and added successfully."
+ "placeholder": "Escreve para adicionar uma nova etiqueta…",
+ "createPlaceholder": "Adicionar isto como nova etiqueta",
+ "addSuccess": "A etiqueta foi adicionada com sucesso.",
+ "createSuccess": "A etiqueta foi criada com sucesso.",
+ "removeSuccess": "A etiqueta foi eliminada com sucesso.",
+ "addCreateSuccess": "A etiqueta foi criada e adicionada com sucesso."
},
"priority": {
- "unset": "Unset",
- "low": "Low",
- "medium": "Medium",
- "high": "high",
- "urgent": "Urgent",
- "doNow": "DO NOW"
+ "unset": "Remover",
+ "low": "Baixa",
+ "medium": "Média",
+ "high": "alta",
+ "urgent": "Urgente",
+ "doNow": "FAZER AGORA"
},
"relation": {
- "add": "Add a New Task Relation",
- "new": "New Task Relation",
- "searchPlaceholder": "Type search for a new task to add as related…",
- "createPlaceholder": "Add this as new related task",
- "differentList": "This task belongs to a different list.",
- "differentNamespace": "This task belongs to a different namespace.",
- "noneYet": "No task relations yet.",
- "delete": "Delete Task Relation",
- "deleteText1": "Are you sure you want to delete this task relation?",
- "deleteText2": "This cannot be undone!",
- "select": "Select a relation kind",
+ "add": "Adicionar Nova Relação Entre Tarefas",
+ "new": "Nova Relação Entre Tarefas",
+ "searchPlaceholder": "Escreve para pesquisar uma tarefa a adicionar como relacionada…",
+ "createPlaceholder": "Adicionar como nova tarefa relacionada",
+ "differentList": "Esta tarefa pertence a uma lista diferente.",
+ "differentNamespace": "Esta tarefa pertence a um espaço diferente.",
+ "noneYet": "Ainda sem tarefas relacionadas.",
+ "delete": "Eliminar Relação Entre Tarefas",
+ "deleteText1": "Tens a certeza que pretendes eliminar esta relação entre tarefas?",
+ "deleteText2": "Isto não pode ser revertido!",
+ "select": "Seleciona um tipo de relação",
"kinds": {
- "subtask": "Subtask | Subtasks",
- "parenttask": "Parent Task | Parent Tasks",
- "related": "Related Task | Related Tasks",
- "duplicateof": "Duplicate Of | Duplicates Of",
- "duplicates": "Duplicates | Duplicates",
- "blocking": "Blocking | Blocking",
- "blocked": "Blocked By | Blocked By",
- "precedes": "Precedes | Precedes",
- "follows": "Follows | Follows",
- "copiedfrom": "Copied From | Copied From",
- "copiedto": "Copied To | Copied To"
+ "subtask": "Subtarefa | Subtarefas",
+ "parenttask": "Tarefa Principal | Tarefas Principais",
+ "related": "Tarefa Relacionada | Tarefas Relacionadas",
+ "duplicateof": "Duplicada de | Duplicadas de",
+ "duplicates": "Duplicada | Duplicadas",
+ "blocking": "Bloqueio | Bloqueio",
+ "blocked": "Bloqueada Por | Bloqueadas Por",
+ "precedes": "Antecede | Antecede",
+ "follows": "Segue | Segue",
+ "copiedfrom": "Copiada De | Copiada De",
+ "copiedto": "Copiada Para | Copiada Para"
}
},
"repeat": {
- "everyDay": "Every Day",
- "everyWeek": "Every Week",
- "everyMonth": "Every Month",
- "mode": "Repeat mode",
- "monthly": "Monthly",
- "fromCurrentDate": "From Current Date",
- "each": "Each",
- "specifyAmount": "Specify an amount…",
- "hours": "Hours",
- "days": "Days",
- "weeks": "Weeks",
- "months": "Months",
- "years": "Years"
+ "everyDay": "Todos os Dias",
+ "everyWeek": "Todas as Semanas",
+ "everyMonth": "Todos os Meses",
+ "mode": "Modo de repetição",
+ "monthly": "Mensal",
+ "fromCurrentDate": "Da Data Atual",
+ "each": "Cada",
+ "specifyAmount": "Especifica uma quantidade…",
+ "hours": "Horas",
+ "days": "Dias",
+ "weeks": "Semanas",
+ "months": "Meses",
+ "years": "Anos"
},
"quickAddMagic": {
- "hint": "You can use Quick Add Magic",
- "what": "What?",
- "title": "Quick Add Magic",
- "intro": "When creating a task, you can use special keywords to directly add attributes to the newly created task. This allows to add commonly used attributes to tasks much faster.",
- "multiple": "You can use this multiple times.",
- "label1": "To add a label, simply prefix the name of the label with {prefix}.",
- "label2": "Vikunja will first check if the label already exist and create it if not.",
- "label3": "To use spaces, simply add a \" around the label name.",
- "label4": "For example: {prefix}\"Label with spaces\".",
- "priority1": "To set a task's priority, add a number 1-5, prefixed with a {prefix}.",
- "priority2": "The higher the number, the higher the priority.",
- "assignees": "To directly assign the task to a user, add their username prefixed with {prefix} to the task.",
- "list1": "To set a list for the task to appear in, enter its name prefixed with {prefix}.",
- "list2": "This will return an error if the list does not exist.",
- "dateAndTime": "Date and time",
- "date": "Any date will be used as the due date of the new task. You can use dates in any of these formats:",
- "dateWeekday": "any weekday, will use the next date with that date",
- "dateCurrentYear": "will use the current year",
- "dateNth": "will use the {day}th of the current month",
- "dateTime": "Combine any of the date formats with \"{time}\" (or {timePM}) to set a time.",
- "repeats": "Repeating tasks",
- "repeatsDescription": "To set a task as repeating in an interval, simply add '{suffix}' to the task text. The amount needs to be a number and can be omitted to use just the type (see examples)."
+ "hint": "Podes utilizar a Introdução Mágica Rápida",
+ "what": "O quê?",
+ "title": "Introdução Mágica Rápida",
+ "intro": "Ao criar uma tarefa, podes usar palavras-chave especiais para adicionar diretamente atributos à tarefa recém-criada. Isto permite adicionar atributos comummente utilizados às tarefas muito mais rapidamente.",
+ "multiple": "Podes usar isto várias vezes.",
+ "label1": "Para adicionar uma etiqueta, basta prefixares o nome da etiqueta com {prefix}.",
+ "label2": "Vikunja irá primeiro verificar se a etiqueta já existe e criá-la se não.",
+ "label3": "Para utilizares espaços, basta adicionares um \" no início e fim do nome da etiqueta.",
+ "label4": "Por exemplo: {prefix}\"Etiqueta com espaços\".",
+ "priority1": "Para definir a prioridade de uma tarefa, adiciona um número entre 1 a 5, prefixado com um {prefix}.",
+ "priority2": "Quanto maior o número, maior a prioridade.",
+ "assignees": "Para atribuires uma tarefa diretamente a um utilizador, adiciona à tarefa os seus nomes de utilizador prefixados com {prefix}.",
+ "list1": "Para definir uma lista onde a tarefa deve aparecer, adiciona o seu nome prefixado com {prefix}.",
+ "list2": "Isto vai retornar um erro se a lista não existir.",
+ "dateAndTime": "Data e hora",
+ "date": "Qualquer data será utilizada como data de vencimento da nova tarefa. Podes utilizar datas em qualquer dos seguintes formatos:",
+ "dateWeekday": "qualquer dia de semana, usará a próxima data com essa data",
+ "dateCurrentYear": "usará o ano atual",
+ "dateNth": "usará o {day} do mês atual",
+ "dateTime": "Combina qualquer um dos formatos de data com \"{time}\" (ou {timePM}) para definir uma hora.",
+ "repeats": "Tarefas recorrentes",
+ "repeatsDescription": "Para definir uma tarefa como recorrente num intervalo, basta adicionar '{suffix}' ao texto da tarefa. O valor necessita ser um número e pode ser omitida para usar apenas o tipo (veja exemplos)."
}
},
"team": {
- "title": "Teams",
- "noTeams": "You are currently not part of any teams.",
+ "title": "Equipas",
+ "noTeams": "Atualmente não fazes parte de nenhuma equipa.",
"create": {
- "title": "Create a new team",
- "success": "The team was successfully created."
+ "title": "Cria uma nova equipa",
+ "success": "A equipa foi criada com sucesso."
},
"edit": {
- "title": "Edit Team \"{team}\"",
- "members": "Team Members",
- "search": "Type to search a user…",
- "addUser": "Add to team",
- "makeMember": "Make Member",
- "makeAdmin": "Make Admin",
- "success": "The team was successfully updated.",
- "userAddedSuccess": "The team member was successfully added.",
- "madeMember": "The team member was successfully made member.",
- "madeAdmin": "The team member was successfully made admin.",
+ "title": "Editar Equipa \"{team}\"",
+ "members": "Membros da Equipa",
+ "search": "Escrever para pesquisar por um utilizador…",
+ "addUser": "Adicionar à equipa",
+ "makeMember": "Tornar Membro",
+ "makeAdmin": "Tornar Administrador",
+ "success": "A equipa foi atualizada com sucesso.",
+ "userAddedSuccess": "O membro da equipa foi adicionado com sucesso.",
+ "madeMember": "O membro da equipa foi tornado membro com sucesso.",
+ "madeAdmin": "O membro da equipa foi tornado admin com sucesso.",
"delete": {
- "header": "Delete the team",
- "text1": "Are you sure you want to delete this team and all of its members?",
- "text2": "All team members will lose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!",
- "success": "The team was successfully deleted."
+ "header": "Eliminar equipa",
+ "text1": "Tens a certeza que pretendes eliminar esta equipa e todos os seus membros?",
+ "text2": "Todos os membros da equipa perderão acesso às listas e espaços partilhados com esta equipa. Isto NÃO PODE SER REVERTIDO!",
+ "success": "A equipa foi eliminada com sucesso."
},
"deleteUser": {
- "header": "Remove a user from the team",
- "text1": "Are you sure you want to remove this user from the team?",
- "text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
- "success": "The user was successfully deleted from the team."
+ "header": "Remover utilizador da equipa",
+ "text1": "Tens a certeza que pretendes remover este utilizador da equipa?",
+ "text2": "Eles perderão o acesso a todas as listas e espaços a que esta equipa tem acesso. Isto NÃO PODER SER REVERTIDO!",
+ "success": "O utilizador foi removido da equipa com sucesso."
}
},
"attributes": {
- "name": "Team Name",
- "namePlaceholder": "The team's name goes here…",
- "nameRequired": "Please specify a name.",
- "description": "Description",
- "descriptionPlaceholder": "The teams description goes here…",
- "admin": "Admin",
- "member": "Member"
+ "name": "Nome da Equipa",
+ "namePlaceholder": "O nome da equipa será aqui…",
+ "nameRequired": "Por favor, específica um nome.",
+ "description": "Descrição",
+ "descriptionPlaceholder": "A descrição da equipa será aqui…",
+ "admin": "Administrador",
+ "member": "Membro"
}
},
"keyboardShortcuts": {
- "title": "Keyboard Shortcuts",
- "general": "General",
- "allPages": "These shortcuts work on all pages.",
- "currentPageOnly": "These shortcuts work only on the current page.",
- "toggleMenu": "Toggle The Menu",
- "quickSearch": "Open the search/quick action bar",
- "then": "then",
+ "title": "Atalhos de Teclado",
+ "general": "Geral",
+ "allPages": "Estes atalhos funcionam em todas as páginas.",
+ "currentPageOnly": "Estes atalhos funcionamente apenas na página atual.",
+ "somePagesOnly": "Estes atalhos funcionam apenas em algumas páginas.",
+ "toggleMenu": "Alternar o Menu",
+ "quickSearch": "Abrir a pesquisa/barra de ação rápida",
+ "then": "então",
"task": {
- "title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
- "labels": "Add labels to this task",
- "dueDate": "Change the due date of this task",
- "attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "title": "Página de Tarefa",
+ "done": "Marcar tarefa como concluída / não concluída",
+ "assign": "Atribuir esta tarefa a um utilizador",
+ "labels": "Adicionar etiquetas a esta tarefa",
+ "dueDate": "Alterar a data de vencimento desta tarefa",
+ "attachment": "Adicionar um anexo a esta tarefa",
+ "related": "Modificar as tarefas relacionadas desta tarefa",
+ "color": "Alterar a cor desta tarefa",
+ "move": "Mover esta tarefa para outra lista"
},
"list": {
- "title": "List Views",
- "switchToListView": "Switch to list view",
- "switchToGanttView": "Switch to gantt view",
- "switchToKanbanView": "Switch to kanban view",
- "switchToTableView": "Switch to table view"
+ "title": "Visualização em Lista",
+ "switchToListView": "Alternar para visualização em lista",
+ "switchToGanttView": "Alternar para visualização em gráfico de Gantt",
+ "switchToKanbanView": "Alternar para visualização em método de Kanban",
+ "switchToTableView": "Alternar para visualização em tabela"
+ },
+ "navigation": {
+ "title": "Navegação",
+ "overview": "Navegar para a vista geral",
+ "upcoming": "Navegar para próximas tarefas",
+ "namespaces": "Navegar para espaços e listas",
+ "labels": "Navegar para etiquetas",
+ "teams": "Navegar para equipas"
}
},
"update": {
- "available": "There is an update for Vikunja available!",
- "do": "Update Now"
+ "available": "Há uma atualização para o Vikunja disponível!",
+ "do": "Atualizar Agora"
},
"menu": {
- "edit": "Edit",
- "archive": "Archive",
- "duplicate": "Duplicate",
- "delete": "Delete",
- "unarchive": "Un-Archive",
- "setBackground": "Set background",
- "share": "Share",
- "newList": "New list"
+ "edit": "Editar",
+ "archive": "Arquivar",
+ "duplicate": "Duplicar",
+ "delete": "Eliminar",
+ "unarchive": "Desarquivar",
+ "setBackground": "Definir Fundo",
+ "share": "Partilhar",
+ "newList": "Nova lista"
},
"apiConfig": {
- "url": "Vikunja URL",
- "urlPlaceholder": "eg. https://localhost:3456",
- "change": "change",
- "use": "Using Vikunja installation at {0}",
- "error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
- "success": "Using Vikunja installation at \"{domain}\".",
- "urlRequired": "A url is required."
+ "url": "URL do Vikunja",
+ "urlPlaceholder": "ex.: https://localhost:3456",
+ "change": "alterar",
+ "use": "A utilizar a instalação do Vikunja em {0}",
+ "error": "Não foi possível encontrar ou utilizar a instalação do Vikunja em \"{domain}\". Por favor, tenta um url diferente.",
+ "success": "A utilizar a instalação do Vikunja em \"{domain}\".",
+ "urlRequired": "É necessário um url."
},
"loadingError": {
- "failed": "Loading failed, please {0}. If the error persists, please {1}.",
- "tryAgain": "try again",
- "contact": "contact us"
+ "failed": "Falha ao carregar, por favor {0}. Se o erro persistir, por favor {1}.",
+ "tryAgain": "tentar novamente",
+ "contact": "contacta-nos"
},
"notification": {
- "title": "Notifications",
- "none": "You don't have any notifications. Have a nice day!",
- "explainer": "Notifications will appear here when actions on namespaces, lists or tasks you subscribed to happen."
+ "title": "Notificações",
+ "none": "Não tens nenhuma notificação. Tem um bom dia!",
+ "explainer": "As notificações aparecerão aqui quando ocorrem ações em espaços, listas ou tarefas às quais estejas subscrito."
},
"quickActions": {
- "commands": "Commands",
- "placeholder": "Type a command or search…",
- "hint": "You can use {list} to limit the search to a list. Combine {list} or {label} (labels) with a search query to search for a task with these labels or on that list. Use {assignee} to only search for teams.",
- "tasks": "Tasks",
- "lists": "Lists",
- "teams": "Teams",
- "newList": "Enter the title of the new list…",
- "newTask": "Enter the title of the new task…",
- "newNamespace": "Enter the title of the new namespace…",
- "newTeam": "Enter the name of the new team…",
- "createTask": "Create a task in the current list ({title})",
- "createList": "Create a list in the current namespace ({title})",
+ "commands": "Comandos",
+ "placeholder": "Escreve um comando ou pesquisa…",
+ "hint": "Podes utilizar {list} para limitar a pesquisa a uma lista. Combina {list} ou {label} (etiquetas) com uma pesquisa para procurar uma tarefa com essas etiquetas ou nessa lista. Utiliza {assignee} para pesquisares apenas por equipas.",
+ "tasks": "Tarefas",
+ "lists": "Listas",
+ "teams": "Equipas",
+ "newList": "Insere o título da nova lista…",
+ "newTask": "Insere o título da nova tarefa…",
+ "newNamespace": "Insere o título do novo espaço…",
+ "newTeam": "Insere o nome da nova equipa…",
+ "createTask": "Cria uma tarefa na lista atual ({title})",
+ "createList": "Cria uma lista no espaço atual ({title})",
"cmds": {
- "newTask": "New task",
- "newList": "New list",
- "newNamespace": "New namespace",
- "newTeam": "New team"
+ "newTask": "Nova tarefa",
+ "newList": "Nova lista",
+ "newNamespace": "Novo espaço",
+ "newTeam": "Nova equipa"
}
},
"date": {
- "locale": "en",
+ "locale": "pt-PT",
"altFormatLong": "j M Y H:i",
"altFormatShort": "j M Y"
},
"error": {
- "error": "Error",
- "success": "Success",
- "0001": "You're not allowed to do that.",
- "1001": "A user with this username already exists.",
- "1002": "A user with this email address already exists.",
- "1004": "No username and password specified.",
- "1005": "The user does not exist.",
- "1006": "Could not get the user id.",
- "1008": "No password reset token provided.",
- "1009": "Invalid password reset token.",
- "1010": "Invalid email confirm token.",
- "1011": "Wrong username or password.",
- "1012": "Email address of the user not confirmed.",
- "1013": "New password is empty.",
- "1014": "Old password is empty.",
- "1015": "Totp is already enabled for this user.",
- "1016": "Totp is not enabled for this user.",
- "1017": "The totp passcode is invalid.",
- "1018": "The user avatar type setting is invalid.",
- "2001": "ID cannot be empty or 0.",
- "2002": "Some of the request data was invalid.",
- "3001": "The list does not exist.",
- "3004": "You need to have read permissions on that list to perform that action.",
- "3005": "The list title cannot be empty.",
- "3006": "The list share does not exist.",
- "3007": "A list with this identifier already exists.",
- "3008": "The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list.",
- "4001": "The list task text cannot be empty.",
- "4002": "The list task does not exist.",
- "4003": "All bulk editing tasks must belong to the same list.",
- "4004": "Need at least one task when bulk editing tasks.",
- "4005": "You do not have the right to see the task.",
- "4006": "You can't set a parent task as the task itself.",
- "4007": "You can't create a task relation with an invalid kind of relation.",
- "4008": "You can't create a task relation which already exists.",
- "4009": "The task relation does not exist.",
- "4010": "Cannot relate a task with itself.",
- "4011": "The task attachment does not exist.",
- "4012": "The task attachment is too large.",
- "4013": "The task sort param is invalid.",
- "4014": "The task sort order is invalid.",
- "4015": "The task comment does not exist.",
- "4016": "Invalid task field.",
- "4017": "Invalid task filter comparator.",
- "4018": "Invalid task filter concatenator.",
- "4019": "Invalid task filter value.",
- "5001": "The namespace does not exist.",
- "5003": "You do not have access to the specified namespace.",
- "5006": "The namespace name cannot be empty.",
- "5009": "You need to have namespace read access to perform that action.",
- "5010": "This team does not have access to that namespace.",
- "5011": "This user has already access to that namespace.",
- "5012": "The namespace is archived and can therefore only be accessed read only.",
- "6001": "The team name cannot be empty.",
- "6002": "The team does not exist.",
- "6004": "The team already has access to that namespace or list.",
- "6005": "The user is already a member of that team.",
- "6006": "Cannot delete the last team member.",
- "6007": "The team does not have access to the list to perform that action.",
- "7002": "The user already has access to that list.",
- "7003": "You do not have access to that list.",
- "8001": "This label already exists on that task.",
- "8002": "The label does not exist.",
- "8003": "You do not have access to this label.",
- "9001": "The right is invalid.",
- "10001": "The bucket does not exist.",
- "10002": "The bucket does not belong to that list.",
- "10003": "You cannot remove the last bucket on a list.",
- "10004": "You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold.",
- "10005": "There can be only one done bucket per list.",
- "11001": "The saved filter does not exist.",
- "11002": "Saved filters are not available for link shares.",
- "12001": "The subscription entity type is invalid.",
- "12002": "You are already subscribed to the entity itself or a parent entity.",
- "13001": "This link share requires a password for authentication, but none was provided.",
- "13002": "The provided link share password was invalid."
+ "error": "Erro",
+ "success": "Sucesso",
+ "0001": "Sem permissões para executar esta tarefa.",
+ "1001": "Já existe um utilizador com esse nome de utilizador.",
+ "1002": "Já existe um utiliador com este endereço de e-mail.",
+ "1004": "Sem nome de utilizador e palavra-passe especificados.",
+ "1005": "O utilizador não existe.",
+ "1006": "Não foi possível obter o id do utilizador.",
+ "1008": "Não foi fornecido nenhum código de redefinição de palavra-passe.",
+ "1009": "Código de redefinição de palavra-passe inválido.",
+ "1010": "Código de confirmação de e-mail inválido.",
+ "1011": "Nome de utilizador ou palavra-passe inválidos.",
+ "1012": "Endereço de e-mail do utlizador não confirmado.",
+ "1013": "Campo de nova palavra-passe vazio.",
+ "1014": "Campo de antiga palavra-passe vazio.",
+ "1015": "Totp já está ativado para este utilizador.",
+ "1016": "Totp não está ativado para este utilizador.",
+ "1017": "O código totp é inválido.",
+ "1018": "A definição do tipo de avatar do utilizador é inválida.",
+ "2001": "O ID não pode ser 0 ou estar vazio.",
+ "2002": "Alguns dos dados solicitados eram inválidos.",
+ "3001": "A lista não existe.",
+ "3004": "Precisas ter permissão de leitura nessa lista para executar essa ação.",
+ "3005": "O título da lista não pode estar vazio.",
+ "3006": "A lista partilhada não existe.",
+ "3007": "Já existe uma lista com este identificador.",
+ "3008": "A lista está arquivada, portanto, só pode ser acedida para leitura. Isto é também verdade para todas as tarefas associadas a esta lista.",
+ "4001": "O texto da tarefa não pode estar vazio.",
+ "4002": "A tarefa não existe.",
+ "4003": "Todas as tarefas para edição em massa devem pertencer à mesma lista.",
+ "4004": "Precisas selecionar pelo menos uma tarefa para realizar uma edição em massa.",
+ "4005": "Não possuis permissão para ver esta tarefa.",
+ "4006": "Não podes definir uma tarefa principal como a tarefa em si.",
+ "4007": "Não podes criar uma relação entre tarefas com um tipo de relação inválido.",
+ "4008": "Não podes criar uma relação entre tarefas que já existe.",
+ "4009": "A relação entre tarefas não existe.",
+ "4010": "Não podes relacionar uma tarefa com ela mesma.",
+ "4011": "O anexo da tarefa não existe.",
+ "4012": "O anexo da tarefa é muito grande.",
+ "4013": "Parâmetro de ordenação da tarefa inválido.",
+ "4014": "Ordem de ordenação da tarefa inválida.",
+ "4015": "O comentário da tarefa não existe.",
+ "4016": "Campo da tarefa inválido.",
+ "4017": "Filtro de comparação de tarefas inválido.",
+ "4018": "Filtro de encademanto de tarefas inválido.",
+ "4019": "Valor de filtro de tarefas inválido.",
+ "5001": "O espaço não existe.",
+ "5003": "Não tens acesso ao espaço especificado.",
+ "5006": "O nome do espaço não pode estar vazio.",
+ "5009": "Precisas ter permissão de leitura deste espaço para executar essa ação.",
+ "5010": "Esta equipa não tem acesso a esse espaço.",
+ "5011": "Este utilizador já tem acesso a esse espaço.",
+ "5012": "O espaço está arquivado, portanto, só pode ser acedido para leitura.",
+ "6001": "O nome da equipa não pode estar vazio.",
+ "6002": "A equipa não existe.",
+ "6004": "A equipa tem já acesso a esse espaço ou lista.",
+ "6005": "O utilizador é já membro dessa equipa.",
+ "6006": "Não podes eliminar o último membro da equipa.",
+ "6007": "A equipa não tem acesso à lista para executar essa ação.",
+ "7002": "O utilizador tem já acesso a essa lista.",
+ "7003": "Não tens acesso a essa lista.",
+ "8001": "Esta etiqueta já existe nessa lista.",
+ "8002": "A etiqueta não existe.",
+ "8003": "Não tens acesso a esta etiqueta.",
+ "9001": "A permissão é inválida.",
+ "10001": "O conjunto não existe.",
+ "10002": "O conjunto não pertence a essa lista.",
+ "10003": "Não podes eliminar o último conjunto numa lista.",
+ "10004": "Não podes adicionar a tarefa a este conjunto, pois dessa forma excede-se o seu limite de tarefas.",
+ "10005": "Só pode existir um conjunto concluído por lista.",
+ "11001": "O filtro memorizado não existe.",
+ "11002": "Filtros memorizados não estão disponíveis para links partilhados.",
+ "12001": "O tipo de entidade da subscrição é inválido.",
+ "12002": "Já estás subcrito na entidade ou numa entidade parente.",
+ "13001": "Este link partilhado requer uma palavra-passe para autenticação, mas nenhuma foi fornecida.",
+ "13002": "A palavra-passe fornecida para o link partilhado é inválida."
},
"about": {
- "title": "About",
- "frontendVersion": "Frontend Version: {version}",
- "apiVersion": "API Version: {version}"
+ "title": "Sobre",
+ "frontendVersion": "Versão Atual: {version}",
+ "apiVersion": "Versão da API: {version}"
}
}
diff --git a/src/i18n/lang/ro-RO.json b/src/i18n/lang/ro-RO.json
index 1f03d175b..86f71fc1e 100644
--- a/src/i18n/lang/ro-RO.json
+++ b/src/i18n/lang/ro-RO.json
@@ -31,10 +31,9 @@
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
+ "email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password",
@@ -45,12 +44,20 @@
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
- "register": "Register",
+ "createAccount": "Create account",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Logout",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Settings",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
- "defaultList": "Default List"
+ "defaultList": "Default List",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Two Factor Authentication",
@@ -327,6 +335,7 @@
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Reset Color",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
+ "percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
"reminders": "Reminders",
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +855,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/ru-RU.json b/src/i18n/lang/ru-RU.json
index 49b46546d..0e446c169 100644
--- a/src/i18n/lang/ru-RU.json
+++ b/src/i18n/lang/ru-RU.json
@@ -31,10 +31,9 @@
"username": "Имя пользователя",
"usernameEmail": "Имя пользователя или Email",
"usernamePlaceholder": "напр. frederick",
- "email": "E-mail адрес",
+ "email": "Email address",
"emailPlaceholder": "напр. frederic{'@'}vikunja.io",
"password": "Пароль",
- "passwordRepeat": "Пароль ещё раз",
"passwordPlaceholder": "напр. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Сбросить пароль",
@@ -45,12 +44,20 @@
"totpTitle": "Код двухфакторной аутентификации",
"totpPlaceholder": "напр. 123456",
"login": "Войти",
- "register": "Зарегистрироваться",
+ "createAccount": "Create account",
"loginWith": "Войти через {provider}",
"authenticating": "Аутентификация…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Выйти"
+ "logout": "Выйти",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Настройки",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Твой текущий пароль",
"passwordsDontMatch": "Новые пароли не совпадают.",
"passwordUpdateSuccess": "Пароль изменён.",
- "updateEmailTitle": "Изменить E-mail",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "Новый Email адрес",
"updateEmailSuccess": "E-mail успешно изменён. Для подтверждения нажми на ссылку в письме, которое мы тебе отправили.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Воскресенье",
"weekStartMonday": "Понедельник",
"language": "Язык",
- "defaultList": "Список по умолчанию"
+ "defaultList": "Список по умолчанию",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Двухфакторная аутентификация",
@@ -97,7 +105,7 @@
"caldav": {
"title": "CalDAV",
"howTo": "Ты можешь подключить Vikunja к клиентам CalDAV, чтобы просматривать и управлять всеми задачами из разных клиентов. Введи этот URL в свой клиент:",
- "more": "Подробнее о caldav в Vikunja"
+ "more": "Подробнее о CalDAV в Vikunja"
},
"avatar": {
"title": "Аватар",
@@ -327,6 +335,7 @@
"archiveText": "Ты не сможешь изменять это пространство имён, пока не вернёшь его из архива. Это также касается всех списков в этом пространстве имён.",
"unarchiveText": "Ты сможешь создавать новые списки или изменять их.",
"success": "Пространство имён архивировано.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "Архивирование пространства имён означает, что ты не сможешь создавать в нём новые списки или изменять их."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Показывать завершённые задачи",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Вкл. фильтр по приоритету",
- "enablePercentDone": "По % завершения",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Диапазон срока",
"startDateRange": "Диапазон даты начала",
"endDateRange": "Диапазон даты завершения",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Сбросить цвет",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Создать",
"selectPlaceholder": "Кликните или нажмите Enter для выбора"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Текущие задачи",
"titleDates": "Задачи с {from} по {to}",
"noDates": "Показать задачи без даты",
- "current": "Текущие задачи",
- "from": "Задачи с",
- "until": "по",
- "today": "Сегодня",
- "nextWeek": "Неделя",
- "nextMonth": "Месяц",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "Будут удалены все вложения, напоминания и отношения, связанные с этой задачей, и отменить это будет нельзя!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Добавить метки",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Установить приоритет",
"dueDate": "Установить срок",
- "startDate": "Установить дату начала",
- "endDate": "Установить дату завершения",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Установить напоминания",
- "repeatAfter": "Установить интервал повтора",
- "percentDone": "Установить процент завершения",
- "attachments": "Добавить вложения",
- "relatedTasks": "Добавить связанные задачи",
- "moveList": "Переместить задачу",
- "color": "Установить цвет задачи",
- "delete": "Удалить задачу",
- "favorite": "Сохранить как ибзранное",
- "unfavorite": "Убрать из избранного"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Срок",
"endDate": "Дата завершения",
"labels": "Метки",
- "percentDone": "% Завершено",
+ "percentDone": "Progress",
"priority": "Приоритет",
"relatedTasks": "Связанные задачи",
"reminders": "Напоминания",
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "Работают на всех страницах.",
"currentPageOnly": "Работают только на текущей странице.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Переключить меню",
"quickSearch": "Открыть панель поиска/быстрых действий",
"then": "then",
"task": {
"title": "Страница задачи",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Добавить метки этой задаче",
"dueDate": "Изменить срок этой задачи",
"attachment": "Добавить вложение к задаче",
- "related": "Изменить связанные задачи"
+ "related": "Изменить связанные задачи",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +855,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/sv-SE.json b/src/i18n/lang/sv-SE.json
index 1f03d175b..86f71fc1e 100644
--- a/src/i18n/lang/sv-SE.json
+++ b/src/i18n/lang/sv-SE.json
@@ -31,10 +31,9 @@
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
+ "email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password",
@@ -45,12 +44,20 @@
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
- "register": "Register",
+ "createAccount": "Create account",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Logout",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Settings",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
- "defaultList": "Default List"
+ "defaultList": "Default List",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Two Factor Authentication",
@@ -327,6 +335,7 @@
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Reset Color",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
+ "percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
"reminders": "Reminders",
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +855,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/tr-TR.json b/src/i18n/lang/tr-TR.json
index 1f03d175b..86f71fc1e 100644
--- a/src/i18n/lang/tr-TR.json
+++ b/src/i18n/lang/tr-TR.json
@@ -31,10 +31,9 @@
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
- "email": "E-mail address",
+ "email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
- "passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"forgotPassword": "Forgot your password?",
"resetPassword": "Reset your password",
@@ -45,12 +44,20 @@
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
- "register": "Register",
+ "createAccount": "Create account",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
- "logout": "Logout"
+ "logout": "Logout",
+ "emailInvalid": "Please enter a valid email address.",
+ "usernameRequired": "Please provide a username.",
+ "passwordRequired": "Please provide a password.",
+ "showPassword": "Show the password",
+ "hidePassword": "Hide the password",
+ "noAccountYet": "Don't have an account yet?",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "remember": "Stay logged in"
},
"settings": {
"title": "Settings",
@@ -61,7 +68,7 @@
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
- "updateEmailTitle": "Update Your E-Mail Address",
+ "updateEmailTitle": "Update Your Email Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
@@ -78,7 +85,8 @@
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
- "defaultList": "Default List"
+ "defaultList": "Default List",
+ "timezone": "Time Zone"
},
"totp": {
"title": "Two Factor Authentication",
@@ -327,6 +335,7 @@
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
+ "unarchiveSuccess": "The namespace was successfully un-archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
@@ -376,7 +385,7 @@
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
- "enablePercentDone": "Enable Filter By Percent Done",
+ "enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@@ -476,7 +485,8 @@
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
- "welcomeBack": "Welcome Back!"
+ "welcomeBack": "Welcome Back!",
+ "custom": "Custom"
},
"input": {
"resetColor": "Reset Color",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
+ },
+ "datepickerRange": {
+ "to": "To",
+ "from": "From",
+ "fromto": "{from} to {to}",
+ "ranges": {
+ "today": "Today",
+ "thisWeek": "This Week",
+ "restOfThisWeek": "The Rest of This Week",
+ "nextWeek": "Next Week",
+ "next7Days": "Next 7 Days",
+ "lastWeek": "Last Week",
+ "thisMonth": "This Month",
+ "restOfThisMonth": "The Rest of This Month",
+ "nextMonth": "Next Month",
+ "next30Days": "Next 30 Days",
+ "lastMonth": "Last Month",
+ "thisYear": "This Year",
+ "restOfThisYear": "The Rest of This Year"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "You can use date math to filter for relative dates.",
+ "learnhow": "Check out how it works",
+ "title": "Date Math",
+ "intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
+ "expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
+ "similar": "These expressions are similar to the ones provided by {0} and {1}.",
+ "add1Day": "Add one day",
+ "minus1Day": "Subtract one day",
+ "roundDay": "Round down to the nearest day",
+ "supportedUnits": "Supported time units are:",
+ "someExamples": "Some examples of time expressions:",
+ "units": {
+ "seconds": "Seconds",
+ "minutes": "Minutes",
+ "hours": "Hours",
+ "days": "Days",
+ "weeks": "Weeks",
+ "months": "Months",
+ "years": "Years"
+ },
+ "examples": {
+ "now": "Right now",
+ "in24h": "In 24h",
+ "today": "Today at 00:00",
+ "beginningOfThisWeek": "The beginning of this week at 00:00",
+ "endOfThisWeek": "The end of this week",
+ "in30Days": "In 30 days",
+ "datePlusMonth": "{0} plus one month at 00:00 of that day"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
- "current": "Current tasks",
- "from": "Tasks from",
- "until": "until",
- "today": "Today",
- "nextWeek": "Next Week",
- "nextMonth": "Next Month",
+ "overdue": "Show overdue tasks",
+ "fromuntil": "Tasks from {from} until {until}",
+ "select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
"detail": {
@@ -561,22 +619,22 @@
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
- "assign": "Assign to a user",
- "label": "Add labels",
+ "assign": "Assign to User",
+ "label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
- "startDate": "Set a Start Date",
- "endDate": "Set an End Date",
+ "startDate": "Set Start Date",
+ "endDate": "Set End Date",
"reminders": "Set Reminders",
- "repeatAfter": "Set a repeating interval",
- "percentDone": "Set Percent Done",
- "attachments": "Add attachments",
- "relatedTasks": "Add task relations",
- "moveList": "Move task",
- "color": "Set task color",
- "delete": "Delete task",
- "favorite": "Save as favorite",
- "unfavorite": "Remove from favorites"
+ "repeatAfter": "Set Repeating Interval",
+ "percentDone": "Set Progress",
+ "attachments": "Add Attachments",
+ "relatedTasks": "Add Relation",
+ "moveList": "Move",
+ "color": "Set Color",
+ "delete": "Delete",
+ "favorite": "Add to Favorites",
+ "unfavorite": "Remove from Favorites"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Labels",
- "percentDone": "% Done",
+ "percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
"reminders": "Reminders",
@@ -776,17 +834,20 @@
"general": "General",
"allPages": "These shortcuts work on all pages.",
"currentPageOnly": "These shortcuts work only on the current page.",
+ "somePagesOnly": "These shortcuts work only on some pages.",
"toggleMenu": "Toggle The Menu",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"task": {
"title": "Task Page",
- "done": "Done",
- "assign": "Assign to a user",
+ "done": "Mark task done / undone",
+ "assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
- "related": "Modify related tasks of this task"
+ "related": "Modify related tasks of this task",
+ "color": "Change the color of this task",
+ "move": "Move this task to another list"
},
"list": {
"title": "List Views",
@@ -794,6 +855,14 @@
"switchToGanttView": "Switch to gantt view",
"switchToKanbanView": "Switch to kanban view",
"switchToTableView": "Switch to table view"
+ },
+ "navigation": {
+ "title": "Navigation",
+ "overview": "Navigato to overview",
+ "upcoming": "Navigato to upcoming taks",
+ "namespaces": "Navigate to namepaces & lists",
+ "labels": "Navigate to labels",
+ "teams": "Navigate to teams"
}
},
"update": {
diff --git a/src/i18n/lang/vi-VN.json b/src/i18n/lang/vi-VN.json
index d027aae70..bd44c559b 100644
--- a/src/i18n/lang/vi-VN.json
+++ b/src/i18n/lang/vi-VN.json
@@ -7,7 +7,7 @@
"lastViewed": "Xem gần đây",
"list": {
"newText": "Bạn có thể tạo một danh sách công việc mới cho mình:",
- "new": "New list",
+ "new": "Danh sách mới",
"importText": "Hoặc nhập danh sách và nhiệm vụ của bạn từ các dịch vụ khác vào Vikunja:",
"import": "Nhập dữ liệu của bạn vào Vikunja"
}
@@ -34,7 +34,6 @@
"email": "Địa chỉ Email",
"emailPlaceholder": "ví dụ: frederic{'@'}vikunja.io",
"password": "Mật khẩu",
- "passwordRepeat": "Nhập lại mật khẩu",
"passwordPlaceholder": "ví dụ: •••••••••••",
"forgotPassword": "Bạn quên mật khẩu?",
"resetPassword": "Reset mật khẩu của bạn",
@@ -45,12 +44,20 @@
"totpTitle": "Mã xác thực hai lớp",
"totpPlaceholder": "ví dụ: 123456",
"login": "Đăng nhập",
- "register": "Đăng ký",
+ "createAccount": "Tạo tài khoản",
"loginWith": "Đăng nhập với {provider}",
"authenticating": "Đang xác thực…",
"openIdStateError": "Trạng thái không khớp, từ chối tiếp tục!",
"openIdGeneralError": "Đã xảy ra lỗi khi xác thực với bên thứ ba.",
- "logout": "Đăng xuất"
+ "logout": "Đăng xuất",
+ "emailInvalid": "Vui lòng nhập một địa chỉ email hợp lệ.",
+ "usernameRequired": "Vui lòng cung cấp tên người dùng.",
+ "passwordRequired": "Vui lòng cung cấp một mật khẩu.",
+ "showPassword": "Hiển thị mật khẩu",
+ "hidePassword": "Ẩn mật khẩu",
+ "noAccountYet": "Bạn chưa có tài khoản?",
+ "alreadyHaveAnAccount": "Đã có tài khoản?",
+ "remember": "Duy trì đăng nhập"
},
"settings": {
"title": "Cài đặt",
@@ -78,7 +85,8 @@
"weekStartSunday": "Chủ nhật",
"weekStartMonday": "Thứ hai",
"language": "Ngôn ngữ",
- "defaultList": "Danh sách mặc định"
+ "defaultList": "Danh sách mặc định",
+ "timezone": "Múi giờ"
},
"totp": {
"title": "Xác thực hai lớp",
@@ -95,7 +103,7 @@
"disableSuccess": "Xác thực hai lớp đã bị vô hiệu hóa thành công."
},
"caldav": {
- "title": "Giao thức Caldav",
+ "title": "Giao thức CalDAV",
"howTo": "Bạn có thể kết nối Vikunja tới các máy khách CalDAV để xem và quản lý tất cả các công việc từ nhiều máy khách khác nhau. Nhập URL này vào ứng dụng khách của bạn:",
"more": "Tìm hiểu thêm về CalDAV"
},
@@ -157,7 +165,7 @@
"searchSelect": "Nhấp hoặc nhấn enter để chọn danh sách này",
"shared": "Đang tham gia",
"create": {
- "header": "New list",
+ "header": "Danh sách mới",
"titlePlaceholder": "Tên danh sách ở đây…",
"addTitleRequired": "Hãy xác định một tên.",
"createdSuccess": "Danh sách đã được tạo thành công.",
@@ -315,7 +323,7 @@
"namespaces": "Góc làm việc",
"search": "Gõ để tìm kiếm một góc làm việc…",
"create": {
- "title": "New namespace",
+ "title": "Góc làm việc mới",
"titleRequired": "Hãy đặt một tiêu đề.",
"explanation": "Góc làm việc là một tập hợp các danh sách mà bạn có thể chia sẻ và sử dụng để sắp xếp các danh sách của mình. Trên thực tế, mọi danh sách đều thuộc về một góc làm việc.",
"tooltip": "Góc làm việc là gì?",
@@ -327,6 +335,7 @@
"archiveText": "Bạn sẽ không thể chỉnh sửa góc làm việc này hoặc tạo danh sách mới cho đến khi bạn bỏ lưu trữ nó. Điều này cũng sẽ lưu trữ tất cả các danh sách trong góc làm việc này.",
"unarchiveText": "Bạn có thể tạo danh sách mới hoặc chỉnh sửa nó.",
"success": "Góc làm việc đã lưu trữ thành công.",
+ "unarchiveSuccess": "Góc làm việc đã được bỏ lưu trữ thành công.",
"description": "Nếu một góc làm việc được lưu trữ, bạn không thể tạo thêm danh sách hoặc chỉnh sửa nó."
},
"delete": {
@@ -376,14 +385,14 @@
"showDoneTasks": "Hiển thị các công việc đã hoàn thành",
"sortAlphabetically": "Xếp theo bảng chữ cái",
"enablePriority": "Bật Bộ lọc theo mức độ ưu tiên",
- "enablePercentDone": "Bật Bộ lọc theo tỉ lệ % hoàn thành",
+ "enablePercentDone": "Bật Bộ lọc theo Tiến độ",
"dueDateRange": "Phạm vi ngày đến hạn",
"startDateRange": "Phạm vi Ngày bắt đầu",
"endDateRange": "Phạm vi Ngày Kết thúc",
"reminderRange": "Phạm vi Ngày nhắc nhở"
},
"create": {
- "title": "New Saved Filter",
+ "title": "Bộ lọc đã lưu mới",
"description": "Bộ lọc sẵn là một danh sách ảo được chọn từ một tập hợp các bộ lọc. Sau khi được tạo, nó sẽ xuất hiện trong một không gian làm việc đặc biệt.",
"action": "Tạo thêm bộ lọc sẵn"
},
@@ -476,7 +485,8 @@
"showMenu": "Hiển thị menu",
"hideMenu": "Ẩn menu",
"forExample": "Ví dụ:",
- "welcomeBack": "Mừng quá! Bạn trở lại rồi."
+ "welcomeBack": "Mừng quá! Bạn trở lại rồi.",
+ "custom": "Tuỳ chỉnh"
},
"input": {
"resetColor": "Đặt lại màu",
@@ -515,6 +525,57 @@
"multiselect": {
"createPlaceholder": "Tạo mới",
"selectPlaceholder": "Nhấp hoặc nhấn enter để chọn"
+ },
+ "datepickerRange": {
+ "to": "Đến",
+ "from": "Từ",
+ "fromto": "{from} đến {to}",
+ "ranges": {
+ "today": "Hôm nay",
+ "thisWeek": "Tuần này",
+ "restOfThisWeek": "Toàn bộ ngày còn lại trong tuần này",
+ "nextWeek": "Tuần tới",
+ "next7Days": "7 ngày tới",
+ "lastWeek": "Tuần trước",
+ "thisMonth": "Tháng này",
+ "restOfThisMonth": "Toàn bộ ngày còn lại trong tháng này",
+ "nextMonth": "Tháng tới",
+ "next30Days": "30 ngày tới",
+ "lastMonth": "Tháng trước",
+ "thisYear": "Năm nay",
+ "restOfThisYear": "Toàn bộ ngày còn lại trong năm"
+ }
+ },
+ "datemathHelp": {
+ "canuse": "Bạn có thể sử dụng biểu thức tính ngày để lọc những ngày liên quan.",
+ "learnhow": "Xem cách hoạt động",
+ "title": "Tính Ngày",
+ "intro": "Tính Ngày cho phép bạn xác định những ngày liên quan được xử lý bởi Vikunja khi áp dụng bộ lọc.",
+ "expression": "Mỗi Biểu thức tính ngày bắt đầu bằng một ngày cố định, có thể là {0}, hoặc kết thúc bằng {1}. Ngày cố định này có thể được theo sau bởi một hoặc nhiều biểu thức toán học.",
+ "similar": "Những biểu thức này tương tự như những biểu thức được cung cấp bởi {0} và {1}.",
+ "add1Day": "Thêm một ngày",
+ "minus1Day": "Bớt đi một ngày",
+ "roundDay": "Làm tròn đến ngày gần nhất",
+ "supportedUnits": "Các đơn vị thời gian hỗ trợ là:",
+ "someExamples": "Một vài ví dụ về cách hiển thị thời gian:",
+ "units": {
+ "seconds": "Giây",
+ "minutes": "Phút",
+ "hours": "Giờ",
+ "days": "Ngày",
+ "weeks": "Tuần",
+ "months": "Tháng",
+ "years": "Năm"
+ },
+ "examples": {
+ "now": "Ngay bây giờ",
+ "in24h": "Trong vòng 24h",
+ "today": "Hôm nay lúc 00:00",
+ "beginningOfThisWeek": "Bắt đầu tuần này lúc 00:00",
+ "endOfThisWeek": "Cuối tuần này",
+ "in30Days": "Trong 30 ngày",
+ "datePlusMonth": "{0} thêm một tháng kể từ lúc 00:00 ngày hôm đó"
+ }
}
},
"task": {
@@ -532,12 +593,9 @@
"titleCurrent": "Công việc hiện tại",
"titleDates": "Công việc từ {from} cho đến {to}",
"noDates": "Hiển thị công việc không có ngày tháng",
- "current": "Công việc hiện tại",
- "from": "Công việc từ",
- "until": "cho đến",
- "today": "Hôm nay",
- "nextWeek": "Tuần tới",
- "nextMonth": "Tháng tới",
+ "overdue": "Hiển thị công việc quá hạn",
+ "fromuntil": "Công việc từ {from} cho đến {until}",
+ "select": "Chọn khoảng ngày",
"noTasks": "Hôm nay thảnh thơi — Hãy tận hưởng ngày tuyệt vời!"
},
"detail": {
@@ -545,7 +603,7 @@
"chooseStartDate": "Bấm vào đây để đặt ngày bắt đầu",
"chooseEndDate": "Bấm vào đây để đặt ngày kết thúc",
"move": "Di chuyển công việc sang một danh sách khác",
- "done": "Mark task done!",
+ "done": "Đánh dấu đã xong!",
"undone": "Đánh dấu Chưa xong",
"created": "Đã tạo được {0} bởi {1}",
"updated": "Đã cập nhật {0}",
@@ -561,22 +619,22 @@
"text2": "Thao tác này cũng sẽ xóa tất cả tệp đính kèm, lời nhắc và liên kết đến công việc này. Nó không thể hoàn tác!"
},
"actions": {
- "assign": "Chỉ định một người",
+ "assign": "Chỉ định người",
"label": "Thêm nhãn",
"priority": "Mức độ ưu tiên",
"dueDate": "Đặt ngày đến hạn",
"startDate": "Chọn ngày bắt đầu",
"endDate": "Chọn ngày kết thúc",
"reminders": "Thiết lập nhắc nhở",
- "repeatAfter": "Đặt khoảng lặp lại",
- "percentDone": "Chọn tỉ lệ hoàn thành",
- "attachments": "Đính kèm tệp",
- "relatedTasks": "Thêm liên kết công việc",
- "moveList": "Di chuyển công việc",
- "color": "Chọn màu",
- "delete": "Loại bỏ công việc",
- "favorite": "Lưu thành ưa thích",
- "unfavorite": "Gỡ khỏi ưa thích"
+ "repeatAfter": "Chọn chu kì lặp",
+ "percentDone": "Đặt tiến trình",
+ "attachments": "Thêm Tệp đính kèm",
+ "relatedTasks": "Thêm Quan hệ",
+ "moveList": "Di chuyển",
+ "color": "Đặt màu",
+ "delete": "Xóa",
+ "favorite": "Thêm vào mục yêu thích",
+ "unfavorite": "Xóa khỏi mục yêu thích"
}
},
"attributes": {
@@ -589,7 +647,7 @@
"dueDate": "Ngày hết hạn",
"endDate": "Ngày kết thúc",
"labels": "Nhãn",
- "percentDone": "Tiến độ hoàn thành",
+ "percentDone": "Tiến độ",
"priority": "Mức độ ưu tiên",
"relatedTasks": "Công việc liên quan",
"reminders": "Nhắc nhở",
@@ -776,17 +834,20 @@
"general": "Tổng quan",
"allPages": "Các phím tắt này hoạt động ở mọi nơi.",
"currentPageOnly": "Các phím tắt này chỉ hoạt động ở trang hiện tại.",
+ "somePagesOnly": "Các phím tắt này chỉ hoạt động trên một số trang.",
"toggleMenu": "Bật/tắt Menu",
"quickSearch": "Mở thanh tìm kiếm/thao tác nhanh",
"then": "sau đó",
"task": {
"title": "Trang công việc",
- "done": "Done",
- "assign": "Chỉ định một người",
+ "done": "Đánh dấu công việc hoàn thành / chưa xong",
+ "assign": "Chỉ định công việc cho một người",
"labels": "Thêm nhãn cho công việc này",
"dueDate": "Thay đổi ngày hết hạn của công việc này",
"attachment": "Thêm tệp đính kèm cho công việc này",
- "related": "Sửa đổi các công việc liên kết"
+ "related": "Sửa đổi các công việc liên kết",
+ "color": "Thay đổi màu công việc này",
+ "move": "Dời công việc này sang danh sách khác"
},
"list": {
"title": "Xem danh sách",
@@ -794,6 +855,14 @@
"switchToGanttView": "Chuyển sang biểu đồ Gantt",
"switchToKanbanView": "Chuyển sang Kanban",
"switchToTableView": "Chuyển qua xem Bảng"
+ },
+ "navigation": {
+ "title": "Điều hướng",
+ "overview": "Điều hướng đến trang tổng quan",
+ "upcoming": "Điều hướng đến công việc sắp tới",
+ "namespaces": "Điều hướng đến Không gian làm việc & danh sách",
+ "labels": "Điều hướng đến nhãn",
+ "teams": "Điều hướng đến Team"
}
},
"update": {
@@ -899,7 +968,7 @@
"4015": "Bình luận không tồn tại.",
"4016": "Trường công việc không hợp lệ.",
"4017": "Bộ so sánh bộ lọc công việc không hợp lệ.",
- "4018": "Invalid task filter concatenator.",
+ "4018": "Bộ lọc kết hợp không hợp lệ.",
"4019": "Giá trị bộ lọc công việc không hợp lệ.",
"5001": "Góc làm việc không có nữa.",
"5003": "Bạn chưa được phép bước vào vào góc làm việc được chỉ định.",
diff --git a/src/icons.js b/src/icons.js
index 48ef230c6..89e1d1a3e 100644
--- a/src/icons.js
+++ b/src/icons.js
@@ -16,6 +16,8 @@ import {
faCocktail,
faCoffee,
faCog,
+ faEye,
+ faEyeSlash,
faEllipsisH,
faEllipsisV,
faExclamation,
@@ -87,6 +89,8 @@ library.add(faCocktail)
library.add(faCoffee)
library.add(faCog)
library.add(faComments)
+library.add(faEye)
+library.add(faEyeSlash)
library.add(faEllipsisH)
library.add(faEllipsisV)
library.add(faExclamation)
diff --git a/src/main.ts b/src/main.ts
index 45108a742..b4ed880a2 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -18,7 +18,7 @@ declare global {
}
}
-import {formatDate, formatDateShort, formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
+import {formatDate, formatDateShort, formatDateLong, formatDateSince, formatISO} from '@/helpers/time/formatDate'
// @ts-ignore
import {VERSION} from './version.json'
@@ -52,6 +52,7 @@ app.use(Notifications)
// directives
import focus from '@/directives/focus'
+// @ts-ignore The export does exist, ts just doesn't find it.
import { VTooltip } from 'v-tooltip'
import 'v-tooltip/dist/v-tooltip.css'
import shortcut from '@/directives/shortcut'
@@ -84,6 +85,7 @@ app.mixin({
format: formatDate,
formatDate: formatDateLong,
formatDateShort: formatDateShort,
+ formatISO,
getNamespaceTitle,
getListTitle,
setTitle,
diff --git a/src/message/index.js b/src/message/index.js
index 0f2ad5064..c960bf84b 100644
--- a/src/message/index.js
+++ b/src/message/index.js
@@ -35,7 +35,6 @@ export function error(e, actions = []) {
text: getErrorText(e),
actions: actions,
})
- console.error(e, actions)
}
export function success(e, actions = []) {
diff --git a/src/models/emailUpdate.js b/src/models/emailUpdate.js
index 42fe91e35..04ed30453 100644
--- a/src/models/emailUpdate.js
+++ b/src/models/emailUpdate.js
@@ -4,7 +4,7 @@ export default class EmailUpdateModel extends AbstractModel {
defaults() {
return {
newEmail: '',
- passwort: '',
+ password: '',
}
}
}
\ No newline at end of file
diff --git a/src/models/task.js b/src/models/task.js
index c03342370..638396898 100644
--- a/src/models/task.js
+++ b/src/models/task.js
@@ -10,9 +10,6 @@ import {parseDateOrNull} from '@/helpers/parseDateOrNull'
const SUPPORTS_TRIGGERED_NOTIFICATION = 'Notification' in window && 'showTrigger' in Notification.prototype
export default class TaskModel extends AbstractModel {
-
- defaultColor = '198CFF'
-
constructor(data) {
super(data)
diff --git a/src/models/userSettings.js b/src/models/userSettings.js
index 6ed0ffe58..41932e261 100644
--- a/src/models/userSettings.js
+++ b/src/models/userSettings.js
@@ -11,6 +11,7 @@ export default class UserSettingsModel extends AbstractModel {
overdueTasksRemindersEnabled: true,
defaultListId: undefined,
weekStart: 0,
+ timezone: '',
}
}
}
\ No newline at end of file
diff --git a/src/modules/parseTaskText.test.js b/src/modules/parseTaskText.test.js
index 19f3ba17a..30e5743c4 100644
--- a/src/modules/parseTaskText.test.js
+++ b/src/modules/parseTaskText.test.js
@@ -1,4 +1,4 @@
-import {describe, it, expect} from 'vitest'
+import {beforeEach, afterEach, describe, it, expect, vi} from 'vitest'
import {parseTaskText} from './parseTaskText'
import {getDateFromText, getDateFromTextIn} from '../helpers/time/parseDate'
@@ -6,6 +6,14 @@ import {calculateDayInterval} from '../helpers/time/calculateDayInterval'
import priorities from '../models/constants/priorities.json'
describe('Parse Task Text', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
it('should return text with no intents as is', () => {
expect(parseTaskText('Lorem Ipsum').text).toBe('Lorem Ipsum')
})
@@ -32,7 +40,7 @@ describe('Parse Task Text', () => {
expect(result.assignees).toHaveLength(1)
expect(result.assignees[0]).toBe('user')
})
-
+
it('should ignore email addresses', () => {
const text = 'Lorem Ipsum email@example.com'
const result = parseTaskText(text)
@@ -211,17 +219,36 @@ describe('Parse Task Text', () => {
expect(`${result.date.getHours()}:${result.date.getMinutes()}`).toBe('14:0')
})
it('should recognize dates of the month in the past but next month', () => {
- const date = new Date()
- date.setDate(date.getDate() - 1)
- const result = parseTaskText(`Lorem Ipsum ${date.getDate()}nd`)
+ const time = new Date(2022, 0, 15)
+ vi.setSystemTime(time)
+
+ const result = parseTaskText(`Lorem Ipsum ${time.getDate() - 1}th`)
expect(result.text).toBe('Lorem Ipsum')
- expect(result.date.getDate()).toBe(date.getDate())
+ expect(result.date.getDate()).toBe(time.getDate() - 1)
+ expect(result.date.getMonth()).toBe(time.getMonth() + 1)
+ })
+ it('should recognize dates of the month in the past but next month when february is the next month', () => {
+ const jan = new Date(2022, 0, 30)
+ vi.setSystemTime(jan)
- const nextMonthWithDate = result.date.getDate() === 31
- ? (date.getMonth() + 2) % 12
- : (date.getMonth() + 1) % 12
- expect(result.date.getMonth()).toBe(nextMonthWithDate)
+ const result = parseTaskText(`Lorem Ipsum ${jan.getDate() - 1}th`)
+
+ const expectedDate = new Date(2022, 2, jan.getDate() - 1)
+ expect(result.text).toBe('Lorem Ipsum')
+ expect(result.date.getDate()).toBe(expectedDate.getDate())
+ expect(result.date.getMonth()).toBe(expectedDate.getMonth())
+ })
+ it('should recognize dates of the month in the past but next month when the next month has less days than this one', () => {
+ const mar = new Date(2022, 2, 32)
+ vi.setSystemTime(mar)
+
+ const result = parseTaskText(`Lorem Ipsum 31st`)
+
+ const expectedDate = new Date(2022, 4, 31)
+ expect(result.text).toBe('Lorem Ipsum')
+ expect(result.date.getDate()).toBe(expectedDate.getDate())
+ expect(result.date.getMonth()).toBe(expectedDate.getMonth())
})
it('should recognize dates of the month in the future', () => {
const nextDay = new Date(+new Date() + 60 * 60 * 24 * 1000)
@@ -242,6 +269,12 @@ describe('Parse Task Text', () => {
expect(result.text).toBe('Lorem Ipsum github')
expect(result.date).toBeNull()
})
+ it('should not recognize date number with no spacing around them', () => {
+ const result = parseTaskText('Lorem Ispum v1.1.1')
+
+ expect(result.text).toBe('Lorem Ispum v1.1.1')
+ expect(result.date).toBeNull()
+ })
describe('Parse weekdays', () => {
diff --git a/src/router/index.ts b/src/router/index.ts
index 7b28a509c..a1f6a2e63 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -2,6 +2,10 @@ import { createRouter, createWebHistory, RouteLocation } from 'vue-router'
import {saveLastVisited} from '@/helpers/saveLastVisited'
import {store} from '@/store'
+import {saveListView, getListView} from '@/helpers/saveListView'
+import {parseDateOrString} from '@/helpers/time/parseDateOrString'
+import {getNextWeekDate} from '@/helpers/time/getNextWeekDate'
+
import HomeComponent from '../views/Home.vue'
import NotFoundComponent from '../views/404.vue'
import About from '../views/About.vue'
@@ -11,11 +15,10 @@ import RegisterComponent from '../views/user/Register.vue'
import OpenIdAuth from '../views/user/OpenIdAuth.vue'
import DataExportDownload from '../views/user/DataExportDownload.vue'
// Tasks
-import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange.vue'
+import UpcomingTasksComponent from '../views/tasks/ShowTasks.vue'
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth.vue'
-import TaskDetailViewModal from '../views/tasks/TaskDetailViewModal.vue'
-import TaskDetailView from '../views/tasks/TaskDetailView.vue'
import ListNamespaces from '../views/namespaces/ListNamespaces.vue'
+import TaskDetailView from '../views/tasks/TaskDetailView.vue'
// Team Handling
import ListTeamsComponent from '../views/teams/ListTeams.vue'
// Label Handling
@@ -25,11 +28,11 @@ import NewLabelComponent from '../views/labels/NewLabel.vue'
import MigrationComponent from '../views/migrator/Migrate.vue'
import MigrateServiceComponent from '../views/migrator/MigrateService.vue'
// List Views
-import ShowListComponent from '../views/list/ShowList.vue'
-import Kanban from '../views/list/views/Kanban.vue'
-import List from '../views/list/views/List.vue'
-import Gantt from '../views/list/views/Gantt.vue'
-import Table from '../views/list/views/Table.vue'
+import ListList from '../views/list/ListList.vue'
+import ListGantt from '../views/list/ListGantt.vue'
+import ListTable from '../views/list/ListTable.vue'
+import ListKanban from '../views/list/ListKanban.vue'
+
// List Settings
import ListSettingEdit from '../views/list/settings/edit.vue'
import ListSettingBackground from '../views/list/settings/background.vue'
@@ -80,7 +83,7 @@ const router = createRouter({
// Scroll to anchor should still work
if (to.hash) {
- return {el: document.getElementById(to.hash.slice(1))}
+ return {el: to.hash}
}
// Otherwise just scroll to the top
@@ -132,7 +135,7 @@ const router = createRouter({
name: 'user.register',
component: RegisterComponent,
meta: {
- title: 'user.auth.register',
+ title: 'user.auth.createAccount',
},
},
{
@@ -201,320 +204,176 @@ const router = createRouter({
{
path: '/namespaces/new',
name: 'namespace.create',
- components: {
- popup: NewNamespaceComponent,
- },
- },
- {
- path: '/namespaces/:id/list',
- name: 'list.create',
- components: {
- popup: NewListComponent,
+ component: NewNamespaceComponent,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/namespaces/:id/settings/edit',
name: 'namespace.settings.edit',
- components: {
- popup: NamespaceSettingEdit,
+ component: NamespaceSettingEdit,
+ meta: {
+ showAsModal: true,
},
},
{
- path: '/namespaces/:id/settings/share',
+ path: '/namespaces/:namespaceId/settings/share',
name: 'namespace.settings.share',
- components: {
- popup: NamespaceSettingShare,
+ component: NamespaceSettingShare,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/namespaces/:id/settings/archive',
name: 'namespace.settings.archive',
- components: {
- popup: NamespaceSettingArchive,
+ component: NamespaceSettingArchive,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/namespaces/:id/settings/delete',
name: 'namespace.settings.delete',
- components: {
- popup: NamespaceSettingDelete,
+ component: NamespaceSettingDelete,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/tasks/:id',
name: 'task.detail',
component: TaskDetailView,
+ props: route => ({ taskId: parseInt(route.params.id as string) }),
},
{
path: '/tasks/by/upcoming',
name: 'tasks.range',
- component: ShowTasksInRangeComponent,
+ component: UpcomingTasksComponent,
+ props: route => ({
+ dateFrom: parseDateOrString(route.query.from as string, new Date()),
+ dateTo: parseDateOrString(route.query.to as string, getNextWeekDate()),
+ showNulls: route.query.showNulls === 'true',
+ showOverdue: route.query.showOverdue === 'true',
+ }),
+ },
+ {
+ path: '/lists/new/:namespaceId/',
+ name: 'list.create',
+ component: NewListComponent,
+ meta: {
+ showAsModal: true,
+ },
},
{
path: '/lists/:listId/settings/edit',
name: 'list.settings.edit',
- components: {
- popup: ListSettingEdit,
+ component: ListSettingEdit,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/background',
name: 'list.settings.background',
- components: {
- popup: ListSettingBackground,
+ component: ListSettingBackground,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/duplicate',
name: 'list.settings.duplicate',
- components: {
- popup: ListSettingDuplicate,
+ component: ListSettingDuplicate,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/share',
name: 'list.settings.share',
- components: {
- popup: ListSettingShare,
+ component: ListSettingShare,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/delete',
name: 'list.settings.delete',
- components: {
- popup: ListSettingDelete,
+ component: ListSettingDelete,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/archive',
name: 'list.settings.archive',
- components: {
- popup: ListSettingArchive,
+ component: ListSettingArchive,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/edit',
name: 'filter.settings.edit',
- components: {
- popup: FilterEdit,
+ component: FilterEdit,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId/settings/delete',
name: 'filter.settings.delete',
- components: {
- popup: FilterDelete,
+ component: FilterDelete,
+ meta: {
+ showAsModal: true,
},
},
{
path: '/lists/:listId',
name: 'list.index',
- component: ShowListComponent,
- children: [
- {
- path: '/lists/:listId/list',
- name: 'list.list',
- component: List,
- children: [
- {
- path: '/tasks/:id',
- name: 'task.list.detail',
- component: TaskDetailViewModal,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'list.list.settings.edit',
- component: ListSettingEdit,
- },
- {
- path: '/lists/:listId/settings/background',
- name: 'list.list.settings.background',
- component: ListSettingBackground,
- },
- {
- path: '/lists/:listId/settings/duplicate',
- name: 'list.list.settings.duplicate',
- component: ListSettingDuplicate,
- },
- {
- path: '/lists/:listId/settings/share',
- name: 'list.list.settings.share',
- component: ListSettingShare,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'list.list.settings.delete',
- component: ListSettingDelete,
- },
- {
- path: '/lists/:listId/settings/archive',
- name: 'list.list.settings.archive',
- component: ListSettingArchive,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'filter.list.settings.edit',
- component: FilterEdit,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'filter.list.settings.delete',
- component: FilterDelete,
- },
- ],
- },
- {
- path: '/lists/:listId/gantt',
- name: 'list.gantt',
- component: Gantt,
- children: [
- {
- path: '/tasks/:id',
- name: 'task.gantt.detail',
- component: TaskDetailViewModal,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'list.gantt.settings.edit',
- component: ListSettingEdit,
- },
- {
- path: '/lists/:listId/settings/background',
- name: 'list.gantt.settings.background',
- component: ListSettingBackground,
- },
- {
- path: '/lists/:listId/settings/duplicate',
- name: 'list.gantt.settings.duplicate',
- component: ListSettingDuplicate,
- },
- {
- path: '/lists/:listId/settings/share',
- name: 'list.gantt.settings.share',
- component: ListSettingShare,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'list.gantt.settings.delete',
- component: ListSettingDelete,
- },
- {
- path: '/lists/:listId/settings/archive',
- name: 'list.gantt.settings.archive',
- component: ListSettingArchive,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'filter.gantt.settings.edit',
- component: FilterEdit,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'filter.gantt.settings.delete',
- component: FilterDelete,
- },
- ],
- },
- {
- path: '/lists/:listId/table',
- name: 'list.table',
- component: Table,
- children: [
- {
- path: '/lists/:listId/settings/edit',
- name: 'list.table.settings.edit',
- component: ListSettingEdit,
- },
- {
- path: '/lists/:listId/settings/background',
- name: 'list.table.settings.background',
- component: ListSettingBackground,
- },
- {
- path: '/lists/:listId/settings/duplicate',
- name: 'list.table.settings.duplicate',
- component: ListSettingDuplicate,
- },
- {
- path: '/lists/:listId/settings/share',
- name: 'list.table.settings.share',
- component: ListSettingShare,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'list.table.settings.delete',
- component: ListSettingDelete,
- },
- {
- path: '/lists/:listId/settings/archive',
- name: 'list.table.settings.archive',
- component: ListSettingArchive,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'filter.table.settings.edit',
- component: FilterEdit,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'filter.table.settings.delete',
- component: FilterDelete,
- },
- ],
- },
- {
- path: '/lists/:listId/kanban',
- name: 'list.kanban',
- component: Kanban,
- children: [
- {
- path: '/tasks/:id',
- name: 'task.kanban.detail',
- component: TaskDetailViewModal,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'list.kanban.settings.edit',
- component: ListSettingEdit,
- },
- {
- path: '/lists/:listId/settings/background',
- name: 'list.kanban.settings.background',
- component: ListSettingBackground,
- },
- {
- path: '/lists/:listId/settings/duplicate',
- name: 'list.kanban.settings.duplicate',
- component: ListSettingDuplicate,
- },
- {
- path: '/lists/:listId/settings/share',
- name: 'list.kanban.settings.share',
- component: ListSettingShare,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'list.kanban.settings.delete',
- component: ListSettingDelete,
- },
- {
- path: '/lists/:listId/settings/archive',
- name: 'list.kanban.settings.archive',
- component: ListSettingArchive,
- },
- {
- path: '/lists/:listId/settings/edit',
- name: 'filter.kanban.settings.edit',
- component: FilterEdit,
- },
- {
- path: '/lists/:listId/settings/delete',
- name: 'filter.kanban.settings.delete',
- component: FilterDelete,
- },
- ],
- },
- ],
+ redirect(to) {
+ // Redirect the user to list view by default
+
+ const savedListView = getListView(to.params.listId)
+ console.debug('Replaced list view with', savedListView)
+
+ return {
+ name: router.hasRoute(savedListView)
+ ? savedListView
+ : 'list.list',
+ params: {listId: to.params.listId},
+ }
+ },
+ },
+ {
+ path: '/lists/:listId/list',
+ name: 'list.list',
+ component: ListList,
+ beforeEnter: (to) => saveListView(to.params.listId, to.name),
+ props: route => ({ listId: parseInt(route.params.listId as string) }),
+ },
+ {
+ path: '/lists/:listId/gantt',
+ name: 'list.gantt',
+ component: ListGantt,
+ beforeEnter: (to) => saveListView(to.params.listId, to.name),
+ props: route => ({ listId: parseInt(route.params.listId as string) }),
+ },
+ {
+ path: '/lists/:listId/table',
+ name: 'list.table',
+ component: ListTable,
+ beforeEnter: (to) => saveListView(to.params.listId, to.name),
+ props: route => ({ listId: parseInt(route.params.listId as string) }),
+ },
+ {
+ path: '/lists/:listId/kanban',
+ name: 'list.kanban',
+ component: ListKanban,
+ beforeEnter: (to) => saveListView(to.params.listId, to.name),
+ props: route => ({ listId: parseInt(route.params.listId as string) }),
},
{
path: '/teams',
@@ -524,8 +383,9 @@ const router = createRouter({
{
path: '/teams/new',
name: 'teams.create',
- components: {
- popup: NewTeamComponent,
+ component: NewTeamComponent,
+ meta: {
+ showAsModal: true,
},
},
{
@@ -541,8 +401,9 @@ const router = createRouter({
{
path: '/labels/new',
name: 'labels.create',
- components: {
- popup: NewLabelComponent,
+ component: NewLabelComponent,
+ meta: {
+ showAsModal: true,
},
},
{
@@ -558,8 +419,9 @@ const router = createRouter({
{
path: '/filters/new',
name: 'filters.create',
- components: {
- popup: FilterNew,
+ component: FilterNew,
+ meta: {
+ showAsModal: true,
},
},
{
@@ -575,11 +437,7 @@ const router = createRouter({
],
})
-router.beforeEach((to) => {
- return checkAuth(to)
-})
-
-function checkAuth(route: RouteLocation) {
+export function getAuthForRoute(route: RouteLocation) {
const authUser = store.getters['auth/authUser']
const authLinkShare = store.getters['auth/authLinkShare']
diff --git a/src/services/abstractService.js b/src/services/abstractService.js
index a28334f3f..4dc0bd6b8 100644
--- a/src/services/abstractService.js
+++ b/src/services/abstractService.js
@@ -2,6 +2,31 @@ import axios from 'axios'
import {objectToSnakeCase} from '@/helpers/case'
import {getToken} from '@/helpers/auth'
+function convertObject(o) {
+ if (o instanceof Date) {
+ return o.toISOString()
+ }
+
+ return o
+}
+
+function prepareParams(params) {
+ if (typeof params !== 'object') {
+ return params
+ }
+
+ for (const p in params) {
+ if (Array.isArray(params[p])) {
+ params[p] = params[p].map(convertObject)
+ continue
+ }
+
+ params[p] = convertObject(params[p])
+ }
+
+ return objectToSnakeCase(params)
+}
+
export default class AbstractService {
/////////////////////////////
@@ -292,7 +317,7 @@ export default class AbstractService {
const finalUrl = this.getReplacedRoute(url, model)
try {
- const response = await this.http.get(finalUrl, {params})
+ const response = await this.http.get(finalUrl, {params: prepareParams(params)})
const result = this.modelGetFactory(response.data)
result.maxRight = Number(response.headers['x-max-right'])
return result
@@ -331,7 +356,7 @@ export default class AbstractService {
const finalUrl = this.getReplacedRoute(this.paths.getAll, model)
try {
- const response = await this.http.get(finalUrl, {params: params})
+ const response = await this.http.get(finalUrl, {params: prepareParams(params)})
this.resultCount = Number(response.headers['x-pagination-result-count'])
this.totalPages = Number(response.headers['x-pagination-total-pages'])
diff --git a/src/store/index.js b/src/store/index.js
index 603f742fc..87d67891d 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -20,6 +20,8 @@ import lists from './modules/lists'
import attachments from './modules/attachments'
import labels from './modules/labels'
+import ListModel from '@/models/list'
+
import ListService from '../services/list'
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
@@ -39,14 +41,16 @@ export const store = createStore({
loading: false,
loadingModule: null,
// This is used to highlight the current list in menu for all list related views
- currentList: {id: 0},
+ currentList: new ListModel({
+ id: 0,
+ isArchived: false,
+ }),
background: '',
blurHash: '',
hasTasks: false,
menuActive: true,
keyboardShortcutsActive: false,
quickActionsActive: false,
- vikunjaReady: false,
},
mutations: {
[LOADING](state, loading) {
@@ -149,10 +153,9 @@ export const store = createStore({
commit(CURRENT_LIST, currentList)
},
- async loadApp({commit, dispatch}) {
+ async loadApp({dispatch}) {
await checkAndSetApiUrl(window.API_URL)
await dispatch('auth/checkAuth')
- commit('vikunjaReady', true)
},
},
})
diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js
index c5dd10325..99291a2a9 100644
--- a/src/store/modules/auth.js
+++ b/src/store/modules/auth.js
@@ -1,12 +1,13 @@
-import {HTTPFactory} from '@/http-common'
-import {getCurrentLanguage, saveLanguage} from '@/i18n'
+import {HTTPFactory, AuthenticatedHTTPFactory} from '@/http-common'
+import {i18n, getCurrentLanguage, saveLanguage} from '@/i18n'
+import {objectToSnakeCase} from '@/helpers/case'
import {LOADING} from '../mutation-types'
import UserModel from '@/models/user'
import UserSettingsService from '@/services/userSettings'
import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
import {setLoading} from '@/store/helper'
-import {i18n} from '@/i18n'
import {success} from '@/message'
+import {redirectToProvider} from '@/helpers/redirectToProvider'
const AUTH_TYPES = {
'UNKNOWN': 0,
@@ -90,17 +91,8 @@ export default {
// Delete an eventually preexisting old token
removeToken()
- const data = {
- username: credentials.username,
- password: credentials.password,
- }
-
- if (credentials.totpPasscode) {
- data.totp_passcode = credentials.totpPasscode
- }
-
try {
- const response = await HTTP.post('login', data)
+ const response = await HTTP.post('login', objectToSnakeCase(credentials))
// Save the token to local storage for later use
saveToken(response.data.token, true)
@@ -201,7 +193,19 @@ export default {
ctx.commit('authenticated', authenticated)
if (!authenticated) {
ctx.commit('info', null)
- ctx.dispatch('config/redirectToProviderIfNothingElseIsEnabled', null, {root: true})
+ ctx.dispatch('redirectToProviderIfNothingElseIsEnabled')
+ }
+ },
+
+ redirectToProviderIfNothingElseIsEnabled({rootState}) {
+ const {auth} = rootState.config
+ if (
+ auth.local.enabled === false &&
+ auth.openidConnect.enabled &&
+ auth.openidConnect.providers?.length === 1 &&
+ window.location.pathname.startsWith('/login') // Kinda hacky, but prevents an endless loop.
+ ) {
+ redirectToProvider(auth.openidConnect.providers[0], auth.openidConnect.redirectUrl)
}
},
@@ -211,13 +215,9 @@ export default {
return
}
- const HTTP = HTTPFactory()
+ const HTTP = AuthenticatedHTTPFactory(jwt)
try {
- const response = await HTTP.get('user', {
- headers: {
- Authorization: `Bearer ${jwt}`,
- },
- })
+ const response = await HTTP.get('user')
const info = new UserModel(response.data)
info.type = state.info.type
info.email = state.info.email
diff --git a/src/store/modules/config.js b/src/store/modules/config.js
index 82c2b20c6..f403b3643 100644
--- a/src/store/modules/config.js
+++ b/src/store/modules/config.js
@@ -1,7 +1,6 @@
import {CONFIG} from '../mutation-types'
import {HTTPFactory} from '@/http-common'
import {objectToCamelCase} from '@/helpers/case'
-import {redirectToProvider} from '../../helpers/redirectToProvider'
import {parseURL} from 'ufo'
export default {
@@ -45,46 +44,15 @@ export default {
},
mutations: {
[CONFIG](state, config) {
- state.version = config.version
- state.frontendUrl = config.frontend_url
- state.motd = config.motd
- state.linkSharingEnabled = config.link_sharing_enabled
- state.maxFileSize = config.max_file_size
- state.registrationEnabled = config.registration_enabled
- state.availableMigrators = config.available_migrators
- state.taskAttachmentsEnabled = config.task_attachments_enabled
- state.totpEnabled = config.totp_enabled
- state.enabledBackgroundProviders = config.enabled_background_providers
- state.legal.imprintUrl = config.legal.imprint_url
- state.legal.privacyPolicyUrl = config.legal.privacy_policy_url
- state.caldavEnabled = config.caldav_enabled
- state.userDeletionEnabled = config.user_deletion_enabled
- state.taskCommentsEnabled = config.task_comments_enabled
- const auth = objectToCamelCase(config.auth)
- state.auth.local.enabled = auth.local.enabled
- state.auth.openidConnect.enabled = auth.openidConnect.enabled
- state.auth.openidConnect.redirectUrl = auth.openidConnect.redirectUrl
- state.auth.openidConnect.providers = auth.openidConnect.providers
+ Object.assign(state, config)
},
},
actions: {
async update(ctx) {
const HTTP = HTTPFactory()
-
- const {data: info} = await HTTP.get('info')
- ctx.commit(CONFIG, info)
- return info
- },
-
- redirectToProviderIfNothingElseIsEnabled(ctx) {
- if (ctx.state.auth.local.enabled === false &&
- ctx.state.auth.openidConnect.enabled &&
- ctx.state.auth.openidConnect.providers &&
- ctx.state.auth.openidConnect.providers.length === 1 &&
- window.location.pathname.startsWith('/login') // Kinda hacky, but prevents an endless loop.
- ) {
- redirectToProvider(ctx.state.auth.openidConnect.providers[0])
- }
+ const {data: config} = await HTTP.get('info')
+ ctx.commit(CONFIG, objectToCamelCase(config))
+ return config
},
},
}
\ No newline at end of file
diff --git a/src/store/modules/namespaces.js b/src/store/modules/namespaces.js
index ae47a95f8..4512fb89f 100644
--- a/src/store/modules/namespaces.js
+++ b/src/store/modules/namespaces.js
@@ -23,8 +23,6 @@ export default {
return
}
- // FIXME: direct manipulation of the prop
- // might not be a problem since this is happening in the mutation
if (!namespace.lists || namespace.lists.length === 0) {
namespace.lists = state.namespaces[namespaceIndex].lists
}
@@ -136,8 +134,8 @@ export default {
},
loadNamespacesIfFavoritesDontExist(ctx) {
- // The first namespace should be the one holding all favorites
- if (ctx.state.namespaces[0].id !== -2) {
+ // The first or second namespace should be the one holding all favorites
+ if (ctx.state.namespaces[0].id !== -2 && ctx.state.namespaces[1]?.id !== -2) {
return ctx.dispatch('loadNamespaces')
}
},
diff --git a/src/store/modules/tasks.js b/src/store/modules/tasks.js
index 8d29cc890..2fadb2a06 100644
--- a/src/store/modules/tasks.js
+++ b/src/store/modules/tasks.js
@@ -133,9 +133,19 @@ export default {
console.debug('Could not add assignee to task in kanban, task not found', t)
return r
}
- // FIXME: direct store manipulation (task)
- t.task.assignees.push(user)
- ctx.commit('kanban/setTaskInBucketByIndex', t, { root: true })
+
+ const assignees = [
+ ...t.task.assignees,
+ user,
+ ]
+
+ ctx.commit('kanban/setTaskInBucketByIndex', {
+ ...t,
+ task: {
+ ...t.task,
+ assignees,
+ },
+ }, {root: true})
return r
},
@@ -153,15 +163,15 @@ export default {
return response
}
- for (const a in t.task.assignees) {
- if (t.task.assignees[a].id === user.id) {
- // FIXME: direct store manipulation (task)
- t.task.assignees.splice(a, 1)
- break
- }
- }
+ const assignees = t.task.assignees.filter(({ id }) => id !== user.id)
- ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true})
+ ctx.commit('kanban/setTaskInBucketByIndex', {
+ ...t,
+ task: {
+ ...t.task,
+ assignees,
+ },
+ }, {root: true})
return response
},
@@ -179,9 +189,20 @@ export default {
console.debug('Could not add label to task in kanban, task not found', t)
return r
}
- // FIXME: direct store manipulation (task)
- t.task.labels.push(label)
- ctx.commit('kanban/setTaskInBucketByIndex', t, { root: true })
+
+ const labels = [
+ ...t.task.labels,
+ label,
+ ]
+
+ ctx.commit('kanban/setTaskInBucketByIndex', {
+ ...t,
+ task: {
+ ...t.task,
+ labels,
+ },
+ }, {root: true})
+
return r
},
@@ -200,15 +221,15 @@ export default {
}
// Remove the label from the list
- for (const l in t.task.labels) {
- if (t.task.labels[l].id === label.id) {
- // FIXME: direct store manipulation (task)
- t.task.labels.splice(l, 1)
- break
- }
- }
+ const labels = t.task.labels.filter(({ id }) => id !== label.id)
- ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true})
+ ctx.commit('kanban/setTaskInBucketByIndex', {
+ ...t,
+ task: {
+ ...t.task,
+ labels,
+ },
+ }, {root: true})
return response
},
diff --git a/src/styles/common-imports.scss b/src/styles/common-imports.scss
index 5ca35fc42..a9e163e3a 100644
--- a/src/styles/common-imports.scss
+++ b/src/styles/common-imports.scss
@@ -16,6 +16,8 @@
// since $tablet is defined by bulma we can just define it after importing the utilities
$mobile: math.div($tablet, 2);
+@import "mixins";
+
$family-sans-serif: 'Open Sans', Helvetica, Arial, sans-serif;
$vikunja-font: 'Quicksand', sans-serif;
diff --git a/src/styles/components/_index.scss b/src/styles/components/_index.scss
index ba87006b7..080c93d03 100644
--- a/src/styles/components/_index.scss
+++ b/src/styles/components/_index.scss
@@ -2,4 +2,4 @@
@import "labels";
@import "list";
@import "task";
-@import "tasks";
\ No newline at end of file
+@import "tasks";
diff --git a/src/styles/components/list.scss b/src/styles/components/list.scss
index 6475a395e..5fbb06778 100644
--- a/src/styles/components/list.scss
+++ b/src/styles/components/list.scss
@@ -23,7 +23,7 @@ $filter-container-top-link-share-list: -47px;
display: flex;
justify-content: flex-end;
- .button:not(:last-child) {
+ .button:not(:last-of-type) {
margin-right: .5rem;
}
}
diff --git a/src/styles/custom-properties/colors.scss b/src/styles/custom-properties/colors.scss
index 2fc3ba0d0..ac1864442 100644
--- a/src/styles/custom-properties/colors.scss
+++ b/src/styles/custom-properties/colors.scss
@@ -1,9 +1,182 @@
// Vikunja colors as CSS custom properties
:root {
+ // Core Bulma color variables
+ // Added (from bulma-css-variables/css/bulma.css) to fix issues with undefined variables
+ // This section should be removed once bulma/sass scoping issues are fixed
+ // see https://kolaente.dev/vikunja/frontend/issues/1064
+ // Variables overriden in Vikunja specific rules below are commented out
+ //--scheme-main: white;
+ --scheme-main-bis: #fafafa;
+ --scheme-main-ter: whitesmoke;
+ --scheme-invert: #0a0a0a;
+ --scheme-invert-rgb: 10.2, 10.2, 10.2;
+ --scheme-invert-bis: #121212;
+ --scheme-invert-ter: #242424;
+ --background: whitesmoke;
+ //--border: #dbdbdb;
+ --border-rgb: 219.3, 219.3, 219.3;
+ --border-hover: #b5b5b5;
+ --border-light: #ededed;
+ --border-light-hover: #b5b5b5;
+ --text: #4a4a4a;
+ --text-invert: #fff;
+ --text-light: #7a7a7a;
+ --text-strong: #363636;
+ --code: #da1039;
+ --code-background: var(--background, whitesmoke);
+ --pre: var(--text, #4a4a4a);
+ --pre-background: var(--background, whitesmoke);
+ --link-visited: #b86bff;
+ //--link-hover: #363636;
+ --link-hover-border: #b5b5b5;
+ --link-focus: #363636;
+ --link-focus-border: var(--link, #485fc7);
+ --link-active: #363636;
+ --link-active-border: #4a4a4a;
+ //--white-h: 0deg;
+ //--white-s: 0%;
+ //--white-l: 100%;
+ //--white-a: 1;
+ //--white: hsla(var(--white-h), var(--white-s), var(--white-l), var(--white-a));
+ --white-invert-l: 4%;
+ --white-invert: #0a0a0a;
+ --white-light-l: 100%;
+ --white-light: hsla(var(--white-h), var(--white-s), var(--white-light-l), var(--white-a));
+ --white-dark-l: 29%;
+ --white-dark: hsla(var(--white-h), var(--white-s), var(--white-dark-l), var(--white-a));
+ //--black-h: 0deg;
+ //--black-s: 0%;
+ //--black-l: 4%;
+ //--black-a: 1;
+ //--black: hsla(var(--black-h), var(--black-s), var(--black-l), var(--black-a));
+ --black-invert-l: 100%;
+ --black-invert: white;
+ --black-light-l: 96%;
+ --black-light: hsla(var(--black-h), var(--black-s), var(--black-light-l), var(--black-a));
+ --black-dark-l: 57%;
+ --black-dark: hsla(var(--black-h), var(--black-s), var(--black-dark-l), var(--black-a));
+ --light-h: 0deg;
+ --light-s: 0%;
+ --light-l: 96%;
+ --light-a: 1;
+ --light: hsla(var(--light-h), var(--light-s), var(--light-l), var(--light-a));
+ --light-invert-l: 0%;
+ --light-invert: rgba(0, 0, 0, 0.7);
+ --light-light-l: 96%;
+ --light-light: hsla(var(--light-h), var(--light-s), var(--light-light-l), var(--light-a));
+ --light-dark-l: 29%;
+ --light-dark: hsla(var(--light-h), var(--light-s), var(--light-dark-l), var(--light-a));
+ --dark-h: 0deg;
+ --dark-s: 0%;
+ --dark-l: 21%;
+ --dark-a: 1;
+ --dark: hsla(var(--dark-h), var(--dark-s), var(--dark-l), var(--dark-a));
+ --dark-invert-l: 100%;
+ --dark-invert: #fff;
+ --dark-light-l: 96%;
+ --dark-light: hsla(var(--dark-h), var(--dark-s), var(--dark-light-l), var(--dark-a));
+ --dark-dark-l: 54%;
+ --dark-dark: hsla(var(--dark-h), var(--dark-s), var(--dark-dark-l), var(--dark-a));
+ //--primary-h: 171deg;
+ //--primary-s: 100%;
+ //--primary-l: 41%;
+ //--primary-a: 1;
+ //--primary: hsla(var(--primary-h), var(--primary-s), var(--primary-l), var(--primary-a));
+ --primary-invert-l: 100%;
+ --primary-invert: #fff;
+ --primary-light-l: 96%;
+ --primary-light: hsla(var(--primary-h), var(--primary-s), var(--primary-light-l), var(--primary-a));
+ --primary-dark-l: 29%;
+ --primary-dark: hsla(var(--primary-h), var(--primary-s), var(--primary-dark-l), var(--primary-a));
+ --link-h: 229deg;
+ --link-s: 53%;
+ --link-l: 53%;
+ --link-a: 1;
+ //--link: hsla(var(--link-h), var(--link-s), var(--link-l), var(--link-a));
+ --link-invert-l: 100%;
+ --link-invert: #fff;
+ --link-light-l: 96%;
+ --link-light: hsla(var(--link-h), var(--link-s), var(--link-light-l), var(--link-a));
+ --link-dark-l: 47%;
+ --link-dark: hsla(var(--link-h), var(--link-s), var(--link-dark-l), var(--link-a));
+ --info-h: 207deg;
+ --info-s: 61%;
+ --info-l: 53%;
+ --info-a: 1;
+ --info: hsla(var(--info-h), var(--info-s), var(--info-l), var(--info-a));
+ --info-invert-l: 100%;
+ --info-invert: #fff;
+ --info-light-l: 96%;
+ --info-light: hsla(var(--info-h), var(--info-s), var(--info-light-l), var(--info-a));
+ --info-dark-l: 41%;
+ --info-dark: hsla(var(--info-h), var(--info-s), var(--info-dark-l), var(--info-a));
+ //--success-h: 153deg;
+ //--success-s: 53%;
+ //--success-l: 53%;
+ //--success-a: 1;
+ //--success: hsla(var(--success-h), var(--success-s), var(--success-l), var(--success-a));
+ --success-invert-l: 100%;
+ --success-invert: #fff;
+ --success-light-l: 96%;
+ --success-light: hsla(var(--success-h), var(--success-s), var(--success-light-l), var(--success-a));
+ --success-dark-l: 31%;
+ --success-dark: hsla(var(--success-h), var(--success-s), var(--success-dark-l), var(--success-a));
+ //--warning-h: 44deg;
+ //--warning-s: 100%;
+ //--warning-l: 77%;
+ //--warning-a: 1;
+ //--warning: hsla(var(--warning-h), var(--warning-s), var(--warning-l), var(--warning-a));
+ --warning-invert-l: 0%;
+ --warning-invert: rgba(0, 0, 0, 0.7);
+ --warning-light-l: 96%;
+ --warning-light: hsla(var(--warning-h), var(--warning-s), var(--warning-light-l), var(--warning-a));
+ --warning-dark-l: 29%;
+ --warning-dark: hsla(var(--warning-h), var(--warning-s), var(--warning-dark-l), var(--warning-a));
+ //--danger-h: 348deg;
+ //--danger-s: 86%;
+ //--danger-l: 61%;
+ //--danger-a: 1;
+ //--danger: hsla(var(--danger-h), var(--danger-s), var(--danger-l), var(--danger-a));
+ --danger-invert-l: 100%;
+ --danger-invert: #fff;
+ --danger-light-l: 96%;
+ --danger-light: hsla(var(--danger-h), var(--danger-s), var(--danger-light-l), var(--danger-a));
+ --danger-dark-l: 43%;
+ --danger-dark: hsla(var(--danger-h), var(--danger-s), var(--danger-dark-l), var(--danger-a));
+ --input-color: var(--text-strong, #363636);
+ --input-background-color: var(--scheme-main, white);
+ //--input-border-color: var(--border, #dbdbdb);
+ --input-shadow: inset 0 0.0625em 0.125em rgba(var(--scheme-invert-rgb, 10.2, 10.2, 10.2), 0.05);
+ --input-placeholder-color: rgba(54, 54, 54, 0.3);
+ --input-hover-color: var(--text-strong, #363636);
+ --input-hover-border-color: var(--border-hover, #b5b5b5);
+ --input-focus-color: var(--text-strong, #363636);
+ --input-focus-border-color: var(--link, #485fc7);
+ --input-focus-box-shadow-color: var(--input-focus-box-shadow-color-hsla, rgba(72, 95, 199, 0.25));
+ --input-disabled-color: var(--text-light, #7a7a7a);
+ //--input-disabled-background-color: var(--background, whitesmoke);
+ //--input-disabled-border-color: var(--background, whitesmoke);
+ --input-disabled-placeholder-color: rgba(122, 122, 122, 0.3);
+ --input-arrow: var(--link, #485fc7);
+ --input-icon-color: var(--border, #dbdbdb);
+ --input-icon-active-color: var(--text, #4a4a4a);
+ --black-bis: #121212;
+ --black-ter: #242424;
+ //--grey-darker: #363636;
+ //--grey-dark: #4a4a4a;
+ //--grey: #7a7a7a;
+ //--grey-light: #b5b5b5;
+ //--grey-lighter: #dbdbdb;
+ --white-ter: whitesmoke;
+ --white-bis: #fafafa;
+// END core Bulma color variables
+
+
// Vikunja specific variables
--grey-50: hsl(210, 20%, 98%);
- --grey-100: hsl(220, 14.3%, 95.9%);
+ --grey-100-hsl: 220, 14.3%, 95.9%;
+ --grey-100: hsl(var(--grey-100-hsl));
--grey-200: hsl(220, 13%, 91%);
--grey-300: hsl(216, 12.2%, 83.9%);
--grey-400: hsl(217.9, 10.6%, 64.9%);
@@ -14,9 +187,9 @@
--grey-800: hsl(215, 27.9%, 16.9%);
--grey-900: hsl(220.9, 39.3%, 11%);
--site-background: var(--grey-100);
- --scheme-main: var(--white);
// Overrides of Bulma defaults
+ --scheme-main: var(--white);
--grey-darker: var(--grey-700);
--grey-dark: var(--grey-800);
--grey: var(--grey-500);
@@ -60,12 +233,18 @@
--danger: hsla(var(--danger-h), var(--danger-s), var(--danger-l), var(--danger-a));
// var(--primary) / $blue is #1973ff
- --primary-h: 216.5deg;
- --primary-s: 100%;
- --primary-l: 54.9%;
+ --primary-h: 217deg;
+ --primary-s: 98%;
+ --primary-l: 53%;
--primary-a: 1;
--primary-hsl: var(--primary-h), var(--primary-s), var(--primary-l);
--primary: hsla(var(--primary-h), var(--primary-s), var(--primary-l), var(--primary-a));
+
+ --link: var(--primary);
+ --link-hover: hsla(var(--primary-h), var(--primary-s), var(--primary-l), .75);
+ --border: var(--grey-200);
+ --input-disabled-background-color: var(--grey-100);
+ --input-disabled-border-color: var(--grey-300);
// END Overrides of Bulma defaults
@@ -76,12 +255,6 @@
--card-border-color: var(--grey-200);
--logo-text-color: hsl(180, 1%, 15%);
- --link: var(--primary);
- --link-hover: hsla(var(--primary-h), var(--primary-s), var(--primary-l), .75);
- --border: var(--grey-200);
- --input-disabled-background-color: var(--grey-100);
- --input-disabled-border-color: var(--grey-300);
-
&.dark {
// Light mode colours reversed for dark mode
--grey-900-hsl: 210, 20%, 98%;
@@ -122,5 +295,10 @@
// Custom color variables we need to override
--card-border-color: hsla(var(--grey-100-hsl), 0.3);
--logo-text-color: var(--grey-700);
+
+ // Slightly different primary color to make sure it has a sufficent contrast ratio
+ --primary-h: 217deg;
+ --primary-s: 98%;
+ --primary-l: 58%;
}
}
\ No newline at end of file
diff --git a/src/styles/mixins.scss b/src/styles/mixins.scss
new file mode 100644
index 000000000..687355e73
--- /dev/null
+++ b/src/styles/mixins.scss
@@ -0,0 +1,12 @@
+/* Transitions */
+@mixin modal-transition() {
+ .modal-enter,
+ .modal-leave-active {
+ opacity: 0;
+ }
+
+ .modal-enter .modal-container,
+ .modal-leave-active .modal-container {
+ transform: scale(0.9);
+ }
+}
\ No newline at end of file
diff --git a/src/styles/theme/background.scss b/src/styles/theme/background.scss
index 73f911b8b..150cb754f 100644
--- a/src/styles/theme/background.scss
+++ b/src/styles/theme/background.scss
@@ -18,7 +18,7 @@
.box,
.card,
.switch-view,
- .table-view .button,
+ .list-table .button,
.filter-container .button,
.search .button {
box-shadow: none;
diff --git a/src/styles/theme/theme.scss b/src/styles/theme/theme.scss
index 77a9ec068..8087a2e19 100644
--- a/src/styles/theme/theme.scss
+++ b/src/styles/theme/theme.scss
@@ -18,6 +18,11 @@
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
+:root {
+ // Bulma sets this to "scroll" which gives us a scrollbar even if there's no content to scroll
+ --body-overflow-y: auto;
+}
+
body {
background: var(--site-background);
min-height: 100vh;
@@ -65,7 +70,7 @@ h6 {
}
.has-overflow {
- overflow: visible;
+ overflow: visible !important;
}
.has-horizontal-overflow {
diff --git a/src/sw.js b/src/sw.js
index 674ca0278..5e1a80822 100644
--- a/src/sw.js
+++ b/src/sw.js
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
/* eslint-disable no-undef */
-const workboxVersion = 'v6.4.2'
+const workboxVersion = 'v6.5.2'
importScripts( `/workbox-${workboxVersion}/workbox-sw.js`)
workbox.setConfig({
modulePathPrefix: `/workbox-${workboxVersion}`,
diff --git a/src/types/faker.d.ts b/src/types/faker.d.ts
new file mode 100644
index 000000000..de01ac250
--- /dev/null
+++ b/src/types/faker.d.ts
@@ -0,0 +1,4 @@
+declare module '@faker-js/faker' {
+ import faker from 'faker'
+ export default faker
+}
\ No newline at end of file
diff --git a/src/types/shims-vue.d.ts b/src/types/shims-vue.d.ts
index 4f0571df7..c04ff091d 100644
--- a/src/types/shims-vue.d.ts
+++ b/src/types/shims-vue.d.ts
@@ -1,8 +1,11 @@
declare module 'vue' {
import { CompatVue } from '@vue/runtime-dom'
const Vue: CompatVue
- export default Vue
- export * from '@vue/runtime-dom'
+ export default Vue
+ export * from '@vue/runtime-dom'
+
+ const { configureCompat } = Vue
+ export { configureCompat }
}
// https://next.vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html#typescript-support
diff --git a/src/types/vue-flatpickr-component.d.ts b/src/types/vue-flatpickr-component.d.ts
new file mode 100644
index 000000000..2ac9cc87f
--- /dev/null
+++ b/src/types/vue-flatpickr-component.d.ts
@@ -0,0 +1 @@
+declare module 'vue-flatpickr-component';
\ No newline at end of file
diff --git a/src/views/Home.vue b/src/views/Home.vue
index f3d2b3ecb..ac82f2615 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -23,7 +23,7 @@
{{ $t('home.list.newText') }}
@@ -50,7 +50,11 @@
/>
-
+
@@ -83,13 +87,14 @@ const userInfo = computed(() => store.state.auth.info)
const hasTasks = computed(() => store.state.hasTasks)
const defaultListId = computed(() => store.state.auth.defaultListId)
const defaultNamespaceId = computed(() => store.state.namespaces.namespaces?.[0]?.id || 0)
-const hasLists = computed (() => store.state.namespaces.namespaces?.[0]?.lists.length > 0)
+const hasLists = computed(() => store.state.namespaces.namespaces?.[0]?.lists.length > 0)
const loading = computed(() => store.state.loading && store.state.loadingModule === 'tasks')
const deletionScheduledAt = computed(() => parseDateOrNull(store.state.auth.info?.deletionScheduledAt))
// This is to reload the tasks list after adding a new task through the global task add.
// FIXME: Should use vuex (somehow?)
const showTasksKey = ref(0)
+
function updateTaskList() {
showTasksKey.value++
}
diff --git a/src/views/list/views/Gantt.vue b/src/views/list/ListGantt.vue
similarity index 65%
rename from src/views/list/views/Gantt.vue
rename to src/views/list/ListGantt.vue
index b3628238b..82b765dd8 100644
--- a/src/views/list/views/Gantt.vue
+++ b/src/views/list/ListGantt.vue
@@ -1,6 +1,6 @@
-
-
+
+
{{ $t('list.gantt.showTasksWithoutDates') }}
@@ -44,65 +44,64 @@
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
-
\ No newline at end of file
diff --git a/src/views/list/views/List.vue b/src/views/list/ListList.vue
similarity index 79%
rename from src/views/list/views/List.vue
rename to src/views/list/ListList.vue
index d61a39e8d..e8ec93d98 100644
--- a/src/views/list/views/List.vue
+++ b/src/views/list/ListList.vue
@@ -1,8 +1,6 @@
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/views/list/ListWrapper.vue b/src/views/list/ListWrapper.vue
new file mode 100644
index 000000000..1153dc7e6
--- /dev/null
+++ b/src/views/list/ListWrapper.vue
@@ -0,0 +1,212 @@
+
+
+
+
+
+ {{ $t('list.list.title') }}
+
+
+ {{ $t('list.gantt.title') }}
+
+
+ {{ $t('list.table.title') }}
+
+
+ {{ $t('list.kanban.title') }}
+
+
+
+
+
+
+ {{ $t('list.archived') }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/list/NewList.vue b/src/views/list/NewList.vue
index fb48e42bc..0317a88e0 100644
--- a/src/views/list/NewList.vue
+++ b/src/views/list/NewList.vue
@@ -61,7 +61,7 @@ export default {
}
this.showError = false
- this.list.namespaceId = parseInt(this.$route.params.id)
+ this.list.namespaceId = parseInt(this.$route.params.namespaceId)
const list = await this.$store.dispatch('lists/createList', this.list)
this.$message.success({message: this.$t('list.create.createdSuccess') })
this.$router.push({
diff --git a/src/views/list/ShowList.vue b/src/views/list/ShowList.vue
deleted file mode 100644
index 019cfb638..000000000
--- a/src/views/list/ShowList.vue
+++ /dev/null
@@ -1,213 +0,0 @@
-
-
-
-
-
- {{ $t('list.list.title') }}
-
-
- {{ $t('list.gantt.title') }}
-
-
- {{ $t('list.table.title') }}
-
-
- {{ $t('list.kanban.title') }}
-
-
-
-
-
- {{ $t('list.archived') }}
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/views/list/settings/background.vue b/src/views/list/settings/background.vue
index a5da89e9f..83c98d87f 100644
--- a/src/views/list/settings/background.vue
+++ b/src/views/list/settings/background.vue
@@ -154,6 +154,7 @@ export default {
const list = await this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
await this.$store.dispatch(CURRENT_LIST, list)
this.$store.commit('namespaces/setListInNamespaceById', list)
+ this.$store.commit('lists/setList', list)
this.$message.success({message: this.$t('list.background.success')})
},
@@ -165,6 +166,7 @@ export default {
const list = await this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
await this.$store.dispatch(CURRENT_LIST, list)
this.$store.commit('namespaces/setListInNamespaceById', list)
+ this.$store.commit('lists/setList', list)
this.$message.success({message: this.$t('list.background.success')})
},
@@ -172,6 +174,7 @@ export default {
const list = await this.listService.removeBackground(this.currentList)
await this.$store.dispatch(CURRENT_LIST, list)
this.$store.commit('namespaces/setListInNamespaceById', list)
+ this.$store.commit('lists/setList', list)
this.$message.success({message: this.$t('list.background.removeSuccess')})
this.$router.back()
},
diff --git a/src/views/list/settings/share.vue b/src/views/list/settings/share.vue
index 4a9967f86..ca11b4e56 100644
--- a/src/views/list/settings/share.vue
+++ b/src/views/list/settings/share.vue
@@ -3,24 +3,38 @@
:title="$t('list.share.header')"
primary-label=""
>
-
-
+
+
+
+
-
+
-
+
+
diff --git a/src/views/list/views/Table.vue b/src/views/list/views/Table.vue
deleted file mode 100644
index b15b0a1b8..000000000
--- a/src/views/list/views/Table.vue
+++ /dev/null
@@ -1,331 +0,0 @@
-
-
-
-
-
-
-
- {{ $t('list.table.columns') }}
-
-
-
-
- #
-
- {{ $t('task.attributes.done') }}
-
-
- {{ $t('task.attributes.title') }}
-
-
- {{ $t('task.attributes.priority') }}
-
-
- {{ $t('task.attributes.labels') }}
-
-
- {{ $t('task.attributes.assignees') }}
-
-
- {{ $t('task.attributes.dueDate') }}
-
-
- {{ $t('task.attributes.startDate') }}
-
-
- {{ $t('task.attributes.endDate') }}
-
-
- {{ $t('task.attributes.percentDone') }}
-
-
- {{ $t('task.attributes.created') }}
-
-
- {{ $t('task.attributes.updated') }}
-
-
- {{ $t('task.attributes.createdBy') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #
-
-
-
- {{ $t('task.attributes.done') }}
-
-
-
- {{ $t('task.attributes.title') }}
-
-
-
- {{ $t('task.attributes.priority') }}
-
-
-
- {{ $t('task.attributes.labels') }}
-
-
- {{ $t('task.attributes.assignees') }}
-
-
- {{ $t('task.attributes.dueDate') }}
-
-
-
- {{ $t('task.attributes.startDate') }}
-
-
-
- {{ $t('task.attributes.endDate') }}
-
-
-
- {{ $t('task.attributes.percentDone') }}
-
-
-
- {{ $t('task.attributes.created') }}
-
-
-
- {{ $t('task.attributes.updated') }}
-
-
-
- {{ $t('task.attributes.createdBy') }}
-
-
-
-
-
-
-
-
- #{{ t.index }}
-
-
- {{ t.identifier }}
-
-
-
-
-
-
-
- {{ t.title }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ t.percentDone * 100 }}%
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/views/migrator/MigrateService.vue b/src/views/migrator/MigrateService.vue
index 1f3573eb9..c77e2aa59 100644
--- a/src/views/migrator/MigrateService.vue
+++ b/src/views/migrator/MigrateService.vue
@@ -254,4 +254,13 @@ export default {
background-color: var(--primary-dark);
}
}
+
+@media (prefers-reduced-motion: reduce) {
+ @keyframes wave {
+ 10% {
+ transform: translate(0, 0);
+ background-color: var(--primary);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/views/namespaces/ListNamespaces.vue b/src/views/namespaces/ListNamespaces.vue
index fdefe4f9c..0a63b0502 100644
--- a/src/views/namespaces/ListNamespaces.vue
+++ b/src/views/namespaces/ListNamespaces.vue
@@ -22,9 +22,9 @@
-
+
{{ $t('namespace.noLists') }}
-
+
{{ $t('namespace.createList') }}
@@ -64,7 +64,7 @@
:show-archived="showArchived"
/>
-
+
diff --git a/src/views/namespaces/settings/archive.vue b/src/views/namespaces/settings/archive.vue
index 9eb1305b4..d9ecc3d72 100644
--- a/src/views/namespaces/settings/archive.vue
+++ b/src/views/namespaces/settings/archive.vue
@@ -4,9 +4,11 @@
@submit="archiveNamespace()"
>
{{ title }}
-
+
- {{ list.isArchived ? $t('namespace.archive.unarchiveText') : $t('namespace.archive.archiveText') }}
+
+ {{ namespace.isArchived ? $t('namespace.archive.unarchiveText') : $t('namespace.archive.archiveText')}}
+
@@ -27,19 +29,21 @@ export default {
created() {
this.namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
this.title = this.namespace.isArchived ?
- this.$t('namespace.archive.titleUnarchive', { namespace: this.namespace.title }) :
- this.$t('namespace.archive.titleArchive', { namespace: this.namespace.title })
+ this.$t('namespace.archive.titleUnarchive', {namespace: this.namespace.title}) :
+ this.$t('namespace.archive.titleArchive', {namespace: this.namespace.title})
this.setTitle(this.title)
},
methods: {
async archiveNamespace() {
- this.namespace.isArchived = !this.namespace.isArchived
-
try {
- const namespace = await this.namespaceService.update(this.namespace)
+ const isArchived = !this.namespace.isArchived
+ const namespace = await this.namespaceService.update({
+ ...this.namespace,
+ isArchived,
+ })
this.$store.commit('namespaces/setNamespaceById', namespace)
- this.$message.success({message: this.$t('namespace.archive.success')})
+ this.$message.success({message: this.$t(isArchived ? 'namespace.archive.success' : 'namespace.archive.unarchiveSuccess')})
} finally {
this.$router.back()
}
diff --git a/src/views/namespaces/settings/share.vue b/src/views/namespaces/settings/share.vue
index 8f70cbd55..f3e4a20ca 100644
--- a/src/views/namespaces/settings/share.vue
+++ b/src/views/namespaces/settings/share.vue
@@ -3,69 +3,67 @@
:title="title"
primary-label=""
>
-
-
+
+
+
+
-
+
+
\ No newline at end of file
diff --git a/src/views/sharing/LinkSharingAuth.vue b/src/views/sharing/LinkSharingAuth.vue
index 41a667394..66744930f 100644
--- a/src/views/sharing/LinkSharingAuth.vue
+++ b/src/views/sharing/LinkSharingAuth.vue
@@ -44,7 +44,7 @@ import Message from '@/components/misc/message.vue'
const {t} = useI18n()
useTitle(t('sharing.authenticating'))
-async function useAuth() {
+function useAuth() {
const store = useStore()
const route = useRoute()
const router = useRouter()
@@ -75,21 +75,21 @@ async function useAuth() {
password: password.value,
})
router.push({name: 'list.list', params: {listId}})
- } catch (e) {
+ } catch (e: any) {
if (e.response?.data?.code === 13001) {
authenticateWithPassword.value = true
return
}
// TODO: Put this logic in a global errorMessage handler method which checks all auth codes
- let errorMessage = t('sharing.error')
+ let err = t('sharing.error')
if (e.response?.data?.message) {
- errorMessage = e.response.data.message
+ err = e.response.data.message
}
if (e.response?.data?.code === 13002) {
- errorMessage = t('sharing.invalidPassword')
+ err = t('sharing.invalidPassword')
}
- errorMessage.value = errorMessage
+ errorMessage.value = err
} finally {
loading.value = false
}
diff --git a/src/views/tasks/ShowTasks.vue b/src/views/tasks/ShowTasks.vue
index d4f06ba33..71e16476a 100644
--- a/src/views/tasks/ShowTasks.vue
+++ b/src/views/tasks/ShowTasks.vue
@@ -1,281 +1,236 @@
-
-
- {{ $t('task.show.noDates') }}
-
-
- {{ $t('task.show.current') }}
+
+
+ {{ pageTitle }}
-
- {{ $t('task.show.from') }}
-
- {{ $t('task.show.until') }}
-
-
-
- {{ $t('task.show.today') }}
- {{ $t('task.show.nextWeek') }}
- {{ $t('task.show.nextMonth') }}
-
+
+
+
+
+ {{ $t('task.show.select') }}
+
+
+
+
+ {{ $t('task.show.noDates') }}
+
+
+ {{ $t('task.show.overdue') }}
+
+
- {{ $t('task.show.noTasks') }}
-
+ {{ $t('task.show.noTasks') }}
+
-
-
-
-
\ No newline at end of file
diff --git a/src/views/tasks/ShowTasksInRange.vue b/src/views/tasks/ShowTasksInRange.vue
deleted file mode 100644
index 02498cc11..000000000
--- a/src/views/tasks/ShowTasksInRange.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/views/tasks/TaskDetailView.vue b/src/views/tasks/TaskDetailView.vue
index e2171698e..f9817faa4 100644
--- a/src/views/tasks/TaskDetailView.vue
+++ b/src/views/tasks/TaskDetailView.vue
@@ -72,7 +72,7 @@
-
+
{{ $t('task.attributes.percentDone') }}
@@ -246,11 +246,11 @@
-
+
+
@@ -446,16 +434,22 @@ import description from '@/components/tasks/partials/description.vue'
import ColorPicker from '../../components/input/colorPicker'
import heading from '@/components/tasks/partials/heading.vue'
import Datepicker from '@/components/input/datepicker.vue'
+import BaseButton from '@/components/base/BaseButton'
import {playPop} from '@/helpers/playPop'
import TaskSubscription from '@/components/misc/subscription.vue'
import {CURRENT_LIST} from '@/store/mutation-types'
import {uploadFile} from '@/helpers/attachments'
import ChecklistSummary from '../../components/tasks/partials/checklist-summary'
+import CreatedUpdated from '@/components/tasks/partials/createdUpdated'
+
export default {
name: 'TaskDetailView',
+ compatConfig: { ATTR_FALSE_VALUE: false },
components: {
+ BaseButton,
+ CreatedUpdated,
ChecklistSummary,
TaskSubscription,
Datepicker,
@@ -473,6 +467,14 @@ export default {
description,
heading,
},
+
+ props: {
+ taskId: {
+ type: Number,
+ required: true,
+ },
+ },
+
data() {
return {
taskService: new TaskService(),
@@ -521,12 +523,15 @@ export default {
},
immediate: true,
},
+ // Using a watcher here because the header component handles saving the task with the api but we want to decouple
+ // it from the page title.
+ 'task.title': {
+ handler(title) {
+ this.setTitle(title)
+ },
+ },
},
computed: {
- taskId() {
- const {id} = this.$route.params
- return id === undefined ? id : Number(id)
- },
currentList() {
return this.$store.state[CURRENT_LIST]
},
@@ -547,18 +552,6 @@ export default {
canWrite() {
return typeof this.task !== 'undefined' && typeof this.task.maxRight !== 'undefined' && this.task.maxRight > rights.READ
},
- updatedSince() {
- return this.formatDateSince(this.task.updated)
- },
- updatedFormatted() {
- return this.formatDate(this.task.updated)
- },
- doneSince() {
- return this.formatDateSince(this.task.doneAt)
- },
- doneFormatted() {
- return this.formatDate(this.task.doneAt)
- },
hasAttachments() {
return this.$store.state.attachments.attachments.length > 0
},
@@ -589,6 +582,9 @@ export default {
}
},
scrollToHeading() {
+ if(!this.$refs?.heading?.$el) {
+ return
+ }
this.$refs.heading.$el.scrollIntoView({block: 'center'})
},
setActiveFields() {
@@ -707,7 +703,7 @@ $flash-background-duration: 750ms;
.subtitle {
color: var(--grey-500);
- margin-bottom: 1rem;
+ margin-bottom: 1rem;
a {
color: var(--grey-800);
@@ -735,15 +731,15 @@ $flash-background-duration: 750ms;
.title {
margin-bottom: 0;
- }
+ }
- .title.input {
- // 1.8rem is the font-size, 1.125 is the line-height, .3rem padding everywhere, 1px border around the whole thing.
- min-height: calc(1.8rem * 1.125 + .6rem + 2px);
+ .title.input {
+ // 1.8rem is the font-size, 1.125 is the line-height, .3rem padding everywhere, 1px border around the whole thing.
+ min-height: calc(1.8rem * 1.125 + .6rem + 2px);
- @media screen and (max-width: $tablet) {
- margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest
- }
+ @media screen and (max-width: $tablet) {
+ margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest
+ }
}
.title.task-id {
@@ -807,7 +803,7 @@ $flash-background-duration: 750ms;
}
&.labels-list,
- .assignees {
+ .assignees {
:deep(.multiselect) {
.input-wrapper {
&:not(:focus-within):not(:hover) {
@@ -892,7 +888,7 @@ $flash-background-duration: 750ms;
padding-bottom: 1rem;
@media screen and (max-width: $desktop) {
- padding-bottom: 0;
+ padding-bottom: 0;
}
.task-view * {
@@ -919,7 +915,7 @@ $flash-background-duration: 750ms;
}
.flash-background-enter-from,
-.flash-background-enter-active {
+.flash-background-enter-active {
animation: flash-background $flash-background-duration ease 1;
}
@@ -931,4 +927,14 @@ $flash-background-duration: 750ms;
background: transparent;
}
}
+
+@media (prefers-reduced-motion: reduce) {
+ @keyframes flash-background {
+ 0% {
+ background: transparent;
+ }
+ }
+}
+
+@include modal-transition();
\ No newline at end of file
diff --git a/src/views/tasks/TaskDetailViewModal.vue b/src/views/tasks/TaskDetailViewModal.vue
deleted file mode 100644
index 9b4932464..000000000
--- a/src/views/tasks/TaskDetailViewModal.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/views/teams/EditTeam.vue b/src/views/teams/EditTeam.vue
index 059530e8e..31c690f90 100644
--- a/src/views/teams/EditTeam.vue
+++ b/src/views/teams/EditTeam.vue
@@ -308,4 +308,6 @@ export default {
padding: 0;
}
}
+
+@include modal-transition();
\ No newline at end of file
diff --git a/src/views/user/Login.vue b/src/views/user/Login.vue
index d04b97366..fa4015d69 100644
--- a/src/views/user/Login.vue
+++ b/src/views/user/Login.vue
@@ -1,9 +1,9 @@
-
+
{{ $t('user.auth.confirmEmailSuccess') }}
-
+
{{ errorMessage }}
+
+ {{ $t('user.auth.usernameRequired') }}
+
-
{{ $t('user.auth.password') }}
-
-
+
+ {{ $t('user.auth.password') }}
+
+ {{ $t('user.auth.forgotPassword') }}
+
+
{{ $t('user.auth.totpTitle') }}
@@ -52,32 +54,34 @@
type="text"
v-focus
@keyup.enter="submit"
+ tabindex="3"
/>
-
-