انتقل إلى المحتوى

الترحيل

إن كان مشروعك يستخدم 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}")

ولذا تبقى الترجمة الموجودة مرتبطة بالرسالة. انطلاقاً من كتالوج يحتوي على

#: app.py:6
#, python-brace-format
msgid "Hello {name}"
msgstr "こんにちは {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، ورقم سطر في المصدر:

#. gettext-tstrings
#: app.py:4
#, python-brace-format
msgid "Hello {name}"
msgstr "こんにちは {name}"

لا علامة 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 وراءك:

# Before
_("Hello %(name)s") % {"name": name}

# After — a different msgid
tr(t"Hello {name}")

يتعرّف 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 المعتادة، فيغطي تعيينٌ واحد ملفاً في منتصف الترحيل:

[gettext_tstrings: **.py]
encoding = utf-8
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 في المشروع نفسه.

قائمة تحقق للترحيل

  1. أضف الإضافة الاختيارية babel حيث يعمل pybabel، وغيّر تعيين python في babel.cfg إلى طريقة gettext_tstrings — فيغطي تعيين واحد عندئذ الأسلوبين معاً، ويظل -k يعمل للاستدعاءات المعتادة.
  2. حوّل مواضع استدعاء .format() أولاً. أعد الاستخراج، وشغّل pybabel update، وأودِع الكتالوجات مع الشيفرة؛ ولا تتوقع أي إدخالات fuzzy.
  3. حوّل مواضع استدعاء صيغة % على دفعات يمكنك أن تُراجَع، مع إعادة كتابة العناصر النائبة المنقولة ومسح علامتي fuzzy وpython-format.
  4. أصلح ما يرفضه القيد: يجب أن يكون الاستيفاء اسماً بسيطاً، فيصير t"Hello {user.name}" متغيراً محلياً أولاً. وهذا تعديل في موضع الاستدعاء، لا في الكتالوج.
  5. فعّل strict = true في تعيين المستخرِج متى اكتمل المسح، كي تُفشل الرسالة التي يتعذر استخراجها البناءَ بدلاً من أن تختفي من القالب.
  6. أضف فحص وقت التشغيل من في الإنتاج: اعرض رسالة واحدة لكل لغة مشحونة عبر Translator صارم.

الخطوتان 2 و3 إيداعان عاديان. ولا شيء في هذه القائمة يحتاج إلى يوم انتقال جماعي.