Arabic Digits: Unicode and JavaScript Reference

Arabic-Indic digits and Extended Arabic-Indic digits encode the same decimal values as Western digits using different Unicode characters. This reference maps all thirty characters, provides copyable JavaScript conversion examples, and separates numeric storage from localized display so validation, parsing, and sorting can follow an explicit input policy.

Thirty digits, three encodings

The value column is the decimal value, not the encoded character. Arabic-Indic digits occupy U+0660–U+0669; Extended Arabic-Indic digits occupy U+06F0–U+06F9. HTML entities and JavaScript escapes below represent individual characters, not entire formatted numbers.

Thirty digits, three encodings
ValueSetGlyphCode pointHTML entityJS escape
0Western / ASCII0U+00300\u0030
1Western / ASCII1U+00311\u0031
2Western / ASCII2U+00322\u0032
3Western / ASCII3U+00333\u0033
4Western / ASCII4U+00344\u0034
5Western / ASCII5U+00355\u0035
6Western / ASCII6U+00366\u0036
7Western / ASCII7U+00377\u0037
8Western / ASCII8U+00388\u0038
9Western / ASCII9U+00399\u0039
0Arabic-Indic٠U+0660٠\u0660
1Arabic-Indic١U+0661١\u0661
2Arabic-Indic٢U+0662٢\u0662
3Arabic-Indic٣U+0663٣\u0663
4Arabic-Indic٤U+0664٤\u0664
5Arabic-Indic٥U+0665٥\u0665
6Arabic-Indic٦U+0666٦\u0666
7Arabic-Indic٧U+0667٧\u0667
8Arabic-Indic٨U+0668٨\u0668
9Arabic-Indic٩U+0669٩\u0669
0Extended Arabic-Indic۰U+06F0۰\u06F0
1Extended Arabic-Indic۱U+06F1۱\u06F1
2Extended Arabic-Indic۲U+06F2۲\u06F2
3Extended Arabic-Indic۳U+06F3۳\u06F3
4Extended Arabic-Indic۴U+06F4۴\u06F4
5Extended Arabic-Indic۵U+06F5۵\u06F5
6Extended Arabic-Indic۶U+06F6۶\u06F6
7Extended Arabic-Indic۷U+06F7۷\u06F7
8Extended Arabic-Indic۸U+06F8۸\u06F8
9Extended Arabic-Indic۹U+06F9۹\u06F9

Choose a numbering system explicitly

Arabic-Indic digits occur in Arabic-language contexts; Extended Arabic-Indic digits are used in Persian and Urdu contexts. European digits are also used in many Arabic-speaking North African countries. Regional preferences vary. A language tag alone is not a promise of a particular digit set: inspect resolvedOptions().numberingSystem and specify numberingSystem when your product requires a stable choice.

Intl.NumberFormat formats numeric values; it does not parse localized input. These examples deliberately request arab, arabext and latn rather than relying on the runtime’s locale defaults.

for (const locale of ["ar-EG", "ar-SA", "ar-MA", "fa-IR", "ur-PK"]) {
  console.log(locale, new Intl.NumberFormat(locale).resolvedOptions().numberingSystem);
}

const value = 2026;
for (const numberingSystem of ["arab", "arabext", "latn"]) {
  const formatter = new Intl.NumberFormat("ar-EG", {
    numberingSystem, useGrouping: false,
  });
  console.log(formatter.format(value));
}

Normalize digits before validating an integer

This converter maps only the two digit ranges. It intentionally leaves signs, spaces, decimal separators and grouping separators untouched. The anchored ASCII check then rejects everything outside a nonnegative integer policy. Keep identifiers as strings when leading zeros matter.

function asciiDigits(input) {
  return input.replace(/[\u0660-\u0669\u06F0-\u06F9]/g, char =>
    String(char.codePointAt(0) - (char <= "\u0669" ? 0x0660 : 0x06F0))
  );
}
const normalized = asciiDigits("\u0661\u0662");
if (!/^[0-9]+$/.test(normalized)) throw new Error("Expected integer digits");
const value = Number(normalized);
if (!Number.isSafeInteger(value)) throw new Error("Outside safe integer range");

Parsing and sorting traps

parseInt does not provide localized number parsing. A leading Arabic-Indic digit is not a base-10 ASCII digit, so parseInt returns NaN; it can also accept an ASCII prefix while ignoring a trailing invalid character. Normalize and validate the entire input first.

JavaScript \d matches ASCII digits. Use explicit ranges for the three sets accepted here; Unicode Decimal_Number also includes other scripts. Default string sorting compares encoded strings rather than numeric values. After validated numeric conversion, use a numeric comparator. For long integers, consider BigInt instead of losing precision with Number.

parseInt("\u0661\u0662", 10); // NaN
parseInt("12x", 10); // 12: not complete validation
[10, 2].sort((a, b) => a - b); // [2, 10]

Related references and tools

Questions and answers

Does Intl.NumberFormat parse Arabic digits?

No. Intl.NumberFormat formats numeric values for display. Normalize supported input digit ranges separately, validate the entire input, and then convert according to your numeric policy.

Should identifiers become JavaScript numbers?

Keep identifiers as strings when leading zeros matter. For arithmetic, validate first and check Number.isSafeInteger; use an appropriate alternative for larger integers.

Sources