نريد تحديث إصدار Ember المستخدم في Discourse.
حاليًا، نحن على الإصدار 3.15، ونود الوصول إلى الإصدار 4.1
لقد حددنا هدفًا لإنهاء هذا العمل بحلول نهاية هذا العام. يرجى ملاحظة أن هذا الموضوع هو خارطة طريق، وجميع الخطط والتقديرات مؤقتة. لا يُقصد من هذا الموضوع استضافة أي نقاشات كبيرة حول الترقيات أو التغييرات المحددة. لذا، دعنا نحافظ على تركيز النقاش هنا. الأسئلة حول كيفية تأثير هذه التحديثات على موقعك/الإضافة/القالب هي خارج الموضوع. التغييرات المخطط لها بنسبة 100% في الواجهة الأمامية (تطبيق Ember).
سنكون حذرين للغاية بشأن التغييرات التي نقدمها. سيتم تحديث جميع الإضافات/القوالب الرسمية (بما في ذلك أي عمل مخصص قامت به CDCK لعملائها). سنرسل أيضًا طلبات السحب (PRs) لتحديث جميع الإضافات/القوالب غير الرسمية الشائعة.
لا تقلق إذا كان لديك إضافة/قالب مخصص قمت ببنائه لموقعك. سنضيف تحذيرات الإلغاء وننشئ الإعلانات اللازمة في الوقت المناسب لمنحك وقتًا كافيًا لإجراء أي تغييرات مطلوبة. سنحاول أيضًا توجيهك خلال هذه التغييرات إذا لزم الأمر.
لنبدأ بالأهداف. نريد بالطبع الوصول إلى أعلى إصدار متاح، ولكننا نحتاج إلى تحديد أهداف وسيطة بين الآن ووجهتنا النهائية.
نخطط لإجراء تحديثات تدريجية. بدلاً من ترقية رئيسية واحدة إلى 4.1، سنقوم بتقسيم هذا العمل إلى ست مراحل. تركز كل مرحلة على ترقية Ember واحدة.
| المرحلة | الحجم | التحديثات |
|---|---|---|
| 1 | size-l | Ember 3.15 → Ember 3.16 |
| 2 | size-m | Ember 3.16 → Ember 3.25 |
| 3 | size-xl | Ember 3.25 → Ember 3.26 (الجزء 1 - عام) Ember 3.25 → Ember 3.26 (الجزء 2 - القوالب) Ember 3.25 → Ember 3.26 (الجزء 3 - jQuery) Ember 3.25 → Ember 3.26 (الجزء 4 - التحضير لـ Octane) Ember 3.25 → Ember 3.26 (الجزء 5 - Octane) |
| 4 | size-l | Ember 3.26 → Ember 3.27 (الجزء 1 - عام) Ember 3.26 → Ember 3.27 (الجزء 2 - RenderTemplate) Ember 3.26 → Ember 3.27 (الجزء 3 - المكونات المدمجة القديمة) |
| 5 | size-s | Ember 3.27 → Ember 3.28 |
| 6 | size-s | Ember 3.28 → Ember 4.1 |
إن اختيار هذه الإصدارات المحددة يعتمد بالكامل على الإلغاءات وكمية العمل التي تقدمها - والمخاطر الضمنية.
لذا، دعنا نقسم هذه إلى أجزاء أكثر وضوحًا.
الإلغاءات لكل تحديث
المرحلة 1 (size-l)
Ember 3.15 → Ember 3.16
يُقدم هذا التحديث إلغاءً واحدًا فقط.
-
استخدم محلل Ember-CLI بدلاً من محلل المتغيرات القديمة
- حتى: 4.0.0 - size-lيحتوي Discourse على محلل مخصص خاص به يمتد حاليًا
Ember.DefaultResolver.الإصلاح الموصى به هو التخلي عن أي محللات مخصصة واستخدام محلل Ember-CLI resolver. من الأسهل قول ذلك من فعله لأن لدينا الكثير من المنطق المخصص للتعامل مع الإضافات والقوالب.
الحل الوسط الذي يعمل هو تمديد محلل Ember-CLI بدلاً من تمديد
Ember.DefaultResolver.
المرحلة 2 (size-m)
Ember 3.16 → Ember 3.25
يُقدم هذا التحديث ثمانية إلغاءات.
-
استخدم getter الخاص بـ Ember والتحقق صراحةً من undefined
- حتى: 4.0.0 - size-sهذا إلغاء بسيط. نحتاج فقط إلى إزالةgetWithDefaultمن قاعدة الكود لدينا، وهناك فقط بضع أماكن نستخدمها فيها. -
امتدادات نموذج السلسلة
- حتى: 4.0.0 - size-mهذا أيضًا تبديل بسيط ومنخفض المخاطر، لكننا نستخدم نموذج السلسلة الممتد الخاص بـ Ember في العديد من الأماكن. من فحص سريع، أعتقد أن لدينا أقل من 100 مكان نفعل ذلك فيه بين النواة والإضافات/القوالب.بمجرد تحديث تلك الأماكن، يجب علينا أيضًا منع Ember من تمديد ذلك النموذج بشيء ما مثل هذا.EXTEND_PROTOTYPES: { String: false }في ملف environment الخاص بنا.
-
استيرادhtmlSafeوisHTMLSafeمن @ember/string
- حتى: 4.0.0 - size-mهناك تداخل كبير بين هذا و #7 في هذا التحديث، ويجب أن يكون مباشرًا ومنخفض المخاطر.
المرحلة 3 (size-xl)
ترقية من 3.25 إلى 3.26 معقدة للغاية. هذا هو المكان الذي نقوم فيه بمعظم “التعويض”. هناك ستة عشر إلغاءًا في المجموع. سنقوم بتقسيم هذا التحديث إلى خمسة أجزاء - حيث نقوم فقط بزيادة الإصدار بعد معالجة جميع الإلغاءات.
Ember 3.25 → Ember 3.26 (الجزء 1 - عام)
يتعامل هذا الجزء مع تسعة إلغاءات أسهل نسبيًا في المعالجة.
-
طرق الانتقال في المسارات والتحكمات
- حتى: 5.0.0 - size-mيزيل هذا الإلغاء بضعة طرق من
routesوcontrollers. قد يبدو وكأنه تغيير معقد، لكنه يجب أن يكون مباشرًا. سنحتاج فقط إلى حقنRouterكخدمة واستدعائها من هناك بدلاً من ذلك - عند الحاجة. الجزء الصعب هو أننا نستخدم تلك في العديد من الأماكن، ويجب تحديثها جميعًا. -
سياسة دعم المتصفح
- حتى: 4.0.0 - size-s~~هذا يجب أن يكون تغييرًا بسيطًا نسبيًا. لن يدعم Ember IE11 من الإصدار 4.0 فصاعدًا. الشيء الجيد هنا هو أننا لم ندعمه لفترة طويلة على أي حال. الشيء الوحيد الذي نحتاج إلى تغييره هو التوقف عن الترجمة لـ IE11 في الإنتاج.
discourse/app/assets/javascripts/discourse/config/targets.js at 1472e47aae5bfdfb6fd9abfe89beb186c751f514 · discourse/discourse · GitHubقمت ببعض الاختبارات الأساسية، وسيوفر لنا هذا التغيير حوالي 60 كيلوبايت (gzip) أو ~6% من حزمنا الرئيسية والموردين في عمليات تثبيت Ember-CLI للإنتاج. -
{{hasBlock}} و {{hasBlockParams}}
- حتى: 4.0.0 - size-sنستخدم هذه في بضع أماكن. هذا إعادة تسمية بسيطة ومنخفضة المخاطر. -
مساعد{{with}}
- حتى: 4.0.0 - size-sنستخدم هذا نادرًا، لكنه لا يزال بحاجة إلى الإصلاح. نحتاج فقط إلى استبدالها واستخدام{{let}}أو مزيج من{{if}}/{{else}}
Ember 3.25 → Ember 3.26 (الجزء 2 - القوالب)
سينصب التركيز في هذا الجزء بشكل أساسي على الإلغاءات التي تتضمن قوالب .hbs. هناك ثلاثة إلغاءات نحتاج إلى التركيز عليها هنا.
-
بحث السمة الاحتياطي
- حتى: 4.0.0 - size-lبدءًا من Ember 4.0، لن يعمل هذا بعد الآن.
Hello, {{name}}!إذا كان لدينا سمة في قالب، يجب أن نبحث عنها بـ
thisالسابقة هكذاHello, {{this.name}}!سنضطر للقيام بذلك مع جميع قوالبنا. هناك طرق لتقليل الألم هنا. يمكننا تجربة ember-no-implicit-this-codemod ونرى مدى وصولنا.
أنا مؤيد لتقييد التغييرات إلى ملف واحد لكل طلب سحب. هذا يجعل المراجعة سهلة - والعودة إذا حدث خطأ ما.
-
الوصول إلى الحجج المسماة عبر {{attrs}}
- حتى: 4.0.0 size-xlسيتم إسقاط كائن
{{attrs}}في Ember 4.0. التغيير نفسه مباشر للغاية، ومثال Ember جميل جدًا.قبل:
{{attrs.foo}} {{this.attrs.foo.bar}} {{deeply (nested attrs.foobar.baz)}}بعد:
{{@foo}} {{@foo.bar}} {{deeply (nested @foobar.baz)}}سنضطر للقيام بذلك مع جميع قوالبنا. يمكننا دمج هذا التغيير مع تحويل القوالب إلى بناء الجملة الأقواس الزاوية. أنا معجب كبير بالأقواس الزاوية لأنها أقرب بكثير إلى بناء الجملة القياسي للمكونات الويب المخصصة.
شيء واحد قد يسرع تقدمنا هنا هو ember-angle-brackets-codemod. سنضطر لتجربته ونرى مدى وصولنا. يتعامل مع الإلغاء ويعطينا بناء الجملة اللامع للأقواس الزاوية.
مشابه لـ #1 في هذا الجزء، أنا أيضًا مؤيد لإصلاح واختبار قالب واحد لكل طلب سحب.
-
الحجج الموضعية لـ
<LinkTo>
- حتى: 4.0.0 - size-mهذا أيضًا إلغاء يهدف إلى تقليل الارتباك. هناك بضع أماكن نستخدم فيها الحجج الموضعية على link-to. يمكننا إصلاحها هكذا
قبل:
{{link-to "About Us" "about"}} {{#link-to "about"}}About Us{{/link-to}} {{#link-to "post" @post}}Read {{@post.title}}...{{/link-to}}بعد (مع الأقواس الزاوية):
<LinkTo @route="about">About Us</LinkTo> <LinkTo @route="about">About Us</LinkTo> <LinkTo @route="post" @model={{@post}}>Read {{@post.title}}...</LinkTo>
Ember 3.25 → Ember 3.26 (الجزء 3 - jQuery)
لا يوجد الكثير مما يمكنني قوله عن هذا لا تعرفه بالفعل. التطبيقات الجديدة لـ Ember لا تستخدم jQuery، وسيتم إسقاطها في Ember 4.0.
في هذا الجزء، سنركز على إلغاء واحد.
-
ميزة اختيارية: jquery-integration
- حتى: 4.0.0 - size-xlخلال السنوات القليلة الماضية، حققنا تقدمًا كبيرًا في تقليل استخدامنا لـ jQuery. لا تزال هناك أماكن نحتاجها فيها، خاصة في composer وكاعتماد لبعض مكتبات الموردين التي نستخدمها. لا أريد الدخول في تفاصيل هذا التغيير هنا. باختصار، يجب أن نتحرك بعيدًا عن استخدام jQuery.
ومع ذلك، أود أن أبرز أنه حتى لو تخلصنا من jQuery في كل مكان، يجب أن نحتفظ بهذا الخيار مضبوطًا على true حتى نكون مستعدين لـ Ember 4.0. نحتاج إلى خطة لتسهيل الانتقال للمواقع ذات القوالب/الإضافات المخصصة التي لا نتحكم فيها. بعبارة أخرى، دعنا نقوم بالعمل ولكن نتعايش مع تحذير الإلغاء بشأن إيقاف الخيار.
Ember 3.25 → Ember 3.26 (الجزء 4 - التحضير لـ Octane)
في هذا الجزء، يجب أن نركز على تجهيز ملفاتنا لـ Octane. سنتعامل مع إلغاءين.
-
ميزة اختيارية: template-only-glimmer-components
- حتى: 4.0.0 - size-mهذا تغيير بسيط من الناحية النظرية، لكن هناك عمل ضمني نحتاج إلى القيام به قبل أن نتمكن من تبديل ذلك الخيار.
نحتاج إلى التأكد من أن مكونات القالب فقط الحالية لدينا تعمل مع دلالات glimmer. قمت ببعض الاختبارات، وتعثر اختباراتنا مع تشغيل ذلك الخيار. إليك قائمة مثال لبعض مكونات القالب فقط التي لدينا في النواة
- app/templates/components/activation-email-form.hbs - app/templates/components/cancel-link.hbs - app/templates/components/categories-with-featured-topics.hbs - app/templates/components/category-name-fields.hbs - app/templates/components/color-input.hbs - app/templates/components/custom-html-container.hbs - app/templates/components/emoji-group-buttons.hbs - app/templates/components/emoji-group-sections.hbs - app/templates/components/empty-state.hbs - app/templates/components/ip-lookup.hbs - app/templates/components/modal-footer-close.hbs - app/templates/components/popup-menu.hbs - app/templates/components/reviewable-created-by-name.hbs - app/templates/components/reviewable-created-by.hbs - app/templates/components/reviewable-field-editor.hbs - app/templates/components/reviewable-field-text.hbs - app/templates/components/reviewable-field-textarea.hbs - app/templates/components/reviewable-field.hbs - app/templates/components/reviewable-flagged-post.hbs - app/templates/components/reviewable-post-header.hbs - app/templates/components/reviewable-post.hbs - app/templates/components/reviewable-scores.hbs - app/templates/components/reviewable-tags.hbs - app/templates/components/reviewable-topic-link.hbs - app/templates/components/score-value.hbs - app/templates/components/selected-posts.hbs - app/templates/components/subcategories-with-featured-topics.hbs - app/templates/components/text-overflow.hbs - app/templates/components/user-fields/confirm.hbs - app/templates/components/user-fields/dropdown.hbs - app/templates/components/user-fields/multiselect.hbs - app/templates/components/user-fields/text.hbs - app/templates/components/user-profile-avatar.hbs - app/templates/components/user-summary-users-list.hbsسنحتاج أيضًا إلى التحقق من قوالب Admin/theme/plugin والتأكد من عمل كل شيء قبل شحن ذلك الخيار.
من النظرة الأولى، لست متأكدًا تمامًا من كيفية تناسب قوالب
.hbrالخام لدينا مع هذا. ومع ذلك، كان @david يعمل على قوائم مواضيع مبنية على glimmer. لذا، ربما يمكننا التخلي عن القوالب الخام تمامًا. -
الحقن الضمني
- حتى: 4.0.0 size-xlنستخدم الحقن الضمني في كل مكان. نفعل هذا في مهيئ هكذا
discourse/app/assets/javascripts/discourse/app/pre-initializers/inject-discourse-objects.js at ac79c5efc61d259705eeb487ca21d0ec3c535807 · discourse/discourse · GitHubيتحرك Ember بعيدًا عن الحقن الضمني. المسار المفضل للأمام هو تحويل أكبر عدد ممكن من كائناتنا إلى خدمات وحقنها صراحةً حيث تكون مطلوبة. بالطبع، قد تكون هناك بضعة أشياء لا تكون فيها الخدمة مثالية. في تلك الحالات، يمكننا البحث عن تلك الكائنات مباشرةً عند الحاجة هكذا
getOwner(this).lookup('thing:main')خيار آخر لدينا (اعتمادًا على الآثار الأدائية) هو تغليف فئات Ember بفئة Discourse الخاصة بنا. سنستخدم بعد ذلك فئتنا في جميع أنحاء التطبيق. نوعًا ما كما نفعل مع
GlimmerComponent Class
discourse/app/assets/javascripts/discourse/app/components/glimmer.js at fa0c796baf9a7f64a3b27823b1aa4b370a74c3eb · discourse/discourse · GitHub. هذا سيجعل هذه المهمة size-l أو حتى size-mعلى أي حال، سيتطلب هذا التغيير بعض التفكير.
Ember 3.25 → Ember 3.26 (الجزء 5 - Octane)
هذا هو الجزء الأخير من ترقية 3.25 → Ember 3.26. سيكون لدينا فقط إلغاء واحد متبقٍ في هذه المرحلة، لكنه كبير.
-
الإصدار: Classic
- حتى: 4.0.0 - size-xlقبل أن نقلب إصدارنا إلى Octane، أود أن أقضي وقتًا في تحويل فئاتنا إلى فئات أصلية ومكوناتنا إلى مكونات Glimmer. سيكون هناك بعض الألم، لكنه يستحق ذلك. يجب أن يخفف ember-native-class-codemod بعض ذلك الألم. سنضطر لرؤية مدى وصولنا.
هناك الكثير من الاعتبارات وقضايا سير العمل التي يجب وضعها في الاعتبار. الشيء الوحيد الذي أريد تسليط الضوء عليه هو أنه سيتبع نفس التدفق الذي ذكرته للقوالب - إصلاح واختبار مكون واحد لكل طلب سحب.
المرحلة 4
تقدم ترقية Ember 3.26 → Ember 3.27 اثني عشر إلغاءًا. أقترح أن نقسمها إلى ثلاثة أجزاء. سنقوم بزيادة الإصدار بعد معالجة جميع الإلغاءات.
Ember 3.26 → Ember 3.27 (الجزء 1 - عام)
-
إلغاء
Route#disconnectOutlet
- حتى: 4.0.0 - size-sنفعل هذا فقط في مكان واحد. إنه
build-category-route، ويجب أن يكون إصلاحه مباشرًا. -
استدعاء المساعدات بدون حجج وأقواس في مواقع الحجج المسماة
- حتى: 4.0.0 - size-sلا أعتقد أننا نفعل هذا في أي مكان، لكنني سأؤكد عندما يحين الوقت. في الأساس، استدعاء مساعد دون تمرير أي حجج إليه.
حتى لو استخدمنا شيئًا كهذا، سنحتاج فقط إلى إضافة أقواس. لذا، هذا
<SomeComponent @arg={{someHelper}} />يصبح
<SomeComponent @arg={{(someHelper)}} />لاحظ الأقواس حول
someHelper -
حلقة التشغيل والوصول بالنقطة للحسابات
- حتى: 4.0.0 - size-mنستخدم
.للوصول إلى دوالcomputedفي إضافةdecoratorsلدينا. تبدو هكذا، على سبيل المثال
discourse/app/assets/javascripts/discourse-common/addon/utils/decorators.js at b05fddaa7ce3968ffc70cd8d4bf290e15d06eb11 · discourse/discourse · GitHubلدينا أيضًا واحد هنا وهناك. على حد علمي، إصلاح تلك يتعلق في الغالب بإصلاح كيفية استيرادها.
لذا، يجب استيراد
computed.filterهكذا بدلاً من ذلك.import { filter } from '@ember/object/computed';يستخدم إصدارنا من إضافة
buffered-proxyالخارجية.للوصول إلى دوالcomputed؛ سنحتاج إلى زيادته.نستخدم أيضًا
.للوصول إلى دوالrunفي بضع أماكن. على ذلك، ينطبق نفس الإصلاح. نحتاج إلى تحديث الطريقة التي نستورد بها. أعتقد أن القوالب والإضافات خاصة قد يكون لديها العديد من الأماكن التي نفعل ذلك فيها معrun -
إلغاء المتغير العالمي Ember
- حتى: 4.0.0 - (#size ?)لن يكون
Emberمتاحًا في السياق العالمي بعد 4.0. من الصعب تقدير مقدار العمل/التأثير هنا دون نظرة أعمق. على ذلك، أعرف أن @cvx يقوم بالكثير من العمل للتخلص من هذا النمط.
Ember 3.26 → Ember 3.27 (الجزء 2 - renderTemplate)
سينصب التركيز في هذا الجزء على إلغاء واحد فقط.
-
إلغاء
Route#renderTemplate
- حتى: 4.0.0 - size-lباختصار، لا يمكننا استخدام المنافذ المسماة في Ember 4.0. لذا، هذا لن يعمل.
{{outlet "thing"}}نستخدم
renderTemplateفي حوالي 30 مكانًا في النواة وحدها. يبدو الترقية نفسه مباشرًا إلى حد ما. يمكننا استخدام{{#in-element}}وعنصر HTML فارغ عادي كعنصر نائب لما كنا نعرضه في المنافذ المسماة.
Ember 3.26 → Ember 3.27 (الجزء 3 - المكونات المدمجة القديمة)
سينصب التركيز في هذا الجزء على المكونات المدمجة القديمة، وسيتعامل مع أربعة إلغاءات.
- استيراد المكونات المدمجة القديمة
- حتى: 4.0.0 - حجج المكونات المدمجة القديمة
- حتى: 4.0.0 - حجج سمات HTML القديمة للمكونات المدمجة
- حتى: 4.0.0 - إعادة فتح المكونات المدمجة القديمة
- حتى: 4.0.0
لم أضيف أحجامًا لهذه لأن… الأمر يعتمد حقًا. دعني أشرح.
سيتم إزالة المكونات المدمجة القديمة مثل Checkbox و TextField و TextArea و LinkComponent في Ember 4.0. نستخدمها في العديد من الأماكن، ونستخدم أيضًا بعض الأنماط الملغاة عليها.
يوفر Ember مسار ترقية يسمح لنا باستمرار استخدامها، لكننا يجب أن نستورد بشكل مختلف. ومع ذلك، لن تتلقى أي تحديثات من Ember وستبقى مجمدة. آمل أن نتمكن من التخلي عن جميعها؛ ومع ذلك، قد يكون ذلك معقدًا بعض الشيء. سيتطلب هذا التغيير المزيد من النقاش عندما يحين الوقت.
المرحلة 5
Ember 3.27 → Ember 3.28
هذا size-s لأنه مجرد زيادة في الإصدار. 3.28 هو آخر إصدار LTS في دورة التطوير 3.x. لا يقدم أي إلغاءات جديدة بعد 3.27، وهو إصدار جيد لنا للتوقف عليه لبضع أسابيع بينما تستقر الأمور.
يدعم 3.28 LTS حتى أغسطس 2022 (إصلاحات الأخطاء وتراحيات الأمان)
لهذه “الاستراحة” فوائد قليلة.
- يمنحنا المزيد من الوقت لرؤية ما إذا كانت هناك أي مشكلات تنشأ
- عندما نشحن إصدارًا مستقرًا، يجب أن يكون على 3.28
- يمنحنا الوقت لإجراء أي إعلانات نحتاج إلى إجرائها فيما يتعلق بالقوالب والإضافات التي نديرها ذاتيًا
- يمنحنا الوقت للتأكد من أن الانتقال من jQuery إلى عدم وجود jQuery سلس قدر الإمكان.
المرحلة 6
بعد مرور بضع أسابيع، يمكننا أخيرًا زيادة إصدارنا إلى Ember 4
Ember 3.28 → Ember 4.1
يمكننا الآن تبديل التكامل الاختياري لـ jQuery كآخر إلغاء من دورة 3.x.
يُقدم هذا التحديث إلغاءين ثانويين.
-
إلغاء Ember.assign
- حتى: 5.0.0 - size-sلا نستخدم ذلك في النواة، لكننا سنحتاج إلى التحقق من القوالب/الإضافات. على أي حال، إنه إعادة تسمية بسيطة. -
فئة AutoLocation
- حتى: 5.0.0 - size-sمن الناحية النظرية، سنحتاج فقط إلى تغيير
locationType: 'auto'إلىlocationType: 'history'في ملف بيئة Ember لدينا، ويجب أن يعمل.
سير العمل
كما ذكرت في البداية، سنكون حذرين للغاية مع هذه التحديثات. سنختبر/نصلح/نثبت جميع الإضافات/القوالب الرسمية مع كل تحديث، وسنرسل أيضًا طلبات سحب إلى الإضافات/القوالب غير الرسمية الشائعة.
الهدف هنا ليس إبطاء التطوير أو إنشاء صداع. لذا، يجب أن تكون طلبات السحب محدودة النطاق بدقة، مع تغيير واحد لكل طلب سحب ولا شيء كبير جدًا.
في عالم مثالي، ستحدث جميع التغييرات في الخلفية دون مقاطعة عمل أي شخص آخر. هذا هو السبب في أننا نخطط لإبقاء طلبات السحب قصيرة وحلوة. أيضًا، نحن لا نحب الأنماط المختلطة. لذا، لا نريد أن نعلق في حالة مؤقتة على أساس كل ملف. المكون إما كلاسيكي أو Glimmer، والقالب إما يستخدم الأقواس المعقوفة أو الأقواس الزاوية، لا شيء بينهما.
آمل أن كانت هذه الخارطة واضحة. كما ذكرت في البداية، هذا مجرد نظرة عامة عامة على المستوى الأعلى. إذا كان هناك أي شيء غير واضح أو غير صحيح أو لا يجلس بشكل صحيح معك، يرجى إعلامنا.