17 changed files with 325 additions and 240 deletions
@ -0,0 +1,93 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public abstract class AggregateRemoteServiceBase<TDto> : IAggregateRemoteService<TDto> |
|||
{ |
|||
private readonly ILogger<AggregateRemoteServiceBase<TDto>> _logger; |
|||
protected JsonSerializerOptions JsonSerializerOptions { get; } |
|||
|
|||
protected AggregateRemoteServiceBase(ILogger<AggregateRemoteServiceBase<TDto>> logger) |
|||
{ |
|||
_logger = logger; |
|||
JsonSerializerOptions = new JsonSerializerOptions() |
|||
{ |
|||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase |
|||
}; |
|||
} |
|||
|
|||
public async Task<Dictionary<string, TDto>> GetMultipleAsync( |
|||
Dictionary<string, string> serviceNameWithUrlDictionary) |
|||
{ |
|||
Dictionary<string, Task<TDto>> completedTasks = new Dictionary<string, Task<TDto>>(); |
|||
Dictionary<string, Task<TDto>> runningTasks = new Dictionary<string, Task<TDto>>(); |
|||
Dictionary<string, TDto> completedResult = new Dictionary<string, TDto>(); |
|||
|
|||
using (HttpClient httpClient = new HttpClient()) |
|||
{ |
|||
foreach (var service in serviceNameWithUrlDictionary) |
|||
{ |
|||
Task<TDto> requestTask = |
|||
MakeRequestAsync<TDto>(httpClient, service.Value); |
|||
runningTasks.Add(service.Key, requestTask); |
|||
} |
|||
|
|||
while (runningTasks.Count > 0) |
|||
{ |
|||
KeyValuePair<string, Task<TDto>> completedTask = await WaitForAnyTaskAsync(runningTasks); |
|||
|
|||
runningTasks.Remove(completedTask.Key); |
|||
|
|||
try |
|||
{ |
|||
TDto result = await completedTask.Value; |
|||
|
|||
completedTasks.Add(completedTask.Key, completedTask.Value); |
|||
completedResult.Add(completedTask.Key, result); |
|||
|
|||
_logger.LogInformation($"Localization Key: {completedTask.Key}, Value: {result}"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogInformation($"Error for the {completedTask.Key}: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return completedResult; |
|||
} |
|||
|
|||
public async Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url) |
|||
{ |
|||
try |
|||
{ |
|||
HttpResponseMessage response = await httpClient.GetAsync(url); |
|||
response.EnsureSuccessStatusCode(); |
|||
|
|||
var content = await response.Content.ReadAsStringAsync(); |
|||
return JsonSerializer.Deserialize<T>(content, JsonSerializerOptions); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
_logger.LogInformation($"Error making request to {url}: {e.Message}"); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
public async Task<KeyValuePair<TKey, Task<TValue>>> WaitForAnyTaskAsync<TKey, TValue>( |
|||
Dictionary<TKey, Task<TValue>> tasks) |
|||
{ |
|||
var completedTask = Task.WhenAny(tasks.Values); |
|||
var result = await completedTask; |
|||
|
|||
var completedTaskPair = tasks.First(kv => kv.Value == result); |
|||
|
|||
return completedTaskPair; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public abstract class AggregateServiceBase<TDto> |
|||
{ |
|||
private readonly IAggregateRemoteService<TDto> _remoteService; |
|||
|
|||
public AggregateServiceBase(IAggregateRemoteService<TDto> remoteService) |
|||
{ |
|||
_remoteService = remoteService; |
|||
} |
|||
|
|||
public virtual async Task<Dictionary<string, TDto>> GetMultipleFromRemoteAsync(List<string> missingKeys, |
|||
Dictionary<string, string> endpoints) |
|||
{ |
|||
return await _remoteService |
|||
.GetMultipleAsync(endpoints |
|||
.Where(kv => missingKeys.Contains(kv.Key)) |
|||
.ToDictionary(k => k.Key, v => v.Value)); |
|||
} |
|||
|
|||
public List<string> GetMissingServiceKeys( |
|||
IDictionary<string, TDto> serviceNamesWithData, |
|||
Dictionary<string, string> serviceNamesWithUrls) |
|||
{ |
|||
List<string> missingKeysInCache = serviceNamesWithUrls.Keys.Except(serviceNamesWithData.Keys).ToList(); |
|||
List<string> missingKeysInUrls = serviceNamesWithData.Keys.Except(serviceNamesWithUrls.Keys).ToList(); |
|||
|
|||
return missingKeysInCache.Concat(missingKeysInUrls).ToList(); |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public abstract class CachedServiceBase<TCacheValue> : ICachedServiceBase<TCacheValue> |
|||
{ |
|||
private readonly IMemoryCache _cache; |
|||
|
|||
protected MemoryCacheEntryOptions CacheEntryOptions { get; } = new() |
|||
{ |
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24), |
|||
SlidingExpiration = TimeSpan.FromHours(4) |
|||
}; |
|||
|
|||
protected CachedServiceBase(IMemoryCache cache) |
|||
{ |
|||
_cache = cache ?? throw new ArgumentNullException(nameof(cache)); |
|||
} |
|||
|
|||
public void Add(string serviceName, TCacheValue data) |
|||
{ |
|||
_cache.Set(serviceName, data, CacheEntryOptions); |
|||
} |
|||
|
|||
public IDictionary<string, TCacheValue> GetManyAsync(IEnumerable<string> serviceNames) |
|||
{ |
|||
var result = new Dictionary<string, TCacheValue>(); |
|||
|
|||
foreach (var serviceName in serviceNames) |
|||
{ |
|||
if (_cache.TryGetValue(serviceName, out TCacheValue data)) |
|||
{ |
|||
result.Add(serviceName, data); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public interface IAggregateRemoteService<TDto> |
|||
{ |
|||
Task<Dictionary<string, TDto>> GetMultipleAsync(Dictionary<string, string> serviceNameWithUrlDictionary); |
|||
Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url); |
|||
Task<KeyValuePair<TKey, Task<TValue>>> WaitForAnyTaskAsync<TKey, TValue>(Dictionary<TKey, Task<TValue>> tasks); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public interface ICachedServiceBase<TValue> |
|||
{ |
|||
void Add(string serviceName, TValue data); |
|||
IDictionary<string, TValue> GetManyAsync(IEnumerable<string> serviceNames); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public interface IRequestInput |
|||
{ |
|||
Dictionary<string, string> Endpoints { get; } |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations; |
|||
|
|||
public interface ILocalizationAggregation : ITransientDependency |
|||
{ |
|||
public string LocalizationRouteName { get; } |
|||
public string LocalizationEndpoint { get; } |
|||
public Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input); |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class CachedLocalizationService : ISingletonDependency |
|||
{ |
|||
protected static ConcurrentDictionary<string, ApplicationLocalizationDto> LocalizationDictionary { get; private set; } |
|||
|
|||
public CachedLocalizationService() |
|||
{ |
|||
LocalizationDictionary = new ConcurrentDictionary<string, ApplicationLocalizationDto>(StringComparer.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
public void AddOrUpdate(string serviceNameWithCulture, ApplicationLocalizationDto localizationInfo) |
|||
{ |
|||
LocalizationDictionary.AddOrUpdate(serviceNameWithCulture, localizationInfo, (key, value) => localizationInfo); |
|||
} |
|||
|
|||
public IDictionary<string, ApplicationLocalizationDto> GetLocalizationsFromCacheAsync(string[] serviceNamesWithCulture) |
|||
{ |
|||
return LocalizationDictionary |
|||
.Where(kv => serviceNamesWithCulture.Contains(kv.Key)) |
|||
.ToDictionary(); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public interface ILocalizationAggregation |
|||
{ |
|||
string RouteName { get; } |
|||
string Endpoint { get; } |
|||
Task<ApplicationLocalizationDto> GetAsync(LocalizationRequest input); |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public interface ILocalizationRemoteService : IAggregateRemoteService<ApplicationLocalizationDto>; |
|||
@ -1,10 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public interface IRemoteLocalizationService |
|||
{ |
|||
Task<Dictionary<string, ApplicationLocalizationDto>> GetMultipleLocalizationsAsync(Dictionary<string, string> serviceNameWithUrlDictionary); |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationAggregation : AggregateServiceBase<ApplicationLocalizationDto>, ILocalizationAggregation, |
|||
ITransientDependency |
|||
{ |
|||
public string RouteName => "EshopOnAbpLocalization"; |
|||
public string Endpoint => "api/abp/application-localization"; |
|||
protected LocalizationCachedService LocalizationCachedService { get; } |
|||
|
|||
public LocalizationAggregation( |
|||
LocalizationCachedService localizationCachedService, |
|||
ILocalizationRemoteService localizationRemoteService) |
|||
: base(localizationRemoteService) |
|||
{ |
|||
LocalizationCachedService = localizationCachedService; |
|||
} |
|||
|
|||
public async Task<ApplicationLocalizationDto> GetAsync(LocalizationRequest input) |
|||
{ |
|||
// Check the cache service
|
|||
var cachedLocalization = LocalizationCachedService |
|||
.GetManyAsync(input.Endpoints.Keys.ToArray()); |
|||
|
|||
// Compare cache with input service list
|
|||
var missingLocalizationKeys = GetMissingServiceKeys(cachedLocalization, input.Endpoints); |
|||
|
|||
if (missingLocalizationKeys.Count != 0) |
|||
{ |
|||
// Make request to remote localization service to get missing localizations
|
|||
var remoteLocalizationResults = |
|||
await GetMultipleFromRemoteAsync(missingLocalizationKeys, input.Endpoints); |
|||
|
|||
// Update localization cache
|
|||
foreach (var result in remoteLocalizationResults) |
|||
{ |
|||
LocalizationCachedService.Add(result.Key, result.Value); |
|||
} |
|||
|
|||
cachedLocalization = LocalizationCachedService |
|||
.GetManyAsync(input.Endpoints.Keys.ToArray()); |
|||
} |
|||
|
|||
//merge result
|
|||
var mergedResult = MergeLocalizationData(cachedLocalization); |
|||
|
|||
//return result
|
|||
return mergedResult; |
|||
} |
|||
|
|||
private static ApplicationLocalizationDto MergeLocalizationData( |
|||
IDictionary<string, ApplicationLocalizationDto> resourceDictionary) |
|||
{ |
|||
var localizationDto = new ApplicationLocalizationDto(); |
|||
|
|||
foreach (var localization in resourceDictionary) |
|||
{ |
|||
foreach (var resource in localization.Value.Resources) |
|||
{ |
|||
localizationDto.Resources.TryAdd(resource.Key, resource.Value); |
|||
} |
|||
} |
|||
|
|||
return localizationDto; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationCachedService(IMemoryCache localizationCache) |
|||
: CachedServiceBase<ApplicationLocalizationDto>(localizationCache), ISingletonDependency; |
|||
@ -0,0 +1,15 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationRemoteService : AggregateRemoteServiceBase<ApplicationLocalizationDto>, |
|||
ILocalizationRemoteService, ITransientDependency |
|||
{ |
|||
public LocalizationRemoteService(ILogger<AggregateRemoteServiceBase<ApplicationLocalizationDto>> logger) : |
|||
base(logger) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationRequest : IRequestInput |
|||
{ |
|||
public Dictionary<string, string> Endpoints { get; } = new(); |
|||
public string CultureName { get; set; } |
|||
|
|||
public LocalizationRequest(string cultureName) |
|||
{ |
|||
CultureName = cultureName; |
|||
} |
|||
} |
|||
@ -1,83 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class RemoteLocalizationService : IRemoteLocalizationService, ITransientDependency |
|||
{ |
|||
private readonly ILogger<RemoteLocalizationService> _logger; |
|||
|
|||
public RemoteLocalizationService(ILogger<RemoteLocalizationService> logger) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
public async Task<Dictionary<string, ApplicationLocalizationDto>> GetMultipleLocalizationsAsync(Dictionary<string, string> serviceNameWithUrlDictionary) |
|||
{ |
|||
Dictionary<string, Task<ApplicationLocalizationDto>> completedTasks = new Dictionary<string, Task<ApplicationLocalizationDto>>(); |
|||
Dictionary<string, Task<ApplicationLocalizationDto>> runningTasks = new Dictionary<string, Task<ApplicationLocalizationDto>>(); |
|||
Dictionary<string, ApplicationLocalizationDto> completedResult = new Dictionary<string, ApplicationLocalizationDto>(); |
|||
|
|||
using (HttpClient httpClient = new HttpClient()) |
|||
{ |
|||
foreach (var service in serviceNameWithUrlDictionary) |
|||
{ |
|||
Task<ApplicationLocalizationDto> requestTask = MakeRequestAsync<ApplicationLocalizationDto>(httpClient, service.Value); |
|||
runningTasks.Add(service.Key, requestTask); |
|||
} |
|||
|
|||
while (runningTasks.Count > 0) |
|||
{ |
|||
KeyValuePair<string, Task<ApplicationLocalizationDto>> completedTask = await WaitForAnyTaskAsync(runningTasks); |
|||
|
|||
runningTasks.Remove(completedTask.Key); |
|||
|
|||
try |
|||
{ |
|||
ApplicationLocalizationDto result = await completedTask.Value; |
|||
|
|||
completedTasks.Add(completedTask.Key, completedTask.Value); |
|||
completedResult.Add(completedTask.Key, result); |
|||
|
|||
_logger.LogInformation($"Localization Key: {completedTask.Key}, Value: {result}"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogInformation($"Error for the {completedTask.Key}: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return completedResult; |
|||
} |
|||
|
|||
private async Task<KeyValuePair<string, Task<ApplicationLocalizationDto>>> WaitForAnyTaskAsync(Dictionary<string, Task<ApplicationLocalizationDto>> tasks) |
|||
{ |
|||
Task<Task<ApplicationLocalizationDto>> completedTask = Task.WhenAny(tasks.Values); |
|||
Task<ApplicationLocalizationDto> result = await completedTask; |
|||
|
|||
KeyValuePair<string, Task<ApplicationLocalizationDto>> completedTaskPair = tasks.First(kv => kv.Value == result); |
|||
|
|||
return completedTaskPair; |
|||
} |
|||
|
|||
private async Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url) |
|||
{ |
|||
HttpResponseMessage response = await httpClient.GetAsync(url); |
|||
response.EnsureSuccessStatusCode(); |
|||
|
|||
string content = await response.Content.ReadAsStringAsync(); |
|||
var options = new JsonSerializerOptions |
|||
{ |
|||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, |
|||
}; |
|||
return JsonSerializer.Deserialize<T>(content, options); |
|||
} |
|||
} |
|||
@ -1,105 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations; |
|||
|
|||
public class LocalizationAggregation : ILocalizationAggregation |
|||
{ |
|||
//Yarp localization route name
|
|||
public string LocalizationRouteName { get; } = "EshopOnAbpLocalization"; |
|||
public string LocalizationEndpoint { get; } = "api/abp/application-localization"; |
|||
|
|||
private readonly CachedLocalizationService _cachedLocalizationService; |
|||
private readonly IRemoteLocalizationService _remoteLocalizationService; |
|||
|
|||
public LocalizationAggregation(CachedLocalizationService cachedLocalizationService, |
|||
IRemoteLocalizationService remoteLocalizationService) |
|||
{ |
|||
_cachedLocalizationService = cachedLocalizationService; |
|||
_remoteLocalizationService = remoteLocalizationService; |
|||
} |
|||
|
|||
|
|||
public async Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input) |
|||
{ |
|||
// Check the cache service
|
|||
var cachedLocalization = _cachedLocalizationService |
|||
.GetLocalizationsFromCacheAsync(input.LocalizationEndpoints.Keys.ToArray()); |
|||
|
|||
// Compare cache with input service list
|
|||
var missingLocalizationKeys = GetMissingLocalizations(cachedLocalization, input.LocalizationEndpoints); |
|||
|
|||
if (missingLocalizationKeys.Count != 0) |
|||
{ |
|||
// Make request to remote localization service to get missing localizations
|
|||
var remoteLocalizationResults = |
|||
await GetLocalizationsFromRemoteAsync(missingLocalizationKeys, input.LocalizationEndpoints); |
|||
|
|||
// Update localization cache
|
|||
foreach (var result in remoteLocalizationResults) |
|||
{ |
|||
_cachedLocalizationService.AddOrUpdate(result.Key, result.Value); |
|||
} |
|||
|
|||
cachedLocalization = _cachedLocalizationService |
|||
.GetLocalizationsFromCacheAsync(input.LocalizationEndpoints.Keys.ToArray()); |
|||
} |
|||
|
|||
//merge result
|
|||
ApplicationLocalizationDto mergedResult = MergeLocalizations(cachedLocalization); |
|||
|
|||
//return result
|
|||
return mergedResult; |
|||
} |
|||
|
|||
private async Task<Dictionary<string, ApplicationLocalizationDto>> GetLocalizationsFromRemoteAsync( |
|||
List<string> missingLocalizationKeys, Dictionary<string, string> localizationEndpoints) |
|||
{ |
|||
return await _remoteLocalizationService |
|||
.GetMultipleLocalizationsAsync(localizationEndpoints |
|||
.Where(kv => missingLocalizationKeys.Contains(kv.Key)) |
|||
.ToDictionary(k => k.Key, v => v.Value)); |
|||
} |
|||
|
|||
private List<string> GetMissingLocalizations( |
|||
IDictionary<string, ApplicationLocalizationDto> serviceNameWithLocalization, |
|||
Dictionary<string, string> serviceNameWithUrls) |
|||
{ |
|||
List<string> missingKeysInCache = serviceNameWithUrls.Keys.Except(serviceNameWithLocalization.Keys).ToList(); |
|||
List<string> missingKeysInUrls = serviceNameWithLocalization.Keys.Except(serviceNameWithUrls.Keys).ToList(); |
|||
|
|||
return missingKeysInCache.Concat(missingKeysInUrls).ToList(); |
|||
} |
|||
|
|||
private ApplicationLocalizationDto MergeLocalizations( |
|||
IDictionary<string, ApplicationLocalizationDto> localizationResults) |
|||
{ |
|||
var localizationDto = new ApplicationLocalizationDto(); |
|||
|
|||
foreach (var localization in localizationResults) |
|||
{ |
|||
foreach (var resource in localization.Value.Resources) |
|||
{ |
|||
localizationDto.Resources.TryAdd(resource.Key, resource.Value); |
|||
} |
|||
} |
|||
|
|||
return localizationDto; |
|||
} |
|||
} |
|||
|
|||
public class LocalizationRequest |
|||
{ |
|||
// Localization clusterName - localization endpoint pair
|
|||
public Dictionary<string, string> LocalizationEndpoints { get; } |
|||
public string CultureName { get; set; } |
|||
|
|||
public LocalizationRequest(string cultureName) |
|||
{ |
|||
CultureName = cultureName; |
|||
LocalizationEndpoints = new Dictionary<string, string>(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue