1

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...

Stas
  • 69
  • 9
  • 1
    Are you using 3.0 or 3.1? Could this be [your issue](https://github.com/dotnet/aspnetcore/issues/6566)? Is your input type text or number? – CrnaStena Sep 24 '20 at 14:41
  • My input has number as type and I use 3.1. It looks like I have the same problem. – Stas Sep 25 '20 at 09:44
  • 1
    Oh well, it looks like they are punting it to 5.0 as far as I can see. Not sure if this custom binder stuff would help you [out in the meantime](https://stackoverflow.com/questions/50977542/asp-net-core-2-0-bind-model-with-decimal-value)? – CrnaStena Sep 25 '20 at 12:52
  • Custom model binder did it, many thanks. – Stas Sep 26 '20 at 20:15

1 Answers1

0

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": "cldrjs@0.5.1",
      "destination": "wwwroot/lib/cldr"
    },
    {
      "library": "cldr-data@35.1.0",
      "destination": "wwwroot/lib/cldr-data"
    },
    {
      "library": "globalize@1.4.2",
      "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/jquery-validation-globalize@1.0.0/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:

LazZiya
  • 3,746
  • 2
  • 13
  • 26