chore(i18n): finish translating code comments and scripts to English
CI / Format and clippy (push) Successful in 1m57s
CI / Dependency audit (push) Successful in 13s
CI / Check desktop app (Linux) (push) Successful in 3m54s
CI / Tests (Linux) (push) Failing after 7m25s
Release / Create release (push) Successful in 6s
Release / CLI (linux-aarch64) (push) Successful in 1m48s
Release / CLI (linux-x86_64) (push) Successful in 2m39s
Release / Desktop app (linux-aarch64) (push) Successful in 10m9s
Release / Desktop app (linux-x86_64) (push) Successful in 12m20s
Release / Desktop app (windows) (push) Successful in 7m32s
Release / Build latest.json (push) Successful in 5s
CI / Format and clippy (push) Successful in 1m57s
CI / Dependency audit (push) Successful in 13s
CI / Check desktop app (Linux) (push) Successful in 3m54s
CI / Tests (Linux) (push) Failing after 7m25s
Release / Create release (push) Successful in 6s
Release / CLI (linux-aarch64) (push) Successful in 1m48s
Release / CLI (linux-x86_64) (push) Successful in 2m39s
Release / Desktop app (linux-aarch64) (push) Successful in 10m9s
Release / Desktop app (linux-x86_64) (push) Successful in 12m20s
Release / Desktop app (windows) (push) Successful in 7m32s
Release / Build latest.json (push) Successful in 5s
Completes the English-only standardization for code, comments, scripts and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en, untouched). Covers the remaining crates/, apps/gui/ui/ and apps/gui/src-tauri/ comments, .gitea/ workflow and template comments, .githooks/ hooks, and the scripts/ source comments for the files already renamed in a prior commit (check.sh, generate-logo.py, install-hooks.sh). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
1 parent
da212e6926
commit
6328220c60
129 files changed
+1961
-1834
No files matched your search
+21
-22
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
// Confere se o dicionário inglês cobre os textos da interface.
|
||||
// Verifies that the English dictionary covers all user-facing interface strings.
|
||||
//
|
||||
// A varredura é estática e sem dependências: lê os arquivos da interface
|
||||
// (JS e HTML), os rótulos que o núcleo em Rust envia para a tela (metadados,
|
||||
// tipos de arquivo e saúde do disco) e falha quando encontra uma string que
|
||||
// parece português e não tem tradução. Também recusa chaves duplicadas no
|
||||
// dicionário (num objeto JS a última vence em silêncio).
|
||||
// Static zero-dependency scanner: reads the UI files (JS and HTML) and the
|
||||
// labels emitted by the Rust core (metadata, file types, disk health), failing
|
||||
// whenever a string looks like Portuguese and lacks a translation. Also rejects
|
||||
// duplicate keys in the dictionary (in a JS object literal the last key wins silently).
|
||||
//
|
||||
// Uso: node scripts/check-ui-i18n.mjs
|
||||
// Usage: node scripts/check-ui-i18n.mjs
|
||||
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
@@ -16,7 +15,7 @@ import { fileURLToPath } from 'node:url';
|
||||
const root = join(fileURLToPath(new URL('.', import.meta.url)), '..');
|
||||
const rel = (p) => relative(root, p).replaceAll('\\', '/');
|
||||
|
||||
// ---------------------------------------------------------------- dicionário
|
||||
// ---------------------------------------------------------------- dictionary
|
||||
|
||||
const dictPath = join(root, 'apps/gui/ui/i18n-en.js');
|
||||
const dictSource = readFileSync(dictPath, 'utf8');
|
||||
@@ -33,8 +32,8 @@ while ((m = keyRe.exec(dictSource))) {
|
||||
}
|
||||
|
||||
if (duplicates.length) {
|
||||
console.error('Chaves duplicadas em apps/gui/ui/i18n-en.js:');
|
||||
for (const d of duplicates) console.error(` ${JSON.stringify(d.key)} (linhas ${d.first} e ${d.again})`);
|
||||
console.error('Duplicate keys in apps/gui/ui/i18n-en.js:');
|
||||
for (const d of duplicates) console.error(` ${JSON.stringify(d.key)} (lines ${d.first} and ${d.again})`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
@@ -53,7 +52,7 @@ function translated(text) {
|
||||
return patterns.some((re) => re.test(text));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- heurística
|
||||
// ---------------------------------------------------------------- heuristic
|
||||
|
||||
const ACCENT = /[áàâãäéèêëíìîïóòôõöúùûüçÁÀÂÃÄÉÈÊËÍÌÎÏÓÒÔÕÖÚÙÛÜÇ]/;
|
||||
const WORDS = [
|
||||
@@ -92,13 +91,13 @@ function suspicious(text) {
|
||||
if (ALLOW.has(s) || ALLOW_EXACT.has(s)) return false;
|
||||
if (ALLOW_PREFIX.some((p) => s.startsWith(p))) return false;
|
||||
if (!isPortuguese(s)) return false;
|
||||
// técnico: seletores, classes, extensões, expressões
|
||||
// Technical tokens: CSS selectors, classes, extensions, expressions
|
||||
if (/^[.#\w-]+(\s*[,>+~]\s*[.#\w-]+)+$/.test(s)) return false;
|
||||
if (/^[a-z0-9_.:/@%{}$-]+$/i.test(s) && !ACCENT.test(s)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- extratores
|
||||
// ---------------------------------------------------------------- extractors
|
||||
|
||||
function jsStrings(source, file, baseLine = 1) {
|
||||
const out = [];
|
||||
@@ -212,14 +211,14 @@ function jsStrings(source, file, baseLine = 1) {
|
||||
function htmlStrings(source, file, baseLine = 1) {
|
||||
const out = [];
|
||||
const lineAt = (idx) => baseLine + source.slice(0, idx).split('\n').length - 1;
|
||||
// todo texto entre tags (inclusive começo e fim) — nós de texto reais
|
||||
// Text between tags (including leading and trailing text nodes)
|
||||
let last = 0;
|
||||
for (const m of source.matchAll(/<[^>]*>/g)) {
|
||||
out.push({ text: source.slice(last, m.index), file, line: lineAt(last) });
|
||||
last = m.index + m[0].length;
|
||||
}
|
||||
out.push({ text: source.slice(last), file, line: lineAt(last) });
|
||||
// atributos traduzíveis (com aspas simples ou duplas)
|
||||
// Translatable attributes (single or double quotes)
|
||||
for (const m of source.matchAll(/\b(placeholder|title|aria-label|alt|data-empty)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
|
||||
out.push({ text: m[2] ?? m[3], file, line: lineAt(m.index) });
|
||||
}
|
||||
@@ -229,16 +228,16 @@ function htmlStrings(source, file, baseLine = 1) {
|
||||
function rustLabels(source, file) {
|
||||
const out = [];
|
||||
const lineAt = (idx) => source.slice(0, idx).split('\n').length;
|
||||
// m.add("Rótulo", ...) e m.add(\n "Rótulo", ...)
|
||||
// m.add("Label", ...) and m.add(\n "Label", ...)
|
||||
for (const m of source.matchAll(/\.add\(\s*"([^"]+)"/g)) out.push({ text: m[1], file, line: lineAt(m.index) });
|
||||
// m.kind = "Tipo".into()
|
||||
// m.kind = "Kind".into()
|
||||
for (const m of source.matchAll(/\.kind\s*=\s*"([^"]+)"/g)) out.push({ text: m[1], file, line: lineAt(m.index) });
|
||||
// rótulos de listas (saúde do disco, tipos de arquivo) dentro de arrays de tuplas
|
||||
// List labels (disk health, file types) inside tuple arrays
|
||||
for (const m of source.matchAll(/\(\s*"([^"]{2,60})"\s*,\s*"(?:[^"]*)"\s*\)/g)) out.push({ text: m[1], file, line: lineAt(m.index) });
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- coleta
|
||||
// ---------------------------------------------------------------- collection
|
||||
|
||||
const uiFiles = readdirSync(join(root, 'apps/gui/ui'))
|
||||
.filter((f) => f.endsWith('.js') && f !== 'i18n-en.js')
|
||||
@@ -278,12 +277,12 @@ for (const { text, file, line } of found) {
|
||||
}
|
||||
|
||||
if (missing.length) {
|
||||
console.error('Textos em português sem tradução em apps/gui/ui/i18n-en.js:\n');
|
||||
console.error('Untranslated Portuguese strings in apps/gui/ui/i18n-en.js:\n');
|
||||
for (const x of missing) console.error(` ${x.file}:${x.line} ${JSON.stringify(x.text)}`);
|
||||
console.error(`\n${missing.length} texto(s) sem tradução.`);
|
||||
console.error(`\n${missing.length} untranslated string(s).`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
if (!process.exitCode) {
|
||||
console.log(`i18n ok: ${found.length} textos verificados, ${Object.keys(EN).length} chaves no dicionário.`);
|
||||
console.log(`i18n ok: ${found.length} strings checked, ${Object.keys(EN).length} dictionary keys.`);
|
||||
}
|
||||
Reference in new issue
Block a user