1
0

Add translations (#562)

Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/562
Co-authored-by: konrad <konrad@kola-entertainments.de>
Co-committed-by: konrad <konrad@kola-entertainments.de>
This commit is contained in:
konrad
2021-06-23 23:24:57 +00:00
parent 5badb65037
commit f0498fd767
103 changed files with 2306 additions and 973 deletions

View File

@ -3,17 +3,17 @@
<h2 class="title has-text-centered">Login</h2>
<div class="box">
<div class="notification is-success has-text-centered" v-if="confirmedEmailSuccess">
You successfully confirmed your email! You can log in now.
{{ $t('user.auth.confirmEmailSuccess') }}
</div>
<api-config @foundApi="hasApiUrl = true"/>
<form @submit.prevent="submit" id="loginform" v-if="hasApiUrl && localAuthEnabled">
<div class="field">
<label class="label" for="username">Username Or Email Address</label>
<label class="label" for="username">{{ $t('user.auth.usernameEmail') }}</label>
<div class="control">
<input
class="input" id="username"
name="username"
placeholder="e.g. frederick"
:placeholder="$t('user.auth.usernamePlaceholder')"
ref="username"
required
type="text"
@ -24,13 +24,13 @@
</div>
</div>
<div class="field">
<label class="label" for="password">Password</label>
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
<div class="control">
<input
class="input"
id="password"
name="password"
placeholder="e.g. ••••••••••••"
:placeholder="$t('user.auth.passwordPlaceholder')"
ref="password"
required
type="password"
@ -40,12 +40,12 @@
</div>
</div>
<div class="field" v-if="needsTotpPasscode">
<label class="label" for="totpPasscode">Two Factor Authentication Code</label>
<label class="label" for="totpPasscode">{{ $t('user.auth.totpTitle') }}</label>
<div class="control">
<input
class="input"
id="totpPasscode"
placeholder="e.g. 123456"
:placeholder="$t('user.auth.totpPlaceholder')"
ref="totpPasscode"
required
type="text"
@ -61,19 +61,19 @@
@click="submit"
:loading="loading"
>
Login
{{ $t('user.auth.login') }}
</x-button>
<x-button
:to="{ name: 'user.register' }"
v-if="registrationEnabled"
type="secondary"
>
Register
{{ $t('user.auth.register') }}
</x-button>
</div>
<div class="control">
<router-link :to="{ name: 'user.password-reset.request' }" class="reset-password-link">
Reset your password
{{ $t('user.auth.resetPassword') }}
</router-link>
</div>
</div>
@ -92,7 +92,7 @@
type="secondary"
class="is-fullwidth mt-2"
>
Log in with {{ p.name }}
{{ $t('user.auth.loginWith', {provider: p.name}) }}
</x-button>
</div>
@ -109,6 +109,7 @@ import {HTTPFactory} from '@/http-common'
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
import legal from '../../components/misc/legal'
import ApiConfig from '@/components/misc/api-config'
import {getErrorText} from '@/message'
export default {
components: {
@ -147,7 +148,7 @@ export default {
},
created() {
this.hasApiUrl = window.API_URL !== ''
this.setTitle('Login')
this.setTitle(this.$t('user.auth.login'))
},
computed: mapState({
registrationEnabled: state => state.config.registrationEnabled,
@ -183,7 +184,14 @@ export default {
}
this.$store.dispatch('auth/login', credentials)
.catch(() => {
.catch(e => {
const err = getErrorText(e, p => this.$t(p))
if (typeof err[1] !== 'undefined') {
this.$store.commit(ERROR_MESSAGE, err[1])
return
}
this.$store.commit(ERROR_MESSAGE, err[0])
})
},
redirectToProvider(provider) {

View File

@ -4,7 +4,7 @@
{{ errorMessage }}
</div>
<div class="notification is-info" v-if="loading">
Authenticating...
{{ $t('user.auth.authenticating') }}
</div>
</div>
</template>
@ -13,6 +13,7 @@
import {mapState} from 'vuex'
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
import {getErrorText} from '@/message'
export default {
name: 'Auth',
@ -41,7 +42,7 @@ export default {
const state = localStorage.getItem('state')
if(typeof this.$route.query.state === 'undefined' || this.$route.query.state !== state) {
localStorage.removeItem('authenticating')
this.$store.commit(ERROR_MESSAGE, 'State does not match, refusing to continue!')
this.$store.commit(ERROR_MESSAGE, this.$t('user.auth.openIdStateError'))
return
}
@ -54,8 +55,14 @@ export default {
.then(() => {
this.$router.push({name: 'home'})
})
.catch(() => {
// Handled through global state
.catch(e => {
const err = getErrorText(e, p => this.$t(p))
if (typeof err[1] !== 'undefined') {
this.$store.commit(ERROR_MESSAGE, err[1])
return
}
this.$store.commit(ERROR_MESSAGE, err[0])
})
.finally(() => {
localStorage.removeItem('authenticating')

View File

@ -1,16 +1,16 @@
<template>
<div>
<h2 class="title has-text-centered">Reset your password</h2>
<h2 class="title has-text-centered">{{ $t('user.auth.resetPassword') }}</h2>
<div class="box">
<form @submit.prevent="submit" id="form" v-if="!successMessage">
<div class="field">
<label class="label" for="password1">Password</label>
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
<div class="control">
<input
class="input"
id="password1"
name="password1"
placeholder="e.g. ••••••••••••"
:placeholder="$t('user.auth.passwordPlaceholder')"
required
type="password"
autocomplete="new-password"
@ -19,13 +19,13 @@
</div>
</div>
<div class="field">
<label class="label" for="password2">Retype your password</label>
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
<div class="control">
<input
class="input"
id="password2"
name="password2"
placeholder="e.g. ••••••••••••"
:placeholder="$t('user.auth.passwordPlaceholder')"
required
type="password"
autocomplete="new-password"
@ -39,12 +39,12 @@
:loading="this.passwordResetService.loading"
@click="submit"
>
Reset your password
{{ $t('user.auth.resetPassoword') }}
</x-button>
</div>
</div>
<div class="notification is-info" v-if="this.passwordResetService.loading">
Loading...
{{ $t('misc.loading') }}
</div>
<div class="notification is-danger" v-if="errorMsg">
{{ errorMsg }}
@ -55,7 +55,7 @@
{{ successMessage }}
</div>
<x-button :to="{ name: 'user.login' }">
Login
{{ $t('user.auth.login') }}
</x-button>
</div>
<legal/>
@ -87,14 +87,14 @@ export default {
this.passwordResetService = new PasswordResetService()
},
mounted() {
this.setTitle('Reset your password')
this.setTitle(this.$t('user.auth.resetPassword'))
},
methods: {
submit() {
this.errorMsg = ''
if (this.credentials.password2 !== this.credentials.password) {
this.errorMsg = 'Passwords don\'t match'
this.errorMsg = this.$t('user.auth.passwordsDontMatch')
return
}

View File

@ -1,16 +1,16 @@
<template>
<div>
<h2 class="title has-text-centered">Register</h2>
<h2 class="title has-text-centered">{{ $t('user.auth.register') }}</h2>
<div class="box">
<form @submit.prevent="submit" id="registerform">
<div class="field">
<label class="label" for="username">Username</label>
<label class="label" for="username">{{ $t('user.auth.username') }}</label>
<div class="control">
<input
class="input"
id="username"
name="username"
placeholder="e.g. frederick"
:placeholder="$t('user.auth.usernamePlaceholder')"
required
type="text"
autocomplete="username"
@ -21,13 +21,13 @@
</div>
</div>
<div class="field">
<label class="label" for="email">E-mail address</label>
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
<div class="control">
<input
class="input"
id="email"
name="email"
placeholder="e.g. frederic@vikunja.io"
:placeholder="$t('user.auth.emailPlaceholder')"
required
type="email"
v-model="credentials.email"
@ -36,13 +36,13 @@
</div>
</div>
<div class="field">
<label class="label" for="password1">Password</label>
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
<div class="control">
<input
class="input"
id="password1"
name="password1"
placeholder="e.g. ••••••••••••"
:placeholder="$t('user.auth.passwordPlaceholder')"
required
type="password"
autocomplete="new-password"
@ -52,13 +52,13 @@
</div>
</div>
<div class="field">
<label class="label" for="password2">Retype your password</label>
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
<div class="control">
<input
class="input"
id="password2"
name="password2"
placeholder="e.g. ••••••••••••"
:placeholder="$t('user.auth.passwordPlaceholder')"
required
type="password"
autocomplete="new-password"
@ -76,13 +76,15 @@
@click="submit"
class="mr-2"
>
Register
{{ $t('user.auth.register') }}
</x-button>
<x-button :to="{ name: 'user.login' }" type="secondary">
{{ $t('user.auth.login') }}
</x-button>
<x-button :to="{ name: 'user.login' }" type="secondary">Login</x-button>
</div>
</div>
<div class="notification is-info" v-if="loading">
Loading...
{{ $t('misc.loading') }}
</div>
<div class="notification is-danger" v-if="errorMessage !== ''">
{{ errorMessage }}
@ -120,7 +122,7 @@ export default {
}
},
mounted() {
this.setTitle('Register')
this.setTitle(this.$t('user.auth.register'))
},
computed: mapState({
authenticated: state => state.auth.authenticated,
@ -133,7 +135,7 @@ export default {
this.$store.commit(ERROR_MESSAGE, '')
if (this.credentials.password2 !== this.credentials.password) {
this.$store.commit(ERROR_MESSAGE, 'Passwords don\'t match.')
this.$store.commit(ERROR_MESSAGE, this.$t('user.auth.passwordsDontMatch'))
this.$store.commit(LOADING, false)
return
}

View File

@ -1,16 +1,16 @@
<template>
<div>
<h2 class="title has-text-centered">Reset your password</h2>
<h2 class="title has-text-centered">{{ $t('user.auth.resetPassword') }}</h2>
<div class="box">
<form @submit.prevent="submit" v-if="!isSuccess">
<div class="field">
<label class="label" for="email">E-mail address</label>
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
<div class="control">
<input
class="input"
id="email"
name="email"
placeholder="e.g. frederic@vikunja.io"
:placeholder="$t('user.auth.emailPlaceholder')"
required
type="email"
v-focus
@ -24,9 +24,11 @@
@click="submit"
:loading="passwordResetService.loading"
>
Send me a password reset link
{{ $t('user.auth.resetPasswordAction') }}
</x-button>
<x-button :to="{ name: 'user.login' }" type="secondary">
{{ $t('user.auth.login') }}
</x-button>
<x-button :to="{ name: 'user.login' }" type="secondary">Login</x-button>
</div>
</div>
<div class="notification is-danger" v-if="errorMsg">
@ -35,9 +37,11 @@
</form>
<div class="has-text-centered" v-if="isSuccess">
<div class="notification is-success">
Check your inbox! You should have a mail with instructions on how to reset your password.
{{ $t('user.auth.resetPasswordSuccess') }}
</div>
<x-button :to="{ name: 'user.login' }">Login</x-button>
<x-button :to="{ name: 'user.login' }">
{{ $t('user.auth.login') }}
</x-button>
</div>
<legal/>
</div>
@ -66,7 +70,7 @@ export default {
this.passwordReset = new PasswordResetModel()
},
mounted() {
this.setTitle('Reset your password')
this.setTitle(this.$t('user.auth.resetPassword'))
},
methods: {
submit() {

View File

@ -3,15 +3,15 @@
:class="{ 'is-loading': passwordUpdateService.loading || emailUpdateService.loading || totpService.loading }"
class="loader-container is-max-width-desktop">
<!-- General -->
<card title="General Settings" class="general-settings">
<card :title="$t('user.settings.general.title')" class="general-settings">
<div class="field">
<label class="label" for="newName">Name</label>
<label class="label" for="newName">{{ $t('user.settings.general.name') }}</label>
<div class="control">
<input
@keyup.enter="updateSettings"
class="input"
id="newName"
placeholder="The new name"
:placeholder="$t('user.settings.general.newName')"
type="text"
v-model="settings.name"/>
</div>
@ -19,42 +19,54 @@
<div class="field">
<label class="checkbox">
<input type="checkbox" v-model="settings.emailRemindersEnabled"/>
Send me reminders for tasks via Email
{{ $t('user.settings.general.emailReminders') }}
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" v-model="settings.overdueTasksRemindersEnabled"/>
Send me reminders for overdue undone tasks via email each morning
{{ $t('user.settings.general.overdueReminders') }}
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" v-model="settings.discoverableByName"/>
Let other users find me when they search for my name
{{ $t('user.settings.general.discoverableByName') }}
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" v-model="settings.discoverableByEmail"/>
Let other users find me when they search for my full email
{{ $t('user.settings.general.discoverableByEmail') }}
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" v-model="playSoundWhenDone"/>
Play a sound when marking tasks as done
{{ $t('user.settings.general.playSoundWhenDone') }}
</label>
</div>
<div class="field">
<label class="is-flex is-align-items-center">
<span>
Week starts on
{{ $t('user.settings.general.weekStart') }}
</span>
<div class="select ml-2">
<select v-model.number="settings.weekStart">
<option value="0">Sunday</option>
<option value="1">Monday</option>
<option value="0">{{ $t('user.settings.general.weekStartSunday') }}</option>
<option value="1">{{ $t('user.settings.general.weekStartMonday') }}</option>
</select>
</div>
</label>
</div>
<div class="field">
<label class="is-flex is-align-items-center">
<span>
{{ $t('user.settings.general.language') }}
</span>
<div class="select ml-2">
<select v-model.number="language">
<option :value="l" v-for="(lang, l) in availableLanguages" :key="l">{{ lang }}</option>
</select>
</div>
</label>
@ -65,7 +77,7 @@
@click="updateSettings()"
class="is-fullwidth mt-4"
>
Save
{{ $t('misc.save') }}
</x-button>
</card>
@ -73,40 +85,40 @@
<avatar-settings/>
<!-- Password update -->
<card title="Update Your Password">
<card :title="$t('user.settings.newPasswordTitle')">
<form @submit.prevent="updatePassword()">
<div class="field">
<label class="label" for="newPassword">New Password</label>
<label class="label" for="newPassword">{{ $t('user.settings.newPassword') }}</label>
<div class="control">
<input
@keyup.enter="updatePassword"
class="input"
id="newPassword"
placeholder="The new password..."
:placeholder="$t('user.auth.passwordPlaceholder')"
type="password"
v-model="passwordUpdate.newPassword"/>
</div>
</div>
<div class="field">
<label class="label" for="newPasswordConfirm">New Password Confirmation</label>
<label class="label" for="newPasswordConfirm">{{ $t('user.settings.newPasswordConfirm') }}</label>
<div class="control">
<input
@keyup.enter="updatePassword"
class="input"
id="newPasswordConfirm"
placeholder="Confirm your new password..."
:placeholder="$t('user.auth.passwordPlaceholder')"
type="password"
v-model="passwordConfirm"/>
</div>
</div>
<div class="field">
<label class="label" for="currentPassword">Current Password</label>
<label class="label" for="currentPassword">{{ $t('user.settings.currentPassword') }}</label>
<div class="control">
<input
@keyup.enter="updatePassword"
class="input"
id="currentPassword"
placeholder="Your current password"
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
type="password"
v-model="passwordUpdate.oldPassword"/>
</div>
@ -117,33 +129,33 @@
:loading="passwordUpdateService.loading"
@click="updatePassword()"
class="is-fullwidth mt-4">
Save
{{ $t('misc.save') }}
</x-button>
</card>
<!-- Update E-Mail -->
<card title="Update Your E-Mail Address">
<card :title="$t('user.settings.updateEmailTitle')">
<form @submit.prevent="updateEmail()">
<div class="field">
<label class="label" for="newEmail">New Email Address</label>
<label class="label" for="newEmail">{{ $t('user.settings.updateEmailNew') }}</label>
<div class="control">
<input
@keyup.enter="updateEmail"
class="input"
id="newEmail"
placeholder="The new email address..."
:placeholder="$t('user.auth.emailPlaceholder')"
type="email"
v-model="emailUpdate.newEmail"/>
</div>
</div>
<div class="field">
<label class="label" for="currentPasswordEmail">Current Password</label>
<label class="label" for="currentPasswordEmail">{{ $t('user.settings.currentPassword') }}</label>
<div class="control">
<input
@keyup.enter="updateEmail"
class="input"
id="currentPasswordEmail"
placeholder="Your current password"
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
type="password"
v-model="emailUpdate.password"/>
</div>
@ -154,82 +166,83 @@
:loading="emailUpdateService.loading"
@click="updateEmail()"
class="is-fullwidth mt-4">
Save
{{ $t('misc.save') }}
</x-button>
</card>
<!-- TOTP -->
<card title="Two Factor Authentication" v-if="totpEnabled">
<card :title="$t('user.settings.totp.title')" v-if="totpEnabled">
<x-button
:loading="totpService.loading"
@click="totpEnroll()"
v-if="!totpEnrolled && totp.secret === ''">
Enroll
{{ $t('user.settings.totp.enroll') }}
</x-button>
<template v-else-if="totp.secret !== '' && !totp.enabled">
<p>
To finish your setup, use this secret in your totp app (Google Authenticator or similar):
{{ $t('user.settings.totp.finishSetupPart1') }}
<strong>{{ totp.secret }}</strong><br/>
After that, enter a code from your app below.
{{ $t('user.settings.totp.finishSetupPart2') }}
</p>
<p>
Alternatively you can scan this QR code:<br/>
{{ $t('user.settings.totp.scanQR') }}<br/>
<img :src="totpQR" alt=""/>
</p>
<div class="field">
<label class="label" for="totpConfirmPasscode">Passcode</label>
<label class="label" for="totpConfirmPasscode">{{ $t('user.settings.totp.passcode') }}</label>
<div class="control">
<input
@keyup.enter="totpConfirm()"
class="input"
id="totpConfirmPasscode"
placeholder="A code generated by your totp application"
:placeholder="$t('user.settings.totp.passcodePlaceholder')"
type="text"
v-model="totpConfirmPasscode"/>
</div>
</div>
<x-button @click="totpConfirm()">Confirm</x-button>
<x-button @click="totpConfirm()">{{ $t('misc.confirm') }}</x-button>
</template>
<template v-else-if="totp.secret !== '' && totp.enabled">
<p>
You've sucessfully set up two factor authentication!
{{ $t('user.settings.totp.setupSuccess') }}
</p>
<p v-if="!totpDisableForm">
<x-button @click="totpDisableForm = true" class="is-danger">Disable</x-button>
<x-button @click="totpDisableForm = true" class="is-danger">{{ $t('misc.disable') }}</x-button>
</p>
<div v-if="totpDisableForm">
<div class="field">
<label class="label" for="currentPassword">Please Enter Your Password</label>
<label class="label" for="currentPassword">{{ $t('user.settings.totp.enterPassword') }}</label>
<div class="control">
<input
@keyup.enter="totpDisable"
class="input"
id="currentPassword"
placeholder="Your current password"
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
type="password"
v-focus
v-model="totpDisablePassword"/>
</div>
</div>
<x-button @click="totpDisable()" class="is-danger">Disable two factor authentication</x-button>
<x-button @click="totpDisable()" class="is-danger">
{{ $t('user.settings.totp.disable') }}
</x-button>
</div>
</template>
</card>
<!-- Migration -->
<card title="Migrate from other services to Vikunja" v-if="migratorsEnabled">
<card :title="$t('migrate.title')" v-if="migratorsEnabled">
<x-button
:to="{name: 'migrate.start'}"
>
Import your data into Vikunja
{{ $t('migrate.import') }}
</x-button>
</card>
<!-- Caldav -->
<card v-if="caldavEnabled" title="Caldav">
<card v-if="caldavEnabled" :title="$t('user.settings.caldav.title')">
<p>
You can connect Vikunja to caldav clients to view and manage all tasks from different clients.
Enter this url into your client:
{{ $t('user.settings.caldav.howTo') }}
</p>
<div class="field has-addons no-input-mobile">
<div class="control is-expanded">
@ -239,14 +252,14 @@
<x-button
@click="copy(caldavUrl)"
:shadow="false"
v-tooltip="'Copy to clipboard'"
v-tooltip="$t('misc.copy')"
icon="paste"
/>
</div>
</div>
<p>
<a href="https://vikunja.io/docs/caldav/" target="_blank">
More information about caldav in Vikunja
{{ $t('user.settings.caldav.more') }}
</a>
</p>
</card>
@ -263,6 +276,7 @@ import TotpService from '../../services/totp'
import UserSettingsService from '../../services/userSettings'
import UserSettingsModel from '../../models/userSettings'
import {playSoundWhenDoneKey} from '@/helpers/playPop'
import {availableLanguages, saveLanguage, getCurrentLanguage} from '@/i18n/setup'
import {mapState} from 'vuex'
@ -288,6 +302,7 @@ export default {
totpDisableForm: false,
totpDisablePassword: '',
playSoundWhenDone: false,
language: getCurrentLanguage(),
settings: UserSettingsModel,
userSettingsService: UserSettingsService,
@ -314,7 +329,7 @@ export default {
this.totpStatus()
},
mounted() {
this.setTitle('Settings')
this.setTitle(this.$t('user.settings.title'))
},
computed: {
caldavUrl() {
@ -328,6 +343,9 @@ export default {
return `${apiBase}/dav/principals/${this.userInfo.username}/`
},
availableLanguages() {
return availableLanguages
},
...mapState({
totpEnabled: state => state.config.totpEnabled,
migratorsEnabled: state => state.config.availableMigrators !== null && state.config.availableMigrators.length > 0,
@ -338,20 +356,20 @@ export default {
methods: {
updatePassword() {
if (this.passwordConfirm !== this.passwordUpdate.newPassword) {
this.error({message: 'The new password and its confirmation don\'t match.'})
this.error({message: this.$t('user.settings.passwordsDontMatch')})
return
}
this.passwordUpdateService.update(this.passwordUpdate)
.then(() => {
this.success({message: 'The password was successfully updated.'})
this.success({message: this.$t('user.settings.passwordUpdateSuccess')})
})
.catch(e => this.error(e))
},
updateEmail() {
this.emailUpdateService.update(this.emailUpdate)
.then(() => {
this.success({message: 'Your email address was successfully updated. We\'ve sent you a link to confirm it.'})
this.success({message: this.$t('user.settings.updateEmailSuccess')})
})
.catch(e => this.error(e))
},
@ -394,7 +412,7 @@ export default {
this.totpService.enable({passcode: this.totpConfirmPasscode})
.then(() => {
this.$set(this.totp, 'enabled', true)
this.success({message: 'You\'ve successfully confirmed your totp setup and can use it from now on!'})
this.success({message: this.$t('user.settings.totp.confirmSuccess')})
})
.catch(e => this.error(e))
},
@ -403,17 +421,18 @@ export default {
.then(() => {
this.totpEnrolled = false
this.$set(this, 'totp', new TotpModel())
this.success({message: 'Two factor authentication was sucessfully disabled.'})
this.success({message: this.$t('user.settings.totp.disableSuccess')})
})
.catch(e => this.error(e))
},
updateSettings() {
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
saveLanguage(this.language)
this.userSettingsService.update(this.settings)
.then(() => {
this.$store.commit('auth/setUserSettings', this.settings)
this.success({message: 'The name was successfully changed.'})
this.success({message: this.$t('user.settings.general.savedSuccess')})
})
.catch(e => this.error(e))
},