feat(i18n): English localization (phase 3)
CI / Lint e auditorias (push) Successful in 1m51s
CI / Trivy (dependências e segredos) (push) Successful in 15s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m41s

- Translate content to English across messages/en.json, data/en, lib/data/en, and public/exemplos/en
- Implement localized routing, slugs, and language negotiation with fallback to en
- Build dedicated search index for English with strict book exclusion
- Update components and pages to dynamically render localized strings and datasets
- Verify full structural parity and exact 1:1 match on Portuguese pages via comparator

Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
Lucas RayanandAntigravity committed 2026-11-01 14:17:13 -03:00
1 parent 22b1f26aed
commit c1bd2c7e11
61 files changed
+18182 -1225

No files matched your search

+54 -14
View File
@@ -8,7 +8,7 @@ import { ArrowUpRight } from "lucide-react";
import { SITE } from "@/lib/seo";
import { LOCALES, getLocalizedHref } from "@/i18n/links";
const COLUMNS = [
const COLUMNS_PT = [
{
title: "Explorar",
links: [
@@ -38,6 +38,35 @@ const COLUMNS = [
},
];
const COLUMNS_EN = [
{
title: "Explore",
links: [
{ href: "/series", label: "ESP32 Series" },
{ href: "/comparacao", label: "Comparator" },
{ href: "/catalogo", label: "Board Catalog" },
],
},
{
title: "Tools",
links: [
{ href: "/seletor", label: "Smart Selector" },
{ href: "/diagnostico", label: "Diagnostics" },
{ href: "/componentes", label: "Component Registry" },
{ href: "/frameworks", label: "Frameworks & SDKs" },
],
},
{
title: "Project",
links: [
{ href: "/sobre", label: "About ESPDocs" },
{ href: "/glossario", label: "Glossary" },
{ href: SITE.repositorio, label: "Repository", external: true },
{ href: `${SITE.repositorio}/issues`, label: "Report an issue", external: true },
],
},
];
export default function Footer() {
const pathname = usePathname() || "";
const year = new Date().getFullYear();
@@ -48,23 +77,34 @@ export default function Footer() {
const getLinkHref = (href) => getLocalizedHref(`/${currentLocale}${href}`, currentLocale);
const columns = COLUMNS.map((column) => ({
...column,
links: column.links.filter((link) => link.href !== "/livro" || isPt),
}));
const columns = isPt
? COLUMNS_PT.map((column) => ({
...column,
links: column.links.filter((link) => link.href !== "/livro" || isPt),
}))
: COLUMNS_EN;
return (
<footer className="mt-20 border-t border-line md:mt-28" aria-label="Rodapé do ESPDocs">
<footer className="mt-20 border-t border-line md:mt-28" aria-label={isPt ? "Rodapé do 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="ESPDocs, página inicial">
<Link href={getLinkHref("/")} className="inline-flex min-h-11 items-center gap-2.5" aria-label={isPt ? "ESPDocs, página inicial" : "ESPDocs, homepage"}>
<Logo className="h-7 w-7" />
<span className="font-display text-2xl font-semibold tracking-[-0.045em] text-ink">ESPDocs</span>
</Link>
<p className="mt-5 max-w-sm text-sm leading-relaxed text-muted">
Documentação independente do ecossistema ESP32, escrita em português brasileiro.
Dados públicos, curadoria editorial e links para as fontes oficiais.
{isPt ? (
<>
Documentação independente do ecossistema ESP32, escrita em português brasileiro.
Dados públicos, curadoria editorial e links para as fontes oficiais.
</>
) : (
<>
Independent documentation of the ESP32 ecosystem.
Open data, editorial curation, and links to official sources.
</>
)}
</p>
{isPt && (
@@ -133,7 +173,7 @@ export default function Footer() {
))}
<div>
<h2 className="text-sm font-semibold text-ink">Autor</h2>
<h2 className="text-sm font-semibold text-ink">{isPt ? "Autor" : "Author"}</h2>
<p className="mt-4 text-sm leading-relaxed text-muted">
Lucas Rayan Guerra
<br />
@@ -144,7 +184,7 @@ export default function Footer() {
href={SITE.repositorio}
target="_blank"
rel="noopener noreferrer"
aria-label="Gitea do projeto (abre em nova aba)"
aria-label={isPt ? "Gitea do projeto (abre em nova aba)" : "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" />
@@ -153,7 +193,7 @@ export default function Footer() {
href={SITE.autorLinkedin}
target="_blank"
rel="noopener noreferrer"
aria-label="LinkedIn do autor (abre em nova aba)"
aria-label={isPt ? "LinkedIn do autor (abre em nova aba)" : "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">
@@ -166,7 +206,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} ESPDocs. Projeto independente, sem afiliação com a Espressif Systems.</p>
<p>© {year} {isPt ? "ESPDocs. Projeto independente, sem afiliação com a 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
@@ -176,7 +216,7 @@ export default function Footer() {
lucasrguerra.dev.br
<ArrowUpRight className="h-3 w-3" aria-hidden="true" />
</a>
<span>Fontes oficiais: Espressif Systems.</span>
<span>{isPt ? "Fontes oficiais: Espressif Systems." : "Official sources: Espressif Systems."}</span>
</p>
</div>
</div>