فارسی

قدرت مسیریاب اپ Next.js را با راهنمای عمیق ما در زمینه مسیریابی مبتنی بر فایل کشف کنید. یاد بگیرید چگونه برنامه خود را ساختاردهی کنید، مسیرهای داینامیک بسازید، چیدمان‌ها را مدیریت کنید و موارد دیگر.

مسیریاب اپ Next.js: راهنمای جامع مسیریابی مبتنی بر فایل

مسیریاب اپ Next.js که در Next.js 13 معرفی و در نسخه‌های بعدی به استاندارد تبدیل شد، نحوه ساختاردهی و ناوبری برنامه‌ها را متحول می‌کند. این مسیریاب یک سیستم مسیریابی قدرتمند و بصری مبتنی بر فایل را معرفی می‌کند که توسعه را ساده‌تر، عملکرد را بهبود و تجربه کلی توسعه‌دهنده را ارتقا می‌بخشد. این راهنمای جامع به طور عمیق به مسیریابی مبتنی بر فایل در مسیریاب اپ می‌پردازد و دانش و مهارت لازم برای ساخت برنامه‌های Next.js قوی و مقیاس‌پذیر را در اختیار شما قرار می‌دهد.

مسیریابی مبتنی بر فایل چیست؟

مسیریابی مبتنی بر فایل یک سیستم مسیریابی است که در آن ساختار مسیرهای برنامه شما مستقیماً توسط سازمان‌دهی فایل‌ها و دایرکتوری‌هایتان تعیین می‌شود. در مسیریاب اپ Next.js، شما مسیرها را با ایجاد فایل‌ها در داخل دایرکتوری `app` تعریف می‌کنید. هر پوشه یک بخش از مسیر (route segment) را نشان می‌دهد و فایل‌های ویژه درون آن پوشه‌ها نحوه مدیریت آن بخش از مسیر را مشخص می‌کنند. این رویکرد چندین مزیت دارد:

شروع کار با مسیریاب اپ

برای استفاده از مسیریاب اپ، باید یک پروژه جدید Next.js ایجاد کنید یا یک پروژه موجود را مهاجرت دهید. اطمینان حاصل کنید که از نسخه ۱۳ Next.js یا بالاتر استفاده می‌کنید.

ایجاد یک پروژه جدید:

می‌توانید یک پروژه جدید Next.js با مسیریاب اپ را با استفاده از دستور زیر ایجاد کنید:

npx create-next-app@latest my-app --example with-app

مهاجرت یک پروژه موجود:

برای مهاجرت یک پروژه موجود، باید صفحات خود را از دایرکتوری `pages` به دایرکتوری `app` منتقل کنید. ممکن است لازم باشد منطق مسیریابی خود را متناسب با آن تنظیم کنید. Next.js یک راهنمای مهاجرت برای کمک به شما در این فرآیند ارائه می‌دهد.

مفاهیم اصلی مسیریابی مبتنی بر فایل

مسیریاب اپ چندین فایل و قرارداد ویژه را معرفی می‌کند که نحوه مدیریت مسیرهای شما را تعریف می‌کنند:

۱. دایرکتوری `app`

دایرکتوری `app` ریشه مسیرهای برنامه شما است. تمام فایل‌ها و پوشه‌های داخل این دایرکتوری برای تولید مسیرها استفاده خواهند شد. هر چیزی خارج از دایرکتوری `app` (مانند دایرکتوری `pages` اگر در حال مهاجرت هستید) توسط مسیریاب اپ نادیده گرفته می‌شود.

۲. فایل `page.js`

فایل `page.js` (یا `page.jsx`، `page.ts`، `page.tsx`) بنیادی‌ترین بخش مسیریاب اپ است. این فایل کامپوننت UI را که برای یک بخش مسیر خاص رندر می‌شود، تعریف می‌کند. این یک فایل ضروری برای هر بخش مسیری است که می‌خواهید مستقیماً قابل دسترسی باشد.

مثال:

اگر ساختار فایلی مانند این داشته باشید:

app/
  about/
    page.js

کامپوننتی که از `app/about/page.js` خروجی گرفته می‌شود، زمانی که کاربر به `/about` مراجعه می‌کند، رندر خواهد شد.

// app/about/page.js
import React from 'react';

export default function AboutPage() {
  return (
    <div>
      <h1>درباره ما</h1>
      <p>درباره شرکت ما بیشتر بدانید.</p>
    </div>
  );
}

۳. فایل `layout.js`

فایل `layout.js` (یا `layout.jsx`، `layout.ts`، `layout.tsx`) یک UI را تعریف می‌کند که در چندین صفحه در یک بخش مسیر به اشتراک گذاشته می‌شود. چیدمان‌ها برای ایجاد هدرها، فوترها، سایدبارها و سایر عناصری که باید در چندین صفحه وجود داشته باشند، مفید هستند.

مثال:

فرض کنید می‌خواهید یک هدر به هر دو صفحه `/about` و یک صفحه فرضی `/about/team` اضافه کنید. می‌توانید یک فایل `layout.js` در دایرکتوری `app/about` ایجاد کنید:

// app/about/layout.js
import React from 'react';

export default function AboutLayout({ children }) {
  return (
    <div>
      <header>
        <h1>درباره شرکت ما</h1>
      </header>
      <main>{children}</main>
    </div>
  );
}

پراپ `children` با UI رندر شده توسط فایل `page.js` در همان دایرکتوری یا در هر دایرکتوری تودرتو جایگزین خواهد شد.

۴. فایل `template.js`

فایل `template.js` شبیه به `layout.js` است، اما برای هر مسیر فرزند یک نمونه جدید از کامپوننت ایجاد می‌کند. این برای سناریوهایی مفید است که می‌خواهید وضعیت کامپوننت را حفظ کنید یا از رندر مجدد هنگام ناوبری بین مسیرهای فرزند جلوگیری کنید. برخلاف چیدمان‌ها، الگوها (templates) هنگام ناوبری دوباره رندر می‌شوند. استفاده از الگوها برای انیمیشن دادن به عناصر هنگام ناوبری عالی است.

مثال:

// app/template.js
'use client'

import { useState } from 'react'

export default function Template({ children }) {
  const [count, setCount] = useState(0)

  return (
    <main>
      <p>الگو: {count}</p>
      <button onClick={() => setCount(count + 1)}>به‌روزرسانی الگو</button>
      {children}
    </main>
  )
}

۵. فایل `loading.js`

فایل `loading.js` (یا `loading.jsx`، `loading.ts`، `loading.tsx`) به شما امکان می‌دهد یک UI بارگذاری ایجاد کنید که هنگام بارگذاری یک بخش مسیر نمایش داده می‌شود. این برای ارائه تجربه کاربری بهتر هنگام دریافت داده یا انجام عملیات ناهمزمان دیگر مفید است.

مثال:

// app/about/loading.js
import React from 'react';

export default function Loading() {
  return <p>در حال بارگذاری اطلاعات درباره ما...</p>;
}

هنگامی که کاربر به `/about` مراجعه می‌کند، کامپوننت `Loading` تا زمانی که کامپوننت `page.js` به طور کامل رندر شود، نمایش داده خواهد شد.

۶. فایل `error.js`

فایل `error.js` (یا `error.jsx`، `error.ts`، `error.tsx`) به شما امکان می‌دهد یک UI خطای سفارشی ایجاد کنید که هنگام بروز خطا در یک بخش مسیر نمایش داده می‌شود. این برای ارائه پیام خطای کاربرپسندتر و جلوگیری از کرش کردن کل برنامه مفید است.

مثال:

// app/about/error.js
'use client'

import React from 'react';

export default function Error({ error, reset }) {
  return (
    <div>
      <h2>خطایی رخ داد!</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>تلاش مجدد</button>
    </div>
  );
}

اگر هنگام رندر کردن صفحه `/about` خطایی رخ دهد، کامپوننت `Error` نمایش داده می‌شود. پراپ `error` حاوی اطلاعاتی در مورد خطا است و تابع `reset` به کاربر امکان می‌دهد تلاش کند صفحه را دوباره بارگذاری کند.

۷. گروه‌های مسیر (Route Groups)

گروه‌های مسیر `(groupName)` به شما امکان می‌دهند مسیرهای خود را بدون تأثیر بر ساختار URL سازمان‌دهی کنید. آنها با قرار دادن نام یک پوشه در داخل پرانتز ایجاد می‌شوند. این به ویژه برای سازمان‌دهی چیدمان‌ها و کامپوننت‌های مشترک مفید است.

مثال:

app/
  (marketing)/
    about/
      page.js
    contact/
      page.js
  (shop)/
    products/
      page.js

در این مثال، صفحات `about` و `contact` تحت گروه `marketing` و صفحه `products` تحت گروه `shop` گروه‌بندی شده‌اند. URLها به ترتیب `/about`، `/contact` و `/products` باقی می‌مانند.

۸. مسیرهای داینامیک (Dynamic Routes)

مسیرهای داینامیک به شما امکان می‌دهند مسیرهایی با بخش‌های متغیر ایجاد کنید. این برای نمایش محتوا بر اساس داده‌های دریافت شده از یک پایگاه داده یا API مفید است. بخش‌های مسیر داینامیک با قرار دادن نام بخش در داخل براکت (مثلاً `[id]`) تعریف می‌شوند.

مثال:

فرض کنید می‌خواهید مسیری برای نمایش پست‌های وبلاگ جداگانه بر اساس شناسه آنها ایجاد کنید. می‌توانید ساختار فایلی مانند این ایجاد کنید:

app/
  blog/
    [id]/
      page.js

بخش `[id]` یک بخش داینامیک است. کامپوننتی که از `app/blog/[id]/page.js` خروجی گرفته می‌شود، زمانی که کاربر به URLای مانند `/blog/123` یا `/blog/456` مراجعه می‌کند، رندر خواهد شد. مقدار پارامتر `id` در پراپ `params` کامپوننت در دسترس خواهد بود.

// app/blog/[id]/page.js
import React from 'react';

export default async function BlogPost({ params }) {
  const { id } = params;

  // دریافت داده برای پست وبلاگ با شناسه داده شده
  const post = await fetchBlogPost(id);

  if (!post) {
    return <p>پست وبلاگ یافت نشد.</p>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
}

async function fetchBlogPost(id) {
  // شبیه‌سازی دریافت داده از پایگاه داده یا API
  return new Promise((resolve) => {
    setTimeout(() => {
      const posts = {
        '123': { title: 'اولین پست وبلاگ من', content: 'این محتوای اولین پست وبلاگ من است.' },
        '456': { title: 'پست وبلاگ دیگر', content: 'این مقداری محتوای هیجان‌انگیز دیگر است.' },
      };
      resolve(posts[id] || null);
    }, 500);
  });
}

همچنین می‌توانید از چندین بخش داینامیک در یک مسیر استفاده کنید. برای مثال، می‌توانید مسیری مانند `/blog/[category]/[id]` داشته باشید.

۹. سگمنت‌های جامع (Catch-all Segments)

سگمنت‌های جامع به شما امکان می‌دهند مسیرهایی ایجاد کنید که با هر تعداد از سگمنت‌ها مطابقت دارند. این برای سناریوهایی مانند ایجاد یک CMS که ساختار URL توسط کاربر تعیین می‌شود، مفید است. سگمنت‌های جامع با افزودن سه نقطه قبل از نام سگمنت تعریف می‌شوند (مثلاً `[...slug]`).

مثال:

app/
  docs/
    [...slug]/
      page.js

سگمنت `[...slug]` با هر تعداد سگمنت بعد از `/docs` مطابقت خواهد داشت. برای مثال، با `/docs/getting-started`، `/docs/api/users` و `/docs/advanced/configuration` مطابقت خواهد داشت. مقدار پارامتر `slug` یک آرایه حاوی سگمنت‌های منطبق خواهد بود.

// app/docs/[...slug]/page.js
import React from 'react';

export default function DocsPage({ params }) {
  const { slug } = params;

  return (
    <div>
      <h1>مستندات</h1>
      <p>اسلاگ: {slug ? slug.join('/') : 'اسلاگ وجود ندارد'}</p>
    </div>
  );
}

سگمنت‌های جامع اختیاری را می‌توان با افزودن نام سگمنت در داخل براکت دوتایی `[[...slug]]` ایجاد کرد. این کار بخش مسیر را اختیاری می‌کند. مثال:

app/
  blog/
    [[...slug]]/
      page.js

این تنظیمات کامپوننت page.js را هم در `/blog` و هم در `/blog/any/number/of/segments` رندر می‌کند.

۱۰. مسیرهای موازی (Parallel Routes)

مسیرهای موازی به شما امکان می‌دهند یک یا چند صفحه را به طور همزمان در یک چیدمان رندر کنید. این به ویژه برای چیدمان‌های پیچیده مانند داشبوردها مفید است، جایی که بخش‌های مختلف صفحه می‌توانند به طور مستقل بارگذاری شوند. مسیرهای موازی با استفاده از نماد `@` و به دنبال آن نام یک اسلات (slot) تعریف می‌شوند (مثلاً `@sidebar`، `@main`).

مثال:

app/
  @sidebar/
    page.js  // محتوا برای سایدبار
  @main/
    page.js  // محتوا برای بخش اصلی
  default.js // ضروری: چیدمان پیش‌فرض برای مسیرهای موازی را تعریف می‌کند

فایل `default.js` هنگام استفاده از مسیرهای موازی ضروری است. این فایل نحوه ترکیب اسلات‌های مختلف برای ایجاد چیدمان نهایی را تعریف می‌کند.

// app/default.js
export default function RootLayout({ children: { sidebar, main } }) {
  return (
    <div style={{ display: 'flex' }}>
      <aside style={{ width: '200px', backgroundColor: '#f0f0f0' }}>
        {sidebar}
      </aside>
      <main style={{ flex: 1, padding: '20px' }}>
        {main}
      </main>
    </div>
  );
}

۱۱. مسیرهای رهگیری (Intercepting Routes)

مسیرهای رهگیری به شما امکان می‌دهند یک مسیر را از بخش دیگری از برنامه خود در چیدمان فعلی بارگذاری کنید. این برای ایجاد مودال‌ها، گالری‌های تصاویر و سایر عناصر UI که باید روی محتوای صفحه موجود ظاهر شوند، مفید است. مسیرهای رهگیری با استفاده از سینتکس `(..)` تعریف می‌شوند که نشان می‌دهد برای پیدا کردن مسیر رهگیری شده چند سطح در درخت دایرکتوری باید بالا رفت.

مثال:

app/
  (.)photos/
    [id]/
      page.js  // مسیر رهگیری شده
  feed/
    page.js  // صفحه‌ای که مودال عکس در آن نمایش داده می‌شود

در این مثال، زمانی که کاربر روی عکسی در صفحه `/feed` کلیک می‌کند، مسیر `app/(.)photos/[id]/page.js` رهگیری شده و به عنوان یک مودال روی صفحه `/feed` نمایش داده می‌شود. سینتکس `(.)` به Next.js می‌گوید که برای پیدا کردن مسیر `photos/[id]` یک سطح به بالا (به دایرکتوری `app`) نگاه کند.

دریافت داده با مسیریاب اپ

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

کامپوننت‌های سرور

کامپوننت‌های سرور در مسیریاب اپ پیش‌فرض هستند. آنها به شما امکان می‌دهند داده‌ها را مستقیماً در کامپوننت‌های خود و بدون نیاز به مسیرهای API جداگانه دریافت کنید. این می‌تواند عملکرد را بهبود بخشد و کد شما را ساده‌تر کند.

مثال:

// app/products/page.js
import React from 'react';

export default async function ProductsPage() {
  const products = await fetchProducts();

  return (
    <div>
      <h1>محصولات</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
}

async function fetchProducts() {
  // شبیه‌سازی دریافت داده از پایگاه داده یا API
  return new Promise((resolve) => {
    setTimeout(() => {
      const products = [
        { id: 1, name: 'محصول الف' },
        { id: 2, name: 'محصول ب' },
        { id: 3, name: 'محصول ج' },
      ];
      resolve(products);
    }, 500);
  });
}

در این مثال، تابع `fetchProducts` مستقیماً در داخل کامپوننت `ProductsPage` فراخوانی می‌شود. کامپوننت در سرور رندر می‌شود و داده‌ها قبل از ارسال HTML به کلاینت دریافت می‌شوند.

کامپوننت‌های کلاینت

کامپوننت‌های کلاینت در کلاینت رندر می‌شوند و به شما امکان می‌دهند از ویژگی‌های سمت کلاینت مانند شنونده‌های رویداد (event listeners)، وضعیت (state) و APIهای مرورگر استفاده کنید. برای استفاده از یک کامپوننت کلاینت، باید دستور `'use client'` را در بالای فایل اضافه کنید.

مثال:

// app/counter/page.js
'use client'

import React, { useState } from 'react';

export default function CounterPage() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>شمارنده</h1>
      <p>تعداد: {count}</p>
      <button onClick={() => setCount(count + 1)}>افزایش</button>
    </div>
  );
}

در این مثال، کامپوننت `CounterPage` یک کامپوننت کلاینت است زیرا از هوک `useState` استفاده می‌کند. دستور `'use client'` به Next.js می‌گوید که این کامپوننت را در کلاینت رندر کند.

تکنیک‌های پیشرفته مسیریابی

مسیریاب اپ چندین تکنیک پیشرفته مسیریابی را ارائه می‌دهد که می‌توان از آنها برای ایجاد برنامه‌های پیچیده و پیشرفته استفاده کرد.

۱. کنترل‌کننده‌های مسیر (Route Handlers)

کنترل‌کننده‌های مسیر به شما امکان می‌دهند نقاط پایانی API را در دایرکتوری `app` خود ایجاد کنید. این نیاز به یک دایرکتوری جداگانه `pages/api` را از بین می‌برد. کنترل‌کننده‌های مسیر در فایل‌هایی به نام `route.js` (یا `route.ts`) تعریف می‌شوند و توابعی را خروجی می‌گیرند که متدهای مختلف HTTP (مانند `GET`، `POST`، `PUT`، `DELETE`) را مدیریت می‌کنند.

مثال:

// app/api/users/route.js
import { NextResponse } from 'next/server'

export async function GET(request) {
  // شبیه‌سازی دریافت کاربران از پایگاه داده
  const users = [
    { id: 1, name: 'جان دو' },
    { id: 2, name: 'جین دو' },
  ];

  return NextResponse.json(users);
}

export async function POST(request) {
  const body = await request.json()
  console.log('داده دریافت شد:', body)
  return NextResponse.json({ message: 'کاربر ایجاد شد' }, { status: 201 })
}

این مثال یک کنترل‌کننده مسیر در `/api/users` تعریف می‌کند که هم درخواست‌های `GET` و هم `POST` را مدیریت می‌کند. تابع `GET` لیستی از کاربران را برمی‌گرداند و تابع `POST` یک کاربر جدید ایجاد می‌کند.

۲. گروه‌های مسیر با چیدمان‌های چندگانه

می‌توانید گروه‌های مسیر را با چیدمان‌ها ترکیب کنید تا چیدمان‌های مختلفی برای بخش‌های مختلف برنامه خود ایجاد کنید. این برای سناریوهایی مفید است که می‌خواهید هدر یا سایدبار متفاوتی برای بخش‌های مختلف سایت خود داشته باشید.

مثال:

app/
  (marketing)/
    layout.js  // چیدمان بازاریابی
    about/
      page.js
    contact/
      page.js
  (admin)/
    layout.js  // چیدمان ادمین
    dashboard/
      page.js

در این مثال، صفحات `about` و `contact` از چیدمان `marketing` استفاده می‌کنند، در حالی که صفحه `dashboard` از چیدمان `admin` استفاده خواهد کرد.

۳. میان‌افزار (Middleware)

میان‌افزار به شما امکان می‌دهد کدی را قبل از اینکه یک درخواست توسط برنامه شما مدیریت شود، اجرا کنید. این برای وظایفی مانند احراز هویت، اعتبارسنجی، لاگ‌گیری و هدایت کاربران بر اساس موقعیت مکانی یا دستگاه آنها مفید است.

میان‌افزار در فایلی به نام `middleware.js` (یا `middleware.ts`) در ریشه پروژه شما تعریف می‌شود.

مثال:

// middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  // بررسی اینکه آیا کاربر احراز هویت شده است
  const isAuthenticated = false; // با منطق احراز هویت خود جایگزین کنید

  if (!isAuthenticated && request.nextUrl.pathname.startsWith('/admin')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

// برای اطلاعات بیشتر به بخش "مسیرهای منطبق" در ادامه مراجعه کنید
export const config = {
  matcher: '/admin/:path*',
}

این مثال یک میان‌افزار تعریف می‌کند که قبل از اجازه دسترسی کاربر به هر مسیری تحت `/admin`، بررسی می‌کند که آیا کاربر احراز هویت شده است یا خیر. اگر کاربر احراز هویت نشده باشد، به صفحه `/login` هدایت می‌شود.

بهترین شیوه‌ها برای مسیریابی مبتنی بر فایل

برای استفاده حداکثری از سیستم مسیریابی مبتنی بر فایل مسیریاب اپ، بهترین شیوه‌های زیر را در نظر بگیرید:

مثال‌هایی از بین‌المللی‌سازی با مسیریاب اپ Next.js

مسیریاب اپ Next.js بین‌المللی‌سازی (i18n) را از طریق مسیریابی مبتنی بر فایل ساده می‌کند. در اینجا نحوه پیاده‌سازی موثر i18n آمده است:

۱. مسیریابی با زیرمسیر (Sub-path)

مسیرهای خود را بر اساس زبان (locale) با استفاده از زیرمسیرها سازمان‌دهی کنید. برای مثال:

app/
  [locale]/
    page.tsx         // صفحه اصلی برای زبان (locale)
    about/
      page.tsx     // صفحه درباره ما برای زبان (locale)
// app/[locale]/page.tsx
import { getTranslations } from './dictionaries';

export default async function HomePage({ params: { locale } }) {
  const t = await getTranslations(locale);
  return (<h1>{t.home.title}</h1>);
}

// dictionaries.js
const dictionaries = {
  en: () => import('./dictionaries/en.json').then((module) => module.default),
  es: () => import('./dictionaries/es.json').then((module) => module.default),
};

export const getTranslations = async (locale) => {
  try {
    return dictionaries[locale]() ?? dictionaries.en();
  } catch (error) {
    console.error(`بارگذاری ترجمه‌ها برای زبان ${locale} با شکست مواجه شد`, error);
    return dictionaries.en();
  }
};

در این تنظیمات، بخش مسیر داینامیک `[locale]` زبان‌های مختلف را مدیریت می‌کند (مثلاً `/en`، `/es`). ترجمه‌ها به صورت پویا بر اساس زبان بارگذاری می‌شوند.

۲. مسیریابی با دامنه

برای یک رویکرد پیشرفته‌تر، می‌توانید از دامنه‌ها یا زیردامنه‌های مختلف برای هر زبان استفاده کنید. این اغلب شامل پیکربندی اضافی با ارائه‌دهنده هاستینگ شما می‌شود.

۳. میان‌افزار برای تشخیص زبان

از میان‌افزار برای تشخیص خودکار زبان ترجیحی کاربر و هدایت او به مسیر مناسب استفاده کنید.

// middleware.js
import { NextResponse } from 'next/server';
import { match } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';

let locales = ['en', 'es', 'fr', 'fa']; // fa اضافه شد

function getLocale(request) {
  const negotiatorHeaders = {};
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
  let languages = new Negotiator({ headers: negotiatorHeaders }).languages();

  try {
      return match(languages, locales, 'fa'); // استفاده از "fa" به عنوان زبان پیش‌فرض
  } catch (error) {
      console.error("خطا در تطبیق زبان:", error);
      return 'fa'; // بازگشت به فارسی در صورت شکست تطبیق
  }
}

export function middleware(request) {
  const pathname = request.nextUrl.pathname;
  const pathnameIsMissingLocale = locales.every(
    (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  );

  if (pathnameIsMissingLocale) {
    const locale = getLocale(request);

    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`,
        request.url
      )
    );
  }
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

این میان‌افزار بررسی می‌کند که آیا مسیر درخواستی پیشوند زبان دارد یا خیر. اگر نداشته باشد، زبان ترجیحی کاربر را با استفاده از هدر `Accept-Language` تشخیص داده و او را به مسیر مناسب با پیشوند زبان هدایت می‌کند. کتابخانه‌هایی مانند `@formatjs/intl-localematcher` و `negotiator` برای مدیریت تطبیق زبان استفاده می‌شوند.

مسیریاب اپ Next.js و دسترسی‌پذیری جهانی

ایجاد برنامه‌های وب قابل دسترس در سطح جهانی نیازمند توجه دقیق به اصول دسترسی‌پذیری (a11y) است. مسیریاب اپ Next.js یک پایه محکم برای ساخت تجربیات قابل دسترس فراهم می‌کند، اما پیاده‌سازی بهترین شیوه‌ها برای اطمینان از اینکه برنامه شما برای همه، صرف نظر از توانایی‌هایشان، قابل استفاده است، ضروری است.

ملاحظات کلیدی دسترسی‌پذیری

  1. HTML معنایی: از عناصر HTML معنایی (مثلاً `<article>`, `<nav>`, `<aside>`, `<main>`) برای ساختاردهی محتوای خود استفاده کنید. این کار به فناوری‌های کمکی معنا می‌بخشد و به کاربران کمک می‌کند تا سایت شما را راحت‌تر ناوبری کنند.
  2. ویژگی‌های ARIA: از ویژگی‌های ARIA (Accessible Rich Internet Applications) برای افزایش دسترسی‌پذیری کامپوننت‌ها و ویجت‌های سفارشی استفاده کنید. ویژگی‌های ARIA اطلاعات بیشتری در مورد نقش، وضعیت و خواص عناصر به فناوری‌های کمکی ارائه می‌دهند.
  3. ناوبری با صفحه‌کلید: اطمینان حاصل کنید که تمام عناصر تعاملی از طریق صفحه‌کلید قابل دسترسی هستند. کاربران باید بتوانند با استفاده از کلید `Tab` در برنامه شما ناوبری کرده و با استفاده از کلید `Enter` یا `Space` با عناصر تعامل داشته باشند.
  4. کنتراست رنگ: از کنتراست رنگ کافی بین متن و پس‌زمینه برای اطمینان از خوانایی برای کاربران با اختلالات بینایی استفاده کنید. دستورالعمل‌های دسترسی‌پذیری محتوای وب (WCAG) نسبت کنتراست حداقل 4.5:1 برای متن عادی و 3:1 برای متن بزرگ را توصیه می‌کند.
  5. متن جایگزین تصویر (Alt Text): برای تمام تصاویر، متن جایگزین توصیفی ارائه دهید. متن جایگزین، یک جایگزین متنی برای تصاویر فراهم می‌کند که توسط صفحه‌خوان‌ها قابل خواندن است.
  6. برچسب‌های فرم: برچسب‌های فرم را با فیلدهای ورودی مربوطه با استفاده از عنصر `<label>` مرتبط کنید. این کار برای کاربران مشخص می‌کند که چه اطلاعاتی در هر فیلد انتظار می‌رود.
  7. تست با صفحه‌خوان: برنامه خود را با یک صفحه‌خوان آزمایش کنید تا اطمینان حاصل کنید که برای کاربران با اختلالات بینایی قابل دسترس است. صفحه‌خوان‌های محبوب شامل NVDA، JAWS و VoiceOver هستند.

پیاده‌سازی دسترسی‌پذیری در مسیریاب اپ Next.js

  1. استفاده از کامپوننت Link در Next.js: برای ناوبری از کامپوننت `<Link>` استفاده کنید. این کامپوننت ویژگی‌های دسترسی‌پذیری داخلی مانند پیش‌واکشی (prefetching) و مدیریت فوکوس را فراهم می‌کند.
  2. مدیریت فوکوس: هنگام ناوبری بین صفحات یا باز کردن مودال‌ها، اطمینان حاصل کنید که فوکوس به درستی مدیریت می‌شود. فوکوس باید روی منطقی‌ترین عنصر در صفحه یا مودال جدید تنظیم شود.
  3. کامپوننت‌های سفارشی قابل دسترس: هنگام ایجاد کامپوننت‌های سفارشی، با پیروی از اصول ذکر شده در بالا، اطمینان حاصل کنید که آنها قابل دسترس هستند. از HTML معنایی، ویژگی‌های ARIA و ناوبری با صفحه‌کلید برای قابل استفاده کردن کامپوننت‌های خود برای همه استفاده کنید.
  4. لینتینگ و تست: از ابزارهای لینتینگ مانند ESLint با پلاگین‌های دسترسی‌پذیری برای شناسایی مشکلات بالقوه دسترسی‌پذیری در کد خود استفاده کنید. همچنین، از ابزارهای تست خودکار برای آزمایش برنامه خود از نظر نقض دسترسی‌پذیری استفاده کنید.

نتیجه‌گیری

سیستم مسیریابی مبتنی بر فایل در مسیریاب اپ Next.js یک راه قدرتمند و بصری برای ساختاردهی و ناوبری برنامه‌های شما ارائه می‌دهد. با درک مفاهیم اصلی و بهترین شیوه‌های ذکر شده در این راهنما، می‌توانید برنامه‌های Next.js قوی، مقیاس‌پذیر و قابل نگهداری بسازید. با ویژگی‌های مختلف مسیریاب اپ آزمایش کنید و کشف کنید که چگونه می‌تواند گردش کار توسعه شما را ساده‌تر کرده و تجربه کاربری را بهبود بخشد.