Root Layout، تنظیم lang="fa" و dir="rtl"
تا اینجا هر page.tsx فقط محتوای صفحهی خودش را تعریف میکرد. اما تگهای html و body، زبان صفحه و جهت متن کجا تعیین میشوند؟ جواب، فایل app/layout.tsx است که به آن Root Layout میگوییم. در این درس این فایل را میشناسیم و پروژهی DevNotes را برای زبان فارسی و چیدمان راستچین آماده میکنیم.
پیشنیاز
- درس «ساخت صفحات با page.tsx»
- آشنایی با ویژگیهای
langوdirدر HTML
Layout چیست؟
Layout یک کامپوننت است که ساختار مشترک چند صفحه را نگه میدارد و صفحهی درونی را بهعنوان children میگیرد. Next.js هنگام نمایش هر مسیر، ابتدا Layout را رندر میکند و صفحه را درون آن قرار میدهد. وقتی بین صفحاتی که Layout مشترک دارند جابهجا میشوید، Layout دوباره از اول ساخته نمیشود و فقط بخش صفحه عوض میشود. به همین دلیل بخشهای تکراری مثل هدر و فوتر بهترین کاندیدا برای Layout هستند. Layoutهای تودرتو را در درس بعد، «Nested Layoutها»، میبینیم.
Root Layout چیست؟
Root Layout بالاترین Layout در پوشهی app است. قواعد مهم آن:
- وجود آن در App Router الزامی است و برای تمام مسیرهای برنامه اعمال میشود.
- باید تگهای
<html>و<body>را خودش تعریف کند. صفحات و Layoutهای دیگر این تگها را تعریف نمیکنند. - باید
childrenرا رندر کند؛ همانجا صفحه یا Layout درونی قرار میگیرد. - بهصورت پیشفرض یک Server Component است.
نگاهی به Root Layout پروژه
فایل app/layout.tsx که create-next-app ساخته است چیزی شبیه این دارد. جزئیات دقیق، مثل فونتهای واردشده، ممکن است بسته به نسخهی ابزار کمی فرق کند:
// app/layout.tsx (نمونهی اولیهی تولیدشده)
import type { Metadata } from "next";
import { Geist } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={geistSans.variable}>{children}</body>
</html>
);
}
دو چیز در همین نمونه توجه ما را جلب میکند: lang="en" که زبان صفحه را انگلیسی اعلام میکند و نبود dir. فونت Geist فقط زیرمجموعهی لاتین دارد و برای متن فارسی مناسب نیست؛ فونت فارسی را در درس «next/font و بارگذاری فونت فارسی» تنظیم میکنیم.
ویژگیهای lang و dir
| ویژگی | مقدار برای DevNotes | اثر |
|---|---|---|
lang |
"fa" |
زبان محتوا را به مرورگر، صفحهخوانها و موتورهای جستوجو اعلام میکند. بر تلفظ صفحهخوانها، غلطیاب املایی و ترجمهی خودکار مرورگر اثر میگذارد. |
dir |
"rtl" |
جهت پایهی متن و چیدمان را راستبهچپ میکند. |
این دو را روی تگ html میگذاریم تا برای کل برنامه اعمال شوند. lang مربوط به معنای زبان است و dir مربوط به جهت نمایش؛ هر دو لازماند و یکی جای دیگری را نمیگیرد.
عملی: Root Layout فارسی DevNotes
فایل app/layout.tsx را با کد زیر جایگزین کنید:
// app/layout.tsx
import type { Metadata } from "next";
import type { ReactNode } from "react";
import "./globals.css";
export const metadata: Metadata = {
title: "DevNotes",
description: "وبسایتی برای انتشار و مدیریت نوشتههای فنی",
};
export default function RootLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<html lang="fa" dir="rtl">
<body>{children}</body>
</html>
);
}توضیح کد
import "./globals.css": استایل سراسری را وارد میکند. Root Layout بهترین جا برای این import است، چون برای همهی صفحات اعمال میشود.metadata: عنوان و توضیح پیشفرض سایت را تعریف میکند. فعلاً فقط مقدار فارسی گذاشتهایم؛ Metadata را در درس «Metadata ثابت و پویا» کامل بررسی میکنیم.ReactNode: نوع هر چیزی که React میتواند رندر کند، از جمله صفحهی درونی. آن را ازreactوارد کردهایم تا به نوع سراسریReactتکیه نکنیم.Readonly<...>: فقط برای اینکه props تغییرناپذیر مشخص شود. حذف آن عملکرد را تغییر نمیدهد.<html lang="fa" dir="rtl">: زبان را فارسی و جهت را راستبهچپ میکند.{children}داخلbody: صفحهی جاری در این نقطه رندر میشود.
پروژهی ما فعلاً فونت Geist را حذف کرده است، پس متن با فونت پیشفرض سیستم نشان داده میشود. این موقتی است و بعداً فونت فارسی را اضافه میکنیم.
خروجی و رفتار مورد انتظار
فایل را ذخیره کنید و صفحههای /، /about و /posts را ببینید. باید این تغییرات را مشاهده کنید:
- متنها از سمت راست صفحه شروع میشوند.
- عنوان تب مرورگر «DevNotes» است.
- برای بررسی خروجی، در مرورگر روی صفحه کلیک راست و گزینهی «View Page Source» را بزنید؛ تگ
htmlباید به این شکل باشد:
<html lang="fa" dir="rtl">اگر تغییر را بلافاصله ندیدید، یکبار صفحه را ریفرش کنید. تغییر در Root Layout گاهی به Fast Refresh کامل نیاز دارد.
اثر dir="rtl" بر چیدمان
dir="rtl" فقط جهت متن را عوض نمیکند، بلکه چیدمان را هم تحتتأثیر قرار میدهد:
- در Flexbox و Grid، ترتیب پیشفرض آیتمها از راست به چپ میشود.
- مقدارهایی مثل
text-align: startبه راست اشاره میکنند. - ویژگیهای «منطقی» CSS مثل
margin-inline-startوpadding-inline-endخودکار با جهت سازگار میشوند؛ ولیmargin-leftوmargin-rightفیزیکی هستند و تغییر نمیکنند.
به همین دلیل، هنگام استایلدهی تا حد امکان از ویژگیهای منطقی استفاده میکنیم. در Tailwind هم معادلهای منطقی مثل ms-* و me-* (فاصلهی ابتدا و انتها) وجود دارند. جزئیات را در درس «طراحی رابط کاربری فارسی و راستچین DevNotes» میبینیم.
نکتهی مهم برای DevNotes: کد باید چپبهراست بماند
وبسایت ما نوشتههای فنی منتشر میکند و کد برنامهنویسی همیشه چپبهراست است. در یک صفحهی راستچین، بلوکهای کد ممکن است با جهت اشتباه نمایش داده شوند، مثلاً علامتگذاری و پرانتزها جابهجا دیده شوند. برای پیشگیری، میتوان در app/globals.css برای کد جهت را صریحاً چپبهراست تعیین کرد:
/* app/globals.css */
pre,
code {
direction: ltr;
text-align: left;
}
این قاعده را الان به انتهای فایل اضافه کنید. در فصلهای بعد وقتی نوشتهها را نمایش میدهیم از آن استفاده میکنیم. اگر فایل globals.css شما قواعد Tailwind یا متغیرهای رنگ دارد، آنها را دست نزنید و این قاعده را فقط در انتها اضافه کنید.
نکتههای مهم
-
فقط یک Root Layout: تگهای
htmlوbodyفقط در Root Layout نوشته میشوند. اگر در Layout تودرتو یا صفحه آنها را دوباره بنویسید، HTML نامعتبر میشود. اگر بعداً پروژه چند Root Layout داشته باشد (با Route Groups)، هرکدام این تگها را تعریف میکنند؛ این موضوع را در درس «Route Groups و سازماندهی مسیرها» میبینیم. -
جهت محلی: اگر بخش کوچکی از صفحه باید چپبهراست باشد (مثلاً یک متن انگلیسی طولانی)، میتوانید روی همان عنصر
dir="ltr"بگذارید، بدون اینکه جهت کل صفحه تغییر کند. - متنهای ترکیبی: وقتی فارسی و انگلیسی یا عدد در یک جمله ترکیب میشوند، مرورگر الگوریتم خودکار جهتدهی دوطرفه (Bidi) را اجرا میکند. گاهی علائم نگارشی در انتهای جملهی ترکیبی جای غیرمنتظره میگیرند. این رفتار خودِ مرورگر است، نه ایراد Next.js.
-
Layout و دادهی صفحه: Layout نمیتواند
searchParamsرا بهعنوان props دریافت کند؛ فقط صفحه میتواند. این نکته را در درس «searchParams و Query String» میبینیم.
جمعبندی
- Root Layout فایل
app/layout.tsxاست؛ وجودش الزامی است، برای کل برنامه اعمال میشود و باید تگهایhtmlوbodyرا بسازد. - Layout صفحهی درونی را از
childrenمیگیرد و هنگام جابهجایی بین صفحات مشترک باقی میماند. - با
lang="fa"زبان صفحه و باdir="rtl"جهت راستبهچپ تعریف میشود؛ هر دو را رویhtmlمیگذاریم. - برای چیدمان راستچین از ویژگیهای منطقی CSS استفاده میکنیم و بلوکهای کد را با
direction: ltrچپبهراست نگه میداریم. - فونت فارسی و Metadata کاملتر را در درسهای بعدی تنظیم میکنیم.
- در درس بعد، «Nested Layoutها»، Layoutهای مخصوص هر بخش از سایت را میسازیم.
