fix(i18n): localize json-ld metadata and eliminate breadcrumb leaks
- Pass currentLocale to jsonLd.serie and jsonLd.framework to ensure English headline, name, and properties - Localize JSON-LD @id anchors to #article, #about, #glossary, #organization, and #author for English routes - Pass isEn prop to PageHero across all internal pages to prevent breadcrumbs from defaulting to Portuguese - Add scripts/verifica-vazamentos.py to smoke-imagem.sh to permanently test all 36 English URLs (visible text and ld+json) Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
1 parent
47c55515bb
commit
0d5f8f9c5f
17 files changed
+151
-21
No files matched your search
@@ -120,6 +120,9 @@ if [[ " $ENABLED_LOCALES " =~ " en " ]]; then
|
||||
for marca in '<title>' 'rel="canonical"'; do
|
||||
case "$head_en" in *"$marca"*) ;; *) echo "FALHOU /en/about: $marca fora do <head>"; falhas=$((falhas + 1));; esac
|
||||
done
|
||||
|
||||
# 7. Varredura estrita contra vazamento de português (texto visível e blocos ld+json) nas 36 rotas
|
||||
python3 scripts/verifica-vazamentos.py "$base" || falhas=$((falhas + 1))
|
||||
fi
|
||||
|
||||
exit $((falhas > 0))
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
scripts/verifica-vazamentos.py
|
||||
Verificação permanente de ausência de vazamento de português em rotas inglesas (en).
|
||||
Analisa tanto o texto visível da página quanto todos os blocos <script type="application/ld+json">.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import html
|
||||
import urllib.request
|
||||
|
||||
base = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:3998"
|
||||
|
||||
series = [
|
||||
"ESP32", "ESP32-S2", "ESP32-S3", "ESP32-S31", "ESP32-C2", "ESP32-C3",
|
||||
"ESP32-C5", "ESP32-C6", "ESP32-C61", "ESP32-P4", "ESP32-H2", "ESP32-H4"
|
||||
]
|
||||
|
||||
frameworks = [
|
||||
"ESP-IDF", "ESP-RainMaker", "ESP-Matter", "ESP-ADF", "ESP-SR", "ESP-IoT-Solution",
|
||||
"ESP-NOW", "ESP-Zigbee-SDK", "ESP-WHO", "ESP-NN", "ESP-DSP", "Rust-ESP",
|
||||
"Arduino", "MicroPython"
|
||||
]
|
||||
|
||||
fixas = [
|
||||
"/en", "/en/series", "/en/compare", "/en/diagnostics", "/en/selector",
|
||||
"/en/frameworks", "/en/components", "/en/glossary", "/en/catalog", "/en/about"
|
||||
]
|
||||
|
||||
urls = fixas + [f"/en/series/{s}" for s in series] + [f"/en/frameworks/{f}" for f in frameworks]
|
||||
|
||||
# Regex rigoroso para detecção de vazamentos em português
|
||||
pt_regex = re.compile(
|
||||
r"\b(não|você|para o|para a|com o|uma|são|também|então|código|sobre|pinagem|"
|
||||
r"série|séries|livro|ção|ções|mais|muito|já|está|estão|é um|que|dos|das|"
|
||||
r"nos|nas|pelo|pela|especificações|especificação|guia|comparativo|catálogo|"
|
||||
r"seletor|recursos|projeto|independente|núcleos|frequência máxima|fabricante|"
|
||||
r"arquitetura|encapsulamento|início|artigo)\b",
|
||||
re.I
|
||||
)
|
||||
|
||||
# Termos técnicos legítimos aceitos em links/IDs/nomes
|
||||
TECHNICAL_EXEMPT_PATHS = {"@id", "@type", "@context", "url", "image", "logo", "sameAs"}
|
||||
|
||||
def check_jsonld(obj, path=""):
|
||||
problems = []
|
||||
if isinstance(obj, str):
|
||||
key = path.split(".")[-1].split("[")[0]
|
||||
if key in TECHNICAL_EXEMPT_PATHS:
|
||||
# Garante que âncoras de id não vazem português
|
||||
if (path.endswith("@id") or path.endswith("url")) and ("#artigo" in obj or "#sobre" in obj):
|
||||
problems.append((path, f"Anchor leak in {obj}"))
|
||||
return problems
|
||||
if pt_regex.search(obj):
|
||||
problems.append((path, obj))
|
||||
elif isinstance(obj, list):
|
||||
for i, val in enumerate(obj):
|
||||
problems.extend(check_jsonld(val, f"{path}[{i}]"))
|
||||
elif isinstance(obj, dict):
|
||||
for k, val in obj.items():
|
||||
problems.extend(check_jsonld(val, f"{path}.{k}" if path else k))
|
||||
return problems
|
||||
|
||||
falhas = 0
|
||||
|
||||
print(f"ESPDocs · Varredura de i18n nas 36 rotas em inglês ({base})")
|
||||
|
||||
for u in urls:
|
||||
try:
|
||||
req = urllib.request.Request(base + u, headers={"User-Agent": "ESPDocs-SmokeTest/1.0"})
|
||||
raw = urllib.request.urlopen(req, timeout=10).read().decode("utf-8")
|
||||
except Exception as e:
|
||||
print(f" ✖ ERRO ao acessar {u}: {e}")
|
||||
falhas += 1
|
||||
continue
|
||||
|
||||
# 1. Verifica blocos ld+json
|
||||
scripts = re.findall(r"<script[^>]*type=[\"']application/ld\+json[\"'][^>]*>(.*?)</script>", raw, re.DOTALL)
|
||||
for i, s in enumerate(scripts):
|
||||
try:
|
||||
data = json.loads(s)
|
||||
issues = check_jsonld(data, f"script[{i}]")
|
||||
for p, v in issues:
|
||||
print(f" ✖ JSON-LD vazamento em {u} -> {p}: \"{v}\"")
|
||||
falhas += 1
|
||||
except Exception as e:
|
||||
print(f" ✖ Erro no parse de JSON-LD em {u}: {e}")
|
||||
falhas += 1
|
||||
|
||||
# 2. Verifica texto visível
|
||||
clean = re.sub(r"<(script|style)[\s\S]*?</\1>", "", raw)
|
||||
clean = html.unescape(re.sub(r"<[^>]+>", "\n", clean))
|
||||
hits = [l.strip() for l in clean.split("\n") if l.strip() and pt_regex.search(l)]
|
||||
for h in hits:
|
||||
print(f" ✖ Texto visível vazamento em {u}: \"{h[:120]}\"")
|
||||
falhas += 1
|
||||
|
||||
if falhas == 0:
|
||||
print(f" ✔ Todas as {len(urls)} rotas em inglês verificadas com ZERO vazamentos (texto e JSON-LD).")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f" ✖ Total de falhas detectadas: {falhas}")
|
||||
sys.exit(1)
|
||||
Reference in new issue
Block a user