diff --git a/README.md b/README.md
index 72bfc6c..274c9eb 100644
--- a/README.md
+++ b/README.md
@@ -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).
- 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`.
+
+### 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`.
diff --git a/package.json b/package.json
index 490b5de..1167101 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
},
"dependencies": {
"rfc4648": "^1.5.2",
- "vue": "^3.2.26"
+ "vue": "^3.2.26",
+ "vue-i18n": "9"
}
}
diff --git a/src/functions/i18n.ts b/src/functions/i18n.ts
new file mode 100644
index 0000000..3c3de68
--- /dev/null
+++ b/src/functions/i18n.ts
@@ -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()
+ }
\ No newline at end of file
diff --git a/src/static/login/resources/locales/de.js b/src/static/login/resources/locales/de.js
new file mode 100644
index 0000000..603a57b
--- /dev/null
+++ b/src/static/login/resources/locales/de.js
@@ -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..."
+ }
+}
\ No newline at end of file
diff --git a/src/static/login/resources/locales/en.js b/src/static/login/resources/locales/en.js
new file mode 100644
index 0000000..b9fb604
--- /dev/null
+++ b/src/static/login/resources/locales/en.js
@@ -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..."
+ }
+}
\ No newline at end of file
diff --git a/src/static/login/theme.properties b/src/static/login/theme.properties
index 98fdc52..a929313 100644
--- a/src/static/login/theme.properties
+++ b/src/static/login/theme.properties
@@ -1,2 +1,2 @@
-locales=en,de
+locales=en
parent=keycloak
\ No newline at end of file
diff --git a/src/views/login/index.ts b/src/views/login/index.ts
index 9344ee4..f75dca7 100644
--- a/src/views/login/index.ts
+++ b/src/views/login/index.ts
@@ -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");
}
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
index a81fe91..e6dc5e3 100644
--- a/src/views/login/index.vue
+++ b/src/views/login/index.vue
@@ -1,16 +1,16 @@
- Login to your account
+ {{ $t("login.title") }}
- Don't have an account yet?
+ {{ $t("login.noAccount") }}
Sign up{{ $t("login.signupLink") }}