diff --git a/.drone.yml b/.drone.yml
index 38ff2044f..c4e9e67e0 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -528,3 +528,34 @@ steps:
status:
- success
- failure
+---
+kind: pipeline
+name: ping-weblate
+
+depends_on:
+ - build
+
+trigger:
+ branch:
+ - main
+ event:
+ - push
+
+steps:
+ - name: update-translation-base
+ image: appleboy/drone-git-push
+ failure: ignore
+ settings:
+ branch: translations
+ remote: ssh://git@kolaente.dev:9022/vikunja/frontend.git
+ ssh_key:
+ from_secret: translations_branch_update_ssh_key
+ - name: notify-weblate
+ image: curlimages/curl
+ depends_on:
+ - update-translation-base
+ environment:
+ WEBLATE_TOKEN:
+ from_secret: weblate_token
+ commands:
+ - ./ping-weblate.sh
diff --git a/README.md b/README.md
index 1cf4e8bef..0e1a8615c 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/frontend/status.svg)](https://drone.kolaente.de/vikunja/frontend)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
[![Download](https://img.shields.io/badge/download-v0.17.0-brightgreen.svg)](https://dl.vikunja.io)
+[![Translation](https://hosted.weblate.org/widgets/vikunja/-/frontend/svg-badge.svg)](https://hosted.weblate.org/engage/vikunja/)
This is the web frontend for Vikunja, written in Vue.js.
diff --git a/cypress/integration/list/list.spec.js b/cypress/integration/list/list.spec.js
index 3c8c0d6fc..843d82913 100644
--- a/cypress/integration/list/list.spec.js
+++ b/cypress/integration/list/list.spec.js
@@ -388,7 +388,7 @@ describe('Lists', () => {
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
- .contains('Limit: Not set')
+ .contains('Limit: Not Set')
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
.first()
diff --git a/cypress/integration/list/namespaces.spec.js b/cypress/integration/list/namespaces.spec.js
index 9d3125b59..e8c15443e 100644
--- a/cypress/integration/list/namespaces.spec.js
+++ b/cypress/integration/list/namespaces.spec.js
@@ -24,7 +24,7 @@ describe('Namepaces', () => {
cy.visit('/namespaces')
cy.get('a.button')
- .contains('Create namespace')
+ .contains('Create a new namespace')
.click()
cy.url()
diff --git a/cypress/integration/sharing/team.spec.js b/cypress/integration/sharing/team.spec.js
index 094556355..7c48a59aa 100644
--- a/cypress/integration/sharing/team.spec.js
+++ b/cypress/integration/sharing/team.spec.js
@@ -11,7 +11,7 @@ describe('Team', () => {
const newTeamName = 'New Team'
cy.get('a.button')
- .contains('New Team')
+ .contains('Create a new team')
.click()
cy.url()
.should('contain', '/teams/new')
@@ -113,7 +113,7 @@ describe('Team', () => {
cy.get('.card')
.contains('Team Members')
.get('.card-content .button')
- .contains('Add To Team')
+ .contains('Add to team')
.click()
cy.get('table.table td')
diff --git a/cypress/integration/task/task.spec.js b/cypress/integration/task/task.spec.js
index e07936375..ac9481156 100644
--- a/cypress/integration/task/task.spec.js
+++ b/cypress/integration/task/task.spec.js
@@ -27,7 +27,7 @@ describe('Task', () => {
it('Should be created new', () => {
cy.visit('/lists/1/list')
- cy.get('input.input[placeholder="Add a new task..."')
+ cy.get('input.input[placeholder="Add a new task…"')
.type('New Task')
cy.get('.button')
.contains('Add')
@@ -43,7 +43,7 @@ describe('Task', () => {
cy.visit('/lists/1/list')
cy.get('.list-is-empty-notice')
.should('not.exist')
- cy.get('input.input[placeholder="Add a new task..."')
+ cy.get('input.input[placeholder="Add a new task…"')
.type('New Task')
cy.get('.button')
.contains('Add')
diff --git a/package.json b/package.json
index de3b34283..e7aaef5c0 100644
--- a/package.json
+++ b/package.json
@@ -14,14 +14,14 @@
},
"dependencies": {
"browserslist": "4.16.6",
- "bulma": "0.9.2",
+ "bulma": "0.9.3",
"camel-case": "4.1.2",
"copy-to-clipboard": "3.3.1",
"date-fns": "2.22.1",
"dompurify": "2.2.9",
"highlight.js": "11.0.1",
"lodash": "4.17.21",
- "marked": "2.0.7",
+ "marked": "2.1.3",
"register-service-worker": "1.7.2",
"sass": "1.35.1",
"snake-case": "3.0.4",
@@ -30,6 +30,7 @@
"vue-advanced-cropper": "1.7.0",
"vue-drag-resize": "1.5.4",
"vue-easymde": "1.4.0",
+ "vue-i18n": "8.24.5",
"vue-shortkey": "3.1.7",
"vue-smooth-dnd": "0.8.1",
"vuex": "3.6.2"
@@ -46,18 +47,18 @@
"@vue/cli-service": "4.5.13",
"axios": "0.21.1",
"babel-eslint": "10.1.0",
- "cypress": "7.5.0",
- "cypress-file-upload": "5.0.7",
- "eslint": "7.28.0",
- "eslint-plugin-vue": "7.11.1",
+ "cypress": "7.6.0",
+ "cypress-file-upload": "5.0.8",
+ "eslint": "7.29.0",
+ "eslint-plugin-vue": "7.12.1",
"faker": "5.5.3",
- "jest": "27.0.4",
+ "jest": "27.0.5",
"sass-loader": "10.2.0",
"vue-flatpickr-component": "8.1.6",
"vue-notification": "1.3.20",
- "vue-router": "3.5.1",
+ "vue-router": "3.5.2",
"vue-template-compiler": "2.6.14",
- "wait-on": "5.3.0"
+ "wait-on": "6.0.0"
},
"eslintConfig": {
"root": true,
diff --git a/ping-weblate.sh b/ping-weblate.sh
new file mode 100755
index 000000000..af0e2e019
--- /dev/null
+++ b/ping-weblate.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -e
+
+# Shell script because yaml doesn't understand the header is a string literal and not a yaml symbol
+
+curl -d operation=pull -H "Authorization: Token $WEBLATE_TOKEN" https://hosted.weblate.org/api/projects/vikunja/repository/
+curl -d operation=push -H "Authorization: Token $WEBLATE_TOKEN" https://hosted.weblate.org/api/projects/vikunja/repository/
diff --git a/src/App.vue b/src/App.vue
index cb017b6b7..774d81617 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -34,6 +34,7 @@ import TopNavigation from '@/components/home/topNavigation'
import ContentAuth from '@/components/home/contentAuth'
import ContentLinkShare from '@/components/home/contentLinkShare'
import ContentNoAuth from '@/components/home/contentNoAuth'
+import {setLanguage} from '@/i18n/setup'
export default {
name: 'app',
@@ -53,6 +54,8 @@ export default {
beforeCreate() {
this.$store.dispatch('config/update')
this.$store.dispatch('auth/checkAuth')
+
+ setLanguage()
},
created() {
// Make sure to always load the home route when running with electron
diff --git a/src/components/home/contentAuth.vue b/src/components/home/contentAuth.vue
index 576d77bf0..55ab90f8d 100644
--- a/src/components/home/contentAuth.vue
+++ b/src/components/home/contentAuth.vue
@@ -130,7 +130,7 @@ export default {
loadLabels() {
this.$store.dispatch('labels/loadAllLabels')
.catch(e => {
- this.error(e, this)
+ this.error(e)
})
},
},
diff --git a/src/components/home/contentLinkShare.vue b/src/components/home/contentLinkShare.vue
index 0cf685568..e3602c561 100644
--- a/src/components/home/contentLinkShare.vue
+++ b/src/components/home/contentLinkShare.vue
@@ -10,12 +10,12 @@
- {{ currentList.title === '' ? 'Loading...' : currentList.title }}
+ {{ currentList.title === '' ? $t('misc.loading') : currentList.title }}
- Logout
+ {{ $t('user.auth.logout') }}
@@ -23,7 +23,7 @@
diff --git a/src/components/home/contentNoAuth.vue b/src/components/home/contentNoAuth.vue
index ad8dc789e..f8349fbdb 100644
--- a/src/components/home/contentNoAuth.vue
+++ b/src/components/home/contentNoAuth.vue
@@ -4,7 +4,7 @@
{{ motd }}
diff --git a/src/components/home/navigation.vue b/src/components/home/navigation.vue
index a2933d759..65b045088 100644
--- a/src/components/home/navigation.vue
+++ b/src/components/home/navigation.vue
@@ -10,7 +10,7 @@
- Overview
+ {{ $t('navigation.overview') }}
@@ -18,7 +18,7 @@
- Upcoming
+ {{ $t('navigation.upcoming') }}
@@ -26,7 +26,7 @@
- Namespaces & Lists
+ {{ $t('namespace.title') }}
@@ -34,7 +34,7 @@
- Labels
+ {{ $t('label.title') }}
@@ -42,7 +42,7 @@
- Teams
+ {{ $t('team.title') }}
@@ -109,7 +109,9 @@
-
+
@@ -161,7 +163,7 @@ export default {
return
}
this.$store.dispatch('lists/toggleListFavorite', list)
- .catch(e => this.error(e, this))
+ .catch(e => this.error(e))
},
resize() {
// Hide the menu by default on mobile
diff --git a/src/components/home/topNavigation.vue b/src/components/home/topNavigation.vue
index 5a475f154..385f971a2 100644
--- a/src/components/home/topNavigation.vue
+++ b/src/components/home/topNavigation.vue
@@ -25,14 +25,16 @@
>
-
-
- {{ currentList.title === '' ? 'Loading...' : currentList.title }}
-
+
+
+
+ {{ currentList.title === '' ? $t('misc.loading') : currentList.title }}
+
-
+
+
@@ -48,7 +50,7 @@
@@ -116,6 +118,16 @@ export default {
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
canWriteCurrentList: state => state.currentList.maxRight > Rights.READ,
}),
+ mounted() {
+ this.$nextTick(() => {
+ if (typeof this.$refs.usernameDropdown === 'undefined' || typeof this.$refs.listTitle === 'undefined') {
+ return
+ }
+
+ const usernameWidth = this.$refs.usernameDropdown.$el.clientWidth
+ this.$refs.listTitle.style.setProperty('--nav-username-width', `${usernameWidth}px`)
+ })
+ },
methods: {
logout() {
this.$store.dispatch('auth/logout')
diff --git a/src/components/home/update.vue b/src/components/home/update.vue
index 63559e3c2..2569c9c3a 100644
--- a/src/components/home/update.vue
+++ b/src/components/home/update.vue
@@ -1,8 +1,8 @@
-
There is an update for Vikunja available!
+
{{ $t('update.available') }}
- Update Now
+ {{ $t('update.do') }}
diff --git a/src/components/input/colorPicker.vue b/src/components/input/colorPicker.vue
index 5b2395fd7..868ad58ea 100644
--- a/src/components/input/colorPicker.vue
+++ b/src/components/input/colorPicker.vue
@@ -19,7 +19,7 @@
:class="{'is-empty': empty}"
/>
- Reset Color
+ {{ $t('input.resetColor') }}
diff --git a/src/components/input/datepicker.vue b/src/components/input/datepicker.vue
index 5062bfe48..5501287c2 100644
--- a/src/components/input/datepicker.vue
+++ b/src/components/input/datepicker.vue
@@ -18,7 +18,7 @@
- Today
+ {{ $t('input.datepicker.today') }}
{{ getWeekdayFromStringInterval('today') }}
@@ -31,7 +31,7 @@
- Tomorrow
+ {{ $t('input.datepicker.tomorrow') }}
{{ getWeekdayFromStringInterval('tomorrow') }}
@@ -44,7 +44,7 @@
- Next Monday
+ {{ $t('input.datepicker.nextMonday') }}
{{ getWeekdayFromStringInterval('nextMonday') }}
@@ -57,7 +57,7 @@
- This Weekend
+ {{ $t('input.datepicker.thisWeekend') }}
{{ getWeekdayFromStringInterval('thisWeekend') }}
@@ -70,7 +70,7 @@
- Later This Week
+ {{ $t('input.datepicker.laterThisWeek') }}
{{ getWeekdayFromStringInterval('laterThisWeek') }}
@@ -83,7 +83,7 @@
- Next Week
+ {{ $t('input.datepicker.nextWeek') }}
{{ getWeekdayFromStringInterval('nextWeek') }}
@@ -102,7 +102,7 @@
:shadow="false"
@click="close"
>
- Confirm
+ {{ $t('misc.confirm') }}
@@ -118,7 +118,6 @@ import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {createDateFromString} from '@/helpers/time/createDateFromString'
-import {mapState} from 'vuex'
export default {
name: 'datepicker',
@@ -142,7 +141,9 @@ export default {
},
chooseDateLabel: {
type: String,
- default: 'Choose a date',
+ default() {
+ return this.$t('input.datepicker.chooseDate')
+ },
},
disabled: {
type: Boolean,
@@ -165,19 +166,21 @@ export default {
this.updateData()
},
},
- computed: mapState({
- flatPickerConfig: state => ({
- altFormat: 'j M Y H:i',
- altInput: true,
- dateFormat: 'Y-m-d H:i',
- enableTime: true,
- time_24hr: true,
- inline: true,
- locale: {
- firstDayOfWeek: state.auth.settings.weekStart,
- },
- }),
- }),
+ computed: {
+ flatPickerConfig() {
+ return {
+ altFormat: this.$t('date.altFormatLong'),
+ altInput: true,
+ dateFormat: 'Y-m-d H:i',
+ enableTime: true,
+ time_24hr: true,
+ inline: true,
+ locale: {
+ firstDayOfWeek: this.$store.state.auth.settings.weekStart,
+ },
+ }
+ },
+ },
methods: {
setDateValue(newVal) {
if(newVal === null) {
diff --git a/src/components/input/editor.vue b/src/components/input/editor.vue
index f91e2241a..803434bc2 100644
--- a/src/components/input/editor.vue
+++ b/src/components/input/editor.vue
@@ -15,7 +15,7 @@
:shadow="false"
type="secondary"
>
- Done
+ {{ $t('input.editor.done') }}
@@ -129,112 +129,112 @@ export default {
{
name: 'heading-1',
action: EasyMDE.toggleHeading1,
- title: 'Heading 1',
+ title: this.$t('input.editor.heading1'),
icon: ' ',
},
{
name: 'heading-2',
action: EasyMDE.toggleHeading2,
- title: 'Heading 2',
+ title: this.$t('input.editor.heading2'),
icon: ' ',
},
{
name: 'heading-3',
action: EasyMDE.toggleHeading3,
- title: 'Heading 3',
+ title: this.$t('input.editor.heading3'),
icon: ' ',
},
{
name: 'heading-smaller',
action: EasyMDE.toggleHeadingSmaller,
- title: 'Heading Smaller',
+ title: this.$t('input.editor.headingSmaller'),
icon: ' ',
},
{
name: 'heading-bigger',
action: EasyMDE.toggleHeadingBigger,
- title: 'Heading Bigger',
+ title: this.$t('input.editor.headingBigger'),
icon: ' ',
},
'|',
{
name: 'bold',
action: EasyMDE.toggleBold,
- title: 'Bold',
+ title: this.$t('input.editor.bold'),
icon: ' ',
},
{
name: 'italic',
action: EasyMDE.toggleItalic,
- title: 'Italic',
+ title: this.$t('input.editor.italic'),
icon: ' ',
},
{
name: 'strikethrough',
action: EasyMDE.toggleStrikethrough,
- title: 'Strikethrough',
+ title: this.$t('input.editor.strikethrough'),
icon: ' ',
},
{
name: 'code',
action: EasyMDE.toggleCodeBlock,
- title: 'Code',
+ title: this.$t('input.editor.code'),
icon: ' ',
},
{
name: 'quote',
action: EasyMDE.toggleBlockquote,
- title: 'Quote',
+ title: this.$t('input.editor.quote'),
icon: ' ',
},
{
name: 'unordered-list',
action: EasyMDE.toggleUnorderedList,
- title: 'Unordered List',
+ title: this.$t('input.editor.unorderedList'),
icon: ' ',
},
{
name: 'ordered-list',
action: EasyMDE.toggleOrderedList,
- title: 'Ordered List',
+ title: this.$t('input.editor.orderedList'),
icon: ' ',
},
'|',
{
name: 'clean-block',
action: EasyMDE.cleanBlock,
- title: 'Clean Block',
+ title: this.$t('input.editor.cleanBlock'),
icon: ' ',
},
{
name: 'link',
action: EasyMDE.drawLink,
- title: 'Link',
+ title: this.$t('input.editor.link'),
icon: ' ',
},
{
name: 'image',
action: EasyMDE.drawImage,
- title: 'Image',
+ title: this.$t('input.editor.image'),
icon: ' ',
},
{
name: 'table',
action: EasyMDE.drawTable,
- title: 'Table',
+ title: this.$t('input.editor.table'),
icon: ' ',
},
{
name: 'horizontal-rule',
action: EasyMDE.drawHorizontalRule,
- title: 'Horizontal Rule',
+ title: this.$t('input.editor.horizontalRule'),
icon: ' ',
},
'|',
{
name: 'side-by-side',
action: EasyMDE.toggleSideBySide,
- title: 'Side By Side',
+ title: this.$t('input.editor.sideBySide'),
icon: ' ',
},
{
@@ -242,7 +242,7 @@ export default {
action: () => {
window.open('https://www.markdownguide.org/basic-syntax/', '_blank')
},
- title: 'Guide',
+ title: this.$t('input.editor.guide'),
icon: ' ',
},
],
diff --git a/src/components/input/multiselect.vue b/src/components/input/multiselect.vue
index 220113d72..a6ac83b6d 100644
--- a/src/components/input/multiselect.vue
+++ b/src/components/input/multiselect.vue
@@ -28,7 +28,7 @@
:placeholder="placeholder"
@keydown.down.exact.prevent="() => preSelect(0)"
ref="searchInput"
- @focus="() => showSearchResults = true"
+ @focus="handleFocus"
/>
@@ -149,15 +149,15 @@ export default {
createPlaceholder: {
type: String,
default() {
- return 'Create new'
- },
+ return this.$t('input.multiselect.createPlaceholder')
+ }
},
// The text shown next to an option.
selectPlaceholder: {
type: String,
default() {
- return 'Click or press enter to select'
- },
+ return this.$t('input.multiselect.selectPlaceholder')
+ }
},
// If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
multiple: {
@@ -258,6 +258,13 @@ export default {
closeSearchResults() {
this.showSearchResults = false
},
+ handleFocus() {
+ // We need the timeout to avoid the hideSearchResultsHandler hiding the search results right after the input
+ // is focused. That would lead to flickering pre-loaded search results and hiding them right after showing.
+ setTimeout(() => {
+ this.showSearchResults = true
+ }, 10)
+ },
select(object) {
if (this.multiple) {
if (this.internalValue === null) {
diff --git a/src/components/list/list-settings-dropdown.vue b/src/components/list/list-settings-dropdown.vue
index 090defb1f..e44bec3a1 100644
--- a/src/components/list/list-settings-dropdown.vue
+++ b/src/components/list/list-settings-dropdown.vue
@@ -5,13 +5,13 @@
:to="{ name: `${listRoutePrefix}.settings.edit`, params: { listId: list.id } }"
icon="pen"
>
- Edit
+ {{ $t('menu.edit') }}
- Delete
+ {{ $t('misc.delete') }}
@@ -19,7 +19,7 @@
:to="{ name: `${listRoutePrefix}.settings.archive`, params: { listId: list.id } }"
icon="archive"
>
- Un-Archive
+ {{ $t('menu.unarchive') }}
@@ -27,32 +27,32 @@
:to="{ name: `${listRoutePrefix}.settings.edit`, params: { listId: list.id } }"
icon="pen"
>
- Edit
+ {{ $t('menu.edit') }}
- Set background
+ {{ $t('menu.setBackground') }}
- Share
+ {{ $t('menu.share') }}
- Duplicate
+ {{ $t('menu.duplicate') }}
- Archive
+ {{ $t('menu.archive') }}
- Delete
+ {{ $t('menu.delete') }}
diff --git a/src/components/list/partials/filters.vue b/src/components/list/partials/filters.vue
index a28f339e6..1bb586593 100644
--- a/src/components/list/partials/filters.vue
+++ b/src/components/list/partials/filters.vue
@@ -1,28 +1,30 @@
- Include Tasks which don't have a value set
+ {{ $t('filters.attributes.includeNulls') }}
- Require all filters to be true for a task to show up
+ {{ $t('filters.attributes.requireAll') }}
-
Show Done Tasks
+
+ {{ $t('filters.attributes.showDoneTasks') }}
+
- Show Done Tasks
+ {{ $t('filters.attributes.showDoneTasks') }}
-
Search
+
{{ $t('misc.search') }}
-
Priority
+
{{ $t('task.attributes.priority') }}
- Enable Filter By Priority
+ {{ $t('filters.attributes.enablePriority') }}
-
Percent Done
+
{{ $t('task.attributes.percentDone') }}
- Enable Filter By Percent Done
+ {{ $t('filters.attributes.enablePercentDone') }}
-
Due Date
+
{{ $t('task.attributes.dueDate') }}
-
Start Date
+
{{ $t('task.attributes.startDate') }}
-
End Date
+
{{ $t('task.attributes.endDate') }}
-
Reminders
+
{{ $t('task.attributes.reminders') }}
-
Assignees
+
{{ $t('task.attributes.assignees') }}
find('users', query)"
:search-results="foundusers"
@select="() => add('users', 'assignees')"
@@ -128,10 +130,10 @@
-
Labels
+
{{ $t('task.attributes.label') }}
addLabel(label)"
@@ -153,11 +155,11 @@
-
Lists
+
{{ $t('list.lists') }}
find('lists', query)"
:search-results="foundlists"
@select="() => add('lists', 'list_id')"
@@ -169,11 +171,11 @@
-
Namespaces
+
{{ $t('namespace.namespaces') }}
find('namespace', query)"
:search-results="foundnamespace"
@select="() => add('namespace', 'namespace')"
@@ -203,7 +205,6 @@ import Multiselect from '@/components/input/multiselect'
import UserService from '@/services/user'
import ListService from '@/services/list'
import NamespaceService from '@/services/namespace'
-import {mapState} from 'vuex'
export default {
name: 'filters',
@@ -290,19 +291,19 @@ export default {
return first.id === second.id
})
},
- ...mapState({
- flatPickerConfig: state => ({
- altFormat: 'j M Y H:i',
+ flatPickerConfig() {
+ return {
+ altFormat: this.$t('date.altFormatLong'),
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
mode: 'range',
locale: {
- firstDayOfWeek: state.auth.settings.weekStart,
+ firstDayOfWeek: this.$store.state.auth.settings.weekStart,
},
- }),
- }),
+ }
+ },
},
methods: {
change() {
@@ -490,7 +491,7 @@ export default {
.then(r => {
this.$set(this, kind, r)
})
- .catch(e => this.error(e, this))
+ .catch(e => this.error(e))
}
},
setDoneFilter() {
@@ -545,7 +546,7 @@ export default {
}))
})
.catch(e => {
- this.error(e, this)
+ this.error(e)
})
},
add(kind, filterName) {
diff --git a/src/components/migrator/migration.vue b/src/components/migrator/migration.vue
index 4b65fc9fc..d24c08bdf 100644
--- a/src/components/migrator/migration.vue
+++ b/src/components/migrator/migration.vue
@@ -1,15 +1,15 @@
-
Import your data from {{ name }} to Vikunja
-
Vikunja will import all lists, tasks, notes, reminders and files you have access to.
+
{{ $t('migrate.titleService', { name: name }) }}
+
{{ $t('migrate.descriptionDo') }}
- To authorize Vikunja to access your {{ name }} Account, click the button below.
+ {{ $t('migrate.authorize', {name: name}) }}
- Get Started
+ {{ $t('migrate.getStarted') }}
-
Importing in progress, hang tight...
+
{{ $t('migrate.inProgress') }}
- It looks like you've already imported your stuff from {{ name }} at {{ formatDate(lastMigrationDate) }}.
- Importing again is possible, but might create duplicates.
- Are you sure?
+ {{ $t('migrate.alreadyMigrated1', { name: name, date: formatDate(lastMigrationDate) }) }}
+ {{ $t('migrate.alreadyMigrated2') }}
- I am sure, please start migrating now!
- Cancel
+ {{ $t('migrate.confirm') }}
+ {{ $t('misc.cancel') }}
@@ -48,7 +47,7 @@
{{ message }}
-
Refresh
+
{{ $t('misc.refresh') }}
@@ -105,7 +104,7 @@ export default {
this.migrate()
})
.catch(e => {
- this.error(e, this)
+ this.error(e)
})
}
},
@@ -116,7 +115,7 @@ export default {
this.authUrl = r.url
})
.catch(e => {
- this.error(e, this)
+ this.error(e)
})
},
migrate() {
@@ -129,7 +128,7 @@ export default {
this.$store.dispatch('namespaces/loadNamespaces')
})
.catch(e => {
- this.error(e, this)
+ this.error(e)
})
.finally(() => {
this.isMigrating = false
diff --git a/src/components/misc/api-config.vue b/src/components/misc/api-config.vue
index d055ed2d8..8673e691f 100644
--- a/src/components/misc/api-config.vue
+++ b/src/components/misc/api-config.vue
@@ -1,13 +1,13 @@
-
Vikunja URL
+
{{ $t('apiConfig.url') }}
{
// Still not found, url is still invalid
this.successMsg = ''
- this.errorMsg = `Could not find or use Vikunja installation at "${this.apiDomain()}".`
+ this.errorMsg = this.$t('apiConfig.error', {domain: this.apiDomain()})
window.API_URL = oldUrl
})
.then((r) => {
if (typeof r !== 'undefined') {
// Set it + save it to local storage to save us the hoops
this.errorMsg = ''
- this.successMsg = `Using Vikunja installation at "${this.apiDomain()}".`
+ this.successMsg = this.$t('apiConfig.success', {domain: this.apiDomain()})
localStorage.setItem('API_URL', window.API_URL)
this.configureApi = false
this.apiUrl = window.API_URL
diff --git a/src/components/misc/create-edit.vue b/src/components/misc/create-edit.vue
index 01aba2d34..c98378d80 100644
--- a/src/components/misc/create-edit.vue
+++ b/src/components/misc/create-edit.vue
@@ -26,7 +26,7 @@
type="secondary"
@click.prevent.stop="$router.back()"
>
- Cancel
+ {{ $t('misc.cancel') }}
diff --git a/src/components/misc/keyboard-shortcuts.vue b/src/components/misc/keyboard-shortcuts.vue
index d95aa2651..d0fb9d447 100644
--- a/src/components/misc/keyboard-shortcuts.vue
+++ b/src/components/misc/keyboard-shortcuts.vue
@@ -2,56 +2,56 @@
-
+
- These shortcuts work on all pages.
+ {{ $t('keyboardShortcuts.allPages') }}
- Toggle The Menu
+ {{ $t('keyboardShortcuts.toggleMenu') }}
- Open the search/quick action bar
+ {{ $t('keyboardShortcuts.quickSearch') }}
- Kanban
+ {{ $t('list.kanban.title') }}
- These shortcuts work only on the current page.
+ {{ $t('keyboardShortcuts.currentPageOnly') }}
- Mark a task as done
+ {{ $t('keyboardShortcuts.task.done') }}
- Task Page
+ {{ $t('keyboardShortcuts.task.title') }}
- These shortcuts work only on the current page.
+ {{ $t('keyboardShortcuts.currentPageOnly') }}
- Assign this task to a user
+ {{ $t('keyboardShortcuts.task.assign') }}
- Add labels to this task
+ {{ $t('keyboardShortcuts.task.labels') }}
- Change the due date of this task
+ {{ $t('keyboardShortcuts.task.dueDate') }}
- Add an attachment to this task
+ {{ $t('keyboardShortcuts.task.attachment') }}
- Modify related tasks of this task
+ {{ $t('keyboardShortcuts.task.related') }}
diff --git a/src/components/misc/legal.vue b/src/components/misc/legal.vue
index 2afcb4727..cc55ea1a4 100644
--- a/src/components/misc/legal.vue
+++ b/src/components/misc/legal.vue
@@ -1,8 +1,8 @@
diff --git a/src/components/misc/notification.vue b/src/components/misc/notification.vue
index c12137ead..2c839a6fb 100644
--- a/src/components/misc/notification.vue
+++ b/src/components/misc/notification.vue
@@ -11,13 +11,17 @@
>
+ >
+ {{ props.item.title }}
+
+ >
+
+ {{ t }}
+
+
- Cancel
+ {{ $t('misc.cancel') }}
- Do it!
+ {{ $t('misc.doit') }}
diff --git a/src/components/namespace/namespace-search.vue b/src/components/namespace/namespace-search.vue
index d2fbc556a..daf46817c 100644
--- a/src/components/namespace/namespace-search.vue
+++ b/src/components/namespace/namespace-search.vue
@@ -1,7 +1,7 @@
{
- this.error(e, this)
+ this.error(e)
})
},
clearAll() {
diff --git a/src/components/namespace/namespace-settings-dropdown.vue b/src/components/namespace/namespace-settings-dropdown.vue
index a647ed711..ad1327a6c 100644
--- a/src/components/namespace/namespace-settings-dropdown.vue
+++ b/src/components/namespace/namespace-settings-dropdown.vue
@@ -5,7 +5,7 @@
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
icon="archive"
>
- Un-Archive
+ {{ $t('menu.unarchive') }}
@@ -13,25 +13,25 @@
:to="{ name: 'namespace.settings.edit', params: { id: namespace.id } }"
icon="pen"
>
- Edit
+ {{ $t('menu.edit') }}
- Share
+ {{ $t('menu.share') }}
- New list
+ {{ $t('menu.newList') }}
- Archive
+ {{ $t('menu.archive') }}
- Delete
+ {{ $t('menu.delete') }}
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index e0f72422d..21049a3fb 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -1,9 +1,11 @@
-
-
-
-
+
@@ -35,9 +37,9 @@
- You don't have any notifications. Have a nice day!
+ {{ $t('notification.none') }}
- Notifications will appear here when actions on namespaces, lists or tasks you subscribed to happen.
+ {{ $t('notification.explainer') }}
@@ -98,7 +100,7 @@ export default {
this.$set(this, 'allNotifications', r)
})
.catch(e => {
- this.error(e, this)
+ this.error(e)
})
},
to(n, index) {
@@ -136,7 +138,7 @@ export default {
.then(r => {
this.$set(this.allNotifications, index, r)
})
- .catch(e => this.error(e, this))
+ .catch(e => this.error(e))
}
},
},
diff --git a/src/components/quick-actions/quick-actions.vue b/src/components/quick-actions/quick-actions.vue
index b5c967bcc..bf1152d01 100644
--- a/src/components/quick-actions/quick-actions.vue
+++ b/src/components/quick-actions/quick-actions.vue
@@ -51,15 +51,13 @@