صفحه فارسی از دور درست دیده میشود: متنها راستچین هستند و منو از سمت راست شروع میشود. اما شماره سفارش کنار پرانتز جابهجا میشود، icon بازگشت جهت غلط دارد و فیلد ایمیل هنگام تایپ رفتار عجیبی نشان میدهد. این مشکلات با افزودن چند کلاس راستچین حل نمیشوند. RTL هم به جهت متن مربوط است، هم به چیدمان و هم به محتوایی که باید در دل یک صفحه فارسی همچنان LTR بماند.
Framework میتواند بخشی از کار را ساده کند، اما کیفیت رابط فارسی به قراردادهای componentها وابسته است. اگر در هر صفحه یک اصلاح محلی اضافه کنیم، بعداً تغییر یک فاصله یا یک زبان ممکن است بخش دیگری را خراب کند. این راهنما نشان میدهد چگونه جهت را در HTML تعریف کنیم، CSS را با شروع و پایان بنویسیم و داده ترکیبی را بدون دستکاری ترتیب واقعی آن نمایش دهیم.
RTL با راستچین بودن فرق دارد
text-align محل قرار گرفتن متن را در یک جعبه تعیین میکند؛ dir جهت پایه محتوا را مشخص میکند. زبان صفحه نیز موضوع جداگانهای است. برای صفحه فارسی، lang برابر fa و dir برابر rtl دو اطلاعات متفاوتاند. راهنمای جهت متن در HTML از W3C توصیه میکند جهت پایه در markup تعریف شود. این قرارداد حتی وقتی stylesheet در دسترس نباشد، اطلاعات مهم محتوا را حفظ میکند.
جهت نباید با معکوس کردن رشته ساخته شود. حروف، اعداد و نشانهها در متن ترکیبی با قواعد خود نمایش داده میشوند. اگر شماره سفارش را دستی برعکس کنیم، نمایش شاید در یک جای خاص درست به نظر برسد، اما کپی کردن و جستوجوی آن خراب میشود. داده را در ترتیب منطقی خودش نگه دارید و جهت نمایش قسمت لازم را جدا تعریف کنید.
از start و end بهجای فرض چپ و راست استفاده کنید
فاصله کنار عنوان ممکن است در فارسی از راست و در انگلیسی از چپ باشد. اگر معنای آن «ابتدای عنوان» است، margin-inline-start قرارداد مناسبتری از margin-left دارد. Logical Properties در MDN محور inline و block را توضیح میدهد. این مدل کمک میکند قصد طراحی در کد دیده شود و برای هر زبان یک نسخه طولانی override ننویسیم.
هر مختصات فیزیکی هم اشتباه نیست. اگر عنصر باید مستقل از زبان همیشه در گوشه مشخصی از تصویر باشد، استفاده از left یا right میتواند عمدی باشد. مسئله این است که بدانیم آن جایگاه به زبان وابسته است یا به خود تصویر. تبدیل کورکورانه تمام خصوصیتها نیز ممکن است یک طراحی درست را خراب کند. نام property باید معنای موقعیت را بازتاب دهد.
Bootstrap و Frameworkها چه کمکی میکنند؟
Bootstrap برای RTL فایل و دستورالعمل مشخص دارد. در راهنمای رسمی RTL در Bootstrap، تنظیم جهت و استفاده از نسخه مناسب stylesheet توضیح داده شده است. این پشتیبانی به معنی اصلاح خودکار تمام CSS اختصاصی پروژه نیست. کلاس سفارشی با padding-left ثابت یا component خارجی با فرض LTR ممکن است همچنان مسئله داشته باشد.
در هر framework، تفاوت utility منطقی و فیزیکی را بشناسید. قراردادی مانند فاصله در ابتدای عنصر را در کتابخانه component مشترک ثبت کنید. اگر پروژه از چند منبع stylesheet استفاده میکند، ترتیب بارگذاری و محدوده اثر آنها مهم است. افزودن یک فایل RTL در پایان، بدون شناخت overrideهای قبلی، میتواند فقط مشکل را از یک صفحه به صفحه دیگر منتقل کند.
متن ترکیبی را جدا کنید
نام کاربر، ایمیل، URL و شماره سفارش ممکن است با متن فارسی ترکیب شوند. برای مقدار مستقل با جهت نامعلوم، bdi کمک میکند اثر جهت آن از متن اطراف جدا شود. برای دادهای که قراردادش مشخصاً LTR است، مانند ایمیل نمونه، dir برابر ltr میتواند روشن باشد. این انتخاب باید به محتوا وابسته باشد؛ هر رشته لاتین لازم نیست جهت کل component را تغییر دهد.
برای ورودی آزاد مثل نام یا توضیح، dir برابر auto میتواند براساس متن جهت را تعیین کند. ولی برای فرمهایی که قالب مشخص دارند، بهتر است قواعد همان فیلد روشن باشد. فیلد ایمیل با فیلد توضیح فارسی یکسان نیست. پیام خطای کنار فیلد نیز باید خوانا بماند و نباید به دلیل جهت مقدار ورودی، ترتیب کل فرم ناخواسته تغییر کند.
یک نمونه کامل کارت سفارش
کد زیر یک صفحه مستقل با کارت سفارش میسازد. تمام HTML و CSS مورد نیاز داخل فایل است و framework یا فایل تصویری ندارد. آن را با نام rtl-order.html ذخیره کنید و در مرورگر باز کنید. دکمه پایین جهت صفحه را بین RTL و LTR تغییر میدهد تا ببینید فاصلههای منطقی چگونه از جهت پیروی میکنند. متن صفحه فارسی میماند؛ تغییر جهت صرفاً برای نمایش رفتار چیدمان است.
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>نمونه کارت سفارش با RTL</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 24px; font-family: Tahoma, sans-serif; line-height: 1.9; background: #eef2f6; color: #17283a; }
main { max-inline-size: 640px; margin-inline: auto; }
.card { padding: 24px; background: white; border: 1px solid #cad5e1; border-radius: 16px; border-inline-start: 5px solid #176c60; }
.card-header { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.card-header h1 { margin: 0; font-size: 1.25rem; }
.badge { margin-inline-start: auto; padding-block: 2px; padding-inline: 12px; background: #e4f5ef; border-radius: 20px; }
.fields { display: grid; gap: 16px; margin-block: 20px; }
.field { display: grid; gap: 6px; min-inline-size: 0; }
label { font-weight: bold; }
input { inline-size: 100%; padding: 12px; border: 1px solid #9aaec1; border-radius: 8px; font: inherit; }
bdi { overflow-wrap: anywhere; }
button { padding-block: 10px; padding-inline: 18px; border: 0; border-radius: 8px; background: #176c60; color: white; font: inherit; cursor: pointer; }
button:focus-visible, input:focus-visible { outline: 3px solid #bc6813; outline-offset: 3px; }
#status { margin-block-end: 0; }
</style>
</head>
<body>
<main>
<article class="card" aria-labelledby="order-title">
<header class="card-header">
<h1 id="order-title">سفارش <bdi dir="ltr">TR-2026-104</bdi></h1>
<span class="badge">در حال آمادهسازی</span>
</header>
<p>گیرنده: <bdi dir="auto">Sara احمدی</bdi></p>
<p>ایمیل: <bdi dir="ltr">sara@example.com</bdi></p>
<p>مبلغ: ۲۵۰٬۰۰۰ تومان</p>
<div class="fields">
<div class="field">
<label for="customer-email">ایمیل گیرنده</label>
<input id="customer-email" type="email" dir="ltr" value="sara@example.com" autocomplete="email">
</div>
<div class="field">
<label for="customer-note">یادداشت</label>
<input id="customer-note" type="text" dir="auto" value="تحویل عصر انجام شود">
</div>
</div>
<button id="toggle-direction" type="button">تغییر جهت نمایشی به LTR</button>
<p id="status" role="status">جهت فعلی: RTL؛ این نمونه دادهای ارسال نمیکند.</p>
</article>
</main>
<script>
const button = document.getElementById('toggle-direction');
const status = document.getElementById('status');
button.addEventListener('click', () => {
const next = document.documentElement.dir === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.dir = next;
button.textContent = `تغییر جهت نمایشی به ${next === 'rtl' ? 'LTR' : 'RTL'}`;
status.textContent = `جهت فعلی: ${next.toUpperCase()}؛ این نمونه دادهای ارسال نمیکند.`;
});
</script>
</body>
</html>
در این نمونه، classهای card، card-header و fields در markup وجود دارند و stylesheet دقیقاً همانها را هدف میگیرد. مقدار ایمیل داخل bdi با جهت مشخص نمایش داده میشود، ولی نام گیرنده جهت خودکار دارد. input ایمیل نیز جهت LTR دارد و label فارسی آن از قرارداد صفحه پیروی میکند. دکمه هیچ دادهای ارسال یا ذخیره نمیکند و تنها ویژگی dir سند را تغییر میدهد.
Grid و Flex را دوباره معکوس نکنید
جهت سند میتواند روی نقطه آغاز بعضی چیدمانها اثر بگذارد. اگر علاوه بر RTL، در هر component row-reverse نیز اضافه کنیم، ممکن است دوبار جهت را تغییر داده باشیم. ابتدا ترتیب منطقی عناصر را در HTML مشخص کنید و بعد رفتار layout را براساس همان ترتیب طراحی کنید. هدف این نیست که با چند reverse، تصویر نهایی شبیه طرح شود؛ ترتیب خواندن و استفاده نیز باید قابل فهم بماند.
اگر کارتها ترتیب زمانی یا اهمیت مشخصی دارند، ترتیب DOM باید همان معنا را حفظ کند. جابهجایی صرفاً بصری میتواند ترتیب focus را از آنچه دیده میشود جدا کند. برای طراحی فهرست و کارت، راهنمای CSS Grid با مثال کامل و مقاله سیستم Grid و نظم بصری به تصمیم درباره ساختار کمک میکنند. جهت زبان یک لایه دیگر روی همان ساختار است.
همه iconها آینهای نمیشوند
پیکانی که معنای ادامه یا بازگشت در جریان خواندن دارد ممکن است به جهت وابسته باشد. لوگو، علامت پخش یا نشانهای با معنای مستقل، لزوماً نباید mirror شود. اگر همه SVGها را با یک transform عمومی برعکس کنیم، بخشی از معنا خراب میشود. برای iconها قرارداد تعریف کنید: کدام وابسته به جهتاند و کدام شکل ثابت دارند؟ این تصمیم باید در component مشترک ثبت شود.
نام قابل فهم برای دکمه icon نیز ضروری است. شکل پیکان بهتنهایی برای همه کاربران توضیح کافی نیست. در یک دکمه بازگشت، متن یا نام دسترسپذیر باید عمل را بیان کند. وقتی صفحه از فارسی به انگلیسی تغییر میکند، بهتر است معنی دکمه ثابت بماند و فقط نمایش مناسب همان زبان تغییر کند. این رفتار از آینهای کردن بیهدف قابل اعتمادتر است.
فونت، عدد و ارتفاع خط
RTL درست، خوانایی متن را تضمین نمیکند. فونت فارسی، ارتفاع خط و وزن متن باید برای اندازه واقعی صفحه انتخاب شوند. یک فونت که در عنوان زیباست ممکن است در label کوچک فرم وضوح کافی نداشته باشد. رقم فارسی و لاتین را نیز براساس کاربرد انتخاب کنید. برای دادهای که کاربر باید با سیستم دیگری تطبیق دهد، کپی و تشخیص درست اهمیت زیادی دارد.
شناسه فنی را برای زیبایی ظاهری تغییر ندهید. شماره سفارش میتواند با فونت هماهنگ نمایش داده شود، اما مقدار کپیشده باید مطابق قرارداد باشد. واحد پول را هم صریح بنویسید. مقاله راهنمای فونت و تایپوگرافی فارسی جزئیات بیشتری درباره خوانایی دارد. در فرم، چیدمان و انتخاب فونت باید کنار هم تصمیمگیری شوند.
جدول، محتوای طولانی و صفحه کوچک
جدول فارسی میتواند ستونها را در ترتیب مناسب RTL نمایش دهد، اما سلول حاوی کد یا ایمیل نیاز به جهت مستقل داشته باشد. جهت کل جدول را فقط برای اصلاح یک سلول تغییر ندهید. برای مقدار طولانی نیز راهبرد مشخص داشته باشید: شکستن خط، فضای بیشتر یا نمایش کنترلشده. پنهان کردن بخش مهم شناسه بدون امکان دسترسی به مقدار کامل، برای کاربر مشکل ایجاد میکند.
روی صفحه کوچک، صرف راستچین کردن مشکل فضای کم را حل نمیکند. label، مقدار و پیام خطا باید با متن واقعی کنار هم جا بگیرند. عرض ثابت یک component ممکن است با عنوان بلند فارسی سرریز کند. استفاده از اندازه منعطف و min-inline-size مناسب در فرزندان layout کمک میکند. راهنمای طراحی Mobile-first برای فکر کردن به این محدودیتها مرتبط است.
اصلاح پایدار در پروژه موجود
از componentهای مشترک شروع کنید: input، button، card، navigation و dialog. برای هر کدام مشخص کنید چه چیزی از جهت سند پیروی میکند و چه بخشی جهت مستقل دارد. سپس اصلاحهای پراکنده را به همان قرارداد برگردانید. اگر در هر صفحه یک margin متفاوت اضافه شده، احتمالاً مسئله در component اصلی است. اصلاح ریشه بهمرور تعداد استثناهای محلی را کم میکند.
در مستندات تیم، نمونه محتوای فارسی و ترکیبی نگه دارید، نه فقط Lorem Ipsum انگلیسی. عنوان بلند، ایمیل، مبلغ و شماره سفارش نمونههای خوبی هستند. هر تصمیم درباره جهت باید با معنای داده قابل توضیح باشد. رابط فارسی خوب نتیجه یک stylesheet جدا نیست؛ نتیجه هماهنگی markup، layout، تایپوگرافی و قرارداد محتواست. Framework در این مسیر کمک میکند، اما تصمیم درست همچنان در دست تیم طراحی و توسعه است.
هنوز دیدگاهی ثبت نشده
اولین نفری باشید که نظر میدهد!