البدء في التدويل (i18n) باستخدام Intlayer و Next.js 15 App Router
ما هو Intlayer؟
Intlayer هي مكتبة مبتكرة ومتاحة للمصدر المفتوح للتدويل (i18n) تهدف إلى تبسيط دعم اللغات المتعددة في تطبيقات الويب الحديثة. يندمج Intlayer بسلاسة مع أحدث إطار عمل Next.js 15، بما في ذلك App Router القوي. إنه مُحسّن للعمل مع Server Components لأداء جيد ويعمل بشكل كامل مع Turbopack.
مع Intlayer، يمكنك:
- إدارة الترجمات بسهولة باستخدام قواميس تعبيرية على مستوى المكون.
- تدويل البيانات الوصفية والطرق والمحتوى بشكل ديناميكي.
- الوصول إلى الترجمات في كل من المكونات ذات الجانب العميل والمكونات ذات الجانب الخادم.
- ضمان دعم TypeScript مع الأنواع التي تُنشأ تلقائيًا، مما يحسن من الإكمال التلقائي واكتشاف الأخطاء.
- الاستفادة من الميزات المتقدمة، مثل اكتشاف اللغة الديناميكي والتبديل.
Intlayer متوافق مع Next.js 12 و 13 و 14 و 15. إذا كنت تستخدم Next.js Page Router، يمكنك الرجوع إلى هذه الدليل. لمستخدمي Next.js 12 و 13 و 14 مع App Router، يرجى الرجوع إلى هذه الدليل.
دليل خطوة بخطوة لإعداد Intlayer في تطبيق Next.js
الخطوة 1: تثبيت التبعيات
تثبيت الحزم اللازمة باستخدام npm:
npm install intlayer next-intlayer
intlayer
الحزمة الأساسية التي توفر أدوات التدويل لإدارة التكوين والترجمة و إعلان المحتوى وعمليات التحويل و أوامر CLI.
next-intlayer
الحزمة التي تدمج Intlayer مع Next.js. توفر موفري السياق والهوكات لتدويل Next.js. بالإضافة إلى ذلك، تتضمن مكون Next.js الإضافي لتكامل Intlayer مع Webpack أو Turbopack، بالإضافة إلى الواجهة الوسيطة لاكتشاف اللغة المفضلة للمستخدم، وإدارة ملفات تعريف الارتباط، والتعامل مع إعادة توجيه URL.
الخطوة 2: تكوين مشروعك
إنشاء ملف تكوين لتكوين لغات تطبيقك:
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:
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: تكوين الواجهة الوسيطة لاكتشاف اللغة
إعداد الواجهة الوسيطة لاكتشاف اللغة المفضلة للمستخدم:
export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = { matcher: "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",};
يتم استخدام intlayerMiddleware لاكتشاف اللغة المفضلة للمستخدم وإعادة توجيههم إلى عنوان URL المناسب كما هو محدد في التكوين. بالإضافة إلى ذلك، فإنه يتيح حفظ اللغة المفضلة للمستخدم في ملف تعريف الارتباط.
الخطوة 5: تعريف طرق اللغات الديناميكية
قم بإزالة كل شيء من RootLayout واستبداله بالكود التالي:
import type { PropsWithChildren, FC } from "react";import "./globals.css";const RootLayout: FC<PropsWithChildren> = ({ children }) => children;export default RootLayout;
الاحتفاظ بمكون RootLayout فارغًا يتيح لك تعيين صفات lang و dir لعلامة <html>.
لتنفيذ التوجيه الديناميكي، قدم المسار للغة بإضافة تخطيط جديد في دليل [locale] الخاص بك:
import type { NextLayoutIntlayer } from "next-intlayer";import { Inter } from "next/font/google";import { getHTMLTextDir } from "intlayer";const inter = Inter({ subsets: ["latin"] });const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => { const { locale } = await params; return ( <html lang={locale} dir={getHTMLTextDir(locale)}> <body className={inter.className}>{children}</body> </html> );};export default LocaleLayout;
يتم استخدام جزء مسار [locale] لتعريف اللغة. مثال: /en-US/about ستشير إلى en-US و /fr/about إلى fr.
ثم، نفّذ دالة generateStaticParams في تخطيط تطبيقك.
export { generateStaticParams } from "next-intlayer"; // خط لإدراجهconst LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => { /*... بقية الكود*/};export default LocaleLayout;
يضمن generateStaticParams أن تطبيقك يبني مسبقًا الصفحات اللازمة لجميع اللغات، مما يقلل من حساب وقت التشغيل ويحسن تجربة المستخدم. للحصول على مزيد من التفاصيل، يرجى الرجوع إلى وثائق Next.js حول generateStaticParams.
الخطوة 6: إعلان المحتوى الخاص بك
قم بإنشاء وإدارة إعلانات محتواك لتخزين الترجمات:
import { t, type DeclarationContent } from "intlayer";const pageContent = { key: "page", content: { getStarted: { main: t({ en: "Get started by editing", fr: "Commencez par éditer", es: "Comience por editar", }), pageLink: "src/app/page.tsx", }, },} satisfies DeclarationContent;export default pageContent;
يمكن تعريف إعلانات المحتوى الخاصة بك في أي مكان في تطبيقك طالما أنها مضمنة في دليل contentDir (بشكل افتراضي، ./src). و تتوافق مع امتداد ملف إعلان المحتوى (بشكل افتراضي، .content.{ts,tsx,js,jsx,mjs,cjs}). للحصول على مزيد من التفاصيل، يرجى الرجوع إلى وثائق إعلان المحتوى.
الخطوة 7: استخدام المحتوى في الكود الخاص بك
الوصول إلى قواميس المحتوى الخاصة بك في جميع أنحاء تطبيقك:
import type { FC } from "react";import { ClientComponentExample } from "@components/ClientComponentExample";import { ServerComponentExample } from "@components/ServerComponentExample";import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";const PageContent: FC = () => { const { title, content } = useIntlayer("page"); return ( <> <p>{content.getStarted.main}</p> <code>{content.getStarted.pageLink}</code> </> );};const Page: NextPageIntlayer = async ({ params }) => { const { locale } = await params; return ( <> <IntlayerServerProvider locale={locale}> <PageContent /> <ServerComponentExample /> <IntlayerClientProvider locale={locale}> <ClientComponentExample /> </IntlayerClientProvider> </IntlayerServerProvider> </> );};export default Page;
- IntlayerClientProvider تُستخدم لتوفير اللغة إلى مكونات جانب العميل. يمكن وضعها في أي مكون أبوي، بما في ذلك التخطيط. ومع ذلك، يُوصى بوضعها في التخطيط لأن Next.js يشارك شفرة التخطيط عبر الصفحات، مما يجعلها أكثر كفاءة. باستخدام IntlayerClientProvider في التخطيط، يمكنك تجنب إعادة تهيئتها لكل صفحة، مما يحسن الأداء ويحافظ على سياق محلي متسق في تطبيقك.
- IntlayerServerProvider تُستخدم لتوفير اللغة للأطفال في الخادم. لا يمكن تعيينها في التخطيط.
لا يمكن للتخطيط والصفحة مشاركة سياق خادم مشترك لأن نظام سياق الخادم يعتمد على متجر بيانات لكل طلب (عبر ذاكرة التخزين المؤقت لـ React آلية)، مما يتسبب في إعادة إنشاء كل "سياق" لقطاعات مختلفة من التطبيق. سيؤدي وضع الموفر في تخطيط مشترك إلى كسر هذه العزلة، مما يمنع النشر الصحيح لقيم سياق الخادم إلى مكونات الخادم الخاصة بك.
"use client";import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ClientComponentExample: FC = () => { const content = useIntlayer("client-component-example"); // إنشاء إعلان محتوى خاص return ( <div> <h2>{content.title} </h2> <p>{content.content}</p> </div> );};
import type { FC } from "react";import { useIntlayer } from "next-intlayer/server";export 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. داخل الدالة، استخدم دالة getTranslationContent لترجمة بياناتك الوصفية.
import { type IConfigLocales, getTranslationContent, getMultilingualUrls,} from "intlayer";import type { Metadata } from "next";import type { LocalPromiseParams } from "next-intlayer";export const generateMetadata = async ({ params: { locale },}: LocalPromiseParams): Metadata => { const { locale } = await params; const t = <T>(content: IConfigLocales<T>) => getTranslationContent(content, locale); /** * Generates an object containing all url for each locale. * * Example: * ```ts * getMultilingualUrls('/about'); * * // Returns * // { * // en: '/about', * // fr: '/fr/about', * // es: '/es/about', * // } * ``` */ const multilingualUrls = getMultilingualUrls("/"); return { title: t<string>({ en: "My title", fr: "Mon titre", es: "Mi título", }), description: t({ 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. هذه الدالة تتيح لك إنشاء URLs متعددة اللغات لخرائط موقعك.
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 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. هذه الدالة تتيح لك تعيين لغة التطبيق وتحديث المحتوى وفقًا لذلك.
"use client";import type { FC } from "react";import { Locales, getHTMLTextDir, getLocaleName, getLocalizedUrl,} from "intlayer";import { useLocale } from "next-intlayer";import Link from "next/link";export const LocaleSwitcher: FC = () => { const { locale, pathWithoutLocale, availableLocales, setLocale } = useLocale(); return ( <ol> {availableLocales.map((localeItem) => ( <li key={localeItem}> <Link href={getLocalizedUrl(pathWithoutLocale, localeItem)} hrefLang={localeItem} aria-current={locale === localeItem ? "page" : undefined} onClick={(e) => { e.preventDefault(); setLocale(localeItem); }} > <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}> {/* اللغة بالإنجليزية - مثال: الفرنسية */} {getLocaleName(localeItem, Locales.ENGLISH)} </span> <span> {/* اللغة بلهجتها الخاصة - مثال: FR */} {localeItem} </span> </Link> </li> ))} </ol> );};
مراجع الوثائق:
تكوين TypeScript
يستخدم Intlayer زيادة في الوحدة للاستفادة من TypeScript وتعزيز قاعدة الكود الخاصة بك.
تأكد من أن تكوين TypeScript الخاص بك يتضمن الأنواع المُنشأة تلقائيًا.
{ // ... تكوينات TypeScript الموجودة لديك "include": [ // ... تكوينات TypeScript الموجودة لديك "types", // تضمين الأنواع التلقائية الإنشاء ],}
تكوين Git
يوصى بتجاهل الملفات المُنتجة بواسطة Intlayer. هذا يسمح لك بتجنب الالتزام بها في مستودع Git الخاص بك.
للقيام بذلك، يمكنك إضافة التعليمات التالية إلى ملف .gitignore الخاص بك:
# تجاهل الملفات المُنتجة بواسطة Intlayer.intlayer
إذا كان لديك فكرة لتحسين هذه الوثيقة، فلا تتردد في المساهمة من خلال تقديم طلب سحب على GitHub.
رابط GitHub للتوثيق