Add localization support
This commit is contained in:
parent
47b0436b14
commit
91f9b8c4a7
|
@ -61,3 +61,9 @@ If you want to add new pages, there are some things to be aware of.
|
||||||
- Create a new subfolder for the page in the `views` folder. The name of the new folder must match the name of the FTL file in the [Keycloak base theme](https://github.com/keycloak/keycloak/tree/main/themes/src/main/resources/theme/base/login).
|
- Create a new subfolder for the page in the `views` folder. The name of the new folder must match the name of the FTL file in the [Keycloak base theme](https://github.com/keycloak/keycloak/tree/main/themes/src/main/resources/theme/base/login).
|
||||||
- Copy the three `index.*` files of an existing page into the new folder. The page name also needs to be adjusted in the `index.ftl` file within the attribute `pageId` as well as in the path for the script.
|
- Copy the three `index.*` files of an existing page into the new folder. The page name also needs to be adjusted in the `index.ftl` file within the attribute `pageId` as well as in the path for the script.
|
||||||
- Add the new page in `webpack.config.js` in the upper part to the list `customPages`.
|
- Add the new page in `webpack.config.js` in the upper part to the list `customPages`.
|
||||||
|
|
||||||
|
### Localization
|
||||||
|
|
||||||
|
If the browser language is supported, it will be loaded automatically. Otherwise, English is loaded by default.
|
||||||
|
|
||||||
|
To add new languages, the corresponding file must be added under `static/login/resources/locales`. Then the language must be added to `SUPPORT_LOCALES` in `functions/i18n.ts`.
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rfc4648": "^1.5.2",
|
"rfc4648": "^1.5.2",
|
||||||
"vue": "^3.2.26"
|
"vue": "^3.2.26",
|
||||||
|
"vue-i18n": "9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
src/functions/i18n.ts
Normal file
39
src/functions/i18n.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { nextTick } from "vue"
|
||||||
|
import { createI18n } from "vue-i18n"
|
||||||
|
import { KcContextBase } from "~/types/context";
|
||||||
|
|
||||||
|
export const SUPPORT_LOCALES = ["en", "de"]
|
||||||
|
|
||||||
|
export function setupI18n(options = {}) {
|
||||||
|
let browserLocale = navigator.language.split("-")[0];
|
||||||
|
let defaultLocale = "en";
|
||||||
|
if (SUPPORT_LOCALES.includes(browserLocale)) {
|
||||||
|
defaultLocale = browserLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
const i18n = createI18n(options)
|
||||||
|
setI18nLanguage(i18n, defaultLocale)
|
||||||
|
return i18n
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setI18nLanguage(i18n, locale) {
|
||||||
|
loadLocaleMessages(i18n, locale);
|
||||||
|
if (i18n.mode === 'legacy') {
|
||||||
|
i18n.global.locale = locale
|
||||||
|
} else {
|
||||||
|
i18n.global.locale.value = locale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadLocaleMessages(i18n, locale) {
|
||||||
|
// load locale messages with dynamic import
|
||||||
|
let context = (window as any).kcContext as KcContextBase.Common;
|
||||||
|
const messages = await import(
|
||||||
|
/* webpackIgnore: true */`${context.url.resourcesPath}/locales/${locale}.js`
|
||||||
|
)
|
||||||
|
|
||||||
|
// set locale and locale message
|
||||||
|
i18n.global.setLocaleMessage(locale, messages.content)
|
||||||
|
|
||||||
|
return nextTick()
|
||||||
|
}
|
37
src/static/login/resources/locales/de.js
Normal file
37
src/static/login/resources/locales/de.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
export const content = {
|
||||||
|
"login": {
|
||||||
|
"title": "Mit Ihrem Konto anmelden",
|
||||||
|
"noAccount": "Sie haben noch kein Konto?",
|
||||||
|
"signupLink": "Jetzt registrieren.",
|
||||||
|
"username": "Benutzername",
|
||||||
|
"usernameOrEmail": "Benutzername oder E-Mail-Adresse",
|
||||||
|
"password": "Passwort",
|
||||||
|
"forgotPassword": "Passwort vergessen?",
|
||||||
|
"rememberMe": "Angemeldet bleiben",
|
||||||
|
"login": "Anmelden",
|
||||||
|
"rety": "Erneut versuchen",
|
||||||
|
"welcome": "Willkommen!"
|
||||||
|
},
|
||||||
|
"2fa": {
|
||||||
|
"selectFactor": "Bitte wählen Sie einen zweiten Faktor aus:",
|
||||||
|
"hwSecKey": "Hardware Sicherheits-Schlüssel",
|
||||||
|
"hwSecKeyDesc": "Verwenden Sie ein WebAuthn kompatibles Gerät",
|
||||||
|
"otp": "Authenticator App",
|
||||||
|
"otpDesc": "Verwenden Sie einen Einmalcode aus ihrem Authenticator",
|
||||||
|
"recoveryCode": "Wiederherstellungsschlüssel",
|
||||||
|
"recoveryCodeDesc": "Verwenden Sie einen ihrer Wiederherstellungsschlüssel",
|
||||||
|
"tryAnotherWay": "Eine andere Option nutzen."
|
||||||
|
},
|
||||||
|
"webauthn": {
|
||||||
|
"title": "Bitte verwenden Sie eines ihrer Geräte, um fortzufahren:",
|
||||||
|
"registered": "Registriert: {date}",
|
||||||
|
"authenticate": "Fortfahren",
|
||||||
|
"noDevice": "Kein Gerät zur Hand?",
|
||||||
|
"noSupport": "Es scheint, als würde ihr Browser WebAuthn nicht unterstützen. Bitte nutzen Sie einen anderen Browser für die Anmeldung.",
|
||||||
|
"noSupportOtherMethod": "Es scheint, als würde ihr Browser WebAuthn nicht unterstützen. Bitte nutzen Sie einen anderen Browser für die Anmeldung, oder wählen Sie eine andere Anmeldeoption.",
|
||||||
|
"error": "Bei der Anmeldung mit ihrem Gerät ist etwas schief gelaufen. Bitte versuchen Sie es erneut, oder wählen Sie eine andere Anmeldeoption."
|
||||||
|
},
|
||||||
|
"redirect": {
|
||||||
|
"message": "Sie werden weitergeleitet..."
|
||||||
|
}
|
||||||
|
}
|
37
src/static/login/resources/locales/en.js
Normal file
37
src/static/login/resources/locales/en.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
export const content = {
|
||||||
|
"login": {
|
||||||
|
"title": "Login to your account",
|
||||||
|
"noAccount": "Don't have an account yet?",
|
||||||
|
"signupLink": "Sign up.",
|
||||||
|
"username": "Username",
|
||||||
|
"usernameOrEmail": "Email or Username",
|
||||||
|
"password": "Password",
|
||||||
|
"forgotPassword": "Forgot Password?",
|
||||||
|
"rememberMe": "Remember me",
|
||||||
|
"login": "Login",
|
||||||
|
"rety": "Retry",
|
||||||
|
"welcome": "Welcome!"
|
||||||
|
},
|
||||||
|
"2fa": {
|
||||||
|
"selectFactor": "Please select a second factor you would like to use:",
|
||||||
|
"hwSecKey": "Hardware Security Key",
|
||||||
|
"hwSecKeyDesc": "Authenticate using a WebAuthn capable device",
|
||||||
|
"otp": "Authenticator App",
|
||||||
|
"otpDesc": "Authenticate using a one time code from your authenticator app",
|
||||||
|
"recoveryCode": "Recovery Code",
|
||||||
|
"recoveryCodeDesc": "Authenticate using one of your recovery codes",
|
||||||
|
"tryAnotherWay": "Try another method."
|
||||||
|
},
|
||||||
|
"webauthn": {
|
||||||
|
"title": "Please use one of your registered devices to continue:",
|
||||||
|
"registered": "Registered: {date}",
|
||||||
|
"authenticate": "Continue",
|
||||||
|
"noDevice": "Don't have your device at hand?",
|
||||||
|
"noSupport": "It seems that your browser doesn't support WebAuthn. Please try logging in with a different browser.",
|
||||||
|
"noSupportOtherMethod": "It seems that your browser doesn't support WebAuthn. Please try logging in with a different browser, or try a different login method using the button below.",
|
||||||
|
"error": "Something went wrong during authentication using your device. Please try again, or use a different login method."
|
||||||
|
},
|
||||||
|
"redirect": {
|
||||||
|
"message": "Redirecting..."
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,2 @@
|
||||||
locales=en,de
|
locales=en
|
||||||
parent=keycloak
|
parent=keycloak
|
|
@ -1,7 +1,9 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import index from "./index.vue";
|
import index from "./index.vue";
|
||||||
|
import { setupI18n } from "~/functions/i18n";
|
||||||
|
|
||||||
if ((window as any).kcContext) {
|
if ((window as any).kcContext) {
|
||||||
const app = createApp(index);
|
const app = createApp(index);
|
||||||
|
app.use(setupI18n())
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<layout>
|
<layout>
|
||||||
<h1 class="mb-8 text-3xl font-semibold text-center text-gray-700">
|
<h1 class="mb-8 text-3xl font-semibold text-center text-gray-700">
|
||||||
Login to your account
|
{{ $t("login.title") }}
|
||||||
</h1>
|
</h1>
|
||||||
<p
|
<p
|
||||||
v-if="context.realm.registrationAllowed"
|
v-if="context.realm.registrationAllowed"
|
||||||
class="mt-4 text-sm font-light text-center text-gray-700">
|
class="mt-4 text-sm font-light text-center text-gray-700">
|
||||||
Don't have an account yet?
|
{{ $t("login.noAccount") }}
|
||||||
<a
|
<a
|
||||||
:href="context.url.registrationUrl"
|
:href="context.url.registrationUrl"
|
||||||
class="font-medium text-blue-500 hover:underline"
|
class="font-medium text-blue-500 hover:underline"
|
||||||
>Sign up</a
|
>{{ $t("login.signupLink") }}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<div v-if="context.message?.type == 'error'">
|
<div v-if="context.message?.type == 'error'">
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
method="post">
|
method="post">
|
||||||
<div>
|
<div>
|
||||||
<label for="username" class="block text-sm text-gray-800">{{
|
<label for="username" class="block text-sm text-gray-800">{{
|
||||||
context.realm.loginWithEmailAllowed ? "Email or Username" : "Username"
|
context.realm.loginWithEmailAllowed ? $t("login.usernameOrEmail") : $t("login.username")
|
||||||
}}</label>
|
}}</label>
|
||||||
<input
|
<input
|
||||||
name="username"
|
name="username"
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="password" class="block text-sm text-gray-800"
|
<label for="password" class="block text-sm text-gray-800"
|
||||||
>Password</label
|
>{{ $t("login.password") }}</label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
v-if="context.realm.resetPasswordAllowed"
|
v-if="context.realm.resetPasswordAllowed"
|
||||||
:href="context.url.loginResetCredentialsUrl"
|
:href="context.url.loginResetCredentialsUrl"
|
||||||
class="text-xs text-gray-600 hover:underline"
|
class="text-xs text-gray-600 hover:underline"
|
||||||
>Forgot Password?</a
|
>{{ $t("login.forgotPassword") }}</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
name="rememberMe"
|
name="rememberMe"
|
||||||
class="w-4 h-4 text-blue-500 bg-gray-100 border-gray-400 rounded dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600" />
|
class="w-4 h-4 text-blue-500 bg-gray-100 border-gray-400 rounded dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600" />
|
||||||
<label for="rememberMe" class="block ml-3 text-sm text-gray-800"
|
<label for="rememberMe" class="block ml-3 text-sm text-gray-800"
|
||||||
>Remember me</label
|
>{{ $t("login.rememberMe") }}</label
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
" />
|
" />
|
||||||
<button
|
<button
|
||||||
class="w-full mt-6 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
class="w-full mt-6 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
||||||
Login
|
{{ $t("login.login") }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import index from "./index.vue";
|
import index from "./index.vue";
|
||||||
|
import { setupI18n } from "~/functions/i18n";
|
||||||
|
|
||||||
if ((window as any).kcContext) {
|
if ((window as any).kcContext) {
|
||||||
const app = createApp(index);
|
const app = createApp(index);
|
||||||
|
app.use(setupI18n())
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
stroke-dasharray="164 56" />
|
stroke-dasharray="164 56" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="mt-4 text-xl text-center text-gray-700">Redirecting...</h1>
|
<h1 class="mt-4 text-xl text-center text-gray-700">{{ $t("redirect.message") }}</h1>
|
||||||
</layout>
|
</layout>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import index from "./index.vue";
|
import index from "./index.vue";
|
||||||
|
import { setupI18n } from "~/functions/i18n";
|
||||||
|
|
||||||
if ((window as any).kcContext) {
|
if ((window as any).kcContext) {
|
||||||
const app = createApp(index);
|
const app = createApp(index);
|
||||||
|
app.use(setupI18n())
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<layout>
|
<layout>
|
||||||
<h1
|
<h1 class="text-3xl font-semibold text-center text-gray-700">
|
||||||
v-if="context.auth.attemptedUsername && context.auth.showUsername"
|
{{ $t("login.welcome") }}
|
||||||
class="text-3xl font-semibold text-center text-gray-700">
|
|
||||||
Welcome {{ context.auth?.attemptedUsername }}!
|
|
||||||
</h1>
|
|
||||||
<h1 v-else class="text-3xl font-semibold text-center text-gray-700">
|
|
||||||
Welcome!
|
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-center mt-3">
|
<p class="text-center mt-3">
|
||||||
Please select a second factor you would like to use:
|
{{ $t("2fa.selectFactor") }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col items-center mt-10 text-gray-700">
|
<div class="flex flex-col items-center mt-10 text-gray-700">
|
||||||
<div
|
<div
|
||||||
|
@ -25,8 +20,8 @@
|
||||||
d="M6 .5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 .5.5v4H6v-4ZM7 1v1h1V1H7Zm2 0v1h1V1H9ZM5.5 5a.5.5 0 0 0-.5.5V15a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1V5.5a.5.5 0 0 0-.5-.5h-6Z" />
|
d="M6 .5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 .5.5v4H6v-4ZM7 1v1h1V1H7Zm2 0v1h1V1H9ZM5.5 5a.5.5 0 0 0-.5.5V15a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1V5.5a.5.5 0 0 0-.5-.5h-6Z" />
|
||||||
</svg>
|
</svg>
|
||||||
<div class="flex flex-col ml-5 leading-none">
|
<div class="flex flex-col ml-5 leading-none">
|
||||||
<h2 class="mb-1.5 font-semibold">Hardware Security Key</h2>
|
<h2 class="mb-1.5 font-semibold">{{ $t("2fa.hwSecKey") }}</h2>
|
||||||
<p class="text-sm">Authenticate using a WebAuthn capable device</p>
|
<p class="text-sm">{{ $t("2fa.hwSecKeyDesc") }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -46,9 +41,9 @@
|
||||||
d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5z" />
|
d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5z" />
|
||||||
</svg>
|
</svg>
|
||||||
<div class="flex flex-col ml-5 leading-none">
|
<div class="flex flex-col ml-5 leading-none">
|
||||||
<h2 class="mb-1.5 font-semibold">Authenticator App</h2>
|
<h2 class="mb-1.5 font-semibold">{{ $t("2fa.otp") }}</h2>
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
Authenticate using a one time code from your authenticator app
|
{{ $t("2fa.otpDesc") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,8 +63,8 @@
|
||||||
d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
||||||
</svg>
|
</svg>
|
||||||
<div class="flex flex-col ml-5 leading-none">
|
<div class="flex flex-col ml-5 leading-none">
|
||||||
<h2 class="mb-1.5 font-semibold">Recovery Code</h2>
|
<h2 class="mb-1.5 font-semibold">{{ $t("2fa.recoveryCode") }}</h2>
|
||||||
<p class="text-sm">Authenticate using one of your recovery codes</p>
|
<p class="text-sm">{{ $t("2fa.recoveryCodeDesc") }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import index from "./index.vue";
|
import index from "./index.vue";
|
||||||
|
import { setupI18n } from "~/functions/i18n";
|
||||||
|
|
||||||
if ((window as any).kcContext) {
|
if ((window as any).kcContext) {
|
||||||
const app = createApp(index);
|
const app = createApp(index);
|
||||||
|
app.use(setupI18n());
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<layout>
|
<layout>
|
||||||
<h1
|
<h1 class="text-3xl font-semibold text-center text-gray-700">
|
||||||
v-if="context.isUserIdentified"
|
{{ $t("login.welcome") }}
|
||||||
class="text-3xl font-semibold text-center text-gray-700">
|
|
||||||
Welcome {{ context.auth?.attemptedUsername }}!
|
|
||||||
</h1>
|
|
||||||
<h1 v-else class="text-3xl font-semibold text-center text-gray-700">
|
|
||||||
Welcome!
|
|
||||||
</h1>
|
</h1>
|
||||||
<div v-if="webauthnSupported && !error">
|
<div v-if="webauthnSupported && !error">
|
||||||
<p class="mt-5 mb-5 text-center">
|
<p class="mt-5 mb-5 text-center">
|
||||||
Please use one of your registered devices to continue:
|
{{ $t("webauthn.title") }}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
v-for="authenticator in context.authenticators.authenticators"
|
v-for="authenticator in context.authenticators.authenticators"
|
||||||
|
@ -29,51 +24,48 @@
|
||||||
<div class="w-full ml-5 flex flex-col">
|
<div class="w-full ml-5 flex flex-col">
|
||||||
<p>{{ authenticator.label }}</p>
|
<p>{{ authenticator.label }}</p>
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
Registered: {{ new Date(authenticator.createdAt).toLocaleString() }}
|
{{ $t("webauthn.registered", {date: new Date(authenticator.createdAt).toLocaleString()}) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
:onclick="prepareAuthenticate"
|
:onclick="prepareAuthenticate"
|
||||||
class="w-full px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
class="w-full px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
||||||
Authenticate
|
{{ $t("webauthn.authenticate") }}
|
||||||
</button>
|
</button>
|
||||||
<p
|
<p
|
||||||
class="mt-5 text-sm text-center"
|
class="mt-5 text-sm text-center"
|
||||||
v-if="context.auth?.showTryAnotherWayLink">
|
v-if="context.auth?.showTryAnotherWayLink">
|
||||||
Don't have your device at hand?
|
{{ $t("webauthn.noDevice") }}
|
||||||
<a
|
<a
|
||||||
:onclick="tryAnotherWay"
|
:onclick="tryAnotherWay"
|
||||||
class="text-blue-500 hover:underline cursor-pointer"
|
class="text-blue-500 hover:underline cursor-pointer"
|
||||||
>Try another way.</a
|
>{{ $t("2fa.tryAnotherWay") }}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!webauthnSupported">
|
<div v-if="!webauthnSupported">
|
||||||
<p class="mt-5 text-center">
|
<p v-if="!context.auth?.showTryAnotherWayLink" class="mt-5 text-center">
|
||||||
It seems that your browser doesn't support WebAuthn. Please try logging
|
{{ $t("webauthn.noSupportOtherMethod") }}
|
||||||
in with a different browser<span
|
</p>
|
||||||
v-if="!context.auth?.showTryAnotherWayLink"
|
<p v-else class="mt-5 text-center">
|
||||||
>.</span
|
{{ $t("webauthn.noSupport") }}
|
||||||
><span v-else
|
|
||||||
>, or try a different login method using the button below.</span
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
v-if="context.auth?.showTryAnotherWayLink"
|
v-if="context.auth?.showTryAnotherWayLink"
|
||||||
:onclick="tryAnotherWay"
|
:onclick="tryAnotherWay"
|
||||||
class="w-full mt-5 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
class="w-full mt-5 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
||||||
Try another method
|
{{ $t("2fa.tryAnotherWay") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="error" class="mt-5 flex flex-col">
|
<div v-if="error" class="mt-5 flex flex-col">
|
||||||
<ErrorBox
|
<ErrorBox
|
||||||
>Something went wrong during authentication using your device.</ErrorBox
|
>{{ $t("webauthn.error") }}</ErrorBox
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
:onclick="retryAuth"
|
:onclick="retryAuth"
|
||||||
class="w-full mt-5 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
class="w-full mt-5 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400">
|
||||||
Retry
|
{{ $t("login.retry") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
55
yarn.lock
55
yarn.lock
|
@ -987,6 +987,44 @@
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||||
|
|
||||||
|
"@intlify/core-base@9.2.2":
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.2.tgz#5353369b05cc9fe35cab95fe20afeb8a4481f939"
|
||||||
|
integrity sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==
|
||||||
|
dependencies:
|
||||||
|
"@intlify/devtools-if" "9.2.2"
|
||||||
|
"@intlify/message-compiler" "9.2.2"
|
||||||
|
"@intlify/shared" "9.2.2"
|
||||||
|
"@intlify/vue-devtools" "9.2.2"
|
||||||
|
|
||||||
|
"@intlify/devtools-if@9.2.2":
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz#b13d9ac4b4e2fe6d2e7daa556517a8061fe8bd39"
|
||||||
|
integrity sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==
|
||||||
|
dependencies:
|
||||||
|
"@intlify/shared" "9.2.2"
|
||||||
|
|
||||||
|
"@intlify/message-compiler@9.2.2":
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz#e42ab6939b8ae5b3d21faf6a44045667a18bba1c"
|
||||||
|
integrity sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==
|
||||||
|
dependencies:
|
||||||
|
"@intlify/shared" "9.2.2"
|
||||||
|
source-map "0.6.1"
|
||||||
|
|
||||||
|
"@intlify/shared@9.2.2":
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.2.tgz#5011be9ca2b4ab86f8660739286e2707f9abb4a5"
|
||||||
|
integrity sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==
|
||||||
|
|
||||||
|
"@intlify/vue-devtools@9.2.2":
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz#b95701556daf7ebb3a2d45aa3ae9e6415aed8317"
|
||||||
|
integrity sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==
|
||||||
|
dependencies:
|
||||||
|
"@intlify/core-base" "9.2.2"
|
||||||
|
"@intlify/shared" "9.2.2"
|
||||||
|
|
||||||
"@jridgewell/gen-mapping@^0.1.0":
|
"@jridgewell/gen-mapping@^0.1.0":
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
|
||||||
|
@ -1251,6 +1289,11 @@
|
||||||
"@vue/compiler-dom" "3.2.47"
|
"@vue/compiler-dom" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
|
"@vue/devtools-api@^6.2.1":
|
||||||
|
version "6.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
|
||||||
|
integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==
|
||||||
|
|
||||||
"@vue/reactivity-transform@3.2.47":
|
"@vue/reactivity-transform@3.2.47":
|
||||||
version "3.2.47"
|
version "3.2.47"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz#e45df4d06370f8abf29081a16afd25cffba6d84e"
|
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz#e45df4d06370f8abf29081a16afd25cffba6d84e"
|
||||||
|
@ -3924,7 +3967,7 @@ source-map-support@~0.5.20:
|
||||||
buffer-from "^1.0.0"
|
buffer-from "^1.0.0"
|
||||||
source-map "^0.6.0"
|
source-map "^0.6.0"
|
||||||
|
|
||||||
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0:
|
source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
@ -4226,6 +4269,16 @@ vue-eslint-parser@^8.0.1:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
semver "^7.3.5"
|
semver "^7.3.5"
|
||||||
|
|
||||||
|
vue-i18n@9:
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.2.2.tgz#aeb49d9424923c77e0d6441e3f21dafcecd0e666"
|
||||||
|
integrity sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==
|
||||||
|
dependencies:
|
||||||
|
"@intlify/core-base" "9.2.2"
|
||||||
|
"@intlify/shared" "9.2.2"
|
||||||
|
"@intlify/vue-devtools" "9.2.2"
|
||||||
|
"@vue/devtools-api" "^6.2.1"
|
||||||
|
|
||||||
vue-loader@^17.0.0:
|
vue-loader@^17.0.0:
|
||||||
version "17.0.1"
|
version "17.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.1.tgz#c0ee8875e0610a0c2d13ba9b4d50a9c8442e7a3a"
|
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.1.tgz#c0ee8875e0610a0c2d13ba9b4d50a9c8442e7a3a"
|
||||||
|
|
Loading…
Reference in a new issue