استخدم مساعدك المفضل للملخص واستخدم هذه الصفحة والموفر AI الذي تريده
تمت ترجمة محتوى هذه الصفحة باستخدام الذكاء الاصطناعي.
اعرض آخر نسخة المحتوى الأصلي باللغة الإنكليزيةإذا كان لديك فكرة لتحسين هذه الوثيقة، فلا تتردد في المساهمة من خلال تقديم طلب سحب على GitHub.
رابط GitHub للتوثيقنسخ الـ Markdown من المستند إلى الحافظة
صيغة رسائل ICU: القواعد والتفاصيل التي يخطئ فيها الكثيرون
تُعد ICU MessageFormat صيغة نصوص تتيح للترجمة أن تحتوي على منطق التفرع الخاص بها: صيغ الجمع، الأشكال المعتمدة على الجنس، وتنسيق الأرقام والتواريخ. تعتمد فكرتها الأساسية على أن القواعد النحوية مسؤولية المترجم وليست مسؤولية المطور الذي يكتب if (count === 1). يستعرض هذا المقال بناء الجملة، والخصائص المعتمدة على اللغة التي تفشل فيها التطبيقات البسيطة، وكيف يتعامل نظام JavaScript البيئي مع هذه التحديات.
جدول المحتويات
المشكلة بشكل عملي
إليك الكود الذي يبدأ بكتابته معظم المطورين:
نسخ الكود إلى الحافظة
يعمل هذا الأسلوب باللغة الإنجليزية ولكنه يفشل في بقية اللغات الأخرى تقريبًا:
- الروسية والبولندية تتطلبان ثلاث أو أربع صيغ، وليس اثنتين.
- اليابانية تحتاج صيغة واحدة فقط، والمسافة التي تمت إضافتها بالدمج غير صحيحة.
- العربية تتطلب ست صيغ جمع، كما يجب عرض الرقم نفسه وفقًا لنظام الأرقام المعتمد في اللغة.
- الفرنسية تضع مسافة غير قابلة للكسر قبل بعض علامات الترقيم، وهو ما يدمره الدمج عبر
+ " ".
المشكلة الأعمق تكمن في تقطيع الجملة إلى أجزاء منفصلة. يرى المترجم الكلمتين item وitems دون أي سياق ودون القدرة على إعادة ترتيب كلمات الجملة. تعالج ICU MessageFormat هذه المسألة بالحفاظ على الجملة كاملة داخل نص واحد قابل للترجمة مع منح المترجم أدوات التفرع الشرطي.
المعاملات البسيطة
أصغر وحدة هي العنصر النائب المحاط بأقواس معقوفة مفردة:
نسخ الكود إلى الحافظة
عند التنسيق، تمرر { name: "Alice" } لتحصل على Hello, Alice!. الأقواس المعقوفة هي الرموز الخاصة الوحيدة، ولطباعة قوس معقوف حرفيًا يتم تغليفه بعلامات اقتباس مفردة: '{'.
هذه هي ميزة التضمين (الاستيفاء) بالكامل، وكل شيء آخر في ICU مبني عليها.
صيغ الجمع (plural)
يقوم plural باختيار الفرع المناسب بناءً على قيمة رقمية:
نسخ الكود إلى الحافظة
ثلاث قواعد أساسية يجب معرفتها:
- يتم استبدال الرمز
#بالقيمة المنسقة لـcountطبقًا للغة المحلية. على سبيل المثال، يتحول1234إلى1,234فيen-US. - الفرع
otherإلزامي. ستتوقف أي مكتبة ICU أو تفشل في التحقق عند غيابه، حيث يعمل كخيار احتياطي عند عدم تطابق أي فئة. - القواعد
=0و=1تطابق قيمًا دقيقة وتتم معالجتها قبل فئات CLDR. استخدمها للنصوص ذات الحالات الخاصة (مثل "لا توجد رسائل")، وليس كبديل عنone.
نسخ الكود إلى الحافظة
الإزاحة (offset)
تقوم offset:n بطرح n من القيمة قبل تحديد الفئة وقبل استبدال #. تُستخدم لأنماط مثل "أعجب أليس و3 أشخاص آخرين بهذا":
نسخ الكود إلى الحافظة
عند تمرير count: 4، يقوم الرمز # بعرض القيمة 3. خاصية offset مفيدة جدًا، ولكن دعمها يتفاوت بين بيئات التشغيل، لذا تأكد من توافق بيئتك قبل الاعتماد عليها.
فئات الجمع تعتمد على اللغة
هذا هو الجانب الذي يقع فيه أغلب المطورين في الخطأ. أسماء الفئات zero، one، two، few، many، other ليست قوالب ثابتة تُطبق على جميع اللغات. تستخدم كل لغة مجموعة فرعية تحددها قواعد الجمع في CLDR، وتخضع هذه القواعد للبناء النحوي وليس للمنطق الحسابي المجرد.
افتح الجدول في نافذة منبثقة لعرض جميع محتويات البيانات بوضوح
| اللغة | الرمز | الفئات المستخدمة | الإجمالي |
|---|---|---|---|
| اليابانية | ja | other | 1 |
| الصينية | zh | other | 1 |
| الإنجليزية | en | one, other | 2 |
| الألمانية | de | one, other | 2 |
| الفرنسية | fr | one, many, other | 3 |
| التشيكية | cs | one, few, many, other | 4 |
| البولندية | pl | one, few, many, other | 4 |
| الروسية | ru | one, few, many, other | 4 |
| العربية | ar | zero, one, two, few, many, other | 6 |
| الويلزية | cy | zero, one, two, few, many, other | 6 |
نتيجتان تفاجئان الكثيرين:
- الفئة
oneلا تعني الرقم "1" حصريًا. في الروسية، تغطيoneالأرقام 1، 21، 31، 101: أي رقم ينتهي بـ 1 باستثناء الأرقام المنتهية بـ 11. وفي الفرنسية، يقع الرقم0ضمن فئةone. - إضافة فئات إلى النص الإنجليزي الأصلي لا يقدم أي فائدة. الرسالة الإنجليزية تحتاج فقط
oneوother، بينما تتطلب الترجمة البولندية أربعة فروع، والترجمة العربية ستة فروع، وهذه البنية تنتمي لنص اللغة المترجم إليها. أي تنسيق يفرض بنية مفاتيح موحدة لجميع اللغات سيتسبب في مشكلات هنا.
يمكنك التحقق من سلوك بيئة التشغيل لديك مباشرة دون تثبيت أي حزم إضافية:
نسخ الكود إلى الحافظة
توفر واجهة Intl.PluralRules بيانات CLDR في كافة المتصفحات الحديثة وبيئة Node.js. أي مكتبة تدعي دعم جمع CLDR تستدعي داخليًا هذه الواجهة البرمجية في الغالب.
select و selectordinal
يتيح select التفرع استنادًا إلى أي نص عشوائي: الجنس، دور المستخدم، الحالة، أو باقة الاشتراك.
نسخ الكود إلى الحافظة
تتم مطابقة المفاتيح بدقة حرفية، ويظل فرع other إلزاميًا هنا أيضًا. يُعد select الأداة المثالية عندما يعتمد تركيب الجملة على قيمة معرفة، لأن اللغات تختلف في القيم التي تؤثر على قواعدها.
أما selectordinal فيتخذ نفس شكل plural، ولكنه يتبع قواعد الأعداد الترتيبية (مثل الأول، الثاني) التي تختلف عن الأعداد الأصلية:
نسخ الكود إلى الحافظة
تستخدم الإنجليزية أربع فئات ترتيبية (1st, 2nd, 3rd, 4th) رغم أنها تستخدم فئتين فقط للأعداد الأصلية. هذا التباين هو سبب وجود معاملين منفصلين.
معاملات الأرقام والتواريخ والأوقات
تستطيع ICU تنسيق القيم المضمنة مباشرة:
نسخ الكود إلى الحافظة
الصيغة الحديثة المتبعة هي الهيكل (skeleton)، التي تم تقديمها في ICU 60 وتتميز بالبادئة ::. توفر الهياكل مرونة وقوة تعبيرية أعلى بكثير من التسميات القديمة:
نسخ الكود إلى الحافظة
يتفاوت دعم صيغ الهياكل عبر المكتبات. تدعمها FormatJS بشكل كامل، في حين تكتفي بيئات تشغيل أخرى بالأساليب القديمة مثل number, currency أو date, long. تحقق من دعم بيئتك لـ :: قبل الاعتماد عليها في الإنتاج.
التداخل وحدود القراءة
تتميز صيغة ICU بقابلية التركيب. يمكن لفرع الجمع أن يحتوي على select، والذي يمكنه بدوره احتواء فرع جمع آخر:
نسخ الكود إلى الحافظة
يمثل هذا المثال الكلاسيكي لـ ICU، وهو في الوقت نفسه الحجة الأساسية ضد التداخل العميق. عند تجاوز مستويين من التداخل، يبدأ المترجمون في ارتكاب أخطاء في الأقواس المعقوفة وتفقد محررات أنظمة الترجمة (TMS) فاعليتها. حافظ على التداخل في حدود مستويين على الأكثر، وإذا احتجت إلى مستوى ثالث، فمن الأفضل تقسيم الجملة إلى رسالتين منفصلتين.
كيفية تعامل مكتبات JavaScript مع ICU
افتح الجدول في نافذة منبثقة لعرض جميع محتويات البيانات بوضوح
| المكتبة | دعم ICU | ما تكتبه عمليًا في الكود |
|---|---|---|
| react-intl (FormatJS) | دعم أصيل وكامل | نصوص ICU كاملة بما يشمل الهياكل ووسوم النصوص الغنية |
| next-intl | دعم أصيل | نصوص ICU عبر مكتبة intl-messageformat التابعة لـ FormatJS |
| i18next | يتطلب إضافة | لواحق المفاتيح key_one / key_other و{{name}}، ودعم ICU عبر i18next-icu |
| vue-i18n | جزئي / صيغة خاصة بها | تضمين {name} وفروع جمع مفصولة بخط عمودي |
Angular ($localize) | دعم لمجموعة فرعية | صيغ plural وselect داخل القوالب واستخراجها إلى ملفات XLIFF |
ملاحظات لتوضيح الجدول أعلاه:
- الصيغة الافتراضية في i18next ليست ICU، وهذا ليس عيبًا في حد ذاته. تطابق مفاتيح اللواحق (
item_one,item_few) فئاتIntl.PluralRulesوتكون أسهل للمترجمين في ملفات JSON المسطحة. ومع ذلك، فإنselectوالتفرعات المتداخلة ليست مدمجة، مما يلزمك بإضافةi18next-icuأو كتابة المنطق برمجياً. - فروع vue-i18n المفصولة بأعمدة تستخدم افتراضيًا دالة قواعد خاصة بكل لغة بدلاً من فئات CLDR. يفي ذلك بالغرض ولكنه يضع القواعد داخل إعدادات التطبيق بدلاً من البيانات نفسها.
- تُعد FormatJS المرجع الرئيسي في نظام JS البيئي. عندما يُذكر مصطلح "ICU MessageFormat" في مجتمع جافاسكريبت، فالمقصود عادة هو ما تقبله مكتبة FormatJS.
- الدعم الكامل لـ ICU يفرض ضريبة على حجم الحزمة. يضيف المحلل اللغوي ومعالجة الهياكل قرابة 10 كيلوبايت من كود JavaScript المضغوط. انظر لماذا لم يتم تصميم ICU لبيئة JavaScript.
نهج Intlayer
لا تعتمد Intlayer على لغة خاصة للنصوص (DSL). بل تُقدم معاملات التفرع كدوال برمجية ذات أنواع محددة داخل ملفات تصريح المحتوى، مما يمنح حماية برمجية ويسمح لكل لغة بتصريح الفئات التي تتطلبها قواعدها النحوية فقط:
نسخ الكود إلى الحافظة
نسخ الكود إلى الحافظة
يتطابق هذا النمط مع مفاهيم ICU بشكل مباشر:
افتح الجدول في نافذة منبثقة لعرض جميع محتويات البيانات بوضوح
| عنصر ICU | المقابل في Intlayer |
|---|---|
{name} | insert("Hello {{name}}") أو الكشف التلقائي |
{count, plural, …} | plural({ zero, one, two, few, many, other }) |
{value, select, …} | select({ draft, published, fallback }) |
تفرع الجنس في select | gender({ male, female, fallback }) |
تفرع القيم المنطقية في select | cond({ true, false }) |
| النطاقات العددية (خارج CLDR) | enu({ "0": …, ">5": …, fallback: … }) |
{n, number, ::currency/EUR} | useCurrency()(1234.5, { currency: "EUR" }) |
يفوض المعامل plural تحديد الفئة إلى Intl.PluralRules مباشرة، وبالتالي ينطبق جدول CLDR الموضح أعلاه كما هو. كما يظل التنسيق منفصلاً: تتم معالجة الأرقام والتواريخ والعملات والقوائم عبر خطافات التنسيق بدلاً من حشرها داخل نص الرسالة.
نقاط ينبغي مراعاتها:
- تتطلب Intlayer مرحلة بناء: يقوم المترجم باستخراج التصريحات أثناء التحزيم. إذا كنت ترغب في تحميل ملفات JSON عادية أثناء وقت التشغيل، فهذا نموذج مختلف.
- لا يمكن حاليًا تضمين
t()داخل فروعplural، بل يتم تضمينpluralداخلt(). - البيئة المحيطة بـ Intlayer أحدث عمرًا من i18next، مع تكاملات أقل جاهزية مع أدوات إدارة الترجمة (TMS).
إذا كنت تنتقل من مشروع يحتوي بالفعل على نصوص ICU، فإن محول التوافق react-intl يحللها مباشرة: plural، select، selectordinal، #، ومعاملات number وdate وtime الكلاسيكية. لا يدعم هذا المحول الهياكل أو offset:، لذا يجب مراجعة تلك النصوص أثناء الترحيل. أما محول i18next فيقوم بحل لواحق المفاتيح (key_one، key_male) عبر Intl.PluralRules.
الأخطاء الشائعة
- كتابة منطق الجمع برمجيًا في JS. التعبير الشرطي
count === 1 ? a : bيعطي نتائج غير صحيحة لـ 8 من أصل 10 لغات في الجدول أعلاه. بمجرد تضمين هذا الشرط في الكود، يعجز المترجم عن تقديم ترجمة نحوية سليمة. - دمج الأجزاء المترجمة كنصوص منفصلة. ترتيب الكلمات والتطابقات النحوية والمسافات تختلف من لغة إلى أخرى. حافظ دائمًا على الجملة كوحدة واحدة متكاملة.
- إغفال فرع
other. هذا الفرع إلزامي في المعيار وليس خيارًا تجميليًا. سترفض أغلب المكتبات النص، ولن تعرض المكتبات الأخرى أي شيء. - افتراض أن الفئات تتطابق بين اللغات. وجود
oneوotherفي الملف الإنجليزي لا يعني أن الملف البولندي أو العربي سيتضمن فرعين فقط. يجب أن تُصرح كل لغة بفروعها الخاصة. راجع تصريح المحتوى لكل لغة. - استخدام
=1بدلاً منone. تطابق=1القيمة الرقمية 1 حصريًا. في الروسية، يحتاج الرقم 21 إلى فئةone، ولن تنطبق عليه القاعدة=1أبدًا. - وضع الرمز
#خارج فروع الجمع. يعمل#كعنصر استبدال فقط داخلpluralأوselectordinal، وخارج ذلك يُعامل كرمز هاش عادي. - نسيان أن الرمز
#منسق مسبقًا. إذا كنت بحاجة إلى الرقم الخام دون فواصل أو تنسيقات محلية، قم بتضمين المعامل باسمه بدلاً من ذلك.
مراجع إضافية
التعليقات
لا توجد تعليقات بعد. كن أول من يشارك أفكاره.
