feat(i18n): Spanish localization (phase 4)
CI / Lint e auditorias (push) Successful in 1m31s
CI / Trivy (dependências e segredos) (push) Successful in 10s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m23s

Complete implementation of Spanish (es-419) language support:
- Canonical catalogs and data: messages/es.json, data/es/**, lib/data/es/**, public/exemplos/es/**
- Canonical slugs: /es/series, /es/catalogo, /es/comparacion, /es/componentes, /es/diagnostico, /es/frameworks, /es/glosario, /es/selector, /es/acerca
- Dedicated search index at /api/busca/es with Spanish accent normalization
- Strict exclusion of book references in Spanish routes and catalogs
- Language selector and root negotiation with es active (ENABLED_LOCALES = ['pt', 'en', 'es'])
- Extended verification suite: check-i18n, smoke-imagem and verifica-vazamentos

Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
Lucas RayanandAntigravity committed 2026-11-01 19:50:20 -03:00
1 parent 0940814522
commit 660b28c09d
72 files changed
+17848 -947

No files matched your search

+45 -7
View File
@@ -67,6 +67,35 @@ const COLUMNS_EN = [
},
];
const COLUMNS_ES = [
{
title: "Explorar",
links: [
{ href: "/series", label: "Series ESP32" },
{ href: "/comparacao", label: "Comparador" },
{ href: "/catalogo", label: "Catálogo de placas" },
],
},
{
title: "Herramientas",
links: [
{ href: "/seletor", label: "Selector inteligente" },
{ href: "/diagnostico", label: "Diagnóstico" },
{ href: "/componentes", label: "Component Registry" },
{ href: "/frameworks", label: "Frameworks y SDKs" },
],
},
{
title: "Proyecto",
links: [
{ href: "/sobre", label: "Acerca de ESPDocs" },
{ href: "/glossario", label: "Glosario" },
{ href: SITE.repositorio, label: "Repositorio", external: true },
{ href: `${SITE.repositorio}/issues`, label: "Reportar un problema", external: true },
],
},
];
export default function Footer() {
const pathname = usePathname() || "";
const year = new Date().getFullYear();
@@ -74,6 +103,8 @@ export default function Footer() {
const currentLocale =
LOCALES.find((l) => pathname === `/${l.code}` || pathname.startsWith(`/${l.code}/`))?.code || "pt";
const isPt = currentLocale === "pt";
const isEn = currentLocale === "en";
const isEs = currentLocale === "es";
const getLinkHref = (href) => getLocalizedHref(`/${currentLocale}${href}`, currentLocale);
@@ -82,14 +113,16 @@ export default function Footer() {
...column,
links: column.links.filter((link) => link.href !== "/livro" || isPt),
}))
: isEs
? COLUMNS_ES
: COLUMNS_EN;
return (
<footer className="mt-20 border-t border-line md:mt-28" aria-label={isPt ? "Rodapé do ESPDocs" : "ESPDocs footer"}>
<footer className="mt-20 border-t border-line md:mt-28" aria-label={isPt ? "Rodapé do ESPDocs" : isEs ? "Pie de página de ESPDocs" : "ESPDocs footer"}>
<div className="shell py-14 md:py-20">
<div className="grid gap-12 lg:grid-cols-[1.1fr_2fr]">
<div>
<Link href={getLinkHref("/")} className="inline-flex min-h-11 items-center gap-2.5" aria-label={isPt ? "ESPDocs, página inicial" : "ESPDocs, homepage"}>
<Link href={getLinkHref("/")} className="inline-flex min-h-11 items-center gap-2.5" aria-label={isPt ? "ESPDocs, página inicial" : isEs ? "ESPDocs, página de inicio" : "ESPDocs, homepage"}>
<Logo className="h-7 w-7" />
<span className="font-display text-2xl font-semibold tracking-[-0.045em] text-ink">ESPDocs</span>
</Link>
@@ -99,6 +132,11 @@ export default function Footer() {
Documentação independente do ecossistema ESP32, escrita em português brasileiro.
Dados públicos, curadoria editorial e links para as fontes oficiais.
</>
) : isEs ? (
<>
Documentación independiente del ecosistema ESP32.
Datos públicos, curaduría editorial y enlaces a las fuentes oficiales.
</>
) : (
<>
Independent documentation of the ESP32 ecosystem.
@@ -173,7 +211,7 @@ export default function Footer() {
))}
<div>
<h2 className="text-sm font-semibold text-ink">{isPt ? "Autor" : "Author"}</h2>
<h2 className="text-sm font-semibold text-ink">{isPt ? "Autor" : isEs ? "Autor" : "Author"}</h2>
<p className="mt-4 text-sm leading-relaxed text-muted">
Lucas Rayan Guerra
<br />
@@ -184,7 +222,7 @@ export default function Footer() {
href={SITE.repositorio}
target="_blank"
rel="noopener noreferrer"
aria-label={isPt ? "Gitea do projeto (abre em nova aba)" : "Project Gitea (opens in new tab)"}
aria-label={isPt ? "Gitea do projeto (abre em nova aba)" : isEs ? "Gitea del proyecto (abre en una pestaña nueva)" : "Project Gitea (opens in new tab)"}
className="grid h-11 w-11 place-items-center rounded-full border border-line text-ink transition-colors hover:border-line-strong hover:bg-surface"
>
<GiteaMark className="h-4 w-4" />
@@ -193,7 +231,7 @@ export default function Footer() {
href={SITE.autorLinkedin}
target="_blank"
rel="noopener noreferrer"
aria-label={isPt ? "LinkedIn do autor (abre em nova aba)" : "Author LinkedIn (opens in new tab)"}
aria-label={isPt ? "LinkedIn do autor (abre em nova aba)" : isEs ? "LinkedIn del autor (abre en una pestaña nueva)" : "Author LinkedIn (opens in new tab)"}
className="grid h-11 w-11 place-items-center rounded-full border border-line text-ink transition-colors hover:border-line-strong hover:bg-surface"
>
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
@@ -206,7 +244,7 @@ export default function Footer() {
</div>
<div className="mt-14 flex flex-col gap-4 border-t border-line pt-6 text-xs leading-relaxed text-faint md:flex-row md:items-center md:justify-between">
<p>© {year} {isPt ? "ESPDocs. Projeto independente, sem afiliação com a Espressif Systems." : "ESPDocs. Independent project, not affiliated with Espressif Systems."}</p>
<p>© {year} {isPt ? "ESPDocs. Projeto independente, sem afiliação com a Espressif Systems." : isEs ? "ESPDocs. Proyecto independiente, sin afiliación con Espressif Systems." : "ESPDocs. Independent project, not affiliated with Espressif Systems."}</p>
<p className="flex flex-wrap items-center gap-x-5 gap-y-2">
<a href={SITE.blog} target="_blank" rel="noopener noreferrer" className="inline-flex min-h-11 items-center gap-1 transition-colors hover:text-accent">
Ciência Embarcada
@@ -216,7 +254,7 @@ export default function Footer() {
lucasrguerra.dev.br
<ArrowUpRight className="h-3 w-3" aria-hidden="true" />
</a>
<span>{isPt ? "Fontes oficiais: Espressif Systems." : "Official sources: Espressif Systems."}</span>
<span>{isPt ? "Fontes oficiais: Espressif Systems." : isEs ? "Fuentes oficiales: Espressif Systems." : "Official sources: Espressif Systems."}</span>
</p>
</div>
</div>