From 4be3f5f1f9ff8bd0333033d6ad9c99711da03f96 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 4 May 2026 20:26:39 +0200 Subject: Add Accept-Language header support for per-request localization --- .../Localization/Core/en-US.json | 34 ++-------------------- 1 file changed, 3 insertions(+), 31 deletions(-) (limited to 'Emby.Server.Implementations/Localization/Core') diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index 9b5049c8c7..ff674bd0d0 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -1,45 +1,29 @@ { - "Albums": "Albums", "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", "Artists": "Artists", "AuthenticationSucceededWithUserName": "{0} successfully authenticated", "Books": "Books", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", "ChapterNameValue": "Chapter {0}", "Collections": "Collections", "Default": "Default", - "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", "External": "External", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "Favorites": "Favorites", "Folders": "Folders", "Forced": "Forced", "Genres": "Genres", - "HeaderAlbumArtists": "Album artists", "HeaderContinueWatching": "Continue Watching", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", "HeaderFavoriteEpisodes": "Favorite Episodes", "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteSongs": "Favorite Songs", "HeaderLiveTV": "Live TV", "HeaderNextUp": "Next Up", - "HeaderRecordingGroups": "Recording Groups", "HearingImpaired": "Hearing Impaired", "HomeVideos": "Home Videos", "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", "LabelIpAddressValue": "IP address: {0}", "LabelRunningTimeValue": "Running time: {0}", "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", + "LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}", "MixedContent": "Mixed content", "Movies": "Movies", "Music": "Music", @@ -66,24 +50,15 @@ "NotificationOptionVideoPlaybackStopped": "Video playback stopped", "Original": "Original", "Photos": "Photos", - "Playlists": "Playlists", - "Plugin": "Plugin", "PluginInstalledWithName": "{0} was installed", "PluginUninstalledWithName": "{0} was uninstalled", "PluginUpdatedWithName": "{0} was updated", - "ProviderValue": "Provider: {0}", "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", "Shows": "Shows", - "Songs": "Songs", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "Sync": "Sync", - "System": "System", "TvShows": "TV Shows", "Undefined": "Undefined", - "User": "User", "UserCreatedWithName": "User {0} has been created", "UserDeletedWithName": "User {0} has been deleted", "UserDownloadingItemWithValues": "{0} is downloading {1}", @@ -91,11 +66,8 @@ "UserOfflineFromDevice": "{0} has disconnected from {1}", "UserOnlineFromDevice": "{0} is online from {1}", "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", - "ValueSpecialEpisodeName": "Special - {0}", "VersionNumber": "Version {0}", "TasksMaintenanceCategory": "Maintenance", "TasksLibraryCategory": "Library", @@ -121,8 +93,8 @@ "TaskUpdatePluginsDescription": "Downloads and installs updates for plugins that are configured to update automatically.", "TaskCleanTranscode": "Clean Transcode Directory", "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.", - "TaskRefreshChannels": "Refresh Channels", - "TaskRefreshChannelsDescription": "Refreshes internet channel information.", + "TasksRefreshChannels": "Refresh Channels", + "TasksRefreshChannelsDescription": "Refreshes internet channel information.", "TaskDownloadMissingLyrics": "Download missing lyrics", "TaskDownloadMissingLyricsDescription": "Downloads lyrics for songs", "TaskDownloadMissingSubtitles": "Download missing subtitles", -- cgit v1.2.3 From 7a5181c3fd3aea8a9913fe07086970c39c9bc1c4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 14 May 2026 07:46:43 +0200 Subject: Address review comments --- .../Localization/Core/en-US.json | 4 +- .../Localization/LocalizationManager.cs | 48 ++++++++++++---------- .../Channels/RefreshChannelsScheduledTask.cs | 4 +- 3 files changed, 30 insertions(+), 26 deletions(-) (limited to 'Emby.Server.Implementations/Localization/Core') diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index ff674bd0d0..856941c61a 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -93,8 +93,8 @@ "TaskUpdatePluginsDescription": "Downloads and installs updates for plugins that are configured to update automatically.", "TaskCleanTranscode": "Clean Transcode Directory", "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.", - "TasksRefreshChannels": "Refresh Channels", - "TasksRefreshChannelsDescription": "Refreshes internet channel information.", + "TaskRefreshChannels": "Refresh Channels", + "TaskRefreshChannelsDescription": "Refreshes internet channel information.", "TaskDownloadMissingLyrics": "Download missing lyrics", "TaskDownloadMissingLyricsDescription": "Downloads lyrics for songs", "TaskDownloadMissingSubtitles": "Download missing subtitles", diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 94aa933c92..0b0b300d30 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -77,22 +77,36 @@ namespace Emby.Server.Implementations.Localization 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. + if (TryGetCultureInfo(option.Value, out var cultureInfo)) { - // Skip novelty codes (e.g. "pr" Pirate, "jbo" Lojban) that .NET cannot resolve. + cultures.Add(cultureInfo); } } return cultures; } + /// + /// Resolves a Jellyfin resource culture code (which may use underscores, e.g. es_419) + /// to a . Returns for codes .NET cannot resolve. + /// + private static bool TryGetCultureInfo(string cultureCode, [NotNullWhen(true)] out CultureInfo? cultureInfo) + { + try + { + // Resource files use underscores for some variants (e.g. es_419); + // CultureInfo only accepts hyphenated BCP-47 codes. + cultureInfo = CultureInfo.GetCultureInfo(cultureCode.Replace('_', '-')); + return true; + } + catch (CultureNotFoundException) + { + cultureInfo = null; + return false; + } + } + private static void OnConfigurationUpdated(object? sender, EventArgs e) { if (sender is IServerConfigurationManager configManager) @@ -614,20 +628,10 @@ namespace Emby.Server.Implementations.Localization private static string GetDisplayName(string cultureCode) { - // Resource files use underscores for codes that .NET's CultureInfo doesn't accept directly (e.g. es_419). - var lookup = cultureCode.Contains('_', StringComparison.Ordinal) - ? cultureCode.Replace('_', '-') + // Custom/novelty codes like "pr" (Pirate) — fall back to code itself + return TryGetCultureInfo(cultureCode, out var cultureInfo) + ? cultureInfo.NativeName : cultureCode; - - try - { - return CultureInfo.GetCultureInfo(lookup).NativeName; - } - catch (CultureNotFoundException) - { - // Custom/novelty codes like "pr" (Pirate) — fall back to code itself - return cultureCode; - } } /// diff --git a/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs index 71e46764ad..bb4238a2ac 100644 --- a/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs +++ b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs @@ -40,10 +40,10 @@ namespace Jellyfin.LiveTv.Channels } /// - public string Name => _localization.GetLocalizedString("TasksRefreshChannels"); + public string Name => _localization.GetLocalizedString("TaskRefreshChannels"); /// - public string Description => _localization.GetLocalizedString("TasksRefreshChannelsDescription"); + public string Description => _localization.GetLocalizedString("TaskRefreshChannelsDescription"); /// public string Category => _localization.GetLocalizedString("TasksChannelsCategory"); -- cgit v1.2.3