Skip to content

feat(settings): offer every language the web does, and make them match - #152

Closed
RXWatcher wants to merge 6 commits into
Silo-Server:mainfrom
RXWatcher:feat/subtitle-language-list
Closed

feat(settings): offer every language the web does, and make them match#152
RXWatcher wants to merge 6 commits into
Silo-Server:mainfrom
RXWatcher:feat/subtitle-language-list

Conversation

@RXWatcher

@RXWatcher RXWatcher commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

The report

On Android I'm running the store version but can't put Dutch as default sub.

Correct, and it is not fixed on main either — the picker offers the same ten languages in both, and Dutch is in neither:

English, Spanish, French, German, Japanese, Korean, Chinese, Portuguese, Italian, Russian

It was never a server limitation

The settings contract types playback.subtitle_language as:

{ "type": "language_tag", "nullable": true }

validated for BCP 47 shape only — there is no enum. The web client already offers 37 languages, Dutch included. Android's picker was simply shorter.

LanguageOptions.tags now mirrors the web's ISO 639-1 table (web/src/player/utils/languageNames.ts in silo-server) in its order. Ten becomes 37.

Worth noting: the table's own comment already named "nl" as a tag that arrives "synced from another surface" and must be preserved. So a Dutch preference set on the web was being honoured by playback the whole time — the picker just refused to display or re-select it. The user was looking at a setting that was working and appeared absent.

Completing the picker exposed an older bug

Auto-selection compares the stored preference against a track's language with both sides through canonicalSubtitleLanguage (MobileSubtitleAutoSelection, TrackSelectionFingerprint). That function's ISO 639-2 alias table covered seven languages while the picker offered ten:

"eng" -> "en"   "spa" -> "es"   "fre", "fra" -> "fr"
"ger", "deu" -> "de"   "dut", "nld" -> "nl"   "jpn" -> "ja"   "dan" -> "da"

A track tagged ita canonicalises to ita, which never equals a stored it. Italian, Portuguese, Korean, Chinese and Russian were already offered in the picker but could not auto-select on any server exposing bibliographic codes. Shipping 27 more languages against that table would have multiplied the problem.

The alias table now covers all 37, carrying both bibliographic and terminological forms where ISO 639-2 has both (cze/ces, gre/ell, may/msa, per/fas, …), plus the pre-1989 codes iwhe and inid that older muxing tools still emit. Norwegian folds nor/nob/nno into the single offered no, since Bokmål and Nynorsk are one choice in the picker.

Dutch itself was already mapped (dut, nldnl), so the reported case needed only the picker entry — but it would have been the only one of the 27 additions that worked.

Tests

SubtitleLanguageTest (new, 9 cases) and LanguageOptionsTest (10) both pass, along with the full :shared suite; :androidApp:assembleDebug and :androidTvApp:assembleDebug both build.

The two that would catch a regression here:

  • every picker tag is already canonical — an option that canonicalises to something else can never equal a matching track
  • every offered language is reachable from its 639-2 form — if a language is in the picker, some bibliographic spelling of it must canonicalise back to the offered tag, or a server exposing that spelling can never match the choice

Plus the reported case directly: Dutch matches across nl, nld, dut, NL, nl-NL, nl_BE.

Parity note

silo-apple offers 12 — these ten plus Arabic and Hindi — and also cannot select Dutch. It needs the same treatment; this PR does not touch it.


Follow-ups landed on this branch

The alias table now mirrors the server instead of paraphrasing it

The first version of the map was hand-rolled and had already diverged on its third entry. The server's canonical_language_code (migration 121, mirroring internal/lang) keeps nobnb and nnonn distinct; the hand-rolled version folded both into no. The catalog stores what the server canonicalised, so that is not cosmetic — a Bokmål file is nb server-side and would have been compared against a client-side no, matching nothing.

It is now a faithful copy of all 90 server mappings. That covers far more languages than the pickers offer, which costs nothing and means an unlisted language synced from another surface still compares correctly.

Kept deliberately beyond the server's table: iwhe and inid, the pre-1989 639-1 spellings Java's Locale still produces. These are 639-1 rather than 639-2, so the server's map does not carry them and passes them through unchanged — worth adding server-side, flagged in a comment.

Language filter facets show names, not raw codes

CatalogFilterState and TvLibraryBrowseFacets mapped each vocabulary entry to itself, so library filters rendered eng, nld, dut instead of English and Dutch. New LanguageOptions.displayLanguage canonicalises before labelling, so all three Dutch spellings read "Dutch". A code no picker offers is returned unchanged rather than blanked — display and wire value stay the same string, and the filter still applies.

The viewer's own languages come first

Thirty-seven entries is a long way to travel on a D-pad. The obvious shortcut — device locale — was measured and rejected: the Shield here ships persist.sys.locale empty, system_locales null, and ro.product.locale at the factory en-US, so locale-first would suggest English to a Dutch viewer.

The catalog is a real signal. Settings fetches catalog/filters?include_technical=true and lifts the languages the library actually carries to the top of all six language pickers (audio, subtitle, metadata × phone, TV). The current value is pinned above them so it stays reachable when the fetch fails. The tail is alphabetical — the web's prominence order stops meaning anything past the first ten.

The fetch is best-effort and silent on failure; the pickers work fine without it.

Not done: visual group headers ("In your library" / "All languages"). Both pickers render flat lists, so headers need a non-selectable row type on two platforms — worth its own change.

Summary by CodeRabbit

  • New Features

    • Language pickers now prioritize the current selection and languages found in your library.
    • Language codes are displayed using recognizable language names, including support for regional and script-specific codes.
    • Audio, subtitle, metadata, and catalog language filters now use consistent language labels.
    • Expanded language coverage and improved handling of alternate language codes.
  • Bug Fixes

    • Preserved language codes that are not available in the picker instead of hiding them.
    • Prevented duplicate language options and ensured selections remain reversible.

Reported from the field: on the store build a user could not set Dutch as
their default subtitle language. It was never a server limitation — the
settings contract types playback.subtitle_language as `language_tag`,
validated for BCP 47 shape only, with no enum. The picker was simply
shorter than the web's.

LanguageOptions.tags now mirrors the web client's ISO 639-1 table
(silo-server web/src/player/utils/languageNames.ts) in its order: 10
languages become 37, Dutch among them. The table's own comment already
described "nl" as a tag that arrives "synced from another surface" and must
be preserved — so a Dutch preference set on the web was being honoured by
playback while the picker refused to display or offer it.

Completing the picker exposed a second, older bug. Auto-selection compares
preference and track language with both sides through
canonicalSubtitleLanguage, whose ISO 639-2 alias table covered seven
languages while the picker offered ten. A track tagged `ita` never matched a
stored `it`, so Italian, Portuguese, Korean, Chinese and Russian were already
silently failing to auto-select on any server exposing bibliographic codes.
The table now covers all 37 offered languages, both the bibliographic and
terminological forms where they differ, plus the pre-1989 `iw`/`in` codes.

Tests: every picker tag is canonical, every alias resolves to an offered
language, and Dutch matches across nl/nld/dut/NL/nl-NL/nl_BE.

Note for parity: silo-apple offers 12 (these ten plus Arabic and Hindi) and
also cannot select Dutch, so it needs the same treatment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@RXWatcher, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 99eaa24d-bb73-499e-92aa-3ebd55a4a5fb

📥 Commits

Reviewing files that changed from the base of the PR and between 7b403be and d8776e1.

📒 Files selected for processing (1)
  • shared/src/commonTest/kotlin/org/siloserver/silo/playback/SubtitleLanguageTest.kt
📝 Walkthrough

Walkthrough

The shared language model now provides expanded picker mappings, canonical display labels, prioritized options, and broader subtitle-code normalization. Android and TV settings load catalog languages and use them to order language pickers and render facet labels, with updated shared tests.

Changes

Language handling and settings integration

Layer / File(s) Summary
Language mappings and canonicalization
shared/src/commonMain/kotlin/org/siloserver/silo/model/settings/LanguageOptions.kt, shared/src/commonMain/kotlin/org/siloserver/silo/playback/SubtitleLanguage.kt
The picker table, prioritized option generation, display-name resolution, and centralized ISO alias handling are expanded.
Language behavior coverage
shared/src/commonTest/kotlin/org/siloserver/silo/model/settings/LanguageOptionsTest.kt, shared/src/commonTest/kotlin/org/siloserver/silo/playback/SubtitleLanguageTest.kt
Tests cover table completeness, canonical labels, ordering and reversibility, legacy and regional codes, unknown values, and three-letter reachability.
Catalog-backed settings pickers
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/..., androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/..., shared/src/commonMain/kotlin/org/siloserver/silo/catalog/filter/CatalogFilterState.kt
Android and TV view models fetch catalog audio and subtitle languages, while settings pickers and catalog facets use prioritized options and canonical display labels.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: quick104

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: expanding settings language pickers to match the web client's language table and canonicalization.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
shared/src/commonMain/kotlin/org/siloserver/silo/playback/SubtitleLanguage.kt (1)

52-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename the private property to camelCase.

THREE_LETTER_ALIASES is a val, not a constant, so rename it to threeLetterAliases and update its reference on Line 32.

As per coding guidelines, Kotlin properties use camelCase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@shared/src/commonMain/kotlin/org/siloserver/silo/playback/SubtitleLanguage.kt`
at line 52, Rename the private property THREE_LETTER_ALIASES to
threeLetterAliases in SubtitleLanguage.kt, and update the reference at the
language lookup on line 32 to use the new camelCase name.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@shared/src/commonTest/kotlin/org/siloserver/silo/model/settings/LanguageOptionsTest.kt`:
- Around line 58-67: Strengthen the parity tests at
shared/src/commonTest/kotlin/org/siloserver/silo/model/settings/LanguageOptionsTest.kt:58-67
and
shared/src/commonTest/kotlin/org/siloserver/silo/playback/SubtitleLanguageTest.kt:100-114.
In thePickerOffersEveryLanguageTheWebClientDoes, assert the complete ordered
37-entry LanguageOptions.tags table rather than only the Dutch mapping and size;
in the alias test, assert each alias resolves to its expected canonical picker
tag through the existing subtitle-language mapping symbols.

---

Nitpick comments:
In
`@shared/src/commonMain/kotlin/org/siloserver/silo/playback/SubtitleLanguage.kt`:
- Line 52: Rename the private property THREE_LETTER_ALIASES to
threeLetterAliases in SubtitleLanguage.kt, and update the reference at the
language lookup on line 32 to use the new camelCase name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b9256183-3032-4725-b9d6-fe01bf933dd2

📥 Commits

Reviewing files that changed from the base of the PR and between 83e23da and a024b78.

📒 Files selected for processing (4)
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/settings/LanguageOptions.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/playback/SubtitleLanguage.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/settings/LanguageOptionsTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/playback/SubtitleLanguageTest.kt

RXWatcher and others added 3 commits July 30, 2026 21:21
…a subset

The hand-rolled alias table had already diverged from the server on its
third entry. The server's canonical_language_code (migration 121, mirroring
internal/lang) keeps `nob` -> `nb` and `nno` -> `nn` distinct; this folded
both into `no`. The catalog stores what the server canonicalised, so that
divergence is not cosmetic: a Bokmal file is `nb` server-side and would have
been compared against a client-side `no` and never matched.

Replaced with a faithful copy of all 90 server mappings. It covers far more
languages than the pickers offer, which costs nothing and means an unlisted
language synced from another surface still compares correctly.

Kept beyond the server's map: `iw` -> `he` and `in` -> `id`, the pre-1989
639-1 spellings Java's Locale still produces. These are 639-1 rather than
639-2 so the server's table does not carry them and passes them through
unchanged — flagged in a comment as worth adding server-side.

Tests updated: Norwegian now asserts the distinction rather than the folding,
and the unknown-tag case uses a tag the server also does not know (`tlh`),
since `cat` legitimately resolves to `ca` now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The audio, subtitle and original-language facets mapped each vocabulary
entry to itself, so the filter list rendered the catalog's raw codes: a
viewer picking a subtitle language saw "eng", "nld", "dut" rather than
English and Dutch. Both surfaces did it — the shared CatalogFilterState and
the TV browse facets.

New LanguageOptions.displayLanguage canonicalises before labelling, because
catalog values are whatever ingest wrote: "nl", "nld" and "dut" all read
"Dutch". A code no picker offers is returned unchanged rather than blanked,
so display and wire value stay the same string and the filter still applies.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thirty-seven entries is a long way to travel on a D-pad. The obvious
shortcut is the device locale, and it is worthless on a TV: the Shield here
ships persist.sys.locale empty, system_locales null, and ro.product.locale
at the factory en-US, so locale-first would suggest English to a Dutch
viewer. Measured, not assumed.

The catalog is a real signal instead. Settings now fetches
catalog/filters?include_technical=true and lifts the languages the library
actually carries to the top of all six language pickers (audio, subtitle and
metadata, on both phone and TV). The currently-set language is pinned above
them, so the choice in force stays reachable even when the fetch fails or
the library has none of it yet. Everything else follows alphabetically: the
web's prominence order stops meaning anything past the first ten, and with
no signal, predictable beats editorial.

The fetch is best-effort and silent on failure — the pickers are fully
usable without it, and a settings screen opened to do something else should
not surface an error about a list that still works.

Both apps' picker lists were file-scope vals; they are now computed from
state, remembered on the catalog languages and the current value so
recomposition does not rebuild them per frame.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RXWatcher and others added 2 commits July 30, 2026 22:25
The parity test asserted `size == 37` plus the Dutch mapping, so a wrong
label or a silent reordering would have passed — while "mirrors the web's
ISO 639-1 table in its order" is the entire claim of this change.

The table is now asserted whole. If the web list moves, this fails and
someone decides deliberately rather than the two drifting apart unnoticed.

Declined the companion suggestion to rename THREE_LETTER_ALIASES to
camelCase: it is a deeply immutable mapOf of string literals, which is what
the Kotlin style guide calls a constant, and REGISTERED_ATTRIBUTES in
DiagnosticsValidation.kt is the same shape under the same spelling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`THREE_LETTER_FORMS[tag]?.any { ... } ?: true` defaulted any offered tag with
no entry to "reachable", so adding a language to the picker and forgetting
its 639-2 spellings — exactly the drift this test exists to catch — passed
silently. A test that cannot fail is not a guard.

Now: every offered tag must have a spelling listed, every listed spelling
must canonicalise back to it, and no spelling may linger for a language the
picker no longer offers. Failures name the specific tag and mapping rather
than reporting a count mismatch.

Verified by deleting the Dutch mapping and watching it fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@RXWatcher

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt`:
- Around line 908-912: Normalize each stored language tag before using it as the
picker selection ID: update the audio-language picker at
androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt
lines 908-912, the subtitle-language picker at lines 1121-1128, and the
metadata-language picker at lines 1130-1136. Apply the same canonicalization
used by LanguageOptions.optionsPrioritising so aliases match the option IDs and
preserve correct selection and initial focus.

In
`@shared/src/commonTest/kotlin/org/siloserver/silo/playback/SubtitleLanguageTest.kt`:
- Around line 114-142: Update
everyOfferedLanguageIsReachableFromItsThreeLetterForm to require an explicit
THREE_LETTER_FORMS entry for each LanguageOptions tag; remove the ?: true
fallback so a missing mapping fails the test rather than being treated as
reachable. Keep the existing canonicalSubtitleLanguage matching behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a153faf5-648c-4b89-9af0-1983d0db0638

📥 Commits

Reviewing files that changed from the base of the PR and between a024b78 and 7b403be.

📒 Files selected for processing (14)
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/di/AndroidModule.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/PlaybackSettings.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/SettingsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/SettingsViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/SubtitleSettings.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/di/AndroidTvModule.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibraryBrowseFacets.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsViewModel.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/catalog/filter/CatalogFilterState.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/settings/LanguageOptions.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/playback/SubtitleLanguage.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/settings/LanguageOptionsTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/playback/SubtitleLanguageTest.kt

Comment on lines +908 to +912
options = LanguageOptions.optionsPrioritising(
unsetLabel = "Default",
libraryLanguages = state.libraryLanguages,
current = state.audioLanguage,
).map { PickerOption(it.first, it.second) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Normalize TV picker selection IDs with the option IDs. LanguageOptions.optionsPrioritising turns aliases into canonical IDs, while each sheet compares its options against an unnormalized stored tag. Legacy aliases are consequently neither selected nor initially focused.

  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt#L908-L912: canonicalize state.audioLanguage before passing it as selectedId.
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt#L1121-L1128: canonicalize state.subtitleLanguage before passing it as selectedId.
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt#L1130-L1136: canonicalize state.metadataLanguage before passing it as selectedId.
📍 Affects 1 file
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt#L908-L912 (this comment)
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt#L1121-L1128
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt#L1130-L1136
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt`
around lines 908 - 912, Normalize each stored language tag before using it as
the picker selection ID: update the audio-language picker at
androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt
lines 908-912, the subtitle-language picker at lines 1121-1128, and the
metadata-language picker at lines 1130-1136. Apply the same canonicalization
used by LanguageOptions.optionsPrioritising so aliases match the option IDs and
preserve correct selection and initial focus.

@RXWatcher

Copy link
Copy Markdown
Contributor Author

Closing as superseded by the contract-driven language catalog architecture merged in #154. The remaining useful behavior—human-readable language labels in library facets—will be extracted as a focused current-main change rather than merging the conflicting hand-maintained catalog. The branch remains available for traceability.

@RXWatcher RXWatcher closed this Aug 2, 2026
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