dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.62k stars 25.3k forks source link

Show support jQuery validation for non-English locales that use a comma (",") for a decimal point #4076

Open Rick-Anderson opened 7 years ago

Rick-Anderson commented 7 years ago

https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/validation mentions You may not be able to enter decimal points or commas in the Price field. To support jQuery validation for non-English locales that use a comma (",") for a decimal point, and non US-English date formats, you must take steps to globalize your app.

Need tutorial that shows how to do this. Until the tutorial is written, follow these instructions:

On Index.cshtml add

@System.Globalization.CultureInfo.CurrentUICulture

Refreshing the page you should see en-US displayed at the very top. This will help to see if you does have localization enabled on mvc. When localization is working it can be removed.

To enable another locale than default on mvc you need to add localization support (read Globalization and localization in ASP.NET Core)

For this sample, let's just add es-UY locale and set as the default.

on Startup.cs:

app.UseRequestLocalization("en-UY", "fr-FR");

this will add support for locale "es-UY" and also make it the default culture.

refreshing the Index page on your browser should now show es-UY as the current UI locale.

At this point, using decimal points on the client as 19.50 will allow the jQuery validation but on the server it won't be seen as decimals so you will end with 1.950,00 value.

Then you need to use Globalize with jquery-validation-globalize plugin.

See Globalize installation instructions and jquery-validation-globalize installation instructions. Unfortunately those instructions use bower for the installation and the ASP.NET CORE 2.0 and late doesn't use bower. If you want to use the recommended NPM way, you can still do it. Either way, I highly recommend you check out both projects for documentation and more info. You just need to install Globalize with jquery-validation-globalize plugin - how you do it is secondary.

After you managed to install Globalize and jquery-validation-globalize plugin then you need to use it on your html pages.

on _ValidationScriptsPartial.cshtml add the new required javascript files (after jQuery):

<!-- cldr scripts (needed for globalize) -->
<script src="~/lib/cldrjs/dist/cldr.js"></script>
<script src="~/lib/cldrjs/dist/cldr/event.js"></script>
<script src="~/lib/cldrjs/dist/cldr/supplemental.js"></script>

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

<script src="~/lib/jquery-validation-globalize/jquery.validate.globalize.js"></script>

Refresh your page, it should work with the current culture (in this case, es-UY).

The LibMan tool requires VS 15.8.0 Preview 2.0 or later.

Hope it helps. image

Rick-Anderson commented 7 years ago

@damienbod @bartmax @hishamco would one of you be able to help out with this? (or recommend someone)?

Rick-Anderson commented 7 years ago

@ryanbrandenburg @danroth27 this is a frequent complaint of customers. Can I get some help on showing how to do this?

Bartmax commented 7 years ago

@Rick-Anderson

On Index.cshtml add

@System.Globalization.CultureInfo.CurrentUICulture

Refreshing the page you should see en-US displayed at the very top. This will help to see if you does have localization enabled on mvc. When localization is working it can be removed.

To enable another locale than default on mvc you need to add localization support (read Globalization and localization in ASP.NET Core)

For this sample, let's just add es-UY locale and set as the default.

on Startup.cs:

var defaultCulture = new CultureInfo("es-UY");
var localizationOptions = new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(defaultCulture),
    SupportedCultures = new List<CultureInfo> { defaultCulture },
    SupportedUICultures = new List<CultureInfo> { defaultCulture }
};
app.UseRequestLocalization(localizationOptions);

this will add support for locale "es-UY" and also make it the default culture.

refreshing the Index page on your browser should now show es-UY as the current UI locale.

At this point, using decimal points on the client as 19.50 will allow the jQuery validation but on the server it won't be seen as decimals so you will end with 1.950,00 value.

Then you need to use Globalize with jquery-validation-globalize plugin.

You can find here Globalize installation instructions and jquery-validation-globalize installation instructions. Unfortunately those are for bower and the ASP.NET CORE team moved away (a good thing) from bower so if you want to use the recommended NPM way, you can still do it. Either way, I highly recommend you check out both projects for documentation and more info.

After you managed to install Globalize and jquery-validation-globalize plugin then you need to use it on your html pages.

on _ValidationScriptsPartial.cshtml add the new required javascript files (after jQuery):

<!-- cldr scripts (needed for globalize) -->
<script src="~/lib/cldrjs/dist/cldr.js"></script>
<script src="~/lib/cldrjs/dist/cldr/event.js"></script>
<script src="~/lib/cldrjs/dist/cldr/supplemental.js"></script>

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

<script src="~/lib/jquery-validation-globalize/jquery.validate.globalize.js"></script>

Refresh your page, it should work with the current culture (in this case, es-UY).

Hope it helps. image

[Edit, moved from top to here]Some notes:

The RegularExpression attribute is used to limit what characters can be input. In the code above, Genre and Rating must use only letters (white space, numbers and special characters are not allowed).

The regular expression is not doing what's described, more notably it requires first letter to be Uppercase.

Bartmax commented 7 years ago

and don't think you need it, but just in case I made the sample project available on my github at https://github.com/Bartmax/MvcMovie.LocalizationSample (let me know when I can delete it)

Rick-Anderson commented 7 years ago

Thanks @Bartmax

kadariuk commented 7 years ago

@Bartmax I have this error in js, during validation, not you?

jquery.validate.js:666 Uncaught Error: E_DEFAULT_LOCALE_NOT_DEFINED: Default locale has not been defined. at createError (globalize.js:105) at validate (globalize.js:182) at validateDefaultLocale (globalize.js:213) at Function.Globalize.numberParser.Globalize.numberParser (number.js:1422) at Function.Globalize.parseNumber.Globalize.parseNumber (number.js:1474) at $.validator.methods.number (jquery.validate.globalize.js:21) at $.validator.check (jquery.validate.js:639) at $.validator.element (jquery.validate.js:437) at $.validator.onfocusout (jquery.validate.js:266) at HTMLInputElement.delegate (jquery.validate.js:380) createError @ globalize.js:105 validate @ globalize.js:182 validateDefaultLocale @ globalize.js:213 Globalize.numberParser.Globalize.numberParser @ number.js:1422 Globalize.parseNumber.Globalize.parseNumber @ number.js:1474 $.validator.methods.number @ jquery.validate.globalize.js:21 check @ jquery.validate.js:639 element @ jquery.validate.js:437 onfocusout @ jquery.validate.js:266 delegate @ jquery.validate.js:380 dispatch @ jquery.js:4732 elemData.handle @ jquery.js:4544 trigger @ jquery.js:7788 simulate @ jquery.js:7859 handler @ jquery.js:7922

coykto-repos commented 7 years ago

I would really like to let both comma and dot to be accepted as a decimal separator. In Russia and i guess in some other places it is ok to use either dot or comma to separate decimal values, but for the thousands people tend to use spaces, like that: 125 000 000.25

Also, i just cloned repository that @Bartmax provided, it does accept comma as a separator so when i input "1,99" i get "$ 1,99", but when i type in "1.99" i get the movie with the price of "$ 199,00". It doesn't seem like an appropriate behavior.

Calkines commented 7 years ago

@coykto-repos is not the case of configure the model with attribute that indicate it is a currency data type? On the other hand, perhaps, you may be using a culture that needs a comma to decimal separator.

coykto-repos commented 7 years ago

@Calkines maybe i'm missing something as i'm not familiar with asp.net core, or .net in general for that matter. I originaly came here because i got really confused by "getting started" tutorial, writen by @Rick-Anderson and others, as neither "." nor "," passed validation for a price field. I have a Django background and i never had problems like that as the framework seems to handled these things well, but here it seems messing with jQuery validation probably would not be enough. As i mentioned, typing in values "1.99" and "1,99" should give me the same result - "$ 1.99" regardless of what culture my browser says it uses, definately not "$ 199". So, this is also server-side (or asp.net-side) problem as far as i understand it, since one of the solutions i found is to write custom IModelBinder (which is far from "getting started").

Rick-Anderson commented 7 years ago

https://github.com/aspnet/Docs/pull/4382 adds link to this issue until I have time to add this info to the doc.

bcalcas commented 6 years ago

This happened after I updated the file .bowercc file:

bower ECMDERR Failed to execute "node ./node_modules/cldr-data-downloader/bin/download.js -i bower_components/cldr-data/index.json -o bower_components/cldr-data/", exit code of #1 Whops ENOENT: no such file or directory, open 'C:\Users\Utilizador\source\repos\MyProject\MyProject\bower_components\cldr-data\index.json'

Additional error details: Whops ENOENT: no such file or directory, open 'C:\Users\Utilizador\source\repos\MyProject\MyProject\bower_components\cldr-data\index.json'

JoaoVictorCardoso commented 6 years ago

please say where I can found this file ".bowercc" I just fund "bower.json" files inside wwwrot>lib>...(multiple folders)

Peter578 commented 6 years ago

I found 16 bower.json files in my Project but no .bowercc. After installing jquery-validation-globalize there is a new hidden dir 'bower_components' where i find cldrjs, jquery-validation-globalize and some others. What should i reference in 'ValidationScriptsPartial.cshtml' ? Move the Contents to www.lib? I am using Visual Studio 2017 ver 15.15.2 on a german language System. Displaying Date works fine but in .net core 2.0 the fields always display american date-format giving Errors on german date Format (dd.mm.yyyy).

bcalcas commented 6 years ago

@JoaoVictorCardoso bower.cc is "inside" bower.json img

Peter578 commented 6 years ago

Sorry, no bower.json in my project root. I just created two new Project (ASP.NET Core-Webanwendung / MVC / Core 2.0). Added jquery-validation-globalize thru Nuget in one Project, thru bower in the other. Bower installer said: "no-json No bower.json file to save to, use bower init to create one". Now i'll Google for "bower init"...

bcalcas commented 6 years ago

@Peter578 bower is about to be discontinued in asp.netcore 2.0. Maybe because of that you can't see the bower.cc file in your project. Check this link: https://wildermuth.com/2017/11/19/ASP-NET-Core-2-0-and-the-End-of-Bower

This thread was to support .net core 1.x (I think)

Rick-Anderson commented 6 years ago

@Bartmax

IMPORTANT: First update .bowercc to looks like this:

Can you update this?

Bartmax commented 6 years ago

@Rick-Anderson update how?

Rick-Anderson commented 6 years ago

@Bartmax I'm getting lots of comments - now that we don't use bower this doesn't work.

Bartmax commented 6 years ago

@Rick-Anderson the authors of both plugins document bower as the "package manager way" to get the libraries. I updated the issue but I think it's out of the scope how to get js libraries from whichever is the package manager of the day.

Let me know if the current update works for you.

XelaNimed commented 6 years ago

Hello! Thanks to @Bartmax but answer not resolved problem, because JavaScript error in console occurred: E_DEFAULT_LOCALE_NOT_DEFINED (have @MatteoSevera too). I solved this problem so:

  1. First, you need to modify the file .bowerrc (you can find it under the file bower.json):
    {
    "directory": "wwwroot/lib",
    "scripts": {
    "preinstall": "npm install cldr-data-downloader@0.2.x",
    "postinstall": "node ./node_modules/cldr-data-downloader/bin/download.js -i wwwroot/lib/cldr-data/index.json -o wwwroot/lib/cldr-data/"
    }
    }
  2. Check dependecies in bower.json:
    {
    "name": "asp.net",
    "private": true,
    "dependencies": {
    "bootstrap": "3.3.7",
    "jquery": "3.2.1",
    "jquery-validation": "1.17.0",
    "jquery-validation-unobtrusive": "3.2.6",
    "cldr-data": "29.0.0",
    "globalize": "v0.1.1",
    "jquery-validation-globalize": "1.0.0",
    "cldrjs": "0.5.0"
    },
    "resolutions": {
    "globalize": "^1.0.0",
    "jquery": "3.2.1",
    "cldrjs": "0.5.0",
    "jquery-validation": "1.17.0"
    }
    }
  3. Modify file _ValidationScriptsPartial.cshtml like so:
    
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

@inject Microsoft.AspNetCore.Hosting.IHostingEnvironment HostingEnvironment @{ string GetDefaultLocale() { const string localePattern = "lib\cldr-data\main\{0}"; var currentCulture = System.Globalization.CultureInfo.CurrentCulture; var cultureToUse = "ru-RU"; //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;
}

}



This solution from [article](https://www.codeproject.com/Articles/1212247/jQuery-culture-validation-in-ASP-NET-Core) by [Stefan Vincent Haug](https://www.codeproject.com/script/Membership/View.aspx?mid=13450171).

Why does the official [documentation](https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/validation#validation-error-ui-in-mvc) refer to an [answer](https://github.com/aspnet/Docs/issues/4076#issuecomment-326590420) that does not solve the problem?
Rick-Anderson commented 6 years ago

@Bartmax I copied your instructions to the top of the issue to resolve some problems people had with the comment we don't use bower. I also fixed a couple problems you mentioned with the tutorial (changes will show up in a couple days) and removed those comments. Can you review my edits? Is there anything we could do to make the instructions easier to follow?

Rick-Anderson commented 6 years ago

@ryanbrandenburg can you review the instructions at the top and suggest improvements?

ryanbrandenburg commented 6 years ago

@Rick-Anderson I would suggest modifying the example to include at least two supported cultures, even if you only use one in the example just to emphasis that this works in a multi-culture scenario.

I also suggest doing a scrub of that text for phrasing and syntax if you weren't already planning to. It's a good rough draft but some stuff like "You can find here Globalize installation instructions and jquery-validation-globalize installation instructions." needs re-wording before it goes into docs.

Where in the docs where you planning to put this? This is an important scenario, but it's relatively niche, so I don't really think it belongs in, for example, the loc fundamentals doc, which is already a pretty dense read.

Rick-Anderson commented 6 years ago

Where in the docs where you planning to put this? I'll create a new doc.

Can you post the updated instructions here? This is our highest support issue.

ryanbrandenburg commented 6 years ago

I leave the language improvements to you, but as far as my first comment it would be enough to replace the Startup.cs code section with:

var defaultCulture = new CultureInfo("es-UY");
var supportedCultures = new List<CultureInfo>{ defaultCulture, new CultureInfo("fr-FR") }
var localizationOptions = new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(defaultCulture),
    SupportedCultures = supportedCultures,
    SupportedUICultures = supportedCultures
};
app.UseRequestLocalization(localizationOptions);
Bartmax commented 6 years ago

maybe we can update the doc to use the Mads client-side library tools that is encouraged to use on the new templates. Let me see if I manage to get some time to do it

Rick-Anderson commented 6 years ago

@Bartmax that would be fantastic.

hishamco commented 6 years ago

@Rick-Anderson we can simplify the above code snippet with

app.UseRequestLocalization("en-UY", "fr-FR");

using the builder APIs that we provide https://github.com/aspnet/Localization/blob/dev/src/Microsoft.AspNetCore.Localization/ApplicationBuilderExtensions.cs#L95

Rick-Anderson commented 6 years ago

@hishamco can you supply the full snippet using builder APIs?

Bartmax commented 6 years ago

@Rick-Anderson there's a problem in trying to include details with the LibMan tool. It was postponed and it's not on VS 😞 anymore

Bartmax commented 6 years ago

@Rick-Anderson we can simplify the above code snippet with

great advise @hishamco !!

@hishamco can you supply the full snippet using builder APIs?

replace:

var defaultCulture = new CultureInfo("es-UY");
var localizationOptions = new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(defaultCulture),
    SupportedCultures = new List<CultureInfo> { defaultCulture },
    SupportedUICultures = new List<CultureInfo> { defaultCulture }
};
app.UseRequestLocalization(localizationOptions);

with this:

app.UseRequestLocalization("en-UY", "fr-FR");

I'm not sure in which version this extension was added and/or if it does matters.

Rick-Anderson commented 6 years ago

@Bartmax how about The LibMan tool requires VS 15.8.0 Preview 2.0 or later.

hishamco commented 6 years ago

@Rick-Anderson as @Bartmax said before the line that I provided is the replacement

FYI I added extra Builder APIs in 2.1 preview 2

hubekpeter commented 6 years ago

Is there any update on this topic ? I'm facing the same issue for sk-sk locales with the double model validators and I'm not willing to write a custom modelbinder or use overcomplicated solutions like applying another 3 layers of js libraries for the simple thing like this. I see that there are bunch of localization methods already in the jquery validation library. https://github.com/jquery-validation/jquery-validation/blob/master/src/localization/methods_pt.js Why they did not write more of them I don't know.

eriawan commented 6 years ago

@Bartmax

On Index.cshtml add

@System.Globalization.CultureInfo.CurrentUICulture

This solution is only applicable on server side. If the user's browser is using different UI culture, then the problem will still occur, and it can break existing client-side script such as Javascript.

This is why @XelaNimed comments is also reproducible and valid.

For me, it is more preferable to honor the UI culture of the user agent (i.e. web browser) used by looking at the request headers, although this can be one or more cultures.

There's one good sample from Stackoverflow: https://stackoverflow.com/questions/9414123/get-cultureinfo-from-current-visitor-and-setting-resources-based-on-that

This is the code that honors the user agent's culture: (I have modified to correctly map string to StringWithQualityHeaderValue)

public IEnumerable<CultureInfo> GetUserPreferredCultures()
{
  var requestedLanguages = Request.Headers["Accept-Language"];
  if (StringValues.IsNullOrEmpty(requestedLanguages) || requestedLanguages.Count == 0)
  {
    return null;
  }

  var preferredCultures = requestedLanguages.ToString().Split(',')
      .Select(s => StringWithQualityHeaderValue.Parse(s))
      .Where(sv => sv.Value != AnyLanguageIdentifier)
      // Remove duplicates with a lower value
      .GroupBy(sv => sv.Value).Select(svg => svg.OrderByDescending(sv => sv.Quality.GetValueOrDefault(1)).First())
      .OrderByDescending(sv => sv.Quality.GetValueOrDefault(1))
      .Select(sv => new CultureInfo(sv.Value.ToString()));

  return preferredCultures;
}

I have tried it, and it works well on ASP.NET Core runtime 2.0.1 and 2.0.2. I haven't tried on ASP.NET Core 2.1.0 yet.

@Rick-Anderson Could I propose to create a new article to document how to handle server side and client side culture on ASP.NET Core 2,1?

Rick-Anderson commented 6 years ago

@eriawan

Could I propose to create a new article to document how to handle server side and client side culture on ASP.NET Core 2,1?

That would be great. Can you create a new issue to track that work?

cc @hishamco

hishamco commented 6 years ago

@eriawan how's the sample differ from built-in AcceptLanguageHeaderRequestCultureProvider?

eriawan commented 6 years ago

@Rick-Anderson

Thanks! I have created a WIP issue #8585 to track this. Feel free to give suggestions and feedback.

eriawan commented 6 years ago

hi @hishamco

Thanks for the info! The sample code was taken from the code from StackOverflow. I will use the AcceptLanguageHeaderRequestCultureProvider too in the written doc page,

blfuentes commented 5 years ago

I have tried to implement the cldr solution but with no success

https://stackoverflow.com/questions/54926621/globalize-validation-problem-with-datetime-input-control

Any help would be appreciated.

ManuelCastejon commented 5 years ago

en Startup.cs

image

hannespreishuber commented 5 years ago

anybody have jquery-validation-globalize in libman?

ghost commented 5 years ago

Why Microsoft will not repair this issue? It exists from years!

JustinMinnaar commented 5 years ago

Perhaps the easiest way to to expose a view model with a string property, then handle the format recognition on the server side when receiving a post or ajax query?

hannespreishuber commented 5 years ago

Perhaps the easiest way to to expose a view model with a string property, then handle the format recognition on the server side when receiving a post or ajax query?

you have every done that? the issue is the Client side Validation

JustinMinnaar commented 5 years ago

Perhaps the easiest way to to expose a view model with a string property, then handle the format recognition on the server side when receiving a post or ajax query?

you have every done that? the issue is the Client side Validation

Duh! I'm not thinking this morning. I retract my comment.

DanielKaramazov commented 5 years ago

anybody have jquery-validation-globalize in libman?

Yes. At least I've got this working in VS 2019. Just use jsdelivr or unpkg as provider. Add/Client side library.../Choose one of these providers.

vukasinpetrovic commented 3 years ago

What happened to this number/date format problem with different languages/locales? I can't find any info about that problem except this issue, which is somewhat old. Are there any new solutions available?

tudor-turcu commented 3 years ago

Unfortunately this issue was closed, without a proper solution.. A beginner following the ASP.NET MVC Core tutorial will arrive here, but the steps are not clear at all.