From 553f38a2377cf843404cd4d3b3602e8a72bc75f8 Mon Sep 17 00:00:00 2001 From: Lasath Fernando Date: Sat, 4 Apr 2026 16:10:07 +0000 Subject: [PATCH 1/2] Fix language display for ISO 639-2-only codes (e.g. mul, und) LoadCultures() in LocalizationManager skipped all iso6392.txt entries without a two-letter ISO 639-1 code, dropping 302 of 496 languages including mul (Multiple languages), und (Undetermined), mis (Uncoded languages), zxx, and many real languages like Achinese, Akkadian, etc. This caused FindLanguageInfo() to return null for these codes, which meant: - ExternalPathParser could not recognize them as valid language codes in subtitle filenames, so the Language field was never set - DisplayTitle fell back to the raw code string (e.g. "Mul") Fix by allowing entries without two-letter codes to be loaded with an empty TwoLetterISOLanguageName. Also set LocalizedLanguage in ProbeResultNormalizer for ffprobe-detected streams (the DB repository path was already handled on master). --- .../Localization/LocalizationManager.cs | 2 +- .../Probing/ProbeResultNormalizer.cs | 6 ++++++ MediaBrowser.Model/Entities/MediaStream.cs | 1 - .../Localization/LocalizationManagerTests.cs | 21 ++++++++++++++++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index bc80c2b405..6fca5bc1ba 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Localization string twoCharName = parts[2]; if (string.IsNullOrWhiteSpace(twoCharName)) { - continue; + twoCharName = string.Empty; } else if (twoCharName.Contains('-', StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index d3e7b52315..203e72de36 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -729,6 +729,9 @@ namespace MediaBrowser.MediaEncoding.Probing stream.Type = MediaStreamType.Audio; stream.LocalizedDefault = _localization.GetLocalizedString("Default"); stream.LocalizedExternal = _localization.GetLocalizedString("External"); + stream.LocalizedLanguage = !string.IsNullOrEmpty(stream.Language) + ? _localization.FindLanguageInfo(stream.Language)?.DisplayName + : null; stream.Channels = streamInfo.Channels; @@ -767,6 +770,9 @@ 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) + ? _localization.FindLanguageInfo(stream.Language)?.DisplayName + : null; if (string.IsNullOrEmpty(stream.Title)) { diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 11f81ff7d8..4491fb5ace 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; -using System.Linq; using System.Text; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index e60522bf78..700ac5dced 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -41,7 +41,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var cultures = localizationManager.GetCultures().ToList(); - Assert.Equal(194, cultures.Count); + Assert.Equal(496, cultures.Count); var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName.Equals("de", StringComparison.Ordinal)); Assert.NotNull(germany); @@ -99,6 +99,25 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Contains("ger", germany.ThreeLetterISOLanguageNames); } + [Theory] + [InlineData("mul", "Multiple languages")] + [InlineData("und", "Undetermined")] + [InlineData("mis", "Uncoded languages")] + [InlineData("zxx", "No linguistic content; Not applicable")] + public async Task FindLanguageInfo_ISO6392Only_Success(string code, string expectedDisplayName) + { + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "en-US" + }); + await localizationManager.LoadAll(); + + var culture = localizationManager.FindLanguageInfo(code); + Assert.NotNull(culture); + Assert.Equal(expectedDisplayName, culture.DisplayName); + Assert.Equal(code, culture.ThreeLetterISOLanguageName); + } + [Fact] public async Task GetParentalRatings_Default_Success() { From c300651d0ddae2e2a3b3cde55989284f5972a79a Mon Sep 17 00:00:00 2001 From: Lasath Fernando Date: Thu, 9 Apr 2026 13:53:38 -0500 Subject: [PATCH 2/2] Simplify null-check ternary style in ProbeResultNormalizer Co-authored-by: Bond-009 --- .../Probing/ProbeResultNormalizer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 203e72de36..3c6a03713f 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -729,9 +729,9 @@ namespace MediaBrowser.MediaEncoding.Probing stream.Type = MediaStreamType.Audio; stream.LocalizedDefault = _localization.GetLocalizedString("Default"); stream.LocalizedExternal = _localization.GetLocalizedString("External"); - stream.LocalizedLanguage = !string.IsNullOrEmpty(stream.Language) - ? _localization.FindLanguageInfo(stream.Language)?.DisplayName - : null; + stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language) + ? null + : _localization.FindLanguageInfo(stream.Language)?.DisplayName; stream.Channels = streamInfo.Channels; @@ -770,9 +770,9 @@ 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) - ? _localization.FindLanguageInfo(stream.Language)?.DisplayName - : null; + stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language) + ? null + : _localization.FindLanguageInfo(stream.Language)?.DisplayName; if (string.IsNullOrEmpty(stream.Title)) {