UNPKG

arabicfmt

Version:

Arabic-first formatting for numbers, currency, dates and bidirectional text across all 22 Arab League countries — with correct handling of the 2025–2026 Unicode currency-symbol transition (Saudi Riyal U+20C1, UAE Dirham U+20C3, Omani Rial U+20C4).

99 lines (95 loc) 2.63 kB
'use strict'; // src/bidi/direction.ts var LETTER = /\p{L}/u; var RTL_LETTER = /[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Samaritan}\p{Script=Mandaic}\p{Script=Adlam}\p{Script=Hanifi_Rohingya}]/u; function charDirection(ch) { if (!LETTER.test(ch)) return "neutral"; return RTL_LETTER.test(ch) ? "rtl" : "ltr"; } function detectDirection(text) { for (const ch of text) { const dir = charDirection(ch); if (dir !== "neutral") return dir; } return "neutral"; } function isRTL(text) { return detectDirection(text) === "rtl"; } // src/bidi/isolate.ts var cc = String.fromCharCode; var LRI = cc(8294); var RLI = cc(8295); var FSI = cc(8296); var PDI = cc(8297); var LRM = cc(8206); var RLM = cc(8207); var ALM = cc(1564); var BIDI_CONTROLS = new RegExp( `[${cc(8206)}${cc(8207)}${cc(1564)}${cc(8234)}-${cc(8238)}${cc(8294)}-${cc(8297)}]`, "g" ); function isolate(text, dir = "auto") { const open = dir === "ltr" ? LRI : dir === "rtl" ? RLI : FSI; return open + text + PDI; } function wrapLTR(text) { return isolate(text, "ltr"); } function wrapRTL(text) { return isolate(text, "rtl"); } function stripBidi(text) { return text.replace(BIDI_CONTROLS, ""); } var ASCII_DIGIT = /[0-9]/; function isolateForeign(text, options = {}) { const base = options.base ?? (detectDirection(text) === "ltr" ? "ltr" : "rtl"); const wrapDir = base === "ltr" ? "rtl" : "ltr"; const includeNumbers = options.numbers ?? true; let result = ""; let run = ""; let runHasOpposite = false; const flush = () => { if (!run) return; if (runHasOpposite) { const lead = run.match(/^\s*/)[0]; const trail = run.match(/\s*$/)[0]; const core = run.slice(lead.length, run.length - trail.length); result += lead + isolate(core, wrapDir) + trail; } else { result += run; } run = ""; runHasOpposite = false; }; for (const ch of text) { const dir = charDirection(ch); if (dir === base) { flush(); result += ch; } else { run += ch; if (dir !== "neutral" && dir !== base || includeNumbers && ASCII_DIGIT.test(ch)) { runHasOpposite = true; } } } flush(); return result; } exports.ALM = ALM; exports.FSI = FSI; exports.LRI = LRI; exports.LRM = LRM; exports.PDI = PDI; exports.RLI = RLI; exports.RLM = RLM; exports.charDirection = charDirection; exports.detectDirection = detectDirection; exports.isRTL = isRTL; exports.isolate = isolate; exports.isolateForeign = isolateForeign; exports.stripBidi = stripBidi; exports.wrapLTR = wrapLTR; exports.wrapRTL = wrapRTL;