الرئيسيةبيئة اختبارمعرض الأعمالتطبيقوثيقةمدونة
    • Englishالإنجليزية
      EN
    • русскийالروسية
      RU
    • 日本語اليابانية
      JA
    • françaisالفرنسية
      FR
    • 한국어الكورية
      KO
    • 中文الصينية
      ZH
    • españolالإسبانية
      ES
    • Deutschالألمانية
      DE
    • العربيةالعربية
      AR
    • italianoالإيطالية
      IT
    • British Englishالإنجليزية (المملكة المتحدة)
      EN-GB
    • portuguêsالبرتغالية
      PT
    • हिन्दीالهندية
      HI
    • Türkçeالتركية
      TR
    • polskiالبولندية
      PL
    • Indonesiaالإندونيسية
      ID
    • Tiếng Việtالفيتنامية
      VI
    • українськаالأوكرانية
      UK
    /
    تصفية المستندات حسب الإطار
    Alt+←
    لماذا Intlayer؟
    ابدأ
    مفهوم
    • كيف يعمل Intlayer
    • التكوين
    • TestFillBuildWatchExtractLoginPushPullConfigurationListVersionEditorLiveDebugDoc ReviewDoc TranslateSDK
    • المحرر المرئي
    • CMS
    • تكامل CI/CD
    • ترجمةجمعتعدادشرطجنسإدراجملفتداخلMarkdownHTMLجلب الوظيفة
    • ملف لكل لغة
    • محرر
    • الملء التلقائي
    • اختبار
    • تحسين الحزمة
    بيئة
    • Next.js 14 وموجه التطبيق
      Next.js 15
      Next.js بدون locale URL
      Next.js وموجه الصفحة
      المترجم
    • Tanstack Start Solid
    • Astro و React
      Astro و Svelte
      Astro و Vue
      Astro و Solid
      Astro و Preact
      Astro و Lit
      Astro و Vanilla JS
    • React Router v7
      React Router v7 (fs-routes)
      Compiler
    • Nuxt و Vue
    • Vite و Solid
    • SvelteKit
    • Vite و Preact
    • Vite و Vanilla JS
    • Vite و Lit
    • Angular 19 (Webpack)
      Analog
    • React CRA
    • React Native و Expo
    • Express.js
      NestJS
      Fastify
      Hono
      Adonis
    • Lynx و React
    Plugins
    • JSON
    • gettext (.po)
    امتداد VS Code
    وكيل
    • خادم MCP
    • مهارات الوكيل
    إصدارات
    • v8
    • v7
    • v6
    مؤشر أداء
    • Next.js
    • TanStack
    • Vue
    • Solid
    • Svelte
    مدونة
    طرح سؤال
    1. Documentation
    2. Next intl
    إنشاء:2025-10-05آخر تحديث:2025-10-05
    عرض قالب التطبيق على GitHub

    هذه الصفحة لديها قالب تطبيق متاح.

    استخدم هذه الصفحة والموفر AI الذي تريده
    ChatGPT
    Claude
    DeepSeek
    Google AI mode
    Gemini
    Perplexity
    Mistral
    Grok

    استخدم مساعدك المفضل للملخص واستخدم هذه الصفحة والموفر AI الذي تريده

    تمت ترجمة محتوى هذه الصفحة باستخدام الذكاء الاصطناعي.

    اعرض آخر نسخة المحتوى الأصلي باللغة الإنكليزية
    تعديل هذه الوثيقة

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

    رابط GitHub للتوثيق
    نسخ

    نسخ الـ Markdown من المستند إلى الحافظة

    ترجمة موقع Next.js 15 باستخدام next-intl مع Intlayer | التدويل (i18n)

    يرشدك هذا الدليل خلال أفضل الممارسات لاستخدام next-intl في تطبيق Next.js 15 (App Router)، ويُظهر كيفية إضافة Intlayer فوقه لإدارة الترجمة بشكل قوي وأتمتة العمليات.

    اطلع على المقارنة في next-i18next مقابل next-intl مقابل Intlayer.

    • للمبتدئين: اتبع الأقسام خطوة بخطوة للحصول على تطبيق متعدد اللغات يعمل.
    • للمطورين المتوسطين: انتبه إلى تحسين الحمولة وفصل الخادم/العميل.
    • للمطورين المتقدمين: لاحظ التوليد الثابت، والوسيط (middleware)، وتكامل SEO، وخطافات الأتمتة.

    ما سنغطيه:

    • الإعداد وهيكل الملفات
    • تحسين كيفية تحميل الرسائل
    • استخدام مكونات العميل والخادم
    • البيانات الوصفية، خريطة الموقع، والروبوتات لتحسين محركات البحث (SEO)
    • الوسيط لتوجيه اللغة
    • إضافة Intlayer فوق ذلك (CLI والأتمتة)

    إعداد تطبيقك باستخدام next-intl

    قم بتثبيت تبعيات next-intl -

    bash
    نسخ الكود

    نسخ الكود إلى الحافظة

    npm install next-intl
    bash
    نسخ الكود

    نسخ الكود إلى الحافظة

    .├── locales│   ├── en│   │  ├── common.json│   │  └── about.json│   ├── fr│   │  ├── common.json│   │  └── about.json│   └── es│      ├── common.json│      └── about.json└── src    ├── i18n.ts    ├── middleware.ts    ├── app    │   └── [locale]    │       ├── layout.tsx    │       └── about    │           └── page.tsx    └── components        ├── ClientComponentExample.tsx        └── ServerComponent.tsx

    الإعداد وتحميل المحتوى

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

    src/i18n.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import { getRequestConfig } from "next-intl/server";import { notFound } from "next/navigation";export const locales = ["en", "fr", "es"] as const;export const defaultLocale = "en" as const;async function loadMessages(locale: string) {  // تحميل فقط مساحات الأسماء التي يحتاجها التخطيط/الصفحات الخاصة بك  const [common, about] = await Promise.all([    import(`../locales/${locale}/common.json`).then((m) => m.default),    import(`../locales/${locale}/about.json`).then((m) => m.default),  ]);  return { common, about } as const;}export default getRequestConfig(async ({ locale }) => {  if (!locales.includes(locale)) notFound();  return {    messages: await loadMessages(locale),  };});
    src/app/[locale]/layout.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { ReactNode } from "react";import { locales } from "@/i18n";import {  getLocaleDirection,  unstable_setRequestLocale,} from "next-intl/server";export const dynamic = "force-static";export function generateStaticParams() {  return locales.map((locale) => ({ locale }));}export default async function LocaleLayout({  children,  params,}: {  children: ReactNode;  params: { locale: string };}) {  const { locale } = params;  // تعيين لغة الطلب النشطة لهذا العرض على الخادم (RSC)  unstable_setRequestLocale(locale);  const dir = getLocaleDirection(locale);  return (    <html lang={locale} dir={dir}>      <body>{children}</body>    </html>  );}
    src/app/[locale]/about/page.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    import { getTranslations, getMessages, getFormatter } from "next-intl/server";import { NextIntlClientProvider } from "next-intl";import pick from "lodash/pick";import ServerComponent from "@/components/ServerComponent";import ClientComponentExample from "@/components/ClientComponentExample";export const dynamic = "force-static";export default async function AboutPage({  params,}: {  params: { locale: string };}) {  const { locale } = params;  // يتم تحميل الرسائل على جانب الخادم. أرسل فقط ما هو مطلوب إلى العميل.  const messages = await getMessages();  const clientMessages = pick(messages, ["common", "about"]);  // الترجمات/التنسيق الخاصة بجانب الخادم فقط  const tAbout = await getTranslations("about");  const tCounter = await getTranslations("about.counter");  const format = await getFormatter();  const initialFormattedCount = format.number(0);  return (    <NextIntlClientProvider locale={locale} messages={clientMessages}>      <main>        <h1>{tAbout("title")}</h1>        <ClientComponentExample />        <ServerComponent          formattedCount={initialFormattedCount}          label={tCounter("label")}          increment={tCounter("increment")}        />      </main>    </NextIntlClientProvider>  );}

    الاستخدام في مكون العميل

    لنأخذ مثالاً على مكون عميل يعرض عدادًا.

    الترجمات (الشكل معاد استخدامه؛ قم بتحميلها في رسائل next-intl كما تفضل)

    locales/en/about.json
    نسخ الكود

    نسخ الكود إلى الحافظة

    {  "counter": {    "label": "Counter",    "increment": "Increment"  }}
    locales/fr/about.json
    نسخ الكود

    نسخ الكود إلى الحافظة

    {  "counter": {    "label": "Compteur",    "increment": "Incrémenter"  }}

    مكون العميل

    src/components/ClientComponentExample.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    "use client";import React, { useState } from "react";import { useTranslations, useFormatter } from "next-intl";const ClientComponentExample = () => {  // نطاق مباشر إلى الكائن المتداخل  const t = useTranslations("about.counter");  const format = useFormatter();  const [count, setCount] = useState(0);  return (    <div>      <p>{format.number(count)}</p>      <button        aria-label={t("label")}        onClick={() => setCount((count) => count + 1)}      >        {t("increment")}      </button>    </div>  );};

    لا تنسَ إضافة رسالة "about" في رسالة عميل الصفحة (قم بتضمين مساحات الأسماء التي يحتاجها عميلك فقط).

    الاستخدام في مكون الخادم

    هذا المكون الخاص بواجهة المستخدم هو مكون خادم ويمكن عرضه تحت مكون عميل (صفحة → عميل → خادم). حافظ عليه متزامنًا عن طريق تمرير سلاسل محسوبة مسبقًا.

    src/components/ServerComponent.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    type ServerComponentProps = {  formattedCount: string;  label: string;  increment: string;};const ServerComponent = ({  formattedCount,  label,  increment,}: ServerComponentProps) => {  return (    <div>      <p>{formattedCount}</p>      <button aria-label={label}>{increment}</button>    </div>  );};

    ملاحظات:

    • قم بحساب formattedCount على جانب الخادم (مثلاً، const initialFormattedCount = format.number(0)).
    • تجنب تمرير الدوال أو الكائنات غير القابلة للتسلسل إلى مكونات الخادم.
    src/app/[locale]/about/layout.tsx
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { Metadata } from "next";import { locales, defaultLocale } from "@/i18n";import { getTranslations } from "next-intl/server";function localizedPath(locale: string, path: string) {  return locale === defaultLocale ? path : "/" + locale + path;}export async function generateMetadata({  params,}: {  params: { locale: string };}): Promise<Metadata> {  const { locale } = params;  const t = await getTranslations({ locale, namespace: "about" });  const url = "/about";  const languages = Object.fromEntries(    locales.map((locale) => [locale, localizedPath(locale, url)])  );  return {    title: t("title"),    description: t("description"),    alternates: {      canonical: localizedPath(locale, url),      languages: { ...languages, "x-default": url },    },  };}// ... بقية كود الصفحة
    src/app/sitemap.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const formatterLocalizedPath = (locale: string, path: string) =>  locale === defaultLocale ? origin + path : origin + "/" + locale + path;export default function sitemap(): MetadataRoute.Sitemap {  const aboutLanguages = Object.fromEntries(    locales.map((l) => [l, formatterLocalizedPath(l, "/about")])  );  return [    {      url: formatterLocalizedPath(defaultLocale, "/about"),      lastModified: new Date(),      changeFrequency: "شهريًا",      priority: 0.7,      alternates: { languages: aboutLanguages },    },  ];}
    src/app/robots.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const withAllLocales = (path: string) => [  path,  ...locales    .filter((locale) => locale !== defaultLocale)    .map((locale) => "/" + locale + path),];export default function robots(): MetadataRoute.Robots {  const disallow = [    ...withAllLocales("/dashboard"),    ...withAllLocales("/admin"),  ];  return {    rules: { userAgent: "*", allow: ["/"], disallow },    host: origin,    sitemap: origin + "/sitemap.xml",  };}

    وسيط التوجيه للغة (Middleware for locale routing)

    أضف وسيطًا للتعامل مع اكتشاف اللغة والتوجيه:

    src/middleware.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import createMiddleware from "next-intl/middleware";import { locales, defaultLocale } from "@/i18n";export default createMiddleware({  locales: [...locales],  defaultLocale,  localeDetection: true,});export const config = {  // تخطي API، مكونات Next الداخلية والموارد الثابتة  matcher: ["/((?!api|_next|.*\\..*).*)"],};

    أفضل الممارسات

    • تعيين html lang و dir: في src/app/[locale]/layout.tsx، احسب dir عبر getLocaleDirection(locale) وقم بتعيين <html lang={locale} dir={dir}>.
    • تقسيم الرسائل حسب النطاق: نظم ملفات JSON لكل لغة ونطاق (مثل common.json، about.json).
    • تقليل حمولة العميل: في الصفحات، أرسل فقط النطاقات المطلوبة إلى NextIntlClientProvider (مثلًا، pick(messages, ['common', 'about'])).
    • تفضيل الصفحات الثابتة: صدّر export const dynamic = 'force-static' وقم بإنشاء معلمات ثابتة لجميع locales.
    • مكونات الخادم المتزامنة: مرّر سلاسل محسوبة مسبقًا (تسميات مترجمة، أرقام منسقة) بدلاً من استدعاءات غير متزامنة أو دوال غير قابلة للتسلسل.

    تنفيذ Intlayer فوق next-intl

    قم بتثبيت تبعيات intlayer:

    bash
    نسخ الكود

    نسخ الكود إلى الحافظة

    npm install intlayer @intlayer/sync-json-plugin --save-dev

    أنشئ ملف تكوين intlayer:

    intlayer.config.ts
    نسخ الكود

    نسخ الكود إلى الحافظة

    import { type IntlayerConfig, Locales } from "intlayer";import { syncJSON } from "@intlayer/sync-json-plugin";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  ai: {    apiKey: process.env.OPENAI_API_KEY,  },  plugins: [    // حافظ على هيكل المجلد لكل مساحة أسماء متزامنًا مع Intlayer    syncJSON({      format: "icu",      source: ({ key, locale }) => `./locales/${locale}/${key}.json`,    }),  ],};export default config;

    أضف سكريبتات package.json:

    package.json
    نسخ الكود

    نسخ الكود إلى الحافظة

    {  "scripts": {    "i18n:fill": "intlayer fill",    "i18n:test": "intlayer test"  }}

    ملاحظات:

    • intlayer fill: يستخدم مزود الذكاء الاصطناعي الخاص بك لملء الترجمات المفقودة بناءً على اللغات التي قمت بتكوينها.
    • intlayer test: يتحقق من الترجمات المفقودة أو غير الصالحة (استخدمه في بيئة التكامل المستمر CI).

    يمكنك تكوين الوسائط والمزودين؛ راجع Intlayer CLI.

    لماذا Intlayer؟
    Alt+→

    في هذه الصفحة

      المناقشات مجهولة الهوية ويتم مراجعتها بانتظام لمعالجة المشكلات الشائعة. لا تتردد في مشاركة أفكار الميزات أو التعليقات على الوثائق أو أي شيء يتعلق بـ Intlayer, نستخدم هذه المدخلات لتشكيل خارطة الطريق وتحسين المنتج.

      npm install next-intl
      .├── locales│   ├── en│   │  ├── common.json│   │  └── about.json│   ├── fr│   │  ├── common.json│   │  └── about.json│   └── es│      ├── common.json│      └── about.json└── src    ├── i18n.ts    ├── middleware.ts    ├── app    │   └── [locale]    │       ├── layout.tsx    │       └── about    │           └── page.tsx    └── components        ├── ClientComponentExample.tsx        └── ServerComponent.tsx
      import { getRequestConfig } from "next-intl/server";import { notFound } from "next/navigation";export const locales = ["en", "fr", "es"] as const;export const defaultLocale = "en" as const;async function loadMessages(locale: string) {  // تحميل فقط مساحات الأسماء التي يحتاجها التخطيط/الصفحات الخاصة بك  const [common, about] = await Promise.all([    import(`../locales/${locale}/common.json`).then((m) => m.default),    import(`../locales/${locale}/about.json`).then((m) => m.default),  ]);  return { common, about } as const;}export default getRequestConfig(async ({ locale }) => {  if (!locales.includes(locale)) notFound();  return {    messages: await loadMessages(locale),  };});
      import type { ReactNode } from "react";import { locales } from "@/i18n";import {  getLocaleDirection,  unstable_setRequestLocale,} from "next-intl/server";export const dynamic = "force-static";export function generateStaticParams() {  return locales.map((locale) => ({ locale }));}export default async function LocaleLayout({  children,  params,}: {  children: ReactNode;  params: { locale: string };}) {  const { locale } = params;  // تعيين لغة الطلب النشطة لهذا العرض على الخادم (RSC)  unstable_setRequestLocale(locale);  const dir = getLocaleDirection(locale);  return (    <html lang={locale} dir={dir}>      <body>{children}</body>    </html>  );}
      import { getTranslations, getMessages, getFormatter } from "next-intl/server";import { NextIntlClientProvider } from "next-intl";import pick from "lodash/pick";import ServerComponent from "@/components/ServerComponent";import ClientComponentExample from "@/components/ClientComponentExample";export const dynamic = "force-static";export default async function AboutPage({  params,}: {  params: { locale: string };}) {  const { locale } = params;  // يتم تحميل الرسائل على جانب الخادم. أرسل فقط ما هو مطلوب إلى العميل.  const messages = await getMessages();  const clientMessages = pick(messages, ["common", "about"]);  // الترجمات/التنسيق الخاصة بجانب الخادم فقط  const tAbout = await getTranslations("about");  const tCounter = await getTranslations("about.counter");  const format = await getFormatter();  const initialFormattedCount = format.number(0);  return (    <NextIntlClientProvider locale={locale} messages={clientMessages}>      <main>        <h1>{tAbout("title")}</h1>        <ClientComponentExample />        <ServerComponent          formattedCount={initialFormattedCount}          label={tCounter("label")}          increment={tCounter("increment")}        />      </main>    </NextIntlClientProvider>  );}
      {  "counter": {    "label": "Counter",    "increment": "Increment"  }}
      {  "counter": {    "label": "Compteur",    "increment": "Incrémenter"  }}
      "use client";import React, { useState } from "react";import { useTranslations, useFormatter } from "next-intl";const ClientComponentExample = () => {  // نطاق مباشر إلى الكائن المتداخل  const t = useTranslations("about.counter");  const format = useFormatter();  const [count, setCount] = useState(0);  return (    <div>      <p>{format.number(count)}</p>      <button        aria-label={t("label")}        onClick={() => setCount((count) => count + 1)}      >        {t("increment")}      </button>    </div>  );};
      type ServerComponentProps = {  formattedCount: string;  label: string;  increment: string;};const ServerComponent = ({  formattedCount,  label,  increment,}: ServerComponentProps) => {  return (    <div>      <p>{formattedCount}</p>      <button aria-label={label}>{increment}</button>    </div>  );};
      import type { Metadata } from "next";import { locales, defaultLocale } from "@/i18n";import { getTranslations } from "next-intl/server";function localizedPath(locale: string, path: string) {  return locale === defaultLocale ? path : "/" + locale + path;}export async function generateMetadata({  params,}: {  params: { locale: string };}): Promise<Metadata> {  const { locale } = params;  const t = await getTranslations({ locale, namespace: "about" });  const url = "/about";  const languages = Object.fromEntries(    locales.map((locale) => [locale, localizedPath(locale, url)])  );  return {    title: t("title"),    description: t("description"),    alternates: {      canonical: localizedPath(locale, url),      languages: { ...languages, "x-default": url },    },  };}// ... بقية كود الصفحة
      import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const formatterLocalizedPath = (locale: string, path: string) =>  locale === defaultLocale ? origin + path : origin + "/" + locale + path;export default function sitemap(): MetadataRoute.Sitemap {  const aboutLanguages = Object.fromEntries(    locales.map((l) => [l, formatterLocalizedPath(l, "/about")])  );  return [    {      url: formatterLocalizedPath(defaultLocale, "/about"),      lastModified: new Date(),      changeFrequency: "شهريًا",      priority: 0.7,      alternates: { languages: aboutLanguages },    },  ];}
      import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const withAllLocales = (path: string) => [  path,  ...locales    .filter((locale) => locale !== defaultLocale)    .map((locale) => "/" + locale + path),];export default function robots(): MetadataRoute.Robots {  const disallow = [    ...withAllLocales("/dashboard"),    ...withAllLocales("/admin"),  ];  return {    rules: { userAgent: "*", allow: ["/"], disallow },    host: origin,    sitemap: origin + "/sitemap.xml",  };}
      import createMiddleware from "next-intl/middleware";import { locales, defaultLocale } from "@/i18n";export default createMiddleware({  locales: [...locales],  defaultLocale,  localeDetection: true,});export const config = {  // تخطي API، مكونات Next الداخلية والموارد الثابتة  matcher: ["/((?!api|_next|.*\\..*).*)"],};
      npm install intlayer @intlayer/sync-json-plugin --save-dev
      import { type IntlayerConfig, Locales } from "intlayer";import { syncJSON } from "@intlayer/sync-json-plugin";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  ai: {    apiKey: process.env.OPENAI_API_KEY,  },  plugins: [    // حافظ على هيكل المجلد لكل مساحة أسماء متزامنًا مع Intlayer    syncJSON({      format: "icu",      source: ({ key, locale }) => `./locales/${locale}/${key}.json`,    }),  ],};export default config;
      {  "scripts": {    "i18n:fill": "intlayer fill",    "i18n:test": "intlayer test"  }}