HTML5 input[type=number] and Locale Formatting
The input[type=number] element introduced in HTML5 has a well-documented locality problem: browsers are permitted — and in several cases required — to display numbers using locale-specific formatting, while the value attribute and form submission always use a dot as the decimal separator. This split between the displayed representation and the programmatic value is the source of a class of bugs that appear only for users whose system locale uses a comma as the decimal separator. This page covers the display-versus-value split, the specific decimal separator divergence between comma and period locales, validation behaviour under different locale conditions, and how browser implementations compare. Sections cover each of these topics plus practical code patterns. This page is part of the web development section and connects to the open web topic hub.
The attribute-versus-display split
The HTML specification requires that input[type=number] always present its programmatic value — the value DOM property, the submitted form field value, and the value returned by input.valueAsNumber — using a dot as the decimal separator, regardless of locale. The display, however, is under the browser's discretion: a browser rendering the input for a German-locale user may display 1,5 on screen while input.value returns 1.5. This split is intentional in the specification, but its implications for form validation and JavaScript interaction are not obvious and cause real bugs in real applications.
This design reflects a broader HTML philosophy: the programmatic interface remains locale-neutral so that JavaScript and server-side code can reliably parse values without locale-specific handling, while the rendered control can be user-friendly for the local convention. The philosophy is sound but the execution is complicated by inconsistent browser implementation and by the validation model that intersects with locale handling in non-obvious ways.
Decimal separator differences across locales
The fundamental tension is between two large groups of countries with incompatible conventions. Most English-speaking countries, along with China, Japan, and a number of others, use a dot as the decimal separator: 3.14. A large part of continental Europe, most of South America, and several other regions use a comma: 3,14. The HTML number input sits directly in the middle of this divide.
Testing input[type=number] in Firefox with a German (de-DE) locale set at the OS level showed the browser rendering the number display with a comma separator: a programmatic value of 1.5 displayed as 1,5. The input.value DOM property still returned 1.5. When a user typed 1,5 into the field — the natural input for their locale — Firefox accepted it as valid and set input.value to 1.5. Chrome on the same locale configuration displayed the value using a dot and rejected comma-separated input as invalid. Safari followed Chrome's behaviour. This three-way divergence existed across major browser versions in 2019–2022, and while improvements have been made, the inconsistency has not fully resolved across all locale/browser combinations.
The Firefox behaviour is arguably more correct from a user experience standpoint — showing the number in the user's local format and accepting their natural input — but it creates a specific problem: web applications that validate the field value client-side using JavaScript's parseFloat() without locale awareness will correctly parse input.value (which is always dot-delimited) but may fail to handle the case where the browser is currently displaying a comma-formatted value that has not yet been committed to the programmatic value.
Validation behaviour
The validation model for input[type=number] interacts with locale handling in two places: the browser's built-in constraint validation and any JavaScript validation layer applied by the application.
The most common bug triggered by this locale split occurs when a developer reads the field's displayed text rather than its value property. input.value always returns the machine-readable dot-decimal string. input.textContent or anything that reads the visible rendering may return a locale-formatted string. If your validation code reads the wrong property and runs parseFloat() on a comma-formatted string, it will return NaN for valid user input, producing false validation failures for users in comma-locale regions. Always use input.value or input.valueAsNumber in JavaScript validation code, never the visible text content.
The browser's built-in constraint validation — triggered by form.checkValidity() or on form submission — applies the locale-appropriate acceptance criteria. A browser configured for a comma-locale that accepts comma input will mark that input as valid through its constraint system. A browser that only accepts dot-decimal input will mark comma input as invalid. This means that input.checkValidity() alone is not a reliable cross-locale validation signal.
Cross-browser consistency
Browser handling of number input locale formatting has historically varied significantly. Firefox has been the most locale-aware implementation, adjusting both display and accepted input to match the OS locale. Chrome and Edge (Chromium-based) have historically been less locale-adaptive, typically displaying and accepting only dot-decimal regardless of OS locale — making them predictable but less user-friendly for comma-locale users. Safari's behaviour has generally tracked Chrome. The practical implication is that your application may work correctly in Chrome testing but fail for Firefox users in affected locales.
Early HTML5 implementation period (2012–2017): The input[type=number] element had extremely inconsistent implementation across browsers. Internet Explorer did not support it at all through IE 10. Firefox's locale-aware implementation was notable for being the only major browser that treated the element as genuinely locale-sensitive. Many web applications effectively worked only in English locales because number input handling was never tested otherwise.
Current state (2024–2026): All major browsers support input[type=number]. The locale handling divergence between Firefox and Chromium-based browsers remains the principal interoperability gap. WHATWG has clarified the specification language on locale handling, but implementation differences persist. The practical advice for production applications is to treat the locale behaviour as undefined and validate through input.valueAsNumber, which returns NaN for invalid entries regardless of how the browser handled the display formatting.
Practical code patterns
The safest approach is to avoid relying on locale-specific display behaviour for programmatic processing and to validate through the numeric value property.
const input = document.querySelector('input[type="number"]');
// Safe: always returns a float or NaN, regardless of locale display
const numericValue = input.valueAsNumber;
if (isNaN(numericValue)) {
// Input is empty or contains non-numeric content
showError('Please enter a valid number.');
} else if (numericValue < input.min || numericValue > input.max) {
showError(`Value must be between ${input.min} and ${input.max}.`);
} else {
// Valid — use numericValue for further processing
processValue(numericValue);
}
When displaying a number back to the user outside the input element, use the Intl.NumberFormat API rather than string interpolation — this correctly formats the number for the user's locale without tying that formatting to the input element's value transmission.
// Format for display — respects user locale
const formatter = new Intl.NumberFormat(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
const displayString = formatter.format(numericValue);
// "1.50" in en-US, "1,50" in de-DE — both correct for that locale
For cases where input[type=number] is genuinely problematic — highly localised user bases, complex number formatting requirements, or applications that need full control over validation messaging — input[type=text] combined with explicit inputmode="decimal" and a JavaScript parsing layer is a viable alternative. It sacrifices the built-in spinner control and constraint validation but provides complete control over accepted input format.
The broader implication
The input[type=number] locale situation is a good example of how HTML's "localise the display, normalise the value" design intention can produce real interoperability problems when browser implementations are inconsistent. The specification's position is reasonable; the implementation gap is the problem. Until all major browsers converge on identical locale handling behaviour, the only safe approach is to treat the display as informational and always process values through the numeric DOM property.