সরাসরি পর্বে যান
পর্ব ১৪ · ৫৯-এর মধ্যে · মডিউল ২

ওয়েবে বাংলা — UTF-8, lang, বাংলা ফন্ট আর টাইপোগ্রাফি

Bangla on the Web — UTF-8, Mojibake, lang, Conjuncts & ZWNJ/ZWJ, Bangla Web Fonts, Digits, Search & URLs

Taught in Bangla, this lesson covers Bangla on the web: how characters become Unicode code points and UTF-8 bytes, diagnosing mojibake, question marks and empty boxes, and setting lang and dir. It explains conjuncts, hasanta, ZWNJ, ZWJ and text shaping, Bangla web fonts with fallbacks, line-height and sizing, Bangla versus Latin digits, normalization pitfalls in search and sorting, and Bangla URLs.

এই পর্বে যা শিখবেন

  • বাংলা লেখা কীভাবে নম্বর আর বাইট হয়, আর ব্রাউজার কোন ক্রমে ঠিক করে ফাইলটি কোন encoding-এ
  • ভাঙা লেখা (mojibake), “Avwg”, “????” আর □ বাক্স — প্রতিটির আলাদা কারণ আর সমাধান
  • lang আর dir attribute কোথায়, কেন — আর মিশ্র ভাষার অংশে কীভাবে লিখবেন
  • যুক্তাক্ষর, হসন্ত, ZWNJ, ZWJ আর text shaping; বাংলা ওয়েব ফন্ট, fallback, line-height আর বাংলা অঙ্ক
  • বাংলা লেখায় খোঁজা-সাজানোর ফাঁদ (normalization), বাংলা URL, আর AI-এর বাংলা কোড যাচাই

১ · অক্ষর থেকে নম্বর, নম্বর থেকে বাইট — Unicode আর UTF-8

কম্পিউটার অক্ষর চেনে না, চেনে শুধু সংখ্যা। “ক” লিখলে ফাইলে আসলে জমা হয় একটি সংখ্যা। কোন অক্ষরের কোন সংখ্যা, সেই চুক্তির নাম Unicode. Unicode পৃথিবীর প্রায় সব লিপির প্রতিটি অক্ষরকে একটি স্থায়ী নম্বর দেয়, যাকে বলে code point. বাংলা লিপির ঘর U+0980 থেকে U+09FF: ক হলো U+0995, া (আ-কার) U+09BE, হসন্ত ্ U+09CD, আর বাংলা অঙ্ক ০ থেকে ৯ হলো U+09E6 থেকে U+09EF.

নম্বর তো হলো, কিন্তু ফাইলে বা ইন্টারনেটে পাঠাতে হয় বাইট (৮ বিটের টুকরো)। নম্বরকে বাইটে লেখার নিয়মকে বলে character encoding. আজকের ওয়েবের নিয়ম একটাই — UTF-8. এতে ইংরেজি অক্ষর ১ বাইটে, আর প্রতিটি বাংলা অক্ষর ৩ বাইটে লেখা হয়। “বাংলা” শব্দে code point ৫টি (ব, া, ং, ল, া), তাই UTF-8-এ বাইট ১৫টি।

ডাকঘরের কথা ভাবুন। প্রতিটি এলাকার একটি পোস্ট কোড আছে — ঢাকা জিপিও ১০০০. এই কোড হলো code point: স্থায়ী, সবার জানা। কিন্তু চিঠির খামে কোডটি কীভাবে লিখবেন — বাংলা অঙ্কে, ইংরেজি অঙ্কে, বারকোডে? সেটা encoding. প্রেরক আর প্রাপক একই লেখার নিয়ম না মানলে চিঠি ভুল জায়গায় যায়। ওয়েবেও তাই: ফাইল যে নিয়মে লেখা, ব্রাউজারকে ঠিক সেই নিয়মেই পড়তে হবে।

Unicode, ASCII, UTF-16-এর পুরো গল্প আর বাইটের হিসাব আছে ICT কোর্সের কোড ও এনকোডিং পর্বে; বাংলা টাইপিং, অভ্র-বিজয় আর ফন্টের সমস্যা আছে বাংলা টাইপিং ও Unicode পর্বে। এখানে আমরা দেখব ওয়েবপেজ বানানোর সময় এসব কোথায় কোথায় কাজে লাগে।

মূল ধারণা

একটি বাংলা ওয়েবপেজে তিনটি আলাদা স্তর আছে, আর তিনটিই ঠিক থাকতে হবে: (১) বাইট ঠিক পড়া — encoding; (২) ভাষা জানানো — lang; (৩) সুন্দর করে আঁকা — ফন্ট আর shaping. প্রথমটি ভুল হলে লেখা ভাঙে, দ্বিতীয়টি ভুল হলে শ্রোতা ভুল উচ্চারণ শোনেন, তৃতীয়টি ভুল হলে যুক্তাক্ষর ভেঙে যায় বা বাক্স দেখায়।

২ · ভাঙা লেখা — mojibake কেন হয়, কীভাবে সারাবেন

কখনো কোনো ওয়েবসাইটে বাংলার জায়গায় “বাংলা”-র মতো অদ্ভুত চিহ্ন দেখেছেন? একে বলে mojibake (জাপানি শব্দ, মোটামুটি মানে “অক্ষরের রূপ বদল”)। লেখা হারিয়ে যায়নি — বাইটগুলো ঠিকই আছে, শুধু ব্রাউজার ভুল নিয়মে পড়ছে। নিচের ছোট স্ক্রিপ্টে নিজেই ঘটিয়ে দেখুন:

// "বাংলা" লেখাটি UTF-8-এ কোন কোন বাইট?
const bytes = new TextEncoder().encode('বাংলা');
console.log('bytes:', bytes.length);
console.log(Array.from(bytes, b => b.toString(16)).join(' '));

// একই বাইট ভুল নিয়মে (windows-1252) পড়লে
console.log('as windows-1252:', new TextDecoder('windows-1252').decode(bytes));

// ঠিক নিয়মে (UTF-8) পড়লে
console.log('as utf-8:', new TextDecoder('utf-8').decode(bytes));

প্রথমে আসবে bytes: 15 — পাঁচটি code point, প্রতিটি ৩ বাইট। তারপর বাইটগুলো hex-এ: e0 a6 ac e0 a6 be … — খেয়াল করুন, প্রতিটি বাংলা অক্ষর e0 a6 বা e0 a7 দিয়ে শুরু। windows-1252 (পশ্চিম ইউরোপের পুরোনো encoding) প্রতিটি বাইটকে আলাদা একটি অক্ষর ধরে, তাই আসে বাংলা — ১৫টি অক্ষর! শেষ লাইনে ঠিক নিয়মে পড়ায় আবার “বাংলা”. চেষ্টা করুন: 'বাংলা'-কে 'Dhaka' করুন — এবার দুই নিয়মেই একই লেখা আসবে, কারণ ইংরেজি অক্ষর দুই encoding-এ একই বাইট। এজন্যই ইংরেজি সাইটে ভুলটা অনেক সময় ধরা পড়ে না, বাংলায় সাথে সাথে পড়ে।

ব্রাউজার কীভাবে ঠিক করে কোন encoding

ব্রাউজারের কাছে আসে শুধু বাইটের স্রোত। কোন নিয়মে পড়বে, তা সে কয়েকটি সূত্র একটি নির্দিষ্ট ক্রমে দেখে ঠিক করে — WHATWG HTML স্পেসিফিকেশনে এই ক্রম লেখা আছে। নিচের স্টেপারে “পরে” চাপুন:

  1. বাইট এলe0 a6 ac …সার্ভার বা ফাইল থেকে
  2. BOM আছে?EF BB BFসবার আগে
  3. HTTP headercharset=utf-8সার্ভারের ঘোষণা
  4. meta charsetপ্রথম 1024 বাইটপাতার নিজের ঘোষণা
  5. অনুমানwindows-1252?শেষ ভরসা
  1. শুধু বাইট. নেটওয়ার্ক থেকে আসা ফাইলটি এখনো অক্ষর নয়, শুধু সংখ্যার সারি। একই সারি UTF-8-এ পড়লে “বাংলা”, অন্য নিয়মে পড়লে আবোলতাবোল। কোন নিয়ম — এই প্রশ্নের উত্তর খোঁজাই ব্রাউজারের প্রথম কাজ।
  2. BOM. ফাইলের একদম শুরুতে BOM (Byte Order Mark) নামের তিনটি বাইট থাকলে ব্রাউজার আর কিছু দেখে না — এটাই চূড়ান্ত। কিছু এডিটর “UTF-8 with BOM” নামে সেভ করে। HTML-এর জন্য BOM ক্ষতিকর নয়, তবে দরকারও নেই; সাধারণ “UTF-8” সেভ করাই প্রচলিত।
  3. সার্ভারের header. BOM না থাকলে ব্রাউজার দেখে সার্ভারের পাঠানো Content-Type header. এখানে charset লেখা থাকলে সেটি পাতার ভেতরের meta-র চেয়েও বেশি ক্ষমতাশালী। তাই সার্ভার ভুল charset পাঠালে আপনার ঠিক meta-ও হেরে যায়।
  4. meta charset. header-এ কিছু না থাকলে ব্রাউজার ফাইলের প্রথম 1024 বাইটে <meta charset> খোঁজে। এজন্যই নিয়ম: এটি <head>-এর একদম প্রথম element হবে, বড় কোনো comment বা script-এর আগে (পর্ব ০৩ আর ১৩-এ দেখেছেন)।
  5. শেষ ভরসা — অনুমান. কোনো ঘোষণা না পেলে ব্রাউজার লেখা দেখে বা কম্পিউটারের ভাষা-সেটিং ধরে অনুমান করে; অনেক জায়গায় ডিফল্ট পুরোনো windows-1252. অনুমান মাঝে মাঝে ঠিক হয়, কিন্তু ভরসা করার মতো নয়। তাই ঘোষণা দিন — দুই জায়গাতেই।

চার রকম ভাঙা লেখা — চেহারা দেখে কারণ চেনা

যা দেখছেনআসল কারণসমাধান
বাংলাফাইল UTF-8, কিন্তু পড়া হচ্ছে windows-1252 বা ISO-8859-1-এ — meta নেই, বা সার্ভারের header ভুল<meta charset="utf-8"> প্রথমে; সার্ভারে UTF-8 header
বাংলা জায়গায় �����ফাইল UTF-8 নয় (যেমন পুরোনো Notepad-এ ANSI-তে সেভ), অথচ ব্রাউজার UTF-8 ধরে পড়ছে — অবৈধ বাইট হয়ে যায় � (replacement character)এডিটরে “Save with Encoding → UTF-8” দিয়ে আবার সেভ
????কোনো ধাপে (প্রায়ই ডেটাবেসে) বাংলা রাখা যায় না এমন encoding — অক্ষরগুলো সেভের সময়ই “?” হয়ে মুছে গেছেডেটাবেস ও কানেকশন UTF-8 (MySQL-এ utf8mb4); হারানো লেখা আর ফেরে না
Avwg evsjv‡`k‡K fv‡jvevwmলেখাটি আসলে ইংরেজি অক্ষর, পুরোনো ANSI ফন্ট (যেমন বিজয়ের SutonnyMJ) দিয়ে বাংলার মতো আঁকা ছিল; ফন্ট নেই, তাই আসল রূপ বেরিয়ে পড়েছেUnicode-এ রূপান্তর করে তারপর ওয়েবে দিন
□□□ বা ফাঁকা বাক্সencoding ঠিক আছে, কিন্তু কোনো ফন্টেই এই অক্ষরের আকার নেই — একে বলে tofuবাংলা ওয়েব ফন্ট বা ভালো fallback (অংশ ৫)

শেষ দুটি সারি বিশেষ করে বাংলাদেশের সমস্যা। বহু বছর ধরে অফিস, প্রেস আর পত্রিকায় বাংলা লেখা হতো বিজয় কি-বোর্ড আর SutonnyMJ-এর মতো ANSI ফন্টে। সেই ফাইলের ভেতরে “আমি” আসলে ইংরেজি “Avwg” — ফন্টটি শুধু A-এর জায়গায় অ, v-এর জায়গায় আ-কার এঁকে দেখায়। কম্পিউটারে ফন্টটি থাকলে চোখে সব ঠিক, কিন্তু ওয়েবে এমন লেখা দিলে Google খোঁজে “Avwg”, screen reader পড়ে “এ-ভি-ডব্লিউ-জি”, আর ফোনে বা অন্য কম্পিউটারে ফন্ট না থাকলে দেখায় হিজিবিজি। ওয়েবে শুধু Unicode বাংলা। পুরোনো লেখা থাকলে আগে রূপান্তর টুল দিয়ে Unicode করে নিন — পদ্ধতি আছে ICT কোর্সের বাংলা টাইপিং ও Unicode পর্বে।

# cPanel বা Apache সার্ভারে সব .html, .css, .js ফাইলের সাথে UTF-8 ঘোষণা
AddDefaultCharset UTF-8
AddCharset UTF-8 .html .css .js

লাইভ এডিটরে এটা পরীক্ষা করা যায় না কেন? এই কোর্সের প্রিভিউ আপনার কোড একটি লেখা (string) হিসেবে পায়, বাইট হিসেবে নয় — তাই সেখানে <meta charset> বদলালে কিছু ঘটে না। আসল পরীক্ষা করতে নিজের কম্পিউটারে একটি ফাইল বানিয়ে VS Code-এর নিচের স্ট্যাটাস বারে “UTF-8” চেপে encoding বদলে সেভ করুন, তারপর ব্রাউজারে খুলুন (অনুশীলন ১ দেখুন)।

৩ · lang — পাতাকে বলে দিন সে কোন ভাষায় কথা বলে

encoding ঠিক হলে ব্রাউজার অক্ষর চেনে, কিন্তু ভাষা চেনে না। “Bus” ইংরেজিতে বাস, আবার কোনো ইউরোপীয় ভাষায় অন্য শব্দ — অক্ষর দেখে ভাষা নিশ্চিত বলা যায় না। তাই HTML-এ আছে lang attribute. পর্ব ০৩-এ <html lang="bn"> লিখেছিলেন; এবার দেখা যাক এটা আসলে কী কী বদলায়।

উচ্চারণ

Screen reader lang দেখে কণ্ঠ (voice) বেছে নেয়। lang ভুল হলে ইংরেজি কণ্ঠ বাংলা পড়ার চেষ্টা করে — হয় অক্ষর ধরে বানান, নয়তো চুপ।

অ্যাক্সেসিবিলিটি

ফন্ট বাছাই

আপনার ফন্টে কোনো অক্ষর না থাকলে ব্রাউজার lang দেখে সিস্টেমের সবচেয়ে মানানসই ফন্ট খোঁজে। একই চীনা-জাপানি অক্ষর lang অনুযায়ী আলাদা আকারে দেখায়।

রেন্ডারিং

বানান ও হাইফেন

ফর্মের লেখার ঘরে ব্রাউজারের বানান-পরীক্ষা lang ধরে অভিধান বাছে। CSS-এর hyphens: auto (শব্দ ভেঙে হাইফেন) ভাষা জানা না থাকলে কাজই করে না।

লেখা

অনুবাদ ও সার্চ

ব্রাউজারের “অনুবাদ করুন” প্রস্তাব, সার্চ ইঞ্জিন আর AI টুল পাতার ভাষা বুঝতে lang-কে একটি সংকেত হিসেবে দেখে।

SEO

lang-এর মান হলো একটি BCP 47 ভাষা-ট্যাগ: bn (বাংলা), bn-BD (বাংলাদেশের বাংলা), bn-IN (ভারতের বাংলা), en, ar, ur. বেশিরভাগ ক্ষেত্রে শুধু bn যথেষ্ট। lang সব সন্তান element-এ উত্তরাধিকার হিসেবে যায়, তাই পাতায় একবার <html lang="bn">, আর যেখানে ভাষা বদলায় শুধু সেখানে আলাদা lang. WCAG 2.2-এর দুটি নিয়ম ঠিক এটাই চায়: ৩.১.১ (Language of Page) — পাতার মূল ভাষা, আর ৩.১.২ (Language of Parts) — ভিন্ন ভাষার অংশ।

<style>
  .up  { text-transform: uppercase; font: 20px sans-serif; margin: 4px 0; }
  .han { font-size: 44px; margin: 8px 0; }
  :lang(en) { color: #1d4ed8; }
</style>
<p class="up" lang="en">istanbul — en</p>
<p class="up" lang="tr">istanbul — tr</p>

<p class="han"><span lang="ja">直骨角</span> · <span lang="zh-CN">直骨角</span></p>

<p>আমি <span lang="en">Bob Dylan</span>-এর গান শুনি আর
  <cite lang="en">The Old Man and the Sea</cite> পড়ি।</p>

একই শব্দ istanbul, একই CSS — কিন্তু প্রথম লাইনে ISTANBUL, দ্বিতীয়টিতে İSTANBUL (ওপরে বিন্দুসহ İ)। তুর্কি ভাষায় ছোট i-এর বড় রূপ İ, আর ব্রাউজার lang দেখে সেই নিয়ম মানে। তৃতীয় লাইনে একই তিনটি অক্ষর (একই code point) জাপানি আর চীনা lang-এ — আপনার কম্পিউটারে দুই ভাষার ফন্ট থাকলে ভেতরের দাগে সূক্ষ্ম পার্থক্য দেখবেন (না থাকলে দুটি একই দেখাতে পারে)। শেষ লাইনে :lang(en) selector ইংরেজি অংশগুলোকে নীল করেছে — প্রথম লাইনটিও নীল, কারণ সেটিও lang="en". চেষ্টা করুন: দ্বিতীয় লাইনের tr-কে en করুন — İ চলে যাবে। তারপর :lang(en)-কে :lang(bn) করুন — এবার বাংলা লেখা নীল হবে, কারণ প্রিভিউর <html>-এ lang="bn" আছে।

dir — লেখার দিক: বাংলা, ইংরেজি দুটোই বাঁ থেকে ডানে (LTR) চলে, এটাই ডিফল্ট — তাই বাংলা পাতায় dir লেখার দরকার নেই। কিন্তু আরবি, উর্দু, ফারসি, হিব্রু চলে ডান থেকে বাঁয়ে (RTL). বাংলা পাতায় একটি কুরআনের আয়াত বা উর্দু শের থাকলে লিখুন <p lang="ar" dir="rtl">…</p>. dir-এর মান ltr, rtl বা auto (ব্যবহারকারীর লেখা দেখে ঠিক হবে — মন্তব্যের ঘরের মতো জায়গায় কাজে লাগে)। দিক জানানোর কাজ HTML-এর, CSS-এর direction property-র নয় — কারণ দিক লেখার অর্থের অংশ। ডান-বাঁ উল্টে গেলেও নকশা ঠিক রাখতে CSS-এর logical property আসবে পর্ব ৩৫-এ।

৪ · যুক্তাক্ষর, হসন্ত, ZWNJ আর ZWJ — ফন্ট কীভাবে বাংলা আঁকে

ইংরেজিতে একটি অক্ষর মানে মোটামুটি একটি আকার: a লিখলে a. বাংলায় তা নয়। “ক্ষ” দেখতে একটি অক্ষর, কিন্তু Unicode-এ এটি তিনটি code point: ক (U+0995) + ্ (U+09CD) + ষ (U+09B7). Unicode-এ যুক্তাক্ষর-এর আলাদা কোনো নম্বর নেই। মাঝের হসন্ত বলে “এই দুই ব্যঞ্জন জোড়া লাগাও”, আর জোড়া লাগানো আকারটি আঁকে ফন্ট। আবার “কি” লিখলে মেমোরিতে আগে ক, পরে ি — অথচ পর্দায় ি বসে ক-এর আগে। জায়গা বদলের এই কাজও ফন্ট আর ব্রাউজারের।

code point-এর সারি থেকে পর্দার আকার বানানোর এই প্রক্রিয়ার নাম text shaping. ফন্টের ভেতরের প্রতিটি আঁকা আকারকে বলে glyph. “ক্ষেত” শব্দটি কীভাবে আঁকা হয়, দেখুন:

ধাপ ১মেমোরিতে যা আছে
ক      ্      ষ      ে      ত
U+0995 U+09CD U+09B7 U+09C7 U+09A4
৫টি code point — ফাইলে ১৫ বাইট
ধাপ ২অক্ষরগুচ্ছ (cluster) ভাগ
[ ক ্ ষ ে ]  [ ত ]
ব্যঞ্জন + হসন্ত + ব্যঞ্জন + কার = একটি গুচ্ছ
ধাপ ৩কার-চিহ্নের জায়গা বদল
ে [ ক ্ ষ ]  ত
এ-কার গুচ্ছের সামনে চলে গেল
ধাপ ৪যুক্তাক্ষরের glyph
ে  ক্ষ  ত
ফন্টের নিয়ম: ক + ্ + ষ → একটি glyph
ধাপ ৫পর্দায়
ক্ষেত
৫টি code point → ৩টি glyph
  1. Code point-এর সারি. আপনি যেভাবে উচ্চারণ করেন, Unicode সেই ক্রমে জমা রাখে: ক, হসন্ত, ষ, এ-কার, ত. এ-কার পর্দায় আগে দেখা গেলেও মেমোরিতে ব্যঞ্জনের পরে। এই “উচ্চারণের ক্রম” থাকায় সার্চ আর সাজানো সহজ হয়।
  2. গুচ্ছ খোঁজা. shaping ইঞ্জিন বাংলার ব্যাকরণ মেনে অক্ষরগুলোকে গুচ্ছে ভাগ করে — যেটুকু একসাথে একটি আকার হবে। “ক্ষে” একটি গুচ্ছ, “ত” আরেকটি। কার্সর সরানো আর মোছার সময়ও ব্রাউজার এই গুচ্ছ মানে।
  3. জায়গা বদল. ি, ে, ৈ — এই তিন কার বসে ব্যঞ্জনের বাঁয়ে; ো আর ৌ দুই ভাগ হয়ে দুই পাশে। ইঞ্জিন glyph-গুলোর ক্রম বদলে দেয়। পুরোনো ANSI ব্যবস্থায় এই কাজ ছিল টাইপিস্টের — তাই বিজয়ে আগে কার, পরে ব্যঞ্জন টাইপ করতে হতো।
  4. যুক্তাক্ষর. ফন্টের ভেতরে OpenType নিয়মের তালিকা থাকে: “ক + ্ + ষ পেলে এই glyph বসাও”। ভালো বাংলা ফন্টে শত শত এমন নিয়ম। ফন্টে কোনো যুক্তাক্ষর না থাকলে দেখায় “ক্‌ষ”-এর মতো ভাঙা রূপ — এটা ফন্টের দুর্বলতা, আপনার HTML-এর ভুল নয়।
  5. চূড়ান্ত রূপ. উ-কার, ঋ-কার, চন্দ্রবিন্দুর মতো চিহ্ন ঠিক কতটা নিচে বা ওপরে বসবে, তাও ফন্ট ঠিক করে। ৫টি code point হলো ৩টি glyph. Chrome আর Firefox এই কাজে ব্যবহার করে ওপেন সোর্স HarfBuzz ইঞ্জিন; Apple-এর ব্রাউজার নিজস্ব CoreText.

অদৃশ্য দুই সহকারী — ZWNJ আর ZWJ

কখনো কখনো আপনি যুক্তাক্ষর চান না, বা একটি বিশেষ রূপ চান। এর জন্য Unicode-এ দুটি অদৃশ্য অক্ষর আছে — চোখে দেখা যায় না, প্রস্থও শূন্য, কিন্তু shaping বদলে দেয়:

  • ZWNJ (Zero Width Non-Joiner, U+200C) — “জোড়া লাগিও না”। হসন্তের পরে দিলে যুক্তাক্ষরের বদলে স্পষ্ট হসন্ত দেখায়: ক্‌ষ. HTML-এ লেখা যায় &zwnj; entity দিয়ে।
  • ZWJ (Zero Width Joiner, U+200D) — “এই বিশেষ জোড়াটা চাই”। বাংলায় এর সবচেয়ে পরিচিত কাজ র-এর পরে য-ফলা: র + ZWJ + ্ + য = র‍্য, যেমন র‍্যাব, র‍্যাম. ZWJ না দিলে র + ্ + য হয়ে যায় রেফ — “র্য” (কার্য শব্দের মতো)। HTML-এ &zwj;.
<style>
  p { font-size: 30px; margin: 4px 0; }
  small { font: 13px monospace; color: #555; }
</style>
<p>ক্ষ <small>ক + ্ + ষ</small></p>
<p>ক্&zwnj;ষ <small>ক + ্ + ZWNJ + ষ</small></p>
<p>কার্য <small>র + ্ + য → রেফ</small></p>
<p>র&zwj;্যাব <small>র + ZWJ + ্ + য → য-ফলা</small></p>
<p>র্যাব <small>ZWJ ছাড়া — ভুল রূপ</small></p>

প্রথম লাইনে পরিচিত যুক্তাক্ষর ক্ষ; দ্বিতীয় লাইনে ZWNJ থাকায় ক-এর নিচে স্পষ্ট হসন্ত, তারপর আলাদা ষ. তৃতীয় লাইনে “কার্য”-তে য-এর মাথায় রেফ — এটাই ঠিক। চতুর্থ লাইনে ZWJ দেওয়ায় র-এর পাশে য-ফলা: র‍্যাব. শেষ লাইনে একই শব্দ ZWJ ছাড়া — য-এর মাথায় রেফ বসে গেছে, যা “র‍্যাব” হিসেবে ভুল। (আকারগুলো আপনার সিস্টেমের বাংলা ফন্টের ওপর নির্ভর করে; বেশিরভাগ আধুনিক ফন্টে এমনই দেখায়।) চেষ্টা করুন: দ্বিতীয় লাইন থেকে &zwnj; মুছে দিন — আবার যুক্তাক্ষর হবে। আর মনে রাখুন, অদৃশ্য বলে এগুলো কপি-পেস্টে চুপচাপ চলে আসে; সার্চে সমস্যা করলে অংশ ৮-এর normalization দেখুন।

আরও গভীরে: OpenType ফন্টের ভেতরে দুটি প্রধান নিয়মের টেবিল — GSUB (glyph বদলানো: যুক্তাক্ষর, রেফ, ফলা) আর GPOS (glyph-এর অবস্থান: কার আর চন্দ্রবিন্দু কতটা সরবে)। বাংলার জন্য Microsoft-এর লেখা স্পেসিফিকেশনে bng2 নামের একটি script tag আর akhn, rphf (রেফ), blwf (নিচের ফলা), pstf-এর মতো feature নির্দিষ্ট ক্রমে চালানোর নিয়ম আছে; পুরোনো beng tag-এর কিছু ভুল শুধরাতে নতুনটি এসেছিল। একটি ফন্টে নিয়ম কম থাকলে Unicode লেখা ঠিক থাকলেও দেখায় ভাঙা — তাই “আমার বাংলা ভাঙছে” অভিযোগের অনেকটাই ফন্টের সমস্যা। আর ১৯৯১ সালের প্রথম Unicode থেকেই বাংলা ঘর থাকলেও খণ্ড-ত (ৎ)-এর নিজস্ব code point U+09CE এসেছে ২০০৫ সালে Unicode 4.1-এ; তার আগে লেখা হতো ত + ্ + ZWJ দিয়ে।

৫ · বাংলা ওয়েব ফন্ট — Google Fonts, পরিচিত নাম আর fallback

আপনার CSS-এ কোনো ফন্ট না বললে ব্রাউজার পাঠকের কম্পিউটারের বাংলা ফন্ট ব্যবহার করে: Windows-এ সাধারণত Nirmala UI (পুরোনো Windows-এ Vrinda), Mac আর iPhone-এ Kohinoor Bangla বা Bangla Sangam MN, Android-এ সাধারণত Noto Sans Bengali. ফলে একই পাতা একেক ফোনে একেক রকম দেখায়। নির্দিষ্ট চেহারা চাইলে web font ব্যবহার করুন — ফন্টের ফাইল পাতার সাথে ডাউনলোড হয়।

ফন্টকোথায় পাবেনচরিত্র — কোথায় মানায়
Noto Sans BengaliGoogle Fontsপরিষ্কার, নিরপেক্ষ; Google-এর Noto পরিবারের অংশ — সাধারণ লেখা, অ্যাপ
Noto Serif BengaliGoogle Fontsছাপা বইয়ের মতো; লম্বা প্রবন্ধ, সাহিত্য
Hind SiliguriGoogle Fontsখুব জনপ্রিয়, কয়েকটি ওজন (weight); ব্লগ, সংবাদ, ব্যবসার সাইট
Anek BanglaGoogle Fontsআধুনিক, variable font (ওজন আর প্রস্থ বদলানো যায়); এই সাইটটিও এটি ব্যবহার করে
Baloo Da 2Google Fontsগোলগাল, বন্ধুসুলভ; শিশুদের সাইট, শিরোনাম
Tiro BanglaGoogle Fontsঐতিহ্যবাহী ছাপার ধাঁচ; সাহিত্য, পত্রিকা
GaladaGoogle Fontsহাতে লেখার মতো সাজানো; শুধু বড় শিরোনাম বা লোগো, মূল লেখায় নয়
SolaimanLipi, Kalpurush, NikoshGoogle Fonts-এ নেই; বিনামূল্যে আলাদা ডাউনলোডবাংলাদেশে বহুল প্রচলিত Unicode ফন্ট; ওয়েবে দিতে চাইলে ফাইল নিজের সার্ভারে রেখে @font-face — আগে লাইসেন্স দেখে নিন

এবার একই লেখা কয়েকটি ফন্টে দেখুন। প্রিভিউর ভেতরের কোড Google Fonts-এর সার্ভার (fonts.googleapis.com) থেকে ফন্ট নামাচ্ছে:

<style>
  @import url("https://fonts.googleapis.com/css2?family=Noto+Sans+Bengali&family=Noto+Serif+Bengali&family=Hind+Siliguri&family=Anek+Bangla&family=Baloo+Da+2&family=Tiro+Bangla&family=Galada&display=swap");
  p { font-size: 24px; margin: 0 0 10px; }
  small { display: block; font: 12px monospace; color: #666; }
</style>
<p style="font-family: 'Noto Sans Bengali', sans-serif"><small>Noto Sans Bengali</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: 'Noto Serif Bengali', serif"><small>Noto Serif Bengali</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: 'Hind Siliguri', sans-serif"><small>Hind Siliguri</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: 'Anek Bangla', sans-serif"><small>Anek Bangla</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: 'Baloo Da 2', sans-serif"><small>Baloo Da 2</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: 'Tiro Bangla', serif"><small>Tiro Bangla</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: 'Galada', cursive"><small>Galada</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>
<p style="font-family: sans-serif"><small>sans-serif (আপনার সিস্টেমের ফন্ট)</small>শ্রাবণের বৃষ্টিতে ক্ষেতে কৃষক ব্যস্ত</p>

ইন্টারনেট সংযোগ থাকলে আটটি লাইনে আটটি আলাদা চেহারা দেখবেন — Noto Serif আর Tiro-তে ছাপা বইয়ের মতো মোটা-চিকন টান, Baloo-তে গোলগাল, Galada-তে হাতের লেখার মতো টান — অথচ code point একই। শ্র, ষ্টি, ক্ষে, কৃ, ব্য, স্ত — প্রতিটি যুক্তাক্ষর ও কার কোন ফন্ট কীভাবে আঁকে, মিলিয়ে দেখুন। ফন্ট নামাতে না পারলে (নেটওয়ার্ক বন্ধ, বা Google Fonts আটকানো) সব লাইন শেষ লাইনের মতো সিস্টেম ফন্টে আসবে — ঠিক এটাই fallback. চেষ্টা করুন: লেখাটি বদলে নিজের নাম লিখুন; আর @import লাইনটি মুছে দেখুন কোন কোন লাইন বদলায়।

ফন্টের তালিকা — fallback stack

CSS-এর font-family-তে একটি নয়, একটি তালিকা দেওয়া হয়। ব্রাউজার প্রতিটি অক্ষরের জন্য বাঁ থেকে ডানে খোঁজে: প্রথম ফন্টটি লোড হয়নি বা তাতে এই অক্ষর নেই? তাহলে পরেরটি। একে বলে font fallback. এজন্য একটি ইংরেজি ফন্ট আগে আর বাংলা ফন্ট পরে লিখলে ইংরেজি অক্ষর আসে প্রথমটি থেকে, বাংলা অক্ষর দ্বিতীয়টি থেকে — দুই লিপি দুই ফন্টে, এক বাক্যেই।

/* index.html-এর head-এ, অন্য CSS-এর আগে:
   <link rel="preconnect" href="https://fonts.googleapis.com">
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Hind+Siliguri:wght@400;600&display=swap"> */

body {
  font-family: "Hind Siliguri",      /* ১. ওয়েব ফন্ট */
               "Noto Sans Bengali",  /* ২. Android-এ প্রায়ই থাকে */
               "Nirmala UI",         /* ৩. Windows */
               "Kohinoor Bangla",    /* ৪. Mac, iPhone */
               sans-serif;           /* ৫. শেষ ভরসা — কিছু একটা */
}

Google Fonts-এর লিংকে দুটি জিনিস চুপচাপ আপনার উপকার করে। প্রথমত, URL-এর display=swap মানে CSS-এ font-display: swap — ফন্ট আসার আগে লেখা সিস্টেম ফন্টে দেখায়, ফন্ট এলে বদলে যায়; ধীর ইন্টারনেটে ফাঁকা পাতা দেখতে হয় না। দ্বিতীয়ত, Google ফন্টটিকে কয়েকটি ছোট ফাইলে ভাগ করে আর প্রতিটির জন্য unicode-range লিখে দেয় — পাতায় বাংলা অক্ষর থাকলে তবেই বাংলা অংশের ফাইল নামে। বাংলা ফন্ট ফাইল সাধারণত ইংরেজির চেয়ে বড়, কারণ শত শত যুক্তাক্ষরের আকার থাকে; তাই দুই-তিনটির বেশি ওজন (weight) নেবেন না। নিজের সার্ভারে ফন্ট রাখা (self-hosting), @font-face লেখা আর পারফরম্যান্সের হিসাব — পর্ব ২১ আর ৪৯-এ।

ওয়েবে ANSI ফন্টের নাম লিখবেন না। font-family: SutonnyMJ লিখে কোনো লাভ নেই, বরং ক্ষতি — ফন্টটি ইংরেজি অক্ষরের জায়গায় বাংলা আঁকার জন্য বানানো, এতে Unicode বাংলার আকার নেই বললেই চলে। তাই Unicode বাংলা লেখা এমনিতেই অন্য কোনো ফন্টে আসে; আর যে পাঠকের কম্পিউটারে ফন্টটি আছে, তাঁর কাছে ওই অংশের ইংরেজি অক্ষর আর অঙ্ক বাংলা-চেহারার হিজিবিজি হয়ে যেতে পারে। পুরোনো সাইট বা AI-এর কোডে এমন নাম দেখলে সন্দেহ করুন।

৬ · বাংলার মাপজোখ — line-height, আকার আর মাত্রা

বাংলা লিপির শরীর তিন তলার বাড়ির মতো। মাঝের তলায় মূল অক্ষর, তার মাথায় মাত্রা (ওপরের টানা দাগ)। ওপরের তলায় ি, ী, ৈ, ৌ-এর বাঁক, রেফ আর চন্দ্রবিন্দু। নিচের তলায় ু, ূ, ৃ আর নানা ফলা। ইংরেজির তুলনায় বাংলার ওপরে-নিচে চিহ্ন অনেক বেশি। তাই লাইনগুলো ঘেঁষাঘেঁষি হলে ওপরের লাইনের উ-কার নিচের লাইনের রেফে ধাক্কা খায়। লাইনের উচ্চতা ঠিক করে CSS-এর line-height (CSS শুরু হবে পর্ব ১৬ থেকে; এখানে শুধু নিচের মানগুলো বদলে দেখুন):

p {
  font-size: 18px;
  width: 280px;
  padding: 4px 8px;
  border: 1px dashed #999;
}
.tight { line-height: 1.1; }
.loose { line-height: 1.7; }

দুটি বাক্সে একই লেখা। ওপরেরটিতে line-height: 1.1 — লাইনগুলো গায়ে গায়ে, সূ, চূ, কৃ-এর নিচের চিহ্ন প্রায় পরের লাইনের মাথা ছুঁয়ে ফেলে, পড়তে চোখে চাপ লাগে। নিচেরটিতে 1.7 — প্রতিটি লাইনের চারপাশে শ্বাস নেওয়ার জায়গা। চেষ্টা করুন: .tight-এর মান 1.4 করুন, তারপর .loose-এর font-size আলাদা করে 20px দিন — কোন মাপে আপনার পড়তে সবচেয়ে আরাম, খুঁজে বের করুন। মানের সাথে কোনো একক (px) না লিখলে সেটি font-size-এর গুণিতক — লেখা বড় হলে লাইনও নিজে বাড়ে।

কয়েকটি ব্যবহারিক নিয়ম, যা বাংলা সাইটে বারবার কাজে লাগে:

  • মূল লেখায় line-height 1.6–1.8, শিরোনামে একটু কম (1.3–1.4). ইংরেজির জন্য যে 1.4–1.5 যথেষ্ট, বাংলায় তা প্রায়ই চাপা লাগে।
  • বাংলা একটু বড় দেখান। একই font-size-এ অনেক বাংলা ফন্টের মূল অক্ষর ইংরেজির চেয়ে ছোট দেখায়। ফোনে মূল লেখা অন্তত 16px, বাংলায় অনেকে 17–18px নেন। দুই লিপির চোখে দেখা আকার মেলাতে CSS-এর font-size-adjust আছে — এটি Baseline Newly available (সব প্রধান ব্রাউজারে সম্প্রতি এসেছে), না চললে ক্ষতি নেই, শুধু আকার একটু অমিল থাকে।
  • বাংলায় italic নেই। বাংলা লিপিতে বাঁকা (italic) লেখার ঐতিহ্য নেই, বেশিরভাগ বাংলা ফন্টে italic রূপও নেই — ব্রাউজার তখন অক্ষর কৃত্রিমভাবে হেলিয়ে দেয়, যা দেখতে খারাপ। জোর দিতে <em> রাখুন (অর্থ ঠিক থাকে), কিন্তু CSS-এ বাংলার জন্য রং বা মোটা অক্ষর ভাবুন।
  • অক্ষরের ফাঁক বাড়াবেন না। letter-spacing দিলে যুক্তাক্ষর আর কার-চিহ্ন আলাদা হয়ে ভাঙা দেখাতে পারে। বাংলায় এটি শূন্যই রাখুন।

৭ · বাংলা অঙ্ক — ১২৩ নাকি 123?

বাংলা অঙ্ক ০–৯ সাধারণ Unicode অক্ষর (U+09E6–U+09EF). প্রবন্ধ বা খবরে সরাসরি টাইপ করে লিখুন — “২১ ফেব্রুয়ারি”, “৳ ১,২৫০” — এটাই সবচেয়ে সহজ, আর কপি করলেও ঠিক থাকে। কিন্তু তালিকার নম্বর আপনি লেখেন না, ব্রাউজার বসায় (পর্ব ০৭)। সেগুলোকে বাংলা করতে CSS-এর counter style আছে: list-style-type: bengali.

<ol style="list-style-type: bengali">
  <li>নিবন্ধন ফর্ম পূরণ করুন</li>
  <li>ফি জমা দিন</li>
  <li>প্রবেশপত্র নামিয়ে নিন</li>
</ol>
<ol start="9" style="list-style-type: bengali">
  <li>নবম ধাপ</li>
  <li>দশম ধাপ</li>
</ol>
<p>পরীক্ষা শুরু হবে
  <time datetime="2026-02-21T10:00">২১ ফেব্রুয়ারি, সকাল ১০টায়</time>, সময়মতো আসুন।</p>
<p>দাম: ৳ ১,২৫০ · মোবাইল: ০১৭১১-০০০০০০ (উদাহরণ)</p>

প্রথম তালিকায় নম্বর ১, ২, ৩ — HTML-এ কোথাও বাংলা অঙ্ক লেখা নেই, CSS-এর bengali counter style বসিয়েছে। দ্বিতীয়টিতে start="9" থাকায় ৯, ১০ — দুই অঙ্কের সংখ্যাও ঠিকঠাক। <time>-এর ভেতরে মানুষের জন্য বাংলায় তারিখ, আর datetime-এ যন্ত্রের জন্য ইংরেজি অঙ্কে মানক রূপ — দুই পাঠকই খুশি। চেষ্টা করুন: প্রথম <ol>-এ bengali-কে devanagari বা arabic-indic করুন — অন্য লিপির অঙ্ক আসবে। bengali-এর মতো আগে থেকে বানানো counter style প্রধান সব ব্রাউজারে কাজ করে; না চললে ব্রাউজার সাধারণ 1, 2, 3 দেখায়, তথ্য হারায় না।

দুটি সাবধানতা। এক, হিসাবের সংখ্যা — ফর্মের <input type="number"> বা JavaScript-এর গণনা ইংরেজি অঙ্ক ধরে কাজ করে; বাংলা অঙ্কে লেখা দাম বা মোবাইল নম্বর প্রোগ্রামে ব্যবহারের আগে ইংরেজি অঙ্কে বদলে নিতে হয়। উল্টো দিকে, JavaScript-এর (1234567).toLocaleString('bn-BD') দেয় ১২,৩৪,৫৬৭ — বাংলা অঙ্ক, আর লাখ-কোটির নিয়মে কমা। দুই, মিশিয়ে ফেলবেন না — এক পাতায় কোথাও “১২৫০”, কোথাও “1250” দেখতে অগোছালো লাগে। একটি নিয়ম বেছে সারা সাইটে মেনে চলুন।

৮ · খোঁজা আর সাজানো — দেখতে এক, ভেতরে আলাদা

একটি অনলাইন দোকানের কথা ভাবুন। দোকানি পণ্যের নাম লিখেছেন “সয়াবিন তেল”, ক্রেতা খুঁজছেন “সয়াবিন তেল” — চোখে হুবহু এক, অথচ সার্চে কিছুই আসে না! কারণ কিছু বাংলা অক্ষর Unicode-এ দুইভাবে লেখা যায়। য়, ড়, ঢ় — প্রতিটির একটি “একক” code point আছে (য় = U+09DF), আবার মূল অক্ষর + নুকতা (U+09BC) দিয়েও লেখা যায় (য + ় )। ো আর ৌ-ও তাই: ো একক (U+09CB), আবার ে + া দুই টুকরোতেও। দেখতে একই, কিন্তু কম্পিউটারের তুলনায় আলাদা।

সমাধানের নাম Unicode normalization — তুলনার আগে দুই লেখাকেই একটি মানক রূপে আনা। সবচেয়ে প্রচলিত রূপ NFC. ধাপে ধাপে দেখুন:

ডেটাবেসেদোকানির লেখা
ব  য়        স
U+09AC U+09DF U+09B8   (৩টি)
সার্চ বক্সেক্রেতার লেখা
ব  য      ়      স
U+09AC U+09AF U+09BC U+09B8   (৪টি)
সরাসরি তুলনাa === b
U+09DF  ≠  U+09AF U+09BC
false  — “কোনো ফল পাওয়া যায়নি”
NFC-তে আনাnormalize('NFC')
দোকানি: ব U+09AF U+09BC স
ক্রেতা: ব U+09AF U+09BC স
নিয়মদুই জায়গায় normalize
সেভের আগে  → normalize
খোঁজার আগে → normalize
true  — ফল পাওয়া গেল
  1. দোকানির লেখা. পণ্যের তালিকায় য় জমা আছে একক code point U+09DF হিসেবে — হয়তো পুরোনো একটি ফাইল রূপান্তর করে আনা।
  2. ক্রেতার লেখা. ক্রেতার ফোনের কি-বোর্ড য় লিখেছে য + নুকতা হিসেবে। পর্দায় দুটির মধ্যে বিন্দুমাত্র পার্থক্য নেই — screen reader-ও একই উচ্চারণ করে।
  3. সরাসরি তুলনা ব্যর্থ. কম্পিউটার অক্ষর দেখে না, নম্বর দেখে। একদিকে ৩টি নম্বর, অন্যদিকে ৪টি — মিল নেই। ক্রেতা ভাবেন পণ্যটি নেই, দোকান বিক্রি হারায়।
  4. NFC. Unicode-এর নিয়মে য়, ড়, ঢ়-এর মানক রূপ হলো দুই টুকরো (মূল অক্ষর + নুকতা) — এই তিনটি অক্ষর NFC-তেও জোড়া লাগে না, এটি Unicode-এর একটি বিশেষ ব্যতিক্রম তালিকার অংশ। আবার ে + া জোড়া লেগে একক ো হয়। ফল যাই হোক, দুই দিকের লেখা এখন হুবহু এক।
  5. নিয়ম. লেখা সেভ করার আগে আর খোঁজার আগে — দুই জায়গাতেই একই normalization. এটি সার্ভারের বা JavaScript-এর কাজ, HTML-এর নয়; কিন্তু বাংলা সাইট বানালে আপনাকেই মনে রাখতে হবে। অদৃশ্য ZWNJ/ZWJ-ও সার্চে একই ঝামেলা করতে পারে।
const a = 'বয়স';        // য় = একটি code point
const b = 'বয়স';  // য + নুকতা = দুটি
console.log(a, b, a === b);
console.log('code units:', a.length, b.length);
console.log('after NFC:', a.normalize('NFC') === b.normalize('NFC'));

// সাজানো: সাধারণ sort() শুধু নম্বর দেখে
console.log(['বহু', a].sort().join(', '), '|', ['বহু', b].sort().join(', '));
// ভাষার নিয়মে সাজানো
const bn = new Intl.Collator('bn');
console.log(['বহু', a].sort(bn.compare).join(', '), '|', ['বহু', b].sort(bn.compare).join(', '));

// ক্ষ কোন কোন code point?
console.log(Array.from('ক্ষ', c => c.codePointAt(0).toString(16)).join(' '));

প্রথম লাইনে দুটি “বয়স” পাশাপাশি — হুবহু এক — আর তারপর false. দ্বিতীয় লাইনে 3 4, তৃতীয় লাইনে normalize-এর পর true. চতুর্থ লাইনে মজার ব্যাপার: সাধারণ sort()-এ একক য় (U+09DF) হ (U+09B9)-এর পরে পড়ে, তাই একবার “বহু” আগে, আরেকবার “বয়স” আগে — একই শব্দ, লেখার ধরন ভেদে আলাদা ক্রম! পঞ্চম লাইনে collation (Intl.Collator) দুই ক্ষেত্রেই একই ক্রম দেয়। শেষ লাইন 995 9cd 9b7 — ক্ষ মানে ক, হসন্ত, ষ. চেষ্টা করুন: শেষ লাইনে 'ক্ষ'-কে 'জ্ঞ' করুন — কোন তিনটি code point আসে?

এই শেষ লাইনটিই সাজানোর আরেকটি প্রশ্নের উত্তর দেয়। অনেক বর্ণমালার বইয়ে ক্ষ-কে ব্যঞ্জনবর্ণের শেষে আলাদা বর্ণ হিসেবে দেখানো হয়। কিন্তু Unicode-এ এটি ক + ্ + ষ, তাই কম্পিউটারের সাজানোয় “ক্ষমা” পড়ে ক-এর দলেই, “কৌশল”-এর পরে আর “খেলা”-র আগে। কোনো তালিকা বা অভিধান বানালে ঠিক করুন আপনি কোন নিয়ম চান — আর code point-এর ক্রম বনাম ভাষার নিয়ম (collation) যে আলাদা হতে পারে, সেটা মাথায় রাখুন।

৯ · URL-এ বাংলা — চলে, কিন্তু ভেবে নিন

পর্ব ০৫-এ দেখেছেন, URL-এ আসলে শুধু ইংরেজি অক্ষর, অঙ্ক আর কয়েকটি চিহ্ন বসতে পারে; বাকি সব percent-encoding হয়ে যায়। বাংলা অক্ষরের UTF-8 বাইটগুলোই % দিয়ে লেখা হয়। তাই /blog/বাংলা ঠিকানাটি নেটওয়ার্কে যায় /blog/%E0%A6%AC%E0%A6%BE%E0%A6%82%E0%A6%B2%E0%A6%BE হয়ে — পাঁচ অক্ষরের শব্দ ৪৫ অক্ষরের জট। আধুনিক ব্রাউজার address bar-এ বাংলাটাই দেখায়, কিন্তু লিংক কপি করে ফেসবুক, ইমেইল বা SMS-এ পাঠালে প্রায়ই সেই লম্বা জটটাই চলে যায়।

বিষয়বাংলা slugইংরেজি slug
দেখতে/রেসিপি/খিচুড়ি — বাংলা পাঠকের কাছে স্পষ্ট/recipe/khichuri
শেয়ার করলেপ্রায়ই %E0%A6…-এর লম্বা জটযেমন আছে তেমন
হাতে টাইপকঠিন; য়/ড়-এর মতো অক্ষরে দুই রূপের ঝামেলাসহজ
সার্ভার ও টুলকিছু পুরোনো প্লাগইন, লগ বা analytics-এ গোলমাল হতে পারেসবখানে নিরাপদ

তাই বেশিরভাগ পেশাদার বাংলা সাইট পাতার শিরোনাম বাংলায় রাখে, কিন্তু URL-এর শেষের অংশ, অর্থাৎ slug, লেখে ছোট হাতের ইংরেজি বা উচ্চারণভিত্তিক বাংলিশে, হাইফেন দিয়ে: bangla-on-the-web.html — এই পাতার মতোই। সার্চ ইঞ্জিন পাতার ভাষা বোঝে লেখা দেখে, URL দেখে নয়; তাই ইংরেজি slug-এ বাংলা পাতার SEO-র ক্ষতি নেই। ডোমেইনেও বাংলা সম্ভব — বাংলাদেশের জন্য .বাংলা নামের দেশীয় ডোমেইন আছে, যা ভেতরে xn-- দিয়ে শুরু হওয়া Punycode রূপে চলে — তবে ব্যবসার মূল ঠিকানা হিসেবে এখনো .com.bd বা .com-ই প্রচলিত।

১০ · AI-এর যুগে — বাংলা পাতা AI-কে দিয়ে বানালে কী দেখবেন

AI টুলগুলো ইংরেজি পাতার কোডে যত পাকা, বাংলায় তত নয়। তাদের শেখার উপাদানের বেশিরভাগ ইংরেজি সাইট, তাই বাংলা পাতা চাইলেও প্রায়ই ইংরেজি অভ্যাস চলে আসে। চেহারা ঠিক দেখালেও নিচের ভুলগুলো চুপচাপ থেকে যায়:

AI-এর ভুলকেন সমস্যাঠিক উপায়
<html lang="en"> রেখে দেওয়াscreen reader ইংরেজি কণ্ঠে বাংলা পড়ে; অনুবাদ টুল বিভ্রান্তlang="bn", ইংরেজি অংশে lang="en"
meta charset head-এর মাঝে বা নেইপ্রথম 1024 বাইটে না পেলে ভাঙা লেখার ঝুঁকিhead-এর প্রথম লাইন
font-family: SutonnyMJ বা Google Fonts-এ নেই এমন ফন্টের লিংকANSI ফন্ট Unicode-এর জন্য নয়; বানানো লিংক কাজই করে নাGoogle Fonts-এ নিজে খুঁজে লিংক নিন; fallback দিন
line-height: 1.2, letter-spacingবাংলা চাপা, যুক্তাক্ষর ভাঙা দেখায়1.6–1.8, letter-spacing শূন্য
বাংলায় italic জোরকৃত্রিমভাবে হেলানো, অসুন্দররং বা মোটা অক্ষর
বাংলা slug বা ফাইলের নামশেয়ারে লম্বা জট, কিছু টুলে সমস্যাইংরেজি ছোট হাতের slug
ভুল বানান, অদৃশ্য অক্ষর, ভুল যুক্তাক্ষরAI-এর বাংলা লেখায় প্রায়ই থাকেনিজে পড়ে দেখুন, সন্দেহ হলে code point দেখুন

শর্তগুলো আগেই লিখে দিলে প্রথম উত্তরেই ভালো কোড পাবেন। শেষের লাইনে AI-কে নিজের সিদ্ধান্ত ব্যাখ্যা করতে বলা হয়েছে — তাতে ভুল ধরা সহজ হয়।

তুমি একজন অভিজ্ঞ বাংলা ওয়েব ডেভেলপার। আমার কোচিং সেন্টারের জন্য একটি বাংলা ল্যান্ডিং পেজের HTML আর CSS বানাও। শর্ত: ১. <html lang="bn">, head-এর প্রথম লাইনে <meta charset="utf-8">; ইংরেজি শব্দ বা নামে আলাদা lang="en". ২. বাংলা ফন্ট শুধু Google Fonts থেকে (Hind Siliguri), display=swap সহ; font-family-তে Noto Sans Bengali, Nirmala UI, Kohinoor Bangla আর sans-serif fallback দাও। কোনো ANSI ফন্ট (SutonnyMJ ইত্যাদি) নয়। ৩. মূল লেখায় line-height 1.7, font-size অন্তত 17px; বাংলা লেখায় italic আর letter-spacing দেবে না। ৪. ধাপের তালিকা ol-এ, নম্বর list-style-type: bengali দিয়ে বাংলা অঙ্কে। ৫. সব ফাইল আর লিংকের নাম ছোট হাতের ইংরেজিতে, হাইফেন দিয়ে। শেষে সহজ বাংলায় বলো, কোন শর্ত কোথায় মেনেছ, আর কোন ফন্টের নাম নিয়ে তুমি নিশ্চিত নও।

AI-এর দেওয়া বাংলা পাতা হাতে পেলে — যাচাইয়ের চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা ফ্রন্ট-এন্ড ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে:

  • Unicode আর UTF-8-এর পার্থক্য? — Unicode প্রতিটি অক্ষরের নম্বর (code point) দেয়; UTF-8 সেই নম্বরকে বাইটে লেখার একটি নিয়ম।
  • <meta charset> কোথায় লিখতে হয়, কেন? — head-এর শুরুতে, ফাইলের প্রথম 1024 বাইটের ভেতরে; ব্রাউজার ততটুকুই খোঁজে।
  • lang attribute কী কাজে লাগে? — screen reader-এর উচ্চারণ, ফন্ট বাছাই, বানান-পরীক্ষা, হাইফেন, অনুবাদ; WCAG ৩.১.১ ও ৩.১.২ এটি চায়।
  • ZWNJ কী? — অদৃশ্য একটি অক্ষর (U+200C), যা জোড়া লাগতে দেয় না; বাংলায় যুক্তাক্ষরের বদলে স্পষ্ট হসন্ত দেখায়।
  • font-family-তে কয়েকটি ফন্ট লেখা হয় কেন? — fallback: প্রথমটি না পেলে বা তাতে অক্ষর না থাকলে ব্রাউজার পরেরটি নেয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে ট্যাগ-মুখস্থকারী থেকে ভাষা-সচেতন ওয়েব নির্মাতা বানাবে।

প্র ০১বাংলাদেশে বিজয়ের ANSI ফন্ট থেকে Unicode-এ যেতে এত বছর লাগল কেন — আর ওয়েবের জন্য এই পরিবর্তন এত জরুরি কেন?

গল্পটা শুরু ১৯৮০-এর দশকের শেষে। তখনকার কম্পিউটারের ফন্ট ব্যবস্থা মূলত ২৫৬টি ঘরের — ইংরেজি অক্ষর, অঙ্ক আর কিছু চিহ্নেই প্রায় সব ঘর ভরা। বাংলার জন্য আলাদা জায়গা ছিল না। সমাধান হলো কৌশলে: ইংরেজি অক্ষরের ঘরগুলোতেই বাংলা আকার এঁকে দেওয়া। ১৯৮৮ সালে মোস্তফা জব্বারের বিজয় কি-বোর্ড আর তার সাথের SutonnyMJ-এর মতো ANSI ফন্ট এই পথে বাংলা কম্পিউটিংকে প্রেস, পত্রিকা আর অফিসে পৌঁছে দেয়। এটা ছিল তার সময়ের দারুণ বাস্তব সমাধান।

সমস্যা ছিল একটাই — লেখার ভেতরে আসলে বাংলা নেই। “আমি” জমা থাকে “Avwg” হিসেবে, আর কার-চিহ্ন টাইপ করতে হয় পর্দায় যে ক্রমে দেখা যায় সেই ক্রমে, উচ্চারণের ক্রমে নয়। একই কম্পিউটারে, একই ফন্টে সব ঠিক; ফাইল অন্য কোথাও গেলেই হিজিবিজি। Unicode ১৯৯১ সালেই বাংলাকে নিজের ঘর দিয়েছিল, কিন্তু Windows-এ জটিল লিপি ঠিকভাবে সাজানোর ব্যবস্থা, ভালো Unicode বাংলা ফন্ট আর সহজ কি-বোর্ড আসতে আরও এক দশকের বেশি লাগে। ২০০৩ সালে বিনামূল্যের অভ্র কি-বোর্ড আসার পর সাধারণ মানুষের কাছে Unicode সহজ হয়। তবু ছাপাখানার অভ্যাস, পুরোনো ফাইলের পাহাড় আর প্রশিক্ষিত টাইপিস্টদের দক্ষতার কারণে ANSI বহু বছর টিকে থাকে।

ওয়েবের জন্য Unicode কেন অপরিহার্য? কারণ ওয়েবের প্রতিটি সুবিধা “লেখার ভেতরে আসল অক্ষর আছে” এই ধারণার ওপর দাঁড়িয়ে। সার্চ ইঞ্জিন “আমি” খোঁজে, “Avwg” নয়। screen reader অক্ষর পড়ে, ফন্টের ছবি নয়। কপি-পেস্ট, অনুবাদ, AI দিয়ে সারাংশ, ফোনের নিজের ফন্টে দেখানো — সবই Unicode ছাড়া ভেঙে পড়ে। ANSI লেখা ওয়েবে দেওয়া মানে লেখাটিকে কার্যত ছবিতে বন্দি করা: চোখে দেখা যায়, কিন্তু কোনো যন্ত্র বোঝে না।

মূল উপলব্ধি: একটি প্রযুক্তি তার সময়ের সঠিক সমাধান হতে পারে, কিন্তু পরের যুগের ভিত হতে পারে না। ওয়েবে বাংলা মানে Unicode বাংলা — পুরোনো লেখা থাকলে আগে রূপান্তর, তারপর প্রকাশ।

প্র ০২UTF-8-এ প্রতিটি বাংলা অক্ষর ৩ বাইট, UTF-16-এ মাত্র ২ বাইট। তাহলে বাংলা সাইটের জন্যও UTF-8 কেন — আর এটি ওয়েবে জিতল কীভাবে?

শুধু বাংলা অক্ষর গুনলে UTF-16 সত্যিই ছোট। কিন্তু একটি HTML ফাইল খুলে দেখুন — ট্যাগ, attribute, CSS-এর class, URL, JavaScript — এগুলোর প্রায় সবই ইংরেজি অক্ষর। UTF-8-এ এগুলো ১ বাইট, UTF-16-এ ২ বাইট। একটি সাধারণ বাংলা ওয়েবপেজে মার্কআপের অংশ প্রায়ই বাংলা লেখার চেয়ে বেশি, তাই মোট হিসাবে UTF-8 সাধারণত ছোট বা কাছাকাছি হয়।

আসল জয় অবশ্য আকারে নয়, নকশায়। ১৯৯২ সালে Ken Thompson আর Rob Pike — বিখ্যাত গল্প অনুযায়ী এক রেস্তোরাঁর কাগজের ম্যাটে — UTF-8-এর নকশা করেন। এর তিনটি গুণ ওয়েবকে জিতিয়ে দেয়। এক, ASCII-র সাথে পুরো মিল: পুরোনো ইংরেজি ফাইল বিনা পরিবর্তনে বৈধ UTF-8, তাই পুরোনো সফটওয়্যার ভাঙেনি। দুই, বাইটের ক্রম (endianness) নিয়ে কোনো ঝামেলা নেই — UTF-16-এর মতো বড়-ছোট ক্রমের BOM লাগে না। তিন, নিজে নিজে সামলে নেয় (self-synchronizing): মাঝখান থেকে পড়া শুরু করলেও পরের অক্ষরের শুরু খুঁজে নেওয়া যায়, কারণ প্রতিটি বাইট দেখেই বোঝা যায় এটি অক্ষরের শুরু না মাঝের অংশ — বাংলার e0 শুরু, a6, ac ধারাবাহিক অংশ।

এরপর বাস্তবতা নিজেই সিদ্ধান্ত নেয়। ২০০০-এর দশকের শেষ দিকে ওয়েবে UTF-8 সবচেয়ে প্রচলিত encoding হয়ে যায়, আর আজ ওয়েব জরিপে প্রায় সব সাইটই UTF-8. WHATWG-এর HTML আর Encoding স্পেসিফিকেশন এখন নতুন পাতার জন্য সরাসরি UTF-8 বাধ্যতামূলক বলে।

মূল উপলব্ধি: মানের লড়াইয়ে প্রায়ই জেতে সেই নিয়ম, যা পুরোনোর সাথে মিলে চলে আর ভুল করা কঠিন করে — কয়েক বাইট বাঁচানো নয়। বাংলা সাইটের জন্যও UTF-8 ছাড়া অন্য কিছু বেছে নেওয়ার কোনো কারণ নেই।

প্র ০৩পাতাটি তো বাংলায় লেখা, চোখেই দেখা যায় — তাহলে lang="bn" না লিখলে আসলে কার কী ক্ষতি হয়?

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

  • দৃষ্টিপ্রতিবন্ধী পাঠক: screen reader lang দেখে কণ্ঠ বাছে। lang না থাকলে বা ভুলভাবে en থাকলে সে ইংরেজি কণ্ঠ ব্যবহার করে — বাংলা অক্ষর হয় এক এক করে নাম ধরে পড়ে, নয়তো এড়িয়ে যায়। উল্টোটাও ঘটে: বাংলা পাতার ভেতরের “Facebook” শব্দ বাংলা কণ্ঠে অদ্ভুত শোনায়, যদি সেখানে lang="en" না থাকে। এজন্যই WCAG-এর ৩.১.১ আর ৩.১.২ নিয়ম।
  • ফন্ট আর আকার: চীনা, জাপানি আর কোরিয়ান ভাষায় হাজারো অক্ষর Unicode-এ একই code point ভাগ করে (Han unification), অথচ প্রতিটি দেশের লেখার রীতি আলাদা। lang না দিলে জাপানি পাঠক চীনা ধাঁচের আকার দেখতে পারেন। বাংলায় এমন বড় সমস্যা নেই, কিন্তু ব্রাউজার fallback ফন্ট বাছতে lang-কে সূত্র হিসেবে ব্যবহার করে।
  • লেখার নিয়ম: তুর্কি i-এর উদাহরণ লাইভ এডিটরে দেখেছেন। CSS-এর hyphens, উদ্ধৃতিচিহ্ন, বড়-ছোট হাতের রূপান্তর — অনেক কিছুই ভাষা-নির্ভর।
  • যন্ত্রের বোঝা: ব্রাউজারের অনুবাদ প্রস্তাব, সার্চ ইঞ্জিন আর AI টুল lang-কে একটি সংকেত হিসেবে দেখে। ভুল সংকেত মানে ভুল অনুমান।

খরচের দিকটা দেখুন: পুরো পাতার জন্য মাত্র কয়েকটি অক্ষর — lang="bn". সুবিধা পান হয়তো আপনার পাঠকদের অল্প কয়েকজন, কিন্তু তাঁদের কাছে পার্থক্যটা “পাতাটি পড়া যায়” আর “পড়া যায় না”-র মধ্যে।

মূল উপলব্ধি: HTML-এর অনেক attribute চোখে কিছু বদলায় না, কিন্তু যন্ত্র আর সহায়ক প্রযুক্তিকে সত্য জানায়। lang সেই সত্যগুলোর মধ্যে সবচেয়ে সস্তা আর সবচেয়ে বেশি অবহেলিত।

প্র ০৪সুন্দর ওয়েব ফন্ট নাকি পাঠকের নিজের সিস্টেম ফন্ট — বাংলা সাইটের জন্য কোনটা ঠিক, আর Google Fonts নিজের সার্ভারে রাখা ভালো কি না?

এটা চেহারা বনাম গতি আর নিয়ন্ত্রণের হিসাব। ওয়েব ফন্টে আপনার সাইট সব ফোনে একরকম দেখায়, ব্র্যান্ডের একটি পরিচয় তৈরি হয়। কিন্তু ফন্ট একটি বাড়তি ডাউনলোড — আর বাংলা ফন্ট ফাইল ইংরেজির চেয়ে ভারী, কারণ যুক্তাক্ষর, ফলা আর কার মিলিয়ে কয়েকশো বাড়তি glyph আর তাদের নিয়ম থাকে। ধীর মোবাইল ডেটায় এর মানে প্রথমবার পাতা খুলতে দেরি, বা লেখা হঠাৎ এক ফন্ট থেকে আরেক ফন্টে লাফ দেওয়া। সিস্টেম ফন্টে কোনো ডাউনলোড নেই, কিন্তু চেহারা আপনার হাতে নেই — পুরোনো Windows-এর Vrinda আর নতুন ফোনের Noto দেখতে অনেক আলাদা।

মাঝামাঝি পথটাই সাধারণত সেরা: একটি বাংলা ওয়েব ফন্ট, সর্বোচ্চ দুই-তিনটি ওজন, font-display: swap, আর ভালো একটি fallback তালিকা — যাতে ফন্ট না এলেও লেখা পরিষ্কার পড়া যায়।

Google Fonts বনাম নিজের সার্ভার (self-hosting) নিয়ে আগে একটি জনপ্রিয় যুক্তি ছিল: “অনেক সাইট একই Google ফন্ট ব্যবহার করে, তাই পাঠকের ব্রাউজারে আগে থেকেই ক্যাশে থাকবে।” কিন্তু ২০২০ সালের দিক থেকে প্রধান ব্রাউজারগুলো গোপনীয়তার কারণে ক্যাশ সাইট অনুযায়ী আলাদা করে ফেলে (cache partitioning), তাই সেই সুবিধা আর নেই। উল্টো দিকে, নিজের সার্ভারে রাখলে আলাদা ডোমেইনে সংযোগ লাগে না, আর ব্যবহারকারীর IP ঠিকানা তৃতীয় পক্ষের কাছে যায় না — ২০২২ সালে জার্মানির একটি আদালত এক সাইটকে ঠিক এই কারণে, অনুমতি ছাড়া Google Fonts থেকে ফন্ট নামানোর জন্য জরিমানা করেছিল। অন্যদিকে Google Fonts নিজে থেকেই ফাইল ভাগ করে, unicode-range লেখে, ব্রাউজার বুঝে সবচেয়ে ছোট ফরম্যাট পাঠায় — নিজে করলে এ কাজগুলো আপনাকেই করতে হবে।

মূল উপলব্ধি: ফন্ট শুধু সাজ নয়, একটি নেটওয়ার্ক অনুরোধ আর একটি নির্ভরতা। সুন্দর চেহারার দাম পাঠক দেন ডেটা আর সময়ে — সেটা মাথায় রেখে বাছুন।

প্র ০৫কম্পিউটার বাংলা শব্দ “ঠিক” ক্রমে সাজাতে পারে না কেন — code point-এর ক্রম, অভিধানের ক্রম আর normalization-এর সম্পর্ক কী?

সাধারণ প্রোগ্রাম শব্দ সাজায় নম্বর ধরে: প্রথম অক্ষরের code point তুলনা, সমান হলে দ্বিতীয়টি, এভাবে। Unicode-এর বাংলা ঘর মোটামুটি বর্ণমালার ক্রমে সাজানো — অ, আ, … ক, খ — তাই অনেক ক্ষেত্রে ফল ঠিকই আসে। কিন্তু কয়েকটি জায়গায় তা ভেঙে পড়ে।

  • পরে যোগ হওয়া অক্ষর: ড়, ঢ়, য়-এর একক code point (U+09DC, U+09DD, U+09DF) আর খণ্ড-ত (U+09CE) ঘরের পেছনের দিকে, হ-এর পরে। তাই একক য় দিয়ে লেখা “বয়স” code point-এর সাজানোয় “বহু”-র পরে চলে যায়, অথচ য + নুকতা দিয়ে লেখা “বয়স” আগে — লাইভ স্ক্রিপ্টে নিজেই দেখেছেন।
  • যুক্তাক্ষর: ক্ষ আলাদা অক্ষর নয়, ক + ্ + ষ; তাই কম্পিউটারের চোখে এটি ক-এর দলের, হসন্তের নম্বর অনুযায়ী জায়গা পায়। কোনো প্রথাগত তালিকা ক্ষ-কে আলাদা বর্ণ ধরলে দুই ক্রম মিলবে না।
  • অনুস্বার, চন্দ্রবিন্দু, কারের ক্রম: “কং” আর “কা” — কোনটা আগে, তা অভিধান-প্রণেতাদের নিয়ম; code point-এর ক্রম সেই নিয়ম জানে না।

এখানে দুটি আলাদা হাতিয়ার কাজ করে। প্রথমটি normalization — একই লেখার বিভিন্ন রূপকে এক রূপে আনা (NFC, NFD). এটি ছাড়া তুলনাই অর্থহীন। দ্বিতীয়টি collation — ভাষার নিয়ম মেনে সাজানো। Unicode Collation Algorithm আর CLDR নামের ভাষা-তথ্যভান্ডারে বাংলার জন্য সাজানোর নিয়ম আছে; JavaScript-এর Intl.Collator('bn'), ডেটাবেসের collation সেটিং আর ICU নামের লাইব্রেরি এগুলো ব্যবহার করে। collation ভেতরে ভেতরে normalization-ও করে নেয়, তাই দুই রূপের “বয়স” একই জায়গায় বসে।

তবু কোনো যন্ত্রের নিয়ম সব অভিধানের সাথে মিলবে, এমন নিশ্চয়তা নেই — তাই আসল ডেটা দিয়ে পরীক্ষা করুন।

মূল উপলব্ধি: “দেখতে এক” আর “কম্পিউটারের কাছে এক” আলাদা কথা। বাংলা ডেটায় তুলনার আগে normalize, সাজানোর জন্য collation — আর ফলাফল মানুষের চোখে যাচাই।

অনুশীলন

প্রথম দুটিতে পর্যবেক্ষণ আর বিশ্লেষণ; তৃতীয়টিতে মার্কআপ, চতুর্থটিতে একটি ছোট CSS লিখবেন।

  1. ইচ্ছা করে লেখা ভাঙুন: নিজের কম্পিউটারে test.html বানিয়ে লিখুন <p>আমি বাংলায় গান গাই</p> — কোনো meta charset ছাড়া। VS Code-এর নিচের স্ট্যাটাস বারে “UTF-8” চেপে “Save with Encoding” → “Western (Windows 1252)” বেছে সেভ করার চেষ্টা করুন। কী ঘটে? তারপর UTF-8-এ সেভ করে ব্রাউজারে খুলুন, আর ফাইলের শুরুতে <meta charset="windows-1252"> যোগ করে আবার খুলুন।

    windows-1252-এ বাংলা অক্ষর রাখার কোনো জায়গা নেই — তাই সেভ করলে বাংলা অক্ষরগুলো “?” হয়ে স্থায়ীভাবে হারিয়ে যায় (এডিটর প্রায়ই সতর্ক করে)। এটাই টেবিলের “????” সারি। UTF-8-এ সেভ করা ফাইল meta ছাড়া খুললে অনেক ব্রাউজার লেখা দেখে ঠিক অনুমান করে ফেলে, আবার না-ও পারে — অনুমানের ওপর ভরসা করা যায় না। আর meta charset="windows-1252" দিলে নিশ্চিতভাবে “আমি…”-এর মতো mojibake — কারণ বাইট UTF-8, কিন্তু ঘোষণা মিথ্যা। শিক্ষা: ফাইলের আসল encoding আর ঘোষণা এক হতে হবে, আর দুটোই UTF-8.

  2. উপসর্গ দেখে রোগ চিনুন: চারটি অভিযোগ এসেছে — (ক) “ওয়েবসাইটে সব লেখা ঠদিয়ে ভরা” (খ) “অফিসের পুরোনো নোটিশ সাইটে দিলাম, এখন ‡bvwUk দেখাচ্ছে” (গ) “পুরোনো ফোনে কিছু যুক্তাক্ষর ভেঙে আলাদা হসন্তসহ দেখায়, নতুন ফোনে ঠিক” (ঘ) “ফর্মে বাংলা নাম জমা দিলে ডেটাবেসে ??? হয়ে যায়”। প্রতিটির কারণ কী?

    (ক) UTF-8 ফাইল ভুল encoding-এ পড়া হচ্ছে — meta charset নেই/ভুল, বা সার্ভারের header ভুল charset পাঠাচ্ছে। DevTools-এর Network-এ header দেখুন। (খ) লেখাটি ANSI (বিজয়) — আসলে ইংরেজি অক্ষর; ফন্ট নেই তাই আসল রূপ দেখা যাচ্ছে। Unicode-এ রূপান্তর করে আবার দিন। (গ) encoding ঠিক, সমস্যা ফন্ট বা shaping-এ — পুরোনো ফোনের সিস্টেম ফন্টে ওই যুক্তাক্ষরের নিয়ম নেই। একটি ভালো বাংলা ওয়েব ফন্ট দিলে সমাধান হয়। (ঘ) ডেটাবেস, টেবিল বা সংযোগের encoding বাংলা ধরে না — সেভের সময়ই অক্ষর মুছে যাচ্ছে। সব স্তর UTF-8 (MySQL-এ utf8mb4) করুন; ইতিমধ্যে হারানো লেখা ফেরানো যাবে না।

  3. মিশ্র ভাষার অনুচ্ছেদ: একটি বাংলা পাতায় এই অনুচ্ছেদটি সঠিক lang আর dir দিয়ে লিখুন: “রবীন্দ্রনাথের Gitanjali ১৯১৩ সালে নোবেল পায়। উর্দুতে شکریہ মানে ধন্যবাদ।” কোন অংশে কোন attribute লাগবে?

    পাতায় আগেই <html lang="bn"> আছে, তাই অনুচ্ছেদে আলাদা lang লাগবে না। বইয়ের নাম ইংরেজি, উর্দু শব্দটি ডান থেকে বাঁয়ে:

    <p>রবীন্দ্রনাথের <cite lang="en">Gitanjali</cite> ১৯১৩ সালে নোবেল পায়।
      উর্দুতে <span lang="ur" dir="rtl">شکریہ</span> মানে ধন্যবাদ।</p>

    প্রিভিউতে অনুচ্ছেদটি স্বাভাবিক দেখায় — lang চোখে কিছু বদলায় না, বদলায় উচ্চারণ। বইয়ের নাম <cite>-এ (পর্ব ০৪) তির্যক হয়ে আসে, সাথে lang="en"; উর্দু শব্দে lang="ur" আর dir="rtl". চেষ্টা করুন: dir="rtl" মুছে দেখুন — এক শব্দে পার্থক্য চোখে পড়ে না, কারণ উর্দু অক্ষর নিজেই ডান-থেকে-বাঁ ধরনের; dir লাগে আশপাশের সংখ্যা আর বিরামচিহ্ন ঠিক জায়গায় রাখতে। শব্দটির পরে ১২৩ যোগ করে দুই অবস্থায় তুলনা করুন।

  4. একটি বাংলা ব্লগের লেখার স্টাইল: শুধু body-র জন্য CSS লিখুন — ফন্ট Hind Siliguri (Google Fonts), তার পরে উপযুক্ত fallback, মূল লেখা 17px, লাইনের উচ্চতা বাংলার উপযোগী। কোন কোন জিনিস ইচ্ছা করে দেবেন না?
    body {
      font-family: "Hind Siliguri", "Noto Sans Bengali",
                   "Nirmala UI", "Kohinoor Bangla", sans-serif;
      font-size: 17px;
      line-height: 1.7;
    }

    head-এ Google Fonts-এর লিংক (display=swap-সহ) আর preconnect লাগবে। ইচ্ছা করে দেবেন না: letter-spacing (যুক্তাক্ষর ভাঙে), বাংলা লেখায় font-style: italic (কৃত্রিম হেলানো), SutonnyMJ-এর মতো ANSI ফন্টের নাম, আর একককসহ স্থির line-height: 24px (লেখা বড় করলে লাইন বাড়বে না)। এককহীন 1.7 font-size-এর সাথে নিজে বাড়ে-কমে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি HTML ফাইলের encoding ঠিক করতে ব্রাউজার যে ক্রমে সূত্রগুলো দেখে, সেভাবে সাজান (▲▼ চেপে সরান)

  1. ফাইলের শুরুতে BOM
  2. সার্ভারের Content-Type header-এর charset
  3. প্রথম 1024 বাইটের <meta charset>
  4. লেখা বা ভাষা-সেটিং দেখে অনুমান

একটি ওয়েবসাইটে বাংলার জায়গায় “বাংলা”-র মতো চিহ্ন দেখা যাচ্ছে। সবচেয়ে সম্ভাব্য কারণ কোনটি?

  • কম্পিউটারে বাংলা ফন্ট নেই
  • UTF-8-এ লেখা ফাইলটি ব্রাউজার windows-1252-এর মতো ভুল encoding-এ পড়ছে
  • লেখাটি বিজয়ের ANSI ফন্টে টাইপ করা
  • <html>-এ lang="bn" লেখা নেই

Unicode-এ বাংলা যুক্তাক্ষর “ক্ষ” কীভাবে জমা থাকে?

  • ক্ষ-এর নিজস্ব একটি code point হিসেবে
  • ক আর ষ — দুটি code point
  • ক + ZWJ + ষ
  • ক + হসন্ত + ষ — তিনটি code point

একটি বাংলা পাতায় ইংরেজি বইয়ের নাম আছে। screen reader যেন সেটি ইংরেজি উচ্চারণে পড়ে, সেজন্য কী করা উচিত?

  • বইয়ের নামের element-এ lang="en" দেওয়া, পাতার <html>-এ lang="bn" রেখে
  • পুরো পাতার <html>-এ lang="en" দেওয়া
  • বইয়ের নামে dir="ltr" দেওয়া
  • বইয়ের নামটি একটি ইংরেজি ফন্টে দেখানো

বাংলায় “র‍্যাব” শব্দে র-এর পাশে য-ফলা দেখাতে (রেফ না বসিয়ে) কোন অদৃশ্য অক্ষর ব্যবহার করা হয়?

  • ZWNJ (U+200C)
  • &nbsp; (non-breaking space)
  • ZWJ (U+200D)
  • BOM (U+FEFF)

CSS-এ font-family: "Hind Siliguri", "Noto Sans Bengali", sans-serif; লেখা আছে, কিন্তু Hind Siliguri লোড হয়নি। ব্রাউজার কী করবে?

  • লেখা দেখাবে না, ফাঁকা থাকবে
  • তালিকার পরের ফন্ট Noto Sans Bengali, সেটিও না থাকলে সিস্টেমের sans-serif ব্যবহার করবে
  • ইন্টারনেট থেকে অন্য কোনো বাংলা ফন্ট নিজে খুঁজে নামাবে
  • পুরো CSS ফাইল বাতিল করে দেবে

একটি বাংলা সাইটে ক্রেতার খোঁজা “বয়স” আর ডেটাবেসের “বয়স” দেখতে হুবহু এক, তবু মিলছে না। সবচেয়ে সঠিক সমাধান কোনটি?

  • সার্চ বক্সে অন্য বাংলা ফন্ট দেওয়া
  • পাতায় lang="bn-BD" লেখা
  • ক্রেতাদের নির্দিষ্ট একটি কি-বোর্ড ব্যবহার করতে বলা
  • সেভের আগে আর খোঁজার আগে দুই লেখাকেই Unicode normalization (যেমন NFC) করা

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. UTF-8 সব স্তরে — ফাইল, <meta charset="utf-8"> (head-এর শুরুতে), সার্ভারের header আর ডেটাবেস। ব্রাউজারের ক্রম: BOM → header → meta → অনুমান।
  2. ভাঙা লেখা চেনা: ঠ= ভুল encoding-এ পড়া, Avwg = ANSI লেখা, ???? = সেভের সময় হারানো, □ = ফন্টে অক্ষর নেই।
  3. <html lang="bn">, ভিন্ন ভাষার অংশে আলাদা lang; RTL ভাষায় dir="rtl", বাংলায় dir লাগে না।
  4. যুক্তাক্ষর = ব্যঞ্জন + হসন্ত + ব্যঞ্জন, আঁকে ফন্ট; ZWNJ জোড়া আটকায়, ZWJ বিশেষ রূপ (র‍্য) চায়।
  5. বাংলা ওয়েব ফন্ট + fallback তালিকা, line-height 1.6–1.8; বাংলা অঙ্ক সরাসরি বা list-style-type: bengali; তুলনার আগে normalize; URL-এ ইংরেজি slug.

পরের পর্বে: মডিউল ২-এর সব শেখা একসাথে — form, semantic layout, অ্যাক্সেসিবিলিটি, head-meta আর বাংলার সঠিক ব্যবহার মিলিয়ে কয়েক পাতার একটি পূর্ণ HTML ওয়েবসাইট বানাবেন। এটাই এই কোর্সের প্রথম মাইলস্টোন।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান