fix(i18n): eliminate portuguese text leaks across english routes
CI / Lint e auditorias (push) Successful in 50s
CI / Trivy (dependências e segredos) (push) Successful in 10s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m31s

- Localize skip-to-content accessibility link and breadcrumb navigation
- Convert section layouts to dynamic generateMetadata with English titles, descriptions, and structured data
- Eliminate mixed language expressions and translate remaining SoC connection pin warnings and descriptions
- Translate component registry items, categories, tags, and IDF Component Manager guide
- Add strict identical string verification to scripts/check-i18n.mjs with technical allowlist

Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
Lucas RayanandAntigravity committed 2026-11-01 17:17:33 -03:00
1 parent c1bd2c7e11
commit 47c55515bb
35 files changed
+2353 -1874

No files matched your search

+27 -12
View File
@@ -1,13 +1,25 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
export const metadata = paginaMeta({
titulo: "Catálogo de placas ESP32",
descricao: "Placas de desenvolvimento com ESP32 disponíveis no Brasil, com filtro por série, conector e conversor USB-serial.",
caminho: "/catalogo",
keywords: ["placas ESP32", "DevKit ESP32", "comprar ESP32", "ESP32 DevKitC"],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
return paginaMeta({
titulo: isEn ? "ESP32 Board Catalog" : "Catálogo de placas ESP32",
descricao: isEn
? "ESP32 development boards and DevKits available in Brazil, with filters by series, connector, and USB-serial converter."
: "Placas de desenvolvimento com ESP32 disponíveis no Brasil, com filtro por série, conector e conversor USB-serial.",
caminho: "/catalogo",
locale,
keywords: isEn
? ["ESP32 boards", "ESP32 DevKit", "buy ESP32", "ESP32 DevKitC"]
: ["placas ESP32", "DevKit ESP32", "comprar ESP32", "ESP32 DevKitC"],
});
}
export default async function Layout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
export default function Layout({ children }) {
return (
<>
<script
@@ -15,9 +27,12 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
nome: "Catálogo de Placas ESP32",
descricao: "Guia e catálogo interativo de placas de desenvolvimento e DevKits com microcontroladores ESP32.",
caminho: "/catalogo",
nome: isEn ? "ESP32 Development Board Catalog" : "Catálogo de Placas ESP32",
descricao: isEn
? "Interactive guide and catalog of development boards and DevKits featuring ESP32 microcontrollers."
: "Guia e catálogo interativo de placas de desenvolvimento e DevKits com microcontroladores ESP32.",
caminho: isEn ? "/en/catalog" : "/catalogo",
locale,
})
),
}}
@@ -27,8 +42,8 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Catálogo de Placas", caminho: "/catalogo" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: isEn ? "Board Catalog" : "Catálogo de Placas", caminho: isEn ? "/en/catalog" : "/catalogo" },
])
),
}}
+44 -13
View File
@@ -1,13 +1,22 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
export const metadata = paginaMeta({
titulo: "Comparar séries ESP32",
descricao: "Compare até quatro séries de ESP32 lado a lado: processador, Wi-Fi, Bluetooth, memória, GPIO, periféricos e segurança em hardware, com os dados dos datasheets oficiais.",
caminho: "/comparacao",
keywords: ["comparar ESP32", "ESP32 vs ESP32-S3", "tabela comparativa ESP32", "qual ESP32 é melhor"],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
return paginaMeta({
titulo: isEn ? "Compare ESP32 Series: Side-by-Side Table" : "Comparar séries ESP32",
descricao: isEn
? "Compare up to four ESP32 series side by side: processor, Wi-Fi, Bluetooth, memory, GPIO, peripherals, and hardware security, with official datasheet data."
: "Compare até quatro séries de ESP32 lado a lado: processador, Wi-Fi, Bluetooth, memória, GPIO, periféricos e segurança em hardware, com os dados dos datasheets oficiais.",
caminho: "/comparacao",
locale,
keywords: isEn
? ["compare ESP32", "ESP32 vs ESP32-S3", "ESP32 comparison table", "which ESP32 is best"]
: ["comparar ESP32", "ESP32 vs ESP32-S3", "tabela comparativa ESP32", "qual ESP32 é melhor"],
});
}
const faqs = [
const faqsPt = [
{
pergunta: "Como comparar microcontroladores ESP32 lado a lado?",
resposta: "O comparador do ESPDocs permite selecionar até quatro séries simultaneamente para confrontar arquitetura, frequência de clock, memória SRAM/PSRAM, padrões de rádio (Wi-Fi 4/6, Bluetooth 5/6, 802.15.4), interfaces de periféricos e blocos de segurança em hardware.",
@@ -22,7 +31,26 @@ const faqs = [
},
];
export default function Layout({ children }) {
const faqsEn = [
{
pergunta: "How to compare ESP32 microcontrollers side by side?",
resposta: "The ESPDocs comparator lets you select up to four series simultaneously to compare architecture, clock speed, SRAM/PSRAM memory, radio standards (Wi-Fi 4/6, Bluetooth 5/6, 802.15.4), peripheral interfaces, and hardware security blocks.",
},
{
pergunta: "What is the difference between classic ESP32 and ESP32-S3?",
resposta: "The classic ESP32 uses Xtensa LX6 cores and legacy Bluetooth/LE. The ESP32-S3 brings Xtensa LX7 cores with vector instructions for AI (TinyML), native USB OTG, more GPIOs (45 vs 36), and Bluetooth 5.0 LE.",
},
{
pergunta: "What is the difference between ESP32-C3 and ESP32-C6?",
resposta: "The ESP32-C3 is a RISC-V microcontroller with Wi-Fi 4 and Bluetooth 5 LE. The ESP32-C6 upgrades to Wi-Fi 6 (802.11ax), Bluetooth 5.4 LE, IEEE 802.15.4 radio for Zigbee 3.0 and Thread 1.3, plus an ultra-low-power coprocessor.",
},
];
export default async function Layout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
const faqs = isEn ? faqsEn : faqsPt;
return (
<>
<script
@@ -30,9 +58,12 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
nome: "Comparador de Séries ESP32",
descricao: "Tabela comparativa técnica para analisar especificações, pinagens e recursos de múltiplas séries de ESP32.",
caminho: "/comparacao",
nome: isEn ? "ESP32 Series Comparator" : "Comparador de Séries ESP32",
descricao: isEn
? "Technical comparison table to analyze specifications, pinouts, and features of multiple ESP32 series."
: "Tabela comparativa técnica para analisar especificações, pinagens e recursos de múltiplas séries de ESP32.",
caminho: isEn ? "/en/compare" : "/comparacao",
locale,
})
),
}}
@@ -42,8 +73,8 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Comparar Séries", caminho: "/comparacao" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: isEn ? "Compare Series" : "Comparar Séries", caminho: isEn ? "/en/compare" : "/comparacao" },
])
),
}}
+70 -23
View File
@@ -1,23 +1,44 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
export const metadata = paginaMeta({
titulo: "ESP Component Registry & IDF Component Manager · ESPDocs",
descricao: "Catálogo oficial e interativo do ESP Component Registry: drivers de displays, LVGL, reconhecimento de voz ESP-SR, RainMaker, BSPs e gerador de idf_component.yml.",
caminho: "/componentes",
keywords: [
"ESP Component Registry",
"IDF Component Manager",
"idf_component.yml",
"idf.py add-dependency",
"componentes ESP32",
"espressif esp_lvgl_port",
"espressif esp_rainmaker",
"espressif esp-sr",
"bibliotecas ESP-IDF",
],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
return paginaMeta({
titulo: isEn
? "ESP Component Registry & IDF Component Manager · ESPDocs"
: "ESP Component Registry & IDF Component Manager · ESPDocs",
descricao: isEn
? "Official interactive catalog of the ESP Component Registry: display drivers, LVGL, ESP-SR voice recognition, RainMaker, BSPs, and idf_component.yml generator."
: "Catálogo oficial e interativo do ESP Component Registry: drivers de displays, LVGL, reconhecimento de voz ESP-SR, RainMaker, BSPs e gerador de idf_component.yml.",
caminho: "/componentes",
locale,
keywords: isEn
? [
"ESP Component Registry",
"IDF Component Manager",
"idf_component.yml",
"idf.py add-dependency",
"ESP32 components",
"espressif esp_lvgl_port",
"espressif esp_rainmaker",
"espressif esp-sr",
"ESP-IDF libraries",
]
: [
"ESP Component Registry",
"IDF Component Manager",
"idf_component.yml",
"idf.py add-dependency",
"componentes ESP32",
"espressif esp_lvgl_port",
"espressif esp_rainmaker",
"espressif esp-sr",
"bibliotecas ESP-IDF",
],
});
}
const faqs = [
const faqsPt = [
{
pergunta: "O que é o ESP Component Registry e o IDF Component Manager?",
resposta: "O ESP Component Registry (components.espressif.com) é o repositório oficial de pacotes modulares da Espressif. O IDF Component Manager é a ferramenta integrada ao ESP-IDF (a partir do v4.4 e padrão no v5.x+) que baixa, compila e resolve versões de dependências automaticamente a partir do manifesto idf_component.yml.",
@@ -36,7 +57,30 @@ const faqs = [
},
];
export default function ComponentesLayout({ children }) {
const faqsEn = [
{
pergunta: "What is the ESP Component Registry and IDF Component Manager?",
resposta: "The ESP Component Registry (components.espressif.com) is the official central repository for modular Espressif packages. The IDF Component Manager is integrated into ESP-IDF (v4.4+ and default in v5.x+) to automatically download, compile, and resolve dependency versions from an idf_component.yml manifest.",
},
{
pergunta: "How to add a component to an ESP-IDF project?",
resposta: "You can run 'idf.py add-dependency \"namespace/component^version\"' in your project terminal (for example: idf.py add-dependency \"espressif/esp_lvgl_port^2.4.0\") or edit main/idf_component.yml directly.",
},
{
pergunta: "What is the difference between using Component Registry and git submodules?",
resposta: "The Component Registry eliminates the complexity of managing heavy Git submodules and manual tracking. It provides semantic versioning (^ and ~), automatic transitive dependency resolution, and modular on-demand compilation.",
},
{
pergunta: "Can I create and publish my own components?",
resposta: "Yes, any developer or company can publish public or private components to the ESP Component Registry using the official Compote CLI tool (pip install idf-component-manager).",
},
];
export default async function ComponentesLayout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
const faqs = isEn ? faqsEn : faqsPt;
return (
<>
<script
@@ -44,8 +88,8 @@ export default function ComponentesLayout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "ESP Component Registry", caminho: "/componentes" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: "ESP Component Registry", caminho: isEn ? "/en/components" : "/componentes" },
])
),
}}
@@ -61,9 +105,12 @@ export default function ComponentesLayout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
nome: "Hub do ESP Component Registry & Construtor de Manifesto",
descricao: "Catálogo curado e gerador de manifesto idf_component.yml para componentes modulares do ESP-IDF.",
caminho: "/componentes",
nome: isEn ? "ESP Component Registry Hub & Manifest Builder" : "Hub do ESP Component Registry & Construtor de Manifesto",
descricao: isEn
? "Curated catalog and idf_component.yml manifest generator for modular ESP-IDF components."
: "Catálogo curado e gerador de manifesto idf_component.yml para componentes modulares do ESP-IDF.",
caminho: isEn ? "/en/components" : "/componentes",
locale,
})
),
}}
+68 -22
View File
@@ -1,22 +1,42 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
export const metadata = paginaMeta({
titulo: "Diagnóstico de Erros Comuns no ESP32: Guia Profissional",
descricao: "Guia técnico definitivo para identificar e resolver Brownout, Guru Meditation, Task Watchdog, Stack Overflow, vazamento de memória e falhas de boot no ESP32 (Arduino e ESP-IDF).",
caminho: "/diagnostico",
keywords: [
"erros comuns ESP32",
"brownout detector was triggered",
"guru meditation error",
"task watchdog got triggered",
"stack canary watchpoint triggered",
"como debugar ESP32",
"ESP32 reiniciando em loop",
"failed to connect to esp32",
],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
return paginaMeta({
titulo: isEn
? "Common ESP32 Errors & Diagnostics: Professional Guide"
: "Diagnóstico de Erros Comuns no ESP32: Guia Profissional",
descricao: isEn
? "Definitive technical guide to identify and fix Brownout, Guru Meditation, Task Watchdog, Stack Overflow, memory leaks, and boot issues on ESP32 (Arduino & ESP-IDF)."
: "Guia técnico definitivo para identificar e resolver Brownout, Guru Meditation, Task Watchdog, Stack Overflow, vazamento de memória e falhas de boot no ESP32 (Arduino e ESP-IDF).",
caminho: "/diagnostico",
locale,
keywords: isEn
? [
"common ESP32 errors",
"brownout detector was triggered",
"guru meditation error",
"task watchdog got triggered",
"stack canary watchpoint triggered",
"how to debug ESP32",
"ESP32 bootloop rebooting",
"failed to connect to esp32",
]
: [
"erros comuns ESP32",
"brownout detector was triggered",
"guru meditation error",
"task watchdog got triggered",
"stack canary watchpoint triggered",
"como debugar ESP32",
"ESP32 reiniciando em loop",
"failed to connect to esp32",
],
});
}
const faqs = [
const faqsPt = [
{
pergunta: "O que causa o erro 'Brownout detector was triggered' no ESP32?",
resposta: "Ocorre quando a tensão no pino 3V3 cai abaixo de 2.8V ~ 2.43V devido a picos rápidos de corrente do rádio Wi-Fi/BLE (superando 500mA), cabos USB finos ou regulador LDO inadequado. A solução de engenharia envolve adicionar capacitores Low-ESR de 10µF a 100µF próximos ao VCC e usar LDOs robustos de 600mA+.",
@@ -35,7 +55,30 @@ const faqs = [
},
];
export default function DiagnosticoLayout({ children }) {
const faqsEn = [
{
pergunta: "What causes 'Brownout detector was triggered' on ESP32?",
resposta: "It occurs when the 3V3 rail drops below 2.8V ~ 2.43V during rapid Wi-Fi/BLE radio current spikes (often exceeding 500mA), caused by thin USB cables or an undersized LDO regulator. The engineering solution involves placing 10µF to 100µF Low-ESR bypass capacitors near VCC and using 600mA+ robust LDO regulators.",
},
{
pergunta: "How to resolve 'Guru Meditation Error (LoadProhibited)'?",
resposta: "LoadProhibited indicates an attempt to read from or write to a null (NULL) or invalid memory address. The fix involves using the Espressif Exception Decoder or addr2line to pinpoint the source line and applying defensive checks such as if (ptr != NULL).",
},
{
pergunta: "Why does the Task Watchdog Timer (TWDT) trigger in FreeRTOS?",
resposta: "The TWDT triggers when a high-priority task monopolizes the CPU in a tight blocking loop (e.g. while(1) without vTaskDelay), starving the lower-priority IDLE task that feeds the operating system watchdog.",
},
{
pergunta: "Why do HTTPS and TLS connections fail with error -0x2700 on ESP32?",
resposta: "On boot, the ESP32 internal system clock starts at epoch year 1970. The TLS certificate validator rejects the server certificate because its validity dates appear in the distant 'future'. Synchronizing time via SNTP (configTime) prior to initiating TLS connections resolves the issue.",
},
];
export default async function DiagnosticoLayout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
const faqs = isEn ? faqsEn : faqsPt;
return (
<>
<script
@@ -43,8 +86,8 @@ export default function DiagnosticoLayout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Diagnóstico & Erros Comuns", caminho: "/diagnostico" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: isEn ? "Diagnostics & Common Errors" : "Diagnóstico & Erros Comuns", caminho: isEn ? "/en/diagnostics" : "/diagnostico" },
])
),
}}
@@ -62,9 +105,12 @@ export default function DiagnosticoLayout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
nome: "Guia de Diagnóstico e Erros no ESP32",
descricao: "Ferramenta e guia técnico de análise de falhas, pânicos de CPU e decodificação de logs no ESP32.",
caminho: "/diagnostico",
nome: isEn ? "ESP32 Error Diagnostics & Debugging Guide" : "Guia de Diagnóstico e Erros no ESP32",
descricao: isEn
? "Technical guide and log decoder for troubleshooting CPU panics, crashes, and exceptions on ESP32."
: "Ferramenta e guia técnico de análise de falhas, pânicos de CPU e decodificação de logs no ESP32.",
caminho: isEn ? "/en/diagnostics" : "/diagnostico",
locale,
})
),
}}
+2
View File
@@ -54,6 +54,8 @@ const GRAVIDADE = {
"Critical": "text-danger",
Alta: "text-gold",
High: "text-gold",
"Média": "text-accent",
Medium: "text-accent",
};
const CHECKLISTS_PT = [
+49 -22
View File
@@ -1,28 +1,52 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
import { categoriasGlossario } from "@/lib/glossarioData";
import { categoriasGlossario as categoriasGlossarioPt } from "@/lib/glossarioData";
import { categoriasGlossario as categoriasGlossarioEn } from "@/lib/data/en/glossario.js";
export const metadata = paginaMeta({
titulo: "Glossário Técnico do ESP32: Termos e Conceitos",
descricao: "Mais de 50 termos e conceitos essenciais do ecossistema ESP32 explicados em português: GPIO, Strapping Pins, ULP, PSRAM, FreeRTOS, eFuse, Deep Sleep, OTA, Secure Boot, ECDSA, Matter e Thread.",
caminho: "/glossario",
keywords: [
"glossário ESP32",
"o que é GPIO",
"o que é strapping pins",
"o que é ULP ESP32",
"o que é PSRAM ESP32",
"termos técnicos microcontrolador",
"conceitos ESP32 português",
],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
return paginaMeta({
titulo: isEn
? "ESP32 Technical Glossary: Terms and Concepts"
: "Glossário Técnico do ESP32: Termos e Conceitos",
descricao: isEn
? "Over 50 essential ESP32 concepts explained: GPIO, Strapping Pins, ULP, PSRAM, FreeRTOS, eFuse, Deep Sleep, OTA, Secure Boot, ECDSA, Matter, and Thread."
: "Mais de 50 termos e conceitos essenciais do ecossistema ESP32 explicados em português: GPIO, Strapping Pins, ULP, PSRAM, FreeRTOS, eFuse, Deep Sleep, OTA, Secure Boot, ECDSA, Matter e Thread.",
caminho: "/glossario",
locale,
keywords: isEn
? [
"ESP32 glossary",
"what is GPIO",
"what are strapping pins",
"what is ESP32 ULP",
"what is ESP32 PSRAM",
"microcontroller technical terms",
"ESP32 hardware concepts",
]
: [
"glossário ESP32",
"o que é GPIO",
"o que é strapping pins",
"o que é ULP ESP32",
"o que é PSRAM ESP32",
"termos técnicos microcontrolador",
"conceitos ESP32 português",
],
});
}
export default async function GlossarioLayout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
const categorias = isEn ? categoriasGlossarioEn : categoriasGlossarioPt;
export default function GlossarioLayout({ children }) {
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd.glossario(categoriasGlossario)),
__html: JSON.stringify(jsonLd.glossario(categorias, locale)),
}}
/>
<script
@@ -30,8 +54,8 @@ export default function GlossarioLayout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Glossário Técnico", caminho: "/glossario" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: isEn ? "Technical Glossary" : "Glossário Técnico", caminho: isEn ? "/en/glossary" : "/glossario" },
])
),
}}
@@ -41,9 +65,12 @@ export default function GlossarioLayout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
nome: "Glossário Técnico do ESP32",
descricao: "Enciclopédia de termos técnicos e conceitos de hardware e software do ecossistema ESP32.",
caminho: "/glossario",
nome: isEn ? "ESP32 Technical Glossary" : "Glossário Técnico do ESP32",
descricao: isEn
? "Encyclopedia of hardware and software technical terms for the ESP32 ecosystem."
: "Enciclopédia de termos técnicos e conceitos de hardware e software do ecossistema ESP32.",
caminho: isEn ? "/en/glossary" : "/glossario",
locale,
})
),
}}
+2 -2
View File
@@ -175,7 +175,7 @@ export default async function RootLayout({ children, params }) {
Google montar o box de busca interna e entender a autoria. */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd.siteESociedade()) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd.siteESociedade(locale)) }}
/>
</head>
<body className="font-sans antialiased bg-canvas text-ink overflow-x-clip" suppressHydrationWarning>
@@ -183,7 +183,7 @@ export default async function RootLayout({ children, params }) {
href="#conteudo"
className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[200] focus:rounded-full focus:bg-ink focus:px-5 focus:py-3 focus:text-sm focus:font-semibold focus:text-canvas"
>
Pular para o conteúdo
{messages?.Common?.skipToContent || "Pular para o conteúdo"}
</a>
<NextIntlClientProvider locale={locale} messages={messages}>
<Providers>
+44 -21
View File
@@ -2,53 +2,76 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer";
import Link from "next/link";
import { ArrowRight, BookOpen, Compass, Scale, SlidersHorizontal } from "lucide-react";
import { getLocale } from "next-intl/server";
import { getLocalizedHref } from "@/i18n/links";
export const metadata = {
title: "Página não encontrada",
title: "404",
robots: { index: false, follow: false },
};
const CAMINHOS = [
const CAMINHOS_PT = [
{ href: "/series", icon: Compass, titulo: "Séries ESP32", texto: "As 12 famílias, com pinagem e especificações." },
{ href: "/seletor", icon: SlidersHorizontal, titulo: "Seletor de chip", texto: "Responda a oito perguntas e receba os modelos indicados." },
{ href: "/comparacao", icon: Scale, titulo: "Comparador", texto: "Até quatro séries lado a lado." },
{ href: "/glossario", icon: BookOpen, titulo: "Glossário", texto: "Termos de hardware explicados em português." },
];
export default function NotFound() {
const CAMINHOS_EN = [
{ href: "/series", icon: Compass, titulo: "ESP32 Series", texto: "The 12 families, with pinouts and specifications." },
{ href: "/seletor", icon: SlidersHorizontal, titulo: "Chip selector", texto: "Answer eight questions and get recommended models." },
{ href: "/comparacao", icon: Scale, titulo: "Comparator", texto: "Up to four series side by side." },
{ href: "/glossario", icon: BookOpen, titulo: "Glossary", texto: "Hardware terms explained clearly." },
];
export default async function NotFound() {
let locale = "pt";
try {
locale = await getLocale();
} catch {
locale = "pt";
}
const isEn = locale === "en";
const caminhos = isEn ? CAMINHOS_EN : CAMINHOS_PT;
return (
<div className="site-canvas min-h-screen">
<Header />
<main id="conteudo" className="shell section">
<div className="mx-auto max-w-2xl text-center">
<p className="eyebrow justify-center">Erro 404</p>
<h1 className="page-title mt-4">Essa página não existe.</h1>
<p className="eyebrow justify-center">{isEn ? "Error 404" : "Erro 404"}</p>
<h1 className="page-title mt-4">{isEn ? "This page does not exist." : "Essa página não existe."}</h1>
<p className="lede mx-auto mt-5 max-w-xl">
O endereço pode ter mudado ou estar digitado errado. Escolha um destes caminhos para continuar.
{isEn
? "The address may have changed or was mistyped. Choose one of these paths to continue."
: "O endereço pode ter mudado ou estar digitado errado. Escolha um destes caminhos para continuar."}
</p>
<div className="mt-8 flex justify-center">
<Link href="/" className="btn btn-primary">
Voltar ao início
<Link href={isEn ? "/en" : "/"} className="btn btn-primary">
{isEn ? "Back to home" : "Voltar ao início"}
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</div>
<ul className="mx-auto mt-14 grid max-w-4xl gap-3 sm:grid-cols-2">
{CAMINHOS.map(({ href, icon: Icon, titulo, texto }) => (
<li key={href}>
<Link href={href} className="card card-interactive group flex h-full items-start gap-4 p-5">
<span className="grid h-10 w-10 shrink-0 place-items-center rounded-full bg-accent-soft text-accent">
<Icon className="h-5 w-5" aria-hidden="true" />
</span>
<span>
<span className="block font-semibold text-ink">{titulo}</span>
<span className="mt-0.5 block text-sm text-muted">{texto}</span>
</span>
</Link>
</li>
))}
{caminhos.map(({ href, icon: Icon, titulo, texto }) => {
const targetHref = isEn ? getLocalizedHref(href, "en") : href;
return (
<li key={href}>
<Link href={targetHref} className="card card-interactive group flex h-full items-start gap-4 p-5">
<span className="grid h-10 w-10 shrink-0 place-items-center rounded-full bg-accent-soft text-accent">
<Icon className="h-5 w-5" aria-hidden="true" />
</span>
<span>
<span className="block font-semibold text-ink">{titulo}</span>
<span className="mt-0.5 block text-sm text-muted">{texto}</span>
</span>
</Link>
</li>
);
})}
</ul>
</main>
+48 -13
View File
@@ -1,13 +1,22 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
export const metadata = paginaMeta({
titulo: "Qual ESP32 escolher",
descricao: "Responda a perguntas simples sobre conectividade (Wi-Fi 6, 5GHz, Matter), energia, periféricos e segurança ou use o filtro paramétrico para encontrar o ESP32 perfeito.",
caminho: "/seletor",
keywords: ["qual ESP32 escolher", "qual ESP32 usar", "escolher microcontrolador", "ESP32 para meu projeto", "comparar ESP32"],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
return paginaMeta({
titulo: isEn ? "Which ESP32 to Choose" : "Qual ESP32 escolher",
descricao: isEn
? "Answer simple questions about connectivity (Wi-Fi 6, 5GHz, Matter), power, peripherals, and security or use the parametric filter to find the perfect ESP32."
: "Responda a perguntas simples sobre conectividade (Wi-Fi 6, 5GHz, Matter), energia, periféricos e segurança ou use o filtro paramétrico para encontrar o ESP32 perfeito.",
caminho: "/seletor",
locale,
keywords: isEn
? ["which ESP32 to choose", "which ESP32 to use", "choose microcontroller", "ESP32 for my project", "compare ESP32"]
: ["qual ESP32 escolher", "qual ESP32 usar", "escolher microcontrolador", "ESP32 para meu projeto", "comparar ESP32"],
});
}
const faqs = [
const faqsPt = [
{
pergunta: "Qual ESP32 escolher para iniciantes?",
resposta: "Para quem está começando no mundo Maker e IoT, o ESP32 clássico (NodeMCU/DevKitC) ou o ESP32-C3 são as melhores opções devido à vasta quantidade de bibliotecas, exemplos prontos no Arduino IDE e farta documentação em português.",
@@ -26,7 +35,30 @@ const faqs = [
},
];
export default function Layout({ children }) {
const faqsEn = [
{
pergunta: "Which ESP32 to choose for beginners?",
resposta: "For makers and developers starting out in IoT, the classic ESP32 (NodeMCU/DevKitC) or ESP32-C3 are top choices thanks to extensive library support, readily available Arduino IDE examples, and thorough documentation.",
},
{
pergunta: "Which ESP32 supports Wi-Fi 6, Bluetooth 6 and Thread/Zigbee/Matter?",
resposta: "The ESP32-C6 and ESP32-C5 are the leading series for modern networking. The ESP32-C6 offers 2.4 GHz Wi-Fi 6 and 802.15.4 radio (Zigbee 3.0 / Thread / Matter). The ESP32-C5 is the first to introduce Dual-Band Wi-Fi 6 on both 2.4 GHz and 5 GHz bands simultaneously.",
},
{
pergunta: "Which is the most powerful ESP32 for AI (TinyML) and Displays?",
resposta: "The ESP32-P4 (dual-core RISC-V at 400 MHz with AI vector extensions, MIPI-CSI/DSI interfaces, and up to 64 MB PSRAM) and ESP32-S3 (dual-core Xtensa at 240 MHz with neural network vector acceleration and RGB LCD support) provide maximum speed for edge AI and rich graphical interfaces.",
},
{
pergunta: "Which ESP32 consumes the least power for battery-operated projects?",
resposta: "Compact RISC-V microcontrollers with ULP coprocessors like the ESP32-C2, ESP32-C3, and ESP32-H2 (dedicated 802.15.4 without Wi-Fi) achieve ultra-low deep sleep currents ranging between 5 µA and 10 µA.",
},
];
export default async function Layout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
const faqs = isEn ? faqsEn : faqsPt;
return (
<>
<script
@@ -34,9 +66,12 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
nome: "Seletor Inteligente de ESP32",
descricao: "Ferramenta interativa que analisa conectividade, consumo, periféricos e segurança para indicar a série ideal de ESP32.",
caminho: "/seletor",
nome: isEn ? "Intelligent ESP32 Selector" : "Seletor Inteligente de ESP32",
descricao: isEn
? "Interactive tool analyzing connectivity, power consumption, peripherals, and security to recommend the ideal ESP32 series."
: "Ferramenta interativa que analisa conectividade, consumo, periféricos e segurança para indicar a série ideal de ESP32.",
caminho: isEn ? "/en/selector" : "/seletor",
locale,
})
),
}}
@@ -46,8 +81,8 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Seletor de ESP32", caminho: "/seletor" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: isEn ? "ESP32 Selector" : "Seletor de ESP32", caminho: isEn ? "/en/selector" : "/seletor" },
])
),
}}
+1 -1
View File
@@ -454,7 +454,7 @@ export default async function SerieDetail({ params }) {
</section>
<div className="shell mt-10">
<SeriesTabMenu tabs={tabs} />
<SeriesTabMenu tabs={tabs} isEn={isEn} />
<PinRestrictions conexoes={conexoes} serieKey={key} isEn={isEn} />
</div>
+39 -12
View File
@@ -1,15 +1,27 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
export const metadata = paginaMeta({
titulo: "Séries ESP32",
descricao: "As 12 séries de ESP32 lado a lado: ESP32, S2, S3, S31, C2, C3, C5, C6, C61, P4, H2 e H4. Arquitetura, memória, rádio, periféricos e pinagem de cada uma, em português.",
caminho: "/series",
keywords: ["séries ESP32", "modelos de ESP32", "ESP32 S3", "ESP32 C6", "ESP32 P4", "diferença entre ESP32"],
});
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
const meta = paginaMeta({
titulo: isEn ? "ESP32 Series: Technical Guide and Pinouts" : "Séries ESP32",
descricao: isEn
? "All 12 ESP32 series side by side: ESP32, S2, S3, S31, C2, C3, C5, C6, C61, P4, H2 and H4. Architecture, memory, radio, peripherals and pinouts for each one."
: "As 12 séries de ESP32 lado a lado: ESP32, S2, S3, S31, C2, C3, C5, C6, C61, P4, H2 e H4. Arquitetura, memória, rádio, periféricos e pinagem de cada uma, em português.",
caminho: "/series",
locale,
keywords: isEn
? ["ESP32 series", "ESP32 models", "ESP32 S3", "ESP32 C6", "ESP32 P4", "difference between ESP32"]
: ["séries ESP32", "modelos de ESP32", "ESP32 S3", "ESP32 C6", "ESP32 P4", "diferença entre ESP32"],
});
meta.title = {
default: isEn ? "ESP32 Series" : "Séries ESP32",
template: "%s · ESPDocs",
};
return meta;
}
metadata.title = { default: "Séries ESP32", template: "%s · ESPDocs" };
const faqs = [
const faqsPt = [
{
pergunta: "Quantas séries de ESP32 existem atualmente?",
resposta: "Atualmente o ecossistema Espressif conta com 12 séries principais: ESP32 clássico, ESP32-S2, ESP32-S3, ESP32-S31, ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-P4, ESP32-H2 e ESP32-H4.",
@@ -20,7 +32,22 @@ const faqs = [
},
];
export default function Layout({ children }) {
const faqsEn = [
{
pergunta: "How many ESP32 series currently exist?",
resposta: "Currently, the Espressif ecosystem features 12 primary series: classic ESP32, ESP32-S2, ESP32-S3, ESP32-S31, ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-P4, ESP32-H2, and ESP32-H4.",
},
{
pergunta: "What are the latest series from Espressif?",
resposta: "The newest series include the ESP32-P4 (high-performance dual-core RISC-V at 400 MHz), the ESP32-C5 (first dual-band 2.4/5 GHz Wi-Fi chip from Espressif), and the ESP32-C61 (cost-effective Wi-Fi 6 and Bluetooth 6).",
},
];
export default async function Layout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
const faqs = isEn ? faqsEn : faqsPt;
return (
<>
<script
@@ -28,8 +55,8 @@ export default function Layout({ children }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Séries ESP32", caminho: "/series" },
{ nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
{ nome: isEn ? "ESP32 Series" : "Séries ESP32", caminho: isEn ? "/en/series" : "/series" },
])
),
}}
+1 -1
View File
@@ -49,7 +49,7 @@ export default async function Sobre({ params }) {
{/* Metadados E-E-A-T de autoria e organização para o Google */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd.paginaSobre()) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd.paginaSobre(locale)) }}
/>
<script
type="application/ld+json"