Skip to content

Tutorial 5 Localization bn

James Morris edited this page Jul 29, 2026 · 1 revision

টিউটোরিয়াল ৫ · লোকালাইজেশন (i18n)

লক্ষ্য: বুঝুন LockedIn CLI কীভাবে ৩৩টি ভাষায় কথা বলে — এবং একটি এজেন্টকে দিয়ে আরেকটি ভাষা যোগ করানোর অনুশীলন করুন। লোকালাইজেশন একটি দুর্দান্ত এজেন্ট টাস্ক: এটি delegate করার মতো যথেষ্ট যান্ত্রিক, কিন্তু এর সত্যিকারের constraint আছে (একটি test গেট, layout নিয়ম, ব্যাকরণ review) যা আপনাকে review করতে শেখায়।

← পূর্ববর্তী: টিউটোরিয়াল ৪ প্রম্পটিং ও রিভিউ · হোম-এ ফিরুন


এখানে "localized" মানে কী

CLI-কে স্প্যানিশ, হিন্দি, জাপানি, সরলীকৃত চীনা, বা যেকোনো shipped ভাষায় চালান এবং সবকিছু বদলায় — splash, help table, প্রতিটি কমান্ডের output, chat session, এমনকি legal fine print। শুধু রসিকতা নয়: গোটা দৃশ্যমান পৃষ্ঠতল।

lockedin --lang bn post
LOCKEDIN_LANG=hi lockedin
lockedin --lang zh aura

ভাষা startup-এ autodetect হয়, অগ্রাধিকার ক্রমে সমাধান করা হয়:

  1. --lang flag (--lang bn, --lang=fr, -l ja)
  2. LOCKEDIN_LANG environment variable
  3. আপনার locale (LC_ALL / LC_MESSAGES / LANG, তারপর OS/runtime locale)
  4. ইংরেজি, fallback হিসেবে

normalizeLang() সাধারণত locale-এর প্রাথমিক subtag ব্যবহার করে। এর মানে de-DE de বেছে নেয়, কিন্তু tlh ভুলবশত tl হয়ে যায় না; আসল alias filtgl ইচ্ছাকৃতভাবে Tagalog (tl)-এ map করে, নরওয়েজিয়ান nbnn no-তে map করে, legacy Indonesian in id-তে map করে, এবং legacy Hebrew iw he-তে map করে। দুটি regional code তাদের প্রাথমিক subtag-এ ভাঁজ না করে হুবহু সংরক্ষিত থাকে: pt-BR/pt_BR canonical regional code বেছে নেয় যখন generic pt backward-compatible Brazilian Portuguese bundle থাকে (দুটিই Brazilian Portuguese এবং একই pool/UI শেয়ার করে), এবং en-SG/en_SG Singlish রাখে (generic en ইংরেজি থাকে)। Hong Kong Traditional Chinese একই ধরনের ব্যতিক্রম: zh-HK, zh_HK.UTF-8, এবং zh-Hant-HK zh-HK বেছে নেয়, যখন generic zh ও mainland tag সরলীকৃত চীনা (zh) বেছে নেয়।

মাঝপথে সুইচ — /language panel। CLI সবসময় আরেকটি ভাষায় শুরু করতে পারত (--lang, LOCKEDIN_LANG); এখন আপনি session-এর মাঝে সুইচ করতে পারেন। /language টাইপ করুন (alias /lang/languages) সব ৩৩টি ভাষা code অনুযায়ী তালিকা করতে, প্রতিটি নিজের script-এ; /language el বাকি session-এর জন্য সুইচ করে। মূল কথা হলো escape hatch: সুইচের পর এটি নতুন ভাষায় নতুন করে আঁকে এবং তারপর, আপনি যে ভাষা এইমাত্র ছাড়লেন সেই ভাষায়, ঠিক ফেরার পথ print করে — এখন /language en, পরের বার lockedin --lang en — যাতে ভুল করে 日本語 বা ಕನ್ನಡ-তে গিয়ে পড়লেও আপনি কখনো আটকে না যান। (দুবার সুইচ করুন এবং আপনার LOCKEDIN_LANG যে ভাষার নাম বলে সেটিও প্রস্তাব করা হয়।) --langLOCKEDIN_LANG অপরিবর্তিত। /a11y-র মতো, এটি একটি আন্তরিক utility, স্যাটায়ারের অংশ নয়।

ধারণা: language bundle

সমস্ত translatable text থাকে bundle-এ, প্রতি ভাষায় একটি, প্রতিটির আকার এমন:

{ meta: { lang: 'bn', name: 'বাংলা', dir: 'ltr' },
  pools: { HOOKS: [ /* ~25 */ ], LESSONS: [ /* ... */ ], /* ... */ },
  ui:    { buzzwordDensity: 'Buzzword ঘনত্ব: ', /* labels, headings */ } }
  • pools হলো content array (রসিকতা) যা আপনি অধ্যায় ২-এ পেয়েছেন।
  • ui হলো chrome string: label, heading, এবং ছোট template।

ইংরেজি হলো src/lockedin.js-এর ভেতরের reference bundle; বাকি ৩২টি bundle module থাকে src/content/*.js-এ: ar, bn, bo, de, el, en-SG, es, eu, fa, fi, fr, he, hi, id, is, it, ja, kn, ms, nl, no, pl, pt, pt-BR, ru, sv, tl, tr, uk, ur, zh, এবং zh-HK (pt-BR pt-র pool/UI পুনর্ব্যবহার করে কিন্তু আলাদাভাবে নিবন্ধিত)। প্রতিটি BUNDLES-এ নিবন্ধিত; SUPPORTED_LANGS ওই key থেকে তৈরি, এবং renderHelp() সেই তৈরি করা code তালিকা print করে। কোনো UI bundle তালিকাটি hard-code করে না।

setLang('fr');       // active ভাষাকে French bundle-এ নির্দেশ করুন
// L = সক্রিয় pools, U = সক্রিয় ui
pick(L.HOOKS)        // একটি French hook
U.buzzwordDensity    // "Densité de jargon : "

যেহেতু প্রতিটি renderer LU পড়ে (কখনো hardcode করা string নয়), শুধু setLang-ই গোটা অভিজ্ঞতা বদলে দেয়। এটাই পুরো কৌশল।

নিরাপত্তা জাল: key parity

এই invariant-ই একটি ভাষা যোগ করা নিরাপদ করে:

প্রতিটি bundle-কে ইংরেজির ঠিক একই poolsui key প্রকাশ করতে হবে।

একটি test এটি সব ৩৩টি bundle জুড়ে প্রয়োগ করে। আপনি যদি ইংরেজিতে একটি নতুন UI string যোগ করেন এবং ইউক্রেনীয়তে অনুবাদ করতে ভুলে যান, npm test লাল হয় এবং বলে কোন key নেই। আপনি নীরবে একটি অর্ধ-অনূদিত ভাষা ship করতে পারবেন না।

কঠিন অংশ: terminal layout

ভাষা terminal layout-কে বিভিন্নভাবে চাপে ফেলে:

  • জাপানি, সরলীকৃত চীনা, এবং Hong Kong Traditional Chinese East Asian Wide / Fullwidth অক্ষর ব্যবহার করে। vw() সেগুলোকে দুই column গণনা করে, এবং wrap() লম্বা space-হীন token hard-break করে যাতে CJK text card ও box-এর ভেতরে থাকে।
  • হিন্দি ও কন্নড় nonspacing/enclosing combining mark (Mn / Me) ব্যবহার করে, যেমন মাত্রা ও বিরাম। vw() সেগুলোকে শূন্য column গণনা করে যাতে সেগুলো মাপা width স্ফীত না করে।
  • box() প্রতিটি body line pad করার আগে wrap করে, যাতে একটি লম্বা অনূদিত banner আর border ভেদ করতে না পারে।
  • প্রতিটি bundle sentenceEndlistSep সেট করে (উদাহরণস্বরূপ . / , , / ) যাতে generator-রচিত বাক্য স্বাভাবিকভাবে পড়ে।

আপনি যখন একটি ভাষা যোগ করেন, card header string (cardSubtitle, cardMeta, cardFooter)-কে এখনো ≤ ৬০ visible column-এ ফিট করতে হবে। আরবি, ফারসি, হিব্রু, ও উর্দু meta.dir: 'rtl' সেট করে। Output-এ default-এ কোনো bidi control থাকে না, কারণ কিছু terminal সেগুলোকে boxed label হিসেবে render করে। LOCKEDIN_BIDI=on স্পষ্টভাবে wrapping-এর পরে balanced isolate সক্রিয় করে সেই terminal-এর জন্য যেগুলো সেগুলো সমর্থন করে জানা, ANSI, ASCII command, এবং logical copy/paste ক্রম সংরক্ষণ করে। Accessible output সবসময় ওই control সরিয়ে দেয়। opt-in ছাড়া, মিশ্র RTL/LTR ক্রম কম পরিশীলিত হতে পারে; কখনো support probe বা infer করবেন না।

গোপন অংশ: raw user input-এর চারপাশে ব্যাকরণ

কিছু UI template {cap}-এর মতো placeholder দিয়ে raw user clause splice করে। সেগুলো slot-for-slot অনুবাদ করবেন না। placeholder যখন ব্যবহারকারীর টাইপ করা একটি phrase হয়, পরিপাটি বিশেষ্য নয়, তখনও বাক্যটি ব্যাকরণসম্মত থাকতে হবে।

একটি সত্যিকারের সাবধানবাণী bug: জাপানি template যেগুলো {cap}-এর ঠিক পরে বসায়, {cap} যখন একটি পূর্ণ clause হয় তখন ভুল শোনাতে পারে। সমাধান "আরও কঠিনভাবে অনুবাদ করা" নয়; সমাধান হলো template-টি পুনর্গঠন করা (উদাহরণস্বরূপ, একটি nominalizer যোগ করা বা placeholder সরানো) যাতে যেকোনো user input এখনো মানানসই হয়।

✅ আপনার এজেন্ট দিয়ে করে দেখুন — একটি ভাষা যোগ করুন

এই অনুশীলন ঠিক একইভাবে কাজ করে। এমন একটি ভাষা বেছে নিন যা আপনি sanity-check করতে পারেন (বা এজেন্টকে করতে বলুন), এবং এটি শুরু থেকে শেষ পর্যন্ত চালান। প্রথমে spec লিখুন:

ড্যানিশ (da) যোগ করুন। ইংরেজির একই key সহ src/content/da.js-কে একটি { meta, pools, ui } bundle হিসেবে তৈরি করুন, প্রতিটি entry অনুবাদ করে (content pool প্রতিটি ~২৫টি করে, সব UI string)। src/lockedin.js-এ BUNDLES-এ da নিবন্ধন করুন। --lang da এবং একটি da-* locale-কে এটি বেছে নিতে হবে। card header string width সীমার মধ্যে রাখুন। npm test সবুজ থাকতে হবে, এবং বিদ্যমান localized-গুলোর প্রতিফলন করে ড্যানিশ invariant + detection test যোগ করুন।

তারপর অধ্যায় ৩–৪-এর loop চালান:

  1. আগে plan। "কোড লেখার আগে, বলো কোন ফাইলগুলো বদলাবে এবং ইংরেজির সঙ্গে key parity কীভাবে রাখবে।"
  2. আগে test। "failing test যোগ করো: da detection, da-র জন্য key parity, এবং একটি ড্যানিশ reflect/connect invariant। bundle-টা এখনো তৈরি করো না।"
  3. Implement। "এবার একটি বিদ্যমান bundle key ধরে ধরে অনুবাদ করে src/content/da.js তৈরি করো, নিবন্ধন করো, এবং test পাস করাও। randomness-এর জন্য শুধু pick/shuffle।"
  4. গেট + review। npm test, তারপর lockedin --lang da post — এবং diff-টা পড়ো: প্রতিটি key কি অনূদিত হয়েছে? card border কি এখনো সারিবদ্ধ? {cap} সহ template কি raw user clause-এ টিকে থাকে?

একটি গোটা ভাষা যদি বেশি হয়, ছোট warm-up অনুশীলন:

  • "সব ৩৩টি language bundle-এ আরেকটি TAGLINE যোগ করো, count সমান রেখে।"
  • "কন্নড় cardFooter কি ≤ ৬০ visible column তা পরীক্ষা করো এবং ব্যাখ্যা করো combining mark কীভাবে মাপা হয়েছিল।"
  • "ja.js থেকে একটি ui key মুছলে যে test fail হবে সেটি দেখাও।"

এরপর কোথায়

  • src/content/es.js চোখ বুলান — এটি এখনো একটি নতুন bundle-এর জন্য বন্ধুত্বপূর্ণ template।
  • docs/HANDOFF.md → "Adding a language" আবার পড়ুন।
  • কমান্ড রেফারেন্স-এর বহুভাষিক রসিকতা উপভোগ করুন।

এটাই সম্পূর্ণ টিউটোরিয়াল। আপনি এখন একটি AI এজেন্টকে দিয়ে feature তৈরি এবং সেগুলো একটি test গেটের পেছনে localize করাতে নির্দেশ দিতে পারেন — ৩৩টি ভাষায় এবং আরও কিছুর জন্য প্রস্তুত। Agree? 👇

📘 LockedIn CLI wiki

Tutorial

Reference


Satire · Sátira · 風刺. Not affiliated with LinkedIn. GPL-3.0-or-later.

Clone this wiki locally