الفرق بين المراجعتين لصفحة: «Next.js/i18n routing»

من موسوعة حسوب
أنشأ الصفحة ب'= الوجهات العالمية في Next.js = تقدم Next.js دعمًا مدمجًا للوجهات العالمية internationalized (wikipedia:Internationaliza...'
 
طلا ملخص تعديل
 
(3 مراجعات متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة)
سطر 1: سطر 1:
= الوجهات العالمية في Next.js =
<noinclude>{{DISPLAYTITLE:الوجهات العالمية i18n routing في Next.js}}</noinclude>
تقدم Next.js دعمًا مدمجًا للوجهات العالمية internationalized ([[wikipedia:Internationalization_and_localization#Naming|i18n]]) routing بدءًا من النسخة <code>v10.0.0</code>. وبإمكانك وضع قائمة بالإعدادات المحلية أو إعداد محلي افتراضي أو مورد مخصص لنطاق معين وستتعامل Next.js تلقائيًا مع مسار التوجه إلى هذا المورد.
تقدم Next.js دعمًا مدمجًا للوجهات العالمية internationalized ([[wikipedia:Internationalization_and_localization#Naming|i18n]]) routing بدءًا من النسخة <code>v10.0.0</code>. وبإمكانك وضع قائمة بالإعدادات المحلية أو إعداد محلي افتراضي أو مورد مخصص لنطاق معين وستتعامل Next.js تلقائيًا مع مسار التوجيه إلى هذا المورد.


إن القصد من دعم التوجه i18n حاليًا هو إكمال حلول المكتبة i18n مثل <code>react-intl</code> و <code>react-i18next</code> و <code>lingui</code> و <code>rosetta</code> و <code>next-intl</code> وغيرها من خلال تبسيط الوجهات و تفسيرها وفق النطاق المحلي.
إن القصد من دعم التوجيه i18n حاليًا هو إضافة حلول المكتبة i18n مثل <code>react-intl</code> و <code>react-i18next</code> و <code>lingui</code> و <code>rosetta</code> و <code>next-intl</code> وغيرها من خلال تبسيط الوجهات وتفسيرها وفق النطاق المحلي.


== البدء مع وجهات i18n ==
== البدء مع وجهات i18n ==
أضف حقل التهيئة <code>i18n</code> إلى الملف <code>next.config.js</code>. وتُعدُّ الموراد المحلية locales عبارة عن [https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers معرّفات UTS] وفق تنسيق معياري تُستخدم لتعريف إعداد محلي. يتألف المعرّف المحلي عادة من لغة ومنطقة وسكربت تفصل بينها شرطة (-) كالتالي <code>language-region-script</code> وتحديد المنطقة region والسكربت script أمر اختياري. إليك بعض الأمثلة:
أضف حقل التهيئة <code>i18n</code> إلى الملف <code>next.config.js</code>. وتُعدُّ الموراد المحلية locales عبارة عن [https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers معرّفات UTS] وفق تنسيق معياري تُستخدم لتعريف إعداد محلي. يتألف المعرّف المحلي عادة من لغة ومنطقة ولهجة تفصل بينها شرطة (-) كالتالي <code>language-region-script</code> وتحديد المنطقة region واللهجة script أمر اختياري. إليك بعض الأمثلة:


* <code>en-US</code>: الإنكليزية كمت تنطق في الولايات المتحدة.
* <code>en-US</code>: الإنكليزية كما تنطق في الولايات المتحدة.
* <code>nl-NL</code>: الهولندية كما تُنطق في هولندا.
* <code>nl-NL</code>: الهولندية كما تُنطق في هولندا.
* <code>nl</code>: الهولندية دون تحديد منطقة.
* <code>nl</code>: الهولندية دون تحديد منطقة.
<syntaxhighlight lang="javascript">
إن كانت محلية المستخدم nl-BE ولكن لم تكن مدرجة في ضبط التطبيق، فستُحول إلى nl إن كانت موجودة أو إلى المحلية الافتراضية، وإن لم تكن تنوي دعم جميع مناطق بلد معين، فيفضل إضافة محليات البلد التي سيُرجع إليها fallback.<syntaxhighlight lang="javascript">
// next.config.js
// next.config.js
module.exports = {
module.exports = {
سطر 58: سطر 58:
</syntaxhighlight>تتيح الإعدادات السابقة وجهات إلى الإعدادات المحلية <code>en-US</code> و <code>fr</code> و <code>nl-NL</code> وتكون الوجهة الافتراضية إلى الإعداد المحلي <code>en-US</code>. فإن كانت لديك الصفحة <code>pages/blog.js</code>، ستتمكن من الوصول إلى عناوين url التالية:
</syntaxhighlight>تتيح الإعدادات السابقة وجهات إلى الإعدادات المحلية <code>en-US</code> و <code>fr</code> و <code>nl-NL</code> وتكون الوجهة الافتراضية إلى الإعداد المحلي <code>en-US</code>. فإن كانت لديك الصفحة <code>pages/blog.js</code>، ستتمكن من الوصول إلى عناوين url التالية:


* <code>/blog</code>
* <code>/blog</code>
* <code>/fr/blog</code>
* <code>/fr/blog</code>
* <code>/nl-nl/blog</code>
* <code>/nl-nl/blog</code>


لا يملك الإعداد المحلي الافتراضي بادئة.
لا يملك الإعداد المحلي الافتراضي بادئة.
سطر 102: سطر 102:


== الكشف التلقائي عن دعم إعدادات محلية ==
== الكشف التلقائي عن دعم إعدادات محلية ==
تحاول Next.js تلقائيًا التقاط الإعدادات المحلية بناء على قيمة الترويسة <code>Accept-Language</code> واسم النطاق الحالي بمجرّد وصول المستخدم إلى جذر التطبيق (عادة <code>/</code>). فإن اكتشفت إعدادات محلية مختلفة عن المورد الافتراضي، سيوجّه الزائر إلى إحدى الوجهتين التاليتين:
تحاول Next.js تلقائيًا التقاط الإعدادات المحلية بناء على قيمة الترويسة <code>[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language Accept-Language]</code> واسم النطاق الحالي بمجرّد وصول المستخدم إلى جذر التطبيق (عادة <code>/</code>). فإن اكتشفت إعدادات محلية مختلفة عن المورد الافتراضي، سيوجّه الزائر إلى إحدى الوجهتين التاليتين:


* عند استخدام التوجيه باستخدام المسارات الفرعية: إلى الإعداد المحلي الذي يبدأ عنوان المسار.
* عند استخدام التوجيه باستخدام المسارات الفرعية: إلى الإعداد المحلي الذي يبدأ عنوان المسار.
* عند استخدام التوجيه من خلال النطاقات: إلى النطاق ذو الإعداد المحلي المُحدد كإعداد افتراضي.
* عند استخدام التوجيه من خلال النطاقات: إلى النطاق ذو الإعداد المحلي المُحدد كإعداد افتراضي.


إذا استخدم التوجه من خلال النطاقات، وكانت قيمة الترويسة <code>Accept-Language</code> هي <code>fr;q=0.9</code> عند زيارة المستخدم للنطاق <code>example.com</code>، فسيحوَّل الزائر إلى النطاق <code>example.fr</code> الذي يتعامل مع الإعداد المحلي <code>fr</code> افتراضيًا. وعند استخدام التوجيه من خلال المسارات الفرعية، سيوجَّه المستخدم إلى العنوان <code>/fr</code>.
إذا استخدم التوجه من خلال النطاقات، وكانت قيمة الترويسة <code>Accept-Language</code> هي <code>fr;q=0.9</code> عند زيارة المستخدم للنطاق <code>example.com</code>، فسيحوَّل الزائر إلى النطاق <code>example.fr</code> الذي يتعامل مع الإعداد المحلي <code>fr</code> افتراضيًا. وعند استخدام التوجيه من خلال المسارات الفرعية، سيوجَّه المستخدم إلى العنوان <code>/fr</code>.


=== إضافة بادئة إلى الإعداد المحلي الافتراضي ===
=== إضافة بادئة إلى الإعداد المحلي الافتراضي ===
يمكن إضافة بادئة إلى الإعداد المحلي الافتراضي [https://github.com/vercel/next.js/discussions/18419 بالالتفاف حول الموضوع] باستخدام Next.js (النسخة 12) و<nowiki/>[[Next.js/middleware|الأداة الوسطية]]. لنتأمل على سبيل المثال الملف <code>next.config.js</code> الذي يدعم عدة لغات وقد أُضيف الإعداد المحلي <code>default</code> عمدًا:<syntaxhighlight lang="javascript">
يمكن إضافة بادئة إلى الإعداد المحلي الافتراضي [https://github.com/vercel/next.js/discussions/18419 بالالتفاف حول الموضوع] باستخدام Next.js (النسخة 12) و<nowiki/>[[Next.js/middleware|البرمجيات الوسطية]]. لنتأمل على سبيل المثال الملف <code>next.config.js</code> الذي يدعم عدة لغات وقد أُضيف الإعداد المحلي <code>"default"</code> عمدًا:<syntaxhighlight lang="javascript">
// next.config.js
// next.config.js


سطر 122: سطر 122:
}
}
</syntaxhighlight>يمكن أن نستخدم بعد ذلك أداة وسطية لإضافة قواعد توجه مخصصة:<syntaxhighlight lang="javascript">
</syntaxhighlight>يمكن أن نستخدم بعد ذلك أداة وسطية لإضافة قواعد توجه مخصصة:<syntaxhighlight lang="javascript">
// pages/_middleware.ts
// middleware.ts


import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from 'next/server'
سطر 128: سطر 128:
const PUBLIC_FILE = /\.(.*)$/
const PUBLIC_FILE = /\.(.*)$/


export function middleware(request: NextRequest) {
export async function middleware(req: NextRequest) {
   const shouldHandleLocale =
   if (
     !PUBLIC_FILE.test(request.nextUrl.pathname) &&
     req.nextUrl.pathname.startsWith('/_next') ||
     !request.nextUrl.pathname.includes('/api/') &&
     req.nextUrl.pathname.includes('/api/') ||
     request.nextUrl.locale === 'default'
     PUBLIC_FILE.test(req.nextUrl.pathname)
  ) {
    return
  }


   if (shouldHandleLocale) {
   if (req.nextUrl.locale === 'default') {
     const url = request.nextUrl.clone()
     const locale = req.cookies.get('NEXT_LOCALE') || 'en'
     url.pathname = `/en${request.nextUrl.pathname}`
 
     return NextResponse.redirect(url)
     return NextResponse.redirect(
      new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
     )
   }
   }
}


  return undefined
</syntaxhighlight>تتجاوز البرمجية الوسطية إضافة البادئة الافتراضية إلى مسار API وإلى [[Next.js/static file serving|الملفات العامة]] مثل الخطوط والصور. وإن وصل طلب إلى الإعداد المحلي الافتراضي سيُعاد توجيهه إلى البادئة <code>en/</code>.
}
</syntaxhighlight>تتجاوز الأداة الوسطية إضافة البادئة الافتراضية إلى مسار API وإلى [[Next.js/static file serving|الملفات العامة]] مثل الخطوط والصور. وإن وصل طلب إلى الإعداد المحلي الافتراضي سيُعاد توجيهه إلى البادئة <code>en/</code>.


=== تعطيل ميزة الكشف التلقائي عن الإعداد المحلي ===
=== تعطيل ميزة الكشف التلقائي عن الإعداد المحلي ===
سطر 154: سطر 158:
</syntaxhighlight>عندما تُضبط قيمة الخيار <code>localeDetection</code> على <code>false</code>، لن توجّه Next.js المستخدم وفقًا للإعداد المحلي المفضّل تلقائيًا، بل تزوّده فقط بمعلومات محلية تلتقطها من الإعداد المحلي للنطاق أو المسار الفرعي المحلي كما أشرنا سابقًا.
</syntaxhighlight>عندما تُضبط قيمة الخيار <code>localeDetection</code> على <code>false</code>، لن توجّه Next.js المستخدم وفقًا للإعداد المحلي المفضّل تلقائيًا، بل تزوّده فقط بمعلومات محلية تلتقطها من الإعداد المحلي للنطاق أو المسار الفرعي المحلي كما أشرنا سابقًا.


== الولوج إلى المعلومات المحلية ==
== الوصول إلى المعلومات المحلية ==
يمكن الوصول إلى الإعدادت المحلية من خلال موجّه Next.js. استخدم مثلًا الخطاف [[Next.js/router|<code>()useRouter</code>]] الذي يتيح لك الخاصيات التالية:
يمكن الوصول إلى الإعدادت المحلية من خلال موجّه Next.js. استخدم مثلًا الخطاف <code>[[Next.js/next router|()useRouter]]</code> الذي يتيح لك الخاصيات التالية:


* <code>locale</code>: يعيد الإعداد المحلي المُفعَّل.
* <code>locale</code>: يعيد الإعداد المحلي المُفعَّل.
سطر 192: سطر 196:
   )
   )
}
}
</syntaxhighlight>لاحظ أن الخاصية <code>locale</code> فقط هي من تحتفظ بمعلومات التوجه مثل قيم استعلام [[Next.js/dynamic routes|الوجهة الديناميكية]] أو قيمة استعلام السمة <code>href</code> المخفيةعند التعامل مع تبديل الإعدادات، كما يمكن أن تستخدم المعامل <code>href</code> على شكل كائن:<syntaxhighlight lang="javascript">
</syntaxhighlight>لاحظ أن الخاصية <code>locale</code> فقط هي من تحتفظ بمعلومات التوجه مثل قيم استعلام [[Next.js/Routing#.D8.A7.D9.84.D9.88.D8.AC.D9.87.D8.A7.D8.AA .D8.A7.D9.84.D8.AF.D9.8A.D9.86.D8.A7.D9.85.D9.8A.D9.83.D9.8A.D8.A9 Dynamic Routes|الوجهة الديناميكية]] أو قيمة استعلام السمة <code>href</code> المخفية عند التعامل مع تبديل الإعدادات، كما يمكن أن تستخدم المعامل <code>href</code> على شكل كائن:<syntaxhighlight lang="javascript">
import { useRouter } from 'next/router'
import { useRouter } from 'next/router'
const router = useRouter()
const router = useRouter()
سطر 199: سطر 203:
// href بما في ذلك استعلام  
// href بما في ذلك استعلام  
router.push({ pathname, query }, asPath, { locale: nextLocale })
router.push({ pathname, query }, asPath, { locale: nextLocale })
</syntaxhighlight>لمزيد من المعلومات عن بنية الكائن <code>router.push</code> راجع توثيق [[Next.js/next router|الواجهة البرمجية للوجهات]].
</syntaxhighlight>لمزيد من المعلومات عن بنية الكائن <code>router.push</code> راجع توثيق [[Next.js/next router|الواجهة البرمجية للوجهات]].  


إن كان لديك سمة <code>href</code> تتضمن الإعدادات المحلية، يمكنك التخلي عن التعامل التقائي مع بادئة الإعداد المحلي:<syntaxhighlight lang="javascript">
إن كان لديك سمة <code>href</code> تتضمن الإعدادات المحلية، يمكنك التخلي عن التعامل التلقائي مع بادئة الإعداد المحلي:<syntaxhighlight lang="javascript">
import Link from 'next/link'
import Link from 'next/link'


سطر 213: سطر 217:
</syntaxhighlight>
</syntaxhighlight>


== استخدام ملف تعريف الارتباط NEXT_LOCALE ==
== استخدام NEXT_LOCALE من ملف تعريف الارتباط ==
تسمح Next.js باستبدال الترويسة <code>accept-language</code> بملف تعريف الارتباط <code>NEXT_LOCALE=the-locale</code>. يمكن ضبط هذا الملف باستخدام مبدِّل لغة، وعندما يعود المستخدم مجددًا إلى الموقع سيبدّل الإعدادات بتلك الموجودة في ملف الارتباط عند زيارة الصفحة الرئيسية <code>/</code>. فلو افترضنا مثلًا أن اللغة المفضلة في الترويسة هي <code>fr</code> لكن ملف تعريف الارتباط قد ضُبط كالتالي <code>NEXT_LOCALE=en</code>، عندها سيُوجَّه المستخدم إلى الإعداد المحلي <code>en</code> عند زيارة الصفحة الرئيسية <code>/</code> حتى يُزال هذا الملف أو تنتهي صلاحيته.
تسمح Next.js باستبدال الترويسة <code>accept-language</code> بقيمة <code>NEXT_LOCALE=the-locale</code> من ملف تعريف الارتباط، ويمكن ضبط هذا الملف باستخدام مبدِّل لغة، وعندما يعود المستخدم مجددًا إلى الموقع سيبدّل الإعدادات بتلك الموجودة في ملف الارتباط عند زيارة الصفحة الرئيسية <code>/</code>. فلو افترضنا مثلًا أن اللغة المفضلة في الترويسة هي <code>fr</code> لكن ملف تعريف الارتباط قد ضُبط كالتالي <code>NEXT_LOCALE=en</code>، عندها سيُوجَّه المستخدم إلى الإعداد المحلي <code>en</code> عند زيارة الصفحة الرئيسية <code>/</code> حتى يُزال هذا الملف أو تنتهي صلاحيته.


== تحسين محركات البحث SEO ==
== تحسين محركات البحث SEO ==
سطر 220: سطر 224:


== الإعدادات المحلية والتوليد الساكن في Next.js ==
== الإعدادات المحلية والتوليد الساكن في Next.js ==
لا يمكن دمج الوجهات العالمية مع المكوّن [[Next.js/static html export|<code>next export</code>]] لأنه لا يعدّل في طبقة التوجّه الخاصة باللغة . لهذا تُدعم تطبيقات Next.js الهجينة التي لا تستخدم <code>next export</code> بشكل كامل.
لا يمكن دمج الوجهات العالمية مع المكوّن [[Next.js/static html export|<code>next export</code>]] لأنه لا يعدّل في طبقة التوجّه الخاصة باللغة. لهذا تُدعم تطبيقات Next.js الهجينة التي لا تستخدم <code>next export</code> بشكل كامل.


=== الوجهات الديناميكية والصفحات التي تستخدم <code>getStaticProps</code> ===
=== الوجهات الديناميكية والصفحات التي تستخدم <code>getStaticProps</code> ===
لا بد من إعادة جميع نسخ الصفحات ذات الإعدادات المحلية المختلفة من الدالة <code>getStaticPaths</code> وذلك في حال استخدمت هذه الصفحات الدالة <code>getStaticProps</code> مع الوجهات الديناميكية. بإمكانك إعادة الحقل <code>locale</code> مع الكائن <code>params</code> الخاص بالمسارات <code>paths</code> لكي تحدد الإعداد المحلي الذي ترغب بتصييره.<syntaxhighlight lang="javascript">
لا بد من إعادة جميع نسخ الصفحات ذات الإعدادات المحلية المختلفة من الدالة <code>getStaticPaths</code> وذلك في حال استخدمت هذه الصفحات الدالة <code>getStaticProps</code> مع الوجهات الديناميكية. بإمكانك إعادة الحقل <code>locale</code> مع الكائن <code>params</code> الخاص بالمسارات <code>paths</code> لكي تحدد الإعداد المحلي الذي ترغب بتصييره.<syntaxhighlight lang="javascript">
// pages/blog/[slug].js
// pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
export const getStaticPaths = ({ locales }) => {
سطر 235: سطر 239:
   }
   }
}
}
</syntaxhighlight>أما بالنسبة للصفحات غير الديناميكية التي تستخدم <code>getStaticProps</code>، و<nowiki/>[[Next.js/automatic static optimization|لأغراض التحسين التلقائي الساكن]]، تولَّد نسخة من الصفحة لكل إعداد محلي. ومن المهم معرفة ذلك، لأنه سيزيد من وقت البناء وفقًا لعدد الإعدادات المحلية التي هُيِّئت ضمن الدالة <code>getStaticProps</code>. فإن هيّأت مثلًا 50 إعداد محلي لعشر صفحات ليست ديناميكية باستخدام <code>getStaticProps</code> فهذا يعني استدعاء الدالة <code>getStaticProps</code> 500 مرة، أي 50 نسخة لكل صفحة من الصفحات العشر خلال زمن البناء.
</syntaxhighlight>أما بالنسبة للصفحات غير الديناميكية التي تستخدم <code>getStaticProps</code>، و<nowiki/>[[Next.js/automatic static optimization|لأغراض التحسين التلقائي الساكن]]، تولَّد نسخة من الصفحة لكل إعداد محلي. ومن المهم معرفة ذلك، لأنه سيزيد من وقت البناء وفقًا لعدد الإعدادات المحلية التي هُيِّئت ضمن الدالة <code>getStaticProps</code>. فإن هيّأت مثلًا 50 إعداد محلي لعشر صفحات ليست ديناميكية باستخدام <code>getStaticProps</code> فهذا يعني استدعاء الدالة <code>getStaticProps</code> قرابة 500 مرة، أي 50 نسخة لكل صفحة من الصفحات العشر خلال زمن البناء.


يُستخدم [[Next.js/api data fetching|نمط التراجع]] لتقليل زمن بناء الصفحات الديناميكية باستخدام <code>getStaticProps</code>، إذ يسمح لك ذلك بالعودة إل أكثر المسارات والإعدادات المحلية استخدامًا من قبل الدالة <code>getStaticPaths</code> لإعادة تصييرها زمن البناء. ومن ثم ستبني Next.js الصفحات الباقية خلال زمن التنفيذ عند الطلب.
يُستخدم [[Next.js/api data fetching#.D8.A7.D9.84.D8.AE.D8.A7.D8.B5.D9.8A.D8.A9 fallback: true|نمط التراجع]] <code>fallback</code> لتقليل زمن بناء الصفحات الديناميكية باستخدام <code>getStaticProps</code>، إذ يسمح لك ذلك بالعودة إل أكثر المسارات والإعدادات المحلية استخدامًا من قبل الدالة <code>getStaticPaths</code> لإعادة تصييرها عند البناء. ومن ثم ستبني Next.js الصفحات الباقية خلال زمن التنفيذ عند الطلب.


=== الصفحات المحسنة تلقائيًا بشكل ساكن ===
=== الصفحات المحسنة تلقائيًا بشكل ساكن ===
سطر 243: سطر 247:


=== الصفحات غير الديناميكية التي تستخدم <code>getStaticProps</code> ===
=== الصفحات غير الديناميكية التي تستخدم <code>getStaticProps</code> ===
تولَّد كذلك نسخة عن الصفحات غير الديناميكية التي تستخدم الدالة <code>getStaticProps</code>. إذ تُستدعى هذه الدالة عند تصيير كل إعداد محلي <code>locale</code>. وإن أردت منع تصيير أي من تلك الإعدادات مسبقًا، بإمكانك أن تعيد القيمة <code>notFound: true</code> من <code>getStaticProps</code>، وهكذا لن تولَّد نسخة خاصة بهذا الإعداد المحلي من الصفحة.<syntaxhighlight lang="javascript">
تولَّد كذلك نسخة عن الصفحات غير الديناميكية التي تستخدم الدالة <code>getStaticProps</code>. إذ تُستدعى هذه الدالة عند تصيير كل إعداد محلي <code>locale</code>. وإن أردت منع تصيير أي من تلك الإعدادات مسبقًا، بإمكانك أن تعيد القيمة <code>notFound: true</code> من <code>getStaticProps</code>، وهكذا لن تولَّد نسخة خاصة بهذا الإعداد المحلي من الصفحة.<syntaxhighlight lang="javascript">
export async function getStaticProps({ locale }) {
export async function getStaticProps({ locale }) {
   //ِ خارجية للحصول على المنشورات API استدع وصلة  
   //ِ خارجية للحصول على المنشورات API استدع وصلة  
سطر 270: سطر 274:
* <code>locales</code>: تتسع فقط 100 إعداد محلي.
* <code>locales</code>: تتسع فقط 100 إعداد محلي.
* <code>domains</code>: تتسع فقط 100 نطاق محلي.
* <code>domains</code>: تتسع فقط 100 نطاق محلي.
'''ملاحظة''': أضيفت هذه الحدود عمدًا لمنع مشاكل الأداء أثناء بناء التطبيق. بإمكانك الإلتفاف على هذه المحدودية باستخدام آلية توجّه مخصص باستخدام [[Next.js/middleware|البرمجيات الوسطية]] ابتداءً من النسخة 12 للغة Next.js.
== أمثلة ==


<blockquote>'''ملاحظة''': أضيفت هذه الحدود عمدًا لمنع مشاكل الإداء أثناء بناء التطبيق. بإمكانك الإلتفاف على هذه المحدودية باستخدام آلية توجّه مخصص باستخدام [[Next.js/middleware|الأداة الوسطية]] ابتداءً من النسخة 13 للغة Next.js.</blockquote>
* [https://github.com/vercel/next.js/tree/canary/examples/i18n-routing i18n routing]


== المصادر ==
== المصادر ==


* الصفحة [https://nextjs.org/docs/advanced-features/i18n-routing Internationalized Routing] من توثيق Next.js الرسمي.
* الصفحة [https://nextjs.org/docs/advanced-features/i18n-routing Internationalized Routing] من توثيق Next.js الرسمي.
[[تصنيف:Next.js|{{SUBPAGENAME}}]]
[[تصنيف:Next.js Advanced Features|{{SUBPAGENAME}}]]

المراجعة الحالية بتاريخ 17:07، 3 يناير 2023

تقدم Next.js دعمًا مدمجًا للوجهات العالمية internationalized (i18n) routing بدءًا من النسخة v10.0.0. وبإمكانك وضع قائمة بالإعدادات المحلية أو إعداد محلي افتراضي أو مورد مخصص لنطاق معين وستتعامل Next.js تلقائيًا مع مسار التوجيه إلى هذا المورد.

إن القصد من دعم التوجيه i18n حاليًا هو إضافة حلول المكتبة i18n مثل react-intl و react-i18next و lingui و rosetta و next-intl وغيرها من خلال تبسيط الوجهات وتفسيرها وفق النطاق المحلي.

البدء مع وجهات i18n

أضف حقل التهيئة i18n إلى الملف next.config.js. وتُعدُّ الموراد المحلية locales عبارة عن معرّفات UTS وفق تنسيق معياري تُستخدم لتعريف إعداد محلي. يتألف المعرّف المحلي عادة من لغة ومنطقة ولهجة تفصل بينها شرطة (-) كالتالي language-region-script وتحديد المنطقة region واللهجة script أمر اختياري. إليك بعض الأمثلة:

  • en-US: الإنكليزية كما تنطق في الولايات المتحدة.
  • nl-NL: الهولندية كما تُنطق في هولندا.
  • nl: الهولندية دون تحديد منطقة.

إن كانت محلية المستخدم nl-BE ولكن لم تكن مدرجة في ضبط التطبيق، فستُحول إلى nl إن كانت موجودة أو إلى المحلية الافتراضية، وإن لم تكن تنوي دعم جميع مناطق بلد معين، فيفضل إضافة محليات البلد التي سيُرجع إليها fallback.

// next.config.js
module.exports = {
  i18n: {
    // هذه هي الإعدادات المحلية التي تريد دعمها في تطبيقك
    locales: ['en-US', 'fr', 'nl-NL'],
    // هذه هذه الإعدادات المحلية الافتراضية التي تريد استخدامها
    //مثلًا `/hello` عند زيارة وجهة غير محدد الإنتماء 
    defaultLocale: 'en-US',
    // هذه قائمة بالنطاقات المحلية والإعداد المحلي الافتراضي
    // الذي ينبغي لها التعامل معه (ضروري فقط عند إعداد وجهة باستخدام نطاق)
    // ملاحظة: لا بد أن توضع النطاقات الفرعية ضمن قيمة النطاق لتجري مطابقتها
    // "fr.example.com" مثال 
    domains: [
      {
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
        //اختياري أيضًا لاختبار النطاقات المحلية محليًا http يمكن استخدام حقل 
        // https بدلًا من  http باستخدام 
        http: true,
      },
    ],
  },
}

استراتيجات التعامل مع الإعدادات المحلية

هنالك استراتيجيتين أساسيتين هما: التوجه بمسارات فرعية Sub-path Routing والتوجه عبر النطاقات Domain Routing.

التوجه باستخدام المسارات الفرعية

يضع المسار الفرعي الإعداد المحلي ضمن عنوان url للمسار:

// next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL'],
    defaultLocale: 'en-US',
  },
}

تتيح الإعدادات السابقة وجهات إلى الإعدادات المحلية en-US و fr و nl-NL وتكون الوجهة الافتراضية إلى الإعداد المحلي en-US. فإن كانت لديك الصفحة pages/blog.js، ستتمكن من الوصول إلى عناوين url التالية:

  • /blog
  • /fr/blog
  • /nl-nl/blog

لا يملك الإعداد المحلي الافتراضي بادئة.

التوجه من خلال النطاقات

يمكن من خلال هذا التوجه تهيئة الإعدادات المحلية لتُخدَّم من نطاقات مختلفة:

// next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
    defaultLocale: 'en-US',

    domains: [
      {
     // لا بد أن توضع النطاقات الفرعية ضمن قيمة النطاق لتجري مطابقتها
    //إن كان هو اسم المضيف المتوقع www.example.com مثال: لا بد من استخدام 
      
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
        // حدد إعدادات محلية أخرى ينبغي أن يُعاد توجيهها إلى هذا النطاق
        locales: ['nl-BE'],
      },
    ],
  },
}

فإن كانت لديك الصفحة pages/blog.js، ستتمكن من الوصول إلى عناوين url التالية:

  • example.com/blog
  • www.example.com/blog
  • example.fr/blog
  • example.nl/blog
  • example.nl/nl-BE/blog

الكشف التلقائي عن دعم إعدادات محلية

تحاول Next.js تلقائيًا التقاط الإعدادات المحلية بناء على قيمة الترويسة Accept-Language واسم النطاق الحالي بمجرّد وصول المستخدم إلى جذر التطبيق (عادة /). فإن اكتشفت إعدادات محلية مختلفة عن المورد الافتراضي، سيوجّه الزائر إلى إحدى الوجهتين التاليتين:

  • عند استخدام التوجيه باستخدام المسارات الفرعية: إلى الإعداد المحلي الذي يبدأ عنوان المسار.
  • عند استخدام التوجيه من خلال النطاقات: إلى النطاق ذو الإعداد المحلي المُحدد كإعداد افتراضي.

إذا استخدم التوجه من خلال النطاقات، وكانت قيمة الترويسة Accept-Language هي fr;q=0.9 عند زيارة المستخدم للنطاق example.com، فسيحوَّل الزائر إلى النطاق example.fr الذي يتعامل مع الإعداد المحلي fr افتراضيًا. وعند استخدام التوجيه من خلال المسارات الفرعية، سيوجَّه المستخدم إلى العنوان ‎/fr.

إضافة بادئة إلى الإعداد المحلي الافتراضي

يمكن إضافة بادئة إلى الإعداد المحلي الافتراضي بالالتفاف حول الموضوع باستخدام Next.js (النسخة 12) والبرمجيات الوسطية. لنتأمل على سبيل المثال الملف next.config.js الذي يدعم عدة لغات وقد أُضيف الإعداد المحلي "default" عمدًا:

// next.config.js

module.exports = {
  i18n: {
    locales: ['default', 'en', 'de', 'fr'],
    defaultLocale: 'default',
    localeDetection: false,
  },
  trailingSlash: true,
}

يمكن أن نستخدم بعد ذلك أداة وسطية لإضافة قواعد توجه مخصصة:

// middleware.ts

import { NextRequest, NextResponse } from 'next/server'

const PUBLIC_FILE = /\.(.*)$/

export async function middleware(req: NextRequest) {
  if (
    req.nextUrl.pathname.startsWith('/_next') ||
    req.nextUrl.pathname.includes('/api/') ||
    PUBLIC_FILE.test(req.nextUrl.pathname)
  ) {
    return
  }

  if (req.nextUrl.locale === 'default') {
    const locale = req.cookies.get('NEXT_LOCALE') || 'en'

    return NextResponse.redirect(
      new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
    )
  }
}

تتجاوز البرمجية الوسطية إضافة البادئة الافتراضية إلى مسار API وإلى الملفات العامة مثل الخطوط والصور. وإن وصل طلب إلى الإعداد المحلي الافتراضي سيُعاد توجيهه إلى البادئة en/.

تعطيل ميزة الكشف التلقائي عن الإعداد المحلي

يمكن تعطيل ميزة الكشف التلقائي عن الإعدادات المحلية كالتالي:

// next.config.js
module.exports = {
  i18n: {
    localeDetection: false,
  },
}

عندما تُضبط قيمة الخيار localeDetection على false، لن توجّه Next.js المستخدم وفقًا للإعداد المحلي المفضّل تلقائيًا، بل تزوّده فقط بمعلومات محلية تلتقطها من الإعداد المحلي للنطاق أو المسار الفرعي المحلي كما أشرنا سابقًا.

الوصول إلى المعلومات المحلية

يمكن الوصول إلى الإعدادت المحلية من خلال موجّه Next.js. استخدم مثلًا الخطاف ()useRouter الذي يتيح لك الخاصيات التالية:

  • locale: يعيد الإعداد المحلي المُفعَّل.
  • locales: ويتضمن كل اللغات المهيأة.
  • defaultLocale: ويتضمن كل الإعداد المحلي الافتراضي المهيأ.

عند التصيير المسبق للصفحات باستخدام getStaticProps أو getServerSideProps، ستجد المعلومات المحلية ضمن السياق الذي تُزوَّد به هاتين الدالتين.

تزوّد الدالة عند استخدامها بالإعدادات المحلية من خلال معامل السياق الخاص بالدالة والخاصية locales أو الخاصية defaultLocale في حال أردت تمرير الإعدادات المحلية الافتراضية.

الانتقال بين الإعدادات المحلية

بإمكانك استخدام المكوّنين next/link أو next/router للتنقل بين الإعدادات المحلية، إذ يمكن تزويد المكوّن next/link بالخاصية locale وذلك للانتقال إلى إعدادات محلية أخرى انطلاقًا من الحالية. وإن لم يزوّد بهذه الخاصية، تُستخدم الإعدادات الحالية أثناء الإنتقال ما بين الصفحات في جانب العميل. إليك مثالًا:

import Link from 'next/link'

export default function IndexPage(props) {
  return (
    <Link href="/another" locale="fr">
      <a>To /fr/another</a>
    </Link>
  )
}

عند استخدام توابع المكوّن next/router بإمكانك تحديد الإعدادات المحلية locale التي ينبغي تطبيقها عبر خيارات الإنتقال. إليك مثالًا:

import { useRouter } from 'next/router'

export default function IndexPage(props) {
  const router = useRouter()

  return (
    <div
      onClick={() => {
        router.push('/another', '/another', { locale: 'fr' })
      }}
    >
      to /fr/another
    </div>
  )
}

لاحظ أن الخاصية locale فقط هي من تحتفظ بمعلومات التوجه مثل قيم استعلام الوجهة الديناميكية أو قيمة استعلام السمة href المخفية عند التعامل مع تبديل الإعدادات، كما يمكن أن تستخدم المعامل href على شكل كائن:

import { useRouter } from 'next/router'
const router = useRouter()
const { pathname, asPath, query } = router
// غير الإعدادات المحلية فقط وحافظ على بقية معلومات الوجهة 
// href بما في ذلك استعلام 
router.push({ pathname, query }, asPath, { locale: nextLocale })

لمزيد من المعلومات عن بنية الكائن router.push راجع توثيق الواجهة البرمجية للوجهات. إن كان لديك سمة href تتضمن الإعدادات المحلية، يمكنك التخلي عن التعامل التلقائي مع بادئة الإعداد المحلي:

import Link from 'next/link'

export default function IndexPage(props) {
  return (
    <Link href="/fr/another" locale={false}>
      <a>To /fr/another</a>
    </Link>
  )
}

استخدام NEXT_LOCALE من ملف تعريف الارتباط

تسمح Next.js باستبدال الترويسة accept-language بقيمة NEXT_LOCALE=the-locale من ملف تعريف الارتباط، ويمكن ضبط هذا الملف باستخدام مبدِّل لغة، وعندما يعود المستخدم مجددًا إلى الموقع سيبدّل الإعدادات بتلك الموجودة في ملف الارتباط عند زيارة الصفحة الرئيسية /. فلو افترضنا مثلًا أن اللغة المفضلة في الترويسة هي fr لكن ملف تعريف الارتباط قد ضُبط كالتالي NEXT_LOCALE=en، عندها سيُوجَّه المستخدم إلى الإعداد المحلي en عند زيارة الصفحة الرئيسية / حتى يُزال هذا الملف أو تنتهي صلاحيته.

تحسين محركات البحث SEO

تحدد Next.js اللغة التي استخدمها الزائر ولهذا ستضيف السمة lang تلقائيًا إلى العنصر <html>. لكنها لا تعرف النسخ المختلفة من الصفحة (التي تدعم إعدادات محلية مختلفة) لهذا يقع على عاتقك إضافة البيانات الوصفية hreflang باستخدام المكوّن next/head. راجع توثيق Google Webmasters لمعلومات أكثر عن hreflang.

الإعدادات المحلية والتوليد الساكن في Next.js

لا يمكن دمج الوجهات العالمية مع المكوّن next export لأنه لا يعدّل في طبقة التوجّه الخاصة باللغة. لهذا تُدعم تطبيقات Next.js الهجينة التي لا تستخدم next export بشكل كامل.

الوجهات الديناميكية والصفحات التي تستخدم getStaticProps

لا بد من إعادة جميع نسخ الصفحات ذات الإعدادات المحلية المختلفة من الدالة getStaticPaths وذلك في حال استخدمت هذه الصفحات الدالة getStaticProps مع الوجهات الديناميكية. بإمكانك إعادة الحقل locale مع الكائن params الخاص بالمسارات paths لكي تحدد الإعداد المحلي الذي ترغب بتصييره.

// pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
  return {
    paths: [
      // إن لم تكن هناك إعدادات محلية فستوّلد الإعدادات الافتراضية فقط
      { params: { slug: 'post-1' }, locale: 'en-US' },
      { params: { slug: 'post-1' }, locale: 'fr' },
    ],
    fallback: true,
  }
}

أما بالنسبة للصفحات غير الديناميكية التي تستخدم getStaticProps، ولأغراض التحسين التلقائي الساكن، تولَّد نسخة من الصفحة لكل إعداد محلي. ومن المهم معرفة ذلك، لأنه سيزيد من وقت البناء وفقًا لعدد الإعدادات المحلية التي هُيِّئت ضمن الدالة getStaticProps. فإن هيّأت مثلًا 50 إعداد محلي لعشر صفحات ليست ديناميكية باستخدام getStaticProps فهذا يعني استدعاء الدالة getStaticProps قرابة 500 مرة، أي 50 نسخة لكل صفحة من الصفحات العشر خلال زمن البناء.

يُستخدم نمط التراجع fallback لتقليل زمن بناء الصفحات الديناميكية باستخدام getStaticProps، إذ يسمح لك ذلك بالعودة إل أكثر المسارات والإعدادات المحلية استخدامًا من قبل الدالة getStaticPaths لإعادة تصييرها عند البناء. ومن ثم ستبني Next.js الصفحات الباقية خلال زمن التنفيذ عند الطلب.

الصفحات المحسنة تلقائيًا بشكل ساكن

لأغراض التحسين التلقائي الساكن، تولَّد نسخة من الصفحة لكل إعداد محلي.

الصفحات غير الديناميكية التي تستخدم getStaticProps

تولَّد كذلك نسخة عن الصفحات غير الديناميكية التي تستخدم الدالة getStaticProps. إذ تُستدعى هذه الدالة عند تصيير كل إعداد محلي locale. وإن أردت منع تصيير أي من تلك الإعدادات مسبقًا، بإمكانك أن تعيد القيمة notFound: true من getStaticProps، وهكذا لن تولَّد نسخة خاصة بهذا الإعداد المحلي من الصفحة.

export async function getStaticProps({ locale }) {
  //ِ خارجية للحصول على المنشورات API استدع وصلة 
  // بإمكانك استخدام أية مكتبة لإحضار البيانات
  
  const res = await fetch(`https://.../posts?locale=${locale}`)
  const posts = await res.json()

  if (posts.length === 0) {
    return {
      notFound: true,
    }
  }
  //{ props: posts } عند إعادة الكائن  Blog سيتلقى المكوّن
  // المنشورات كخاصية في زمن البناء
  return {
    props: {
      posts,
    },
  }
}

محدودية إعدادات i18n

  • locales: تتسع فقط 100 إعداد محلي.
  • domains: تتسع فقط 100 نطاق محلي.

ملاحظة: أضيفت هذه الحدود عمدًا لمنع مشاكل الأداء أثناء بناء التطبيق. بإمكانك الإلتفاف على هذه المحدودية باستخدام آلية توجّه مخصص باستخدام البرمجيات الوسطية ابتداءً من النسخة 12 للغة Next.js.

أمثلة

المصادر