اللغات
كيف يختار Learnio اللغة، وأين توجد كل ترجمة، وكيف تغيّر نصًا أو تضيف لغة جديدة إلى كل تطبيق.
لحزمة الحزمة الكاملة
اللغات المتوفرة
يأتي كل تطبيق بـالإنجليزية، وهي اللغة الافتراضية، والعربية، التي تُعرض من اليمين إلى اليسار. يحتفظ كل تطبيق بترجماته الخاصة:
| التطبيق | ملفات الترجمة | الملفات لكل لغة |
|---|---|---|
| موقع الطلاب | frontend/messages/<namespace>/<locale>.json | 22 |
| لوحة الإدارة | admin-dashboard/messages/<namespace>/<locale>.json | 24 |
| API | back-end/src/i18n/translations/<locale>/<namespace>.json | 18 |
مساحة الأسماء (namespace) هي جزء واحد من التطبيق، مثل layouts أو auth أو courses. بعضها مجلدات متداخلة، مثل messages/dashboard/courses/en.json.
كيف تُختار اللغة
اللغة هي الجزء الأول من العنوان: /en/courses أو /ar/courses. لا يوجد ملف تعريف ارتباط (cookie) للغة ولا اكتشاف تلقائي من المتصفح.
- يُعاد توجيه العنوان الذي لا يتضمن لغة إلى اللغة الافتراضية، الإنجليزية:
/coursesيذهب إلى/en/courses. وفي لوحة الإدارة،/يذهب إلى/en/dashboard. - يفتح مبدّل اللغة الصفحة نفسها تحت البادئة الأخرى.
- يحمل كل طلب إلى واجهة API لغة الصفحة في الترويسة
Accept-Language، فتجيب واجهة API بها.
لجعل الفتح بالعربية افتراضيًا، اضبط defaultLocale على "ar" في src/config/locales.ts في موقع الطلاب، وفي src/proxy.ts في لوحة الإدارة.
غيّر نصًا
كل كلمة على الشاشة تأتي من ملف رسائل. ابحث عن النص الذي تراه لتجد مفتاحه، ثم غيّره في كل لغة.
frontendgrep -rn "All rights reserved" messagesيعثر هذا على سطر حقوق النشر في التذييل، وهو المفتاح footer.copyright في messages/layouts/en.json. غيّر المفتاح نفسه في ar.json:
"footer": {
"copyright": "© {year} Learnio. All rights reserved."
}- أبقِ العناصر النائبة مثل
{year}والوسوم مثل<brand>كما هي تمامًا. تملؤها الشيفرة. - غيّر المفتاح في كل ملف لغة. المفتاح المفقود من إحدى اللغات يُظهر مساره بدلًا من النص.
- يلتقط
yarn devالتغيير. أما بناء الإنتاج فيتضمن الرسائل، لذا أعد البناء.
الإشعارات كذلك: تحفظ واجهة API نوع الإشعار فقط، مثل enrollment_created، ويصوغه كل تطبيق من ملفاته الخاصة. في لوحة الإدارة توجد تحت header.notifications.types في messages/dashboard/<locale>.json؛ وفي موقع الطلاب تحت notifications.types في الملف نفسه.
الدورات والمقالات وسائر المحتوى
النصوص التي تكتبها في لوحة الإدارة، مثل عناوين الدورات والدروس والفئات ومقالات المدونة وملفات المدرّسين، تُحفظ مرة واحدة باللغتين معًا، كقيمة JSON فيها مفتاح en ومفتاح ar:
{ "en": "Project Management", "ar": "إدارة المشاريع" }- تعرض نماذج لوحة الإدارة تبويبًا للإنجليزية وآخر للعربية لكل حقل من هذه الحقول.
- تُرجع واجهة API القيمتين، وتعرض كل واجهة أمامية القيمة الخاصة بلغة الصفحة. وإذا كانت فارغة، تعرض الأخرى، فلا تبقى أي دورة بلا اسم.
- تأتي نتائج البحث محسومة اللغة من واجهة API، بناءً على الترويسة
Accept-Language. - في البيانات التجريبية التي ينشئها
yarn seed، تحمل الفئات ومقالات المدونة وملفات المدرّسين والاختبارات وعناوين أقسام الدورات نصوصًا عربية. أما عناوين الدورات وأوصافها وعناوين الدروس فتحمل النص الإنجليزي تحت المفتاحين، لذا ترجمها في لوحة الإدارة.
للمحتوى لغتان فقط
يتوقع التحقق في واجهة API قيمة en وقيمة ar في هذه الحقول ويرفض أي مفتاح آخر، ولمحررات لوحة الإدارة تبويبان. إضافة لغة ثالثة للمحتوى تعني تغيير الكيانات وكائنات DTO في واجهة API، وحقول الإدخال متعددة اللغات في لوحة الإدارة والدالة المساعدة localizedText فيها. أما localizedText في موقع الطلاب فتقرأ بالفعل المفتاح المطابق للغة الصفحة. ومن دون هذه التغييرات، تعرض الصفحة بلغة واجهة جديدة المحتوى الإنجليزي.
ما تترجمه واجهة API
- رسائل الاستجابة، مثل نصوص النجاح والخطأ التي تعرضها لوحة الإدارة في تنبيهاتها، تأتي من
src/i18n/translations/<locale>/<namespace>.jsonعبرI18nServiceفيsrc/i18n/i18n.service.ts. - رسائل البريد الإلكتروني مصاغة في
mail.json. تستخدم رسائل التحقق وإعادة تعيين كلمة المرور لغة الطلب. وتستخدم الإيصالات وتأكيدات التسجيل اللغة التي قُدّم بها الطلب. وتُعرض الرسائل العربية من اليمين إلى اليسار. - أسماء الدول موجودة في
countries.json.
تُؤخذ اللغة من ترويسة Accept-Language. والرمز الإقليمي مثل ar-SA يُحسب ar. الطلب الذي لا يحمل ترويسة، أو يحمل لغة لا تدعمها واجهة API، يحصل على اللغة الافتراضية. والمفتاح الناقص في لغة ما يعود إلى نصه في اللغة الافتراضية، ثم إلى المفتاح نفسه.
- اللغة الافتراضية هي العنصر Default Language (
default_locale) في تبويب App Settings ضمن Settings في لوحة الإدارة، وقيمتهاenبعد البذر. يُطبَّق أي تغيير على الطلب التالي دون إعادة تشغيل. وترفض واجهة API أي قيمة ليست من لغاتها، فلا يمكن لخطأ مطبعي أن يجعلها تجيب بلغة لا ترجمات لها. - تقرأ واجهة API ملفات ترجمتها مرة واحدة عند بدء تشغيلها: أعد تشغيلها بعد تعديل أي منها. وينسخها
yarn buildإلىdist/.
أضف لغة إلى موقع الطلاب
تستخدم الخطوات الفرنسية، fr، مثالًا. استخدم رمزًا من حرفين صغيرين: فعدة دوال مساعدة تتعرّف على اللغة بوصفها مقطعًا أول من حرفين في العنوان. المسارات داخل frontend/.
أضف الرمز إلى قائمة اللغات
src/config/locales.tsهو القائمة الوحيدة التي يقرؤها موقع الطلاب: المسارات، وإعادة التوجيه، ومحمّل الرسائل، والسكربت الذي يضبطlangوdirقبل أول رسم، ومبدّل اللغة، وطلبات واجهة API، وإعادة التوجيه بعد تسجيل الدخول، والبيانات الوصفية للصفحات، وخريطة الموقع، وكل رقم وتاريخ ينسّقه. أضف الرمز إلىlocales، ثم أعطه مدخلًا في الخرائط الثلاث المجاورة. يشير المترجم إلى أي منها فاتك.frontend/src/config/locales.tsexport const locales = ["en", "ar", "fr"] as const; export const OG_LOCALES: Record<Locale, string> = { en: "en_US", ar: "ar_AE", fr: "fr_FR" }; export const LOCALE_FLAGS: Record<Locale, FlagIconCode> = { en: "US", ar: "AE", fr: "FR" }; export const LOCALE_FORMAT_TAGS: Record<Locale, string> = { en: "en-US", ar: "ar-EG", fr: "fr-FR" };المدخل ما يضبطه OG_LOCALESلغة بطاقة المشاركة للصفحة ( og:locale)LOCALE_FLAGSالعلم الذي يعرضه مبدّل اللغة، كرمز دولة LOCALE_FORMAT_TAGSطريقة كتابة الأرقام والتواريخ. استخدم وسمًا يتضمن المنطقة، مثل fr-FR، كي لا تعتمد الأرقام وأسماء الأشهر على المتصفح.RTL_LOCALESأضف الرمز للغة تُكتب من اليمين إلى اليسار. JOINING_SCRIPT_LOCALESأضف الرمز لكتابة تتصل حروفها، كما في العربية. عندها يُرسم شعار الصفحة الرئيسية النصي كمسارات، لأن Safari يترك هذه الحروف منفصلة في نص SVG. أنشئ ملفات الرسائل
انسخ كل ملف إنجليزي بجوار نفسه تحت الرمز الجديد، ثم ترجم القيم. أبقِ المفاتيح كما هي.
الطرفيةفيfrontendfind messages -name en.json -exec sh -c 'cp "$1" "$(dirname "$1")/fr.json"' _ {} \;في PowerShell:
الطرفيةفيfrontendGet-ChildItem messages -Recurse -Filter en.json | ForEach-Object { Copy-Item $_.FullName (Join-Path $_.DirectoryName 'fr.json') }تحقق من الخطوط
يُحمَّل خط Inter بالمجموعة الفرعية
latinفيsrc/app/layout.tsx، وهي تغطي الفرنسية والإسبانية والألمانية. أضفlatin-extللغات مثل البولندية والتشيكية والتركية. ولنظام كتابة آخر، حمّل خطًا له هناك وأضف قاعدة للخاصيةlangالخاصة به فيsrc/styles/base.css، كما في القاعدة العربية.افتح اللغة الجديدة
أعد تشغيل
yarn devوافتح localhost:3030/frمحلي.النتيجة المتوقعة: تعرض الصفحة نصك المترجم، ويسرد المبدّل اللغة الجديدة، وتذكرها كل صفحة في روابط
hreflangالبديلة وفي خريطة الموقع.
تحتاج صفحة الدفع إلى أن تعرف واجهة API اللغة
يرسل الدفع لغة الصفحة مع الطلب، ولا تقبل واجهة API هناك إلا اللغات الموجودة في قائمتها. وإلى أن يُضاف الرمز إلى واجهة API أيضًا (إضافة لغة إلى واجهة API)، تُرفض عملية الشراء من صفحة باللغة الجديدة.
أضف لغة إلى لوحة الإدارة
تحتفظ لوحة الإدارة بقائمة لغاتها في ثلاثة ملفات. تستخدم الخطوات fr؛ والتزم برمز من حرفين صغيرين. المسارات داخل admin-dashboard/.
أضف الرمز إلى القوائم الثلاث
localesفيsrc/config/i18n.ts، الذي يحمّل الرسائل.localesفيsrc/proxy.ts، الذي يعيد توجيه العناوين التي لا تتضمن لغة.localesفيsrc/app/[locale]/layout.tsx، الذي يحدد العناوين الموجودة.
أنشئ ملفات الرسائل
انسخ كل ملف إنجليزي تحت الرمز الجديد، ثم ترجم القيم.
الطرفيةفيadmin-dashboardfind messages -name en.json -exec sh -c 'cp "$1" "$(dirname "$1")/fr.json"' _ {} \;حدّث الدوال المساعدة
- يقبل
src/hooks/locale/useLocale.tsاللغاتenوfrوesوar. أضف رمزك إن لم يكن منها. - يعامل
src/hooks/locale/useDirection.tsاللغةarوحدها على أنها من اليمين إلى اليسار. وسّع الشرط للغة أخرى تُكتب من اليمين إلى اليسار. - لا يتعرّف
src/lib/api/client-utils/locale.tsوsrc/lib/api/client-utils/auth.tsفي العنوان إلا علىenوar. أضف رمز لغتك.
- يقبل
أضفها إلى مبدّل اللغة
في
src/components/LanguageSwitcher.tsx، استورد العلم منcountry-flag-icons/react/3x2وأضف عنصرًا إلىlanguages.admin-dashboard/src/components/LanguageSwitcher.tsximport { US, SA, FR } from "country-flag-icons/react/3x2"; const languages = [ { code: "en", country: "US" as const, Flag: US }, { code: "ar", country: "SA" as const, Flag: SA }, { code: "fr", country: "FR" as const, Flag: FR }, ];تحقق من الخطوط
تُحمَّل الخطوط في
src/app/layout.tsx، ويبدّلsrc/styles/base.cssإلى الخط العربي معhtml[lang="ar"]. تحتاج اللغة المكتوبة بنظام كتابة آخر إلى خطها الخاص وقاعدة مطابقة.
لأسماء العلامة التجارية في src/config/brand.config.ts قيمتان فقط، en وar، وتُعرض القيمة الإنجليزية لأي لغة أخرى.
أضف لغة إلى واجهة API
المسارات داخل back-end/.
انسخ مجلد الترجمة
ثم ترجم الملفات الثمانية عشر الموجودة فيه.
الطرفيةفيback-endcp -R src/i18n/translations/en src/i18n/translations/frفي PowerShell:
الطرفيةفيback-endCopy-Item -Recurse src/i18n/translations/en src/i18n/translations/frأضف الرمز إلى قائمة اللغات المدعومة
SUPPORTED_LOCALESفيsrc/i18n/locales.tsهي القائمة الوحيدة في واجهة API. تقرؤها الترجمات، والتحقق في صفحة الدفع، واللغة المثبّتة على كل طلب، وصفحات الدفع، والرسائل وروابطها إلى الواجهتين. وإلى أن يُضاف الرمز هناك، تحصل الطلبات باللغة الجديدة على اللغة الافتراضية.back-end/src/i18n/locales.tsexport const SUPPORTED_LOCALES = ['en', 'ar', 'fr'] as const;تحقّق من الموضعين اللذين يختبران العربية
يجعل
src/modules/mail/templates/layout.tsاتجاه الرسالة من اليمين إلى اليسار لـarفقط؛ وسّعه ليشمل لغة أخرى تُكتب من اليمين إلى اليسار. أما البحثان،src/modules/search/search.service.tsوsrc/modules/public/student-search.service.ts، فيقرآن العنوان المخزّن بالعربية أو الإنجليزية فقط، لأن للمحتوى لغتين. اعرضهما:الطرفيةفيback-endgrep -rn "=== 'ar'" srcأعد تشغيل واجهة API
تقرأ ملفات الترجمة عند بدء التشغيل. وفي بيئة الإنتاج، ينسخها
yarn buildإلىdist/.