الترحيل¶
إن كان مشروعك يستخدم gettext بالفعل، فالأسئلة التي تحسم قابلية تبنّي هذه المكتبة أسئلة ضيّقة: هل تُبطل الكتالوجات التي لديك، وهل تتعايش مع الشيفرة التي لست مستعداً لتغييرها، وكم من الانتقال يجب أن يحدث دفعة واحدة. وهذه هي الأجوبة، أقصرها أولاً:
| السؤال | الجواب |
|---|---|
هل تظل ملفات .po و.mo الحالية تعمل؟ |
نعم. الملفات نفسها، والأدوات نفسها. |
| هل يتعايش الاستدعاء القديم والجديد في ملف واحد؟ | نعم، وتعيين واحد للمستخرِج يغطيهما معاً. |
| هل يتغير msgid؟ | لا، إن كان المصدر .format(). نعم، إن كان بصيغة %. |
| هل يجب أن ينتقل المشروع كله دفعة واحدة؟ | لا. موضع استدعاء واحد تغييرٌ صالح. |
| وماذا عن Jinja وقوالب Django وJavaScript؟ | لا تُمسّ، والكتالوجات نفسها. |
وبقية هذه الصفحة هي التفصيل وراء كل واحد من هذه الأجوبة.
من .format(): لا يتغير msgid¶
هذه هي الحالة التي لا يكلف فيها الترحيل شيئاً تقريباً. فرسالة str.format
ورسالة t-string تشتقّان مفتاح الكتالوج نفسه، لأن المفتاح هو النص وفيه
{name} كما هو في الحالتين:
# Before
_("Hello {name}").format(name=name)
# After — the msgid is still "Hello {name}"
tr(t"Hello {name}")
ولذا تبقى الترجمة الموجودة مرتبطة بالرسالة. انطلاقاً من كتالوج يحتوي على
غيّر الاستدعاء، ثم أعد الاستخراج، ثم حدّث:
$ pybabel extract -F babel.cfg -o locales/messages.pot .
extracting messages from app.py (encoding="utf-8")
writing PO template file to locales/messages.pot
$ pybabel update -i locales/messages.pot -d locales
updating catalog locales/ja/LC_MESSAGES/messages.po based on locales/messages.pot
الإدخال العائد يختلف في سطرين من البيانات الوصفية ولا شيء غير ذلك — تعليق واسم يعرّفه كرسالة t-string، ورقم سطر في المصدر:
لا علامة fuzzy، ولا إعادة ترجمة، في أي لغة. وتُعرض الرسالة فوراً:
$ pybabel compile -d locales
compiling catalog locales/ja/LC_MESSAGES/messages.po to locales/ja/LC_MESSAGES/messages.mo
$ python app.py
こんにちは Ada
سيبلّغ update --check عن الكتالوجات بأنها متأخرة
ذلك التعليق الواسم وأرقام الأسطر المتغيرة تكفي لأن يقول
pybabel update --check إن كتالوجاً يحتاج إلى إعادة توليد، لأنه يقارن
الإدخال كله لا الترجمة وحدها. شغّل pybabel update الحقيقي في الإيداع
نفسه الذي يحمل تغيير الشيفرة، وأودِع الكتالوجات معه — وهي العادة نفسها
التي تطلبها بوابة CI أصلاً.
من صيغة %: يتغير msgid، فتصير الترجمات fuzzy¶
تعيش صيغة printf داخل الرسالة، لذا فإن استبدالها يعيد كتابة مفتاح
الكتالوج. لا مفرّ من ذلك، وهو الثمن الصريح لترك %(name)s وراءك:
يتعرّف pybabel update على الرسالة الجديدة بوصفها قريبةً من الرسالة
المحذوفة، فينقل الترجمة القديمة إليها موسومةً بـfuzzy:
#. gettext-tstrings
#: app.py:4
#, fuzzy, python-brace-format, python-format
msgid "Hello {name}"
msgstr "こんにちは %(name)s"
ثلاثة أمور ينبغي معرفتها عن هذه الحالة:
- لا شيء ينكسر وقت التشغيل. تُستبعد إدخالات fuzzy من ملف
.moالمجمّع، فيعرض التطبيق رسالة المصدر إلى أن يؤكد إنسانٌ الزوج — التدهور نفسه الذي تمر به أي رسالة أُعيدت صياغتها. - تبقى CI خضراء ما دامت fuzzy. يتخطى مدقق العناصر النائبة إدخالات
fuzzy، تماماً كما يفعل
msgfmt --check-format، لأن إدخالاً لا يمكن أن يبلغ وقت التشغيل لا ينبغي أن يُفشل بناءً. وما إن يمسح مترجمٌ العلامة حتى يُفحص الإدخال كأي إدخال آخر — فتُلتقط عندئذ أي%(name)sبقيت في ترجمة مؤكدة، وهي اللحظة التي كانت ستبدأ فيها بالظهور لولا ذلك. - العلامة القديمة
python-formatتسافر معها، وينبغي حذفها مع علامةfuzzy، وإلا ظلmsgfmt --check-formatيطبّق قواعد printf على رسالة بصيغة الأقواس المعقوفة.
أما العناصر النائبة المسماة في printf فتعديلها آلي — يصير %(name)s هو
{name} ولا يتحرك شيء غير ذلك — فيصبح الكتالوج الكبير مروراً بسكربت تتبعه
مراجعة مترجم، لا إعادة ترجمة. أما %s الموضعي فليس آلياً: لا اسم له
لينتقل، واختيار اسم له هو مقصد التغيير نفسه.
ولذا يمكن للترحيل أن يمضي بالوتيرة التي تسمح بها المراجعة: فالإدخال الـfuzzy غير المحوَّل عملٌ ظاهر في الكتالوج، لا بناءٌ معطوب.
تعايش الاستدعاءات القديمة والجديدة¶
المستخرِج الذي يقرأ t-strings يقرأ أيضاً استدعاءات gettext المعتادة، فيغطي تعيينٌ واحد ملفاً في منتصف الترحيل:
from gettext_tstrings import tr
from myapp.i18n import _
name = "Ada"
print(_("Save changes"))
print(tr(t"Hello {name}"))
تصل الرسالتان إلى القالب نفسه، ولا يحمل التعليق الواسم الذي يشغّل الفحص الإضافي لهذه المكتبة إلا رسالة t-string:
#: app.py:5
msgid "Save changes"
msgstr ""
#. gettext-tstrings
#: app.py:6
#, python-brace-format
msgid "Hello {name}"
msgstr ""
وهو يتعرف على _()، وأسماء gettext القياسية الأربعة، والاسمين البديلين
tr() وntr()، والمؤجَّلين lazy_gettext() وlazy_pgettext(). أما دالة
مساعدة من عندك فيجب تسميتها في التعيين.
وفي وقت التشغيل يستقل الأسلوبان استقلالاً متساوياً: تعيد
gettext.translation() كائن ترجمات واحداً، ويقرأ منه كلٌّ من _ ومداخل
هذه المكتبة.
ما لا ينتقل¶
- لغات القوالب. يظل
{% trans %}في Jinja2، ووسوم قوالب Django، ومستخرِجاتها في Babel تعمل كما هي وتغذّي كتالوجات PO نفسها. فالـt-strings صيغةٌ في Python، وتنطبق على شيفرة Python. - ملفات كتالوجك. لا تغيير في الصيغة، ولا ملف جديد، ولا خطوة تحويل.
- منصة الترجمة لديك. تبادل
.poهو نفسه، والعلامةpython-brace-formatالتي تحملها رسالة t-string هي العلامة نفسها التي تحملها رسالة.format()— فيظل ضمان جودة العناصر النائبة يعمل. - الشيفرة غير المكتوبة بـPython. لا يتأثر كتالوج JavaScript أو C في المشروع نفسه.
قائمة تحقق للترحيل¶
- أضف الإضافة الاختيارية
babelحيث يعملpybabel، وغيّر تعيينpythonفيbabel.cfgإلى طريقةgettext_tstrings— فيغطي تعيين واحد عندئذ الأسلوبين معاً، ويظل-kيعمل للاستدعاءات المعتادة. - حوّل مواضع استدعاء
.format()أولاً. أعد الاستخراج، وشغّلpybabel update، وأودِع الكتالوجات مع الشيفرة؛ ولا تتوقع أي إدخالات fuzzy. - حوّل مواضع استدعاء صيغة
%على دفعات يمكنك أن تُراجَع، مع إعادة كتابة العناصر النائبة المنقولة ومسح علامتيfuzzyوpython-format. - أصلح ما يرفضه القيد: يجب أن يكون الاستيفاء اسماً بسيطاً، فيصير
t"Hello {user.name}"متغيراً محلياً أولاً. وهذا تعديل في موضع الاستدعاء، لا في الكتالوج. - فعّل
strict = trueفي تعيين المستخرِج متى اكتمل المسح، كي تُفشل الرسالة التي يتعذر استخراجها البناءَ بدلاً من أن تختفي من القالب. - أضف فحص وقت التشغيل من في الإنتاج: اعرض رسالة
واحدة لكل لغة مشحونة عبر
Translatorصارم.
الخطوتان 2 و3 إيداعان عاديان. ولا شيء في هذه القائمة يحتاج إلى يوم انتقال جماعي.