التدويل
يتيح لك Next.js تكوين التوجيه وعرض المحتوى لدعم لغات متعددة. جعل موقعك متكيفًا مع اللغات المختلفة يتضمن محتوى مترجم (توطين) ومسارات دولية.
المصطلحات
- اللغة المحلية (Locale): معرف لمجموعة من تفضيلات اللغة والتنسيق. يشمل عادةً اللغة المفضلة للمستخدم وربما منطقته الجغرافية.
en-US
: الإنجليزية كما تُستخدم في الولايات المتحدةnl-NL
: الهولندية كما تُستخدم في هولنداnl
: الهولندية بدون منطقة محددة
نظرة عامة على التوجيه
يُوصى باستخدام تفضيلات لغة المستخدم في المتصفح لتحديد اللغة المحلية المراد استخدامها. تغيير لغتك المفضلة سيعدل رأس Accept-Language
الوارد إلى تطبيقك.
على سبيل المثال، باستخدام المكتبات التالية، يمكنك النظر إلى Request
الوارد لتحديد اللغة المحلية المطلوبة، بناءً على Headers
واللغات التي تخطط لدعمها واللغة المحلية الافتراضية.
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
let headers = { 'accept-language': 'en-US,en;q=0.5' }
let languages = new Negotiator({ headers }).languages()
let locales = ['en-US', 'nl-NL', 'nl']
let defaultLocale = 'en-US'
match(languages, locales, defaultLocale) // -> 'en-US'
يمكن تدويل التوجيه إما عن طريق المسار الفرعي (/fr/products
) أو النطاق (my-site.fr/products
). باستخدام هذه المعلومات، يمكنك الآن إعادة توجيه المستخدم بناءً على اللغة المحلية داخل البرنامج الوسيط (Middleware).
let locales = ['en-US', 'nl-NL', 'nl']
// الحصول على اللغة المحلية المفضلة، مشابه لما سبق أو باستخدام مكتبة
function getLocale(request) { ... }
export function middleware(request) {
// التحقق مما إذا كان هناك أي لغة مدعومة في مسار URL
const { pathname } = request.nextUrl
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
)
if (pathnameHasLocale) return
// إعادة التوجيه إذا لم تكن هناك لغة محددة
const locale = getLocale(request)
request.nextUrl.pathname = `/${locale}${pathname}`
// مثال: الطلب الوارد هو /products
// عنوان URL الجديد الآن هو /en-US/products
return Response.redirect(request.nextUrl)
}
export const config = {
matcher: [
// تخطي جميع المسارات الداخلية (_next)
'/((?!_next).*)',
// اختياري: التشغيل فقط على عنوان URL الجذر (/)
// '/'
],
}
أخيرًا، تأكد من أن جميع الملفات الخاصة داخل app/
متداخلة تحت app/[lang]
. هذا يمكّن موجه Next.js من التعامل ديناميكيًا مع اللغات المختلفة في المسار، وإعادة معلمة lang
إلى كل تخطيط وصفحة. على سبيل المثال:
// لديك الآن إمكانية الوصول إلى اللغة الحالية
// مثال: /en-US/products -> `lang` هي "en-US"
export default async function Page({ params: { lang } }) {
return ...
}
يمكن أيضًا تداخل التخطيط الجذر في المجلد الجديد (مثال: app/[lang]/layout.js
).
التوطين
تغيير المحتوى المعروض بناءً على اللغة المفضلة للمستخدم، أو التوطين، ليس شيئًا خاصًا بـ Next.js. الأنماط الموضحة أدناه ستعمل بنفس الطريقة مع أي تطبيق ويب.
لنفترض أننا نريد دعم المحتوى بالإنجليزية والهولندية داخل تطبيقنا. قد نحتفظ بـ "قواميس" مختلفة، وهي كائنات تعطينا تعيينًا من مفتاح معين إلى سلسلة مترجمة. على سبيل المثال:
{
"products": {
"cart": "Add to Cart"
}
}
{
"products": {
"cart": "Toevoegen aan Winkelwagen"
}
}
يمكننا بعد ذلك إنشاء دالة getDictionary
لتحميل الترجمات للغة المطلوبة:
import 'server-only'
const dictionaries = {
en: () => import('./dictionaries/en.json').then((module) => module.default),
nl: () => import('./dictionaries/nl.json').then((module) => module.default),
}
export const getDictionary = async (locale) => dictionaries[locale]()
بالنظر إلى اللغة المحددة حاليًا، يمكننا جلب القاموس داخل تخطيط أو صفحة.
import { getDictionary } from './dictionaries'
export default async function Page({ params: { lang } }) {
const dict = await getDictionary(lang) // en
return <button>{dict.products.cart}</button> // Add to Cart
}
لأن جميع التخطيطات والصفحات في دليل app/
تكون افتراضيًا مكونات الخادم (Server Components)، لا داعي للقلق بشأن حجم ملفات الترجمة الذي يؤثر على حجم حزمة JavaScript على جانب العميل. هذا الرمز سيعمل فقط على الخادم، وسيتم إرسال HTML الناتج فقط إلى المتصفح.
التوليد الثابت
لتوليد مسارات ثابتة لمجموعة معينة من اللغات، يمكننا استخدام generateStaticParams
مع أي صفحة أو تخطيط. يمكن أن يكون هذا عالميًا، على سبيل المثال، في التخطيط الجذر:
export async function generateStaticParams() {
return [{ lang: 'en-US' }, { lang: 'de' }]
}
export default function Root({ children, params }) {
return (
<html lang={params.lang}>
<body>{children}</body>
</html>
)
}