Fix decimal and float inputs rejecting locale decimal separator on Chromium by Tokonigeorge · Pull Request #27189 · directus/directus · GitHub
Skip to content

Fix decimal and float inputs rejecting locale decimal separator on Chromium#27189

Open
Tokonigeorge wants to merge 2 commits intodirectus:mainfrom
Tokonigeorge:fix/decimal-input-locale-24803
Open

Fix decimal and float inputs rejecting locale decimal separator on Chromium#27189
Tokonigeorge wants to merge 2 commits intodirectus:mainfrom
Tokonigeorge:fix/decimal-input-locale-24803

Conversation

@Tokonigeorge
Copy link
Copy Markdown

@Tokonigeorge Tokonigeorge commented Apr 23, 2026

Scope

What's changed:

  • Float and decimal field types now render as <input type="text" inputmode="decimal"> instead of <input type="number">, so the app locale's decimal separator (e.g. , in nl-NL, de-DE) is accepted consistently across browsers.
  • Stored values are displayed using the app locale's separator (via Intl.NumberFormat), and user input is normalised back to . before emission, matching the existing text+float path.
  • Integer fields are unchanged, they still render as <input type="number"> with native spinner arrows.

Why the change

Chromium-based browsers (Chrome, Edge, Safari) ignore the lang attribute on <input type="number"> and always require . as the decimal separator, regardless of the user's OS locale or any lang we set on the element (see Chromium issue 40593334). Users on comma-locale browsers therefore couldn't enter 300,44 into a Decimal field, the browser rejected it with "not a number" before the value ever reached Directus code. Because the bug was in the browser's validity layer, a small fix in v-input.vue alone couldn't solve it; the field had to stop being type="number" altogether for float/decimal.

Potential Risks / Drawbacks

  • No native spinner for float/decimal. The up/down step buttons previously rendered by v-input.vue (gated on type === 'number') are gone for these field types. Integer fields still have them.
  • Keyboard ↑/↓ no longer steps float/decimal values, since that is a browser behaviour tied to type="number". A JS-side handler could restore this; left as a possible follow-up to keep this PR focused on the bug.
  • Client-side HTML5 min/max/step validation is no longer enforced by the browser for float/decimal. The existing keydown filter (regexValidFloat, non-numeric key blocking) still prevents junk input, and server-side validation is unaffected.
  • Emitted value type changed from number to string for float/decimal. The API coerces string → numeric on insert/update for decimal/float columns, and the round-trip was verified end-to-end against SQLite.

Tested Scenarios

Repro (before fix)

  1. Set Directus app language to Dutch (nl-NL) or German (de-DE).
  2. Create a collection with a Decimal field.
  3. Open the item form and type 300,44 into the field, the browser shows "Geen getal" ("not a number") and blocks input on Chrome/Safari/Edge.

After fix — verified locally on Chrome with SQLite backend

  1. Dutch locale, Decimal field: typed 300,44 → accepted, no warning → saved → DB stores 300.44 (numeric) → reopen shows 300,44
  2. Dutch locale, Float field: same as above ✅
  3. English locale, Decimal field: displays 300.44, typing and saving works, DB stores 300.44
  4. DOM inspection: float/decimal fields render as <input type="text" inputmode="decimal"> with no lang attribute; integer fields still <input type="number">
  5. Unit tests: 66/66 pass across v-input.test.ts (46) and input.test.ts (20). New tests:
    • Float text input renders stored 300.44 as 300,44 under nl-NL.
    • Float text input leaves 300.44 as-is under en-US.
    • Float text input emits '300.45' when user types 300,45 under nl-NL.
    • Float/decimal field types pass type="text" and inputmode="decimal" through input.vue.
    • Integer field type still passes type="number" and no inputmode.

Review Notes / Questions

  • Out of scope for this PR: the list-view display of decimal values is not locale-aware (it still shows the raw stored 300.44 regardless of app locale). That would need changes to the display component and feels like a separate PR.
  • Possible follow-up: a JS handler on ArrowUp / ArrowDown could restore keyboard stepping for float/decimal inputs. Not done here to keep the diff minimal and focused on the reported bug.
  • APP_NUMERIC_TYPES (['integer', 'float']) is still used elsewhere (get-js-type, slider, input/index.ts) for semantic field-type checks and is intentionally left intact, only the render-time dispatch in input.vue was changed.

Checklist

  • Added or updated tests
  • Documentation PR created here or not required — not required (no user-facing docs describe the old decimal-input behaviour)
  • OpenAPI package PR created here or not required — not required (no API surface changes)

Fixes #24803

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decimal type input field shows wrong decimal point seperator (,) instead of (.)

1 participant