feat(i18n): Spanish localization (phase 4)
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:
1 parent
0940814522
commit
660b28c09d
72 files changed
+17848
-947
No files matched your search
@@ -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() {
|
||||
<main id="conteudo">
|
||||
<PageHero
|
||||
isEn={isEn}
|
||||
trail={[{ label: isEn ? "Diagnostics" : "Diagnóstico" }]}
|
||||
isEs={isEs}
|
||||
trail={[{ label: isEs ? "Diagnóstico" : isEn ? "Diagnostics" : "Diagnóstico" }]}
|
||||
icon={AlertTriangle}
|
||||
label={isEn ? "Firmware Debugging" : "Depuração de firmware"}
|
||||
title={isEn ? "ESP32 Diagnostics and Crash Decoder" : "Diagnóstico de falhas do ESP32"}
|
||||
label={isEs ? "Depuración de firmware" : isEn ? "Firmware Debugging" : "Depuração de firmware"}
|
||||
title={isEs ? "Diagnóstico de fallas del ESP32" : isEn ? "ESP32 Diagnostics and Crash Decoder" : "Diagnóstico de falhas do ESP32"}
|
||||
lede={
|
||||
isEn
|
||||
isEs
|
||||
? "Comprenda la causa raíz de las fallas por Brownout, Guru Meditation, Watchdog y Stack Overflow, y aplique la corrección de ingeniería en lugar de soluciones improvisadas."
|
||||
: isEn
|
||||
? "Understand the root cause of Brownout, Guru Meditation, Watchdog, and Stack Overflow crashes, and apply engineering solutions instead of workarounds."
|
||||
: "Entenda a causa de Brownout, Guru Meditation, Watchdog e Stack Overflow, e aplique a correção de engenharia, não o remendo."
|
||||
}
|
||||
@@ -229,9 +274,9 @@ export default function DiagnosticoPage() {
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong"
|
||||
>
|
||||
{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"}
|
||||
<ExternalLink className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="sr-only"> {isEn ? "(opens in a new tab)" : "(abre em nova aba)"}</span>
|
||||
<span className="sr-only"> {isEs ? "(abre en una nueva pestaña)" : isEn ? "(opens in a new tab)" : "(abre em nova aba)"}</span>
|
||||
</a>
|
||||
</PageHero>
|
||||
|
||||
@@ -240,14 +285,14 @@ export default function DiagnosticoPage() {
|
||||
<div className="flex flex-col justify-between gap-4 md:flex-row md:items-end">
|
||||
<div className="max-w-xl">
|
||||
<h2 id="decodificador-titulo" className="section-title">
|
||||
{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"}
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-muted">
|
||||
{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."}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-sm text-muted">{isEn ? "Test with" : "Testar com"}</span>
|
||||
<span className="text-sm text-muted">{isEs ? "Probar con" : isEn ? "Test with" : "Testar com"}</span>
|
||||
{exemplosLog.map(([rotulo, texto]) => (
|
||||
<button key={rotulo} type="button" onClick={() => analisarLog(texto)} className="chip-btn">
|
||||
{rotulo}
|
||||
@@ -258,21 +303,21 @@ export default function DiagnosticoPage() {
|
||||
|
||||
<div className="relative mt-5">
|
||||
<label htmlFor="log-serial" className="sr-only">
|
||||
{isEn ? "Serial monitor log" : "Log do monitor serial"}
|
||||
{isEs ? "Registro del monitor serie" : isEn ? "Serial monitor log" : "Log do monitor serial"}
|
||||
</label>
|
||||
<textarea
|
||||
id="log-serial"
|
||||
rows={4}
|
||||
value={logInput}
|
||||
onChange={(e) => analisarLog(e.target.value)}
|
||||
placeholder={isEn ? "Paste serial monitor log here" : "Cole o log do monitor serial aqui"}
|
||||
placeholder={isEs ? "Pegue el registro del monitor serie aquí" : isEn ? "Paste serial monitor log here" : "Cole o log do monitor serial aqui"}
|
||||
spellCheck={false}
|
||||
className="code-panel field !min-h-32 w-full resize-y !rounded-xl !border-white/10 !bg-[var(--code-bg)] p-4 font-mono text-sm !text-[#e9eef4] placeholder:!text-[#a6afbd]"
|
||||
/>
|
||||
{logInput && (
|
||||
<button type="button" onClick={() => analisarLog("")} className="absolute right-3 top-3 inline-flex min-h-11 items-center gap-1 rounded-full border border-white/15 px-3 text-xs font-medium text-[#e9eef4] hover:bg-white/10">
|
||||
<X className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
{isEn ? "Clear" : "Limpar"}
|
||||
{isEs ? "Limpiar" : isEn ? "Clear" : "Limpar"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -286,8 +331,8 @@ export default function DiagnosticoPage() {
|
||||
<h3 className="font-semibold text-ink">{resultadoLog.titulo}</h3>
|
||||
<span className="chip">{resultadoLog.tipo}</span>
|
||||
</div>
|
||||
<p><strong className="font-semibold text-ink">{isEn ? "Likely cause:" : "Causa provável:"}</strong> {resultadoLog.explicacao}</p>
|
||||
<p><strong className="font-semibold text-ink">{isEn ? "What to do:" : "O que fazer:"}</strong> {resultadoLog.acaoRecomendada}</p>
|
||||
<p><strong className="font-semibold text-ink">{isEs ? "Causa probable:" : isEn ? "Likely cause:" : "Causa provável:"}</strong> {resultadoLog.explicacao}</p>
|
||||
<p><strong className="font-semibold text-ink">{isEs ? "Qué hacer:" : isEn ? "What to do:" : "O que fazer:"}</strong> {resultadoLog.acaoRecomendada}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -297,13 +342,13 @@ export default function DiagnosticoPage() {
|
||||
|
||||
<section className="shell section pt-12 sm:pt-14" aria-labelledby="catalogo-titulo">
|
||||
<h2 id="catalogo-titulo" className="section-title">
|
||||
{isEn ? "Fault catalog" : "Catálogo de falhas"}
|
||||
{isEs ? "Catálogo de fallas" : isEn ? "Fault catalog" : "Catálogo de falhas"}
|
||||
</h2>
|
||||
|
||||
<div className="panel mt-6 p-5 sm:p-6">
|
||||
<div className="relative">
|
||||
<label htmlFor="busca-erro" className="sr-only">
|
||||
{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"}
|
||||
</label>
|
||||
<Search className="pointer-events-none absolute left-4 top-1/2 h-5 w-5 -translate-y-1/2 text-muted" aria-hidden="true" />
|
||||
<input
|
||||
@@ -312,14 +357,14 @@ export default function DiagnosticoPage() {
|
||||
type="search"
|
||||
value={termoBusca}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
<h3 className="text-sm font-semibold text-ink">{isEn ? "Symptom" : "Sintoma"}</h3>
|
||||
<h3 className="text-sm font-semibold text-ink">{isEs ? "Síntoma" : isEn ? "Symptom" : "Sintoma"}</h3>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
{sintomasRapidos.map((sintoma) => (
|
||||
<button key={sintoma.id} type="button" onClick={() => setSintomaAtivo(sintomaAtivo === sintoma.id ? null : sintoma.id)} aria-pressed={sintomaAtivo === sintoma.id} className="chip-btn">
|
||||
@@ -330,11 +375,11 @@ export default function DiagnosticoPage() {
|
||||
</div>
|
||||
|
||||
<div className="mt-5 border-t border-line pt-5">
|
||||
<h3 className="text-sm font-semibold text-ink">{isEn ? "Area" : "Área"}</h3>
|
||||
<h3 className="text-sm font-semibold text-ink">{isEs ? "Área" : isEn ? "Area" : "Área"}</h3>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<button type="button" onClick={() => setCategoriaAtiva("todos")} aria-pressed={categoriaAtiva === "todos"} className="chip-btn">
|
||||
<LayoutGrid className="h-4 w-4" aria-hidden="true" />
|
||||
{isEn ? "All" : "Todas"}
|
||||
{isEs ? "Todas" : isEn ? "All" : "Todas"}
|
||||
<span className="tabular-nums opacity-80">{errosCatalogo.length}</span>
|
||||
</button>
|
||||
{categoriasDiagnostico.map((cat) => {
|
||||
@@ -354,13 +399,13 @@ export default function DiagnosticoPage() {
|
||||
|
||||
<div className="mt-6 flex flex-wrap items-center justify-between gap-3">
|
||||
<p role="status" aria-live="polite" className="text-sm font-semibold text-ink">
|
||||
{errosFiltrados.length} {errosFiltrados.length === 1 ? (isEn ? "fault" : "falha") : (isEn ? "faults" : "falhas")}
|
||||
{totalFiltros > 0 && <span className="font-normal text-muted">{isEn ? " with current filters" : " com os filtros atuais"}</span>}
|
||||
{errosFiltrados.length} {errosFiltrados.length === 1 ? (isEs ? "falla" : isEn ? "fault" : "falha") : (isEs ? "fallas" : isEn ? "faults" : "falhas")}
|
||||
{totalFiltros > 0 && <span className="font-normal text-muted">{isEs ? " con los filtros actuales" : isEn ? " with current filters" : " com os filtros atuais"}</span>}
|
||||
</p>
|
||||
{temFiltroAtivo && (
|
||||
<button type="button" onClick={limparTodosFiltros} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong">
|
||||
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
||||
{isEn ? "Reset filters" : "Redefinir filtros"}
|
||||
{isEs ? "Restablecer filtros" : isEn ? "Reset filters" : "Redefinir filtros"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -370,13 +415,13 @@ export default function DiagnosticoPage() {
|
||||
<div className="card px-6 py-14 text-center">
|
||||
<HelpCircle className="mx-auto h-10 w-10 text-muted" aria-hidden="true" />
|
||||
<h3 className="mt-4 font-display text-xl font-semibold text-ink">
|
||||
{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"}
|
||||
</h3>
|
||||
<p className="mx-auto mt-2 max-w-md text-sm text-muted">
|
||||
{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."}
|
||||
</p>
|
||||
<button type="button" onClick={limparTodosFiltros} className="btn btn-primary mt-6">
|
||||
{isEn ? "Clear filters" : "Limpar filtros"}
|
||||
{isEs ? "Limpiar filtros" : isEn ? "Clear filters" : "Limpar filtros"}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
@@ -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}
|
||||
</span>
|
||||
<span className={`chip ${GRAVIDADE[erro.gravidade] ?? "text-accent"}`}>
|
||||
{isEn ? (
|
||||
{isEs ? (
|
||||
<>Severidad {erro.gravidade.toLowerCase()}</>
|
||||
) : isEn ? (
|
||||
<>Severity: {erro.gravidade.toLowerCase()}</>
|
||||
) : (
|
||||
<>Gravidade {erro.gravidade.toLowerCase()}</>
|
||||
@@ -427,11 +474,11 @@ export default function DiagnosticoPage() {
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h4 className="flex items-center gap-2 text-sm font-semibold text-ink">
|
||||
<Terminal className="h-4 w-4 text-accent" aria-hidden="true" />
|
||||
{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"}
|
||||
</h4>
|
||||
<button type="button" onClick={() => 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}` ? <Check className="h-4 w-4 text-ok" aria-hidden="true" /> : <Copy className="h-4 w-4" aria-hidden="true" />}
|
||||
{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")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="code-panel mt-1">
|
||||
@@ -442,7 +489,7 @@ export default function DiagnosticoPage() {
|
||||
<div>
|
||||
<h4 className="flex items-center gap-2 text-sm font-semibold text-ink">
|
||||
<Cpu className="h-4 w-4 text-accent" aria-hidden="true" />
|
||||
{isEn ? "Root cause" : "Causa raiz"}
|
||||
{isEs ? "Causa raíz" : isEn ? "Root cause" : "Causa raiz"}
|
||||
</h4>
|
||||
<p className="callout mt-2">{erro.causaRaiz}</p>
|
||||
</div>
|
||||
@@ -451,17 +498,17 @@ export default function DiagnosticoPage() {
|
||||
<div className="callout callout-danger">
|
||||
<p className="flex items-center gap-2 font-semibold text-ink">
|
||||
<XCircle className="h-4 w-4 shrink-0 text-danger" aria-hidden="true" />
|
||||
{isEn ? "The common workaround" : "O remendo comum"}
|
||||
{isEs ? "La solución improvisada común" : isEn ? "The common workaround" : "O remendo comum"}
|
||||
</p>
|
||||
<p className="mt-2 font-medium text-ink">{erro.gambiarra.titulo}</p>
|
||||
<p className="mt-1">{erro.gambiarra.descricao}</p>
|
||||
<p className="mt-2"><strong className="font-semibold text-danger">{isEn ? "Consequence:" : "Consequência:"}</strong> {erro.gambiarra.consequencia}</p>
|
||||
<p className="mt-2"><strong className="font-semibold text-danger">{isEs ? "Consecuencia:" : isEn ? "Consequence:" : "Consequência:"}</strong> {erro.gambiarra.consequencia}</p>
|
||||
</div>
|
||||
|
||||
<div className="callout callout-ok">
|
||||
<p className="flex items-center gap-2 font-semibold text-ink">
|
||||
<CheckCircle2 className="h-4 w-4 shrink-0 text-ok" aria-hidden="true" />
|
||||
{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"}
|
||||
</p>
|
||||
<p className="mt-2 font-medium text-ink">{erro.solucaoEngenharia.titulo}</p>
|
||||
<ul className="mt-2 space-y-1.5">
|
||||
@@ -477,7 +524,7 @@ export default function DiagnosticoPage() {
|
||||
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div role="group" aria-label={isEn ? "Example language" : "Linguagem do exemplo"} className="flex flex-wrap gap-2">
|
||||
<div role="group" aria-label={isEs ? "Lenguaje del ejemplo" : isEn ? "Example language" : "Linguagem do exemplo"} className="flex flex-wrap gap-2">
|
||||
{[
|
||||
["arduino", "Arduino C++"],
|
||||
["espidf", "ESP-IDF (C)"],
|
||||
@@ -490,7 +537,7 @@ export default function DiagnosticoPage() {
|
||||
</div>
|
||||
<button type="button" onClick={() => 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}` ? <Check className="h-4 w-4 text-ok" aria-hidden="true" /> : <Copy className="h-4 w-4" aria-hidden="true" />}
|
||||
{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")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="code-panel mt-3">
|
||||
@@ -500,7 +547,7 @@ export default function DiagnosticoPage() {
|
||||
|
||||
<div className="callout callout-warn flex items-start gap-3">
|
||||
<Wrench className="mt-0.5 h-4 w-4 shrink-0 text-gold" aria-hidden="true" />
|
||||
<p><strong className="font-semibold text-ink">{isEn ? "Engineering tip:" : "Dica de engenharia:"}</strong> {erro.dicaPro}</p>
|
||||
<p><strong className="font-semibold text-ink">{isEs ? "Consejo de ingeniería:" : isEn ? "Engineering tip:" : "Dica de engenharia:"}</strong> {erro.dicaPro}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -513,10 +560,10 @@ export default function DiagnosticoPage() {
|
||||
|
||||
<section className="shell pb-20 md:pb-28" aria-labelledby="checklist-titulo">
|
||||
<h2 id="checklist-titulo" className="section-title">
|
||||
{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"}
|
||||
</h2>
|
||||
<p className="mt-3 max-w-2xl text-base text-muted">
|
||||
{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."}
|
||||
</p>
|
||||
<div className="mt-8 grid gap-4 md:grid-cols-2">
|
||||
{checklists.map(({ icon: Icon, titulo, itens }) => (
|
||||
|
||||
Reference in new issue
Block a user