Skip to content

feat(i18n): translate the GUI strings left in English, guard against drift, document the workflow - #499

Open
Guts wants to merge 3 commits into
masterfrom
fix/i18n-missing-gui-strings
Open

Guts wants to merge 3 commits into
masterfrom
fix/i18n-missing-gui-strings

Conversation

@Guts

@Guts Guts commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Description

Widget text goes through Qt's own i18n, whose .ts files are refreshed by running pylupdate6 by hand. Three merged PRs — cancellation, parallel scan, driver-based format gating — added widget text without that step, so 8 strings had no French or Spanish translation at all and showed in English whatever the language selected:

Context Source string
DicoGIS Cancel
DicoGIS Canceling...
DicoGIS Folder scan canceled.
DicoGIS Processing canceled. Partial results were saved to the output.
TabFiles Not available: the installed GDAL build does not include the driver required for this format.
TabSettings Parallelize folder scan (recommended only for network-mounted / high-latency storage; …)
TabSettings Parallel scan: max workers (0 = auto)
TabSettings Auto

Ran pylupdate6 over dicogis/ui, translated the 8 new entries in both locales, and recompiled the .qm with lrelease. The .ts files go from 68 to 76 messages, with 0 unfinished in FR and ES.

Verified at runtime, not just on paper: a QTranslator loading each compiled .qm returns the expected translation for all 8 — e.g. CancelAnnuler / Cancelar. Existing translations are untouched; the FR/ES wording follows the file's conventions (French typographic space before :, none in Spanish).

Correcting my own earlier count, in case it was quoted anywhere: I had said "at least 9 strings". It is 8 — Fund & Support was already translated, my grep had missed it because the .ts escapes it as Fund & Support.

Guarding against the next drift

The real defect is that nothing enforced the pipeline being re-run, so tests/test_ui_i18n.py is added:

  • test_every_widget_string_is_translated collects every self.tr() source string by walking the GUI modules with ast — so it needs neither PyQt6 nor GDAL and runs in any environment — and fails naming the offending strings.
  • test_compiled_qm_matches_the_ts loads the compiled .qm and compares it against the .ts. Only the .qm is read at runtime, so a .ts updated without re-running lrelease changes nothing for the end user; this catches that half of the mistake. It skips when PyQt6 is absent.
  • test_translatable_sources_are_found guards the guard: an extraction returning nothing would make the rest vacuously true.

Both fail on the previous state, listing exactly those 8 strings:

AssertionError: 8 string(s) have no FR translation. Re-run pylupdate6 (see CLAUDE.md),
fill the new <translation> entries and recompile the .qm: ['Auto', 'Cancel', …]

New page: docs/development/translation.md

The pipeline was only described in CLAUDE.md, an agent-facing file, so a human contributor had nowhere to learn it — which is how those 8 strings shipped untranslated in the first place. Modelled on qtribu's translation page, updated for PyQt6 and for this project:

  • pylupdate6 ships with PyQt6 itself, so the gui extra suffices; only lrelease and Linguist need system packages (qt6-l10n-tools, linguist-qt6), which Ubuntu installs outside the default PATH;
  • no .pro profile is involved, unlike pylupdate5pylupdate6 takes the source files directly;
  • unlike qtribu, whose CI generates them, the compiled .qm are tracked here: they are package data and PyInstaller --add-data inputs, so a stale .qm ships a stale interface;
  • dicogis_en.ts is a source-language reference, never compiled nor loaded;
  • the second, separate mechanism — TextsManager and locale/lang_*.xml — is documented alongside, since which one applies depends on whether the string is GUI or pipeline output.

It also covers adding a new language and points at the new tests. Package names and binary paths were checked against the real packages rather than assumed: the Qt 6 Linguist binary is linguist, not linguist6.

Sphinx build verified: it succeeds, the page is registered in the index toctree, and the warning set is identical to master's (55 non-intersphinx warnings either way). One iteration was needed — a ### Requirements heading collided with development/ubuntu.md under autosectionlabel, so it is now ### Required tools.

Related issue(s)

None — found while reviewing the code ahead of the 4.0.0-beta13 release.

Type of change

  • Bug fix (fix/… branch)
  • New feature or enhancement (feature/… branch) — user-visible translations plus a new guard; the branch is named fix/… since it repairs shipped behaviour, rename or relabel as you prefer
  • Documentation (docs/… branch or *.md/docs/** changes)
  • Packaging / build (packaging/… branch or builder/** changes)
  • Tooling / CI-CD (tooling/… branch or .github/** changes)
  • Tests only (tests/** changes)

Checklist

  • I have read the Contributing Guidelines
  • pre-commit run --all-files passes locally — its two ruff hooks on the changed files: ruff-format reports them already formatted, ruff check --fix-only rewrites nothing, and ruff check reports no violation on the new test. It needed one # noqa: S314 with a stated reason, the parsed file being the project's own .ts; dicogis/utils/texts.py carries the same unsuppressed pattern on master, so tell me if you would rather I dropped the suppression for consistency
  • I have added or updated tests covering my changes
  • I have updated the documentation where relevant — the new page, plus the index toctree
  • pytest passes locally — 226 passed on the non-GDAL modules and 41 on tests/ui, with the same 8 pre-existing sandbox failures as on master

AI/LLM disclosure

🤖 Generated with Claude Code

https://claude.ai/code/session_01WABM4NMiD2DhEVreKLxWLq

…drift

Widget text goes through Qt's own i18n, whose .ts files are refreshed by
running pylupdate6 by hand. Three merged PRs -- cancellation, parallel scan,
driver-based format gating -- added widget text without that step, so 8
strings had no French or Spanish translation at all and showed in English
whatever the language selected: "Cancel", "Canceling...", "Folder scan
canceled.", "Processing canceled. Partial results were saved to the output.",
the unsupported-format tooltip, and the three parallel scan options.

Re-run pylupdate6 over dicogis/ui, translate the 8 new entries in both
locales, and recompile the .qm with lrelease. Checked at runtime: a
QTranslator loading each .qm returns the expected translation for all 8.

Add tests/test_ui_i18n.py so this cannot drift again. It collects every
`self.tr()` source string by walking the GUI modules with ast -- needing
neither PyQt6 nor GDAL -- and fails, naming the offending strings, when one
has no translation in a shipped locale. A second test loads the compiled .qm
and compares it against the .ts, since only the .qm is read at runtime: a .ts
updated without re-running lrelease would change nothing for the end user.
Both fail on the previous state, listing exactly those 8 strings.

AI-Level: 7 (human specced, bots coded)
AI-Model: claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WABM4NMiD2DhEVreKLxWLq
@github-actions github-actions Bot added bug Houston, we've a problem! quality labels Sep 3, 2026
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.09%. Comparing base (b92f5b0) to head (e307c44).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #499   +/-   ##
=======================================
  Coverage   76.09%   76.09%           
=======================================
  Files          46       46           
  Lines        3409     3409           
  Branches      415      415           
=======================================
  Hits         2594     2594           
  Misses        695      695           
  Partials      120      120           
Flag Coverage Δ
unittests 76.03% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The pylupdate6/lrelease pipeline was only described in CLAUDE.md, an
agent-facing file, so a human contributor had nowhere to learn that adding a
`self.tr()` string means regenerating the .ts files and recompiling the .qm.
That is exactly how 8 strings ended up shipped untranslated.

Modelled on qtribu's own translation page, updated for PyQt6 and for this
project's specifics:

- pylupdate6 comes with PyQt6 itself, so the `gui` extra suffices; only
  lrelease and Linguist need system packages (qt6-l10n-tools, linguist-qt6),
  and Ubuntu puts them outside the default PATH;
- no .pro profile is involved, pylupdate6 takes the source files directly;
- unlike qtribu, the compiled .qm files are tracked here: they are package
  data and PyInstaller --add-data inputs, so a stale .qm ships a stale
  interface;
- dicogis_en.ts is a source-language reference, never compiled nor loaded;
- the second, separate mechanism -- TextsManager and locale/lang_*.xml -- is
  documented alongside, since which one applies depends on whether the string
  is GUI or pipeline output.

Also covers adding a new language and points at tests/test_ui_i18n.py.

Verified: the Sphinx build succeeds and its warning set is unchanged from
master's, page registered in the index toctree.

AI-Level: 7 (human specced, bots coded)
AI-Model: claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WABM4NMiD2DhEVreKLxWLq
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 3, 2026
@Guts Guts changed the title feat(i18n): translate the GUI strings left in English, guard against drift feat(i18n): translate the GUI strings left in English, guard against drift, document the workflow Sep 3, 2026
…tion

Rewrapped the new translation guide to one line per paragraph/list item,
matching the style already used across docs/**/*.md (see
docs/development/documentation.md) instead of the hard-wrapped prose it
shipped with.

Added the convention to CLAUDE.md's "Conventions" section so it isn't
reintroduced next time Markdown prose is authored here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WABM4NMiD2DhEVreKLxWLq
@sonarqubecloud

sonarqubecloud Bot commented Sep 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Houston, we've a problem! documentation Improvements or additions to documentation quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants