ASP.NET8 using DataTables.net – Part4 – Multilingual
ASP.NET8 using DataTables.net – Part4 – Multilingual
A practical guide to using jQuery DataTables.net component in Asp.Net 8 MVC application.
Abstract: A practical guide to building an Asp.Net 8 MVC application that uses jQuery component DataTables.net. This is a continuation of article Part3.
1 ASP.NET8 using jQuery DataTables.net
I was evaluating the jQuery DataTables.net component [1] for usage in ASP.NET8 projects and created several prototype (proof-of-concept) applications that are presented in these articles.
1.1 Articles in this series
Articles in this series are:
- ASP.NET8 using DataTables.net – Part1 – Foundation
- ASP.NET8 using DataTables.net – Part2 – Action buttons
- ASP.NET8 using DataTables.net – Part3 – State saving
- ASP.NET8 using DataTables.net – Part4 – Multilingual
- ASP.NET8 using DataTables.net – Part5 – Passing additional parameters in AJAX
- ASP.NET8 using DataTables.net – Part6 – Returning additional parameters in AJAX
- ASP.NET8 using DataTables.net – Part7 – Buttons regular
- ASP.NET8 using DataTables.net – Part8 – Select rows
- ASP.NET8 using DataTables.net – Part9 – Advanced Filters
2 Final result
The goal of this article is to create a proof-of-concept application that demos DataTables.net component in ASP.NET multi-language application. Let us present the result of this article.
Here is the English version:
Here is the German version:
So, DataTables.net component can be used in multilingual applications. Please note that we need to set up translations on two levels:
- Translations for DataTables.net component itself
- Translations for Strings used by DataTables.net component
It is a bit tricky to set up it all properly, but it is doable.
3 Multi-language setup
If you are not familiar with the multi-language setup for ASP.NET8, please read my articles [2]-[5]. I am not going to copy-paste the same text again here, I will just use the same code from those examples here. As explained in those articles, the main trick is to set up AspNetCore.Culture cookie properly for the particular language. All is explained in detail in those articles [2]-[5].
Here is the controller code that is doing that:
//HomeController.cs
public IActionResult ChangeLanguage(ChangeLanguageViewModel model)
{
if (model.IsSubmit)
{
HttpContext myContext = this.HttpContext;
ChangeLanguage_SetCookie(myContext, model.SelectedLanguage);
//doing funny redirect to get new Request Cookie
//for presentation
return LocalRedirect("/Home/ChangeLanguage");
}
//prepare presentation
ChangeLanguage_PreparePresentation(model);
return View(model);
}
private void ChangeLanguage_PreparePresentation(ChangeLanguageViewModel model)
{
model.ListOfLanguages = new List<SelectListItem>
{
new SelectListItem
{
Text = "English",
Value = "en"
},
new SelectListItem
{
Text = "German",
Value = "de",
},
new SelectListItem
{
Text = "French",
Value = "fr"
},
new SelectListItem
{
Text = "Italian",
Value = "it"
}
};
}
private void ChangeLanguage_SetCookie(HttpContext myContext, string? culture)
{
if (culture == null) { throw new Exception("culture == null"); };
//this code sets .AspNetCore.Culture cookie
CookieOptions cookieOptions = new CookieOptions();
cookieOptions.Expires = DateTimeOffset.UtcNow.AddMonths(1);
cookieOptions.IsEssential = true;
myContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
cookieOptions
);
}
Here is the footer in the view that I use for debugging purposes
@* _Debug.AspNetCore.CultureCookie.cshtml ===================================================*@
@using Microsoft.AspNetCore.Localization;
@{
string text = String.Empty;
try
{
var myContext = Context;
string? cultureCookieValue = null;
myContext.Request.Cookies.TryGetValue(
CookieRequestCultureProvider.DefaultCookieName, out cultureCookieValue);
string? text1 = "Request Cookie was (Refresh might be needed if changing language) " +
CookieRequestCultureProvider.DefaultCookieName + "[" + cultureCookieValue + "]";
text = text1;
}
catch (Exception ex)
{
text = ex.Message;
}
<span>
@text
</span>
}
Here are translations for DataTables.net component itself
Here are translations for strings in the application
4 Client-side DataTables.net component
Here I will just show what the ASP.NET view using DataTables component looks like.
<!-- Employees.cshtml -->
<partial name="_LoadingDatatablesJsAndCss" />
@{
<div class="text-center">
<h3 class="display-4">Employees table</h3>
</div>
<!-- Here is our table HTML element defined. JavaScript library Datatables
will do all the magic to turn it into interactive component -->
<table id="EmployeesTable01" class="table table-striped table-bordered ">
</table>
}
@{
<!-- Method to tell DatTables.net component which
language file to load -->
string GetUrlForDatatablesLanguageFile()
{
string urlResult = String.Empty;
try
{
string culture = Thread.CurrentThread.CurrentUICulture.ToString();
if (culture.Length > 2)
{
culture = culture.Substring(0, 2).ToLower();
}
string baseUrl = Url.Content("~/lib/datatables/i18n/");
switch (culture)
{
case "de":
urlResult = baseUrl + "de-DE.json";
break;
case "fr":
urlResult = baseUrl + "fr-FR.json";
break;
case "it":
urlResult = baseUrl + "it-IT.json";
break;
default:
urlResult = String.Empty;
break;
}
}
catch
{
urlResult = String.Empty;
}
return urlResult;
}
}
<script>
// Datatables script initialization=========================================
// we used defer attribute on jQuery so it might not be available at this point
// so we go for vanilla JS event
document.addEventListener("DOMContentLoaded", InitializeDatatable);
function InitializeDatatable() {
$("#EmployeesTable01").dataTable({
//processing-Feature control the processing indicator.
processing: true,
//paging-Enable or disable table pagination.
paging: true,
//info-Feature control table information display field
info: true,
//ordering-Feature control ordering (sorting) abilities in DataTables.
ordering: true,
//searching-Feature control search (filtering) abilities
searching: true,
//search.return-Enable / disable DataTables' search on return.
search: {
return: true
},
//autoWidth-Feature control DataTables' smart column width handling.
autoWidth: true,
//lengthMenu-Change the options in the page length select list.
lengthMenu: [10, 15, 25, 50, 100],
//pageLength-Change the initial page length (number of rows per page)
pageLength: 10,
//order-Initial order (sort) to apply to the table.
order: [[1, 'asc']],
//serverSide-Feature control DataTables' server-side processing mode.
serverSide: true,
//stateSave-State saving - restore table state on page reload.
stateSave: true,
//stateDuration-Saved state validity duration.
//-1 sessionStorage will be used, while for 0 or greater localStorage will be used.
stateDuration: -1,
//language.url- Load language information from remote file.
language: {
url: '@GetUrlForDatatablesLanguageFile()'
},
//Load data for the table's content from an Ajax source.
ajax: {
"url": "@Url.Action("EmployeesDT", "Home")",
"type": "POST",
"datatype": "json"
},
//Set column specific initialization properties
columns: [
//name-Set a descriptive name for a column
//data-Set the data source for the column from the rows data object / array
//title-Set the column title
//orderable-Enable or disable ordering on this column
//searchable-Enable or disable search on the data in this column
//type-Set the column type - used for filtering and sorting string processing
//visible-Enable or disable the display of this column.
//width-Column width assignment.
//render-Render (process) the data for use in the table.
//className-Class to assign to each cell in the column.
{ //0
name: 'id',
data: 'id',
title: "@Example04.Resources.SharedResource.EmployeeId",
orderable: true,
searchable: false,
type: 'num',
visible: true
},
{
//1
name: 'givenName',
data: "givenName",
title: "@Example04.Resources.SharedResource.GivenName",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//2
name: 'familyName',
data: "familyName",
title: "@Example04.Resources.SharedResource.FamilyName",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//3
name: 'town',
data: "town",
title: "@Example04.Resources.SharedResource.Town",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//4
name: 'country',
data: "country",
title: "@Example04.Resources.SharedResource.Country",
orderable: true,
searchable: true,
type: 'string',
visible: true,
width: "150px",
className: 'text-center '
},
{
//5
name: 'email',
data: "email",
title: "@Example04.Resources.SharedResource.Email",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//6
name: 'phoneNo',
data: "phoneNo",
title: "@Example04.Resources.SharedResource.PhoneNumber",
orderable: false,
searchable: true,
type: 'string',
visible: true
},
{
//7
name: 'actions',
data: "actions",
title: "@Example04.Resources.SharedResource.Actions",
orderable: false,
searchable: false,
type: 'string',
visible: true,
render: renderActions
},
{
//8
name: 'urlForEdit',
data: "urlForEdit",
title: "urlForEdit",
orderable: false,
searchable: false,
type: 'string',
visible: false
}
]
});
function renderActions(data, type, row, meta) {
//for Edit button we get Url from the table data
let html1 =
'<a class="btn btn-info" href="' +
row.urlForEdit +
'"> @Example04.Resources.SharedResource.Edit</a>';
//for Info button we create Url in JavaScript
let editUrl = "@Url.Action("EmployeeInfo", "Home")" +
"?EmployeeId=" + row.id;
let html2 =
'<a class="btn btn-info" style="margin-left: 10px" href="' +
editUrl + '"> @Example04.Resources.SharedResource.Info</a>';
return html1 + html2;
}
}
</script>
Please note that we need to set up translations on two levels:
- Translations for DataTables.net component itself
- Translations for Strings used by DataTables.net component
For 1), we use the method GetUrlForDatatablesLanguageFile() to tell DatTables.net component which language file to load.
For 2) we use Resource Manager, as explained in [5].
5 Conclusion
The full example code project can be downloaded at GitHub [99].
6 References
[2] https://www.codeproject.com/Articles/5378651/ASP-NET-8-Multilingual-Application-with-Single-Res
[3] https://www.codeproject.com/Articles/5378997/ASP-NET-8-Multilingual-Application-with-Single-R-2
[4] https://www.codeproject.com/Articles/5379125/ASP-NET-8-Multilingual-Application-with-Single-R-3
[5] https://www.codeproject.com/Articles/5379436/ASP-NET-8-Multilingual-Application-with-Single-R-4
[99] https://github.com/MarkPelf/ASPNET8UsingDataTablesNet
Subscribe to my newsletter
Read articles from Mark Pelf directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Mark Pelf
Mark Pelf
A Software Engineer from Belgrade, Serbia.