التدويل (i18n) في JavaScript (Node.js)
التدويل (اختصاراً i18n - من الكلمة الإنجليزية «internationalization»، حيث 18 تعني عدد الحروف بين الحرف الأول «i» والحرف الأخير «n») هو عملية تصميم وتطوير البرمجيات بحيث يمكن تكييفها بسهولة مع لغات ومناطق مختلفة دون إجراء تغييرات في الكود. في نظام Node.js البيئي، توجد عدة مكتبات قوية تبسط هذه المهمة بشكل كبير. في هذه المقالة، سننظر في اثنتين من أشهرها: i18next و node-polyglot.
لماذا نحتاج إلى التدويل؟
غالباً ما تتعامل تطبيقات الويب الحديثة وخدمات الخادم مع مستخدمين من دول مختلفة. دعم عدة لغات يحسن تجربة المستخدم (UX) ويوسع نطاق الجمهور. بدون i18n، سيضطر المطورون إلى تضمين النصوص بلغة واحدة بشكل ثابت، مما يجعل دعم تعدد اللغات شاقاً للغاية وعرضة للأخطاء.
المهام الرئيسية التي تحلها مكتبات i18n في Node.js:
- دعم ملفات الترجمة (عادةً JSON أو YAML).
- الجمع (التصريف الصحيح للكلمات حسب العدد: «1 قهوة»، «2 قهوة»، «5 قهوات»).
- إدراج المتغيرات في النصوص (مثل: «مرحباً، {{name}}!»).
- دعم المفاتيح المتداخلة لتنظيم هيكل الترجمات.
- الكشف التلقائي عن لغة المستخدم (عبر رؤوس HTTP، ملفات تعريف الارتباط، النطاقات الفرعية).
مكتبة i18next
i18next هي بلا شك المكتبة الأكثر شهرة ووظيفية للتدويل في عالم JavaScript. تعمل في المتصفح وعلى خادم Node.js. تقدم i18next نظاماً بيئياً غنياً من الإضافات، بما في ذلك التكامل مع React و Vue و Express وأطر العمل الأخرى.
التثبيت والإعداد الأساسي
// التثبيت عبر npmnpm install i18nextلنقم بإنشاء ملفات الترجمة. عادةً ما يتم تخزينها في مجلد locales:
locales/ar/translation.json
{ "greeting": "مرحباً، {{name}}!", "coffee": "لقد شربت {{count}} فنجان قهوة", "coffee_plural": "لقد شربت {{count}} فناجين قهوة", "menu": { "home": "الرئيسية", "about": "معلومات عنا" }}locales/en/translation.json
{ "greeting": "Hello, {{name}}!", "coffee": "You drank {{count}} cup of coffee", "coffee_plural": "You drank {{count}} cups of coffee", "menu": { "home": "Home", "about": "About us" }}الآن نقوم بتهيئة i18next في التطبيق:
const i18next = require('i18next');const fs = require('fs');const path = require('path');
// تحميل الترجمات من الملفاتfunction loadTranslation(lng) { const filePath = path.join(__dirname, 'locales', lng, 'translation.json'); return JSON.parse(fs.readFileSync(filePath, 'utf8'));}
i18next.init({ lng: 'ar', // اللغة الافتراضية fallbackLng: 'en', // اللغة الاحتياطية resources: { ar: { translation: loadTranslation('ar') }, en: { translation: loadTranslation('en') } }, interpolation: { escapeValue: false // لـ Node.js لا حاجة لتخطي HTML }});
// الاستخدامconsole.log(i18next.t('greeting', { name: 'أحمد' }));// الإخراج: مرحباً، أحمد!
console.log(i18next.t('coffee', { count: 1 }));// الإخراج: لقد شربت 1 فنجان قهوة
console.log(i18next.t('coffee', { count: 5 }));// الإخراج: لقد شربت 5 فناجين قهوة
console.log(i18next.t('menu.home'));// الإخراج: الرئيسيةتغيير اللغة أثناء التشغيل
i18next.changeLanguage('en');console.log(i18next.t('greeting', { name: 'Alex' }));// الإخراج: Hello, Alex!مكتبة node-polyglot
node-polyglot هي بديل خفيف وبسيط لـ i18next. إنها مثالية للمشاريع الصغيرة أو عندما لا