@intlayer/docs 6.1.4 → 6.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1135 -75
  2. package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
  3. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1139 -72
  4. package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
  5. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +224 -240
  6. package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
  7. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1134 -37
  8. package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
  9. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1122 -64
  10. package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
  11. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1132 -75
  12. package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
  13. package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
  14. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1120 -55
  15. package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
  16. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1140 -76
  17. package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
  18. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1129 -73
  19. package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
  20. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1133 -76
  21. package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
  22. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1142 -74
  23. package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
  24. package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
  25. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
  26. package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
  27. package/dist/cjs/generated/blog.entry.cjs +16 -0
  28. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  29. package/dist/esm/generated/blog.entry.mjs +16 -0
  30. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  31. package/dist/types/generated/blog.entry.d.ts +1 -0
  32. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  33. package/docs/en/interest_of_intlayer.md +2 -2
  34. package/package.json +10 -10
  35. package/src/generated/blog.entry.ts +16 -0
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  createdAt: 2025-08-23
3
- updatedAt: 2025-08-23
3
+ updatedAt: 2025-09-29
4
4
  title: next-i18next مقابل next-intl مقابل Intlayer
5
- description: مقارنة بين next-i18next و next-intl و Intlayer لتدويل (i18n) تطبيق Next.js
5
+ description: مقارنة بين next-i18next و next-intl و Intlayer لتدويل تطبيق Next.js
6
6
  keywords:
7
7
  - next-intl
8
8
  - next-i18next
@@ -10,7 +10,7 @@ keywords:
10
10
  - التدويل
11
11
  - مدونة
12
12
  - Next.js
13
- - جافاسكريبت
13
+ - جافا سكريبت
14
14
  - React
15
15
  slugs:
16
16
  - blog
@@ -19,144 +19,1204 @@ slugs:
19
19
 
20
20
  # next-i18next مقابل next-intl مقابل intlayer | التدويل في Next.js (i18n)
21
21
 
22
- تُقارن هذه الدليل بين ثلاث خيارات شائعة للتدويل (i18n) لـ **Next.js**: **next-intl**، **next-i18next**، و **Intlayer**.
23
- نركز على **موجه التطبيقات في Next.js 13+** (مع **مكونات خادم React**) ونقيّم:
22
+ لنلقي نظرة على أوجه التشابه والاختلاف بين ثلاثة خيارات للتدويل في Next.js: next-i18next، next-intl، و Intlayer.
23
+
24
+ هذا ليس درسًا كاملاً. إنها مقارنة لمساعدتك في الاختيار.
25
+
26
+ نركز على **موجه التطبيقات في Next.js 13+** (مع **مكونات خادم React**) ونقيم:
24
27
 
25
28
  1. **البنية والتنظيم المحتوى**
26
29
  2. **TypeScript والأمان**
27
30
  3. **معالجة الترجمات المفقودة**
28
- 4. **التوجيه والوسيط**
31
+ 4. **التوجيه والوسيطات**
29
32
  5. **الأداء وسلوك التحميل**
30
33
  6. **تجربة المطور (DX)، الأدوات والصيانة**
31
34
  7. **تحسين محركات البحث (SEO) وقابلية التوسع في المشاريع الكبيرة**
32
35
 
33
- > **ملخص**: يمكن لجميع الثلاثة تعريب تطبيق Next.js. إذا كنت تريد **محتوى مخصص للمكونات**، **أنواع TypeScript صارمة**، **فحوصات مفاتيح مفقودة أثناء البناء**، **قواميس معزولة بالشجرة (tree-shaken)**، و**موجه تطبيقات من الدرجة الأولى + مساعدي SEO**، فإن **Intlayer** هو الخيار الأكثر اكتمالاً وحداثة.
36
+ > **ملخص**: يمكن لجميع الثلاثة تعريب تطبيق Next.js. إذا كنت تريد **محتوى مخصص للمكونات**، **أنواع TypeScript صارمة**، **فحوصات المفاتيح المفقودة أثناء البناء**، **قواميس مُحسّنة بالتخلص من الشجر غير المستخدم**، و**موجه تطبيقات من الدرجة الأولى + مساعدات SEO**، فإن **Intlayer** هو الخيار الأكثر اكتمالًا وحداثة.
37
+
38
+ > من الالتباسات الشائعة بين المطورين هو الاعتقاد بأن `next-intl` هو نسخة Next.js من `react-intl`. هذا غير صحيح — فـ `next-intl` تتم صيانته بواسطة [Amann](https://github.com/amannn)، بينما `react-intl` تتم صيانته بواسطة [FormatJS](https://github.com/formatjs/formatjs).
34
39
 
35
40
  ---
36
41
 
37
- ## التموقع على مستوى عالٍ
42
+ ## باختصار
38
43
 
39
44
  - **next-intl** - تنسيق رسائل خفيف الوزن وبسيط مع دعم قوي لـ Next.js. الكتالوجات المركزية شائعة؛ تجربة المطور بسيطة، لكن الأمان والصيانة على نطاق واسع تبقى في الغالب مسؤوليتك.
40
- - **next-i18next** - i18next في قالب Next.js. نظام بيئي ناضج وميزات عبر الإضافات (مثل ICU)، لكن التكوين قد يكون مطولًا وتميل الكتالوجات إلى المركزية مع نمو المشاريع.
41
- - **Intlayer** - نموذج محتوى يركز على المكونات لـ Next.js، **أنواع TypeScript صارمة**، **فحوصات أثناء وقت البناء**، **عزل القواميس بالشجرة (tree-shaking)**، **وسائط مدمجة ومساعدي SEO**، محرر/نظام إدارة محتوى بصري اختياري، وترجمات بمساعدة الذكاء الاصطناعي.
45
+ - **next-i18next** - i18next في هيئة Next.js. نظام بيئي ناضج وميزات عبر الإضافات (مثل ICU)، لكن التهيئة قد تكون مطولة وتميل الكتالوجات إلى المركزية مع نمو المشاريع.
46
+ - **Intlayer** - نموذج محتوى يركز على المكونات لـ Next.js، **كتابة صارمة بـ TS**، **فحوصات وقت البناء**، **إزالة الشيفرة غير المستخدمة (tree-shaking)**، **وسائط مدمجة ومساعدات SEO**، محرر/نظام إدارة محتوى بصري اختياري، وترجمات بمساعدة الذكاء الاصطناعي.
42
47
 
43
48
  ---
44
49
 
45
- ## مقارنة الميزات جنبًا إلى جنب (مركز على Next.js)
50
+ | Library | GitHub Stars | Total Commits | Last Commit | First Version | NPM Version | NPM Downloads |
51
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
52
+ | `aymericzip/intlayer` | [![GitHub Repo stars](https://img.shields.io/github/stars/aymericzip/intlayer?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/aymericzip/intlayer/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/aymericzip/intlayer?style=for-the-badge&label=commits)](https://github.com/aymericzip/intlayer/commits) | [![Last Commit](https://img.shields.io/github/last-commit/aymericzip/intlayer?style=for-the-badge)](https://github.com/aymericzip/intlayer/commits) | April 2024 | [![npm](https://img.shields.io/npm/v/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) | [![npm downloads](https://img.shields.io/npm/dm/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) |
53
+ | `amannn/next-intl` | [![GitHub Repo stars](https://img.shields.io/github/stars/amannn/next-intl?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/amannn/next-intl/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/amannn/next-intl?style=for-the-badge&label=commits)](https://github.com/amannn/next-intl/commits) | [![Last Commit](https://img.shields.io/github/last-commit/amannn/next-intl?style=for-the-badge)](https://github.com/amannn/next-intl/commits) | Nov 2020 | [![npm](https://img.shields.io/npm/v/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) | [![npm downloads](https://img.shields.io/npm/dm/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) |
54
+ | `i18next/i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/i18next?style=for-the-badge&label=commits)](https://github.com/i18next/i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/i18next?style=for-the-badge)](https://github.com/i18next/i18next/commits) | Jan 2012 | [![npm](https://img.shields.io/npm/v/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) | [![npm downloads](https://img.shields.io/npm/dm/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) |
55
+ | `i18next/next-i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/next-i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/next-i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/next-i18next?style=for-the-badge&label=commits)](https://github.com/i18next/next-i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/next-i18next?style=for-the-badge)](https://github.com/i18next/next-i18next/commits) | Nov 2018 | [![npm](https://img.shields.io/npm/v/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) | [![npm downloads](https://img.shields.io/npm/dm/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) |
46
56
 
47
- | الميزة | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
48
- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
49
- | **الترجمات بالقرب من المكونات** | ✅ نعم، المحتوى موضوع بجانب كل مكون | ❌ لا | ❌ لا |
50
- | **تكامل TypeScript** | ✅ متقدم، أنواع صارمة مولدة تلقائيًا | ✅ جيد | ⚠️ أساسي |
51
- | **كشف الترجمات المفقودة** | ✅ تمييز أخطاء TypeScript وتحذير/خطأ أثناء وقت البناء | ⚠️ استرجاع وقت التشغيل | ⚠️ استرجاع وقت التشغيل |
52
- | **المحتوى الغني (JSX/Markdown/المكونات)** | ✅ دعم مباشر | ❌ غير مصمم للعقد الغنية | ⚠️ محدود |
53
- | **الترجمة المدعومة بالذكاء الاصطناعي** | ✅ نعم، يدعم عدة مزودي ذكاء اصطناعي. يمكن استخدامه باستخدام مفاتيح API الخاصة بك. يأخذ في الاعتبار سياق تطبيقك ونطاق المحتوى | ❌ لا | ❌ لا |
54
- | **المحرر المرئي** | ✅ نعم، محرر مرئي محلي + نظام إدارة محتوى اختياري؛ يمكنه إخراج محتوى قاعدة الشيفرة؛ قابل للتضمين | ❌ لا / متوفر عبر منصات التوطين الخارجية | ❌ لا / متوفر عبر منصات التوطين الخارجية |
55
- | **التوجيه المحلي** | ✅ نعم، يدعم المسارات المحلية مباشرة (يعمل مع Next.js و Vite) | ✅ مدمج، يدعم App Router جزء `[locale]` | ✅ مدمج |
56
- | **توليد المسارات الديناميكية** | ✅ نعم | ✅ نعم | ✅ نعم |
57
- | **التصريف الجمعي** | ✅ أنماط قائمة على التعداد | ✅ جيد | ✅ جيد |
58
- | **التنسيق (التواريخ، الأرقام، العملات)** | ✅ منسقات محسّنة (Intl في الخلفية) | ✅ جيد (مساعدات Intl) | ✅ جيد (مساعدات Intl) |
59
- | **تنسيق المحتوى** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml قيد العمل) | ✅ .json, .js, .ts | ⚠️ .json |
60
- | **دعم ICU** | ⚠️ قيد العمل | ✅ نعم | ⚠️ عبر الإضافة (`i18next-icu`) |
61
- | **مساعدو تحسين محركات البحث (hreflang، خريطة الموقع)** | ✅ أدوات مدمجة: مساعدات لخريطة الموقع، robots.txt، البيانات الوصفية | ✅ جيد | ✅ جيد |
62
- | **النظام البيئي / المجتمع** | ⚠️ أصغر حجماً لكنه ينمو بسرعة ويتسم بالتفاعل | ✅ متوسط الحجم، يركز على Next.js | ✅ متوسط الحجم، يركز على Next.js |
63
- | **التصيير على جانب الخادم ومكونات الخادم** | ✅ نعم، مُبسّط للتصيير على جانب الخادم / مكونات React Server | ⚠️ مدعوم على مستوى الصفحة ولكن يحتاج إلى تمرير دوال t على شجرة المكونات لمكونات الخادم الفرعية | ⚠️ مدعوم على مستوى الصفحة ولكن يحتاج إلى تمرير دوال t على شجرة المكونات لمكونات الخادم الفرعية |
64
- | **Tree-shaking (تحميل المحتوى المستخدم فقط)** | ✅ نعم، لكل مكون أثناء وقت البناء عبر إضافات Babel/SWC | ⚠️ جزئي | ⚠️ جزئي |
65
- | **التحميل الكسول** | ✅ نعم، لكل لغة / لكل قاموس | ✅ نعم (لكل مسار/لكل لغة)، يحتاج إلى إدارة مساحة الأسماء | ✅ نعم (لكل مسار/لكل لغة)، يحتاج إلى إدارة مساحة الأسماء |
66
- | **تنظيف المحتوى غير المستخدم** | ✅ نعم، لكل قاموس أثناء وقت البناء | ❌ لا، يمكن إدارته يدويًا باستخدام إدارة مساحة الأسماء | ❌ لا، يمكن إدارته يدويًا باستخدام إدارة مساحة الأسماء |
67
- | **إدارة المشاريع الكبيرة** | ✅ يشجع على التكوين المعياري، مناسب لأنظمة التصميم | ✅ معياري مع الإعداد | ✅ معياري مع الإعداد |
57
+ > يتم تحديث الشارات تلقائيًا. ستختلف اللقطات مع مرور الوقت.
68
58
 
69
59
  ---
70
60
 
71
- ## مقارنة متعمقة
61
+ ## مقارنة الميزات جنبًا إلى جنب (مركزة على Next.js)
72
62
 
73
- ### 1) البنية القابلة للتوسع
63
+ | الميزة | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
64
+ | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
65
+ | **الترجمات بالقرب من المكونات** | ✅ نعم، المحتوى موضوع بجانب كل مكون | ❌ لا | ❌ لا |
66
+ | **تكامل TypeScript** | ✅ متقدم، أنواع صارمة مولدة تلقائيًا | ✅ جيد | ⚠️ أساسي |
67
+ | **كشف الترجمات المفقودة** | ✅ تمييز أخطاء TypeScript وتحذير/خطأ أثناء وقت البناء | ⚠️ تراجع في وقت التشغيل | ⚠️ تراجع في وقت التشغيل |
68
+ | **المحتوى الغني (JSX/Markdown/المكونات)** | ✅ دعم مباشر | ❌ غير مصمم للعقد الغنية | ⚠️ محدود |
69
+ | **الترجمة المدعومة بالذكاء الاصطناعي** | ✅ نعم، يدعم عدة مزودي ذكاء اصطناعي. يمكن استخدامه باستخدام مفاتيح API الخاصة بك. يأخذ في الاعتبار سياق تطبيقك ونطاق المحتوى | ❌ لا | ❌ لا |
70
+ | **المحرر المرئي** | ✅ نعم، محرر مرئي محلي + نظام إدارة محتوى اختياري؛ يمكنه إخراج محتوى قاعدة الشيفرة؛ قابل للتضمين | ❌ لا / متوفر عبر منصات التوطين الخارجية | ❌ لا / متوفر عبر منصات التوطين الخارجية |
71
+ | **التوجيه المحلي** | ✅ نعم، يدعم المسارات المحلية مباشرة (يعمل مع Next.js و Vite) | ✅ مدمج، يدعم App Router جزء `[locale]` | ✅ مدمج |
72
+ | **توليد المسارات الديناميكية** | ✅ نعم | ✅ نعم | ✅ نعم |
73
+ | **التعددية (جمع الكلمات)** | ✅ أنماط قائمة على التعداد | ✅ جيد | ✅ جيد |
74
+ | **تنسيق (التواريخ، الأرقام، العملات)** | ✅ منسقات محسّنة (Intl في الخلفية) | ✅ جيد (مساعدات Intl) | ✅ جيد (مساعدات Intl) |
75
+ | **تنسيق المحتوى** | ✅ .tsx، .ts، .js، .json، .md، .txt، (.yaml قيد العمل) | ✅ .json، .js، .ts | ⚠️ .json |
76
+ | **دعم ICU** | ⚠️ جاري العمل عليه | ✅ نعم | ⚠️ عبر الإضافة (`i18next-icu`) |
77
+ | **مساعدو تحسين محركات البحث (hreflang، خريطة الموقع)** | ✅ أدوات مدمجة: مساعدات لخريطة الموقع، robots.txt، البيانات الوصفية | ✅ جيد | ✅ جيد |
78
+ | **النظام البيئي / المجتمع** | ⚠️ أصغر ولكن ينمو بسرعة ويتفاعل | ✅ جيد | ✅ جيد |
79
+ | **التصيير على جانب الخادم ومكونات الخادم** | ✅ نعم، مُبسّط للتصيير على جانب الخادم / مكونات خادم React | ⚠️ مدعوم على مستوى الصفحة ولكن يحتاج إلى تمرير دوال t على شجرة المكونات لمكونات الخادم الفرعية | ⚠️ مدعوم على مستوى الصفحة ولكن يحتاج إلى تمرير دوال t على شجرة المكونات لمكونات الخادم الفرعية |
80
+ | **إزالة الشيفرة غير المستخدمة (تحميل المحتوى المستخدم فقط)** | ✅ نعم، لكل مكون أثناء وقت البناء عبر إضافات Babel/SWC | ⚠️ جزئي | ⚠️ جزئي |
81
+ | **التحميل الكسول** | ✅ نعم، لكل لغة / لكل قاموس | ✅ نعم (لكل مسار/لكل لغة)، يحتاج إلى إدارة مساحة الأسماء | ✅ نعم (لكل مسار/لكل لغة)، يحتاج إلى إدارة مساحة الأسماء |
82
+ | **تنظيف المحتوى غير المستخدم** | ✅ نعم، لكل قاموس أثناء وقت البناء | ❌ لا، يمكن إدارته يدويًا باستخدام إدارة المساحات الاسمية | ❌ لا، يمكن إدارته يدويًا باستخدام إدارة المساحات الاسمية |
83
+ | **إدارة المشاريع الكبيرة** | ✅ يشجع على التصميم المعياري، مناسب لأنظمة التصميم | ✅ معياري مع الإعداد | ✅ معياري مع الإعداد |
84
+ | **اختبار الترجمات المفقودة (CLI/CI)** | ✅ CLI: `npx intlayer content test` (تدقيق مناسب لـ CI) | ⚠️ غير مدمج؛ الوثائق تقترح `npx @lingual/i18n-check` | ⚠️ غير مدمج؛ يعتمد على أدوات i18next / وقت التشغيل `saveMissing` |
85
+
86
+ ---
74
87
 
75
- - **next-intl / next-i18next**: الافتراضي هو **كتالوجات مركزية** لكل لغة (بالإضافة إلى **مساحات الأسماء** في i18next). يعمل بشكل جيد في البداية، لكنه غالبًا ما يصبح مساحة مشتركة كبيرة مع زيادة الترابط وتغير المفاتيح بشكل متكرر.
76
- - **Intlayer**: يشجع على وجود **قواميس لكل مكون** (أو لكل ميزة) **متمركزة** مع الكود الذي تخدمه. هذا يقلل العبء الذهني، ويسهل تكرار/ترحيل أجزاء واجهة المستخدم، ويقلل من النزاعات بين الفرق المختلفة. المحتوى غير المستخدم يكون من السهل اكتشافه وحذفه بشكل طبيعي.
88
+ ## المقدمة
77
89
 
78
- **لماذا هذا مهم:** في قواعد الشيفرة الكبيرة أو إعدادات أنظمة التصميم، **المحتوى المعياري** يتوسع بشكل أفضل من الكتالوجات الأحادية.
90
+ يوفر Next.js دعمًا مدمجًا للتوجيه الدولي (مثل مقاطع اللغة). لكن هذه الميزة لا تقوم بالترجمة بمفردها. لا يزال يتعين عليك استخدام مكتبة لعرض المحتوى المحلي للمستخدمين.
91
+
92
+ توجد العديد من مكتبات i18n، ولكن في عالم Next.js اليوم، هناك ثلاث مكتبات تكتسب شعبية: next-i18next، next-intl، و Intlayer.
79
93
 
80
94
  ---
81
95
 
82
- ### 2) TypeScript والأمان
96
+ ## الهندسة والقابلية للتوسع
83
97
 
84
- - **next-intl**: دعم قوي لـ TypeScript، لكن **المفاتيح ليست مُعرفة بصرامة بشكل افتراضي**؛ ستحتاج للحفاظ على أنماط الأمان يدويًا.
85
- - **next-i18next**: تعريفات أساسية للخطافات؛ **التعريف الصارم للمفاتيح يتطلب أدوات/إعدادات إضافية**.
86
- - **Intlayer**: **ينشئ أنواعًا صارمة** من محتواك. **الإكمال التلقائي في بيئة التطوير** و**أخطاء وقت الترجمة** تكتشف الأخطاء المطبعية والمفاتيح المفقودة قبل النشر.
98
+ - **next-intl / next-i18next**: تعتمد بشكل افتراضي على **كتالوجات مركزية** لكل لغة (بالإضافة إلى **مساحات الأسماء** في i18next). تعمل بشكل جيد في البداية، لكنها غالبًا ما تصبح مساحة مشتركة كبيرة مع زيادة الترابط وتغير المفاتيح.
99
+ - **Intlayer**: تشجع على استخدام **قواميس لكل مكون** (أو لكل ميزة) **متمركزة** مع الكود الذي تخدمه. هذا يقلل من العبء المعرفي، ويسهل تكرار/ترحيل أجزاء واجهة المستخدم، ويقلل من النزاعات بين الفرق. المحتوى غير المستخدم يكون أسهل في الاكتشاف والحذف بشكل طبيعي.
87
100
 
88
- **لماذا هذا مهم:** التعريف الصارم يحول الفشل إلى الجانب **الأيسر** (CI/البناء) بدلاً من الجانب **الأيمن** (وقت التشغيل).
101
+ **لماذا هذا مهم:** في قواعد الكود الكبيرة أو إعدادات نظام التصميم، **المحتوى المعياري** يتوسع بشكل أفضل من الكتالوجات الأحادية.
89
102
 
90
103
  ---
91
104
 
92
- ### 3) التعامل مع الترجمات المفقودة
105
+ ## أحجام الحزم والاعتمادات
106
+
107
+ بعد بناء التطبيق، الحزمة هي جافا سكريبت التي سيقوم المتصفح بتحميلها لعرض الصفحة. لذلك، حجم الحزمة مهم لأداء التطبيق.
108
+
109
+ هناك مكونان مهمان في سياق حزمة تطبيق متعدد اللغات:
110
+
111
+ - كود التطبيق
112
+ - المحتوى الذي يتم تحميله بواسطة المتصفح
113
+
114
+ ## كود التطبيق
115
+
116
+ أهمية كود التطبيق ضئيلة في هذه الحالة. جميع الحلول الثلاثة تدعم تقنية tree-shaking، مما يعني أن الأجزاء غير المستخدمة من الكود لا تُدرج في الحزمة.
117
+
118
+ إليك مقارنة لحجم حزمة جافا سكريبت التي يتم تحميلها بواسطة المتصفح لتطبيق متعدد اللغات مع الحلول الثلاثة.
119
+
120
+ إذا لم نحتاج إلى أي مُنسق في التطبيق، فإن قائمة الدوال المُصدرة بعد تطبيق tree-shaking ستكون:
121
+
122
+ - **next-intlayer**: `useIntlayer`, `useLocale`, `NextIntlClientProvider`، (حجم الحزمة هو 180.6 كيلوبايت -> 78.6 كيلوبايت (gzip))
123
+ - **next-intl**: `useTranslations`, `useLocale`, `NextIntlClientProvider`، (حجم الحزمة هو 101.3 كيلوبايت -> 31.4 كيلوبايت (gzip))
124
+ - **next-i18next**: `useTranslation`, `useI18n`, `I18nextProvider`، (حجم الحزمة هو 80.7 كيلوبايت -> 25.5 كيلوبايت (gzip))
125
+
126
+ هذه الدوال هي مجرد أغلفة حول سياق/حالة React، لذا فإن التأثير الكلي لمكتبة i18n على حجم الحزمة هو ضئيل.
127
+
128
+ > Intlayer أكبر قليلاً من `next-intl` و `next-i18next` لأنه يتضمن منطقًا أكثر في دالة `useIntlayer`. هذا مرتبط بالتكامل مع markdown و `intlayer-editor`.
129
+
130
+ ## المحتوى والترجمات
131
+
132
+ غالبًا ما يتجاهل المطورون هذا الجزء، ولكن دعونا نعتبر حالة تطبيق يتكون من 10 صفحات بـ 10 لغات. لنفترض أن كل صفحة تحتوي على محتوى فريد بنسبة 100% لتبسيط الحساب (في الواقع، هناك الكثير من المحتوى المكرر بين الصفحات، مثل عنوان الصفحة، الرأس، التذييل، إلخ).
133
+
134
+ المستخدم الذي يرغب في زيارة صفحة `/fr/about` سيقوم بتحميل محتوى صفحة واحدة بلغة معينة. تجاهل تحسين المحتوى يعني تحميل 8200% `((1 + (((10 صفحات - 1) × (10 لغات - 1)))) × 100)` من محتوى التطبيق بشكل غير ضروري. هل ترى المشكلة؟ حتى لو كان هذا المحتوى نصًا فقط، وبينما من المحتمل أنك تفضل التفكير في تحسين صور موقعك، فإنك ترسل محتوى غير مفيد عبر العالم وتجعل أجهزة المستخدمين تعالجه دون فائدة.
135
+
136
+ مسألتان مهمتان:
137
+
138
+ - **التقسيم حسب المسار:**
139
+
140
+ > إذا كنت في صفحة `/about`، لا أريد تحميل محتوى صفحة `/home`
141
+
142
+ - **التقسيم حسب اللغة:**
143
+
144
+ > إذا كنت في صفحة `/fr/about`، لا أريد تحميل محتوى صفحة `/en/about`
145
+
146
+ مرة أخرى، كل الحلول الثلاثة تدرك هذه القضايا وتسمح بإدارة هذه التحسينات. الفرق بين الحلول الثلاثة هو تجربة المطور (DX).
147
+
148
+ يستخدم كل من `next-intl` و `next-i18next` نهجًا مركزيًا لإدارة الترجمات، مما يسمح بتقسيم ملفات JSON حسب اللغة وحسب الملفات الفرعية. في `next-i18next`، نسمي ملفات JSON "مساحات الأسماء" (namespaces)؛ بينما يسمح `next-intl` بإعلان الرسائل. في `intlayer`، نسمي ملفات JSON "القواميس" (dictionaries).
149
+
150
+ - في حالة `next-intl`، مثل `next-i18next`، يتم تحميل المحتوى على مستوى الصفحة/التخطيط، ثم يتم تحميل هذا المحتوى في مزود السياق. هذا يعني أن المطور يجب أن يدير ملفات JSON التي سيتم تحميلها لكل صفحة يدويًا.
151
+
152
+ > في الممارسة العملية، هذا يعني أن المطورين غالبًا ما يتخطون هذا التحسين، مفضلين تحميل كل المحتوى في مزود سياق الصفحة من أجل البساطة.
153
+
154
+ - في حالة `intlayer`، يتم تحميل كل المحتوى في التطبيق. ثم يتولى مكون إضافي (`@intlayer/babel` / `@intlayer/swc`) مهمة تحسين الحزمة عن طريق تحميل المحتوى المستخدم فقط في الصفحة. لذلك لا يحتاج المطور إلى إدارة القواميس التي سيتم تحميلها يدويًا. هذا يسمح بتحسين أفضل، وصيانة أفضل، ويقلل من وقت التطوير.
155
+
156
+ مع نمو التطبيق (خاصة عندما يعمل عدة مطورين على التطبيق)، من الشائع نسيان إزالة المحتوى الذي لم يعد مستخدمًا من ملفات JSON.
157
+
158
+ > لاحظ أن جميع ملفات JSON يتم تحميلها في جميع الحالات (next-intl، next-i18next، intlayer).
159
+
160
+ لهذا السبب، فإن نهج Intlayer أكثر كفاءة: إذا لم يعد يتم استخدام مكون ما، فلن يتم تحميل قاموسه في الحزمة.
161
+
162
+ كيفية تعامل المكتبة مع الاستبدالات (fallbacks) أمر مهم أيضًا. لنفترض أن التطبيق باللغة الإنجليزية بشكل افتراضي، وزار المستخدم صفحة `/fr/about`. إذا كانت الترجمات مفقودة بالفرنسية، فسنعتبر الاستبدال باللغة الإنجليزية.
163
+
164
+ في حالة `next-intl` و `next-i18next`، تتطلب المكتبة تحميل ملفات JSON المتعلقة باللغة الحالية، ولكن أيضًا لغة التراجع (fallback). وبالتالي، مع افتراض أن كل المحتوى قد تُرجم، ستقوم كل صفحة بتحميل محتوى غير ضروري بنسبة 100%. **بالمقارنة، تقوم `intlayer` بمعالجة التراجع أثناء وقت بناء القاموس. لذا، ستقوم كل صفحة بتحميل المحتوى المستخدم فقط.**
93
165
 
94
- - **next-intl / next-i18next**: تعتمد على **الاستعادات في وقت التشغيل** (مثل عرض المفتاح أو اللغة الافتراضية). البناء لا يفشل.
95
- - **Intlayer**: **الكشف أثناء البناء** مع **تحذيرات/أخطاء** للمواقع أو المفاتيح المفقودة.
166
+ فيما يلي مثال على تأثير تحسين حجم الحزمة باستخدام `intlayer` في تطبيق vite + react:
96
167
 
97
- **لماذا هذا مهم:** اكتشاف الفجوات أثناء البناء يمنع ظهور "سلاسل غامضة" في الإنتاج ويتماشى مع بوابات الإصدار الصارمة.
168
+ | الحزمة المحسنة | الحزمة غير المحسنة |
169
+ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
170
+ | ![حزمة محسنة](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png) | ![حزمة غير محسنة](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle_no_optimization.png) |
98
171
 
99
172
  ---
100
173
 
101
- ### 4) التوجيه، الوسيط واستراتيجية عناوين URL
174
+ ## تايب سكريبت والسلامة
102
175
 
103
- - الثلاثة يعملون مع **التوجيه المحلي في Next.js** على App Router.
104
- - **Intlayer** يذهب أبعد من ذلك مع **الوسيط الدولي (i18n middleware)** (كشف اللغة عبر الرؤوس/الكوكيز) و**المساعدين** لتوليد عناوين URL محلية ووسوم `<link rel="alternate" hreflang="…">`.
176
+ <Columns>
177
+ <Column>
105
178
 
106
- **لماذا هذا مهم:** تقليل طبقات الربط المخصصة؛ **تجربة مستخدم متسقة** و**تحسين محركات البحث نظيف** عبر اللغات.
179
+ **next-intl**
180
+
181
+ - دعم قوي لـ TypeScript، لكن **المفاتيح ليست مُعرفة بدقة بشكل افتراضي**؛ ستحتاج للحفاظ على أنماط الأمان يدويًا.
182
+
183
+ </Column>
184
+ <Column>
185
+
186
+ **next-i18next**
187
+
188
+ - تعريفات أساسية للهوكس؛ **التعريف الصارم للمفاتيح يتطلب أدوات/تكوين إضافي**.
189
+
190
+ </Column>
191
+ <Column>
192
+
193
+ **intlayer**
194
+
195
+ - **ينشئ أنواعًا صارمة** من المحتوى الخاص بك. **الإكمال التلقائي في بيئة التطوير** و**أخطاء وقت الترجمة** تكتشف الأخطاء الإملائية والمفاتيح المفقودة قبل النشر.
196
+
197
+ </Column>
198
+ </Columns>
199
+
200
+ **لماذا هذا مهم:** التحقق الصارم من الأنواع ينقل الأخطاء إلى اليسار (التكامل المستمر/البناء) بدلاً من اليمين (وقت التشغيل).
107
201
 
108
202
  ---
109
203
 
110
- ### 5) توافق مكونات الخادم (RSC)
204
+ ## التعامل مع الترجمات المفقودة
111
205
 
112
- - **الجميع** يدعم Next.js 13+.
113
- - **Intlayer** يسهل **الحد الفاصل بين الخادم والعميل** بواجهة برمجة تطبيقات موحدة ومزودين مصممين لـ RSC، بحيث لا تحتاج إلى تمرير منسقات أو دوال الترجمة عبر شجرة المكونات.
206
+ **next-intl**
114
207
 
115
- **لماذا هذا مهم:** نموذج ذهني أنظف وحالات حافة أقل في الأشجار المختلطة.
208
+ - يعتمد على **الاستعادات أثناء وقت التشغيل** (مثل عرض المفتاح أو اللغة الافتراضية). البناء لا يفشل.
209
+
210
+ **next-i18next**
211
+
212
+ - يعتمد على **الاستعادات أثناء وقت التشغيل** (مثل عرض المفتاح أو اللغة الافتراضية). البناء لا يفشل.
213
+
214
+ **intlayer**
215
+
216
+ - **الكشف أثناء وقت البناء** مع **تحذيرات/أخطاء** للمواقع أو المفاتيح المفقودة.
217
+
218
+ **لماذا هذا مهم:** اكتشاف الفجوات أثناء البناء يمنع ظهور "سلاسل غامضة" في الإنتاج ويتماشى مع قواعد الإصدار الصارمة.
116
219
 
117
220
  ---
118
221
 
119
- ### 6) الأداء وسلوك التحميل
222
+ ## التوجيه، الوسيط واستراتيجية عناوين URL
223
+
224
+ <Columns>
225
+ <Column>
226
+
227
+ **next-intl**
228
+
229
+ - يعمل مع **توجيه Next.js المحلي** على App Router.
120
230
 
121
- - **next-intl / next-i18next**: تحكم جزئي عبر **مساحات الأسماء** و**تقسيمات على مستوى المسار**؛ خطر تجميع سلاسل غير مستخدمة إذا تم التراخي في الانضباط.
122
- - **Intlayer**: يقوم بـ **تحليل الشجرة** أثناء البناء و**تحميل كسول لكل قاموس/لغة**. المحتوى غير المستخدم لا يتم شحنه.
231
+ </Column>
232
+ <Column>
123
233
 
124
- **لماذا هذا مهم:** حزم أصغر وتشغيل أسرع، خاصة على المواقع متعددة اللغات.
234
+ **next-i18next**
235
+
236
+ - يعمل مع **توجيه Next.js المحلي** على App Router.
237
+
238
+ </Column>
239
+ <Column>
240
+
241
+ **intlayer**
242
+
243
+ - كل ما سبق، بالإضافة إلى **وسيط i18n** (اكتشاف اللغة عبر الرؤوس/الكوكيز) و**مساعدين** لإنشاء عناوين URL محلية ووسوم `<link rel="alternate" hreflang="…">`.
244
+
245
+ </Column>
246
+ </Columns>
247
+
248
+ **لماذا هذا مهم:** تقليل طبقات الربط المخصصة؛ **تجربة مستخدم متسقة** و**تحسين SEO نظيف** عبر اللغات.
125
249
 
126
250
  ---
127
251
 
128
- ### 7) تجربة المطور، الأدوات والصيانة
252
+ ## توافق مكونات الخادم (RSC)
253
+
254
+ <Columns>
255
+ <Column>
256
+
257
+ **next-intl**
258
+
259
+ - يدعم Next.js 13+. غالبًا ما يتطلب تمرير دوال الترجمة/المنسقات عبر شجرة المكونات في الإعدادات المختلطة.
129
260
 
130
- - **next-intl / next-i18next**: عادةً ما تقوم بربط منصات خارجية للترجمات وسير العمل التحريري.
131
- - **Intlayer**: يأتي مع **محرر بصري مجاني** و**نظام إدارة محتوى اختياري** (متوافق مع Git أو خارجي). بالإضافة إلى **امتداد VSCode** لتأليف المحتوى و**ترجمات بمساعدة الذكاء الاصطناعي** باستخدام مفاتيح المزود الخاصة بك.
261
+ </Column>
262
+ <Column>
263
+
264
+ **next-i18next**
265
+
266
+ - يدعم Next.js 13+. قيود مماثلة مع تمرير أدوات الترجمة عبر الحدود.
267
+
268
+ </Column>
269
+ <Column>
270
+
271
+ **intlayer**
272
+
273
+ - يدعم Next.js 13+ ويسهل **الحد الفاصل بين الخادم/العميل** من خلال واجهة برمجة تطبيقات متسقة ومزودين موجهين لـ RSC، متجنبًا نقل أدوات التنسيق أو دوال الترجمة.
274
+
275
+ </Column>
276
+ </Columns>
277
+
278
+ **لماذا هذا مهم:** نموذج ذهني أنظف وحالات حافة أقل في الأشجار الهجينة.
279
+
280
+ ---
281
+
282
+ ## تجربة المطور، الأدوات والصيانة
283
+
284
+ <Columns>
285
+ <Column>
286
+
287
+ **next-intl**
288
+
289
+ - غالبًا ما يُستخدم مع منصات التوطين الخارجية وسير العمل التحريري.
290
+
291
+ </Column>
292
+ <Column>
293
+
294
+ **next-i18next**
295
+
296
+ - غالبًا ما يُستخدم مع منصات التوطين الخارجية وسير العمل التحريري.
297
+
298
+ </Column>
299
+ <Column>
300
+
301
+ **intlayer**
302
+
303
+ - يوفر محررًا بصريًا مجانيًا ونظام إدارة محتوى اختياري (متوافق مع Git أو خارجي)، بالإضافة إلى امتداد VSCode وترجمات بمساعدة الذكاء الاصطناعي باستخدام مفاتيح المزود الخاصة بك.
304
+
305
+ </Column>
306
+ </Columns>
132
307
 
133
308
  **لماذا هذا مهم:** يقلل من تكلفة العمليات ويقصر دورة التواصل بين المطورين ومؤلفي المحتوى.
134
309
 
310
+ ## التكامل مع منصات الترجمة (TMS)
311
+
312
+ تعتمد المؤسسات الكبيرة غالبًا على أنظمة إدارة الترجمة (TMS) مثل **Crowdin**، **Phrase**، **Lokalise**، **Localizely**، أو **Localazy**.
313
+
314
+ - **لماذا تهتم الشركات**
315
+ - **التعاون والأدوار**: يشارك عدة أطراف: المطورون، مدراء المنتجات، المترجمون، المراجعون، فرق التسويق.
316
+ - **الحجم والكفاءة**: الترجمة المستمرة، والمراجعة في السياق.
317
+
318
+ - **next-intl / next-i18next**
319
+ - عادةً ما تستخدم **كتالوجات JSON مركزية**، لذا فإن التصدير/الاستيراد مع أنظمة إدارة الترجمة (TMS) يكون بسيطًا.
320
+ - أنظمة بيئية ناضجة وأمثلة/تكاملات للمنصات المذكورة أعلاه.
321
+
322
+ - **Intlayer**
323
+ - يشجع على **القواميس اللامركزية لكل مكون** ويدعم محتوى **TypeScript/TSX/JS/JSON/MD**.
324
+ - هذا يحسن من التجزئة في الكود، لكنه قد يجعل تكامل أنظمة إدارة الترجمة (TMS) السهل الاستخدام أكثر صعوبة عندما يتوقع الأداة ملفات JSON مركزية ومسطحة.
325
+ - يوفر Intlayer بدائل: **ترجمات بمساعدة الذكاء الاصطناعي** (باستخدام مفاتيح المزود الخاصة بك)، و**محرر مرئي/نظام إدارة محتوى**، وعمليات **CLI/CI** لالتقاط وملء الفجوات.
326
+
327
+ > ملاحظة: `next-intl` و `i18next` يقبلان أيضًا كتالوجات TypeScript. إذا كان فريقك يخزن الرسائل في ملفات `.ts` أو يوزعها حسب الميزة، فقد تواجه احتكاكًا مشابهًا مع نظام إدارة الترجمة (TMS). ومع ذلك، تبقى العديد من إعدادات `next-intl` مركزية في مجلد `locales/`، مما يجعل إعادة هيكلتها إلى JSON لـ TMS أسهل قليلاً.
328
+
329
+ ## تجربة المطور
330
+
331
+ هذا الجزء يقدم مقارنة عميقة بين الحلول الثلاثة. بدلاً من النظر في الحالات البسيطة كما هو موضح في وثائق "البدء السريع" لكل حل، سنأخذ في الاعتبار حالة استخدام حقيقية، أكثر تشابهًا مع مشروع حقيقي.
332
+
333
+ ### هيكل التطبيق
334
+
335
+ هيكل التطبيق مهم لضمان سهولة صيانة قاعدة الشيفرة الخاصة بك.
336
+
337
+ <Tab defaultTab="next-intl" group='techno'>
338
+
339
+ <TabItem label="next-i18next" value="next-i18next">
340
+
341
+ ```bash
342
+ .
343
+ ├── public
344
+ │ └── locales
345
+ │ ├── en
346
+ │ │ ├── home.json
347
+ │ │ └── navbar.json
348
+ │ ├── fr
349
+ │ │ ├── home.json
350
+ │ │ └── navbar.json
351
+ │ └── es
352
+ │ ├── home.json
353
+ │ └── navbar.json
354
+ ├── next-i18next.config.js
355
+ └── src
356
+ ├── middleware.ts
357
+ ├── app
358
+ │ └── home.tsx
359
+ └── components
360
+ └── Navbar
361
+ └── index.tsx
362
+ ```
363
+
364
+ </TabItem>
365
+ <TabItem label="next-intl" value="next-intl">
366
+
367
+ ```bash
368
+ .
369
+ ├── locales
370
+ │ ├── en
371
+ │ │ ├── home.json
372
+ │ │ └── navbar.json
373
+ │ ├── fr
374
+ │ │ ├── home.json
375
+ │ │ └── navbar.json
376
+ │ └── es
377
+ │ ├── home.json
378
+ │ └── navbar.json
379
+ ├── i18n.ts
380
+ └── src
381
+ ├── middleware.ts
382
+ ├── app
383
+ │ └── home.tsx
384
+ └── components
385
+ └── Navbar
386
+ └── index.tsx
387
+ ```
388
+
389
+ </TabItem>
390
+ <TabItem label="intlayer" value="intlayer">
391
+
392
+ ```bash
393
+ .
394
+ ├── intlayer.config.ts
395
+ └── src
396
+ ├── middleware.ts
397
+ ├── app
398
+ │ └── home
399
+ │ └── index.tsx
400
+ │ └── index.content.ts
401
+ └── components
402
+ └── Navbar
403
+ ├── index.tsx
404
+ └── index.content.ts
405
+ ```
406
+
407
+ </TabItem>
408
+ </Tab>
409
+
410
+ #### المقارنة
411
+
412
+ - **next-intl / next-i18next**: كتالوجات مركزية (JSON؛ مساحات الأسماء/الرسائل). هيكل واضح، يتكامل جيدًا مع منصات الترجمة، لكنه قد يؤدي إلى المزيد من التعديلات عبر الملفات مع نمو التطبيقات.
413
+ - **Intlayer**: قواميس `.content.{ts|js|json}` لكل مكون متواجدة بجانب المكونات. يسهل إعادة استخدام المكونات والتفكير المحلي؛ يضيف ملفات ويعتمد على أدوات وقت البناء.
414
+
415
+ #### الإعداد وتحميل المحتوى
416
+
417
+ كما ذُكر سابقًا، يجب عليك تحسين كيفية استيراد كل ملف JSON إلى كودك.
418
+ كيفية تعامل المكتبة مع تحميل المحتوى أمر مهم.
419
+
420
+ <Tab defaultTab="next-intl" group='techno'>
421
+ <TabItem label="next-i18next" value="next-i18next">
422
+
423
+ ```tsx fileName="next-i18next.config.js"
424
+ module.exports = {
425
+ i18n: {
426
+ locales: ["en", "fr", "es"],
427
+ defaultLocale: "en",
428
+ },
429
+ };
430
+ ```
431
+
432
+ ```tsx fileName="src/app/_app.tsx"
433
+ import { appWithTranslation } from "next-i18next";
434
+
435
+ const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />;
436
+
437
+ export default appWithTranslation(MyApp);
438
+ ```
439
+
440
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
441
+ import type { GetStaticProps } from "next";
442
+ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
443
+ import { useTranslation } from "next-i18next";
444
+ import { I18nextProvider, initReactI18next } from "react-i18next";
445
+ import { createInstance } from "i18next";
446
+ import { ClientComponent, ServerComponent } from "@components";
447
+
448
+ export default function HomePage({ locale }: { locale: string }) {
449
+ // أعلن صراحة عن مساحة الأسماء المستخدمة من قبل هذا المكون
450
+ const resources = await loadMessagesFor(locale); // محملك (JSON، إلخ)
451
+
452
+ const i18n = createInstance();
453
+ i18n.use(initReactI18next).init({
454
+ lng: locale,
455
+ fallbackLng: "en",
456
+ resources,
457
+ ns: ["common", "about"],
458
+ defaultNS: "common",
459
+ interpolation: { escapeValue: false },
460
+ });
461
+
462
+ const { t } = useTranslation("about");
463
+
464
+ return (
465
+ <I18nextProvider i18n={i18n}>
466
+ <main>
467
+ <h1>{t("title")}</h1>
468
+ <ClientComponent />
469
+ <ServerComponent />
470
+ </main>
471
+ </I18nextProvider>
472
+ );
473
+ }
474
+
475
+ export const getStaticProps: GetStaticProps = async ({ locale }) => {
476
+ // قم بتحميل مساحات الأسماء الضرورية فقط لهذه الصفحة
477
+ return {
478
+ props: {
479
+ ...(await serverSideTranslations(locale ?? "en", ["common", "about"])),
480
+ },
481
+ };
482
+ };
483
+ ```
484
+
485
+ </TabItem>
486
+ <TabItem label="next-intl" value="next-intl">
487
+
488
+ ```tsx fileName="i18n.ts"
489
+ import { getRequestConfig } from "next-intl/server";
490
+ import { notFound } from "next/navigation";
491
+
492
+ // يمكن استيرادها من إعداد مشترك
493
+ const locales = ["en", "fr", "es"];
494
+
495
+ export default getRequestConfig(async ({ locale }) => {
496
+ // تحقق من أن معامل `locale` الوارد صالح
497
+ if (!locales.includes(locale as any)) notFound();
498
+
499
+ return {
500
+ messages: (await import(`../messages/${locale}.json`)).default,
501
+ };
502
+ });
503
+ ```
504
+
505
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
506
+ import { NextIntlClientProvider } from "next-intl";
507
+ import { getMessages, unstable_setRequestLocale } from "next-intl/server";
508
+ import pick from "lodash/pick";
509
+
510
+ export default async function LocaleLayout({
511
+ children,
512
+ params,
513
+ }: {
514
+ children: React.ReactNode;
515
+ params: { locale: string };
516
+ }) {
517
+ const { locale } = params;
518
+
519
+ // تعيين اللغة النشطة للطلب لهذا العرض على الخادم (RSC)
520
+ unstable_setRequestLocale(locale);
521
+
522
+ // يتم تحميل الرسائل على جانب الخادم عبر src/i18n/request.ts
523
+ // (انظر وثائق next-intl). هنا ندفع فقط جزءًا فرعيًا إلى العميل
524
+ // اللازم لمكونات العميل (تحسين الحمولة).
525
+ const messages = await getMessages();
526
+ const clientMessages = pick(messages, ["common", "about"]);
527
+
528
+ return (
529
+ <html lang={locale}>
530
+ <body>
531
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
532
+ {children}
533
+ </NextIntlClientProvider>
534
+ </body>
535
+ </html>
536
+ );
537
+ }
538
+ ```
539
+
540
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
541
+ import { getTranslations } from "next-intl/server";
542
+ import { ClientComponent, ServerComponent } from "@components";
543
+
544
+ export default async function LandingPage({
545
+ params,
546
+ }: {
547
+ params: { locale: string };
548
+ }) {
549
+ // تحميل صارم على جانب الخادم فقط (غير مفعّل على العميل)
550
+ const t = await getTranslations("about");
551
+
552
+ return (
553
+ <main>
554
+ <h1>{t("title")}</h1>
555
+ <ClientComponent />
556
+ <ServerComponent />
557
+ </main>
558
+ );
559
+ }
560
+ ```
561
+
562
+ </TabItem>
563
+ <TabItem label="intlayer" value="intlayer">
564
+
565
+ ```tsx fileName="intlayer.config.ts"
566
+ export default {
567
+ internationalization: {
568
+ locales: ["en", "fr", "es"],
569
+ defaultLocale: "en",
570
+ },
571
+ };
572
+ ```
573
+
574
+ ```tsx fileName="src/app/[locale]/layout.tsx"
575
+ import { getHTMLTextDir } from "intlayer";
576
+ import {
577
+ IntlayerClientProvider,
578
+ generateStaticParams,
579
+ type NextLayoutIntlayer,
580
+ } from "next-intlayer";
581
+
582
+ export const dynamic = "force-static";
583
+
584
+ const LandingLayout: NextLayoutIntlayer = async ({ children, params }) => {
585
+ const { locale } = await params;
586
+
587
+ return (
588
+ <html lang={locale} dir={getHTMLTextDir(locale)}>
589
+ <IntlayerClientProvider locale={locale}>
590
+ {children}
591
+ </IntlayerClientProvider>
592
+ </html>
593
+ );
594
+ };
595
+
596
+ export default LandingLayout;
597
+ ```
598
+
599
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
600
+ import { PageContent } from "@components/PageContent";
601
+ import type { NextPageIntlayer } from "next-intlayer";
602
+ import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
603
+ import { ClientComponent, ServerComponent } from "@components";
604
+
605
+ const LandingPage: NextPageIntlayer = async ({ params }) => {
606
+ const { locale } = await params;
607
+ const { title } = useIntlayer("about", locale);
608
+
609
+ return (
610
+ <IntlayerServerProvider locale={locale}>
611
+ <main>
612
+ <h1>{title}</h1>
613
+ <ClientComponent />
614
+ <ServerComponent />
615
+ </main>
616
+ </IntlayerServerProvider>
617
+ );
618
+ };
619
+
620
+ export default LandingPage;
621
+ ```
622
+
623
+ </TabItem>
624
+ </Tab>
625
+
626
+ #### مقارنة
627
+
628
+ جميع الثلاثة تدعم تحميل المحتوى حسب اللغة والمزودين.
629
+
630
+ - مع **next-intl/next-i18next**، عادةً ما تقوم بتحميل الرسائل/المساحات المختارة لكل مسار وتضع الموفرين حيثما دعت الحاجة.
631
+
632
+ - مع **Intlayer**، يتم إضافة تحليل وقت البناء لاستنتاج الاستخدام، مما يمكن أن يقلل من التوصيل اليدوي وقد يسمح بموفر جذر واحد.
633
+
634
+ اختر بين التحكم الصريح والأتمتة بناءً على تفضيل الفريق.
635
+
636
+ ### الاستخدام في مكون العميل
637
+
638
+ لنأخذ مثالاً على مكون عميل يعرض عدادًا.
639
+
640
+ <Tab defaultTab="next-intl" group='techno'>
641
+ <TabItem label="next-i18next" value="next-i18next">
642
+
643
+ **الترجمات (يجب أن تكون JSON حقيقية في `public/locales/...`)**
644
+
645
+ ```json fileName="public/locales/en/about.json"
646
+ {
647
+ "counter": {
648
+ "label": "Counter",
649
+ "increment": "Increment"
650
+ }
651
+ }
652
+ ```
653
+
654
+ ```json fileName="public/locales/fr/about.json"
655
+ {
656
+ "counter": {
657
+ "label": "عداد",
658
+ "increment": "زيادة"
659
+ }
660
+ }
661
+ ```
662
+
663
+ **مكون العميل**
664
+
665
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
666
+ "use client";
667
+
668
+ import React, { useMemo, useState } from "react";
669
+ import { useTranslation } from "next-i18next";
670
+
671
+ const ClientComponentExample = () => {
672
+ const { t, i18n } = useTranslation("about");
673
+ const [count, setCount] = useState(0);
674
+
675
+ // next-i18next لا يوفر useNumber؛ استخدم Intl.NumberFormat
676
+ const numberFormat = new Intl.NumberFormat(i18n.language);
677
+
678
+ return (
679
+ <div>
680
+ <p>{numberFormat.format(count)}</p>
681
+ <button
682
+ aria-label={t("counter.label")}
683
+ onClick={() => setCount((count) => count + 1)}
684
+ >
685
+ {t("counter.increment")}
686
+ </button>
687
+ </div>
688
+ );
689
+ };
690
+ ```
691
+
692
+ > لا تنسَ إضافة مساحة الأسماء "about" في serverSideTranslations للصفحة
693
+ > نستخدم هنا إصدار React 19.x.x، ولكن للإصدارات الأقدم، ستحتاج إلى استخدام useMemo لتخزين نسخة من المُنسق لأنه دالة ثقيلة
694
+
695
+ </TabItem>
696
+ <TabItem label="next-intl" value="next-intl">
697
+
698
+ **الترجمات (تم إعادة استخدام الشكل؛ قم بتحميلها في رسائل next-intl كما تفضل)**
699
+
700
+ ```json fileName="locales/en/about.json"
701
+ {
702
+ "counter": {
703
+ "label": "Counter",
704
+ "increment": "Increment"
705
+ }
706
+ }
707
+ ```
708
+
709
+ ```json fileName="locales/fr/about.json"
710
+ {
711
+ "counter": {
712
+ "label": "Compteur",
713
+ "increment": "Incrémenter"
714
+ }
715
+ }
716
+ ```
717
+
718
+ **مكون العميل**
719
+
720
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
721
+ "use client";
722
+
723
+ import React, { useState } from "react";
724
+ import { useTranslations, useFormatter } from "next-intl";
725
+
726
+ const ClientComponentExample = () => {
727
+ // نطاق مباشر للكائن المتداخل
728
+ const t = useTranslations("about.counter");
729
+ const format = useFormatter();
730
+ const [count, setCount] = useState(0);
731
+
732
+ return (
733
+ <div>
734
+ <p>{format.number(count)}</p>
735
+ <button
736
+ aria-label={t("label")}
737
+ onClick={() => setCount((count) => count + 1)}
738
+ >
739
+ {t("increment")}
740
+ </button>
741
+ </div>
742
+ );
743
+ };
744
+ ```
745
+
746
+ > لا تنسَ إضافة رسالة "about" في رسالة العميل للصفحة
747
+
748
+ </TabItem>
749
+ <TabItem label="intlayer" value="intlayer">
750
+
751
+ **المحتوى**
752
+
753
+ ```ts fileName="src/components/ClientComponentExample/index.content.ts"
754
+ import { t, type Dictionary } from "intlayer";
755
+
756
+ const counterContent = {
757
+ key: "counter",
758
+ content: {
759
+ label: t({ ar: "عداد", en: "Counter", fr: "Compteur" }),
760
+ increment: t({ ar: "زيادة", en: "Increment", fr: "Incrémenter" }),
761
+ },
762
+ } satisfies Dictionary;
763
+
764
+ export default counterContent;
765
+ ```
766
+
767
+ **مكون العميل**
768
+
769
+ ```tsx fileName="src/components/ClientComponentExample/index.tsx"
770
+ "use client";
771
+
772
+ import React, { useState } from "react";
773
+ import { useNumber, useIntlayer } from "next-intlayer";
774
+
775
+ const ClientComponentExample = () => {
776
+ const [count, setCount] = useState(0);
777
+ const { label, increment } = useIntlayer("counter"); // يعيد سلاسل نصية
778
+ const { number } = useNumber();
779
+
780
+ return (
781
+ <div>
782
+ <p>{number(count)}</p>
783
+ <button aria-label={label} onClick={() => setCount((count) => count + 1)}>
784
+ {increment}
785
+ </button>
786
+ </div>
787
+ );
788
+ };
789
+ ```
790
+
791
+ </TabItem>
792
+ </Tab>
793
+
794
+ #### المقارنة
795
+
796
+ - **تنسيق الأرقام**
797
+ - **next-i18next**: لا يوجد `useNumber`؛ يستخدم `Intl.NumberFormat` (أو i18next-icu).
798
+ - **next-intl**: `useFormatter().number(value)`.
799
+ - **Intlayer**: `useNumber()` مدمج.
800
+
801
+ - **المفاتيح**
802
+ - احتفظ ببنية متداخلة (`about.counter.label`) وحدد نطاق الخطاف الخاص بك وفقًا لذلك (`useTranslation("about")` + `t("counter.label")` أو `useTranslations("about.counter")` + `t("label")`).
803
+
804
+ - **مواقع الملفات**
805
+ - **next-i18next** يتوقع JSON في `public/locales/{lng}/{ns}.json`.
806
+ - **next-intl** مرن؛ يمكنك تحميل الرسائل كيفما تريد.
807
+ - **Intlayer** يخزن المحتوى في قواميس TS/JS ويحلها حسب المفتاح.
808
+
135
809
  ---
136
810
 
137
- ## متى تختار أي منها؟
811
+ ### الاستخدام في مكون الخادم
812
+
813
+ سوف نأخذ حالة مكون واجهة مستخدم. هذا المكون هو مكون خادم، ويجب أن يكون قادرًا على الإدراج كطفل لمكون عميل. (صفحة (مكون خادم) -> مكون عميل -> مكون خادم). بما أن هذا المكون يمكن إدراجه كطفل لمكون عميل، فلا يمكن أن يكون غير متزامن (async).
814
+
815
+ <Tab defaultTab="next-intl" group='techno'>
816
+ <TabItem label="next-i18next" value="next-i18next">
817
+
818
+ ```tsx fileName="src/pages/about.tsx"
819
+ import type { GetStaticProps } from "next";
820
+ import { useTranslation } from "next-i18next";
821
+
822
+ type ServerComponentProps = {
823
+ count: number;
824
+ };
825
+
826
+ const ServerComponent = ({ count }: ServerComponentProps) => {
827
+ const { t, i18n } = useTranslation("about");
828
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
829
+
830
+ return (
831
+ <div>
832
+ <p>{formatted}</p>
833
+ <button aria-label={t("counter.label")}>{t("counter.increment")}</button>
834
+ </div>
835
+ );
836
+ };
837
+ ```
838
+
839
+ > بما أن مكون الخادم لا يمكن أن يكون غير متزامن (async)، يجب عليك تمرير الترجمات ودالة التنسيق كخصائص (props).
840
+
841
+ </TabItem>
842
+ <TabItem label="next-intl" value="next-intl">
843
+
844
+ ```tsx fileName="src/components/ServerComponent.tsx"
845
+ type ServerComponentProps = {
846
+ count: number;
847
+ t: (key: string) => string;
848
+ };
849
+
850
+ const ServerComponent = ({ t, count }: ServerComponentProps) => {
851
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
852
+
853
+ return (
854
+ <div>
855
+ <p>{formatted}</p>
856
+ <button aria-label={t("label")}>{t("increment")}</button>
857
+ </div>
858
+ );
859
+ };
860
+ ```
861
+
862
+ > بما أن مكون الخادم لا يمكن أن يكون غير متزامن، تحتاج إلى تمرير الترجمات ودالة التنسيق كخصائص.
863
+ >
864
+ > - `const t = await getTranslations("about.counter");`
865
+ > - `const format = await getFormatter();`
866
+
867
+ </TabItem>
868
+ <TabItem label="intlayer" value="intlayer">
869
+
870
+ ```tsx fileName="src/components/ServerComponent.tsx"
871
+ import { useIntlayer, useNumber } from "next-intlayer/server";
872
+
873
+ const ServerComponent = ({ count }: { count: number }) => {
874
+ const { label, increment } = useIntlayer("counter");
875
+ const { number } = useNumber();
876
+
877
+ return (
878
+ <div>
879
+ <p>{number(count)}</p>
880
+ <button aria-label={label}>{increment}</button>
881
+ </div>
882
+ );
883
+ };
884
+ ```
885
+
886
+ </TabItem>
887
+ </Tab>
888
+
889
+ > تعرض Intlayer خطافات **آمنة للخادم** عبر `next-intlayer/server`. للعمل، يستخدم كل من `useIntlayer` و `useNumber` صيغة تشبه الخطافات، مشابهة لخطافات العميل، لكنها تعتمد في الأساس على سياق الخادم (`IntlayerServerProvider`).
890
+
891
+ ### البيانات الوصفية / خريطة الموقع / روبوتات البحث
892
+
893
+ ترجمة المحتوى أمر رائع. لكن الناس عادةً ما ينسون أن الهدف الرئيسي من التدويل هو جعل موقعك الإلكتروني أكثر ظهورًا للعالم. التدويل هو رافعة مذهلة لتحسين ظهور موقعك الإلكتروني.
894
+
895
+ إليك قائمة بالممارسات الجيدة المتعلقة بتحسين محركات البحث متعددة اللغات (SEO).
138
896
 
139
- - **اختر next-intl** إذا كنت تريد حلاً **بسيطًا**، وتشعر بالراحة مع الكتالوجات المركزية، وكان تطبيقك **صغير إلى متوسط الحجم**.
140
- - **اختر next-i18next** إذا كنت تحتاج إلى **نظام الإضافات الخاص بـ i18next** (مثل قواعد ICU المتقدمة عبر الإضافات) وكان فريقك يعرف i18next بالفعل، مع قبول **المزيد من التهيئة** من أجل المرونة.
141
- - **اختر Intlayer** إذا كنت تقدر **المحتوى المخصص للمكونات**، و**TypeScript الصارم**، و**ضمانات وقت البناء**، و**إزالة الشفرات غير المستخدمة (tree-shaking)**، وأدوات التوجيه/SEO/التحرير المدمجة - خاصةً لـ **Next.js App Router** وقواعد الشفرات الكبيرة والمودولية.
897
+ - تعيين علامات hreflang الوصفية داخل وسم `<head>`
898
+ > يساعد هذا محركات البحث على فهم اللغات المتاحة في الصفحة
899
+ - قم بإدراج جميع ترجمات الصفحات في ملف sitemap.xml باستخدام مخطط XML `http://www.w3.org/1999/xhtml`
900
+ >
901
+ - لا تنسَ استبعاد الصفحات ذات البادئة من ملف robots.txt (مثل `/dashboard`، و `/fr/dashboard`، و `/es/dashboard`)
902
+ >
903
+ - استخدم مكون Link مخصص لإعادة التوجيه إلى الصفحة الأكثر تخصيصًا للغة (مثلًا بالفرنسية `<a href="/fr/about">A propos</a>`)
904
+ >
905
+
906
+ غالبًا ما ينسى المطورون الإشارة بشكل صحيح إلى صفحاتهم عبر اللغات المختلفة.
907
+
908
+ <Tab defaultTab="next-intl" group='techno'>
909
+
910
+ <TabItem label="next-i18next" value="next-i18next">
911
+
912
+ ```ts fileName="i18n.config.ts"
913
+ export const locales = ["en", "fr"] as const;
914
+ export type Locale = (typeof locales)[number];
915
+ export const defaultLocale: Locale = "en";
916
+
917
+ export function localizedPath(locale: string, path: string) {
918
+ return locale === defaultLocale ? path : "/" + locale + path;
919
+ }
920
+
921
+ const ORIGIN = "https://example.com";
922
+ export function abs(locale: string, path: string) {
923
+ return ORIGIN + localizedPath(locale, path);
924
+ }
925
+ ```
926
+
927
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
928
+ import type { Metadata } from "next";
929
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
930
+
931
+ export async function generateMetadata({
932
+ params,
933
+ }: {
934
+ params: { locale: string };
935
+ }): Promise<Metadata> {
936
+ const { locale } = params;
937
+
938
+ // استيراد ملف JSON الصحيح بشكل ديناميكي
939
+ const messages = (
940
+ await import("@/../public/locales/" + locale + "/about.json")
941
+ ).default;
942
+
943
+ const languages = Object.fromEntries(
944
+ locales.map((locale) => [locale, localizedPath(locale, "/about")])
945
+ );
946
+
947
+ return {
948
+ title: messages.title,
949
+ description: messages.description,
950
+ alternates: {
951
+ canonical: localizedPath(locale, "/about"),
952
+ languages: { ...languages, "x-default": "/about" },
953
+ },
954
+ };
955
+ }
956
+
957
+ export default async function AboutPage() {
958
+ return <h1>حول</h1>;
959
+ }
960
+ ```
961
+
962
+ ```ts fileName="src/app/sitemap.ts"
963
+ import type { MetadataRoute } from "next";
964
+ import { locales, defaultLocale, abs } from "@/i18n.config";
965
+
966
+ export default function sitemap(): MetadataRoute.Sitemap {
967
+ const languages = Object.fromEntries(
968
+ locales.map((locale) => [locale, abs(locale, "/about")])
969
+ );
970
+ return [
971
+ {
972
+ url: abs(defaultLocale, "/about"),
973
+ lastModified: new Date(),
974
+ changeFrequency: "شهري",
975
+ priority: 0.7,
976
+ alternates: { languages },
977
+ },
978
+ ];
979
+ }
980
+ ```
981
+
982
+ ```ts fileName="src/app/robots.ts"
983
+ import type { MetadataRoute } from "next";
984
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
985
+
986
+ const ORIGIN = "https://example.com";
987
+
988
+ const expandAllLocales = (path: string) => [
989
+ localizedPath(defaultLocale, path),
990
+ ...locales
991
+ .filter((locale) => locale !== defaultLocale)
992
+ .map((locale) => localizedPath(locale, path)),
993
+ ];
994
+
995
+ export default function robots(): MetadataRoute.Robots {
996
+ const disallow = [
997
+ ...expandAllLocales("/dashboard"),
998
+ ...expandAllLocales("/admin"),
999
+ ];
1000
+
1001
+ return {
1002
+ rules: { userAgent: "*", allow: ["/"], disallow },
1003
+ host: ORIGIN,
1004
+ sitemap: ORIGIN + "/sitemap.xml",
1005
+ };
1006
+ }
1007
+ ```
1008
+
1009
+ </TabItem>
1010
+ <TabItem label="next-intl" value="next-intl">
1011
+
1012
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
1013
+ import type { Metadata } from "next";
1014
+ import { locales, defaultLocale } from "@/i18n";
1015
+ import { getTranslations } from "next-intl/server";
1016
+
1017
+ function localizedPath(locale: string, path: string) {
1018
+ return locale === defaultLocale ? path : "/" + locale + path;
1019
+ }
1020
+
1021
+ export async function generateMetadata({
1022
+ params,
1023
+ }: {
1024
+ params: { locale: string };
1025
+ }): Promise<Metadata> {
1026
+ const { locale } = params;
1027
+ const t = await getTranslations({ locale, namespace: "about" });
1028
+
1029
+ const url = "/about";
1030
+ const languages = Object.fromEntries(
1031
+ locales.map((locale) => [locale, localizedPath(locale, url)])
1032
+ );
1033
+
1034
+ return {
1035
+ title: t("title"),
1036
+ description: t("description"),
1037
+ alternates: {
1038
+ canonical: localizedPath(locale, url),
1039
+ languages: { ...languages, "x-default": url },
1040
+ },
1041
+ };
1042
+ }
1043
+
1044
+ // ... بقية كود الصفحة
1045
+ ```
1046
+
1047
+ ```tsx fileName="src/app/sitemap.ts"
1048
+ import type { MetadataRoute } from "next";
1049
+ import { locales, defaultLocale } from "@/i18n";
1050
+
1051
+ const origin = "https://example.com";
1052
+
1053
+ const formatterLocalizedPath = (locale: string, path: string) =>
1054
+ locale === defaultLocale ? origin + path : origin + "/" + locale + path;
1055
+
1056
+ export default function sitemap(): MetadataRoute.Sitemap {
1057
+ const aboutLanguages = Object.fromEntries(
1058
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
1059
+ );
1060
+
1061
+ return [
1062
+ {
1063
+ url: formatterLocalizedPath(defaultLocale, "/about"),
1064
+ lastModified: new Date(),
1065
+ changeFrequency: "monthly",
1066
+ priority: 0.7,
1067
+ alternates: { languages: aboutLanguages },
1068
+ },
1069
+ ];
1070
+ }
1071
+ ```
1072
+
1073
+ ```tsx fileName="src/app/robots.ts"
1074
+ import type { MetadataRoute } from "next";
1075
+ import { locales, defaultLocale } from "@/i18n";
1076
+
1077
+ const origin = "https://example.com";
1078
+ const withAllLocales = (path: string) => [
1079
+ path,
1080
+ ...locales
1081
+ .filter((locale) => locale !== defaultLocale)
1082
+ .map((locale) => "/" + locale + path),
1083
+ ];
1084
+
1085
+ export default function robots(): MetadataRoute.Robots {
1086
+ const disallow = [
1087
+ ...withAllLocales("/dashboard"),
1088
+ ...withAllLocales("/admin"),
1089
+ ];
1090
+
1091
+ return {
1092
+ rules: { userAgent: "*", allow: ["/"], disallow },
1093
+ host: origin,
1094
+ sitemap: origin + "/sitemap.xml",
1095
+ };
1096
+ }
1097
+ ```
1098
+
1099
+ </TabItem>
1100
+ <TabItem label="intlayer" value="intlayer">
1101
+
1102
+ ```typescript fileName="src/app/[locale]/about/layout.tsx"
1103
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
1104
+ import type { Metadata } from "next";
1105
+ import type { LocalPromiseParams } from "next-intlayer";
1106
+
1107
+ export const generateMetadata = async ({
1108
+ params,
1109
+ }: LocalPromiseParams): Promise<Metadata> => {
1110
+ const { locale } = await params;
1111
+
1112
+ const metadata = getIntlayer("page-metadata", locale);
1113
+
1114
+ const multilingualUrls = getMultilingualUrls("/about");
1115
+
1116
+ return {
1117
+ ...metadata,
1118
+ alternates: {
1119
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
1120
+ languages: { ...multilingualUrls, "x-default": "/about" },
1121
+ },
1122
+ };
1123
+ };
1124
+
1125
+ // ... بقية كود الصفحة
1126
+ ```
1127
+
1128
+ ```tsx fileName="src/app/sitemap.ts"
1129
+ import { getMultilingualUrls } from "intlayer";
1130
+ import type { MetadataRoute } from "next";
1131
+
1132
+ const sitemap = (): MetadataRoute.Sitemap => [
1133
+ {
1134
+ url: "https://example.com/about",
1135
+ alternates: {
1136
+ languages: { ...getMultilingualUrls("https://example.com/about") },
1137
+ },
1138
+ },
1139
+ ];
1140
+ ```
1141
+
1142
+ ```tsx fileName="src/app/robots.ts"
1143
+ import { getMultilingualUrls } from "intlayer";
1144
+ import type { MetadataRoute } from "next";
1145
+
1146
+ // دالة لجلب جميع الروابط متعددة اللغات من قائمة الروابط
1147
+ const getAllMultilingualUrls = (urls: string[]) =>
1148
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
1149
+
1150
+ // إعدادات ملف robots.txt مع قواعد السماح والمنع للزواحف
1151
+ const robots = (): MetadataRoute.Robots => ({
1152
+ rules: {
1153
+ userAgent: "*",
1154
+ allow: ["/"],
1155
+ disallow: getAllMultilingualUrls(["/dashboard"]), // منع الوصول إلى لوحة التحكم بجميع اللغات
1156
+ },
1157
+ host: "https://example.com",
1158
+ sitemap: "https://example.com/sitemap.xml",
1159
+ });
1160
+
1161
+ export default robots;
1162
+ ```
1163
+
1164
+ </TabItem>
1165
+ </Tab>
1166
+
1167
+ > توفر Intlayer دالة `getMultilingualUrls` لتوليد روابط متعددة اللغات لخريطة موقعك.
1168
+
1169
+ ---
142
1170
 
143
1171
  ---
144
1172
 
145
- ## ملاحظات عملية للترحيل (من next-intl / next-i18next إلى Intlayer)
1173
+ ## والفائز هو…
1174
+
1175
+ ليس الأمر بسيطًا. كل خيار له مزاياه وعيوبه. إليك كيف أراه:
1176
+
1177
+ <Columns>
1178
+ <Column>
1179
+
1180
+ **next-intl**
1181
+
1182
+ - أبسط، خفيف الوزن، مع قرارات أقل مفروضة عليك. إذا كنت تريد حلاً **بسيطًا**، وتشعر بالراحة مع الكتالوجات المركزية، وكان تطبيقك **صغير إلى متوسط الحجم**.
1183
+
1184
+ </Column>
1185
+ <Column>
1186
+
1187
+ **next-i18next**
1188
+
1189
+ - ناضج، مليء بالميزات، يحتوي على العديد من الإضافات المجتمعية، لكن تكلفة الإعداد أعلى. إذا كنت تحتاج إلى **نظام إضافات i18next** (مثل قواعد ICU المتقدمة عبر الإضافات) وكان فريقك يعرف i18next بالفعل، مع قبول **المزيد من التكوين** من أجل المرونة.
1190
+
1191
+ </Column>
1192
+ <Column>
1193
+
1194
+ **Intlayer**
1195
+
1196
+ - مبني لـ Next.js الحديث، مع محتوى معياري، أمان نوعي، أدوات، وتقليل الكود المكرر. إذا كنت تقدر **المحتوى المخصص للمكونات**، **TypeScript الصارم**، **ضمانات وقت البناء**، **tree-shaking**، وأدوات التوجيه/SEO/المحرر المدمجة - خاصة لـ **Next.js App Router**، أنظمة التصميم و**قواعد الشيفرة الكبيرة والمعيارية**.
1197
+
1198
+ </Column>
1199
+ </Columns>
1200
+
1201
+ إذا كنت تفضل إعدادًا بسيطًا وتقبل بعض التوصيلات اليدوية، فإن next-intl خيار جيد. إذا كنت تحتاج كل الميزات ولا تمانع التعقيد، فإن next-i18next يعمل بشكل جيد. ولكن إذا كنت تريد حلاً حديثًا وقابلًا للتوسع وذو محتوى معياري مع أدوات مدمجة، فإن Intlayer تهدف إلى تقديم ذلك لك مباشرةً من الصندوق.
1202
+
1203
+ > **بديل لفرق المؤسسات**: إذا كنت بحاجة إلى حل مثبت يعمل بشكل مثالي مع منصات التوطين المعروفة مثل **Crowdin**، **Phrase**، أو أنظمة إدارة الترجمة الاحترافية الأخرى، فكر في استخدام **next-intl** أو **next-i18next** لنظامهم البيئي الناضج والتكاملات المثبتة.
1204
+
1205
+ > **خارطة الطريق المستقبلية**: تخطط Intlayer أيضًا لتطوير إضافات تعمل فوق حلول **i18next** و **next-intl**. هذا سيمنحك مزايا Intlayer في الأتمتة، والبنية النحوية، وإدارة المحتوى مع الحفاظ على الأمان والاستقرار الذي توفره هذه الحلول المعروفة في كود التطبيق الخاص بك.
1206
+
1207
+ ## نجوم GitHub
1208
+
1209
+ نجوم GitHub هي مؤشر قوي على شعبية المشروع، وثقة المجتمع، وأهميته على المدى الطويل. وعلى الرغم من أنها ليست مقياسًا مباشرًا للجودة التقنية، إلا أنها تعكس عدد المطورين الذين يجدون المشروع مفيدًا، ويتابعون تقدمه، ومن المحتمل أن يتبنوه. لتقدير قيمة المشروع، تساعد النجوم في مقارنة الجذب بين البدائل وتوفر رؤى حول نمو النظام البيئي.
146
1210
 
147
- - **ابدأ بالميزات بشكل منفصل**: انقل مسارًا أو مكونًا واحدًا في كل مرة إلى **القواميس المحلية**.
148
- - **احتفظ بالكتالوجات القديمة بالتوازي**: استخدم جسرًا أثناء الترحيل؛ تجنب التغيير الجذري المفاجئ.
149
- - **فعّل الفحوصات الصارمة**: دع اكتشاف وقت البناء يكشف الفجوات مبكرًا.
150
- - **اعتمد الوسائط الوسيطة والمساعدين**: قم بتوحيد اكتشاف اللغة وعلامات SEO على مستوى الموقع.
151
- - **قِس حجم الحزم**: توقع **تقليل حجم الحزمة** مع حذف المحتوى غير المستخدم.
1211
+ [![مخطط تاريخ النجوم](https://api.star-history.com/svg?repos=i18next/next-i18next&repos=amannn/next-intl&repos=aymericzip/intlayer&type=Date)](https://www.star-history.com/#i18next/next-i18next&amannn/next-intl&aymericzip/intlayer)
152
1212
 
153
1213
  ---
154
1214
 
155
1215
  ## الخلاصة
156
1216
 
157
- جميع المكتبات الثلاث تنجح في التوطين الأساسي. الفرق هو **كمية العمل التي يجب عليك القيام بها** لتحقيق إعداد قوي وقابل للتوسع في **Next.js الحديث**:
1217
+ تنجح المكتبات الثلاث جميعها في التوطين الأساسي. الفرق هو **كمية العمل التي يجب عليك القيام بها** لتحقيق إعداد قوي وقابل للتوسع في **Next.js الحديثة**:
158
1218
 
159
- - مع **Intlayer**، فإن **المحتوى المعياري**، و**TypeScript الصارم**، و**السلامة أثناء وقت البناء**، و**حزم الشجرة المهشمة**، و**موجه التطبيقات من الدرجة الأولى + أدوات SEO** هي **الإعدادات الافتراضية**، وليست مهامًا شاقة.
160
- - إذا كانت فرقك تقدر **قابلية الصيانة والسرعة** في تطبيق متعدد اللغات يعتمد على المكونات، فإن Intlayer تقدم **التجربة الأكثر اكتمالًا** اليوم.
1219
+ - مع **Intlayer**، يكون **المحتوى المعياري**، و**TypeScript الصارم**، و**السلامة أثناء وقت البناء**، و**حزم شجرة المهملة**، و**موجه التطبيقات من الدرجة الأولى + أدوات تحسين محركات البحث** هي **الإعدادات الافتراضية**، وليست مهامًا شاقة.
1220
+ - إذا كانت فرقك تقدر **قابلية الصيانة والسرعة** في تطبيق متعدد اللغات يعتمد على المكونات، فإن Intlayer تقدم التجربة **الأكمل** اليوم.
161
1221
 
162
- راجع مستند ['لماذا Intlayer؟'](https://intlayer.org/doc/why) لمزيد من التفاصيل.
1222
+ راجع [وثيقة "لماذا Intlayer؟"](https://intlayer.org/doc/why) لمزيد من التفاصيل.