mcpField
A
arabicfmt-mcp

arabicfmt-mcp

本地服务
github.com/cc1a2b开发工具36 周前更新

关于 arabicfmt-mcp

arabicfmt-mcp 是面向 JavaScript 和 TypeScript 的阿拉伯语格式化库,覆盖 22 个阿拉伯国家的货币、数字、日期及 RTL 文本处理。输入资料未说明其 MCP 功能。

主要功能

  1. 01格式化阿拉伯货币与精度
  2. 02格式化回历日期
  3. 03数字转阿拉伯语文字
  4. 04支票金额文字化
  5. 05处理 RTL 与双向文本

适用场景

  1. 01生成阿拉伯语发票金额
  2. 02展示本地化的回历日期
  3. 03解析东方阿拉伯数字
  4. 04修复混排文本的方向问题

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"cc1a2b-arabicfmt": {
"command": "npx",
"args": [
"-y",
"arabicfmt-mcp"
]
}
}
}
启动命令npx -y arabicfmt-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmarabicfmt-mcp0.1.1

工具

17

收录的工具列表 · 更新于 3 周前

参数类型必填说明
amountnumber是要格式化的货币金额。
localestring否BCP-47 区域设置,例如 'ar'、'ar-SA'、'en'。默认值为 'ar'。
currencystring否ISO 4217 货币代码,例如 'SAR'、'AED'、'KWD'。默认值为 SAR 区域的默认货币。
numeralsenum否数字系统:'latn'(1234)、'arab'(东阿拉伯数字 ١٢٣٤)或 'arabext'(波斯语/乌尔都语数字 ۱۲۳۴)。latn | arab | arabext

详细介绍

arabicfmt

面向阿拉伯语的 JavaScript 和 TypeScript 格式化

货币符号 · 回历/伊斯兰历 · 数字转文字 · تفقيط · 6 种复数形式 · RTL/双向文本 —
适用于阿拉伯国家联盟全部 22 个国家,零依赖,并提供完整的 TypeScript 类型。

أرقامٌ وعملاتٌ وتواريخُ هجريةٌ ولغةٌ عربيةٌ سليمة — في سطرٍ واحد.

npm 版本 下载量 jsDelivr 命中次数 gzip 压缩后大小 零依赖 包含类型

arabicfmt — 交互式阿拉伯语格式化体验场

npm · 在线演示 · GitHub

arabicfmt 是唯一一款能在一个零依赖包中处理完整阿拉伯语格式化需求的 JavaScript 库,涵盖货币符号、数字精度、回历/伊斯兰历日期、RTL 双向文本、阿拉伯语数字转文字和 تفقيط,并提供完整的 TypeScript 类型。适用于 Node、浏览器、Deno、Bun 和 React Native。

0.1.5 新增: Unicode 17.0/18.0 过渡期的所有货币符号——SAR U+20C1、MVR U+20C2、AED U+20C3、OMR U+20C4——以及真正渲染这些符号所需的工具:类型化部分、生成的 @font-face、价格区间和日期感知型过渡注册表。

npm install arabicfmt

其他库存在的问题

问题其他库arabicfmt
沙特里亚尔 U+20C1输出 ﷼(U+FDFC)——这是伊朗里亚尔正确使用 U+20C1,并提供安全的文本回退
Unicode 18.0 货币符号(MVR/AED/OMR)未编码,或仍使用旧缩写提供全部四种符号,以及 transitionStatus() 和生成的 @font-face
为新符号设置样式用正则表达式从格式化字符串中提取符号formatCurrencyToParts()——符号本身就是一个带类型的部分
伊拉克第纳尔(IQD)小数位数0(CLDR 实用标准)3 位小数——ISO 4217 法律标准
回历日期输出在 Node、Chrome、Safari 和 Hermes 中各不相同冻结的 Umm al-Qura 表——在所有引擎中完全一致
阿拉伯语复数1–2 种形式;阿拉伯语法律上需要 6 种完整的 CLDR 6 种形式系统(zero/one/two/few/many/other)
将数字转换为阿拉伯语文字没有零依赖的解决方案arabicToWords(1234) → "ألف ومئتان وأربعة وثلاثون"
将金额转换为支票大写金额(تفقيط)自行实现,容易弄错语法spellCurrency(1234.5, {currency:"SAR"}) → "ألف ومئتان وأربعة وثلاثون ريالاً وخمسون هللةً"
序数(ترتيبية)缺失或不区分性别arabicOrdinal(25) → "الخامس والعشرون",支持性别区分
口语时长Intl.DurationFormat 支持度很低formatDuration(7_500_000) → "ساعتان وخمس دقائق",支持完整的语法一致
RTL 句子错乱电话号码会在句子中间翻转自动用 Unicode 隔离符包裹 LTR 文本段
东阿拉伯数字解析parseInt("١٢٣") → NaNparseNumber("١٬٢٣٤٫٥٦") → 1234.56
阿拉伯语 URL slug清除后变为空,或出现乱码slugify("مدينة جدة") → "mdynh-jdh"
IBAN / 沙特身份证校验会接受错误号码的正则表达式真正的 ISO 7064 mod-97 + Luhn 校验和

安装

npm install arabicfmt
# or
yarn add arabicfmt
# or
pnpm add arabicfmt

要求: Node.js ≥ 18 · TypeScript ≥ 4.7(可选)· 零运行时依赖。

浏览器 / CDN — 无需构建步骤

每个版本都会自动镜像到 jsDelivr 和 unpkg CDN。直接从 URL 导入可用于浏览器的 ESM 包——无需安装,也无需打包器:

<script type="module">
  import { formatCurrency, formatHijri } from "https://cdn.jsdelivr.net/npm/arabicfmt/+esm";

  console.log(formatCurrency(1234.5, { currency: "SAR", numerals: "arab" })); // ١٬٢٣٤٫٥٠ ر.س
  console.log(formatHijri(new Date(), { numerals: "arab" }));                 // ٢٧ ذو الحجة ١٤٤٧ هـ
</script>

子路径也可用——例如,https://cdn.jsdelivr.net/npm/arabicfmt/dist/currency/index.js 仅用于货币模块。生产环境请固定版本,例如 [email protected]。


快速开始

import {
  formatCurrency,      // correct symbol + precision for every Arab currency
  formatCurrencyRange, // "1,000.00 – 5,000.00 ر.س"
  formatCurrencyToParts, // typed parts — font-scope just the new Unicode sign
  transitionStatus,    // "none" | "announced" | "encoded" for any date
  signFontFaceCSS,     // the scoped @font-face the new signs need
  formatCompact,       // 1,200,000 → "1.2M" / "١٫٢ مليون"
  arabicToWords,       // 1234 → "ألف ومئتان وأربعة وثلاثون"
  spellCurrency,       // تفقيط: 1234.5 SAR → "...ريالاً وخمسون هللةً"
  arabicOrdinal,       // 25 → "الخامس والعشرون"
  formatDuration,      // 7_500_000ms → "ساعتان وخمس دقائق"
  formatFileSize,      // 1536 → "1.5 كيلوبايت"
  formatRelativeTime,  // "منذ ٣ أيام"
  formatList,          // ["أحمد","علي"] → "أحمد وعلي"
  parseCurrency,       // "١٬٢٣٤٫٥٠ ر.س" → 1234.5
  arabicPlural,        // full 6-form Arabic plural selection
  sortArabic,          // Arabic-locale collation
  slugify,             // "مدينة جدة" → "mdynh-jdh" (URL slugs)
  isValidIBAN,         // ISO 7064 mod-97 IBAN checksum
  isValidSaudiId,      // Saudi national ID / Iqama check digit
  isolateForeign,      // fix broken RTL sentences
  normalizeForSearch,  // search-key normalization
  detectLocale,        // auto-detect from browser / Node environment
} from "arabicfmt";

import { formatHijri, toHijri } from "arabicfmt/umalqura"; // deterministic Hijri calendar

// Currency
formatCurrency(1.2,   { currency: "KWD" });                      // "1.200 د.ك"
formatCurrency(1234,  { locale: "ar-SA", numerals: "arab" });    // "١٬٢٣٤٫٠٠ ر.س"
formatCurrency(-500,  { currency: "SAR", accounting: true });    // "(500.00 ر.س)"
formatCurrencyRange(1000, 5000, { currency: "SAR" });            // "1,000.00 – 5,000.00 ر.س"
formatCompact(1_500_000, { locale: "ar", numerals: "arab" });    // "١٫٥ مليون"

// The Unicode currency-sign transition, as data
transitionStatus("AED");                          // "encoded" (Unicode 18.0)
transitionStatus("AED", new Date("2026-01-01"));  // "announced"
transitionStatus("KWD");                          // "none" — Kuwait has no sign
signFontFaceCSS({ src: "/fonts/signs.woff2" });   // @font-face … unicode-range: U+20C1…U+20C4

// Number to Arabic words
arabicToWords(1234);                     // "ألف ومئتان وأربعة وثلاثون"
arabicToWords(1_000_000);               // "مليون"
arabicToWords(5, { gender: "female" }); // "خمس"

// Spell money for invoices & cheques (التفقيط)
spellCurrency(1234.5, { currency: "SAR" });
// "ألف ومئتان وأربعة وثلاثون ريالاً وخمسون هللةً"
spellCurrency(100, { currency: "SAR", suffix: true }); // "مئة ريال فقط لا غير"

// Ordinals — gender-aware
arabicOrdinal(1);                        // "الأول"
arabicOrdinal(25);                       // "الخامس والعشرون"
arabicOrdinal(1, { gender: "female" });  // "الأولى"

// Duration & file size
formatDuration(7_500_000);               // "ساعتان وخمس دقائق"
formatFileSize(1536);                    // "1.5 كيلوبايت"

// Lists
formatList(["أحمد", "محمد", "علي"]);                    // "أحمد ومحمد وعلي"
formatList(["تفاح", "موز"], { type: "disjunction" });   // "تفاح أو موز"

// Hijri dates (deterministic — same output on Node, Chrome, Safari, Hermes)
formatHijri(new Date("2025-09-23"));                            // "1 ربيع الآخر 1447 هـ"
formatHijri(new Date("2025-09-23"), { numerals: "arab" });     // "١ ربيع الآخر ١٤٤٧ هـ"
toHijri(new Date("2025-09-23"));                               // { year: 1447, month: 4, day: 1 }

// Relative time
formatRelativeTime(new Date(Date.now() - 3 * 86400_000));      // "منذ 3 أيام"

// Parse formatted strings back to numbers
parseCurrency("١٬٢٣٤٫٥٠ ر.س");   // 1234.5
parseCurrency("(500.00 SAR)");    // -500

// RTL
isolateForeign("اتصل على +1 (555) 234-5678 الآن"); // phone stays intact in RTL

// Locale auto-detection
detectLocale(); // "ar-SA" in a Saudi browser, "ar-EG" in Node with LANG=ar_EG

货币格式化

符号策略:symbolMode

沙特里亚尔于 2025 年 9 月获得了自己的 Unicode 符号(U+20C1)。大多数库要么输出错误的连字(U+FDFC,即伊朗里亚尔),要么回退到 SAR。arabicfmt 让你完全掌控:

import { formatCurrency, resolveCurrencySymbol, getCurrencyInfo } from "arabicfmt/currency";

formatCurrency(1234.5, { currency: "SAR" });
// → "1,234.50 ر.س"   (auto: safe text symbol, renders everywhere today)

formatCurrency(1234.5, { currency: "SAR", symbolMode: "new" });
// → "1,234.50 ⃁"     (U+20C1 — use with a webfont; see webfont guide below)

formatCurrency(1234.5, { currency: "SAR", symbolMode: "code" });
// → "1,234.50 SAR"   (ISO code — for accounting tables)
symbolModeSARAEDOMR适用场景
auto (默认)ر.س⃃ U+20C3⃄ U+20C4默认值。AED/OMR 使用专用符号;SAR 使用安全的文本形式
new⃁ U+20C1⃃ U+20C3⃄ U+20C4强制使用专用符号(需要字体支持)
textر.سد.إر.ع.始终使用安全的文本符号——可在所有地方正常显示
codeSARAEDOMRISO 代码

Unicode 18.0(2026 年 9 月 16 日): AED(U+20C3)、OMR(U+20C4)和 MVR (U+20C2)符号现已处于 live 状态,auto 会优先使用它们。今天需要最高 兼容性?请使用 symbolMode: "text"。根据设计,沙特里亚尔仍默认使用 安全的文本形式。完整表格请参阅 过渡说明。

正确的小数精度——阿拉伯国家联盟全部 22 个国家

在构建时根据 CLDR 48.2.0 生成,并且每次构建都会验证:

formatCurrency(1.2, { currency: "KWD" });  // "1.200 د.ك"  ← 3 decimals
formatCurrency(1.2, { currency: "BHD" });  // "1.200 د.ب"  ← 3 decimals
formatCurrency(1.2, { currency: "IQD" });  // "1.200 ع.د"  ← 3 decimals (ISO 4217, not CLDR's 0)
formatCurrency(500, { currency: "KMF" });  // "500 ف.ج.ق"  ← 0 decimals
formatCurrency(500, { currency: "SAR" });  // "500.00 ر.س" ← 2 decimals
小数位数货币
3KWD、BHD、OMR、JOD、IQD、LYD、TND
0DJF、KMF
2SAR、AED、QAR 等
所有货币选项
// Resolve from locale region — no need to know the currency code
formatCurrency(99.9,  { locale: "ar-BH" });                  // "99.900 د.ب"
formatCurrency(1234,  { locale: "ar-AE", numerals: "arab", symbolMode: "text" }); // "١٬٢٣٤٫٠٠ د.إ"  (auto → U+20C3 sign)

// Accounting notation (negatives in parentheses)
formatCurrency(-1234.5, { currency: "SAR", accounting: true }); // "(1,234.50 ر.س)"

// Hide/override
formatCurrency(100, { currency: "SAR", showSymbol: false, fractionDigits: 0 }); // "100"

// Currency metadata
getCurrencyInfo("SAR");
// {
//   code: "SAR", digits: 2,
//   symbols: { auto: "ر.س", text: "ر.س", code: "SAR", new: "⃁" },
//   unicode: { codepoint: "U+20C1", unicodeVersion: "17.0", live: true, autoDefault: false },
//   displayName: "ريال سعودي"
// }
价格范围
import { formatCurrencyRange } from "arabicfmt/currency";

formatCurrencyRange(1000, 5000, { currency: "SAR" })
// "1,000.00 – 5,000.00 ر.س"     ← one symbol, the way ranges are actually set

formatCurrencyRange(1000, 5000, { currency: "SAR", symbolEach: true })
// "1,000.00 ر.س – 5,000.00 ر.س"

formatCurrencyRange(1.2, 2, { currency: "KWD" })
// "1.200 – 2.000 د.ك"           ← both ends share the currency's precision

formatCurrencyRange(1000, 5000, { currency: "SAR", numerals: "arab" })
// "١٬٠٠٠٫٠٠ – ٥٬٠٠٠٫٠٠ ر.س"

formatCurrencyRange(50, 50, { currency: "AED" })   // "50.00 ⃃"  — collapses
formatCurrencyRange(500, 100, { currency: "SAR" }) // throws: reversed range

如需使用默认空格连接的 en dash 以外的分隔符,请传入 rangeSeparator({ rangeSeparator: " إلى " });若范围位于混合方向的句子中,请传入 isolate: true,以包裹整个范围。

渲染新货币符号

全新的货币符号需要两样字符串无法提供的东西:限定到其码位的 Web 字体,以及一种只将 符号 包装在使用该字体的元素中的方式。两者都只需调用一次。

1. parts API — 货币领域对应于 Intl.NumberFormat.prototype.formatToParts 的 API:

import { formatCurrencyToParts } from "arabicfmt/currency";

formatCurrencyToParts(1234.5, { currency: "SAR", symbolMode: "new" });
// [ { type: "integer",  value: "1" },
//   { type: "group",    value: "," },
//   { type: "integer",  value: "234" },
//   { type: "decimal",  value: "." },
//   { type: "fraction", value: "50" },
//   { type: "literal",  value: " " },
//   { type: "currency", value: "⃁", symbolMode: "new",
//     codepoint: "U+20C1", needsFont: true } ]

当且仅当某个部分包含专用 Unicode 符号时,needsFont 才为 true,因此组件不必对格式化后的字符串进行模式匹配(双向文本 bug 正是这样产生的):

<span dir="rtl">
  {formatCurrencyToParts(total, { currency: "SAR", symbolMode: "new" }).map(
    (part, i) =>
      part.needsFont
        ? <span key={i} className="riyal-sign">{part.value}</span>
        : <span key={i}>{part.value}</span>,
  )}
</span>

部分类型与 Intl 一致(integer、group、decimal、fraction、 minusSign、plusSign、literal),此外 arabicfmt 还添加了四种类型:currency、 parenthesis(会计格式)、isolate(双向文本控制符)和 rangeSeparator。

2. @font-face 规则 — 自动生成,并带有 unicode-range,因此从未显示货币符号的页面无需承担额外开销:

import { signFontFaceCSS } from "arabicfmt/currency";

signFontFaceCSS({ src: "/fonts/currency-signs.woff2" });
// @font-face {
//   font-family: "Arabicfmt Signs";
//   src: url("/fonts/currency-signs.woff2") format("woff2");
//   font-display: swap;
//   unicode-range: U+20C1, U+20C2, U+20C3, U+20C4;
// }

// Saudi riyal only, into a family name you already use:
signFontFaceCSS({ src: "/fonts/riyal.woff2", family: "Riyal", currencies: ["SAR"] });
// unicode-range: U+20C1;

由于范围限定为这些码位,浏览器只会在实际绘制其中某个码位时下载字体,不会影响正文。将该字体放在字体栈首位,其余字体作为回退:

:root { font-family: "Arabicfmt Signs", "Noto Naskh Arabic", sans-serif; }

signUnicodeRange() 只返回范围字符串("U+20C1, U+20C2, U+20C3, U+20C4"),适用于 CSS-in-JS。这两个辅助函数都会拒绝任何可能逃逸出规则的内容,因此配置中的 URL 无法注入 CSS。


数字格式化

import {
  formatNumber, formatCompact, formatPercent,
  toArabicDigits, toExtendedArabicDigits, toLatinDigits,
  parseNumber, parseCurrency,
  arabicToWords,
  formatRelativeTime,
} from "arabicfmt/number";

// Standard
formatNumber(1_234_567.89, { locale: "en" });              // "1,234,567.89"
formatNumber(1234.5,       { numerals: "arab" });           // "١٬٢٣٤٫٥"

// Compact / short notation — dashboards and data cards
formatCompact(1_500_000);                                   // "1.5M"
formatCompact(1_500_000, { locale: "ar" });                 // "1.5 مليون"
formatCompact(1_500_000, { locale: "ar", numerals: "arab" }); // "١٫٥ مليون"

// Percent
formatPercent(0.853, { locale: "en" });                    // "85.3%"

// Three numeral systems: latn (default), arab, arabext
formatNumber(1234.5, { numerals: "arab" });                // "١٬٢٣٤٫٥"  Eastern Arabic
formatNumber(1234.5, { numerals: "arabext" });             // "۱٬۲۳۴٫۵"  Persian/Urdu

// Transliteration
toArabicDigits("Order #2026");                             // "Order #٢٠٢٦"
toExtendedArabicDigits("2026");                            // "۲۰۲۶"
toLatinDigits("٢٠٢٦");                                     // "2026"  (handles Persian ۰–۹ too)

// Parsing — round-trip support
parseNumber("١٬٢٣٤٫٥٦");         // 1234.56  (Eastern Arabic digits + separators)
parseNumber("1,234.56");          // 1234.56  (Western)
parseCurrency("١٬٢٣٤٫٥٠ ر.س");  // 1234.5
parseCurrency("(500.00 SAR)");   // -500      (accounting notation)

// Relative time
formatRelativeTime(new Date(Date.now() - 3 * 86400_000));            // "منذ 3 أيام"
formatRelativeTime(new Date(Date.now() + 3600_000), new Date(), { locale: "en" }); // "in 1 hour"

数字转阿拉伯语词语(arabicToWords)

将整数转换为对应的阿拉伯语词语形式——支持性别一致以及所有六个数量级。

import { arabicToWords } from "arabicfmt";

// Basic
arabicToWords(0)       // "صفر"
arabicToWords(1)       // "واحد"
arabicToWords(2)       // "اثنان"
arabicToWords(11)      // "أحد عشر"
arabicToWords(25)      // "خمسة وعشرون"
arabicToWords(100)     // "مئة"
arabicToWords(350)     // "ثلاثمئة وخمسون"

// Thousands
arabicToWords(1000)    // "ألف"
arabicToWords(2000)    // "ألفان"
arabicToWords(5000)    // "خمسة آلاف"
arabicToWords(11000)   // "أحد عشر ألفاً"
arabicToWords(100000)  // "مئة ألف"

// Millions / billions
arabicToWords(1_000_000)    // "مليون"
arabicToWords(2_000_000)    // "مليونان"
arabicToWords(5_000_000)    // "خمسة ملايين"
arabicToWords(1_000_000_000)// "مليار"

// Large composite
arabicToWords(1_234_567)
// "مليون ومئتان وأربعة وثلاثون ألفاً وخمسمئة وسبعة وستون"

// Gender agreement — feminine noun (ليرة، روبية…)
arabicToWords(3, { gender: "female" })  // "ثلاث"
arabicToWords(5, { gender: "female" })  // "خمس"

// Negative
arabicToWords(-42)  // "سالب اثنان وأربعون"

// Decimals — opt in (default truncates, stays backward compatible)
arabicToWords(3.14, { fraction: "digits" })  // "ثلاثة فاصلة واحد أربعة"
arabicToWords(3.14, { fraction: "number" })  // "ثلاثة فاصلة أربعة عشر"

// Common fractions (denominators 2–10)
import { arabicFraction } from "arabicfmt";
arabicFraction(1, 2)  // "نصف"
arabicFraction(3, 4)  // "ثلاثة أرباع"
arabicFraction(2, 3)  // "ثلثان"

用文字拼写金额 — التفقيط

spellCurrency 是每张阿拉伯语发票、支票和合同都需要的 tafqit:它会将数字金额转换为完整的法定阿拉伯语表述,拆分主要单位和次要单位,并对每个名词进行变格,使其符合正确的语法一致关系(单数/双数/复数/宾格)。

import { spellCurrency } from "arabicfmt";

spellCurrency(1234.5, { currency: "SAR" })
// "ألف ومئتان وأربعة وثلاثون ريالاً وخمسون هللةً"

// Unit agreement is automatic (العدد والمعدود)
spellCurrency(1,   { currency: "SAR" })   // "ريال واحد"      (singular)
spellCurrency(2,   { currency: "SAR" })   // "ريالان"         (dual)
spellCurrency(3,   { currency: "SAR" })   // "ثلاثة ريالات"   (plural, 3–10)
spellCurrency(11,  { currency: "SAR" })   // "أحد عشر ريالاً" (accusative, 11–99)
spellCurrency(100, { currency: "SAR" })   // "مئة ريال"       (genitive singular)

// Minor-unit precision comes from CLDR — KWD = 1000 fils, SAR = 100 halalas
spellCurrency(1.5, { currency: "KWD" })   // "دينار واحد وخمسمئة فلس"
spellCurrency(0.75, { currency: "SAR" })  // "خمس وسبعون هللةً"

// Cheque-ready ending and locale-derived currency
spellCurrency(100, { currency: "SAR", suffix: true }) // "مئة ريال فقط لا غير"
spellCurrency(-5,  { locale: "ar-AE" })               // "سالب خمسة دراهم"

已为阿拉伯联盟的全部 22 种货币(SAR、AED、KWD、BHD、QAR、OMR、JOD、EGP、IQD、LYD、TND、DZD、MAD、SDG、LBP、SYP、YER、SOS、DJF、KMF、MRU)内置完整的阿拉伯语名词范式。可通过导出的 CURRENCY_WORDS 表进行查看或扩展。


序数 — الأعداد الترتيبية

import { arabicOrdinal } from "arabicfmt";

arabicOrdinal(1)    // "الأول"
arabicOrdinal(2)    // "الثاني"
arabicOrdinal(10)   // "العاشر"
arabicOrdinal(11)   // "الحادي عشر"
arabicOrdinal(25)   // "الخامس والعشرون"

// Gender agreement
arabicOrdinal(1, { gender: "female" })   // "الأولى"
arabicOrdinal(25, { gender: "female" })  // "الخامسة والعشرون"

// Indefinite (drop the article ال)
arabicOrdinal(3, { definite: false })    // "ثالث"
arabicOrdinal(25, { definite: false })   // "خامس وعشرون"

时长 — 阿拉伯语文字形式

formatDuration 会将时间跨度转换为口语化的阿拉伯语形式,并确保每个单位都符合正确的双数/复数/宾格一致关系——这是(支持度仍然很低的)Intl.DurationFormat 无法提供的。

import { formatDuration } from "arabicfmt";

formatDuration(7_500_000)                  // "ساعتان وخمس دقائق"  (2h 5m)
formatDuration(90, { input: "s" })         // "دقيقة واحدة وثلاثون ثانيةً"
formatDuration(3_600_000, { largest: 1 })  // "ساعة واحدة"
formatDuration(2 * 86_400_000)             // "يومان"
formatDuration(500)                        // "أقل من ثانية"

// Restrict the units considered
formatDuration(125 * 60_000, { units: ["minute"], largest: 1 })
// "مئة وخمس وعشرون دقيقةً"

largest(默认为 2)用于限制显示的单位数量,按从大到小排列。想自行控制名词一致关系?countedNoun(n, forms) 已导出,可用于任何自定义量词名词。


文件大小 — 阿拉伯语数据单位

import { formatFileSize } from "arabicfmt";

formatFileSize(0)                          // "0 بايت"
formatFileSize(1536)                       // "1.5 كيلوبايت"
formatFileSize(5 * 1024 * 1024)            // "5 ميجابايت"
formatFileSize(1_500_000, { base: 1000 })  // "1.5 ميجابايت"  (decimal/SI)
formatFileSize(2048, { numerals: "arab" }) // "٢ كيلوبايت"
formatFileSize(2048, { unitStyle: "latin" })// "2 KB"

单位依次为 بايت · كيلوبايت · ميجابايت · جيجابايت · تيرابايت · بيتابايت, 可使用 base: 1024(二进制,默认值)或 base: 1000(十进制)。


阿拉伯语复数规则(6 种形式)

阿拉伯语有六种复数形式,超过任何其他主要语言。标准国际化库只支持 1–2 种形式,因此无法正确处理阿拉伯语。

import { arabicPluralForm, arabicPlural } from "arabicfmt";

// Get the CLDR form name
arabicPluralForm(0)    // "zero"
arabicPluralForm(1)    // "one"
arabicPluralForm(2)    // "two"
arabicPluralForm(5)    // "few"   (3–10)
arabicPluralForm(15)   // "many"  (11–99)
arabicPluralForm(100)  // "other"

// Select the right string
const forms = {
  zero:  "لا كتب",
  one:   "كتاب واحد",
  two:   "كتابان",
  few:   "كتب",       // 3–10
  many:  "كتاباً",    // 11–99
  other: "كتاب",
};

arabicPlural(0,   forms)  // "لا كتب"
arabicPlural(1,   forms)  // "كتاب واحد"
arabicPlural(2,   forms)  // "كتابان"
arabicPlural(5,   forms)  // "كتب"
arabicPlural(25,  forms)  // "كتاباً"
arabicPlural(100, forms)  // "كتاب"

回历/伊斯兰历日期

提供两个 API 完全相同的引擎:

arabicfmt/datearabicfmt/umalqura
算法表格算术官方 Umm al-Qura 历表
准确度±1–2 天精确
包体积极小(无历表)较大(冻结的 ICU 历表)
范围任意年份回历 1300–1599 年
确定性是是 — 在 Node/Chrome/Safari/Hermes 上结果相同
import { toHijri, fromHijri, formatHijri, umalquraToGregorian } from "arabicfmt/umalqura";

// Convert
toHijri(new Date("2025-09-23"))        // { year: 1447, month: 4, day: 1 }
umalquraToGregorian(1447, 9, 1)        // JavaScript Date — first day of Ramadan 1447

// Format — Arabic
formatHijri(new Date("2025-09-23"))
// "1 ربيع الآخر 1447 هـ"

formatHijri(new Date("2025-09-23"), { numerals: "arab" })
// "١ ربيع الآخر ١٤٤٧ هـ"

// Format — English
formatHijri(new Date("2025-09-23"), { locale: "en" })
// "1 Rabi al-Thani 1447 AH"

// Format — ISO-style numeric
formatHijri(new Date("2025-09-23"), {
  locale: "en", month: "2-digit", day: "2-digit", order: "ymd", era: false,
})
// "1447/04/01"
月份和星期名称表
import {
  HIJRI_MONTHS_AR,     // Arabic Hijri month names
  HIJRI_MONTHS_EN,     // English Hijri month names
  GREGORIAN_MONTHS_AR, // Arabic Gregorian month names (يناير، فبراير…)
  GREGORIAN_MONTHS_EN,
  ARABIC_WEEKDAYS_AR,  // Arabic weekday names (الأحد، الاثنين…)
  ARABIC_WEEKDAYS_EN,
} from "arabicfmt/date";

HIJRI_MONTHS_AR[8]        // "رمضان"  (index 0 = Muharram)
GREGORIAN_MONTHS_AR[0]    // "يناير"  (index 0 = January)
ARABIC_WEEKDAYS_AR[5]     // "الجمعة" (index 0 = Sunday)

双向文本(RTL)辅助工具

防止电话号码和英文单词在阿拉伯语句子中顺序错乱:

import { detectDirection, isolate, isolateForeign, stripBidi } from "arabicfmt/bidi";

// Before fix: "+1 (555) 234-5678" flips the area code in RTL context
// After fix:  the phone number is wrapped in Unicode isolates — sentence intact
isolateForeign("اتصل على +1 (555) 234-5678 الآن");

detectDirection("مرحبا");   // "rtl"
detectDirection("Hello");   // "ltr"

isolate("9:41 AM");         // FSI … PDI isolate around a mixed run
stripBidi(dirtyStr);        // remove every Unicode bidi control character

面向阿拉伯语搜索的文本规范化

即使阿拉伯语文本在音标符号、alef 变体、hamza 和 taa marbuta 方面存在差异,也能进行匹配:

import {
  stripTashkeel,
  normalizeArabic,
  normalizeForSearch,
  sortArabic,
  compareArabic,
} from "arabicfmt/text";

stripTashkeel("مُحَمَّد")        // "محمد"
normalizeArabic("الأحمد")        // "الاحمد"  (alef variants unified)

// Robust search — these two strings produce the same key:
normalizeForSearch("مُؤسَّسة") === normalizeForSearch("موسسه")  // true

// Arabic-locale collation
sortArabic(["ياسر", "أحمد", "بسام"])   // ["أحمد", "بسام", "ياسر"]
["ج", "أ", "ب"].sort(compareArabic)    // ["أ", "ب", "ج"]

列表格式化

将值连接成符合阿拉伯语语法的列表。封装 Intl.ListFormat,并在不支持它的运行时中优雅降级。

import { formatList } from "arabicfmt";

formatList(["أحمد", "محمد", "علي"])                      // "أحمد ومحمد وعلي"
formatList(["تفاح", "موز", "برتقال"], { type: "disjunction" }) // "تفاح أو موز أو برتقال"
formatList([1, 2, 3], { numerals: "arab" })              // "١ و٢ و٣"

转写与 URL slug

将阿拉伯文转写为易读的拉丁文字,或转换为适用于路由、文件名和 CMS 永久链接的 URL 安全 slug。结果是确定性的——只有文本带有元音符号(附带 tashkeel)时才会显示短元音。

import { transliterate, slugify } from "arabicfmt";

transliterate("مُحَمَّد")    // "muhammad"   (vowelled)
transliterate("محمد")        // "mhmd"       (bare → consonant-only)
transliterate("الرياض")      // "alryad"
transliterate("غرفة ٢٠١")    // "ghrfh 201"  (digits converted)

slugify("مدينة جدة")                      // "mdynh-jdh"
slugify("الرياض 2026")                    // "alryad-2026"
slugify("Hello العالم", { separator: "_" }) // "hello_alalm"
slugify("Hello World", { lowercase: false }) // "Hello-World"

注意:这是一种实用、近似可逆的转写方式,并非严格的学术转写(DIN 31635 / ISO 233)。它适用于 slug、搜索键和易读 ID。


验证 — IBAN 与沙特 ID

进行真实的校验和检查,而非依赖正则表达式猜测。isValidIBAN 使用 ISO 7064 mod-97 算法,并根据 SWIFT 注册表检查长度;isValidSaudiId 使用 Luhn 校验位并区分公民与居民。

import { isValidIBAN, formatIBAN, isValidSaudiId, saudiIdType } from "arabicfmt";

isValidIBAN("SA03 8000 0000 6080 1016 7519")  // true
isValidIBAN("SA03 8000 0000 6080 1016 7510")  // false (bad checksum)
formatIBAN("SA0380000000608010167519")        // "SA03 8000 0000 6080 1016 7519"

isValidSaudiId("1012345672")                  // true
saudiIdType("1012345672")                     // "citizen"
saudiIdType("2100000005")                     // "resident"  (Iqama)

SA、AE、KW、BH、QA、JO、LB、EG、IQ、PS、TN、MR、LY(以及常见合作国家)的注册表长度均会强制检查。未知国家的 IBAN 会通过校验和及通用的 15–34 位长度范围进行验证,绝不会仅凭结构就被接受。


框架用法

React / Next.js
import { formatCurrency, detectLocale } from "arabicfmt";
import { formatHijri } from "arabicfmt/umalqura";

export function PriceTag({ amount, currency }: { amount: number; currency: string }) {
  const locale = detectLocale();
  return (
    <span dir="rtl">
      {formatCurrency(amount, { currency, locale })}
    </span>
  );
}

export function HijriDate({ date }: { date: Date }) {
  return <time>{formatHijri(date, { numerals: "arab" })}</time>;
}
Vue 3
import { formatCurrency } from "arabicfmt";

// composable
export function useArabicCurrency(currency: string) {
  return (amount: number) =>
    formatCurrency(amount, { currency, numerals: "arab" });
}
Node.js / Express
import { formatCurrency, detectLocale } from "arabicfmt";
import { formatHijri } from "arabicfmt/umalqura";

app.get("/invoice/:id", (req, res) => {
  const locale = req.headers["accept-language"]?.split(",")[0] ?? "ar-SA";
  const total  = formatCurrency(order.total, { locale });
  const date   = formatHijri(order.date, { locale: "ar" });
  res.json({ total, date });
});

Locale 自动检测

import { detectLocale } from "arabicfmt";

// Browser: reads navigator.language
// Node.js: reads LANG / LANGUAGE / LC_ALL / LC_MESSAGES env vars
// Fallback: "ar"

const locale = detectLocale(); // "ar-SA", "ar-EG", "en-US", …
formatCurrency(1234, { locale });

子路径导入 — 支持 tree-shaking

只选择所需内容,以获得尽可能小的打包体积:

import { formatCurrency, spellCurrency } from "arabicfmt/currency";
import { formatNumber, arabicToWords, formatDuration, formatFileSize } from "arabicfmt/number";
import { formatHijri, toHijri }  from "arabicfmt/date";       // tabular core (tiny)
import { formatHijri, toHijri }  from "arabicfmt/umalqura";   // accurate, opt-in
import { isolateForeign }        from "arabicfmt/bidi";
import { normalizeForSearch, arabicPlural, slugify } from "arabicfmt/text";
import { isValidIBAN, isValidSaudiId }      from "arabicfmt/validate";

各入口点的实测开销(esbuild --bundle --minify,gzip 压缩后 — v0.1.5):

导入内容min + gzip
arabicfmt以下全部内容13.2 kB
arabicfmt/currency22 种货币、تفقيط、parts、范围、转换注册表7.6 kB
arabicfmt/number单词、序数、分数、解析、时长等3.6 kB
arabicfmt/umalqura300 年的官方 Umm al-Qura 表2.4 kB
arabicfmt/text规范化、复数形式、排序规则、列表、slug1.7 kB
arabicfmt/date表格式 Hijri 核心1.6 kB
arabicfmt/bidi方向检测 + 隔离符0.8 kB
arabicfmt/validateIBAN + 沙特 ID 校验和0.7 kB

完整的阿拉伯文格式化工具栈,体积比单张小图片还小。


完整 API 参考

按模块列出所有公共函数。完整签名和选项见上文各章节及随包提供的 TypeScript 类型。

模块函数
arabicfmt/currencyformatCurrency · formatCurrencyToParts · formatCurrencyRange · spellCurrency · getCurrencyInfo · resolveCurrencySymbol · transitionStatus · getCurrencyTransition · listCurrencyTransitions · signFontFaceCSS · signUnicodeRange
arabicfmt/numberformatNumber · formatPercent · formatCompact · parseNumber · parseCurrency · toArabicDigits · toExtendedArabicDigits · toLatinDigits · shapeDigits · arabicToWords · arabicOrdinal · arabicFraction · countedNoun · formatDuration · formatFileSize · formatRelativeTime
arabicfmt/umalquraformatHijri · toHijri · fromHijri · gregorianToUmalqura · umalquraToGregorian
arabicfmt/dateformatHijri · toHijri · fromHijri (表格式核心)
arabicfmt/textstripTashkeel · removeTatweel · normalizeArabic · normalizeForSearch · arabicPlural · arabicPluralForm · sortArabic · compareArabic · createArabicCollator · formatList · transliterate · slugify
arabicfmt/bidiisolateForeign · isolate · wrapLTR · wrapRTL · stripBidi · detectDirection · isRTL · charDirection
arabicfmt/validateisValidIBAN · formatIBAN · normalizeIBAN · isValidSaudiId · saudiIdType
arabicfmt (根模块)重新导出以上所有内容 + detectLocale

MCP 服务 — 让 AI Agent 使用 arabicfmt

AI Agent(Claude Desktop、Claude Code、Cursor)可通过 arabicfmt-mcp Model Context Protocol 服务直接调用 arabicfmt — 提供 21 个工具(format_currency、format_currency_range、currency_transition、 spell_currency、format_hijri、arabic_to_words、isolate_foreign、 validate_iban 等)。将其添加到客户端的 mcpServers 配置中:

{
  "mcpServers": {
    "arabicfmt": { "command": "npx", "args": ["-y", "arabicfmt-mcp"] }
  }
}

源码和完整工具列表:mcp/。

示例

每项功能的可运行脚本都位于 examples/:

cd examples && npm install
node currency.mjs   # or numbers / words / dates / text / bidi / validate

工程特性

依赖项零运行时依赖
体积整个库约为 13.2 kB(min+gzip);子路径导入从 0.7 kB 起
格式ESM + CJS 双格式,完整的 .d.ts / .d.cts 类型
Tree-shaking"sideEffects": false — 只为导入的内容付费
数据源CLDR 48.2.0 + ICU — 在构建时验证,而非手工输入
测试覆盖242 项测试 — 货币过渡、部分结果、范围、精度、伊斯兰历、复数、文字、tafqit、时长、IBAN/ID
平台Node ≥ 18、所有常青浏览器、React Native / Hermes、Deno、Bun
发布方式npm 来源证明(GitHub Actions 证明)

Unicode 货币符号过渡

截至 Unicode 18.0 已全部完成 — 于 2026 年 9 月 16 日发布

本轮有四种货币从临时缩写改用专属 Unicode 符号,arabicfmt 已支持这四种货币。请注意,以下列表中没有科威特:科威特从未发行过第纳尔符号 — KWD 仍为 د.ك / KD,且 transitionStatus("KWD") 返回 "none"。

货币符号码位Unicode公布日期auto 默认值
沙特里亚尔(SAR)⃁U+20C117.0(2025 年 9 月 9 日)SAMA,2025 年 2 月 20 日文本 ر.س(保守选择)
马尔代夫卢菲亚(MVR)⃂U+20C218.0(2026 年 9 月 16 日)MMA,2022 年 7 月 3 日符号
阿联酋迪拉姆(AED)⃃U+20C318.0(2026 年 9 月 16 日)CBUAE,2025 年 3 月 27 日符号
阿曼里亚尔(OMR)⃄U+20C418.0(2026 年 9 月 16 日)CBO,2025 年 11 月 19 日符号

卢菲亚并非阿拉伯国家联盟货币,但它在同一批次中完成编码,并位于货币符号区块中迪拉姆和里亚尔之间 — 纳入它可以保持范围连续,因此一个 unicode-range 就能覆盖整个过渡范围。

由于系统字体对全新符号的支持情况仍不一致,symbolMode: "text" 始终返回安全的缩写;按照设计,沙特里亚尔的 auto 默认值仍为文本符号。

将过渡作为数据

编码不等于渲染。符号写入标准后,系统字体可能还要数月甚至数年才能绘制它;在此期间发布产品的团队都必须决定要打印什么。该时间线可供查询:

import {
  transitionStatus,
  listCurrencyTransitions,
  getCurrencyTransition,
} from "arabicfmt/currency";

transitionStatus("SAR")                          // "encoded"
transitionStatus("SAR", new Date("2025-03-01"))  // "announced" — SAMA had it, Unicode didn't
transitionStatus("AED", new Date("2026-01-01"))  // "announced"
transitionStatus("KWD")                          // "none" — no sign exists

listCurrencyTransitions({ unicodeVersion: "18.0" }).map((t) => t.code);
// ["MVR", "AED", "OMR"]

getCurrencyTransition("OMR");
// {
//   code: "OMR", sign: "⃄", codepoint: "U+20C4", name: "OMANI RIAL SIGN",
//   unicodeVersion: "18.0", unicodeReleased: "2026-09-16",
//   announced: "2025-11-19", authority: "Central Bank of Oman (CBO)",
//   autoDefault: true, textSymbol: "ر.ع.",
// }

transitionStatus 接受一个时间点,因此可以重新渲染历史发票,并使用其开具日期对应的正确符号。


在线演示

arabicfmt.vercel.app — 整个库都可在浏览器中交互使用并实时计算。更改任意输入,即可实时查看阿拉伯语内容更新:货币工作室、تفقيط、伊斯兰历转换器、复数、RTL 修复等。

arabicfmt 交互式演练场

在本地运行:

cd demo && npm install && npm run dev

参与贡献

欢迎在 GitHub 提交问题和拉取请求。


许可证

MIT — 可免费用于商业和个人用途。


作者及更多项目

由 cc1a2b 构建并维护。

如果 arabicfmt 为你节省了时间,请在 GitHub 上**为它点星** — 这有助于其他阿拉伯语开发者找到它。欢迎探索我的**其他开源项目**,或通过 issue 提交想法、错误报告和功能请求。

为阿拉伯语优先的软件而构建

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。