aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbaka0815 <git@baka0815.de>2025-04-08 05:29:12 +0200
committerGitHub <noreply@github.com>2025-04-07 21:29:12 -0600
commit5fc1b1c862f30f81010c3c87dbb753c6f0e4dcfb (patch)
treef18d4bebb06502e13b84e8aa096ad6f8dd9fff49
parent77ad7f6139e4911168e2199fe48e78bf7fdddbf1 (diff)
Translate the ISO-639-2/B codes to ISO-639-2/T. (#13068)
* Translate the ISO-639-2/B codes to ISO-639-2/T. This enables 19 additional languages to be displayed correctly. * Convert the 2-dimensional array to a dictionary * Added the French language to the list of ISO-639-2/B codes * Don't change the property, use a local variable instead. * When creating the MediaStream in the MediaStreamRepository ensure that the ISO 639-2/T (f.e. deu) code is used for the language as that is the one the .NET culture info knows. The other code is most likely the ISO 639-2/B code (f.e. ger) which is unknown to the .NET culture info and will result in just displaying the code instead of the display name. * Move the substitution of ISO 639-2/B to /T to the localization manager. Some language (like Chinese) have multiple entries in the iso6392.txt file (f.e. zho|chi|zh|..., zho|chi|zh-tw|...) but the conversation between /T and /B is the same so use .TryAdd. * Change the method definition from GetISO6392TFromB to TryGetISO6392TFromB and return true if a case was found. * Add unit tests for TryGetISO6392TFromB.
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs31
-rw-r--r--Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs13
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs5
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs9
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs25
5 files changed, 80 insertions, 3 deletions
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 9598f9e6c..17db7ad4c 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Concurrent;
+using System.Collections.Frozen;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -38,6 +40,8 @@ namespace Emby.Server.Implementations.Localization
private List<CultureDto> _cultures = [];
+ private FrozenDictionary<string, string> _iso6392BtoT = null!;
+
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
/// </summary>
@@ -100,6 +104,7 @@ namespace Emby.Server.Implementations.Localization
private async Task LoadCultures()
{
List<CultureDto> list = [];
+ Dictionary<string, string> iso6392BtoTdict = new Dictionary<string, string>();
using var stream = _assembly.GetManifestResourceStream(CulturesPath);
if (stream is null)
@@ -142,12 +147,17 @@ namespace Emby.Server.Implementations.Localization
else
{
threeLetterNames = [parts[0], parts[1]];
+
+ // In cases where there are two TLN the first one is ISO 639-2/T and the second one is ISO 639-2/B
+ // We need ISO 639-2/T for the .NET cultures so we cultivate a dictionary for the translation B->T
+ iso6392BtoTdict.TryAdd(parts[1], parts[0]);
}
list.Add(new CultureDto(name, name, twoCharName, threeLetterNames));
}
_cultures = list;
+ _iso6392BtoT = iso6392BtoTdict.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
}
}
@@ -505,5 +515,26 @@ namespace Emby.Server.Implementations.Localization
yield return new LocalizationOption("漢語 (繁體字)", "zh-TW");
yield return new LocalizationOption("廣東話 (香港)", "zh-HK");
}
+
+ /// <inheritdoc />
+ public bool TryGetISO6392TFromB(string isoB, [NotNullWhen(true)] out string? isoT)
+ {
+ // Unlikely case the dictionary is not (yet) initialized properly
+ if (_iso6392BtoT == null)
+ {
+ isoT = null;
+ return false;
+ }
+
+ var result = _iso6392BtoT.TryGetValue(isoB, out isoT) && !string.IsNullOrEmpty(isoT);
+
+ // Ensure the ISO code being null if the result is false
+ if (!result)
+ {
+ isoT = null;
+ }
+
+ return result;
+ }
}
}
diff --git a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
index 1be31db72..7eb13b740 100644
--- a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
@@ -100,7 +100,18 @@ public class MediaStreamRepository : IMediaStreamRepository
dto.IsAVC = entity.IsAvc;
dto.Codec = entity.Codec;
- dto.Language = entity.Language;
+
+ var language = entity.Language;
+
+ // Check if the language has multiple three letter ISO codes
+ // if yes choose the first as that is the ISO 639-2/T code we're needing
+ if (language != null && _localization.TryGetISO6392TFromB(language, out string? isoT))
+ {
+ language = isoT;
+ }
+
+ dto.Language = language;
+
dto.ChannelLayout = entity.ChannelLayout;
dto.Profile = entity.Profile;
dto.AspectRatio = entity.AspectRatio;
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 95b5b43f8..5c8f37fcd 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -2,6 +2,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
@@ -272,7 +273,7 @@ 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. Will not work for some languages which use ISO 639-2/B instead of /T codes.
+ // Get full language string i.e. eng -> English.
string fullLanguage = CultureInfo
.GetCultures(CultureTypes.NeutralCultures)
.FirstOrDefault(r => r.ThreeLetterISOLanguageName.Equals(Language, StringComparison.OrdinalIgnoreCase))
@@ -375,7 +376,7 @@ namespace MediaBrowser.Model.Entities
if (!string.IsNullOrEmpty(Language))
{
- // Get full language string i.e. eng -> English. Will not work for some languages which use ISO 639-2/B instead of /T codes.
+ // Get full language string i.e. eng -> English.
string fullLanguage = CultureInfo
.GetCultures(CultureTypes.NeutralCultures)
.FirstOrDefault(r => r.ThreeLetterISOLanguageName.Equals(Language, StringComparison.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index d9df95325..f6e65028e 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Globalization;
@@ -61,4 +62,12 @@ public interface ILocalizationManager
/// <param name="language">The language.</param>
/// <returns>The correct <see cref="CultureDto" /> for the given language.</returns>
CultureDto? FindLanguageInfo(string language);
+
+ /// <summary>
+ /// Returns the language in ISO 639-2/T when the input is ISO 639-2/B.
+ /// </summary>
+ /// <param name="isoB">The language in ISO 639-2/B.</param>
+ /// <param name="isoT">The language in ISO 639-2/T.</param>
+ /// <returns>Whether the language could be converted.</returns>
+ public bool TryGetISO6392TFromB(string isoB, [NotNullWhen(true)] out string? isoT);
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 026da4992..a7a1e5e81 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using BitFaster.Caching;
using Emby.Server.Implementations.Localization;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration;
@@ -51,6 +52,30 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
Assert.Contains("ger", germany.ThreeLetterISOLanguageNames);
}
+ [Fact]
+ public async Task TryGetISO6392TFromB_Success()
+ {
+ var localizationManager = Setup(new ServerConfiguration
+ {
+ UICulture = "de-DE"
+ });
+ await localizationManager.LoadAll();
+
+ string? isoT;
+
+ // Translation ger -> deu
+ Assert.True(localizationManager.TryGetISO6392TFromB("ger", out isoT));
+ Assert.Equal("deu", isoT);
+
+ // chi -> zho
+ Assert.True(localizationManager.TryGetISO6392TFromB("chi", out isoT));
+ Assert.Equal("zho", isoT);
+
+ // eng is already ISO 639-2/T
+ Assert.False(localizationManager.TryGetISO6392TFromB("eng", out isoT));
+ Assert.Null(isoT);
+ }
+
[Theory]
[InlineData("de")]
[InlineData("deu")]