Locales & i18n
Using raqam with non-Latin digit systems — Persian, Arabic, Bengali, Hindi, Thai.
raqam uses Intl.NumberFormat for all formatting and parsing. You never
hardcode separators — they're extracted dynamically from the browser's
internationalization engine.
Basic locale switching
Pass any BCP 47 locale tag to the locale prop:
// German: 1.234,56
<NumberField.Root locale="de-DE" formatOptions={{ style: "currency", currency: "EUR" }} />
// French: 1 234,56 €
<NumberField.Root locale="fr-FR" formatOptions={{ style: "currency", currency: "EUR" }} />
// Japanese: ¥1,234
<NumberField.Root locale="ja-JP" formatOptions={{ style: "currency", currency: "JPY" }} />Separators, currency symbols, minus signs, and digit systems are all resolved automatically — no configuration needed.
Non-Latin digit systems
Five locale plugins add support for writing systems that use non-ASCII digits:
| Plugin | Script | Digits | BCP 47 tags |
|---|---|---|---|
raqam/locales/fa | Persian (Extended Arabic-Indic) | ۰۱۲۳۴۵۶۷۸۹ | fa, fa-IR, fa-AF |
raqam/locales/ar | Arabic-Indic | ٠١٢٣٤٥٦٧٨٩ | ar, ar-EG, ar-SA, … |
raqam/locales/hi | Devanagari | ०१२३४५६७८९ | hi, hi-IN, mr, ne |
raqam/locales/bn | Bengali | ০১২৩৪৫৬৭৮৯ | bn, bn-BD, bn-IN |
raqam/locales/th | Thai | ๐๑๒๓๔๕๖๗๘๙ | th, th-TH |
Installing a plugin
Import the locale plugin once, anywhere in your app (it runs as a side effect):
// app/layout.tsx or src/main.tsx
import "raqam/locales/fa"; // adds Persian digit support
import "raqam/locales/ar"; // adds Arabic-Indic digit supportThen use the locale normally:
<NumberField.Root locale="fa-IR" />Without the plugin, Persian ۱۲۳ typed by the user won't be normalized. With
the plugin, ۱۲۳ is accepted and internally treated as 123.
All plugins at once
import "raqam/locales"; // imports fa, ar, hi, bn, thEach plugin is tiny — well under 200 B (e.g. raqam/locales/fa is 189 B, min +
brotli), enforced in CI via .size-limit.json.
Locale metadata exports
If you want to build locale pickers or feature flags, the raqam/locales
entrypoint also re-exports the supported locale lists (the main raqam entry
does not):
import {
FA_LOCALE_CODES,
AR_LOCALE_CODES,
BN_LOCALE_CODES,
HI_LOCALE_CODES,
TH_LOCALE_CODES,
} from "raqam/locales";These arrays are useful when you want to map a runtime locale to a matching plugin without hardcoding the supported tags yourself.
Lakh/crore grouping
South Asian locales (hi-IN, bn-BD, mr-IN) use a different grouping
pattern than Western locales (lakhs and crores):
en-US: 10,000,000 (millions)
hi-IN: 1,00,00,000 (crores)raqam handles this automatically via Intl.NumberFormat. Native digit input for
Marathi (mr) and Nepali (ne) ships in the raqam/locales/hi plugin.
RTL locales
Arabic, Persian, Hebrew, and Urdu are right-to-left. raqam automatically detects RTL locales and applies the correct rendering. See the RTL guide for details.
Custom digit blocks
If you need a digit system not covered by the built-in plugins, register it:
import { registerLocale } from "raqam/core";
// Mongolian digits: ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ (U+1810–U+1819)
registerLocale({
digitBlocks: [[0x1810, 0x1819]],
});Locale plugins are tree-shakeable. Only the plugins you import are included in your bundle.
Supported locale examples
const LOCALES = [
{ locale: "en-US", label: "English (US)", formatOptions: { style: "currency", currency: "USD" } },
{ locale: "de-DE", label: "German", formatOptions: { style: "currency", currency: "EUR" } },
{ locale: "fr-FR", label: "French", formatOptions: { style: "currency", currency: "EUR" } },
{ locale: "fa-IR", label: "Persian (Iran)", formatOptions: { style: "currency", currency: "IRR" } },
{ locale: "ar-EG", label: "Arabic (Egypt)", formatOptions: { style: "currency", currency: "EGP" } },
{ locale: "hi-IN", label: "Hindi (India)", formatOptions: { style: "currency", currency: "INR" } },
{ locale: "bn-BD", label: "Bengali (Bangladesh)", formatOptions: { style: "currency", currency: "BDT" } },
{ locale: "th-TH", label: "Thai", formatOptions: { style: "currency", currency: "THB" } },
{ locale: "ja-JP", label: "Japanese", formatOptions: { style: "currency", currency: "JPY" } },
{ locale: "zh-CN", label: "Chinese (Simplified)", formatOptions: { style: "currency", currency: "CNY" } },
];