فارسی‌محور بودن

اصل اول پرتو — طراحی از ابتدا برای زبان فارسی، نه ترجمه از انگلیسی

چرا فارسی اول؟

اکثر کتابخانه‌های 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-lefttext-startتراز متن
text-righttext-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 Propertiesms/me نه ml/mr، ps/pe نه pl/pr
OpenType برای اعدادss01 زیر lang="fa" — پیش‌فرض سیستم؛ شناسه‌ها با LatinDigits
rtl:rotate-180آیکون‌های جهت‌دار مثل فلش‌ها
فونت یکان بخپیش‌فرض سیستم — تغییر ندهید

صفحات مرتبط