# 20 - English-Only Lockdown > Operator doc for the multi-layer English-only lockdown on arrflix.s8n.ru. > Goal: everything English only, no opt-out, no drift. Server, per-user, > and web-SPA layers all pinned; idempotent re-apply runner ships in this > repo so a Jellyfin restart, container recreate, or new-user-out-of-band > can never quietly reintroduce another locale. Date: 2026-05-08 Jellyfin version: 10.10.3 (`jellyfin/jellyfin` image) Live target: `https://arrflix.s8n.ru` --- ## Goal **Everything English only, no opt-out, no drift.** Three things this means in practice: 1. No user — admin or non-admin — can flip the UI to a non-English locale, either through the settings drawer or by deleting their `UICulture` value and letting `Accept-Language` win. 2. No new user created (via `bin/add-jellyfin-user.sh`, the web admin panel, or a future API integration) starts in any state other than `en-US`. 3. No server-side default (UI, metadata language, metadata country) drifts away from English over time, regardless of Jellyfin upgrades, container recreates, or admin-panel touches. The earlier first-pass attempt (`docs/15-force-english.md`, `bin/force-english-all-users.sh`) only covered point (2) for the five existing users at the time it ran. Points (1) and (3) and the persistence mechanism are handled here. Audit baseline for "what each layer looked like before this lockdown" is in `docs/19-english-only-audit.md`. --- ## Layers covered The Jellyfin locale story is layered, and **each layer must be pinned independently** — fixing one does not protect the others. The lockdown covers all four: ### 1. Server-wide Three keys in `/System/Configuration` (the JSON returned by `GET /System/Configuration`): | Key | Pinned value | What it controls | |---|---|---| | `UICulture` | `en-US` | Dashboard / admin UI default. Does NOT propagate to user UI (that's per-user — see layer 2) but is still pinned for consistency and so admin chrome never drifts. | | `PreferredMetadataLanguage` | `en` | Default language for metadata fetched from TMDB / TVDB / etc. when a library has no per-library override. | | `MetadataCountryCode` | `US` | Default country code for region-specific metadata (release dates, ratings boards, etc.). | The runner POSTs these via `/System/Configuration` (full read-modify-write — Jellyfin replaces the whole config dict). ### 2. Per-user Four keys in each user's `Configuration` object (the nested object inside `GET /Users/{id}` JSON): | Key | Pinned value | What it controls | |---|---|---| | `UICulture` | `en-US` | The actual UI language the web SPA renders for this user. **This is what fixes the "Abspielen" Play-button bug from doc 15.** | | `AudioLanguagePreference` | `eng` | Default audio track selection for playback. | | `SubtitleLanguagePreference` | `eng` | Default subtitle language for playback. | | `PlayDefaultAudioTrack` | `true` | Play the file's default audio track when languages match — keeps playback deterministic. | The runner iterates `GET /Users` and POSTs the merged config to `/Users/{id}/Configuration` for every account. ### 3. Web SPA (pre-auth + UI affordance) Pinning per-user `UICulture` only kicks in **after** authentication. Two extra surfaces are pre-auth or user-controllable: - **Pre-auth bundle strings** (login form, splash, "Sign In" button). The SPA picks the bundle based on `navigator.language` before any authentication. Without intervention, a `de-*` browser sees German login chrome. - **User settings drawer language switcher.** Even with `UICulture` pinned, a user can technically reopen `MyProfile/Display` and pick another language — the pin protects the default but not the switcher. Both are handled by the web overrides shipped in `web-overrides/english-lockdown.{js,css}` (sibling-agent commit, separate file from this doc): - **`english-lockdown.js`** — runs at the top of `index.html` before the bundle initialises. Overrides `navigator.language`, `navigator.languages`, and pins `localStorage["language"]` to `"en-us"` so the bundle's pre-auth locale loader picks English regardless of browser headers. - **`english-lockdown.css`** — hides the language `