Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
Expand All @@ -17,9 +17,20 @@ namespace AXSharp.Connector.Localizations
/// </summary>
public class Translator
{
//private ResxLocalizations LocalizationResources { get; set; }

private ResourceManager _resourceManager;
private IEnumerable<ResourceManager> ResourceManagers
{
get
{
// IMPORTANT: order matters. Application should be searched first to allow overrides.
// Do not cache: resources can be configured at runtime (e.g., SetPrimaryTranslatorResource).
if (_applicationResourceManager != null) yield return _applicationResourceManager;
if (_libraryResourceManager != null) yield return _libraryResourceManager;
}
}

private static ResourceManager _applicationResourceManager;

private ResourceManager _libraryResourceManager;

private CultureInfo Culture = CultureInfo.InvariantCulture;

Expand All @@ -45,7 +56,7 @@ public void SetLocalizationResource(Type resourceType)
{
if (resourceType != null)
{
_resourceManager = new ResourceManager(resourceType)
_libraryResourceManager = new ResourceManager(resourceType)
{
IgnoreCase = true
};
Expand Down Expand Up @@ -115,20 +126,28 @@ private string LocalizeInParents(string token, ITwinElement rootObj, CultureInfo

public string Localize(string str, ITwinElement twinElement, CultureInfo culture)
{

foreach (var localizable in GetTranslatable(str))
{
var validIdentifier = LocalizationHelper.CreateId(localizable.CleanUpLocalizationTokens());

// Search in first level resource
var translation = _resourceManager?.GetString(validIdentifier, culture);

// Search in parent resources
// Search through all resource managers for the key
string translation = null;
foreach (var resourceManager in ResourceManagers)
{
translation = resourceManager?.GetString(validIdentifier, culture);
if (translation != null)
{
break; // Found translation, stop searching
}
}

// Search in parent resources if not found
if (translation == null)
{
try
{
return LocalizeInParents(str, twinElement, culture);
translation = LocalizeInParents(localizable, twinElement, culture);
}
catch
{
Expand All @@ -144,5 +163,20 @@ public string Localize(string str, ITwinElement twinElement, CultureInfo culture

return str;
}

/// <summary>
/// Sets the primary application resource for all translators.
/// This would be tipycally set to the resource containing the application's translations.
/// Any matching localization key will be first searched in this resource and then in the library resource.
/// You can leverage this to override library translations with application specific ones.
/// </summary>
/// <param name="resourceType"></param>
public static void SetPrimaryTranslatorResource(Type resourceType)
{
_applicationResourceManager = new ResourceManager(resourceType)
{
IgnoreCase = true
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace AXSharp.ConnectorTests.Localizations.Resources
{
/// <summary>
/// Marker type used to locate the embedded resource `OverrideApplication.resx`.
/// </summary>
public class OverrideApplication
{
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Override_token" xml:space="preserve">
<value>APP</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace AXSharp.ConnectorTests.Localizations.Resources
{
/// <summary>
/// Marker type used to locate the embedded resource `OverrideLibrary.resx`.
/// </summary>
public class OverrideLibrary
{
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Override_token" xml:space="preserve">
<value>LIB</value>
</data>
<data name="Library_only" xml:space="preserve">
<value>LIB_ONLY</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ namespace AXSharp.ConnectorTests.Localizations
{
using AXSharp.Connector.Localizations;
using System;
using System.Globalization;
using Xunit;
using NSubstitute;
using AXSharp.Connector;
using AXSharp.ConnectorTests.Localizations.Resources;

public class TranslatorTests
{
Expand Down Expand Up @@ -53,6 +55,57 @@ public void CanCallSetLocalizationResource()

// Act
_testClass.SetLocalizationResource(resourceType);
}
}

[Fact]
public void Translate_prefers_primary_application_resource_over_library_resource()
{
// Arrange
Translator.SetPrimaryTranslatorResource(typeof(OverrideApplication));
var translator = new Translator();
translator.SetLocalizationResource(typeof(OverrideLibrary));
var twin = Substitute.For<ITwinElement>();
var originalString = "<#Override token#>";

// Act
var result = translator.Translate(originalString, twin, CultureInfo.InvariantCulture);

// Assert
Assert.Equal("APP", result);
}

[Fact]
public void Translate_falls_back_to_library_resource_when_primary_does_not_have_key()
{
// Arrange
Translator.SetPrimaryTranslatorResource(typeof(OverrideApplication));
var translator = new Translator();
translator.SetLocalizationResource(typeof(OverrideLibrary));
var twin = Substitute.For<ITwinElement>();
var originalString = "<#Library only#>";

// Act
var result = translator.Translate(originalString, twin, CultureInfo.InvariantCulture);

// Assert
Assert.Equal("LIB_ONLY", result);
}

[Fact]
public void Translate_when_key_missing_returns_text_without_localization_tokens()
{
// Arrange
Translator.SetPrimaryTranslatorResource(typeof(OverrideApplication));
var translator = new Translator();
translator.SetLocalizationResource(typeof(OverrideLibrary));
var twin = Substitute.For<ITwinElement>();
var originalString = "<#Does not exist#>";

// Act
var result = translator.Translate(originalString, twin, CultureInfo.InvariantCulture);

// Assert
Assert.Equal("Does not exist", result);
}
}
}