# NumberParser

A NumberParser can be used to perform locale-aware parsing of numbers from Unicode strings,
as well as validation of partial user input. It automatically detects the numbering system
used in the input, and supports parsing decimals, percentages, currency values, and units
according to the locale.

## Introduction

Numbers can be formatted in many different ways, including percentages, units, decimals, currencies, and more. In addition, number formatting varies around the world. For example, currency symbols, units, decimal separators, and even digits themselves may be different across languages and regions. While Latin numerals are the most commonly used, many other numbering systems are also used around the world. For example, in the Latin numbering system, the number twelve is represented as “12”, and in the Arabic decimal system, it is “١٢”.

`NumberParser` is designed to validate and parse numbers from user input according to a specific locale and format. It supports several different numbering systems including the Latin, Arabic, and Han positional numbering systems, as well as parsing decimals, percentages, currencies, and unit values. The numbering system is automatically detected from the input string. This means that users may input numbers in a different numbering system than the default for their locale, e.g. a Latin number in an Arabic locale.

Parsing numbers while taking into account all locale-specific detail is quite complex and error-prone. `NumberParser` uses information about the locale and expected format for a number in order to parse it correctly. This means it is somewhat strict about the accepted formats. It is not designed to handle use cases where the user can enter numbers in an unknown format (e.g. either a unit value *or* a percentage), this must be known up front or selected via an external UI control.

Read our [blog post](how-we-internationalized-our-numberfield.md) for more details on how the number parser is implemented.

### Example

To create a `NumberParser`, call the constructor with a locale string and optional format options. The same options as supported by the [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat) object are supported. See the docs on MDN linked above for full details.

This example creates a parser than accepts values in inches in the `en-US` locale.

```tsx
import {NumberParser} from '@internationalized/number';

let parser = new NumberParser('en-US', {style: 'unit', unit: 'inch'});
parser.parse('10 in'); // 10
```

## Interface

### Constructor

| Parameter | Type | Description |
|-----------|------|-------------|
| `locale` | `string` | — |
| `options` | `Intl.NumberFormatOptions` | — |

## Parsing

Numbers can be parsed using the `parse` method. If the input is not a valid number according to the locale and format options, `NaN` is returned instead.

### Decimals

By default, or with the `style: 'decimal'` option, `NumberParser` supports parsing decimal values.

```tsx
let parser = new NumberParser('en-US');
parser.parse('10.5'); // 10.5
parser.parse('-25.6'); // -25.6
parser.parse('1,000,000'); // 1000000
parser.parse('١٢'); // 12
parser.parse('X'); // NaN
```

### Percentages

The `style: 'percent'` option can be provided to the constructor to parse percentage values. In this mode, parsed values are divided by 100, for example the string `"45%"` is parsed as `0.45`. The `maximumFractionDigits` option is also taken into account during parsing, and the resulting value is rounded accordingly.

```tsx
let parser = new NumberParser('en-US', {
  style: 'percent',
  maximumFractionDigits: 2
});

parser.parse('45%'); // 0.45
parser.parse('62.3333%'); // 0.6233
```

### Currency values

The `style: 'currency'` option can be provided to the constructor to parse currency values. The `currency` option must also be passed to set the currency code (e.g. `USD`) to use. In addition, the `currencyDisplay` option can be used to choose whether to display the currency symbol, currency code, or currency name. Finally, the `currencySign` option can be set to `accounting` to use accounting notation for negative numbers, which uses parentheses rather than a minus sign in some locales.

Note that partial input is not accepted by this method. For example, if a partial currency symbol is included in the string, `NaN` will be returned. The `isValidPartialNumber` method, described below, can be used in scenarios where you have partial user input.

```tsx
let parser = new NumberParser('en-US', {
  style: 'currency',
  currency: 'EUR',
  currencyDisplay: 'code',
  currencySign: 'accounting'
});

parser.parse('45'); // 45
parser.parse('EUR 45'); // 45
parser.parse('EUR 26.45'); // 26.45
parser.parse('EUR -25'); // -25
parser.parse('(EUR 25)'); // -25
parser.parse('EU 45'); // NaN (partial currency symbol)
parser.parse('$45'); // NaN (different currency symbol)
```

### Unit values

The `style: 'unit'` option can be passed to the constructor to parse values with a unit of measurement. The `unit` option must also be passed to set which unit to use (e.g. `inch`). In addition, the `unitDisplay` option can be used to choose whether the unit is accepted in long, short, or narrow format.

```tsx
let parser = new NumberParser('en-US', {
  style: 'unit',
  unit: 'inch',
  unitDisplay: 'long'
});

parser.parse('12'); // 12
parser.parse('12 inches'); // 12
parser.parse('1 inch'); // 1
parser.parse('12 in'); // NaN (partial unit)
parser.parse('23 ft'); // NaN (different unit)
```

## Validation

`NumberParser` can also be used to validate partial user input using the `isValidPartialNumber` method, for example, as the user types into an input field. The `parse` method only accepts complete input, whereas `isValidPartialNumber` determines if the given input *might* be valid but incomplete. For example, only entering a decimal point is invalid when passed to `parse`, but accepted by `isValidPartialNumber`.

Note that partial units and currency symbols are *not* accepted. Since the unit itself must be known upfront and passed to the constructor, it is better to only allow entering a number rather and autocompleting the unit rather than typing it in one letter at a time.

```tsx
let parser = new NumberParser('en-US', {style: 'unit', unit: 'inch'});
parser.isValidPartialNumber('.'); // true
parser.isValidPartialNumber('.2'); // true
parser.isValidPartialNumber('10 in'); // true
parser.isValidPartialNumber('10 i'); // false
parser.isValidPartialNumber('10 x'); // false
```

## Detecting the numbering system

Under the hood, `NumberParser` automatically detects the numbering system used in the input. This is also exposed by the `getNumberingSystem` method, which returns a Unicode script identifier. For example, this can be used to create a number formatter based on the numbering system used in the input.

```tsx
import {NumberParser, NumberFormatter} from '@internationalized/number';

let parser = new NumberParser('en-US', {style: 'decimal'});
let numberingSystem = parser.getNumberingSystem('١٢'); // -> 'arab'

let formatter = new NumberFormatter('en-US', {style: 'decimal', numberingSystem});
formatter.format(12); // '١٢'
```
