feat(i18n): French and German localization (phase 5)
- Add full message catalogs and content for French (fr) and German (de) - Implement ASCII canonical slugs for French and German routes - Add language-specific search endpoints (/api/busca/fr, /api/busca/de) - Configure strict 404 responses for /fr/livro and /de/livro - Localize SEO, OpenGraph (fr_FR, de_DE), Twitter and JSON-LD schemas - Add localized code example comments across 14 examples for fr and de - Enable 'fr' and 'de' in ENABLED_LOCALES with root negotiation and redirect - Extend test suite in check-i18n, smoke-imagem and verifica-vazamentos Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
1 parent
7b673aeb4b
commit
7fce46b9e1
104 files changed
+33369
-686
No files matched your search
+68
-26
@@ -95,8 +95,11 @@ export const jsonLd = {
|
||||
siteESociedade(locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const orgAnchor = isEn ? "organization" : isEs ? "organizacion" : "organizacao";
|
||||
const authorAnchor = isEn ? "author" : "autor";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const orgAnchor = isEn ? "organization" : isEs ? "organizacion" : (isFr || isDe) ? "organisation" : "organizacao";
|
||||
const authorAnchor = isEn ? "author" : isFr ? "auteur" : "autor";
|
||||
const inLang = isEn ? "en" : isEs ? "es-419" : isFr ? "fr" : isDe ? "de" : "pt-BR";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@graph": [
|
||||
@@ -105,11 +108,15 @@ export const jsonLd = {
|
||||
"@id": `${SITE.url}/#site`,
|
||||
url: `${SITE.url}/${locale}`,
|
||||
name: SITE.nome,
|
||||
inLanguage: isEn ? "en" : isEs ? "es-419" : "pt-BR",
|
||||
inLanguage: inLang,
|
||||
description: isEn
|
||||
? "ESP32 ecosystem documentation: specifications, pinouts, comparison, and series selection."
|
||||
: isEs
|
||||
? "Documentación del ecosistema ESP32: especificaciones, patillaje, comparación y selección de series."
|
||||
: isFr
|
||||
? "Documentation de l'écosystème ESP32 : spécifications, brochages, comparaison et sélection de séries."
|
||||
: isDe
|
||||
? "Dokumentation des ESP32-Ökosystems: Spezifikationen, Pinbelegung, Vergleich und Serienauswahl."
|
||||
: "Documentação do ecossistema ESP32 em português brasileiro: especificações, pinagem, comparação e seleção de séries.",
|
||||
publisher: { "@id": `${SITE.url}/#${orgAnchor}` },
|
||||
},
|
||||
@@ -157,32 +164,35 @@ export const jsonLd = {
|
||||
serie(chave, serie, locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const prop = (nome, valor) =>
|
||||
valor ? { "@type": "PropertyValue", name: nome, value: String(valor) } : null;
|
||||
|
||||
const propriedades = [
|
||||
prop(isEn ? "Manufacturer" : isEs ? "Fabricante" : "Fabricante", "Espressif Systems"),
|
||||
prop(isEn ? "Architecture" : isEs ? "Arquitectura" : "Arquitetura", serie.arquitetura),
|
||||
prop(isEn ? "Cores" : isEs ? "Núcleos" : "Núcleos", serie.nucleos),
|
||||
prop(isEn ? "Max frequency" : isEs ? "Frecuencia máxima" : "Frequência máxima", serie.frequencia),
|
||||
prop(isEn ? "Manufacturer" : isEs ? "Fabricante" : isFr ? "Fabricant" : isDe ? "Hersteller" : "Fabricante", "Espressif Systems"),
|
||||
prop(isEn ? "Architecture" : isEs ? "Arquitectura" : isFr ? "Architecture" : isDe ? "Architektur" : "Arquitetura"),
|
||||
prop(isEn ? "Cores" : isEs ? "Núcleos" : isFr ? "Cœurs" : isDe ? "Kerne" : "Núcleos", serie.nucleos),
|
||||
prop(isEn ? "Max frequency" : isEs ? "Frecuencia máxima" : isFr ? "Fréquence maximale" : isDe ? "Maximale Frequenz" : "Frequência máxima", serie.frequencia),
|
||||
prop("SRAM", serie.memoria_sram),
|
||||
prop("RTC SRAM", serie.memoria_sram_rtc),
|
||||
prop(isEn ? "Supported Flash" : isEs ? "Flash soportada" : "Flash suportada", serie.flash_suportada),
|
||||
prop(isEn ? "Supported PSRAM" : isEs ? "PSRAM soportada" : "PSRAM suportada", serie.psram_suportada),
|
||||
prop(isEn ? "Supported Flash" : isEs ? "Flash soportada" : isFr ? "Flash prise en charge" : isDe ? "Unterstützter Flash" : "Flash suportada", serie.flash_suportada),
|
||||
prop(isEn ? "Supported PSRAM" : isEs ? "PSRAM soportada" : isFr ? "PSRAM prise en charge" : isDe ? "Unterstütztes 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(isEn ? "Package" : isEs ? "Encapsulado" : "Encapsulamento", serie.encapsulamento),
|
||||
prop(isEn ? "Minimum ESP-IDF" : isEs ? "ESP-IDF mínimo" : "ESP-IDF mínimo", serie.esp_idf_minimo),
|
||||
prop(isEn ? "Package" : isEs ? "Encapsulado" : isFr ? "Boîtier" : isDe ? "Gehäuse" : "Encapsulamento", serie.encapsulamento),
|
||||
prop(isEn ? "Minimum ESP-IDF" : isEs ? "ESP-IDF mínimo" : isFr ? "ESP-IDF minimum" : isDe ? "Mindestens 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}` : isEs ? `/es/series/${chave}` : `/series/${chave}`;
|
||||
const anchor = isEn ? "article" : isEs ? "articulo" : "artigo";
|
||||
const basePath = isDe ? `/de/serien/${chave}` : isFr ? `/fr/series/${chave}` : isEn ? `/en/series/${chave}` : isEs ? `/es/series/${chave}` : `/series/${chave}`;
|
||||
const anchor = isEn ? "article" : isEs ? "articulo" : isFr ? "article" : isDe ? "artikel" : "artigo";
|
||||
const inLang = isEn ? "en" : isEs ? "es-419" : isFr ? "fr" : isDe ? "de" : "pt-BR";
|
||||
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
@@ -192,15 +202,23 @@ export const jsonLd = {
|
||||
? `${serie.nome_completo || chave}: specifications and pinout`
|
||||
: isEs
|
||||
? `${serie.nome_completo || chave}: especificaciones y patillaje`
|
||||
: isFr
|
||||
? `${serie.nome_completo || chave} : spécifications et brochage`
|
||||
: isDe
|
||||
? `${serie.nome_completo || chave}: Spezifikationen und Pinbelegung`
|
||||
: `${serie.nome_completo || chave}: especificações e pinagem`,
|
||||
name: isEn
|
||||
? `${serie.nome_completo || chave} - Technical Documentation and Specifications`
|
||||
: isEs
|
||||
? `${serie.nome_completo || chave} - Documentación técnica y especificaciones`
|
||||
: isFr
|
||||
? `${serie.nome_completo || chave} - Documentation technique et spécifications`
|
||||
: isDe
|
||||
? `${serie.nome_completo || chave} - Technische Dokumentation und Spezifikationen`
|
||||
: `${serie.nome_completo || chave} - Documentação Técnica e Especificações`,
|
||||
description: serie.descricao,
|
||||
image: [imagem],
|
||||
inLanguage: isEn ? "en" : isEs ? "es-419" : "pt-BR",
|
||||
inLanguage: inLang,
|
||||
datePublished: "2026-08-25T00:00:00.000Z",
|
||||
dateModified: new Date().toISOString(),
|
||||
mainEntityOfPage: `${SITE.url}${basePath}`,
|
||||
@@ -234,8 +252,11 @@ export const jsonLd = {
|
||||
framework(chave, framework, locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const basePath = isEn ? `/en/frameworks/${encodeURIComponent(chave)}` : isEs ? `/es/frameworks/${encodeURIComponent(chave)}` : `/frameworks/${encodeURIComponent(chave)}`;
|
||||
const anchor = isEn ? "article" : isEs ? "articulo" : "artigo";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const basePath = isDe ? `/de/frameworks/${encodeURIComponent(chave)}` : isFr ? `/fr/frameworks/${encodeURIComponent(chave)}` : isEn ? `/en/frameworks/${encodeURIComponent(chave)}` : isEs ? `/es/frameworks/${encodeURIComponent(chave)}` : `/frameworks/${encodeURIComponent(chave)}`;
|
||||
const anchor = isEn ? "article" : isEs ? "articulo" : isFr ? "article" : isDe ? "artikel" : "artigo";
|
||||
const inLang = isEn ? "en" : isEs ? "es-419" : isFr ? "fr" : isDe ? "de" : "pt-BR";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "TechArticle",
|
||||
@@ -244,13 +265,21 @@ export const jsonLd = {
|
||||
? `${framework.nome} for ESP32: Guide and Documentation`
|
||||
: isEs
|
||||
? `${framework.nome} para ESP32: Guía y documentación`
|
||||
: isFr
|
||||
? `${framework.nome} pour ESP32 : Guide et documentation`
|
||||
: isDe
|
||||
? `${framework.nome} für ESP32: Leitfaden und Dokumentation`
|
||||
: `${framework.nome} para ESP32: Guia e Documentação`,
|
||||
name: isEn
|
||||
? `${framework.nome_completo || framework.nome} for ESP32`
|
||||
: isFr
|
||||
? `${framework.nome_completo || framework.nome} pour ESP32`
|
||||
: isDe
|
||||
? `${framework.nome_completo || framework.nome} für ESP32`
|
||||
: `${framework.nome_completo || framework.nome} para ESP32`,
|
||||
description: framework.descricao,
|
||||
image: [`${SITE.url}/opengraph-image.png`],
|
||||
inLanguage: isEn ? "en" : isEs ? "es-419" : "pt-BR",
|
||||
inLanguage: inLang,
|
||||
datePublished: "2026-08-25T00:00:00.000Z",
|
||||
dateModified: new Date().toISOString(),
|
||||
mainEntityOfPage: `${SITE.url}${basePath}`,
|
||||
@@ -286,14 +315,17 @@ export const jsonLd = {
|
||||
glossario(categorias, locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const basePath = isEn ? "/en/glossary" : isEs ? "/es/glosario" : "/glossario";
|
||||
const anchor = isEn ? "glossary" : isEs ? "glosario" : "glossario";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const basePath = isDe ? "/de/glossar" : isFr ? "/fr/glossaire" : isEn ? "/en/glossary" : isEs ? "/es/glosario" : "/glossario";
|
||||
const anchor = isDe ? "glossar" : isFr ? "glossaire" : isEn ? "glossary" : isEs ? "glosario" : "glossario";
|
||||
const inLang = isEn ? "en" : isEs ? "es-419" : isFr ? "fr" : isDe ? "de" : "pt-BR";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "DefinedTermSet",
|
||||
"@id": `${SITE.url}${basePath}#${anchor}`,
|
||||
name: isEn ? "ESP32 Technical Glossary" : isEs ? "Glosario técnico de ESP32" : "Glossário Técnico do ESP32",
|
||||
inLanguage: isEn ? "en" : isEs ? "es-419" : "pt-BR",
|
||||
name: isEn ? "ESP32 Technical Glossary" : isEs ? "Glosario técnico de ESP32" : isFr ? "Glossaire technique de l'ESP32" : isDe ? "Technisches Glossar für ESP32" : "Glossário Técnico do ESP32",
|
||||
inLanguage: inLang,
|
||||
hasDefinedTerm: categorias.flatMap((cat) =>
|
||||
cat.termos.map((t) => ({
|
||||
"@type": "DefinedTerm",
|
||||
@@ -322,6 +354,9 @@ export const jsonLd = {
|
||||
aplicacaoWeb({ nome, descricao, caminho, locale = "pt" }) {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const inLang = isEn ? "en" : isEs ? "es-419" : isFr ? "fr" : isDe ? "de" : "pt-BR";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebApplication",
|
||||
@@ -332,7 +367,7 @@ export const jsonLd = {
|
||||
applicationCategory: "DeveloperApplication",
|
||||
operatingSystem: "All",
|
||||
browserRequirements: "Requires JavaScript",
|
||||
inLanguage: isEn ? "en" : isEs ? "es-419" : "pt-BR",
|
||||
inLanguage: inLang,
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: SITE.autor,
|
||||
@@ -415,20 +450,27 @@ export const jsonLd = {
|
||||
paginaSobre(locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const basePath = isEn ? "/en/about" : isEs ? "/es/acerca" : "/sobre";
|
||||
const anchor = isEn ? "about" : isEs ? "acerca" : "sobre";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const basePath = isDe ? "/de/ueber-uns" : isFr ? "/fr/a-propos" : isEn ? "/en/about" : isEs ? "/es/acerca" : "/sobre";
|
||||
const anchor = isDe ? "ueber-uns" : isFr ? "a-propos" : isEn ? "about" : isEs ? "acerca" : "sobre";
|
||||
const inLang = isEn ? "en" : isEs ? "es-419" : isFr ? "fr" : isDe ? "de" : "pt-BR";
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "AboutPage",
|
||||
"@id": `${SITE.url}${basePath}#${anchor}`,
|
||||
name: isEn ? "About ESPDocs" : isEs ? "Acerca de ESPDocs" : "Sobre o ESPDocs",
|
||||
name: isEn ? "About ESPDocs" : isEs ? "Acerca de ESPDocs" : isFr ? "À propos d'ESPDocs" : isDe ? "Über ESPDocs" : "Sobre o ESPDocs",
|
||||
description: isEn
|
||||
? "ESPDocs is an independent platform organizing technical documentation of the ESP32 ecosystem."
|
||||
: isEs
|
||||
? "ESPDocs es una plataforma independiente que reúne la documentación técnica del ecosistema ESP32."
|
||||
: isFr
|
||||
? "ESPDocs est une plateforme indépendante qui rassemble la documentation technique de l'écosystème ESP32."
|
||||
: isDe
|
||||
? "ESPDocs ist eine unabhängige Plattform, die die technische Dokumentation des ESP32-Ökosystems zusammenführt."
|
||||
: "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" : isEs ? "es-419" : "pt-BR",
|
||||
inLanguage: inLang,
|
||||
mainEntity: {
|
||||
"@type": "Person",
|
||||
name: SITE.autor,
|
||||
|
||||
Reference in new issue
Block a user