مقالات › CSS و طراحی وب

طراحی سایت راست‌چین و دوزبانه؛ راهنمای عملی برای فارسی و عربی

سایت حرفه‌ای راست‌چین و دوزبانه (RTL/LTR) بسازید: dir و lang، ویژگی‌های منطقی CSS، متن دوجهته، باگ‌های bidi، فونت، اعداد، تاریخ شمسی، آیکون‌ها و سئو با hreflang.

صدها میلیون نفر از راست به چپ می‌خوانند: فارسی‌زبان‌ها، عرب‌زبان‌ها، عبری‌زبان‌ها و اردوزبان‌ها. ساختن سایتی که در RTL طبیعی به نظر برسد — و به‌راحتی به انگلیسی تغییر کند — خیلی بیشتر از اضافه کردن direction: rtl است. این راهنما درس‌هایی را جمع کرده که هنگام ساخت همین سایت یاد گرفتم؛ سایتی که فارسی و انگلیسی را از یک کدبیس ارائه می‌دهد.

۱. با dir و lang روی المان html شروع کنید

<html lang="fa" dir="rtl">
  • dir="rtl" جهت پایه را برعکس می‌کند: تراز متن، جریان Flex و Grid، ترتیب ستون‌های جدول و محل اسکرول‌بار.
  • lang="fa" به مرورگر اجازه می‌دهد فونت، شکستن کلمات، گیومه و تلفظ صفحه‌خوان را درست انتخاب کند و به موتورهای جستجو هم کمک می‌کند.

در یک اپلیکیشن React دوزبانه، با هر تغییر زبان هر دو را به‌روز کنید:

useEffect(() => {
  document.documentElement.lang = lang;
  document.documentElement.dir = lang === "fa" ? "rtl" : "ltr";
}, [lang]);

ویژگی dir را به خاصیت CSS direction ترجیح دهید؛ معنای معنایی دارد و الگوریتم bidi همه‌جا به آن احترام می‌گذارد.

۲. از ویژگی‌های منطقی CSS استفاده کنید

ویژگی‌های فیزیکی (left، right، margin-left) برعکس نمی‌شوند. ویژگی‌های منطقی خودکار از جهت نوشتار پیروی می‌کنند:

فیزیکی منطقی
margin-left / margin-right margin-inline-start / margin-inline-end
padding-left padding-inline-start
left: 0 inset-inline-start: 0
border-right border-inline-end
text-align: left text-align: start
top / bottom inset-block-start / inset-block-end
.card__badge {
  position: absolute;
  inset-block-start: 12px;
  inset-inline-start: 12px;   /* در انگلیسی بالا-چپ، در فارسی بالا-راست */
}

Flexbox و Grid از قبل از dir پیروی می‌کنند: در RTL، justify-content: flex-start یعنی راست. با ویژگی‌های منطقی یک فایل استایل برای هر دو زبان کافی است؛ نیازی به rtl.css جداگانه نیست.

۳. آیکون‌های جهت‌دار و Transformها

فلش‌ها، شورون‌ها و آیکون‌های «بازگشت» و «بعدی» باید قرینه شوند؛ اما لوگوها، کنترل‌های رسانه (Play)، تیک و آیکون برندها نه.

[dir="rtl"] .flip-rtl {
  transform: scaleX(-1);
}

Transformها و گرادیان‌ها هم منطقی نیستند. translateX(100%) در هر دو جهت به راست حرکت می‌کند و linear-gradient(90deg, …) همیشه از چپ به راست است. آن‌ها را صریح مدیریت کنید:

.progress { transform-origin: left; }
[dir="rtl"] .progress { transform-origin: right; }

۴. متن دوجهته و الگوریتم bidi

متن فنی فارسی مدام کلمات انگلیسی دارد: «ASP.NET Core»، «React»، «C#». الگوریتم دوجهته‌ی یونیکد (bidi) بیشتر موارد را درست مدیریت می‌کند، اما چند تله‌ی رایج وجود دارد.

تله‌ی ۱: کاراکترهای خنثی در لبه‌ها

در یک پاراگراف RTL، C# وقتی قبل از یک کلمه‌ی فارسی بیاید اغلب به شکل #C نمایش داده می‌شود؛ چون # یک کاراکتر خنثی است و جهت پاراگراف را می‌گیرد. با یک LRM نامرئی (U+200E) بعد از علامت یا قرار دادن عبارت در <bdi> یا <span dir="ltr"> درستش کنید:

<p>تخصص من <bdi>C#</bdi> و ASP.NET Core است.</p>

تله‌ی ۲: انیمیشن‌های کلمه‌به‌کلمه

کتابخانه‌های انیمیشن متن اغلب هر کلمه را در یک span از نوع inline-block می‌پیچند. هر span برای الگوریتم bidi یک واحد ایزوله می‌شود و یک عبارت انگلیسی داخل متن فارسی برعکس نمایش داده می‌شود: «Core ASP.NET». راه‌حل‌ها: متن فارسی را به‌جای کلمه بر اساس خط تقسیم کنید یا قبل از انیمیشن، کلمات لاتین پشت‌سرهم را با dir="ltr" در یک واحد گروه‌بندی کنید.

تله‌ی ۳: محتوای کاربران

برای محتوایی که جهتش را نمی‌دانید (نام کاربری، نظرات، نتایج جستجو) بگذارید مرورگر برای هر المان تصمیم بگیرد:

<span dir="auto">{comment.text}</span>

برای مقادیر درون‌خطی مثل نام کاربری داخل جمله هم از unicode-bidi: isolate (یا <bdi>) استفاده کنید تا متن اطراف به هم نریزد.

۵. کد، آدرس، ایمیل و شماره تلفن

این‌ها همیشه LTR هستند، حتی داخل متن RTL:

<a href="mailto:info@example.com" dir="ltr">info@example.com</a>
<span dir="ltr">0912 345 6789</span>
<pre dir="ltr"><code>var x = 42;</code></pre>

در مقالات، هر بلوک کد را dir="ltr" و text-align: left کنید. کامنت‌های فارسی داخل کد هم به فونتی نیاز دارند که حروف فارسی داشته باشد؛ یک فونت فارسی را بعد از فونت مونواسپیس در font-family اضافه کنید، وگرنه حروف ممکن است جدا جدا نمایش داده شوند.

۶. فونت و تایپوگرافی

  • از یک فونت فارسی باکیفیت مثل وزیرمتن (Vazirmatn) — متغیر و متن‌باز — استفاده کنید و برای کارایی و اطمینان آن را روی سرور خودتان میزبانی کنید.
  • برای هر زبان فونت جداگانه بارگذاری کنید: متن فارسی در فونت‌های لاتین بد دیده می‌شود و انگلیسی هم معمولاً با یک فونت لاتین اختصاصی زیباتر است.
:root[lang="fa"] { --font: "Vazirmatn Variable", Tahoma, sans-serif; }
:root[lang="en"] { --font: "Inter Variable", system-ui, sans-serif; }
body { font-family: var(--font); }
  • فارسی به ارتفاع خط بیشتری (حدود ۱٫۹ تا ۲٫۱ برای متن اصلی) نسبت به انگلیسی (حدود ۱٫۶ تا ۱٫۸) نیاز دارد.
  • در فارسی از letter-spacing منفی پرهیز کنید؛ اتصال حروف را خراب می‌کند.
  • نیم‌فاصله (ZWNJ) را در متن فارسی درست به کار ببرید: «می‌شود»، «کتاب‌ها».

۷. اعداد و تاریخ

new Intl.NumberFormat("fa-IR").format(1250000);           // ۱٬۲۵۰٬۰۰۰
new Intl.DateTimeFormat("fa-IR-u-ca-persian", {
  day: "numeric", month: "long", year: "numeric",
}).format(new Date());                                      // ۲ تیر ۱۴۰۵
new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(new Date()); // June 23, 2026
  • به‌جای مبدل‌های دست‌نویس از API داخلی Intl استفاده کنید.
  • تاریخ را به UTC ذخیره و هنگام نمایش برای هر زبان قالب‌بندی کنید.
  • در ورودی‌ها هم ارقام فارسی و هم لاتین را بپذیرید (کاربران هر دو را تایپ می‌کنند) و قبل از اعتبارسنجی یکسان‌سازی کنید.

۸. فرم‌ها و ورودی‌ها

  • فیلدهای تلفن، ایمیل، آدرس و کد حتی در صفحات فارسی باید dir="ltr" باشند.
  • Placeholder و برچسب از زبان صفحه پیروی می‌کنند.
  • پیام‌های اعتبارسنجی باید ترجمه شوند؛ و از چسباندن تکه‌های جمله پرهیز کنید، چون ترتیب کلمات در زبان‌ها متفاوت است.

۹. معماری دوزبانه

  • زبان را در آدرس قرار دهید: /fa/articles/… و /en/articles/…. قابل اشتراک، قابل کش و قابل ایندکس است.
  • متن‌های رابط کاربری را در دیکشنری نگه دارید؛ ترجمه‌ی محتوا را در فیلدها (title، titleEn) یا جدول‌های جداگانه.
  • انتخاب کاربر را به خاطر بسپارید (کوکی/localStorage)؛ برای اولین بازدید می‌توانید بر اساس کشور IP یا زبان مرورگر پیش‌فرض بگذارید؛ اما همیشه یک دکمه‌ی تغییر زبان قابل دیدن داشته باشید.
  • وقتی ترجمه‌ای وجود ندارد، با ظرافت به زبان دیگر برگردید.

۱۰. سئو برای سایت‌های چندزبانه

به موتورهای جستجو بگویید صفحات چه ارتباطی با هم دارند:

<link rel="canonical" href="https://example.com/fa/articles/rtl-guide" />
<link rel="alternate" hreflang="fa" href="https://example.com/fa/articles/rtl-guide" />
<link rel="alternate" hreflang="en" href="https://example.com/en/articles/rtl-guide" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/articles/rtl-guide" />
  • هر نسخه‌ی زبانی آدرس و Canonical خودش را دارد.
  • جفت‌های hreflang باید دوطرفه باشند.
  • ربات‌های موتور جستجو را برای همه‌ی آدرس‌ها بر اساس IP ریدایرکت نکنید؛ آدرس‌های مخصوص هر زبان باید قابل دسترسی بمانند.
  • عنوان، توضیحات متا و داده‌ی ساخت‌یافته (inLanguage) را هم ترجمه کنید، نه فقط متن اصلی.
  • همه‌ی نسخه‌های زبانی را در Sitemap قرار دهید.

چک‌لیست تست

  • در همه‌ی انواع صفحه زبان را عوض کنید؛ تراز، آیکون‌ها و انیمیشن‌ها را بررسی کنید.
  • دنبال عبارت‌های انگلیسی برعکس‌شده و باگ‌هایی مثل #C در متن فارسی بگردید.
  • فرم‌ها را بررسی کنید: فیلدهای تلفن و ایمیل و پیام‌های اعتبارسنجی.
  • قالب تاریخ، عدد و واحد پول را در هر دو زبان بررسی کنید.
  • تگ‌های lang، dir، hreflang و Canonical را در HTML بررسی کنید.

جمع‌بندی

سایت‌های حرفه‌ای راست‌چین و دوزبانه به چند اصل تکیه دارند: dir و lang را تنظیم کنید، CSS را با ویژگی‌های منطقی بنویسید، فقط آیکون‌های جهت‌دار را قرینه کنید، به الگوریتم bidi احترام بگذارید (عبارت‌های LTR را ایزوله و از تقسیم کلمه‌به‌کلمه‌ی فارسی پرهیز کنید)، فونت و ارتفاع خط مناسب انتخاب کنید، اعداد و تاریخ را با Intl قالب‌بندی کنید، زبان را در آدرس بگذارید و نسخه‌ها را با hreflang به هم وصل کنید. با رعایت این‌ها سایت شما در هر زبانی بومی به نظر می‌رسد.

پرسش‌های متداول

آیا به یک فایل استایل جداگانه برای RTL نیاز دارم؟

اگر از ویژگی‌های منطقی استفاده کنید نه. چند بازنویسی مخصوص جهت (Transform، گرادیان، آیکون) کافی است.

آیا سایت فارسی باید از ارقام فارسی استفاده کند؟

در متن و رابط کاربری بله؛ حس بومی بودن می‌دهد. در کد، شناسه‌ها و برخی مقادیر فنی ارقام لاتین مورد انتظار است.

آیا ریدایرکت کاربران بر اساس IP برای سئو خوب است؟

ریدایرکت فقط آدرس ریشه مشکلی ندارد، به شرطی که هر زبان آدرس قابل خزش خودش را داشته باشد و یک دکمه‌ی تغییر دستی زبان هم فراهم کنید.