feat(i18n): routing and localization infrastructure (phase 1)

Implement Phase 1 of i18n spec:
- Configure next-intl with 6 locales (pt, en, es, fr, de, zh-CN) and pathnames
- Move routes to app/[locale] structure with localized layouts and metadata
- Add middleware for root language negotiation (307), legacy redirects (308), and 404 for non-pt book
- Implement language selector dropdown in header with cookie persistence
- Add system typography rules for zh-CN and word-break/hyphenation for de/fr

Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
Lucas RayanandAntigravity committed 2026-11-01 09:31:22 -03:00
1 parent e92aea0c84
commit 83c0ba3e74
38 files changed
+1699 -324

No files matched your search

+136
View File
@@ -0,0 +1,136 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import PageHero from "@/components/PageHero";
import SectionHead from "@/components/SectionHead";
import Link from "next/link";
import frameworksData from "@/public/frameworks.json";
import { ArrowRight, ArrowUpRight, Box, Brain, Cloud, GraduationCap, Layers, Mic, Music, Sparkles } from "lucide-react";
const GUIAS = [
{
icon: GraduationCap,
titulo: "Estou começando",
texto: "Arduino ou MicroPython são os caminhos mais rápidos para testar sensores e APIs. Para produto, vá de ESP-IDF ou Rust.",
links: [
["Arduino", "/frameworks/Arduino"],
["MicroPython", "/frameworks/MicroPython"],
["Rust", "/frameworks/Rust-ESP"],
],
},
{
icon: Cloud,
titulo: "Nuvem e casa conectada",
texto: "ESP-Matter para interoperabilidade, ESP-RainMaker para nuvem pronta e Zigbee para redes locais de baixo consumo.",
links: [
["RainMaker", "/frameworks/ESP-RainMaker"],
["Matter", "/frameworks/ESP-Matter"],
["Zigbee", "/frameworks/ESP-Zigbee-SDK"],
],
},
{
icon: Sparkles,
titulo: "Áudio, voz e IA",
texto: "ESP-ADF para áudio, ESP-SR para reconhecimento de fala offline e ESP-NN para acelerar TinyML.",
links: [
["Áudio", "/frameworks/ESP-ADF", Music],
["Voz", "/frameworks/ESP-SR", Mic],
["IA", "/frameworks/ESP-NN", Brain],
],
},
];
export default function Frameworks() {
const frameworks = Object.entries(frameworksData);
return (
<div className="site-canvas min-h-screen">
<Header />
<main id="conteudo">
<PageHero
trail={[{ label: "Frameworks e SDKs" }]}
icon={Layers}
label={`${frameworks.length} frameworks e SDKs`}
title="Frameworks e SDKs do ESP32"
lede="Do C/C++ nativo com FreeRTOS ao Rust, passando por nuvem IoT, reconhecimento de voz, áudio e Matter. Escolha pelo que você quer construir."
/>
<section className="shell pt-10 sm:pt-12" aria-label="Por onde começar">
<div className="grid gap-4 md:grid-cols-3">
{GUIAS.map(({ icon: Icon, titulo, texto, links }) => (
<div key={titulo} className="card flex flex-col p-5 sm:p-6">
<span className="grid h-10 w-10 place-items-center rounded-full bg-accent-soft text-accent">
<Icon className="h-5 w-5" aria-hidden="true" />
</span>
<h2 className="mt-4 font-display text-xl font-semibold tracking-[-0.03em] text-ink">{titulo}</h2>
<p className="mt-2 flex-1 text-sm leading-relaxed text-muted">{texto}</p>
<ul className="mt-5 flex flex-wrap gap-2 border-t border-line pt-4">
{links.map(([rotulo, href, Extra]) => (
<li key={rotulo}>
<Link href={href} className="chip-btn">
{Extra && <Extra className="h-4 w-4" aria-hidden="true" />}
{rotulo}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</section>
<section className="shell mt-10">
<div className="panel flex flex-col items-start justify-between gap-6 p-7 sm:p-10 md:flex-row md:items-center">
<div className="max-w-2xl">
<span className="chip chip-accent">
<Box className="h-3.5 w-3.5" aria-hidden="true" />
Arquitetura modular
</span>
<h2 className="section-title mt-4">ESP Component Registry</h2>
<p className="mt-3 text-base leading-relaxed text-muted">
Instale drivers de display (LVGL), pilhas industriais (Modbus), recursos de nuvem e pacotes de placa
(BSPs) com uma linha no manifesto <code className="rounded bg-surface-2 px-1.5 py-0.5 font-mono text-sm">idf_component.yml</code>.
</p>
</div>
<Link href="/componentes" className="btn btn-primary shrink-0">
Explorar componentes
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</section>
<section className="shell section" aria-labelledby="todos-titulo">
<SectionHead id="todos-titulo" eyebrow="Todos" title="Todos os frameworks" lede="Cada ficha traz recursos, aplicações ideais, exemplo de código e links oficiais." />
<div className="mt-8 grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{frameworks.map(([key, framework]) => (
<Link key={key} href={`/frameworks/${encodeURIComponent(key)}`} className="card card-interactive group flex flex-col p-5 sm:p-6">
<div className="flex flex-wrap items-start justify-between gap-2">
<h3 className="font-display text-xl font-semibold tracking-[-0.03em] text-ink">{framework.nome}</h3>
<span className="chip max-w-full">{framework.linguagem}</span>
</div>
<p className="mt-1 text-sm text-muted">{framework.tipo}</p>
<p className="mt-3 line-clamp-3 text-sm leading-relaxed text-muted">{framework.descricao}</p>
<ul className="mt-5 flex flex-1 flex-wrap content-start gap-1.5">
{framework.casos_uso.slice(0, 3).map((caso) => (
<li key={caso} className="chip max-w-full">
<span className="truncate">{caso}</span>
</li>
))}
</ul>
<span className="mt-5 inline-flex items-center gap-1 border-t border-line pt-4 text-sm font-medium text-accent">
Ver detalhes e exemplo
<ArrowUpRight className="h-4 w-4 transition-transform group-hover:-translate-y-0.5 group-hover:translate-x-0.5" aria-hidden="true" />
</span>
</Link>
))}
</div>
</section>
</main>
<Footer />
</div>
);
}