Files
kayjaydee 33bbb7a3b9 feat(localization): implement localePath for project links across components
- Updated HytaleDemoGrid.vue, ProjectCard.vue, FeaturedProjectsSection.vue, about.vue, and project/[id].vue to utilize localePath for project links, enhancing localization support.
- This change ensures that project URLs are correctly localized, improving user experience across different languages.

This commit strengthens the site's multilingual capabilities by standardizing project link paths.
2026-04-28 19:29:40 +02:00

92 lines
3.7 KiB
Vue

<script setup lang="ts">
import type { Project } from '~~/shared/types'
interface Props {
project: Project
}
const props = defineProps<Props>()
const { t } = useI18n()
const localePath = useLocalePath()
const translatedCategory = computed(() => {
if (!props.project.category) return ''
const categoryKey = props.project.category.replace(/\s+/g, '').toLowerCase()
return t(`projects.categories.${categoryKey}`, props.project.category)
})
</script>
<template>
<article
class="group relative rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm overflow-hidden transition-all duration-300 hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1.5"
itemscope
itemtype="https://schema.org/CreativeWork"
>
<!-- Image -->
<NuxtLink :to="localePath(`/project/${project.id}`)" class="block relative overflow-hidden">
<NuxtImg
:src="project.image"
:alt="`${project.title} - ${project.description.slice(0, 60)}...`"
loading="lazy"
format="webp"
width="400"
height="300"
class="w-full h-52 object-cover transition-transform duration-500 group-hover:scale-105"
itemprop="image"
/>
<!-- Gradient overlay -->
<div class="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-end justify-center pb-5">
<span class="text-white text-sm font-semibold flex items-center gap-1.5 translate-y-2 group-hover:translate-y-0 transition-transform duration-300">
{{ t('projects.buttons.viewProject') }}
<UIcon name="i-lucide-arrow-right" class="w-4 h-4" />
</span>
</div>
</NuxtLink>
<!-- Content -->
<div class="p-5 sm:p-6 flex flex-col gap-3">
<!-- Category & Date -->
<div class="flex items-center justify-between">
<UBadge v-if="project.category" color="primary" variant="subtle" itemprop="genre">
{{ translatedCategory }}
</UBadge>
<time v-if="project.date" class="text-xs text-gray-400 dark:text-gray-500 font-mono" :datetime="project.date" itemprop="dateCreated">
{{ project.date }}
</time>
</div>
<!-- Title -->
<h3 class="text-lg font-bold text-gray-900 dark:text-white group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors" itemprop="name">
{{ project.title }}
</h3>
<!-- Description -->
<p class="text-sm text-gray-500 dark:text-gray-400 line-clamp-2 leading-relaxed" itemprop="description">
{{ project.description }}
</p>
<!-- Technologies -->
<div v-if="project.technologies?.length" class="flex flex-wrap gap-1.5 pt-2" itemprop="keywords">
<span
v-for="tech in project.technologies.slice(0, 3)"
:key="tech"
class="text-[11px] px-2.5 py-1 rounded-full bg-gray-100 dark:bg-gray-800/80 text-gray-600 dark:text-gray-400 font-medium border border-gray-200/50 dark:border-gray-700/30"
>
{{ tech }}
</span>
<span v-if="project.technologies.length > 3" class="text-[11px] px-2.5 py-1 rounded-full bg-gray-100 dark:bg-gray-800/80 text-gray-500 dark:text-gray-500 font-medium border border-gray-200/50 dark:border-gray-700/30">
+{{ project.technologies.length - 3 }}
</span>
</div>
</div>
<!-- Hidden SEO link -->
<NuxtLink
:to="localePath(`/project/${project.id}`)"
class="absolute inset-0 z-10"
:aria-label="`${t('projects.buttons.viewProject')} - ${project.title}`"
itemprop="url"
/>
</article>
</template>