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"])