الحصول على جميع صيغ الجمع المتاحة في لغة معينة
اكتشف فئات الجمع التي تحتاج إلى توفير ترجمات لها
مقدمة
عند بناء تطبيقات متعددة اللغات، تحتاج إلى توفير أشكال نصية مختلفة للكميات المختلفة. في اللغة الإنجليزية، تكتب "1 item" و"2 items". يبدو هذا بسيطًا حتى تبدأ في دعم لغات أخرى.
تستخدم اللغة الروسية ثلاثة أشكال مختلفة اعتمادًا على العدد. بينما تستخدم اللغة العربية ستة أشكال. وتستخدم بعض اللغات نفس الشكل لجميع الأعداد. قبل أن تتمكن من توفير ترجمات لهذه الأشكال، تحتاج إلى معرفة الأشكال الموجودة في كل لغة.
توفر جافا سكريبت طريقة لاكتشاف فئات الجمع التي تستخدمها اللغة المحلية. تُرجع طريقة resolvedOptions() على نسخة PluralRules خاصية pluralCategories التي تسرد جميع أشكال الجمع التي تحتاجها اللغة المحلية. هذا يخبرك بالضبط بالترجمات التي يجب توفيرها دون تخمين أو الحفاظ على جداول قواعد خاصة باللغة.
ما هي فئات الجمع
فئات الجمع هي أسماء موحدة لأشكال الجمع المختلفة المستخدمة عبر اللغات. يحدد CLDR (مستودع بيانات اللغات المشتركة) التابع ليونيكود ست فئات: صفر، واحد، اثنان، قليل، كثير، وأخرى.
لا تستخدم كل لغة جميع الفئات الست. تستخدم اللغة الإنجليزية فئتين فقط: واحد وأخرى. تنطبق فئة واحد على العدد 1، وتنطبق فئة أخرى على كل شيء آخر.
تستخدم اللغة العربية جميع الفئات الست. تنطبق فئة صفر على 0، وواحد على 1، واثنان على 2، وقليل على أعداد مثل 3-10، وكثير على أعداد مثل 11-99، وأخرى على أعداد مثل 100 وما فوق.
تستخدم اللغة الروسية ثلاث فئات: واحد للأعداد التي تنتهي بـ 1 (باستثناء 11)، وقليل للأعداد التي تنتهي بـ 2-4 (باستثناء 12-14)، وكثير لكل شيء آخر.
تستخدم اللغتان اليابانية والصينية فئة أخرى فقط لأن هذه اللغات لا تميز بين المفرد والجمع.
تمثل هذه الفئات القواعد اللغوية لكل لغة. عند توفير الترجمات، تقوم بإنشاء سلسلة واحدة لكل فئة تستخدمها اللغة.
الحصول على فئات الجمع باستخدام resolvedOptions
تُرجع طريقة resolvedOptions() على مثيل PluralRules كائنًا يحتوي على معلومات حول القواعد، بما في ذلك فئات الجمع التي تستخدمها اللغة المحلية.
const enRules = new Intl.PluralRules('en-US');
const options = enRules.resolvedOptions();
console.log(options.pluralCategories);
// الناتج: ["one", "other"]
خاصية pluralCategories هي مصفوفة من السلاسل النصية. كل سلسلة هي واحدة من أسماء الفئات الستة القياسية. تحتوي المصفوفة فقط على الفئات التي تستخدمها اللغة المحلية فعليًا.
بالنسبة للإنجليزية، تحتوي المصفوفة على one و other لأن اللغة الإنجليزية تميز بين صيغ المفرد والجمع.
بالنسبة للغة ذات قواعد أكثر تعقيدًا، تحتوي المصفوفة على المزيد من الفئات:
const arRules = new Intl.PluralRules('ar-EG');
const options = arRules.resolvedOptions();
console.log(options.pluralCategories);
// الناتج: ["zero", "one", "two", "few", "many", "other"]
تستخدم اللغة العربية جميع الفئات الستة، لذلك تحتوي المصفوفة على جميع القيم الستة.
مشاهدة فئات الجمع للغات المحلية المختلفة
تمتلك اللغات المختلفة قواعد جمع مختلفة، مما يعني أنها تستخدم مجموعات مختلفة من الفئات. قارن بين عدة لغات لرؤية الاختلاف:
const locales = ['en-US', 'ar-EG', 'ru-RU', 'pl-PL', 'ja-JP', 'zh-CN'];
locales.forEach(locale => {
const rules = new Intl.PluralRules(locale);
const categories = rules.resolvedOptions().pluralCategories;
console.log(`${locale}: [${categories.join(', ')}]`);
});
// الناتج:
// en-US: [one, other]
// ar-EG: [zero, one, two, few, many, other]
// ru-RU: [one, few, many, other]
// pl-PL: [one, few, many, other]
// ja-JP: [other]
// zh-CN: [other]
اللغة الإنجليزية لديها فئتان. اللغة العربية لديها ست فئات. اللغة الروسية والبولندية لديهما أربع فئات لكل منهما. اللغة اليابانية والصينية لديهما فئة واحدة فقط لأنهما لا تميزان صيغ الجمع على الإطلاق.
يوضح هذا التباين لماذا لا يمكنك افتراض أن كل لغة تعمل مثل اللغة الإنجليزية. تحتاج إلى التحقق من الفئات التي تستخدمها كل لغة محلية وتوفير الترجمات المناسبة لكل منها.
فهم ما تعنيه الفئات لكل لغة محلية
اسم الفئة نفسه يعني أشياء مختلفة في لغات مختلفة. فئة "one" في اللغة الإنجليزية تنطبق فقط على الرقم 1. في اللغة الروسية، تنطبق فئة "one" على الأرقام التي تنتهي بـ 1 باستثناء 11، لذا فهي تشمل 1، 21، 31، 101، وهكذا.
اختبر أي الأرقام تنتمي إلى أي فئات في لغات محلية مختلفة:
const enRules = new Intl.PluralRules('en-US');
const ruRules = new Intl.PluralRules('ru-RU');
const numbers = [0, 1, 2, 3, 5, 11, 21, 22, 100];
console.log('English:');
numbers.forEach(n => {
console.log(` ${n}: ${enRules.select(n)}`);
});
console.log('Russian:');
numbers.forEach(n => {
console.log(` ${n}: ${ruRules.select(n)}`);
});
// Output:
// English:
// 0: other
// 1: one
// 2: other
// 3: other
// 5: other
// 11: other
// 21: other
// 22: other
// 100: other
// Russian:
// 0: many
// 1: one
// 2: few
// 3: few
// 5: many
// 11: many
// 21: one
// 22: few
// 100: many
في اللغة الإنجليزية، فقط الرقم 1 يستخدم فئة "one". في اللغة الروسية، 1 و21 كلاهما يستخدمان فئة "one" لأنهما ينتهيان بـ 1. الأرقام 2، 3، و22 تستخدم فئة "few" لأنها تنتهي بـ 2-4. الأرقام 0، 5، 11، و100 تستخدم فئة "many".
هذا يوضح أنه لا يمكنك التنبؤ بالفئة التي تنطبق على رقم معين دون معرفة قواعد اللغة. المصفوفة pluralCategories تخبرك بالفئات الموجودة، والطريقة select() تخبرك بالفئة التي تنطبق على كل رقم.
الحصول على فئات للأعداد الترتيبية
الأعداد الترتيبية مثل 1st، 2nd، 3rd لها قواعد جمع خاصة بها تختلف عن الأعداد الأصلية. قم بإنشاء نسخة من PluralRules مع type: 'ordinal' للحصول على الفئات للأعداد الترتيبية:
const enCardinalRules = new Intl.PluralRules('en-US', { type: 'cardinal' });
const enOrdinalRules = new Intl.PluralRules('en-US', { type: 'ordinal' });
console.log('Cardinal:', enCardinalRules.resolvedOptions().pluralCategories);
// Output: Cardinal: ["one", "other"]
console.log('Ordinal:', enOrdinalRules.resolvedOptions().pluralCategories);
// Output: Ordinal: ["one", "two", "few", "other"]
الأعداد الأصلية في اللغة الإنجليزية تستخدم فئتين. الأعداد الترتيبية في اللغة الإنجليزية تستخدم أربع فئات لأن الأعداد الترتيبية تحتاج إلى التمييز بين 1st، 2nd، 3rd، وكل الأعداد الأخرى.
فئات الأعداد الترتيبية تتوافق مع لواحق الأعداد الترتيبية:
const enOrdinalRules = new Intl.PluralRules('en-US', { type: 'ordinal' });
const numbers = [1, 2, 3, 4, 11, 21, 22, 23];
numbers.forEach(n => {
const category = enOrdinalRules.select(n);
console.log(`${n}: ${category}`);
});
// Output:
// 1: one
// 2: two
// 3: few
// 4: other
// 11: other
// 21: one
// 22: two
// 23: few
الفئة "one" تتوافق مع اللاحقة st (1st، 21st)، و"two" مع nd (2nd، 22nd)، و"few" مع rd (3rd، 23rd)، و"other" مع th (4th، 11th).
اللغات المختلفة لها فئات ترتيبية مختلفة:
const locales = ['en-US', 'es-ES', 'fr-FR'];
locales.forEach(locale => {
const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
const categories = rules.resolvedOptions().pluralCategories;
console.log(`${locale}: [${categories.join(', ')}]`);
});
// Output:
// en-US: [one, two, few, other]
// es-ES: [other]
// fr-FR: [one, other]
اللغة الإسبانية تستخدم فئة ترتيبية واحدة فقط لأن الأعداد الترتيبية الإسبانية تتبع نمطًا أبسط. اللغة الفرنسية تستخدم فئتين للتمييز بين المرتبة الأولى وجميع المراتب الأخرى.
استخدام فئات الجمع لبناء خرائط الترجمة
عندما تعرف الفئات التي تستخدمها اللغة المحلية، يمكنك بناء خريطة ترجمة بالعدد الصحيح من الإدخالات:
function buildTranslationMap(locale, translations) {
const rules = new Intl.PluralRules(locale);
const categories = rules.resolvedOptions().pluralCategories;
const map = new Map();
categories.forEach(category => {
if (translations[category]) {
map.set(category, translations[category]);
} else {
console.warn(`Missing translation for category "${category}" in locale "${locale}"`);
}
});
return map;
}
const enTranslations = {
one: 'item',
other: 'items'
};
const arTranslations = {
zero: 'لا توجد عناصر',
one: 'عنصر واحد',
two: 'عنصران',
few: 'عناصر',
many: 'عنصرًا',
other: 'عنصر'
};
const enMap = buildTranslationMap('en-US', enTranslations);
const arMap = buildTranslationMap('ar-EG', arTranslations);
console.log(enMap);
// Output: Map(2) { 'one' => 'item', 'other' => 'items' }
console.log(arMap);
// Output: Map(6) { 'zero' => 'لا توجد عناصر', 'one' => 'عنصر واحد', ... }
تتحقق هذه الدالة من أنك قدمت ترجمات لجميع الفئات المطلوبة وتحذرك إذا كان أي منها مفقودًا. هذا يمنع أخطاء وقت التشغيل عندما يتم استخدام فئة ولكن ليس لها ترجمة.
التحقق من اكتمال الترجمة
استخدم فئات الجمع للتحقق من أن ترجماتك تتضمن جميع الصيغ الضرورية قبل النشر في بيئة الإنتاج:
function validateTranslations(locale, translations) {
const rules = new Intl.PluralRules(locale);
const requiredCategories = rules.resolvedOptions().pluralCategories;
const providedCategories = Object.keys(translations);
const missing = requiredCategories.filter(cat => !providedCategories.includes(cat));
const extra = providedCategories.filter(cat => !requiredCategories.includes(cat));
if (missing.length > 0) {
console.error(`Locale ${locale} is missing categories: ${missing.join(', ')}`);
return false;
}
if (extra.length > 0) {
console.warn(`Locale ${locale} has unused categories: ${extra.join(', ')}`);
}
return true;
}
const enTranslations = {
one: 'item',
other: 'items'
};
const incompleteArTranslations = {
one: 'عنصر واحد',
other: 'عنصر'
};
validateTranslations('en-US', enTranslations);
// Output: true
validateTranslations('ar-EG', incompleteArTranslations);
// Output: Locale ar-EG is missing categories: zero, two, few, many
// Output: false
يكتشف هذا التحقق الترجمات المفقودة أثناء التطوير بدلاً من اكتشافها عندما يواجه المستخدمون نصًا غير مترجم.
بناء واجهات الترجمة الديناميكية
عند بناء أدوات للمترجمين، استعلم عن فئات الجمع لعرض الصيغ التي تحتاج إلى ترجمة بالضبط:
function generateTranslationForm(locale, key) {
const rules = new Intl.PluralRules(locale);
const categories = rules.resolvedOptions().pluralCategories;
const form = document.createElement('div');
form.className = 'translation-form';
const heading = document.createElement('h3');
heading.textContent = `Translate "${key}" for ${locale}`;
form.appendChild(heading);
categories.forEach(category => {
const label = document.createElement('label');
label.textContent = `${category}:`;
const input = document.createElement('input');
input.type = 'text';
input.name = `${key}.${category}`;
input.placeholder = `Enter ${category} form`;
const wrapper = document.createElement('div');
wrapper.appendChild(label);
wrapper.appendChild(input);
form.appendChild(wrapper);
});
return form;
}
const enForm = generateTranslationForm('en-US', 'items');
const arForm = generateTranslationForm('ar-EG', 'items');
document.body.appendChild(enForm);
document.body.appendChild(arForm);
هذا ينشئ نموذجًا بعدد حقول الإدخال الصحيحة لكل لغة. تحصل اللغة الإنجليزية على حقلين (one وother)، بينما تحصل اللغة العربية على ستة حقول (zero وone وtwo وfew وmany وother).
مقارنة الفئات عبر اللغات المختلفة
عند إدارة الترجمات للغات متعددة، قارن الفئات التي تستخدمها لفهم تعقيد الترجمة:
function compareLocalePluralCategories(locales) {
const comparison = {};
locales.forEach(locale => {
const rules = new Intl.PluralRules(locale);
const categories = rules.resolvedOptions().pluralCategories;
comparison[locale] = categories;
});
return comparison;
}
const locales = ['en-US', 'es-ES', 'ar-EG', 'ru-RU', 'ja-JP'];
const comparison = compareLocalePluralCategories(locales);
console.log(comparison);
// Output:
// {
// 'en-US': ['one', 'other'],
// 'es-ES': ['one', 'other'],
// 'ar-EG': ['zero', 'one', 'two', 'few', 'many', 'other'],
// 'ru-RU': ['one', 'few', 'many', 'other'],
// 'ja-JP': ['other']
// }
هذا يوضح أن اللغتين الإنجليزية والإسبانية لديهما نفس فئات الجمع، مما يسهل إعادة استخدام هياكل الترجمة بينهما. تتطلب اللغة العربية عملًا أكثر بكثير في الترجمة لأنها تستخدم ست فئات.
التحقق مما إذا كانت اللغة تستخدم فئة محددة
قبل استخدام فئة جمع محددة في الكود الخاص بك، تحقق مما إذا كانت اللغة تستخدمها بالفعل:
function localeUsesCategory(locale, category) {
const rules = new Intl.PluralRules(locale);
const categories = rules.resolvedOptions().pluralCategories;
return categories.includes(category);
}
console.log(localeUsesCategory('en-US', 'zero'));
// Output: false
console.log(localeUsesCategory('ar-EG', 'zero'));
// Output: true
console.log(localeUsesCategory('ja-JP', 'one'));
// Output: false
هذا يمنعك من افتراض أن كل لغة تحتوي على فئة صفر أو فئة واحد. استخدم هذا الفحص لتنفيذ المنطق الخاص بالفئة بأمان.
فهم فئة "other"
كل لغة تستخدم فئة "other". تعمل هذه الفئة كحالة افتراضية عندما لا تنطبق أي فئة أخرى.
في اللغة الإنجليزية، تغطي فئة "other" جميع الأعداد باستثناء 1. في اللغة العربية، تغطي فئة "other" الأعداد الكبيرة مثل 100 وما فوق. في اللغة اليابانية، تغطي فئة "other" جميع الأعداد لأن اليابانية لا تميز صيغ الجمع.
قدم دائمًا ترجمة لفئة "other". من المضمون وجود هذه الفئة في كل لغة وسيتم استخدامها عندما لا تتطابق أي فئة أكثر تحديدًا.
const locales = ['en-US', 'ar-EG', 'ru-RU', 'ja-JP'];
locales.forEach(locale => {
const rules = new Intl.PluralRules(locale);
const categories = rules.resolvedOptions().pluralCategories;
const hasOther = categories.includes('other');
console.log(`${locale} uses "other": ${hasOther}`);
});
// Output:
// en-US uses "other": true
// ar-EG uses "other": true
// ru-RU uses "other": true
// ja-JP uses "other": true
الحصول على جميع الخيارات المحددة معًا
طريقة resolvedOptions() تعيد أكثر من مجرد فئات الجمع. تتضمن معلومات حول اللغة والنوع وخيارات تنسيق الأرقام:
const rules = new Intl.PluralRules('de-DE', {
type: 'cardinal',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
const options = rules.resolvedOptions();
console.log(options);
// Output:
// {
// locale: 'de-DE',
// type: 'cardinal',
// pluralCategories: ['one', 'other'],
// minimumIntegerDigits: 1,
// minimumFractionDigits: 2,
// maximumFractionDigits: 2,
// minimumSignificantDigits: undefined,
// maximumSignificantDigits: undefined
// }
خاصية pluralCategories هي جزء واحد من المعلومات في كائن الخيارات المحددة. تخبرك الخصائص الأخرى بالضبط عن التكوين الذي تستخدمه نسخة PluralRules، بما في ذلك أي خيارات تم تعيينها إلى القيم الافتراضية.
تخزين فئات الجمع مؤقتًا لتحسين الأداء
إنشاء نسخ من PluralRules واستدعاء resolvedOptions() له تكلفة. قم بتخزين النتائج مؤقتًا لكل لغة بدلاً من الاستعلام عنها بشكل متكرر:
const categoriesCache = new Map();
function getPluralCategories(locale, type = 'cardinal') {
const key = `${locale}:${type}`;
if (categoriesCache.has(key)) {
return categoriesCache.get(key);
}
const rules = new Intl.PluralRules(locale, { type });
const categories = rules.resolvedOptions().pluralCategories;
categoriesCache.set(key, categories);
return categories;
}
const enCardinal = getPluralCategories('en-US', 'cardinal');
const enOrdinal = getPluralCategories('en-US', 'ordinal');
const arCardinal = getPluralCategories('ar-EG', 'cardinal');
console.log('en-US cardinal:', enCardinal);
console.log('en-US ordinal:', enOrdinal);
console.log('ar-EG cardinal:', arCardinal);
// الاستدعاءات اللاحقة تستخدم النتائج المخزنة مؤقتًا
const enCardinal2 = getPluralCategories('en-US', 'cardinal');
// لم يتم إنشاء نسخة جديدة من PluralRules
هذا النمط مهم بشكل خاص في التطبيقات التي تقوم بتنسيق العديد من النصوص المتعددة أو تدعم العديد من اللغات.
دعم المتصفح والتوافق
تمت إضافة خاصية pluralCategories إلى resolvedOptions() في جافاسكريبت عام 2020. وهي مدعومة في كروم 106+، فايرفوكس 116+، سفاري 15.4+، وإيدج 106+.
المتصفحات القديمة التي تدعم Intl.PluralRules ولكن لا تدعم pluralCategories سترجع undefined لهذه الخاصية. تحقق من وجودها قبل استخدامها:
function getPluralCategories(locale) {
const rules = new Intl.PluralRules(locale);
const options = rules.resolvedOptions();
if (options.pluralCategories) {
return options.pluralCategories;
}
// بديل للمتصفحات القديمة
return ['one', 'other'];
}
يفترض هذا البديل نظامًا بسيطًا من فئتين، وهو يعمل للغة الإنجليزية والعديد من اللغات الأوروبية ولكنه قد لا يكون صحيحًا للغات ذات القواعد الأكثر تعقيدًا. للحصول على توافق أفضل، قم بتوفير بدائل خاصة باللغة أو استخدم polyfill.