@intlayer/docs 8.7.0-canary.0 → 8.7.0-canary.1
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.
- package/blog/ar/i18n_using_next-i18next.md +1 -1
- package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/de/i18n_using_next-i18next.md +1 -1
- package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/en/i18n_using_next-i18next.md +1 -1
- package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/en-GB/i18n_using_next-i18next.md +1 -1
- package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/es/i18n_using_next-i18next.md +1 -1
- package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/fr/i18n_using_next-i18next.md +1 -1
- package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/hi/i18n_using_next-i18next.md +1 -1
- package/blog/id/i18n_using_next-i18next.md +1 -1
- package/blog/id/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/it/i18n_using_next-i18next.md +1 -1
- package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ja/i18n_using_next-i18next.md +1 -1
- package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ko/i18n_using_next-i18next.md +1 -1
- package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/pl/i18n_using_next-i18next.md +1 -1
- package/blog/pl/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/pt/i18n_using_next-i18next.md +1 -1
- package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ru/i18n_using_next-i18next.md +1 -1
- package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/tr/i18n_using_next-i18next.md +1 -1
- package/blog/uk/i18n_using_next-i18next.md +1 -1
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/vi/i18n_using_next-i18next.md +1 -1
- package/blog/vi/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/zh/i18n_using_next-i18next.md +1 -1
- package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/docs/ar/bundle_optimization.md +454 -0
- package/docs/ar/intlayer_with_next-i18next.md +1 -1
- package/docs/ar/intlayer_with_next-intl.md +1 -1
- package/docs/ar/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ar/intlayer_with_tanstack.md +45 -68
- package/docs/bn/bundle_optimization.md +454 -0
- package/docs/cs/bundle_optimization.md +454 -0
- package/docs/de/bundle_optimization.md +454 -0
- package/docs/de/intlayer_with_next-i18next.md +1 -1
- package/docs/de/intlayer_with_next-intl.md +1 -1
- package/docs/de/intlayer_with_tanstack+solid.md +24 -5
- package/docs/de/intlayer_with_tanstack.md +45 -68
- package/docs/en/bundle_optimization.md +36 -8
- package/docs/en/intlayer_with_next-i18next.md +1 -1
- package/docs/en/intlayer_with_next-intl.md +1 -1
- package/docs/en/intlayer_with_tanstack+solid.md +24 -5
- package/docs/en/intlayer_with_tanstack.md +45 -68
- package/docs/en-GB/bundle_optimization.md +454 -0
- package/docs/en-GB/intlayer_with_next-i18next.md +1 -1
- package/docs/en-GB/intlayer_with_next-intl.md +1 -1
- package/docs/en-GB/intlayer_with_tanstack+solid.md +24 -5
- package/docs/en-GB/intlayer_with_tanstack.md +47 -70
- package/docs/es/bundle_optimization.md +454 -0
- package/docs/es/intlayer_with_next-i18next.md +1 -1
- package/docs/es/intlayer_with_next-intl.md +1 -1
- package/docs/es/intlayer_with_tanstack+solid.md +24 -5
- package/docs/es/intlayer_with_tanstack.md +45 -68
- package/docs/fr/bundle_optimization.md +454 -0
- package/docs/fr/intlayer_with_next-i18next.md +1 -1
- package/docs/fr/intlayer_with_next-intl.md +1 -1
- package/docs/fr/intlayer_with_tanstack+solid.md +24 -5
- package/docs/fr/intlayer_with_tanstack.md +45 -68
- package/docs/hi/bundle_optimization.md +454 -0
- package/docs/hi/intlayer_with_next-i18next.md +1 -1
- package/docs/hi/intlayer_with_next-intl.md +1 -1
- package/docs/hi/intlayer_with_tanstack+solid.md +24 -5
- package/docs/hi/intlayer_with_tanstack.md +45 -68
- package/docs/id/bundle_optimization.md +454 -0
- package/docs/id/intlayer_with_next-i18next.md +1 -1
- package/docs/id/intlayer_with_next-intl.md +1 -1
- package/docs/id/intlayer_with_tanstack+solid.md +24 -5
- package/docs/id/intlayer_with_tanstack.md +45 -68
- package/docs/it/bundle_optimization.md +454 -0
- package/docs/it/intlayer_with_next-i18next.md +1 -1
- package/docs/it/intlayer_with_next-intl.md +1 -1
- package/docs/it/intlayer_with_tanstack+solid.md +24 -5
- package/docs/it/intlayer_with_tanstack.md +45 -68
- package/docs/ja/bundle_optimization.md +454 -0
- package/docs/ja/intlayer_with_next-i18next.md +1 -1
- package/docs/ja/intlayer_with_next-intl.md +1 -1
- package/docs/ja/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ja/intlayer_with_tanstack.md +45 -36
- package/docs/ko/bundle_optimization.md +454 -0
- package/docs/ko/intlayer_with_next-i18next.md +1 -1
- package/docs/ko/intlayer_with_next-intl.md +1 -1
- package/docs/ko/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ko/intlayer_with_tanstack.md +45 -68
- package/docs/nl/bundle_optimization.md +454 -0
- package/docs/pl/bundle_optimization.md +454 -0
- package/docs/pl/intlayer_with_next-i18next.md +1 -1
- package/docs/pl/intlayer_with_next-intl.md +1 -1
- package/docs/pl/intlayer_with_tanstack+solid.md +24 -5
- package/docs/pl/intlayer_with_tanstack.md +45 -68
- package/docs/pt/bundle_optimization.md +454 -0
- package/docs/pt/intlayer_with_next-i18next.md +1 -1
- package/docs/pt/intlayer_with_next-intl.md +1 -1
- package/docs/pt/intlayer_with_tanstack+solid.md +24 -5
- package/docs/pt/intlayer_with_tanstack.md +45 -68
- package/docs/ru/bundle_optimization.md +454 -0
- package/docs/ru/intlayer_with_next-i18next.md +1 -1
- package/docs/ru/intlayer_with_next-intl.md +1 -1
- package/docs/ru/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ru/intlayer_with_tanstack.md +45 -68
- package/docs/tr/bundle_optimization.md +454 -0
- package/docs/tr/intlayer_with_next-i18next.md +1 -1
- package/docs/tr/intlayer_with_next-intl.md +1 -1
- package/docs/tr/intlayer_with_tanstack+solid.md +24 -5
- package/docs/tr/intlayer_with_tanstack.md +45 -68
- package/docs/uk/bundle_optimization.md +454 -0
- package/docs/uk/intlayer_with_next-i18next.md +1 -1
- package/docs/uk/intlayer_with_next-intl.md +1 -1
- package/docs/uk/intlayer_with_tanstack+solid.md +24 -5
- package/docs/uk/intlayer_with_tanstack.md +45 -68
- package/docs/ur/bundle_optimization.md +454 -0
- package/docs/vi/bundle_optimization.md +454 -0
- package/docs/vi/intlayer_with_next-i18next.md +1 -1
- package/docs/vi/intlayer_with_next-intl.md +1 -1
- package/docs/vi/intlayer_with_tanstack+solid.md +24 -5
- package/docs/vi/intlayer_with_tanstack.md +45 -68
- package/docs/zh/bundle_optimization.md +454 -0
- package/docs/zh/intlayer_with_next-i18next.md +1 -1
- package/docs/zh/intlayer_with_next-intl.md +1 -1
- package/docs/zh/intlayer_with_tanstack+solid.md +24 -5
- package/docs/zh/intlayer_with_tanstack.md +45 -68
- package/package.json +7 -7
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-11-25
|
|
3
|
+
updatedAt: 2026-04-08
|
|
4
|
+
title: تحسين حجم وأداء حزمة i18n
|
|
5
|
+
description: تقليل حجم حزمة التطبيق من خلال تحسين محتوى التدويل (i18n). تعلّم كيفية الاستفادة من tree shaking والتحميل الكسول للقواميس باستخدام Intlayer.
|
|
6
|
+
keywords:
|
|
7
|
+
- تحسين الحزمة
|
|
8
|
+
- أتمتة المحتوى
|
|
9
|
+
- المحتوى الديناميكي
|
|
10
|
+
- Intlayer
|
|
11
|
+
- Next.js
|
|
12
|
+
- JavaScript
|
|
13
|
+
- React
|
|
14
|
+
slugs:
|
|
15
|
+
- doc
|
|
16
|
+
- concept
|
|
17
|
+
- bundle-optimization
|
|
18
|
+
history:
|
|
19
|
+
- version: 8.7.0
|
|
20
|
+
date: 2026-04-08
|
|
21
|
+
changes: "إضافة خيارات `minify` و `purge` إلى تكوين البناء"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# تحسين حجم وأداء حزمة i18n
|
|
25
|
+
|
|
26
|
+
واحدة من أكثر التحديات شيوعًا مع حلول i18n التقليدية التي تعتمد على ملفات JSON هي إدارة حجم المحتوى. إذا لم يقم المطورون بفصل المحتوى يدويًا إلى مساحات أسماء، غالبًا ما ينتهي الأمر بالمستخدمين إلى تنزيل ترجمات لكل صفحة وربما لكل لغة لمجرد عرض صفحة واحدة.
|
|
27
|
+
|
|
28
|
+
على سبيل المثال، قد يؤدي تطبيق يحتوي على 10 صفحات مترجمة إلى 10 لغات إلى قيام المستخدم بتنزيل محتوى 100 صفحة، على الرغم من حاجته إلى **صفحة واحدة** فقط (الصفحة الحالية باللغة الحالية). هذا يؤدي إلى هدر في عرض النطاق الترددي وبطء في أوقات التحميل.
|
|
29
|
+
|
|
30
|
+
**تحل Intlayer هذه المشكلة من خلال تحسين وقت البناء.** تقوم بتحليل الكود الخاص بك لاكتشاف القواميس المستخدمة فعليًا لكل مكون وتعيد حقن المحتوى الضروري فقط في الحزمة الخاصة بك.
|
|
31
|
+
|
|
32
|
+
## جدول المحتويات
|
|
33
|
+
|
|
34
|
+
<TOC />
|
|
35
|
+
|
|
36
|
+
## فحص حزمتك
|
|
37
|
+
|
|
38
|
+
يعد تحليل حزمتك الخطوة الأولى في تحديد ملفات JSON "الثقيلة" وفرص تقسيم الكود. تولد هذه الأدوات خريطة شجرية (treemap) مرئية للكود المجمع لتطبيقك، مما يسمح لك برؤية أي المكتبات تستهلك أكبر مساحة.
|
|
39
|
+
|
|
40
|
+
<Tabs>
|
|
41
|
+
<Tab value="vite">
|
|
42
|
+
|
|
43
|
+
### Vite / Rollup
|
|
44
|
+
|
|
45
|
+
يستخدم Vite مكتبة Rollup في الخلفية. تولد إضافة `rollup-plugin-visualizer` ملف HTML تفاعلي يوضح حجم كل وحدة في الرسم البياني الخاص بك.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -D rollup-plugin-visualizer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```typescript fileName="vite.config.ts"
|
|
52
|
+
import { defineConfig } from "vite";
|
|
53
|
+
import { visualizer } from "rollup-plugin-visualizer";
|
|
54
|
+
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
plugins: [
|
|
57
|
+
visualizer({
|
|
58
|
+
open: true, // افتح التقرير تلقائيًا في متصفحك
|
|
59
|
+
filename: "stats.html",
|
|
60
|
+
gzipSize: true,
|
|
61
|
+
brotliSize: true,
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
</Tab>
|
|
68
|
+
<Tab value="nextjs (turbopack)">
|
|
69
|
+
|
|
70
|
+
### Next.js (Turbopack)
|
|
71
|
+
|
|
72
|
+
للمشاريع التي تستخدم App Router و Turbopack، يوفر Next.js محللاً تجريبيًا مدمجاً لا يتطلب أي تبعات إضافية.
|
|
73
|
+
|
|
74
|
+
```bash packageManager='npm'
|
|
75
|
+
npx next experimental-analyze
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```bash packageManager='yarn'
|
|
79
|
+
yarn next experimental-analyze
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```bash packageManager='pnpm'
|
|
83
|
+
pnpm next experimental-analyze
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```bash packageManager='bun'
|
|
87
|
+
bun next experimental-analyze
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
</Tab>
|
|
91
|
+
<Tab value="nextjs (Webpack)">
|
|
92
|
+
|
|
93
|
+
### Next.js (Webpack)
|
|
94
|
+
|
|
95
|
+
إذا كنت تستخدم مجمع Webpack الافتراضي في Next.js، فاستخدم محلل الحزمة الرسمي. قم بتفعيله عن طريق تعيين متغير بيئة أثناء البناء.
|
|
96
|
+
|
|
97
|
+
```bash packageManager='npm'
|
|
98
|
+
npm install -D @next/bundle-analyzer
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```bash packageManager='yarn'
|
|
102
|
+
yarn add -D @next/bundle-analyzer
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```bash packageManager='pnpm'
|
|
106
|
+
pnpm add -D @next/bundle-analyzer
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```bash packageManager='bun'
|
|
110
|
+
bun add -d @next/bundle-analyzer
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```javascript fileName="next.config.js"
|
|
114
|
+
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
|
115
|
+
enabled: process.env.ANALYZE === "true",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
module.exports = withBundleAnalyzer({
|
|
119
|
+
// تكوين Next.js الخاص بك
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**الاستخدام:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ANALYZE=true npm run build
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
</Tab>
|
|
130
|
+
<Tab value="Webpack (CRA / Angular / etc)">
|
|
131
|
+
|
|
132
|
+
### Webpack القياسي
|
|
133
|
+
|
|
134
|
+
بالنسبة لـ Create React App (ejected)، أو Angular، أو إعدادات Webpack المخصصة، استخدم `webpack-bundle-analyzer` القياسي في الصناعة.
|
|
135
|
+
|
|
136
|
+
```bash packageManager='npm'
|
|
137
|
+
npm install -D webpack-bundle-analyzer
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```bash packageManager='yarn'
|
|
141
|
+
yarn add -D webpack-bundle-analyzer
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```bash packageManager='pnpm'
|
|
145
|
+
pnpm add -D webpack-bundle-analyzer
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```bash packageManager='bun'
|
|
149
|
+
bun add -d webpack-bundle-analyzer
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```typescript fileName="webpack.config.ts
|
|
153
|
+
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
|
154
|
+
|
|
155
|
+
export default {
|
|
156
|
+
plugins: [
|
|
157
|
+
new BundleAnalyzerPlugin({
|
|
158
|
+
analyzerMode: "static",
|
|
159
|
+
reportFilename: "bundle-analyzer.html",
|
|
160
|
+
openAnalyzer: false,
|
|
161
|
+
}),
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
</Tab>
|
|
167
|
+
</Tabs>
|
|
168
|
+
|
|
169
|
+
## كيف تعمل
|
|
170
|
+
|
|
171
|
+
تستخدم Intlayer **نهجًا لكل مكون**. على عكس ملفات JSON العالمية، يتم تعريف المحتوى الخاص بك بجانب مكوناتك أو داخلها. خلال عملية البناء، تقوم Intlayer بـ:
|
|
172
|
+
|
|
173
|
+
1. **تحليل** الكود الخاص بك للعثور على استدعاءات `useIntlayer`.
|
|
174
|
+
2. **بناء** محتوى القاموس المقابل.
|
|
175
|
+
3. **استبدال** استدعاء `useIntlayer` بكود محسن بناءً على تكوينك.
|
|
176
|
+
|
|
177
|
+
هذا يضمن أن:
|
|
178
|
+
|
|
179
|
+
- إذا لم يتم استيراد مكون، فلن يتم تضمين محتواه في الحزمة (إزالة الكود الميت).
|
|
180
|
+
- إذا تم تحميل مكون بشكل كسول، فسيتم أيضًا تحميل محتواه بشكل كسول.
|
|
181
|
+
|
|
182
|
+
## الإعداد حسب المنصة
|
|
183
|
+
|
|
184
|
+
<Tabs>
|
|
185
|
+
<Tab value="nextjs">
|
|
186
|
+
|
|
187
|
+
### Next.js
|
|
188
|
+
|
|
189
|
+
يتطلب Next.js إضافة `@intlayer/swc` للتعامل مع التحويل، حيث يستخدم Next.js SWC في عمليات البناء.
|
|
190
|
+
|
|
191
|
+
> هذه الإضافة ليست مثبتة افتراضيًا لأن إضافات SWC لا تزال تجريبية لـ Next.js. قد يتغير ذلك في المستقبل.
|
|
192
|
+
|
|
193
|
+
```bash packageManager="npm"
|
|
194
|
+
npm install -D @intlayer/swc
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```bash packageManager="yarn"
|
|
198
|
+
yarn add -D @intlayer/swc
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```bash packageManager="pnpm"
|
|
202
|
+
pnpm add -D @intlayer/swc
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```bash packageManager="bun"
|
|
206
|
+
bun add -d @intlayer/swc
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
بمجرد التثبيت، سيتعرف Intlayer تلقائيًا على الإضافة ويستخدمها.
|
|
210
|
+
|
|
211
|
+
</Tab>
|
|
212
|
+
<Tab value="vite">
|
|
213
|
+
|
|
214
|
+
### Vite
|
|
215
|
+
|
|
216
|
+
يستخدم Vite إضافة `@intlayer/babel` التي تدرج كاعتماد ضمن `vite-intlayer`. يتم تمكين التحسين بشكل افتراضي. لا يلزم القيام بأي شيء آخر.
|
|
217
|
+
|
|
218
|
+
</Tab>
|
|
219
|
+
<Tab value="webpack">
|
|
220
|
+
|
|
221
|
+
### Webpack
|
|
222
|
+
|
|
223
|
+
لتمكين تحسين الحزمة مع Intlayer على Webpack، تحتاج إلى تثبيت وتكوين إضافة Babel المناسبة (`@intlayer/babel`) أو SWC (`@intlayer/swc`).
|
|
224
|
+
|
|
225
|
+
```bash packageManager="npm"
|
|
226
|
+
npm install -D @intlayer/babel
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
```bash packageManager="yarn"
|
|
230
|
+
yarn add -D @intlayer/babel
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```bash packageManager="pnpm"
|
|
234
|
+
pnpm add -D @intlayer/babel
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```bash packageManager="bun"
|
|
238
|
+
bun add -d @intlayer/babel
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
```typescript fileName="babel.config.js"
|
|
242
|
+
const {
|
|
243
|
+
getOptimizePluginOptions,
|
|
244
|
+
intlayerOptimizeBabelPlugin,
|
|
245
|
+
} = require("@intlayer/babel");
|
|
246
|
+
|
|
247
|
+
module.exports = {
|
|
248
|
+
plugins: [[intlayerOptimizeBabelPlugin, getOptimizePluginOptions()]],
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
</Tab>
|
|
253
|
+
</Tabs>
|
|
254
|
+
|
|
255
|
+
## التكوين
|
|
256
|
+
|
|
257
|
+
يمكنك التحكم في كيفية تحسين Intlayer لحزمتك عبر خاصية `build` في ملف `intlayer.config.ts` الخاص بك.
|
|
258
|
+
|
|
259
|
+
```typescript fileName="intlayer.config.ts"
|
|
260
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
261
|
+
|
|
262
|
+
const config: IntlayerConfig = {
|
|
263
|
+
internationalization: {
|
|
264
|
+
locales: [Locales.ENGLISH, Locales.FRENCH],
|
|
265
|
+
defaultLocale: Locales.ENGLISH,
|
|
266
|
+
},
|
|
267
|
+
dictionary: {
|
|
268
|
+
importMode: "dynamic",
|
|
269
|
+
},
|
|
270
|
+
build: {
|
|
271
|
+
/**
|
|
272
|
+
* تصغير القواميس لتقليل حجم الحزمة.
|
|
273
|
+
*/
|
|
274
|
+
minify: true;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* تطهير المفاتيح غير المستخدمة في القواميس
|
|
278
|
+
*/
|
|
279
|
+
purge: true;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* يشير إلى ما إذا كان يجب على البناء التحقق من أنواع TypeScript
|
|
283
|
+
*/
|
|
284
|
+
checkTypes: false;
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export default config;
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
> يُنصح بالاحتفاظ بالخيار الافتراضي لـ `optimize` في معظم الحالات.
|
|
292
|
+
|
|
293
|
+
> راجع توثيق التكوين لمزيد من التفاصيل: [التكوين](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ar/configuration.md)
|
|
294
|
+
|
|
295
|
+
### خيارات البناء
|
|
296
|
+
|
|
297
|
+
الخيارات التالية متاحة ضمن كائن تكوين `build`:
|
|
298
|
+
|
|
299
|
+
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|
|
300
|
+
| :------------- | :-------- | :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| **`optimize`** | `boolean` | `undefined` | يتحكم فيما إذا كان تفعيل تحسين البناء ممكّنًا. إذا كانت القيمة `true`، يقوم Intlayer باستبدال استدعاءات القاموس بحقنات محسّنة. إذا كانت `false`، يتم تعطيل التحسين. من المثالي تعيينها إلى `true` في بيئة الإنتاج. |
|
|
302
|
+
| **`minify`** | `boolean` | `false` | ما إذا كان سيتم تصغير القواميس لتقليل حجم الحزمة. |
|
|
303
|
+
| **`purge`** | `boolean` | `false` | ما إذا كان سيتم تطهير المفاتيح غير المستخدمة في القواميس. |
|
|
304
|
+
|
|
305
|
+
### التصغير (Minification)
|
|
306
|
+
|
|
307
|
+
يؤدي تصغير القواميس إلى إزالة المسافات البيضاء والتعليقات غير الضرورية وتقليل حجم محتوى JSON. هذا مفيد بشكل خاص للقواميس الكبيرة.
|
|
308
|
+
|
|
309
|
+
```typescript fileName="intlayer.config.ts"
|
|
310
|
+
import type { IntlayerConfig } from "intlayer";
|
|
311
|
+
|
|
312
|
+
const config: IntlayerConfig = {
|
|
313
|
+
build: {
|
|
314
|
+
minify: true,
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
export default config;
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
> ملاحظة: يتم تجاهل التصغير إذا تم تعطيل `optimize` أو إذا تم تمكين المحرر المرئي (Visual Editor) (لأن المحرر يحتاج إلى المحتوى الكامل للسماح بالتحرير).
|
|
322
|
+
|
|
323
|
+
### التطهير (Purging)
|
|
324
|
+
|
|
325
|
+
يضمن التطهير تضمين المفاتيح المستخدمة فعليًا في الكود الخاص بك فقط في حزمة القاموس النهائية. يمكن أن يقلل هذا بشكل كبير من حجم الحزمة الخاصة بك إذا كان لديك قواميس كبيرة تحتوي على العديد من المفاتيح التي لا تُستخدم في كل جزء من تطبيقك.
|
|
326
|
+
|
|
327
|
+
```typescript fileName="intlayer.config.ts"
|
|
328
|
+
import type { IntlayerConfig } from "intlayer";
|
|
329
|
+
|
|
330
|
+
const config: IntlayerConfig = {
|
|
331
|
+
build: {
|
|
332
|
+
purge: true,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
export default config;
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
> ملاحظة: يتم تجاهل التطهير إذا تم تعطيل `optimize`.
|
|
340
|
+
|
|
341
|
+
### وضع الاستيراد (Import Mode)
|
|
342
|
+
|
|
343
|
+
بالنسبة للتطبيقات الكبيرة، التي تتضمن عدة صفحات ولغات، يمكن لملفات JSON أن تمثل جزءًا كبيرًا من حجم الحزمة. يسمح لك Intlayer بالتحكم في كيفية تحميل القواميس.
|
|
344
|
+
|
|
345
|
+
يمكن تعريف وضع الاستيراد افتراضيًا بشكل عالمي في ملف `intlayer.config.ts`.
|
|
346
|
+
|
|
347
|
+
```typescript fileName="intlayer.config.ts"
|
|
348
|
+
import type { IntlayerConfig } from "intlayer";
|
|
349
|
+
|
|
350
|
+
const config: IntlayerConfig = {
|
|
351
|
+
build: {
|
|
352
|
+
minify: true,
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
export default config;
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
وكذلك لكل قاموس في ملفات `.content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5}}` الخاصة بك.
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import { type Dictionary, t } from "intlayer";
|
|
363
|
+
|
|
364
|
+
const appContent: Dictionary = {
|
|
365
|
+
key: "app",
|
|
366
|
+
importMode: "dynamic", // تجاوز وضع الاستيراد الافتراضي
|
|
367
|
+
content: {
|
|
368
|
+
// ...
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export default appContent;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|
|
376
|
+
| :--------------- | :--------------------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------- |
|
|
377
|
+
| **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **برجاء استبداله:** استخدم `dictionary.importMode` بدلاً منه. يحدد كيفية تحميل القواميس (انظر التفاصيل أدناه). |
|
|
378
|
+
|
|
379
|
+
تحدد إعدادات `importMode` كيفية حقن محتوى القاموس في مكونك.
|
|
380
|
+
يمكنك تعريفه عالميًا في ملف `intlayer.config.ts` تحت كائن `dictionary` أو تجاوزه لقاموس معين في ملف `.content.ts` الخاص به.
|
|
381
|
+
|
|
382
|
+
### 1. الوضع الثابت (`default`)
|
|
383
|
+
|
|
384
|
+
في الوضع الثابت، يقوم Intlayer باستبدال `useIntlayer` بـ `useDictionary` ويحقن القاموس مباشرة في حزمة جافا سكريبت.
|
|
385
|
+
|
|
386
|
+
- **الإيجابيات:** عرض فوري (متزامن)، بدون طلبات شبكة إضافية أثناء التهيئة.
|
|
387
|
+
- **السلبيات:** الحزمة تتضمن ترجمات **لكل** اللغات المتاحة لذلك المكون المحدد.
|
|
388
|
+
- **الأفضل لـ:** تطبيقات الصفحة الواحدة (SPA).
|
|
389
|
+
|
|
390
|
+
**مثال على الكود المحول:**
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
// كودك
|
|
394
|
+
const content = useIntlayer("my-key");
|
|
395
|
+
|
|
396
|
+
// الكود المحسن (الوضع الثابت)
|
|
397
|
+
const content = useDictionary({
|
|
398
|
+
key: "my-key",
|
|
399
|
+
content: {
|
|
400
|
+
nodeType: "translation",
|
|
401
|
+
translation: {
|
|
402
|
+
en: "My title",
|
|
403
|
+
fr: "Mon titre",
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 2. الوضع الديناميكي
|
|
410
|
+
|
|
411
|
+
في الوضع الديناميكي، يستبدل Intlayer الدالة `useIntlayer` بـ `useDictionaryAsync`. تستخدم هذه الدالة `import()` (آلية تشبه Suspense) لتحميل ملف JSON الخاص باللغة الحالية بشكل كسول.
|
|
412
|
+
|
|
413
|
+
- **الإيجابيات:** **تحسين على مستوى اللغة (Locale-level tree shaking).** المستخدم الذي يعرض النسخة الإنجليزية سيقوم بتحميل قاموس اللغة الإنجليزية فقط. قاموس اللغة الفرنسية لن يتم تحميله أبدًا.
|
|
414
|
+
- **السلبيات:** يؤدي إلى طلب شبكة (جلب الأصول) لكل مكون أثناء عملية التهيئة (hydration).
|
|
415
|
+
- **الأفضل لـ:** كتل نصية كبيرة، مقالات، أو تطبيقات تدعم العديد من اللغات حيث يكون حجم الحزمة أمرًا حاسمًا.
|
|
416
|
+
|
|
417
|
+
**مثال على الكود المحول:**
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
// كودك
|
|
421
|
+
const content = useIntlayer("my-key");
|
|
422
|
+
|
|
423
|
+
// الكود المحسن (الديناميكي)
|
|
424
|
+
const content = useDictionaryAsync({
|
|
425
|
+
en: () =>
|
|
426
|
+
import(".intlayer/dynamic_dictionary/my-key/en.json").then(
|
|
427
|
+
(mod) => mod.default
|
|
428
|
+
),
|
|
429
|
+
fr: () =>
|
|
430
|
+
import(".intlayer/dynamic_dictionary/my-key/fr.json").then(
|
|
431
|
+
(mod) => mod.default
|
|
432
|
+
),
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
> عند استخدام `importMode: 'dynamic'`، إذا كان لديك 100 مكون يستخدم `useIntlayer` في صفحة واحدة، سيحاول المتصفح إجراء 100 طلب تحميل منفصل. لتجنب هذا "تسلسل" الطلبات، قم بتجميع المحتوى في عدد أقل من ملفات `.content` (مثل قاموس واحد لكل قسم من الصفحة) بدلاً من واحد لكل مكون ذرة.
|
|
437
|
+
|
|
438
|
+
### 3. وضع الجلب (Fetch Mode)
|
|
439
|
+
|
|
440
|
+
يتصرف بشكل مشابه للوضع الديناميكي لكنه يحاول أولاً جلب القواميس من واجهة برمجة تطبيقات المزامنة الحية Intlayer Live Sync API. إذا فشل استدعاء API أو لم يكن المحتوى معلمًا للتحديثات الحية، فإنه يعود إلى الاستيراد الديناميكي.
|
|
441
|
+
|
|
442
|
+
> راجع توثيق CMS لمزيد من التفاصيل: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ar/intlayer_CMS.md)
|
|
443
|
+
|
|
444
|
+
> في وضع الجلب، لا يمكن استخدام التطهير والتصغير.
|
|
445
|
+
|
|
446
|
+
## الملخص: الوضع الثابت مقابل الوضع الديناميكي
|
|
447
|
+
|
|
448
|
+
| الميزة | الوضع الثابت | الوضع الديناميكي |
|
|
449
|
+
| :----------------------- | :--------------------------------------- | :-------------------------------------------------- |
|
|
450
|
+
| **حجم حزمة جافا سكريبت** | أكبر (يشمل جميع اللغات للمكون) | الأصغر (كود فقط، بدون محتوى) |
|
|
451
|
+
| **التحميل الأولي** | فوري (المحتوى موجود في الحزمة) | تأخير بسيط (يجلب JSON) |
|
|
452
|
+
| **طلبات الشبكة** | 0 طلبات إضافية | طلب واحد لكل قاموس |
|
|
453
|
+
| **Tree Shaking** | على مستوى المكون | على مستوى المكون + مستوى اللغة |
|
|
454
|
+
| **أفضل استخدام** | مكونات واجهة المستخدم، التطبيقات الصغيرة | الصفحات التي تحتوي على نصوص كثيرة، العديد من اللغات |
|
|
@@ -258,7 +258,7 @@ export default function LocaleLayout({
|
|
|
258
258
|
}) {
|
|
259
259
|
// تحديد اللغة بناءً على المعلمات أو استخدام اللغة الافتراضية
|
|
260
260
|
const locale: Locale = (locales as readonly string[]).includes(params.locale)
|
|
261
|
-
?
|
|
261
|
+
? params.locale
|
|
262
262
|
: defaultLocale;
|
|
263
263
|
|
|
264
264
|
// تحديد اتجاه النص بناءً على اللغة (يمين إلى يسار أو يسار إلى يمين)
|
|
@@ -103,7 +103,7 @@ async function loadMessages(locale: string) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export default getRequestConfig(async ({ locale }) => {
|
|
106
|
-
if (!locales.includes(locale
|
|
106
|
+
if (!locales.includes(locale)) notFound();
|
|
107
107
|
|
|
108
108
|
return {
|
|
109
109
|
messages: await loadMessages(locale),
|
|
@@ -193,9 +193,7 @@ const RootComponent: ParentComponent = (props) => {
|
|
|
193
193
|
</head>
|
|
194
194
|
<body>
|
|
195
195
|
<IntlayerProvider locale={locale}>
|
|
196
|
-
<Suspense>
|
|
197
|
-
{props.children}
|
|
198
|
-
</Suspense>
|
|
196
|
+
<Suspense>{props.children}</Suspense>
|
|
199
197
|
</IntlayerProvider>
|
|
200
198
|
<Scripts />
|
|
201
199
|
</body>
|
|
@@ -505,12 +503,33 @@ export const Route = createFileRoute("/{-$locale}/")({
|
|
|
505
503
|
component: RouteComponent,
|
|
506
504
|
head: ({ params }) => {
|
|
507
505
|
const { locale } = params;
|
|
508
|
-
const
|
|
506
|
+
const path = "/"; // The path for this route
|
|
507
|
+
|
|
508
|
+
const metaContent = getIntlayer("app", locale);
|
|
509
509
|
|
|
510
510
|
return {
|
|
511
|
+
links: [
|
|
512
|
+
// Canonical link: Points to the current localized page
|
|
513
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
514
|
+
|
|
515
|
+
// Hreflang: Tell Google about all localized versions
|
|
516
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
517
|
+
rel: "alternate",
|
|
518
|
+
hrefLang: mapLocale,
|
|
519
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
520
|
+
})),
|
|
521
|
+
|
|
522
|
+
// x-default: For users in unmatched languages
|
|
523
|
+
// Define the default fallback locale (usually your primary language)
|
|
524
|
+
{
|
|
525
|
+
rel: "alternate",
|
|
526
|
+
hrefLang: "x-default",
|
|
527
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
528
|
+
},
|
|
529
|
+
],
|
|
511
530
|
meta: [
|
|
512
531
|
{ title: metaContent.title },
|
|
513
|
-
{
|
|
532
|
+
{ name: "description", content: metaContent.meta.description },
|
|
514
533
|
],
|
|
515
534
|
};
|
|
516
535
|
},
|
|
@@ -224,9 +224,7 @@ function RootDocument({ children }: { children: ReactNode }) {
|
|
|
224
224
|
<HeadContent />
|
|
225
225
|
</head>
|
|
226
226
|
<body>
|
|
227
|
-
<IntlayerProvider locale={locale}>
|
|
228
|
-
{children}
|
|
229
|
-
</IntlayerProvider>
|
|
227
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
230
228
|
<Scripts />
|
|
231
229
|
</body>
|
|
232
230
|
</html>
|
|
@@ -325,30 +323,20 @@ import { getPrefix } from "intlayer";
|
|
|
325
323
|
|
|
326
324
|
export const LOCALE_ROUTE = "{-$locale}" as const;
|
|
327
325
|
|
|
328
|
-
|
|
329
|
-
export type RemoveLocaleParam<T> = T extends string
|
|
330
|
-
? RemoveLocaleFromString<T>
|
|
331
|
-
: T;
|
|
326
|
+
export type To = StripLocalePrefix<LinkComponentProps["to"]>;
|
|
332
327
|
|
|
333
|
-
export type
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
328
|
+
export type StripLocalePrefix<T extends string | undefined> = T extends
|
|
329
|
+
| `/${typeof LOCALE_ROUTE}/`
|
|
330
|
+
| `/${typeof LOCALE_ROUTE}`
|
|
331
|
+
? "/"
|
|
332
|
+
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
333
|
+
? `/${Rest}`
|
|
334
|
+
: T;
|
|
337
335
|
|
|
338
336
|
type LocalizedLinkProps = {
|
|
339
337
|
to?: To;
|
|
340
338
|
} & Omit<LinkComponentProps, "to">;
|
|
341
339
|
|
|
342
|
-
// المساعدات
|
|
343
|
-
type RemoveAll<
|
|
344
|
-
S extends string,
|
|
345
|
-
Sub extends string,
|
|
346
|
-
> = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
|
|
347
|
-
|
|
348
|
-
type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
|
|
349
|
-
RemoveAll<S, typeof LOCALE_ROUTE>
|
|
350
|
-
>;
|
|
351
|
-
|
|
352
340
|
export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
353
341
|
const { locale } = useLocale();
|
|
354
342
|
const { localePrefix } = getPrefix(locale);
|
|
@@ -377,26 +365,26 @@ export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
|
377
365
|
import { useNavigate } from "@tanstack/react-router";
|
|
378
366
|
import { getPrefix } from "intlayer";
|
|
379
367
|
import { useLocale } from "react-intlayer";
|
|
380
|
-
import {
|
|
368
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
381
369
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
382
370
|
|
|
383
|
-
type
|
|
384
|
-
|
|
385
|
-
| `/${typeof LOCALE_ROUTE}/`
|
|
386
|
-
? "/"
|
|
387
|
-
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
388
|
-
? `/${Rest}`
|
|
389
|
-
: never;
|
|
371
|
+
type NavigateFn = ReturnType<typeof useNavigate>;
|
|
372
|
+
type BaseNavigateOptions = Parameters<NavigateFn>[0];
|
|
390
373
|
|
|
391
374
|
type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
|
|
392
375
|
|
|
393
|
-
type
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
376
|
+
export type LocalizedNavigateOptions = Omit<
|
|
377
|
+
BaseNavigateOptions,
|
|
378
|
+
"to" | "params"
|
|
379
|
+
> & {
|
|
380
|
+
to: LocalizedTo;
|
|
381
|
+
params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
|
|
398
382
|
};
|
|
399
383
|
|
|
384
|
+
type LocalizedNavigate = (
|
|
385
|
+
options: LocalizedNavigateOptions
|
|
386
|
+
) => ReturnType<NavigateFn>;
|
|
387
|
+
|
|
400
388
|
export const useLocalizedNavigate = () => {
|
|
401
389
|
const navigate = useNavigate();
|
|
402
390
|
|
|
@@ -443,38 +431,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
|
|
|
443
431
|
|
|
444
432
|
export const Route = createFileRoute("/{-$locale}/")({
|
|
445
433
|
component: RouteComponent,
|
|
446
|
-
head: ({ params }) => {
|
|
447
|
-
const { locale } = params;
|
|
448
|
-
const path = "/"; // The path for this route
|
|
449
|
-
|
|
450
|
-
const metaContent = getIntlayer("app", locale);
|
|
451
|
-
|
|
452
|
-
return {
|
|
453
|
-
links: [
|
|
454
|
-
// Canonical link: Points to the current localized page
|
|
455
|
-
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
456
|
-
|
|
457
|
-
// Hreflang: Tell Google about all localized versions
|
|
458
|
-
...localeMap(({ locale: mapLocale }) => ({
|
|
459
|
-
rel: "alternate",
|
|
460
|
-
hrefLang: mapLocale,
|
|
461
|
-
href: getLocalizedUrl(path, mapLocale),
|
|
462
|
-
})),
|
|
463
|
-
|
|
464
|
-
// x-default: For users in unmatched languages
|
|
465
|
-
// Define the default fallback locale (usually your primary language)
|
|
466
|
-
{
|
|
467
|
-
rel: "alternate",
|
|
468
|
-
hrefLang: "x-default",
|
|
469
|
-
href: getLocalizedUrl(path, defaultLocale),
|
|
470
|
-
},
|
|
471
|
-
],
|
|
472
|
-
meta: [
|
|
473
|
-
{ title: metaContent.title },
|
|
474
|
-
{ name: "description", content: metaContent.meta.description },
|
|
475
|
-
],
|
|
476
|
-
};
|
|
477
|
-
},
|
|
478
434
|
});
|
|
479
435
|
|
|
480
436
|
function RouteComponent() {
|
|
@@ -629,12 +585,33 @@ export const Route = createFileRoute("/{-$locale}/")({
|
|
|
629
585
|
component: RouteComponent,
|
|
630
586
|
head: ({ params }) => {
|
|
631
587
|
const { locale } = params;
|
|
632
|
-
const
|
|
588
|
+
const path = "/"; // The path for this route
|
|
589
|
+
|
|
590
|
+
const metaContent = getIntlayer("app", locale);
|
|
633
591
|
|
|
634
592
|
return {
|
|
593
|
+
links: [
|
|
594
|
+
// Canonical link: Points to the current localized page
|
|
595
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
596
|
+
|
|
597
|
+
// Hreflang: Tell Google about all localized versions
|
|
598
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
599
|
+
rel: "alternate",
|
|
600
|
+
hrefLang: mapLocale,
|
|
601
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
602
|
+
})),
|
|
603
|
+
|
|
604
|
+
// x-default: For users in unmatched languages
|
|
605
|
+
// Define the default fallback locale (usually your primary language)
|
|
606
|
+
{
|
|
607
|
+
rel: "alternate",
|
|
608
|
+
hrefLang: "x-default",
|
|
609
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
610
|
+
},
|
|
611
|
+
],
|
|
635
612
|
meta: [
|
|
636
613
|
{ title: metaContent.title },
|
|
637
|
-
{
|
|
614
|
+
{ name: "description", content: metaContent.meta.description },
|
|
638
615
|
],
|
|
639
616
|
};
|
|
640
617
|
},
|