Skip to content

Commit db8c3ce

Browse files
committed
Add bilingual
1 parent 4d6d07c commit db8c3ce

File tree

9 files changed

+246
-68
lines changed

9 files changed

+246
-68
lines changed

app/[lang]/page.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { About } from '@/components/sections/about';
2+
import { Architecture } from '@/components/sections/architecture';
3+
import { Contact } from '@/components/sections/contact';
4+
import { Hero } from '@/components/sections/hero';
5+
import { Projects } from '@/components/sections/projects';
6+
import { Navbar } from '@/components/sections/navbar';
7+
import { getDictionary, type Locale } from '@/lib/i18n';
8+
import { notFound } from 'next/navigation';
9+
10+
type Params = { lang: Locale };
11+
12+
export function generateStaticParams() {
13+
return [{ lang: 'es' }, { lang: 'en' }];
14+
}
15+
16+
export default function LangPage({ params }: { params: Params }) {
17+
if (params.lang !== 'es' && params.lang !== 'en') {
18+
return notFound();
19+
}
20+
const dict = getDictionary(params.lang);
21+
return (
22+
<main className="min-h-screen">
23+
<Navbar content={dict.nav} />
24+
<div className="bg-page-gradient">
25+
<Hero content={dict.hero} />
26+
<About content={dict.about} />
27+
<Projects content={dict.projects} />
28+
<Architecture content={dict.architecture} />
29+
<Contact content={dict.contact} />
30+
</div>
31+
</main>
32+
);
33+
}

app/page.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ import { Architecture } from '@/components/sections/architecture';
33
import { Contact } from '@/components/sections/contact';
44
import { Hero } from '@/components/sections/hero';
55
import { Projects } from '@/components/sections/projects';
6+
import { Navbar } from '@/components/sections/navbar';
7+
import { getDictionary } from '@/lib/i18n';
68

79
export default function HomePage() {
10+
const dict = getDictionary('es');
811
return (
912
<main className="min-h-screen">
10-
<Navbar />
11-
<div className="bg-page-gradient bg-grid">
12-
<Hero />
13-
<About />
14-
<Projects />
15-
<Architecture />
16-
<Contact />
13+
<Navbar content={dict.nav} />
14+
<div className="bg-page-gradient">
15+
<Hero content={dict.hero} />
16+
<About content={dict.about} />
17+
<Projects content={dict.projects} />
18+
<Architecture content={dict.architecture} />
19+
<Contact content={dict.contact} />
1720
</div>
1821
</main>
1922
);
2023
}
21-
import { Navbar } from '@/components/sections/navbar';

components/sections/about.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
import { Badge } from '@/components/ui/badge';
22
import { getExperienceYears } from '@/lib/data/experience';
3+
import type { AboutContent } from '@/lib/i18n';
34

45
const coreStack = ['React', 'React Native', 'Node.js', 'NestJS', 'Express', 'PostgreSQL', 'GraphQL'];
56

6-
export function About() {
7+
type Props = {
8+
content: AboutContent;
9+
};
10+
11+
export function About({ content }: Props) {
712
const years = getExperienceYears();
13+
const intro = content.intro.replace('{years}', years.toString());
814
return (
915
<section id="about" className="py-16 md:py-20 bg-gradient-to-b from-base-surface/40 to-transparent">
1016
<div className="max-w-5xl mx-auto px-4 grid gap-10 md:grid-cols-[1.2fr_0.8fr] items-center">
1117
<div className="space-y-4">
12-
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">Sobre mí</p>
13-
<h2 className="text-3xl font-semibold text-base-text font-display">Hola, soy Jedabero</h2>
14-
<p className="text-base text-base-muted">
15-
Ingeniero de Sistemas con {years}+ años construyendo productos web y móviles. Me enfoco en arquitecturas
16-
ligeras, DX y performance en React/React Native con backends en Express, Next.js o NestJS sobre PostgreSQL.
17-
</p>
18-
<p className="text-base text-base-muted">
19-
Trabajo con equipos distribuidos para entregar features sostenibles, con medición continua y buenas
20-
prácticas listas para escalar.
21-
</p>
18+
{content.eyebrow ? (
19+
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">{content.eyebrow}</p>
20+
) : null}
21+
<h2 className="text-3xl font-semibold text-base-text font-display">{content.title}</h2>
22+
<p className="text-base text-base-muted">{intro}</p>
23+
<p className="text-base text-base-muted">{content.detail}</p>
2224
</div>
2325
<div className="flex flex-wrap gap-2">
2426
{coreStack.map((item) => (

components/sections/architecture.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DiagramArrow, DiagramBlock } from '@/lib/diagrams/blocks';
2+
import type { SectionContent } from '@/lib/i18n';
23

34
const diagrams = [
45
{
@@ -13,17 +14,18 @@ const diagrams = [
1314
}
1415
];
1516

16-
export function Architecture() {
17+
type Props = {
18+
content: SectionContent;
19+
};
20+
21+
export function Architecture({ content }: Props) {
1722
return (
1823
<section id="architecture" className="py-16 md:py-20 bg-base-surface/30">
1924
<div className="max-w-6xl mx-auto px-4 space-y-8">
2025
<div className="space-y-2">
21-
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">Arquitectura</p>
22-
<h2 className="text-3xl font-semibold text-base-text font-display">Diagramas ligeros</h2>
23-
<p className="text-base text-base-muted max-w-3xl">
24-
Referencias rápidas de cómo estructuro productos: BFFs en Node/GraphQL o Next.js, data en Postgres y
25-
caching donde aporta más. Sin assets sensibles; solo bloques conceptuales.
26-
</p>
26+
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">{content.eyebrow ?? ''}</p>
27+
<h2 className="text-3xl font-semibold text-base-text font-display">{content.title}</h2>
28+
<p className="text-base text-base-muted max-w-3xl">{content.description}</p>
2729
</div>
2830
<div className="grid gap-6 md:grid-cols-2">
2931
{diagrams.map((item) => (

components/sections/contact.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { buttonVariants } from '@/components/ui/button';
2+
import type { ContactContent } from '@/lib/i18n';
23
import { cn } from '@/lib/utils';
34
import { Github, Linkedin, Mail } from 'lucide-react';
45

@@ -8,16 +9,18 @@ const socials = [
89
{ name: 'Email', href: 'mailto:[email protected]', icon: Mail }
910
];
1011

11-
export function Contact() {
12+
type Props = {
13+
content: ContactContent;
14+
};
15+
16+
export function Contact({ content }: Props) {
1217
return (
1318
<section id="contact" className="py-16 md:py-20">
1419
<div className="max-w-5xl mx-auto px-4 space-y-6">
1520
<div className="space-y-2">
16-
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">Contacto</p>
17-
<h2 className="text-3xl font-semibold text-base-text font-display">Hablemos</h2>
18-
<p className="text-base text-base-muted max-w-3xl">
19-
Casos de estudio privados. Si quieres ver detalles o hablar de un reto, escríbeme o conecta por redes.
20-
</p>
21+
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">{content.eyebrow ?? ''}</p>
22+
<h2 className="text-3xl font-semibold text-base-text font-display">{content.title}</h2>
23+
<p className="text-base text-base-muted max-w-3xl">{content.description}</p>
2124
</div>
2225
<div className="flex flex-wrap gap-3">
2326
{socials.map((item) => {

components/sections/hero.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
import { Button } from '@/components/ui/button';
2+
import type { SectionContent } from '@/lib/i18n';
23
import Link from 'next/link';
34

4-
export function Hero() {
5+
type Props = {
6+
content: SectionContent;
7+
};
8+
9+
export function Hero({ content }: Props) {
510
return (
611
<section id="hero" className="py-16 md:py-20">
712
<div className="max-w-5xl mx-auto px-4 flex flex-col gap-6">
8-
<div className="inline-flex items-center gap-2 text-xs uppercase tracking-[0.2em] text-base-muted">
9-
<span className="h-2 w-2 rounded-full bg-accent-mint" aria-hidden />
10-
Disponible para colaborar en productos web y móviles
11-
</div>
12-
<h1 className="text-4xl md:text-5xl font-semibold font-display text-base-text">
13-
Full Stack Engineer enfocado en React, React Native, Node/Nest/Express y PostgreSQL.
14-
</h1>
15-
<p className="text-lg text-base-muted max-w-3xl">
16-
Diseño arquitecturas ligeras, optimizo performance y acelero equipos con buenas prácticas. Casos de estudio
17-
privados con foco en logística, salud, medios y educación.
18-
</p>
13+
{content.eyebrow ? (
14+
<div className="inline-flex items-center gap-2 text-xs uppercase tracking-[0.2em] text-base-muted">
15+
<span className="h-2 w-2 rounded-full bg-accent-mint" aria-hidden />
16+
{content.eyebrow}
17+
</div>
18+
) : null}
19+
<h1 className="text-4xl md:text-5xl font-semibold font-display text-base-text">{content.title}</h1>
20+
{content.description ? <p className="text-lg text-base-muted max-w-3xl">{content.description}</p> : null}
1921
<div className="flex flex-wrap gap-4">
2022
<Link href="#contact" className="inline-flex">
21-
<Button size="lg">Hablemos</Button>
22-
</Link>
23-
<Link href="/cv.pdf" className="inline-flex">
24-
<Button variant="outline" size="lg">
25-
Descargar CV
26-
</Button>
23+
<Button size="lg">{content.ctaPrimary}</Button>
2724
</Link>
25+
{content.ctaSecondary ? (
26+
<Link href="/cv.pdf" className="inline-flex">
27+
<Button variant="outline" size="lg">
28+
{content.ctaSecondary}
29+
</Button>
30+
</Link>
31+
) : null}
2832
</div>
2933
</div>
3034
</section>

components/sections/navbar.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import Link from 'next/link';
2+
import type { NavContent } from '@/lib/i18n';
23

3-
const links = [
4-
{ href: '#projects', label: 'Proyectos' },
5-
{ href: '#architecture', label: 'Arquitectura' },
6-
{ href: '#contact', label: 'Contacto' }
7-
];
4+
type Props = {
5+
content: NavContent;
6+
};
87

9-
export function Navbar() {
8+
export function Navbar({ content }: Props) {
109
return (
1110
<header className="sticky top-0 z-20 bg-base-bg/70 backdrop-blur-md border-b border-white/10">
1211
<div className="max-w-6xl mx-auto px-4 h-14 flex items-center justify-between">
1312
<Link href="#hero" className="text-base-text font-semibold font-display">
14-
Jedabero
13+
{content.brand}
1514
</Link>
1615
<nav className="flex items-center gap-4 text-sm text-base-muted">
17-
{links.map((link) => (
16+
{content.links.map((link) => (
1817
<Link key={link.href} href={link.href} className="hover:text-base-text transition-colors">
1918
{link.label}
2019
</Link>

components/sections/projects.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ import { Badge } from '@/components/ui/badge';
33
import { Button } from '@/components/ui/button';
44
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
55
import { projects } from '@/lib/data/projects';
6+
import type { ProjectsContent } from '@/lib/i18n';
67
import Link from 'next/link';
78

8-
export function Projects() {
9+
type Props = {
10+
content: ProjectsContent;
11+
};
12+
13+
export function Projects({ content }: Props) {
914
return (
1015
<section id="projects" className="py-16 md:py-20">
1116
<div className="max-w-6xl mx-auto px-4 space-y-8">
1217
<div className="space-y-2">
13-
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">Proyectos</p>
14-
<h2 className="text-3xl font-semibold text-base-text font-display">Casos de estudio privados</h2>
15-
<p className="text-base text-base-muted max-w-3xl">
16-
Trabajo en logística, salud, medios, educación y operaciones internas. Los detalles viven en casos de
17-
estudio sin enlaces públicos; usa los tabs para ver frontend, backend e impacto.
18-
</p>
18+
<p className="text-sm uppercase tracking-[0.2em] text-base-muted">{content.eyebrow ?? ''}</p>
19+
<h2 className="text-3xl font-semibold text-base-text font-display">{content.title}</h2>
20+
<p className="text-base text-base-muted max-w-3xl">{content.description}</p>
1921
</div>
2022
<div className="grid gap-6 grid-cols-1 md:grid-cols-2 xl:grid-cols-3">
2123
{projects.map((project) => (
@@ -44,9 +46,7 @@ export function Projects() {
4446
</div>
4547
<div className="pt-2">
4648
<Link href={`/projects/${project.slug}`} className="inline-flex">
47-
<Button variant="ghost" size="md">
48-
Ver caso de estudio
49-
</Button>
49+
<Button variant="ghost" size="md">{content.cta}</Button>
5050
</Link>
5151
</div>
5252
</CardContent>

0 commit comments

Comments
 (0)