diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt
index 6a2e1584d..07954478b 100644
--- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt
+++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt
@@ -29,7 +29,7 @@ actual object ThemeSettingsStorage {
fun initialize(context: Context) {
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
- applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code)
+ applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.DEVICE.code)
}
actual fun loadSelectedTheme(): String? =
@@ -84,9 +84,13 @@ actual object ThemeSettingsStorage {
}
actual fun applySelectedAppLanguage(languageCode: String) {
- AppCompatDelegate.setApplicationLocales(
- LocaleListCompat.forLanguageTags(languageCode),
- )
+ if (languageCode.equals("device", ignoreCase = true)) {
+ AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
+ } else {
+ AppCompatDelegate.setApplicationLocales(
+ LocaleListCompat.forLanguageTags(languageCode),
+ )
+ }
}
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
@@ -103,6 +107,6 @@ actual object ThemeSettingsStorage {
payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme)
payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled)
payload.decodeSyncBoolean(liquidGlassNativeTabBarEnabledKey)?.let(::saveLiquidGlassNativeTabBarEnabled)
- applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code)
+ applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.DEVICE.code)
}
}
diff --git a/composeApp/src/commonMain/composeResources/values-pl/strings.xml b/composeApp/src/commonMain/composeResources/values-pl/strings.xml
index f179fcf56..e8091e03d 100644
--- a/composeApp/src/commonMain/composeResources/values-pl/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-pl/strings.xml
@@ -1459,4 +1459,415 @@
Logo dodatku
Pokazuj logo i nazwę dodatku obok źródeł.
Wyświetlanie
+ Język oryginalny
+ Wymaga włączonego wzbogacania TMDB
+ Język urządzenia
+ W manifeście brakuje \"%1$s\"
+ Biblioteka w chmurze jest wyłączona.
+ Biblioteka w chmurze nie jest dostępna dla %1$s.
+ Filmy
+ Seriale
+ Rozpoznana lista Trakt
+ Kolekcja TMDB %1$s
+ Australia
+ Kanada
+ Niemcy
+ US, KR, JP, IN
+ 2020-01-01
+ 2024-12-31
+ Reżyser TMDB %1$s
+ TMDB Odkrywaj
+ TMDB Odkrywaj
+ 28,12
+ 18,35
+ Wprowadź prawidłowy ID lub URL TMDB.
+ Wprowadź prawidłowy ID lub URL TMDB.
+ en, ko, ja, hi
+ Lista TMDB %1$s
+ Nie udało się załadować źródła TMDB
+ Sieć TMDB %1$s
+ Osoba TMDB %1$s
+ Produkcja TMDB %1$s
+ 10
+ 7.0
+ Nie udało się załadować źródła TMDB
+ 100
+ 2024
+ Wprowadź ID lub URL listy Trakt
+ Wprowadź nazwę, URL lub ID listy Trakt
+ Lista Trakt %1$d
+ Wprowadź ID lub URL listy Trakt
+ Wprowadź nazwę, URL lub ID listy Trakt
+ ID %1$s
+ Lista Trakt %1$s
+ Nie udało się załadować listy Trakt
+ Nie udało się załadować listy Trakt
+ Nie znaleziono list Trakt
+ Rozpoznana lista Trakt
+ US, KR, JP, IN
+ Lista filmów Trakt
+ Lista seriali Trakt
+ Dodaj klucz API TMDB w Ustawieniach, aby korzystać ze źródeł TMDB.
+ Nie znaleziono kolekcji TMDB
+ Nie znaleziono firmy TMDB
+ TMDB Odkrywaj nie zwrócił danych
+ Nie znaleziono listy TMDB
+ Brak ID kolekcji TMDB
+ Brak ID listy TMDB
+ Brak ID osoby TMDB
+ Nie znaleziono sieci TMDB
+ Nie znaleziono filmografii osoby TMDB
+ Nie znaleziono osoby TMDB
+ Brak danych uwierzytelniających Trakt.
+ %1$s (%2$d)
+ Wprowadź prawidłowy ID lub URL listy Trakt
+ %1$d elementów
+ %1$d polubień
+ Lista Trakt nie znaleziona lub niepubliczna
+ Brak ID listy Trakt
+ Lista Trakt nie zawiera numerycznego ID
+ Publiczna lista Trakt
+ Osiągnięto limit zapytań Trakt
+ Zapytanie Trakt nie powiodło się
+ Nie znaleziono linii napisów
+ Nie udało się załadować linii napisów
+ Zaawansowane
+ Źródła
+ Uruchamianie i zachowanie profilu.
+ ZAAWANSOWANE
+ Wyświetlanie wyników źródeł i reguły badge URL.
+ Napędzane przez TMDB
+ Napędzane przez Trakt
+ Więcej akcji
+ Nie udało się załadować komentarzy Trakt (%1$d)
+ %1$dh %2$dm
+ %1$dh
+ %1$dm
+ Nie udało się sfinalizować pobieranego pliku
+ Nie udało się sfinalizować pobieranego pliku
+ Nie udało się otworzyć częściowego pliku
+ Nie udało się otworzyć częściowego pliku
+ Częściowy plik nie jest otwarty
+ Częściowy plik nie jest otwarty
+ Nie udało się zapisać częściowego pliku
+ Nie udało się zapisać częściowego pliku
+ Systemowy odtwarzacz Android
+ Pozostało %1$dh %2$dm
+ Pozostało %1$dm
+ Biblioteka Nuvio
+ Problem z połączeniem
+ Pusta treść odpowiedzi
+ Pusta treść odpowiedzi
+ Zapytanie nie powiodło się z kodem HTTP %1$d
+ Sprawdź połączenie i spróbuj ponownie.
+ Zapytanie nie powiodło się z kodem HTTP %1$d
+ Ten strumień wykorzystuje technologię peer-to-peer (P2P). Włączając P2P, przyjmujesz do wiadomości i zgadzasz się, że:\n\n• Twój adres IP będzie widoczny dla innych użytkowników w sieci\n• Ponosisz wyłączną odpowiedzialność za treści, do których uzyskujesz dostęp\n• Potwierdzasz, że masz prawo do strumieniowania tych treści w swojej jurysdykcji\n• Nuvio nie hostuje, nie dystrybuuje ani nie kontroluje żadnych treści P2P\n• Nuvio nie ponosi odpowiedzialności za jakiekolwiek konsekwencje prawne wynikające z korzystania ze strumieniowania P2P\n\nKorzystasz z tej funkcji wyłącznie na własne ryzyko. P2P można wyłączyć w dowolnym momencie w Ustawieniach.
+ Anuluj
+ Włącz P2P
+ Strumieniowanie P2P
+ Nieznany błąd torrenta
+ Zgłoś intro
+ Ustawienia wideo
+ %1$s (%2$s)
+ Silnik odtwarzacza MPV niedostępny. Przebuduj aplikację.
+ Nie udało się uruchomić torrenta: %1$s
+ Silnik odtwarzacza MPV niedostępny. Przebuduj aplikację.
+ Błąd torrenta: %1$s
+ Nie można odtworzyć tego strumienia.
+ Wyłączony
+ Najbliżej starszego zachowania MPV na iOS.
+ Kompatybilność
+ Użyj własnych zaawansowanych wartości poniżej.
+ Własne
+ Najlepsze dla iPhone\'ów i iPadów z obsługą HDR.
+ Natywne EDR
+ Bardziej przewidywalne biele i czernie na wyjściu SDR.
+ Mapowanie tonalne SDR
+ %1$s buforowane · %2$s · %3$s
+ Łączenie z peerami…
+ %1$d seedów · %2$d peerów
+ Uruchamianie silnika P2P…
+ %1$d peerów · %2$d seedów · %3$d%%
+ %1$s · %2$s
+ Jasność
+ Kontrast
+ Deband
+ Zmniejsz pasmowanie kolorów kosztem wydajności.
+ Gamma
+ Wykrywanie szczytów HDR
+ Szacuj szczytową jasność HDR gdy metadane są błędne lub brakujące.
+ Interpolacja klatek
+ Płynny ruch gdy mpv może czysto zsynchronizować z wyświetlaczem.
+ Preset wyjściowy
+ Resetuj strojenie
+ Nasycenie
+ Wideo
+ Mapowanie tonalne
+ Wtyczki wyłączone
+ Wtyczki włączone
+ %1$d dostawców
+ Odświeżanie
+ %1$d repozytoriów
+ Brak klucza API TMDB
+ Klucz API TMDB ustawiony
+ Instalowanie…
+ Zainstaluj repozytorium wtyczek
+ Testowanie…
+ Testuj dostawcę
+ Usuń repozytorium wtyczek
+ Odśwież repozytorium wtyczek
+ Brak dostępnych dostawców.
+ Dodaj URL repozytorium, aby zainstalować wtyczki do odkrywania źródeł.
+ Nie zainstalowano jeszcze repozytoriów wtyczek.
+ Używaj dostawców wtyczek podczas wyszukiwania źródeł.
+ Włącz dostawców wtyczek globalnie
+ To repozytorium wtyczek jest już zainstalowane.
+ Wprowadź URL repozytorium wtyczek.
+ Wprowadź prawidłowy URL wtyczki.
+ Nie udało się zainstalować repozytorium wtyczek
+ Nie znaleziono dostawcy
+ Nie udało się odświeżyć repozytorium
+ Wtyczki nie są dostępne w tej wersji.
+ W Źródłach pokazuj jednego dostawcę na repozytorium zamiast na źródło.
+ Grupuj dostawców wtyczek wg repozytorium
+ URL manifestu wtyczki
+ Brak nazwy w manifeście.
+ Manifest nie ma dostawców.
+ Brak wersji w manifeście.
+ Brak nazwy w manifeście.
+ Manifest nie ma dostawców.
+ Brak wersji w manifeście.
+ Zainstalowano %1$s.
+ Wyłączony przez repozytorium
+ Brak opisu
+ v%1$s
+ Repozytorium wtyczek
+ To repozytorium wtyczek jest już zainstalowane.
+ Nie udało się zainstalować repozytorium wtyczek
+ Nie udało się odświeżyć repozytorium
+ Wersja %1$s
+ DODAJ REPOZYTORIUM
+ ZAINSTALOWANE REPOZYTORIA
+ PRZEGLĄD
+ DOSTAWCY
+ Błąd
+ Test dostawcy nie powiódł się
+ Wyniki testu (%1$d)
+ Dostawcy wtyczek wymagają klucza API TMDB. Ustaw go na ekranie TMDB, w przeciwnym razie dostawcy nie będą działać poprawnie.
+ Brak wyników wyszukiwania dla %1$s.
+ Backend synchronizacji
+ Wyczyść pamięć Kontynuuj oglądanie
+ Pamięć wyczyszczona
+ Usuń dane Kontynuuj oglądanie i odśwież postęp
+ Zapamiętaj ostatni profil
+ Zapamiętaj ostatnio wybrany profil przy uruchomieniu
+ PAMIĘĆ PODRĘCZNA
+ URUCHAMIANIE
+ Karta
+ Karta pozioma w stylu TV
+ Dowiedz się więcej
+ Maks. wyników
+ Ogranicz liczbę wyświetlanych wyników.
+ Limit na jakość
+ Ogranicz powtarzające się wyniki BluRay, WEB-DL, REMUX po sortowaniu.
+ Limit na rozdzielczość
+ Ogranicz powtarzające się wyniki 2160p, 1080p, 720p po sortowaniu.
+ Wprowadź jedną grupę na linię.
+ Wszystkie wyniki
+ %1$d wyników
+ Wykluczone tagi audio
+ Ukryj wybrane tagi audio.
+ Wykluczone kanały
+ Ukryj wybrane układy kanałów.
+ Wykluczone kodowania
+ Ukryj wybrane kodowania.
+ Wykluczone języki
+ Ukryj wyniki gdzie każdy język jest wykluczony.
+ Wykluczone jakości
+ Ukryj wybrane jakości.
+ Wykluczone grupy release
+ Ukryj wybrane grupy release.
+ Wykluczone rozdzielczości
+ Ukryj wybrane rozdzielczości.
+ Wykluczone tagi wizualne
+ Ukryj DV, HDR, 10bit, 3D i podobne tagi.
+ Preferowane tagi audio
+ Sortuj Atmos, TrueHD, DTS, AAC i podobne tagi.
+ Preferowane kanały
+ Sortuj preferowane układy kanałów jako pierwsze.
+ Preferowane kodowania
+ Sortuj AV1, HEVC, AVC i podobne kodowania.
+ Preferowane języki
+ Sortuj preferowane języki audio jako pierwsze.
+ Preferowane jakości
+ Sortuj wybrane jakości jako pierwsze, w domyślnej kolejności.
+ Preferowane rozdzielczości
+ Sortuj wybrane rozdzielczości jako pierwsze, w domyślnej kolejności.
+ Preferowane tagi wizualne
+ Sortuj DV, HDR, 10bit, IMAX i podobne tagi.
+ Wymagane tagi audio
+ Wymagaj Atmos, TrueHD, DTS, AAC i podobnych tagów.
+ Wymagane kanały
+ Pokazuj tylko wybrane układy kanałów.
+ Wymagane kodowania
+ Wymagaj AV1, HEVC, AVC i podobnych kodowań.
+ Wymagane języki
+ Pokazuj tylko wyniki z wybranymi językami.
+ Wymagane jakości
+ Pokazuj tylko wybrane jakości.
+ Wymagane grupy release
+ Pokazuj tylko wybrane grupy release.
+ Wymagane rozdzielczości
+ Pokazuj tylko wybrane rozdzielczości.
+ Wymagane tagi wizualne
+ Wymagaj DV, HDR, 10bit, IMAX, SDR i podobnych tagów.
+ Zarządzanie wynikami
+ Dowolne
+ %1$d wybranych
+ %1$dGB+
+ Zakres rozmiaru
+ Filtruj wyniki wg rozmiaru pliku.
+ %1$d-%2$dGB
+ Do %1$dGB
+ Najlepszy dźwięk najpierw
+ Najlepsza jakość najpierw
+ Język najpierw
+ Największe najpierw
+ Oryginalna kolejność
+ Sortuj wyniki
+ Wybierz kolejność wyników.
+ Najmniejsze najpierw
+ Format domyślny
+ Format oryginalny
+ Grupa %1$d
+ Inne badge Fusion
+ Podgląd
+ %1$d badge Fusion z tego URL
+ Brak badge Fusion w tym URL.
+ Podgląd badge Fusion
+ Brak zaimportowanych URL badge Fusion.
+ %1$d/%2$d URL, %3$d aktywnych badge Fusion
+ Aktywny
+ Nieaktywny
+ URL JSON badge Fusion
+ %1$d/%2$d URL Fusion zaimportowanych
+ %1$s, %2$d włączonych badge, %3$d grup
+ Odtwarzanie zwiastuna w tle
+ Odtwarzaj podgląd zwiastuna w tle szczegółów gdy dostępny.
+ Ukryj bufor, seedy, peery i prędkość pobierania podczas ładowania i odtwarzania
+ Ukryj statystyki torrenta
+ Zezwalaj na strumienie peer-to-peer (torrent)
+ Strumieniowanie P2P
+ Nieprawidłowy klucz API lub błąd połączenia
+ Nieprawidłowy klucz API lub błąd połączenia
+ Wyjście audio
+ Użyj starszego wyjścia AudioUnit.
+ Najpierw AVFoundation, potem AudioUnit jako zapasowe.
+ Eksperymentalna obsługa Spatial Audio i wielokanałowego wyjścia.
+ Wyjście audio
+ Wyjście audio iOS
+ Wskazówka koloru wyświetlacza
+ Pozwól mpv celować w aktywną przestrzeń kolorów wyświetlacza.
+ Rozszerzony zakres dynamiczny
+ Domyślny tryb wyjścia Metal dla nowych sesji odtwarzania.
+ Dekoder sprzętowy
+ Dekoder sprzętowy
+ Docelowe kolory podstawowe
+ Docelowe kolory podstawowe
+ Docelowy transfer
+ Docelowy transfer
+ Wyjście wideo iOS
+ Gesty dotykowe
+ Pozwól na przesunięcia i podwójne dotknięcia na powierzchni odtwarzacza do przewijania, regulacji jasności lub głośności.
+ Wprowadź URL JSON badge.
+ Import badge nie powiódł się.
+ Możesz zaimportować do %1$d URL badge.
+ Na dole
+ Wybierz czy badge Fusion i rozmiaru pojawiają się nad czy pod kartami źródeł.
+ Wybierz gdzie badge źródeł pojawiają się na kartach.
+ Pozycja badge
+ Pozycja badge
+ Na górze
+ Styl Fusion
+ URL badge musi zaczynać się od http:// lub https://.
+ Importuj do %1$d URL JSON badge Fusion. Każdy URL można aktualizować lub usuwać osobno.
+ Zarządzaj zaimportowanymi URL JSON badge Fusion.
+ URL badge Fusion
+ Pokazuj badge rozmiaru pliku w wynikach źródeł i panelu źródeł odtwarzacza.
+ Badge rozmiaru
+ Repozytorium wtyczek
+ Zgłoś intro
+ Wyślij
+ Przechwyć
+ Przechwyć
+ CZAS KOŃCA (MM:SS)
+ CZAS KOŃCA (MM:SS)
+ Intro
+ Outro
+ Podsumowanie
+ TYP SEGMENTU
+ TYP SEGMENTU
+ CZAS STARTU (MM:SS)
+ CZAS STARTU (MM:SS)
+ Wyślij
+ Zgłoś znaczniki czasu
+ Dodaj klucz API TMDB w Ustawieniach, aby korzystać ze źródeł TMDB.
+ Kolekcja TMDB %1$d
+ Nie znaleziono kolekcji TMDB
+ Produkcja TMDB %1$d
+ Nie znaleziono firmy TMDB
+ Reżyser TMDB %1$d
+ TMDB Odkrywaj nie zwrócił danych
+ TMDB Odkrywaj
+ Wprowadź prawidłowy ID lub URL TMDB.
+ Lista TMDB %1$d
+ Nie znaleziono listy TMDB
+ Nie udało się załadować źródła TMDB
+ Brak ID kolekcji TMDB
+ Brak ID listy TMDB
+ Brak ID osoby TMDB
+ Sieć TMDB %1$d
+ Nie znaleziono sieci TMDB
+ Nie znaleziono filmografii osoby TMDB
+ Osoba TMDB %1$d
+ Nie znaleziono osoby TMDB
+ Połączono z Trakt
+ Połączono z Trakt
+ Rozłączono z Trakt
+ Rozłączono z Trakt
+ Nie udało się dodać do listy Trakt
+ Nie udało się dodać do listy obserwowanych Trakt
+ Autoryzacja Trakt wygasła
+ Pusta treść odpowiedzi
+ Osiągnięto limit list Trakt
+ Nie znaleziono listy Trakt
+ Brak kompatybilnych ID Trakt
+ Osiągnięto limit zapytań Trakt
+ Zapytanie Trakt nie powiodło się
+ Wybierz źródło rekomendacji na stronach szczegółów.
+ Źródło Podobnych
+ Wybierz skąd pochodzą rekomendacje na stronach szczegółów
+ Źródło Podobnych
+ TMDB
+ Trakt
+ Publiczna lista Trakt
+ Wprowadź prawidłowy ID lub URL listy Trakt
+ %1$d elementów
+ %1$d polubień
+ Brak danych uwierzytelniających Trakt w local.properties (TRAKT_CLIENT_ID).
+ Brak ID listy Trakt
+ Lista Trakt nie zawiera numerycznego ID
+ Lista Trakt nie znaleziona lub niepubliczna
+ Osiągnięto limit zapytań Trakt
+ Zapytanie Trakt nie powiodło się
+ TB
+ Brak pliku APK w wydaniu
+ Pobrany plik aktualizacji nie istnieje.
+ Pusta treść pobierania
+ Pobieranie nie powiodło się z kodem HTTP %1$d
+ Pobrany plik aktualizacji nie istnieje.
+ Pusta treść pobierania
+ Błąd API GitHub releases: %1$d
+ Nie opublikowano jeszcze żadnej aktualizacji.
+ Wydanie nie ma tagu ani nazwy
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index eddd4964a..58cf290ae 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -536,6 +536,7 @@
AMOLED Black
Use pure black backgrounds for OLED screens.
App Language
+ Device language
Choose Language
Settings for the Continue Watching section.
Liquid Glass
@@ -864,6 +865,8 @@
Not set
Default (media file)
Device language
+ Original language
+ Requires TMDB enrichment to be enabled
Forced
None
All subtitles
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
index a431641b9..42fb5aafa 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
@@ -2004,6 +2004,7 @@ private fun MainAppContent(
parentMetaType = launch.parentMetaType ?: launch.type,
initialPositionMs = launch.resumePositionMs ?: 0L,
initialProgressFraction = launch.resumeProgressFraction,
+ contentLanguage = cached.contentLanguage,
)
if (playerSettings.externalPlayerEnabled) {
openExternalPlayback(playerLaunch)
@@ -2430,6 +2431,7 @@ private fun MainAppContent(
torrentTrackers = launch.torrentTrackers,
initialPositionMs = launch.initialPositionMs,
initialProgressFraction = launch.initialProgressFraction,
+ contentLanguage = launch.contentLanguage,
onBack = {
ResumePromptRepository.markPlayerExitedNormally()
PlayerLaunchStore.remove(route.launchId)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerLanguagePreferences.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerLanguagePreferences.kt
index 6f5c2348d..8f9e28bfc 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerLanguagePreferences.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerLanguagePreferences.kt
@@ -84,6 +84,7 @@ import nuvio.composeapp.generated.resources.settings_playback_option_default
import nuvio.composeapp.generated.resources.settings_playback_option_device_language
import nuvio.composeapp.generated.resources.settings_playback_option_forced
import nuvio.composeapp.generated.resources.settings_playback_option_none
+import nuvio.composeapp.generated.resources.settings_playback_option_original
import nuvio.composeapp.generated.resources.subtitle_language_unknown
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.getString
@@ -97,6 +98,7 @@ data class LanguagePreferenceOption(
object AudioLanguageOption {
const val DEFAULT = "default"
const val DEVICE = "device"
+ const val ORIGINAL = "original"
}
object SubtitleLanguageOption {
@@ -463,6 +465,8 @@ fun languageLabelForCode(code: String?): String = when {
code.equals(AudioLanguageOption.DEVICE, ignoreCase = true) ||
code.equals(SubtitleLanguageOption.DEVICE, ignoreCase = true) ->
stringResource(Res.string.settings_playback_option_device_language)
+ code.equals(AudioLanguageOption.ORIGINAL, ignoreCase = true) ->
+ stringResource(Res.string.settings_playback_option_original)
else -> languageLabelResForCode(code)?.let { stringResource(it) }
?: stringResource(Res.string.subtitle_language_unknown)
}
@@ -477,6 +481,8 @@ suspend fun getLanguageLabelForCode(code: String?): String = when {
code.equals(AudioLanguageOption.DEVICE, ignoreCase = true) ||
code.equals(SubtitleLanguageOption.DEVICE, ignoreCase = true) ->
getString(Res.string.settings_playback_option_device_language)
+ code.equals(AudioLanguageOption.ORIGINAL, ignoreCase = true) ->
+ getString(Res.string.settings_playback_option_original)
else -> languageLabelResForCode(code)?.let { getString(it) }
?: getString(Res.string.subtitle_language_unknown)
}
@@ -485,6 +491,7 @@ fun resolvePreferredAudioLanguageTargets(
preferredAudioLanguage: String,
secondaryPreferredAudioLanguage: String?,
deviceLanguages: List,
+ contentOriginalLanguage: String? = null,
): List {
fun normalize(language: String?): String? {
val normalized = normalizeLanguageCode(language)
@@ -495,6 +502,7 @@ fun resolvePreferredAudioLanguageTargets(
SubtitleLanguageOption.NONE,
SubtitleLanguageOption.FORCED,
-> null
+ AudioLanguageOption.ORIGINAL -> contentOriginalLanguage?.trim()?.lowercase()?.takeIf { it.isNotBlank() }
else -> normalized
}
}
@@ -511,6 +519,21 @@ fun resolvePreferredAudioLanguageTargets(
+ listOfNotNull(normalize(secondaryPreferredAudioLanguage))
).distinct()
+ AudioLanguageOption.ORIGINAL -> {
+ val originalLang = contentOriginalLanguage?.trim()?.lowercase()?.takeIf { it.isNotBlank() }
+ if (originalLang != null) {
+ listOfNotNull(
+ originalLang,
+ normalize(secondaryPreferredAudioLanguage),
+ ).distinct()
+ } else {
+ // Fallback to device languages when original language is unknown
+ (deviceLanguages.mapNotNull(::normalize)
+ + listOfNotNull(normalize(secondaryPreferredAudioLanguage))
+ ).distinct()
+ }
+ }
+
else -> listOfNotNull(
normalize(preferredAudioLanguage),
normalize(secondaryPreferredAudioLanguage),
@@ -575,3 +598,51 @@ fun inferForcedSubtitleTrack(
if ("forced" in text) return true
return text.contains("songs") && text.contains("sign")
}
+
+/**
+ * Best-effort mapping from country name/code to ISO 639-1 primary language.
+ * Used as a fallback when [resolveContentLanguage] has no explicit language field.
+ */
+fun countryToLanguageCode(country: String?): String? {
+ val normalized = country?.trim()?.lowercase()?.takeIf { it.isNotBlank() } ?: return null
+ return COUNTRY_TO_LANGUAGE_MAP[normalized]
+}
+
+/**
+ * Resolves the original content language as an ISO 639-1 code.
+ * Falls back to country-based inference when the explicit language field is absent.
+ */
+fun resolveContentLanguage(language: String?, country: String?): String? {
+ normalizeLanguageCode(language)?.let { return it }
+ countryToLanguageCode(country)?.let { return it }
+ return null
+}
+
+private val COUNTRY_TO_LANGUAGE_MAP = mapOf(
+ // ISO 3166-1 alpha-2
+ "jp" to "ja", "kr" to "ko", "cn" to "zh", "tw" to "zh",
+ "fr" to "fr", "de" to "de", "it" to "it", "es" to "es",
+ "pt" to "pt", "br" to "pt", "ru" to "ru", "in" to "hi",
+ "tr" to "tr", "pl" to "pl", "nl" to "nl", "se" to "sv",
+ "no" to "no", "dk" to "da", "fi" to "fi", "th" to "th",
+ "il" to "he", "cz" to "cs", "ro" to "ro", "hu" to "hu",
+ "ua" to "uk", "gr" to "el",
+ // ISO 3166-1 alpha-3
+ "jpn" to "ja", "kor" to "ko", "chn" to "zh", "twn" to "zh",
+ "fra" to "fr", "deu" to "de", "ita" to "it", "esp" to "es",
+ "prt" to "pt", "bra" to "pt", "rus" to "ru", "ind" to "hi",
+ "tur" to "tr", "pol" to "pl", "nld" to "nl", "swe" to "sv",
+ "nor" to "no", "dnk" to "da", "fin" to "fi", "tha" to "th",
+ "isr" to "he", "cze" to "cs", "rou" to "ro", "hun" to "hu",
+ "ukr" to "uk", "grc" to "el",
+ // Common full names
+ "japan" to "ja", "south korea" to "ko", "korea" to "ko",
+ "china" to "zh", "taiwan" to "zh", "france" to "fr",
+ "germany" to "de", "italy" to "it", "spain" to "es",
+ "portugal" to "pt", "brazil" to "pt", "russia" to "ru",
+ "india" to "hi", "turkey" to "tr", "poland" to "pl",
+ "netherlands" to "nl", "sweden" to "sv", "norway" to "no",
+ "denmark" to "da", "finland" to "fi", "thailand" to "th",
+ "israel" to "he", "romania" to "ro", "hungary" to "hu",
+ "ukraine" to "uk", "greece" to "el",
+)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt
index e5c4d81c1..2d52d6c61 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt
@@ -50,6 +50,7 @@ data class PlayerLaunch(
val torrentTrackers: List = emptyList(),
val initialPositionMs: Long = 0L,
val initialProgressFraction: Float? = null,
+ val contentLanguage: String? = null,
)
object PlayerLaunchStore {
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt
index a4cce8c28..d3882286c 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt
@@ -38,6 +38,7 @@ fun PlayerScreen(
torrentTrackers: List = emptyList(),
initialPositionMs: Long = 0L,
initialProgressFraction: Float? = null,
+ contentLanguage: String? = null,
) {
PlayerScreenContent(
PlayerScreenArgs(
@@ -74,6 +75,7 @@ fun PlayerScreen(
torrentTrackers = torrentTrackers,
initialPositionMs = initialPositionMs,
initialProgressFraction = initialProgressFraction,
+ contentLanguage = contentLanguage,
)
)
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenArgs.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenArgs.kt
index 6f5c229da..d5e549015 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenArgs.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenArgs.kt
@@ -36,4 +36,5 @@ internal data class PlayerScreenArgs(
val torrentTrackers: List,
val initialPositionMs: Long,
val initialProgressFraction: Float?,
+ val contentLanguage: String? = null,
)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeSourceActions.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeSourceActions.kt
index ade131f57..43095cdc8 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeSourceActions.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeSourceActions.kt
@@ -482,5 +482,9 @@ private fun PlayerScreenRuntime.saveDirectStreamForReuse(
videoSize = stream.behaviorHints.videoSize,
bingeGroup = stream.behaviorHints.bingeGroup,
streamType = stream.streamType,
+ contentLanguage = resolveContentLanguage(
+ language = metaUiState.meta?.language,
+ country = metaUiState.meta?.country,
+ ),
)
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeTrackActions.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeTrackActions.kt
index 118baba9c..098c258f5 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeTrackActions.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeTrackActions.kt
@@ -152,6 +152,10 @@ internal fun PlayerScreenRuntime.refreshTracks() {
preferredAudioLanguage = playerSettingsUiState.preferredAudioLanguage,
secondaryPreferredAudioLanguage = playerSettingsUiState.secondaryPreferredAudioLanguage,
deviceLanguages = DeviceLanguagePreferences.preferredLanguageCodes(),
+ contentOriginalLanguage = resolveContentLanguage(
+ language = metaUiState.meta?.language,
+ country = metaUiState.meta?.country,
+ ) ?: args.contentLanguage,
)
if (preferredAudioTargets.isEmpty()) {
preferredAudioSelectionApplied = true
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt
index 4cdb8924a..85bcf497a 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt
@@ -13,12 +13,14 @@ import nuvio.composeapp.generated.resources.lang_portuguese_portugal
import nuvio.composeapp.generated.resources.lang_spanish
import nuvio.composeapp.generated.resources.lang_turkish
import nuvio.composeapp.generated.resources.lang_norwegian
+import nuvio.composeapp.generated.resources.settings_appearance_app_language_device
import org.jetbrains.compose.resources.StringResource
enum class AppLanguage(
val code: String,
val labelRes: StringResource,
) {
+ DEVICE("device", Res.string.settings_appearance_app_language_device),
CZECH("cs", Res.string.lang_czech),
ENGLISH("en", Res.string.lang_english),
FRENCH("fr", Res.string.lang_french),
@@ -35,6 +37,6 @@ enum class AppLanguage(
companion object {
fun fromCode(code: String?): AppLanguage =
- entries.firstOrNull { it.code.equals(code, ignoreCase = true) } ?: ENGLISH
+ entries.firstOrNull { it.code.equals(code, ignoreCase = true) } ?: DEVICE
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
index 54718c44f..6dc6f3093 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
@@ -404,6 +404,7 @@ private fun PlaybackSettingsSection(
description = when (preferredAudioLanguage) {
AudioLanguageOption.DEFAULT -> stringResource(Res.string.settings_playback_option_default)
AudioLanguageOption.DEVICE -> stringResource(Res.string.settings_playback_option_device_language)
+ AudioLanguageOption.ORIGINAL -> stringResource(Res.string.settings_playback_option_original)
else -> languageLabelForCode(preferredAudioLanguage)
},
enabled = audioLanguageEnabled,
@@ -1090,11 +1091,13 @@ private fun PlaybackSettingsSection(
}
if (showPreferredAudioDialog) {
+ val originalHint = stringResource(Res.string.settings_playback_option_original_hint)
LanguageSelectionDialog(
title = stringResource(Res.string.settings_playback_preferred_audio_language),
options = listOf(
LanguageSelectionOption(AudioLanguageOption.DEFAULT, stringResource(Res.string.settings_playback_option_default)),
LanguageSelectionOption(AudioLanguageOption.DEVICE, stringResource(Res.string.settings_playback_option_device_language)),
+ LanguageSelectionOption(AudioLanguageOption.ORIGINAL, stringResource(Res.string.settings_playback_option_original), description = originalHint),
) + AvailableLanguageOptions.map { option ->
LanguageSelectionOption(option.code, stringResource(option.labelRes))
},
@@ -1108,10 +1111,12 @@ private fun PlaybackSettingsSection(
}
if (showSecondaryAudioDialog) {
+ val originalHint = stringResource(Res.string.settings_playback_option_original_hint)
LanguageSelectionDialog(
title = stringResource(Res.string.settings_playback_secondary_audio_language),
options = listOf(
LanguageSelectionOption(null, stringResource(Res.string.settings_playback_option_none)),
+ LanguageSelectionOption(AudioLanguageOption.ORIGINAL, stringResource(Res.string.settings_playback_option_original), description = originalHint),
) + AvailableLanguageOptions.map { option ->
LanguageSelectionOption(option.code, stringResource(option.labelRes))
},
@@ -1441,6 +1446,7 @@ private fun formatReuseCacheDuration(hours: Int): String = when {
private data class LanguageSelectionOption(
val value: String?,
val label: String,
+ val description: String? = null,
)
@Composable
@@ -1720,12 +1726,20 @@ private fun LanguageSelectionDialog(
.padding(horizontal = 14.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
- Text(
- text = option.label,
- style = MaterialTheme.typography.bodyLarge,
- color = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier.weight(1f),
- )
+ Column(modifier = Modifier.weight(1f)) {
+ Text(
+ text = option.label,
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ if (!option.description.isNullOrBlank()) {
+ Text(
+ text = option.description,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ }
Box(
modifier = Modifier.size(24.dp),
contentAlignment = Alignment.Center,
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt
index 431b9d20e..217faa940 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt
@@ -17,7 +17,7 @@ object ThemeSettingsRepository {
private val _liquidGlassNativeTabBarEnabled = MutableStateFlow(false)
val liquidGlassNativeTabBarEnabled: StateFlow = _liquidGlassNativeTabBarEnabled.asStateFlow()
- private val _selectedAppLanguage = MutableStateFlow(AppLanguage.ENGLISH)
+ private val _selectedAppLanguage = MutableStateFlow(AppLanguage.DEVICE)
val selectedAppLanguage: StateFlow = _selectedAppLanguage.asStateFlow()
private var hasLoaded = false
@@ -38,7 +38,7 @@ object ThemeSettingsRepository {
_liquidGlassNativeTabBarEnabled.value = false
NativeTabBridge.publishAccentColor(AppTheme.WHITE.nativeTabAccentHex())
NativeTabBridge.publishLiquidGlassEnabled(false)
- _selectedAppLanguage.value = AppLanguage.ENGLISH
+ _selectedAppLanguage.value = AppLanguage.DEVICE
}
private fun loadFromDisk() {
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamLinkCacheRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamLinkCacheRepository.kt
index e3a7e8efc..d1b830227 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamLinkCacheRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamLinkCacheRepository.kt
@@ -19,6 +19,7 @@ data class CachedStreamLink(
val sources: List = emptyList(),
val bingeGroup: String? = null,
val streamType: String? = null,
+ val contentLanguage: String? = null,
)
internal expect fun epochMs(): Long
@@ -56,6 +57,7 @@ object StreamLinkCacheRepository {
sources: List = emptyList(),
bingeGroup: String? = null,
streamType: String? = null,
+ contentLanguage: String? = null,
) {
if (url.isNotBlank() && url.hasLikelyExpiringPlaybackCredentials()) {
remove(contentKey)
@@ -77,6 +79,7 @@ object StreamLinkCacheRepository {
sources = sources,
bingeGroup = bingeGroup,
streamType = streamType,
+ contentLanguage = contentLanguage,
)
val payload = json.encodeToString(CachedStreamLink.serializer(), entry)
StreamLinkCacheStorage.saveEntry(hashedKey(contentKey), payload)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt
index a25b4a493..bdf8f0ece 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt
@@ -85,6 +85,9 @@ import androidx.compose.material3.rememberModalBottomSheetState
import coil3.compose.AsyncImage
import com.nuvio.app.core.ui.nuvioSafeBottomPadding
import com.nuvio.app.features.debrid.DebridSettingsRepository
+import com.nuvio.app.features.debrid.DirectDebridPlayableResult
+import com.nuvio.app.features.debrid.DirectDebridPlaybackResolver
+import com.nuvio.app.features.debrid.toastMessage
import com.nuvio.app.features.player.PlayerSettingsRepository
import com.nuvio.app.features.watchprogress.WatchProgressRepository
import kotlinx.coroutines.launch
@@ -145,6 +148,7 @@ fun StreamsScreen(
val streamLinkCopiedText = stringResource(Res.string.streams_link_copied)
val noDirectStreamLinkText = stringResource(Res.string.streams_no_direct_link)
var streamActionsTarget by remember(videoId) { mutableStateOf(null) }
+ val downloadScope = rememberCoroutineScope()
var preferredFilterApplied by remember(videoId) { mutableStateOf(false) }
var autoPlayOverlayLogoLoadError by remember(logo) { mutableStateOf(false) }
val autoPlayOverlayLogoUrl = logo?.takeIf { it.isNotBlank() }
@@ -356,27 +360,88 @@ fun StreamsScreen(
if (!directUrl.isNullOrBlank()) {
clipboardManager.setText(AnnotatedString(directUrl))
NuvioToastController.show(streamLinkCopiedText)
+ } else if (DirectDebridPlaybackResolver.shouldResolveToPlayableStream(stream)) {
+ downloadScope.launch {
+ val resolved = DirectDebridPlaybackResolver.resolveToPlayableStream(
+ stream = stream,
+ season = seasonNumber,
+ episode = episodeNumber,
+ )
+ when (resolved) {
+ is DirectDebridPlayableResult.Success -> {
+ val resolvedUrl = resolved.stream.playableDirectUrl
+ if (!resolvedUrl.isNullOrBlank()) {
+ clipboardManager.setText(AnnotatedString(resolvedUrl))
+ NuvioToastController.show(streamLinkCopiedText)
+ } else {
+ NuvioToastController.show(noDirectStreamLinkText)
+ }
+ }
+ else -> {
+ val message = resolved.toastMessage()
+ if (message != null) {
+ NuvioToastController.show(message)
+ }
+ }
+ }
+ }
} else {
NuvioToastController.show(noDirectStreamLinkText)
}
},
onDownload = { stream ->
- val result = DownloadsRepository.enqueueFromStream(
- contentType = type,
- videoId = videoId,
- parentMetaId = parentMetaId,
- parentMetaType = parentMetaType,
- title = title,
- logo = logo,
- poster = poster,
- background = background,
- seasonNumber = seasonNumber,
- episodeNumber = episodeNumber,
- episodeTitle = episodeTitle,
- episodeThumbnail = episodeThumbnail,
- stream = stream,
- )
- NuvioToastController.show(result.toastMessage())
+ if (DirectDebridPlaybackResolver.shouldResolveToPlayableStream(stream)) {
+ downloadScope.launch {
+ val resolved = DirectDebridPlaybackResolver.resolveToPlayableStream(
+ stream = stream,
+ season = seasonNumber,
+ episode = episodeNumber,
+ )
+ when (resolved) {
+ is DirectDebridPlayableResult.Success -> {
+ val result = DownloadsRepository.enqueueFromStream(
+ contentType = type,
+ videoId = videoId,
+ parentMetaId = parentMetaId,
+ parentMetaType = parentMetaType,
+ title = title,
+ logo = logo,
+ poster = poster,
+ background = background,
+ seasonNumber = seasonNumber,
+ episodeNumber = episodeNumber,
+ episodeTitle = episodeTitle,
+ episodeThumbnail = episodeThumbnail,
+ stream = resolved.stream,
+ )
+ NuvioToastController.show(result.toastMessage())
+ }
+ else -> {
+ val message = resolved.toastMessage()
+ if (message != null) {
+ NuvioToastController.show(message)
+ }
+ }
+ }
+ }
+ } else {
+ val result = DownloadsRepository.enqueueFromStream(
+ contentType = type,
+ videoId = videoId,
+ parentMetaId = parentMetaId,
+ parentMetaType = parentMetaType,
+ title = title,
+ logo = logo,
+ poster = poster,
+ background = background,
+ seasonNumber = seasonNumber,
+ episodeNumber = episodeNumber,
+ episodeTitle = episodeTitle,
+ episodeThumbnail = episodeThumbnail,
+ stream = stream,
+ )
+ NuvioToastController.show(result.toastMessage())
+ }
},
onOpen = { stream, openExternally ->
onStreamActionOpen(
@@ -922,7 +987,7 @@ private fun LazyListScope.streamSection(
}
},
onLongClick = {
- if (stream.playableDirectUrl != null) {
+ if (stream.playableDirectUrl != null || stream.isAddonDebridCandidate) {
onStreamLongPress(stream)
}
},
diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt
index bf14a4528..f36f0a921 100644
--- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt
+++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt
@@ -72,6 +72,11 @@ actual object ThemeSettingsStorage {
}
actual fun applySelectedAppLanguage(languageCode: String) {
+ if (languageCode.equals("device", ignoreCase = true)) {
+ NSUserDefaults.standardUserDefaults.removeObjectForKey("AppleLanguages")
+ NSUserDefaults.standardUserDefaults.synchronize()
+ return
+ }
val normalizedCode = languageCode
.trim()
.takeIf { it.isNotBlank() }
@@ -97,6 +102,6 @@ actual object ThemeSettingsStorage {
payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme)
payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled)
payload.decodeSyncBoolean(liquidGlassNativeTabBarEnabledKey)?.let(::saveLiquidGlassNativeTabBarEnabled)
- applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code)
+ applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.DEVICE.code)
}
}