المزالق¶
هذا الموقع مترجم إلى خمس وثلاثين لغة، وقد أُنتج كل إصدار منها بتشغيل الحلقة التي تعلّمها هذه الوثائق. وهذا متن صغير بمقاييس الصناعة، ومع ذلك كفى للوقوع في معظم الفخاخ التي تجعل التوطين أصعب مما يبدو.
كل قسم مما يلي خطأ وقع هنا فعلاً، وكيف بدا حين وقع، وأين يمر الخط الفاصل بين ما تتحقق منه المكتبة عنك وما يبقى موكولاً إلى حكمك.
إعادة تسمية متغيّر تُعيد ترجمة جملة¶
قيمة msgid هي مفتاح الكتالوج، واسم العنصر المُقحَم يقع داخلها. فنقلُ ثابت
واحد إلى نطاق الوحدة وكتابته بأحرف كبيرة كما يطلب أسلوب Python — من author
إلى AUTHOR — حوّل Copyright © 2026 {author} · MIT License إلى رسالة لم
يرها أي كتالوج من قبل. وكانت كل ترجمة لذلك السطر ستعود إلى دورة fuzzy، في كل
لغة، مقابل إعادة تسمية لم تغيّر شيئاً يستطيع القارئ رؤيته.
لن تمنعك المكتبة: فكلا الهجاءين اسم عنصر نائب صالح. لكن ما تفعله هو أنها تجعل الاسم جديراً بالحماية — إذ يجب أن يكون الإقحام اسماً مجرداً، فيكون ما في مفتاح الكتالوج كلمةً يقرؤها المترجم، لا تعبيراً برمجياً.
أما الحالة المعاكسة فآمنة بحكم البناء. فالتحويلات ومواصفات التنسيق ليست جزءاً
من msgid، ومن ثم فإن تضييق {amount:,.2f} إلى {amount:,.0f} لا يغيّر أي
مفتاح ولا يُبطل أي ترجمة في أي مكان.
nplurals=2 لا تعني سلسلتين مختلفتين¶
تعلن التركية والمجرية والفارسية والبنغالية جميعاً صيغتي جمع، وفي الأربع كلها
تكون صيغتا الرسالة المعدودة السلسلة نفسها عن حق — فالاسم يبقى مفرداً بعد
العدد، ومن ثم فإن {n} sayfa صحيحة لصفحة واحدة ولعشر صفحات على السواء.
والمراجع الذي «يصحّح» هذا التكرار يُفسد الترجمة.
والخطأ المعاكس بالسهولة نفسها. فالصيغة الثالثة في اللاتفية موجودة للصفر
وحده؛ والثانية في السلوفينية مثنّى، للاثنين بالضبط؛ وصيغة الرومانية
الأخيرة تستلزم الكلمة de التي يجب ألا تظهر في الأوليين. وملء تلك الخانات
بمفرد وجمع يُنتج كتالوجاً لا يخطئ إلا عند أعداد لا يختبرها أحد.
والأسوأ أن ترتيب الخانات ليس دلالياً. فالويلزية تفهرس صيغها الخمس بحيث
تكون msgstr[0] هي الحالة العامة وmsgstr[1] هي المفرد. وملؤها بالتسلسل
البديهي يضع المفرد حيث ستجده كل رسالة غير معدودة.
لا تتولى المكتبة شيئاً من ذلك، وهذا هو المقصود: فقاعدة الجمع في اللغة الهدف تعيش في ترويسة كتالوجها هي، وقاعدة الاتحاد/التقاطع تتيح للترجمة صيغاً أكثر من المصدر أو أقل منه. أما ما تتحقق منه المكتبة فهو الشيء الوحيد الذي يمكن التحقق منه من دون معرفة اللغة — أن كل صيغة تحتفظ بالعناصر النائبة التي تحتاجها.
قد تتطابق صيغتان لسبب وجيه¶
للأيرلندية خمس صيغ جمع، وفي تقرير بناء هذا الموقع تُكتب عدة صيغ منها بالهجاء
نفسه. وليس ذلك زلة نسخ ولصق: فكلمة leathanach تبدأ بحرف l، ولا يُكتب على
l أيٌّ من التغيّرين الاستهلاليين اللذين تُحدثهما الأعداد في الأيرلندية.
ومع ذلك تؤدي الصيغ عملاً حقيقياً — إذ يتناوب الجذع بين leathanach
وleathanaigh، وتعود الأعداد فوق العشرة إلى المفرد — لكن ما من اسم بمعنى
«صفحة» كان ليُظهر هذا التباين.
وأي فحص يَعُدّ الصيغ المكررة مريبة سيرفع علماً على أيرلندية صحيحة. وإنسان يعرف اللغة هو المراجع الوحيد لهذا.
لا تطابق الرسالة إلا عدداً واحداً¶
يذكر تقرير بناء هذا الموقع كم صفحة عُرضت وكم استغرق ذلك. وكتابته على صورة
«Rendered {n} pages in {seconds} seconds» تبدو بلا ضرر، وهي غير قابلة
للترجمة: فـgettext يختار صيغة واحدة اعتماداً على عدد واحد، وذلك العدد هو
n. أما كلمة seconds فكان عليها أن تطابق عدداً لا تراه آلية الجمع أبداً.
والعلاج أن تجعل الكمية الثانية رمز وحدة لا كلمة، ورموز الوحدات نفسها
مُوطَّنة: فكتالوجات هذا الموقع تحمل s وс وث وשנ׳ وmp، كما تطلب
الطباعة الفرنسية والإسبانية والسويدية مسافة قبل الرمز حيث لا تطلبها
الإنجليزية. لا شيء من ذلك من شأن المكتبة — لكن الانتباه إلى أن الرسالة تحتاج
إلى مطابقتين اثنتين أمر لا بد منه، وأداته الوحيدة هي كتابة الرسالة كتابةً
أخرى.
تعديل جملة إنجليزية يعدّل نحو لغات أخرى¶
كانت الصفحة الرئيسية تقول «all ten language editions». وحذفُ العدد — تعديل بكلمة واحدة في الإنجليزية، أُجري لأن العدد ظل يتقادم — حوّل فاعلاً جمعاً إلى مفرد. فاضطرت الإسبانية والإيطالية والبرتغالية والروسية والأوكرانية واليونانية والهولندية والعبرية جميعاً إلى إعادة مطابقة الفعل؛ واحتاجت عدة لغات منها إلى تغيير اسم المفعول أيضاً.
فالتعديل الذي يبدو تافهاً في المصدر الإنجليزي ليس تافهاً فيما بعده. ووسمه
بـfuzzy، وهو ما يفعله pybabel update، هو الآلية التي تمنح كل مترجم فرصة
الانتباه.
الفروق غير المرئية تنجو من كل نسخ ولصق¶
يقتبس الدليل تشخيصاً يحتوي (nаme) — وهو تهريب مقصود، لأن الحرف الذي يسميه
هو а سيريلية لا يستطيع أي قارئ تمييزها عن اللاتينية. وقد حوّل مترجمو هذا
الموقع ذلك التهريب إلى الحرف الفعلي خمس مرات منفصلة، في خمس لغات مختلفة،
فأنتجوا في كل مرة صفحة تبدو صحيحة وهي خاطئة.
وهذا مما تلتقطه المكتبة فعلاً، وهو سبب تشكّل التشخيصات على هذه الصورة: فالعنصر النائب الذي تختلط في حروفه أنظمة كتابة يُعرض مرتين، مرة مقروءة ومرة مهرَّبة، لأن الصورة المهرَّبة هي الهجاء الوحيد الذي يفرّق بينهما. وتُطبع المسافة غير المنقسمة داخل الأقواس المعقوفة بنقطة ترميزها للسبب نفسه. ويرفض فاحص الكتالوج الرسالة قبل أن تُشحن.
غير الفارغ ليس مترجماً¶
الكتالوج المُهيَّأ بنسخ قيم msgid إلى msgstr يجتاز كل فحص ساذج: لا شيء فارغ، ولا شيء موسوم بـfuzzy، ومجموعة الرسائل مطابقة تماماً. وقد شُحن أحد إصدارات هذا الموقع على هذه الحال ساعات عدة. وكذلك ثماني صفحات من إصدار آخر كانت نسخاً مطابقة بايتاً ببايت للمصدر الإنجليزي — وهي تجتاز فحصاً يقارن كتل الشيفرة بينهما، لأنهما الملف نفسه.
وليس أيٌّ منهما شيئاً تستطيع مكتبة ترجمة أن تراه. وكلاهما رخيص الاختبار، لكن
لا باشتراط أن يختلف كل إدخال عن مصدره: فـOK، وأسماء المنتجات، وأسماء
الأشخاص، والاختصارات، ومعرّفات الشيفرة كلها تُترجم إلى نفسها، وفحصٌ يمنع ذلك
ينتج إيجابيات كاذبة إلى الأبد.
قِس النسبة بدلاً من ذلك، على مستوى كتالوج كامل أو صفحة كاملة، وأرسل الحالات الشاذة إلى إنسان. واختبار هذا الموقع نفسه يفعل ذلك بالضبط — يقارن أسطر النثر في كل إصدار بالمصدر الإنجليزي ويفشل فوق 25% متطابقة. وقد بلغ الإصدار المزيف 87%، بينما تقع كل ترجمة حقيقية بين 4% و8%، وهي الذيل الصغير من الأسطر التي تتصادف بحق، مثل الروابط ومخرجات البرامج المقتبسة. والمجموعتان متباعدتان بما يكفي كي لا يحتاج الحدّ إلى دقة.
الكتالوج ليس الشيء المترجم الوحيد¶
إخفاقان هنا لم تكن لهما صلة بـgettext.
ترجمة عنوانٍ تغيّر المرساة المولّدة منه، فتنكسر كل وصلة من صفحة أخرى إلى ذلك القسم — بصمت، وفي تلك اللغة وحدها. ولذلك يثبّت هذا الموقع المرساة الإنجليزية على كل عنوان، ويشتق اختبارٌ القائمةَ المتوقعة من الصفحة الإنجليزية.
كما أن مولّد الموقع يشحن ترجمات واجهة لثمانٍ وستين لغة، ليست منها السواحيلية ولا الأيرلندية. وعند غيابها لا يتراجع البناء إلى الإنجليزية؛ بل يفشل تضمين القالب ولا يمكن بناء الإصدار أصلاً. ويوجد ملفان من ملفات هذا المستودع نفسه لسدّ تلك الفجوة.
أدواتك بها علل أيضاً¶
خطوة CI التي توصي بها هذه الوثائق لالتقاط الكتالوجات المتقادمة، أي
pybabel update --check، لا تستطيع أداء تلك المهمة في أي مشروع يستخدم
pgettext أو npgettext. فعلى Babel 2.18.0 تُبلغ عن كل كتالوج فيه msgctxt
بأنه متقادم، في كل تشغيل. تمر المقارنة عبر Catalog.is_identical، التي تبحث
عن كل رسالة بالمفتاح المخزَّنة تحته — ومفتاح الرسالة ذات السياق هو الزوج
(id, context)، وهو ما لا تقبله Catalog.get. فيعود البحث بلا شيء، ولا
يتساوى الكتالوجان أبداً:
>>> from babel.messages.catalog import Catalog
>>> c = Catalog(locale="ja")
>>> c.add("Guide", "ガイド", context="navigation")
<Message 'Guide' (flags: [])>
>>> c.is_identical(c)
False
وقد اكتُشفت العلة هنا بمحاولة استخدامها، وأُبلغ عنها في المنبع، والفحص البديل موجود في صفحة الإنتاج.
والدرس العام هو الدرس المزعج: بوابة حمراء دائماً أسوأ من لا بوابة، لأن الفريق يطفئها. تحقق من أن فحص CI لديك يستطيع النجاح فعلاً قبل أن تأتمنه على أن يفشل.
ما وُجدت المكتبة من أجله، في سطر واحد¶
معظم هذه الصفحة حكمٌ لا تستطيع أداة أن تتولاه. أما ما تستطيعه الأداة فهو ضمان ألا تغيّر الترجمة بنية الجملة التي تترجمها — ألا تُسقط قيمة، ولا تخترع أخرى، ولا تعيد تنسيق واحدة، ولا تمتد إلى كائناتك — وأن تقول ذلك في جملة يستطيع من عليه الإصلاح أن يتصرف بناءً عليها. هذا هو كل ما تَعِد به هذه المكتبة، وبقية هذا الموقع هي كيف تفي به.