Dates and numbers Recommended
Read numbers the way people type them
Read numbers and amounts in the user's locale, so 1 234,50 in Sweden and 1,234.50 in the US both mean the same amount. Never parse money as a floating-point number.
How to do it
Use a text input with inputmode="decimal", as mobile inputs explains, because type="number" drops input it can't read. Find the locale's group and decimal signs with the formatter. Accept any space where the group sign is a space. Check the groups are 3 digits, then remove them and swap the decimal sign for a dot. Send money as a decimal string or in minor units, as money requires. Reject ambiguous input instead of guessing.
Example
const escapeForRegExp = (sign: string) => sign.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export function parseAmount(text: string, locale: string): string | null {
const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
const group = parts.find(part => part.type === 'group')?.value ?? ',';
const decimal = parts.find(part => part.type === 'decimal')?.value ?? '.';
const input = /\s/.test(group) ? text.trim().replace(/\s/g, group) : text.trim();
const shape = new RegExp(`^-?(\\d{1,3}(${escapeForRegExp(group)}\\d{3})*|\\d+)(${escapeForRegExp(decimal)}\\d+)?$`);
return shape.test(input) ? input.replaceAll(group, '').replace(decimal, '.') : null;
}
import re
from decimal import Decimal
from babel import Locale
from babel.numbers import NumberFormatError, get_group_symbol, parse_decimal
def parse_amount(text: str, locale_tag: str) -> Decimal | None:
locale = Locale.parse(locale_tag, sep="-")
if get_group_symbol(locale).isspace():
text = re.sub(r"\s", "", text)
try:
return parse_decimal(text, locale=locale, strict=True)
except NumberFormatError:
return None
How to check
Unit tests parse 1 234,50 in sv-SE, typed with a plain space, and 1,234.50 in en-US into the same value, 1234.50. The texts 12abc and, in en-US, 1.234,50 are rejected.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/dates-and-numbers/locale-number-input.