fix: parse ASCII minus in locale-aware number binding#15726
Conversation
Accept browser-submitted ASCII hyphen-minus when binding numbers for locales whose DecimalFormat minus sign is U+2212. This keeps display formatting unchanged while allowing Long, BigDecimal, and BigInteger converters to parse negative values for nb_NO. Fixes #15714 Assisted-by: Hephaestus:gpt-5.5 oracle
There was a problem hiding this comment.
Pull request overview
This PR fixes locale-aware request data binding for negative numbers when browsers submit an ASCII hyphen-minus (-, U+002D) but the active locale’s DecimalFormatSymbols.minusSign is a different character (notably U+2212 for locales like nb_NO). It does so by normalizing only a leading ASCII minus to the locale’s expected minus sign before parsing, and adds targeted Spock coverage.
Changes:
- Normalize a leading ASCII
-to the locale-specific minus sign forDecimalFormat-backed parsers inLocaleAwareNumberConverterbefore callingNumberFormat.parse. - Reuse a single formatter instance per conversion and validate parse consumption against the normalized input.
- Add Spock tests covering
Long,BigDecimal, andBigIntegerconversions fornb_NOanden_US.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| grails-databinding/src/main/groovy/org/grails/databinding/converters/web/LocaleAwareNumberConverter.groovy | Normalizes leading ASCII minus to locale minus for DecimalFormat to prevent parse failures in locales with non-ASCII minus signs. |
| grails-databinding/src/test/groovy/org/grails/databinding/converters/web/LocaleAwareNumberConverterSpec.groovy | Adds regression coverage for negative number parsing across nb_NO and en_US for Long, BigDecimal, and BigInteger. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
✅ All tests passed ✅🏷️ Commit: b272702 Learn more about TestLens at testlens.app. |
| Object convert(Object value) { | ||
| def trimmedValue = value.toString().trim() | ||
| def formatter = numberFormatter | ||
| def valueToParse = replaceAsciiMinusWithLocaleMinus(formatter, trimmedValue) |
There was a problem hiding this comment.
So the JVM has built in locale handling and yet we still have to work around it?
|
The JVM's built-in locale handling is often insufficient for web applications because it relies on the server's default locale, which may not match the user's browser-provided locale. This converter is designed to bridge that gap by explicitly handling locale-aware number parsing, ensuring that data binding respects the specific locale context of the incoming request rather than defaulting to the server's environment. |
Short version: this isn't Grails working around itself - it's a hard limitation of Root cause For Probe on JDK 21 (Corretto 21.0.11): So Why Java won't "just pass it through" There is no leniency knob for this. The "just use Java's default" options I ruled out
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 |
Summary
Fixes #15714 by accepting browser-submitted ASCII hyphen-minus values in locale-aware number binding for locales whose DecimalFormat minus sign is U+2212, such as nb_NO.
The earlier PR #15475 correctly scoped the rendering fix to form field input output, but #15714 shows the remaining failure is on the request binding side. The sample application's proposed direction was right: convert a leading ASCII minus to the active locale minus before calling NumberFormat.parse, instead of changing display tags.
Changes
Verification