العربية والفرنسية والدرهم: التدويل و RTL في واجهات المؤسسات
و في واجهات المؤسسات بالمغرب والخليج: الترجمة وقت البناء أو وقت التشغيل، و dir و المنطقي، والنص ، وتنسيق الدرهم.

الرقم نفسه، 12450.5 درهماً، يخرج بثلاث صيغ من واجهة Intl نفسها، بحسب سلسلة نصية واحدة تحدد الإعداد المحلي (locale). ثم تصل النسخة العربية من الشاشة، فتشير كل الأسهم إلى الاتجاه الخطأ، وقد يظهر داخل جملة عربية بمجموعات أرقام في ترتيب لم يكتبه أحد. في المغرب والخليج، هذه ليست حالة هامشية. إنها الجمهور الأساسي.
اللغات في الواجهة الأمامية للمؤسسة قرار في تصميم الأنظمة، لأنها تمسّ كل شيء دفعة واحدة: البناء، والتخطيط، والمكوّنات، والبيانات، ونظام التصميم. وإن تأخّر القرار، مسّ RTL كل شاشة.
المفاهيم القليلة التي تهمّ
الترجمة وقت البناء أو وقت التشغيل. يبني نظام i18n المدمج في «نسخة قابلة للتوزيع منفصلة من التطبيق لكل إعداد محلي»، مع دمج الترجمات وقت البناء. ويكتب كل إعداد محلي في مجلده الخاص، ويُقدَّم عادةً من مسار فرعي خاص به. أما مكتبة وقت التشغيل فتحمّل الترجمات أثناء عمل التطبيق. وليس في نظام i18n مدمج، لذلك يصبح الخيار في React هو اختيار المكتبة.
dir على العنصر الجذر، و CSS المنطقي في كل مكان. جواب قصير: «اضبط dir صراحةً على العنصر الجذر»، وهذا «مهم بشكل خاص للكتابات من اليمين إلى اليسار»، واستخدم السمة «بدلاً من خصائص CSS المقابلة متى أمكن». والخصائص المنطقية «مكافئات مرتبطة بالاتجاه» للخصائص الفيزيائية، فتتبع margin-inline-start اللغة، بينما لا تتبعها margin-left. والأيقونات تحتاج إلى قرار أيضاً: سهم الرجوع ينعكس، أما زر التشغيل أو الشعار فلا. وفي tt-tech.io تحمل الأسهم الصنف rtl:-scale-x-100 لهذا الغرض بالضبط.
الصورة: tt-tech.io، مقتبسة من web.dev، Internationalization، بترخيص .
تستخدم الإطارات الثلاثة CSS نفسها: border-inline-start و border-inline-end و border-block-start و border-block-end. اللغة وحدها تتغير، والمتصفح ينقل الحدود.
النص ثنائي الاتجاه في البيانات. اتجاه الصفحة هو الجزء السهل. أما الصعب فهو قيمة داخل جملة: RIB، أو رقم هاتف، أو اسم منتج إنجليزي داخل نص عربي. ففي خوارزمية Unicode ثنائية الاتجاه، تتصرف الأرقام «كأنها R»، أي كأحرف تُكتب من اليمين إلى اليسار، بالنسبة إلى الأحرف المحايدة المحيطة بها، فتأخذ المسافة بين مجموعتين من الأرقام الاتجاه من اليمين إلى اليسار. وداخل نص عربي، قد تظهر مجموعاتُ رقم RIB المكتوب بمسافات بترتيب معكوس. ويقول MDN إن dir="auto" «ينبغي استخدامه للبيانات مجهولة الاتجاه، مثل البيانات الآتية من إدخال المستخدم أو البيانات الخارجية». ويصف <bdi> بأنه يطلب من الخوارزمية ثنائية الاتجاه معاملة نصه «بمعزل عن النص المحيط به».
الأرقام والعملات والتواريخ. يقول MDN إن Intl.NumberFormat «يتيح تنسيق الأرقام بحسب اللغة». والتنسيق بحسب اللغة يعني أيضاً التنسيق بحسب البيانات. شغّلت هذا في 24 ( 78):
const mad = (locale: string) =>
new Intl.NumberFormat(locale, { style: 'currency', currency: 'MAD' }).format(12450.5);
mad('fr-FR'); // '12 450,50 MAD' (مسافة ضيقة غير قابلة للكسر)
mad('fr-MA'); // '12.450,50 MAD' (التجميع بالنقطة)
mad('ar-MA'); // '12.450,50 د.م.' (ومعها علامتا اتجاه غير مرئيتين من اليمين إلى اليسار)ثلاث نتائج، وهي تعتمد على بيانات الإعدادات المحلية التي يأتي بها إصدار المتصفح أو Node لديك. لا بأس بذلك للعرض، لكنه لا يصلح لاختبار يتحقق من سلسلة نصية بعينها، ولا لبنك قرّر شكل كشوف حساباته.
اختر صيغة المبالغ عن قصد، وإلا اختارتها بيانات الإعداد المحلي نيابة عنك.
نظام التصميم في الاتجاهين. يجب أن تعمل والمكوّنات والاختبارات المرئية في RTL أيضاً، وإلا اكتشف كل فريق ميزة الأخطاء نفسها وحده.
الخطوط وطول النص. تحتاج العربية إلى خط يعرضها جيداً، لا إلى الخط البديل للخط اللاتيني. والنص المترجم يطول، ويشير دليل من Mozilla إلى أن «لغات كثيرة تستخدم نصوصاً أطول من الإنجليزية».
ماذا تختار، ومتى
- وقت البناء أو وقت التشغيل: وقت البناء حين يكون الإعداد المحلي ثابتاً لكل مستخدم وتُشحن النصوص مع الكود؛ ووقت التشغيل حين يغيّر المستخدمون اللغة كثيراً، أو تأتي النصوص من .
Intlأو أداة تنسيق ثابتة:Intlلإعدادات محلية كثيرة وللعرض فقط؛ وأداة تنسيق ثابتة ومختبَرة حين تكون الصيغة قراراً تجارياً.<bdi>أوdir="ltr":<bdi>(أوdir="auto") للنصوص التي لا تتحكم فيها، كالأسماء؛ وdir="ltr"للقيم التي تُكتب دائماً من اليسار إلى اليمين، مثل RIB أو .
في Atlas Bank
واجهة Atlas Bank بالإنجليزية فقط اليوم (<html lang="en"> في apps/atlas/src/index.html). وتنسيق المبالغ فيه خيار مقصود. فالدالة formatMad في libs/domain/src/lib/format.ts تقول عن نفسها إنها «ليست Intl عن قصد»: صيغة واحدة ثابتة، بالتجميع بمسافة وفاصلة للكسور العشرية، «تُعرض بالشكل نفسه في كل متصفح وفي Node، كي تتحقق الاختبارات من السلسلة النصية بعينها». وبيانات ICU نفسها لـ fr-MA تجمّع بالنقطة، لذلك هذا خيار يتخذه الكود عن قصد. ويتحقق format.spec.ts من '12 450,50 MAD'.
ويُظهر Atlas أيضاً كلفة RTL. فأنماطه ما زالت تستخدم الخصائص الفيزيائية في بعض المواضع: margin-left: auto في account-detail.ts، و text-align: right للصنف .num. كل منها إصلاح صغير، لكنه اكتُشف متأخراً.
وزر للتبديل بين الفرنسية والعربية مع dir="rtl" سيكون إضافة جيدة إلى Atlas في هذا الجزء. إنه اقتراح، وليس موجوداً في المستودع اليوم.
ما تركته جانباً، ولماذا
- الترجمة الوهمية ()، وأنظمة إدارة الترجمة ()، وقواعد الجمع. أدوات حقيقية وقواعد حقيقية؛ سطر واحد لكل منها. وفي الجمع، استخدم رسائل ICU، ولا تركّب الجمل بوصل السلاسل النصية أبداً.
- اكتشاف الإعداد المحلي. قرار يخصّ المنتج؛ دع المستخدم يختار، وتذكّر اختياره.
مع بداية الأسبوع
- اضبط
dir="rtl"على العنصرhtmlفي تطبيقك داخل فرع، وتنقّل عبر التدفق الرئيسي. دوّن كل شاشة تنكسر. - ابحث في أنماطك عن
margin-leftوpadding-rightوtext-align: left، وعُدّها. ذلك الرقم هو حجم عمل RTL لديك. - اعثر على كل موضع ينسّق المبالغ، وتحقّق من أنه يمر عبر دالة واحدة.
لوكيل البرمجة الخاص بك
قواعد هذا المقال، مكتوبة لوكيل. الصقها في ملف AGENTS.md لديك، أو احفظها مهارةً ()، أو وجّه وكيلك إلى الملف.
# Internationalisation and RTL rules for enterprise apps Source: https://tt-tech.io/blog/arabic-french-mad-i18n-rtl-enterprise For Angular and React frontends. Add to AGENTS.md or save as a skill. Your project's own rules win where they differ. ## Rules 1. Choose build-time translation (Angular i18n, one build per locale) when the locale is fixed per user and the texts ship with the code. Choose a runtime library when users switch language often or texts come from a CMS. 2. Set `dir` explicitly on the root `html` element. Use the `dir` attribute instead of the CSS `direction` property where possible. 3. Use logical CSS properties: `margin-inline-start`, `padding-inline-end`, `border-inline-start`, `text-align: start`. Never add `margin-left`, `padding-right` or `text-align: left` in new code. 4. Mirror icons that show direction, such as back and forward arrows, in RTL. Do not mirror a play button or a logo. 5. Wrap text whose direction you do not control (names, user input, external data) in `<bdi>` or give it `dir="auto"`. 6. Mark values that are always left to right, such as a RIB or an IBAN, with `dir="ltr"`. 7. Format money through one function. Use `Intl.NumberFormat` for display across many locales; use a fixed, tested formatter when the format is a business decision. 8. Never assert an exact `Intl` output string in a test. It depends on the locale data the browser or Node version ships. 9. Build plurals and variable messages with ICU messages. Never concatenate translated strings. 10. Run design tokens, components and visual tests in RTL as well as LTR. 11. Use a font that renders Arabic well, not the Latin font's fallback. Leave room for translated text, which is often longer than English. 12. Let the user choose the language and remember the choice. ## Before you say done - Search the diff for `margin-left`, `margin-right`, `padding-left`, `padding-right`, `text-align: left` and `text-align: right`. Replace each with a logical property or explain it. - Search the diff for money formatting (`Intl.NumberFormat`, `toFixed`, currency codes) outside the shared formatter. - Search the diff for string concatenation in translated text, and for names, user input or external values rendered without `<bdi>`, `dir="auto"` or `dir="ltr"`.
المصادر
- Angular, Merge translations into the app
- Angular, Deploy multiple locales
- MDN, dir HTML global attribute
- MDN, CSS logical properties and values
- Unicode, UAX #9: Unicode Bidirectional Algorithm
- MDN, <bdi> HTML bidirectional isolate element
- MDN, Intl.NumberFormat
- Mozilla, Fluent for Firefox Developers
- web.dev, Internationalization