Skip to content
This repository has been archived by the owner on Oct 13, 2024. It is now read-only.

Commit

Permalink
feat(i18n): add localization support (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
ReenigneArcher authored Mar 23, 2024
1 parent d43d128 commit 9965542
Show file tree
Hide file tree
Showing 20 changed files with 666 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
<ProjectReference Include="..\Jellyfin.Plugin.Themerr\Jellyfin.Plugin.Themerr.csproj" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="..\Jellyfin.Plugin.ThemerrLocale\**" />
</ItemGroup>

<ItemGroup>
<None Update="data\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
79 changes: 78 additions & 1 deletion Jellyfin.Plugin.Themerr.Tests/TestThemerrController.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -25,7 +26,18 @@ public TestThemerrController(ITestOutputHelper output)

Mock<ILibraryManager> mockLibraryManager = new();
Mock<ILogger<ThemerrManager>> mockLogger = new();
_controller = new ThemerrController(mockLibraryManager.Object, mockLogger.Object);
Mock<IServerConfigurationManager> 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);
}

/// <summary>
Expand Down Expand Up @@ -56,4 +68,69 @@ public void TestGetProgress()

// todo: add tests for when there are items
}

/// <summary>
/// Test GetTranslations from API.
/// </summary>
[Fact]
[Trait("Category", "Unit")]
public void TestGetTranslations()
{
var actionResult = _controller.GetTranslations();
Assert.IsType<OkObjectResult>(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<string, object>;

Assert.NotNull(data);

// Assert the data contains the expected keys
Assert.True(data.ContainsKey("locale"));
Assert.True(data.ContainsKey("fallback"));
}

/// <summary>
/// Test GetCultureResource function.
/// </summary>
[Fact]
[Trait("Category", "Unit")]
public void TestGetCultureResource()
{
// list of english cultures
var enCultures = new List<string>
{
"de",
"en",
"en-GB",
"en-US",
"es",
"fr",
"it",
"ru",
"sv",
"zh"
};

foreach (var t in enCultures)
{
var result = _controller.GetCultureResource(t);
Assert.IsType<List<string>>(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);
}
}
}
18 changes: 18 additions & 0 deletions Jellyfin.Plugin.Themerr.Tests/TestableServerConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using MediaBrowser.Model.Configuration;

namespace Jellyfin.Plugin.Themerr.Tests;

/// <summary>
/// Represents a testable server configuration for unit testing.
/// </summary>
public class TestableServerConfiguration : ServerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="TestableServerConfiguration"/> class.
/// </summary>
/// <param name="uiCulture">Mocked UI culture.</param>
public TestableServerConfiguration(string uiCulture)
{
UICulture = uiCulture;
}
}
116 changes: 115 additions & 1 deletion Jellyfin.Plugin.Themerr/Api/ThemerrController.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,18 +28,22 @@ public class ThemerrController : ControllerBase
{
private readonly ThemerrManager _themerrManager;
private readonly ILogger<ThemerrManager> _logger;
private readonly IServerConfigurationManager _configurationManager;

/// <summary>
/// Initializes a new instance of the <see cref="ThemerrController"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="configurationManager">The configuration manager.</param>
public ThemerrController(
ILibraryManager libraryManager,
ILogger<ThemerrManager> logger)
ILogger<ThemerrManager> logger,
IServerConfigurationManager configurationManager)
{
_themerrManager = new ThemerrManager(libraryManager, logger);
_logger = logger;
_configurationManager = configurationManager;
}

/// <summary>
Expand Down Expand Up @@ -120,5 +128,111 @@ public ActionResult GetProgress()

return new JsonResult(tmpObject);
}

/// <summary>
/// Get the localization strings from Locale/{selected_locale}.json.
/// </summary>
///
/// <returns>JSON object containing localization strings.</returns>
[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<string, object>();

// 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<Dictionary<string, string>>(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<Dictionary<string, string>>(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);
}

/// <summary>
/// Get the resources of the given culture.
/// </summary>
///
/// <param name="culture">The culture to get the resource for.</param>
/// <returns>A list of file names.</returns>
public List<string> GetCultureResource(string culture)
{
string tmp;
var fileNames = new List<string>();
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;
}
}
}
Loading

0 comments on commit 9965542

Please sign in to comment.