aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBond-009 <bond.009@outlook.com>2026-01-18 12:19:31 +0100
committerGitHub <noreply@github.com>2026-01-18 12:19:31 +0100
commit226da3b3718ce3a1ad8774e25b7855c2ae4fb546 (patch)
tree539af6f40bdef088f9fe7c4921a62a1d09eae286
parent8d052a6cb17421e6c7774bafc6957029f3c9d4ae (diff)
parent90390772869495228793e1b29db20c8741a309de (diff)
Fix zh-CN subtitle language display (#15947)
The DisplayTitle property was using .NET's CultureInfo.GetCultures(NeutralCultures) to resolve language display names. Since zh-CN is a specific culture (not neutral), it would fall back to the base 'zh' code, resulting in generic 'Chinese' instead of 'Chinese (Simplified)'. This change adds a LocalizedLanguage property to MediaStream that gets populated via LocalizationManager.FindLanguageInfo() when streams are retrieved from the database. This leverages Jellyfin's existing iso6392.txt mappings which correctly map zh-CN to 'Chinese (Simplified)'. The same pattern is already used for other localized strings like LocalizedDefault and LocalizedExternal.
-rw-r--r--Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs6
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs52
-rw-r--r--tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs43
3 files changed, 55 insertions, 46 deletions
diff --git a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
index 7eb13b740..64874ccad 100644
--- a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
@@ -158,6 +158,12 @@ public class MediaStreamRepository : IMediaStreamRepository
dto.LocalizedDefault = _localization.GetLocalizedString("Default");
dto.LocalizedExternal = _localization.GetLocalizedString("External");
+ if (!string.IsNullOrEmpty(dto.Language))
+ {
+ var culture = _localization.FindLanguageInfo(dto.Language);
+ dto.LocalizedLanguage = culture?.DisplayName;
+ }
+
if (dto.Type is MediaStreamType.Subtitle)
{
dto.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index b1626e2c9..c443af32c 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -260,6 +260,8 @@ namespace MediaBrowser.Model.Entities
public string LocalizedHearingImpaired { get; set; }
+ public string LocalizedLanguage { get; set; }
+
public string DisplayTitle
{
get
@@ -273,29 +275,8 @@ namespace MediaBrowser.Model.Entities
// Do not display the language code in display titles if unset or set to a special code. Show it in all other cases (possibly expanded).
if (!string.IsNullOrEmpty(Language) && !_specialCodes.Contains(Language, StringComparison.OrdinalIgnoreCase))
{
- // Get full language string i.e. eng -> English, zh-Hans -> Chinese (Simplified).
- var cultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
- CultureInfo match = null;
- if (Language.Contains('-', StringComparison.OrdinalIgnoreCase))
- {
- match = cultures.FirstOrDefault(r =>
- r.Name.Equals(Language, StringComparison.OrdinalIgnoreCase));
-
- if (match is null)
- {
- string baseLang = Language.AsSpan().LeftPart('-').ToString();
- match = cultures.FirstOrDefault(r =>
- r.TwoLetterISOLanguageName.Equals(baseLang, StringComparison.OrdinalIgnoreCase));
- }
- }
- else
- {
- match = cultures.FirstOrDefault(r =>
- r.ThreeLetterISOLanguageName.Equals(Language, StringComparison.OrdinalIgnoreCase));
- }
-
- string fullLanguage = match?.DisplayName;
- attributes.Add(StringHelper.FirstToUpper(fullLanguage ?? Language));
+ // Use pre-resolved localized language name, falling back to raw language code.
+ attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
}
if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase))
@@ -393,29 +374,8 @@ namespace MediaBrowser.Model.Entities
if (!string.IsNullOrEmpty(Language))
{
- // Get full language string i.e. eng -> English, zh-Hans -> Chinese (Simplified).
- var cultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
- CultureInfo match = null;
- if (Language.Contains('-', StringComparison.OrdinalIgnoreCase))
- {
- match = cultures.FirstOrDefault(r =>
- r.Name.Equals(Language, StringComparison.OrdinalIgnoreCase));
-
- if (match is null)
- {
- string baseLang = Language.AsSpan().LeftPart('-').ToString();
- match = cultures.FirstOrDefault(r =>
- r.TwoLetterISOLanguageName.Equals(baseLang, StringComparison.OrdinalIgnoreCase));
- }
- }
- else
- {
- match = cultures.FirstOrDefault(r =>
- r.ThreeLetterISOLanguageName.Equals(Language, StringComparison.OrdinalIgnoreCase));
- }
-
- string fullLanguage = match?.DisplayName;
- attributes.Add(StringHelper.FirstToUpper(fullLanguage ?? Language));
+ // Use pre-resolved localized language name, falling back to raw language code.
+ attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
}
else
{
diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
index f4c0d9fe8..c1a3a4544 100644
--- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
+++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
@@ -108,6 +108,49 @@ namespace Jellyfin.Model.Tests.Entities
IsExternal = true
});
+ // Test LocalizedLanguage is used when set (fixes zh-CN display issue #15935)
+ data.Add(
+ "Chinese (Simplified) - SRT",
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = null,
+ Language = "zh-CN",
+ LocalizedLanguage = "Chinese (Simplified)",
+ IsForced = false,
+ IsDefault = false,
+ Codec = "SRT"
+ });
+
+ // Test LocalizedLanguage for audio streams
+ data.Add(
+ "Japanese - AAC - Stereo",
+ new MediaStream
+ {
+ Type = MediaStreamType.Audio,
+ Title = null,
+ Language = "jpn",
+ LocalizedLanguage = "Japanese",
+ IsForced = false,
+ IsDefault = false,
+ Codec = "AAC",
+ ChannelLayout = "stereo"
+ });
+
+ // Test fallback to Language when LocalizedLanguage is null
+ data.Add(
+ "Eng - ASS",
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = null,
+ Language = "eng",
+ LocalizedLanguage = null,
+ IsForced = false,
+ IsDefault = false,
+ Codec = "ASS"
+ });
+
return data;
}