fingers10 / JqueryDataTablesServerSide

Asp.Net Core Server Side for Jquery DataTables Multiple Column Filtering and Sorting with Pagination and Excel Export
MIT License
227 stars 37 forks source link

Localized JqueryDataTablesTagHelper:TagHelper #16

Closed ElChepos closed 4 years ago

ElChepos commented 5 years ago

Hi,

it more a question than a bug.

The software that i'm building have multiple language. I use the attribute tag [DisplayName("Type")]. When I render the table manually with @Html.DisplayFor the text is localized. But when I use your tag helper it not.

In your tag helper line 60 headerRow.AppendLine($"{column.Name}");is it possible to use column.DisplayName ? Or maybe we can implement the Localized directly in that class too.

What do you think ?

Best,

fingers10 commented 5 years ago

Hey jptoros,

Nice idea. I'll make sure to add this in the next release. If you have any ideas / PR. Your welcome to submit. Please add a star to my works if you think this deserves.

Thanks, Abdul

fingers10 commented 5 years ago

@jptoros Right Now if you decorate the property with [DisplayName("Your Localized String")] to the properties in your view model, it works. Can you check from your end to see of that is useful for you?

Anyways I'll be releasing a patch tonight to include [Display(Name = "Your Localized String")] attribute as well.

Here is the order of precedence. The TagHelper will process the name with the below priority.

  1. [Display(Name = "Your Localized String")]
  2. [DisplayName("Your Localized String")]
  3. Your Property Name
ElChepos commented 5 years ago

No luck on my side.

Here is an example of the viewmodel.

public class CustomerViewModel
    {
        public int Id { get; set; }
        [SearchableString]
        [Sortable(Default = true)]
        public string Company { get; set; }
        [SearchableString]
        [Sortable]
        public string NickName { get; set; }
        [SearchableString]
        [Sortable]
        [DisplayName("Phone")]
        public string FullPhone { get; set; }
    }

Note that we use the portable object localization https://docs.microsoft.com/en-us/aspnet/core/fundamentals/portable-object-localization?view=aspnetcore-3.0

Maybe it has a link to that ? Normally in our view we need to use @Html.DisplayFor or Localized["Phone"] for example and it is working.

fingers10 commented 5 years ago

Your using Asp.Net Core 2.x or 3.0? I tried with 2.x project and it worked for me. Try clearing Nuget Cache and check. I double checked form my end. Here is the link to demo project. Please check.

ElChepos commented 4 years ago

Sorry for the late reply, finally had time to check that error back and it still not working. The datable table display the 'DisplayName' attribute but if we use localization in the app to switch from english to french the translation it not applied.

In the demo project you don't have the option to switch language so we can't test it.

I will try to make you a demo project next week to display the problem

ElChepos commented 4 years ago

Hey @fingers10,

so here is what we need to do. Pretty easy. We import the the localizer and we add _localizer[column.Name].Value to replace everywhere you call column.Name


using Microsoft.AspNetCore.Mvc.Localization;

[HtmlTargetElement("jquery-datatablesTranslated", Attributes = "id,class,model")]
    public class JqueryDataTablesTagHelper : TagHelper
    {
        **private readonly IHtmlLocalizer<JqueryDataTablesTagHelper> _localizer;

        public JqueryDataTablesTagHelper(IHtmlLocalizer<JqueryDataTablesTagHelper> localizer)
        {
            _localizer = localizer;
        }**

        public string Id { get; set; }
        public string Class { get; set; }
        public object Model { get; set; }

        [HtmlAttributeName("enable-searching")]
        public bool EnableSearching { get; set; }

        [HtmlAttributeName("thead-class")]
        public string TheadClass { get; set; }

        [HtmlAttributeName("search-row-th-class")]
        public string SearchRowThClass { get; set; }

        [HtmlAttributeName("search-input-class")]
        public string SearchInputClass { get; set; }

        [HtmlAttributeName("search-input-style")]
        public string SearchInputStyle { get; set; }

        [HtmlAttributeName("search-input-placeholder-prefix")]
        public string SearchInputPlaceholderPrefix { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "table";
            output.Attributes.Add("id", Id);
            output.Attributes.Add("class", Class);

            output.PreContent.SetHtmlContent($@"<thead class=""{TheadClass}"">");

            var headerRow = new StringBuilder();
            var searchRow = new StringBuilder();

            headerRow.AppendLine("<tr>");

            if (EnableSearching)
            {
                searchRow.AppendLine("<tr>");
            }

            var columns = GetColumnsFromModel(Model.GetType()).Where(c => !c.Exclude).OrderBy(c => c.Order);

            foreach (var column in columns)
            {
                **headerRow.AppendLine($@"<th>{_localizer[column.Name].Value}</th>");**

                if (!EnableSearching)
                {
                    continue;
                }

                **searchRow.AppendLine($@"<th class=""{SearchRowThClass}""><span class=""sr-only"">{_localizer[column.Name].Value}</span>");**

                if (column.HasSearch)
                {
                    **searchRow.AppendLine($@"<input type=""search"" style=""{SearchInputStyle}"" class=""{SearchInputClass}"" placeholder=""{SearchInputPlaceholderPrefix} {_localizer[column.Name].Value}"" aria-label=""{_localizer[column.Name].Value}"" />");**
                }

                searchRow.AppendLine("</th>");
            }

            headerRow.AppendLine("</tr>");
            if (EnableSearching)
            {
                searchRow.AppendLine("</tr>");
            }

            output.Content.SetHtmlContent($"{headerRow.ToString()}{searchRow.ToString()}");
            output.PostContent.SetHtmlContent("</thead>");
        }
fingers10 commented 4 years ago

Thanks @jptoros. I'll add this in the upcoming release.

fingers10 commented 4 years ago

No luck on my side.

Here is an example of the viewmodel.

public class CustomerViewModel
    {
        public int Id { get; set; }
        [SearchableString]
        [Sortable(Default = true)]
        public string Company { get; set; }
        [SearchableString]
        [Sortable]
        public string NickName { get; set; }
        [SearchableString]
        [Sortable]
        [DisplayName("Phone")]
        public string FullPhone { get; set; }
    }

Note that we use the portable object localization https://docs.microsoft.com/en-us/aspnet/core/fundamentals/portable-object-localization?view=aspnetcore-3.0

Maybe it has a link to that ? Normally in our view we need to use @Html.DisplayFor or Localized["Phone"] for example and it is working.

@jptoros , I was looking at your implementation. I have a question here, in your above view model, did you try to pass the localized name in the [Display] attribute? Something like below,

public class CustomerViewModel
{
    public int Id { get; set; }

    [SearchableString]
    [Sortable(Default = true)]
    public string Company { get; set; }

    [SearchableString]
    [Sortable]
    public string NickName { get; set; }

    [SearchableString]
    [Sortable]
    [Display(ResourceType = typeof(MyResources), Name = "Phone")]
    public string FullPhone { get; set; }
}

I think the above one should resolve your issue as I'm reading the value coming from the [Display] attribute. Did you give a try?

Instead of [DisplayName("Phone")] try with [Display(ResourceType = typeof(MyResources), Name = "Phone")]. I think this should be sufficient than changing the tag helper source code.

ElChepos commented 4 years ago

Hey @fingers10 ,

Two option to put ressource for localization in .net Core

  1. Use RessourceFile from Microsoft. Work with [Display(ResourceType = typeof(MyResources), Name = "Phone")]

  2. Use Portable Object Localization Don't work with [Display(ResourceType = typeof(MyResources), Name = "Phone")] since the MyRessourcec won't exist since it use .po file instead of the normal Ressource File

More info on Portable Object Localization https://docs.microsoft.com/en-us/aspnet/core/fundamentals/portable-object-localization?view=aspnetcore-3.1

fingers10 commented 4 years ago

In this case adding IHtmlLocalizer​<​JqueryDataTablesTagHelper> as a dependency inside the TagHelper will force the user of this package to register the localization service in their app even though they are not using it. If they fail do add then the app will throw runtime exception. Hence I'm thinking of alternative as this is a specific use case.

fingers10 commented 4 years ago

@jptoros Added a push to master for this enhancement. This will be available from next release.

shrutigoyal123 commented 4 years ago

Hello @jptoros @fingers10 Can I get a demo of localization of new tag helper that you have added.

I have tried but getting error- Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.Localization.IHtmlLocalizer`1[JqueryDataTables.ServerSide.AspNetCoreWeb.TagHelpers.JqueryDataTablesTagHelper]' while attempting to activate 'JqueryDataTables.ServerSide.AspNetCoreWeb.TagHelpers.JqueryDataTablesHtmlLocalizedTagHelper'.

fingers10 commented 4 years ago

@shrutigoyal123 if you're using portable object localization then please refer the following link - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/portable-object-localization?view=aspnetcore-3.1#registering-the-service

Looks like you failed to add required services in the startup

shrutigoyal123 commented 4 years ago

@fingers10 I am using JSON file as Resource file and my startup.cs ss as below- image In case of JSON resource file. what I need to do?

shrutigoyal123 commented 4 years ago

Thank you for your support. It is working now. I had some implementation mistake at my end.

fingers10 commented 4 years ago

@shrutigoyal123 sorry for the delayed response and glad that you made it. If possible Please share the solution here so that it might help other developers in future.

ElChepos commented 4 years ago

@shrutigoyal123 let me know if you need help with anything.

shrutigoyal123 commented 4 years ago

@fingers10 Yes here I am sharing implementation with JSON Resource file-

1 . Add AddJsonLocalization and AddViewLocalization in StartUp.cs image

  1. In appsetting.json image

  2. data.json in Resources folder image

  3. In _ViewImports.cshtml image

  4. In our listing page image

  5. Add [Display(Name = "")] in Model

Above is the implementation of column value changes in DataTable

@jptoros but how to change the language of Pagination and PageLength language like-

  1. Showing 1 to 4 of 4 entries
  2. image

  3. image

ElChepos commented 4 years ago

You need to load the js file with the correct translate.

You check the current language and you get the specific language file. Example on the top of you datatable page. { var requestCulture = Context.Features.Get(); var langFileUrl = string.Empty; if (requestCulture.RequestCulture.UICulture.Name == "fr") { langFileUrl = "/lang/DataTable/fr.json"; } else { langFileUrl = "/lang/DataTable/en.json"; } }

And in the Datatable init you specify it. var table = $('#@Model.Name').DataTable({ language: { url: "@langFileUrl" }, processing: true, serverSide: true, It not the most elegant way, but it work.

fingers10 commented 4 years ago

@jptoros many thanks for helping fellow developer. I'm happy that we can get working an example out here. Or may be we can make these as PR and add it to demo project. It would be a great addition. What say? Can you please give a PR for a demo project?

fingers10 commented 4 years ago

@jptoros please can you help with a HTML Localization sample?

ElChepos commented 4 years ago

Sure ! With all the help that you provided to me during my dev here you go. Don't hesitate if you have any questions.

https://github.com/fingers10/JqueryDataTablesServerSideDemo/pull/9

If you don't mind add me into the Contributions section of your GitHub project. Gonna be a first for me :)

fingers10 commented 4 years ago

Sure @jptoros . Will review and get back if I have any questions.