Add localization support

This commit is contained in:
Oliver Traber 2023-04-16 21:33:06 +02:00
parent 47b0436b14
commit 91f9b8c4a7
Signed by: Bluemedia
GPG key ID: C0674B105057136C
15 changed files with 217 additions and 49 deletions

39
src/functions/i18n.ts Normal file
View 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()
}

View 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..."
}
}

View 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..."
}
}

View file

@ -1,2 +1,2 @@
locales=en,de
locales=en
parent=keycloak

View file

@ -1,7 +1,9 @@
import { createApp } from "vue";
import index from "./index.vue";
import { setupI18n } from "~/functions/i18n";
if ((window as any).kcContext) {
const app = createApp(index);
app.use(setupI18n())
app.mount("#app");
}

View file

@ -1,16 +1,16 @@
<template>
<layout>
<h1 class="mb-8 text-3xl font-semibold text-center text-gray-700">
Login to your account
{{ $t("login.title") }}
</h1>
<p
v-if="context.realm.registrationAllowed"
class="mt-4 text-sm font-light text-center text-gray-700">
Don't have an account yet?
{{ $t("login.noAccount") }}
<a
:href="context.url.registrationUrl"
class="font-medium text-blue-500 hover:underline"
>Sign up</a
>{{ $t("login.signupLink") }}</a
>
</p>
<div v-if="context.message?.type == 'error'">
@ -22,7 +22,7 @@
method="post">
<div>
<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>
<input
name="username"
@ -38,7 +38,7 @@
<div class="mt-4">
<div>
<label for="password" class="block text-sm text-gray-800"
>Password</label
>{{ $t("login.password") }}</label
>
<input
type="password"
@ -50,7 +50,7 @@
v-if="context.realm.resetPasswordAllowed"
:href="context.url.loginResetCredentialsUrl"
class="text-xs text-gray-600 hover:underline"
>Forgot Password?</a
>{{ $t("login.forgotPassword") }}</a
>
</div>
<div
@ -61,7 +61,7 @@
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" />
<label for="rememberMe" class="block ml-3 text-sm text-gray-800"
>Remember me</label
>{{ $t("login.rememberMe") }}</label
>
</div>
<input
@ -72,7 +72,7 @@
" />
<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">
Login
{{ $t("login.login") }}
</button>
</form>
</layout>

View file

@ -1,7 +1,9 @@
import { createApp } from "vue";
import index from "./index.vue";
import { setupI18n } from "~/functions/i18n";
if ((window as any).kcContext) {
const app = createApp(index);
app.use(setupI18n())
app.mount("#app");
}

View file

@ -12,7 +12,7 @@
stroke-dasharray="164 56" />
</svg>
</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>
</template>
<script lang="ts">

View file

@ -1,7 +1,9 @@
import { createApp } from "vue";
import index from "./index.vue";
import { setupI18n } from "~/functions/i18n";
if ((window as any).kcContext) {
const app = createApp(index);
app.use(setupI18n())
app.mount("#app");
}

View file

@ -1,15 +1,10 @@
<template>
<layout>
<h1
v-if="context.auth.attemptedUsername && context.auth.showUsername"
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 class="text-3xl font-semibold text-center text-gray-700">
{{ $t("login.welcome") }}
</h1>
<p class="text-center mt-3">
Please select a second factor you would like to use:
{{ $t("2fa.selectFactor") }}
</p>
<div class="flex flex-col items-center mt-10 text-gray-700">
<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" />
</svg>
<div class="flex flex-col ml-5 leading-none">
<h2 class="mb-1.5 font-semibold">Hardware Security Key</h2>
<p class="text-sm">Authenticate using a WebAuthn capable device</p>
<h2 class="mb-1.5 font-semibold">{{ $t("2fa.hwSecKey") }}</h2>
<p class="text-sm">{{ $t("2fa.hwSecKeyDesc") }}</p>
</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" />
</svg>
<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">
Authenticate using a one time code from your authenticator app
{{ $t("2fa.otpDesc") }}
</p>
</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" />
</svg>
<div class="flex flex-col ml-5 leading-none">
<h2 class="mb-1.5 font-semibold">Recovery Code</h2>
<p class="text-sm">Authenticate using one of your recovery codes</p>
<h2 class="mb-1.5 font-semibold">{{ $t("2fa.recoveryCode") }}</h2>
<p class="text-sm">{{ $t("2fa.recoveryCodeDesc") }}</p>
</div>
</div>
</div>

View file

@ -1,7 +1,9 @@
import { createApp } from "vue";
import index from "./index.vue";
import { setupI18n } from "~/functions/i18n";
if ((window as any).kcContext) {
const app = createApp(index);
app.use(setupI18n());
app.mount("#app");
}

View file

@ -1,16 +1,11 @@
<template>
<layout>
<h1
v-if="context.isUserIdentified"
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 class="text-3xl font-semibold text-center text-gray-700">
{{ $t("login.welcome") }}
</h1>
<div v-if="webauthnSupported && !error">
<p class="mt-5 mb-5 text-center">
Please use one of your registered devices to continue:
{{ $t("webauthn.title") }}
</p>
<div
v-for="authenticator in context.authenticators.authenticators"
@ -29,51 +24,48 @@
<div class="w-full ml-5 flex flex-col">
<p>{{ authenticator.label }}</p>
<p class="text-sm">
Registered: {{ new Date(authenticator.createdAt).toLocaleString() }}
{{ $t("webauthn.registered", {date: new Date(authenticator.createdAt).toLocaleString()}) }}
</p>
</div>
</div>
<button
: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">
Authenticate
{{ $t("webauthn.authenticate") }}
</button>
<p
class="mt-5 text-sm text-center"
v-if="context.auth?.showTryAnotherWayLink">
Don't have your device at hand?
{{ $t("webauthn.noDevice") }}
<a
:onclick="tryAnotherWay"
class="text-blue-500 hover:underline cursor-pointer"
>Try another way.</a
>{{ $t("2fa.tryAnotherWay") }}</a
>
</p>
</div>
<div v-if="!webauthnSupported">
<p class="mt-5 text-center">
It seems that your browser doesn't support WebAuthn. Please try logging
in with a different browser<span
v-if="!context.auth?.showTryAnotherWayLink"
>.</span
><span v-else
>, or try a different login method using the button below.</span
>
<p v-if="!context.auth?.showTryAnotherWayLink" class="mt-5 text-center">
{{ $t("webauthn.noSupportOtherMethod") }}
</p>
<p v-else class="mt-5 text-center">
{{ $t("webauthn.noSupport") }}
</p>
<button
v-if="context.auth?.showTryAnotherWayLink"
: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">
Try another method
{{ $t("2fa.tryAnotherWay") }}
</button>
</div>
<div v-if="error" class="mt-5 flex flex-col">
<ErrorBox
>Something went wrong during authentication using your device.</ErrorBox
>{{ $t("webauthn.error") }}</ErrorBox
>
<button
: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">
Retry
{{ $t("login.retry") }}
</button>
</div>
</layout>