- {isEn ? (
+ {isEs ? (
+ <>Elija de {MIN_SERIES} a {MAX_SERIES} series>
+ ) : isEn ? (
<>Choose {MIN_SERIES} to {MAX_SERIES} series>
) : (
<>Escolha de {MIN_SERIES} a {MAX_SERIES} séries>
)}
- {isEn ? (
+ {isEs ? (
+ <>
+ {selectedSeries.length} de {MAX_SERIES} seleccionadas
+ {selectedSeries.length === MAX_SERIES && ". Elimine una para cambiar."}
+ >
+ ) : isEn ? (
<>
{selectedSeries.length} of {MAX_SERIES} selected
{selectedSeries.length === MAX_SERIES && ". Remove one to switch."}
@@ -298,7 +389,7 @@ export default function Comparacao() {
- {isEn ? "Specifications" : "Especificações"}
+ {isEs ? "Especificaciones" : isEn ? "Specifications" : "Especificações"}
setSoDiferencas(e.target.checked)}
className="h-5 w-5 cursor-pointer accent-[var(--accent)]"
/>
- {isEn ? "Show differences only" : "Mostrar só as diferenças"}
+ {isEs ? "Mostrar solo diferencias" : isEn ? "Show differences only" : "Mostrar só as diferenças"}
@@ -315,7 +406,9 @@ export default function Comparacao() {
- {isEn ? (
+ {isEs ? (
+ <>Comparación de las series {selectedSeries.join(", ")}>
+ ) : isEn ? (
<>Comparison of series {selectedSeries.join(", ")}>
) : (
<>Comparação das séries {selectedSeries.join(", ")}>
@@ -324,7 +417,7 @@ export default function Comparacao() {
- {isEn ? "Specification" : "Especificação"}
+ {isEs ? "Especificación" : isEn ? "Specification" : "Especificação"}
{selectedSeries.map((seriesKey) => (
@@ -381,7 +474,7 @@ export default function Comparacao() {
- {isEn ? "Official documentation" : "Documentação oficial"}
+ {isEs ? "Documentación oficial" : isEn ? "Official documentation" : "Documentação oficial"}
@@ -410,16 +503,18 @@ export default function Comparacao() {
- {isEn ? "Want to test on the bench?" : "Quer testar na bancada?"}
+ {isEs ? "¿Desea probar en banco?" : isEn ? "Want to test on the bench?" : "Quer testar na bancada?"}
- {isEn
+ {isEs
+ ? "Vea las placas de desarrollo con patillaje, periféricos y enlace a la tienda oficial."
+ : isEn
? "Explore development boards with pinouts, peripherals, and official store links."
: "Veja as placas de desenvolvimento com pinagem, periféricos e link para a loja."}
- {isEn ? "View boards catalog" : "Ver catálogo de placas"}
+ {isEs ? "Ver catálogo de placas" : isEn ? "View boards catalog" : "Ver catálogo de placas"}
diff --git a/app/[locale]/componentes/layout.jsx b/app/[locale]/componentes/layout.jsx
index d015390..83a7b7a 100644
--- a/app/[locale]/componentes/layout.jsx
+++ b/app/[locale]/componentes/layout.jsx
@@ -3,16 +3,33 @@ import { paginaMeta, jsonLd } from "@/lib/seo";
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
+ const isEs = locale === "es";
return paginaMeta({
- titulo: isEn
+ titulo: isEs
+ ? "ESP Component Registry & IDF Component Manager · ESPDocs"
+ : isEn
? "ESP Component Registry & IDF Component Manager · ESPDocs"
: "ESP Component Registry & IDF Component Manager · ESPDocs",
- descricao: isEn
+ descricao: isEs
+ ? "Catálogo interactivo del ESP Component Registry: controladores de pantalla, LVGL, reconocimiento de voz ESP-SR, RainMaker, BSPs y generador de idf_component.yml."
+ : 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
+ keywords: isEs
+ ? [
+ "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",
+ ]
+ : isEn
? [
"ESP Component Registry",
"IDF Component Manager",
@@ -76,10 +93,30 @@ const faqsEn = [
},
];
+const faqsEs = [
+ {
+ pergunta: "¿Qué es el ESP Component Registry y el IDF Component Manager?",
+ resposta: "El ESP Component Registry (components.espressif.com) es el repositorio oficial de paquetes modulares de Espressif. El IDF Component Manager es la herramienta integrada en ESP-IDF (a partir de v4.4 y estándar en v5.x+) que descarga, compila y resuelve versiones de dependencias automáticamente desde el manifiesto idf_component.yml.",
+ },
+ {
+ pergunta: "¿Cómo agregar un componente a mi proyecto ESP-IDF?",
+ resposta: "Puede ejecutar el comando 'idf.py add-dependency \"namespace/componente^version\"' en la terminal del proyecto (por ejemplo: idf.py add-dependency \"espressif/esp_lvgl_port^2.4.0\") o editar directamente el archivo main/idf_component.yml.",
+ },
+ {
+ pergunta: "¿Cuál es la diferencia entre usar Component Registry y submódulos git?",
+ resposta: "El Component Registry elimina la complejidad de gestionar submódulos Git pesados y versiones manuales. Ofrece versionamiento semántico (^ y ~), resolución automática de dependencias transitivas y compilación modular bajo demanda.",
+ },
+ {
+ pergunta: "¿Puedo crear y publicar mis propios componentes?",
+ resposta: "Sí, cualquier desarrollador o empresa puede publicar componentes públicos o privados en el ESP Component Registry utilizando la herramienta oficial Compote CLI (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;
+ const isEs = locale === "es";
+ const faqs = isEs ? faqsEs : isEn ? faqsEn : faqsPt;
return (
<>
@@ -88,8 +125,8 @@ export default async function ComponentesLayout({ children, params }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
- { nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
- { nome: "ESP Component Registry", caminho: isEn ? "/en/components" : "/componentes" },
+ { nome: isEs ? "Inicio" : isEn ? "Home" : "Início", caminho: isEs ? "/es" : isEn ? "/en" : "/" },
+ { nome: "ESP Component Registry", caminho: isEs ? "/es/componentes" : isEn ? "/en/components" : "/componentes" },
])
),
}}
@@ -105,11 +142,17 @@ export default async function ComponentesLayout({ children, params }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
- nome: isEn ? "ESP Component Registry Hub & Manifest Builder" : "Hub do ESP Component Registry & Construtor de Manifesto",
- descricao: isEn
+ nome: isEs
+ ? "Hub de ESP Component Registry & Generador de Manifiesto"
+ : isEn
+ ? "ESP Component Registry Hub & Manifest Builder"
+ : "Hub do ESP Component Registry & Construtor de Manifesto",
+ descricao: isEs
+ ? "Catálogo curado y generador de manifiesto idf_component.yml para componentes modulares de ESP-IDF."
+ : 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",
+ caminho: isEs ? "/es/componentes" : isEn ? "/en/components" : "/componentes",
locale,
})
),
diff --git a/app/[locale]/componentes/page.jsx b/app/[locale]/componentes/page.jsx
index 61447ef..82bebf1 100644
--- a/app/[locale]/componentes/page.jsx
+++ b/app/[locale]/componentes/page.jsx
@@ -8,6 +8,7 @@ import PageHero from "@/components/PageHero";
import SectionHead from "@/components/SectionHead";
import * as componentesPt from "@/lib/componentRegistryData";
import * as componentesEn from "@/lib/data/en/componentes";
+import * as componentesEs from "@/lib/data/es/componentes";
import {
ArrowUpRight,
Box,
@@ -44,7 +45,8 @@ const ICONES_CATEGORIA = {
export default function ComponentRegistryPage() {
const pathname = usePathname();
const isEn = pathname?.startsWith("/en");
- const { CATEGORIAS_COMPONENTES, COMPONENTES_REGISTRY, GUIA_COMPONENT_MANAGER } = isEn ? componentesEn : componentesPt;
+ const isEs = pathname?.startsWith("/es");
+ const { CATEGORIAS_COMPONENTES, COMPONENTES_REGISTRY, GUIA_COMPONENT_MANAGER } = isEs ? componentesEs : isEn ? componentesEn : componentesPt;
const [busca, setBusca] = useState("");
const [categoriaAtiva, setCategoriaAtiva] = useState("todos");
@@ -98,19 +100,23 @@ export default function ComponentRegistryPage() {
);
if (selecionados.length === 0) {
- return isEn
+ return isEs
+ ? `# No hay componentes seleccionados.\n# Haga clic en "+ Agregar al manifiesto" en las tarjetas de abajo.`
+ : isEn
? `# No components selected.\n# Click "+ Add to Manifest" on the cards below.`
: `# Nenhum componente selecionado.\n# Clique em "+ Adicionar ao Manifesto" nos cards abaixo.`;
}
- let yaml = isEn
+ let yaml = isEs
+ ? `# main/idf_component.yml\n# Generado mediante ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/es/componentes)\n\ndependencies:\n`
+ : isEn
? `# main/idf_component.yml\n# Generated via ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/en/components)\n\ndependencies:\n`
: `# main/idf_component.yml\n# Gerado via ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/componentes)\n\ndependencies:\n`;
selecionados.forEach((c) => {
yaml += ` ${c.namespace}/${c.name}: "${c.versao}"\n`;
});
return yaml;
- }, [componentesSelecionados, COMPONENTES_REGISTRY, isEn]);
+ }, [componentesSelecionados, COMPONENTES_REGISTRY, isEn, isEs]);
const copiarManifesto = () => {
navigator.clipboard.writeText(manifestoYaml);
@@ -145,31 +151,34 @@ export default function ComponentRegistryPage() {
- {isEn ? "Open official registry" : "Abrir o registro oficial"}
+ {isEs ? "Abrir el registro oficial" : isEn ? "Open official registry" : "Abrir o registro oficial"}
- {isEn ? "(opens in a new tab)" : "(abre em nova aba)"}
+ {isEs ? "(abre en una nueva pestaña)" : isEn ? "(opens in a new tab)" : "(abre em nova aba)"}
- {isEn ? "Generate idf_component.yml" : "Gerar idf_component.yml"}
+ {isEs ? "Generar idf_component.yml" : isEn ? "Generate idf_component.yml" : "Gerar idf_component.yml"}
-
+
- {isEn ? "Search by name, namespace, or tag" : "Buscar por nome, namespace ou tag"}
+ {isEs ? "Buscar por nombre, namespace o etiqueta" : isEn ? "Search by name, namespace, or tag" : "Buscar por nome, namespace ou tag"}
setBusca(e.target.value)}
- placeholder={isEn ? "Search by name, namespace, or tag" : "Buscar por nome, namespace ou tag"}
+ placeholder={isEs ? "Buscar por nombre, namespace o etiqueta" : isEn ? "Search by name, namespace, or tag" : "Buscar por nome, namespace ou tag"}
autoComplete="off"
className="field pl-12 pr-4 text-base"
/>
-
{isEn ? "Category" : "Categoria"}
+
{isEs ? "Categoría" : isEn ? "Category" : "Categoria"}
{CATEGORIAS_COMPONENTES.map((cat) => {
const Icone = ICONES_CATEGORIA[cat.id] ?? Layers;
@@ -192,7 +201,7 @@ export default function ComponentRegistryPage() {
return (
setCategoriaAtiva(cat.id)} aria-pressed={categoriaAtiva === cat.id} className="chip-btn">
- {cat.id === "todos" ? (isEn ? "All" : "Todos") : cat.nome.replace(/\s*\(.*\)/, "").split(",")[0].split(" &")[0]}
+ {cat.id === "todos" ? (isEs ? "Todos" : isEn ? "All" : "Todos") : cat.nome.replace(/\s*\(.*\)/, "").split(",")[0].split(" &")[0]}
{contagem}
);
@@ -201,11 +210,11 @@ export default function ComponentRegistryPage() {
-
{isEn ? "Compatible chip" : "Chip compatível"}
+
{isEs ? "Chip compatible" : isEn ? "Compatible chip" : "Chip compatível"}
{chipsDisponiveis.map((chip) => (
setChipFiltro(chip)} aria-pressed={chipFiltro === chip} className="chip-btn">
- {chip === "todos" ? (isEn ? "All" : "Todos") : chip}
+ {chip === "todos" ? (isEs ? "Todos" : isEn ? "All" : "Todos") : chip}
))}
@@ -216,17 +225,17 @@ export default function ComponentRegistryPage() {
- {componentesFiltrados.length} {componentesFiltrados.length === 1 ? (isEn ? "component" : "componente") : (isEn ? "components" : "componentes")}
+ {componentesFiltrados.length} {componentesFiltrados.length === 1 ? (isEs ? "componente" : isEn ? "component" : "componente") : (isEs ? "componentes" : isEn ? "components" : "componentes")}
@@ -235,13 +244,17 @@ export default function ComponentRegistryPage() {
- {isEn ? "No components found" : "Nenhum componente encontrado"}
+ {isEs ? "No se encontraron componentes" : isEn ? "No components found" : "Nenhum componente encontrado"}
- {isEn ? "Search for a more general term or return to all categories." : "Busque por um termo mais geral ou volte para todas as categorias."}
+ {isEs
+ ? "Busque por un término más general o regrese a todas las categorías."
+ : isEn
+ ? "Search for a more general term or return to all categories."
+ : "Busque por um termo mais geral ou volte para todas as categorias."}
- {isEn ? "Clear filters" : "Limpar filtros"}
+ {isEs ? "Limpiar filtros" : isEn ? "Clear filters" : "Limpar filtros"}
) : (
@@ -261,7 +274,7 @@ export default function ComponentRegistryPage() {
{comp.oficial && (
- {isEn ? "Official" : "Oficial"}
+ {isEs ? "Oficial" : isEn ? "Official" : "Oficial"}
)}
@@ -285,12 +298,12 @@ export default function ComponentRegistryPage() {
type="button"
onClick={() => copiarComando(comp.id, cliCmd)}
className="code-panel flex w-full min-h-11 items-center justify-between gap-3 rounded-lg px-3 py-2 text-left font-mono text-xs transition-colors hover:border-white/30"
- aria-label={isEn ? `Copy command to add ${comp.name}` : `Copiar comando para adicionar ${comp.name}`}
+ aria-label={isEs ? `Copiar comando para agregar ${comp.name}` : isEn ? `Copy command to add ${comp.name}` : `Copiar comando para adicionar ${comp.name}`}
>
{cliCmd}
{foiCopiado ? : }
- {foiCopiado ? (isEn ? "Command copied" : "Comando copiado") : ""}
+ {foiCopiado ? (isEs ? "Comando copiado" : isEn ? "Command copied" : "Comando copiado") : ""}
Chips:
@@ -306,14 +319,14 @@ export default function ComponentRegistryPage() {
className={`btn btn-sm ${selecionado ? "btn-primary" : "btn-ghost"}`}
>
{selecionado ? : }
- {selecionado ? (isEn ? "In manifest" : "No manifesto") : (isEn ? "Add to manifest" : "Adicionar ao manifesto")}
+ {selecionado ? (isEs ? "En manifiesto" : isEn ? "In manifest" : "No manifesto") : (isEs ? "Agregar al manifiesto" : isEn ? "Add to manifest" : "Adicionar ao manifesto")}
@@ -329,12 +342,14 @@ export default function ComponentRegistryPage() {
-
{isEn ? "Manifest generator" : "Gerador de manifesto"}
+
{isEs ? "Generador de manifiesto" : isEn ? "Manifest generator" : "Gerador de manifesto"}
- {isEn ? "Your idf_component.yml" : "Seu idf_component.yml"}
+ {isEs ? "Su idf_component.yml" : isEn ? "Your idf_component.yml" : "Seu idf_component.yml"}
- {isEn ? (
+ {isEs ? (
+ <>Guarde el archivo en la carpeta main/ del proyecto y ESP-IDF descargará todas las bibliotecas durante la compilación.>
+ ) : isEn ? (
<>Save this file in your project's main/ folder and ESP-IDF will download all dependencies during build.>
) : (
<>Salve o arquivo na pasta main/ do projeto e o ESP-IDF baixa todas as bibliotecas na compilação.>
@@ -344,27 +359,27 @@ export default function ComponentRegistryPage() {
{manifestoCopiado ? : }
- {manifestoCopiado ? (isEn ? "Copied" : "Copiado") : (isEn ? "Copy YAML" : "Copiar YAML")}
+ {manifestoCopiado ? (isEs ? "Copiado" : isEn ? "Copied" : "Copiado") : (isEs ? "Copiar YAML" : isEn ? "Copy YAML" : "Copiar YAML")}
- {isEn ? "Download .yml" : "Baixar .yml"}
+ {isEs ? "Descargar .yml" : isEn ? "Download .yml" : "Baixar .yml"}
{componentesSelecionados.length > 0 && (
setComponentesSelecionados([])} className="btn btn-ghost btn-sm">
- {isEn ? "Clear" : "Limpar"}
+ {isEs ? "Limpiar" : isEn ? "Clear" : "Limpar"}
)}
-
{manifestoYaml}
+
{manifestoYaml}
{componentesSelecionados.length > 0 && (
-
+
{componentesSelecionados.map((id) => {
const c = COMPONENTES_REGISTRY.find((item) => item.id === id);
if (!c) return null;
@@ -375,7 +390,7 @@ export default function ComponentRegistryPage() {
type="button"
onClick={() => toggleSelecaoManifesto(id)}
className="-my-2 grid h-11 w-11 place-items-center rounded-full hover:bg-accent/15"
- aria-label={isEn ? `Remove ${c.name} from manifest` : `Remover ${c.name} do manifesto`}
+ aria-label={isEs ? `Eliminar ${c.name} del manifiesto` : isEn ? `Remove ${c.name} from manifest` : `Remover ${c.name} do manifesto`}
>
@@ -388,7 +403,7 @@ export default function ComponentRegistryPage() {
-
+
{GUIA_COMPONENT_MANAGER.vantagens.map((v) => (
@@ -401,7 +416,7 @@ export default function ComponentRegistryPage() {
- {isEn ? "Essential commands" : "Comandos essenciais"}
+ {isEs ? "Comandos esenciales" : isEn ? "Essential commands" : "Comandos essenciais"}
{GUIA_COMPONENT_MANAGER.comandosCli.map((cmd, i) => (
@@ -416,7 +431,7 @@ export default function ComponentRegistryPage() {
className="inline-flex min-h-11 shrink-0 items-center gap-1.5 self-start rounded-full border border-white/15 px-4 text-sm font-medium text-[#e9eef4] transition-colors hover:bg-white/10 md:self-auto"
>
{copiadoId === `cmd-${i}` ? : }
- {copiadoId === `cmd-${i}` ? (isEn ? "Copied" : "Copiado") : (isEn ? "Copy" : "Copiar")}
+ {copiadoId === `cmd-${i}` ? (isEs ? "Copiado" : isEn ? "Copied" : "Copiado") : (isEs ? "Copiar" : isEn ? "Copy" : "Copiar")}
))}
@@ -425,10 +440,17 @@ export default function ComponentRegistryPage() {
- {isEn ? "Semantic versioning in ESP-IDF" : "Versionamento semântico no ESP-IDF"}
+ {isEs ? "Versionamiento semántico en ESP-IDF" : isEn ? "Semantic versioning in ESP-IDF" : "Versionamento semântico no ESP-IDF"}
- {isEn ? (
+ {isEs ? (
+ <>
+ ^1.2.0: acepta actualizaciones menores y correcciones (1.2.1, 1.3.0), pero bloquea cambios importantes de API (2.0.0). Recomendado.
+ ~1.2.0: acepta solo correcciones de errores (1.2.1, 1.2.5).
+ 1.2.0: bloquea en esta versión exacta.
+ *: acepta cualquier versión. Evite en producción.
+ >
+ ) : isEn ? (
<>
^1.2.0: accepts minor updates and bug fixes (1.2.1, 1.3.0), but blocks breaking API changes (2.0.0). Recommended.
~1.2.0: accepts patch releases only (1.2.1, 1.2.5).
diff --git a/app/[locale]/diagnostico/layout.jsx b/app/[locale]/diagnostico/layout.jsx
index 9032274..b3b7c85 100644
--- a/app/[locale]/diagnostico/layout.jsx
+++ b/app/[locale]/diagnostico/layout.jsx
@@ -3,16 +3,32 @@ import { paginaMeta, jsonLd } from "@/lib/seo";
export async function generateMetadata({ params }) {
const { locale } = await params;
const isEn = locale === "en";
+ const isEs = locale === "es";
return paginaMeta({
- titulo: isEn
+ titulo: isEs
+ ? "Diagnóstico de errores comunes en ESP32: Guía profesional"
+ : isEn
? "Common ESP32 Errors & Diagnostics: Professional Guide"
: "Diagnóstico de Erros Comuns no ESP32: Guia Profissional",
- descricao: isEn
+ descricao: isEs
+ ? "Guía técnica definitiva para identificar y solucionar Brownout, Guru Meditation, Task Watchdog, Stack Overflow, fugas de memoria y fallas de arranque en ESP32 (Arduino y ESP-IDF)."
+ : 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
+ keywords: isEs
+ ? [
+ "errores comunes ESP32",
+ "brownout detector was triggered",
+ "guru meditation error",
+ "task watchdog got triggered",
+ "stack canary watchpoint triggered",
+ "cómo depurar ESP32",
+ "ESP32 reiniciando en bucle",
+ "failed to connect to esp32",
+ ]
+ : isEn
? [
"common ESP32 errors",
"brownout detector was triggered",
@@ -74,10 +90,30 @@ const faqsEn = [
},
];
+const faqsEs = [
+ {
+ pergunta: "¿Qué causa el error 'Brownout detector was triggered' en el ESP32?",
+ resposta: "Ocurre cuando la tensión en el pin 3V3 cae por debajo de 2.8V ~ 2.43V debido a picos rápidos de corriente de la radio Wi-Fi/BLE (que superan los 500mA), cables USB delgados o un regulador LDO inadecuado. La solución de ingeniería implica agregar condensadores Low-ESR de 10µF a 100µF cerca de VCC y usar reguladores LDO robustos de más de 600mA.",
+ },
+ {
+ pergunta: "¿Cómo solucionar el 'Guru Meditation Error (LoadProhibited)'?",
+ resposta: "LoadProhibited indica un intento de acceder a un puntero nulo (NULL) o inválido en la memoria. La solución consiste en utilizar Espressif Exception Decoder o addr2line para localizar la línea exacta en el código e implementar comprobaciones defensivas como if (puntero != NULL).",
+ },
+ {
+ pergunta: "¿Por qué se activa el Task Watchdog Timer (TWDT) en FreeRTOS?",
+ resposta: "El TWDT se activa cuando una tarea de mayor prioridad monopoliza la CPU en un bucle bloqueante (por ejemplo: while(1) sin vTaskDelay), impidiendo que la tarea IDLE alimente el watchdog del sistema operativo.",
+ },
+ {
+ pergunta: "¿Por qué fallan las conexiones HTTPS y TLS con el código -0x2700 en ESP32?",
+ resposta: "Porque al encenderse, el reloj interno del ESP32 comienza en el año 1970. El validador de certificados TLS considera inválido el certificado del servidor por estar en el 'futuro'. Sincronizar el reloj mediante SNTP (configTime) antes de la conexión resuelve el problema.",
+ },
+];
+
export default async function DiagnosticoLayout({ children, params }) {
const { locale } = await params;
const isEn = locale === "en";
- const faqs = isEn ? faqsEn : faqsPt;
+ const isEs = locale === "es";
+ const faqs = isEs ? faqsEs : isEn ? faqsEn : faqsPt;
return (
<>
@@ -86,8 +122,8 @@ export default async function DiagnosticoLayout({ children, params }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.trilha([
- { nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
- { nome: isEn ? "Diagnostics & Common Errors" : "Diagnóstico & Erros Comuns", caminho: isEn ? "/en/diagnostics" : "/diagnostico" },
+ { nome: isEs ? "Inicio" : isEn ? "Home" : "Início", caminho: isEs ? "/es" : isEn ? "/en" : "/" },
+ { nome: isEs ? "Diagnóstico y errores comunes" : isEn ? "Diagnostics & Common Errors" : "Diagnóstico & Erros Comuns", caminho: isEs ? "/es/diagnostico" : isEn ? "/en/diagnostics" : "/diagnostico" },
])
),
}}
@@ -105,11 +141,13 @@ export default async function DiagnosticoLayout({ children, params }) {
dangerouslySetInnerHTML={{
__html: JSON.stringify(
jsonLd.aplicacaoWeb({
- nome: isEn ? "ESP32 Error Diagnostics & Debugging Guide" : "Guia de Diagnóstico e Erros no ESP32",
- descricao: isEn
+ nome: isEs ? "Guía de diagnóstico y errores en ESP32" : isEn ? "ESP32 Error Diagnostics & Debugging Guide" : "Guia de Diagnóstico e Erros no ESP32",
+ descricao: isEs
+ ? "Herramienta y guía técnica de análisis de fallas, pánicos de CPU y decodificación de registros en ESP32."
+ : 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",
+ caminho: isEs ? "/es/diagnostico" : isEn ? "/en/diagnostics" : "/diagnostico",
locale,
})
),
diff --git a/app/[locale]/diagnostico/page.jsx b/app/[locale]/diagnostico/page.jsx
index 0426a6c..3424bd1 100644
--- a/app/[locale]/diagnostico/page.jsx
+++ b/app/[locale]/diagnostico/page.jsx
@@ -7,6 +7,7 @@ import Footer from "@/components/Footer";
import PageHero from "@/components/PageHero";
import * as diagnosticoPt from "@/lib/diagnosticoData";
import * as diagnosticoEn from "@/lib/data/en/diagnostico";
+import * as diagnosticoEs from "@/lib/data/es/diagnostico";
import {
Activity,
AlertTriangle,
@@ -49,6 +50,13 @@ const EXEMPLOS_LOG_EN = [
["TLS Handshake", "mbedtls_ssl_handshake returned -0x2700\nThe certificate is not yet valid"],
];
+const EXEMPLOS_LOG_ES = [
+ ["Brownout", "Brownout detector was triggered\n\nrst:0xc (SW_CPU_RESET),boot:0x13"],
+ ["LoadProhibited", "Guru Meditation Error: Core 0 panic'ed (LoadProhibited).\nEXCVADDR: 0x00000000"],
+ ["Task watchdog", "Task watchdog got triggered. The following tasks/users did not reset the watchdog: - IDLE0"],
+ ["Handshake TLS", "mbedtls_ssl_handshake returned -0x2700\nThe certificate is not yet valid"],
+];
+
const GRAVIDADE = {
"Crítica": "text-danger",
"Critical": "text-danger",
@@ -56,6 +64,7 @@ const GRAVIDADE = {
High: "text-gold",
"Média": "text-accent",
Medium: "text-accent",
+ Media: "text-accent",
};
const CHECKLISTS_PT = [
@@ -104,12 +113,36 @@ const CHECKLISTS_EN = [
},
];
+const CHECKLISTS_ES = [
+ {
+ icon: Zap,
+ titulo: "Hardware y alimentación",
+ itens: [
+ "Condensador de 10 µF a 100 µF de bajo ESR en VCC, más uno cerámico de 100 nF junto al chip.",
+ "Regulador LDO dimensionado para picos de 600 mA a 1 A, con margen térmico.",
+ "Ningún periférico forzando niveles lógicos en los pines de strapping durante el arranque.",
+ "Condensador de 10 µF o 1 µF en el pin EN (RST) para auto-reset estable durante la programación.",
+ ],
+ },
+ {
+ icon: Layers,
+ titulo: "Firmware y FreeRTOS",
+ itens: [
+ "Cada tarea de FreeRTOS cede la CPU con vTaskDelay, colas o semáforos.",
+ "Cada ISR marcada con IRAM_ATTR y sin llamadas bloqueantes.",
+ "Sin uso continuo de String de Arduino en bucles para evitar la fragmentación del heap.",
+ "Hora sincronizada mediante SNTP antes de abrir conexiones TLS/HTTPS.",
+ ],
+ },
+];
+
export default function DiagnosticoPage() {
const pathname = usePathname();
const isEn = pathname?.startsWith("/en");
- const { categoriasDiagnostico, sintomasRapidos, errosCatalogo, regrasDecodificadorLog } = isEn ? diagnosticoEn : diagnosticoPt;
- const exemplosLog = isEn ? EXEMPLOS_LOG_EN : EXEMPLOS_LOG_PT;
- const checklists = isEn ? CHECKLISTS_EN : CHECKLISTS_PT;
+ const isEs = pathname?.startsWith("/es");
+ const { categoriasDiagnostico, sintomasRapidos, errosCatalogo, regrasDecodificadorLog } = isEs ? diagnosticoEs : isEn ? diagnosticoEn : diagnosticoPt;
+ const exemplosLog = isEs ? EXEMPLOS_LOG_ES : isEn ? EXEMPLOS_LOG_EN : EXEMPLOS_LOG_PT;
+ const checklists = isEs ? CHECKLISTS_ES : isEn ? CHECKLISTS_EN : CHECKLISTS_PT;
const [termoBusca, setTermoBusca] = useState("");
const [categoriaAtiva, setCategoriaAtiva] = useState("todos");
@@ -139,7 +172,16 @@ export default function DiagnosticoPage() {
}
setResultadoLog(
- isEn
+ isEs
+ ? {
+ titulo: "Patrón de error no categorizado automáticamente",
+ tipo: "Desconocido",
+ explicacao:
+ "No identificamos una firma clásica de Guru Meditation o Brownout en este fragmento. Verifique que la velocidad de baudios (ej: 115200) sea correcta y si el encabezado 'rst:' o 'panic' está presente.",
+ acaoRecomendada:
+ "Busque palabras clave como 'Guru Meditation', 'rst:', 'LoadProhibited', 'watchdog' o 'panic' en la consola serie.",
+ }
+ : isEn
? {
titulo: "Error pattern not automatically classified",
tipo: "Unknown",
@@ -213,12 +255,15 @@ export default function DiagnosticoPage() {
- {isEn ? "Based on article by Ciência Embarcada" : "Baseado no artigo do Ciência Embarcada"}
+ {isEs ? "Basado en el artículo de Ciência Embarcada" : isEn ? "Based on article by Ciência Embarcada" : "Baseado no artigo do Ciência Embarcada"}
- {isEn ? "(opens in a new tab)" : "(abre em nova aba)"}
+ {isEs ? "(abre en una nueva pestaña)" : isEn ? "(opens in a new tab)" : "(abre em nova aba)"}
@@ -240,14 +285,14 @@ export default function DiagnosticoPage() {
- {isEn ? "Paste the log to see the cause" : "Cole o log e veja a causa"}
+ {isEs ? "Pegue el registro y descubra la causa" : isEn ? "Paste the log to see the cause" : "Cole o log e veja a causa"}
- {isEn ? "Copy the serial monitor output snippet. Diagnostics appear instantly." : "Copie o trecho do monitor serial. O diagnóstico aparece na hora."}
+ {isEs ? "Copie el fragmento del monitor serie. El diagnóstico aparece al instante." : isEn ? "Copy the serial monitor output snippet. Diagnostics appear instantly." : "Copie o trecho do monitor serial. O diagnóstico aparece na hora."}
-
{isEn ? "Test with" : "Testar com"}
+
{isEs ? "Probar con" : isEn ? "Test with" : "Testar com"}
{exemplosLog.map(([rotulo, texto]) => (
analisarLog(texto)} className="chip-btn">
{rotulo}
@@ -258,21 +303,21 @@ export default function DiagnosticoPage() {
- {isEn ? "Serial monitor log" : "Log do monitor serial"}
+ {isEs ? "Registro del monitor serie" : isEn ? "Serial monitor log" : "Log do monitor serial"}
@@ -286,8 +331,8 @@ export default function DiagnosticoPage() {
{resultadoLog.titulo}
{resultadoLog.tipo}
-
{isEn ? "Likely cause:" : "Causa provável:"} {resultadoLog.explicacao}
-
{isEn ? "What to do:" : "O que fazer:"} {resultadoLog.acaoRecomendada}
+
{isEs ? "Causa probable:" : isEn ? "Likely cause:" : "Causa provável:"} {resultadoLog.explicacao}
+
{isEs ? "Qué hacer:" : isEn ? "What to do:" : "O que fazer:"} {resultadoLog.acaoRecomendada}
)}
@@ -297,13 +342,13 @@ export default function DiagnosticoPage() {
- {isEn ? "Fault catalog" : "Catálogo de falhas"}
+ {isEs ? "Catálogo de fallas" : isEn ? "Fault catalog" : "Catálogo de falhas"}
- {isEn ? "Search by error, symptom, or serial message" : "Buscar por erro, sintoma ou mensagem serial"}
+ {isEs ? "Buscar por error, síntoma o mensaje serie" : isEn ? "Search by error, symptom, or serial message" : "Buscar por erro, sintoma ou mensagem serial"}
setTermoBusca(e.target.value)}
- placeholder={isEn ? "Search error, symptom, or message" : "Buscar erro, sintoma ou mensagem"}
+ placeholder={isEs ? "Buscar error, síntoma o mensaje" : isEn ? "Search error, symptom, or message" : "Buscar erro, sintoma ou mensagem"}
autoComplete="off"
className="field pl-12 pr-4 text-base"
/>
-
{isEn ? "Symptom" : "Sintoma"}
+
{isEs ? "Síntoma" : isEn ? "Symptom" : "Sintoma"}
{sintomasRapidos.map((sintoma) => (
setSintomaAtivo(sintomaAtivo === sintoma.id ? null : sintoma.id)} aria-pressed={sintomaAtivo === sintoma.id} className="chip-btn">
@@ -330,11 +375,11 @@ export default function DiagnosticoPage() {
-
{isEn ? "Area" : "Área"}
+
{isEs ? "Área" : isEn ? "Area" : "Área"}
setCategoriaAtiva("todos")} aria-pressed={categoriaAtiva === "todos"} className="chip-btn">
- {isEn ? "All" : "Todas"}
+ {isEs ? "Todas" : isEn ? "All" : "Todas"}
{errosCatalogo.length}
{categoriasDiagnostico.map((cat) => {
@@ -354,13 +399,13 @@ export default function DiagnosticoPage() {
- {errosFiltrados.length} {errosFiltrados.length === 1 ? (isEn ? "fault" : "falha") : (isEn ? "faults" : "falhas")}
- {totalFiltros > 0 && {isEn ? " with current filters" : " com os filtros atuais"} }
+ {errosFiltrados.length} {errosFiltrados.length === 1 ? (isEs ? "falla" : isEn ? "fault" : "falha") : (isEs ? "fallas" : isEn ? "faults" : "falhas")}
+ {totalFiltros > 0 && {isEs ? " con los filtros actuales" : isEn ? " with current filters" : " com os filtros atuais"} }
{temFiltroAtivo && (
- {isEn ? "Reset filters" : "Redefinir filtros"}
+ {isEs ? "Restablecer filtros" : isEn ? "Reset filters" : "Redefinir filtros"}
)}
@@ -370,13 +415,13 @@ export default function DiagnosticoPage() {
- {isEn ? "No faults matching these filters" : "Nenhuma falha com esses filtros"}
+ {isEs ? "Ninguna falla coincide con estos filtros" : isEn ? "No faults matching these filters" : "Nenhuma falha com esses filtros"}
- {isEn ? "Search for a more general term or remove selected symptom and area." : "Busque por um termo mais geral ou remova o sintoma e a área selecionados."}
+ {isEs ? "Busque por un término más general o elimine el síntoma y el área seleccionados." : isEn ? "Search for a more general term or remove selected symptom and area." : "Busque por um termo mais geral ou remova o sintoma e a área selecionados."}
- {isEn ? "Clear filters" : "Limpar filtros"}
+ {isEs ? "Limpiar filtros" : isEn ? "Clear filters" : "Limpar filtros"}
) : (
@@ -384,7 +429,7 @@ export default function DiagnosticoPage() {
const estaAberto = cardAberto === erro.id;
const lang = linguagemCodigo[erro.id] || "arduino";
const codigoAtual =
- lang === "arduino" ? erro.codigoArduino : lang === "espidf" ? erro.codigoEspIdf : erro.codigoMicroPython || (isEn ? "# Example not applicable" : "# Exemplo não aplicável");
+ lang === "arduino" ? erro.codigoArduino : lang === "espidf" ? erro.codigoEspIdf : erro.codigoMicroPython || (isEs ? "# Ejemplo no aplicable" : isEn ? "# Example not applicable" : "# Exemplo não aplicável");
const catInfo = categoriasDiagnostico.find((c) => c.id === erro.categoria);
const IconeCat = ICONES[catInfo?.icone] ?? AlertTriangle;
@@ -405,7 +450,9 @@ export default function DiagnosticoPage() {
{catInfo?.nome}
- {isEn ? (
+ {isEs ? (
+ <>Severidad {erro.gravidade.toLowerCase()}>
+ ) : isEn ? (
<>Severity: {erro.gravidade.toLowerCase()}>
) : (
<>Gravidade {erro.gravidade.toLowerCase()}>
@@ -427,11 +474,11 @@ export default function DiagnosticoPage() {
- {isEn ? "How it appears in serial monitor" : "Como aparece no monitor serial"}
+ {isEs ? "Cómo aparece en el monitor serie" : isEn ? "How it appears in serial monitor" : "Como aparece no monitor serial"}
copiarParaClipboard(erro.logSerial, `log-${erro.id}`)} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong">
{copiadoId === `log-${erro.id}` ? : }
- {copiadoId === `log-${erro.id}` ? (isEn ? "Copied" : "Copiado") : (isEn ? "Copy log" : "Copiar log")}
+ {copiadoId === `log-${erro.id}` ? (isEs ? "Copiado" : isEn ? "Copied" : "Copiado") : (isEs ? "Copiar registro" : isEn ? "Copy log" : "Copiar log")}
@@ -442,7 +489,7 @@ export default function DiagnosticoPage() {
- {isEn ? "Root cause" : "Causa raiz"}
+ {isEs ? "Causa raíz" : isEn ? "Root cause" : "Causa raiz"}
{erro.causaRaiz}
@@ -451,17 +498,17 @@ export default function DiagnosticoPage() {
- {isEn ? "The common workaround" : "O remendo comum"}
+ {isEs ? "La solución improvisada común" : isEn ? "The common workaround" : "O remendo comum"}
{erro.gambiarra.titulo}
{erro.gambiarra.descricao}
-
{isEn ? "Consequence:" : "Consequência:"} {erro.gambiarra.consequencia}
+
{isEs ? "Consecuencia:" : isEn ? "Consequence:" : "Consequência:"} {erro.gambiarra.consequencia}
- {isEn ? "The engineering solution" : "A solução de engenharia"}
+ {isEs ? "La solución de ingeniería" : isEn ? "The engineering solution" : "A solução de engenharia"}
{erro.solucaoEngenharia.titulo}
@@ -477,7 +524,7 @@ export default function DiagnosticoPage() {
-
+
{[
["arduino", "Arduino C++"],
["espidf", "ESP-IDF (C)"],
@@ -490,7 +537,7 @@ export default function DiagnosticoPage() {
copiarParaClipboard(codigoAtual, `code-${erro.id}`)} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong">
{copiadoId === `code-${erro.id}` ? : }
- {copiadoId === `code-${erro.id}` ? (isEn ? "Copied" : "Copiado") : (isEn ? "Copy code" : "Copiar código")}
+ {copiadoId === `code-${erro.id}` ? (isEs ? "Copiado" : isEn ? "Copied" : "Copiado") : (isEs ? "Copiar código" : isEn ? "Copy code" : "Copiar código")}
@@ -500,7 +547,7 @@ export default function DiagnosticoPage() {
-
{isEn ? "Engineering tip:" : "Dica de engenharia:"} {erro.dicaPro}
+
{isEs ? "Consejo de ingeniería:" : isEn ? "Engineering tip:" : "Dica de engenharia:"} {erro.dicaPro}
)}
@@ -513,10 +560,10 @@ export default function DiagnosticoPage() {
- {isEn ? "Pre-deployment checklist" : "Checklist antes de ir para campo"}
+ {isEs ? "Lista de verificación previa a campo" : isEn ? "Pre-deployment checklist" : "Checklist antes de ir para campo"}
- {isEn ? "Essential items to validate on hardware and firmware before shipping a product." : "Itens essenciais para validar no hardware e no firmware antes de entregar o produto."}
+ {isEs ? "Elementos esenciales para validar en hardware y firmware antes de entregar el producto." : isEn ? "Essential items to validate on hardware and firmware before shipping a product." : "Itens essenciais para validar no hardware e no firmware antes de entregar o produto."}
{checklists.map(({ icon: Icon, titulo, itens }) => (
diff --git a/app/[locale]/frameworks/[key]/page.jsx b/app/[locale]/frameworks/[key]/page.jsx
index 958398e..39f17e4 100644
--- a/app/[locale]/frameworks/[key]/page.jsx
+++ b/app/[locale]/frameworks/[key]/page.jsx
@@ -7,14 +7,16 @@ import { notFound } from "next/navigation";
import CodeExample from "@/components/CodeExample";
import frameworksDataPt from "@/public/frameworks.json";
import frameworksDataEn from "@/data/en/frameworks.json";
+import frameworksDataEs from "@/data/es/frameworks.json";
import { getLocalizedHref } from "@/i18n/links";
import { ArrowRight, ArrowUpRight, BookOpen, Box, Check, Code, Layers } from "lucide-react";
export default async function FrameworkDetail({ params }) {
const { locale, key } = await params;
const isEn = locale === "en";
- const currentLocale = isEn ? "en" : "pt";
- const frameworksData = isEn ? frameworksDataEn : frameworksDataPt;
+ const isEs = locale === "es";
+ const currentLocale = isEs ? "es" : isEn ? "en" : "pt";
+ const frameworksData = isEs ? frameworksDataEs : isEn ? frameworksDataEn : frameworksDataPt;
const framework = frameworksData[key];
if (!framework) {
@@ -36,8 +38,8 @@ export default async function FrameworkDetail({ params }) {
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd.trilha([
- { nome: isEn ? "Home" : "Início", caminho: isEn ? "/en" : "/" },
- { nome: isEn ? "Frameworks" : "Frameworks", caminho: getLocalizedHref("/frameworks", currentLocale) },
+ { nome: isEs ? "Inicio" : isEn ? "Home" : "Início", caminho: isEs ? "/es" : isEn ? "/en" : "/" },
+ { nome: "Frameworks", caminho: getLocalizedHref("/frameworks", currentLocale) },
{ nome: framework.nome, caminho: getLocalizedHref(`/frameworks/${encodeURIComponent(key)}`, currentLocale) },
])),
}}
@@ -48,7 +50,8 @@ export default async function FrameworkDetail({ params }) {
- {isEn ? "Official documentation" : "Documentação oficial"}
+ {isEs ? "Documentación oficial" : isEn ? "Official documentation" : "Documentação oficial"}
)}
{framework.repositorio && (
- {isEn ? "GitHub repository" : "Repositório no GitHub"}
+ {isEs ? "Repositorio en GitHub" : isEn ? "GitHub repository" : "Repositório no GitHub"}
)}
-
+
-
{isEn ? "Language" : "Linguagem"}
+ {isEs ? "Lenguaje" : isEn ? "Language" : "Linguagem"}
{framework.linguagem}
{framework.nome_completo}
-
{isEn ? "Category" : "Categoria"}
+ {isEs ? "Categoría" : isEn ? "Category" : "Categoria"}
{framework.tipo}
-
{isEn ? "Main purpose" : "Objetivo principal"}
+ {isEs ? "Objetivo principal" : isEn ? "Main purpose" : "Objetivo principal"}
{framework.funcao_principal}
-
+
- {isEn ? "Key features" : "Recursos de destaque"}
+ {isEs ? "Funciones destacadas" : isEn ? "Key features" : "Recursos de destaque"}