إعداد i18n باستخدام next-intl في Next.js 16 مع proxy.ts
اضبط next-intl Next.js 16 باستخدام proxy.ts وasync params ومسارات locale ثابتة وبيانات App Router i18n الوصفية لصفحات SaaS خطوة بخطوة.

صفحة الهبوط الخاصة بتطبيق SaaS لديك جاهزة، لكن النسخة الألمانية تعرض 404، ومبدّل اللغة ينقل المستخدمين إلى URL خاطئ، وما زال دليل قديم يخبرك بإنشاء middleware.ts. الأجزاء المتحركة صغيرة، لكنها يجب أن تتوافق معًا: proxy.ts، وparams غير المتزامنة، وتوجيه اللغات، والتوليد الثابت، والبيانات الوصفية. يوضح هذا الدليل إعدادًا آمنًا للإنتاج باستخدام next-intl مع Next.js 16.
src/proxy.ts: يستخدم Next.js 16 ملف proxy.ts للتفاوض على اللغة وقت الطلب؛ وقد أُعيدت تسمية اصطلاح ملف middleware إلى Proxy وأصبح deprecated في Next.js 16.
params غير المتزامنة: في app/[locale]/layout.tsx، عرّف نوع params على أنه Promise<{locale: string}>، ثم انتظره، وتحقق من اللغة، ثم اعرض المزوّد.
- مسارات اللغات الثابتة: استخدم
generateStaticParams() لإرجاع اللغات المدعومة، واستدعِ setRequestLocale(locale) قبل استخدام واجهات next-intl في layout أو page ثابتة.
- تنقّل آمن لـ SEO: استخدم مساعدات التوجيه من next-intl حتى تبقى الروابط المحلية، وعمليات إعادة التوجيه، وأسماء المسارات، وURLs اللغات البديلة متسقة.
- حمولات العميل: في التطبيقات الأكبر، أبقِ معظم نصوص الصفحات داخل Server Components ومرّر فقط namespaces الرسائل التي تحتاجها Client Components.
ما هو next-intl Next.js 16؟
next-intl Next.js 16 هو نمط التكامل الحالي مع App Router لبناء تطبيقات Next.js متعددة اللغات باستخدام next-intl وsrc/proxy.ts.
الهدف ليس مجرد البحث عن الترجمات. يحتاج إعداد الإنتاج إلى URLs واعية باللغة، وتفاوض على الطلب، والتحقق من route params، والتصيير الثابت حيثما أمكن، وبيانات وصفية مترجمة، وروابط لغات يمكن لمحركات البحث فهمها.
أكبر مصدر للأخطاء هو نسخ شجرة ملفات من عصر Next.js 15 إلى تطبيق Next.js 16. المفاهيم مألوفة، لكن سطح التكامل تغيّر بما يكفي لأن تؤدي الاختلافات الصغيرة إلى أخطاء مثل Unable to find next-intl locale أو تجعل المسارات الثابتة تعود إلى التصيير الديناميكي.
لماذا يختلف إعداد App Router i18n هذا عن الأدلة القديمة
لا يمنحك App Router نظام ترجمة كاملًا بمفرده. يدعم Next.js التدويل من خلال بدائيات التوجيه، وdynamic route segments، وProxy، وstatic params. تضيف next-intl تحميل الرسائل، والتفاوض على اللغة، ومساعدات توجيه typed، وواجهات ترجمة server/client، وروابط بديلة موجهة لـ SEO.
بالنسبة إلى i18n في Next.js 16، فإن التغيير الأكثر وضوحًا هو اسم ملف اعتراض الطلبات. استخدم src/proxy.ts في أي إعداد جديد. قد ما زالت المقالات الأقدم تعرض middleware.ts، وهو اصطلاح الملف السابق وأصبح الآن deprecated في Next.js 16.
| الاهتمام | نمط Next.js 16 + next-intl | عدم التطابق الشائع في الأدلة القديمة |
|---|---|---|
| التفاوض على الطلب | src/proxy.ts مع createMiddleware(routing) |
middleware.ts في شجرة الملفات |
| مسارات اللغة | app/[locale]/... |
صفحات خارج مقطع اللغة |
| Route params | params: Promise<{locale: string}> |
تعريف نوع params بشكل متزامن |
| i18n الثابت | generateStaticParams() بالإضافة إلى setRequestLocale(locale) |
فقط generateStaticParams() |
| التنقّل | مساعدات createNavigation(routing) |
تركيب السلاسل يدويًا |
إذا كان لديك بالفعل إعداد Next.js 15، فقارنه بهذه المقالة بدلًا من تغيير أسماء الملفات عشوائيًا. ما زال دليل Aniq UI الأقدم حول إضافة next-intl إلى Next.js 15 مفيدًا للمفاهيم، لكن هذه المقالة تبدأ من Next.js 16 أولًا.
تثبيت next-intl وإضافة Next.js plugin
ابدأ بمشروع App Router حديث. يتطلب Next.js 16 إصدار Node >=20.9.0، وتستخدم تطبيقات React الحالية React 19. ثبّت next-intl كالمعتاد:
npm install next-intl
أضف next-intl plugin إلى next.config.ts:
import createNextIntlPlugin from 'next-intl/plugin';
import type {NextConfig} from 'next';
const nextConfig: NextConfig = {};
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);
تحافظ بنية ملفات إنتاج صغيرة على وضوح الإعداد:
src/
app/
[locale]/
layout.tsx
page.tsx
i18n/
request.ts
routing.ts
navigation.ts
proxy.ts
messages/
en.json
de.json
أنشئ ملفات الرسائل أولًا حتى يحتوي إعداد الطلب على شيء حقيقي لتحميله:
{
"HomePage": {
"title": "Ship your product in more languages"
},
"Metadata": {
"title": "Localized SaaS landing page",
"description": "A localized landing page built with Next.js and next-intl."
}
}
حافظ على قابلية توقع namespaces. نمط شائع هو namespace واحدة لكل route أو feature، بالإضافة إلى namespace باسم Metadata للعناوين والأوصاف.
إعداد التوجيه حسب اللغة باستخدام src/proxy.ts
يبدأ التوجيه القائم على اللغة بإعداد توجيه مشترك. ضع جميع اللغات المدعومة في ملف واحد وأعد استخدام ذلك الإعداد في كل مكان آخر.
// src/i18n/routing.ts
import {defineRouting} from 'next-intl/routing';
export const routing = defineRouting({
locales: ['en', 'de'],
defaultLocale: 'en'
});
بعد ذلك، أنشئ ملف Proxy. هنا يمكن لـ next-intl التفاوض على اللغة، وإعادة توجيه الطلبات أو إعادة كتابتها، وتوفير سلوك التوجيه بناءً على اللغة النشطة.
// src/proxy.ts
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};
يستثني matcher مسارات API، وداخليات Next.js، وداخليات Vercel، والطلبات الخاصة بالملفات التي تحتوي على امتدادات. هذا يمنع middleware اللغة من العمل على assets مثل الصور، والخطوط، وsource maps.
الآن أضف إعداد الطلب. هنا تُقرأ اللغة وتُحمّل الرسائل للطلب النشط.
// src/i18n/request.ts
import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';
export default getRequestConfig(async ({requestLocale}) => {
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default
};
});
هذا fallback مهم. عادةً يجب أن يوفر Proxy لغة صالحة، لكن يجب أن يتعامل إعداد الطلب بأمان مع قيمة غير صالحة أو مفقودة. توصي وثائق next-intl أيضًا باستبدال قيم [locale] غير الصالحة بلغة صالحة في إعداد الطلب واستدعاء notFound() في root layout عند اللزوم.
بناء layout الخاص بـ [locale] مع params غير المتزامنة
في App Router، يجب أن يغلّف مقطع المسار المترجم الصفحات التي تحتاج إلى ترجمات. بالنسبة إلى معظم مواقع تسويق SaaS، يعني ذلك نقل الصفحات العامة إلى داخل app/[locale].
تفصيل Next.js 16 المهم هو شكل params: الخاصية هي Promise، لذلك انتظر الخاصية قبل استخدام locale.
// src/app/[locale]/layout.tsx
import {NextIntlClientProvider, hasLocale} from 'next-intl';
import {setRequestLocale} from 'next-intl/server';
import {notFound} from 'next/navigation';
import {routing} from '../../i18n/routing';
type Props = {
children: React.ReactNode;
params: Promise<{locale: string}>;
};
export function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}
export default async function LocaleLayout({children, params}: Props) {
const {locale} = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
// Enables static rendering for next-intl APIs in this locale scope.
setRequestLocale(locale);
return (
<html lang={locale}>
<body>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</body>
</html>
);
}
هناك ثلاث مهام منفصلة في هذا layout:
generateStaticParams()يخبر Next.js أي route params خاصة باللغة يمكن توليدها بشكل ثابت.hasLocale()يتحقق من route param قبل تصيير الصفحة.setRequestLocale(locale)يجعل اللغة متاحة لواجهات next-intl أثناء التصيير الثابت.
لا تستبدل التحقق من اللغة بفحص نصي فضفاض. إعداد التوجيه هو مصدر الحقيقة، وhasLocale() يحافظ على توافق فحص وقت التشغيل مع ذلك الإعداد.
استخدام الترجمات في Server وClient Components
تدعم next-intl كلًا من Server Components وClient Components، لكن سطح API يعتمد على مكان تشغيل المكوّن.
بالنسبة إلى shared أو server components غير المتزامنة، يكون useTranslations صالحًا:
// src/app/[locale]/page.tsx
import {useTranslations} from 'next-intl';
export default function HomePage() {
const t = useTranslations('HomePage');
return (
<main className='mx-auto max-w-3xl px-6 py-24'>
<h1 className='text-4xl font-semibold tracking-tight'>
{t('title')}
</h1>
</main>
);
}
بالنسبة إلى async Server Components، استخدم getTranslations من next-intl/server:
import {getTranslations} from 'next-intl/server';
export default async function PricingPage() {
const t = await getTranslations('PricingPage');
return <h1>{t('title')}</h1>;
}
استخدم Client Components فقط عندما تحتاج إلى حالة على جانب العميل، أو browser APIs، أو interactive hooks. يجعل provider في layout الرسائل متاحة لـ Client Components الموجودة تحته، لكن هذا لا يعني أن كل مكوّن مترجم يجب أن يصبح Client Component. توثق next-intl واجهات getTranslations، وgetMessages، وgetLocale، والواجهات القابلة للانتظار ذات الصلة لـ async Server Components، بينما تعمل hooks مثل useTranslations في shared components العادية حسب مكان تنفيذها.
قاعدة عملية لصفحات الهبوط بسيطة: أبقِ الأقسام الثقيلة بالنصوص كـ Server Components، واعزل الأجزاء التفاعلية مثل القوائم، والتبويبات، ومبدلات اللغة.
تفعيل التصيير الثابت باستخدام generateStaticParams i18n
عادةً يكون التصيير الثابت مناسبًا لصفحات التسويق، والتوثيق، وصفحات التسعير، وصفحات SaaS العامة حيث يتغير المحتوى عبر عمليات النشر أو revalidation بدلًا من كل طلب.
بالنسبة إلى generateStaticParams i18n، فإن إرجاع اللغات ضروري لكنه غير كافٍ عند استخدام واجهات next-intl. استدعِ setRequestLocale(locale) في كل layout أو page يجب أن تُصيّر بشكل ثابت وتستخدم واجهات next-intl. يجب أن يحدث الاستدعاء قبل useTranslations، أو getMessages، أو واجهات next-intl الأخرى التي تعتمد على اللغة النشطة.
// src/app/[locale]/features/page.tsx
import {getTranslations, setRequestLocale} from 'next-intl/server';
import {routing} from '../../../i18n/routing';
type Props = {
params: Promise<{locale: string}>;
};
export function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}
export default async function FeaturesPage({params}: Props) {
const {locale} = await params;
setRequestLocale(locale);
const t = await getTranslations('FeaturesPage');
return <h1>{t('title')}</h1>;
}
للتصيير الثابت، استدعِ setRequestLocale(locale) في كل layout أو page ذات صلة قبل استخدام واجهات next-intl. هذا يتجنب الرجوع إلى التصيير الديناميكي عندما تحتاج next-intl إلى اللغة النشطة.
هنا أيضًا تظهر العديد من أخطاء الترقية. يضيف المطورون generateStaticParams() وما زالوا يرون سلوكًا ديناميكيًا لأن اللغة لم تُعيّن صراحةً لنطاق next-intl.
إضافة بيانات وصفية مترجمة، وتنقّل، وفحوصات hreflang
يجب أن تستخدم البيانات الوصفية المترجمة نفس locale param الخاصة بالصفحة. في async metadata functions، استخدم getTranslations ومرّر اللغة صراحةً.
// src/app/[locale]/page.tsx
import type {Metadata} from 'next';
import {getTranslations} from 'next-intl/server';
import {useTranslations} from 'next-intl';
type Props = {
params: Promise<{locale: string}>;
};
export async function generateMetadata({params}: Props): Promise<Metadata> {
const {locale} = await params;
const t = await getTranslations({locale, namespace: 'Metadata'});
return {
title: t('title'),
description: t('description'),
alternates: {
languages: {
en: '/en',
de: '/de'
}
}
};
}
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
بالنسبة إلى التنقّل، تجنب بناء سلاسل يدويًا مثل /${locale}/pricing. صدّر مساعدات التنقّل من next-intl مرة واحدة:
// src/i18n/navigation.ts
import {createNavigation} from 'next-intl/navigation';
import {routing} from './routing';
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
ثم استخدم مكوّن Link المحلي:
import {Link} from '../../i18n/navigation';
export function Header() {
return (
<nav className='flex gap-4'>
<Link href='/'>Home</Link>
<Link href='/pricing'>Pricing</Link>
<Link href='/pricing' locale='de'>Deutsch</Link>
</nav>
);
}
قبل الإطلاق، تحقق من ثلاثة أساسيات لـ SEO:
- يجب أن يكون لكل صفحة مترجمة URL واضح واحد، مثل
/en/pricingأو/de/pricing. - يجب أن تطابق قيمة
<html lang>اللغة النشطة. - يجب أن تشير روابط اللغات البديلة إلى محتوى مكافئ، وليس فقط إلى الصفحة الرئيسية المترجمة.
إذا كنت تقرن هذا بصفحة هبوط عالية التحويل، فتنطبق المبادئ نفسها على البنية والأداء. منشور Aniq UI حول تحسين أداء Next.js مفيد كمرجع مكمّل عندما تبدأ الصفحات المترجمة في النمو.
توسيع next-intl للتطبيقات الأكبر
الإعداد أعلاه مناسب للمدونات وصفحات الهبوط. لكن مع نمو تطبيقك، يمكن أن يصبح ملف واحد مثل messages/en.json عائقًا لتجربة المطور، وإذا مررت كائن الرسائل الكامل إلى Client Components، فإنه يزيد أيضًا من حمولة العميل serialized المرسلة إلى المتصفح. يجب ألا تقوم الصفحة الرئيسية بعمل serialize لرسائل AboutPage أو CheckoutPage إذا كان ذلك route لا يستخدمها مطلقًا.
يساعد تقسيم الرسائل إلى ملفات لكل namespace في التنظيم، لكنه لا يصلح التسريب وحده: إذا دمجت كل ملف مرة أخرى في request.ts، فسيظل الكتالوج الكامل يُحمّل على جانب الخادم. الحل الفعلي هو رسائل provider انتقائية مع إبقاء نصوص الصفحات في Server Components.
في next-intl v4، يرث NextIntlClientProvider الإعداد من request.ts افتراضيًا، لذلك إذا كان إعداد الطلب يحمّل الكتالوج الكامل، يمكن أن يعرّض provider كائن الرسائل بالكامل لـ Client Components ما لم تقم بالاستبعاد. عيّن messages={null} في الجذر. ستستمر Server Components في العمل، ولن تُمرّر أي رسائل إلى العميل تلقائيًا:
// app/[locale]/layout.tsx
// Keep hasLocale() validation and setRequestLocale() as shown earlier
<NextIntlClientProvider messages={null}>
{children}
</NextIntlClientProvider>
ثم غلّف فقط client islands التي تحتاج إلى الترجمات، ومرّر namespaces الخاصة بها فقط:
import pick from 'lodash/pick';
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
export default async function HomePage() {
const messages = await getMessages();
return (
<>
<Hero /> {/* Server Component, no client message payload */}
<NextIntlClientProvider
messages={pick(messages, ['Common', 'PricingCalculator'])}
>
<PricingCalculator />
</NextIntlClientProvider>
</>
);
}
يجب أن يتضمن provider كل namespace يستخدمها Client Component. لا تقوم nested providers بعمل deep-merge للرسائل، لذلك إذا كان PricingCalculator يستدعي أيضًا useTranslations('Common')، فاختر كلًا من Common وPricingCalculator. تصف وثائق next-intl خصائص provider بأنها atomic وتشير إلى أن messages تحتاج إلى الدمج يدويًا عند الضرورة.
تنبيه واحد: عندما تختار subset، قد يضع TypeScript علامة على prop الخاصة بـ messages لأن نوع AppConfig['Messages'] المعزز يتوقع الشكل الكامل. هذه حافة خشنة معروفة في v4، وليست خطأ في إعدادك.
أبقِ واجهة المستخدم المشتركة في namespaces صغيرة مثل Common، وNavigation، وFooter، وأبقِ المحتوى الخاص بالمسار قريبًا من مساره. لا تحتاج إلى هذا في موقع صغير، لكنه في لوحات SaaS والتطبيقات الكبيرة متعددة اللغات يحافظ على أن تكون حمولات العميل مقصودة وملفات الترجمة قابلة للصيانة.
أخطاء شائعة
- استخدام
middleware.tsفي تطبيق Next.js 16 جديد → قد لا تطابق طبقة الطلب الوثائق الحالية → أنشئsrc/proxy.tsوصدّرcreateMiddleware(routing). - تعريف نوع
paramsعلى أنه{locale: string}→ لم يعد layout يطابق شكل prop الحالي في App Router → عرّفه على أنهPromise<{locale: string}>وانتظره. - تخطي
hasLocale()→ يمكن لمقاطع اللغة غير الصالحة أن تعرض محتوى غير متوقع → تحقق مقابلrouting.localesواستدعِnotFound(). - إضافة
generateStaticParams()فقط → قد تظل next-intl بحاجة إلى تعيين اللغة في نطاق التصيير → استدعِsetRequestLocale(locale)قبل واجهات الترجمة. - بناء URLs اللغات يدويًا → يمكن أن تتعطل الروابط عندما تتغير قواعد التوجيه → استخدم مساعدات
createNavigation(routing). - تمرير كل الرسائل إلى root client provider في تطبيق كبير → يمكن أن تتلقى Client Components namespaces لا تستخدمها أبدًا → عيّن
messages={null}في الجذر ومرّر namespaces مختارة إلى client islands.
FAQ
هل يحتوي Next.js App Router على دعم i18n مدمج؟
يوفر Next.js App Router لبنات بناء i18n، وليس إطار ترجمة كاملًا. يدعم أنماطًا مثل التفاوض القائم على Proxy، ومقاطع [locale] الديناميكية، وgenerateStaticParams() لمسارات اللغة. تضيف مكتبات مثل next-intl تحميل الرسائل، وواجهات الترجمة، والتنقّل المترجم، والتعامل مع اللغات البديلة.
كيف أفعّل التصيير الثابت مع i18n في App Router؟
فعّل مسارات i18n الثابتة بإرجاع كل لغة من generateStaticParams() وتعيين اللغة النشطة قبل تشغيل واجهات الترجمة. مع next-intl، يعني ذلك عادةً استدعاء setRequestLocale(locale) في layout أو page ذات الصلة داخل [locale] قبل useTranslations، أو getMessages، أو واجهات مشابهة.
كيف تعمل وسوم hreflang مع next-intl؟
يمكن أن تساعد next-intl في إنتاج روابط اللغات البديلة من خلال routing middleware وإعداد التنقّل المترجم. بالنسبة إلى SEO، يجب أن يمثل كل URL بديل المحتوى نفسه بلغة أخرى، مثل /en/pricing و/de/pricing. يجب أن تضبط الصفحة أيضًا قيمة <html lang> الصحيحة.
كيف أصلح “Unable to find next-intl locale because the middleware didn’t run on this request”؟
يعني هذا الخطأ عادةً أن تفاوض طلب next-intl لم يعمل للمسار الذي يتم تصييره. في Next.js 16، تأكد من وجود src/proxy.ts، وأنه يصدّر createMiddleware(routing)، وأن لديه matcher يشمل الصفحة المترجمة. تأكد أيضًا من أن الصفحة موجودة تحت app/[locale] عند استخدام التوجيه القائم على اللغة.
لماذا غيّر Next.js 16 اسم middleware.ts إلى proxy.ts؟
يستخدم Next.js 16 اصطلاح ملف proxy.ts لطبقة اعتراض الطلبات التي كانت كثير من الأدلة القديمة تسميها middleware.ts. تنص الوثائق على أن Middleware أُعيدت تسميته إلى Proxy ليعكس غرضه بشكل أفضل، وأن الوظيفة بقيت كما هي. في التطبيقات الجديدة، ضع تصدير next-intl middleware في src/proxy.ts وتجنب خلط الاسمين في دليل واحد أو شجرة ملفات واحدة.
كيف أعد next-intl Next.js 16 لصفحة هبوط SaaS؟
أعد توجيه اللغة باستخدام defineRouting، وأنشئ src/proxy.ts مع createMiddleware(routing)، وغلّف الصفحات في app/[locale]/layout.tsx، وحمّل الرسائل من خلال i18n/request.ts. بالنسبة إلى صفحة هبوط SaaS، أضف أيضًا بيانات وصفية مترجمة، وتحقق من اللغات، واستخدم مساعدات التنقّل من next-intl لروابط اللغات.
هل يجب أن أقسم رسائل next-intl حسب route؟
بالنسبة إلى المواقع الصغيرة، يكفي ملف رسالة واحد لكل لغة. بالنسبة إلى التطبيقات الأكبر، قسّم الرسائل حسب route أو feature لتحسين تجربة المطور، لكن لا تدمج الكتالوج الكامل وتمرره إلى كل Client Component. أبقِ معظم المحتوى المترجم في Server Components، وعيّن messages={null} في root provider، ومرّر namespaces مختارة فقط إلى client islands التي تحتاجها.
الخلاصة
يعتمد إعداد App Router متعدد اللغات موثوق على بضعة اختيارات دقيقة: استخدم src/proxy.ts، وانتظر وتحقق من params الخاصة باللغة، وأبقِ إعداد التوجيه مركزيًا، واقرن generateStaticParams() مع setRequestLocale() عندما يكون التصيير الثابت مهمًا. بعد أن تصبح هذه الأجزاء في مكانها، تصبح البيانات الوصفية وروابط اللغات أسهل بكثير في الفهم.
بالنسبة إلى تطبيقات SaaS الأكبر، فإن الخطوة التالية هي الانضباط في الحمولة: أبقِ نصوص الصفحات على الخادم، واستبعد الرسائل التلقائية للعميل في الجذر، ومرّر فقط namespaces التي تحتاجها الجزر التفاعلية. إذا كنت تعمل على ترجمة صفحة هبوط SaaS أو dashboard UI، فإن البدء من قالب Aniq UI يمكن أن يوفر الوقت في التخطيط المحيط بينما تركز على طبقة i18n.
هل وجدت هذا المقال مفيدًا؟
قد يعجبك أيضًا

next-intl nextjs 16: تخزين المكونات مؤقتًا مع root-params
أتقن دمج next-intl مع nextjs 16 عبر الاستفادة من next/root-params لتمكين التخزين المؤقت للمكونات بأمان دون الحاجة لعملية prop-drilling للـ locale.

نافذة Modal بـ Intercepting Routes في Next.js: نماذج لوحة تحكم SaaS
تعلم بناء نوافذ Next.js intercepting routes منبثقة تدعم الروابط العميقة وتحفظ الحالة باستخدام parallel slots وServer Actions وshadcn/ui للوحات تحكم SaaS.

Next.js View Transitions: رسوم متحركة أصلية بدون JS
استبدل مكتبات الرسوم المتحركة الثقيلة بـ Next.js view transitions لبناء انتقالات سلسة تماما وبدون JavaScript مباشرة داخل App Router الخاص بك.