البدء في التدويل (i18n) باستخدام Intlayer و Next.js 14 مع App Router

    ما هو Intlayer؟

    Intlayer هو مكتبة تدويل (i18n) مبتكرة ومفتوحة المصدر مصممة لتبسيط دعم اللغات المتعددة في تطبيقات الويب الحديثة. يتكامل Intlayer بسلاسة مع إطار العمل الحديث Next.js 14، بما في ذلك App Router القوي. تم تحسينه للعمل مع مكونات الخادم لتقديم فعال ومتوافق تمامًا مع Turbopack (من Next.js >= 15).

    مع Intlayer، يمكنك:

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

    يتوافق Intlayer مع Next.js 12، 13، 14، و15. إذا كنت تستخدم Next.js Page Router، يمكنك الرجوع إلى هذا الدليل. بالنسبة لـ Next.js 15 مع أو بدون turbopack، راجع هذا الدليل.


    دليل خطوة بخطوة لإعداد Intlayer في تطبيق Next.js

    الخطوة 1: تثبيت التبعيات

    قم بتثبيت الحزم اللازمة باستخدام npm:

    bash
    npm install intlayer next-intlayer
    • intlayer

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

    • next-intlayer

      الحزمة التي تدمج Intlayer مع Next.js. توفر موفري السياق وخطافات لتدويل Next.js. بالإضافة إلى ذلك، تتضمن مكون إضافي لـ Next.js لدمج Intlayer مع Webpack أو Turbopack، بالإضافة إلى وسيط لاكتشاف اللغة المفضلة للمستخدم، إدارة الكوكيز، والتعامل مع إعادة توجيه URL.

    الخطوة 2: تكوين مشروعك

    قم بإنشاء ملف تكوين لتحديد لغات تطبيقك:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // لغاتك الأخرى    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

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

    الخطوة 3: دمج Intlayer في تكوين Next.js الخاص بك

    قم بتكوين إعداد Next.js الخاص بك لاستخدام Intlayer:

    next.config.mjs
    import { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = {};export default withIntlayer(nextConfig);

    يتم استخدام مكون withIntlayer() الإضافي لـ Next.js لدمج Intlayer مع Next.js. يضمن بناء ملفات إعلان المحتوى ومراقبتها في وضع التطوير. يحدد متغيرات بيئة Intlayer داخل بيئات Webpack أو Turbopack. بالإضافة إلى ذلك، يوفر أسماء مستعارة لتحسين الأداء ويضمن التوافق مع مكونات الخادم.

    الخطوة 4: تكوين الوسيط لاكتشاف اللغة

    قم بإعداد الوسيط لاكتشاف اللغة المفضلة للمستخدم:

    src/middleware.ts
    export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = {  matcher:    "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",};

    يتم استخدام intlayerMiddleware لاكتشاف اللغة المفضلة للمستخدم وإعادة توجيههم إلى عنوان URL المناسب كما هو محدد في التكوين. بالإضافة إلى ذلك، يتيح حفظ اللغة المفضلة للمستخدم في كوكيز.

    قم بتعديل معلمة matcher لتطابق مسارات تطبيقك. لمزيد من التفاصيل، راجع وثائق Next.js حول تكوين المطابقة.

    الخطوة 5: تحديد مسارات اللغة الديناميكية

    قم بإزالة كل شيء من RootLayout واستبدله بالكود التالي:

    src/app/layout.tsx
    import type { PropsWithChildren, FC } from "react";import "./globals.css";const RootLayout: FC<PropsWithChildren> = ({ children }) => children;export default RootLayout;

    الحفاظ على مكون RootLayout فارغًا يسمح بتعيين سمات lang وdir إلى علامة <html>.

    لتنفيذ التوجيه الديناميكي، قم بتوفير المسار للغة عن طريق إضافة تخطيط جديد في دليل [locale] الخاص بك:

    src/app/[locale]/layout.tsx
    import type { Next14LayoutIntlayer } from "next-intlayer";import { Inter } from "next/font/google";import { getHTMLTextDir } from "intlayer";const inter = Inter({ subsets: ["latin"] });const LocaleLayout: Next14LayoutIntlayer = ({  children,  params: { locale },}) => (  <html lang={locale} dir={getHTMLTextDir(locale)}>    <body className={inter.className}>{children}</body>  </html>);

    import { Inter } from "next/font/google"; import { getHTMLTextDir } from "intlayer";

    const inter = Inter({ subsets: ["latin"] });

    const LocaleLayout = ({ children, params: { locale } }) => (

    {children}

    );

    export default LocaleLayout;

    ```jsx fileName="src/app/[locale]/layout.csx" codeFormat="commonjs" const { Inter } = require("next/font/google"); const { getHTMLTextDir } = require("intlayer"); const inter = Inter({ subsets: ["latin"] }); const LocaleLayout = ({ children, params: { locale } }) => ( <html lang={locale} dir={getHTMLTextDir(locale)}> <body className={inter.className}>{children}</body> </html> ); module.exports = LocaleLayout;

    يتم استخدام جزء المسار [locale] لتحديد اللغة المحلية. على سبيل المثال: /ar/about يشير إلى ar و /fr/about يشير إلى fr.

    ثم قم بتنفيذ وظيفة generateStaticParams في تخطيط التطبيق الخاص بك.

    src/app/[locale]/layout.tsx
    export { generateStaticParams } from "next-intlayer"; // السطر للإدراجconst LocaleLayout: Next14LayoutIntlayer = ({  children,  params: { locale },}) => {  /*... بقية الكود */};export default LocaleLayout;

    تضمن generateStaticParams أن يقوم تطبيقك ببناء الصفحات الضرورية مسبقًا لجميع اللغات المحلية، مما يقلل من الحساب أثناء وقت التشغيل ويحسن تجربة المستخدم. لمزيد من التفاصيل، راجع وثائق Next.js حول generateStaticParams.

    الخطوة 6: إعلان المحتوى الخاص بك

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

    src/app/[locale]/page.content.ts
    import { t, type Dictionary } from "intlayer";const pageContent = {  key: "page",  content: {    getStarted: {      main: t({        ar: "ابدأ بالتعديل",        en: "Get started by editing",        fr: "Commencez par éditer",        es: "Comience por editar",      }),      pageLink: "src/app/page.tsx",    },  },} satisfies Dictionary;export default pageContent;

    يمكن تعريف إعلانات المحتوى الخاصة بك في أي مكان في تطبيقك طالما تم تضمينها في دليل contentDir (افتراضيًا، ./src). وتطابق امتداد ملف إعلان المحتوى (افتراضيًا، .content.{ts,tsx,js,jsx,mjs,cjs}). لمزيد من التفاصيل، راجع وثائق إعلان المحتوى.

    الخطوة 7: استخدام المحتوى في الكود الخاص بك

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

    src/app/[locale]/page.tsx
    import { ClientComponentExample } from "@components/ClientComponentExample";import { ServerComponentExample } from "@components/ServerComponentExample";import { type Next14PageIntlayer, IntlayerClientProvider } from "next-intlayer";import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";const Page: Next14PageIntlayer = ({ params: { locale } }) => {  const content = useIntlayer("page", locale);  return (    <>      <p>        {content.getStarted.main}        <code>{content.getStarted.pageLink}</code>      </p>      <IntlayerServerProvider locale={locale}>        <IntlayerClientProvider locale={locale}>          <ServerComponentExample />          <ClientComponentExample />        </IntlayerClientProvider>      </IntlayerServerProvider>    </>  );};export default Page;
    • IntlayerClientProvider تُستخدم لتوفير اللغة المحلية لمكونات الجانب العميل. يمكن وضعها في أي مكون رئيسي، بما في ذلك التخطيط. ومع ذلك، يُوصى بوضعها في التخطيط لأن Next.js يشارك كود التخطيط عبر الصفحات، مما يجعله أكثر كفاءة. باستخدام IntlayerClientProvider في التخطيط، تتجنب إعادة تهيئته لكل صفحة، مما يحسن الأداء ويحافظ على سياق توطين متسق في جميع أنحاء التطبيق الخاص بك.
    • IntlayerServerProvider تُستخدم لتوفير اللغة المحلية للأطفال على الخادم. لا يمكن تعيينها في التخطيط.
    src/components/ClientComponentExample.tsx
    "use client";import type { FC } from "react";import { useIntlayer } from "next-intlayer";const ClientComponentExample: FC = () => {  const content = useIntlayer("client-component-example"); // إنشاء إعلان محتوى ذو صلة  return (    <div>      <h2>{content.title} </h2>      <p>{content.content}</p>    </div>  );};
    src/components/ServerComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer/server";const ServerComponentExample: FC = () => {  const content = useIntlayer("server-component-example"); // إنشاء إعلان محتوى ذو صلة  return (    <div>      <h2>{content.title} </h2>      <p>{content.content}</p>    </div>  );};

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

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    لمعرفة المزيد عن الخطاف useIntlayer، راجع التوثيق.

    (اختياري) الخطوة 8: تدويل البيانات الوصفية الخاصة بك

    في حال كنت ترغب في تدويل البيانات الوصفية الخاصة بك، مثل عنوان الصفحة، يمكنك استخدام وظيفة generateMetadata المقدمة من Next.js. داخل الوظيفة، استخدم وظيفة getTranslation لترجمة البيانات الوصفية الخاصة بك.

    src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx
    import {  type IConfigLocales,  getTranslation,  getMultilingualUrls,} from "intlayer";import type { Metadata } from "next";import type { LocalParams } from "next-intlayer";export const generateMetadata = ({  params: { locale },}: LocalParams): Metadata => {  const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);  /**   * ينشئ كائنًا يحتوي على جميع الروابط لكل لغة.   *   * مثال:   * ```ts   *  getMultilingualUrls('/about');   *   *  // النتيجة   *  // {   *  //   en: '/about',   *  //   fr: '/fr/about',   *  //   es: '/es/about',   *  // }   * ```   */  const multilingualUrls = getMultilingualUrls("/");  return {    title: t<string>({      ar: "عنواني",      en: "My title",      fr: "Mon titre",      es: "Mi título",    }),    description: t({      ar: "وصف صفحتي",      en: "My description",      fr: "Ma description",      es: "Mi descripción",    }),    alternates: {      canonical: "/",      languages: { ...multilingualUrls, "x-default": "/" },    },    openGraph: {      url: multilingualUrls[locale],    },  };};// ... بقية الكود

    لمعرفة المزيد عن تحسين البيانات الوصفية راجع التوثيق الرسمي لـ Next.js.

    (اختياري) الخطوة 9: تدويل ملف sitemap.xml و robots.txt

    لتدويل ملف sitemap.xml و robots.txt، يمكنك استخدام وظيفة getMultilingualUrls المقدمة من Intlayer. تتيح لك هذه الوظيفة إنشاء روابط متعددة اللغات لخريطة الموقع.

    src/app/sitemap.ts
    import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const sitemap = (): MetadataRoute.Sitemap => [  {    url: "https://example.com",    alternates: {      languages: getMultilingualUrls("https://example.com"),    },  },  {    url: "https://example.com/login",    alternates: {      languages: getMultilingualUrls("https://example.com/login"),    },  },  {    url: "https://example.com/register",    alternates: {      languages: getMultilingualUrls("https://example.com/register"),    },  },];export default sitemap;import { getMultilingualUrls } from "intlayer";const sitemap = () => [  {    url: "https://example.com",    alternates: {      languages: getMultilingualUrls("https://example.com"),    },  },  {    url: "https://example.com/login",    alternates: {      languages: getMultilingualUrls("https://example.com/login"),    },  },  {    url: "https://example.com/register",    alternates: {      languages: getMultilingualUrls("https://example.com/register"),    },  },];export default sitemap;
    src/app/robots.ts
    import type { MetadataRoute } from "next";import { getMultilingualUrls } from "intlayer";const getAllMultilingualUrls = (urls: string[]) =>  urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);const robots = (): MetadataRoute.Robots => ({  rules: {    userAgent: "*",    allow: ["/"],    disallow: getAllMultilingualUrls(["/login", "/register"]),  },  host: "https://example.com",  sitemap: `https://example.com/sitemap.xml`,});export default robots;

    تعرف على المزيد حول تحسين خريطة الموقع في الوثائق الرسمية لـ Next.js. تعرف على المزيد حول تحسين robots.txt في الوثائق الرسمية لـ Next.js.

    (اختياري) الخطوة 10: تغيير لغة المحتوى الخاص بك

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

    src/components/LocaleSwitcher.tsx
    "use client";import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocale } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocale();  return (    <div>      <button popoverTarget="localePopover">{getLocaleName(locale)}</button>      <div id="localePopover" popover="auto">        {availableLocales.map((localeItem) => (          <Link            href={getLocalizedUrl(pathWithoutLocale, localeItem)}            hrefLang={localeItem}            key={localeItem}            aria-current={locale === localeItem ? "page" : undefined}            onClick={(e) => {              e.preventDefault();              setLocale(localeItem);            }}          >            <span>              {/* اللغة المحلية - مثال: FR */}              {localeItem}            </span>            <span>              {/* اللغة في لغتها المحلية - مثال: Français */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* اللغة في اللغة الحالية - مثال: Francés مع تعيين اللغة الحالية إلى Locales.SPANISH */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* اللغة باللغة الإنجليزية - مثال: French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </Link>        ))}      </div>    </div>  );};

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

    (اختياري) الخطوة 11: إنشاء مكون رابط محلي

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

    هذا السلوك مفيد لعدة أسباب:

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

    فيما يلي تنفيذ مكون Link المحلي باستخدام TypeScript:

    src/components/Link.tsx
    "use client";import { getLocalizedUrl } from "intlayer";import NextLink, { type LinkProps as NextLinkProps } from "next/link";import { useLocale } from "next-intlayer";import { forwardRef, PropsWithChildren, type ForwardedRef } from "react";/** * وظيفة مساعدة للتحقق مما إذا كان عنوان URL معين خارجيًا. * إذا بدأ عنوان URL بـ http:// أو https://، فإنه يعتبر خارجيًا. */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:///.test(href ?? "");/** * مكون رابط مخصص يتكيف مع خاصية href بناءً على اللغة الحالية. * بالنسبة للروابط الداخلية، يستخدم `getLocalizedUrl` لإضافة بادئة للعنوان باللغة (مثل /ar/about). * يضمن ذلك أن يظل التنقل ضمن سياق اللغة نفسها. */export const Link = forwardRef<  HTMLAnchorElement,  PropsWithChildren<NextLinkProps>>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => {  const { locale } = useLocale();  const isExternalLink = checkIsExternalLink(href.toString());  // إذا كان الرابط داخليًا وتم توفير href صالح، احصل على عنوان URL المحلي.  const hrefI18n: NextLinkProps["href"] =    href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;  return (    <NextLink href={hrefI18n} ref={ref} {...props}>      {children}    </NextLink>  );});Link.displayName = "Link";

    كيف يعمل

    • اكتشاف الروابط الخارجية:
      تحدد وظيفة المساعدة checkIsExternalLink ما إذا كان عنوان URL خارجيًا. يتم ترك الروابط الخارجية دون تغيير لأنها لا تحتاج إلى توطين.

    • استرجاع اللغة الحالية:
      يوفر الخطاف useLocale اللغة الحالية (مثل ar للعربية).

    • توطين عنوان URL:
      بالنسبة للروابط الداخلية (أي غير الخارجية)، يتم استخدام getLocalizedUrl لإضافة بادئة تلقائيًا للعنوان باللغة الحالية. هذا يعني أنه إذا كان المستخدم يستخدم اللغة العربية، فإن تمرير /about كـ href سيحولها إلى /ar/about.

    • إرجاع الرابط:
      يقوم المكون بإرجاع عنصر <a> مع عنوان URL المحلي، مما يضمن أن التنقل يتماشى مع اللغة.

    من خلال دمج هذا المكون Link في جميع أنحاء تطبيقك، تحافظ على تجربة مستخدم متماسكة وواعية باللغة مع الاستفادة أيضًا من تحسين محركات البحث وسهولة الاستخدام.

    تكوين TypeScript

    يستخدم Intlayer توسيع الوحدات للحصول على فوائد TypeScript وجعل قاعدة الكود الخاصة بك أقوى.

    alt text

    alt text

    تأكد من أن تكوين TypeScript الخاص بك يتضمن الأنواع التي يتم إنشاؤها تلقائيًا.

    tsconfig.json
    {  // ... تكوينات TypeScript الحالية الخاصة بك  "include": [    // ... تكوينات TypeScript الحالية الخاصة بك    ".intlayer/**/*.ts", // تضمين الأنواع التي يتم إنشاؤها تلقائيًا  ],}

    تكوين Git

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

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

    .gitignore
    # تجاهل الملفات التي يتم إنشاؤها بواسطة Intlayer.intlayer

    المزيد

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

    رابط GitHub للتوثيق