Easily localize all infrastructure of your Asp.Net Core 2.x web application using LazZiya.ExpressLocalization
Code Project Article and Sample:
Express Localization Sample
Introduction
Developing multicultural ASP.NET Core 2.x web application requires a lot of infrastructure setup and it consumes time and effort. In this article, we will use
LazZiya.ExpressLocalization
nuget package for localizing our web application with one step.
Background
Most of the web apps use URL based localization, so we can see the selected culture in the URL, e.g.,
http://www.example.com/en/Contact
. Unfortunately, ASP.NET Core provides only below request culture providers:
QueryStringRequestCultureProvider
CookieRequestCultureProvider
AcceptLanguageHeaderRequestCultureProvider
In order to have route value localization, we need to build custom localization provider and define a global route template, so basically, we need to complete the below localization steps to have fully localized web application:
Build route value request culture provider
:
For request localization according to {culture} route value
Define global route template for culture parameter
:
Add {culture} parameter to url, e.g., www.example.com/en-US/
Setup
DataAnnotations
localization
:
Localizaton of data annotations like Display names, Required, StringLength, ...etc.
DataAnnotations are defined in
System.ComponentModel.DataAnnotations
namespace.
In addition to localizing the default DataAnnotations, custom attributes also must be localized using the same logic.
For more details, see
DataAnnotations.
Setup
ModelBinding
error messages localization
:
Validation of input types at server side after submit, e.g.,
ValueMustBeANumberAccessor
,
AttemptedValueIsInvalidAccessor
, etc.
For more details, see
DefaultModelBindingMessageProvider
.
Setup IdentityDescriber error messages
:
Localization of all user and role related messages like "
User already in role
", "
User name already exists
", ...etc.
For more details, see
IdentityErroDescriber
.
Setup view localization
:
Localize text/html in razor pages.
For more details, see
ViewLocalization
.
Setup client side validation scripts
:
Localizing of client validation messages is essential, forms will be validated before submit. The client side validation scripts must be localized so the user will see localized messages like: The field is required, Passwords must match, etc.
One more important thing is validating localized decimal numbers, some cultures use period and other cultures use comma in decimal numbers like (1,2) and (1.2), this case should be handled carefully in client side validation.
What is more: Some cultures may use totally different numbering systems; e.g., Arabic cultures are using numbers like "٠١٢٣٤٥٦٧٨٩" and Latin cultures are using
"0123456789"
. If the numbering system setup is not correct, a validation error will rise. (See
this article
to learn how to change numbering system for client side validation.)
For more details, see
Client side validation
and localization
related issue in GitHub
.
Create localized resource files for each culture
:
Views, DataAnnotations, ModelBinding and IdentityErrors all require localized resources. This step consumes a lot of time and effort!
All these steps require a lot of work and consume too much time. So, here comes the benefit of
LazZiya.ExpressLocalization
nuget package that eliminates the localization setup time and effort with simple lines of code.
Creating the Project
Let's start by creating a basic ASP.NET Core 2.2 web application (I'm using VS2019):
Create a new project by selecting ASP.NET Core Web Application:
Click
Next
, give the project a friendly name and click
Create
:
Select
Web Application
and make sure you change the authentication to
Individual User Accounts
.
Click
Create
and wait till the solution creates the basic template, once it is done, you can do a test run by selecting the project name in the solution explorer, then pressing (
Ctrl + Shift + B
) to build the project, then (
Ctrl + Shift + W
) to run without debugging in the browser.
Go to
Browse
tab and search for "
LazZiya
", select "
LazZiya.ExpressLocalization
" and click "
Install
", select the latest version and install.
It will ask to install two packages, click "
Ok
" and wait till installation is done:
LazZiya.ExpressLocalization
(required for all localization setup)
LazZiya.TagHelpers
(required for client side localization validation and language dropdown)
Manually install
LazZiya.TagHelpers.Localization
package, required for easily localizing text/html in razor pages.
Check for any updates to the relevant nuget packages before proceeding.
Creating Localized Resources
I've already prepared localized resources for the project, so you don't have to waste your time on creating localized resources.
Under the project root, create a new folder and name it "
LocalizationResources
":
Under
LocalizationResources
folder, create new
public
class and name it "
ViewLocalizationResource
", this class will be used to group resource files for view localization:
namespace ExpressLocalizationSample.LocalizationResources
public class ViewLocalizationResource
Under
LocalizationResources
folder, create new
public
class and name it "
ExpressLocalizationResource
", this class will be used to group resource files for identity, model binding and data annotations.
namespace ExpressLocalizationSample.LocalizationResources
public class ExpressLocalizationResource
We will use these two classes to pass resource type to the express localization method.
Finally, download relevant cultures resources from this
repository folder
. Please notice that you need to download two files for each culture, e.g., (
ExpressLocalizationResource.tr.resx
and
ViewLocalizationResource.tr.resx
). Copy the downloaded files to "
LocalizationResources
" folder.
Using the Code
Finally, we are ready for the localization setup. :)
Open
startup.cs
file and add the required namespace:
using LazZiya.ExpressLocalization;
then inside
ConfigureServices
method, define supported cultures and add localization setup as below:
var cultures = new[]
new CultureInfo("tr"),
new CultureInfo("ar"),
new CultureInfo("hi"),
new CultureInfo("en"),
services.AddMvc()
.AddExpressLocalization<ExpressLocalizationResource, ViewLocalizationResource>(
ops =>
ops.ResourcesPath = "LocalizationResources";
ops.RequestLocalizationOptions = o =>
o.SupportedCultures = cultures;
o.SupportedUICultures = cultures;
o.DefaultRequestCulture = new RequestCulture("en");
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
Then under
Configure
method, configure the app to use request localization:
app.UseRequestLocalization();
Adding Language Navigation
Under
Pages
folder, open
_ViewImports.cshtml
file and add
LazZiya.TagHelpers
that will help in creating language navigation:
@using LazZiya.TagHelpers
@addTagHelper *, LazZiya.TagHelpers
Then open
Pages/Shared/_Layout.cshtml
file and add the language navigation tag helper under the
_LoginPartial
tag as below:
<partial name="_LoginPartial" />
<language-nav view-context="ViewContext"></language-nav>
LanguageNav
has one required parameter "
view-context
", read more about
LanguageNav tag helper.
We are ready for the first run:
So good, so far we have our navigation with supported cultures, but we still need to localize view texts to see the localized versions.
Localizing Views
The localized texts for the default project are already provided in "
ViewLocalizationResource.xx.resx
" in the downloaded files. If you need to add more custom texts for views, add them to the "
ViewLocalizationResource.xx.resx
" files.
Option 1
Here we will use
LocalizeTagHelper
that ships with
LazZiya.TagHelpers.Localization
, it is offering a simple and clear HTML tag for localizing views.
Open
Pages/_ViewImports.cshtml
file and add localization tag helper:
@using LazZiya.TagHelpers.Localization
@addTagHelper *, LazZiya.TagHelpers.Localization
Then open
Pages/Index.cshtml
and use localize tag helper to localize texts/html:
@page
@model IndexModel
ViewData["Title"] = "Home page";
<div class="text-center">
<h1 class="display-4" localize-content>Welcome</h1>
<p localize-content>Learn about <a href='https://docs.microsoft.com/aspnet/core'>
building Web apps with ASP.NET Core</a>.</p>
Use the same process to localize all texts in other views as well.
See more details in
live demo page
and
GitHub
.
Option 2
Open
Pages/_ViewImports.cshtml
file and inject
SharedCultureLocalizer
that already comes with
ExpressLocalization
:
@using LazZiya.ExpressLocalization
@inject SharedCultureLocalizer _loc
Then open
Pages/Index.cshtml
and use localizer function for texts:
@page
@model IndexModel
ViewData["Title"] = _loc.Text("Home page");
<div class="text-center">
<h1 class="display-4">@_loc.Text("Welcome")</h1>
<p>@_loc.Text("Learn about <a href='https://docs.microsoft.com/aspnet/core'>
building Web apps with ASP.NET Core</a>").</p>
Use the same process to localize all texts in other views as well.
Localizing URLs
While the page is in any culture other than the default, if you click on
Privacy
,
Login
or
Register
links, you will notice that we are losing the selected culture, that is because we didn't add the culture route value to the link.
Open
Pages/_ViewImports.cshtml
and add reference to
System.Globalization
:
@using System.Globalization
Then open
Pages/_LoginPartial.cshtml
and add to the top of the page a
culture
parameter as below:
var culture = CultureInfo.CurrentCulture.Name;
Use this parameter to provide
culture
route value to all links as below:
<a class="nav-link text-dark"
asp-area="Identity"
asp-page="/Account/Register"
asp-route-culture="@culture"
localize-content>Register</a>
Do this to all views in the project.
Localizing Identity Views
Identity related like login, register and profile needs to be overridden in order to be modified.
Right click on the project name, select
Add
-->
New Scaffolded Item
...
Select
Identity
and click
Add
:
Select "
Override all files
" and select "
ApplicationDbContext
":
When you click
Add
, a new
Areas
folder will be created including all identity related views:
Identity area has three
_ViewImports
folders:
Areas/Identity/Pages/_ViewImports.cshtml
Areas/Identity/Pages/Account/_ViewImports.cshtml
Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml
Add the below code to all of them as we did for
Pages/_ViewImports.cshtml
previously:
@using System.Globalization
@using LazZiya.TagHelpers
@addTagHelper *, LazZiya.TagHelpers
@using LazZiya.TagHelpers.Localization
@addTagHelper *, LazZiya.TagHelpers.Localization
Go over the views and use localization steps as we did before for localizing views and add
culture
route parameter as well. Below is the
Register.cshtml
page:
@page
@model RegisterModel
ViewData["Title"] = "Register";
var culture = CultureInfo.CurrentCulture.Name;
<h1 localize-content>Register</h1>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="@Model.ReturnUrl"
method="post" asp-route-culture="@culture">
<h4 localize-content>Create a new account.</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password"
class="text-danger"></span>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
<button type="submit" class="btn btn-primary" localize-content>Register</button>
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
Localizing DataAnnotations
If you run the page and do some invalid inputs, you will notice that the validation messages are in English, so we will need to localize the data annotations messages, e.g.,
Required
,
StringLength
, etc.
Open
Areas/Identity/Pages/Account/Register.cshtml.cs
file and add reference to "
LazZiya.ExpressLocalization.Messages
" at the top of the page; it contains a predefined
struct
for all
DataAnnotations
error messages for easy use:
@using LazZiya.ExpressLocalization.Messages;
Then modify the Input model to be like below:
public class InputModel
[Required(ErrorMessage = DataAnnotationsErrorMessages.RequiredAttribute_ValidationError)]
[EmailAddress(ErrorMessage = DataAnnotationsErrorMessages.EmailAddressAttribute_Invalid)]
[Display(Name = "Email")]
public string Email { get; set; }
[Required(ErrorMessage = DataAnnotationsErrorMessages.RequiredAttribute_ValidationError)]
[StringLength(100, ErrorMessage =
DataAnnotationsErrorMessages.StringLengthAttribute_ValidationErrorIncludingMinimum,
MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password",
ErrorMessage = DataAnnotationsErrorMessages.CompareAttribute_MustMatch)]
public string ConfirmPassword { get; set; }
Compile and run the project, you will see localized data annotation error messages:
Client Side Validation
The server side validation is working well, but we still need to add client side validation as well, so the input fields will be validated on client side before submitting the form.
One major issue with client side validation is validating localized inputs like numbers, dates, etc. For example, if you are using a decimal input, you will see validation error for localized numbers like 1.3 is valid in English culture, but is invalid for Turkish because it should be 1,3 (comma instead of period).
Here, we will use another useful tag helper that comes with
LazZiya.TagHelpers
.
Open
Register.cshtml
page and add the tag helper under the default validation scripts partial:
@section Scripts {
<partial name="_ValidationScriptsPartial" />
<localization-validation-scripts></localization-validation-scripts>
That's all, now the fields will be validated before submitting the form with localized validation messages:
Sample Project
You can download a sample project with more than 19 cultures included from GitHub (not yet updated to use
LocalizeTagHelper
):
https://github.com/LazZiya/ExpressLocalizationSample
References
Read more details about the used nuget packages here:
LazZiya.TagHelpers
LazZiya.TagHelpers.Localization
(
Live demo
-
GitHub
)
Manually installing client side localization validation scripts
Manually creating language drop down navigation
Detailed tutorial for localizing ASP.NET Core 2.1
GitHub Repository:
Express Localization Sample
Code Project Article and Sample:
Express Localization Sample
History
3
rd
June, 2019: Initial version
17
th
June, 2019: Updated version using
LazZiya.TagHelpers.Localization
22
nd
June, 2019: Updated sample, hotfix for some issues in localizing
ModelBinding
and Identity messages (Update
LazZiya.ExpressLocalization
to v2.0.1)