কনটেন্টে যান

সাধারণ ভুল

এই সাইট পঁয়ত্রিশটি ভাষায় অনূদিত, আর তার প্রতিটিই তৈরি হয়েছে এই ডকুমেন্টেশন যে লুপটি শেখায়, সেটি চালিয়ে। শিল্পের মানদণ্ডে এটি ছোট corpus, তবু i18n-কে দেখতে যতটা মনে হয় তার চেয়ে কঠিন করে তোলে এমন বেশিরভাগ ফাঁদে পড়ার পক্ষে সেটুকুই যথেষ্ট ছিল।

নিচের প্রতিটি বিভাগ এমন কিছু যা এখানে সত্যিই ভুল হয়েছিল — তখন সেটি দেখতে কেমন ছিল, আর লাইব্রেরি আপনার হয়ে যা যাচাই করে আর যা আপনার নিজের বিচারবুদ্ধির হাতেই থেকে যায়, তাদের মধ্যেকার সীমারেখাটি কোথায় পড়ে।

একটি ভেরিয়েবলের নাম বদলালে গোটা বাক্যটি আবার অনূদিত হয়

msgid-ই ক্যাটালগের কী, আর ইন্টারপোলেট করা নামটি থাকে তারই ভিতরে। একটি ধ্রুবককে মডিউল স্কোপে সরিয়ে Python-এর রীতি যেমন চায় তেমন বড় হাতের অক্ষরে লেখা — author থেকে AUTHORCopyright © 2026 {author} · MIT License-কে এমন একটি বার্তায় বদলে দিয়েছিল যা কোনও ক্যাটালগ কখনও দেখেইনি। ওই লাইনের প্রতিটি অনুবাদ, প্রতিটি ভাষায়, আবার fuzzy চক্রের মধ্য দিয়ে যেত — এমন একটি নাম-বদলের জন্য, যা কোনও পাঠকের চোখে পড়ার মতো কিছুই বদলায়নি।

লাইব্রেরি আপনাকে আটকাবে না: দুটি বানানই বৈধ placeholder নাম। সে যা করে তা হল নামটিকে রক্ষা করার যোগ্য করে তোলা — একটি ইন্টারপোলেশনকে সরল নাম হতেই হবে, তাই ক্যাটালগ কী-এর ভিতরে যা বসে তা একটি শব্দ, যা একজন অনুবাদক পড়তে পারেন, কোনও এক্সপ্রেশন নয়।

উল্টো ক্ষেত্রটি রচনার গুণেই নিরাপদ। কনভার্শন ও ফরম্যাট স্পেক msgid-এর অংশ নয়, তাই {amount:,.2f}-কে আঁটসাঁট করে {amount:,.0f} করলে কোনও কী বদলায় না আর কোথাওই কোনও অনুবাদ বাতিল হয় না।

nplurals=2 মানে দুটি আলাদা স্ট্রিং নয়

তুর্কি, হাঙ্গেরীয়, ফারসি ও বাংলা — চারটি ভাষাই দুটি বহুবচন রূপ ঘোষণা করে, আর চারটিতেই গণনাযুক্ত বার্তার দুটি রূপ বৈধভাবেই একই স্ট্রিং — সংখ্যার পরে বিশেষ্য একবচনেই থাকে, তাই {n} sayfa এক পৃষ্ঠার জন্যও ঠিক, দশের জন্যও। যে পর্যালোচক এই পুনরাবৃত্তিটি "সারিয়ে" দেন, তিনি অনুবাদটি ভেঙে ফেলেন।

উল্টো ভুলটিও ঠিক ততটাই সহজ। লাতভীয়ের তৃতীয় রূপটি আছে কেবল শূন্যের জন্য; স্লোভেনীয়ের দ্বিতীয়টি একটি দ্বিবচন, ঠিক দুইয়ের জন্য; রোমানীয়ের শেষ রূপটির দরকার de শব্দটি, যা তার প্রথম দুটিতে থাকা চলবেই না। ওই খোপগুলিকে একটি একবচন আর একটি বহুবচন দিয়ে ভরে দিলে এমন ক্যাটালগ তৈরি হয় যা কেবল সেইসব গণনার জন্যই ভুল, যেগুলি কেউ পরীক্ষা করে না।

আরও খারাপ: খোপগুলির ক্রম অর্থবহ নয়। ওয়েলশ তার পাঁচটি রূপকে এমনভাবে সূচিবদ্ধ করে যে msgstr[0] সাধারণ ক্ষেত্র আর msgstr[1] একবচন। স্পষ্ট বলে মনে হওয়া ক্রমে সেগুলি ভরে দিলে একবচনটি ঠিক সেখানেই গিয়ে বসে, যেখানে প্রতিটি গণনাহীন বার্তা তাকে খুঁজে পাবে।

লাইব্রেরি এর কিছুই নিজের কাঁধে নেয় না, আর সেটাই মূল কথা: লক্ষ্য ভাষার বহুবচন নিয়ম থাকে তার নিজেরই ক্যাটালগ হেডারে, আর union/intersection নিয়ম কোনও অনুবাদকে উৎসের চেয়ে বেশি রূপ রাখতে দেয়, বা কম। ভাষাটি না জেনে যা যাচাই করা সম্ভব সে কেবল সেটুকুই যাচাই করে — প্রতিটি রূপ তার দরকারি placeholder-গুলি ধরে রেখেছে কি না।

দুটি রূপ একটি কারণেই এক হতে পারে

আইরিশে পাঁচটি বহুবচন রূপ আছে, আর এই সাইটের বিল্ড রিপোর্টে তাদের কয়েকটির বানান একই। এটি copy-paste-এর ভুল নয়: leathanach শুরু হয় l দিয়ে, আর আইরিশ সংখ্যাবাচক শব্দ যে দুটি আদি-বিকার ঘটায়, তার একটিও l-এর উপর লেখা হয় না। রূপগুলি তবু সত্যিকারের কাজ করে — ধাতুটি leathanachleathanaigh-এর মধ্যে বদলায়, আর দশের উপরের গণনা একবচনে ফিরে আসে — কিন্তু "পৃষ্ঠা" অর্থের কোনও বিশেষ্যই এই পার্থক্যটি দেখাত না।

যে কোনও যাচাই যা একরকম রূপকে সন্দেহজনক বলে চিহ্নিত করে, সে সঠিক আইরিশকেও চিহ্নিত করবে। এর একমাত্র পর্যালোচক ভাষাটি জানেন এমন একজন মানুষ।

একটি বার্তা কেবল একটি গণনার সঙ্গেই মিলতে পারে

এই সাইটের বিল্ড রিপোর্ট বলে কতগুলি পৃষ্ঠা রেন্ডার হয়েছে আর তাতে কত সময় লেগেছে। সেটিকে "Rendered {n} pages in {seconds} seconds" হিসেবে লেখা নিরীহ দেখায় আর অনুবাদযোগ্য নয়: gettext একটি গণনা থেকে একটি রূপ বাছে, আর সেই গণনাটি nseconds শব্দটিকে এমন একটি সংখ্যার সঙ্গে মিলতে হত, বহুবচনের যন্ত্রপাতি যাকে কখনও দেখতেই পায় না।

সমাধান হল দ্বিতীয় পরিমাণটিকে শব্দ না করে একক-প্রতীক করা, আর একক-প্রতীকগুলি নিজেরাই স্থানীয়কৃত: এই সাইটের ক্যাটালগগুলি বহন করে s, с, ث, שנ׳mp, আর ফরাসি, স্প্যানিশ ও সুইডিশ টাইপোগ্রাফি প্রতীকটির আগে একটি স্পেস চায়, যেখানে ইংরেজি চায় না। এর কিছুই লাইব্রেরির এক্তিয়ার নয় — কিন্তু একটি বার্তার দুটি মিলের দরকার, এটুকু লক্ষ করা নিশ্চয়ই তার এক্তিয়ার, আর তার একমাত্র হাতিয়ার বার্তাটিকে অন্যভাবে লেখা।

একটি ইংরেজি বাক্য সম্পাদনা করা মানে বিদেশি ব্যাকরণ সম্পাদনা করা

হোম পেজে আগে লেখা ছিল "all ten language editions"। সংখ্যাটি সরিয়ে দেওয়া — ইংরেজিতে এক শব্দের সম্পাদনা, করা হয়েছিল কারণ সংখ্যাটি বারবার পুরনো হয়ে যাচ্ছিল — একটি বহুবচন কর্তাকে একবচন করে দিল। স্প্যানিশ, ইতালীয়, পর্তুগিজ, রুশ, ইউক্রেনীয়, গ্রিক, ওলন্দাজ ও হিব্রু — সবাইকেই ক্রিয়ার মিলটি নতুন করে বসাতে হল; কয়েকটিতে কৃদন্তও বদলাতে হল।

ইংরেজিতে যে উৎস-সম্পাদনা তুচ্ছ বলে পড়া যায়, ভাটিতে তা তুচ্ছ নয়। তাকে fuzzy চিহ্নিত করা — pybabel update যা করেই থাকে — সেই যন্ত্রটিই প্রতিটি অনুবাদককে বিষয়টি লক্ষ করার সুযোগ দেয়।

অদৃশ্য পার্থক্য প্রতিটি copy-paste পেরিয়ে টিকে যায়

গাইড এমন একটি ডায়াগনস্টিক উদ্ধৃত করে যাতে আছে (nаme) — একটি ইচ্ছাকৃত escape, কারণ সে যে অক্ষরটির নাম নিচ্ছে সেটি একটি সিরিলিক а, যাকে কোনও পাঠকই লাতিনটির থেকে আলাদা করতে পারেন না। এই সাইটের অনুবাদকেরা ওই escape-কে আসল অক্ষরে বদলে ফেলেছেন পাঁচটি পৃথক বার, পাঁচটি ভিন্ন ভাষায়, আর প্রতিবারই তৈরি হয়েছে এমন একটি পৃষ্ঠা যা দেখতে সঠিক ছিল আর ছিল ভুল।

এটি লাইব্রেরি সত্যিই ধরে ফেলে, আর ডায়াগনস্টিকগুলি যে আকৃতির সেই আকৃতিরই হওয়ার কারণও এটাই: যে placeholder-এর অক্ষরগুলি লিখনপদ্ধতি মিশিয়ে ফেলে, তাকে দুবার রিপোর্ট করা হয়, একবার পাঠযোগ্যভাবে আর একবার escape করে, কারণ escape করা রূপটিই একমাত্র বানান যা দুটিকে আলাদা করে চেনায়। বন্ধনীর ভিতরে একটি no-break space একই কারণে code point হিসেবে ছাপা হয়। ক্যাটালগ চেকার বার্তাটিকে শিপ হওয়ার আগেই প্রত্যাখ্যান করে।

খালি না হওয়া মানে অনূদিত হওয়া নয়

যে ক্যাটালগের msgid-গুলিই msgstr-এ কপি করে খাড়া করা হয়েছে, সে প্রতিটি সরল যাচাই পাশ করে যায়: কিছুই খালি নয়, কিছুই fuzzy নয়, বার্তার সেট হুবহু মেলে। এই সাইটের একটি সংস্করণ কয়েক ঘণ্টা ওভাবেই শিপ হয়ে ছিল। আরেকটি সংস্করণের আটটি পৃষ্ঠাও তাই ছিল, যেগুলি ছিল ইংরেজি উৎসের বাইট-দর-বাইট নকল — আর সেগুলি দুইয়ের মধ্যে code block তুলনা করা যাচাই পাশ করে যায়, কারণ তারা একই ফাইল।

এর কোনওটিই এমন কিছু নয় যা কোনও অনুবাদ লাইব্রেরি দেখতে পায়। দুটিই পরীক্ষা করা সস্তা, তবে প্রতিটি এন্ট্রি তার উৎস থেকে আলাদা হতেই হবে — এমন দাবি করে নয়: OK, পণ্যের নাম, ব্যক্তিনাম, সংক্ষিপ্ত রূপ আর কোড আইডেন্টিফায়ার সবই নিজেরই অনুবাদ হয়, আর যে যাচাই তা নিষিদ্ধ করে সে চিরকাল মিথ্যা সতর্কতা তৈরি করতে থাকবে।

বরং গোটা একটি ক্যাটালগ বা গোটা একটি পৃষ্ঠার উপর হারটি মাপুন, আর বাইরে পড়া মানগুলি কোনও মানুষের কাছে পাঠান। এই সাইটের নিজের টেস্ট ঠিক তা-ই করে — এটি প্রতিটি সংস্করণের গদ্য লাইনগুলিকে ইংরেজি উৎসের সঙ্গে তুলনা করে আর 25% অভিন্নতার উপরে গেলে ফেল করে। জাল সংস্করণটি ছিল 87%-এ; প্রতিটি খাঁটি অনুবাদ থাকে 4% থেকে 8%-এর মধ্যে, যা সেই ছোট লেজটুকু যে লাইনগুলি বৈধভাবেই মিলে যায়, যেমন URL আর উদ্ধৃত প্রোগ্রাম আউটপুট। জনসংখ্যা দুটি যথেষ্ট দূরে, তাই থ্রেশহোল্ডটির নিখুঁত হওয়ার দরকার নেই।

ক্যাটালগই একমাত্র অনূদিত জিনিস নয়

এখানকার দুটি ব্যর্থতার সঙ্গে gettext-এর কোনও সম্পর্কই ছিল না।

একটি শিরোনাম অনুবাদ করলে তার থেকে তৈরি হওয়া anchor বদলে যায়, তাই ওই বিভাগে ঢোকা প্রতিটি আন্তঃপৃষ্ঠা লিঙ্ক ভেঙে পড়ে — নীরবে, কেবল সেই ভাষাতেই। এই সাইট প্রতিটি শিরোনামে ইংরেজি anchor-টি গেঁথে দেয়, আর একটি টেস্ট প্রত্যাশিত তালিকাটি ইংরেজি পৃষ্ঠা থেকে বের করে নেয়।

আর সাইট জেনারেটর আটষট্টিটি ভাষার জন্য ইন্টারফেস অনুবাদ পাঠায়, যার মধ্যে সোয়াহিলি বা আইরিশ নেই। সেটি না থাকলে বিল্ড ইংরেজিতে নেমে আসে না; টেমপ্লেটের include ব্যর্থ হয় আর সংস্করণটি আদৌ তৈরিই করা যায় না। এই রিপোজিটরির নিজের দুটি ফাইল আছেই ওই ফাঁকটুকু ভরাট করার জন্য।

আপনার টুলগুলিতেও বাগ থাকে

বাসি ক্যাটালগ ধরার জন্য এই ডকুমেন্টেশন যে CI ধাপটির সুপারিশ করে, pybabel update --check, সে pgettext বা npgettext ব্যবহার করে এমন কোনও প্রকল্পের জন্য ওই কাজটি করতে পারে না। Babel 2.18.0-এ সে msgctxt আছে এমন প্রতিটি ক্যাটালগকেই, প্রতিবার চলার সময়, পুরনো বলে রিপোর্ট করে। তুলনাটি চলে Catalog.is_identical-এর মধ্য দিয়ে, যে প্রতিটি বার্তা খোঁজে যে কী-এর নিচে সে সংরক্ষিত সেটি ধরে — আর context-যুক্ত বার্তার ক্ষেত্রে সেই কী হল (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

এটি এখানেই ধরা পড়েছে, তাকে ব্যবহার করতে গিয়ে; upstream-এ রিপোর্ট করা হয়েছে, আর তার বদলি যাচাইটি রয়েছে প্রোডাকশন পৃষ্ঠায়

সাধারণ শিক্ষাটি অস্বস্তিকর: যে গেট সর্বদা লাল, সে কোনও গেট না থাকার চেয়েও খারাপ, কারণ দল সেটিকে বন্ধ করে দেয়। আপনার CI যাচাইটির উপর ব্যর্থ হওয়ার ভরসা রাখার আগে নিশ্চিত হয়ে নিন যে সে সত্যিই পাশও করতে পারে।

লাইব্রেরিটি কীসের জন্য, এক লাইনে

এই পৃষ্ঠার বেশিরভাগটাই এমন বিচারবুদ্ধি, যা কোনও টুল নিজের কাঁধে নিতে পারে না। একটি টুল যা পারে, তা হল এই নিশ্চয়তা দেওয়া যে কোনও অনুবাদ সে যে বাক্যটি অনুবাদ করছে তার কাঠামো বদলাতে পারবে না — কোনও মান ফেলে দিতে, বানিয়ে নিতে, নতুন করে ফরম্যাট করতে বা আপনার অবজেক্টের ভিতরে হাত বাড়াতে পারবে না — আর সে কথাটি এমন একটি বাক্যে বলতে পারা, যার উপর ভিত্তি করে যাঁকে এটি সারাতে হবে তিনি কাজ করতে পারেন। এই লাইব্রেরি যা প্রতিশ্রুতি দেয় তার পুরোটাই এটুকু, আর এই সাইটের বাকি অংশ হল সে কীভাবে তা রক্ষা করে।