mirror of
https://github.com/BeamMP/BeamMP-Website.git
synced 2026-04-05 23:36:10 +00:00
Add initial templates for additional languages
Co-Authored-By: Tixx <83774803+WiserTixx@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from '@/components/ui/navigation-menu'
|
||||
import { cn } from '@/lib/utils'
|
||||
import ThemeToggle from '@/components/ThemeToggle.vue'
|
||||
import LanguageSelector from '@/components/LanguageSelector.vue'
|
||||
import { Menu, X } from 'lucide-vue-next'
|
||||
|
||||
const mobileMenuOpen = ref(false)
|
||||
@@ -126,13 +127,17 @@ function closeMobileMenu() {
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
<ThemeToggle />
|
||||
<div class="flex items-center gap-2">
|
||||
<LanguageSelector />
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button and Theme Toggle -->
|
||||
<div class="flex lg:hidden items-center gap-2">
|
||||
<LanguageSelector />
|
||||
<ThemeToggle />
|
||||
<button
|
||||
<button
|
||||
@click="toggleMobileMenu"
|
||||
class="p-2 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
||||
aria-label="Toggle menu"
|
||||
@@ -152,8 +157,8 @@ function closeMobileMenu() {
|
||||
leave-from-class="opacity-100 translate-y-0"
|
||||
leave-to-class="opacity-0 -translate-y-2"
|
||||
>
|
||||
<div
|
||||
v-if="mobileMenuOpen"
|
||||
<div
|
||||
v-if="mobileMenuOpen"
|
||||
class="md:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900"
|
||||
>
|
||||
<div class="px-4 py-3 space-y-1">
|
||||
|
||||
85
src/components/LanguageSelector.vue
Normal file
85
src/components/LanguageSelector.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { LANGUAGES, loadLocaleMessages } from '@/i18n'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
|
||||
const { locale } = useI18n()
|
||||
const isOpen = ref(false)
|
||||
|
||||
const currentLanguage = () => {
|
||||
return LANGUAGES[locale.value] || LANGUAGES['en']
|
||||
}
|
||||
|
||||
const selectLanguage = async (langCode) => {
|
||||
// Load messages dynamically before switching
|
||||
try {
|
||||
await loadLocaleMessages(window.i18n, langCode)
|
||||
} catch (e) {
|
||||
// Fallback if dynamic import fails
|
||||
console.warn('Failed to load locale messages for', langCode, e)
|
||||
}
|
||||
locale.value = langCode
|
||||
localStorage.setItem('lang', langCode)
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
const toggleDropdown = () => {
|
||||
isOpen.value = !isOpen.value
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const saved = localStorage.getItem('lang')
|
||||
if (saved && saved !== locale.value) {
|
||||
try {
|
||||
await loadLocaleMessages(window.i18n, saved)
|
||||
locale.value = saved
|
||||
} catch (e) {
|
||||
console.warn('Failed to hydrate saved locale', saved, e)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative">
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg bg-neutral-200 dark:bg-neutral-800/50 hover:bg-neutral-300 dark:hover:bg-neutral-800 transition-colors text-neutral-900 dark:text-white text-sm font-medium"
|
||||
:aria-expanded="isOpen"
|
||||
:aria-label="$t('message.nav.language')"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<img
|
||||
:src="`/flags/${currentLanguage().flag}.png`"
|
||||
:alt="currentLanguage().name"
|
||||
class="w-5 h-4 rounded-sm"
|
||||
/>
|
||||
<span class="hidden sm:inline">{{ currentLanguage().name }}</span>
|
||||
<ChevronDown class="w-4 h-4 transition-transform" :class="{ 'rotate-180': isOpen }" />
|
||||
</button>
|
||||
|
||||
<!-- Dropdown menu -->
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="absolute right-0 mt-2 w-48 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-lg shadow-lg z-50"
|
||||
>
|
||||
<div class="py-1">
|
||||
<button
|
||||
v-for="(lang, code) in LANGUAGES"
|
||||
:key="code"
|
||||
:class="[
|
||||
'w-full px-4 py-2 flex items-center gap-3 text-sm transition-colors',
|
||||
locale === code
|
||||
? 'bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-white'
|
||||
: 'text-neutral-700 dark:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-800/70',
|
||||
]"
|
||||
@click="selectLanguage(code)"
|
||||
>
|
||||
<img :src="`/flags/${lang.flag}.png`" :alt="lang.name" class="w-5 h-4 rounded-sm" />
|
||||
<span>{{ lang.name }}</span>
|
||||
<span v-if="locale === code" class="ml-auto text-neutral-500">✓</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user