Skip to content

Commit 1b10299

Browse files
authored
Merge pull request aspnetboilerplate#5519 from KevinBassaDevelopment/enhancement/multi-lingual-mapping-fallback
Added multi-lingual object mapping language fallback from region-lang…
2 parents ed68960 + 3462980 commit 1b10299

File tree

3 files changed

+100
-9
lines changed

3 files changed

+100
-9
lines changed

src/Abp.AutoMapper/AutoMapper/AutoMapExtensions.cs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
using System;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Globalization;
34
using Abp.Configuration;
45
using Abp.Dependency;
56
using Abp.Domain.Entities;
67
using Abp.Localization;
78
using AutoMapper;
89
using System.Linq;
10+
using Abp.Collections.Extensions;
11+
using Abp.Extensions;
912

1013
namespace Abp.AutoMapper
1114
{
@@ -39,7 +42,7 @@ public static TDestination MapTo<TSource, TDestination>(this TSource source, TDe
3942
}
4043

4144
public static CreateMultiLingualMapResult<TMultiLingualEntity, TTranslation, TDestination> CreateMultiLingualMap<TMultiLingualEntity, TMultiLingualEntityPrimaryKey, TTranslation, TDestination>(
42-
this IMapperConfigurationExpression configuration, MultiLingualMapContext multiLingualMapContext)
45+
this IMapperConfigurationExpression configuration, MultiLingualMapContext multiLingualMapContext, bool fallbackToParentCultures = false)
4346
where TTranslation : class, IEntityTranslation<TMultiLingualEntity, TMultiLingualEntityPrimaryKey>
4447
where TMultiLingualEntity : IMultiLingualEntity<TTranslation>
4548
{
@@ -48,15 +51,29 @@ public static CreateMultiLingualMapResult<TMultiLingualEntity, TTranslation, TDe
4851
result.TranslationMap = configuration.CreateMap<TTranslation, TDestination>();
4952
result.EntityMap = configuration.CreateMap<TMultiLingualEntity, TDestination>().BeforeMap((source, destination, context) =>
5053
{
54+
if (source.Translations.IsNullOrEmpty())
55+
{
56+
return;
57+
}
58+
5159
var translation = source.Translations.FirstOrDefault(pt => pt.Language == CultureInfo.CurrentUICulture.Name);
5260
if (translation != null)
5361
{
5462
context.Mapper.Map(translation, destination);
5563
return;
5664
}
5765

58-
var defaultLanguage = multiLingualMapContext.SettingManager
59-
.GetSettingValue(LocalizationSettingNames.DefaultLanguage);
66+
if (fallbackToParentCultures)
67+
{
68+
translation = GeTranslationBasedOnCulturalRecursive<TMultiLingualEntity, TMultiLingualEntityPrimaryKey, TTranslation>(CultureInfo.CurrentUICulture.Parent, source.Translations, 0);
69+
if (translation != null)
70+
{
71+
context.Mapper.Map(translation, destination);
72+
return;
73+
}
74+
}
75+
76+
var defaultLanguage = multiLingualMapContext.SettingManager.GetSettingValue(LocalizationSettingNames.DefaultLanguage);
6077

6178
translation = source.Translations.FirstOrDefault(pt => pt.Language == defaultLanguage);
6279
if (translation != null)
@@ -75,11 +92,27 @@ public static CreateMultiLingualMapResult<TMultiLingualEntity, TTranslation, TDe
7592
return result;
7693
}
7794

78-
public static CreateMultiLingualMapResult<TMultiLingualEntity, TTranslation, TDestination> CreateMultiLingualMap<TMultiLingualEntity, TTranslation, TDestination>(this IMapperConfigurationExpression configuration, MultiLingualMapContext multiLingualMapContext)
95+
private const int MaxCultureFallbackDepth = 5;
96+
97+
private static TTranslation GeTranslationBasedOnCulturalRecursive<TMultiLingualEntity, TMultiLingualEntityPrimaryKey, TTranslation>(CultureInfo culture, ICollection<TTranslation> translations, int currentDepth)
98+
where TTranslation : class, IEntityTranslation<TMultiLingualEntity, TMultiLingualEntityPrimaryKey>
99+
{
100+
if (culture == null || culture.Name.IsNullOrWhiteSpace() || translations.IsNullOrEmpty() || currentDepth > MaxCultureFallbackDepth)
101+
{
102+
return null;
103+
}
104+
105+
var translation = translations.FirstOrDefault(pt => pt.Language.Equals(culture.Name, StringComparison.OrdinalIgnoreCase));
106+
return translation ?? GeTranslationBasedOnCulturalRecursive<TMultiLingualEntity, TMultiLingualEntityPrimaryKey, TTranslation>(culture.Parent, translations, currentDepth + 1);
107+
}
108+
109+
public static CreateMultiLingualMapResult<TMultiLingualEntity, TTranslation, TDestination> CreateMultiLingualMap<TMultiLingualEntity, TTranslation, TDestination>(this IMapperConfigurationExpression configuration,
110+
MultiLingualMapContext multiLingualMapContext,
111+
bool fallbackToParentCultures = false)
79112
where TTranslation : class, IEntity, IEntityTranslation<TMultiLingualEntity, int>
80113
where TMultiLingualEntity : IMultiLingualEntity<TTranslation>
81114
{
82-
return configuration.CreateMultiLingualMap<TMultiLingualEntity, int, TTranslation, TDestination>(multiLingualMapContext);
115+
return configuration.CreateMultiLingualMap<TMultiLingualEntity, int, TTranslation, TDestination>(multiLingualMapContext, fallbackToParentCultures);
83116
}
84117
}
85118
}

test/Abp.ZeroCore.SampleApp/AbpZeroCoreSampleAppModule.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ internal static class CustomDtoMapper
5959
{
6060
public static void CreateMappings(IMapperConfigurationExpression configuration, MultiLingualMapContext context)
6161
{
62-
configuration.CreateMultiLingualMap<Product, ProductTranslation, ProductListDto>(context);
62+
configuration.CreateMultiLingualMap<Product, ProductTranslation, ProductListDto>(context, true);
6363

6464
configuration.CreateMap<ProductCreateDto, Product>();
6565
configuration.CreateMap<ProductUpdateDto, Product>();
6666

6767
configuration.CreateMap<ProductTranslationDto, ProductTranslation>();
6868

69-
configuration.CreateMultiLingualMap<Order, OrderTranslation, OrderListDto>(context)
69+
configuration.CreateMultiLingualMap<Order, OrderTranslation, OrderListDto>(context, true)
7070
.EntityMap.ForMember(dest => dest.ProductCount, opt => opt.MapFrom(src => src.Products.Count));
7171
}
7272
}

test/Abp.ZeroCore.Tests/Zero/MultiLingual/MultiLingual_Mapping_Tests.cs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,64 @@ public async Task CreateMultiLingualMap_Tests()
6262
product3.Name.ShouldBe("Giornale");
6363
}
6464

65+
[Fact]
66+
public async Task CreateMultiLingualMap_RegionSpecificLanguage_ShouldFallbackToGlobalLanguage()
67+
{
68+
CultureInfo.CurrentUICulture = new CultureInfo("en-GB");
69+
70+
var products = await _productAppService.GetProducts();
71+
products.ShouldNotBeNull();
72+
73+
products.Items.Count.ShouldBe(3);
74+
var product1 = products.Items[0];
75+
var product2 = products.Items[1];
76+
var product3 = products.Items[2];
77+
78+
product1.Language.ShouldBe("en");
79+
product1.Name.ShouldBe("Watch");
80+
81+
product2.Language.ShouldBe("en");
82+
product2.Name.ShouldBe("Bike");
83+
84+
product3.Language.ShouldBe("it");
85+
product3.Name.ShouldBe("Giornale");
86+
}
87+
88+
[Fact]
89+
public async Task CreateMultiLingualMap_NoTranslations_ShouldStillMapObject()
90+
{
91+
/*
92+
* When no translation is available, it should still map the
93+
*/
94+
95+
UsingDbContext(context =>
96+
{
97+
context.ProductTranslations.RemoveRange(context.ProductTranslations.ToList());
98+
});
99+
100+
CultureInfo.CurrentUICulture = new CultureInfo("en-GB");
101+
102+
var products = await _productAppService.GetProducts();
103+
products.ShouldNotBeNull();
104+
105+
products.Items.Count.ShouldBe(3);
106+
var product1 = products.Items[0];
107+
var product2 = products.Items[1];
108+
var product3 = products.Items[2];
109+
110+
product1.Language.ShouldBe(null);
111+
product1.Name.ShouldBe(null);
112+
product1.Price.ShouldBe(10);
113+
114+
product2.Language.ShouldBe(null);
115+
product2.Name.ShouldBe(null);
116+
product2.Price.ShouldBe(99);
117+
118+
product3.Language.ShouldBe(null);
119+
product3.Name.ShouldBe(null);
120+
product3.Price.ShouldBe(15);
121+
}
122+
65123
[Fact]
66124
public async Task CreateMultiLingualMap_Tests_Dont_Override_MultiLingual_Entity_Id()
67125
{
@@ -94,4 +152,4 @@ public async Task Allow_Modifying_CreateMultiLingualMap_Mapping()
94152
testOrder.ProductCount.ShouldBe(3);
95153
}
96154
}
97-
}
155+
}

0 commit comments

Comments
 (0)