jamesfredley commented on PR #15726:
URL: https://github.com/apache/grails-core/pull/15726#issuecomment-4732489247
> I'm ok with the change, but I don't think I fully understand why we have
to handle this locale issue directly
Short version: this isn't Grails working around itself - it's a hard
limitation of `java.text.DecimalFormat` once the JVM uses CLDR locale data (the
default since JDK 9). I dug into the JDK internals to be sure we weren't just
doing something dumb on our side, and we aren't.
**Root cause**
For `nb_NO` (and `nn_NO`, `sv_SE`, `fi_FI`, ...) CLDR defines the minus sign
as **U+2212 MINUS SIGN**, not **U+002D ASCII HYPHEN-MINUS**. `DecimalFormat`
builds its negative prefix straight from `DecimalFormatSymbols.getMinusSign()`
and matches that prefix *literally* during `parse()`. So an ASCII `-` is simply
not recognized as a sign - the parser stops at index 0 and the bind fails.
Probe on JDK 21 (Corretto 21.0.11):
```
=== Locale nb_NO ===
minusSign char = U+2212
negativePrefix = U+2212
parse("-1" [U+002D U+0031]) -> null consumedIndex=0/2 // ASCII minus:
FAILS
parse("...1" [U+2212 U+0031]) -> -1 consumedIndex=2/2 // locale
minus: ok
format(-1) -> [U+2212 U+0031]
=== Locale en_US ===
minusSign char = U+002D
parse("-1" [U+002D U+0031]) -> -1 consumedIndex=2/2 // ASCII minus:
ok
```
So `NumberFormat.getInstance(new Locale("nb","NO")).parse("-1")` returns
`null`. `en_US` only works because its CLDR minus *happens* to be ASCII - which
is exactly why this stayed hidden until a Norwegian-locale browser posted a
form.
**Why Java won't "just pass it through"**
There is no leniency knob for this. `DecimalFormat` has no "accept either
minus" mode, and it has no idea the string arrived from an HTML form. The
request side, meanwhile, is always ASCII: per the WHATWG "valid floating-point
number" rule an `<input type=number>` submits ASCII `-`, and on a `text` field
the user types the `-` on their keyboard, which is also ASCII. So the wire is
ASCII while the locale parser only accepts U+2212, and nothing in the JDK
reconciles that gap for us.
**The "just use Java's default" options I ruled out**
- `-Djava.locale.providers=COMPAT` restores the pre-9 ASCII minus, but it's
a global, deprecated JVM flag (COMPAT data is being removed) and a wrong fix
for one parse path.
- Forcing `symbols.minusSign = '-'` makes ASCII parse but then *rejects* a
genuine U+2212 - strictly less lenient than what we ship now.
That's why #15475 fixed the render side (emit ASCII minus into inputs) and
this PR fixes the matching bind side. Normalizing only a leading ASCII `-` to
the active locale's minus before `parse()` is the minimal change that keeps
full locale-aware parsing (grouping/decimal separators) intact while accepting
the sign every browser actually sends - and the locale's native minus still
parses too, so it's strictly more lenient, not less.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]