From 94d53264111b2fee3824afeaa0eea69f6ecd7e83 Mon Sep 17 00:00:00 2001
From: 854562 <44002186+854562@users.noreply.github.com>
Date: Mon, 22 Jun 2026 20:25:13 +0200
Subject: Truncate ISO-639-2 language display names at first delimiter
Prevents raw ISO-639-2 values (e.g. "Greek, Modern (1453-)" from cluttering the audio and subtitle display names by truncating them at the first comma or semicolon ("Greek"). Applies to MediaStreamRepository and ProbeResultNormalizer.
---
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
(limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs')
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 06060988e2..98bdce0e9c 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -732,7 +732,9 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.LocalizedOriginal = _localization.GetLocalizedString("Original");
stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language)
? null
- : _localization.FindLanguageInfo(stream.Language)?.DisplayName;
+ : _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
+ ? name.Split(';', ',')[0].Trim()
+ : null;
stream.Channels = streamInfo.Channels;
@@ -773,7 +775,9 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired");
stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language)
? null
- : _localization.FindLanguageInfo(stream.Language)?.DisplayName;
+ : _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
+ ? name.Split(';', ',')[0].Trim()
+ : null;
if (string.IsNullOrEmpty(stream.Title))
{
--
cgit v1.2.3
From c632417dda8e3f9a2472ad0bdd43c69d38d95f62 Mon Sep 17 00:00:00 2001
From: 854562 <44002186+854562@users.noreply.github.com>
Date: Tue, 23 Jun 2026 18:32:47 +0200
Subject: Fix SonarCloud warnings
---
.../Item/MediaStreamRepository.cs | 2 +-
.../Probing/ProbeResultNormalizer.cs | 18 ++++++++++--------
2 files changed, 11 insertions(+), 9 deletions(-)
(limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs')
diff --git a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
index b3c7fe131e..17cfdffe84 100644
--- a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
@@ -175,7 +175,7 @@ public class MediaStreamRepository : IMediaStreamRepository
var culture = _localization.FindLanguageInfo(dto.Language);
// Truncate at the first delimiter to avoid cluttered display names
dto.LocalizedLanguage = culture?.DisplayName is { } name
- ? name.Split(';', ',')[0].Trim()
+ ? name.Split([';', ','])[0].Trim()
: null;
}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 98bdce0e9c..a9c37c0025 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -730,11 +730,12 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.LocalizedDefault = _localization.GetLocalizedString("Default");
stream.LocalizedExternal = _localization.GetLocalizedString("External");
stream.LocalizedOriginal = _localization.GetLocalizedString("Original");
- stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language)
- ? null
- : _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
- ? name.Split(';', ',')[0].Trim()
+ if (!string.IsNullOrEmpty(stream.Language))
+ {
+ stream.LocalizedLanguage = _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
+ ? name.Split([';', ','])[0].Trim()
: null;
+ }
stream.Channels = streamInfo.Channels;
@@ -773,11 +774,12 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.LocalizedForced = _localization.GetLocalizedString("Forced");
stream.LocalizedExternal = _localization.GetLocalizedString("External");
stream.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired");
- stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language)
- ? null
- : _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
- ? name.Split(';', ',')[0].Trim()
+ if (!string.IsNullOrEmpty(stream.Language))
+ {
+ stream.LocalizedLanguage = _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
+ ? name.Split([';', ','])[0].Trim()
: null;
+ }
if (string.IsNullOrEmpty(stream.Title))
{
--
cgit v1.2.3
From 2cd2f36fe4912cb465cf5e78a055463f8a5c44a9 Mon Sep 17 00:00:00 2001
From: 854562 <44002186+854562@users.noreply.github.com>
Date: Mon, 20 Jul 2026 22:00:10 +0200
Subject: Extract truncation logic to helper and add tests
---
.../Localization/LocalizationManager.cs | 18 ++++++++++++
.../Item/MediaStreamRepository.cs | 6 +---
.../Probing/ProbeResultNormalizer.cs | 8 ++---
.../Globalization/ILocalizationManager.cs | 8 +++++
.../Localization/LocalizationManagerTests.cs | 34 ++++++++++++++++++++++
5 files changed, 63 insertions(+), 11 deletions(-)
(limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs')
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 843e35afcc..068dde639e 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -261,6 +261,24 @@ namespace Emby.Server.Implementations.Localization
_cultures);
}
+ ///
+ public string? GetLanguageDisplayName(string language)
+ {
+ if (string.IsNullOrEmpty(language))
+ {
+ return null;
+ }
+
+ var displayName = FindLanguageInfo(language)?.DisplayName;
+ if (displayName is null)
+ {
+ return null;
+ }
+
+ // Truncate at the first delimiter to avoid cluttered display names
+ return displayName.Split([';', ','], StringSplitOptions.None)[0].Trim();
+ }
+
///
public IReadOnlyList GetCountries()
{
diff --git a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
index 17cfdffe84..a25629132b 100644
--- a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
@@ -172,11 +172,7 @@ public class MediaStreamRepository : IMediaStreamRepository
if (!string.IsNullOrEmpty(dto.Language))
{
- var culture = _localization.FindLanguageInfo(dto.Language);
- // Truncate at the first delimiter to avoid cluttered display names
- dto.LocalizedLanguage = culture?.DisplayName is { } name
- ? name.Split([';', ','])[0].Trim()
- : null;
+ dto.LocalizedLanguage = _localization.GetLanguageDisplayName(dto.Language);
}
if (dto.Type is MediaStreamType.Audio)
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index a9c37c0025..449c18a306 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -732,9 +732,7 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.LocalizedOriginal = _localization.GetLocalizedString("Original");
if (!string.IsNullOrEmpty(stream.Language))
{
- stream.LocalizedLanguage = _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
- ? name.Split([';', ','])[0].Trim()
- : null;
+ stream.LocalizedLanguage = _localization.GetLanguageDisplayName(stream.Language);
}
stream.Channels = streamInfo.Channels;
@@ -776,9 +774,7 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired");
if (!string.IsNullOrEmpty(stream.Language))
{
- stream.LocalizedLanguage = _localization.FindLanguageInfo(stream.Language)?.DisplayName is { } name
- ? name.Split([';', ','])[0].Trim()
- : null;
+ stream.LocalizedLanguage = _localization.GetLanguageDisplayName(stream.Language);
}
if (string.IsNullOrEmpty(stream.Title))
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index 7ad240abfb..0fff70c4e0 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -72,6 +72,14 @@ public interface ILocalizationManager
/// The correct for the given language.
CultureDto? FindLanguageInfo(string language);
+ ///
+ /// Gets a human-readable display name for the given language code.
+ /// Truncates at the first semicolon or comma to avoid cluttered ISO-639-2 names.
+ ///
+ /// An ISO language code.
+ /// The display name, or null if not found.
+ string? GetLanguageDisplayName(string language);
+
///
/// Returns the language in ISO 639-2/T when the input is ISO 639-2/B.
///
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 3b8fe5ca60..b608d3ea49 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -119,6 +119,40 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
Assert.Equal(code, culture.ThreeLetterISOLanguageName);
}
+ [Theory]
+ [InlineData("ell", "Greek")] // Comma truncation
+ [InlineData("nld", "Dutch")] // Semicolon truncation
+ [InlineData("ron", "Romanian")] // Semicolon truncation, multiple
+ [InlineData("eng", "English")] // No truncation
+ [InlineData("zh-CN", "Chinese (Simplified)")] // No truncation, with parentheses
+ public async Task GetLanguageDisplayName_DelimitedName_ReturnsTruncatedName(string language, string expected)
+ {
+ var localizationManager = Setup(new ServerConfiguration
+ {
+ UICulture = "en-US"
+ });
+ await localizationManager.LoadAll();
+
+ var result = localizationManager.GetLanguageDisplayName(language);
+ Assert.Equal(expected, result);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData("xyz")]
+ public async Task GetLanguageDisplayName_InvalidInput_ReturnsNull(string? language)
+ {
+ var localizationManager = Setup(new ServerConfiguration
+ {
+ UICulture = "en-US"
+ });
+ await localizationManager.LoadAll();
+
+ var result = localizationManager.GetLanguageDisplayName(language!);
+ Assert.Null(result);
+ }
+
[Fact]
public async Task GetParentalRatings_Default_Success()
{
--
cgit v1.2.3