From 7aea626d4439f9de3f6d2877b7d1d2b332e5bd2f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 23 May 2026 18:17:39 -0600 Subject: [PATCH 1/3] Add KMP locale API for SubtitleHelper --- .../utils/SubtitleHelperPlatform.android.kt | 24 +++++++++++++++++ .../cloudstream3/utils/SubtitleHelper.kt | 26 +++---------------- .../utils/SubtitleHelperPlatform.kt | 13 ++++++++++ .../utils/SubtitleHelperPlatform.js.kt | 18 +++++++++++++ .../utils/SubtitleHelperPlatform.jvm.kt | 21 +++++++++++++++ 5 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt create mode 100644 library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt create mode 100644 library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt diff --git a/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt new file mode 100644 index 00000000000..1a87b512db9 --- /dev/null +++ b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt @@ -0,0 +1,24 @@ +package com.lagradost.cloudstream3.utils + +import java.util.Locale + +// TODO: add androidMain actual using LocaleListCompat.getAdjustedDefault() to respect +// per-app language preferences set via AppCompatDelegate.setApplicationLocales() + +actual fun getCurrentLocale(): String = + Locale.getDefault().toLanguageTag() + +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? { + val localeOfLangCode = Locale.forLanguageTag(ietfTag) + val localeOfLocalizeTo = Locale.forLanguageTag(localizedTo) + val displayName = localeOfLangCode.getDisplayName(localeOfLocalizeTo) + + // Locale.getDisplayName() falls back to the raw tag or "language (country)" form + // when it doesn't know how to render the name. + val langCodeWithCountry = "${localeOfLangCode.language} (" + val failed = + displayName.equals(ietfTag, ignoreCase = true) || + displayName.contains(langCodeWithCountry, ignoreCase = true) + + return if (failed) null else displayName +} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt index 7becf4d19c0..b08f9996652 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt @@ -1,13 +1,6 @@ package com.lagradost.cloudstream3.utils import me.xdrop.fuzzywuzzy.FuzzySearch -import java.util.Locale - -// If you find a way to use SettingsGeneral getCurrentLocale() -// instead of this function do it. -fun getCurrentLocale(): String { - return Locale.getDefault().toLanguageTag() -} @Suppress( "unused", @@ -48,23 +41,10 @@ object SubtitleHelper { val ISO_639_3: String, // ISO 639-6 missing as it's intended to differentiate specific dialects and variants val openSubtitles: String, // inconsistent codes that do not conform ISO 639 ) { - fun localizedName(localizedTo: String? = null): String { - // Use system locale to localize language name - val localeOfLangCode = Locale.forLanguageTag(this.IETF_tag) - val localeOfLocalizeTo = Locale.forLanguageTag(localizedTo ?: getCurrentLocale()) - val sysLocalizedName = localeOfLangCode.getDisplayName(localeOfLocalizeTo) - - val langCodeWithCountry = "${localeOfLangCode.language} (" // ${localeOfLangCode.country})" - val failedToLocalize = - sysLocalizedName.equals(this.IETF_tag, ignoreCase = true) || - sysLocalizedName.contains(langCodeWithCountry, ignoreCase = true) - - return if (failedToLocalize) + fun localizedName(localizedTo: String? = null): String = + localizedLanguageName(this.IETF_tag, localizedTo ?: getCurrentLocale()) // fallback to native language name - this.nativeName - else - sysLocalizedName - } + ?: this.nativeName fun nameNextToFlagEmoji(localizedTo: String? = null): String { // fallback to [A][A] -> [?] question mak flag diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt new file mode 100644 index 00000000000..38a254da8c9 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt @@ -0,0 +1,13 @@ +package com.lagradost.cloudstream3.utils + +/** + * Returns the current locale as an IETF BCP 47 language tag. + */ +expect fun getCurrentLocale(): String + +/** + * Returns the display name of [ietfTag] localized into [localizedTo]. + * Returns null if the platform couldn't produce a meaningful name + * (i.e. it just echoed back the tag or contained a bare language code with parentheses). + */ +expect fun localizedLanguageName(ietfTag: String, localizedTo: String): String? \ No newline at end of file diff --git a/library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt b/library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt new file mode 100644 index 00000000000..da7544d0b1d --- /dev/null +++ b/library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt @@ -0,0 +1,18 @@ +package com.lagradost.cloudstream3.utils + +actual fun getCurrentLocale(): String = + js("navigator.language") as? String ?: "en" + +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? = + try { + val dn = IntlDisplayNames(arrayOf(localizedTo), js("({ type: 'language' })")) + val name = dn.of(ietfTag) + if (name.isNullOrBlank() || name.equals(ietfTag, ignoreCase = true)) null else name + } catch (e: Throwable) { + null + } + +@JsName("Intl.DisplayNames") +external class IntlDisplayNames(locales: Array, options: dynamic) { + fun of(code: String): String? +} \ No newline at end of file diff --git a/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt b/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt new file mode 100644 index 00000000000..a43358ccb65 --- /dev/null +++ b/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt @@ -0,0 +1,21 @@ +package com.lagradost.cloudstream3.utils + +import java.util.Locale + +actual fun getCurrentLocale(): String = + Locale.getDefault().toLanguageTag() + +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? { + val localeOfLangCode = Locale.forLanguageTag(ietfTag) + val localeOfLocalizeTo = Locale.forLanguageTag(localizedTo) + val displayName = localeOfLangCode.getDisplayName(localeOfLocalizeTo) + + // Locale.getDisplayName() falls back to the raw tag or "language (country)" form + // when it doesn't know how to render the name. + val langCodeWithCountry = "${localeOfLangCode.language} (" + val failed = + displayName.equals(ietfTag, ignoreCase = true) || + displayName.contains(langCodeWithCountry, ignoreCase = true) + + return if (failed) null else displayName +} \ No newline at end of file From 9aee74c34feea241437460a264d5dbb55ee758f5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:25:36 -0600 Subject: [PATCH 2/3] Fix for wasm and use correct default source set for JS and wasmjs --- .../utils/SubtitleHelperPlatform.js.kt | 18 ---------- .../utils/SubtitleHelperPlatform.web.kt | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) delete mode 100644 library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt create mode 100644 library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt diff --git a/library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt b/library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt deleted file mode 100644 index da7544d0b1d..00000000000 --- a/library/src/jsMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.js.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.lagradost.cloudstream3.utils - -actual fun getCurrentLocale(): String = - js("navigator.language") as? String ?: "en" - -actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? = - try { - val dn = IntlDisplayNames(arrayOf(localizedTo), js("({ type: 'language' })")) - val name = dn.of(ietfTag) - if (name.isNullOrBlank() || name.equals(ietfTag, ignoreCase = true)) null else name - } catch (e: Throwable) { - null - } - -@JsName("Intl.DisplayNames") -external class IntlDisplayNames(locales: Array, options: dynamic) { - fun of(code: String): String? -} \ No newline at end of file diff --git a/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt new file mode 100644 index 00000000000..98f647e75a3 --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt @@ -0,0 +1,34 @@ +package com.lagradost.cloudstream3.utils + +@OptIn(ExperimentalWasmJsInterop::class) +external object Intl : JsAny + +@OptIn(ExperimentalWasmJsInterop::class) +external class IntlDisplayNames(locale: String, options: JsAny) : JsAny { + fun of(code: String): String? +} + +@OptIn(ExperimentalWasmJsInterop::class) +private fun navigatorLanguage(): String = + js("navigator.language") + +actual fun getCurrentLocale(): String = + try { navigatorLanguage() } catch (_: Throwable) { "en" } + +@OptIn(ExperimentalWasmJsInterop::class) +private fun buildOptions(): JsAny = + js("({ type: 'language' })") + +@OptIn(ExperimentalWasmJsInterop::class) +private fun makeDisplayNames(locale: String, options: JsAny): IntlDisplayNames = + js("new Intl.DisplayNames([locale], options)") + +@OptIn(ExperimentalWasmJsInterop::class) +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? = + try { + val dn = makeDisplayNames(localizedTo, buildOptions()) + val name = dn.of(ietfTag) + if (name.isNullOrBlank() || name.equals(ietfTag, ignoreCase = true)) null else name + } catch (_: Throwable) { + null + } From 3a90aa14af00aca49e97f990b75970aead06d333 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:28:03 -0600 Subject: [PATCH 3/3] Remove accidentally added --- .../lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt index 98f647e75a3..33e293f3124 100644 --- a/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt +++ b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt @@ -1,8 +1,5 @@ package com.lagradost.cloudstream3.utils -@OptIn(ExperimentalWasmJsInterop::class) -external object Intl : JsAny - @OptIn(ExperimentalWasmJsInterop::class) external class IntlDisplayNames(locale: String, options: JsAny) : JsAny { fun of(code: String): String?