📄 Huge intl feature

This commit is contained in:
RJ
2025-12-03 00:17:34 +02:00
parent 8b05aae5a8
commit 7e8b82f571
48 changed files with 955 additions and 138 deletions

View File

@@ -1,7 +1,8 @@
'use client'
import Link from 'next/link'
import {Link} from '@/i18n/navigation'
import { usePathname } from 'next/navigation'
import { useLocale, useTranslations } from 'next-intl'
import { Fragment } from 'react'
import { BreadcrumbsSchema } from './breadcrumbs-schema'
@@ -38,25 +39,33 @@ function ChevronIcon({ className }: { className?: string }) {
)
}
function formatSegmentLabel(segment: string): string {
const specialCases: { [key: string]: string } = {
blog: 'Blog',
tags: 'Tag-uri',
about: 'Despre',
}
if (specialCases[segment]) {
return specialCases[segment]
}
return segment
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}
export function Breadcrumbs({ items }: { items?: BreadcrumbItem[] }) {
const pathname = usePathname()
const locale = useLocale()
const t = useTranslations('Breadcrumbs')
// Hide breadcrumbs on main page
const isMainPage = pathname === `/${locale}` || pathname === '/'
if (isMainPage) {
return null
}
const formatSegmentLabel = (segment: string): string => {
const specialCases: { [key: string]: string } = {
blog: t('blog'),
tags: t('tags'),
about: t('about'),
}
if (specialCases[segment]) {
return specialCases[segment]
}
return segment
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}
let breadcrumbs: BreadcrumbItem[] = items || []
@@ -71,12 +80,8 @@ export function Breadcrumbs({ items }: { items?: BreadcrumbItem[] }) {
})
}
if (pathname === '/') {
return null
}
const schemaItems = [
{ position: 1, name: 'Acasă', item: '/' },
{ position: 1, name: t('home'), item: '/' },
...breadcrumbs.map((item, index) => ({
position: index + 2,
name: item.label,
@@ -96,7 +101,7 @@ export function Breadcrumbs({ items }: { items?: BreadcrumbItem[] }) {
<Link
href="/"
className="flex items-center text-gray-500 hover:text-primary-600 transition"
aria-label="Acasă"
aria-label={t('home')}
>
<HomeIcon className="w-4 h-4" />
</Link>

View File

@@ -0,0 +1,59 @@
'use client';
import {useLocale} from 'next-intl';
import {useRouter, usePathname} from '@/i18n/navigation';
import {routing} from '@/i18n/routing';
import {useState} from 'react';
export default function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const [isOpen, setIsOpen] = useState(false);
const handleLocaleChange = (newLocale: string) => {
router.replace(pathname, {locale: newLocale});
router.refresh();
setIsOpen(false);
};
return (
<div className="relative z-[100]">
<button
onClick={() => setIsOpen(!isOpen)}
className="px-3 py-1 border-2 border-slate-700 font-mono uppercase text-xs hover:border-cyan-500 transition-colors"
aria-label="Switch language"
>
{locale}
</button>
{isOpen && (
<div className="absolute right-0 top-full mt-2 bg-slate-900 border-2 border-slate-700 min-w-[120px] z-[100]">
{routing.locales.map((loc: string) => (
<button
key={loc}
onClick={() => handleLocaleChange(loc)}
className={`
w-full text-left px-4 py-2 font-mono uppercase text-xs
border-b border-slate-700 last:border-b-0
${locale === loc
? 'bg-cyan-900 text-cyan-300'
: 'text-slate-400 hover:bg-slate-800'
}
`}
>
{loc === 'en' ? 'English' : 'Română'}
</button>
))}
</div>
)}
{isOpen && (
<div
className="fixed inset-0 z-40"
onClick={() => setIsOpen(false)}
/>
)}
</div>
);
}