diff --git a/Jellyfin.Plugin.Themerr.Tests/Jellyfin.Plugin.Themerr.Tests.csproj b/Jellyfin.Plugin.Themerr.Tests/Jellyfin.Plugin.Themerr.Tests.csproj
index cf033c5..fa743d6 100644
--- a/Jellyfin.Plugin.Themerr.Tests/Jellyfin.Plugin.Themerr.Tests.csproj
+++ b/Jellyfin.Plugin.Themerr.Tests/Jellyfin.Plugin.Themerr.Tests.csproj
@@ -30,6 +30,10 @@
+
+
+
+
PreserveNewest
diff --git a/Jellyfin.Plugin.Themerr.Tests/TestThemerrController.cs b/Jellyfin.Plugin.Themerr.Tests/TestThemerrController.cs
index cc17cef..bbdb05c 100644
--- a/Jellyfin.Plugin.Themerr.Tests/TestThemerrController.cs
+++ b/Jellyfin.Plugin.Themerr.Tests/TestThemerrController.cs
@@ -1,5 +1,6 @@
using System.Collections;
using Jellyfin.Plugin.Themerr.Api;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -25,7 +26,18 @@ public TestThemerrController(ITestOutputHelper output)
Mock mockLibraryManager = new();
Mock> mockLogger = new();
- _controller = new ThemerrController(mockLibraryManager.Object, mockLogger.Object);
+ Mock mockServerConfigurationManager = new();
+
+ // Create a TestableServerConfiguration with UICulture set to "en-US"
+ var testableServerConfiguration = new TestableServerConfiguration("en-US");
+
+ // Setup the Configuration property of the IServerConfigurationManager mock to return the TestableServerConfiguration
+ mockServerConfigurationManager.Setup(x => x.Configuration).Returns(testableServerConfiguration);
+
+ _controller = new ThemerrController(
+ mockLibraryManager.Object,
+ mockLogger.Object,
+ mockServerConfigurationManager.Object);
}
///
@@ -56,4 +68,69 @@ public void TestGetProgress()
// todo: add tests for when there are items
}
+
+ ///
+ /// Test GetTranslations from API.
+ ///
+ [Fact]
+ [Trait("Category", "Unit")]
+ public void TestGetTranslations()
+ {
+ var actionResult = _controller.GetTranslations();
+ Assert.IsType(actionResult);
+
+ // Cast the result to OkObjectResult to access the data
+ var okResult = actionResult as OkObjectResult;
+
+ // Access the data returned by the API
+ var data = okResult?.Value as Dictionary;
+
+ Assert.NotNull(data);
+
+ // Assert the data contains the expected keys
+ Assert.True(data.ContainsKey("locale"));
+ Assert.True(data.ContainsKey("fallback"));
+ }
+
+ ///
+ /// Test GetCultureResource function.
+ ///
+ [Fact]
+ [Trait("Category", "Unit")]
+ public void TestGetCultureResource()
+ {
+ // list of english cultures
+ var enCultures = new List
+ {
+ "de",
+ "en",
+ "en-GB",
+ "en-US",
+ "es",
+ "fr",
+ "it",
+ "ru",
+ "sv",
+ "zh"
+ };
+
+ foreach (var t in enCultures)
+ {
+ var result = _controller.GetCultureResource(t);
+ Assert.IsType>(result);
+
+ // replace - with _ in the culture
+ var t2 = t.Replace("-", "_");
+
+ // en is not included in the list
+ if (t != "en")
+ {
+ // assert that `en_<>.json` is in the list
+ Assert.Contains(t2 + ".json", result);
+ }
+
+ // assert that `en` is NOT in the list
+ Assert.DoesNotContain("en.json", result);
+ }
+ }
}
diff --git a/Jellyfin.Plugin.Themerr.Tests/TestableServerConfiguration.cs b/Jellyfin.Plugin.Themerr.Tests/TestableServerConfiguration.cs
new file mode 100644
index 0000000..bb9f45e
--- /dev/null
+++ b/Jellyfin.Plugin.Themerr.Tests/TestableServerConfiguration.cs
@@ -0,0 +1,18 @@
+using MediaBrowser.Model.Configuration;
+
+namespace Jellyfin.Plugin.Themerr.Tests;
+
+///
+/// Represents a testable server configuration for unit testing.
+///
+public class TestableServerConfiguration : ServerConfiguration
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Mocked UI culture.
+ public TestableServerConfiguration(string uiCulture)
+ {
+ UICulture = uiCulture;
+ }
+}
diff --git a/Jellyfin.Plugin.Themerr/Api/ThemerrController.cs b/Jellyfin.Plugin.Themerr/Api/ThemerrController.cs
index a7b21b9..dee3554 100644
--- a/Jellyfin.Plugin.Themerr/Api/ThemerrController.cs
+++ b/Jellyfin.Plugin.Themerr/Api/ThemerrController.cs
@@ -1,8 +1,12 @@
using System;
using System.Collections;
+using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Net.Mime;
+using System.Reflection;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -24,18 +28,22 @@ public class ThemerrController : ControllerBase
{
private readonly ThemerrManager _themerrManager;
private readonly ILogger _logger;
+ private readonly IServerConfigurationManager _configurationManager;
///
/// Initializes a new instance of the class.
///
/// The library manager.
/// The logger.
+ /// The configuration manager.
public ThemerrController(
ILibraryManager libraryManager,
- ILogger logger)
+ ILogger logger,
+ IServerConfigurationManager configurationManager)
{
_themerrManager = new ThemerrManager(libraryManager, logger);
_logger = logger;
+ _configurationManager = configurationManager;
}
///
@@ -120,5 +128,111 @@ public ActionResult GetProgress()
return new JsonResult(tmpObject);
}
+
+ ///
+ /// Get the localization strings from Locale/{selected_locale}.json.
+ ///
+ ///
+ /// JSON object containing localization strings.
+ [HttpGet("GetTranslations")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult GetTranslations()
+ {
+ // get the locale from the user's settings
+ var culture = _configurationManager.Configuration.UICulture;
+
+ _logger.LogInformation("Server culture: {ServerCulture}", culture);
+
+ // get file paths from LocalizationManager
+ var filePaths = GetCultureResource(culture);
+
+ // Get the current assembly
+ var assembly = Assembly.GetExecutingAssembly();
+
+ for (var i = 0; i < filePaths.Count; i++)
+ {
+ // construct the resource name
+ var resourceName = $"Jellyfin.Plugin.Themerr.Locale.{filePaths[i]}";
+
+ // Get the resource stream
+ using var stream = assembly.GetManifestResourceStream(resourceName);
+
+ if (stream == null)
+ {
+ _logger.LogWarning(
+ "Locale resource does not exist: {ResourceName}",
+ resourceName.Replace(Environment.NewLine, string.Empty));
+ continue;
+ }
+
+ // Initialize the result dictionary
+ var result = new Dictionary();
+
+ // read the resource content
+ using var reader = new StreamReader(stream);
+ var json = reader.ReadToEnd();
+
+ // deserialize the JSON content into a dictionary
+ var localizedStrings = JsonConvert.DeserializeObject>(json);
+
+ // Add the localized strings to the 'locale' key
+ result["locale"] = localizedStrings;
+
+ // Now get the fallback resource
+ var fallbackResourceName = "Jellyfin.Plugin.Themerr.Locale.en.json";
+ using var fallbackStream = assembly.GetManifestResourceStream(fallbackResourceName);
+
+ if (fallbackStream != null)
+ {
+ // read the fallback resource content
+ using var fallbackReader = new StreamReader(fallbackStream);
+ var fallbackJson = fallbackReader.ReadToEnd();
+
+ // deserialize the fallback JSON content into a dictionary
+ var fallbackLocalizedStrings =
+ JsonConvert.DeserializeObject>(fallbackJson);
+
+ // Add the fallback localized strings to the 'fallback' key
+ result["fallback"] = fallbackLocalizedStrings;
+ }
+ else
+ {
+ _logger.LogError("Fallback locale resource does not exist: {ResourceName}", fallbackResourceName);
+ }
+
+ // return the result as a JSON object
+ return Ok(result);
+ }
+
+ // return an error if we get this far
+ return StatusCode(StatusCodes.Status500InternalServerError);
+ }
+
+ ///
+ /// Get the resources of the given culture.
+ ///
+ ///
+ /// The culture to get the resource for.
+ /// A list of file names.
+ public List GetCultureResource(string culture)
+ {
+ string tmp;
+ var fileNames = new List();
+ var parts = culture.Split('-');
+
+ if (parts.Length == 2)
+ {
+ tmp = parts[0].ToLowerInvariant() + "_" + parts[1].ToUpperInvariant();
+ fileNames.Add(tmp + ".json");
+ }
+
+ tmp = parts[0].ToLowerInvariant();
+ if (tmp != "en")
+ {
+ fileNames.Add(tmp + ".json");
+ }
+
+ return fileNames;
+ }
}
}
diff --git a/Jellyfin.Plugin.Themerr/Configuration/configPage.html b/Jellyfin.Plugin.Themerr/Configuration/configPage.html
index 1de93a9..bce43bc 100644
--- a/Jellyfin.Plugin.Themerr/Configuration/configPage.html
+++ b/Jellyfin.Plugin.Themerr/Configuration/configPage.html
@@ -20,7 +20,7 @@
Themerr
DiscordSupport Center
+ href="https://app.lizardbyte.dev/support" data-localize="support_center">Support Center
Themerr
-
This plugin relies on the TMDB provider.
+
This plugin relies on the TMDB provider.
Please make sure it is enabled!