📝Update breadcrumbs i18n

This commit is contained in:
RJ
2025-12-05 16:19:58 +02:00
parent 919e638144
commit 0fe115f46e
12 changed files with 175 additions and 78 deletions

View File

@@ -1,11 +1,16 @@
'use client'
import { Breadcrumbs } from '@/components/layout/Breadcrumbs' import { Breadcrumbs } from '@/components/layout/Breadcrumbs'
import { useTranslations } from 'next-intl'
export default function AboutBreadcrumb() { export default function AboutBreadcrumb() {
const t = useTranslations('Breadcrumbs')
return ( return (
<Breadcrumbs <Breadcrumbs
items={[ items={[
{ {
label: 'Despre', label: t('about'),
href: '/about', href: '/about',
current: true, current: true,
}, },

View File

@@ -1,5 +1,6 @@
import { Breadcrumbs } from '@/components/layout/Breadcrumbs' import { Breadcrumbs } from '@/components/layout/Breadcrumbs'
import { getPostBySlug } from '@/lib/markdown' import { getPostBySlug } from '@/lib/markdown'
import { getTranslations } from 'next-intl/server'
interface BreadcrumbItem { interface BreadcrumbItem {
label: string label: string
@@ -7,28 +8,19 @@ interface BreadcrumbItem {
current?: boolean current?: boolean
} }
function formatDirectoryName(name: string): string {
const directoryNames: { [key: string]: string } = {
tech: 'Tehnologie',
design: 'Design',
tutorial: 'Tutoriale',
}
return directoryNames[name] || name.charAt(0).toUpperCase() + name.slice(1)
}
export default async function BlogPostBreadcrumb({ export default async function BlogPostBreadcrumb({
params, params,
}: { }: {
params: Promise<{ slug: string[] }> params: Promise<{ slug: string[] }>
}) { }) {
const t = await getTranslations('Breadcrumbs')
const { slug } = await params const { slug } = await params
const slugPath = slug.join('/') const slugPath = slug.join('/')
const post = await getPostBySlug(slugPath) const post = await getPostBySlug(slugPath)
const items: BreadcrumbItem[] = [ const items: BreadcrumbItem[] = [
{ {
label: 'Blog', label: t('blog'),
href: '/blog', href: '/blog',
}, },
] ]
@@ -36,8 +28,9 @@ export default async function BlogPostBreadcrumb({
if (slug.length > 1) { if (slug.length > 1) {
for (let i = 0; i < slug.length - 1; i++) { for (let i = 0; i < slug.length - 1; i++) {
const segmentPath = slug.slice(0, i + 1).join('/') const segmentPath = slug.slice(0, i + 1).join('/')
const dirName = slug[i]
items.push({ items.push({
label: formatDirectoryName(slug[i]), label: t(dirName) || dirName.charAt(0).toUpperCase() + dirName.slice(1),
href: `/blog/${segmentPath}`, href: `/blog/${segmentPath}`,
}) })
} }

View File

@@ -1,11 +1,16 @@
'use client'
import { Breadcrumbs } from '@/components/layout/Breadcrumbs' import { Breadcrumbs } from '@/components/layout/Breadcrumbs'
import { useTranslations } from 'next-intl'
export default function BlogBreadcrumb() { export default function BlogBreadcrumb() {
const t = useTranslations('Breadcrumbs')
return ( return (
<Breadcrumbs <Breadcrumbs
items={[ items={[
{ {
label: 'Blog', label: t('blog'),
href: '/blog', href: '/blog',
current: true, current: true,
}, },

View File

@@ -1,6 +1,8 @@
import { Breadcrumbs } from '@/components/layout/Breadcrumbs' import { Breadcrumbs } from '@/components/layout/Breadcrumbs'
import { getTranslations } from 'next-intl/server'
export default async function TagBreadcrumb({ params }: { params: Promise<{ tag: string }> }) { export default async function TagBreadcrumb({ params }: { params: Promise<{ tag: string }> }) {
const t = await getTranslations('Breadcrumbs')
const { tag } = await params const { tag } = await params
const tagName = tag const tagName = tag
.split('-') .split('-')
@@ -11,7 +13,7 @@ export default async function TagBreadcrumb({ params }: { params: Promise<{ tag:
<Breadcrumbs <Breadcrumbs
items={[ items={[
{ {
label: 'Tag-uri', label: t('tags'),
href: '/tags', href: '/tags',
}, },
{ {

View File

@@ -1,11 +1,16 @@
'use client'
import { Breadcrumbs } from '@/components/layout/Breadcrumbs' import { Breadcrumbs } from '@/components/layout/Breadcrumbs'
import { useTranslations } from 'next-intl'
export default function TagsBreadcrumb() { export default function TagsBreadcrumb() {
const t = useTranslations('Breadcrumbs')
return ( return (
<Breadcrumbs <Breadcrumbs
items={[ items={[
{ {
label: 'Tag-uri', label: t('tags'),
href: '/tags', href: '/tags',
current: true, current: true,
}, },

View File

@@ -13,7 +13,7 @@ export default async function HomePage({ params }: Props) {
const { locale } = await params const { locale } = await params
setRequestLocale(locale) setRequestLocale(locale)
const t = await getTranslations('Home') const t = await getTranslations('Home')
const tNav = await getTranslations('Navigation') // const tNav = await getTranslations('Navigation')
const allPosts = await getAllPosts() const allPosts = await getAllPosts()
const featuredPosts = allPosts.slice(0, 6) const featuredPosts = allPosts.slice(0, 6)
@@ -29,11 +29,7 @@ export default async function HomePage({ params }: Props) {
<div className="relative z-10 max-w-5xl mx-auto px-6 w-full"> <div className="relative z-10 max-w-5xl mx-auto px-6 w-full">
<div className="border-4 border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-900/80 p-8 md:p-12 transition-colors duration-300"> <div className="border-4 border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-900/80 p-8 md:p-12 transition-colors duration-300">
{/* Logo */} {/* Logo */}
<HeroHeader <HeroHeader />
terminalVersion={t('terminalVersion')}
blogLabel={tNav('blog')}
aboutLabel={tNav('about')}
/>
<div className="border-l-4 border-cyan-700 dark:border-cyan-900 pl-6 mb-8"> <div className="border-l-4 border-cyan-700 dark:border-cyan-900 pl-6 mb-8">
<p className="font-mono text-xs text-slate-500 dark:text-slate-500 uppercase tracking-widest mb-2"> <p className="font-mono text-xs text-slate-500 dark:text-slate-500 uppercase tracking-widest mb-2">

View File

@@ -473,12 +473,62 @@
@layer utilities { @layer utilities {
.glitch-btn-cyber { .glitch-btn-cyber {
--glitch-shimmy: 5; --glitch-shimmy: 5;
--glitch-clip-1: polygon(0 2%, 100% 2%, 100% 95%, 95% 95%, 95% 90%, 85% 90%, 85% 95%, 8% 95%, 0 70%); --glitch-clip-1: polygon(
--glitch-clip-2: polygon(0 78%, 100% 78%, 100% 100%, 95% 100%, 95% 90%, 85% 90%, 85% 100%, 8% 100%, 0 78%); 0 2%,
--glitch-clip-3: polygon(0 44%, 100% 44%, 100% 54%, 95% 54%, 95% 54%, 85% 54%, 85% 54%, 8% 54%, 0 54%); 100% 2%,
100% 95%,
95% 95%,
95% 90%,
85% 90%,
85% 95%,
8% 95%,
0 70%
);
--glitch-clip-2: polygon(
0 78%,
100% 78%,
100% 100%,
95% 100%,
95% 90%,
85% 90%,
85% 100%,
8% 100%,
0 78%
);
--glitch-clip-3: polygon(
0 44%,
100% 44%,
100% 54%,
95% 54%,
95% 54%,
85% 54%,
85% 54%,
8% 54%,
0 54%
);
--glitch-clip-4: polygon(0 0, 100% 0, 100% 0, 95% 0, 95% 0, 85% 0, 85% 0, 8% 0, 0 0); --glitch-clip-4: polygon(0 0, 100% 0, 100% 0, 95% 0, 95% 0, 85% 0, 85% 0, 8% 0, 0 0);
--glitch-clip-5: polygon(0 40%, 100% 40%, 100% 85%, 95% 85%, 95% 85%, 85% 85%, 85% 85%, 8% 85%, 0 70%); --glitch-clip-5: polygon(
--glitch-clip-6: polygon(0 63%, 100% 63%, 100% 80%, 95% 80%, 95% 80%, 85% 80%, 85% 80%, 8% 80%, 0 70%); 0 40%,
100% 40%,
100% 85%,
95% 85%,
95% 85%,
85% 85%,
85% 85%,
8% 85%,
0 70%
);
--glitch-clip-6: polygon(
0 63%,
100% 63%,
100% 80%,
95% 80%,
95% 80%,
85% 80%,
85% 80%,
8% 80%,
0 70%
);
} }
.glitch-overlay { .glitch-overlay {
@@ -498,29 +548,80 @@
} }
@keyframes glitch-btn-animate { @keyframes glitch-btn-animate {
0% { clip-path: var(--glitch-clip-1); } 0% {
2%, 8% { clip-path: var(--glitch-clip-2); transform: translate(calc(var(--glitch-shimmy) * -1%), 0); } clip-path: var(--glitch-clip-1);
6% { clip-path: var(--glitch-clip-2); transform: translate(calc(var(--glitch-shimmy) * 1%), 0); } }
9% { clip-path: var(--glitch-clip-2); transform: translate(0, 0); } 2%,
10% { clip-path: var(--glitch-clip-3); transform: translate(calc(var(--glitch-shimmy) * 1%), 0); } 8% {
13% { clip-path: var(--glitch-clip-3); transform: translate(0, 0); } clip-path: var(--glitch-clip-2);
14%, 21% { clip-path: var(--glitch-clip-4); transform: translate(calc(var(--glitch-shimmy) * 1%), 0); } transform: translate(calc(var(--glitch-shimmy) * -1%), 0);
25%, 30% { clip-path: var(--glitch-clip-5); transform: translate(calc(var(--glitch-shimmy) * -1%), 0); } }
35%, 45% { clip-path: var(--glitch-clip-6); transform: translate(calc(var(--glitch-shimmy) * -1%), 0); } 6% {
40% { clip-path: var(--glitch-clip-6); transform: translate(calc(var(--glitch-shimmy) * 1%), 0); } clip-path: var(--glitch-clip-2);
50% { clip-path: var(--glitch-clip-6); transform: translate(0, 0); } transform: translate(calc(var(--glitch-shimmy) * 1%), 0);
55% { clip-path: var(--glitch-clip-3); transform: translate(calc(var(--glitch-shimmy) * 1%), 0); } }
60% { clip-path: var(--glitch-clip-3); transform: translate(0, 0); } 9% {
61%, 100% { clip-path: var(--glitch-clip-4); } clip-path: var(--glitch-clip-2);
transform: translate(0, 0);
}
10% {
clip-path: var(--glitch-clip-3);
transform: translate(calc(var(--glitch-shimmy) * 1%), 0);
}
13% {
clip-path: var(--glitch-clip-3);
transform: translate(0, 0);
}
14%,
21% {
clip-path: var(--glitch-clip-4);
transform: translate(calc(var(--glitch-shimmy) * 1%), 0);
}
25%,
30% {
clip-path: var(--glitch-clip-5);
transform: translate(calc(var(--glitch-shimmy) * -1%), 0);
}
35%,
45% {
clip-path: var(--glitch-clip-6);
transform: translate(calc(var(--glitch-shimmy) * -1%), 0);
}
40% {
clip-path: var(--glitch-clip-6);
transform: translate(calc(var(--glitch-shimmy) * 1%), 0);
}
50% {
clip-path: var(--glitch-clip-6);
transform: translate(0, 0);
}
55% {
clip-path: var(--glitch-clip-3);
transform: translate(calc(var(--glitch-shimmy) * 1%), 0);
}
60% {
clip-path: var(--glitch-clip-3);
transform: translate(0, 0);
}
61%,
100% {
clip-path: var(--glitch-clip-4);
}
} }
.glitch-btn-subtle { .glitch-btn-subtle {
--glitch-shimmy: 2; --glitch-shimmy: 2;
} }
.glitch-overlay-pink { color: var(--neon-pink); } .glitch-overlay-pink {
.glitch-overlay-purple { color: var(--neon-purple); } color: var(--neon-pink);
.glitch-overlay-magenta { color: var(--neon-magenta); } }
.glitch-overlay-purple {
color: var(--neon-purple);
}
.glitch-overlay-magenta {
color: var(--neon-magenta);
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.glitch-btn-cyber:is(:hover, :focus-visible) .glitch-overlay { .glitch-btn-cyber:is(:hover, :focus-visible) .glitch-overlay {

View File

@@ -19,11 +19,7 @@ export function GlitchButton({
...props ...props
}: GlitchButtonProps) { }: GlitchButtonProps) {
const glitchClasses = !disabled const glitchClasses = !disabled
? cn( ? cn('glitch-btn-cyber', variant === 'subtle' && 'glitch-btn-subtle', 'relative')
'glitch-btn-cyber',
variant === 'subtle' && 'glitch-btn-subtle',
'relative'
)
: '' : ''
const overlayColorClass = { const overlayColorClass = {
@@ -34,18 +30,11 @@ export function GlitchButton({
}[glitchColor] }[glitchColor]
return ( return (
<button <button className={cn(glitchClasses, className)} disabled={disabled} {...props}>
className={cn(glitchClasses, className)}
disabled={disabled}
{...props}
>
{children} {children}
{!disabled && ( {!disabled && (
<div <div className={cn('glitch-overlay', overlayColorClass)} aria-hidden="true">
className={cn('glitch-overlay', overlayColorClass)}
aria-hidden="true"
>
{children} {children}
</div> </div>
)} )}

View File

@@ -5,17 +5,20 @@ import { Link } from '@/i18n/navigation'
import Image from 'next/image' import Image from 'next/image'
import { ThemeToggle } from '@/components/theme-toggle' import { ThemeToggle } from '@/components/theme-toggle'
import { GlitchButton } from '@/components/effects/glitch-button' import { GlitchButton } from '@/components/effects/glitch-button'
import LanguageSwitcher from './LanguageSwitcher'
import { useLocale, useTranslations } from 'next-intl'
interface HeroHeaderProps { export function HeroHeader() {
terminalVersion: string const locale = useLocale()
blogLabel: string const t = useTranslations('Home')
aboutLabel: string const tNav = useTranslations('Navigation')
}
const terminalVersion = t('terminalVersion')
const blogLabel = tNav('blog')
const aboutLabel = tNav('about')
export function HeroHeader({ terminalVersion, blogLabel, aboutLabel }: HeroHeaderProps) {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const [isMobile, setIsMobile] = useState(false) const [isMobile, setIsMobile] = useState(false)
useEffect(() => { useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768) const checkMobile = () => setIsMobile(window.innerWidth < 768)
checkMobile() checkMobile()
@@ -84,8 +87,9 @@ export function HeroHeader({ terminalVersion, blogLabel, aboutLabel }: HeroHeade
> >
[{aboutLabel}] [{aboutLabel}]
</Link> </Link>
<div className="px-3 py-2"> <div className="flex items-center gap-4 px-4 py-2">
<ThemeToggle /> <ThemeToggle />
<LanguageSwitcher />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -81,5 +81,5 @@ export function generateSlug(title: string): string {
} }
export function cn(...inputs: (string | undefined | null | false)[]): string { export function cn(...inputs: (string | undefined | null | false)[]): string {
return inputs.filter(Boolean).join(" ") return inputs.filter(Boolean).join(' ')
} }

View File

@@ -3,21 +3,21 @@
"siteTitle": "Personal Blog", "siteTitle": "Personal Blog",
"siteDescription": "Thoughts on technology and development" "siteDescription": "Thoughts on technology and development"
}, },
"Navigation": { "Navigation": {
"home": "Home", "home": "Home",
"blog": "Blog", "blog": "Blog",
"tags": "Tags", "tags": "Tags",
"about": "About" "about": "About"
}, },
"Breadcrumbs": { "Breadcrumbs": {
"home": "Home", "home": "Home",
"blog": "Blog", "blog": "Blog",
"tags": "Tags", "tags": "Tags",
"about": "About" "about": "About",
"tech": "Technology",
"design": "Design",
"tutorial": "Tutorials"
}, },
"Home": { "Home": {
"terminalVersion": "TERMINAL:// V2.0", "terminalVersion": "TERMINAL:// V2.0",
"documentLevel": "DOCUMENT LEVEL-1 //", "documentLevel": "DOCUMENT LEVEL-1 //",
@@ -32,7 +32,6 @@
"seePostsButton": "[SEE POSTS] >>", "seePostsButton": "[SEE POSTS] >>",
"seeAllTagsButton": "[SEE ALL TAGS] >>" "seeAllTagsButton": "[SEE ALL TAGS] >>"
}, },
"BlogListing": { "BlogListing": {
"title": "Blog", "title": "Blog",
"subtitle": "Latest articles and thoughts", "subtitle": "Latest articles and thoughts",
@@ -48,7 +47,6 @@
"prev": "< PREV", "prev": "< PREV",
"next": "NEXT >" "next": "NEXT >"
}, },
"BlogPost": { "BlogPost": {
"readMore": "Read more", "readMore": "Read more",
"readingTime": "{minutes} min read", "readingTime": "{minutes} min read",
@@ -58,7 +56,6 @@
"relatedPosts": "Related Posts", "relatedPosts": "Related Posts",
"sharePost": "Share this post" "sharePost": "Share this post"
}, },
"Tags": { "Tags": {
"title": "Tags", "title": "Tags",
"subtitle": "Browse by topic", "subtitle": "Browse by topic",
@@ -67,7 +64,6 @@
"relatedTags": "Related tags", "relatedTags": "Related tags",
"quickNav": "Quick navigation" "quickNav": "Quick navigation"
}, },
"About": { "About": {
"title": "About", "title": "About",
"subtitle": "Learn more about me", "subtitle": "Learn more about me",
@@ -119,13 +115,11 @@
"techStackSelfHostingText": "Home lab, privacy-focused services, full control, Git server", "techStackSelfHostingText": "Home lab, privacy-focused services, full control, Git server",
"contactTitle": "> CONTACT" "contactTitle": "> CONTACT"
}, },
"NotFound": { "NotFound": {
"title": "Page Not Found", "title": "Page Not Found",
"description": "The page you're looking for doesn't exist", "description": "The page you're looking for doesn't exist",
"goHome": "Go to homepage" "goHome": "Go to homepage"
}, },
"LanguageSwitcher": { "LanguageSwitcher": {
"switchLanguage": "Switch language", "switchLanguage": "Switch language",
"currentLanguage": "Current language" "currentLanguage": "Current language"

View File

@@ -13,7 +13,10 @@
"home": "Acasă", "home": "Acasă",
"blog": "Blog", "blog": "Blog",
"tags": "Etichete", "tags": "Etichete",
"about": "Despre" "about": "Despre",
"tech": "Tehnologie",
"design": "Design",
"tutorial": "Tutoriale"
}, },
"Home": { "Home": {
"terminalVersion": "TERMINAL:// V2.0", "terminalVersion": "TERMINAL:// V2.0",