Skip to content

Add opt-in i18n foundation for apps (Language setting + injected helpers) - #56

Open
avandeputte wants to merge 2 commits into
csader:mainfrom
avandeputte:feat/i18n-core
Open

Add opt-in i18n foundation for apps (Language setting + injected helpers)#56
avandeputte wants to merge 2 commits into
csader:mainfrom
avandeputte:feat/i18n-core

Conversation

@avandeputte

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in internationalization foundation so apps can render in the user's language — without changing the contract for any existing app.

The whole feature sits behind a new "Enable internationalization" master toggle, which defaults to OFF. With it off, the UI and behaviour are identical to today.

What's in it

  • server/i18n.py + server/i18n_data.json — a logic/data split so translations live in data, not code. Strings are grouped by semantic topic domain (the gettext msgctxt model), which keeps homographs distinct (weather HIGH vs tides HIGH; M as minutes vs metres). ~34 languages including regional variants (pt-BR, es-MX, fr-CA…) that inherit their base language's translations. Every key carries a context note for translators.
  • server/location.py + server/weather.py — one shared geocoder, babel-backed currency/country lookup (no hardcoded tables), and a 10-minute TTL cache on weather so the helper can't be called excessively.
  • Runtime injectionget_plugin_pages() inspects fetch()'s signature and passes i18n / get_weather / get_location only when the app declares them. Classic 4-arg apps are completely untouched.
  • Settings UI — a Language selector and a 🌐 badge, both gated by the master toggle. The toggle carries a disclaimer that it only makes sense on modules running Universal Firmware with a configurable character map (a stock reel set is ASCII-only, so it has no accented flaps).
  • Gateway RX decode fix — the gateway receive path decoded/re-encoded frames as UTF-8 with errors="ignore", which silently dropped lone high bytes (0x800xFF). That corrupted a module's A-command character-map sync for any reel set containing extended glyphs. It now uses latin-1, a transparent byte↔code-point codec, so the raw cp1252 bytes survive to the downstream .decode('cp1252'). This is a standalone bug fix and can be cherry-picked separately if you'd prefer.

Compatibility

  • The toggle defaults off → no behaviour change for existing installs.
  • babel is a soft dependency; i18n.py falls back to English/strftime without it.
  • Existing 4-arg apps receive exactly the call they get today.

Testing

  • Full suite green (52 tests), including new tests/test_i18n.py and tests/test_gateway_transport.py.
  • Verified this branch works against the unmodified upstream apps — i.e. the foundation alone is backward compatible.
  • Run end-to-end on real hardware.

The apps that adopt this foundation are in a follow-up PR.

🤖 Generated with Claude Code

avandeputte and others added 2 commits July 11, 2026 18:03
…ers)

Adds an opt-in internationalization foundation that apps can adopt without
breaking the existing app contract:

- server/i18n.py + server/i18n_data.json: logic/data split. Translations are
  grouped by semantic topic domain (gettext msgctxt model) so homographs stay
  distinct (weather HIGH vs tides HIGH). ~34 languages, incl. regional variants
  that inherit their base language. Each key carries a translator 'context' note.
- server/location.py + server/weather.py: one shared geocoder, babel-backed
  currency/country (no hardcoded tables), 10-minute TTL-cached weather.
- Runtime injection: get_plugin_pages inspects fetch()'s signature and passes
  i18n / get_weather / get_location only when the app declares them. Classic
  4-arg apps are completely untouched.
- Settings: an 'Enable internationalization' master toggle (DEFAULT OFF) gates
  the Language selector and the app badge. With it off the UI and behaviour are
  identical to today -- the toggle carries a disclaimer that it needs modules on
  Universal Firmware with configurable character maps.
- babel is a soft dependency; i18n.py falls back to English without it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The gateway receive path decoded/re-encoded frames as UTF-8 with
errors=ignore, which silently dropped lone high bytes (0x80-0xFF). That
corrupted a module's A-command character-map sync for any reel set with
extended (international) glyphs. Decode and re-encode with latin-1 instead
-- a transparent byte<->code-point codec -- so the raw cp1252 bytes survive
to the downstream .decode('cp1252'). Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant