فارسیمحور بودن
اصل اول پرتو — طراحی از ابتدا برای زبان فارسی، نه ترجمه از انگلیسی
چرا فارسی اول؟
اکثر کتابخانههای UI برای LTR (انگلیسی) ساخته شدهاند و RTL به عنوان «حالت جایگزین» اضافه میشود. این رویکرد مشکلات زیادی ایجاد میکند: فاصلهگذاری معکوس، آیکونهای برعکس، انیمیشنهای ناهماهنگ.
پرتو از صفر برای فارسی طراحی شده است. RTL تنها حالت اصلی است، نه یک افزونه.
یکی از اصول بنیادین پرتو این است: فارسی اول، RTL بومی. این صفحه آنچه را که این اصل در عمل معنا میدهد توضیح میدهد.
پیکربندی پایه
تگ HTML
<html lang="fa" dir="rtl"></html>این دو attribute باید همیشه روی تگ <html> باشند:
lang="fa"— رقم فارسی را روشن میکند (از 4.0 قابلیتss01فونت فقط زیرlang="fa"روشن است؛ اعداد فارسی)، و برای screen readerها و موتورهای جستجو لازم استdir="rtl"— برای کارکرد صحیح CSS Logical Properties
CSS Logical Properties
پرتو از CSS Logical Properties به جای مقادیر فیزیکی استفاده میکند. این یعنی چیدمانها به صورت خودکار با جهت متن تطبیق مییابند.
جدول معادلها
| فیزیکی — استفاده نکنید | Logical — استفاده کنید | معنا |
|---|---|---|
ml-* | ms-* | margin-inline-start |
mr-* | me-* | margin-inline-end |
pl-* | ps-* | padding-inline-start |
pr-* | pe-* | padding-inline-end |
left-* | start-* | موقعیت از ابتدای خط |
right-* | end-* | موقعیت از انتهای خط |
border-l-* | border-s-* | حاشیه ابتدای خط |
border-r-* | border-e-* | حاشیه انتهای خط |
rounded-l-* | rounded-s-* | گوشه ابتدای خط |
rounded-r-* | rounded-e-* | گوشه انتهای خط |
text-left | text-start | تراز متن |
text-right | text-end | تراز متن |
مثال
// درست — با dir="rtl" به سمت راست قرار میگیرد
<div className="ms-4 ps-6 border-s-2 text-start">
محتوا
</div>
// غلط — در RTL و LTR یکسان عمل میکند، تغییر جهت نمیدهد
<div className="ml-4 pl-6 border-l-2 text-left">
محتوا
</div>استثنا: مرکزگرایی مطلق
left-1/2 -translate-x-1/2 برای مرکز قرار دادن المانهای absolute جهتدار نیستند و این یک استثنا مجاز است:
// این استفاده صحیح است
<div className="absolute left-1/2 -translate-x-1/2">محتوای مرکزی</div>اعداد فارسی
ارقام فارسی از فونت میآیند، نه از جاوااسکریپت. این تنها یک ترجیح سبکی نیست؛ نتیجهٔ یک واقعیت قابلاندازهگیری است.
چرا — اثبات، نه ادعا
قابلیت ss01 در YekanBakh-VF یک lookup جانشینی است که رقمهای لاتین (0–9) و رقمهای عربی-هندی را به گلیف رقم فارسی نگاشت میکند و به هیچ چیز دیگری دست نمیزند (این با پارس مستقیم جدول GSUB فونت تأیید شده)، و globals.css آن را برای محتوای فارسی روشن میکند: هر عنصری که lang آن فارسی است (fa، fa-IR) و زیرشاخهاش. زبان دیگری (ar، en، …) آن را برای زیرشاخهٔ خودش خاموش میکند.
/* پیشفرضِ خودِ سیستم طراحی (4.0) — کافی است <html lang="fa"> باشد */
[lang]:lang(fa),
body:lang(fa) {
font-feature-settings:
'kern' 1,
'rlig' 1,
'ss01' 1;
}پس صفحهٔ عربی یا انگلیسی، و جزیرهٔ انگلیسی داخل صفحهٔ فارسی (<div lang="en">)، رقم لاتین میماند؛ صفحهای که lang
ندارد هم رقم لاتین نشان میدهد. body:lang(fa) (بدنه با زبانی که از <html> به ارث میبرد) برای برنامهٔ
Tailwind v3 است که stylesheet را با <link> بار میکند: preflight آن بیرون از لایه است و روی <html> مقدار normal
میگذارد، ولی به body دست نمیزند. (تا 3٫x این قابلیت روی body همهٔ صفحهها روشن بود و صفحهٔ عربی و انگلیسی را هم فارسی
میکرد.) نمودارها هم زبان خودشان را دنبال میکنند: ریشهٔ نمودار lang را از locale میگیرد.
سه نکته که مرز این قاعده را روشن میکند:
- فقط برچسب BCP 47 فارسی حساب میشود:
fa،fa-IRو هرfa-…. شکل POSIX یعنیfa_IR(که خیلی از backendها میفرستند) وperوfasفارسی شمرده نمیشوند و رقم لاتین میدهند. - لایههای شناور زبان صفحه را میگیرند، نه زبان جزیره را. محتوای Dialog، Popover، Select، Tooltip و تقویم
DatePicker در portal زیر
<body>رندر میشود. ویجت فارسی (lang="fa") داخل میزبانی که فارسی نیست، در این لایهها رقم لاتین نشان میدهد.langرا روی خود صفحه بگذارید یا روی محتوای لایه بدهید، مثلاً<PopoverContent lang="fa">. localeنمودار پیشفرضfaاست. ریشهٔ نمودارlangرا ازlocaleمیگیرد، پس رقم محور و tooltip در نمودار فارسی فارسی دیده میشود، حتی در صفحهٔ عربی یا انگلیسی، مگرlocale="ar"یاlocale="en"بدهید.
نتیجه: مینویسید 1234، کاربر آن را با رقم فارسی میبیند، و codepointها لاتین میمانند: کسی که عدد را کپی
میکند 1234 میگیرد. این چهار چیز را سالم نگه میدارد که تبدیل با جاوااسکریپت هر چهار را میشکند:
| چه چیزی سالم میماند | چرا با تبدیل JS میشکند |
|---|---|
| Copy/Paste | رقم U+06F1 در Excel یا ماشینحساب عدد نیست |
| Screen reader | رفتار قارئها روی U+06Fx یکدست نیست |
| مرتبسازی و مقایسه | رشتهٔ «10» و «9» درست مرتب نمیشوند |
Ctrl+F | کاربری که «1234» تایپ کند، پیدا نمیکند |
این را در کد محصول خودتان تکرار نکنید
چند صفحهٔ الگو قبلاً toLocaleString('fa-IR') را توصیه میکردند که با همین اصل در تناقض بود. تناقض به نفع همین صفحه
حل شد: در کد محصولی که روی پرتو سوار است، تبدیل دستی هیچ سودی ندارد و آن چهار مورد بالا را از بین میبرد.
چه چیزی مجاز است
جداکننده و مختصرسازی کار جاوااسکریپت است — چون آنها شکل عدد نیستند، قالب آناند:
import { , } from '@partodata/ui/server'
// جداکنندهٔ هزارها. خروجی رقم لاتین است و فونت آن را فارسی نشان میدهد.
// دقت: ss01 فقط ارقام را جانشین میکند. کاما، نقطه و ٪ دستنخورده میمانند —
// پوشش آن lookup با پارس مستقیم GSUB سنجیده شد: رقم لاتین و عربی-هندی، و دیگر هیچ.
(1234567) // → «1,234,567»، با رقم فارسی و کامای لاتین دیده میشود
(1234567, 'short') // → «1.2M» — فقط بسترِ لاتین؛ هشدار زیر را ببینید
// مقدار مختصر با پسوند فارسی. پسوند لاتین را هرگز روی رقم فارسی نگذارید.
(1234567, 'fa') // → «1.2 میلیون»، رقمش را فونت فارسی میکند
(1234567, 'en') // → «1.2M»`formatNumber(n, 'short')` را در رابط فارسی به کار نبرید
خروجیاش 1.2M است و چون ss01 زیر lang="fa" روشن است، کاربر رقم فارسی با پسوند لاتین M میبیند، همان چیزی
که چند خط بالاتر منع شد. برای مقدار مختصر در رابط فارسی formatLargeNumber(n, 'fa') را به کار ببرید؛ 'short' برای
locale="en" است.
`convertToLocalNumbers` و `toPersianDigits` منسوخاند
هیچ کد سیستم طراحی رقم را به فارسی یا عربی تبدیل نمیکند: هر قالبدهنده (formatLargeNumber، formatPercentage،
formatRelativeLocaleTime، برچسبهای تاریخ و صفحهبندی و نمودار) رقم لاتین برمیگرداند و فونت آن را فارسی نشان میدهد. این
دو تابع برای سازگاری ماندهاند، ولی حالا فقط رقم فارسی و عربی را به لاتین برمیگردانند و در 6.0 حذف میشوند. برای
عادیسازی ورودی کاربر (جستجو، فیلد عدد، URL) toEnglishDigits را صدا بزنید.
رقم لاتین داخل محتوای فارسی: شناسهها
بعضی رشتهها شناسهاند و باید دقیقاً همانطور که تایپ شدهاند دیده شوند: نام کاربری و هندل، IP، URL، کدهایی مثل
T0023، شناسهٔ مدل، کلید API. عدد اینها «مقدار» نیست که کاربر بخواند؛ فارسیکردنش آن را به رشتهٔ دیگری تبدیل میکند.
برای اینها یک راه رسمی هست:
import { LatinDigits } from '@partodata/ui/latin-digits'
export function TrackingCode() {
return (
<p>
کد پیگیری: <LatinDigits>T0023</LatinDigits> · سرور: <LatinDigits>192.168.1.1</LatinDigits>
</p>
)
}LatinDigitsیکspanبا کلاسdigits-latinوdir="ltr"است (ترتیب شناسه داخل جملهٔ فارسی بههم نمیریزد؛ برای هندلی که ممکن است فارسی باشدdir="auto"بدهید).- کلاس
digits-latinرا مستقیم هم میتوانید روی هر عنصری بگذارید، از جمله<input>و<button>(مثلاً فیلد IP)؛ss01را برای آن عنصر و زیرشاخهاش خاموش میکند. اعلانش!importantاست تا در هر نوع نصب روی خود عنصر برنده باشد؛ فرزندی کهlang="fa"دارد رقم فارسی را دوباره روشن میکند. code،kbd،sampوpreخودشان رقم لاتین دارند.- برای عدد (تعداد، تاریخ، درصد) به کار نبرید: عدد از زبان صفحه پیروی میکند.
- تقویم میلادی (LTR) هم همچنان رقم لاتین دارد.
آیکونهای جهتدار
برخی آیکونها مانند فلشها در RTL باید چرخانده شوند:
import { Icons } from '@partodata/ui/icons'
// آیکون فلشی که خودتان میگذارید و در RTL چرخش میخورد
// (آیکون جهتدار کامپوننتهای سیستم طراحی خودش جهت را میخواند؛ آن را نچرخانید)
<Icons.arrowRight className="rtl:rotate-180" />
<Icons.chevronRight className="rtl:rotate-180" />کلاسهای Tailwind برای RTL
// فقط در RTL نمایش داده میشود
<div className="hidden rtl:block">محتوای RTL</div>
// فقط در LTR نمایش داده میشود
<div className="hidden ltr:block">LTR Content</div>چکلیست RTL
قبل از commit کردن هر کامپوننت جدید:
- هیچ property فیزیکی (
ml,mr,pl,pr,left,right,border-l,border-r) در کد وجود ندارد - متنها
text-startدارند (نهtext-right) - آیکونهای جهتدارِ خودتان کلاس
rtl:rotate-180دارند؛ آیکون جهتدار کامپوننتهای سیستم طراحی (Breadcrumb، Pagination، Carousel، زیرمنو) را دست نزنید، خودشان جهت را ازDirectionProviderیاdirخود میخوانند - dropdownها و popoverها از طرف درست باز میشوند
- انیمیشنهای کشویی جهت RTL را رعایت میکنند
تایپوگرافی فارسی
فضاگذاری (letter-spacing)
در متن فارسی، letter-spacing مثبت اغلب خوانایی را کاهش میدهد:
// مناسب برای فارسی
<h1 className="tracking-normal">عنوان</h1>
// نامناسب — فاصله بیش از حد بین حروف فارسی
<h1 className="tracking-widest">عنوان</h1>شکستن خط
برای متنهای طولانی فارسی، overflow-wrap را تنظیم کنید:
word-break: normal;
overflow-wrap: break-word;خلاصه
| قانون | توضیح |
|---|---|
lang="fa" dir="rtl" روی <html> | پایه هر صفحه |
| CSS Logical Properties | ms/me نه ml/mr، ps/pe نه pl/pr |
| OpenType برای اعداد | ss01 زیر lang="fa" — پیشفرض سیستم؛ شناسهها با LatinDigits |
rtl:rotate-180 | آیکونهای جهتدار مثل فلشها |
| فونت یکان بخ | پیشفرض سیستم — تغییر ندهید |
صفحات مرتبط
- دسترسیپذیری —
lang="fa"برای screen reader، کنتراست - تایپوگرافی — فونت یکان بخ و ویژگیهای OpenType
- فضابندی — CSS Logical Properties در padding و margin