پرتوپرتو

قالب‌بندی عدد و تاریخ

تقویم شمسی، جداکنندهٔ عدد و مقدار مختصر — کدام تابع برای کدام کار، و کدام تله‌ها واقعی‌اند

اصل

این فارسی‌ترین کاری است که دیزاین‌سیستم انجام می‌دهد و تا امروز صفحه نداشت. دو قاعده کل ماجراست:

  1. رقم را تبدیل نکنید. ارقام فارسی از ویژگی ss01 فونت می‌آیند. جزئیات و اثباتش در فارسی‌محور بودن.
  2. قالب را تبدیل کنید. جداکنندهٔ هزار، مختصرسازی و تاریخ شمسی کارِ کد است، چون آن‌ها شکل عدد نیستند، معنای آن‌اند.

نمونه بصری

فراخوانیرشتهٔ خروجیآنچه کاربر می‌بیند
formatNumber(1234567)1,234,5671,234,567
formatNumber(1234567, 'short')1.2M1.2M
formatLargeNumber(1234567, 'fa')۱.۲ میلیون۱.۲ میلیون
formatLargeNumber(1234567, 'en')1.2M1.2M
formatJalaliDate(date, 'yyyy/MM/dd')1405/05/051405/05/05
getPersianMonthName(date)مردادمرداد
getPersianWeekdayName(date)دوشنبهدوشنبه
formatPersianDateRange(from, to)۵ - ۹ مرداد ۱۴۰۵۵ - ۹ مرداد ۱۴۰۵

ستون دوم و سوم یک رشته‌اند. تفاوتشان کار فونت است: ویژگی ss01 گلیف ارقام لاتین را به فارسی نگاشت می‌کند، بدون آنکه codepoint عوض شود. به همین دلیل کپی‌کردن از ستون سوم، عددِ قابل‌استفاده می‌دهد. دقت کنید که کاما در هیچ ستونی فارسی نمی‌شود — ss01 فقط ارقام را می‌گیرد.


قوانین اجباری

برای عدد، یکی از این دو تابع

import { formatNumber, formatLargeNumber } from '@partodata/ui'

formatNumber(1234567) // «1,234,567» — کدپوینت لاتین، فونت آن را «۱,۲۳۴,۵۶۷» نشان می‌دهد
formatLargeNumber(1234567, 'fa') // «۱.۲ میلیون» — پسوند فارسی
formatLargeNumber(1234567, 'en') // «1.2M» — برای رابط لاتین

حالت `'short'` را در رابط فارسی به کار نبرید

خروجی‌اش پسوند لاتین دارد، و چون ss01 سراسری است کاربر رقم فارسی را با پسوند لاتین کنار هم می‌بیند. برای مقدار مختصر در فارسی همیشه formatLargeNumber با locale فارسی. چرایی‌اش با اثبات، در فارسی‌محور بودن.

جداکننده‌ها را فونت عوض نمی‌کند

ss01 فقط ده گلیف رقم را جانشین می‌کند — این با پارس مستقیم جدول GSUB سنجیده شده. کاما، نقطه، % و ٪ دست‌نخورده می‌مانند. پس formatNumber(1234567) به‌شکل «۱,۲۳۴,۵۶۷» دیده می‌شود، با کاماى اسکی، نه جداکنندهٔ فارسی «٬». اگر جداکنندهٔ فارسی می‌خواهید باید صریحاً بنویسیدش؛ خودبه‌خود نمی‌آید.

برای تاریخ، از توابع شمسی — نه محاسبهٔ دستی

import {
  formatJalaliDate,
  getPersianMonthName,
  formatPersianDateRange,
  jalaliToGregorian,
} from '@partodata/ui'

formatJalaliDate(new Date(), 'yyyy/MM/dd')
getPersianMonthName(new Date()) // «مرداد»
formatPersianDateRange(from, to) // «۳ - ۷ مرداد ۱۴۰۵»
jalaliToGregorian(1405, 5, 3) // Date

پشت همه‌ی این‌ها date-fns-jalali است، و این انتخاب قفل شده: moment-jalaali از ۲۰۱۹ بی‌نگهدارنده است و یک قانون ESLint هر import از moment* را می‌بندد. جایگزینش نکنید.


موارد استفاده رایج

زمان نسبی

import { formatRelativeLocaleTime } from '@partodata/ui'

formatRelativeLocaleTime(post.createdAt, 'fa') // «۳ ساعت پیش»

درصد و واحد

درصد را با نشانهٔ فارسی ٪ بنویسید، نه %. هیچ‌کدام از این دو را فونت عوض نمی‌کند، پس انتخابش دست شماست و باید در کل محصول یکدست باشد.


چه نکنیم

  • تبدیل رقم در مسیر نمایش — نه با toLocaleString و locale فارسی، نه با convertToLocalNumbers. هیچ سودی ندارد و Ctrl+F، کپی به Excel، صفحه‌خوان و مرتب‌سازی را می‌شکند.
  • toPersianDigits برای متن رابط. export شده و لازم است، ولی فقط برای مسیرهای غیرنمایشی: خروجی PDF، رندر روی canvas، یا عنصری که font-family را عوض می‌کند.
  • تکیه بر PERSIAN_WEEKDAYS با فرض ترتیب هفتهٔ فارسی. این آرایه به ترتیب getDay() جاوااسکریپت است و با یکشنبه شروع می‌شود، نه شنبه. اگر با ایندکسِ روزِ فارسی سراغش بروید، روز اشتباه می‌گیرید. برای نام روز، getPersianWeekdayName(date) را صدا بزنید که خودش از locale faIR می‌خواند و به این آرایه کاری ندارد.
  • انتظار ارقام هم‌عرض. فونت یکان بخ ویژگی tnum ندارد و عرض ارقام فارسی‌اش از ۲۶۰ تا ۷۰۰ یونیت در یک em هزار‌یونیتی نوسان دارد. tabular-nums بی‌اثر است؛ اگر پایداری چیدمان لازم دارید، عرض ظرف را تثبیت کنید.

صفحات مرتبط