الرئيسيةبيئة اختبارمعرض الأعمالتطبيقوثيقةمدونة
    • Englishالإنجليزية
      EN
    • Русскийالروسية
      RU
    • 日本語اليابانية
      JA
    • françaisالفرنسية
      FR
    • 한국어الكورية
      KO
    • 中文الصينية
      ZH
    • Españolالإسبانية
      ES
    • Deutschالألمانية
      DE
    • العربيةالعربية
      AR
    • Italianoالإيطالية
      IT
    • British Englishالإنجليزية البريطانية
      EN-GB
    • Portuguêsالبرتغالية
      PT
    • हिन्दीالهندية
      HI
    • Türkçeالتركية
      TR
    • polskiالبولندية
      PL
    • Indonesiaالإندونيسية
      ID
    • Tiếng Việtالفيتنامية
      VI
    • Українськаالأوكرانية
      UK
    /
    Alt+←
    ما هو التدويل (i18n)?
    SEO و التدويل
    دليل
    • i18n باستخدام next-i18next
    • i18n باستخدام next-intl
    استخدم Intlayer على الحل الخاص بك
    • أتمتة next-i18next
    • أتمتة react-i18next
    • أتمتة next-intl
    • أتمتة react-intl
    • أتمتة vue-i18n
    مقارنات
    • next-i18next vs next-intl vs Intlayer
    • react-i18next vs react-intl vs Intlayer
    الوثائق
    1. Blog
    2. Blog seo i18n nextjs
    Creation:2025-09-28Last update:2025-09-28
    استخدم هذه الصفحة والموفر AI الذي تريده
    ChatGPT
    Claude
    DeepSeek
    Google AI mode
    Gemini
    Perplexity
    Mistral
    Grok

    استخدم مساعدك المفضل للملخص واستخدم هذه الصفحة والموفر AI الذي تريده

    تمت ترجمة محتوى هذه الصفحة باستخدام الذكاء الاصطناعي.

    اعرض آخر نسخة المحتوى الأصلي باللغة الإنكليزية
    Edit this doc

    If you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.

    GitHub link to the documentation
    Copy

    Copy doc Markdown to clipboard

    تحسين محركات البحث والتدويل في Next.js: الترجمة ليست كافية

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

    في هذه المدونة، سنستكشف لماذا التدويل (i18n) هو قوة خارقة في تحسين محركات البحث (SEO) وكيفية تنفيذه بشكل صحيح في Next.js باستخدام next-intl و next-i18next و Intlayer.


    لماذا تحسين محركات البحث والتدويل

    إضافة لغات ليست مجرد تحسين تجربة المستخدم (UX). إنها أيضًا رافعة قوية لـ الرؤية العضوية. وإليك السبب:

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

    أفضل الممارسات لتحسين محركات البحث متعدد اللغات في Next.js

    إليك قائمة تحقق يجب على كل تطبيق متعدد اللغات تنفيذها:

    • تعيين علامات hreflang في <head>
      يساعد جوجل على فهم الإصدارات المتوفرة لكل لغة.

    • إدراج جميع الصفحات المترجمة في sitemap.xml
      استخدم مخطط xhtml حتى يتمكن الزواحف من العثور على البدائل بسهولة.

    • استبعاد المسارات الخاصة/المحلية في robots.txt
      على سبيل المثال، لا تسمح بفهرسة /dashboard، /fr/dashboard، /es/dashboard.

    • استخدام روابط محلية
      مثال: <a href="/fr/about">À propos</a> بدلاً من الربط إلى /about الافتراضي.

    هذه خطوات بسيطة, لكن تجاهلها قد يكلفك الرؤية.


    أمثلة على التنفيذ

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

    next-intl

    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { Metadata } from "next";import { locales, defaultLocale } from "@/i18n";import { getTranslations, unstable_setRequestLocale } from "next-intl/server";// دالة لإرجاع المسار المحلي بناءً على اللغةfunction localizedPath(locale: string, path: string) {return locale === defaultLocale ? path : `/${locale}${path}`;}export async function generateMetadata({params,}: {params: { locale: string };}): Promise<Metadata> {const { locale } = params;// الحصول على الترجمات الخاصة بالصفحة للغة المحددةconst t = await getTranslations({ locale, namespace: "about" });const url = "/about";const languages = Object.fromEntries(  locales.map((l) => [l, localizedPath(l, url)]));return {  title: t("title"),  description: t("description"),  alternates: {    canonical: localizedPath(locale, url),    languages: { ...languages, "x-default": url },  },};}// ... بقية كود الصفحة
    src/app/sitemap.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";// دالة لتنسيق المسار المحلي مع الأصلconst formatterLocalizedPath = (locale: string, path: string) =>locale === defaultLocale ? `${origin}${path}` : `${origin}/${locale}${path}`;export default function sitemap(): MetadataRoute.Sitemap {const aboutLanguages = Object.fromEntries(  locales.map((l) => [l, formatterLocalizedPath(l, "/about")]));return [  {    url: formatterLocalizedPath(defaultLocale, "/about"),    lastModified: new Date(),    changeFrequency: "شهري",    priority: 0.7,    alternates: { languages: aboutLanguages },  },];}
    src/app/robots.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const withAllLocales = (path: string) => [path,...locales.filter((l) => l !== defaultLocale).map((l) => `/${l}${path}`),];export default function robots(): MetadataRoute.Robots {const disallow = [  ...withAllLocales("/dashboard"),  ...withAllLocales("/admin"),];return {  rules: { userAgent: "*", allow: ["/"], disallow },  host: origin,  sitemap: `${origin}/sitemap.xml`,};}

    next-i18next

    i18n.config.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    export const locales = ["en", "fr"] as const;export type Locale = (typeof locales)[number];export const defaultLocale: Locale = "en";/** بادئة المسار مع اللغة ما لم تكن اللغة الافتراضية */export function localizedPath(locale: string, path: string) {return locale === defaultLocale ? path : `/${locale}${path}`;}/** مساعد URL مطلق */const ORIGIN = "https://example.com";export function abs(locale: string, path: string) {return `${ORIGIN}${localizedPath(locale, path)}`;}
    src/app/[locale]/about/layout.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { Metadata } from "next";import { locales, defaultLocale, localizedPath } from "@/i18n.config";export async function generateMetadata({params,}: {params: { locale: string };}): Promise<Metadata> {const { locale } = params;// استيراد ملف JSON الصحيح ديناميكيًاconst messages = (await import(`@/../public/locales/${locale}/about.json`))  .default;const languages = Object.fromEntries(  locales.map((l) => [l, localizedPath(l, "/about")]));return {  title: messages.title,  description: messages.description,  alternates: {    canonical: localizedPath(locale, "/about"),    languages: { ...languages, "x-default": "/about" },  },};}export default async function AboutPage() {return <h1>حول</h1>;}
    src/app/sitemap.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { MetadataRoute } from "next";import { locales, defaultLocale, abs } from "@/i18n.config";export default function sitemap(): MetadataRoute.Sitemap {const languages = Object.fromEntries(  locales.map((l) => [l, abs(l, "/about")]));return [  {    url: abs(defaultLocale, "/about"),    lastModified: new Date(),    changeFrequency: "monthly",    priority: 0.7,    alternates: { languages },  },];}
    src/app/robots.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { MetadataRoute } from "next";import { locales, defaultLocale, localizedPath } from "@/i18n.config";const ORIGIN = "https://example.com";const expandAllLocales = (path: string) => [localizedPath(defaultLocale, path),...locales  .filter((l) => l !== defaultLocale)  .map((l) => localizedPath(l, path)),];export default function robots(): MetadataRoute.Robots {const disallow = [  ...expandAllLocales("/dashboard"),  ...expandAllLocales("/admin"),];return {  rules: { userAgent: "*", allow: ["/"], disallow },  host: ORIGIN,  sitemap: `${ORIGIN}/sitemap.xml`,};}

    إنتلاير

    src/app/[locale]/about/layout.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    import { getIntlayer, getMultilingualUrls } from "intlayer";import type { Metadata } from "next";import type { LocalPromiseParams } from "next-intlayer";export const generateMetadata = async ({params,}: LocalPromiseParams): Promise<Metadata> => {const { locale } = await params;const metadata = getIntlayer("page-metadata", locale);/** * ينشئ كائن يحتوي على جميع الروابط لكل لغة. * * مثال: * ```ts *  getMultilingualUrls('/about'); * *  // يعيد *  // { *  //   en: '/about', *  //   fr: '/fr/about', *  //   es: '/es/about', *  // } * ``` */const multilingualUrls = getMultilingualUrls("/about");return {  ...metadata,  alternates: {    canonical: multilingualUrls[locale as keyof typeof multilingualUrls],    languages: { ...multilingualUrls, "x-default": "/about" },  },};};// ... بقية كود الصفحة
    src/app/sitemap.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const sitemap = (): MetadataRoute.Sitemap => [{  url: "https://example.com/about",  alternates: {    languages: { ...getMultilingualUrls("https://example.com/about") },  },},];
    src/app/robots.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const getAllMultilingualUrls = (urls: string[]) =>urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);// دالة لإنشاء قواعد ملف robots.txt مع السماح والوصول للروابط متعددة اللغاتconst robots = (): MetadataRoute.Robots => ({rules: {  userAgent: "*", // السماح لجميع عناكب البحث  allow: ["/"], // السماح بالوصول إلى الصفحة الرئيسية  disallow: getAllMultilingualUrls(["/dashboard"]), // منع الوصول إلى صفحات لوحة التحكم بكل اللغات},host: "https://example.com", // المضيف الأساسي للموقعsitemap: `https://example.com/sitemap.xml`, // رابط خريطة الموقع});export default robots;
    توفر Intlayer دالة getMultilingualUrls لإنشاء روابط متعددة اللغات لخريطة الموقع الخاصة بك.

    الخاتمة

    تحقيق التدويل (i18n) بشكل صحيح في Next.js لا يقتصر فقط على ترجمة النصوص، بل يتعلق بضمان أن محركات البحث والمستخدمين يعرفون بالضبط أي نسخة من المحتوى يجب عرضها. إعداد hreflang، خرائط الموقع، وقواعد robots هو ما يحول الترجمات إلى قيمة حقيقية لتحسين محركات البحث (SEO).

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

    وهنا يبرز دور Intlayer حقًا:

    فهو يأتي مع مساعدين مدمجين مثل getMultilingualUrls، مما يجعل دمج hreflang، خرائط الموقع، وملفات robots أمرًا سهلاً للغاية.

    تظل البيانات الوصفية مركزية بدلاً من التشتت عبر ملفات JSON أو الأدوات المخصصة.

    تم تصميمه خصيصًا لـ Next.js من الأساس، بحيث تقضي وقتًا أقل في تصحيح إعدادات التكوين ووقتًا أكثر في إطلاق المشاريع.

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

    ما هو التدويل (i18n)?
    Alt+→

    في هذه الصفحة

      المناقشات مجهولة الهوية ويتم مراجعتها بانتظام لمعالجة المشكلات الشائعة. لا تتردد في مشاركة أفكار الميزات أو التعليقات على الوثائق أو أي شيء يتعلق بـ Intlayer, نستخدم هذه المدخلات لتشكيل خارطة الطريق وتحسين المنتج.

      import type { Metadata } from "next";import { locales, defaultLocale } from "@/i18n";import { getTranslations, unstable_setRequestLocale } from "next-intl/server";// دالة لإرجاع المسار المحلي بناءً على اللغةfunction localizedPath(locale: string, path: string) {return locale === defaultLocale ? path : `/${locale}${path}`;}export async function generateMetadata({params,}: {params: { locale: string };}): Promise<Metadata> {const { locale } = params;// الحصول على الترجمات الخاصة بالصفحة للغة المحددةconst t = await getTranslations({ locale, namespace: "about" });const url = "/about";const languages = Object.fromEntries(  locales.map((l) => [l, localizedPath(l, url)]));return {  title: t("title"),  description: t("description"),  alternates: {    canonical: localizedPath(locale, url),    languages: { ...languages, "x-default": url },  },};}// ... بقية كود الصفحة
      import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";// دالة لتنسيق المسار المحلي مع الأصلconst formatterLocalizedPath = (locale: string, path: string) =>locale === defaultLocale ? `${origin}${path}` : `${origin}/${locale}${path}`;export default function sitemap(): MetadataRoute.Sitemap {const aboutLanguages = Object.fromEntries(  locales.map((l) => [l, formatterLocalizedPath(l, "/about")]));return [  {    url: formatterLocalizedPath(defaultLocale, "/about"),    lastModified: new Date(),    changeFrequency: "شهري",    priority: 0.7,    alternates: { languages: aboutLanguages },  },];}
      import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const withAllLocales = (path: string) => [path,...locales.filter((l) => l !== defaultLocale).map((l) => `/${l}${path}`),];export default function robots(): MetadataRoute.Robots {const disallow = [  ...withAllLocales("/dashboard"),  ...withAllLocales("/admin"),];return {  rules: { userAgent: "*", allow: ["/"], disallow },  host: origin,  sitemap: `${origin}/sitemap.xml`,};}
      export const locales = ["en", "fr"] as const;export type Locale = (typeof locales)[number];export const defaultLocale: Locale = "en";/** بادئة المسار مع اللغة ما لم تكن اللغة الافتراضية */export function localizedPath(locale: string, path: string) {return locale === defaultLocale ? path : `/${locale}${path}`;}/** مساعد URL مطلق */const ORIGIN = "https://example.com";export function abs(locale: string, path: string) {return `${ORIGIN}${localizedPath(locale, path)}`;}
      import type { Metadata } from "next";import { locales, defaultLocale, localizedPath } from "@/i18n.config";export async function generateMetadata({params,}: {params: { locale: string };}): Promise<Metadata> {const { locale } = params;// استيراد ملف JSON الصحيح ديناميكيًاconst messages = (await import(`@/../public/locales/${locale}/about.json`))  .default;const languages = Object.fromEntries(  locales.map((l) => [l, localizedPath(l, "/about")]));return {  title: messages.title,  description: messages.description,  alternates: {    canonical: localizedPath(locale, "/about"),    languages: { ...languages, "x-default": "/about" },  },};}export default async function AboutPage() {return <h1>حول</h1>;}
      import type { MetadataRoute } from "next";import { locales, defaultLocale, abs } from "@/i18n.config";export default function sitemap(): MetadataRoute.Sitemap {const languages = Object.fromEntries(  locales.map((l) => [l, abs(l, "/about")]));return [  {    url: abs(defaultLocale, "/about"),    lastModified: new Date(),    changeFrequency: "monthly",    priority: 0.7,    alternates: { languages },  },];}
      import type { MetadataRoute } from "next";import { locales, defaultLocale, localizedPath } from "@/i18n.config";const ORIGIN = "https://example.com";const expandAllLocales = (path: string) => [localizedPath(defaultLocale, path),...locales  .filter((l) => l !== defaultLocale)  .map((l) => localizedPath(l, path)),];export default function robots(): MetadataRoute.Robots {const disallow = [  ...expandAllLocales("/dashboard"),  ...expandAllLocales("/admin"),];return {  rules: { userAgent: "*", allow: ["/"], disallow },  host: ORIGIN,  sitemap: `${ORIGIN}/sitemap.xml`,};}
      import { getIntlayer, getMultilingualUrls } from "intlayer";import type { Metadata } from "next";import type { LocalPromiseParams } from "next-intlayer";export const generateMetadata = async ({params,}: LocalPromiseParams): Promise<Metadata> => {const { locale } = await params;const metadata = getIntlayer("page-metadata", locale);/** * ينشئ كائن يحتوي على جميع الروابط لكل لغة. * * مثال: * ```ts *  getMultilingualUrls('/about'); * *  // يعيد *  // { *  //   en: '/about', *  //   fr: '/fr/about', *  //   es: '/es/about', *  // } * ``` */const multilingualUrls = getMultilingualUrls("/about");return {  ...metadata,  alternates: {    canonical: multilingualUrls[locale as keyof typeof multilingualUrls],    languages: { ...multilingualUrls, "x-default": "/about" },  },};};// ... بقية كود الصفحة
      import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const sitemap = (): MetadataRoute.Sitemap => [{  url: "https://example.com/about",  alternates: {    languages: { ...getMultilingualUrls("https://example.com/about") },  },},];
      import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const getAllMultilingualUrls = (urls: string[]) =>urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);// دالة لإنشاء قواعد ملف robots.txt مع السماح والوصول للروابط متعددة اللغاتconst robots = (): MetadataRoute.Robots => ({rules: {  userAgent: "*", // السماح لجميع عناكب البحث  allow: ["/"], // السماح بالوصول إلى الصفحة الرئيسية  disallow: getAllMultilingualUrls(["/dashboard"]), // منع الوصول إلى صفحات لوحة التحكم بكل اللغات},host: "https://example.com", // المضيف الأساسي للموقعsitemap: `https://example.com/sitemap.xml`, // رابط خريطة الموقع});export default robots;