Fix zh-CN subtitle language display

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.
This commit is contained in:
ZeusCraft10 2026-01-05 06:22:44 -05:00
parent 706a8d2850
commit 9039077286
3 changed files with 55 additions and 46 deletions

View file

@ -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");

View file

@ -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
{

View file

@ -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;
}