diff --git a/client/src/components/AppFooter.vue b/client/src/components/AppFooter.vue
index 96c68c37..19f67a71 100644
--- a/client/src/components/AppFooter.vue
+++ b/client/src/components/AppFooter.vue
@@ -1,13 +1,15 @@
@@ -22,9 +24,16 @@ async function changeLocale(event: Event) {
Deutsch
+
{{ $t("footer.contact") }}
diff --git a/client/src/i18n.ts b/client/src/i18n.ts
index 8a012703..7a222ab9 100644
--- a/client/src/i18n.ts
+++ b/client/src/i18n.ts
@@ -6,7 +6,9 @@ import { createI18n } from "vue-i18n";
export const SUPPORT_LOCALES = ["de", "fr", "it"];
let i18n: any = null;
-export function setupI18n(options = { locale: "de", legacy: false }) {
+export function setupI18n(
+ options = { locale: "de", legacy: false, fallbackLocale: "de" }
+) {
i18n = createI18n(options);
setI18nLanguage(options.locale);
dayjs.locale(options.locale);
diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts
index 91514ca2..3c06473a 100644
--- a/client/src/stores/user.ts
+++ b/client/src/stores/user.ts
@@ -1,12 +1,15 @@
import log from "loglevel";
import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers";
+import { loadLocaleMessages, setI18nLanguage } from "@/i18n";
import { useAppStore } from "@/stores/app";
import { defineStore } from "pinia";
const logoutRedirectUrl = import.meta.env.VITE_LOGOUT_REDIRECT;
// typed state https://stackoverflow.com/questions/71012513/when-using-pinia-and-typescript-how-do-you-use-an-action-to-set-the-state
+export type availableLanguages = "de" | "fr" | "it";
+
export type UserState = {
id: number;
first_name: string;
@@ -17,6 +20,7 @@ export type UserState = {
is_superuser: boolean;
course_session_experts: number[];
loggedIn: boolean;
+ language: availableLanguages;
};
const initialUserState: UserState = {
@@ -29,8 +33,14 @@ const initialUserState: UserState = {
is_superuser: false,
course_session_experts: [],
loggedIn: false,
+ language: "de",
};
+async function setLocale(language: availableLanguages) {
+ await loadLocaleMessages(language);
+ setI18nLanguage(language);
+}
+
export const useUserStore = defineStore({
id: "user",
state: () => initialUserState as UserState,
@@ -80,6 +90,12 @@ export const useUserStore = defineStore({
this.$state = data;
this.loggedIn = true;
appStore.userLoaded = true;
+ await setLocale(data.language);
+ },
+ async setUserLanguages(language: availableLanguages) {
+ await setLocale(language);
+ this.$state.language = language;
+ await itPost("/api/core/me/", { language }, { method: "PUT" });
},
},
});
diff --git a/server/vbv_lernwelt/core/migrations/0002_user_model.py b/server/vbv_lernwelt/core/migrations/0002_user_model.py
index 837d3c0a..00a46e80 100644
--- a/server/vbv_lernwelt/core/migrations/0002_user_model.py
+++ b/server/vbv_lernwelt/core/migrations/0002_user_model.py
@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
-
dependencies = [
("core", "0001_initial"),
]
@@ -22,4 +21,13 @@ class Migration(migrations.Migration):
max_length=254, unique=True, verbose_name="email address"
),
),
+ migrations.AddField(
+ model_name="user",
+ name="language",
+ field=models.CharField(
+ choices=[("de", "Deutsch"), ("fr", "Français"), ("it", "Italiano")],
+ default="de",
+ max_length=2,
+ ),
+ ),
]
diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py
index 9940cd78..73e498f1 100644
--- a/server/vbv_lernwelt/core/models.py
+++ b/server/vbv_lernwelt/core/models.py
@@ -11,6 +11,12 @@ class User(AbstractUser):
If adding fields that need to be filled at user signup,
"""
+ LANGUAGE_CHOICES = (
+ ("de", "Deutsch"),
+ ("fr", "Français"),
+ ("it", "Italiano"),
+ )
+
# FIXME: look into it...
# objects = UserManager()
avatar_url = models.CharField(max_length=254, blank=True, default="")
@@ -19,6 +25,7 @@ class User(AbstractUser):
"SSO subscriber ID", unique=True, null=True, blank=True, default=None
)
additional_json_data = JSONField(default=dict, blank=True)
+ language = models.CharField(max_length=2, choices=LANGUAGE_CHOICES, default="de")
objects = UserManager()
diff --git a/server/vbv_lernwelt/core/serializers.py b/server/vbv_lernwelt/core/serializers.py
index 0d6ef107..a927d748 100644
--- a/server/vbv_lernwelt/core/serializers.py
+++ b/server/vbv_lernwelt/core/serializers.py
@@ -18,6 +18,15 @@ class UserSerializer(serializers.ModelSerializer):
"avatar_url",
"is_superuser",
"course_session_experts",
+ "language",
+ ]
+ read_only_fields = [
+ "id",
+ "is_superuser",
+ "first_name",
+ "last_name",
+ "email",
+ "username",
]
def get_course_session_experts(self, obj):
diff --git a/server/vbv_lernwelt/core/views.py b/server/vbv_lernwelt/core/views.py
index ad1f2ec9..07a25aad 100644
--- a/server/vbv_lernwelt/core/views.py
+++ b/server/vbv_lernwelt/core/views.py
@@ -82,11 +82,25 @@ def vue_login(request):
)
-@api_view(["GET"])
+@api_view(["GET", "PUT"])
def me_user_view(request):
- if request.user.is_authenticated:
+ if not request.user.is_authenticated:
+ return Response(status=403)
+
+ if request.method == "GET":
return Response(UserSerializer(request.user).data)
- return Response(status=403)
+
+ if request.method == "PUT":
+ serializer = UserSerializer(
+ request.user,
+ data={"language": request.data.get("language", "de")},
+ partial=True,
+ )
+ if serializer.is_valid():
+ serializer.save()
+ return Response(UserSerializer(request.user).data)
+
+ return Response(status=400)
@api_view(["POST"])