aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/Localization/Core/en_US.json64
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs10
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs14
3 files changed, 21 insertions, 67 deletions
diff --git a/Emby.Server.Implementations/Localization/Core/en_US.json b/Emby.Server.Implementations/Localization/Core/en_US.json
deleted file mode 100644
index b093f73099..0000000000
--- a/Emby.Server.Implementations/Localization/Core/en_US.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "AppDeviceValues": "App: {0}, Device: {1}",
- "Artists": "Artists",
- "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
- "Books": "Books",
- "ChapterNameValue": "Chapter {0}",
- "Collections": "Collections",
- "Default": "Default",
- "External": "External",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
- "Favorites": "Favorites",
- "Folders": "Folders",
- "Forced": "Forced",
- "Genres": "Genres",
- "HeaderContinueWatching": "Continue Watching",
- "HeaderFavoriteEpisodes": "Favorite Episodes",
- "HeaderFavoriteShows": "Favorite Shows",
- "HeaderLiveTV": "Live TV",
- "HeaderNextUp": "Next Up",
- "HearingImpaired": "Hearing Impaired",
- "HomeVideos": "Home Videos",
- "Inherit": "Inherit",
- "LabelIpAddressValue": "IP address: {0}",
- "LabelRunningTimeValue": "Running time: {0}",
- "Latest": "Latest",
- "LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}",
- "MixedContent": "Mixed content",
- "Movies": "Movies",
- "Music": "Music",
- "MusicVideos": "Music Videos",
- "NameInstallFailed": "{0} installation failed",
- "NameSeasonNumber": "Season {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
- "NotificationOptionApplicationUpdateAvailable": "Application update available",
- "NotificationOptionApplicationUpdateInstalled": "Application update installed",
- "NotificationOptionAudioPlayback": "Audio playback started",
- "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
- "NotificationOptionCameraImageUploaded": "Camera image uploaded",
- "NotificationOptionInstallationFailed": "Installation failure",
- "NotificationOptionNewLibraryContent": "New content added",
- "NotificationOptionPluginError": "Plugin failure",
- "NotificationOptionPluginInstalled": "Plugin installed",
- "NotificationOptionPluginUninstalled": "Plugin uninstalled",
- "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
- "NotificationOptionServerRestartRequired": "Server restart required",
- "NotificationOptionTaskFailed": "Scheduled task failure",
- "NotificationOptionUserLockedOut": "User locked out",
- "NotificationOptionVideoPlayback": "Video playback started",
- "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
- "Original": "Original",
- "Photos": "Photos",
- "PluginInstalledWithName": "{0} was installed",
- "PluginUninstalledWithName": "{0} was uninstalled",
- "PluginUpdatedWithName": "{0} was updated",
- "ScheduledTaskFailedWithName": "{0} failed",
- "Shows": "Shows",
- "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} to {1}",
- "TvShows": "TV Shows",
- "Undefined": "Undefined",
- "UserCreatedWithName": "User {0} has been created",
- "UserDeletedWithName": "User {0} has been deleted"
-}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 843e35afcc..6971431155 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -566,11 +566,15 @@ namespace Emby.Server.Implementations.Localization
private static string GetResourceFilename(string culture)
{
- var parts = culture.Split('-');
+ // Region codes may use a '-' (BCP-47, e.g. "pt-BR") or '_' (e.g. "es_419", "ar_SA") separator.
+ // Normalize the casing (lower-case language, upper-case region) while preserving the separator
+ // so the result matches the embedded resource file name, which is case-sensitive.
+ var separatorIndex = culture.IndexOfAny(['-', '_']);
- if (parts.Length == 2)
+ if (separatorIndex > 0)
{
- culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant();
+ var separator = culture[separatorIndex];
+ culture = culture[..separatorIndex].ToLowerInvariant() + separator + culture[(separatorIndex + 1)..].ToUpperInvariant();
}
else
{
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 3b8fe5ca60..bdb726f06d 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -345,6 +345,20 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
}
[Fact]
+ public void GetLocalizedString_WithBcp47NormalizationToUppercaseRegion_ReturnsTranslation()
+ {
+ var localizationManager = Setup(new ServerConfiguration
+ {
+ UICulture = "en-US"
+ });
+
+ // he-IL normalizes to the underscore resource he_IL. The resource lookup is case-sensitive,
+ // so the region casing has to be preserved or the file is not found and we fall back to en-US.
+ var translated = localizationManager.GetLocalizedString("Books", "he-IL");
+ Assert.Equal("ספרים", translated);
+ }
+
+ [Fact]
public void GetServerLocalizedString_UsesServerCulture()
{
var localizationManager = Setup(new ServerConfiguration