Abstract
A practical guide to building a multi-language Asp.Net 8 MVC application where all language resource strings are kept in a single shared file, as opposed to having separate resource files for each controller/view. In this part, we focus on the localization of form validation error strings.
1. Multilingual form validation error strings
A separate task is how to handle in the Asp.Net MVC application a localization of form validation error strings, or so-called Data Annotation Localization. That is the focus of this article.
2. Other articles in this series
Articles in this series are
3. Shared Resources approach
By default, Asp.Net Core 8 MVC technology envisions separate resource file .resx for each controller and the view. But most people do not like it, since most multilanguage strings are the same in different places in the application, we would like it to be all in the same place. Literature [1] calls that approach the “Shared Resources” approach. In order to implement it, we will create a marker class SharedResoureces.cs to group all the resources. Then in our application, we will invoke Dependency Injection (DI) for that particular class/type instead of a specific controller/view. That is a little trick mentioned in Microsoft documentation [1] that has been a source of confusion in StackOverflow articles [6]. We plan to demystify it here. While everything is explained in [1], what is needed are some practical examples, like the one we provide here.
4. Steps to Multilingual Application
4.1. Configuring Localization Services and Middleware
Localization services are configured in the Program. cs.
4.2. Create marker class SharedResources.cs
This is just a dummy marker class to group shared resources. We need it for its name and type.
It seems the namespace needs to be the same as the app root namespace, which needs to be the same as the assembly name. I had some problems when changing the namespace, it would not work. If it doesn't work for you, you can try to use the full class name in your DI instruction, like this one:
IStringLocalizer<SharedResources01.SharedResource> StringLocalizer
There is no magic in the name "SharedResource", you can name it "MyResources" and change all references in the code to "MyResources" and all will still work.
The location seems can be any folder, although some articles ([6] claim it needs to be the root project folder I do not see such problems in this example. To me looks like it can be any folder, just keep your namespace tidy.
4.3. Create language resources files
In the folder “Resources” create your language resources files, and make sure you name them SharedResources.xx.resx.
4.4. Selecting Language/Culture
Based on [5], the Localization service has three default providers
- QueryStringRequestCultureProvider
- CookieRequestCultureProvider
- AcceptLanguageHeaderRequestCultureProvider
Since most apps will often provide a mechanism to set the culture with the ASP.NET Core culture cookie, we will focus only on that approach in our example.
This is the code to set.AspNetCore.Culture cookie.
A cookie can be easily seen with Chrome DevTools.
I built a small application to demo it, and here is the screen where I can change the language.
Note. I added some debugging info into the footer to show the value of the Request language cookie to see if the app is working as desired.
4.5. Using Data Annotation – Field Validation
In your model class, you set up validation attributes with proper strings that need to be localized.
For model-level validation in the controller, we will use classical IStringLocalizer.
4.6. Synchronization Asp.Net CSS error classes with Bootstrap CSS classes
Asp.Net will add the CSS class .input-validation-error to form a field with an error. But, Bootstrap does not know what to do with that CSS class, so that class needs to be mapped to the CSS class that Bootstrap understands, and that is CSS class .is-invalid.
That is the purpose of this JavaScript code that is here. Of course, we hook to the DOMContentLoaded event and do the mapping of CSS classes.
All this is to sync Asp.Net CSS error classes with Bootstrap CSS classes to mark error input elements border to red by Bootstrap.
The final result is that the red line on the form control marks an invalid field.
4.7. Sample view with field and model validation messages
Here is our sample view.
Note. we used novalidate attribute in the form element to suppress browser-level integrated validation that is popping out and is not localized.
So, there are 3 possible levels of validation
- Server-side validation: used in this example and is localized (multilingual)
- Client-side validation: in ASP.NET, it can be enabled by using jquery.validate.unobtrusive.min.js, but we are not using it in this example.
- Browser-integrated validation: disabled in this example by the usage of novalidate attribute because it is not localized and is always in English.
If you do not set the novalidate attribute, the browser will pop up its validation dialog, and you will see messages on the following screen. It can be confusing to the user to multiple different messages.
4.8. Execution result
Here is what the execution result looks like
Note. I added some debugging info into the footer, to show the value of the Request language cookie, to see if the app is working as desired.
5. Full Code
Since most people like code they can copy-paste, here is the full code of the application.
6. References
[1] https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/make-content-localizable?view=aspnetcore-8.0: Make an ASP.NET Core app's content localizable
[2] https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/provide-resources?view=aspnetcore-8.0: Provide localized resources for languages and cultures in an ASP.NET Core app
[3] https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/select-language-culture?view=aspnetcore-8.0: Implement a strategy to select the language/culture for each request in a localized ASP.NET Core app
[4] https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-8.0: Globalization and localization in ASP.NET Core
[5] https://learn.microsoft.com/en-us/aspnet/core/fundamentals/troubleshoot-aspnet-core-localization?view=aspnetcore-8.0: Troubleshoot ASP.NET Core Localization
[6] https://stackoverflow.com/questions/42647384/asp-net-core-localization-with-help-of-sharedresources: ASP.NET Core Localization with the help of shared resources