generated from Seekra/repository-template
Add more Languages and switch button #70
@@ -1,157 +0,0 @@
|
|||||||
<!--
|
|
||||||
Copyright 2026 Seekra
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { loadLanguage } from '@/i18n';
|
|
||||||
|
|
||||||
const { locale } = useI18n();
|
|
||||||
|
|
||||||
const isOpen = ref(false);
|
|
||||||
|
|
||||||
const languages = [
|
|
||||||
{ code: 'en', label: 'English', flag: '🇬🇧' },
|
|
||||||
{ code: 'de', label: 'Deutsch', flag: '🇩🇪' },
|
|
||||||
{ code: 'fr', label: 'Français', flag: '🇫🇷' },
|
|
||||||
{ code: 'es', label: 'Español', flag: '🇪🇸' },
|
|
||||||
{ code: 'it', label: 'Italiano', flag: '🇮🇹' },
|
|
||||||
{ code: 'pt', label: 'Português', flag: '🇵🇹' },
|
|
||||||
{ code: 'zh', label: '中文', flag: '🇨🇳' },
|
|
||||||
{ code: 'ja', label: '日本語', flag: '🇯🇵' },
|
|
||||||
{ code: 'ar', label: 'العربية', flag: '🇸🇦' },
|
|
||||||
{ code: 'hi', label: 'हिन्दी', flag: '🇮🇳' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const currentLanguage = computed(
|
|
||||||
() => languages.find(l => l.code === locale.value) ?? languages[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
async function selectLanguage(code) {
|
|
||||||
await loadLanguage(code);
|
|
||||||
localStorage.setItem('locale', code);
|
|
||||||
document.documentElement.lang = code;
|
|
||||||
document.documentElement.dir = code === 'ar' ? 'rtl' : 'ltr';
|
|
||||||
isOpen.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle() {
|
|
||||||
isOpen.value = !isOpen.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeOnBlur() {
|
|
||||||
// Kleines Delay damit Click auf Option noch registriert wird
|
|
||||||
setTimeout(() => { isOpen.value = false; }, 150);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="language-switch" @blur="closeOnBlur" tabindex="-1">
|
|
||||||
<button
|
|
||||||
class="language-button button"
|
|
||||||
@click="toggle"
|
|
||||||
:aria-expanded="isOpen"
|
|
||||||
aria-haspopup="listbox"
|
|
||||||
>
|
|
||||||
<span class="flag">{{ currentLanguage.flag }}</span>
|
|
||||||
<span class="lang-code">{{ currentLanguage.code.toUpperCase() }}</span>
|
|
||||||
<span class="chevron" :class="{ open: isOpen }">▾</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<ul v-if="isOpen" class="language-dropdown" role="listbox">
|
|
||||||
<li
|
|
||||||
v-for="lang in languages"
|
|
||||||
:key="lang.code"
|
|
||||||
role="option"
|
|
||||||
:aria-selected="lang.code === locale"
|
|
||||||
:class="{ active: lang.code === locale }"
|
|
||||||
@click="selectLanguage(lang.code)"
|
|
||||||
>
|
|
||||||
<span class="flag">{{ lang.flag }}</span>
|
|
||||||
<span class="lang-label">{{ lang.label }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.language-switch {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
background: none;
|
|
||||||
border: 1px solid var(--dark);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 4px 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: var(--dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-button:hover {
|
|
||||||
background-color: var(--light-d-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chevron {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chevron.open {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-dropdown {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: calc(100% + 6px);
|
|
||||||
background-color: var(--light-bg);
|
|
||||||
border: 1px solid var(--light-d-1);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 6px 0;
|
|
||||||
min-width: 160px;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-dropdown li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-dropdown li:hover {
|
|
||||||
background-color: var(--light-d-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-dropdown li.active {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flag {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
<!--
|
|
||||||
Copyright 2026 Seekra
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import ColorSchemeButton from '@/features/colorScheme/components/ColorSchemeButton.vue';
|
|
||||||
|
|
||||||
import logo from '@/assets/logo.svg';
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<nav class="global-nav">
|
|
||||||
<RouterLink to="/" class="link button link">
|
|
||||||
<img :src="logo" alt="Seekra" class="nav-logo" />
|
|
||||||
</RouterLink>
|
|
||||||
<ul class="right-links">
|
|
||||||
<li>
|
|
||||||
<ColorSchemeButton />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.global-nav {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.global-nav .right-links {
|
|
||||||
display: flex;
|
|
||||||
gap: 30px;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.global-nav .right-links a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.global-nav .right-links a:hover{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-logo {
|
|
||||||
height: 24px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"search": {
|
|
||||||
"searchBar": {
|
|
||||||
"submit": "بحث",
|
|
||||||
"placeholder": "بحث..."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"searchNotAvailable": "البحث غير متاح في الوقت الحالي."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"tryAgainToAnotherTime": "يرجى المحاولة مرة أخرى لاحقاً.",
|
|
||||||
"pageNotFound": "الصفحة التي تبحث عنها غير موجودة. يرجى التحقق من الرابط أو العودة إلى صفحة البحث."
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"back": {
|
|
||||||
"search": "العودة إلى البحث"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preferences": {
|
|
||||||
"colorScheme": {
|
|
||||||
"switch": {
|
|
||||||
"light": "التبديل إلى الوضع الفاتح",
|
|
||||||
"dark": "التبديل إلى الوضع الداكن",
|
|
||||||
"auto": "استخدام نظام الألوان للجهاز"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"slogan": "صُنع للبحث."
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"search": {
|
|
||||||
"searchBar": {
|
|
||||||
"submit": "खोजें",
|
|
||||||
"placeholder": "खोजें..."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"searchNotAvailable": "खोज अभी उपलब्ध नहीं है।"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"tryAgainToAnotherTime": "कृपया बाद में पुनः प्रयास करें।",
|
|
||||||
"pageNotFound": "आप जिस पेज को खोज रहे हैं वह मौजूद नहीं है। कृपया URL जांचें या खोज पेज पर वापस जाएं।"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"back": {
|
|
||||||
"search": "खोज पर वापस जाएं"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preferences": {
|
|
||||||
"colorScheme": {
|
|
||||||
"switch": {
|
|
||||||
"light": "लाइट मोड पर स्विच करें",
|
|
||||||
"dark": "डार्क मोड पर स्विच करें",
|
|
||||||
"auto": "सिस्टम कलर स्कीम का उपयोग करें"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"slogan": "खोज के लिए बनाया गया।"
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"search": {
|
|
||||||
"searchBar": {
|
|
||||||
"submit": "検索",
|
|
||||||
"placeholder": "検索..."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"searchNotAvailable": "現在、検索をご利用いただけません。"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"tryAgainToAnotherTime": "後ほど再度お試しください。",
|
|
||||||
"pageNotFound": "お探しのページは存在しません。URLを確認するか、検索ページに戻ってください。"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"back": {
|
|
||||||
"search": "検索に戻る"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preferences": {
|
|
||||||
"colorScheme": {
|
|
||||||
"switch": {
|
|
||||||
"light": "ライトモードに切り替え",
|
|
||||||
"dark": "ダークモードに切り替え",
|
|
||||||
"auto": "システムのカラースキームを使用"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"slogan": "検索のために作られた。"
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"search": {
|
|
||||||
"searchBar": {
|
|
||||||
"submit": "搜索",
|
|
||||||
"placeholder": "搜索..."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"searchNotAvailable": "搜索功能暂时不可用。"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"tryAgainToAnotherTime": "请稍后再试。",
|
|
||||||
"pageNotFound": "您查找的页面不存在。请检查网址或返回搜索页面。"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"back": {
|
|
||||||
"search": "返回搜索"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preferences": {
|
|
||||||
"colorScheme": {
|
|
||||||
"switch": {
|
|
||||||
"light": "切换到浅色模式",
|
|
||||||
"dark": "切换到深色模式",
|
|
||||||
"auto": "使用系统配色方案"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"slogan": "专为搜索而生。"
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user