elearning/Frontend-Learner/components/common/LanguageSwitcher.vue

140 lines
3.5 KiB
Vue

<script setup lang="ts">
/**
* @file LanguageSwitcher.vue
* @description Language switcher component using Quasar dropdown.
* Allows switching between Thai (th) and English (en) locales.
*/
const { locale, setLocale, locales } = useI18n()
// Get available locales with their names
const availableLocales = computed(() => {
return (locales.value as Array<{ code: string; name: string }>).map((loc) => ({
code: loc.code,
name: loc.name
}))
})
// Get current locale display (TH or EN)
const currentLocaleDisplay = computed(() => {
return locale.value.toUpperCase()
})
// Handle locale change
const changeLocale = async (code: string) => {
await setLocale(code as 'th' | 'en')
// Cookie is automatically handled by @nuxtjs/i18n with detectBrowserLanguage.useCookie
}
</script>
<template>
<q-btn
round
flat
class="language-btn"
:aria-label="$t('language.label')"
>
<span class="language-text">{{ currentLocaleDisplay }}</span>
<q-menu
anchor="bottom right"
self="top right"
class="language-menu"
>
<q-list style="min-width: 150px">
<q-item-label header class="text-xs font-bold uppercase tracking-wider" style="color: var(--text-secondary);">
{{ $t('language.label') }}
</q-item-label>
<q-item
v-for="loc in availableLocales"
:key="loc.code"
clickable
v-close-popup
@click="changeLocale(loc.code)"
:class="{ 'active-locale': locale === loc.code }"
class="language-item"
>
<q-item-section avatar>
<span class="text-lg">{{ loc.code === 'th' ? '🇹🇭' : '🇺🇸' }}</span>
</q-item-section>
<q-item-section>
<q-item-label class="font-semibold" style="color: var(--text-primary);">
{{ loc.name }}
</q-item-label>
<q-item-label caption style="color: var(--text-secondary);">
{{ loc.code.toUpperCase() }}
</q-item-label>
</q-item-section>
<q-item-section side v-if="locale === loc.code">
<q-icon name="check" color="primary" />
</q-item-section>
</q-item>
</q-list>
</q-menu>
<q-tooltip>{{ $t('language.label') }}</q-tooltip>
</q-btn>
</template>
<style scoped>
.language-btn {
width: 40px;
height: 40px;
background: linear-gradient(135deg, var(--primary) 0%, #2f5ed7 100%);
color: white;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(75, 130, 247, 0.3);
}
.language-btn:hover {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(75, 130, 247, 0.4);
}
.language-text {
font-size: 12px;
font-weight: 800;
letter-spacing: 0.5px;
}
.language-item {
border-radius: 8px;
margin: 4px 8px;
transition: all 0.2s ease;
}
.language-item:hover {
background: var(--bg-secondary);
}
.active-locale {
background: rgba(75, 130, 247, 0.1);
}
:deep(.language-menu) {
border-radius: 16px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
background: var(--bg-surface);
border: 1px solid var(--border-color);
}
:global(.dark) .language-btn {
background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);
}
:global(.dark) .language-btn:hover {
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.5);
}
:global(.dark) :deep(.language-menu) {
background: #1e293b;
border-color: rgba(255, 255, 255, 0.1);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
}
:global(.dark) .active-locale {
background: rgba(59, 130, 246, 0.2);
}
</style>