Skip to content

fix(viewer): send Accept-Language from system locale - #2878

Merged
vpetersson merged 4 commits into
masterfrom
fix/viewer-accept-language-from-system-locale
May 12, 2026
Merged

fix(viewer): send Accept-Language from system locale#2878
vpetersson merged 4 commits into
masterfrom
fix/viewer-accept-language-from-system-locale

Conversation

@vpetersson

@vpetersson vpetersson commented May 12, 2026

Copy link
Copy Markdown
Contributor

Issues Fixed

Closes #480.

Description

AnthiasWebview (the Qt WebEngine binary that renders URL assets) never set an Accept-Language HTTP 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 7231 Accept-Language value from QLocale::system().uiLanguages() (which honors LANGUAGE / LC_ALL / LANG per the GNU gettext convention) and set it on the shared QWebEngineProfile. A Pi with LANG=nl_NL.UTF-8 now advertises nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7. Skips C / POSIX so a misconfigured device falls back to QtWebEngine's default rather than to "C".
  • bin/upgrade_containers.sh — source /etc/default/locale before envsubst runs, so the host's configured LANG / LANGUAGE end up baked into the generated docker-compose.yml.
  • docker-compose.yml.tmpl — declare LANG=${LANG} / LANGUAGE=${LANGUAGE} in the viewer service's environment: 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/locale doesn't exist.
  • bin/start_viewer.sh — add LANG / LANGUAGE / LC_ALL to sudo --preserve-env so the env vars survive the drop from root to the viewer user on both Pi and x86 (cage) paths.
  • docker-compose.balena.yml.tmpl / docker-compose.balena.dev.yml.tmpl — comment explaining that balena operators set LANG as 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

  • I have performed a self-review of my own code.
  • New and existing unit tests pass locally and on CI with my changes.
  • I have done an end-to-end test for Raspberry Pi devices.
  • I have tested my changes for x86 devices.
  • I added a documentation for the changes I have made (when necessary).

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]>
@vpetersson
vpetersson requested a review from a team as a code owner May 12, 2026 19:34
@vpetersson vpetersson self-assigned this May 12, 2026
@vpetersson
vpetersson requested a review from Copilot May 12, 2026 19:34

Copilot AI left a comment

Copy link
Copy Markdown

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 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-Language value from QLocale::system().uiLanguages() and apply it to the shared QWebEngineProfile.
  • Source /etc/default/locale in the viewer startup script and preserve locale env vars across the sudo -u viewer boundary.
  • Bind-mount /etc/default/locale into the viewer container via docker-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.

Comment thread docker-compose.yml.tmpl Outdated
vpetersson and others added 2 commits May 12, 2026 19:41
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]>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated 2 comments.

Comment thread docker-compose.yml.tmpl Outdated
Comment thread docker-compose.yml.tmpl Outdated
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
vpetersson requested a review from Copilot May 12, 2026 19:49
@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated no new comments.

@vpetersson
vpetersson merged commit 1ddf845 into master May 12, 2026
12 checks passed
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]>
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.

Multi language website shows English website, I want Dutch language

2 participants