apply translations in all pages

This commit is contained in:
2026-04-17 11:13:20 +02:00
parent dbeb294920
commit 7a8022d11a
15 changed files with 108 additions and 124 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
import HeroSection from "@/components/sections/HeroSection"; import HeroSection from "@/components/sections/HeroSection";
import WorksSection from "@/components/sections/WorksSection"; import WorksSection from "@/components/sections/WorksSection";
import FeatureSection from "@/components/sections/FeatureSection"; import ApproachSection from "@/components/sections/AppoachSection";
import TimelineSection from "@/components/sections/AboutSection"; import TimelineSection from "@/components/sections/AboutSection";
import { getAllProjects } from "@/lib/projects/get-projects"; import { getAllProjects } from "@/lib/projects/get-projects";
@@ -20,7 +20,7 @@ export default async function Home({ params }: props) {
<HeroSection /> <HeroSection />
<div className="px-8 md:px-14 lg:px-20"> <div className="px-8 md:px-14 lg:px-20">
<WorksSection projects={projects} locale={locale} /> <WorksSection projects={projects} locale={locale} />
<FeatureSection /> <ApproachSection />
<TimelineSection /> <TimelineSection />
</div> </div>
</main> </main>
+8 -5
View File
@@ -1,6 +1,9 @@
import { FiFacebook, FiInstagram, FiLinkedin, FiGithub } from "react-icons/fi"; import { FiFacebook, FiInstagram, FiLinkedin, FiGithub } from "react-icons/fi";
import { useTranslations } from "next-intl";
export default function Footer() { export default function Footer() {
const t = useTranslations('footer');
return ( return (
<footer className="w-[calc(100vw - 11px*2)] bg-brand-brown rounded-t-[10px] mx-2.75 flex flex-col items-center justify-center py-15 text-brand-beige gap-6"> <footer className="w-[calc(100vw - 11px*2)] bg-brand-brown rounded-t-[10px] mx-2.75 flex flex-col items-center justify-center py-15 text-brand-beige gap-6">
{/* Logo */} {/* Logo */}
@@ -9,13 +12,13 @@ export default function Footer() {
<div className="flex flex-col lg:flex-row justify-center items-start gap-16"> <div className="flex flex-col lg:flex-row justify-center items-start gap-16">
<div className="flex flex-col items-start justify-start gap-4"> <div className="flex flex-col items-start justify-start gap-4">
<h3 className="font-serif text-3xl">Mathéo Guilbert EI</h3> <h3 className="font-serif text-3xl">Mathéo Guilbert EI</h3>
<p className="font-sans text-base">Développeur Full Stack & Architecte Web</p> <p className="font-sans text-base">{t('me')}</p>
</div> </div>
<div className="flex flex-col items-start justify-start gap-4"> <div className="flex flex-col items-start justify-start gap-4">
<h3 className="font-serif text-3xl">Légal</h3> <h3 className="font-serif text-3xl">{t('legal.title')}</h3>
<p className="font-sans text-base">Mentions Légales</p> <p className="font-sans text-base">{t('legal.legalNotice')}</p>
<p className="font-sans text-base">Conditions Générales de Vente</p> <p className="font-sans text-base">{t('legal.termsAndConditions')}</p>
</div> </div>
<div className="flex flex-col items-start justify-start gap-4"> <div className="flex flex-col items-start justify-start gap-4">
@@ -32,7 +35,7 @@ export default function Footer() {
<FiGithub /> <FiGithub />
</div> </div>
<p className="text-brand-beige/70 font-sans text-sm">© 2026 M. Guilbert. Tous droits réservés.</p> <p className="text-brand-beige/70 font-sans text-sm">{t('copyright')}</p>
</footer> </footer>
); );
} }
+6 -4
View File
@@ -5,6 +5,7 @@ import React, { useState, useEffect, MouseEvent } from "react";
import { useParams, usePathname } from "next/navigation"; import { useParams, usePathname } from "next/navigation";
import { CiMenuFries } from "react-icons/ci"; import { CiMenuFries } from "react-icons/ci";
import { IoCloseOutline } from "react-icons/io5"; import { IoCloseOutline } from "react-icons/io5";
import { useTranslations } from "next-intl";
import LanguageSwitcher from "../ui/LanguageSwitcher"; import LanguageSwitcher from "../ui/LanguageSwitcher";
@@ -12,13 +13,14 @@ export default function Header() {
const params = useParams<{ locale?: string }>(); const params = useParams<{ locale?: string }>();
const pathname = usePathname(); const pathname = usePathname();
const locale = params?.locale ?? "fr"; const locale = params?.locale ?? "fr";
const t = useTranslations('header');
const homePath = `/${locale}`; const homePath = `/${locale}`;
const links = [ const links = [
{ text: "Accueil", target: homePath }, { text: t('home'), target: homePath },
{ text: "Travaux", target: `${homePath}#works` }, { text: t('works'), target: `${homePath}#works` },
{ text: "A propos", target: `${homePath}#about` }, { text: t('about'), target: `${homePath}#about` },
{ text: "Blog", target: `${homePath}/blog` }, { text: t('blog'), target: `${homePath}/blog` },
]; ];
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
+18 -16
View File
@@ -1,36 +1,38 @@
'use client'; 'use client';
import { useRef, useEffect, useState } from 'react'; import { useRef, useEffect, useState } from 'react';
import { useTranslations } from "next-intl";
const steps = [ export default function TimelineSection() {
const sectionRef = useRef<HTMLElement>(null);
const [visible, setVisible] = useState(false);
const t = useTranslations('about');
const lineTop = '58%';
const steps = [
{ {
title: 'Les Débuts', title: t('step1.title'),
text: "J'ai commencé par le web en 2019 suite à un stage, je n'ai plus jamais arrêté. Aujourd'hui je veux en faire mon métier.", text: t('step1.text'),
year: '2019', year: '2019',
position: 'top' as const, position: 'top' as const,
left: '16%', left: '16%',
}, },
{ {
title: 'L\'Évolution', title: t('step2.title'),
text: "Après un bac STI2D avec mention Très bien j'ai entamé un BUT Informatique Parcours Développement IA.", text: t('step2.text'),
year: '2022', year: '2022',
position: 'bottom' as const, position: 'bottom' as const,
left: '50%', left: '50%',
}, },
{ {
title: 'Aujourd\'hui', title: t('step3.title'),
text: "Je suis entrepreneur, co-fondateur d'une startup et développeur freelance, je vis de ma passion.", text: t('step3.text'),
year: '2026', year: '2026',
position: 'top' as const, position: 'top' as const,
left: '84%', left: '84%',
}, },
]; ];
const lineTop = '58%';
export default function TimelineSection() {
const sectionRef = useRef<HTMLElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => { useEffect(() => {
const section = sectionRef.current; const section = sectionRef.current;
@@ -56,11 +58,11 @@ export default function TimelineSection() {
{/* Label */} {/* Label */}
<div className="reveal flex items-center gap-3 mb-6"> <div className="reveal flex items-center gap-3 mb-6">
<span className="w-8 h-px bg-brand-brown/40" /> <span className="w-8 h-px bg-brand-brown/40" />
<span className="font-sans text-[11px] uppercase tracking-[0.2em] text-brand-brown/45">Parcours</span> <span className="font-sans text-[11px] uppercase tracking-[0.2em] text-brand-brown/45">{t('sectionLabel')}</span>
</div> </div>
<h2 className="reveal font-serif text-[clamp(48px,7vw,100px)] leading-none text-brand-brown mb-16 md:mb-24"> <h2 className="reveal font-serif text-[clamp(48px,7vw,100px)] leading-none text-brand-brown mb-16 md:mb-24">
À propos {t('title')}
</h2> </h2>
{/* Mobile layout */} {/* Mobile layout */}
@@ -1,28 +1,29 @@
'use client'; 'use client';
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
import { FeatureColumn } from '@/components/ui/FeatureColumn'; import { useTranslations } from "next-intl";
const features = [ export default function ApproachSection() {
const sectionRef = useRef<HTMLElement>(null);
const t = useTranslations('approach');
const features = [
{ {
imagePath: '/icons/brain.svg', imagePath: '/icons/brain.svg',
title: 'Plus qu\'un simple exécutant', title: t('feature1.title'),
text: 'Grâce à mon background en conception numérique et mobile, je ne me contente pas de coder. J\'apporte un regard critique et constructif sur l\'ensemble de votre projet.', text: t('feature1.text'),
}, },
{ {
imagePath: '/icons/building.svg', imagePath: '/icons/building.svg',
title: 'Des fondations en béton', title: t('feature2.title'),
text: 'Ma spécialité : l\'architecture. Je construis des solutions pérennes, évolutives et capables d\'absorber la croissance de votre produit dans le temps.', text: t('feature2.text'),
}, },
{ {
imagePath: '/icons/people.svg', imagePath: '/icons/people.svg',
title: 'L\'utilisateur au centre', title: t('feature3.title'),
text: 'Le code parfait n\'est rien si le produit ne répond pas à un besoin. Je place l\'expérience utilisateur et la fonctionnalité au cœur de mes développements.', text: t('feature3.text'),
}, },
]; ];
export default function FeatureSection() {
const sectionRef = useRef<HTMLElement>(null);
useEffect(() => { useEffect(() => {
const section = sectionRef.current; const section = sectionRef.current;
@@ -51,7 +52,7 @@ export default function FeatureSection() {
{/* Section label */} {/* Section label */}
<div className="reveal flex items-center gap-3 mb-12 md:mb-16"> <div className="reveal flex items-center gap-3 mb-12 md:mb-16">
<span className="w-8 h-px bg-brand-brown/40" /> <span className="w-8 h-px bg-brand-brown/40" />
<span className="font-sans text-[11px] uppercase tracking-[0.2em] text-brand-brown/45">Approche</span> <span className="font-sans text-[11px] uppercase tracking-[0.2em] text-brand-brown/45">{t('sectionLabel')}</span>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-12 md:gap-6 lg:gap-16"> <div className="grid grid-cols-1 md:grid-cols-3 gap-12 md:gap-6 lg:gap-16">
+7 -8
View File
@@ -1,9 +1,11 @@
'use client'; 'use client';
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { useTranslations } from "next-intl";
export default function HeroSection() { export default function HeroSection() {
const sectionRef = useRef<HTMLElement>(null); const sectionRef = useRef<HTMLElement>(null);
const t = useTranslations('hero');
useEffect(() => { useEffect(() => {
const section = sectionRef.current; const section = sectionRef.current;
@@ -36,7 +38,7 @@ export default function HeroSection() {
> >
<span className="inline-flex items-center gap-2 font-sans text-xs uppercase tracking-[0.2em] text-brand-beige/50"> <span className="inline-flex items-center gap-2 font-sans text-xs uppercase tracking-[0.2em] text-brand-beige/50">
<span className="w-6 h-px bg-brand-beige/30" /> <span className="w-6 h-px bg-brand-beige/30" />
Développeur Full Stack &amp; Architecte Web {t('sectionLabel')}
</span> </span>
</div> </div>
@@ -46,9 +48,7 @@ export default function HeroSection() {
style={{ opacity: 0, transform: 'translateY(28px)', transition: 'opacity 700ms cubic-bezier(0.22,1,0.36,1), transform 700ms cubic-bezier(0.22,1,0.36,1)' }} style={{ opacity: 0, transform: 'translateY(28px)', transition: 'opacity 700ms cubic-bezier(0.22,1,0.36,1), transform 700ms cubic-bezier(0.22,1,0.36,1)' }}
> >
<h1 className="font-serif leading-[0.95] text-[clamp(36px,5.5vw,88px)] text-brand-beige"> <h1 className="font-serif leading-[0.95] text-[clamp(36px,5.5vw,88px)] text-brand-beige">
Ensemble,<br /> {t('title')}
<em className="not-italic text-brand-beige/70">construisons</em><br />
ce qui dure.
</h1> </h1>
</div> </div>
@@ -58,8 +58,7 @@ export default function HeroSection() {
style={{ opacity: 0, transform: 'translateY(28px)', transition: 'opacity 700ms cubic-bezier(0.22,1,0.36,1), transform 700ms cubic-bezier(0.22,1,0.36,1)' }} style={{ opacity: 0, transform: 'translateY(28px)', transition: 'opacity 700ms cubic-bezier(0.22,1,0.36,1), transform 700ms cubic-bezier(0.22,1,0.36,1)' }}
> >
<p className="font-sans text-base font-light text-brand-beige/60 max-w-md leading-relaxed"> <p className="font-sans text-base font-light text-brand-beige/60 max-w-md leading-relaxed">
Je conçois des architectures robustes, scalables et pérennes {t('subtitle')}
des fondations qui supportent la croissance de votre produit dans le temps.
</p> </p>
</div> </div>
@@ -77,7 +76,7 @@ export default function HeroSection() {
}} }}
> >
<span className="relative"> <span className="relative">
Voir mes travaux {t('cta')}
<span className="absolute bottom-0 left-0 w-full h-px bg-brand-beige/40 origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-300" /> <span className="absolute bottom-0 left-0 w-full h-px bg-brand-beige/40 origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-300" />
</span> </span>
<svg className="w-4 h-4 group-hover:translate-x-1 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"> <svg className="w-4 h-4 group-hover:translate-x-1 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
@@ -135,7 +134,7 @@ export default function HeroSection() {
animationDelay: '1s' animationDelay: '1s'
}} }}
> >
<span className="font-sans text-[10px] uppercase tracking-[0.15em]">Défiler</span> <span className="font-sans text-[10px] uppercase tracking-[0.15em]">{t('scrollPrompt')}</span>
<svg <svg
className="w-4 h-4" className="w-4 h-4"
style={{ animation: 'arrowBounce 1.8s ease-in-out infinite' }} style={{ animation: 'arrowBounce 1.8s ease-in-out infinite' }}
+4 -2
View File
@@ -2,6 +2,7 @@
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { useTranslations } from "next-intl";
type props = { type props = {
locale: string; locale: string;
@@ -15,6 +16,7 @@ type props = {
export function OtherProjects({ locale, projects }: props) { export function OtherProjects({ locale, projects }: props) {
const ref = useRef<HTMLElement>(null); const ref = useRef<HTMLElement>(null);
const t = useTranslations('projectPage.otherProjects');
useEffect(() => { useEffect(() => {
const el = ref.current; const el = ref.current;
@@ -45,12 +47,12 @@ export function OtherProjects({ locale, projects }: props) {
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<span className="w-6 h-px bg-brand-brown/40" /> <span className="w-6 h-px bg-brand-brown/40" />
<span className="font-sans text-[10px] uppercase tracking-[0.2em] text-brand-brown/40"> <span className="font-sans text-[10px] uppercase tracking-[0.2em] text-brand-brown/40">
Autres projets {t('sectionLabel')}
</span> </span>
</div> </div>
<h2 className="reveal font-serif text-[clamp(48px,6vw,80px)] leading-none text-brand-brown mb-12"> <h2 className="reveal font-serif text-[clamp(48px,6vw,80px)] leading-none text-brand-brown mb-12">
Voir aussi {t('title')}
</h2> </h2>
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 md:grid-cols-2">
+5 -3
View File
@@ -2,6 +2,7 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { useTranslations } from "next-intl";
type props = { type props = {
title: string; title: string;
@@ -13,6 +14,7 @@ type props = {
export function ProjectHero({ title, description, dateLabel, locale, cover }: props) { export function ProjectHero({ title, description, dateLabel, locale, cover }: props) {
const ref = useRef<HTMLElement>(null); const ref = useRef<HTMLElement>(null);
const t = useTranslations('projectPage.hero');
useEffect(() => { useEffect(() => {
const section = ref.current; const section = ref.current;
@@ -46,7 +48,7 @@ export function ProjectHero({ title, description, dateLabel, locale, cover }: pr
)} )}
{/* Back link */} {/* Back link */}
<div className="relative z-10 px-8 md:px-14 lg:px-20 pt-10"> <div className="relative z-10 px-8 md:px-14 lg:px-20 pt-30">
<div <div
className="hero-reveal" className="hero-reveal"
style={{ opacity: 0, transform: 'translateY(20px)', transition: 'opacity 500ms ease, transform 500ms cubic-bezier(0.22,1,0.36,1)' }} style={{ opacity: 0, transform: 'translateY(20px)', transition: 'opacity 500ms ease, transform 500ms cubic-bezier(0.22,1,0.36,1)' }}
@@ -64,7 +66,7 @@ export function ProjectHero({ title, description, dateLabel, locale, cover }: pr
> >
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 16l-4-4m0 0l4-4m-4 4h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 16l-4-4m0 0l4-4m-4 4h18" />
</svg> </svg>
Retour aux projets {t('backToProjects')}
</Link> </Link>
</div> </div>
</div> </div>
@@ -107,7 +109,7 @@ export function ProjectHero({ title, description, dateLabel, locale, cover }: pr
{/* Scroll indicator */} {/* Scroll indicator */}
<div className="relative z-10 flex justify-center pb-8 text-brand-beige/30"> <div className="relative z-10 flex justify-center pb-8 text-brand-beige/30">
<div className="flex flex-col items-center gap-1.5"> <div className="flex flex-col items-center gap-1.5">
<span className="font-sans text-[10px] uppercase tracking-[0.15em]">Défiler</span> <span className="font-sans text-[10px] uppercase tracking-[0.15em]">{t('scrollPrompt')}</span>
<svg <svg
className="w-4 h-4" className="w-4 h-4"
style={{ animation: 'arrowBounce 1.8s ease-in-out infinite' }} style={{ animation: 'arrowBounce 1.8s ease-in-out infinite' }}
+6 -4
View File
@@ -1,6 +1,7 @@
'use client'; 'use client';
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
import { useTranslations } from "next-intl";
type props = { type props = {
dateLabel: string; dateLabel: string;
@@ -24,6 +25,7 @@ function MetaItem({ label, children }: { label: string; children: React.ReactNod
export function ProjectMeta({ dateLabel, category, roles, client }: props) { export function ProjectMeta({ dateLabel, category, roles, client }: props) {
const ref = useRef<HTMLElement>(null); const ref = useRef<HTMLElement>(null);
const t = useTranslations('projectPage.meta');
useEffect(() => { useEffect(() => {
const el = ref.current; const el = ref.current;
@@ -47,16 +49,16 @@ export function ProjectMeta({ dateLabel, category, roles, client }: props) {
<div className="w-full h-px bg-brand-brown/10 mb-10" /> <div className="w-full h-px bg-brand-brown/10 mb-10" />
<div className="grid grid-cols-2 gap-8 md:grid-cols-4 md:gap-12"> <div className="grid grid-cols-2 gap-8 md:grid-cols-4 md:gap-12">
<MetaItem label="Année">{dateLabel}</MetaItem> <MetaItem label={t('year')}>{dateLabel}</MetaItem>
<MetaItem label="Catégorie">{category}</MetaItem> <MetaItem label={t('category')}>{category}</MetaItem>
<MetaItem label="Rôles"> <MetaItem label={t('roles')}>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
{roles.map((role) => ( {roles.map((role) => (
<span key={role}>{role}</span> <span key={role}>{role}</span>
))} ))}
</div> </div>
</MetaItem> </MetaItem>
<MetaItem label="Client">{client}</MetaItem> <MetaItem label={t('client')}>{client}</MetaItem>
</div> </div>
{/* Bottom divider */} {/* Bottom divider */}
+3 -1
View File
@@ -1,6 +1,7 @@
'use client'; 'use client';
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
import { useTranslations } from "next-intl";
type props = { type props = {
technologies: string[]; technologies: string[];
@@ -8,6 +9,7 @@ type props = {
export function ProjectTechnologies({ technologies }: props) { export function ProjectTechnologies({ technologies }: props) {
const ref = useRef<HTMLElement>(null); const ref = useRef<HTMLElement>(null);
const t = useTranslations('projectPage.technologies');
useEffect(() => { useEffect(() => {
const el = ref.current; const el = ref.current;
@@ -32,7 +34,7 @@ export function ProjectTechnologies({ technologies }: props) {
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<span className="w-6 h-px bg-brand-brown/40" /> <span className="w-6 h-px bg-brand-brown/40" />
<span className="font-sans text-[10px] uppercase tracking-[0.2em] text-brand-brown/40"> <span className="font-sans text-[10px] uppercase tracking-[0.2em] text-brand-brown/40">
Technologies {t('sectionLabel')}
</span> </span>
</div> </div>
+5 -2
View File
@@ -1,6 +1,8 @@
'use client'; 'use client';
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
import { useTranslations } from "next-intl";
import { Project } from "@/lib/projects/types"; import { Project } from "@/lib/projects/types";
import { ProjectCard } from "../ui/ProjectCard"; import { ProjectCard } from "../ui/ProjectCard";
@@ -11,6 +13,7 @@ type props = {
export default function WorksSection({ projects, locale }: props) { export default function WorksSection({ projects, locale }: props) {
const sectionRef = useRef<HTMLElement>(null); const sectionRef = useRef<HTMLElement>(null);
const t = useTranslations('works');
useEffect(() => { useEffect(() => {
const section = sectionRef.current; const section = sectionRef.current;
@@ -39,11 +42,11 @@ export default function WorksSection({ projects, locale }: props) {
{/* Section label */} {/* Section label */}
<div className="reveal flex items-center gap-3 mb-4"> <div className="reveal flex items-center gap-3 mb-4">
<span className="w-8 h-px bg-brand-brown/40" /> <span className="w-8 h-px bg-brand-brown/40" />
<span className="font-sans text-[11px] uppercase tracking-[0.2em] text-brand-brown/45">Sélection de projets</span> <span className="font-sans text-[11px] uppercase tracking-[0.2em] text-brand-brown/45">{t('sectionLabel')}</span>
</div> </div>
<h2 className="reveal font-serif text-[clamp(56px,8vw,112px)] text-brand-brown leading-none mb-12 md:mb-16"> <h2 className="reveal font-serif text-[clamp(56px,8vw,112px)] text-brand-brown leading-none mb-12 md:mb-16">
Travaux {t('title')}
</h2> </h2>
<div className="w-full"> <div className="w-full">
-33
View File
@@ -1,33 +0,0 @@
type props = {
imagePath: string;
title: string;
text: string;
delay?: number;
};
export const FeatureColumn = ({ imagePath, title, text, delay = 0 }: props) => {
return (
<div
className="reveal flex flex-col items-center justify-start max-w-[320px] gap-6"
style={{ transitionDelay: `${delay}ms` }}
>
{/* Icon container */}
<div className="w-20 h-20 rounded-2xl bg-brand-brown/6 flex items-center justify-center group-hover:bg-brand-brown/10 transition-colors duration-300">
<img
className="w-10 h-10"
src={imagePath}
alt=""
aria-hidden="true"
/>
</div>
<div className="font-serif text-brand-brown text-center leading-[1.1] text-[clamp(1.5rem,2.5vw,1.75rem)]">
{title}
</div>
<div className="font-sans text-sm text-center text-brand-brown/65 leading-relaxed">
{text}
</div>
</div>
);
};
+4 -1
View File
@@ -2,6 +2,8 @@
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
import Link from "next/link"; import Link from "next/link";
import { useTranslations } from "next-intl";
import { Project } from "@/lib/projects/types"; import { Project } from "@/lib/projects/types";
type props = { type props = {
@@ -14,6 +16,7 @@ type props = {
export const ProjectCard = ({ project, locale, isTheLast = false, index = 0 }: props) => { export const ProjectCard = ({ project, locale, isTheLast = false, index = 0 }: props) => {
const num = String(index + 1).padStart(2, '0'); const num = String(index + 1).padStart(2, '0');
const cardRef = useRef<HTMLDivElement>(null); const cardRef = useRef<HTMLDivElement>(null);
const t = useTranslations('projectCard');
useEffect(() => { useEffect(() => {
const el = cardRef.current; const el = cardRef.current;
@@ -85,7 +88,7 @@ export const ProjectCard = ({ project, locale, isTheLast = false, index = 0 }: p
{/* CTA */} {/* CTA */}
<div className="flex items-center gap-2 font-sans text-xs uppercase tracking-[0.18em] text-brand-brown border-b border-brand-brown/30 pb-0.5 group-hover:border-brand-brown transition-colors duration-200"> <div className="flex items-center gap-2 font-sans text-xs uppercase tracking-[0.18em] text-brand-brown border-b border-brand-brown/30 pb-0.5 group-hover:border-brand-brown transition-colors duration-200">
<span>Lire le cas</span> <span>{t('cta')}</span>
<svg <svg
className="w-3.5 h-3.5 group-hover:translate-x-1.5 transition-transform duration-200" className="w-3.5 h-3.5 group-hover:translate-x-1.5 transition-transform duration-200"
fill="none" fill="none"
+3 -5
View File
@@ -7,11 +7,7 @@
}, },
"hero": { "hero": {
"sectionLabel": "Full Stack Developer & Web Architect", "sectionLabel": "Full Stack Developer & Web Architect",
"title": [ "title": "Together, let's build something that lasts.",
"Together,",
"let's build",
"something that lasts."
],
"subtitle": "I design robust, scalable, and sustainable architectures—foundations that support your products growth over time.", "subtitle": "I design robust, scalable, and sustainable architectures—foundations that support your products growth over time.",
"cta": "View my work", "cta": "View my work",
"scrollPrompt": "Scroll" "scrollPrompt": "Scroll"
@@ -55,6 +51,7 @@
} }
}, },
"footer": { "footer": {
"me": "Full Stack Developer & Web Architect",
"legal": { "legal": {
"title": "Legal", "title": "Legal",
"legalNotice": "Legal Notice", "legalNotice": "Legal Notice",
@@ -64,6 +61,7 @@
}, },
"projectPage": { "projectPage": {
"hero": { "hero": {
"backToProjects": "Back to projects",
"scrollPrompt": "Scroll" "scrollPrompt": "Scroll"
}, },
"meta": { "meta": {
+3 -5
View File
@@ -7,11 +7,7 @@
}, },
"hero": { "hero": {
"sectionLabel": "Développeur Full Stack & Architecte Web", "sectionLabel": "Développeur Full Stack & Architecte Web",
"title": [ "title": "Ensemble, construisons quelque chose de durable.",
"Ensemble,",
"construisons",
"ce qui dure."
],
"subtitle": "Je conçois des architectures robustes, scalables et pérennes — des fondations qui supportent la croissance de votre produit dans le temps.", "subtitle": "Je conçois des architectures robustes, scalables et pérennes — des fondations qui supportent la croissance de votre produit dans le temps.",
"cta": "Voir mes travaux", "cta": "Voir mes travaux",
"scrollPrompt": "Défiler" "scrollPrompt": "Défiler"
@@ -55,6 +51,7 @@
} }
}, },
"footer": { "footer": {
"me": "Développeur Full Stack & Architecte Web",
"legal": { "legal": {
"title": "Légal", "title": "Légal",
"legalNotice": "Mentions Légales", "legalNotice": "Mentions Légales",
@@ -64,6 +61,7 @@
}, },
"projectPage": { "projectPage": {
"hero": { "hero": {
"backToProjects": "Retour aux projets",
"scrollPrompt": "Défiler" "scrollPrompt": "Défiler"
}, },
"meta": { "meta": {