diff --git a/nuxt.config.ts b/nuxt.config.ts index d4eb837..5b7cb0b 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -9,6 +9,7 @@ export default defineNuxtConfig({ vapidPrivateKeyFile: "", public: { defaultTimezone: "Europe/Oslo", + defaultLocale: "en-GB", vapidPublicKey: "", } }, diff --git a/pages/account/settings.vue b/pages/account/settings.vue index 04691d5..5b4ac8c 100644 --- a/pages/account/settings.vue +++ b/pages/account/settings.vue @@ -10,6 +10,10 @@ Timezone + @@ -38,6 +42,7 @@ const sessionStore = useSessionStore(); const accountStore = useAccountStore(); const timezone = ref(accountStore.timezone ?? ""); +const locale = ref(accountStore.locale ?? ""); async function changeSettings() { try { @@ -45,6 +50,7 @@ async function changeSettings() { method: "patch", body: { timezone: timezone.value, + locale: locale.value, } }); await sessionStore.fetch(); diff --git a/server/api/auth/account.patch.ts b/server/api/auth/account.patch.ts index 948392c..d148e67 100644 --- a/server/api/auth/account.patch.ts +++ b/server/api/auth/account.patch.ts @@ -24,6 +24,15 @@ export default defineEventHandler(async (event) => { }); } } + if (patch.locale?.length) { + const locale = DateTime.local({ locale: patch.locale }).resolvedLocaleOptions().locale; + if (locale !== patch.locale) { + throw createError({ + status: 400, + message: `Invalid locale: the locale "${patch.locale}" is not supported (was treated as "${locale}")` + }); + } + } const accounts = await readAccounts(); const sessionAccount = accounts.find(account => account.id === session.accountId); @@ -51,6 +60,12 @@ export default defineEventHandler(async (event) => { else delete sessionAccount.timezone; } + if (patch.locale !== undefined) { + if (patch.locale) + sessionAccount.locale = patch.locale; + else + delete sessionAccount.locale; + } await writeAccounts(accounts); // Update Schedule counts. diff --git a/shared/types/api.ts b/shared/types/api.ts index 9ff31bc..ce51f71 100644 --- a/shared/types/api.ts +++ b/shared/types/api.ts @@ -9,6 +9,7 @@ export interface ApiAccount { interestedEventIds?: number[], interestedEventSlotIds?: number[], timezone?: string, + locale?: string, } export const apiAccountPatchSchema = z.object({ @@ -16,6 +17,7 @@ export const apiAccountPatchSchema = z.object({ interestedEventIds: z.optional(z.array(z.number())), interestedEventSlotIds: z.optional(z.array(z.number())), timezone: z.optional(z.string()), + locale: z.optional(z.string()), }); export type ApiAccountPatch = z.infer; diff --git a/stores/account.ts b/stores/account.ts index e1f4c03..bf92f77 100644 --- a/stores/account.ts +++ b/stores/account.ts @@ -8,6 +8,7 @@ export const useAccountStore = defineStore("account", () => { id: ref(), name: ref(), timezone: ref(), + locale: ref(), type: ref(), interestedEventIds: ref>(new Set()), interestedEventSlotIds: ref>(new Set()), @@ -19,6 +20,7 @@ export const useAccountStore = defineStore("account", () => { state.id.value = account?.id; state.name.value = account?.name; state.timezone.value = account?.timezone; + state.locale.value = account?.locale; state.type.value = account?.type; state.interestedEventIds.value = new Set(account?.interestedEventIds ?? []); state.interestedEventSlotIds.value = new Set(account?.interestedEventSlotIds ?? []); @@ -30,6 +32,8 @@ export const useAccountStore = defineStore("account", () => { canEditPublic: computed(() => state.type.value === "admin"), activeTimezone: computed(() => state.timezone.value || runtimeConfig.public.defaultTimezone), defaultTimezone: computed(() => runtimeConfig.public.defaultTimezone), + activeLocale: computed(() => state.locale.value || runtimeConfig.public.defaultLocale), + defaultLocale: computed(() => runtimeConfig.public.defaultLocale), }; const actions = {