From 5cfb379aa63689435077c8f1ebc10c98f625238c Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 4 May 2026 21:57:11 +0200 Subject: Use native middleware --- .../HttpServer/WebSocketConnection.cs | 28 +++------- .../HttpServer/WebSocketManager.cs | 4 +- .../Localization/LocalizationManager.cs | 63 +++++++++------------- 3 files changed, 33 insertions(+), 62 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 6dc6d9d289..17070c39ba 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -1,6 +1,5 @@ using System; using System.Buffers; -using System.Collections.Generic; using System.Globalization; using System.IO.Pipelines; using System.Net; @@ -9,7 +8,6 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.Localization; using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net.WebSocketMessages; @@ -72,12 +70,6 @@ namespace Emby.Server.Implementations.HttpServer /// public IPAddress? RemoteEndPoint { get; } - /// - /// Gets or initializes the culture fallback chain captured from the - /// Accept-Language header of the upgrade request. - /// - public IReadOnlyList? RequestCultureFallback { get; init; } - /// /// Gets or initializes the UI culture name captured from the upgrade request. /// @@ -98,22 +90,18 @@ namespace Emby.Server.Implementations.HttpServer /// public void ApplyRequestCulture() { - if (RequestCultureFallback is not null) + if (string.IsNullOrEmpty(RequestUICulture)) { - LocalizationManager.RequestCultureFallback = RequestCultureFallback; + return; } - if (!string.IsNullOrEmpty(RequestUICulture)) + try { - try - { - CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo(RequestUICulture); - } - catch (CultureNotFoundException) - { - // Jellyfin culture codes (e.g. "es_419") aren't always valid .NET cultures — - // skip setting CurrentUICulture; RequestCultureFallback above carries the chain. - } + CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo(RequestUICulture); + } + catch (CultureNotFoundException) + { + // Codes that aren't valid .NET cultures are ignored. } } diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs index 3b5f6d1d09..dcdfda5472 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs @@ -8,7 +8,6 @@ using System.Globalization; using System.Linq; using System.Net.WebSockets; using System.Threading.Tasks; -using Emby.Server.Implementations.Localization; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Http; @@ -50,7 +49,7 @@ namespace Emby.Server.Implementations.HttpServer WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); - // Capture the culture context set by AcceptLanguageMiddleware so it can be + // Capture the culture set by RequestLocalizationMiddleware so it can be // restored both when processing incoming messages and when periodic // listeners produce server-initiated payloads on background tasks. var connection = new WebSocketConnection( @@ -59,7 +58,6 @@ namespace Emby.Server.Implementations.HttpServer authorizationInfo, context.GetNormalizedRemoteIP()) { - RequestCultureFallback = LocalizationManager.RequestCultureFallback, RequestUICulture = CultureInfo.CurrentUICulture.Name }; connection.OnReceive = result => diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 2a7a3388aa..5c2376ea00 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text.Json; -using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; @@ -32,10 +31,9 @@ namespace Emby.Server.Implementations.Localization private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = ["n/a", "unrated", "not rated", "nr"]; - /// - /// Gets the mapping from BCP-47 hyphenated culture codes to Jellyfin's underscore-based codes. - /// - public static readonly FrozenDictionary Bcp47ToJellyfinMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + // Maps BCP-47 hyphenated culture codes (set by ASP.NET Core's RequestLocalizationMiddleware + // and used as CurrentUICulture.Name) to Jellyfin's underscore-based resource file codes. + private static readonly FrozenDictionary _bcp47ToJellyfinMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["es-419"] = "es_419", ["es-DO"] = "es_DO", @@ -47,8 +45,6 @@ namespace Emby.Server.Implementations.Localization private readonly Dictionary> _allParentalRatings = new(StringComparer.OrdinalIgnoreCase); - private static readonly AsyncLocal?> _requestCultureFallback = new(); - private readonly ConcurrentDictionary> _cultureOnlyDictionaries = new(StringComparer.OrdinalIgnoreCase); private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; @@ -76,24 +72,28 @@ namespace Emby.Server.Implementations.Localization } /// - /// Gets or sets the per-request culture fallback chain resolved from Accept-Language. - /// Each entry is a Jellyfin culture code (e.g. "de", "nl", "en-US") in priority order. + /// Gets the supported UI cultures. /// - public static IReadOnlyList? RequestCultureFallback + /// A list of objects covering every embedded translation. + public static IList GetSupportedUICultures() { - get => _requestCultureFallback.Value; - set => _requestCultureFallback.Value = value; - } + var cultures = new List(); + foreach (var option in _localizationOptions) + { + // Resource files use underscores for some variants (e.g. es_419); + // CultureInfo only accepts hyphenated BCP-47 codes. + var code = option.Value.Replace('_', '-'); + try + { + cultures.Add(CultureInfo.GetCultureInfo(code)); + } + catch (CultureNotFoundException) + { + // Skip novelty codes (e.g. "pr" Pirate, "jbo" Lojban) that .NET cannot resolve. + } + } - /// - /// Checks whether a translation resource file exists for the given culture code. - /// - /// The culture code to check (e.g. "de", "pt-BR", "es_419"). - /// true if an embedded translation resource exists for the culture. - public static bool HasTranslation(string culture) - { - var resourceName = CoreResourcePrefix + GetResourceFilename(culture); - return _assembly.GetManifestResourceInfo(resourceName) is not null; + return cultures; } private static void OnConfigurationUpdated(object? sender, EventArgs e) @@ -472,21 +472,6 @@ namespace Emby.Server.Implementations.Localization /// public string GetLocalizedString(string phrase) { - var fallback = _requestCultureFallback.Value; - if (fallback is not null) - { - foreach (var culture in fallback) - { - var dict = GetLocalizationDictionary(culture); - if (dict.TryGetValue(phrase, out var value)) - { - return value; - } - } - - return phrase; - } - return GetLocalizedString(phrase, CultureInfo.CurrentUICulture.Name); } @@ -510,7 +495,7 @@ namespace Emby.Server.Implementations.Localization } // Normalize BCP-47 hyphenated codes to Jellyfin's underscore-based codes - if (Bcp47ToJellyfinMap.TryGetValue(culture, out var mapped)) + if (_bcp47ToJellyfinMap.TryGetValue(culture, out var mapped)) { culture = mapped; } @@ -626,7 +611,7 @@ namespace Emby.Server.Implementations.Localization private static string GetDisplayName(string cultureCode) { // Handle Jellyfin-specific codes that aren't valid CultureInfo names - if (Bcp47ToJellyfinMap.Values.Contains(cultureCode)) + if (_bcp47ToJellyfinMap.Values.Contains(cultureCode)) { // Convert underscore to hyphen for CultureInfo lookup var normalized = cultureCode.Replace('_', '-'); -- cgit v1.2.3