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
@@ -41,10 +41,19 @@ pt_regex = re.compile(
|
||||
re.I
|
||||
)
|
||||
|
||||
# Regex estrito para vazamentos de português em páginas em espanhol
|
||||
# Palavras/sufixos que existem em português mas são incorretos em espanhol
|
||||
pt_in_es_regex = re.compile(
|
||||
r"\b(não|você|para o|para a|com o|com a|são|também|então|pinagem|"
|
||||
r"série|séries|livro|ção|ções|muito|já|estão|é um|é uma|"
|
||||
r"pelo|pela|especificações|especificação|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=""):
|
||||
def check_jsonld(obj, path="", regex=pt_regex, is_es=False):
|
||||
problems = []
|
||||
if isinstance(obj, str):
|
||||
key = path.split(".")[-1].split("[")[0]
|
||||
@@ -53,14 +62,14 @@ def check_jsonld(obj, path=""):
|
||||
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):
|
||||
if 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}]"))
|
||||
problems.extend(check_jsonld(val, f"{path}[{i}]", regex, is_es))
|
||||
elif isinstance(obj, dict):
|
||||
for k, val in obj.items():
|
||||
problems.extend(check_jsonld(val, f"{path}.{k}" if path else k))
|
||||
problems.extend(check_jsonld(val, f"{path}.{k}" if path else k, regex, is_es))
|
||||
return problems
|
||||
|
||||
falhas = 0
|
||||
@@ -81,7 +90,7 @@ for u in urls:
|
||||
for i, s in enumerate(scripts):
|
||||
try:
|
||||
data = json.loads(s)
|
||||
issues = check_jsonld(data, f"script[{i}]")
|
||||
issues = check_jsonld(data, f"script[{i}]", pt_regex, False)
|
||||
for p, v in issues:
|
||||
print(f" ✖ JSON-LD vazamento em {u} -> {p}: \"{v}\"")
|
||||
falhas += 1
|
||||
@@ -97,8 +106,47 @@ for u in urls:
|
||||
print(f" ✖ Texto visível vazamento em {u}: \"{h[:120]}\"")
|
||||
falhas += 1
|
||||
|
||||
# Varredura das rotas em espanhol
|
||||
fixas_es = [
|
||||
"/es", "/es/series", "/es/comparacion", "/es/diagnostico", "/es/selector",
|
||||
"/es/frameworks", "/es/componentes", "/es/glosario", "/es/catalogo", "/es/acerca"
|
||||
]
|
||||
urls_es = fixas_es + [f"/es/series/{s}" for s in series] + [f"/es/frameworks/{f}" for f in frameworks]
|
||||
|
||||
print(f"\nESPDocs · Varredura de i18n nas 36 rotas em espanhol ({base})")
|
||||
|
||||
for u in urls_es:
|
||||
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}]", pt_in_es_regex, True)
|
||||
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_in_es_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).")
|
||||
print(f" ✔ Todas as {len(urls)} rotas em inglês e {len(urls_es)} rotas em espanhol verificadas com ZERO vazamentos (texto e JSON-LD).")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f" ✖ Total de falhas detectadas: {falhas}")
|
||||
|
||||
Reference in new issue
Block a user