.Net Core Razor has wrong culture

591 Views Asked by At

I have a localized .Net Core 3 app. My resources are in a separate assembly in .resx files. The localization is set up to use cookies like this:

var cultureProvider = new CookieRequestCultureProvider();
cultureProvider.CookieName = "MyCultureCookie";

var localizationOptions = new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("de"),
    SupportedCultures = StaticData.SupportedCultures,
    SupportedUICultures = StaticData.SupportedCultures,
    RequestCultureProviders = new List<IRequestCultureProvider> 
    { 
        cultureProvider,
        new AcceptLanguageHeaderRequestCultureProvider()
    }
};

app.UseRequestLocalization(localizationOptions);

and to localize validation messages:

services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddControllersWithViews()
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
            factory.Create(typeof(MyLocalizationDll));
    })
    .AddViewLocalization()
    .AddRazorRuntimeCompilation();
services.AddMvc()
    .SetupModelBindingLocalization(services)
    .AddViewLocalization();

The input in my view:

<input type="number" class="form-control" min="0" max="100" step="0.1" />

It works well, I can switch the language and all page texts including almost all data validation error messages will be translated, numbers are shown in correct format (e.g. 1.00 for "en" and 1,00 for "de").

But the problem is: when I have a decimal number like 1,5 in german culture and I submit my form, then it comes as 15 in my controller. Only when I switch the language to "en" I get the right number, because the decimal separator is point as expected. And I also get some of the validation messages for this field in other language. Just can't figure out what is here the problem...

1

There are 1 best solutions below

0
On

Custom model binder may provide a workaround for the issue, but if you need a better approach you have to install a set of cldr scripts for number/date/currency/etc validation.

Option 1: Manually install cldr scripts

  • install cldr libraries:
{
  "version": "1.0",
  "defaultProvider": "jsdelivr",
  "libraries": [
    {
      "library": "[email protected]",
      "destination": "wwwroot/lib/cldr"
    },
    {
      "library": "[email protected]",
      "destination": "wwwroot/lib/cldr-data"
    },
    {
      "library": "[email protected]",
      "destination": "wwwroot/lib/globalize"
    }
  ]
}
  • open wwwroot/lib/cldr-data/package.json file and add below code before the last closing paranthes:
"peerDependencies": {
    "cldr-data": ">=26"
  }
  • Use the scripts where you want to use decimal validation:
<!-- cldr scripts (needed for globalize) -->
<script src="/lib/cldr/dist/cldr.min.js"></script>
<script src="/lib/cldr/dist/cldr/event.min.js"></script>
<script src="/lib/cldr/dist/cldr/supplemental.min.js"></script>

<!-- globalize scripts -->
<script src="/lib/globalize/dist/globalize.min.js"></script>
<script src="/lib/globalize/dist/globalize/number.min.js"></script>
<script src="/lib/globalize/dist/globalize/date.min.js"></script>
<script src="/lib/globalize/dist/globalize/currency.min.js"></script>

<!-- this file can be downloaded from : -->
<!-- https://github.com/johnnyreilly/jquery-validation-globalize -->
<script src="https://cdn.jsdelivr.net/gh/johnnyreilly/[email protected]/jquery.validate.globalize.min.js"></script>

<!-- code to get check if current cultures scripts are exists -->
<!-- if not, select parent cultures scripts -->
@inject Microsoft.AspNetCore.Hosting.IHostingEnvironment HostingEnvironment
@{
    string GetDefaultLocale()
    {
        const string localePattern = "lib\\cldr-data\\main\\{0}";
        var currentCulture = System.Globalization.CultureInfo.CurrentCulture;
        var cultureToUse = "en"; //Default regionalisation to use

        if (System.IO.Directory.Exists(System.IO.Path.Combine(HostingEnvironment.WebRootPath, string.Format(localePattern, currentCulture.Name))))
            cultureToUse = currentCulture.Name;
        else if (System.IO.Directory.Exists(System.IO.Path.Combine(HostingEnvironment.WebRootPath, string.Format(localePattern, currentCulture.TwoLetterISOLanguageName))))
            cultureToUse = currentCulture.TwoLetterISOLanguageName;

        return cultureToUse;
    }
}

<script type="text/javascript">
    var culture = "@GetDefaultLocale()";
    $.when(
        $.get("/lib/cldr-data/supplemental/likelySubtags.json"),
        $.get("/lib/cldr-data/main/" + culture + "numbers.json"),
        $.get("/lib/cldr-data/main/" + culture + "/currencies.json"),
        $.get("/lib/cldr-data/supplemental/numberingSystems.json"),
        $.get("/lib/cldr-data/main/" + culture + "/ca-gregorian.json"),
        $.get("/lib/cldr-data/main/" + culture + "/timeZoneNames.json"),
        $.get("/lib/cldr-data/supplemental/timeData.json"),
        $.get("/lib/cldr-data/supplemental/weekData.json"),
    ).then(function () {
        // Normalize $.get results, we only need the JSON, not the request statuses.
        return [].slice.apply(arguments, [0]).map(function (result) {
            return result[0];
        });
    }).then(Globalize.load).then(function () {
        Globalize.locale(culture);
    });
</script>

Option 2: Use LocalizationValidationScripts taghelper

This tag helper provides the above solution in a simple way.

  • Install LazZiya.TagHelpers from nuge
PM > Install-Package LazZiya.TagHelpers
  • Register in startup
@using LazZiya.TagHelpers

services.AddTransient<ITagHelperComponent, LocalizationValidationScriptsTagHelperComponent>();
  • Add to _ViewImports.cshtml
@addTagHelper *, LazZiya.TagHelpers
  • Insert the html tag inside Scripts section just after validation scripts partial
@section Scripts {
    <partial name="_ValidationScriptsPartial" />
    <localization-validation-scripts></localization-validation-scripts>
}

Sources: