fix(web): derive settings language options from canonical language list (#363)

The profile and device settings language dropdowns (preferred subtitle
language, spoken language, metadata language, device audio language, and
per-library audio/subtitle overrides) were built from two hardcoded
12-language lists, so Dutch and 24 other languages supported by the
canonical list in languageNames.ts could not be selected and stored codes
like "nl" rendered as raw codes instead of names.

Derive both settings option lists from the shared LANGUAGES array and
route getLanguageLabel through getLanguageName, so every supported
language is selectable everywhere and stored values always render a
proper display name (e.g. "nl" -> "Dutch"). Options are now alphabetized
by label, matching the other language dropdowns that already use
LANGUAGES; the "No preference" sentinel stays first.

Co-authored-by: rxwatcher <rxwatcher@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
RXWatcher
2026-07-16 10:37:00 -04:00
committed by GitHub
co-authored by rxwatcher Claude Opus 4.8
parent c36d70dcba
commit 035ef457a4
2 changed files with 17 additions and 27 deletions
+7 -12
View File
@@ -1,3 +1,5 @@
import { LANGUAGES } from "@/player/utils/languageNames";
export type SettingScope = "user" | "device";
export type SettingControl = "switch" | "select" | "slider" | "json";
@@ -21,20 +23,13 @@ export interface SettingDefinition {
summary?: (value: string | null | undefined) => string;
}
/**
* Language choices for settings dropdowns, derived from the canonical
* language list so every supported language stays selectable.
*/
export const LANGUAGE_OPTIONS: SettingOption[] = [
{ value: "", label: "No preference" },
{ value: "en", label: "English" },
{ value: "es", label: "Spanish" },
{ value: "fr", label: "French" },
{ value: "de", label: "German" },
{ value: "it", label: "Italian" },
{ value: "pt", label: "Portuguese" },
{ value: "ja", label: "Japanese" },
{ value: "ko", label: "Korean" },
{ value: "zh", label: "Chinese" },
{ value: "ru", label: "Russian" },
{ value: "ar", label: "Arabic" },
{ value: "hi", label: "Hindi" },
...LANGUAGES.map(({ code, label }) => ({ value: code, label })),
];
const definitions: SettingDefinition[] = [
@@ -1,5 +1,6 @@
import type { LibraryPlaybackPreference } from "@/api/types";
import type { UpdateLibraryPlaybackPreferenceRequest } from "@/hooks/queries/libraryPlaybackPreferences";
import { getLanguageName, LANGUAGES } from "@/player/utils/languageNames";
export const INHERIT_VALUE = "inherit";
export const NONE_VALUE = "none";
@@ -8,20 +9,11 @@ export const ORIGINAL_LANGUAGE_LABEL = "Original Language";
export const DEFAULT_SUBTITLE_MODE = "auto";
export const DEFAULT_SHOW_FORCED_SUBTITLES = true;
export const LANGUAGE_OPTIONS = [
{ code: "en", label: "English" },
{ code: "es", label: "Spanish" },
{ code: "fr", label: "French" },
{ code: "de", label: "German" },
{ code: "it", label: "Italian" },
{ code: "pt", label: "Portuguese" },
{ code: "ja", label: "Japanese" },
{ code: "ko", label: "Korean" },
{ code: "zh", label: "Chinese" },
{ code: "ru", label: "Russian" },
{ code: "ar", label: "Arabic" },
{ code: "hi", label: "Hindi" },
] as const;
/**
* Language choices for per-library playback overrides, re-exported from the
* canonical language list so every supported language stays selectable.
*/
export const LANGUAGE_OPTIONS = LANGUAGES;
export const SUBTITLE_MODE_OPTIONS = [
{ value: "auto", label: "Auto" },
@@ -40,8 +32,11 @@ export function getLanguageLabel(code: string) {
if (code === ORIGINAL_LANGUAGE_VALUE) {
return ORIGINAL_LANGUAGE_LABEL;
}
if (!code) {
return code;
}
return LANGUAGE_OPTIONS.find((language) => language.code === code)?.label ?? code;
return getLanguageName(code);
}
export function getSubtitleModeLabel(mode: string) {