Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi Lingual entities new system #1832

Closed
hitaspdotnet opened this issue Oct 2, 2019 · 4 comments
Closed

Multi Lingual entities new system #1832

hitaspdotnet opened this issue Oct 2, 2019 · 4 comments
Labels

Comments

@hitaspdotnet
Copy link
Contributor

hitaspdotnet commented Oct 2, 2019

I have some problems on working with Multi lingual entities previous ABP version, where my model has required translatable field and where I need origin input without translation (or default app culture and skip current culture). So, what you think if we do little changes for passing this cases:

    public interface ITranslatable<T, TTranslation>
    {
        IDictionary<string, TTranslation> Translations { get; set; }
        T Translate(string culture);
    }

    public class Translation
    {
        [Required]
        public string Culture { get; set; }
        public IDictionary<string, string> Metadata { get; set; }
    }

    public class BlogTranslation : Translation
    {
        [Required]
        public string Title { get; set; }
        [Required]
        public string Content { get; set; }
    }

    public class Blog : Entity, ITranslatable<Blog, BlogTranslation>
    {
        [Required]
        public string Title { get; set; }
        [Required]
        public string Content { get; set; }
        public IDictionary<string, BlogTranslation> Translations { get; set; }

        public Blog Translate(string culture)
        {
            if (Translations != null && !string.IsNullOrEmpty(culture))
            {
                if (Translations.TryGetValue(culture, out BlogTranslation translation))
                {
                    Title = string.IsNullOrEmpty(translation.Title) ? Title : translation.Title;
                    Content = string.IsNullOrEmpty(translation.Content) ? Content : translation.Content;
                }
            }
            return this;
        }
    }
@hitaspdotnet
Copy link
Contributor Author

Sample code test results:
Works well with MongoDB.
Not working with EFCore. (Dictionary objects map error)

@maliming
Copy link
Member

maliming commented Oct 6, 2019

Maybe you can refer to the EF Core mapping of ExtraProperties.

@hitaspdotnet
Copy link
Contributor Author

hitaspdotnet commented Oct 8, 2019

@maliming Thanks for helpful code line hint
Can I have PR for this? @hikalkan

    public interface ITranslation
    {
        string Language { get; set; }
    }

    public interface ITranslatable<TTranslation> where TTranslation : class, ITranslation
    {
        IDictionary<string, TTranslation> Translations { get; set; }
    }

    public static class AutoMapExtensions
    {
        public static CreateMultiLingualMapResult<TTranslatableEntity, TTranslation, TDestination>
            CreateTranslatableMap<TTranslatableEntity, TTranslation, TDestination>(
                this IMapperConfigurationExpression configuration, MultiLingualMapContext multiLingualMapContext)
            where TTranslatableEntity : ITranslatable<TTranslation>
            where TTranslation : class, ITranslation
        {
            return new CreateMultiLingualMapResult<TTranslatableEntity, TTranslation, TDestination>
            {
                TranslationMap = configuration.CreateMap<TTranslation, TDestination>(),
                EntityMap = configuration.CreateMap<TTranslatableEntity, TDestination>().AfterMap(
                    (source, destination, context) =>
                    {
                        if (source.Translations == null)
                        {
                            return;
                        }

                        if (source.Translations.TryGetValue(CultureInfo.CurrentUICulture.Name,
                            out TTranslation translation))
                        {
                            if (translation != null)
                            {
                                context.Mapper.Map(translation, destination, opts => opts.ConfigureMap()
                                    .ForAllMembers(cfg => cfg.Condition((src, dest, srcMember) => srcMember != null)));
                                return;
                            }
                        }

                        var defaultLanguage = multiLingualMapContext.SettingManager
                            .GetSettingValue(LocalizationSettingNames.DefaultLanguage);

                        if (source.Translations.TryGetValue(defaultLanguage, out translation))
                        {
                            if (translation != null)
                            {
                                context.Mapper.Map(translation, destination, opts => opts.ConfigureMap()
                                    .ForAllMembers(cfg => cfg.Condition((src, dest, srcMember) => srcMember != null)));
                                return;
                            }
                        }

                        translation = source.Translations.FirstOrDefault().Value;
                        if (translation != null)
                        {
                            context.Mapper.Map(translation, destination, opts => opts.ConfigureMap()
                                .ForAllMembers(cfg => cfg.Condition((src, dest, srcMember) => srcMember != null)));
                        }
                    }
                )
            };
        }
    }

    protected virtual void  ConfigureTranslationsProperty<TTranslatableEntity, TTranslation>(ModelBuilder builder)
        where TTranslatableEntity : class, ITranslatable<TTranslation>
        where TTranslation : class, ITranslation
    {
        if (!typeof(ITranslatable<TTranslation>).IsAssignableFrom(typeof(TTranslatableEntity)))
        {
            return;
        }

        builder.Entity<TTranslatableEntity>(b =>
        {
            b.Property(x => ((ITranslatable<TTranslation>) x).Translations)
                .HasConversion(
                    d => d.Count == 0
                        ? null 
                        : JsonConvert.SerializeObject(d, Formatting.None),
                    s => s == null
                        ? new Dictionary<string, TTranslation>()
                        : JsonConvert.DeserializeObject<Dictionary<string, TTranslation>>(s)
                );
        });
    }

This code works well in ABZ. Is 17 times faster than old-multi-lingual-entity in my ABZ. With this style we can skip additional translations table and we don't need to care about required translatable properties.
Of curse we can add validator in client same as I do before.
You need to get current culture and set that language's input form input for translatable fields as first and set them as required! ooops.

For who has my problem:

    <tabset class="tab-container tabbable-line">
        <div class="form-group">
            <label for="Category_Name">{{l("Name")}} *</label>
            <input type="text" id="Category_Name" class="form-control" [(ngModel)]="category.name" name="Name" required />
        </div>
        <tab *ngFor="let translation of translations; let i=index" customClass="m-tabs__item">
            <ng-template tabHeading><i [class]="languages[i].icon"></i>{{languages[i].displayName}}</ng-template>
            <div class="form-group">
                <label for="Category_DisplayName_{{i}}">{{l("DisplayName")}} *</label>
                <input type="text" [required]="i === 0 ? 'required' : null" id="Category_DisplayName_{{i}}" class="form-control" [(ngModel)]="translation.displayName" name="DisplayName_{{i}}" />
            </div>
        </tab>
    </tabset>
this.currentLanguage = this.localization.currentLanguage;
    this.languages = _.filter(this.localization.languages, l => (l).isDisabled === false);

    _.remove(this.languages, lng => lng.name === this.currentLanguage.name);

    this.languages.splice(0, 0, this.currentLanguage);

    this.category = new CreateOrEditCategoryDto();
    this.category.translations = [];
    this.category.id = this.categoryId;

    for (let lng of this.languages) {
        let translation = new CategoryTranslationDto();
        translation.language = (lng).name;
        this.translations.push(translation);

    this.active = true;
}

@stale
Copy link

stale bot commented Jul 29, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants