Skip to content

Commit

Permalink
feat(Localization): Allow setting a Language resource to an empty str…
Browse files Browse the repository at this point in the history
…ing value nopSolutions#6954
  • Loading branch information
jafin committed Jan 18, 2024
1 parent d304790 commit 88846d5
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 18 deletions.
29 changes: 16 additions & 13 deletions src/Libraries/Nop.Services/Localization/LocalizationService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq.Expressions;
#nullable enable
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Xml;
Expand Down Expand Up @@ -225,7 +226,7 @@ public virtual async Task<LocaleStringResource> GetLocaleStringResourceByIdAsync
/// A task that represents the asynchronous operation
/// The task result contains the locale string resource
/// </returns>
public virtual async Task<LocaleStringResource> GetLocaleStringResourceByNameAsync(string resourceName, int languageId,
public virtual async Task<LocaleStringResource?> GetLocaleStringResourceByNameAsync(string resourceName, int languageId,
bool logIfNotFound = true)
{
var query = from lsr in _lsrRepository.Table
Expand All @@ -250,7 +251,7 @@ orderby lsr.ResourceName
/// <returns>
/// The locale string resource
/// </returns>
public virtual LocaleStringResource GetLocaleStringResourceByName(string resourceName, int languageId,
public virtual LocaleStringResource? GetLocaleStringResourceByName(string resourceName, int languageId,
bool logIfNotFound = true)
{
var query = from lsr in _lsrRepository.Table
Expand All @@ -273,9 +274,11 @@ orderby lsr.ResourceName
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task InsertLocaleStringResourceAsync(LocaleStringResource localeStringResource)
{
if (!string.IsNullOrEmpty(localeStringResource?.ResourceName))
localeStringResource.ResourceName = localeStringResource.ResourceName.Trim().ToLowerInvariant();
ArgumentNullException.ThrowIfNull(localeStringResource);

if (!string.IsNullOrEmpty(localeStringResource.ResourceName))
localeStringResource.ResourceName = localeStringResource.ResourceName.Trim().ToLowerInvariant();

await _lsrRepository.InsertAsync(localeStringResource);
}

Expand Down Expand Up @@ -364,7 +367,7 @@ orderby l.ResourceName
/// A task that represents the asynchronous operation
/// The task result contains a string representing the requested resource string.
/// </returns>
public virtual async Task<string> GetResourceAsync(string resourceKey)
public virtual async Task<string?> GetResourceAsync(string resourceKey)
{
var workingLanguage = await _workContext.GetWorkingLanguageAsync();

Expand All @@ -386,10 +389,10 @@ public virtual async Task<string> GetResourceAsync(string resourceKey)
/// A task that represents the asynchronous operation
/// The task result contains a string representing the requested resource string.
/// </returns>
public virtual async Task<string> GetResourceAsync(string resourceKey, int languageId,
public virtual async Task<string?> GetResourceAsync(string? resourceKey, int languageId,
bool logIfNotFound = true, string defaultValue = "", bool returnEmptyIfNotFound = false)
{
var result = string.Empty;
string? result = null;
resourceKey ??= string.Empty;
resourceKey = resourceKey.Trim().ToLowerInvariant();
if (_localizationSettings.LoadAllLocaleRecordsOnStartup)
Expand Down Expand Up @@ -418,7 +421,7 @@ public virtual async Task<string> GetResourceAsync(string resourceKey)
result = lsr;
}

if (!string.IsNullOrEmpty(result))
if (result != null)
return result;

if (logIfNotFound)
Expand Down Expand Up @@ -542,7 +545,7 @@ public virtual async Task ImportResourcesFromXmlAsync(Language language, StreamR
/// A task that represents the asynchronous operation
/// The task result contains the localized property
/// </returns>
public virtual async Task<TPropType> GetLocalizedAsync<TEntity, TPropType>(TEntity entity, Expression<Func<TEntity, TPropType>> keySelector,
public virtual async Task<TPropType?> GetLocalizedAsync<TEntity, TPropType>(TEntity entity, Expression<Func<TEntity, TPropType>> keySelector,
int? languageId = null, bool returnDefaultValue = true, bool ensureTwoPublishedLanguages = true)
where TEntity : BaseEntity, ILocalizedEntity
{
Expand Down Expand Up @@ -608,7 +611,7 @@ public virtual async Task ImportResourcesFromXmlAsync(Language language, StreamR
/// A task that represents the asynchronous operation
/// The task result contains the localized property
/// </returns>
public virtual async Task<string> GetLocalizedSettingAsync<TSettings>(TSettings settings, Expression<Func<TSettings, string>> keySelector,
public virtual async Task<string?> GetLocalizedSettingAsync<TSettings>(TSettings settings, Expression<Func<TSettings, string>> keySelector,
int languageId, int storeId, bool returnDefaultValue = true, bool ensureTwoPublishedLanguages = true)
where TSettings : ISettings, new()
{
Expand Down Expand Up @@ -758,7 +761,7 @@ public virtual async Task DeleteLocalizedPermissionNameAsync(PermissionRecord pe
/// <param name="resourceValue">Resource value</param>
/// <param name="languageCulture">Language culture code. If null or empty, then a resource will be added for all languages</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task AddOrUpdateLocaleResourceAsync(string resourceName, string resourceValue, string languageCulture = null)
public virtual async Task AddOrUpdateLocaleResourceAsync(string resourceName, string resourceValue, string? languageCulture = null)
{
foreach (var lang in await _languageService.GetAllLanguagesAsync(true))
{
Expand Down Expand Up @@ -934,7 +937,7 @@ public virtual async Task<string> GetLocalizedFriendlyNameAsync<TPlugin>(TPlugin
if (string.IsNullOrEmpty(result) && returnDefaultValue)
result = plugin.PluginDescriptor.FriendlyName;

return result;
return result!;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public Localizer T
_localizer ??= (format, args) =>
{
var resFormat = _localizationService.GetResourceAsync(format).Result;
if (string.IsNullOrEmpty(resFormat))
if (resFormat == null)
{
return new LocalizedString(format);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Nop.Web.Framework.Models;
using Nop.Web.Framework.Mvc;
using Nop.Web.Framework.Mvc.ModelBinding;

namespace Nop.Web.Areas.Admin.Models.Localization;
Expand All @@ -14,7 +15,8 @@ public partial record LocaleResourceModel : BaseNopEntityModel
public string ResourceName { get; set; }

[NopResourceDisplayName("Admin.Configuration.Languages.Resources.Fields.Value")]
public string ResourceValue { get; set; }
[NoTrim]
public string ResourceValue { get; set; } = string.Empty;

public int LanguageId { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public LanguageResourceValidator(ILocalizationService localizationService)
.WithMessageAwait(localizationService.GetResourceAsync("Admin.Configuration.Languages.Resources.Fields.Name.Required"));
RuleFor(model => model.ResourceValue)
.NotEmpty()
.NotNull()
.WithMessageAwait(localizationService.GetResourceAsync("Admin.Configuration.Languages.Resources.Fields.Value.Required"));
SetDatabaseValidationRules<LocaleStringResource>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public async Task AddOrUpdateLocaleResourceShouldSkipAlreadyExistsResources()
}

[Test]
public async Task AddOrUpdateLocaleResourceShouldApdateAllResorcesIfLangIdIsNull()
public async Task AddOrUpdateLocaleResourceShouldUpdateAllResourcesIfLangIdIsNull()
{
var languageService = GetService<ILanguageService>();
var language = new Language
Expand Down Expand Up @@ -152,7 +152,7 @@ public async Task CanDeleteLocaleResources()
}

[Test]
public async Task DeleteLocaleResourcesShuoldIgnoreCase()
public async Task DeleteLocaleResourcesShouldIgnoreCase()
{
await _localizationService.AddOrUpdateLocaleResourceAsync(_resources);

Expand All @@ -169,6 +169,16 @@ public async Task DeleteLocaleResourcesShuoldIgnoreCase()
rez.Count.Should().Be(0);
}

[TestCase("A Value")]
[TestCase("")] //empty string is valid value
public async Task CanGetResourceAsync(string val)
{
var resKey = $"{PREFIX}.CanGetResourceAsync";
await _localizationService.AddOrUpdateLocaleResourceAsync(resKey,val);
var resource = await _localizationService.GetResourceAsync(resKey);
resource.Should().Be(val);
}

public class LocaleResourceConsumer : IConsumer<EntityUpdatedEvent<LocaleStringResource>>
{
public static int UpdateCount { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using FluentValidation.Internal;
using FluentValidation.TestHelper;
using Moq;
using Nop.Services.Localization;
using Nop.Web.Areas.Admin.Models.Localization;
using Nop.Web.Areas.Admin.Validators.Localization;
using NUnit.Framework;

namespace Nop.Tests.Nop.Web.Tests.Public.Validators.Localization;

[TestFixture]
public class LanguageResourceValidatorTests : BaseNopTest
{
private LanguageResourceValidator _validator;

[SetUp]
public void SetUp()
{
var localizationServiceMock = new Mock<ILocalizationService>();
localizationServiceMock.Setup(x => x.GetResourceAsync(It.IsAny<string>())).ReturnsAsync("Mocked message");
_validator = new LanguageResourceValidator(localizationServiceMock.Object);
}

[Test]
public void ShouldHaveErrorWhenResourceNameIsEmpty()
{
var model = new LocaleResourceModel { ResourceName = string.Empty, ResourceValue = "Value" };
var result = _validator.TestValidate(model, DefaultValidationOptions());
result.ShouldHaveValidationErrorFor(x => x.ResourceName);
}

[Test]
public void ShouldNotHaveErrorWhenResourceNameIsSpecified()
{
var model = new LocaleResourceModel { ResourceName = "Name", ResourceValue = "Value" };
var result = _validator.TestValidate(model, DefaultValidationOptions());
result.ShouldNotHaveValidationErrorFor(x => x.ResourceName);
}

[Test]
public void ShouldHaveErrorWhenResourceValueIsNull()
{
var model = new LocaleResourceModel { ResourceName = "Name", ResourceValue = null };
var result = _validator.TestValidate(model, DefaultValidationOptions());
result.ShouldHaveValidationErrorFor(x => x.ResourceValue);
}

[Test]
public void ShouldNotHaveErrorWhenResourceValueIsSpecified()
{
var model = new LocaleResourceModel { ResourceName = "Name", ResourceValue = "Value" };
var result = _validator.TestValidate(model, DefaultValidationOptions());
result.ShouldNotHaveValidationErrorFor(x => x.ResourceValue);
}

private static Action<ValidationStrategy<LocaleResourceModel>> DefaultValidationOptions()
{
return options=>options.IncludeAllRuleSets();
}
}

0 comments on commit 88846d5

Please sign in to comment.