تدويل Next.js مع next-intl — دليل شامل لتعدد اللغات مع App Router

AI Bot
بواسطة AI Bot ·

جاري تحميل مشغل تحويل النص إلى كلام الصوتي...

بناء تطبيقات للجمهور العالمي يتطلب أكثر من مجرد ترجمة النصوص. تحتاج إلى توجيه مبني على اللغة، ودعم التخطيط من اليمين إلى اليسار (RTL)، وقواعد الجمع، وتنسيق التواريخ والأرقام، وتجربة مطور تتوسع مع نمو تطبيقك. أصبح next-intl الحل المفضل لتدويل تطبيقات Next.js مع App Router، حيث يوفر ترجمات آمنة الأنواع، ودعم مكونات الخادم، وتكامل سلس مع React Server Components.

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

المتطلبات الأساسية

قبل البدء، تأكد من توفر:

  • Node.js 20+ مثبت على جهازك
  • معرفة أساسية بـ Next.js App Router (التخطيطات، الصفحات، مكونات الخادم)
  • إلمام بـ TypeScript
  • محرر أكواد مثل VS Code مع إضافة i18n Ally (موصى به)

ما الذي ستبنيه

تطبيق Next.js متعدد اللغات يتضمن:

  • توجيه مبني على اللغة (/en/about، /ar/about، /fr/about)
  • اكتشاف تلقائي للغة وإعادة التوجيه
  • دعم تخطيط RTL للعربية
  • رسائل ترجمة آمنة الأنواع
  • محتوى ديناميكي مع الاستيفاء وصيغ الجمع
  • تنسيق تواريخ وأرقام حسب اللغة
  • مكون تبديل اللغات
  • تحسين SEO مع وسوم hreflang الصحيحة

الخطوة 1: إنشاء مشروع Next.js جديد

ابدأ بإنشاء تطبيق Next.js جديد:

npx create-next-app@latest my-i18n-app --typescript --tailwind --eslint --app --src-dir
cd my-i18n-app

ثبّت next-intl:

npm install next-intl

هيكل المشروع يجب أن يكون كالتالي:

my-i18n-app/
├── src/
│   ├── app/
│   │   ├── layout.tsx
│   │   └── page.tsx
│   └── ...
├── package.json
└── tsconfig.json

الخطوة 2: تحديد إعدادات التدويل

أنشئ ملف الإعدادات الذي يحدد اللغات المدعومة واللغة الافتراضية.

أنشئ src/i18n/config.ts:

export const locales = ["en", "ar", "fr"] as const;
export type Locale = (typeof locales)[number];
 
export const defaultLocale: Locale = "en";
 
export const localeNames: Record<Locale, string> = {
  en: "English",
  ar: "العربية",
  fr: "Français",
};
 
// اللغات التي تستخدم اتجاه النص من اليمين إلى اليسار
export const rtlLocales: Locale[] = ["ar"];
 
export function isRtlLocale(locale: Locale): boolean {
  return rtlLocales.includes(locale);
}

الخطوة 3: إنشاء ملفات الترجمة

أنشئ مجلد messages في جذر المشروع مع ملف JSON لكل لغة.

messages/en.json:

{
  "Metadata": {
    "title": "My International App",
    "description": "A fully internationalized Next.js application"
  },
  "Navigation": {
    "home": "Home",
    "about": "About",
    "contact": "Contact",
    "blog": "Blog"
  },
  "HomePage": {
    "title": "Welcome to Our Platform",
    "subtitle": "Build global applications with ease",
    "cta": "Get Started",
    "features": {
      "title": "Why Choose Us",
      "speed": "Lightning Fast",
      "speedDescription": "Optimized for performance across all regions",
      "i18n": "Built for Global",
      "i18nDescription": "Native support for {count, plural, =1 {# language} other {# languages}}",
      "secure": "Enterprise Security",
      "secureDescription": "Bank-grade encryption and compliance"
    },
    "stats": {
      "users": "{count, number} active users",
      "countries": "Available in {count, number} countries",
      "uptime": "{value}% uptime"
    }
  },
  "Common": {
    "loading": "Loading...",
    "error": "Something went wrong",
    "retry": "Try Again",
    "back": "Go Back",
    "lastUpdated": "Last updated: {date, date, medium}"
  }
}

messages/ar.json:

{
  "Metadata": {
    "title": "تطبيقي الدولي",
    "description": "تطبيق Next.js متعدد اللغات بالكامل"
  },
  "Navigation": {
    "home": "الرئيسية",
    "about": "من نحن",
    "contact": "اتصل بنا",
    "blog": "المدونة"
  },
  "HomePage": {
    "title": "مرحباً بكم في منصتنا",
    "subtitle": "بناء تطبيقات عالمية بسهولة",
    "cta": "ابدأ الآن",
    "features": {
      "title": "لماذا تختارنا",
      "speed": "سرعة فائقة",
      "speedDescription": "محسّن للأداء في جميع المناطق",
      "i18n": "مصمم للعالمية",
      "i18nDescription": "{count, plural, =1 {لغة واحدة} two {لغتان} few {# لغات} many {# لغة} other {# لغة}}",
      "secure": "أمان مؤسسي",
      "secureDescription": "تشفير بمستوى البنوك والامتثال"
    },
    "stats": {
      "users": "{count, number} مستخدم نشط",
      "countries": "متاح في {count, number} دولة",
      "uptime": "{value}٪ وقت التشغيل"
    }
  },
  "Common": {
    "loading": "جارٍ التحميل...",
    "error": "حدث خطأ ما",
    "retry": "حاول مرة أخرى",
    "back": "العودة",
    "lastUpdated": "آخر تحديث: {date, date, medium}"
  }
}

messages/fr.json:

{
  "Metadata": {
    "title": "Mon Application Internationale",
    "description": "Une application Next.js entièrement internationalisée"
  },
  "Navigation": {
    "home": "Accueil",
    "about": "À propos",
    "contact": "Contact",
    "blog": "Blog"
  },
  "HomePage": {
    "title": "Bienvenue sur Notre Plateforme",
    "subtitle": "Créez des applications mondiales facilement",
    "cta": "Commencer",
    "features": {
      "title": "Pourquoi Nous Choisir",
      "speed": "Ultra Rapide",
      "speedDescription": "Optimisé pour la performance dans toutes les régions",
      "i18n": "Conçu pour le Monde",
      "i18nDescription": "{count, plural, =1 {# langue} other {# langues}} prises en charge nativement",
      "secure": "Sécurité Entreprise",
      "secureDescription": "Chiffrement de niveau bancaire et conformité"
    },
    "stats": {
      "users": "{count, number} utilisateurs actifs",
      "countries": "Disponible dans {count, number} pays",
      "uptime": "{value} % de disponibilité"
    }
  },
  "Common": {
    "loading": "Chargement...",
    "error": "Une erreur est survenue",
    "retry": "Réessayer",
    "back": "Retour",
    "lastUpdated": "Dernière mise à jour : {date, date, medium}"
  }
}

لاحظ كيف تستخدم الترجمات العربية قواعد جمع ICU مع الفئات الخاصة بالعربية (two، few، many)، والتي يعالجها next-intl تلقائياً من خلال معيار ICU MessageFormat.

الخطوة 4: إعداد تهيئة الطلبات في next-intl

أنشئ src/i18n/request.ts — هذا هو الإعداد الأساسي الذي يستخدمه next-intl لحل الرسائل لكل طلب:

import { getRequestConfig } from "next-intl/server";
import { locales, type Locale } from "./config";
 
export default getRequestConfig(async ({ requestLocale }) => {
  // التحقق من أن اللغة المطلوبة مدعومة
  let locale = await requestLocale;
 
  if (!locale || !locales.includes(locale as Locale)) {
    locale = "en";
  }
 
  return {
    locale,
    messages: (await import(`../../../messages/${locale}.json`)).default,
    timeZone: "UTC",
    now: new Date(),
  };
});

الخطوة 5: تهيئة Next.js مع إضافة next-intl

حدّث ملف next.config.ts ليتضمن إضافة next-intl:

import createNextIntlPlugin from "next-intl/plugin";
 
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
 
const nextConfig = {
  // إعدادات Next.js الحالية
};
 
export default withNextIntl(nextConfig);

الخطوة 6: إعداد التوجيه المبني على اللغة

أعد هيكلة مجلد app لاستخدام مقطع ديناميكي [locale]. هذا هو أساس التوجيه المبني على اللغة.

src/app/
├── [locale]/
│   ├── layout.tsx        # التخطيط الجذري مع اللغة
│   ├── page.tsx           # الصفحة الرئيسية
│   ├── about/
│   │   └── page.tsx       # صفحة من نحن
│   └── contact/
│       └── page.tsx       # صفحة التواصل
├── layout.tsx             # تخطيط جذري بسيط (بدون لغة)
└── not-found.tsx          # صفحة 404 عامة

أنشئ التخطيط الجذري src/app/layout.tsx (بسيط — يمرر الأبناء فقط):

import { ReactNode } from "react";
 
type Props = {
  children: ReactNode;
};
 
export default function RootLayout({ children }: Props) {
  return children;
}

الآن أنشئ src/app/[locale]/layout.tsx — هنا يوجد منطق التخطيط الحقيقي:

import { ReactNode } from "react";
import { notFound } from "next/navigation";
import { NextIntlClientProvider } from "next-intl";
import { getMessages, setRequestLocale } from "next-intl/server";
import { locales, type Locale, isRtlLocale } from "@/i18n/config";
import "./globals.css";
 
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();
  }
 
  // تفعيل العرض الثابت
  setRequestLocale(locale);
 
  // الحصول على جميع الرسائل للغة الحالية
  const messages = await getMessages();
 
  const dir = isRtlLocale(locale as Locale) ? "rtl" : "ltr";
 
  return (
    <html lang={locale} dir={dir}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

النقاط الرئيسية:

  • generateStaticParams يفعّل التوليد الثابت لجميع اللغات
  • setRequestLocale يفعّل العرض الثابت (مطلوب للتوليد الثابت)
  • NextIntlClientProvider يجعل الترجمات متاحة لمكونات العميل
  • خاصية dir تُعيّن ديناميكياً — rtl للعربية، ltr للبقية

الخطوة 7: إنشاء الـ Middleware لاكتشاف اللغة

أنشئ src/middleware.ts في جذر المصدر:

import createMiddleware from "next-intl/middleware";
import { locales, defaultLocale } from "./i18n/config";
 
export default createMiddleware({
  locales,
  defaultLocale,
  // إعادة التوجيه إلى مسارات مسبوقة باللغة
  localePrefix: "always",
  // اكتشاف اللغة من رأس Accept-Language
  localeDetection: true,
});
 
export const config = {
  // مطابقة جميع المسارات باستثناء API والملفات الثابتة
  matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
};

مع هذا الـ Middleware:

  • زيارة / تعيد التوجيه إلى /en (أو اللغة المكتشفة)
  • زيارة /about تعيد التوجيه إلى /en/about
  • زيارة /ar/about تعرض النسخة العربية مباشرة
  • يُستخدم رأس Accept-Language للزوار الجدد

الخطوة 8: بناء الصفحة الرئيسية مع الترجمات

أنشئ src/app/[locale]/page.tsx:

import { useTranslations } from "next-intl";
import { setRequestLocale } from "next-intl/server";
 
type Props = {
  params: Promise<{ locale: string }>;
};
 
export default async function HomePage({ params }: Props) {
  const { locale } = await params;
  setRequestLocale(locale);
 
  return <HomeContent />;
}
 
function HomeContent() {
  const t = useTranslations("HomePage");
 
  return (
    <main className="min-h-screen">
      {/* قسم البطل */}
      <section className="flex flex-col items-center justify-center px-4 py-24 text-center">
        <h1 className="mb-4 text-5xl font-bold tracking-tight">
          {t("title")}
        </h1>
        <p className="mb-8 max-w-2xl text-xl text-gray-600">
          {t("subtitle")}
        </p>
        <button className="rounded-lg bg-blue-600 px-8 py-3 text-lg font-semibold text-white transition hover:bg-blue-700">
          {t("cta")}
        </button>
      </section>
 
      {/* قسم الميزات */}
      <section className="mx-auto max-w-6xl px-4 py-16">
        <h2 className="mb-12 text-center text-3xl font-bold">
          {t("features.title")}
        </h2>
        <div className="grid gap-8 md:grid-cols-3">
          <FeatureCard
            title={t("features.speed")}
            description={t("features.speedDescription")}
          />
          <FeatureCard
            title={t("features.i18n")}
            description={t("features.i18nDescription", { count: 3 })}
          />
          <FeatureCard
            title={t("features.secure")}
            description={t("features.secureDescription")}
          />
        </div>
      </section>
 
      {/* قسم الإحصائيات */}
      <section className="bg-gray-50 px-4 py-16 dark:bg-gray-900">
        <div className="mx-auto grid max-w-4xl gap-8 text-center md:grid-cols-3">
          <div>
            <p className="text-4xl font-bold text-blue-600">
              {t("stats.users", { count: 50000 })}
            </p>
          </div>
          <div>
            <p className="text-4xl font-bold text-blue-600">
              {t("stats.countries", { count: 120 })}
            </p>
          </div>
          <div>
            <p className="text-4xl font-bold text-blue-600">
              {t("stats.uptime", { value: 99.9 })}
            </p>
          </div>
        </div>
      </section>
    </main>
  );
}
 
function FeatureCard({
  title,
  description,
}: {
  title: string;
  description: string;
}) {
  return (
    <div className="rounded-xl border p-6 transition hover:shadow-lg">
      <h3 className="mb-2 text-xl font-semibold">{title}</h3>
      <p className="text-gray-600">{description}</p>
    </div>
  );
}

لاحظ كيف يعمل useTranslations في مكونات الخادم — لا حاجة لتوجيه "use client". تدعم دالة t() صيغة ICU MessageFormat لقواعد الجمع وتنسيق الأرقام تلقائياً.

الخطوة 9: بناء مكون تبديل اللغات

أنشئ src/components/LanguageSwitcher.tsx:

"use client";
 
import { useLocale, useTranslations } from "next-intl";
import { useRouter, usePathname } from "next/navigation";
import { locales, localeNames, type Locale } from "@/i18n/config";
import { useTransition } from "react";
 
export default function LanguageSwitcher() {
  const locale = useLocale();
  const router = useRouter();
  const pathname = usePathname();
  const t = useTranslations("LanguageSwitcher");
  const [isPending, startTransition] = useTransition();
 
  function handleLocaleChange(newLocale: string) {
    // استبدال مقطع اللغة الحالي في المسار
    const segments = pathname.split("/");
    segments[1] = newLocale;
    const newPathname = segments.join("/");
 
    startTransition(() => {
      router.replace(newPathname);
    });
  }
 
  return (
    <div className="relative">
      <label htmlFor="locale-select" className="sr-only">
        {t("label")}
      </label>
      <select
        id="locale-select"
        value={locale}
        onChange={(e) => handleLocaleChange(e.target.value)}
        disabled={isPending}
        className="appearance-none rounded-lg border bg-white px-4 py-2 text-sm font-medium shadow-sm transition hover:border-blue-500 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 disabled:opacity-50 dark:bg-gray-800 rtl:text-right"
        aria-label={t("current", { language: localeNames[locale as Locale] })}
      >
        {locales.map((loc) => (
          <option key={loc} value={loc}>
            {localeNames[loc]}
          </option>
        ))}
      </select>
      {isPending && (
        <span className="absolute end-2 top-1/2 -translate-y-1/2">
          <span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-blue-600 border-t-transparent" />
        </span>
      )}
    </div>
  );
}

هذا المكون:

  • يستخدم useTransition لتبديل سلس بين اللغات دون حجب واجهة المستخدم
  • يستبدل مقطع اللغة في مسار URL
  • يعرض مؤشر تحميل أثناء الانتقال
  • يستخدم rtl:text-right لمحاذاة RTL الصحيحة
  • متاح بالكامل مع وسوم ARIA المناسبة

الخطوة 10: التعامل مع تخطيطات RTL باستخدام Tailwind CSS

يتضمن Tailwind CSS v3.3+ دعم RTL مدمج باستخدام المتغيرات rtl: و ltr:. بما أننا عيّنّا خاصية dir في التخطيط، تعمل هذه المتغيرات تلقائياً.

إليك الأنماط الرئيسية لتخطيطات واعية بـ RTL:

{/* استخدم الخصائص المنطقية بدل الفيزيائية */}
{/* بدل ml-4 / mr-4، استخدم ms-4 / me-4 */}
<div className="ms-4">هامش في البداية (يسار في LTR، يمين في RTL)</div>
<div className="me-4">هامش في النهاية (يمين في LTR، يسار في RTL)</div>
 
{/* بدل pl-4 / pr-4، استخدم ps-4 / pe-4 */}
<div className="ps-6 pe-2">حشو البداية والنهاية</div>
 
{/* بدل left-0 / right-0، استخدم start-0 / end-0 */}
<div className="absolute start-0">موضع في البداية</div>
 
{/* للأيقونات الاتجاهية التي تحتاج للقلب */}
<svg className="rtl:rotate-180">→</svg>
 
{/* محاذاة النص */}
<p className="text-start">محاذاة لبداية اتجاه النص</p>
 
{/* Flexbox و Grid - واعية تلقائياً بـ RTL */}
<div className="flex gap-4">العناصر تتدفق بشكل صحيح في كلا الاتجاهين</div>

القاعدة الأهم: استخدم الخصائص المنطقية (start/end) بدل الفيزيائية (left/right). هذا التغيير الواحد يعالج 90% من مشاكل تخطيط RTL.

الخطوة 11: إضافة أمان الأنواع للترجمات

من أقوى ميزات next-intl هي الترجمات الآمنة الأنواع. أنشئ ملف تصريح أنواع للحصول على الإكمال التلقائي والتحقق في وقت الترجمة.

أنشئ src/i18n/types.ts:

import en from "../../messages/en.json";
 
// استخدام الرسائل الإنجليزية كمصدر حقيقة للأنواع
type Messages = typeof en;
 
declare global {
  interface IntlMessages extends Messages {}
}

الآن عند استخدام t("HomePage.features.speed")، سيقوم TypeScript بـ:

  • إكمال مفاتيح الرسائل المتاحة تلقائياً
  • عرض خطأ إذا أشرت إلى مفتاح غير موجود
  • التحقق من معاملات الاستيفاء

الخطوة 12: تنسيق التواريخ والأرقام والأوقات النسبية

يوفر next-intl أدوات تنسيق واعية باللغة تستفيد من واجهة Intl.

import { useFormatter, useNow, useTranslations } from "next-intl";
 
function StatsSection() {
  const format = useFormatter();
  const now = useNow();
  const t = useTranslations("Common");
 
  // تنسيق الأرقام
  const revenue = format.number(1234567.89, {
    style: "currency",
    currency: "USD",
  });
  // en: "$1,234,567.89" | ar: "١٬٢٣٤٬٥٦٧٫٨٩ US$" | fr: "1 234 567,89 $US"
 
  // تنسيق التاريخ
  const date = format.dateTime(new Date("2026-03-18"), {
    year: "numeric",
    month: "long",
    day: "numeric",
  });
  // en: "March 18, 2026" | ar: "١٨ مارس ٢٠٢٦" | fr: "18 mars 2026"
 
  // الوقت النسبي
  const lastWeek = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
  const relative = format.relativeTime(lastWeek);
  // en: "7 days ago" | ar: "قبل ٧ أيام" | fr: "il y a 7 jours"
 
  return (
    <div>
      <p>{revenue}</p>
      <p>{date}</p>
      <p>{relative}</p>
      <p>{t("lastUpdated", { date: new Date() })}</p>
    </div>
  );
}

الخطوة 13: التعامل مع المحتوى الديناميكي والنص المنسق

يدعم next-intl تنسيق النص المنسق، مما يسمح لك بتضمين مكونات داخل نصوص مترجمة.

في ملفات الرسائل:

{
  "RichText": {
    "welcome": "مرحباً بكم في <bold>منصتنا</bold>. اقرأ <link>شروط الخدمة</link>.",
    "highlight": "هذه الميزة <highlight>جديدة</highlight> ومتاحة لمستخدمي <badge>Pro</badge>."
  }
}

في المكون:

import { useTranslations } from "next-intl";
 
function WelcomeMessage() {
  const t = useTranslations("RichText");
 
  return (
    <p>
      {t.rich("welcome", {
        bold: (chunks) => <strong>{chunks}</strong>,
        link: (chunks) => (
          <a href="/terms" className="text-blue-600 underline">
            {chunks}
          </a>
        ),
      })}
    </p>
  );
}

هذا النهج يبقي ملفات الترجمة نظيفة مع السماح للمترجمين بالتحكم في ترتيب الكلمات — وهو أمر حاسم للغات مثل العربية حيث يختلف هيكل الجملة بشكل كبير عن الإنجليزية.

الخطوة 14: إضافة SEO مع بيانات وصفية واعية باللغة

أنشئ بيانات وصفية صحيحة لكل لغة، بما في ذلك روابط hreflang البديلة.

حدّث src/app/[locale]/layout.tsx:

import { getTranslations } from "next-intl/server";
import { locales } from "@/i18n/config";
import type { Metadata } from "next";
 
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" });
 
  // توليد روابط بديلة لجميع اللغات
  const languages: Record<string, string> = {};
  for (const loc of locales) {
    languages[loc] = `https://example.com/${loc}`;
  }
 
  return {
    title: {
      default: t("title"),
      template: `%s | ${t("title")}`,
    },
    description: t("description"),
    alternates: {
      languages,
    },
    openGraph: {
      title: t("title"),
      description: t("description"),
      locale: locale,
      alternateLocale: locales.filter((l) => l !== locale),
    },
  };
}

الخطوة 15: اختبار تطبيقك المتعدد اللغات

شغّل خادم التطوير وتحقق من كل شيء:

npm run dev

اختبر هذه السيناريوهات:

  1. إعادة التوجيه الافتراضية: زر http://localhost:3000 — يجب أن تتم إعادة التوجيه إلى /en
  2. تبديل اللغة: استخدم مبدّل اللغات للتنقل بين الإنجليزية والعربية والفرنسية
  3. تخطيط RTL: بدّل إلى العربية وتحقق من أن التخطيط يعكس بشكل صحيح
  4. الوصول المباشر: زر http://localhost:3000/ar مباشرة
  5. استيفاء الترجمة: تحقق من أن الأرقام والتواريخ وصيغ الجمع تُعرض بشكل صحيح
  6. معالجة 404: زر http://localhost:3000/de (لغة غير مدعومة)

الخطوة 16: نصائح تحسين الإنتاج

تفعيل العرض الثابت

للحصول على أفضل أداء، فعّل التوليد الثابت لجميع صفحاتك المحلية. أضف setRequestLocale في أعلى كل صفحة وتخطيط يستخدم الترجمات:

import { setRequestLocale } from "next-intl/server";
 
export default async function Page({ params }: Props) {
  const { locale } = await params;
  setRequestLocale(locale);
 
  // ... بقية المكون
}

تقسيم الرسائل

للتطبيقات الكبيرة، تجنب تحميل جميع الترجمات في كل صفحة. قسّم الرسائل حسب مساحة الاسم لتحسين حجم الحزمة من جانب العميل.

تحليل الحزمة

تحقق من أن حزم الترجمة لا تضخم JavaScript من جانب العميل:

npx @next/bundle-analyzer

مكونات الخادم التي تستخدم useTranslations لا تضيف ترجمات إلى حزمة العميل — فقط المكونات التي تحمل "use client" وتستخدم NextIntlClientProvider تتضمن الترجمات في حزمة العميل.

استكشاف الأخطاء

مشكلة شائعة: "Unable to find next-intl locale"

عادة تعني أن الـ Middleware لا يطابق مساراتك. تحقق من:

  1. نمط matcher في middleware.ts صحيح
  2. ملف الـ Middleware في src/middleware.ts (ليس داخل app/)
  3. أعدت تشغيل خادم التطوير بعد إضافة الـ Middleware

مشكلة شائعة: عدم تطابق الهيدريشن مع RTL

إذا رأيت تحذيرات هيدريشن عند التبديل بين LTR و RTL:

  1. تأكد أن dir و lang معيّنان على عنصر html في تخطيط الخادم
  2. أضف suppressHydrationWarning لوسم html إذا كنت تستخدم مزود سمة

الخطوات التالية

الآن بعد أن لديك أساس i18n قوي، فكّر في:

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

الخلاصة

لقد بنيت تطبيق Next.js متعدد اللغات جاهز للإنتاج مع next-intl يدعم:

  • ترجمات آمنة الأنواع مع إكمال تلقائي
  • توجيه مبني على اللغة مع اكتشاف تلقائي
  • دعم تخطيط RTL للعربية باستخدام خصائص Tailwind CSS المنطقية
  • صيغة ICU MessageFormat لقواعد الجمع والتنسيق
  • بيانات وصفية محسنة لـ SEO مع وسوم hreflang
  • تجربة تبديل لغات سلسة

مزيج Next.js App Router و next-intl يوفر واحدة من أفضل تجارب المطورين لبناء تطبيقات متعددة اللغات. النهج القائم على الخادم يعني أن الترجمات لا تضخم حزمة العميل، ودعم صيغة ICU MessageFormat يعالج تفاصيل اللغات المختلفة — من قواعد الجمع العربية إلى تنسيق الأرقام الفرنسية — بدون أي منطق مخصص.

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


هل تريد قراءة المزيد من الدروس التعليمية؟ تحقق من أحدث درس تعليمي لدينا على بناء مهام خلفية للإنتاج باستخدام Trigger.dev v3 و Next.js.

ناقش مشروعك معنا

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

دعنا نجد أفضل الحلول لاحتياجاتك.

مقالات ذات صلة

إضافة المصادقة لتطبيق Next.js 15 باستخدام Auth.js v5: البريد الإلكتروني وOAuth والتحكم بالأدوار

تعلم كيفية إضافة نظام مصادقة جاهز للإنتاج لتطبيق Next.js 15 باستخدام Auth.js v5. يغطي هذا الدليل الشامل تسجيل الدخول عبر Google OAuth وبيانات الاعتماد بالبريد الإلكتروني وكلمة المرور والمسارات المحمية والتحكم بالوصول حسب الأدوار.

30 د قراءة·

بناء روبوت دردشة ذكاء اصطناعي محلي باستخدام Ollama و Next.js: الدليل الشامل

ابنِ روبوت دردشة ذكاء اصطناعي خاص يعمل بالكامل على جهازك المحلي باستخدام Ollama و Next.js. يغطي هذا الدليل العملي التثبيت والبث المباشر واختيار النماذج وبناء واجهة دردشة جاهزة للإنتاج — كل ذلك دون إرسال بياناتك إلى السحابة.

25 د قراءة·