Skip to content

fix: normalize Unicode minus sign in GSP number formatting for locale compatibility#15475

Open
jamesfredley wants to merge 3 commits into7.1.xfrom
fix/negative-number-locale-15178
Open

fix: normalize Unicode minus sign in GSP number formatting for locale compatibility#15475
jamesfredley wants to merge 3 commits into7.1.xfrom
fix/negative-number-locale-15178

Conversation

@jamesfredley
Copy link
Contributor

@jamesfredley jamesfredley commented Mar 1, 2026

Summary

Fixes #15178 - Negative numbers with Norwegian locale (and similar locales) produce Unicode minus sign (U+2212) instead of ASCII hyphen-minus (U+002D) when rendered in input type=number fields via the f:field tag. This violates the WHATWG HTML spec which requires ASCII hyphen-minus for valid floating-point number values.

Changes

Source (1 file):

  • grails-fields/.../FormFieldsTagLib.groovy - Modified getNumberFormatter() to normalize DecimalFormatSymbols.minusSign to ASCII hyphen-minus. This method is called exclusively from renderNumericInput() which outputs input elements, making the fix isolated to the input rendering pipeline.

Tests (1 file):

  • grails-fields/.../DefaultInputRenderingSpec.groovy - Added data-driven Spock test covering 5 locales (nb_NO, nn_NO, sv_SE, fi_FI, en_US) to verify negative number input values use ASCII minus.

Design Decisions

Per reviewer feedback, this fix is now scoped to only the FormFieldsTagLib input rendering path:

  • No changes to FormatTagLib.formatNumber - This is a display tag (g:formatNumber) and should preserve locale-specific formatting including Unicode minus.
  • No changes to ValidationTagLib.formatValue - This is used in both display and form contexts and should not be globally modified.
  • Fix only in FormFieldsTagLib.getNumberFormatter() - This is explicitly input-specific, called from renderNumericInput() which produces input elements where the WHATWG spec mandates ASCII minus.

Technical Details

  • Java DecimalFormat uses DecimalFormatSymbols.minusSign which varies by locale (e.g., Norwegian uses U+2212)
  • The WHATWG HTML spec defines valid floating-point number for input type=number as requiring U+002D (ASCII hyphen-minus)
  • The normalization is instance-local (per-formatter), not mutating any shared state
  • For locales that already use ASCII minus, the assignment is a no-op

Verification

  • All DefaultInputRenderingSpec tests pass (including 5 new locale tests)
  • All existing grails-fields tests remain passing
  • No changes to display formatting behavior (g:formatNumber, g:fieldValue)

…or locale compatibility

Some locales (e.g., Norwegian nb-NO) use Unicode minus U+2212 instead of
ASCII hyphen-minus U+002D when formatting negative numbers. This causes
issues in HTML number input fields that expect standard ASCII minus.

Normalize DecimalFormatSymbols.minusSign to ASCII '-' in FormatTagLib,
ValidationTagLib, and FormFieldsTagLib before formatting numbers.

Fixes #15178

Assisted-by: Claude Code <Claude@Claude.ai>
@github-actions github-actions bot added the bug label Mar 1, 2026
@jamesfredley jamesfredley changed the base branch from 7.0.x to 7.1.x March 1, 2026 00:12
@jamesfredley jamesfredley self-assigned this Mar 1, 2026
@jamesfredley jamesfredley marked this pull request as ready for review March 1, 2026 00:13
Copilot AI review requested due to automatic review settings March 1, 2026 00:13
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Normalizes the minus sign used when formatting negative numbers in GSP-related taglibs so generated values are compatible with HTML <input type="number"> (which expects ASCII -), fixing locale-specific formatting issues such as Norwegian nb-NO emitting U+2212.

Changes:

  • Force DecimalFormatSymbols.minusSign to ASCII hyphen-minus (U+002D) when formatting numbers (including fallback formatting paths).
  • Apply the same normalization in ValidationTagLib.formatValue() and FormFieldsTagLib number formatting used for field rendering.
  • Add regression tests (JUnit + Spock) covering negative numeric outputs under nb_NO.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
grails-gsp/plugin/src/main/groovy/org/grails/plugins/web/taglib/FormatTagLib.groovy Normalizes minus sign in formatNumber and its ArithmeticException fallback formatter.
grails-gsp/plugin/src/main/groovy/org/grails/plugins/web/taglib/ValidationTagLib.groovy Normalizes minus sign both for custom-editor formatting and DecimalFormat formatting in formatValue().
grails-fields/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy Normalizes minus sign for NumberFormat used to populate numeric field values.
grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/FormatTagLibTests.groovy Adds JUnit regression tests ensuring ASCII minus output for negative values in nb_NO.
grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/FormatTagLibSpec.groovy Adds Spock regression test for Norwegian locale minus sign behavior in formatNumber.
grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/ValidationTagLibSpec.groovy Adds Spock regression test for ASCII minus in g:fieldValue under Norwegian locale.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Add Geb integration tests in gsp-layout test example to verify that
g:formatNumber renders negative numbers with ASCII minus (U+002D)
instead of Unicode minus (U+2212) when using Norwegian locale (nb_NO).

New files:
- formatLocaleNumber.gsp view with locale-specific number formatting
- formatLocaleNumber() controller action providing negative test values
- 4 Geb specs in GspTagLibSpec for int, long, BigDecimal, and positive

Relates to #15178

Assisted-by: Claude Code <Claude@Claude.ai>
Copy link
Contributor

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes seem to not be the right approach:

  1. they assume all output is for an html input
  2. they do not handle only that locale case, but rather them all
  3. they are not isolated and specific - we should be changing the property binding and output for only this locale and only where used in an input
  4. they have side effect behavior; wouldn't it be better to implement or extend our own formatter and document these rules? how have others handled this? does the formatter already have an ascii solution?

This change feels rushed and hacky because its handling a specific case in generic code

Address review feedback on PR #15475 by narrowing the fix scope:

- Revert changes to FormatTagLib (display tag, not input-specific)
- Revert changes to ValidationTagLib (not input-specific)
- Revert functional tests that tested g:formatNumber with locale
- Keep fix only in FormFieldsTagLib.getNumberFormatter() which is
  explicitly input-specific (called from renderNumericInput)
- Add data-driven Spock test covering nb_NO, nn_NO, sv_SE, fi_FI,
  and en_US locales in DefaultInputRenderingSpec

The fix now only normalizes Unicode minus (U+2212) to ASCII minus
(U+002D) when formatting values for HTML <input type="number">
elements via the f:field tag, which is the correct and isolated
location per WHATWG spec requirements.

Fixes #15178

Assisted-by: Claude Code <Claude@Claude.ai>
@jamesfredley
Copy link
Contributor Author

Addressed all review feedback. The fix is now scoped to only FormFieldsTagLib.getNumberFormatter() which is explicitly input-specific (called from renderNumericInput() for HTML input elements). All changes to FormatTagLib, ValidationTagLib, and functional tests have been reverted. Added data-driven Spock test covering nb_NO, nn_NO, sv_SE, fi_FI, and en_US locales.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Negative number bug with gsp and fields and Norwegian locale in browser.

3 participants