wydy
wydy

Reputation: 183

Localization of DB Entries

I'm currently working with a project where you store a list of products in the database. Every product has a name, description... Now I want to translate the productname, description...

My current solution is to use a translation table, where I can add all the translated db entries like:

product.Title= result.GetLocalized(x => x.Title, language, _localizedPropertyRepository);

and the extenstion method:

public static string GetLocalized<T>(this T entity,
            Expression<Func<T, string>> keySelector, LanguageDTO language, ILocalizedPropertyRepository localizedPropertyRepository)
            where T : Mapping, ILocalizedEntity
        {
...
            resultStr = localizedPropertyRepository.GetLocalizedValue(language, entity.Id, localeKeyGroup, localeKey);
...
        }

You can also see a similar example of it here: https://github.com/nopSolutions/nopCommerce/blob/develop/src/Libraries/Nop.Services/Localization/LocalizationExtensions.cs

My problem is, I'm not really happy with it. To work with an static class so I have access to the Context or send the whole injected object in the extension method doesn't look like a good practice. Are there other ways to solve this problem?

Also I work with AutoMapper where I map the whole Entity to a DTO. I think the current solution can't work with AutoMapper. I have to make something like:

CreateMap<Product, ProductDTO>()
                .ForMember(x => x.Title, opt => opt.???GetLocalized(????));

But I need the entity in GetLocalized and not only the Property. Also I don't have access to the _localizedPropertyRepository or the language of the customer.

Upvotes: 1

Views: 797

Answers (1)

wydy
wydy

Reputation: 183

Well it took me over a day, but I got a working solution. So if someone else is stuck with the same problem, here is my solution. I hope it helps:

public class Mapping
{
    [DataMember]
    public long Id { get; set; }
}

[DataContract]
public class ProductDTO: Mapping
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public string ShortDescription{ get; set; }
}

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Product, ProductDTO>()
            .ForMember(t => t.Name, opt => opt.ResolveUsing<LocalizationResolver, EntityInfo>(src => new EntityInfo()
            {
                LocaleKeyGroup = "Product",
                LocaleKey = "Name",
                DefaultValue  = src.Name
            }))
            .ForMember(t => t.ShortDescription, opt => opt.ResolveUsing<LocalizationResolver, EntityInfo> (src => new EntityInfo()
            {
                LocaleKeyGroup = "Product",
                LocaleKey = "ShortDescription",
                DefaultValue = src.ShortDescription
            }));
    }
}

public class EntityInfo
{
    public string LocaleKeyGroup { get; set; }
    public string LocaleKey { get; set; }
    public string DefaultValue { get; set; }
}

public class LocalizationResolver : IMemberValueResolver<Mapping, object, EntityInfo, string>
{
    public string Resolve(Mapping source, object destination, EntityInfo sourceMember, string destMember,
        ResolutionContext context)
    {

        context.Items.TryGetValue("Language", out object languageObject);
        context.Items.TryGetValue("Repository", out object repositoryObject);

        ILocalizedPropertyRepository repository = repositoryObject as ILocalizedPropertyRepository;
        LanguageDTO language = languageObject as LanguageDTO;

        if (language == null || repository == null)
        {
            throw new ArgumentNullException($"Language and LocalizationRepository as AutoMapper Parameter");
        }      

        if(source.Id == 0)
        {
            return sourceMember.DefaultValue;
        }
        //Get the value from the DB
        return LocalizationExtension.GetLocalized(sourceMember.LocaleKey, sourceMember.LocaleKeyGroup, sourceMember.DefaultValue, source.Id, language, repository);
    }
}

public static class LocalizationExtension
{
    public static string GetLocalized(string localeKey, string localeKeyGroup, string defaultValue, long entityId, LanguageDTO language, ILocalizedPropertyRepository localizedPropertyRepository)
    {
        var resultStr = String.Empty;

        if (language != null)
        {
            resultStr = localizedPropertyRepository.GetLocalizedValue(language, entityId, localeKeyGroup, localeKey);
        }

        if (string.IsNullOrEmpty(resultStr))
        {
            resultStr = defaultValue;
        }

        return resultStr;
    }

    public static void Localice(IMappingOperationOptions<object, object> opt, LanguageDTO language, ILocalizedPropertyRepository localizedPropertyRepository)
    {
        opt.Items["Language"] = language;
        opt.Items["Repository"] = localizedPropertyRepository;
    }
}

There is still stuff to optimize. The method names aren't perfect. The creation of the EntityInfo is ugly. And I still have the use the whole repository object as a parameter because of the dependency injection. Also, it tries to get a localized value everytime, which is necessary in the "default" language and slows the whole stuff down a bit.

Upvotes: 1

Related Questions