المؤلف:
    إنشاء:2025-09-09آخر تحديث:2026-08-25

    ترجم موقع TanStack Start الخاص بك باستخدام Intlayer | التدويل (i18n)

    جدول المحتويات

    يوضح هذا الدليل كيفية دمج Intlayer لتحقيق تدويل سلس في مشاريع TanStack Start مع توجيه مدرك للغة، ودعم TypeScript، وممارسات التطوير الحديثة.

    لماذا Intlayer على البدائل؟

    بالمقارنة مع الحلول الرئيسية مثل react-i18next أو use-intl أو paraglide، فإن Intlayer هو الحل الذي يأتي مع تحسينات متكاملة مثل:

    تغطية كاملة لـ TanStack Start

    تم تحسين Intlayer بالكامل لـ TanStack Start، مما يوفر توجيه متعدد اللغات، إدارة ملفات تعريف الارتباط، إنشاء خريطة الموقع، تحميل المحتوى الديناميكي، وجميع الميزات اللازمة لتوسيع نطاق جهود التدويل (i18n).

    حجم البندل

    بدلاً من تحميل ملفات JSON ضخمة إلى صفحاتك، قم بتحميل المحتوى الضروري فقط. يساعد Intlayer في تقليل أحجام البندل وصفحاتك بنسبة تصل إلى 50%.

    الصيانة

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

    وكيل الذكاء الاصطناعي

    يؤدي تحديد موقع المحتوى المشترك إلى تقليل السياق المطلوب بواسطة نماذج اللغات الكبيرة (LLMs). يأتي Intlayer أيضًا مزودًا بمجموعة من الأدوات، مثل CLI لاختبار الترجمات المفقودة،LSP، MCP وagent skills، لجعل تجربة المطور (DX) أكثر سلاسة للذكاء الاصطناعي وكلاء.

    الأتمتة

    استخدم الأتمتة للترجمة في مسار CI/CD الخاص بك باستخدام LLM من اختيارك على حساب مزود الذكاء الاصطناعي الخاص بك. يقدم Intlayer أيضًا مترجمًا لأتمتة استخراج المحتوى، بالإضافة إلى منصة ويب للمساعدة في الترجمة في الخلفية.

    أداء

    يمكن أن يؤدي ربط ملفات JSON الضخمة بالمكونات إلى حدوث مشكلات في الأداء والتفاعل. يعمل Intlayer على تحسين تحميل المحتوى الخاص بك في وقت الإنشاء.

    التحجيم مع عدم وجود مطور

    أكثر من مجرد حل i18n، يوفر Intlayer [محررًا مرئيًا] مستضافًا ذاتيًا](/ar/doc/concept/editor) وكامل CMS لمساعدتك في إدارة المحتوى متعدد اللغات في الوقت الفعلي، مما يجعل التعاون مع المترجمين ومؤلفي النصوص وأعضاء الفريق الآخرين سلسًا. يمكن تخزين المحتوى محليًا و/أو عن بعد.

    </Accordion>


    دليل خطوة بخطوة لإعداد Intlayer في تطبيق TanStack Start

    www.youtube.com
    ide.intlayer.org
    intlayer-tanstack-start-template.vercel.app

    انظر قالب التطبيق على GitHub.

    1. إنشاء المشروع

      ابدأ بإنشاء مشروع TanStack Start جديد باتباع دليل بدء مشروع جديد على موقع TanStack Start.

    2. تثبيت حزم Intlayer

      قم بتثبيت الحزم اللازمة باستخدام مدير الحزم المفضل لديك:

      bash
      npx intlayer init --interactive
      
      علامة --interactive اختيارية. استخدم intlayer-cli init إذا كنت وكيل ذكاء اصطناعي.
      سيقوم هذا الأمر باكتشاف بيئتك وتثبيت الحزم المطلوبة. على سبيل المثال:
      bash
      npm install intlayer react-intlayer
      npm install vite-intlayer --save-dev
      
      • intlayer

        الحزمة الأساسية التي توفر أدوات التدويل لإدارة التكوين، والترجمة، وإعلان المحتوى، والتحويل البرمجي، وأوامر CLI.

      • react-intlayer الحزمة التي تدمج Intlayer مع تطبيق React. توفر مزودي السياق والخطافات لتدويل React.

      • vite-intlayer تتضمن إضافة Vite لدمج Intlayer مع مجمّع Vite، بالإضافة إلى وسيط لاكتشاف اللغة المفضلة للمستخدم، وإدارة ملفات تعريف الارتباط، والتعامل مع إعادة توجيه URL.

    3. تكوين مشروعك

      أنشئ ملف تكوين لتكوين لغات تطبيقك:

      intlayer.config.ts
      import type { IntlayerConfig } from "intlayer";
      
      import { Locales } from "intlayer";
      
      const config: IntlayerConfig = {
        internationalization: {
          defaultLocale: Locales.ENGLISH,
          locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        },
      };
      
      export default config;
      
      من خلال ملف التكوين هذا، يمكنك إعداد عناوين URL المترجمة، وإعادة توجيه الوسيط، وأسماء ملفات تعريف الارتباط، وموقع وامتداد إعلانات المحتوى الخاصة بك، وتعطيل سجلات Intlayer في وحدة التحكم، والمزيد. للحصول على قائمة كاملة بالمعلمات المتاحة، راجع توثيق التكوين.
    4. دمج Intlayer في تكوين Vite الخاص بك

      أضف إضافة intlayer إلى تكوينك:

      vite.config.ts
      import { tanstackStart } from "@tanstack/react-start/plugin/vite";
      import viteReact from "@vitejs/plugin-react";
      import { nitro } from "nitro/vite";
      import { defineConfig } from "vite";
      import { intlayer } from "vite-intlayer";
      
      const config = defineConfig({
        plugins: [
          nitro(),
          intlayer({
            proxy: {
              ignore: (req) => req.url?.startsWith("/api"),
            },
          }),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          viteReact(),
        ],
      });
      
      export default config;
      
      تُستخدم إضافة intlayer() لـ Vite لدمج Intlayer مع Vite. تضمن بناء ملفات إعلان المحتوى ومراقبتها في وضع التطوير. كما تُعرف متغيرات بيئة Intlayer داخل تطبيق Vite. بالإضافة إلى ذلك، توفر أسماء مستعارة لتحسين الأداء.
    5. إنشاء التخطيط الجذري

      قم بتكوين التخطيط الجذري الخاص بك لدعم التدويل باستخدام useParams للكشف عن اللغة الحالية وتعيين سمات lang و dir على علامة html.

      src/routes/__root.tsx
      import {
        createRootRouteWithContext,
        getRouteApi,
        HeadContent,
        Scripts,
      } from "@tanstack/react-router";
      import { defaultLocale, getHTMLTextDir } from "intlayer";
      import { type ReactNode } from "react";
      import { IntlayerProvider } from "react-intlayer";
      
      const localeRoute = getRouteApi("/{-$locale}");
      
      export const Route = createRootRouteWithContext<{}>()({
        head: () => ({
          meta: [
            {
              charSet: "utf-8",
            },
            {
              content: "width=device-width, initial-scale=1",
              name: "viewport",
            },
            {
              title: "TanStack Start Starter",
            },
          ],
        }),
      
        shellComponent: RootDocument,
      });
      
      function RootDocument({ children }: { children: ReactNode }) {
        const params = localeRoute.useParams();
        const locale = params?.locale ?? defaultLocale;
      
        return (
          <html dir={getHTMLTextDir(locale)} lang={locale}>
            <head>
              <HeadContent />
            </head>
            <body>
              <IntlayerProvider locale={locale}>{children}</IntlayerProvider>
              <Scripts />
            </body>
          </html>
        );
      }
      
    6. إنشاء تخطيط اللغة

      أنشئ تخطيطًا يتعامل مع بادئة اللغة ويقوم بالتحقق من الصحة.

      src/routes/{-$locale}/route.tsx
      import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
      import { validatePrefix } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}")({
        beforeLoad: ({ params }) => {
          const localeParam = params.locale;
      
          // التحقق من صحة بادئة اللغة
          const { isValid, localePrefix } = validatePrefix(localeParam);
      
          if (!isValid) {
            throw redirect({
              to: "/{-$locale}/404",
              params: { locale: localePrefix },
            });
          }
        },
        component: Outlet,
      });
      
      هنا، {-$locale} هي معلمة مسار ديناميكية يتم استبدالها باللغة الحالية. هذا الترميز يجعل الجزء اختياريًا، مما يسمح له بالعمل مع أوضاع التوجيه مثل 'prefix-no-default' وما إلى ذلك.

      كن على علم بأن هذا الجزء قد يسبب مشاكل إذا كنت تستخدم أجزاء ديناميكية متعددة في نفس المسار (على سبيل المثال، /{-$locale}/other-path/$anotherDynamicPath/...). بالنسبة لوضع 'prefix-all'، قد تفضل تبديل الجزء إلى $locale بدلاً من ذلك. بالنسبة لوضع 'no-prefix' أو 'search-params'، يمكنك إزالة الجزء تمامًا.

    7. إعلان المحتوى الخاص بك

      قم بإنشاء وإدارة إعلانات المحتوى الخاصة بك لتخزين الترجمات:

      src/contents/page.content.ts
      import type { Dictionary } from "intlayer";
      
      import { t } from "intlayer";
      
      const appContent = {
        content: {
          links: {
            about: t({
              en: "About",
              es: "Acerca de",
              fr: "À propos",
            }),
            home: t({
              en: "Home",
              es: "Inicio",
              fr: "Accueil",
            }),
          },
          meta: {
            title: t({
              en: "Welcome to Intlayer + TanStack Router",
              es: "Bienvenido a Intlayer + TanStack Router",
              fr: "Bienvenue à Intlayer + TanStack Router",
            }),
            description: t({
              en: "This is an example of using Intlayer with TanStack Router",
              es: "Este es un ejemplo de uso de Intlayer con TanStack Router",
              fr: "Ceci est un exemple d'utilisation d'Intlayer avec TanStack Router",
            }),
          },
        },
        key: "app",
      } satisfies Dictionary;
      
      export default appContent;
      
      يمكن تعريف إعلانات المحتوى الخاصة بك في أي مكان في تطبيقك بمجرد تضمينها في دليل contentDir (افتراضيًا، ./app). وتطابق امتداد ملف إعلان المحتوى (افتراضيًا، .content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}).
      لمزيد من التفاصيل، راجع توثيق إعلان المحتوى.
    8. إنشاء مكونات وخطافات مدركة للغة

      أنشئ مكون LocalizedLink للتنقل المدرك للغة:

      src/components/localized-link.tsx
      import type { FC } from "react";
      
      import { Link, type LinkComponentProps } from "@tanstack/react-router";
      import { useLocale } from "react-intlayer";
      import { getPrefix } from "intlayer";
      
      export const LOCALE_ROUTE = "{-$locale}" as const;
      
      export type To = StripLocalePrefix<LinkComponentProps["to"]>;
      
      export type StripLocalePrefix<T extends string | undefined> = T extends
        `/${typeof LOCALE_ROUTE}/` | `/${typeof LOCALE_ROUTE}`
        ? "/"
        : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
          ? `/${Rest}`
          : T;
      
      type LocalizedLinkProps = {
        to?: To;
      } & Omit<LinkComponentProps, "to">;
      
      export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
        const { locale } = useLocale();
        const { localePrefix } = getPrefix(locale);
      
        return (
          <Link
            {...props}
            params={{
              locale: localePrefix,
              ...(typeof props?.params === "object" ? props?.params : {}),
            }}
            to={`/${LOCALE_ROUTE}${props.to}` as LinkComponentProps["to"]}
          />
        );
      };
      

      هذا المكون له هدفان:

      • إزالة بادئة {-$locale} غير الضرورية من عنوان URL.
      • حقن معلمة اللغة في عنوان URL لضمان إعادة توجيه المستخدم مباشرة إلى المسار المترجم.

      ثم يمكننا إنشاء خطاف useLocalizedNavigate للتنقل البرمجي:

      src/hooks/useLocalizedNavigate.tsx
      import { useNavigate } from "@tanstack/react-router";
      import { getPrefix } from "intlayer";
      import { useLocale } from "react-intlayer";
      import type { StripLocalePrefix } from "@/components/localized-link";
      import type { FileRouteTypes } from "@/routeTree.gen";
      
      type NavigateFn = ReturnType<typeof useNavigate>;
      type BaseNavigateOptions = Parameters<NavigateFn>[0];
      
      type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
      
      export type LocalizedNavigateOptions = Omit<
        BaseNavigateOptions,
        "to" | "params"
      > & {
        to: LocalizedTo;
        params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
      };
      
      type LocalizedNavigate = (
        options: LocalizedNavigateOptions
      ) => ReturnType<NavigateFn>;
      
      export const useLocalizedNavigate = () => {
        const navigate = useNavigate();
      
        const { locale } = useLocale();
      
        const localizedNavigate: LocalizedNavigate = (args: any) => {
          const { localePrefix } = getPrefix(locale);
      
          if (typeof args === "string") {
            return navigate({
              to: `/${LOCALE_ROUTE}${args}`,
              params: { locale: localePrefix },
            });
          }
      
          const { to, ...rest } = args;
      
          const localizedTo = `/${LOCALE_ROUTE}${to}` as any;
      
          return navigate({
            to: localizedTo,
            params: { locale: localePrefix, ...rest } as any,
          });
        };
      
        return localizedNavigate;
      };
      
    9. استخدام Intlayer في صفحاتك

      استخدم useIntlayer افتراضيًا: فهي الطريقة الموصى بها لقراءة المحتوى داخل المكوّنات، ويقوم المُصرِّف بتحويلها إلى اللغة التي يجري عرضها. لا تلجأ إلى getIntlayer / getIntlayerAsync إلا خارج شجرة React: في head المسارات، والمُحمِّلات، ودوال الخادم.

      قم بالوصول إلى قواميس المحتوى الخاصة بك في جميع أنحاء تطبيقك:

      صفحة رئيسية مترجمة

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import { useIntlayer } from "react-intlayer";
      
      import LocaleSwitcher from "@/components/locale-switcher";
      import { LocalizedLink } from "@/components/localized-link";
      import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
      });
      
      function RouteComponent() {
        const content = useIntlayer("app");
        const navigate = useLocalizedNavigate();
      
        return (
          <div>
            <div>
              {content.title}
              <LocaleSwitcher />
              <div>
                <LocalizedLink to="/">{content.links.home}</LocalizedLink>
                <LocalizedLink to="/about">{content.links.about}</LocalizedLink>
              </div>
              <div>
                <button onClick={() => navigate({ to: "/" })}>
                  {content.links.home}
                </button>
                <button onClick={() => navigate({ to: "/about" })}>
                  {content.links.about}
                </button>
              </div>
            </div>
          </div>
        );
      }
      

      إذا كنت تريد استخدام محتواك في خاصية string، مثل alt أو title أو href أو aria-label، إلخ، يمكنك استخدام قيمة الدالة، مثل:

      html
      <img src="{content.image.src.value}" alt="{content.image.value}" />
      <img src="{content.image.src.toString()}" alt="{content.image.toString()}" />
      <img src="{String(content.image.src)}" alt="{String(content.image)}" />
      
      لمعرفة المزيد حول خطاف useIntlayer ، راجع التوثيق.
    10. إنشاء مكون لتبديل اللغة

      أنشئ مكونًا للسماح للمستخدمين بتغيير اللغات:

      src/components/locale-switcher.tsx
      import { useLocation } from "@tanstack/react-router";
      import {
        getHTMLTextDir,
        getLocaleName,
        getPathWithoutLocale,
        getPrefix,
        Locales,
      } from "intlayer";
      import type { FC } from "react";
      import { useLocale } from "react-intlayer";
      
      import { LocalizedLink, type To } from "./localized-link";
      
      export const LocaleSwitcher: FC = () => {
        const { pathname } = useLocation();
      
        const { availableLocales, locale, setLocale } = useLocale();
      
        const pathWithoutLocale = getPathWithoutLocale(pathname);
      
        return (
          <ol>
            {availableLocales.map((localeEl) => (
              <li key={localeEl}>
                <LocalizedLink
                  aria-current={localeEl === locale ? "page" : undefined}
                  onClick={() => setLocale(localeEl)}
                  params={{ locale: getPrefix(localeEl).localePrefix }}
                  to={pathWithoutLocale as To}
                >
                  <span>
                    {/* اللغة - على سبيل المثال FR */}
                    {localeEl}
                  </span>
                  <span>
                    {/* اللغة في لغتها الخاصة - على سبيل المثال Français */}
                    {getLocaleName(localeEl, locale)}
                  </span>
                  <span dir={getHTMLTextDir(localeEl)} lang={localeEl}>
                    {/* اللغة في اللغة الحالية - على سبيل المثال Francés مع تعيين اللغة الحالية إلى Locales.SPANISH */}
                    {getLocaleName(localeEl)}
                  </span>
                  <span dir="ltr" lang={Locales.ENGLISH}>
                    {/* اللغة بالإنجليزية - على سبيل المثال French */}
                    {getLocaleName(localeEl, Locales.ENGLISH)}
                  </span>
                </LocalizedLink>
              </li>
            ))}
          </ol>
        );
      };
      
      لمعرفة المزيد حول خطاف useLocale ، راجع التوثيق.
    11. إدارة سمات HTML

      كما رأينا في الخطوة 5، يمكنك إدارة سمات lang و dir لعلامة html باستخدام useParams في مكونك الجذري. يضمن هذا تعيين السمات الصحيحة على الخادم والعميل.

      src/routes/__root.tsx
      function RootDocument({ children }: { children: ReactNode }) {
        const params = localeRoute.useParams();
        const locale = params?.locale ?? defaultLocale;
      
        return (
          <html dir={getHTMLTextDir(locale)} lang={locale}>
            {/* ... */}
          </html>
        );
      }
      

    12. إضافة وسيط

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

      لاحظ أنه لاستخدام intlayerProxy في الإنتاج، تحتاج إلى نقل حزمة vite-intlayer من devDependencies إلى dependencies.
      منذ إصدار Intlayer v9، يتم دمج intlayerProxy() مباشرة في مكون intlayer() ويتم تفعيله افتراضياً من خلال خيار routing.enableProxy (true بشكل افتراضي). تسجيله بشكل منفصل كما هو موضح أدناه أصبح اختيارياً الآن، حيث يتم الاحتفاظ به للتوافقية العكسية والإعدادات التي تحتاج إلى التحكم في ترتيب المكونات. اضبط routing.enableProxy: false للتحديد عن البحث. اطلع على ملاحظات إصدار v9.
      vite.config.ts
      import { tanstackStart } from "@tanstack/react-start/plugin/vite";
      import viteReact from "@vitejs/plugin-react";
      import { nitro } from "nitro/vite";
      import { defineConfig } from "vite";
      import { intlayer } from "vite-intlayer";
      
      export default defineConfig({
        plugins: [
          nitro(),
          intlayer({
            proxy: {
              ignore: (req) => req.url?.startsWith("/api"),
            },
          }),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          viteReact(),
        ],
      });
      

    13. تدويل البيانات الوصفية الخاصة بك

      يُحلّ getIntlayer بشكل متزامن اعتمادًا على القاموس المدمج الذي يحتوي على كل اللغات المعلنة. يبقى head متزامنًا ولا يُنتظر أي شيء، لكن القاموس متعدد اللغات بأكمله يُدرَج داخل جزء المسار المُرسَل إلى المتصفح.

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import {
        defaultLocale,
        getIntlayer,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        head: ({ params }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // The path for this route
      
          const metaContent = getIntlayer("app", locale);
      
          return {
            links: [
              // Canonical link: Points to the current localized page
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: Tell Google about all localized versions
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: For users in unmatched languages
              // Define the default fallback locale (usually your primary language)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: metaContent.title },
              { name: "description", content: metaContent.meta.description },
            ],
          };
        },
      });
      

      الأنسب لقواميس البيانات الوصفية الصغيرة، أو لعدد قليل من اللغات، أو أثناء بناء النماذج الأولية.

      يتصرف getIntlayerAsync (المتاح ابتداءً من الإصدار 9.4) مثل getIntlayer، لكن إضافة البناء توجّهه إلى الجزء الخاص بكل لغة في .intlayer/dynamic_dictionaries/ بدلًا من القاموس المدمج. لذلك لا ترسل الصفحة سوى اللغة التي تعرضها. ولأن هذا الجزء يُحمَّل عند الطلب، يصبح head دالة async:

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import {
        defaultLocale,
        getIntlayerAsync,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        head: async ({ params }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // The path for this route
      
          const metaContent = await getIntlayerAsync("app", locale);
      
          return {
            links: [
              // Canonical link: Points to the current localized page
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: Tell Google about all localized versions
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: For users in unmatched languages
              // Define the default fallback locale (usually your primary language)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: metaContent.title },
              { name: "description", content: metaContent.meta.description },
            ],
          };
        },
      });
      
      إذا كان head يقرأ عدة قواميس، فحلّها باستخدام Promise.all؛ فانتظار كل getIntlayerAsync في سطر مستقل يجعل الطلبات متسلسلة بدلًا من تنفيذها بالتوازي.

      المقايضة: يُحلّ الاستيراد الديناميكي أثناء تنفيذ head، على المسار الحرج لعرض المستند. على مسار "بارد" يؤخر ذلك head ببضعة أجزاء من الألف من الثانية وقد يُضعف LCP قليلًا.

      حلّ القاموس داخل loader المسار ثم اقرأه مجددًا من loaderData في head. تعمل مُحمِّلات المسارات المطابقة على التوازي، ويُخبر staleTime: Infinity جهاز التوجيه TanStack Router بأن النتيجة لا تنتهي صلاحيتها أبدًا، فيُحلّ الجزء الخاص باللغة مرة واحدة ثم يُقدَّم من ذاكرة التوجيه المؤقتة، ويبقى head متزامنًا.

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import {
        defaultLocale,
        getIntlayerAsync,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        // Resolved in parallel with the other matched routes, off the head critical path
        loader: async ({ params }) => {
          const { locale = defaultLocale } = params;
      
          return { metaContent: await getIntlayerAsync("app", locale) };
        },
        // The dictionary never changes for a given locale: resolve the chunk once
        staleTime: Infinity,
        head: ({ params, loaderData }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // The path for this route
      
          return {
            links: [
              // Canonical link: Points to the current localized page
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: Tell Google about all localized versions
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: For users in unmatched languages
              // Define the default fallback locale (usually your primary language)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: loaderData?.metaContent.title },
              {
                name: "description",
                content: loaderData?.metaContent.meta.description,
              },
            ],
          };
        },
      });
      
      قد يُستدعى head قبل اكتمال المُحمِّل، لذا فإن نوع loaderData قد يكون undefined. أبقِ على التسلسل الاختياري، أو أعِد عنوانًا احتياطيًا.

      تحتفظ بالجزء الخاص بكل لغة دون دفع تكلفته على المسار الحرج لـ head. الثمن هو تجربة المطوّر: يجب تمرير المحتوى صراحةً من المُحمِّل إلى head عبر loaderData.

      أي طريقة تحليل ينبغي أن أختار؟

      التحليل الثابت التحليل الديناميكي التحليل الديناميكي المخزَّن مؤقتًا
      واجهة البرمجة getIntlayer getIntlayerAsync (9.4+) getIntlayerAsync داخل loader (9.4+)
      توقيع head متزامن async متزامن، يقرأ loaderData
      اللغات المُرسَلة كل اللغات المعلنة اللغة المطلوبة فقط اللغة المطلوبة فقط
      التنقلات في العميل لا شيء ليُحلّ يُعاد تنفيذه عند كل مطابقة يُقدَّم من ذاكرة التوجيه المؤقتة
      تجربة المطوّر الأبسط await واحد المحتوى يُمرَّر عبر loaderData

    14. جلب اللغة في إجراءات الخادم الخاصة بك

      قد ترغب في الوصول إلى اللغة الحالية من داخل إجراءات الخادم أو نقاط نهاية API الخاصة بك. يمكنك القيام بذلك باستخدام مساعد getLocale من intlayer.

      إليك مثال باستخدام وظائف خادم TanStack Start:

      src/routes/{-$locale}/index.tsx
      import { createServerFn } from "@tanstack/react-start";
      import {
        getRequestHeader,
        getRequestHeaders,
      } from "@tanstack/react-start/server";
      import { getCookie, getIntlayerAsync, getLocale } from "intlayer";
      
      export const getLocaleServer = createServerFn().handler(async () => {
        const locale = await getLocale({
          // الحصول على ملف تعريف الارتباط من الطلب (الافتراضي: 'INTLAYER_LOCALE')
          getCookie: (name) => {
            const cookieString = getRequestHeader("cookie");
      
            return getCookie(name, cookieString);
          },
          // الحصول على الترويسة من الطلب (الافتراضي: 'x-intlayer-locale')
          // تراجع باستخدام مفاوضة Accept-Language
          getHeader: (name) => getRequestHeader(name),
        });
      
        // جلب بعض المحتوى باستخدام getIntlayer()
        const content = await getIntlayerAsync("app", locale);
      
        return { locale, content };
      });
      

    15. إدارة الصفحات غير الموجودة

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

      فهم معالجة 404 في TanStack Router مع بادئات اللغة

      في TanStack Router، تتطلب معالجة صفحات 404 مع المسارات المترجمة نهجًا متعدد الطبقات:

      1. مسار 404 مخصص: مسار محدد لعرض واجهة مستخدم 404
      2. التحقق من الصحة على مستوى المسار: يتحقق من صحة بادئات اللغة ويعيد توجيه البادئات غير الصالحة إلى 404
      3. مسار catch-all: يلتقط أي مسارات غير متطابقة داخل جزء اللغة
      src/routes/{-$locale}/404.tsx
      import { createFileRoute } from "@tanstack/react-router";
      
      // هذا ينشئ مسار /[locale]/404 مخصص
      // يتم استخدامه كمسار مباشر ويتم استيراده كمكون في ملفات أخرى
      export const Route = createFileRoute("/{-$locale}/404")({
        component: NotFoundComponent,
      });
      
      // يتم تصديره بشكل منفصل بحيث يمكن إعادة استخدامه في notFoundComponent ومسارات catch-all
      export function NotFoundComponent() {
        return (
          <div>
            <h1>404</h1>
          </div>
        );
      }
      
      src/routes/{-$locale}/route.tsx
      import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
      import { validatePrefix } from "intlayer";
      import { NotFoundComponent } from "./404";
      
      export const Route = createFileRoute("/{-$locale}")({
        // يتم تشغيل beforeLoad قبل رندر المسار (على كل من الخادم والعميل)
        // إنه المكان المثالي للتحقق من صحة بادئة اللغة
        beforeLoad: ({ params }) => {
          const localeParam = params.locale;
      
          // validatePrefix يتحقق مما إذا كانت اللغة صالحة وفقًا لتكوين intlayer الخاص بك
          const { isValid, localePrefix } = validatePrefix(localeParam);
      
          if (!isValid) {
            // بادئة لغة غير صالحة - إعادة التوجيه إلى صفحة 404 ببادئة لغة صالحة
            throw redirect({
              to: "/{-$locale}/404",
              params: { locale: localePrefix },
            });
          }
        },
        component: Outlet,
        // يتم استدعاء notFoundComponent عندما لا يوجد مسار طفل
        // على سبيل المثال، /en/non-existent-page يطلق هذا داخل تخطيط /en
        notFoundComponent: NotFoundComponent,
      });
      
      src/routes/{-$locale}/$.tsx
      import { createFileRoute } from "@tanstack/react-router";
      
      import { NotFoundComponent } from "./404";
      
      // المسار $ (splat/catch-all) يطابق أي مسار لا يطابق المسارات الأخرى
      // على سبيل المثال، /en/some/deeply/nested/invalid/path
      // هذا يضمن أن جميع المسارات غير المتطابقة داخل اللغة تعرض صفحة 404
      // بدون هذا، قد تعرض المسارات العميقة غير المتطابقة صفحة فارغة أو خطأ
      export const Route = createFileRoute("/{-$locale}/$")({
        component: NotFoundComponent,
      });
      
    16. استخراج محتوى مكوناتك

      اختياري

      إذا كان لديك قاعدة بيانات كود موجودة، فقد يكون تحويل آلاف الملفات مستهلكًا للوقت.

      لتسهيل هذه العملية، يقترح Intlayer مترجمًا / مستخرجًا لتحويل مكوناتك واستخراج المحتوى.

      لإعداده، يمكنك إضافة قسم compiler في ملف intlayer.config.ts الخاص بك:

      intlayer.config.ts
      import { type IntlayerConfig } from "intlayer";
      
      const config: IntlayerConfig = {
        // ... بقية التكوين الخاص بك
        compiler: {
          /**
           * يشير إلى ما إذا كان يجب تمكين المترجم.
           */
          enabled: true,
      
          /**
           * يحدد مسار ملفات المخرجات
           */
          output: ({ fileName, extension }) => `./${fileName}${extension}`,
      
          /**
           * يشير إلى ما إذا كان يجب حفظ المكونات بعد تحويلها. بهذه الطريقة، يمكن تشغيل المترجم مرة واحدة فقط لتحويل التطبيق، ثم يمكن إزالته.
           */
          saveComponents: false,
      
          /**
           * بادئة مفتاح القاموس
           */
          dictionaryKeyPrefix: "",
        },
      };
      
      export default config;
      

      قم بتشغيل المستخرج لتحويل مكوناتك واستخراج المحتوى

      bash
      npx intlayer extract
      
      Since v9, the intlayerCompiler is included in the intlayer plugin. So you don't need to add it manually.
      babel.config.js
      const {
        intlayerExtractBabelPlugin,
        getExtractPluginOptions,
      } = require("@intlayer/babel");
      
      module.exports = {
        presets: ["next/babel"],
        plugins: [
          // استخراج المحتوى من المكونات إلى القواميس
          [intlayerExtractBabelPlugin, getExtractPluginOptions()],
        ],
      };
      
      bash
      npm run build # أو npm run dev
      

    17. Pre-render & Generate Sitemap

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

      خريطة الموقع المُنشأة من قِبل Intlayer تدعم مساحة الأسماء xhtml:link (Hreflang XML Extensions). على عكس مولدات خرائط الموقع الافتراضية التي تُدرج عناوين URL الخام فقط، ينشئ Intlayer تلقائياً الروابط ثنائية الاتجاه المطلوبة بين جميع إصدارات الصفحة باللغات المختلفة (على سبيل المثال، /about و /about?lang=fr و /about?lang=es). هذا يضمن أن محركات البحث تفهرس بشكل صحيح وتقدم النسخة الصحيحة من اللغة للجمهور المناسب.

      لاستخدامه، تحتاج أولاً إلى تكوين vite.config.ts لتفعيل العرض المسبق (pre-rendering) لمساراتك المحلية وتعطيل توليد خريطة الموقع الافتراضية لـ TanStack Start.

      vite.config.ts
      import { localeFlatMap } from "intlayer";
      // ... الواردات الأخرى
      
      export const pathList = ["", "/about", "/404"];
      
      const localizedPages = localeFlatMap(({ urlPrefix }) =>
        pathList.map((path) => ({
          path: `${urlPrefix}${path}`,
          prerender: {
            enabled: true,
          },
        }))
      );
      
      export default defineConfig({
        plugins: [
          // ... المكونات الإضافية الأخرى
          tanstackStart({
            // ... الإعدادات الأخرى
            sitemap: {
              enabled: false,
            },
            prerender: {
              enabled: true,
              crawlLinks: false,
              concurrency: 10,
            },
            pages: localizedPages,
          }),
        ],
      });
      

      ثم، قم بإنشاء مسار src/routes/sitemap[.]xml.ts يستخدم دالة generateSitemap:

      src/routes/sitemap[.]xml.ts
      import { createFileRoute } from "@tanstack/react-router";
      import { generateSitemap } from "intlayer";
      
      const SITE_URL = (
        import.meta.env.VITE_SITE_URL ?? "http://localhost:3000"
      ).replace(/\/$/, "");
      
      export const Route = createFileRoute("/sitemap.xml")({
        server: {
          handlers: {
            GET: async () => {
              const sitemap = generateSitemap(
                [
                  { path: "/", changefreq: "daily", priority: 1.0 },
                  { path: "/about", changefreq: "monthly", priority: 0.8 },
                ],
                { siteUrl: SITE_URL }
              );
      
              return new Response(sitemap, {
                headers: { "Content-Type": "application/xml" },
              });
            },
          },
        },
      });
      

    18. تكوين TypeScript

      يستخدم Intlayer module augmentation للاستفادة من TypeScript وجعل codebase الخاص بك أقوى.

      تأكد من أن تكوين TypeScript الخاص بك يتضمن الأنواع المُنشأة تلقائيًا:

      tsconfig.json
      {
        // ... your existing configurations
        include: [
          // ... your existing includes
          ".intlayer/**/*.ts", // تضمين الأنواع المُنشأة تلقائيًا
        ],
      }
      

    تكوين Git

    يوصى بتجاهل الملفات الناتجة عن Intlayer. يتيح لك هذا تجنب الالتزام بها في مستودع Git الخاص بك.

    للقيام بذلك، يمكنك إضافة التعليمات التالية إلى ملف .gitignore الخاص بك:

    .gitignore
    # تجاهل الملفات الناتجة عن Intlayer
    .intlayer
    

    إضافة VS Code

    لتحسين تجربة التطوير الخاصة بك مع Intlayer، يمكنك تثبيت إضافة Intlayer VS Code الرسمية.

    التثبيت من VS Code Marketplace

    توفر هذه الإضافة:

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

    لمزيد من التفاصيل حول كيفية استخدام الإضافة، راجع توثيق إضافة Intlayer لـ VS Code.


    اذهب أبعد من ذلك

    للذهاب أبعد من ذلك، يمكنك تنفيذ المحرر المرئي أو إخراج محتواك باستخدام نظام إدارة المحتوى (CMS).


    مراجع التوثيق