fix(i18n): eliminate portuguese text leaks across english routes
- 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:
1 parent
c1bd2c7e11
commit
47c55515bb
35 files changed
+2353
-1874
No files matched your search
+63
-41
@@ -92,18 +92,20 @@ export function paginaMeta({ titulo, descricao, caminho, keywords = [], tipo = "
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
export const jsonLd = {
|
||||
siteESociedade() {
|
||||
siteESociedade(locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@graph": [
|
||||
{
|
||||
"@type": "WebSite",
|
||||
"@id": `${SITE.url}/#site`,
|
||||
url: SITE.url,
|
||||
url: `${SITE.url}/${locale}`,
|
||||
name: SITE.nome,
|
||||
inLanguage: "pt-BR",
|
||||
description:
|
||||
"Documentação do ecossistema ESP32 em português brasileiro: especificações, pinagem, comparação e seleção de séries.",
|
||||
inLanguage: isEn ? "en" : "pt-BR",
|
||||
description: isEn
|
||||
? "ESP32 ecosystem documentation: specifications, pinouts, comparison, and series selection."
|
||||
: "Documentação do ecossistema ESP32 em português brasileiro: especificações, pinagem, comparação e seleção de séries.",
|
||||
publisher: { "@id": `${SITE.url}/#organizacao` },
|
||||
},
|
||||
{
|
||||
@@ -147,44 +149,51 @@ export const jsonLd = {
|
||||
},
|
||||
|
||||
/** Documentação e ficha técnica da série como TechArticle para o Google. */
|
||||
serie(chave, serie) {
|
||||
serie(chave, serie, locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const prop = (nome, valor) =>
|
||||
valor ? { "@type": "PropertyValue", name: nome, value: String(valor) } : null;
|
||||
|
||||
const propriedades = [
|
||||
prop("Fabricante", "Espressif Systems"),
|
||||
prop("Arquitetura", serie.arquitetura),
|
||||
prop("Núcleos", serie.nucleos),
|
||||
prop("Frequência máxima", serie.frequencia),
|
||||
prop(isEn ? "Manufacturer" : "Fabricante", "Espressif Systems"),
|
||||
prop(isEn ? "Architecture" : "Arquitetura", serie.arquitetura),
|
||||
prop(isEn ? "Cores" : "Núcleos", serie.nucleos),
|
||||
prop(isEn ? "Max frequency" : "Frequência máxima", serie.frequencia),
|
||||
prop("SRAM", serie.memoria_sram),
|
||||
prop("RTC SRAM", serie.memoria_sram_rtc),
|
||||
prop("Flash suportada", serie.flash_suportada),
|
||||
prop("PSRAM suportada", serie.psram_suportada),
|
||||
prop(isEn ? "Supported Flash" : "Flash suportada", serie.flash_suportada),
|
||||
prop(isEn ? "Supported PSRAM" : "PSRAM suportada", serie.psram_suportada),
|
||||
prop("GPIO", serie.gpio),
|
||||
prop("Wi-Fi", serie.wifi),
|
||||
prop("Bluetooth", serie.bluetooth),
|
||||
prop("Zigbee / Thread / 802.15.4", serie.zigbee_thread),
|
||||
prop("Encapsulamento", serie.encapsulamento),
|
||||
prop("ESP-IDF mínimo", serie.esp_idf_minimo),
|
||||
prop(isEn ? "Package" : "Encapsulamento", serie.encapsulamento),
|
||||
prop(isEn ? "Minimum ESP-IDF" : "ESP-IDF mínimo", serie.esp_idf_minimo),
|
||||
].filter(Boolean);
|
||||
|
||||
const imagem = serie.placa
|
||||
? `${SITE.url}${serie.placa}`
|
||||
: `${SITE.url}/opengraph-image.png`;
|
||||
|
||||
const basePath = isEn ? `/en/series/${chave}` : `/series/${chave}`;
|
||||
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "TechArticle",
|
||||
"@id": `${SITE.url}/series/${chave}#artigo`,
|
||||
headline: `${serie.nome_completo || chave}: especificações e pinagem`,
|
||||
name: `${serie.nome_completo || chave} - Documentação Técnica e Especificações`,
|
||||
"@id": `${SITE.url}${basePath}#artigo`,
|
||||
headline: isEn
|
||||
? `${serie.nome_completo || chave}: specifications and pinout`
|
||||
: `${serie.nome_completo || chave}: especificações e pinagem`,
|
||||
name: isEn
|
||||
? `${serie.nome_completo || chave} - Technical Documentation and Specifications`
|
||||
: `${serie.nome_completo || chave} - Documentação Técnica e Especificações`,
|
||||
description: serie.descricao,
|
||||
image: [imagem],
|
||||
inLanguage: "pt-BR",
|
||||
inLanguage: isEn ? "en" : "pt-BR",
|
||||
datePublished: "2026-08-25T00:00:00.000Z",
|
||||
dateModified: new Date().toISOString(),
|
||||
mainEntityOfPage: `${SITE.url}/series/${chave}`,
|
||||
url: `${SITE.url}/series/${chave}`,
|
||||
mainEntityOfPage: `${SITE.url}${basePath}`,
|
||||
url: `${SITE.url}${basePath}`,
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: SITE.autor,
|
||||
@@ -211,20 +220,26 @@ export const jsonLd = {
|
||||
},
|
||||
|
||||
/** Documentação técnica de framework. */
|
||||
framework(chave, framework) {
|
||||
framework(chave, framework, locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const basePath = isEn ? `/en/frameworks/${encodeURIComponent(chave)}` : `/frameworks/${encodeURIComponent(chave)}`;
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "TechArticle",
|
||||
"@id": `${SITE.url}/frameworks/${encodeURIComponent(chave)}#artigo`,
|
||||
headline: `${framework.nome} para ESP32: Guia e Documentação`,
|
||||
name: `${framework.nome_completo || framework.nome} para ESP32`,
|
||||
"@id": `${SITE.url}${basePath}#artigo`,
|
||||
headline: isEn
|
||||
? `${framework.nome} for ESP32: Guide and Documentation`
|
||||
: `${framework.nome} para ESP32: Guia e Documentação`,
|
||||
name: isEn
|
||||
? `${framework.nome_completo || framework.nome} for ESP32`
|
||||
: `${framework.nome_completo || framework.nome} para ESP32`,
|
||||
description: framework.descricao,
|
||||
image: [`${SITE.url}/opengraph-image.png`],
|
||||
inLanguage: "pt-BR",
|
||||
inLanguage: isEn ? "en" : "pt-BR",
|
||||
datePublished: "2026-08-25T00:00:00.000Z",
|
||||
dateModified: new Date().toISOString(),
|
||||
mainEntityOfPage: `${SITE.url}/frameworks/${encodeURIComponent(chave)}`,
|
||||
url: `${SITE.url}/frameworks/${encodeURIComponent(chave)}`,
|
||||
mainEntityOfPage: `${SITE.url}${basePath}`,
|
||||
url: `${SITE.url}${basePath}`,
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: SITE.autor,
|
||||
@@ -247,25 +262,27 @@ export const jsonLd = {
|
||||
applicationCategory: "DeveloperApplication",
|
||||
operatingSystem: "Embedded",
|
||||
description: framework.descricao,
|
||||
url: framework.documentacao || `${SITE.url}/frameworks/${encodeURIComponent(chave)}`,
|
||||
url: framework.documentacao || `${SITE.url}${basePath}`,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
/** Glossário: marca cada verbete como termo definido. */
|
||||
glossario(categorias) {
|
||||
glossario(categorias, locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const basePath = isEn ? "/en/glossary" : "/glossario";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "DefinedTermSet",
|
||||
"@id": `${SITE.url}/glossario#glossario`,
|
||||
name: "Glossário Técnico do ESP32",
|
||||
inLanguage: "pt-BR",
|
||||
"@id": `${SITE.url}${basePath}#glossario`,
|
||||
name: isEn ? "ESP32 Technical Glossary" : "Glossário Técnico do ESP32",
|
||||
inLanguage: isEn ? "en" : "pt-BR",
|
||||
hasDefinedTerm: categorias.flatMap((cat) =>
|
||||
cat.termos.map((t) => ({
|
||||
"@type": "DefinedTerm",
|
||||
name: t.termo,
|
||||
description: t.definicao,
|
||||
inDefinedTermSet: `${SITE.url}/glossario#glossario`,
|
||||
inDefinedTermSet: `${SITE.url}${basePath}#glossario`,
|
||||
}))
|
||||
),
|
||||
};
|
||||
@@ -285,7 +302,8 @@ export const jsonLd = {
|
||||
},
|
||||
|
||||
/** Aplicação web / ferramenta interativa (Seletor, Comparador, Catálogo). */
|
||||
aplicacaoWeb({ nome, descricao, caminho }) {
|
||||
aplicacaoWeb({ nome, descricao, caminho, locale = "pt" }) {
|
||||
const isEn = locale === "en";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebApplication",
|
||||
@@ -296,7 +314,7 @@ export const jsonLd = {
|
||||
applicationCategory: "DeveloperApplication",
|
||||
operatingSystem: "All",
|
||||
browserRequirements: "Requires JavaScript",
|
||||
inLanguage: "pt-BR",
|
||||
inLanguage: isEn ? "en" : "pt-BR",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: SITE.autor,
|
||||
@@ -376,15 +394,19 @@ export const jsonLd = {
|
||||
},
|
||||
|
||||
/** Página Sobre com metadados E-E-A-T de autoria e reputação. */
|
||||
paginaSobre() {
|
||||
paginaSobre(locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const basePath = isEn ? "/en/about" : "/sobre";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "AboutPage",
|
||||
"@id": `${SITE.url}/sobre#sobre`,
|
||||
name: "Sobre o ESPDocs",
|
||||
description: "O ESPDocs é uma plataforma independente que reúne a documentação do ecossistema ESP32 em português brasileiro.",
|
||||
url: `${SITE.url}/sobre`,
|
||||
inLanguage: "pt-BR",
|
||||
"@id": `${SITE.url}${basePath}#sobre`,
|
||||
name: isEn ? "About ESPDocs" : "Sobre o ESPDocs",
|
||||
description: isEn
|
||||
? "ESPDocs is an independent platform organizing technical documentation of the ESP32 ecosystem."
|
||||
: "O ESPDocs é uma plataforma independente que reúne a documentação do ecossistema ESP32 em português brasileiro.",
|
||||
url: `${SITE.url}${basePath}`,
|
||||
inLanguage: isEn ? "en" : "pt-BR",
|
||||
mainEntity: {
|
||||
"@type": "Person",
|
||||
name: SITE.autor,
|
||||
|
||||
Reference in new issue
Block a user