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

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:
Lucas RayanandClaude Sonnet 5 committed 2026-10-30 20:43:02 -03:00
1 parent da212e6926
commit 6328220c60
129 files changed
+1961 -1834

No files matched your search

+23 -23
View File
@@ -1,5 +1,5 @@
//! Recursos periciais: hashes, metadados (EXIF/PDF/Office/MP4), miniaturas,
//! busca de texto, exportações e navegação (facetas/duplicadas).
//! Forensic features: multi-hash, metadata extraction (EXIF/PDF/Office/MP4), thumbnails,
//! full-disk text search, CSV/HTML exports, and explorer facets/deduplication.
use guara_core::explorer::{self, Query};
use guara_core::hashing::hash_bytes;
@@ -79,7 +79,7 @@ fn rat(p: &[(u32, u32)]) -> Vec<u8> {
}
fn jpeg_with_exif(orientation: u16, w: usize, h: usize) -> Vec<u8> {
// pixels: gradiente
// gradient pixel pattern
let mut px = Vec::with_capacity(w * h * 3);
for y in 0..h {
for x in 0..w {
@@ -143,7 +143,7 @@ fn exif_camera_data_e_gps() {
let m = meta::extract(&MemSource(vec![]), &f);
assert_eq!(m.camera.as_deref(), Some("Canon EOS 80D"));
assert_eq!(m.px, Some((64, 48)));
// 2023-07-14 18:22:05 lido como UTC
// `2023:07:14 18:22:05` interpreted as UTC
assert_eq!(m.taken, Some(1_689_358_925));
let (lat, lon) = m.gps.unwrap();
assert!((lat + 23.5550).abs() < 0.001, "{lat}");
@@ -157,14 +157,14 @@ fn exif_camera_data_e_gps() {
#[test]
fn miniatura_respeita_orientacao_e_tamanho() {
let src = MemSource(vec![]);
// 64x48 com orientação 6 (girar 90 graus) => retrato 48x64, reduzido para caber em 32
// 64x48 with EXIF orientation 6 (90° CW) -> portrait 48x64, scaled down to fit within 32px
let f = resident("foto.jpg", jpeg_with_exif(6, 64, 48));
let t = thumb::thumbnail(&src, &f, 32).expect("miniatura");
let mut dec = jpeg_decoder::Decoder::new(&t[..]);
dec.decode().unwrap();
let i = dec.info().unwrap();
assert_eq!((i.width, i.height), (24, 32));
// PNG também
// PNG thumbnail generation
let mut img = ::image::RgbImage::new(100, 50);
for p in img.pixels_mut() {
*p = ::image::Rgb([200, 30, 30]);
@@ -179,7 +179,7 @@ fn miniatura_respeita_orientacao_e_tamanho() {
(dec.info().unwrap().width, dec.info().unwrap().height),
(40, 20)
);
// lixo não vira miniatura
// random bytes return `None`
assert!(thumb::thumbnail(&src, &resident("x.jpg", vec![1, 2, 3, 4, 5]), 32).is_none());
}
@@ -325,7 +325,7 @@ fn metadados_de_mp4() {
assert_eq!(get("Dimensões").as_deref(), Some("1920 × 1080 px"));
}
// ------------------------------------------------------------------ busca de texto
// ------------------------------------------------------------------ text search
#[test]
fn busca_acha_utf8_utf16_e_na_fronteira_dos_blocos() {
@@ -338,7 +338,7 @@ fn busca_acha_utf8_utf16_e_na_fronteira_dos_blocos() {
.flat_map(|c| c.to_le_bytes())
.collect();
put(&mut disk, 3 * mb + 6, &u16s);
// exatamente cruzando o limite do primeiro bloco de 8 MiB
// string placed right across the 8 MiB chunk boundary
put(&mut disk, 8 * mb - 4, b"SEGREDO-123");
let opts = search::SearchOpts {
terms: vec!["segredo-123".into()],
@@ -364,7 +364,7 @@ fn busca_acha_utf8_utf16_e_na_fronteira_dos_blocos() {
assert_eq!(hits[1].offset, 3 * mb as u64 + 6);
assert_eq!(hits[1].encoding, "UTF-16");
assert_eq!(hits[2].offset, 8 * mb as u64 - 4);
// sem ignorar maiúsculas só acha a grafia exata
// case-sensitive mode matches only exact casing
let opts = search::SearchOpts {
terms: vec!["Segredo-123".into()],
ignore_case: false,
@@ -381,7 +381,7 @@ fn busca_acha_utf8_utf16_e_na_fronteira_dos_blocos() {
assert_eq!(n, 1);
}
// ------------------------------------------------------------------ exportações
// ------------------------------------------------------------------ exports
#[test]
fn csv_escapa_e_neutraliza_formulas() {
@@ -431,7 +431,7 @@ fn relatorio_html_escapa_conteudo_e_traz_o_caso() {
assert!(html.contains("somente para leitura"));
}
// ------------------------------------------------------------------ navegação
// ------------------------------------------------------------------ explorer facets & sorting
#[test]
fn facetas_filtros_e_duplicadas() {
@@ -473,7 +473,7 @@ fn facetas_filtros_e_duplicadas() {
Some("Canon"),
Some(8),
),
mk(3, "c.jpg", Some("Viagens"), Some(jul23), None, Some(7)), // duplicada de a.jpg
mk(3, "c.jpg", Some("Viagens"), Some(jul23), None, Some(7)), // exact content duplicate of `a.jpg`
mk(4, "d.jpg", None, None, None, None),
];
let q = |json: &str| -> Query { serde_json::from_str(json).unwrap() };
@@ -485,7 +485,7 @@ fn facetas_filtros_e_duplicadas() {
assert_eq!(f["undated"], 1);
assert_eq!(f["carved"], 1);
assert_eq!(f["duplicates"], 1);
assert_eq!(f["small"], 4); // todos têm 10 bytes
assert_eq!(f["small"], 4); // all three files are 10 bytes long
assert_eq!(q(r#"{"min_size":30720}"#).select(&found).len(), 0);
assert_eq!(f["cameras"][0]["name"], "Canon");
let dcim = f["folders"]
@@ -507,8 +507,8 @@ fn facetas_filtros_e_duplicadas() {
assert_eq!(q(r#"{"query":"viagens"}"#).select(&found).len(), 1);
let mut items = q("{}").select(&found);
explorer::sort_items(&mut items, Some("date"));
assert_eq!(items[0].id, 2); // mais recente primeiro
assert_eq!(items.last().unwrap().id, 4); // sem data por último
assert_eq!(items[0].id, 2); // newest first
assert_eq!(items.last().unwrap().id, 4); // undated files sort last
}
// ------------------------------------------------------------------ carving
@@ -521,8 +521,8 @@ fn png_chunk(ty: &[u8; 4], data: &[u8]) -> Vec<u8> {
c
}
/// Regressão: texto ASCII depois de um PNG truncado vira "tamanho" de ~1,9 GB;
/// o carving não pode inventar um PNG gigante.
/// Regression test: ASCII text immediately following a truncated PNG looks like a ~1.9 GB chunk
/// length; the carver must reject the bogus chunk and never emit a multi-GB PNG.
#[test]
fn png_truncado_seguido_de_texto_nao_vira_arquivo_gigante() {
let mut d = vec![0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
@@ -540,13 +540,13 @@ fn png_truncado_seguido_de_texto_nao_vira_arquivo_gigante() {
);
assert_eq!(hit.confidence, Confidence::Low);
// sem nenhum dado de imagem (só cabeçalho) não é arquivo
// header-only PNG without any `IDAT` chunk is rejected
let mut d2 = vec![0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
d2.extend(png_chunk(b"IHDR", &[0, 0, 0, 8, 0, 0, 0, 8, 8, 2, 0, 0, 0]));
d2.resize(1 << 20, b'e');
assert!(signatures::detect(&MemSource(d2.clone()), 0, &d2[..64], 8 << 30).is_none());
// PNG completo continua com confiança alta e tamanho exato
// complete PNG with `IEND` still carves with `High` confidence and exact byte length
let mut ok = vec![0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
ok.extend(png_chunk(b"IHDR", &[0, 0, 0, 8, 0, 0, 0, 8, 8, 2, 0, 0, 0]));
ok.extend(png_chunk(b"IDAT", &[1; 100]));
@@ -557,10 +557,10 @@ fn png_truncado_seguido_de_texto_nao_vira_arquivo_gigante() {
assert_eq!((h.len as usize, h.confidence), (n, Confidence::High));
}
// ------------------------------------------------------------------ abrir no player do sistema
// ------------------------------------------------------------------ open in external system app
/// O vídeo é copiado para uma pasta temporária e entregue ao aplicativo padrão do sistema
/// (aqui um `xdg-open`/`open` falso grava o que recebeu).
/// `open_external` materializes the recovered file into a private temp folder and invokes
/// the platform opener (stubbed here via a fake `xdg-open`/`open` script in `PATH`).
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn abre_no_player_do_sistema() {