Localize Dates, Numbers & Currency for Arabic Users

Last updated: February 2, 2026

Localizing dates, numbers, and currencies for Arabic users is not just “flip the UI to RTL.” It touches calendars (Gregorian and Hijri), digit sets (Arabic‑Indic vs Latin), thousands/decimal separators, currency symbols and placement, negative values, and bidi safety when Latin tokens appear inside Arabic text. This practical 2026 guide shows how to implement, test, and govern localization so Arabic interfaces feel native and stay consistent across web, iOS, and Android—without guesswork or brittle hacks.

Who this guide is for and outcomes

For product managers, content designers, localization engineers, and QA specialists building Arabic experiences. After following this guide, you will:

  • Choose calendar, date formats, and a clear digits policy with documented fallbacks.
  • Format currencies correctly for GCC, North Africa, and global contexts using standard APIs.
  • Protect mixed LTR/RTL numeric fragments to avoid visual corruption.
  • Ship a token‑based, testable setup you can reuse across web and mobile.
Engineering note: Arabic localization is data‑driven. Align on one source of truth (CLDR/ICU versions, calendar and numbering policies) so server, web, iOS, and Android don’t drift.

Quick start: the minimum viable setup

  1. Decide policies:
    • Digits: Arabic‑Indic for editorial; Latin for dashboards/codes (or one style everywhere—just be consistent).
    • Calendar: Gregorian default; add Hijri (Umm al‑Qura) where culturally expected (KSA public sector, religious dates).
    • Currency: Use locale formatters; accounting negatives in finance views.
  2. Implement with standard APIs:
    • Web: Intl.DateTimeFormat/NumberFormat (specify calendar and numberingSystem).
    • iOS/Android: Locale‑aware formatters; set calendars and numbering where supported.
  3. Make bidi safe:
    • Wrap Latin tokens (ISO dates, currency codes, IDs) in bdi or isolate via CSS.
  4. Test and govern:
    • Add unit tests for formats; visual snapshots for RTL; a one‑page style guide with examples.

Foundations: Arabic locales, scripts, and calendars

“Arabic users” are not one group. At minimum, consider Gulf (e.g., ar‑SA, ar‑AE), Levant (ar‑JO, ar‑LB), North Africa (ar‑EG, ar‑MA, ar‑TN), and diaspora users on mixed settings.

  • Script and digits: Arabic‑Indic digits (٠١٢٣٤٥٦٧٨٩) feel native in editorial content; many data‑heavy UIs prefer Latin digits (0123456789). Pick one per surface.
  • Calendars: Gregorian dominates commerce. KSA often references Hijri (Umm al‑Qura) alongside Gregorian for official/holiday contexts.
  • Directionality: UI mirrors RTL; but codes/IDs often stay LTR inside RTL paragraphs—use isolation.
Production tip: Lock browser/server runtimes to compatible ICU/CLDR versions. Small data differences (month names, minor units) can produce subtle inconsistencies.

Dates: Gregorian vs Hijri, formats, and UX choices

Choose a default that fits your product; offer a toggle or dual display where appropriate. Enterprise/finance/travel: Gregorian default. Government/education/KSA holidays: show Hijri alongside.

Format tiers (treat as tokens)

  • Long (human‑friendly): الثلاثاء، ٢٣ يوليو ٢٠٢٦
  • Medium/UI: ٢٣‏/٠٧‏/٢٠٢٦ or 23/07/2026 per digits policy
  • Short/data: 2026‑07‑23 (ISO‑like, LTR, isolated)

Dual‑calendar display

In KSA and public sector apps, dual dates reduce doubt. Keep the primary date prominent and the secondary in a lighter style:

Primary (Gregorian, Arabic‑Indic): الثلاثاء، ٢٣ يوليو ٢٠٢٦
Secondary (Hijri, Umm al‑Qura): ١٧ ذو الحجة ١٤٤٧هـ

Common pitfalls

  • Mirroring the grid but forgetting weekday labels or navigation arrows.
  • Mixing digit styles within the same component.
  • Assuming Gregorian when Hijri is expected (or vice versa).
  • Truncating long month names—Arabic strings are often longer than English.
QA hint: Hijri calculations differ by provider. Align on Umm al‑Qura for KSA and document verification dates (e.g., Eid) so QA can assert exact outputs.

Numbers: Arabic‑Indic vs Latin digits, separators, and units

Decide your digits policy per surface (marketing vs dashboards) and enforce it with a formatter and CSS. Use locale data for thousands/decimal separators—do not hardcode.

Digits policy patterns

SurfaceRecommended digitsRationale
Marketing/editorialArabic‑Indic (١٢٣)Feels native in headlines/CTAs
Dashboards/logsLatin (123)Clarity with codes, IDs, and filters
Finance/invoicesPer regulationAlignment, decimals, and audit consistency

Grouping and decimals

Arabic locales often use “٬” (thousands) and “٫” (decimal). Let the formatter decide.

Arabic‑Indic: ١٢٬٣٤٥٫٦٧
Latin:        12,345.67

Units and measurement

  • Use localized unit names and correct plurals.
  • Prefer Intl.NumberFormat with style: “unit” for automatic rendering (where supported).
  • Confirm local standards (°C, km/h) and abbreviations.
Performance (web): Cache/memoize NumberFormat/DateTimeFormat instances. Creating them per render is expensive.

Currencies: symbols, placement, decimals, and negatives

Currency style varies by country and product type. Use a locale‑aware currency formatter; do not concatenate symbols and amounts.

Examples (illustrative)

// Arabic‑Indic digits and localized separators
ar-SA (SAR): ‏١٬٢٣٤٫٥٠ ر.س
ar-AE (AED): ‏١٬٢٣٤٫٥٠ د.إ
ar-EG (EGP): ‏١٬٢٣٤٫٥٠ ج.م
ar-MA (MAD): ‏١٬٢٣٤٫٥٠ د.م

// Latin digits policy
ar-SA (Latin): SAR 1,234.50
  • Negative values: Accounting often prefers parentheses “(1,234.50)”. Use currencySign: "accounting" where available.
  • Decimals: Some currencies use 3 decimals (e.g., KWD, BHD). Trust CLDR minor units—do not hardcode 2 decimals everywhere.
  • Ambiguity: When multiple currencies appear, show ISO codes (SAR, AED) instead of symbols.

Web implementation with Intl (ECMA‑402)

Modern browsers expose robust locale formatting via Intl. Always pass an Arabic locale and desired options (calendar, numberingSystem, currency).

Dates

// Gregorian, Arabic‑Indic digits, long
new Intl.DateTimeFormat('ar', {
  calendar: 'gregory',
  numberingSystem: 'arab',
  dateStyle: 'long'
}).format(new Date());

// Hijri (Umm al‑Qura) — KSA
new Intl.DateTimeFormat('ar-SA', {
  calendar: 'islamic-umalqura',
  numberingSystem: 'arab',
  dateStyle: 'long'
}).format(new Date());
Edge case: ar vs ar-SA defaults can vary. Specify both calendar and numberingSystem explicitly.

Numbers and units

// Arabic‑Indic digits with grouping
new Intl.NumberFormat('ar-EG', {
  numberingSystem: 'arab',
  useGrouping: true,
  maximumFractionDigits: 2
}).format(12345.678);

// Units (km/h)
new Intl.NumberFormat('ar', {
  style: 'unit',
  unit: 'kilometer-per-hour',
  unitDisplay: 'short',
  numberingSystem: 'arab'
}).format(120);

Currency

// AED with accounting negatives
new Intl.NumberFormat('ar-AE', {
  style: 'currency',
  currency: 'AED',
  currencySign: 'accounting',
  numberingSystem: 'arab',
  minimumFractionDigits: 2
}).format(-1234.5);

// Show ISO code instead of symbol
new Intl.NumberFormat('ar-SA', {
  style: 'currency',
  currency: 'SAR',
  currencyDisplay: 'code',
  numberingSystem: 'latn'
}).format(1234.5);
SSR alignment: If you format on both server and client, align locales/options exactly. Snapshot tests catch mismatches early.

Lists and ranges

// Range: 1–3 days
new Intl.NumberFormat('ar', { numberingSystem: 'arab' }).formatRange(1, 3);

// List of currencies (filters)
new Intl.ListFormat('ar', { type: 'conjunction' })
  .format(['SAR', 'AED', 'EGP']);

Server‑side equivalents

# Python (Babel)
from babel.numbers import format_currency
from babel.dates import format_datetime
format_currency(1234.5, 'AED', locale='ar_AE')   # ‏١٬٢٣٤٫٥٠ د.إ
format_datetime(datetime.now(), locale='ar_SA')   # Specify calendar if needed

# PHP (Intl)
$fmt = new NumberFormatter('ar_EG', NumberFormatter::CURRENCY);
echo $fmt->formatCurrency(1234.5, 'EGP');

# Java (ICU4J)
NumberFormat nf = NumberFormat.getCurrencyInstance(new ULocale("ar_SA"));
nf.setCurrency(Currency.getInstance("SAR"));
nf.format(1234.5);

Mobile implementation (iOS and Android)

iOS

// Dates
let fmt = DateFormatter()
fmt.locale = Locale(identifier: "ar_SA")
fmt.calendar = Calendar(identifier: .islamicUmmAlQura)
fmt.dateStyle = .long
fmt.string(from: Date())

// Numbers & currency
let nf = NumberFormatter()
nf.locale = Locale(identifier: "ar_AE")
nf.numberStyle = .currency
nf.currencyCode = "AED"
// Accounting negatives: set negativeFormat if needed
nf.negativeFormat = "(\u00A4#,##0.00)"

Android

// Kotlin
val locale = Locale("ar", "EG")
val nf = java.text.NumberFormat.getCurrencyInstance(locale)
nf.currency = java.util.Currency.getInstance("EGP")
nf.format(1234.5)

// Java time (API 26+)
val formatter = java.time.format.DateTimeFormatter
  .ofLocalizedDate(java.time.format.FormatStyle.LONG)
  .withLocale(Locale("ar", "SA"))
val out = java.time.LocalDate.now().format(formatter)
  • Enable RTL support and test with Arabic locales so widgets mirror correctly.
  • For Arabic‑Indic digits, use a locale keyword where supported (e.g., Locale.forLanguageTag("ar-EG-u-nu-arab")) and verify on your minSdk.

Bidi safety for mixed LTR/RTL content

When numbers, codes, or currency symbols appear inside Arabic, isolate LTR fragments to prevent reordering.

<p dir="rtl">
  تم دفع <bdi>SAR 1,234.50</bdi> عبر <bdi>TX-9A1C</bdi> بتاريخ
  <bdi>2026-07-23</bdi>.
</p>

<span dir="auto">Invoice #A123 في جدة</span>
  • Use <bdi>, dir="auto", or CSS unicode-bidi: isolate.
  • Render ISO dates and codes in LTR for clarity.
  • Format currency atomically (formatter output) or wrap the whole token in bdi.

Fonts, typography, and rendering stability

Arabic shaping and numerals require good font coverage. Poor stacks cause fallback jumps and misaligned digits.

  • Pick Arabic‑optimized fonts with matching Latin sets to avoid style clashes.
  • Enable tabular figures in data‑dense views so digits line up.
/* CSS for stable numeric columns */
.data-number {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  text-align: end;     /* logical in RTL */
  direction: ltr;      /* if using Latin digits */
  unicode-bidi: isolate;
}

Forms and input handling (normalizing digits)

Users will type both Arabic‑Indic and Latin digits. Normalize on submit so backends are consistent and validators don’t fail.

// Map Arabic-Indic (U+0660–U+0669) and Eastern Arabic (U+06F0–U+06F9) to Latin
function normalizeDigits(str) {
  const map = {
    '\u0660':'0','\u0661':'1','\u0662':'2','\u0663':'3','\u0664':'4',
    '\u0665':'5','\u0666':'6','\u0667':'7','\u0668':'8','\u0669':'9',
    '\u06F0':'0','\u06F1':'1','\u06F2':'2','\u06F3':'3','\u06F4':'4',
    '\u06F5':'5','\u06F6':'6','\u06F7':'7','\u06F8':'8','\u06F9':'9',
  };
  return str.replace(/[\u0660-\u0669\u06F0-\u06F9]/g, d => map[d]);
}

// HTML
// Use inputmode to show numeric keyboard; keep type="text" to avoid strict parsing quirks
<input inputmode="numeric" pattern="[0-9]*" dir="ltr" />
  • Use inputmode to surface numeric keyboards without forcing browser parsing.
  • Normalize digits server‑side too for reliability.
  • For CSV/Excel exports, prefer ISO dates and currency codes; be cautious with commas in locales using Arabic comma.

QA plan, acceptance criteria, and automation

Make a repeatable checklist. Test formatters with unit tests and verify UI with RTL snapshots.

Acceptance criteria

  • Dates follow the chosen calendar and token (long/medium/short) consistently.
  • Digits policy is consistent per surface (no mixing within one view).
  • Currency shows correct symbol/code, spacing, decimals, and negative style.
  • Numeric columns align; no truncation for long month names or large amounts.
  • Bidi isolation prevents flipped tokens in paragraphs and controls.

Automation ideas

// Jest: AED accounting format in ar-AE
test('AED accounting in ar-AE', () => {
  const s = new Intl.NumberFormat('ar-AE', {
    style: 'currency', currency: 'AED', currencySign: 'accounting',
    numberingSystem: 'arab'
  }).format(-1234.5);
  expect(/[()]/.test(s)).toBe(true); // accounting parentheses
});

// Cypress: RTL snapshot for date picker
cy.visit('/checkout?locale=ar-AE');
cy.get('[data-testid="date-picker"]').should('have.attr', 'dir', 'rtl');
cy.percySnapshot('date-picker-ar');
Data QA: Validate exports separately. Use ISO for dates and currency codes, and verify re‑import across locales.

Governance: policies, glossary, and release checklist

Most issues come from unclear policy. Publish a one‑page style guide with examples and code snippets:

  • Digits policy: Arabic‑Indic for editorial; Latin for developer surfaces and codes; finance per regulation.
  • Calendar policy: Gregorian default; dual display/toggle for KSA/Hijri contexts.
  • Currency policy: symbol vs ISO, negative style, decimals per CLDR, rounding rules.
  • Units policy: preferred units (km, °C) and abbreviations; pluralization notes.
  • Bidi policy: isolation rules for tokens and codes; where to enforce LTR spans.

Audit real pages monthly (screenshots + sample data) and update the guide where friction persists.

Troubleshooting and common mistakes

Frequent problems

  • Garbled numbers in paragraphs: Missing isolation. Wrap tokens in bdi or set unicode-bidi: isolate.
  • Inconsistent digits: No policy per surface. Decide once and enforce via formatters.
  • Wrong decimals for currency: Hardcoded “2 decimals.” Use CLDR minor units via locale formatters.
  • Hijri mismatch: Different providers. Document “Umm al‑Qura” and verify known dates.
  • Form validation failing: Users typed Arabic‑Indic digits. Normalize to Latin before validation.
  • Truncated month names: UI not sized for Arabic. Allow flexible width or abbreviations with QA.

Quick fixes

  • Replace string concatenation of prices with a single call to the currency formatter.
  • Add dir="rtl" at container level; use bdi for inline Latin tokens.
  • Memoize Intl formatters; align SSR/CSR options to avoid hydration mismatches.
  • Update fonts to support Arabic shaping and enable tabular numbers in data tables.

Standards and trusted resources

Launching in Arabic e‑commerce? Use this practical checklist: Localize Online Stores: Product + SEO Checklist

Conclusion and takeaways

  • Decide clear policies: digits per surface, calendar defaults, currency presentation (including negatives and decimals).
  • Implement with standard locale APIs (Intl/ICU); avoid string hacks and hardcoded separators.
  • Make mixed content safe with bdi/isolation and render Latin tokens in LTR.
  • Normalize digits on submit; add unit tests and RTL snapshots; publish a one‑page style guide.

Treat Arabic localization as a system—tokens, policies, and automated checks. Do it once, document it, and you’ll ship Arabic UIs that feel native and stay consistent across platforms and releases.

Share this article

Leave a Comment