Skip to content

i18n: Add wxGUI screenshot capture and Weblate uploader - #738

Draft
echoix wants to merge 9 commits into
mainfrom
claude/kind-pasteur-1akw59
Draft

echoix wants to merge 9 commits into
mainfrom
claude/kind-pasteur-1akw59

Conversation

@echoix

@echoix echoix commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Description

Prototype for giving translators visual context on Weblate for the grasswxpy domain, without storing images in the repository.

  • utils/weblate_screenshots/wxgui_capture.py captures the Layer Manager notebook pages, each Preferences page, and each category of the tool tree expanded in turn, under Xvfb. It writes PNG files and a manifest.json listing the grasswxpy messages, keyed by (msgctxt, msgid), that are drawn in each screenshot, with their file:line locations from the template as Weblate shows them.
    • Strings are found without OCR: the translation function is wrapped to map rendered text back to messages, and the widget tree is walked to collect the text drawn on screen. Hidden or clipped widgets are left out.
    • Tooltips are not drawn, so each tool of icon-only toolbars gets a copy of the screenshot with the tool framed, linked to its tooltip.
  • .github/workflows/wxgui_screenshots.yml builds GRASS with the GUI, runs the capture, and uploads the PNG files and the manifest as the wxgui-screenshots artifact. Periodic update calls it, and it also runs for pull requests that change it or the tools. The screenshots can then be linked by hand or synchronized with the uploader below.
  • utils/weblate_screenshots/weblate_upload.py synchronizes that output to a Weblate component through the REST API, using only endpoints that are the same in Weblate 5.16.2 and 2026.10.1:
    • It creates screenshots on the source language, matched by name.
    • It replaces an image only when its pixels differ.
    • It adds and removes string links to match the manifest.
    • It only touches screenshots with the managed prefix. --prune deletes those that are no longer captured, and --dry-run prints the planned changes.

Motivation and context

Weblate shows screenshots next to the strings they are linked to. Generating and linking them automatically keeps them current without binary churn in Git.

How has this been tested?

  • Capture: run on a CMake build with Xvfb, and by the new workflow on this pull request, which uploaded the artifact. Two runs with a fixed project path give pixel-identical images and identical manifests. Each manifest string drawn in a screenshot was checked against it with Tesseract OCR; the few strings OCR missed were checked by eye.
  • Uploader: pytest tests against a fake API server that models the Weblate responses, checked against the Weblate source at the 5.16.2 and 2026.10.1 tags. Not yet run against a real Weblate instance.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing
    functionality to not work as before)

Checklist

  • PR title provides summary of the changes and starts with one of the
    pre-defined prefixes
  • My code follows the code style
    of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.

Written with the help of Claude Code.

Capture screenshots of the Layer Manager notebook pages and of each
Preferences page, and write a manifest of the grasswxpy messages shown
in each screenshot so that they can later be uploaded to Weblate and
linked to the source strings as visual context.

Visible strings are found without OCR: the translation function is
wrapped to map rendered text back to messages (including messages with
placeholders), and the widget tree of the captured window is walked to
collect the shown text. Messages are keyed by (msgctxt, msgid) so that
templates with contexts work as well.

The screen is read with ImageMagick because wx.ScreenDC returns stale
content with GTK 3. With a fixed project path, two runs give identical
images and manifests.

Written with the help of Claude Code.
Add weblate_upload.py, which synchronizes the output of
wxgui_capture.py to a Weblate component using the REST API of Weblate
5.16.2: screenshots are created on the source language by name, their
image is replaced only when the pixels differ, and their links to
source strings are added or removed to match the manifest. Only
screenshots with the managed name prefix are modified, and --prune
deletes those no longer captured. --dry-run prints the planned changes.
The API token is read from the WEBLATE_TOKEN environment variable.

The tests run the uploader against a fake API server modeling the
5.16.2 responses (hyperlinked units, no id in the creation response,
trailing slashes required by the router). They are skipped when
requests is not installed.

Move both tools from locale/screenshots to utils/weblate_screenshots
because a tests directory under locale/ makes pytest's importlib mode
shadow the standard library locale module.

Written with the help of Claude Code.
Comment thread utils/weblate_screenshots/wxgui_capture.py Fixed
@echoix

echoix commented Oct 11, 2026

Copy link
Copy Markdown
Owner Author

Is there a way to not tie ourselves too much to the upload or Weblate? Like allowing us to manually do the work?

Can there be a zipped artifact upload in the job that updates the translations, which contains the images and the references to the strings , eg, what's needed to link the images?

Do you think the screenshots could be usable for documentation purposes?

Add a reusable workflow which builds GRASS with the GUI, runs
wxgui_capture.py under Xvfb, and uploads the PNG files and the manifest
of the strings they show as an artifact. This keeps the screenshots
usable without Weblate: they can be downloaded and linked by hand, or
synchronized with weblate_upload.py. The periodic update workflow calls
it, since its update-i18n job builds without the GUI, and it also runs
for pull requests changing it or the screenshot tools.

Resolve ImageMagick's import tool to a full path and exit with a clear
message when it is missing.

Written with the help of Claude Code.
@github-actions github-actions Bot added the CI label Oct 11, 2026

echoix commented Oct 11, 2026

Copy link
Copy Markdown
Owner Author

Is there a way to not tie ourselves too much to the upload or Weblate? Like allowing us to manually do the work?

Yes. The capture output doesn't depend on Weblate: it's PNG files plus manifest.json, which lists the exact msgids (and msgctxt) visible in each image. weblate_upload.py is just one optional consumer of it. Without it you can:

  • upload the images in Weblate's screenshot UI and use the manifest to search for and link the strings (or try Weblate's OCR);
  • run weblate_upload.py --dry-run locally against the downloaded artifact to see what it would change, then run it for real;
  • use the same output with another platform, since the manifest is plain JSON.

Can there be a zipped artifact upload in the job that updates the translations, which contains the images and the references to the strings?

Added in 79c0438. The new reusable workflow .github/workflows/wxgui_screenshots.yml builds GRASS with the GUI, runs the capture under Xvfb, and uploads a wxgui-screenshots artifact (zipped by GitHub, kept 30 days) with the PNGs and manifest.json. Periodic update calls it as a separate job. It can't go in update-i18n itself, because that job uses a minimal build without the GUI and wxPython. It also runs for PRs that change the workflow or the tools, so this PR exercises it. It doesn't upload anything to Weblate; that stays a manual or later step.

Do you think the screenshots could be usable for documentation purposes?

Partly:

  • Where it helps: dialogs whose look is the point, such as Preferences pages, tool dialogs and wizard pages. Those docs screenshots could be regenerated from the same scenarios, so they stay current and consistent in size and theme. Localized versions could come almost for free by running with LANG=xx.
  • Where it doesn't: most existing wxGUI documentation images show maps with sample data, specific tool states or annotations. Those need dedicated scenarios with the nc dataset, or are better made by hand.
  • Storage: committing generated images for docs brings back the binary churn. Generating them at documentation build time, or publishing them from this artifact, would avoid it.

I'd keep the docs use as a possible follow-up once there are more scenarios.


Generated by Claude Code

claude and others added 6 commits October 11, 2026 13:13
The grass command exits when GRASS_CONFIG_DIR points to a directory
which does not exist.
Match the other reusable workflow calls, which zizmor now checks in
pre-commit.
The manifest listed strings which are not visible in the screenshots:
tooltips, window titles (drawn by the window manager), labels of
icon-only toolbar tools, tabs of notebooks which hide them for a single
page, and widgets clipped out of view by their parents. Leave these out
and add the visible items of tree widgets, such as the tool tree.

Prefer an exact match of the rendered text, so that a "&File" menu does
not also match a "File" label used elsewhere.

Add the file:line locations of each string from the template, as
Weblate shows them, to ease linking strings by hand.

Written with the help of Claude Code.
Tooltips are not drawn in screenshots, so give each tool of icon-only
toolbars its own copy of the screenshot with the tool framed, linked to
its tooltip. AUI toolbars give the position of their tools. wx.ToolBar
does not with GTK, so a tool is found by toggling its enabled state and
comparing the toolbar before and after.

Add a tool-tree scenario capturing each category of the tool tree
expanded in turn, page by page, linked only to the items of that
category which are entirely in view. Look up text made of parts
separated by several spaces part by part, as the tree shows the label of
a tool followed by its command.

Written with the help of Claude Code.
The endpoints and responses it uses are the same in Weblate 5.16.2 and
2026.10.1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants