<template>
<div class="account-panel">
<!-- First block of the Account tab: the language of the interface itself. -->
<div class="account-language">
<div class="account-language-header">
<h2 class="account-language-title">{{ t('settings.languageToolbar') }}</h2>
</div>
<p class="account-language-description">{{ languageDescription }}</p>
<GnSelect
class="language-select"
:aria-label="t('settings.languageToolbar')"
:model-value="currentLocale"
:options="localeOptions"
:disabled="saving"
@update:model-value="changeLanguage"
/>
</div>
<ApiKeysPanel />
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { GnSelect, useToast } from 'gnexus-ui-kit/vue'
import { updateMe } from '@/api'
import { useAuthStore } from '@/stores/auth.js'
import { SUPPORTED_LOCALES, locale, rememberLocale, setLocale, t } from '@/i18n/index.js'
import ApiKeysPanel from './ApiKeysPanel.vue'
const authStore = useAuthStore()
const toast = useToast()
const saving = ref(false)
// Signed in, the language is a property of the account and follows it to other
// devices. Without an account there is nothing to follow: sign-in may be off
// altogether, and a visitor who is not signed in has no account either.
const signedIn = computed(() => !authStore.isNoAuthMode && authStore.user !== null)
const LANG_SLUGS = { en: 'profile.langEn', uk: 'profile.langUk', ru: 'profile.langRu' }
// Computed, not a module-level constant: the labels go through t() and have to
// be rebuilt whenever the language changes.
const localeOptions = computed(() => {
const languages = SUPPORTED_LOCALES.map(code => ({ value: code, label: t(LANG_SLUGS[code]) }))
return signedIn.value
? [{ value: '', label: t('profile.languageAuto') }, ...languages]
: languages
})
// '' is the Auto option — a value GnSelect can hold, unlike null.
const currentLocale = computed(() => (signedIn.value ? authStore.user.locale ?? '' : locale.value))
const languageDescription = computed(() =>
t(signedIn.value ? 'settings.languageDescription' : 'settings.languageDescriptionOpen')
)
async function changeLanguage(value) {
if (!signedIn.value) {
// No account to write to: the choice stays in this browser.
rememberLocale(value)
setLocale(value)
toast.success({ title: t('toast.languageSaved') })
return
}
saving.value = true
try {
// '' is Auto; the backend reads both it and 'auto' as "follow the account".
const updated = await updateMe({ locale: value || 'auto' })
authStore.user = updated
setLocale(updated.locale_effective)
toast.success({ title: t('toast.languageSaved') })
} catch (err) {
// The stored language is unchanged, so the picker still shows the truth.
toast.error({ title: t('common.error'), text: err.message })
} finally {
saving.value = false
}
}
</script>
<style scoped lang="scss">
@use 'kit-deps' as *;
.account-language {
margin-bottom: 24px;
}
.account-language-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.account-language-title {
font-size: 1.1rem;
font-weight: 600;
margin: 0;
color: var(--gn-color-text-medium);
}
.account-language-description {
margin: 0 0 16px;
font-size: 0.875rem;
color: var(--gn-color-text-medium);
}
</style>