fix(viewer): send Accept-Language from system locale - #2878
Merged
vpetersson merged 4 commits intoMay 12, 2026
Conversation
The Qt WebEngine in AnthiasWebview never set an Accept-Language header, so multi-language URL assets served their default (typically English) regardless of how the Pi's locale was configured. Plumb the host's locale through: bind-mount /etc/default/locale into the viewer container, source it in start_viewer.sh, and have the C++ webview build an RFC 7231 header from QLocale::system().uiLanguages(). A Pi configured with LANG=nl_NL.UTF-8 now advertises nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7 to origin servers. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR addresses multi-language URL assets rendering in the wrong language by propagating the host locale into the viewer/webview process and using it to set an Accept-Language header in QtWebEngine (fixes issue #480).
Changes:
- Build an RFC 7231
Accept-Languagevalue fromQLocale::system().uiLanguages()and apply it to the sharedQWebEngineProfile. - Source
/etc/default/localein the viewer startup script and preserve locale env vars across thesudo -u viewerboundary. - Bind-mount
/etc/default/localeinto the viewer container viadocker-compose.yml.tmpl.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
webview/src/view.cpp |
Derives Accept-Language from system UI languages and sets it on the default QtWebEngine profile. |
bin/start_viewer.sh |
Loads locale env from /etc/default/locale and preserves LANG/LANGUAGE/LC_ALL into the viewer process. |
docker-compose.yml.tmpl |
Mounts /etc/default/locale into the viewer container to surface host locale settings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Bind-mounting /etc/default/locale was risky: when the file is missing on the host (some minimal RPi OS / x86 images), Docker silently creates an empty *directory* at the mount path on the host, then maps it into the container — where the start_viewer.sh source would fail. Drop the bind mount and forward LANG/LANGUAGE through compose envsubst instead: upgrade_containers.sh sources /etc/default/locale before templating, so docker-compose.yml ends up with the host's locale baked into the viewer service's environment block. No host filesystem mutation, no compose-time bind dependency. Same change extends the fix to balena: the balena supervisor injects Device / Service Variables as env vars into the running container, so setting LANG=nl_NL.UTF-8 in the balena dashboard now reaches AnthiasWebview without any compose mount. Add discoverability comments in both balena compose templates. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Document how to override the device locale on Raspberry Pi OS (via update-locale) and on balena (via Device Variable) so multi-language URL assets serve the right language. Companion to the Accept-Language plumbing in the viewer. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Two Copilot review follow-ups on the locale plumbing:
* `LC_ALL` was missing from the envsubst forwarding — some operators
configure their locale via `LC_ALL` rather than `LANG`/`LANGUAGE`.
Add it to the viewer service's environment block and the sudo
--preserve-env allowlist (the latter was already in place).
* `envsubst` substitutes `${LANG}` to an empty string when LANG is
unset on the host, which means the viewer container starts with
`LANG=""` — semantically different from "unset" and capable of
overriding image defaults that downstream consumers (Python's
`locale`, libc helpers) rely on. Strip empty locale vars in
`start_viewer.sh` before launch so an unconfigured host leaves the
container's image defaults in place.
Reword the comment block so "issue 480" doesn't visually collide with
the YAML `#` comment marker.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
vpetersson
added a commit
that referenced
this pull request
May 12, 2026
* Master's #2881 (e2e-test findings) added an `installer_venv` persistent venv provisioned by `bin/install.sh::provision_host_agent_venv` and kept the systemd unit ExecStart pointed at that stable path. Drops the duplicate ansible-side `.anthias-venv` task and reverts the unit-template path I'd introduced — master's approach is upstream and avoids rewriting the unit path on devices installed pre-refactor. * Master's #2880 replaced the gum UI with whiptail. The auto-merge took whiptail's wiring; my arm64-aware INTRO_MESSAGE / set_device_type / Pi-tag-skip branches survived alongside it. * Master's #2878 viewer locale changes auto-merged with my arm64 start_viewer.sh conditional. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Issues Fixed
Closes #480.
Description
AnthiasWebview(the Qt WebEngine binary that renders URL assets) never set anAccept-LanguageHTTP header, so multi-language sites always served their default — typically English — regardless of how the device's locale was configured.Plumb the host locale through to the viewer container:
webview/src/view.cpp— build an RFC 7231Accept-Languagevalue fromQLocale::system().uiLanguages()(which honorsLANGUAGE/LC_ALL/LANGper the GNU gettext convention) and set it on the sharedQWebEngineProfile. A Pi withLANG=nl_NL.UTF-8now advertisesnl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7. SkipsC/POSIXso a misconfigured device falls back to QtWebEngine's default rather than to "C".bin/upgrade_containers.sh— source/etc/default/localebeforeenvsubstruns, so the host's configuredLANG/LANGUAGEend up baked into the generateddocker-compose.yml.docker-compose.yml.tmpl— declareLANG=${LANG}/LANGUAGE=${LANGUAGE}in the viewer service'senvironment:block; envsubst fills them in at compose-generation time. No host bind mount, so there's no risk of Docker silently creating an empty directory at the mount path when/etc/default/localedoesn't exist.bin/start_viewer.sh— addLANG/LANGUAGE/LC_ALLtosudo --preserve-envso the env vars survive the drop from root to thevieweruser on both Pi and x86 (cage) paths.docker-compose.balena.yml.tmpl/docker-compose.balena.dev.yml.tmpl— comment explaining that balena operators setLANGas a Device or Service Variable in the dashboard; the balena supervisor injects dashboard variables as container env vars at runtime, so no compose entry is needed.website/data/faq.yaml— FAQ entry walking the operator through setting the locale on Raspberry Pi OS (sudo update-locale) and balena (Device Variable).Checklist