Skip to content

feat(i18n): varrer portugues sem acento e ensinar o scanner a ve-lo - #467

Merged
MarkSant merged 2 commits into
mainfrom
feat/i18n-unaccented-sweep
Aug 15, 2026
Merged

feat(i18n): varrer portugues sem acento e ensinar o scanner a ve-lo#467
MarkSant merged 2 commits into
mainfrom
feat/i18n-unaccented-sweep

Conversation

@MarkSant

Copy link
Copy Markdown
Owner

O critério era mais fraco do que parecia

A fase 3 fechou com i18n_scan.py reportando TOTAL: 0. Mas o scanner detectava português pelos acentos — então Salvar projeto, Gravando e Nenhum video passavam por ele, e pelo ratchet construído em cima dele.

Detector

Segundo passe por lista de palavras (PORTUGUESE_WORDS). Achados agora vêm rotulados [accent] ou [word], filtráveis com --kind. Uma palavra só entra na lista se for portuguesa e não for também inglesaate, anterior, data, taxa e cores ficam de fora de propósito, cada uma um falso positivo real encontrado durante a varredura.

Dois ajustes mantêm o passe silencioso o bastante para continuar ligado: literais em forma de identificador (distancia_no_{}_cm, caminhos, eventos do structlog) não são texto de interface; e linhas marcadas # i18n: not-ui cobrem português que é comparado, nunca exibido.

A allowlist escondia interface real

Ela casa por substring, e a entrada grupo — necessária para uma única chave de dict — isentava toda frase que contivesse a palavra. Sete strings acentuadas passavam despercebidas por isso, duas delas tooltips de vários parágrafos do wizard, enquanto o scanner anunciava TOTAL: 0. Padrões agora podem ser escritos =grupo para casar apenas o literal inteiro.

Varredura: 123 literais

Quase todos estavam ao lado de uma chamada _() já traduzida, no mesmo arquivo e muitas vezes na mesma função: arduino_manager misturava os dois no mesmo _notify_log; wizard_service.validate_experimental_design tinha _() nos dois primeiros return e português cru nos cinco seguintes; hardware_benchmark traduziu os passos 2/3/4/6 e deixou 1 e 5; config_validator traduziu três das quatro checagens de faixa.

Um achado era inconsistência de esquema, não de idioma: a fase 3 anglicizou os cabeçalhos de geotaxia no ramo dimensionado de rename_geotaxis_columns e não tocou no fallback, duplicado em data_transformer e word_reporter. A mesma análise gerava Bottom (0.0-5.0cm) [%] ou Geotaxis Zona 1 - Fundo (%) conforme as dimensões do aquário estivessem disponíveis.

Duas colisões de msgid foram barradas antes de entrar no catálogo, onde i18n_pairs.py teria mantido a primeira e descartado a segunda em silêncio: File: {name} (gravado com outro nome de placeholder) e Pending, cujo texto pt_BR é o plural Pendentes por rotular uma contagem — a linha por sujeito do bloco recebeu msgid próprio, Not recorded.

Mensagens de exceção que só chegam a logs seguiram para inglês simples, sem _(), alinhadas às vizinhas que já estavam assim.

Rebase sobre #464 e #465

Quatro conflitos; dois merecem nota.

video_selector_tree_manager — este PR anotava o literal Nenhum com # i18n: not-ui, justificando por que é comparado e nunca exibido. O #464 descobriu que o método inteiro não tem chamador e o removeu. A deleção vence: a anotação defendia uma linha que deixou de existir.

Catálogos — não foram mesclados como texto. Parti da versão do main, que já carrega as cinco strings novas do #465, e reconstruí .pot/.po/.mo com scripts/update_translations.py sobre as fontes já mescladas. Mesclar .po textualmente é como um conflito acaba descartado em silêncio; regenerar é a única resolução verificável.

Verificação

🤖 Generated with Claude Code

A fase 3 fechou com `i18n_scan.py` reportando TOTAL: 0, mas o criterio era
mais fraco do que parecia: o scanner detectava portugues pelos ACENTOS, entao
`Salvar projeto`, `Gravando` e `Nenhum video` passavam por ele e pelo ratchet
construido em cima dele.

Detector
--------
Segundo passe por lista de palavras (`PORTUGUESE_WORDS`). Achados agora sao
rotulados `[accent]` ou `[word]`, filtraveis com `--kind`. Uma palavra so entra
na lista se for portuguesa E nao for tambem inglesa -- `ate`, `anterior`,
`data`, `taxa` e `cores` ficam de fora de proposito, cada uma delas um falso
positivo real encontrado durante a varredura.

Dois ajustes mantem o passe silencioso o bastante para continuar ligado:
literais em forma de identificador (`distancia_no_{}_cm`, caminhos, eventos do
structlog) nao sao texto de interface; e linhas marcadas `# i18n: not-ui`
cobrem portugues que e COMPARADO, nunca exibido.

A allowlist escondia texto de interface real
--------------------------------------------
Ela casa por substring, e a entrada `grupo` -- necessaria para uma unica chave
de dict -- isentava toda frase que contivesse a palavra. Sete strings
ACENTUADAS passavam despercebidas por isso, duas delas tooltips de varios
paragrafos do wizard, enquanto o scanner anunciava TOTAL: 0. Padroes agora
podem ser escritos `=grupo` para casar apenas o literal inteiro.

Varredura: 123 literais
-----------------------
Quase todos estavam ao lado de uma chamada `_()` ja traduzida, no mesmo arquivo
e muitas vezes na mesma funcao: `arduino_manager` misturava os dois no mesmo
`_notify_log`; `wizard_service.validate_experimental_design` tinha `_()` nos
dois primeiros `return` e portugues cru nos cinco seguintes;
`hardware_benchmark` traduziu os passos 2/3/4/6 e deixou 1 e 5;
`config_validator` traduziu tres das quatro checagens de faixa.

Um achado era inconsistencia de ESQUEMA, nao de idioma: a fase 3 anglicizou os
cabecalhos de geotaxia no ramo dimensionado de `rename_geotaxis_columns` e nao
tocou no fallback, duplicado em `data_transformer` e `word_reporter` -- a mesma
analise gerava `Bottom (0.0-5.0cm) [%]` ou `Geotaxis Zona 1 - Fundo (%)`
conforme as dimensoes do aquario estivessem disponiveis.

Duas colisoes de msgid foram barradas antes de entrar no catalogo, onde
`i18n_pairs.py` teria mantido a primeira e descartado a segunda em silencio:
`File: {name}` (gravado com outro nome de placeholder) e `Pending`, cujo texto
pt_BR e o plural `Pendentes` por rotular uma contagem -- a linha por sujeito do
bloco recebeu msgid proprio, `Not recorded`.

Mensagens de excecao que so chegam a logs seguiram para ingles simples, sem
`_()`, alinhadas as vizinhas que ja estavam assim
(`project_lifecycle_manager`, `schemas`, `wizard/models`).

Rebase sobre os PRs #464 e #465
-------------------------------
Quatro conflitos, dois deles interessantes.

`video_selector_tree_manager`: este commit anotava o literal `Nenhum` com
`# i18n: not-ui`, justificando por que ele e comparado e nunca exibido. O #464
descobriu que o metodo inteiro nao tem chamador nenhum e o removeu. A delecao
vence -- a anotacao defendia uma linha que deixou de existir.

Catalogos: nao foram mesclados como texto. Parti da versao do main, que ja
carrega as cinco strings novas do #465, e reconstrui `.pot`/`.po`/`.mo` com
`scripts/update_translations.py` sobre as fontes ja mescladas. Mesclar `.po`
textualmente e como um conflito acaba silenciosamente descartado; regenerar e a
unica resolucao verificavel. Conferido em runtime via `gettext`: as 5 strings do
#465 mais as 84 deste PR, 89 de 89, todas resolvem.

Verificacao: ruff e ruff format limpos, mypy limpo em 703 arquivos, 4256
rapidos, 1087 gui, `i18n_scan.py` em zero nos dois passes -- agora tambem sobre
o codigo que o #465 acabou de introduzir.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 15, 2026 14:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the i18n migration gate by updating scripts/i18n_scan.py to detect Portuguese UI strings that contain no accents (via a wordlist-based second pass), tightening allowlist semantics (exact-match patterns like =grupo), and sweeping a large set of remaining Portuguese UI literals into _() with updated catalogs/pairs/tests.

Changes:

  • Extend i18n_scan.py with a second “word” heuristic, line-level # i18n: not-ui exemptions, and --kind filtering.
  • Fix allowlist overreach by adding exact-match patterns (=...) and documenting the rule.
  • Migrate remaining Portuguese UI strings to English source + _(), updating tests and gettext catalogs accordingly.

Reviewed changes

Copilot reviewed 71 out of 73 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/ui/dialogs/test_dialogs_batch3.py Adjusts dialog-title test to assert localized “No Day” label rather than leaking stored sentinel.
tests/ui/dialogs/test_dialogs_batch2.py Updates expectations for translated progress/cancel status strings.
tests/test_wizard_experimental_design.py Updates assertions to match English validation messages.
tests/test_io_arduino_manager.py Updates assertions for Arduino log messages now emitted via translated msgids.
tests/i18n/test_scanner_unaccented_pass.py Adds unit coverage for the new unaccented (“word”) scanner heuristic and its exemptions.
tests/core/test_video_processing_service_tracking.py Updates cancellation status assertion to English msgid.
tests/core/test_project_workflow_service.py Updates welcome-guide title expectation to translated English msgid.
src/zebtrack/utils/hardware_benchmark.py Wraps previously-untranslated benchmark progress steps with _().
src/zebtrack/ui/wizard/wizard_dialog.py Translates wizard window title, step title, and cancel confirmation prompts.
src/zebtrack/ui/wizard/models.py Updates several validation messages to English source strings (but some remain unwrapped by _()).
src/zebtrack/ui/wizard/experimental_design_step.py Translates multi-paragraph tooltips for experimental design controls.
src/zebtrack/ui/wizard/custom_regex_dialog.py Translates dialog subtitle, headings, and error marker text.
src/zebtrack/ui/project_workflow_adapter.py Translates default error dialog title/message when detector setup fails.
src/zebtrack/ui/gui.py Translates initial model/OpenVINO/hardware status display strings.
src/zebtrack/ui/format_utils.py Adds # i18n: not-ui marker on stored “sem dia” comparison.
src/zebtrack/ui/dialogs/template_dialog.py Translates template dialog options and labels; adds i18n import.
src/zebtrack/ui/dialogs/subject_selection_dialog.py Localizes day title rendering while keeping comparisons against stored Portuguese tokens.
src/zebtrack/ui/dialogs/start_recording_dialog.py Translates selection labels and cancel button.
src/zebtrack/ui/dialogs/project_video_import_dialog.py Translates dialog title and subject summary strings.
src/zebtrack/ui/dialogs/preview_polygon_dialog.py Translates reject/approve actions, retry label, status, and overlay label.
src/zebtrack/ui/dialogs/pending_videos_dialog.py Translates tree headings and action buttons.
src/zebtrack/ui/dialogs/multi_aquarium_live_preview_window.py Translates timer label text.
src/zebtrack/ui/dialogs/model_diagnostics_dialog.py Translates “Close” button label.
src/zebtrack/ui/dialogs/diagnostic_progress_dialog.py Translates “Processing…”, cancel, and cancellation status text.
src/zebtrack/ui/dialogs/create_project_dialog.py Translates dialog title and validation error UI copy.
src/zebtrack/ui/dialogs/calibration_dialog.py Translates close button and “copy globals”/folder chooser UI strings.
src/zebtrack/ui/dialogs/block_detail_dialog.py Translates recorded/not-recorded labels, “no files”, cancel, and error dialogs; adds # i18n: not-ui where persistence contracts apply.
src/zebtrack/ui/dialogs/aquarium_detection_progress_dialog.py Translates “Waiting for frames…” label.
src/zebtrack/ui/dialogs/aquarium_assignment_dialog.py Translates filename label, metadata labels, and confirm/cancel actions.
src/zebtrack/ui/components/validators/config_validator.py Translates one remaining range-validation message.
src/zebtrack/ui/components/validation_manager.py Adds # i18n: not-ui markers for comparisons against stored day/group spellings.
src/zebtrack/ui/components/state_synchronizer.py Adds # i18n: not-ui marker for legacy prefix stripping logic.
src/zebtrack/ui/components/roi_template_manager.py Translates warning dialogs for missing template/zone data.
src/zebtrack/ui/components/project_initializer.py Translates “Project ready” status message.
src/zebtrack/ui/components/event_dispatcher.py Translates default warning/error titles used by dispatcher.
src/zebtrack/ui/components/dialog_manager.py Translates ROI color dialog title.
src/zebtrack/ui/components/canvas/event_handler.py Translates success/status messages for zone definition and redo.
src/zebtrack/ui/components/analysis_display.py Translates “Active track ID” label.
src/zebtrack/ui/components/analysis_controls.py Translates “Active track ID” label.
src/zebtrack/ui/builders/project_widgets.py Translates progress-grid tab labels and error rendering message.
src/zebtrack/ui/builders/common_widgets.py Adds # i18n: not-ui markers and translates welcome text.
src/zebtrack/locales/zebtrack.pot Regenerates main POT with new msgids and updated references.
src/zebtrack/locales/reporter.pot Regenerates reporter POT with updated line references.
src/zebtrack/locales/pt_BR/LC_MESSAGES/reporter.po Syncs pt_BR reporter translations to regenerated POT metadata/refs.
src/zebtrack/locales/_pairs/pr4-unaccented-ui.json Adds msgid→pt_BR pairs for UI strings found by unaccented sweep (batch 2).
src/zebtrack/locales/_pairs/pr4-unaccented-core.json Adds msgid→pt_BR pairs for core/coordinator/service strings found by unaccented sweep (batch 1).
src/zebtrack/io/arduino_manager.py Translates connect/disconnect/send command logs using _() and format placeholders.
src/zebtrack/core/viewmodels/hardware_status_view_model.py Translates file picker title and filetype label.
src/zebtrack/core/video/tracking_session_runner.py Translates cancellation and “starting process” status messages.
src/zebtrack/core/video/processing_worker.py Translates processing progress status text.
src/zebtrack/core/services/wizard_service.py Translates Arduino port “[no handshake]” label and experimental-design validation returns.
src/zebtrack/core/services/weight_manager.py Translates OpenVINO conversion failure messages (shown verbatim in UI).
src/zebtrack/core/services/detector_service.py Translates detector initialization failure message.
src/zebtrack/core/services/arduino_ack_semantics.py Adds # i18n: not-ui marker for regex literal exemption in scanner.
src/zebtrack/core/recording/live_session_manager.py Translates recording countdown/status messages using formatted placeholders.
src/zebtrack/core/recording/frame_processing_pipeline.py Translates stabilization status and “● Recording” indicator.
src/zebtrack/core/project/schemas.py Anglicizes a schema validation message (non-UI, schema validation).
src/zebtrack/core/project/project_workflow_service.py Translates the welcome guide’s title string.
src/zebtrack/core/project/project_lifecycle_manager.py Anglicizes developer-facing guards for parameter validation.
src/zebtrack/core/detection/multi_aquarium_detector.py Translates aquarium overlay label (“Aquarium {number}”).
src/zebtrack/coordinators/ui_state_coordinator.py Translates cancellation feedback status text.
src/zebtrack/coordinators/recording_session_coordinator.py Translates “Ready.” status on external-trigger clear.
src/zebtrack/coordinators/progress_tracking_coordinator.py Translates processing status/steps (“Processing…”, “Finalizing…”, “Ready.”).
src/zebtrack/coordinators/live_camera_session_coordinator.py Adds # i18n: not-ui marker for stored Portuguese “dia ” prefix stripping.
src/zebtrack/analysis/reporters/word_reporter.py Fixes geotaxis fallback headers to stable English exported schema (non-_()), matching sized branch.
src/zebtrack/analysis/data_transformer.py Fixes geotaxis fallback headers to stable English exported schema (non-_()), matching sized branch.
scripts/i18n_scan.py Implements unaccented Portuguese detection, exact allowlist semantics, per-line exemptions, and --kind filtering.
scripts/i18n_allowlist.txt Documents and applies exact-match allowlist patterns to avoid substring overreach.
docs/guides/developer/i18n.md Documents exact-match allowlist and # i18n: not-ui usage; updates scanner/ratchet guidance.
CHANGELOG.md Updates changelog to describe unaccented sweep and allowlist/scanner improvements.
Suppressed comments (1)

src/zebtrack/ui/wizard/models.py:204

  • The group name validation errors raised here are shown to the user via wizard validation, but the messages are not wrapped with _(). That prevents localization (pt_BR will show English), despite these msgids already existing in the translation catalogs.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 151 to 155
def validate_external_trigger(cls, v, info):
"""External trigger requires Arduino to be enabled."""
if v and not info.data.get("use_arduino"):
raise ValueError("Modo de trigger externo requer Arduino ativado")
raise ValueError("External trigger mode requires Arduino to be enabled")
return v
Co-authored-by: MarkSant <22659808+MarkSant@users.noreply.github.com>
@MarkSant
MarkSant merged commit 45ba168 into main Aug 15, 2026
6 checks passed
@MarkSant
MarkSant deleted the feat/i18n-unaccented-sweep branch August 15, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants