Files
ESPDocs/scripts/check-i18n.mjs
T
Lucas RayanandAntigravity 47c55515bb
CI / Lint e auditorias (push) Successful in 50s
CI / Trivy (dependências e segredos) (push) Successful in 10s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m31s
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>
2026-11-01 17:17:33 -03:00

634 lines
21 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* ESPDocs - Script de Verificação Automatizada de Internacionalização (check:i18n)
*
* Implementa os critérios de aceite da seção 10 (itens 1 e 2) da spec docs/specs/i18n.md:
* 1. Consistência estrutural de catálogos e dados (chaves idênticas ao pt).
* 2. Ausência de valores vazios.
* 3. Detecção estrita de strings idênticas ao pt sem allowlist (em messages, data e lib/data).
* 4. Verificação estrita de ausência de referências ao livro fora do pt (seção 7).
*
* Uso: npm run check:i18n
*/
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { ENABLED_LOCALES } from "../i18n/links.js";
const CORES = {
reset: "\x1b[0m",
verde: "\x1b[32m",
vermelho: "\x1b[31m",
amarelo: "\x1b[33m",
azul: "\x1b[34m",
ciano: "\x1b[36m",
negrito: "\x1b[1m",
cinza: "\x1b[90m",
};
const LOCALES = ["pt", "en", "es", "fr", "de", "zh-CN"];
const FORBIDDEN_BOOK_TERMS = /\b(livro|libro|livre|buch|书|book|isbn|amazon|uiclap|capítulo|capitulo|chapter|chapitre|kapitel|章节)\b/i;
// Allowlist estrita: nomes próprios, marcas, siglas e termos técnicos aceitos como idênticos
const TECHNICAL_ALLOWLIST = new Set([
"ESPDocs",
"Lucas Rayan Guerra",
"Ciência Embarcada",
"Hardware",
"Component Registry",
"ESP Component Registry",
"ESP32",
"ESP32-S2",
"ESP32-S3",
"ESP32-S31",
"ESP32-C2",
"ESP32-C3",
"ESP32-C5",
"ESP32-C6",
"ESP32-C61",
"ESP32-P4",
"ESP32-H2",
"ESP32-H4",
"ESP-IDF",
"Arduino",
"MicroPython",
"FreeRTOS",
"Rust",
"Matter",
"ESP-RainMaker",
"ESP-Matter",
"ESP-ADF",
"ESP-SR",
"ESP-IoT-Solution",
"ESP-NOW & Mesh",
"ESP-Zigbee-SDK",
"ESP-WHO",
"ESP-NN",
"ESP-DSP",
"Rust on ESP32",
"Arduino Core",
"Xtensa",
"RISC-V",
"Wi-Fi",
"Bluetooth",
"BLE",
"Zigbee",
"Thread",
"USB",
"GPIO",
"SPI",
"I2C",
"I2S",
"UART",
"CAN",
"TWAI",
"ADC",
"DAC",
"PWM",
"JTAG",
"Gitea",
"GitHub",
"LinkedIn",
"MIT",
"DevKit",
"SoC",
"PSRAM",
"SRAM",
"ROM",
"Flash",
"OTA",
"DMA",
"WDT",
"TWDT",
"IWDT",
"LDO",
"DCDC",
"LVGL",
"IDF",
"TinyUSB",
"LittleFS",
"CBOR",
"RFC 8949",
"Cloud & AIoT Framework",
"Smart Home Framework",
"Smart Home & Mesh",
"{count} frameworks",
"C / C++",
"C++",
"Python",
"12",
"78",
"100%",
"1",
"2",
"3",
"4",
"5",
"6",
]);
function isTechnicalAllowlisted(val, path = "") {
if (typeof val !== "string") return true;
const s = val.trim();
if (TECHNICAL_ALLOWLIST.has(s)) return true;
// Campos que são metadados técnicos, URLs, identificadores ou configs de código
if (/(^|\.)(id|namespace|name|nome_completo|versao|categoria|oficial|popular|urlRegistry|urlRepo|exemploYaml|icone|cor|placa|site_oficial|repositorio_github|documentacao_oficial|datasheet|manual_tecnico|guia_de_programacao|gpio|uart|i2c|i2s|spi|adc|dac|touch|pino|funcoes|rotulo|chave|slug)(\[\d+\])*$/i.test(path)) {
return true;
}
if (s.startsWith("http://") || s.startsWith("https://") || s.startsWith("/") || s.startsWith("mailto:")) return true;
// Versões semver, tags e identificadores
if (/^\^?[0-9]+(\.[0-9]+)*([a-zA-Z0-9.-]*)$/.test(s)) return true;
// Emojis / Pictogramas / Símbolos Unicode
if (/^(\p{Extended_Pictographic}|\p{Emoji_Presentation}|\s)+$/u.test(s)) return true;
// Medidas, números, percentuais, frequências, consumos, intervalos
if (/^[-+]?\d+([.,]\d+)?(\s*[%]|(\s*(MHz|GHz|KB|MB|GB|µA|mA|A|V|mV|mW|W|dBm|°C|mm|ohms|kΩ|µF|nF)))?$/i.test(s)) return true;
if (/^[-+]?\d+([.,]\d+)?\s*(a|to|-)\s*[-+]?\d+([.,]\d+)?(\s*(MHz|GHz|KB|MB|GB|µA|mA|A|V|mV|mW|W|dBm|°C|mm|ohms|kΩ|µF|nF))?$/i.test(s)) return true;
if (/^\d+\s*mW\s*\([^)]*\)$/i.test(s)) return true;
// Hardware specs / contagem de periféricos: "1 (CAN 2.0, TWAI®)", "1 (Single-core)", "2 (Dual-core)"
if (/^\d+(\s*\(.*\))?(\s*(\+|\/|,)\s*\d+(\s*\(.*\))?)*$/i.test(s)) return true;
// Acrônimos de segurança / criptografia
if (/^(AES|SHA|RSA|ECDSA|XTS|HMAC|ECC|Digital Signature|V\d+(\s*\(.*\))?)[A-Za-z0-9_/ -]*$/i.test(s)) return true;
// Identificadores de módulos / produtos / encapsulamentos / pinos
if (/^[A-Za-z0-9_+-]+(\/[A-Za-z0-9_.-]+)*(\^[0-9.]+)?$/.test(s)) return true;
if (/^v\d+(\.\d+)*$/i.test(s)) return true;
if (/^QFN\d+(\s*\(\d+×\d+\s*mm\))?$/i.test(s)) return true;
// Lista de módulos / chips separados por vírgula ou +
if (/^([A-Za-z0-9_().-]+(,\s*|\s*\+\s*|\s+))+[A-Za-z0-9_().-]+$/.test(s)) return true;
// Cores hex
if (/^#?[0-9a-fA-F]{3,8}$/.test(s)) return true;
// Blocos de comandos e YAML
if (s.startsWith("dependencies:") || s.startsWith("idf.py") || s.startsWith("pip install")) return true;
// Sinais de hardware / categorias de pinos
if (/^(USB|JTAG|UART|SPI|I2C|I2S|GPIO|MIPI|Low Power|Strapping|Digital|Analog|Power)[A-Za-z0-9_.+/ -]*$/i.test(s)) return true;
// Nomes próprios de componentes / SDKs
if (/^(ESP|ESP32|LVGL|TinyUSB|FreeRTOS|Rust|Arduino)[A-Za-z0-9_()./ -]+$/i.test(s)) return true;
return false;
}
// Chaves específicas do livro (seção 7: apenas permitidas no pt)
const BOOK_KEYS = new Set([
"Header.book",
"Header.bookNew",
"Header.bookTitle",
"Header.bookSubtitle",
"Footer.bookNew",
"Footer.bookTitle",
"Footer.bookSubtitle",
"Footer.amazon",
"Footer.uiclap",
"Home.bookChapters",
"Home.bookTitle",
"Home.bookSubtitle",
"Home.bookNewBadge",
"Home.bookDescription",
"Home.bookParts",
"Home.bookChaptersCount",
"Home.buyDigital",
"Home.buyPrint",
"Home.fullRecord",
"Livro.title",
"Livro.subtitle",
"Livro.badge",
"Livro.description",
"Livro.buyDigital",
"Livro.buyPrint",
"Livro.chaptersTitle",
]);
let totalErros = 0;
let totalAvisos = 0;
function sucesso(msg) {
console.log(` ${CORES.verde}✔${CORES.reset} ${msg}`);
}
function falha(msg) {
console.log(` ${CORES.vermelho}✖${CORES.reset} ${msg}`);
totalErros++;
}
function aviso(msg) {
console.log(` ${CORES.amarelo}⚠${CORES.reset} ${msg}`);
totalAvisos++;
}
function info(msg) {
console.log(` ${CORES.cinza}ℹ${CORES.reset} ${msg}`);
}
/** Achata um objeto aninhado em chaves dot-notation */
function achatarObjeto(obj, prefixo = "") {
const resultado = {};
for (const [k, v] of Object.entries(obj)) {
const chave = prefixo ? `${prefixo}.${k}` : k;
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
Object.assign(resultado, achatarObjeto(v, chave));
} else {
resultado[chave] = v;
}
}
return resultado;
}
// ---------------------------------------------------------------------------
// 1. Verificação da Base Canônica em Português (pt)
// ---------------------------------------------------------------------------
function verificarBasePt() {
console.log(`\n${CORES.negrito}${CORES.ciano}[1/4] Verificação da Fonte Canônica (pt)${CORES.reset}`);
// messages/pt.json
const messagesPtPath = "messages/pt.json";
if (!existsSync(messagesPtPath)) {
falha(`Arquivo de mensagens '${messagesPtPath}' não encontrado.`);
return null;
}
let ptMessages;
try {
ptMessages = JSON.parse(readFileSync(messagesPtPath, "utf8"));
sucesso(`Catálogo '${messagesPtPath}' carregado com sucesso (${Object.keys(ptMessages).length} namespaces).`);
} catch (e) {
falha(`Erro ao processar JSON em '${messagesPtPath}': ${e.message}`);
return null;
}
// data/pt/
const dataFiles = ["series.json", "frameworks.json"];
for (const f of dataFiles) {
const p = join("data/pt", f);
if (existsSync(p)) {
try {
JSON.parse(readFileSync(p, "utf8"));
sucesso(`Arquivo de dados '${p}' validado com sucesso.`);
} catch (e) {
falha(`Erro em '${p}': ${e.message}`);
}
} else {
falha(`Arquivo esperado '${p}' não encontrado.`);
}
}
// data/pt/conexoes
const conexoesDir = "data/pt/conexoes";
if (existsSync(conexoesDir)) {
const arquivos = readdirSync(conexoesDir).filter((f) => f.endsWith(".json"));
if (arquivos.length >= 11) {
sucesso(`Diretório '${conexoesDir}' contém ${arquivos.length} arquivos de conexões validados.`);
} else {
aviso(`Diretório '${conexoesDir}' contém apenas ${arquivos.length} arquivos.`);
}
} else {
falha(`Diretório '${conexoesDir}' não encontrado.`);
}
// lib/data/pt/
const libDataFiles = ["glossario.js", "diagnostico.js", "componentes.js", "topicos.js"];
for (const f of libDataFiles) {
const p = join("lib/data/pt", f);
if (existsSync(p)) {
sucesso(`Módulo de dados '${p}' verificado.`);
} else {
falha(`Módulo esperado '${p}' não encontrado.`);
}
}
return ptMessages;
}
// ---------------------------------------------------------------------------
// 2. Verificação de Catálogos & Chaves por Idioma
// ---------------------------------------------------------------------------
function verificarOutrosIdiomas(ptMessages) {
console.log(`\n${CORES.negrito}${CORES.ciano}[2/4] Verificação de Catálogos & Chaves por Idioma${CORES.reset}`);
if (!ptMessages) return;
const chavesPt = achatarObjeto(ptMessages);
const totalChavesPt = Object.keys(chavesPt).length;
sucesso(`Catálogo 'pt' contém ${totalChavesPt} chaves de tradução ativas.`);
for (const loc of LOCALES) {
if (loc === "pt") continue;
const isEnabled = ENABLED_LOCALES.includes(loc);
const pathMessages = `messages/${loc}.json`;
if (!existsSync(pathMessages)) {
if (isEnabled) {
falha(`Catálogo '${loc}' (${pathMessages}) não encontrado para idioma ativo.`);
} else {
info(`Catálogo '${loc}' (${pathMessages}) ainda não criado (idioma inativo).`);
}
continue;
}
let locMessages;
try {
locMessages = JSON.parse(readFileSync(pathMessages, "utf8"));
} catch (e) {
falha(`Erro de parsing em '${pathMessages}': ${e.message}`);
continue;
}
const chavesLoc = achatarObjeto(locMessages);
let faltando = 0;
let extras = 0;
let vazios = 0;
let iguaisSemAllowlist = 0;
for (const [k, vPt] of Object.entries(chavesPt)) {
// Chaves exclusivas do livro não são esperadas fora do pt (§7)
if (BOOK_KEYS.has(k) || k.startsWith("Livro.")) {
continue;
}
if (!(k in chavesLoc)) {
faltando++;
} else {
const vLoc = chavesLoc[k];
if (typeof vLoc === "string" && vLoc.trim() === "") {
vazios++;
} else if (vLoc === vPt && typeof vPt === "string") {
if (!isTechnicalAllowlisted(vPt, k)) {
iguaisSemAllowlist++;
falha(`'${loc}': chave '${k}' tem valor idêntico ao pt fora da allowlist: "${vPt}"`);
}
}
}
}
for (const k of Object.keys(chavesLoc)) {
if (BOOK_KEYS.has(k) || k.startsWith("Livro.")) {
falha(`'${loc}': chave proibida do livro '${k}' presente no catálogo fora de pt.`);
} else if (!(k in chavesPt)) {
extras++;
}
}
if (isEnabled) {
if (faltando > 0) falha(`'${loc}': ${faltando} chave(s) ausente(s) em relação ao pt.`);
if (extras > 0) aviso(`'${loc}': ${extras} chave(s) excedente(s) em relação ao pt.`);
if (vazios > 0) falha(`'${loc}': ${vazios} valor(es) vazio(s).`);
if (faltando === 0 && vazios === 0 && iguaisSemAllowlist === 0) {
sucesso(`Catálogo '${loc}' ativo possui paridade e tradução completas com 'pt'.`);
}
} else {
if (faltando === 0 && vazios === 0) {
sucesso(`Catálogo '${loc}' (inativo) possui paridade estrutural completa com 'pt'.`);
}
}
}
}
// ---------------------------------------------------------------------------
// 3. Verificação de Dados por Idioma (data/ e lib/data/)
// ---------------------------------------------------------------------------
function encontrarIdenticos(pt, en, path = "") {
const problemas = [];
if (typeof pt === "string" && typeof en === "string") {
if (pt === en && !isTechnicalAllowlisted(pt, path)) {
problemas.push({ path, value: pt });
}
} else if (Array.isArray(pt) && Array.isArray(en)) {
for (let i = 0; i < Math.min(pt.length, en.length); i++) {
problemas.push(...encontrarIdenticos(pt[i], en[i], `${path}[${i}]`));
}
} else if (typeof pt === "object" && pt !== null && typeof en === "object" && en !== null) {
for (const k of Object.keys(en)) {
if (k in pt) {
problemas.push(...encontrarIdenticos(pt[k], en[k], path ? `${path}.${k}` : k));
}
}
}
return problemas;
}
async function verificarDadosPorIdioma() {
console.log(`\n${CORES.negrito}${CORES.ciano}[3/4] Verificação de Dados (data/ e lib/data/) por Idioma${CORES.reset}`);
for (const loc of ENABLED_LOCALES) {
if (loc === "pt") continue;
// 1. data/<loc>/series.json vs data/pt/series.json
const ptSeriesPath = "data/pt/series.json";
const locSeriesPath = `data/${loc}/series.json`;
if (existsSync(locSeriesPath)) {
const ptSeries = JSON.parse(readFileSync(ptSeriesPath, "utf8"));
const locSeries = JSON.parse(readFileSync(locSeriesPath, "utf8"));
const problemas = encontrarIdenticos(ptSeries, locSeries, `data/${loc}/series.json`);
if (problemas.length > 0) {
falha(`'${loc}': ${problemas.length} valor(es) string em series.json idênticos ao pt fora da allowlist.`);
for (const p of problemas.slice(0, 5)) {
console.log(` ${CORES.cinza}${p.path}: "${p.value}"${CORES.reset}`);
}
} else {
sucesso(`'${loc}': data/${loc}/series.json validado sem strings vazadas.`);
}
}
// 2. data/<loc>/frameworks.json vs data/pt/frameworks.json
const ptFwPath = "data/pt/frameworks.json";
const locFwPath = `data/${loc}/frameworks.json`;
if (existsSync(locFwPath)) {
const ptFw = JSON.parse(readFileSync(ptFwPath, "utf8"));
const locFw = JSON.parse(readFileSync(locFwPath, "utf8"));
const problemas = encontrarIdenticos(ptFw, locFw, `data/${loc}/frameworks.json`);
if (problemas.length > 0) {
falha(`'${loc}': ${problemas.length} valor(es) string em frameworks.json idênticos ao pt fora da allowlist.`);
for (const p of problemas.slice(0, 5)) {
console.log(` ${CORES.cinza}${p.path}: "${p.value}"${CORES.reset}`);
}
} else {
sucesso(`'${loc}': data/${loc}/frameworks.json validado sem strings vazadas.`);
}
}
// 3. data/<loc>/conexoes/*.json vs public/conexoes/*.json
const locConexoesDir = `data/${loc}/conexoes`;
if (existsSync(locConexoesDir)) {
let totalConexoesProblemas = 0;
const arqs = readdirSync(locConexoesDir).filter((f) => f.endsWith(".json"));
for (const f of arqs) {
const ptPath = existsSync(`data/pt/conexoes/${f}`) ? `data/pt/conexoes/${f}` : `public/conexoes/${f}`;
if (existsSync(ptPath)) {
const pt = JSON.parse(readFileSync(ptPath, "utf8"));
const en = JSON.parse(readFileSync(join(locConexoesDir, f), "utf8"));
const problemas = encontrarIdenticos(pt, en, `conexoes/${f}`);
totalConexoesProblemas += problemas.length;
if (problemas.length > 0) {
for (const p of problemas.slice(0, 3)) {
console.log(` ${CORES.cinza}${p.path}: "${p.value}"${CORES.reset}`);
}
}
}
}
if (totalConexoesProblemas > 0) {
falha(`'${loc}': ${totalConexoesProblemas} valor(es) em conexoes idênticos ao pt fora da allowlist.`);
} else {
sucesso(`'${loc}': ${arqs.length} arquivos de conexoes validados sem strings vazadas.`);
}
}
// 4. lib/data/<loc>/
try {
const ptComp = (await import("../lib/data/pt/componentes.js")).COMPONENTES_REGISTRY;
const locComp = (await import(`../lib/data/${loc}/componentes.js`)).COMPONENTES_REGISTRY;
const problemasComp = encontrarIdenticos(ptComp, locComp, `lib/data/${loc}/componentes.js`);
if (problemasComp.length > 0) {
falha(`'${loc}': ${problemasComp.length} string(s) em componentes.js idênticas ao pt fora da allowlist.`);
for (const p of problemasComp.slice(0, 3)) {
console.log(` ${CORES.cinza}${p.path}: "${p.value}"${CORES.reset}`);
}
} else {
sucesso(`'${loc}': lib/data/${loc}/componentes.js validado sem strings vazadas.`);
}
} catch (e) {
falha(`Erro ao validar lib/data/${loc}/componentes.js: ${e.message}`);
}
try {
const ptDiag = (await import("../lib/data/pt/diagnostico.js")).ERROS_COMUNS;
const locDiag = (await import(`../lib/data/${loc}/diagnostico.js`)).ERROS_COMUNS;
const problemasDiag = encontrarIdenticos(ptDiag, locDiag, `lib/data/${loc}/diagnostico.js`);
if (problemasDiag.length > 0) {
falha(`'${loc}': ${problemasDiag.length} string(s) em diagnostico.js idênticas ao pt fora da allowlist.`);
} else {
sucesso(`'${loc}': lib/data/${loc}/diagnostico.js validado sem strings vazadas.`);
}
} catch (e) {
falha(`Erro ao validar lib/data/${loc}/diagnostico.js: ${e.message}`);
}
try {
const ptGloss = (await import("../lib/data/pt/glossario.js")).categoriasGlossario;
const locGloss = (await import(`../lib/data/${loc}/glossario.js`)).categoriasGlossario;
const problemasGloss = encontrarIdenticos(ptGloss, locGloss, `lib/data/${loc}/glossario.js`);
if (problemasGloss.length > 0) {
falha(`'${loc}': ${problemasGloss.length} string(s) em glossario.js idênticas ao pt fora da allowlist.`);
} else {
sucesso(`'${loc}': lib/data/${loc}/glossario.js validado sem strings vazadas.`);
}
} catch (e) {
falha(`Erro ao validar lib/data/${loc}/glossario.js: ${e.message}`);
}
}
}
// Usos técnicos legítimos permitidos (seção 10: allowlist explícita)
const TECHNICAL_BOOK_ALLOWLIST = [
"https://esp-rs.github.io/book/",
];
function sanitizarTextoParaLivro(conteudo) {
let limpo = conteudo;
for (const termo of TECHNICAL_BOOK_ALLOWLIST) {
limpo = limpo.split(termo).join("");
}
return limpo;
}
// ---------------------------------------------------------------------------
// 4. Verificação de Regras do Livro (§7 da spec: ausência fora do pt)
// ---------------------------------------------------------------------------
function verificarRegrasLivro() {
console.log(`\n${CORES.negrito}${CORES.ciano}[4/4] Verificação Estrita de Referências ao Livro (Seção 7)${CORES.reset}`);
let violacoes = 0;
for (const loc of LOCALES) {
if (loc === "pt") continue;
// Checar messages/<loc>.json
const pathMsg = `messages/${loc}.json`;
if (existsSync(pathMsg)) {
const conteudo = sanitizarTextoParaLivro(readFileSync(pathMsg, "utf8"));
const m = conteudo.match(FORBIDDEN_BOOK_TERMS);
if (m) {
falha(`Termo proibido do livro '${m[0]}' encontrado em '${pathMsg}'.`);
violacoes++;
}
}
// Checar data/<loc>/
const dirData = `data/${loc}`;
if (existsSync(dirData)) {
const arquivos = readdirSync(dirData).filter((f) => f.endsWith(".json"));
for (const arq of arquivos) {
const conteudo = sanitizarTextoParaLivro(readFileSync(join(dirData, arq), "utf8"));
const m = conteudo.match(FORBIDDEN_BOOK_TERMS);
if (m) {
falha(`Termo proibido do livro '${m[0]}' encontrado em '${dirData}/${arq}'.`);
violacoes++;
}
}
const dirConexoes = join(dirData, "conexoes");
if (existsSync(dirConexoes)) {
const arqsConexoes = readdirSync(dirConexoes).filter((f) => f.endsWith(".json"));
for (const arq of arqsConexoes) {
const conteudo = sanitizarTextoParaLivro(readFileSync(join(dirConexoes, arq), "utf8"));
const m = conteudo.match(FORBIDDEN_BOOK_TERMS);
if (m) {
falha(`Termo proibido do livro '${m[0]}' encontrado em '${dirConexoes}/${arq}'.`);
violacoes++;
}
}
}
}
// Checar lib/data/<loc>/
const dirLibData = `lib/data/${loc}`;
if (existsSync(dirLibData)) {
const arquivos = readdirSync(dirLibData).filter((f) => f.endsWith(".js"));
for (const arq of arquivos) {
const conteudo = sanitizarTextoParaLivro(readFileSync(join(dirLibData, arq), "utf8"));
const m = conteudo.match(FORBIDDEN_BOOK_TERMS);
if (m) {
falha(`Termo proibido do livro '${m[0]}' encontrado em '${dirLibData}/${arq}'.`);
violacoes++;
}
}
}
// Checar public/exemplos/<loc>/
const dirExemplos = `public/exemplos/${loc}`;
if (existsSync(dirExemplos)) {
const arquivos = readdirSync(dirExemplos);
for (const arq of arquivos) {
const conteudo = sanitizarTextoParaLivro(readFileSync(join(dirExemplos, arq), "utf8"));
const m = conteudo.match(FORBIDDEN_BOOK_TERMS);
if (m) {
falha(`Termo proibido do livro '${m[0]}' encontrado em '${dirExemplos}/${arq}'.`);
violacoes++;
}
}
}
}
if (violacoes === 0) {
sucesso("Nenhuma menção ao livro encontrada em catálogos ou dados fora do português.");
}
}
async function main() {
console.log(`${CORES.negrito}${CORES.azul}======================================================${CORES.reset}`);
console.log(`${CORES.negrito}${CORES.azul} ESPDocs · Verificação de Internacionalização (i18n) ${CORES.reset}`);
console.log(`${CORES.negrito}${CORES.azul}======================================================${CORES.reset}`);
const ptMessages = verificarBasePt();
verificarOutrosIdiomas(ptMessages);
await verificarDadosPorIdioma();
verificarRegrasLivro();
console.log(`\n${CORES.negrito}------------------------------------------------------${CORES.reset}`);
if (totalErros === 0 && totalAvisos === 0) {
console.log(`${CORES.verde}${CORES.negrito}🎉 TODAS AS VERIFICAÇÕES DE I18N PASSARAM! (0 erros, 0 avisos)${CORES.reset}\n`);
return 0;
} else if (totalErros === 0) {
console.log(`${CORES.amarelo}${CORES.negrito}✔ Verificações passaram com ${totalAvisos} aviso(s).${CORES.reset}\n`);
return 0;
} else {
console.log(`${CORES.vermelho}${CORES.negrito}✖ Foram encontrados ${totalErros} erro(s) de i18n que bloqueiam o aceite.${CORES.reset}\n`);
return 1;
}
}
process.exitCode = await main();