دعم تعدد اللغات في Next.js من خلال مكتبة next-translate


تعدد اللغات لأي موقع من الأمور المهمة و المطلوبة، في هذه التدوينة سنتطرق لمكتبة next-translate
المختصة في تعدد اللغات و توافقها مع Next.js
، كما إنها تمتاز بسهولة ضبطها و إعدادها مقارنة بقريناتها، بالإضافة إلى مميزاتها الآخرى، و الآن لنبدأ!
تثبيت مكتبة next-translate
في البداية نفتح Terminal أو CMD في مسار مشروع Next.js ثم ننفّذ الأمر التالي :
yarn add next-translate
ثم ننفذ الأمر التالي لتثبيت إضافة أخرى للمكتبة السابقة :
yarn add -D next-translate-plugin
إعداد ملف i18n
في البداية لننشئ ملف i18n.js
في المسار الرئيسي لمشروعنا، ثم نضيف الكود التالي :
module.exports = {
"locales": ["ar", "en"],
"defaultLocale": "ar",
"pages": {
"*": ["common"],
},
"loadLocaleFrom": (lang, ns) => import(`./public/locales/${lang}/${ns}.json`).then((m) => m.default),
}
شرح الكود
كل ما يهمنا هو المفاتيح الموجودة في الكود، و هي كالتالي :
locales
: هنا نمرر مصفوفة اللغات التي سندعمها في موقعنا.
defaultLocale
: من خلال هذا المفتاح نحدّد اللغة الإفتراضية و في مثالنا تم تحديد اللغة العربية كلغة إفتراضية للموقع.
pages
هنا يمكننا تحديد ملفات الترجمة لكل صفحة فمثلًا يمكن تحديد ملف ترجمة أو أكثر لكل صفحة على حده فقط، مثال :
"pages" : {
"*" : ["common"],
"/": ["home", "example"],
"/about": ["about"]
}
loadLocaleFrom
دالة لتحديد مسار المخصص لملفات الترجمة، سيتم شرحها لاحقًا في قسم الخاص بها.
ملاحظة : إذا كنت لا ترغب بتغيير مسار مجلد
locales
فيمكنك إعداد ملفi18n.json
بدلi18n.js
.
ضبط ملف next.config
نحتاج إلى تعديل ملف next.config.js
ليصبح كالتالي :
/** @type {import('next').NextConfig} */
const nextTranslate = require('next-translate-plugin')
const nextConfig = {
reactStrictMode: false,
i18n: {
locales: ['ar', 'en'],
defaultLocale: 'ar',
localeDetection: false,
},
}
module.exports = nextTranslate(nextConfig);
ملاحظة : ستلاحظ إختلاف بين الكود السابق و الكود المرادف له في وثائق next-translate و يعود سبب ذلك، هو تجربتي حيث ضبط اللغة الإفتراضية و إلغاء معرفة اللغة تلقائيًا لم يضبط إلا بهذه الطريقة.
يهمّنا من الكود السابق هو الكائن i18n
و سأشرح كل مفتاح و وظيفته :
locales
: هنا نمرر مصفوفة اللغات التي سندعمها في موقعنا.
defaultLocale
: من خلال هذا المفتاح نحدّد اللغة الإفتراضية و في مثالنا تم تحديد اللغة العربية كلغة إفتراضية للموقع.
localeDetection
: لإلغاء خاصية تحديد اللغة تلقائيًا من خلال لغة المتصفح.
إنشاء ملفات الترجمة
في البداية ننشئ مجلد locales
في مسار الرئيسي للمشروع حيث يكون :
/locales/
و هو المسار الإفتراضي لـ next-translate
، ثم ننشئ مجلد لكل لغة بداخله حيث يصبح مسارهم :
/locales/ar/
/locales/en/
بداخل كل مجلد لغة ننشئ ملفات json
التي نحتاجها، مثال على إنشاء ملف common
:
/locales/ar/common.json
/locales/en/common.json
في ملف common.json
المتواجد في مجلد ar
نضع ترجمات اللغة العربية، مثال :
{
"hello" : "مرحبًا",
"language" : "اللغة",
"change_language" : "تغيير اللغة",
"arabic" : "العربية",
"english" : "English"
}
اللغة الإنجليزية en
:
{
"hello" : "Hello",
"language" : "Language",
"change_language" : "Change language",
"arabic" : "العربية",
"english" : "English"
}
في حال أردت تغيير مسار المجلد locales
لمسار آخر، مثلًا بداخل مجلد public
حينئذ تحتاج إلى استخدام دالة loadLocaleFrom
و ضبط المسار، مثال على ذلك :
module.exports = {
"locales": ["ar", "en"],
"defaultLocale": "ar",
"pages": {
"*": ["common"],
},
"loadLocaleFrom": (lang, ns) => import(`./public/locales/${lang}/${ns}.json`).then((m) => m.default),
}
إعداد ملف middleware.ts
هذه الخطوة فقط لمن يستخدم الهيكلة الجديدة App router لـ Next.js، في حال كُنت تستخدم src directory فيمكن التخطّي و الذهاب للخطوة التالية.
هذه الخطوة من أجل حل مشكلة ظهور صفحة 404 عند تغيير اللغة الإفتراضية التي أخترناها للغة أخرى أو العكس.
في البداية ننشئ ملف middleware.ts
في الجذر الرئيسي للمشروع بحيث يكون :
/middleware.ts
ثم نضيف له الكود التالي :
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import i18n from "./i18n";
export function middleware(request: NextRequest) {
const locale = request.nextUrl.locale || i18n.defaultLocale;
request.nextUrl.searchParams.set("lang", locale);
request.nextUrl.href = request.nextUrl.href.replace(`/${locale}`, "");
return NextResponse.rewrite(request.nextUrl);
}
و بهذه الطريقة لن تظهر لنا صفحة 404 مجددًا عند تغيير اللغة.
إضافة تعدد اللغات للصفحات
بعد ضبط الإعدادات و إنشاء ملفات اللغات، كل ما تبقّى لدينا هو جعل الصفحات تدعم تعدد اللغات، و يمكن ذلك من خلال استخدام useTranslation
التي توفّرها المكتبة، و مثال على استخدامها :
import useTranslation from 'next-translate/useTranslation';
export default function Home() {
const { t } = useTranslation('common');
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 gap-4">
<div className='font-noto-sans font-bold text-2xl'>
{t('hello')}
</div>
</main>
)
}
الآن كما نرى أضفناها للصفحة، و تم تمرير اسم namespace
و الذي بمثالنا اسمه common
، و تمرير مفتاح الترجمة إلى t
، الآن عند الذهاب إلى الصفحة سنجد بأن الترجمة قد ظهرت.
تمرير نصوص و قيّم إلى الترجمات
يمكنك تمرير النصوص أو الأرقام إلى الترجمات لتكون متضمنة في الترجمة من خلال الطريقة التالية :
{
"welcome_user": "مرحبًا {{name}} مُجددًا",
"your_age": "عمرك {{age}} سنة",
}
حيث نفتح أقواس معكوفة متداخلة و نمرر اسم المفتاح لنتعامل معه من خلال t
، كما سيتوضح معنا في المثال التالي :
import useTranslation from 'next-translate/useTranslation';
export default function Home() {
const { t } = useTranslation('common');
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 gap-4">
<div className='font-noto-sans font-bold text-2xl'>
{t('welcome_user', { name: "سليمان" })}
</div>
<div className='font-noto-sans font-semibold text-xl'>
{t('your_age', { age: 30 })}
</div>
</main>
)
}
كل ما نحتاجه هو تمرير كائن كمعلّم ثاني parameter
لـ t
و نعطيه قيمة كما في المثال السابق.
يمكنك ضبط ترجمة للمعدودات
توفّر مكتبة next-translate
طريقة للتعامل مع الترجمات لصيغة الجمع "المعدود" من خلال إضافة لاحقة Suffix لمفتاح الترجمة، و هو أمر جيد لضبط ترجمات و بالذات صيغة المعدود في اللغة العربية مثل ( قلم - قلمان - أقلام )، و هذه اللواحق هي :
0 يمثّل صفر.
one يمثّل المعدود 1.
two يمثّل المعدود اثنان 2.
few تمثّل المعدود من 3 إلى 10.
many تمثّل المعدود من 11 إلى 99.
other من 100 فأعلى.
مثال على طريقة كتابتها :
{
"cart_message_0": "السلة الفارغة",
"cart_message_one": "تحتوي السلة على منتج واحد {{count}}",
"cart_message_two": "تحتوي السلة على منتجان {{count}}",
"cart_message_few": "تحتوي السلة على {{count}} منتجات",
"cart_message_many": "تحتوي السلة على {{count}} منتج",
"cart_message_other": "تحتوي السلة على {{count}} منتج",
}
طريقة الإستخدام مثل طريقة تمرير السابقة.
إضافة تغيير اللغة
هنا سأتطرّق إلى طريقتين، طريقة لـ app router
و طريقة لـ src directory
.
app router
الكود :
import useTranslation from 'next-translate/useTranslation';
import Link from 'next/link';
export default function Home() {
const { t } = useTranslation('common');
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 gap-4">
<div className='font-noto-sans font-bold text-2xl'>
{t('hello')}
</div>
<div className='font-noto-sans'>{t('change_language')}</div>
<div className="flex gap-4">
<Link
className='bg-cyan-800 font-semibold font-noto-sans rounded-2xl w-28 text-center py-2'
href="/?lang=ar" as="/">
{t('arabic')}
</Link>
<Link
className='bg-cyan-800 font-semibold font-noto-sans rounded-2xl w-28 text-center py-2'
href="/?lang=en" as="/en">
{t('english')}
</Link>
</div>
</main>
)
}
من أجل إضافة زر تغيير اللغة إلى اللغة العربية و كذلك زر تغيير اللغة إلى اللغة الإنجليزية سنستخدم مكوّن component
و هو Link
التي توفّره nextjs
و نضبطه بحيث يكون قيمة href
اسم الباراميتر و هو lang
و قيمته هي اللغة المحددة، و من أجل ضبط url ليصبح فقط اللغة المختارة دون query param
يكون ذلك من خلال as
.
ملاحظة : في اللغة العربية مررنا قيمة
/
لـ خاصيةas
، و ذلك بأنّه لا توجد حاجة لإظهار ما هي اللغة الإفتراضية في الرابط، و نكتفي بإظهار لغات الأخرى في الرابط.
src directory
الكود
import useTranslation from 'next-translate/useTranslation';
import setLanguage from 'next-translate/setLanguage'
export default function Home() {
const { t } = useTranslation('common');
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 gap-4">
<div className='font-noto-sans font-bold text-2xl'>
{t('hello')}
</div>
<div className='font-noto-sans'>{t('change_language')}</div>
<div className="flex gap-4">
<button onClick={async () => await setLanguage('ar')}>{t('arabic')}</button>
<button onClick={async () => await setLanguage('en')}>{t('english')}</button>
</div>
</main>
)
}
كل ما نحتاجه هو إستيراد و استخدام دالة setLanguage
، و طريقة استخدامها هو إضافتها لـ onClick و تمرير اللغة المراد استخدامها كما في الكود السابق.
الختام
في ختام التدوينة أود أن أنوّه بأنّي لم أشرح كافّة المكتبة و النقاط المهمة، فمثلًا هناك getT
و التي تمكّننا من استخدام الترجمات من جهة server-side
في api route
و getStaticProps
و غيرها، و كذلك مكوّن Trans
و الذي يسمح لنا بتضمين وسوم html
في الترجمات، و لكني أرجو إن وُفّقت بتغطية أهم النقاط التي يجب معرفتها في المكتبة.