كيفية ربط إصدارات اللغات البديلة في TanStack Start v1

ربط بدائل اللغات لمحركات البحث

المشكلة

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

الحل

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

الخطوات

1. إنشاء دالة مساعدة لبناء عناوين URL البديلة للغات

تقوم طريقة buildLocation الخاصة بالموجه ببناء عناوين URL كاملة من معاملات المسار، والتي ستستخدمها لإنشاء عناوين URL لكل إصدار لغوي.

import { AnyRouter } from "@tanstack/react-router";

export function buildLanguageAlternates(
  router: AnyRouter,
  currentPath: string,
  currentLang: string,
  availableLanguages: string[],
) {
  return availableLanguages.map((lang) => {
    const location = router.buildLocation({
      to: currentPath,
      params: { lang },
    });
    return {
      lang,
      href: `${location.pathname}${location.search}${location.hash}`,
    };
  });
}

تأخذ هذه الدالة مسار الصفحة الحالي وتُنشئ عناوين URL بديلة عن طريق استبدال كل رمز لغة في معاملات المسار.

2. حدد اللغات المتاحة لديك

أنشئ ملف تكوين يسرد جميع اللغات التي يدعمها تطبيقك.

export const AVAILABLE_LANGUAGES = ["en", "fr", "de", "es"];

export const DEFAULT_LANGUAGE = "en";

سيتم استخدام هذه القائمة المركزية لإنشاء روابط hreflang لكل صفحة.

3. أضف روابط hreflang في دالة head الخاصة بالمسار

تستقبل دالة head سياقًا يتضمن matches وparams وloaderData، مما يوفر الوصول إلى معامل اللغة الحالي ونسخة الموجه.

import { createFileRoute } from "@tanstack/react-router";
import { buildLanguageAlternates, AVAILABLE_LANGUAGES } from "../i18n-config";

export const Route = createFileRoute("/$lang/about")({
  head: ({ params }) => {
    const alternates = buildLanguageAlternates(
      Route.router,
      "/$lang/about",
      params.lang,
      AVAILABLE_LANGUAGES,
    );

    return {
      links: alternates.map((alt) => ({
        rel: "alternate",
        hreflang: alt.lang,
        href: alt.href,
      })),
    };
  },
  component: AboutPage,
});

function AboutPage() {
  return <div>About page content</div>;
}

تستخدم خاصية hreflang رموز اللغة ISO 639-1، ويشير كل رابط إلى نفس الصفحة بلغة مختلفة.

4. أضف hreflang بقيمة x-default لسلوك احتياطي

تشير خاصية hreflang بقيمة x-default إلى الصفحة الافتراضية عندما لا تتطابق أي لغة.

export const Route = createFileRoute("/$lang/about")({
  head: ({ params }) => {
    const alternates = buildLanguageAlternates(
      Route.router,
      "/$lang/about",
      params.lang,
      AVAILABLE_LANGUAGES,
    );

    const defaultUrl = alternates.find((alt) => alt.lang === "en");

    return {
      links: [
        ...alternates.map((alt) => ({
          rel: "alternate",
          hreflang: alt.lang,
          href: alt.href,
        })),
        {
          rel: "alternate",
          hreflang: "x-default",
          href: defaultUrl?.href || alternates[0].href,
        },
      ],
    };
  },
  component: AboutPage,
});

يوفر رابط x-default عنوان URL احتياطيًا للمستخدمين الذين لا تتطابق تفضيلات لغتهم مع أي بديل معلن.

5. طبّق على المسارات الديناميكية مع المعاملات

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

export const Route = createFileRoute("/$lang/posts/$postId")({
  head: ({ params }) => {
    const alternates = AVAILABLE_LANGUAGES.map((lang) => {
      const location = Route.router.buildLocation({
        to: "/$lang/posts/$postId",
        params: { lang, postId: params.postId },
      });
      return {
        lang,
        href: `${location.pathname}${location.search}${location.hash}`,
      };
    });

    return {
      links: [
        ...alternates.map((alt) => ({
          rel: "alternate",
          hreflang: alt.lang,
          href: alt.href,
        })),
        {
          rel: "alternate",
          hreflang: "x-default",
          href:
            alternates.find((a) => a.lang === "en")?.href || alternates[0].href,
        },
      ],
    };
  },
});

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

6. تحقق من تطبيق hreflang

تُرجع دالة head عناصر link التي يتم عرضها بواسطة مكون HeadContent. افحص HTML المعروض للتأكد من ظهور الروابط في رأس المستند.

<link rel="alternate" hreflang="en" href="/en/about" />
<link rel="alternate" hreflang="fr" href="/fr/about" />
<link rel="alternate" hreflang="de" href="/de/about" />
<link rel="alternate" hreflang="es" href="/es/about" />
<link rel="alternate" hreflang="x-default" href="/en/about" />

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