Command Palette

Search for a command to run...

كيفية إضافة Hreflang في WordPress (بدون مكون إضافي)

كيفية إضافة Hreflang في WordPress (بدون مكون إضافي)

T
Toolz Team
|Aug 23, 2026|13 دقيقة قراءة

جزء من مجموعة أدوات تحسين محركات البحث

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

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

يجلس تحت دليل هريفلانج الكاملبجانب الإصدار التالي.js.

ليرة تركية؛DR: لا يصدر WordPress hreflang من تلقاء نفسه. خطاف wp_head، قم بالتكرار فوق خريطة محلية، وقم بطباعة المجموعة المتبادلة الكاملة بما في ذلك x-default في كل صفحة في المجموعة. بناء عناوين URL باستخدام home_url() أو أصل لكل لغة بدلاً من الترميز الثابت، احتفظ بكل صفحة &#39؛s المرجعية الذاتية الأساسية، وتأكد من عدم وجود مكون إضافي لتحسين محركات البحث يصدر بالفعل كتلة منافسة. للإعدادات متعددة المواقع، get_sites() يعطيك الخريطة؛ بالنسبة لموقع واحد يحتوي على صفحات مترجمة، يكفي تعيين حقل ما بعد التعريف.

هل يضيف WordPress hreflang بمفرده؟

يقوم No. Core بإخراج رابط أساسي ورابط قصير وروابط تغذية وREST متنوعة من wp_head، ولكن لا شيء عن بدائل اللغة. get_locale() يعرف اللغة التي يوجد بها الموقع ولا يوجد أي جزء من النواة يحول ذلك إلى تعليق توضيحي.

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

الإصدار الأدنى: مصفوفة واحدة وخطاف واحد

بالنسبة لموقع تكون ترجماته عبارة عن مجموعة صغيرة معروفة من بادئات URL

<?php
/**
 * Hreflang annotations for a hand-translated multilingual site.
 * Drop in the theme's functions.php or a small mu-plugin.
 */
function mysite_hreflang_map(): array {
    return [
        'en-US'     => 'https://example.com',
        'de-DE'     => 'https://example.com/de',
        'es-ES'     => 'https://example.com/es',
        'x-default' => 'https://example.com',
    ];
}

function mysite_print_hreflang(): void {
    $path = mysite_current_path(); // e.g. '/pricing/'
    foreach ( mysite_hreflang_map() as $hreflang => $origin ) {
        printf(
            '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
            esc_attr( $hreflang ),
            esc_url( untrailingslashit( $origin ) . $path )
        );
    }
}
add_action( 'wp_head', 'mysite_print_hreflang', 1 );

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

يقوم بطباعة المجموعة الكاملة في كل صفحة، بما في ذلك الصفحة &#39؛s لغتهم الخاصة. المعاملة بالمثل مرضية من الناحية الهيكلية: لا يوجد فرع يمكنه حذف الإدخال، لأنه لا يوجد فرع على الإطلاق.

x-default هو صف في نفس المصفوفة، لذلك ينبعث من نفس الحلقة ولا يمكن نسيانه في قالب واحد.

تم الهروب من عناوين URL. esc_url على href في wp_head ليس اختياريًا، ويتم تمرير قيم hreflang esc_attr لنفس السبب.

العمل على المسار الحالي

المسار هو الجزء الذي يحتاج إلى رعاية، لأنه يجب أن يكون المسار داخل اللغة، وليس عنوان URI للطلب الكامل.

function mysite_current_path(): string {
    $home = wp_parse_url( home_url(), PHP_URL_PATH ) ?: '';
    $req  = wp_parse_url( $_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH ) ?: '/';

    // Strip the language prefix this install is served under.
    if ( $home && str_starts_with( $req, $home ) ) {
        $req = substr( $req, strlen( $home ) );
    }
    return user_trailingslashit( '/' . ltrim( $req, '/' ) );
}

إذا كان التثبيت الألماني الخاص بك يعيش في example.com/de والزائر على example.com/de/preise/، يعود هذا /preise/، والتي تصبح مسبوقة بعد ذلك بكل لغة &#39؛s الأصل. معالجة الشرطة المائلة الخلفية مهمة أكثر مما تبدو /preise و /preise/ هي عناوين URL مختلفة، والإشارة إلى تعليق توضيحي في النموذج غير القانوني هي عدم تطابق الشرطة المائلة وينتج عن ذلك أخطاء في علامة الإرجاع في مجموعة تبدو مثالية. user_trailingslashit يحترم إعداد الرابط الثابت للموقع &#39؛، لذا فهو يطابق أي شيء يقدمه WordPress.

الافتراض هنا هو أن عناوين URL المترجمة متوازية من الناحية الهيكلية /pricing/ خرائط ل /de/pricing/. إذا تمت ترجمة slugs الألمانية الخاصة بك أيضًا، /de/preise/هذه الوظيفة ليست كافية وتحتاج إلى تعيين واضح، وهو القسم التالي.

عندما يختلف slugs: تعيين ما بعد التعريف

تعد الترجمة slugs أفضل للمستخدمين وللتصنيف، وتعني أنه لا يمكن اشتقاق المسار. تخزين النظراء بدلا من ذلك:

/**
 * Meta key holds a JSON map: {"de-DE": 123, "es-ES": 456} of post IDs,
 * or absolute URLs for external installs.
 */
function mysite_alternates_for( int $post_id ): array {
    $raw = get_post_meta( $post_id, '_hreflang_alternates', true );
    $map = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $map ) ) {
        return [];
    }

    $out = [];
    foreach ( $map as $hreflang => $target ) {
        $url = is_numeric( $target ) ? get_permalink( (int) $target ) : $target;
        if ( $url ) {
            $out[ $hreflang ] = $url;
        }
    }
    return $out;
}

ثم ال wp_head يقوم رد الاتصال بدمج الإدخال الحالي الخاص بالمنشور &#39؛ في تلك الخريطة قبل الطباعة، وبالتالي تكون المجموعة كاملة ومرجعية ذاتيًا

function mysite_print_hreflang_mapped(): void {
    if ( ! is_singular() ) {
        return;
    }
    $post_id = get_queried_object_id();
    $alts    = mysite_alternates_for( $post_id );
    if ( count( $alts ) < 1 ) {
        return; // Nothing to declare: a one-entry cluster is not a cluster.
    }

    $alts[ mysite_hreflang_for_locale( get_locale() ) ] = get_permalink( $post_id );
    $alts['x-default'] = $alts['en-US'] ?? get_permalink( $post_id );

    foreach ( $alts as $hreflang => $url ) {
        printf(
            '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
            esc_attr( $hreflang ),
            esc_url( $url )
        );
    }
}

تكلفة الصيانة هي أنه يجب كتابة التعريف على كلا الجانبين. واجهة المستخدم الإدارية مع منتقي النظير هي الحل الصادق؛ أمر WP-CLI الذي يملأ الخريطة من ملف CSV هو الأمر السريع. ما لا يجب عليك فعله هو ملؤها على جانب واحد فقط، لأن الإعلان أحادي الاتجاه هو بالضبط علامة العودة المفقودة فشل.

Multisite: استمد الخريطة من الشبكة

إذا كانت كل لغة موقعًا في شبكة متعددة المواقع، فإن الخريطة موجودة بالفعل

function mysite_network_hreflang_map(): array {
    $map = [];
    foreach ( get_sites( [ 'number' => 50 ] ) as $site ) {
        switch_to_blog( (int) $site->blog_id );
        $map[ mysite_hreflang_for_locale( get_locale() ) ] = home_url();
        restore_current_blog();
    }
    return $map;
}

قم بتخزين النتيجة في صورة عابرة؛ switch_to_blog في أ wp_head يعد ربط كل طلب تكلفة حقيقية على شبكة من أي حجم. وكن حذرا مع get_locale() تحت switch_to_blog: إنه يعكس الموقع الذي تم تبديله فقط إذا كان الموقع &#39؛s WPLANG يتم تعيين الخيار، وهي الحالة المعتادة ولكنها غير مضمونة على الشبكات التي تم تكوينها من خلال ثابت.

ما هو النهج الذي يناسب موقعك؟

يثبت يقترب جهد
الموازي slugs، عدد قليل من اللغات مصفوفة ثابتة بالإضافة إلى المسار المشتق الأدنى؛ وظيفة واحدة
مترجم slugs خريطة نظير ما بعد الفوقية معتدل؛ يحتاج إلى سير عمل التحرير
موقع متعدد المواقع، موقع واحد لكل لغة يشتق من get_sites()، مخبأة كود منخفض، يحتاج إلى انضباط ذاكرة التخزين المؤقت
Polylang أو WPML لإدارة الترجمات استخدم البرنامج المساعد &#39؛s الإخراج التحقق من أنه ينبعث x الافتراضي
البرنامج المساعد لتحسين محركات البحث مع حقول الترجمة اليدوية استخدمه، ثم تحقق من الرأس المعروض شاهد الكتل المكررة

وضع hreflang في خريطة الموقع بدلاً من ذلك

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

المسار العملي عبارة عن خريطة موقع مستقلة يتم تقديمها من خلال قاعدة إعادة الكتابة بدلاً من القتال معها wp_sitemaps:

add_action( 'init', function () {
    add_rewrite_rule( '^sitemap-i18n\.xml$', 'index.php?mysite_i18n_sitemap=1', 'top' );
} );

add_filter( 'query_vars', fn( $vars ) => array_merge( $vars, [ 'mysite_i18n_sitemap' ] ) );

add_action( 'template_redirect', function () {
    if ( ! get_query_var( 'mysite_i18n_sitemap' ) ) {
        return;
    }
    header( 'Content-Type: application/xml; charset=UTF-8' );

    echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
    echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" ';
    echo 'xmlns:xhtml="http://www.w3.org/1999/xhtml">' . "\n";

    foreach ( mysite_translated_paths() as $path ) {
        echo "  <url>\n    <loc>" . esc_url( home_url( $path ) ) . "</loc>\n";
        foreach ( mysite_hreflang_map() as $hreflang => $origin ) {
            printf(
                '    <xhtml:link rel="alternate" hreflang="%s" href="%s"/>' . "\n",
                esc_attr( $hreflang ),
                esc_url( untrailingslashit( $origin ) . $path )
            );
        }
        echo "  </url>\n";
    }
    echo '</urlset>';
    exit;
} );

شيئان للوصول إلى الحق. ال xmlns:xhtml إعلان بشأن <urlset> مطلوب؛ وبدونه يتم تجاهل الإدخالات البديلة ويستمر التحقق من صحة الملف كخريطة موقع، وهو نمط الفشل الصامت مرة أخرى. وإعادة كتابة القواعد مرة واحدة بعد إضافة القاعدة، عند التنشيط وليس عند كل طلب.

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

مشكلة الكتلة المكررة

هذا هو الفشل الخاص بـ WordPress وهو الفشل الذي يستحق التحقق منه قبل كتابة أي تعليمات برمجية. تنبعث العديد من المكونات الإضافية من hreflang، ويمكن أن يكون أكثر من واحد نشطًا في وقت واحد. سيتم طباعة مكون إضافي لتحسين محركات البحث تم تكوينه بعلاقات ترجمة، ومكون إضافي متعدد اللغات، ووظيفة السمة الجديدة الخاصة بك بكل سرور wp_headوالنتيجة هي كتلتين أو ثلاث كتل بمحتويات مختلفة وربما اثنتين x-default إدخالات.

إن تعامل Google&#39؛s مع التعليقات التوضيحية المتناقضة داخل صفحة واحدة هو عدم الثقة بها. لذا فإن الموقع الذي لا يحتوي على hreflang والذي يحتوي الآن على ثلاث كتل يمكن أن يكون أسوأ حالًا من ذي قبل.

تحقق من الإخراج المعروض بدلاً من إعدادات البرنامج المساعد

curl -s https://example.com/de/preise/ | grep 'rel="alternate"' | grep hreflang

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

// Example shape: find the real hook and callback with `has_action`.
add_action( 'init', function () {
    remove_action( 'wp_head', 'some_plugin_hreflang_output', 10 );
} );

العلامات الأساسية، وعدم كسرها

ينبعث ووردبريس rel="canonical" للحصول على وجهات نظر فردية من خلال rel_canonical()، ويشير إلى الرابط الثابت الخاص بـ post&#39؛، وهو ما تريده. تستبدله معظم المكونات الإضافية لتحسين محركات البحث بالرابط الخاص بها، والذي يكون أيضًا مرجعيًا ذاتيًا بشكل افتراضي.

القاعدة من هريفلانج مقابل الكنسي ينطبق دون تغيير: كل صفحة تقوم بتدوين نفسها، ويصف hreflang العلاقات بين تلك الصفحات الأساسية. الطريقة التي تنكه WordPress لكسره هي إعداد مكون إضافي يقوم بتدوين المنشورات المترجمة إلى &quot؛primary&quot؛ إصدار اللغة، الذي يُقرأ كخيار معقول لإلغاء البيانات المكررة ويزيل ترجماتك بهدوء من الفهرس. إذا كان الإعداد يعرض إضفاء الطابع القانوني على اللغات، فاتركه.

التحقق من النتيجة

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

لصق المجموعة في مولد هريفلانج مجاني للتحقق من صحة الرموز: يقوم بفحص كل قيمة مقابل شكل ISO، ووضع علامات على العلامات الفرعية غير المعروفة مثل en-UK، يلتقط التكرارات، ويرفض عناوين URL النسبية، ويحذر عندما لا تحتوي المجموعة على أي احتياطي. يتم تشغيله بالكامل في المتصفح، وهو أمر مهم عندما تصف عناوين URL موقع عميل غير عام بعد. ثم استخدم تقرير الاستهداف الدولي Search Console&#39؛s للفحص المستمر، و دليل أداة هريفلانج لسير العمل من حوله.

الأسئلة المتداولة

هل يضيف WordPress علامات hreflang تلقائيًا؟

يصدر No. Core رابطًا أساسيًا وروابط رئيسية مختلفة ولكن لا شيء يتعلق ببدائل اللغة. يأتي Hreflang من مكون إضافي أو من برنامجك الخاص wp_head مخرج.

كيف يمكنني إضافة hreflang في WordPress بدون مكون إضافي؟

خطاف wp_head وطباعة أ <link rel="alternate" hreflang> سطر لكل لغة في الخريطة تتحكم فيها، بما في ذلك x-default الإدخال، في كل صفحة في المجموعة. الهروب من القيم مع esc_attr و esc_url.

أين تذهب علامات hreflang في سمة WordPress؟

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

ماذا لو كانت صفحاتي المترجمة تحتوي على slugs مختلفة؟

لا تشتق شيئا. قم بتخزين معرفات المنشورات أو عناوين URL النظيرة في ملف تعريف المنشور واقرأ الخريطة في وقت العرض. قم بملئها على كلا الجانبين، أو يمكنك إنشاء علامة إرجاع مفقودة.

هل يتعامل Polylang وWPML مع hreflang؟

نعم، للترجمات التي يديرونها. التحقق من أن الإخراج يشمل x-default وأنه لا يوجد مكون إضافي ثانٍ يصدر كتلة منافسة.

لماذا لدي كتلتين من hreflang في صفحة واحدة؟

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

هل يجب أن يكون لكل إصدار لغة WordPress إصدار أساسي خاص به؟

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

كيف يمكنني إضافة hreflang عبر شبكة WordPress متعددة المواقع؟

حلقة get_sites()قم بالتبديل إلى كل مدونة لقراءة لغتها و home_url()، وقم بتخزين الخريطة الناتجة مؤقتًا في صورة عابرة. يجري switch_to_blog على كل طلب في wp_head مكلفة على شبكة كبيرة.


Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!