From 88bfd1bcf45bdfa6c64e3439d7f406799645163c Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Mon, 10 May 2021 17:58:21 +0200 Subject: [PATCH 1/8] Add tests for LocalizationManager --- .../Localization/LocalizationManagerTests.cs | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs new file mode 100644 index 0000000000..acdf74c4f7 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -0,0 +1,167 @@ +using System.Linq; +using System.Threading.Tasks; +using Emby.Server.Implementations.Localization; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Localization +{ + public class LocalizationManagerTests + { + private LocalizationManager _localizationManager = null!; + + public LocalizationManagerTests() + { + var config = new ServerConfiguration() { UICulture = "de-DE" }; + Setup(config); + } + + [Fact] + public void GetCountries_All_Success() + { + var countries = _localizationManager.GetCountries(); + var countryInfos = countries.ToList(); + + Assert.Equal(139, countryInfos.Count); + + var germany = countryInfos.FirstOrDefault(x => x.Name == "DE"); + Assert.NotNull(germany); + Assert.Equal("Germany", germany!.DisplayName); + Assert.Equal("DEU", germany!.ThreeLetterISORegionName); + Assert.Equal("DE", germany!.TwoLetterISORegionName); + } + + [Fact] + public async Task GetCultures_All_Success() + { + await _localizationManager.LoadAll(); + var cultures = _localizationManager.GetCultures().ToList(); + + Assert.Equal(189, cultures.Count); + + var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName == "de"); + Assert.NotNull(germany); + Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); + Assert.Equal("German", germany!.DisplayName); + Assert.Equal("German", germany!.Name); + Assert.Contains("deu", germany!.ThreeLetterISOLanguageNames); + Assert.Contains("ger", germany!.ThreeLetterISOLanguageNames); + } + + [Theory] + [InlineData("de")] + [InlineData("ger")] + [InlineData("german")] + public async Task FindLanguage_Valid_Success(string identifier) + { + await _localizationManager.LoadAll(); + + var germany = _localizationManager.FindLanguageInfo(identifier); + Assert.NotNull(germany); + + Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); + Assert.Equal("German", germany!.DisplayName); + Assert.Equal("German", germany!.Name); + Assert.Contains("deu", germany!.ThreeLetterISOLanguageNames); + Assert.Contains("ger", germany!.ThreeLetterISOLanguageNames); + } + + [Fact] + public async Task ParentalRatings_Default_Success() + { + await _localizationManager.LoadAll(); + var ratings = _localizationManager.GetParentalRatings().ToList(); + + Assert.Equal(23, ratings.Count); + + var tvma = ratings.FirstOrDefault(x => x.Name == "TV-MA"); + Assert.NotNull(tvma); + Assert.Equal(9, tvma!.Value); + } + + [Fact] + public async Task ParentalRatings_ConfiguredCountryCode_Success() + { + Setup(new ServerConfiguration() + { + MetadataCountryCode = "DE" + }); + await _localizationManager.LoadAll(); + var ratings = _localizationManager.GetParentalRatings().ToList(); + + Assert.Equal(10, ratings.Count); + + var fsk = ratings.FirstOrDefault(x => x.Name == "FSK-12"); + Assert.NotNull(fsk); + Assert.Equal(7, fsk!.Value); + } + + [Theory] + [InlineData("CA-R", "CA", 10)] + [InlineData("FSK-16", "DE", 8)] + [InlineData("FSK-18", "DE", 9)] + [InlineData("FSK-18", "US", 9)] + [InlineData("TV-MA", "US", 9)] + [InlineData("XXX", "asdf", 100)] + [InlineData("Germany: FSK-18", "DE", 9)] + public async Task GetRatingLevelFromString_Valid_Success(string value, string countryCode, int expectedLevel) + { + Setup(new ServerConfiguration() + { + MetadataCountryCode = countryCode + }); + await _localizationManager.LoadAll(); + var level = _localizationManager.GetRatingLevel(value); + Assert.NotNull(level); + Assert.Equal(expectedLevel, level!); + } + + [Fact] + public async Task GetRatingLevelFromString_Unrated_Success() + { + await _localizationManager.LoadAll(); + Assert.Null(_localizationManager.GetRatingLevel("n/a")); + } + + [Theory] + [InlineData("Default", "Default")] + [InlineData("HeaderLiveTV", "Live TV")] + public void GetLocalizedString_Valid_Success(string key, string expected) + { + Setup(new ServerConfiguration() + { + UICulture = "en-US" + }); + + var translated = _localizationManager.GetLocalizedString(key); + Assert.NotNull(translated); + Assert.Equal(expected, translated); + } + + [Fact] + public void GetLocalizedString_Invalid_Success() + { + Setup(new ServerConfiguration() + { + UICulture = "en-US" + }); + + var key = "SuperInvalidTranslationKeyThatWillNeverBeAdded"; + + var translated = _localizationManager.GetLocalizedString(key); + Assert.NotNull(translated); + Assert.Equal(key, translated); + } + + private void Setup(ServerConfiguration config) + { + var mockConfiguration = new Mock(); + mockConfiguration.SetupGet(x => x.Configuration).Returns(config); + + _localizationManager = new LocalizationManager(mockConfiguration.Object, new NullLogger()); + } + } +} From db2b53a4b52d0c1e9797bfc70030b04421ba46a6 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Mon, 10 May 2021 18:05:35 +0200 Subject: [PATCH 2/8] Refactor LocalizationManager and remove dead method --- .../Localization/LocalizationManager.cs | 426 +++++++++--------- .../Globalization/ILocalizationManager.cs | 8 - 2 files changed, 203 insertions(+), 231 deletions(-) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 220e423bf5..efbccaa5b9 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -25,18 +25,18 @@ namespace Emby.Server.Implementations.Localization private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; - private readonly IServerConfigurationManager _configurationManager; - private readonly ILogger _logger; - private readonly Dictionary> _allParentalRatings = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private readonly IServerConfigurationManager _configurationManager; + private readonly ConcurrentDictionary> _dictionaries = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - private List _cultures; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private readonly ILogger _logger; + + private List _cultures; /// /// Initializes a new instance of the class. @@ -51,57 +51,6 @@ namespace Emby.Server.Implementations.Localization _logger = logger; } - /// - /// Loads all resources into memory. - /// - /// . - public async Task LoadAll() - { - const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; - - // Extract from the assembly - foreach (var resource in _assembly.GetManifestResourceNames()) - { - if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) - { - continue; - } - - string countryCode = resource.Substring(RatingsResource.Length, 2); - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - - using (var str = _assembly.GetManifestResourceStream(resource)) - using (var reader = new StreamReader(str)) - { - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - string[] parts = line.Split(','); - if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - var name = parts[0]; - dict.Add(name, new ParentalRating(name, value)); - } -#if DEBUG - else - { - _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); - } -#endif - } - } - - _allParentalRatings[countryCode] = dict; - } - - await LoadCultures().ConfigureAwait(false); - } - /// /// Gets the cultures. /// @@ -109,62 +58,6 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetCultures() => _cultures; - private async Task LoadCultures() - { - List list = new List(); - - const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - - using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) - using (var reader = new StreamReader(stream)) - { - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - var parts = line.Split('|'); - - if (parts.Length == 5) - { - string name = parts[3]; - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - string twoCharName = parts[2]; - if (string.IsNullOrWhiteSpace(twoCharName)) - { - continue; - } - - string[] threeletterNames; - if (string.IsNullOrWhiteSpace(parts[1])) - { - threeletterNames = new[] { parts[0] }; - } - else - { - threeletterNames = new[] { parts[0], parts[1] }; - } - - list.Add(new CultureDto - { - DisplayName = name, - Name = name, - ThreeLetterISOLanguageNames = threeletterNames, - TwoLetterISOLanguageName = twoCharName - }); - } - } - } - - _cultures = list; - } - /// public CultureDto FindLanguageInfo(string language) => GetCultures() @@ -186,34 +79,6 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetParentalRatings() => GetParentalRatingsDictionary().Values; - /// - /// Gets the parental ratings dictionary. - /// - /// . - private Dictionary GetParentalRatingsDictionary() - { - var countryCode = _configurationManager.Configuration.MetadataCountryCode; - - if (string.IsNullOrEmpty(countryCode)) - { - countryCode = "us"; - } - - return GetRatings(countryCode) ?? GetRatings("us"); - } - - /// - /// Gets the ratings. - /// - /// The country code. - /// The ratings. - private Dictionary GetRatings(string countryCode) - { - _allParentalRatings.TryGetValue(countryCode, out var value); - - return value; - } - /// public int? GetRatingLevel(string rating) { @@ -250,7 +115,7 @@ namespace Emby.Server.Implementations.Localization var index = rating.IndexOf(':', StringComparison.Ordinal); if (index != -1) { - rating = rating.Substring(index).TrimStart(':').Trim(); + rating = rating.Substring(index + 1).Trim(); if (!string.IsNullOrWhiteSpace(rating)) { @@ -262,20 +127,6 @@ namespace Emby.Server.Implementations.Localization return null; } - /// - public bool HasUnicodeCategory(string value, UnicodeCategory category) - { - foreach (var chr in value) - { - if (char.GetUnicodeCategory(chr) == category) - { - return true; - } - } - - return false; - } - /// public string GetLocalizedString(string phrase) { @@ -305,74 +156,6 @@ namespace Emby.Server.Implementations.Localization return phrase; } - private Dictionary GetLocalizationDictionary(string culture) - { - if (string.IsNullOrEmpty(culture)) - { - throw new ArgumentNullException(nameof(culture)); - } - - const string Prefix = "Core"; - - return _dictionaries.GetOrAdd( - culture, - f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); - } - - private async Task> GetDictionary(string prefix, string culture, string baseFilename) - { - if (string.IsNullOrEmpty(culture)) - { - throw new ArgumentNullException(nameof(culture)); - } - - var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - var namespaceName = GetType().Namespace + "." + prefix; - - await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false); - await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false); - - return dictionary; - } - - private async Task CopyInto(IDictionary dictionary, string resourcePath) - { - using (var stream = _assembly.GetManifestResourceStream(resourcePath)) - { - // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain - if (stream != null) - { - var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); - - foreach (var key in dict.Keys) - { - dictionary[key] = dict[key]; - } - } - else - { - _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); - } - } - } - - private static string GetResourceFilename(string culture) - { - var parts = culture.Split('-'); - - if (parts.Length == 2) - { - culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant(); - } - else - { - culture = culture.ToLowerInvariant(); - } - - return culture + ".json"; - } - /// public IEnumerable GetLocalizationOptions() { @@ -414,5 +197,202 @@ namespace Emby.Server.Implementations.Localization yield return new LocalizationOption("Turkish", "tr"); yield return new LocalizationOption("Tiếng Việt", "vi"); } + + /// + /// Loads all resources into memory. + /// + /// . + public async Task LoadAll() + { + const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; + + // Extract from the assembly + foreach (var resource in _assembly.GetManifestResourceNames()) + { + if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) + { + continue; + } + + string countryCode = resource.Substring(RatingsResource.Length, 2); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + await using var str = _assembly.GetManifestResourceStream(resource); + using var reader = new StreamReader(str); + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + string[] parts = line.Split(','); + if (parts.Length == 2 + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + { + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); + } +#if DEBUG + else + { + _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); + } +#endif + } + + _allParentalRatings[countryCode] = dict; + } + + await LoadCultures().ConfigureAwait(false); + } + + private async Task LoadCultures() + { + List list = new List(); + + const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; + + await using var stream = _assembly.GetManifestResourceStream(ResourcePath); + using var reader = new StreamReader(stream); + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var parts = line.Split('|'); + + if (parts.Length == 5) + { + string name = parts[3]; + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + string twoCharName = parts[2]; + if (string.IsNullOrWhiteSpace(twoCharName)) + { + continue; + } + + string[] threeletterNames; + if (string.IsNullOrWhiteSpace(parts[1])) + { + threeletterNames = new[] { parts[0] }; + } + else + { + threeletterNames = new[] { parts[0], parts[1] }; + } + + list.Add(new CultureDto + { + DisplayName = name, + Name = name, + ThreeLetterISOLanguageNames = threeletterNames, + TwoLetterISOLanguageName = twoCharName + }); + } + } + + _cultures = list; + } + + /// + /// Gets the parental ratings dictionary. + /// + /// . + private Dictionary GetParentalRatingsDictionary() + { + var countryCode = _configurationManager.Configuration.MetadataCountryCode; + + if (string.IsNullOrEmpty(countryCode)) + { + countryCode = "us"; + } + + return GetRatings(countryCode) ?? GetRatings("us"); + } + + /// + /// Gets the ratings. + /// + /// The country code. + /// The ratings. + private Dictionary GetRatings(string countryCode) + { + _allParentalRatings.TryGetValue(countryCode, out var value); + + return value; + } + + private Dictionary GetLocalizationDictionary(string culture) + { + if (string.IsNullOrEmpty(culture)) + { + throw new ArgumentNullException(nameof(culture)); + } + + const string Prefix = "Core"; + + return _dictionaries.GetOrAdd( + culture, + _ => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); + } + + private async Task> GetDictionary(string prefix, string culture, string baseFilename) + { + if (string.IsNullOrEmpty(culture)) + { + throw new ArgumentNullException(nameof(culture)); + } + + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + var namespaceName = GetType().Namespace + "." + prefix; + + await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false); + await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false); + + return dictionary; + } + + private async Task CopyInto(IDictionary dictionary, string resourcePath) + { + await using var stream = _assembly.GetManifestResourceStream(resourcePath); + // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain + if (stream != null) + { + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); + + foreach (var key in dict.Keys) + { + dictionary[key] = dict[key]; + } + } + else + { + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + } + } + + private static string GetResourceFilename(string culture) + { + var parts = culture.Split('-'); + + if (parts.Length == 2) + { + culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant(); + } + else + { + culture = culture.ToLowerInvariant(); + } + + return culture + ".json"; + } } } diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index baefeb39cf..e0e7317efd 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -56,14 +56,6 @@ namespace MediaBrowser.Model.Globalization /// . IEnumerable GetLocalizationOptions(); - /// - /// Checks if the string contains a character with the specified unicode category. - /// - /// The string. - /// The unicode category. - /// Wether or not the string contains a character with the specified unicode category. - bool HasUnicodeCategory(string value, UnicodeCategory category); - /// /// Returns the correct for the given language. /// From e33e3ba61037b594fd9035550e3ca646bb7f2c8f Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 25 May 2021 12:33:55 +0200 Subject: [PATCH 3/8] Make localizationManager local instead of field --- .../Localization/LocalizationManagerTests.cs | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index acdf74c4f7..651957ae39 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -11,18 +11,14 @@ namespace Jellyfin.Server.Implementations.Tests.Localization { public class LocalizationManagerTests { - private LocalizationManager _localizationManager = null!; - - public LocalizationManagerTests() - { - var config = new ServerConfiguration() { UICulture = "de-DE" }; - Setup(config); - } - [Fact] public void GetCountries_All_Success() { - var countries = _localizationManager.GetCountries(); + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + var countries = localizationManager.GetCountries(); var countryInfos = countries.ToList(); Assert.Equal(139, countryInfos.Count); @@ -37,8 +33,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [Fact] public async Task GetCultures_All_Success() { - await _localizationManager.LoadAll(); - var cultures = _localizationManager.GetCultures().ToList(); + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + var cultures = localizationManager.GetCultures().ToList(); Assert.Equal(189, cultures.Count); @@ -57,9 +57,13 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("german")] public async Task FindLanguage_Valid_Success(string identifier) { - await _localizationManager.LoadAll(); + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); - var germany = _localizationManager.FindLanguageInfo(identifier); + var germany = localizationManager.FindLanguageInfo(identifier); Assert.NotNull(germany); Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); @@ -72,8 +76,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [Fact] public async Task ParentalRatings_Default_Success() { - await _localizationManager.LoadAll(); - var ratings = _localizationManager.GetParentalRatings().ToList(); + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + var ratings = localizationManager.GetParentalRatings().ToList(); Assert.Equal(23, ratings.Count); @@ -85,12 +93,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [Fact] public async Task ParentalRatings_ConfiguredCountryCode_Success() { - Setup(new ServerConfiguration() + var localizationManager = Setup(new ServerConfiguration() { MetadataCountryCode = "DE" }); - await _localizationManager.LoadAll(); - var ratings = _localizationManager.GetParentalRatings().ToList(); + await localizationManager.LoadAll(); + var ratings = localizationManager.GetParentalRatings().ToList(); Assert.Equal(10, ratings.Count); @@ -109,12 +117,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("Germany: FSK-18", "DE", 9)] public async Task GetRatingLevelFromString_Valid_Success(string value, string countryCode, int expectedLevel) { - Setup(new ServerConfiguration() + var localizationManager = Setup(new ServerConfiguration() { MetadataCountryCode = countryCode }); - await _localizationManager.LoadAll(); - var level = _localizationManager.GetRatingLevel(value); + await localizationManager.LoadAll(); + var level = localizationManager.GetRatingLevel(value); Assert.NotNull(level); Assert.Equal(expectedLevel, level!); } @@ -122,8 +130,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [Fact] public async Task GetRatingLevelFromString_Unrated_Success() { - await _localizationManager.LoadAll(); - Assert.Null(_localizationManager.GetRatingLevel("n/a")); + var localizationManager = Setup(new ServerConfiguration() + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + Assert.Null(localizationManager.GetRatingLevel("n/a")); } [Theory] @@ -131,12 +143,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("HeaderLiveTV", "Live TV")] public void GetLocalizedString_Valid_Success(string key, string expected) { - Setup(new ServerConfiguration() + var localizationManager = Setup(new ServerConfiguration() { UICulture = "en-US" }); - var translated = _localizationManager.GetLocalizedString(key); + var translated = localizationManager.GetLocalizedString(key); Assert.NotNull(translated); Assert.Equal(expected, translated); } @@ -144,24 +156,24 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [Fact] public void GetLocalizedString_Invalid_Success() { - Setup(new ServerConfiguration() + var localizationManager = Setup(new ServerConfiguration() { UICulture = "en-US" }); var key = "SuperInvalidTranslationKeyThatWillNeverBeAdded"; - var translated = _localizationManager.GetLocalizedString(key); + var translated = localizationManager.GetLocalizedString(key); Assert.NotNull(translated); Assert.Equal(key, translated); } - private void Setup(ServerConfiguration config) + private LocalizationManager Setup(ServerConfiguration config) { var mockConfiguration = new Mock(); mockConfiguration.SetupGet(x => x.Configuration).Returns(config); - _localizationManager = new LocalizationManager(mockConfiguration.Object, new NullLogger()); + return new LocalizationManager(mockConfiguration.Object, new NullLogger()); } } } From 77aee515a25b04d53aea05302209121daa33923b Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 10 Aug 2021 13:37:33 +0200 Subject: [PATCH 4/8] Use correct string comparison --- .../Localization/LocalizationManagerTests.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 651957ae39..0a9f4d817a 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Threading.Tasks; using Emby.Server.Implementations.Localization; using MediaBrowser.Controller.Configuration; @@ -23,11 +24,11 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(139, countryInfos.Count); - var germany = countryInfos.FirstOrDefault(x => x.Name == "DE"); + var germany = countryInfos.FirstOrDefault(x => x.Name.Equals("DE", StringComparison.Ordinal)); Assert.NotNull(germany); Assert.Equal("Germany", germany!.DisplayName); - Assert.Equal("DEU", germany!.ThreeLetterISORegionName); - Assert.Equal("DE", germany!.TwoLetterISORegionName); + Assert.Equal("DEU", germany.ThreeLetterISORegionName); + Assert.Equal("DE", germany.TwoLetterISORegionName); } [Fact] @@ -45,10 +46,10 @@ namespace Jellyfin.Server.Implementations.Tests.Localization var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName == "de"); Assert.NotNull(germany); Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); - Assert.Equal("German", germany!.DisplayName); - Assert.Equal("German", germany!.Name); - Assert.Contains("deu", germany!.ThreeLetterISOLanguageNames); - Assert.Contains("ger", germany!.ThreeLetterISOLanguageNames); + Assert.Equal("German", germany.DisplayName); + Assert.Equal("German", germany.Name); + Assert.Contains("deu", germany.ThreeLetterISOLanguageNames); + Assert.Contains("ger", germany.ThreeLetterISOLanguageNames); } [Theory] @@ -66,11 +67,11 @@ namespace Jellyfin.Server.Implementations.Tests.Localization var germany = localizationManager.FindLanguageInfo(identifier); Assert.NotNull(germany); - Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); - Assert.Equal("German", germany!.DisplayName); - Assert.Equal("German", germany!.Name); - Assert.Contains("deu", germany!.ThreeLetterISOLanguageNames); - Assert.Contains("ger", germany!.ThreeLetterISOLanguageNames); + Assert.Equal("ger", germany.ThreeLetterISOLanguageName); + Assert.Equal("German", germany.DisplayName); + Assert.Equal("German", germany.Name); + Assert.Contains("deu", germany.ThreeLetterISOLanguageNames); + Assert.Contains("ger", germany.ThreeLetterISOLanguageNames); } [Fact] From 6b61b50b5331a3b088fa9fd5e742962dcb9b5b0b Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 10 Aug 2021 13:39:51 +0200 Subject: [PATCH 5/8] Revert "Refactor LocalizationManager and remove dead method" This reverts commit db2b53a4b52d0c1e9797bfc70030b04421ba46a6. --- .../Localization/LocalizationManager.cs | 426 +++++++++--------- .../Globalization/ILocalizationManager.cs | 8 + 2 files changed, 231 insertions(+), 203 deletions(-) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index efbccaa5b9..220e423bf5 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -25,19 +25,19 @@ namespace Emby.Server.Implementations.Localization private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; + private readonly IServerConfigurationManager _configurationManager; + private readonly ILogger _logger; + private readonly Dictionary> _allParentalRatings = new Dictionary>(StringComparer.OrdinalIgnoreCase); - private readonly IServerConfigurationManager _configurationManager; - private readonly ConcurrentDictionary> _dictionaries = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; - private readonly ILogger _logger; - private List _cultures; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + /// /// Initializes a new instance of the class. /// @@ -51,6 +51,57 @@ namespace Emby.Server.Implementations.Localization _logger = logger; } + /// + /// Loads all resources into memory. + /// + /// . + public async Task LoadAll() + { + const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; + + // Extract from the assembly + foreach (var resource in _assembly.GetManifestResourceNames()) + { + if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) + { + continue; + } + + string countryCode = resource.Substring(RatingsResource.Length, 2); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + using (var str = _assembly.GetManifestResourceStream(resource)) + using (var reader = new StreamReader(str)) + { + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + string[] parts = line.Split(','); + if (parts.Length == 2 + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + { + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); + } +#if DEBUG + else + { + _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); + } +#endif + } + } + + _allParentalRatings[countryCode] = dict; + } + + await LoadCultures().ConfigureAwait(false); + } + /// /// Gets the cultures. /// @@ -58,6 +109,62 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetCultures() => _cultures; + private async Task LoadCultures() + { + List list = new List(); + + const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; + + using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) + using (var reader = new StreamReader(stream)) + { + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var parts = line.Split('|'); + + if (parts.Length == 5) + { + string name = parts[3]; + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + string twoCharName = parts[2]; + if (string.IsNullOrWhiteSpace(twoCharName)) + { + continue; + } + + string[] threeletterNames; + if (string.IsNullOrWhiteSpace(parts[1])) + { + threeletterNames = new[] { parts[0] }; + } + else + { + threeletterNames = new[] { parts[0], parts[1] }; + } + + list.Add(new CultureDto + { + DisplayName = name, + Name = name, + ThreeLetterISOLanguageNames = threeletterNames, + TwoLetterISOLanguageName = twoCharName + }); + } + } + } + + _cultures = list; + } + /// public CultureDto FindLanguageInfo(string language) => GetCultures() @@ -79,6 +186,34 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetParentalRatings() => GetParentalRatingsDictionary().Values; + /// + /// Gets the parental ratings dictionary. + /// + /// . + private Dictionary GetParentalRatingsDictionary() + { + var countryCode = _configurationManager.Configuration.MetadataCountryCode; + + if (string.IsNullOrEmpty(countryCode)) + { + countryCode = "us"; + } + + return GetRatings(countryCode) ?? GetRatings("us"); + } + + /// + /// Gets the ratings. + /// + /// The country code. + /// The ratings. + private Dictionary GetRatings(string countryCode) + { + _allParentalRatings.TryGetValue(countryCode, out var value); + + return value; + } + /// public int? GetRatingLevel(string rating) { @@ -115,7 +250,7 @@ namespace Emby.Server.Implementations.Localization var index = rating.IndexOf(':', StringComparison.Ordinal); if (index != -1) { - rating = rating.Substring(index + 1).Trim(); + rating = rating.Substring(index).TrimStart(':').Trim(); if (!string.IsNullOrWhiteSpace(rating)) { @@ -127,6 +262,20 @@ namespace Emby.Server.Implementations.Localization return null; } + /// + public bool HasUnicodeCategory(string value, UnicodeCategory category) + { + foreach (var chr in value) + { + if (char.GetUnicodeCategory(chr) == category) + { + return true; + } + } + + return false; + } + /// public string GetLocalizedString(string phrase) { @@ -156,6 +305,74 @@ namespace Emby.Server.Implementations.Localization return phrase; } + private Dictionary GetLocalizationDictionary(string culture) + { + if (string.IsNullOrEmpty(culture)) + { + throw new ArgumentNullException(nameof(culture)); + } + + const string Prefix = "Core"; + + return _dictionaries.GetOrAdd( + culture, + f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); + } + + private async Task> GetDictionary(string prefix, string culture, string baseFilename) + { + if (string.IsNullOrEmpty(culture)) + { + throw new ArgumentNullException(nameof(culture)); + } + + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + var namespaceName = GetType().Namespace + "." + prefix; + + await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false); + await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false); + + return dictionary; + } + + private async Task CopyInto(IDictionary dictionary, string resourcePath) + { + using (var stream = _assembly.GetManifestResourceStream(resourcePath)) + { + // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain + if (stream != null) + { + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); + + foreach (var key in dict.Keys) + { + dictionary[key] = dict[key]; + } + } + else + { + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + } + } + } + + private static string GetResourceFilename(string culture) + { + var parts = culture.Split('-'); + + if (parts.Length == 2) + { + culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant(); + } + else + { + culture = culture.ToLowerInvariant(); + } + + return culture + ".json"; + } + /// public IEnumerable GetLocalizationOptions() { @@ -197,202 +414,5 @@ namespace Emby.Server.Implementations.Localization yield return new LocalizationOption("Turkish", "tr"); yield return new LocalizationOption("Tiếng Việt", "vi"); } - - /// - /// Loads all resources into memory. - /// - /// . - public async Task LoadAll() - { - const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; - - // Extract from the assembly - foreach (var resource in _assembly.GetManifestResourceNames()) - { - if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) - { - continue; - } - - string countryCode = resource.Substring(RatingsResource.Length, 2); - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - - await using var str = _assembly.GetManifestResourceStream(resource); - using var reader = new StreamReader(str); - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - string[] parts = line.Split(','); - if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - var name = parts[0]; - dict.Add(name, new ParentalRating(name, value)); - } -#if DEBUG - else - { - _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); - } -#endif - } - - _allParentalRatings[countryCode] = dict; - } - - await LoadCultures().ConfigureAwait(false); - } - - private async Task LoadCultures() - { - List list = new List(); - - const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - - await using var stream = _assembly.GetManifestResourceStream(ResourcePath); - using var reader = new StreamReader(stream); - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - var parts = line.Split('|'); - - if (parts.Length == 5) - { - string name = parts[3]; - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - string twoCharName = parts[2]; - if (string.IsNullOrWhiteSpace(twoCharName)) - { - continue; - } - - string[] threeletterNames; - if (string.IsNullOrWhiteSpace(parts[1])) - { - threeletterNames = new[] { parts[0] }; - } - else - { - threeletterNames = new[] { parts[0], parts[1] }; - } - - list.Add(new CultureDto - { - DisplayName = name, - Name = name, - ThreeLetterISOLanguageNames = threeletterNames, - TwoLetterISOLanguageName = twoCharName - }); - } - } - - _cultures = list; - } - - /// - /// Gets the parental ratings dictionary. - /// - /// . - private Dictionary GetParentalRatingsDictionary() - { - var countryCode = _configurationManager.Configuration.MetadataCountryCode; - - if (string.IsNullOrEmpty(countryCode)) - { - countryCode = "us"; - } - - return GetRatings(countryCode) ?? GetRatings("us"); - } - - /// - /// Gets the ratings. - /// - /// The country code. - /// The ratings. - private Dictionary GetRatings(string countryCode) - { - _allParentalRatings.TryGetValue(countryCode, out var value); - - return value; - } - - private Dictionary GetLocalizationDictionary(string culture) - { - if (string.IsNullOrEmpty(culture)) - { - throw new ArgumentNullException(nameof(culture)); - } - - const string Prefix = "Core"; - - return _dictionaries.GetOrAdd( - culture, - _ => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); - } - - private async Task> GetDictionary(string prefix, string culture, string baseFilename) - { - if (string.IsNullOrEmpty(culture)) - { - throw new ArgumentNullException(nameof(culture)); - } - - var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - var namespaceName = GetType().Namespace + "." + prefix; - - await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false); - await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false); - - return dictionary; - } - - private async Task CopyInto(IDictionary dictionary, string resourcePath) - { - await using var stream = _assembly.GetManifestResourceStream(resourcePath); - // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain - if (stream != null) - { - var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); - - foreach (var key in dict.Keys) - { - dictionary[key] = dict[key]; - } - } - else - { - _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); - } - } - - private static string GetResourceFilename(string culture) - { - var parts = culture.Split('-'); - - if (parts.Length == 2) - { - culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant(); - } - else - { - culture = culture.ToLowerInvariant(); - } - - return culture + ".json"; - } } } diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index e0e7317efd..baefeb39cf 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -56,6 +56,14 @@ namespace MediaBrowser.Model.Globalization /// . IEnumerable GetLocalizationOptions(); + /// + /// Checks if the string contains a character with the specified unicode category. + /// + /// The string. + /// The unicode category. + /// Wether or not the string contains a character with the specified unicode category. + bool HasUnicodeCategory(string value, UnicodeCategory category); + /// /// Returns the correct for the given language. /// From b5880c26808a6d7f183acb3f7977b42e13ccbf8a Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 10 Aug 2021 14:03:15 +0200 Subject: [PATCH 6/8] Minor improvements --- .../Localization/LocalizationManager.cs | 145 +++++++++--------- 1 file changed, 69 insertions(+), 76 deletions(-) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 220e423bf5..3015786162 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -22,6 +22,9 @@ namespace Emby.Server.Implementations.Localization public class LocalizationManager : ILocalizationManager { private const string DefaultCulture = "en-US"; + private const string RatingsPath = "Emby.Server.Implementations.Localization.Ratings."; + private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt"; + private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json"; private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; @@ -57,43 +60,39 @@ namespace Emby.Server.Implementations.Localization /// . public async Task LoadAll() { - const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; - // Extract from the assembly foreach (var resource in _assembly.GetManifestResourceNames()) { - if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) + if (!resource.StartsWith(RatingsPath, StringComparison.Ordinal)) { continue; } - string countryCode = resource.Substring(RatingsResource.Length, 2); + string countryCode = resource.Substring(RatingsPath.Length, 2); var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - using (var str = _assembly.GetManifestResourceStream(resource)) - using (var reader = new StreamReader(str)) + await using var str = _assembly.GetManifestResourceStream(resource); + using var reader = new StreamReader(str); + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) { - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + if (string.IsNullOrWhiteSpace(line)) { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - string[] parts = line.Split(','); - if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - var name = parts[0]; - dict.Add(name, new ParentalRating(name, value)); - } -#if DEBUG - else - { - _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); - } -#endif + continue; } + + string[] parts = line.Split(','); + if (parts.Length == 2 + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + { + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); + } +#if DEBUG + else + { + _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); + } +#endif } _allParentalRatings[countryCode] = dict; @@ -113,52 +112,48 @@ namespace Emby.Server.Implementations.Localization { List list = new List(); - const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - - using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) - using (var reader = new StreamReader(stream)) + await using var stream = _assembly.GetManifestResourceStream(CulturesPath); + using var reader = new StreamReader(stream); + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) { - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + if (string.IsNullOrWhiteSpace(line)) { - if (string.IsNullOrWhiteSpace(line)) + continue; + } + + var parts = line.Split('|'); + + if (parts.Length == 5) + { + string name = parts[3]; + if (string.IsNullOrWhiteSpace(name)) { continue; } - var parts = line.Split('|'); - - if (parts.Length == 5) + string twoCharName = parts[2]; + if (string.IsNullOrWhiteSpace(twoCharName)) { - string name = parts[3]; - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - string twoCharName = parts[2]; - if (string.IsNullOrWhiteSpace(twoCharName)) - { - continue; - } - - string[] threeletterNames; - if (string.IsNullOrWhiteSpace(parts[1])) - { - threeletterNames = new[] { parts[0] }; - } - else - { - threeletterNames = new[] { parts[0], parts[1] }; - } - - list.Add(new CultureDto - { - DisplayName = name, - Name = name, - ThreeLetterISOLanguageNames = threeletterNames, - TwoLetterISOLanguageName = twoCharName - }); + continue; } + + string[] threeletterNames; + if (string.IsNullOrWhiteSpace(parts[1])) + { + threeletterNames = new[] { parts[0] }; + } + else + { + threeletterNames = new[] { parts[0], parts[1] }; + } + + list.Add(new CultureDto + { + DisplayName = name, + Name = name, + ThreeLetterISOLanguageNames = threeletterNames, + TwoLetterISOLanguageName = twoCharName + }); } } @@ -177,7 +172,7 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetCountries() { - using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); + using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath)); return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions); } @@ -338,23 +333,21 @@ namespace Emby.Server.Implementations.Localization private async Task CopyInto(IDictionary dictionary, string resourcePath) { - using (var stream = _assembly.GetManifestResourceStream(resourcePath)) + await using var stream = _assembly.GetManifestResourceStream(resourcePath); + // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain + if (stream != null) { - // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain - if (stream != null) - { - var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); - foreach (var key in dict.Keys) - { - dictionary[key] = dict[key]; - } - } - else + foreach (var key in dict.Keys) { - _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + dictionary[key] = dict[key]; } } + else + { + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + } } private static string GetResourceFilename(string culture) From d7c9b141758c0cb2d1dd708fe4ce625b95d64434 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 10 Aug 2021 20:28:57 +0200 Subject: [PATCH 7/8] Apply suggestions from code review --- .../Localization/LocalizationManagerTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 0a9f4d817a..e2c998b54b 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -19,12 +19,11 @@ namespace Jellyfin.Server.Implementations.Tests.Localization { UICulture = "de-DE" }); - var countries = localizationManager.GetCountries(); - var countryInfos = countries.ToList(); + var countries = localizationManager.GetCountries().ToList(); - Assert.Equal(139, countryInfos.Count); + Assert.Equal(139, countries.Count); - var germany = countryInfos.FirstOrDefault(x => x.Name.Equals("DE", StringComparison.Ordinal)); + var germany = countries.FirstOrDefault(x => x.Name.Equals("DE", StringComparison.Ordinal)); Assert.NotNull(germany); Assert.Equal("Germany", germany!.DisplayName); Assert.Equal("DEU", germany.ThreeLetterISORegionName); @@ -43,7 +42,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(189, cultures.Count); - var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName == "de"); + var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName.Equals("de", StringComparison.Ordinal)); Assert.NotNull(germany); Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); Assert.Equal("German", germany.DisplayName); @@ -86,7 +85,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(23, ratings.Count); - var tvma = ratings.FirstOrDefault(x => x.Name == "TV-MA"); + var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); Assert.Equal(9, tvma!.Value); } @@ -103,7 +102,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(10, ratings.Count); - var fsk = ratings.FirstOrDefault(x => x.Name == "FSK-12"); + var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); Assert.Equal(7, fsk!.Value); } From 3a47ad11e9b8b229026e2fe479a0012861a4e5bd Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 11 Aug 2021 12:57:31 +0200 Subject: [PATCH 8/8] Apply suggestions from code review --- .../Localization/LocalizationManagerTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index e2c998b54b..edd4b1e558 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -55,7 +55,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("de")] [InlineData("ger")] [InlineData("german")] - public async Task FindLanguage_Valid_Success(string identifier) + public async Task FindLanguageInfo_Valid_Success(string identifier) { var localizationManager = Setup(new ServerConfiguration { @@ -74,7 +74,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization } [Fact] - public async Task ParentalRatings_Default_Success() + public async Task GetParentalRatings_Default_Success() { var localizationManager = Setup(new ServerConfiguration { @@ -91,7 +91,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization } [Fact] - public async Task ParentalRatings_ConfiguredCountryCode_Success() + public async Task GetParentalRatings_ConfiguredCountryCode_Success() { var localizationManager = Setup(new ServerConfiguration() { @@ -115,7 +115,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("TV-MA", "US", 9)] [InlineData("XXX", "asdf", 100)] [InlineData("Germany: FSK-18", "DE", 9)] - public async Task GetRatingLevelFromString_Valid_Success(string value, string countryCode, int expectedLevel) + public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel) { var localizationManager = Setup(new ServerConfiguration() { @@ -128,7 +128,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization } [Fact] - public async Task GetRatingLevelFromString_Unrated_Success() + public async Task GetRatingLevel_GivenUnratedString_Success() { var localizationManager = Setup(new ServerConfiguration() {