Getting Started Internationalizing (i18n) with Intlayer and Next.js using Page Router

    ما هو Intlayer؟

    Intlayer هو مكتبة مفتوحة المصدر مبتكرة لتدويل التطبيقات (i18n) مصممة لتبسيط دعم اللغات المتعددة في تطبيقات الويب الحديثة. يتكامل Intlayer بسلاسة مع أحدث إطار عمل Next.js، بما في ذلك Page Router التقليدي.

    مع Intlayer، يمكنك:

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

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


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

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

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

    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 = {  // تكوين Next.js الحالي الخاص بك};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).*)",};

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

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

    قم بتنفيذ التوجيه الديناميكي لتقديم المحتوى المحلي بناءً على لغة المستخدم.

    1. إنشاء صفحات خاصة باللغة:

      أعد تسمية ملف الصفحة الرئيسي الخاص بك ليشمل الجزء الديناميكي [locale].

      bash
      mv src/pages/index.tsx src/pages/[locale]/index.tsx
    2. تحديث _app.tsx للتعامل مع التوطين:

      قم بتعديل _app.tsx لتضمين مزودي Intlayer.

      src/pages/_app.tsx
      import type { FC } from "react";import type { AppProps } from "next/app";import { IntlayerClientProvider } from "next-intlayer";const App = FC<AppProps>({ Component, pageProps }) => {  const { locale } = pageProps;  return (    <IntlayerClientProvider locale={locale}>      <Component {...pageProps} />    </IntlayerClientProvider>  );}export default MyApp;
    3. إعداد getStaticPaths و getStaticProps:

      في [locale]/index.tsx، قم بتعريف المسارات والخصائص للتعامل مع اللغات المختلفة.

      src/pages/[locale]/index.tsx
      import type { FC } from "react";import type { GetStaticPaths, GetStaticProps } from "next";import { type Locales, getConfiguration } from "intlayer";const HomePage: FC = () => <div>{/* المحتوى الخاص بك هنا */}</div>;export const getStaticPaths: GetStaticPaths = () => {  const { internationalization } = getConfiguration();  const { locales } = internationalization;  const paths = locales.map((locale) => ({    params: { locale },  }));  return { paths, fallback: false };};export const getStaticProps: GetStaticProps = ({ params }) => {  const locale = params?.locale as string;  return {    props: {      locale,    },  };};export default HomePage;

    getStaticPaths و getStaticProps يضمنان أن تطبيقك يقوم ببناء الصفحات الضرورية مسبقًا لجميع اللغات في Next.js Page Router. هذا النهج يقلل من الحساب أثناء وقت التشغيل ويؤدي إلى تحسين تجربة المستخدم. لمزيد من التفاصيل، راجع وثائق Next.js حول getStaticPaths و getStaticProps.

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

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

    src/pages/[locale]/home.content.ts
    import { t, type Dictionary } from "intlayer";const homeContent = {  key: "home",  content: {    title: t({      en: "Welcome to My Website",      fr: "Bienvenue sur mon site Web",      es: "Bienvenido a mi sitio web",      ar: "مرحبًا بك في موقعي الإلكتروني",    }),    description: t({      en: "Get started by editing this page.",      fr: "Commencez par éditer cette page.",      es: "Comience por editar esta página.",      ar: "ابدأ بتحرير هذه الصفحة.",    }),  },} satisfies Dictionary;export default homeContent;

    لمزيد من المعلومات حول إعلان المحتوى، راجع دليل إعلان المحتوى.

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

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

    src/pages/[locale]/index.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";import { ComponentExample } from "@components/ComponentExample";const HomePage: FC = () => {  const content = useIntlayer("home");  return (    <div>      <h1>{content.title}</h1>      <p>{content.description}</p>      <ComponentExample />      {/* مكونات إضافية */}    </div>  );};// ... بقية الكود، بما في ذلك getStaticPaths و getStaticPropsexport default HomePage;
    src/components/ComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ComponentExample: FC = () => {  const content = useIntlayer("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: تدويل البيانات الوصفية الخاصة بك

    لتدويل البيانات الوصفية مثل عناوين الصفحات والأوصاف، استخدم وظيفة getStaticProps بالاقتران مع وظيفة getTranslation الخاصة بـ Intlayer.

    src/pages/[locale]/index.tsx
    import { GetStaticPaths, GetStaticProps } from "next";import { type IConfigLocales, getTranslation, Locales } from "intlayer";import { useIntlayer } from "next-intlayer";interface HomePageProps {  locale: string;  metadata: Metadata;}const HomePage = ({ metadata }: HomePageProps) => {  // يمكن استخدام البيانات الوصفية في الرأس أو المكونات الأخرى حسب الحاجة  return (    <div>      <Head>        <title>{metadata.title}</title>        <meta name="description" content={metadata.description} />      </Head>      {/* محتوى إضافي */}    </div>  );};export const getStaticProps: GetStaticProps = async ({ params }) => {  const locale = params?.locale as string;  const t = <T,>(content: IConfigLocales<T>) => getTranslation(content, locale);  const metadata = {    title: t({      en: "My Website",      fr: "Mon Site Web",      es: "Mi Sitio Web",      ar: "موقعي الإلكتروني",    }),    description: t({      en: "Welcome to my website.",      fr: "Bienvenue sur mon site Web.",      es: "Bienvenido a mi sitio web.",      ar: "مرحبًا بك في موقعي الإلكتروني.",    }),  };  return {    props: {      locale,      metadata,    },  };};export default HomePage;// ... بقية الكود بما في ذلك getStaticPaths

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

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

    src/components/LanguageSwitcher.tsx
    import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocalePageRouter } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocalePageRouter();  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>              {/* Locale - e.g. FR */}              {localeItem}            </span>            <span>              {/* Language in its own Locale - e.g. Français */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* Language in current Locale - e.g. Francés with current locale set to Locales.SPANISH */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* Language in English - e.g. French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </Link>        ))}      </div>    </div>  );};

    واجهة برمجة التطبيقات useLocalePageRouter هي نفسها useLocale. لمعرفة المزيد حول الخطاف useLocale، راجع الوثائق.

    مراجع الوثائق:

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

    لضمان أن التنقل في تطبيقك يحترم اللغة الحالية، يمكنك إنشاء مكون Link مخصص. يقوم هذا المكون تلقائيًا بإضافة بادئة لعناوين URL الداخلية باللغة الحالية، بحيث يتم توجيه المستخدم الفرنسي، على سبيل المثال، إلى /fr/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` لإضافة بادئة لعناوين URL باللغة (على سبيل المثال، /fr/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 اللغة الحالية (على سبيل المثال، fr للفرنسية).

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

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

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

    تكوين TypeScript

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

    alt text

    alt text

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

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

    تكوين Git

    للحفاظ على نظافة مستودعك وتجنب الالتزام بالملفات المولدة، يُوصى بتجاهل الملفات التي تم إنشاؤها بواسطة Intlayer.

    أضف الأسطر التالية إلى ملف .gitignore الخاص بك:

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

    موارد إضافية

    باتباع هذا الدليل، يمكنك دمج Intlayer بشكل فعال في تطبيق Next.js الخاص بك باستخدام Page Router، مما يتيح دعم التدويل القوي والقابل للتطوير لمشاريع الويب الخاصة بك.

    الذهاب أبعد

    للمضي قدمًا، يمكنك تنفيذ المحرر المرئي أو نقل المحتوى الخاص بك باستخدام CMS.

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

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