মাইগ্রেশন¶
আপনার প্রকল্প আগে থেকেই gettext ব্যবহার করলে এই লাইব্রেরি গ্রহণযোগ্য কি না তা যে প্রশ্নগুলি ঠিক করে দেয় সেগুলি সংকীর্ণ: আপনার হাতে থাকা ক্যাটালগ কি এতে বাতিল হয়ে যায়, আপনি যে কোড এখনও বদলাতে প্রস্তুত নন তার পাশে কি সে থাকতে পারে, আর এই সরে আসার কতটা একসঙ্গে ঘটতেই হবে। উত্তরগুলি, সংক্ষিপ্ততমটি আগে:
| প্রশ্ন | উত্তর |
|---|---|
বিদ্যমান .po ও .mo ফাইল কি এখনও কাজ করে? |
হ্যাঁ। একই ফাইল, একই টুল। |
| পুরনো ও নতুন কল কি এক ফাইলে থাকতে পারে? | হ্যাঁ, আর একটিমাত্র এক্সট্র্যাক্টর ম্যাপিংই দুটিকে ঢেকে দেয়। |
| msgid কি বদলায়? | .format() থেকে নয়। %-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}")
তাই বিদ্যমান অনুবাদটি জোড়া লেগেই থাকে। এমন একটি ক্যাটালগ থেকে শুরু করুন যাতে আছে
কলটি বদলান, আবার এক্সট্র্যাক্ট করুন, আর update চালান:
$ 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 গেট আগে থেকেই চেয়ে আসছে।
%-format থেকে: 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থেকে বাদ পড়ে, তাই কোনও মানুষ জোড়াটি নিশ্চিত না করা পর্যন্ত অ্যাপ্লিকেশন উৎস বার্তাটিই রেন্ডার করে — নতুন করে লেখা যেকোনও বার্তা ঠিক এই অবনমনের মধ্য দিয়েই যায়। - fuzzy থাকা অবস্থায় CI সবুজ থাকে। Placeholder চেকার fuzzy এন্ট্রি বাদ
দিয়ে যায়, ঠিক যেমন
msgfmt --check-formatকরে, কারণ যে এন্ট্রি রানটাইমে পৌঁছতেই পারে না তার কোনও বিল্ড ফেলে দেওয়া উচিত নয়। কোনও অনুবাদক ফ্ল্যাগটি সরানোমাত্রই এন্ট্রিটি আর সবার মতোই যাচাই হয় — তাই নিশ্চিত করা কোনও অনুবাদে রয়ে যাওয়া%(name)sতখনই ধরা পড়ে, অর্থাৎ ঠিক যে মুহূর্তে সে না হলে রেন্ডার হতে শুরু করত। - পুরনো
python-formatফ্ল্যাগটি সঙ্গে চলে আসে, আর তাকেfuzzyফ্ল্যাগের সঙ্গেই মুছে ফেলা উচিত, নইলেmsgfmt --check-formatএকটি brace-format বার্তায় printf নিয়ম প্রয়োগ করতেই থাকবে।
নাম-যুক্ত printf placeholder-এর ক্ষেত্রে সম্পাদনাটি যান্ত্রিক — %(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() উপনাম, আর deferred
lazy_gettext() / lazy_pgettext()। আপনার নিজের কোনও helper-কে
ম্যাপিংয়ে নাম দিয়ে জানাতে হবে।
রানটাইমে দুটি শৈলী সমান স্বাধীন: gettext.translation() একটিমাত্র translations
অবজেক্ট ফেরায়, আর _ ও এই লাইব্রেরির এন্ট্রি পয়েন্ট — দুই-ই তার থেকেই পড়ে।
যা সরে না¶
- টেমপ্লেট ভাষা। Jinja2-এর
{% trans %}, Django-র টেমপ্লেট ট্যাগ আর তাদের Babel এক্সট্র্যাক্টর অবিকল আগের মতোই চলতে থাকে আর একই PO ক্যাটালগেই খোরাক জোগায়। t-strings হল Python সিনট্যাক্স; তারা Python সোর্সেই প্রযোজ্য। - আপনার ক্যাটালগ ফাইল। ফরম্যাট বদল নেই, নতুন ফাইল নেই, রূপান্তরের ধাপ নেই।
- আপনার অনুবাদ প্ল্যাটফর্ম।
.poবিনিময় অবিকল একই, আর একটি t-string বার্তা যেpython-brace-formatফ্ল্যাগ বয়ে আনে সেটি একটি.format()বার্তার ফ্ল্যাগটিই — তাই placeholder QA আগের মতোই কাজ করে। - Python নয় এমন কোড। একই প্রকল্পে থাকা কোনও JavaScript বা C ক্যাটালগ অপ্রভাবিত।
মাইগ্রেশনের একটি চেকলিস্ট¶
- যেখানে
pybabelচলে সেখানেbabelextra যোগ করুন, আরbabel.cfg-এরpythonম্যাপিংটিgettext_tstringsপদ্ধতিতে বদলে দিন — তখন একটিমাত্র ম্যাপিংই দুই শৈলী ঢেকে দেয়, আর সাধারণ কলগুলির জন্য-kআগের মতোই চলে। - আগে
.format()কল-সাইটগুলি রূপান্তর করুন। আবার এক্সট্র্যাক্ট করুন,pybabel updateচালান, আর ক্যাটালগগুলি কোডের সঙ্গেই কমিট করুন; কোনও fuzzy এন্ট্রির প্রত্যাশা করবেন না। %-format কল-সাইটগুলি এমন ব্যাচে রূপান্তর করুন যাদের পর্যালোচনা করানো সম্ভব, বয়ে আসা placeholder-গুলি নতুন করে লিখে আরfuzzyওpython-formatফ্ল্যাগ মুছে দিয়ে।- সীমাবদ্ধতাটি যা প্রত্যাখ্যান করে তা সারিয়ে নিন: একটি ইন্টারপোলেশনকে সরল
নাম হতেই হবে, তাই
t"Hello {user.name}"আগে একটি local ভেরিয়েবল হয়ে যায়। এটি কল-সাইটের সম্পাদনা, ক্যাটালগের নয়। - ঝাড়াই-পর্ব শেষ হলে এক্সট্র্যাক্টর ম্যাপিংয়ে
strict = trueচালু করুন, যাতে যে বার্তা এক্সট্র্যাক্ট করা যায় না সে টেমপ্লেট থেকে উধাও না হয়ে বিল্ড ফেলে দেয়। - প্রোডাকশনে থেকে রানটাইম যাচাইটি যোগ করুন:
পাঠানো প্রতিটি ভাষায় একটি করে বার্তা একটি কড়া
Translator-এর মধ্য দিয়ে রেন্ডার করুন।
২ ও ৩ নম্বর ধাপ সাধারণ কমিট। এই তালিকার কোনও কিছুরই একটি "flag day" লাগে না।