apply translations in all pages
This commit is contained in:
@@ -1,36 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { useRef, useEffect, useState } from 'react';
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: 'Les Débuts',
|
||||
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.",
|
||||
year: '2019',
|
||||
position: 'top' as const,
|
||||
left: '16%',
|
||||
},
|
||||
{
|
||||
title: 'L\'Évolution',
|
||||
text: "Après un bac STI2D avec mention Très bien j'ai entamé un BUT Informatique Parcours Développement IA.",
|
||||
year: '2022',
|
||||
position: 'bottom' as const,
|
||||
left: '50%',
|
||||
},
|
||||
{
|
||||
title: 'Aujourd\'hui',
|
||||
text: "Je suis entrepreneur, co-fondateur d'une startup et développeur freelance, je vis de ma passion.",
|
||||
year: '2026',
|
||||
position: 'top' as const,
|
||||
left: '84%',
|
||||
},
|
||||
];
|
||||
|
||||
const lineTop = '58%';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function TimelineSection() {
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const t = useTranslations('about');
|
||||
|
||||
const lineTop = '58%';
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: t('step1.title'),
|
||||
text: t('step1.text'),
|
||||
year: '2019',
|
||||
position: 'top' as const,
|
||||
left: '16%',
|
||||
},
|
||||
{
|
||||
title: t('step2.title'),
|
||||
text: t('step2.text'),
|
||||
year: '2022',
|
||||
position: 'bottom' as const,
|
||||
left: '50%',
|
||||
},
|
||||
{
|
||||
title: t('step3.title'),
|
||||
text: t('step3.text'),
|
||||
year: '2026',
|
||||
position: 'top' as const,
|
||||
left: '84%',
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const section = sectionRef.current;
|
||||
@@ -56,11 +58,11 @@ export default function TimelineSection() {
|
||||
{/* Label */}
|
||||
<div className="reveal flex items-center gap-3 mb-6">
|
||||
<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>
|
||||
|
||||
<h2 className="reveal font-serif text-[clamp(48px,7vw,100px)] leading-none text-brand-brown mb-16 md:mb-24">
|
||||
À propos
|
||||
{t('title')}
|
||||
</h2>
|
||||
|
||||
{/* Mobile layout */}
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { FeatureColumn } from '@/components/ui/FeatureColumn';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const features = [
|
||||
{
|
||||
imagePath: '/icons/brain.svg',
|
||||
title: 'Plus qu\'un simple exécutant',
|
||||
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.',
|
||||
},
|
||||
{
|
||||
imagePath: '/icons/building.svg',
|
||||
title: 'Des fondations en béton',
|
||||
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.',
|
||||
},
|
||||
{
|
||||
imagePath: '/icons/people.svg',
|
||||
title: 'L\'utilisateur au centre',
|
||||
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.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function FeatureSection() {
|
||||
export default function ApproachSection() {
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('approach');
|
||||
|
||||
const features = [
|
||||
{
|
||||
imagePath: '/icons/brain.svg',
|
||||
title: t('feature1.title'),
|
||||
text: t('feature1.text'),
|
||||
},
|
||||
{
|
||||
imagePath: '/icons/building.svg',
|
||||
title: t('feature2.title'),
|
||||
text: t('feature2.text'),
|
||||
},
|
||||
{
|
||||
imagePath: '/icons/people.svg',
|
||||
title: t('feature3.title'),
|
||||
text: t('feature3.text'),
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const section = sectionRef.current;
|
||||
@@ -51,7 +52,7 @@ export default function FeatureSection() {
|
||||
{/* Section label */}
|
||||
<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="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 className="grid grid-cols-1 md:grid-cols-3 gap-12 md:gap-6 lg:gap-16">
|
||||
@@ -1,9 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function HeroSection() {
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('hero');
|
||||
|
||||
useEffect(() => {
|
||||
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="w-6 h-px bg-brand-beige/30" />
|
||||
Développeur Full Stack & Architecte Web
|
||||
{t('sectionLabel')}
|
||||
</span>
|
||||
</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)' }}
|
||||
>
|
||||
<h1 className="font-serif leading-[0.95] text-[clamp(36px,5.5vw,88px)] text-brand-beige">
|
||||
Ensemble,<br />
|
||||
<em className="not-italic text-brand-beige/70">construisons</em><br />
|
||||
ce qui dure.
|
||||
{t('title')}
|
||||
</h1>
|
||||
</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)' }}
|
||||
>
|
||||
<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 —
|
||||
des fondations qui supportent la croissance de votre produit dans le temps.
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -77,7 +76,7 @@ export default function HeroSection() {
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
<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'
|
||||
}}
|
||||
>
|
||||
<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
|
||||
className="w-4 h-4"
|
||||
style={{ animation: 'arrowBounce 1.8s ease-in-out infinite' }}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type props = {
|
||||
locale: string;
|
||||
@@ -15,6 +16,7 @@ type props = {
|
||||
|
||||
export function OtherProjects({ locale, projects }: props) {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('projectPage.otherProjects');
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
@@ -45,12 +47,12 @@ export function OtherProjects({ locale, projects }: props) {
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<span className="w-6 h-px bg-brand-brown/40" />
|
||||
<span className="font-sans text-[10px] uppercase tracking-[0.2em] text-brand-brown/40">
|
||||
Autres projets
|
||||
{t('sectionLabel')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2 className="reveal font-serif text-[clamp(48px,6vw,80px)] leading-none text-brand-brown mb-12">
|
||||
Voir aussi
|
||||
{t('title')}
|
||||
</h2>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type props = {
|
||||
title: string;
|
||||
@@ -13,6 +14,7 @@ type props = {
|
||||
|
||||
export function ProjectHero({ title, description, dateLabel, locale, cover }: props) {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('projectPage.hero');
|
||||
|
||||
useEffect(() => {
|
||||
const section = ref.current;
|
||||
@@ -46,7 +48,7 @@ export function ProjectHero({ title, description, dateLabel, locale, cover }: pr
|
||||
)}
|
||||
|
||||
{/* 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
|
||||
className="hero-reveal"
|
||||
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" />
|
||||
</svg>
|
||||
Retour aux projets
|
||||
{t('backToProjects')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,7 +109,7 @@ export function ProjectHero({ title, description, dateLabel, locale, cover }: pr
|
||||
{/* Scroll indicator */}
|
||||
<div className="relative z-10 flex justify-center pb-8 text-brand-beige/30">
|
||||
<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
|
||||
className="w-4 h-4"
|
||||
style={{ animation: 'arrowBounce 1.8s ease-in-out infinite' }}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type props = {
|
||||
dateLabel: string;
|
||||
@@ -24,6 +25,7 @@ function MetaItem({ label, children }: { label: string; children: React.ReactNod
|
||||
|
||||
export function ProjectMeta({ dateLabel, category, roles, client }: props) {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('projectPage.meta');
|
||||
|
||||
useEffect(() => {
|
||||
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="grid grid-cols-2 gap-8 md:grid-cols-4 md:gap-12">
|
||||
<MetaItem label="Année">{dateLabel}</MetaItem>
|
||||
<MetaItem label="Catégorie">{category}</MetaItem>
|
||||
<MetaItem label="Rôles">
|
||||
<MetaItem label={t('year')}>{dateLabel}</MetaItem>
|
||||
<MetaItem label={t('category')}>{category}</MetaItem>
|
||||
<MetaItem label={t('roles')}>
|
||||
<div className="flex flex-col gap-1">
|
||||
{roles.map((role) => (
|
||||
<span key={role}>{role}</span>
|
||||
))}
|
||||
</div>
|
||||
</MetaItem>
|
||||
<MetaItem label="Client">{client}</MetaItem>
|
||||
<MetaItem label={t('client')}>{client}</MetaItem>
|
||||
</div>
|
||||
|
||||
{/* Bottom divider */}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type props = {
|
||||
technologies: string[];
|
||||
@@ -8,6 +9,7 @@ type props = {
|
||||
|
||||
export function ProjectTechnologies({ technologies }: props) {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('projectPage.technologies');
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
@@ -32,7 +34,7 @@ export function ProjectTechnologies({ technologies }: props) {
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<span className="w-6 h-px bg-brand-brown/40" />
|
||||
<span className="font-sans text-[10px] uppercase tracking-[0.2em] text-brand-brown/40">
|
||||
Technologies
|
||||
{t('sectionLabel')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import { Project } from "@/lib/projects/types";
|
||||
import { ProjectCard } from "../ui/ProjectCard";
|
||||
|
||||
@@ -11,6 +13,7 @@ type props = {
|
||||
|
||||
export default function WorksSection({ projects, locale }: props) {
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
const t = useTranslations('works');
|
||||
|
||||
useEffect(() => {
|
||||
const section = sectionRef.current;
|
||||
@@ -39,11 +42,11 @@ export default function WorksSection({ projects, locale }: props) {
|
||||
{/* Section label */}
|
||||
<div className="reveal flex items-center gap-3 mb-4">
|
||||
<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>
|
||||
|
||||
<h2 className="reveal font-serif text-[clamp(56px,8vw,112px)] text-brand-brown leading-none mb-12 md:mb-16">
|
||||
Travaux
|
||||
{t('title')}
|
||||
</h2>
|
||||
|
||||
<div className="w-full">
|
||||
|
||||
Reference in New Issue
Block a user