إنشاء تطبيق متعدد اللغات باستخدام Next.js i18n

إنشاء تطبيق متعدد اللغات باستخدام Next.js i18n

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

عندما تبدأ في التفكير في تطبيق متعدد اللغات، ستلاحظ أن المسألة ليست مجرد ترجمة نصوص من الإنجليزية إلى العربية أو الفرنسية أو الإسبانية. الأمر أعمق من ذلك. أنت تبني طبقة كاملة من التجربة تشمل توجيه الصفحات حسب اللغة، وإدارة ملفات الترجمة، وتنسيق الأرقام والتواريخ، والتعامل مع اتجاه الصفحة من اليمين إلى اليسار أو العكس، وربما حتى تغيير الصور أو الرسائل حسب البلد أو الثقافة. وكلما كان التنفيذ من البداية منظمًا، كلما أصبح المشروع أسهل في التوسع والصيانة.

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

لماذا تحتاج إلى i18n في Next.js؟

الفائدة الأولى واضحة: الوصول إلى جمهور أوسع. لو كان تطبيقك يخدم مستخدمين من المغرب، أو الخليج، أو فرنسا، أو كندا، أو أي سوق متعدد اللغات، فوجود لغة واحدة فقط سيجعل جزءًا كبيرًا من المستخدمين يشعرون أن المنتج لا يخاطبهم. لكن الفائدة لا تتوقف هنا. التطبيق متعدد اللغات يعطي انطباعًا أعلى احترافية، ويساعد في تحسين تجربة الاستخدام، ويزيد من معدلات التفاعل والتحويل، خصوصًا إذا كانت اللغة جزءًا من قرار الشراء أو التسجيل.

الفائدة الثانية هي تحسين قابلية التوسع. عندما تصمم مشروعك مع i18n من البداية، فإن إضافة لغة جديدة لاحقًا تصبح عملية منظمة بدل أن تكون إعادة هندسة كاملة. لن تضطر إلى إعادة كتابة الصفحات، بل غالبًا ستضيف ملفات ترجمة جديدة وبعض التعديلات في الإعدادات، وهذا فرق كبير جدًا في المشاريع التي تنمو بسرعة.

الفائدة الثالثة تتعلق بـ SEO. عندما تُهيئ صفحاتك بلغات متعددة بشكل صحيح، يمكن لمحركات البحث فهم النسخ اللغوية المختلفة من المحتوى، مما يساعد على الظهور في نتائج بحث مناسبة لكل لغة أو سوق. وهذا مهم خصوصًا للمواقع التسويقية، والمدونات، والمتاجر الإلكترونية، والمنصات التعليمية.

الفكرة الأساسية: ماذا نعني بـ i18n؟

اختصار i18n يعني internationalization، أي تهيئة التطبيق ليعمل مع عدة لغات وثقافات. أما localization أو l10n فهي عملية تكييف التطبيق بالفعل للغة أو سوق محدد. بمعنى أبسط: i18n هو تجهيز البنية، وl10n هو ملء هذه البنية بالمحتوى المناسب لكل لغة.

في Next.js، يمكنك تنفيذ i18n بعدة طرق، لكن الأسلوب الأشهر اليوم يعتمد على مبدأين:

  1. تنظيم المسارات حسب اللغة مثل /en/about و/ar/about.

  2. فصل النصوص إلى ملفات ترجمة بدل كتابتها مباشرة داخل المكونات.

وبذلك يصبح التطبيق قابلًا للتبديل بين اللغات بطريقة نظيفة ومفهومة.

ماذا سنبني؟

سنبني تصورًا عمليًا لمشروع Next.js يدعم ثلاث لغات:

  • العربية ar

  • الإنجليزية en

  • الفرنسية fr

وسندعم فيه:

  • توجيه الصفحات حسب اللغة

  • ملف ترجمة منفصل لكل لغة

  • زر تغيير اللغة

  • واجهة تدعم RTL للعربية

  • ترجمة العناوين والمحتوى

  • تنسيق التواريخ والأرقام

  • تحسين SEO للنسخ اللغوية المختلفة

سأستخدم أمثلة واضحة ومباشرة حتى يكون بإمكانك نقل الفكرة إلى أي مشروع آخر بسهولة.

إعداد مشروع Next.js

أول خطوة هي إنشاء مشروع جديد. يمكنك استخدام أي إصدار حديث من Next.js، وسأعرض الفكرة بشكل عام بحيث تناسب المشاريع التي تستخدم App Router أو Pages Router، لكن التركيز سيكون على أسلوب حديث ومنظم.

npx create-next-app@latest multilingual-next-app
cd multilingual-next-app
npm run dev

بعد ذلك ستحتاج إلى حزم لإدارة الترجمات. هناك أكثر من خيار، لكن من أكثر الحلول شيوعًا ومرونة:

npm install next-intl

يمكنك أيضًا استخدام حلول أخرى مثل react-i18next، لكن next-intl مناسب جدًا مع Next.js لأنه ينسجم مع البنية الحديثة ويقدم أدوات جيدة للتعامل مع الترجمة والـ routing.

هيكلة المشروع بطريقة نظيفة

من الأخطاء الشائعة أن تبدأ بإضافة الترجمة بشكل عشوائي. هذا يعمل في البداية، ثم يتحول إلى فوضى عندما تكبر الصفحات. الأفضل أن تضع هيكلة واضحة من أول يوم.

مثال على هيكلة جيدة:

src/
  app/
    [locale]/
      layout.tsx
      page.tsx
      about/
        page.tsx
  components/
    LanguageSwitcher.tsx
    Navbar.tsx
    Footer.tsx
  i18n/
    request.ts
    routing.ts
  messages/
    en.json
    ar.json
    fr.json

هذه البنية بسيطة لكنها قوية.
لدينا:

  • مجلد messages لملفات الترجمة

  • مجلد i18n لإعدادات اللغات والمسارات

  • مجلد app/[locale] لوضع الصفحات حسب اللغة

  • مجلد components للمكونات المشتركة

الفكرة هنا أن كل شيء يذهب في مكانه الطبيعي. وهذا مهم جدًا لأن المشروع متعدد اللغات ينمو بسرعة، وأي فوضى صغيرة في البداية تتحول إلى صداع كبير لاحقًا.

إعداد اللغات والمسارات

سنبدأ بتعريف اللغات المدعومة. في ملف مثل src/i18n/routing.ts يمكننا كتابة:

export const locales = ['en', 'ar', 'fr'] as const;
export type Locale = (typeof locales)[number];

export const defaultLocale: Locale = 'en';

هذا الملف بسيط لكنه مهم جدًا لأنه يجعل باقي التطبيق يعتمد على مصدر واحد للحقيقة. بدل أن تكرر اللغات في أكثر من مكان، تحتفظ بها هنا.

ثم نجهز ملفًا آخر للتعامل مع جلب الرسائل المناسبة لكل لغة، مثل src/i18n/request.ts:

import {getRequestConfig} from 'next-intl/server';
import {locales, defaultLocale} from './routing';

export default getRequestConfig(async ({requestLocale}) => {
  const locale = requestLocale && locales.includes(requestLocale as any)
    ? (requestLocale as any)
    : defaultLocale;

  return {
    locale,
    messages: (await import(`../messages/${locale}.json`)).default
  };
});

الفكرة هنا أن Next.js سيعرف أي ملف ترجمة يجب تحميله بحسب اللغة الحالية.

ملفات الترجمة

الآن نأتي إلى ملفات المحتوى. بدل أن تكتب النصوص داخل المكونات، ضعها في ملفات JSON.

src/messages/en.json

{
  "HomePage": {
    "title": "Build a multilingual app with Next.js",
    "subtitle": "A clean and scalable approach to internationalization.",
    "cta": "Get Started"
  },
  "Navbar": {
    "home": "Home",
    "about": "About",
    "contact": "Contact"
  },
  "Footer": {
    "rights": "All rights reserved."
  }
}

src/messages/ar.json

{
  "HomePage": {
    "title": "بناء تطبيق متعدد اللغات باستخدام Next.js",
    "subtitle": "أسلوب نظيف وقابل للتوسع لإدارة الترجمة.",
    "cta": "ابدأ الآن"
  },
  "Navbar": {
    "home": "الرئيسية",
    "about": "من نحن",
    "contact": "اتصل بنا"
  },
  "Footer": {
    "rights": "جميع الحقوق محفوظة."
  }
}

src/messages/fr.json

{
  "HomePage": {
    "title": "Créer une application multilingue avec Next.js",
    "subtitle": "Une approche propre et évolutive de l’internationalisation.",
    "cta": "Commencer"
  },
  "Navbar": {
    "home": "Accueil",
    "about": "À propos",
    "contact": "Contact"
  },
  "Footer": {
    "rights": "Tous droits réservés."
  }
}

هذه الطريقة تجعل النصوص مرتبة وواضحة. كما أنها تسهّل على فريق المحتوى أو المترجمين تعديل العبارات دون لمس الكود نفسه.

إعداد الـ Layout الرئيسي

في تطبيق متعدد اللغات، الـ layout ليس مجرد إطار بصري، بل هو المكان الذي تحدد فيه خصائص اللغة واتجاه الصفحة. بالنسبة للعربية، نحتاج rtl. أما الإنجليزية والفرنسية فغالبًا ltr.

مثال:

import type {ReactNode} from 'react';
import {notFound} from 'next/navigation';
import {locales, type Locale} from '@/i18n/routing';

type Props = {
  children: ReactNode;
  params: Promise<{locale: string}>;
};

export function generateStaticParams() {
  return locales.map((locale) => ({locale}));
}

export default async function LocaleLayout({children, params}: Props) {
  const {locale} = await params;

  if (!locales.includes(locale as Locale)) {
    notFound();
  }

  const dir = locale === 'ar' ? 'rtl' : 'ltr';

  return (
    <html lang={locale} dir={dir}>
      <body>
        {children}
      </body>
    </html>
  );
}

هذا المثال يوضح الفكرة الأساسية: اللغة تحدد lang وdir.
ووجود dir مهم جدًا لأن العربية ليست فقط ترجمة نصية، بل تجربة بصرية مختلفة تمامًا. اتجاه المحاذاة، ترتيب الأيقونات، موضع السهم، وحتى ترتيب بعض العناصر يجب أن يشعر المستخدم أنها وُضعت بطريقة طبيعية.

صفحة رئيسية متعددة اللغات

الآن نكتب الصفحة الرئيسية. في src/app/[locale]/page.tsx:

import {useTranslations} from 'next-intl';
import Link from 'next/link';

export default function HomePage() {
  const t = useTranslations('HomePage');

  return (
    <main className="mx-auto max-w-4xl p-6">
      <h1 className="text-4xl font-bold">{t('title')}</h1>
      <p className="mt-4 text-lg">{t('subtitle')}</p>

      <div className="mt-8">
        <Link
          href="/about"
          className="rounded-lg bg-black px-5 py-3 text-white"
        >
          {t('cta')}
        </Link>
      </div>
    </main>
  );
}

لاحظ كيف أصبح النص معزولًا في ملفات الترجمة، بينما بقيت الواجهة نظيفة. هذه من أجمل فوائد i18n: المكونات تصبح أخف، والترجمة تصبح قابلية للتبديل بدل أن تكون عبئًا.

إضافة صفحة About

مثال لصفحة أخرى:

import {useTranslations} from 'next-intl';

export default function AboutPage() {
  const t = useTranslations('Navbar');

  return (
    <main className="mx-auto max-w-4xl p-6">
      <h1 className="text-3xl font-bold">{t('about')}</h1>
      <p className="mt-4 leading-8">
        هذه صفحة تعريفية يمكن أن تختلف نصوصها من لغة إلى أخرى حسب الجمهور المستهدف.
      </p>
    </main>
  );
}

هنا استخدمنا مفتاحًا من Navbar فقط كمثال، لكن في مشروع حقيقي يفضل أن يكون لكل صفحة namespace خاص بها، مثل AboutPage أو ContactPage.

شريط التنقل المترجم

المستخدم غالبًا سيشعر بوجود اللغة من شريط التنقل أولًا. لذا من المهم أن يكون الـ navbar مترجمًا بشكل صحيح.

'use client';

import Link from 'next/link';
import {useTranslations} from 'next-intl';

export default function Navbar() {
  const t = useTranslations('Navbar');

  return (
    <header className="border-b">
      <nav className="mx-auto flex max-w-5xl items-center justify-between p-4">
        <div className="font-bold">MyApp</div>

        <div className="flex gap-6">
          <Link href="/">{t('home')}</Link>
          <Link href="/about">{t('about')}</Link>
          <Link href="/contact">{t('contact')}</Link>
        </div>
      </nav>
    </header>
  );
}

النقطة المهمة هنا أن الروابط نفسها قد تحتاج أن تكون مرتبطة باللغة الحالية أيضًا، وليس مجرد عرض نص مترجم. وسنأتي لهذا لاحقًا عند مناقشة اللغة الحالية في المسارات.

زر تبديل اللغة

واحدة من أهم التجارب في التطبيق متعدد اللغات هي القدرة على تغيير اللغة بسهولة. الأفضل أن يكون الزر واضحًا، سريعًا، ولا يربك المستخدم.

مثال بسيط:

'use client';

import {usePathname, useRouter} from 'next/navigation';
import {locales} from '@/i18n/routing';

export default function LanguageSwitcher() {
  const pathname = usePathname();
  const router = useRouter();

  const switchLanguage = (locale: string) => {
    const segments = pathname.split('/');
    segments[1] = locale;
    const newPath = segments.join('/');
    router.push(newPath);
  };

  return (
    <div className="flex gap-2">
      {locales.map((locale) => (
        <button
          key={locale}
          onClick={() => switchLanguage(locale)}
          className="rounded-md border px-3 py-1"
        >
          {locale.toUpperCase()}
        </button>
      ))}
    </div>
  );
}

هذا المثال يوضح الفكرة، لكن في مشروع حقيقي قد تحتاج إلى مراعاة:

  • الحفاظ على الصفحة الحالية عند تغيير اللغة

  • الحفاظ على query parameters

  • دعم التحويل السلس بين النسخ المترجمة

  • عرض اسم اللغة بشكل مفهوم للمستخدم، مثل العربية / English / Français

وهنا يصبح الزر ليس مجرد عنصر UI، بل جزءًا أساسيًا من بنية التنقل.

الحفاظ على المسار عند تغيير اللغة

تخيل أن المستخدم يتصفح صفحة المنتج في الإنجليزية ثم يبدّل إلى العربية. من غير الجيد أن يعود إلى الصفحة الرئيسية فقط. الأفضل أن ينتقل إلى نفس الصفحة بالنسخة العربية إن كانت موجودة.

مثال أكثر دقة:

'use client';

import {usePathname, useRouter, useSearchParams} from 'next/navigation';

export default function LanguageSwitcher() {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const router = useRouter();

  const changeLocale = (newLocale: string) => {
    const segments = pathname.split('/');
    segments[1] = newLocale;

    const query = searchParams.toString();
    const newPath = segments.join('/') + (query ? `?${query}` : '');

    router.push(newPath);
  };

  return (
    <div className="flex gap-2">
      <button onClick={() => changeLocale('en')}>English</button>
      <button onClick={() => changeLocale('ar')}>العربية</button>
      <button onClick={() => changeLocale('fr')}>Français</button>
    </div>
  );
}

هذا الأسلوب أفضل لأنه يحافظ على حالة التصفح. وفي المشاريع المتقدمة قد يكون لديك mapping بين الصفحات المترجمة بدل مجرد استبدال اللغة في الرابط.

دعم RTL بشكل صحيح

اللغة العربية تحتاج عناية بصرية إضافية. كثير من التطبيقات تبدو "مترجمة" نصيًا لكنها غير مريحة بصريًا للعربي. السبب أن الاتجاه بقي LTR في معظم المكونات.

إليك بعض الملاحظات المهمة:

  • استخدم dir="rtl" في <html>

  • تأكد من أن المحاذاة تعتمد على الاتجاه، لا على قيمة ثابتة

  • راقب الأيقونات التي تحتوي سهامًا أو مؤشرات

  • لا تفترض أن كل العناصر يجب أن تبقى في اليسار

  • اختبر الجداول، البطاقات، والقوائم بعناية

مثال على تنسيق يعتمد على الاتجاه:

<div className="flex items-center gap-3 rtl:flex-row-reverse">
  <span className="rounded-full bg-gray-200 p-2">★</span>
  <div>
    <h3 className="font-semibold">عنوان البطاقة</h3>
    <p className="text-sm text-gray-600">وصف قصير للعنصر.</p>
  </div>
</div>

في الواقع، دعم RTL لا يجب أن يكون شيئًا لاحقًا. من الأفضل التفكير فيه من البداية لأنه يؤثر في البنية visual design نفسها.

تنسيق التواريخ والأرقام حسب اللغة

الترجمة لا تشمل الكلمات فقط. التاريخ والرقم والعملة أيضًا جزء من التجربة.
مثلًا:

  • الإنجليزية: 12/31/2026

  • العربية: 31/12/2026

  • الفرنسية: 31/12/2026

يمكنك استخدام Intl.DateTimeFormat:

export function formatDate(date: Date, locale: string) {
  return new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }).format(date);
}

واستخدامها في المكونات:

import {formatDate} from '@/utils/formatDate';

export default function ArticleMeta({locale}: {locale: string}) {
  const publishedAt = new Date('2026-06-16');

  return (
    <p>
      {formatDate(publishedAt, locale)}
    </p>
  );
}

أما الأرقام فيمكن تنسيقها أيضًا:

export function formatNumber(value: number, locale: string) {
  return new Intl.NumberFormat(locale).format(value);
}

هذا مهم جدًا في التطبيقات المالية، والتجارية، والتعليمية، وحتى الإحصائية.

التعامل مع النصوص الديناميكية

غالبًا لا تكون كل النصوص ثابتة. أحيانًا تحتاج إلى رسائل فيها متغيرات، مثل اسم المستخدم أو عدد العناصر.

مثال:

en.json

{
  "Dashboard": {
    "welcome": "Welcome, {name}!",
    "items": "You have {count} items."
  }
}

ar.json

{
  "Dashboard": {
    "welcome": "مرحبًا، {name}!",
    "items": "لديك {count} عنصرًا."
  }
}

في المكون:

import {useTranslations} from 'next-intl';

export default function Dashboard({name, count}: {name: string; count: number}) {
  const t = useTranslations('Dashboard');

  return (
    <section>
      <h2>{t('welcome', {name})}</h2>
      <p>{t('items', {count})}</p>
    </section>
  );
}

بهذه الطريقة تصبح الرسائل مرنة وديناميكية بدل أن تكون نصوصًا جامدة.

الجمع والمفرد والصيغ المتعددة

اللغة العربية غنية جدًا في الصيغ العددية، وهذا يجعل موضوع pluralization مهمًا للغاية. لو تعاملت مع الأعداد كأنها مجرد رقم + كلمة ثابتة، ستظهر النصوص بشكل غير طبيعي.

مثال أقرب للواقع:

{
  "Cart": {
    "item_one": "عنصر واحد",
    "item_other": "{count} عناصر"
  }
}

لكن في العربية قد تحتاج إلى صيغ أكثر تعقيدًا حسب المكتبة التي تستخدمها. بعض المكتبات تدعم ذلك بشكل أفضل من غيرها. المهم هنا أن تتأكد من أن الرسالة الناتجة تبدو طبيعية للقارئ العربي، لأن الخطأ في الصياغة العددية يظهر بسرعة ويعطي انطباعًا غير احترافي.

ترجمة العناوين وSEO

في المواقع التي تعتمد على البحث، ليس كافيًا أن تترجم المحتوى فقط. يجب أن تترجم أيضًا:

  • <title>

  • <meta description>

  • Open Graph tags

  • canonical URLs

  • hreflang

مثال في صفحة متعددة اللغات:

import type {Metadata} from 'next';

export async function generateMetadata({
  params
}: {
  params: Promise<{locale: string}>;
}): Promise<Metadata> {
  const {locale} = await params;

  const titles = {
    en: 'Multilingual App with Next.js',
    ar: 'تطبيق متعدد اللغات باستخدام Next.js',
    fr: 'Application multilingue avec Next.js'
  };

  const descriptions = {
    en: 'Learn how to build a multilingual app with Next.js i18n.',
    ar: 'تعلم كيفية بناء تطبيق متعدد اللغات باستخدام Next.js i18n.',
    fr: 'Apprenez à créer une application multilingue avec Next.js i18n.'
  };

  return {
    title: titles[locale as keyof typeof titles] ?? titles.en,
    description: descriptions[locale as keyof typeof descriptions] ?? descriptions.en
  };
}

هذا جزء بالغ الأهمية لأن اللغة المناسبة في SEO قد ترفع معدل الظهور والنقرات بشكل واضح.

hreflang ولماذا هو مهم

إذا كان لديك أكثر من نسخة لغوية لنفس الصفحة، فمن الأفضل أن تخبر محركات البحث بهذا الترابط. وهنا تأتي أهمية hreflang.

مثال:

export function generateAlternates(locale: string) {
  return {
    canonical: `https://example.com/${locale}`,
    languages: {
      en: 'https://example.com/en',
      ar: 'https://example.com/ar',
      fr: 'https://example.com/fr'
    }
  };
}

في مشروع حقيقي قد تُدمج هذه المعلومات داخل generateMetadata.
الفكرة هي أن جوجل ومحركات البحث الأخرى تفهم أن هذه صفحات بديلة لنفس المحتوى وليس نسخًا مكررة.

تحميل الترجمة على جهة السيرفر

من مزايا Next.js أنه يسمح لك بتحميل البيانات على السيرفر، وهذا مفيد جدًا للترجمة. بدل أن ترسل كل ملفات اللغة إلى العميل، يمكنك تحميل ما يلزم فقط.

هذا يقلل الحجم ويحسن الأداء.
والأجمل من ذلك أن المحتوى يخرج من الخادم جاهزًا للعرض، مما يجعل الصفحة أكثر انسجامًا مع SSR وSEO.

مثال:

import {getTranslations} from 'next-intl/server';

export default async function ServerPage() {
  const t = await getTranslations('HomePage');

  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('subtitle')}</p>
    </div>
  );
}

هذا الأسلوب رائع عندما تكون الصفحة تعتمد على بيانات ثابتة أو شبه ثابتة، لأنه يجمع بين الأداء والتنظيم.

ماذا عن المحتوى القادم من قاعدة البيانات؟

في كثير من المشاريع، لن تكون كل النصوص في ملفات JSON. أحيانًا يأتي المحتوى من قاعدة البيانات، مثل المقالات، أسماء المنتجات، أو وصف الخدمات. هنا تحتاج إلى استراتيجية مختلفة.

يمكنك مثلًا أن تخزن الحقول المترجمة في قاعدة البيانات بهذا الشكل:

{
  "title": {
    "en": "About us",
    "ar": "من نحن",
    "fr": "À propos de nous"
  },
  "description": {
    "en": "We build digital products.",
    "ar": "نحن نبني منتجات رقمية.",
    "fr": "Nous créons des produits numériques."
  }
}

أو يمكنك استخدام جداول ترجمة منفصلة لكل لغة. كل خيار له مزاياه.
إذا كان المحتوى صغيرًا وثابتًا، فالـ JSON ممتاز.
أما إذا كان المحتوى كبيرًا أو يدار عبر لوحة تحكم، فقد تكون قاعدة البيانات أفضل.

الترجمة داخل المكونات المشتركة

من أكبر الأخطاء أن تضع الترجمة فقط في الصفحات الرئيسية ثم تنسى المكونات الصغيرة. لا شيء يفضح التطبيق أكثر من زر أو رسالة نظام أو حالة خطأ تظهر بلغة مختلفة عن بقية الواجهة.

أمثلة على عناصر ينبغي ترجمتها:

  • أزرار الحفظ والإلغاء

  • رسائل التحميل

  • رسائل الخطأ

  • نصوص التحقق من النموذج

  • رسائل empty state

  • إشعارات النجاح والفشل

مثال:

import {useTranslations} from 'next-intl';

export default function SaveButton() {
  const t = useTranslations('Common');

  return (
    <button className="rounded-md bg-blue-600 px-4 py-2 text-white">
      {t('save')}
    </button>
  );
}

وفي ملف الترجمة:

{
  "Common": {
    "save": "حفظ",
    "cancel": "إلغاء",
    "loading": "جاري التحميل..."
  }
}

معالجة الرسائل الخطأ بلغات مختلفة

رسائل الأخطاء جزء حساس جدًا. المستخدم يتفاعل معها عندما يحدث خلل، وفي تلك اللحظة يريد وضوحًا لا تعقيدًا.

بدل أن تعرض:

Something went wrong

استخدم رسالة مناسبة للغة الحالية:

{
  "Errors": {
    "generic": "حدث خطأ غير متوقع. حاول مرة أخرى.",
    "network": "تعذر الاتصال بالخادم."
  }
}

وفي الواجهة:

import {useTranslations} from 'next-intl';

export default function ErrorMessage({type}: {type: 'generic' | 'network'}) {
  const t = useTranslations('Errors');

  return <p>{t(type)}</p>;
}

الرسالة الجيدة هنا ليست فقط مترجمة، بل أيضًا مطمئنة وواضحة.

أفضل طريقة للتفكير في اللغة داخل المشروع

من المفيد أن تتعامل مع اللغة باعتبارها جزءًا من الـ state العام للتطبيق، لا مجرد قيمة في الرابط. هذا يعني أن اللغة تؤثر على:

  • المسار

  • النصوص

  • الاتجاه

  • التاريخ والوقت

  • العملة

  • الصور أحيانًا

  • SEO

  • الرسائل الداخلية

عندما تبدأ التفكير بهذه الطريقة، ستتغير طريقة تصميمك من "ترجمة واجهة" إلى "بناء تجربة متعددة الأسواق". وهذه نقلة مهمة جدًا في جودة المشروع.

دعم أكثر من لغة دون تعقيد زائد

قد تقع في فخ إضافة عشرات اللغات لأنك تستطيع ذلك تقنيًا. لكن ليس كل مشروع يحتاج ذلك.
من الأفضل أن تبدأ بعدد لغات معقول، ثم تضيف المزيد عندما يكون هناك احتياج حقيقي ودعم لمحتوى مناسب.

كل لغة جديدة تعني:

  • ملفات ترجمة إضافية

  • مراجعة تصميم

  • اختبار اتجاه الصفحة

  • تدقيق SEO

  • مراجعة المحتوى

  • ربما دعم تنسيق مختلف للتواريخ والأرقام

لذلك لا تجعل i18n عبئًا مفتوحًا دون خطة. الأفضل أن يكون التوسع محسوبًا.

اختبار التطبيق متعدد اللغات

الاختبار مهم جدًا، لأن الأخطاء في i18n قد لا تكون تقنية فقط، بل لغوية وبصرية أيضًا.

عند الاختبار تأكد من:

  • أن كل صفحة تفتح بالمسار الصحيح

  • أن اللغة تنتقل مع الروابط الداخلية

  • أن dir يتغير للعربية

  • أن النصوص لا تتداخل أو تنكسر

  • أن الزر أو القائمة لا يختفي بسبب طول النص

  • أن الخطوط تدعم العربية بشكل جيد

  • أن رسائل الخطأ مترجمة

  • أن صفحة 404 و500 مترجمتان أيضًا

الأخطاء هنا قد تكون صغيرة، لكنها تؤثر بشكل كبير على الانطباع العام.

اختيار الخطوط المناسبة

الخط العربي يحتاج اهتمامًا خاصًا. ليس كل خط جميلًا على الشاشة، وليس كل خط مناسبًا للعناوين أو الفقرات.
في كثير من الحالات، ستحتاج إلى خط عربي واضح ومتوازن، مع خط لاتيني متناغم معه.

عندما تختار الخطوط، راعِ:

  • سهولة القراءة

  • وضوح الحروف في الأحجام الصغيرة

  • التناسق مع هوية العلامة التجارية

  • دعم التشكيل إن لزم

  • أداء التحميل

قد تستخدم مثلًا خطًا عربيًا للعربية وخطًا مختلفًا للإنجليزية، لكن احرص ألا يخلق ذلك تنافرًا بصريًا. المهم أن يشعر المستخدم بالانسجام.

تنسيق الواجهة حسب اللغة

بعض اللغات أطول من غيرها. كلمة واحدة في الإنجليزية قد تصبح جملة أطول في الفرنسية أو العربية. لذلك لا تعتمد على أزرار ضيقة جدًا أو مساحات ثابتة جدًا.

مثال: زر Save قد يصبح حفظ التغييرات أو Enregistrer les modifications.
إذا كانت المساحة ضيقة، قد ينكسر التخطيط.

الحل هو أن تبني الواجهة مرنة من البداية:

  • استخدم flex-wrap

  • تجنب عرض الأزرار بعرض ثابت صارم

  • اختبر النصوص الطويلة

  • لا تفترض أن كل العناوين قصيرة

هذا جزء من احترام اللغات نفسها، لا مجرد احترام النص المترجم.

التعامل مع التوجيه التلقائي حسب اللغة

بعض التطبيقات تريد أن تكتشف لغة المستخدم من المتصفح أو من إعدادات النظام وتوجهه تلقائيًا. هذه فكرة جيدة، لكن يجب تنفيذها بحذر.

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

  • تكشف اللغة لأول زيارة فقط

  • تحفظ اختيار المستخدم

  • لا تعيد التوجيه في كل مرة بشكل مزعج

  • تمنح المستخدم السيطرة النهائية

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

مثال كامل مبسط

إليك مثالًا صغيرًا يجمع الفكرة:

// src/app/[locale]/page.tsx
import {useTranslations} from 'next-intl';
import LanguageSwitcher from '@/components/LanguageSwitcher';

export default function HomePage() {
  const t = useTranslations('HomePage');

  return (
    <main className="mx-auto max-w-5xl p-6">
      <div className="flex justify-end">
        <LanguageSwitcher />
      </div>

      <section className="mt-10">
        <h1 className="text-4xl font-bold">{t('title')}</h1>
        <p className="mt-4 text-lg leading-8">{t('subtitle')}</p>
      </section>
    </main>
  );
}
// src/components/LanguageSwitcher.tsx
'use client';

import {usePathname, useRouter} from 'next/navigation';

export default function LanguageSwitcher() {
  const pathname = usePathname();
  const router = useRouter();

  const setLang = (locale: string) => {
    const segments = pathname.split('/');
    segments[1] = locale;
    router.push(segments.join('/'));
  };

  return (
    <div className="flex gap-2">
      <button onClick={() => setLang('en')}>EN</button>
      <button onClick={() => setLang('ar')}>AR</button>
      <button onClick={() => setLang('fr')}>FR</button>
    </div>
  );
}
// src/messages/ar.json
{
  "HomePage": {
    "title": "مرحبًا بك في تطبيق متعدد اللغات",
    "subtitle": "هذا مثال عملي يوضح كيف تجعل Next.js يدعم عدة لغات بطريقة منظمة."
  }
}

هذا المثال صغير، لكنه يختصر الكثير من المفاهيم الأساسية التي تحدثنا عنها.

منطق العمل الحقيقي في المشاريع الكبيرة

في المشاريع الكبيرة، i18n لا يكون مجرد ملفات ترجمة. بل قد يشمل:

  • نظام إدارة ترجمة داخلي

  • ربط مع منصة ترجمة خارجية

  • مراجعة لغوية من فريق محتوى

  • ترجمة تلقائية أولية ثم مراجعة بشرية

  • تتبع النصوص الناقصة

  • تنبيهات عند وجود مفتاح ترجمة غير موجود

وهنا تصبح العملية أقرب إلى خط إنتاج متكامل.
النجاح ليس في "ترجمة كل شيء" فقط، بل في إدارة الترجمة بشكل يمكن الوثوق به.

أخطاء شائعة يجب تجنبها

من التجارب التي تتكرر كثيرًا:

1) وضع النصوص داخل JSX مباشرة

هذا يجعل الترجمة صعبة لاحقًا.

2) نسيان اتجاه الصفحة للعربية

والنتيجة واجهة تبدو غريبة وغير طبيعية.

3) عدم ترجمة رسائل الخطأ

فيظهر جزء من الواجهة بلغة مختلفة.

4) تجاهل طول النصوص

فتنكسر الواجهة في الفرنسية أو العربية.

5) ربط اللغة بتصميم ثابت

بدل أن يكون التصميم مرنًا.

6) عدم تجهيز SEO متعدد اللغات

فتضيع فائدة النسخ اللغوية في البحث.

7) ترك أسماء الملفات والنصوص بدون تنظيم

ومع الوقت يصبح المشروع صعب الصيانة.

الخبر الجيد أن كل هذه الأخطاء يمكن تجنبها بسهولة إذا بدأ المشروع بخطة واضحة.

متى يكون next-intl خيارًا جيدًا؟

يكون مناسبًا جدًا عندما تريد:

  • تكاملًا جيدًا مع Next.js

  • التعامل مع server-side rendering

  • ملفات ترجمة منظمة

  • واجهة تطوير مريحة

  • أداءً جيدًا

  • دعمًا نظيفًا للترجمة داخل المكونات

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

نصيحة عملية من تجربة المشاريع

أفضل طريقة لبناء تطبيق متعدد اللغات ليست أن "تترجم كل شيء لاحقًا"، بل أن تُصمم اللغة كجزء من الهيكل منذ البداية.
كلما كان هذا القرار مبكرًا، كانت باقي الخطوات أسهل بكثير.

اجعل أسماء المفاتيح واضحة.
قسم الملفات بعناية.
اختبر العربية والإنجليزية بنفس الجدية.
لا تكتفِ بترجمة الكلمات، بل ترجم الشعور العام للواجهة أيضًا.

وهنا تكمن الحرفة الحقيقية: أن يشعر المستخدم أن التطبيق يفهمه، لا أنه فقط يعرض له كلمات مترجمة.

خاتمة

إنشاء تطبيق متعدد اللغات باستخدام Next.js i18n هو أحد القرارات التي ترفع قيمة المشروع بشكل واضح، ليس على مستوى التقنية فقط، بل على مستوى التجربة والاحترافية والتوسع. عندما تتعامل مع i18n بشكل صحيح، أنت لا تضيف طبقة ترجمة فحسب، بل تبني جسرًا بين المنتج والمستخدم بلغته الطبيعية وطريقته المألوفة في التصفح والقراءة والتفاعل.

الفكرة الأساسية بسيطة: افصل النصوص عن المكونات، نظّم اللغات في مسارات واضحة، راعِ اتجاه الصفحة، لا تنسَ الأرقام والتواريخ والـ SEO، واختبر كل لغة كما لو كانت هي اللغة الأساسية للمشروع. بهذه الطريقة يصبح التطبيق مرنًا، قابلًا للنمو، ومريحًا للمستخدم من أول زيارة.

#Next.js i18n #Next.js multilingual app #internationalization in Next.js #localization Next.js #Arabic RTL Next.js #language switcher Next.js #next-intl tutorial #multilingual website Next.js #translation files JSON #SEO multilingual Next.js

اشترك في نشرتنا البريدية

12k+

المشتركون

أسبوعيًا

التكرار

مجاني

دائمًا