IlmHamroh
JavaScript Full-stack/12-qism. JavaScript: ilg'or mavzular va kod sifati18/44-dars17 daqiqa
Mundarija (35)

JavaScript Intl.Collator va Intl.Segmenter: o'zbek alifbosi bo'yicha saralash va matnni bo'lish

Qisqacha: Intl.Collator — til qoidalari bo'yicha taqqoslash: ismlar.sort(new Intl.Collator("uz").compare) o'zbek alifbosiga ko'ra saralaydi — z dan keyin o', g', sh, ch. Sozlamalar: sensitivity katta-kichik harf va belgilarni hisobga olish-olmaslikni, numeric: true — rasm2 ning rasm10 dan oldin turishini belgilaydi. Ikkinchi asbob — Intl.Segmenter — matnni ko'z ko'radigan bo'laklarga bo'ladi: grafemalar (emoji bitta belgi), so'zlar (so'm — bitta so'z) va gaplar. Chrome 154 da o'zbekcha saralash qoidalari yo'q.

Bu darsda

  • Intl.Collator bilan ismlar ro'yxatini o'zbek alifbosi bo'yicha saralaysiz va ICU bu alifboni aslida qanday tushunishini bilasiz.
  • sensitivity, numeric, caseFirst va ignorePunctuation sozlamalarini ishlatasiz.
  • Intl.Segmenter bilan emoji'li matnni to'g'ri sanaysiz va kesasiz.
  • Matndagi so'zlar va gaplarni split siz, apostrofni buzmasdan ajratasiz.
  • Node 24 va Chrome 154 dagi farqlarni va brauzerda nima qilishni bilasiz.

Oldin bilishingiz kerak: Intl.PluralRules, ListFormat, DisplayNames, sort va taqqoslash funksiyasi, Unicode va emoji: matn ichki tuzilishi.

1. Nega bu kerak?

Bu darsda ikki eski va'dani bajaramiz.

Birinchisi — sort va taqqoslash funksiyasi darsidan. U yerda localeCompare(b, "uz") va Intl.Collator ni ko'rdik, numeric: true ni ishlatdik va "boshqa sozlamalarini keyin ko'ramiz" dedik. «Bahor» administratoriga mijozlar ro'yxati alifbo tartibida kerak. Oddiy sort esa buni bilmaydi:

js
const mijozlar = ["Zafar", "olim", "Oybek", "Bobur", "anvar"];

console.log(mijozlar.sort().join(", "));
// Bobur, Oybek, Zafar, anvar, olim

Katta harflar oldin, kichiklari keyin — chunki sort belgilarni Unicode raqami bo'yicha solishtiradi.

Ikkinchisi — Unicode va emoji darsidan. U yerda oila emoji'sining uzunligi 8 chiqqan edi, Intl.Segmenter esa 1 degan edi. O'shanda "tayyor retsept" dedik — bugun uni to'liq ochamiz. «Bahor» sharhlarida 300 belgi chegarasi bor, va mehmonlar emoji'ni yaxshi ko'radi. Belgilarni noto'g'ri sanasak, emoji o'rtasidan kesilib, � belgisi chiqadi.

2. Intl.Collator — tilga mos taqqoslash

Diqqat: Bu darsdagi "Konsolda" natijalari Node 24 da olingan. «Ishga tushir» tugmasi kodni brauzeringizda bajaradi, Chrome'da esa uz saralash natijalari boshqacha chiqadi. Sababini «Node va Chrome: saralash» bo'limida ochamiz.

2.1 Asosiy ishlatish

js
const uzTartib = new Intl.Collator("uz");
const mijozlar = ["Zafar", "olim", "Oybek", "Bobur", "anvar"];

console.log(mijozlar.sort(uzTartib.compare).join(", "));
// anvar, Bobur, olim, Oybek, Zafar

Collator nomi inglizcha collation so'zidan olingan. Collation (saralash qoidalari) — bir tilda qaysi harf qaysisidan oldin turishi, katta-kichik harf va belgilar qanday hisoblanishi haqidagi qoidalar to'plami. Xuddi lug'at tuzuvchi kabi: u so'zlarni qaysi tartibda terishni o'sha tilning alifbosidan biladi.

compare — tayyor taqqoslash funksiyasi: a oldin tursa manfiy son, keyin tursa musbat, teng bo'lsa 0 qaytaradi. sort aynan shunday funksiyani kutadi. Endi katta-kichik harf tartibni buzmaydi.

localeCompare ham xuddi shu qoidalarni ishlatadi: "olma".localeCompare("o'rik", "uz") → -1. Farqi — localeCompare har chaqiruvda qoidalarni qaytadan tayyorlaydi. Ming ismni saralaganda u o'n minglab marta chaqiriladi. Collator esa qoidalarni bir marta tayyorlaydi. NumberFormat darsida ko'rgan qoida bu yerda ham ishlaydi: bir marta yarating, ko'p ishlating.

2.2 O'zbek alifbosi: ICU nima qiladi

O'zbek lotin alifbosida z oxirgi harf emas. Undan keyin o', g', sh, ch keladi. Biz Node 24 (ICU 78.3) da sinab ko'rdik:

js
const uzTartib = new Intl.Collator("uz");
const ismlar = [
  "Zafar", "O'tkir", "Olim", "G'ayrat",
  "Shuhrat", "Sardor", "Chori", "Gulnora",
];

console.log(ismlar.sort(uzTartib.compare).join(", "));
// Gulnora, Olim, Sardor, Zafar, O'tkir, G'ayrat, Shuhrat, Chori

Har ism o'z joyida:

  • O'tkir — Olim dan keyin emas, Zafar dan keyin: o' — alohida harf.
  • Shuhrat Sardor dan keyin emas, oxirida: sh ham alohida harf.
  • Chori eng oxirida: ch — alifboning oxirgi harfi.

Diagrammada ICU'dagi tartib:

flowchart LR
  A["a, b, d ... x, y, z"] --> B["o'"]
  B --> C["g'"]
  C --> D["sh"]
  D --> E["ch"]
  F["ng = n + g"] -.-> A

Nimaga qarang: lotin alifbosining 26 harfi odatdagi tartibda, keyin to'rtta maxsus harf. ng esa alohida harf hisoblanmaydi — u oddiy n va g sifatida saralanadi. Rasmiy o'zbek alifbosida ng ham harf, lekin ICU qoidalarida u yo'q. Amalda bu kam seziladi: ng bilan boshlanadigan so'z deyarli yo'q.

2.3 Apostrofning uch xil yozilishi

O'zbek matnida o' uch xil yoziladi: oddiy apostrof ', teskari vergul ‘ (U+2018) va o'zbek harfi belgisi ʻ (U+02BB). Biz oldingi darslarda ko'rdik — ICU'ning o'zi ham ikkitasini aralash ishlatadi. Collator uchalasini bitta harf deb biladi:

js
const uzTartib = new Intl.Collator("uz");

console.log(uzTartib.compare("o'rik", "oʻrik")); // 0
console.log(uzTartib.compare("o'rik", "o‘rik")); // 0
console.log(uzTartib.compare("o'rik", "o`rik")); // 1

0 — "teng". Uchinchi qator — backtick (`) — boshqa: u o'zbek harfining qismi emas, oddiy tinish belgisi. Klaviaturada apostrofni topa olmagan odam ba'zan aynan shuni yozadi. Bunday matnni saqlashdan oldin tozalash kerak — Matnni o'zgartirish va tozalash darsidagi replaceAll bilan.

2.4 Tutuq belgisi

O'zbek tilida apostrof yana bir ish qiladi — tutuq belgisi: Is'hoq, Mas'ud, san'at. Is'hoq da s va h alohida tovushlar, sh emas. ICU buni farqlaydimi?

js
const uzTartib = new Intl.Collator("uz");
const ismlar = ["Ishoq", "Ismoil", "Is'hoq"];

console.log(ismlar.sort(uzTartib.compare).join(", "));
// Is'hoq, Ismoil, Ishoq

Farqlaydi. Ishoq (sh bilan) — Ismoil dan keyin, chunki sh alifbo oxirida. Is'hoq da esa apostrof s va h ni ajratdi — sh harfi hosil bo'lmadi. Apostrof tinish belgisi sifatida harflardan oldin turadi, shuning uchun Is'hoq birinchi.

Tekshirib ko'ring: ["sut", "shirin", "sabzi", "soat"] ni Intl.Collator("uz") bilan saralasangiz, shirin qayerda turadi?

Javob

Oxirida: sabzi, soat, sut, shirin. sh — alohida harf va u s bilan boshlanadigan hamma so'zlardan keyin keladi. Ingliz tili qoidalari bilan esa sabzi, shirin, soat, sut bo'lardi.

3. Collator sozlamalari

3.1 sensitivity — nimani farq deb hisoblash

Qidiruvda "olma" deb yozgan odam "Olma" ni ham topishni xohlaydi. sensitivity qaysi farqlar muhimligini belgilaydi:

sensitivity "a" va "A" "a" va "á"
"base" teng teng
"accent" teng farqli
"case" farqli teng
"variant" (saralashda standart) farqli farqli

"base" — faqat asosiy harf muhim. Qidiruv va takroriy yozuvlarni topish uchun eng foydalisi:

js
const qidiruv = new Intl.Collator("uz", { sensitivity: "base" });
const mijozlar = ["Oʻtkir", "O‘tkir", "Olim"];

const topildi = mijozlar.filter(
  (m) => qidiruv.compare(m, "o'tkir") === 0,
);
console.log(topildi); // [ 'Oʻtkir', 'O‘tkir' ]

Kichik harf va oddiy apostrof bilan yozilgan so'rov ikki xil yozilgan ismni ham topdi. === bilan bu mumkin emas edi.

3.2 numeric — sonlarni son sifatida

sort darsida ko'rgan edik:

js
const oddiy = new Intl.Collator("uz");
const raqamli = new Intl.Collator("uz", { numeric: true });
const fayllar = ["rasm10.png", "rasm2.png", "rasm1.png"];

console.log(fayllar.toSorted(oddiy.compare).join(", "));
console.log(fayllar.toSorted(raqamli.compare).join(", "));

Konsolda:

text
rasm1.png, rasm10.png, rasm2.png
rasm1.png, rasm2.png, rasm10.png

Fayl nomlari, stol raqamlari, versiyalar (v2, v10) — hammasida numeric: true kerak. Kompyuteringizdagi fayl menejeri ham shunday saralaydi.

3.3 caseFirst va ignorePunctuation

Bir xil so'z turli harflar bilan yozilsa, standart holatda kichigi oldin turadi. caseFirst: "upper" buni teskari qiladi:

js
const kichikOldin = new Intl.Collator("uz");
const kattaOldin = new Intl.Collator("uz", { caseFirst: "upper" });
const sozlar = ["olma", "Olma", "OLMA"];

console.log(sozlar.toSorted(kichikOldin.compare).join(", "));
console.log(sozlar.toSorted(kattaOldin.compare).join(", "));

Konsolda:

text
olma, Olma, OLMA
OLMA, Olma, olma

ignorePunctuation: true — tinish belgilariga e'tibor bermaydi: "e-mail" va "email" teng bo'ladi. O'zbek tilida bu yerda tuzoq bor. Node'da o' dagi apostrof harfning qismi, shuning uchun "o'rik" va "orik" baribir farqli. Chrome'da esa (o'zbek qoidalari yo'q) apostrof tinish belgisi bo'lib qoladi va ikkalasi teng chiqadi. ignorePunctuation ni o'zbekcha matnda ishlatmang.

Tekshirib ko'ring: Ro'yxatda takroriy ismlarni topish kerak: "Aziz", "aziz", "AZIZ" — bitta odam. Qaysi sensitivity kerak?

Javob

"base" yoki "accent". Ikkalasi ham katta-kichik harfni farq deb hisoblamaydi. O'zbek ismlarida urg'uli harf yo'q, shuning uchun farqi sezilmaydi. Ko'pincha "base" tanlanadi — u eng "kechirimli".

4. Node va Chrome: saralash

sort darsida bu farqni ko'rgan edik. Biz uni Chrome 154.0.8037.95 da qayta tekshirdik:

Kod Node 24 Chrome 154
supportedLocalesOf(["uz"]) [ 'uz' ] []
resolvedOptions().locale "uz" brauzer tili, bizda "en-US"
«O'zbek alifbosi» bo'limidagi ismlar ... Zafar, O'tkir, G'ayrat, Shuhrat, Chori Chori, G'ayrat, Gulnora, O'tkir, ...
compare("o'rik", "oʻrik") 0 -1

Chrome'da o'zbekcha saralash qoidalari yo'q, va u buni halol aytadi: resolvedOptions().locale — brauzerning o'z tili (bizda "en-US"). Bu ListFormat dagi xulq bilan bir xil, NumberFormat dagidan farqli: u yerda uz lotin ma'lumotlari yo'q, resolvedOptions esa baribir uz deydi.

Ikkinchi farq — apostroflar. Chrome'da uch xil apostrof turli belgi, shuning uchun "base" qidiruvi Oʻtkir ni topmaydi. Brauzerda qidirishdan oldin apostroflarni bitta shaklga keltiring: matn.replace(/[‘ʻ]/g, "'")`.

Rus va ingliz tillarida natijalar ikkala muhitda bir xil.

Yana bir kuzatish: ICU'ning kirill yozuvidagi o'zbek tili (uz-Cyrl) uchun alohida qoidasi yo'q. Ғ — Г dan keyin, Ў — У dan keyin turadi. Rasmiy kirill alifbosida esa Ў Қ Ғ Ҳ oxirida. Kirill yozuvidagi ro'yxat uchun buni bilib qo'ying.

Amaliy xulosa (10-qismdagi bilan bir xil): o'zbekcha tartib muhim bo'lsa — saralashni serverda (Node'da) qiling yoki o'zingizning alifbo jadvalingiz bilan taqqoslash funksiyasini yozing. Ikkinchisini sort darsining mashqlarida yozgansiz.

5. Intl.Segmenter — matnni bo'laklash

5.1 Uch xil "uzunlik"

Unicode va emoji darsini eslang. Bitta oila emoji'si ekranda bitta belgi, lekin ichida 5 ta kod nuqta va 8 ta UTF-16 birligi:

js
const oila = "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}";
const bolgich = new Intl.Segmenter("uz");

console.log(oila.length); // 8
console.log([...oila].length); // 5
console.log([...bolgich.segment(oila)].length); // 1
Bitta oila emoji'si — uch xil sanash
  • .lengthUTF-16 birliklari8 ta
  • [...matn]kod nuqtalar: 3 odam + 2 ZWJ5 ta
  • Segmentergrafema — ko'z ko'rgani1 ta

Manba: Node 24.21.0 va Chrome 154 da ishga tushirilgan kod natijasi (2026-10-05)

Nimaga qarang: faqat Segmenter odam ko'radigan sonni beradi. Ko'z bitta belgi deb qabul qiladigan bo'lak — grafema (grapheme cluster). Bu atamani Unicode va emoji darsida eshitgansiz.

5.2 segment nima qaytaradi

js
const bolgich = new Intl.Segmenter("uz", { granularity: "grapheme" });
const bolaklar = [...bolgich.segment("Salom 👋🏽")];

console.log(bolaklar.length); // 7
console.log(bolaklar.at(-1));

Konsolda:

text
7
{ segment: '👋🏽', index: 6, input: 'Salom 👋🏽' }

segment(matn) massiv emas, iterable qaytaradi — shuning uchun [...] bilan massivga aylantirdik (Iteratsiya protokoli). Har bo'lak — obyekt:

  • segment — bo'lakning o'zi (qo'l emoji'si teri rangi bilan — bitta grafema);
  • index — matndagi boshlanish o'rni (UTF-16 birligida);
  • input — butun matn.

granularity — bo'lish darajasi: "grapheme" (standart), "word" yoki "sentence".

5.3 Emoji'ni buzmasdan kesish

«Bahor» sharh oldindan ko'rinishida 10 ta belgini ko'rsatadi. slice(0, 10) emoji o'rtasidan kesishi mumkin:

js
const sharh = "Zo'r osh 👍🏽 rahmat";
const bolgich = new Intl.Segmenter("uz");

function qisqartir(matn, soni) {
  const grafemalar = [...bolgich.segment(matn)].map((b) => b.segment);
  if (grafemalar.length <= soni) {
    return matn;
  }
  return `${grafemalar.slice(0, soni).join("")}…`;
}

console.log(qisqartir(sharh, 10)); // Zo'r osh 👍🏽…
console.log(sharh.slice(0, 11)); // Zo'r osh 👍

qisqartir grafemalarni sanaydi: 10-belgi — teri rangli qo'l, butunligicha. Oddiy slice(0, 11) esa qo'lni oldi, teri rangini esa kesib tashladi. slice(0, 10) bundan ham yomon: emoji'ning o'zini ikkiga bo'ladi va ekranda � chiqadi.

Tekshirib ko'ring: "Oʻzbekiston 🇺🇿" da nechta grafema bor? length nechchi?

Javob

Grafemalar — 13: Oʻzbekiston dagi 11 ta (ʻ — alohida belgi, harf bilan birlashmaydi), bo'sh joy va bayroq. length esa 16: bayroq ikki "harf-belgi"dan iborat, har biri 2 ta UTF-16 birligi.

6. So'zlar va gaplar

6.1 granularity: "word"

So'zlarni sanash uchun split(" ") ko'pincha xato:

js
const sharh = "Osh  juda   mazali!!!";
const bolgich = new Intl.Segmenter("uz", { granularity: "word" });

const bolaklar = [...bolgich.segment(sharh)];
const sozlar = bolaklar.filter((b) => b.isWordLike);
console.log(sharh.split(" ").length); // 6
console.log(sozlar.length); // 3

Ikki va uch bo'sh joy split ni chalg'itdi — bo'sh satrlar ham sanaldi. Segmenter esa matnni so'zlarga va oradagi bo'laklarga (bo'sh joy, tinish belgisi) bo'ladi. Xususiyat isWordLike — "bu bo'lak so'zmi?": so'z va sonlar uchun true, bo'sh joy va tinish belgilari uchun false.

6.2 Apostrof — so'z ichida

RegExp asoslari darsida ko'rgan tuzoqni eslang: \w apostrofni tanimaydi. Segmenter esa taniydi:

js
const matn = "Bugun osh 35 000 so'm, ertaga — manti!";
const bolgich = new Intl.Segmenter("uz", { granularity: "word" });

console.log(matn.match(/\w+/g).join(" | "));
console.log(
  [...bolgich.segment(matn)]
    .filter((b) => b.isWordLike)
    .map((b) => b.segment)
    .join(" | "),
);

Konsolda:

text
Bugun | osh | 35 | 000 | so | m | ertaga | manti
Bugun | osh | 35 | 000 | so'm | ertaga | manti

Regex so'm ni ikkiga bo'ldi, Segmenter — bitta so'z qoldirdi. Apostrofning ‘ va ʻ shakllari ham so'z ichida qoladi. Lekin 35 000 ikkita "so'z" bo'ldi — Segmenter raqamlar orasidagi bo'sh joyni oddiy bo'sh joy deb biladi. Tire bilan yozilgan so'zlar ham bo'linadi: ko'k-choy → ko'k, choy.

containing(indeks) — berilgan o'rindagi bo'lakni beradi. Masalan, foydalanuvchi matnning qaysi so'ziga bosganini topish uchun:

js
const bolgich = new Intl.Segmenter("uz", { granularity: "word" });
const bolaklar = bolgich.segment("Osh va manti");

console.log(bolaklar.containing(5).segment); // va

5-o'rin va so'zi ichida. Matn uzunligidan tashqaridagi indeks uchun undefined qaytadi.

6.3 granularity: "sentence"

js
const matn = "Osh tayyor. Manti 30 000 so'm! Choy bormi? Ha.";
const bolgich = new Intl.Segmenter("uz", { granularity: "sentence" });

for (const { segment } of bolgich.segment(matn)) {
  console.log(segment.trim());
}

Konsolda:

text
Osh tayyor.
Manti 30 000 so'm!
Choy bormi?
Ha.

Nuqta, undov va so'roq belgilari bo'yicha bo'lindi. Segmenter split(".") dan aqlliroq: 3.5 dagi nuqtada gapni bo'lmaydi. Lekin mukammal emas: "Dr. Aliyev keldi." ni ikki gapga bo'ldi — Dr. va Aliyev keldi.. Ingliz tilida esa Mr. qisqartmasini taniydi. Gap sanash — taxminiy ish, buni yodda tuting.

for...of va destructuring { segment } — har bo'lakdan faqat segment xususiyatini olamiz (Obyekt destructuring).

7. Segmenter va brauzerlar

Intl.Segmenter — yangiroq asbob. Baseline ma'lumotiga ko'ra u 2024-yil 16-aprel (Firefox 125) dan beri barcha asosiy brauzerlarda bor — "Baseline yangi". "Keng tarqalgan" maqomini esa 30 oy o'tib, 2026-yil oktabr o'rtasida oladi. Eski telefonlarda bo'lmasligi mumkin: ishlatishdan oldin typeof Intl.Segmenter === "function" bilan tekshiring.

Node 24 va Chrome 154 da biz darsdagi barcha misollarni sinadik — natijalar bir xil chiqdi, bitta istisno bilan. ali@example.com ni Node bitta example.com so'zi qoldirdi, Chrome esa example va com ga bo'ldi. Grafema va gap bo'lish qoidalari ikkalasida bir xil. So'z chegaralari esa ICU versiyasiga qarab biroz farq qilishi mumkin.

8. Ko'p uchraydigan xatolar

8.1 sort() ni taqqoslash funksiyasisiz chaqirish

["olim", "Zafar"].sort() → Zafar, olim. Har doim Collator bering. Asl massivni o'zgartirmaslik kerak bo'lsa — toSorted (Nusxa bilan o'zgartirish).

8.2 compare ni chaqirib yuborish

js
const uzTartib = new Intl.Collator("uz");

["olim", "Zafar"].sort(uzTartib.compare());
text
TypeError: The comparison function must be either a function or undefined: 0

Tarjimasi: "Taqqoslash funksiyasi funksiya yoki undefined bo'lishi kerak: 0". compare() — qavs bilan — funksiyani chaqirdi va natijasini (0) sort ga berdi. Kerak bo'lgani — funksiyaning o'zi: sort(uzTartib.compare). Xabar oxiridagi 0 — aynan o'sha noto'g'ri berilgan qiymat.

8.3 Belgilarni length bilan sanash

"Salom 👋🏽".length — 10, ko'z esa 7 ta belgi ko'radi. Foydalanuvchiga ko'rinadigan cheklovlar (sharh uzunligi, ism uzunligi) — grafema bilan. Ma'lumotlar bazasi cheklovi (bayt yoki birlik) — boshqa savol.

8.4 Chrome'da o'zbekcha tartibga ishonish

localeCompare(b, "uz") Node'da to'g'ri, Chrome'da — inglizcha tartib. Brauzerda saralangan ro'yxatni ko'rsatish kerak bo'lsa, uni serverdan tayyor saralangan holda oling.

8.5 ignorePunctuation o'zbek matnida

Chrome'da "so'm" va "som" teng bo'lib qoladi. Apostrof o'zbek tilida harfning qismi — uni "tinish belgisi" deb tashlamang.

9. Mashqlar

1-mashq (oson): Filiallar ro'yxati

«Bahor» filiallari: ["Yunusobod", "Chilonzor", "Olmazor", "Sergeli", "Shayxontohur", "O'rikzor"]. Ularni o'zbek alifbosi bo'yicha saralab, ", " bilan chiqaring. Asl massiv o'zgarmasin. Ishora: toSorted va Collator("uz").

Yechim
js
const uzTartib = new Intl.Collator("uz");
const filiallar = [
  "Yunusobod", "Chilonzor", "Olmazor",
  "Sergeli", "Shayxontohur", "O'rikzor",
];

console.log(filiallar.toSorted(uzTartib.compare).join(", "));
// Olmazor, Sergeli, Yunusobod, O'rikzor, Shayxontohur, Chilonzor

O'rikzor — Yunusobod dan keyin (o' — y va z dan keyin), Shayxontohur va Chilonzor — oxirida. Bu natija Node'da. Chrome'da Chilonzor birinchi bo'lardi.

2-mashq (o'rta): Ism bo'yicha qidiruv

mijozniTop(royxat, sorov) funksiyasi ro'yxatdan sorov ga teng ismlarni qaytarsin — katta-kichik harf va apostrof shaklidan qat'i nazar. Brauzerda ham ishlashi uchun avval apostroflarni bitta shaklga keltiring. Ro'yxat: ["Oʻtkir", "O'tkir", "otkir", "O‘TKIR", "Olim"], so'rov: "o'tkir". Ishora: replace(/[‘ʻ]/g, "'")vasensitivity: "base"`.

Yechim
js
const qidiruv = new Intl.Collator("uz", { sensitivity: "base" });

function apostrofniTozala(matn) {
  return matn.replace(/[‘ʻ`]/g, "'");
}

function mijozniTop(royxat, sorov) {
  const toza = apostrofniTozala(sorov);
  return royxat.filter(
    (ism) => qidiruv.compare(apostrofniTozala(ism), toza) === 0,
  );
}

const royxat = ["Oʻtkir", "O'tkir", "otkir", "O‘TKIR", "Olim"];
console.log(mijozniTop(royxat, "o'tkir"));
// [ 'Oʻtkir', "O'tkir", 'O‘TKIR' ]

otkir topilmadi — apostrofsiz bu boshqa so'z (o va o' — ikki xil harf). Natijada asl yozuvlar qaytdi: tozalash faqat solishtirish uchun ishlatildi.

3-mashq (qiyin): Sharh statistikasi

sharhniTahlilQil(matn) sharh uchun obyekt qaytarsin: belgilar (grafemalar soni), sozlar (so'zlar soni), gaplar (gaplar soni) va uzun — 280 grafemadan oshganmi. Uchta Segmenter ni funksiyadan tashqarida bir marta yarating. Matn: "Osh zo'r! 👍🏽 Lekin choy sovuq edi. Yana kelamiz.".

Yechim
js
const harflar = new Intl.Segmenter("uz");
const sozlar = new Intl.Segmenter("uz", { granularity: "word" });
const gaplar = new Intl.Segmenter("uz", { granularity: "sentence" });

function sanash(bolgich, matn, faqatSoz = false) {
  let soni = 0;
  for (const b of bolgich.segment(matn)) {
    if (!faqatSoz || b.isWordLike) {
      soni++;
    }
  }
  return soni;
}

function sharhniTahlilQil(matn) {
  const belgilar = sanash(harflar, matn);
  return {
    belgilar,
    sozlar: sanash(sozlar, matn, true),
    gaplar: sanash(gaplar, matn),
    uzun: belgilar > 280,
  };
}

const sharh = "Osh zo'r! 👍🏽 Lekin choy sovuq edi. Yana kelamiz.";
console.log(sharhniTahlilQil(sharh));

Konsolda:

text
{ belgilar: 47, sozlar: 8, gaplar: 3, uzun: false }

sanash massiv yasamasdan, for...of bilan sanaydi — uzun matnda xotirani tejaydi. Emoji 👍🏽 bitta grafema, lekin so'z emas (isWordLike — false). Uchta gap: undov belgisi birinchi gapni tugatdi, emoji esa ikkinchi gapning boshiga qo'shildi.

4-mashq: Vazifalar qadami — vazifa matni uzunligi

vazifalar da Vazifa.MAKS_UZUNLIK = 100 va index.html dagi maxlength bor. Ikkalasi ham UTF-16 birliklarini sanaydi: emoji'li vazifa "100 belgi"dan oldin kesiladi. Kanon kodni o'zgartirmaymiz — avval muammoni o'lchaymiz. kurs/mashqlar/12/18-grafema/uzunlik.mjs da grafemaSoni(matn) ni yozing va "Bozorga borish 🛒🥕🍅" uchun length, kod nuqtalar va grafemalar sonini chiqaring.

Yechim
js
const bolgich = new Intl.Segmenter("uz");

export function grafemaSoni(matn) {
  let soni = 0;
  for (const _ of bolgich.segment(matn)) {
    soni++;
  }
  return soni;
}

const vazifa = "Bozorga borish 🛒🥕🍅";
console.log(vazifa.length, [...vazifa].length, grafemaSoni(vazifa));
// 20 17 17

Uchta emoji length da 6 birlik oldi, ko'zga esa 3 ta. Bu farq maxlength bilan to'qnashganda foydalanuvchi "hali joy bor" deb o'ylaydi, maydon esa yozishni to'xtatadi. _ — "bu o'zgaruvchi ishlatilmaydi" degan kelishilgan nom. Vazifa matnini grafema bilan cheklash — kanon uchun taklif; uni Legacy kod va texnik qarz darsidagi qarzlar ro'yxatiga qo'shish mumkin.

bash
git add 12/18-grafema/uzunlik.mjs
git commit -m "12/18: vazifa matnini grafema bilan sanash"

10. Real ishda

  • Ro'yxatlar va jadvallar. Mijozlar, mahsulotlar, shaharlar — saralangan ro'yxat har admin panelda bor. Jadval kutubxonalari (masalan, TanStack Table) saralash funksiyasini qabul qiladi — unga Collator.compare beriladi.
  • Ma'lumotlar bazasi. PostgreSQL'da ham "collation" bor — xuddi shu ICU qoidalari bilan saralash. SQL qismida ORDER BY ism COLLATE ... ni ko'rasiz.
  • Belgi chegaralari va muharrirlar. Sharh, bio, xabar uzunligi chegarasi bor har joyda "nechta belgi?" savoli chiqadi. Har platforma o'z usulida sanaydi, lekin foydalanuvchi ko'zi grafemani sanaydi. Matn muharrirlarida kursor emoji ichiga tushib qolmasligi va Backspace butun emoji'ni o'chirishi ham grafema bo'yicha ishlashdan.
  • Intervyu. "'é'.length nega 2 bo'lishi mumkin?", "Ro'yxatni alifbo bo'yicha qanday saralaysiz?" — localeCompare/Collator va grafema haqida gapiring.

Xulosa

  • new Intl.Collator(til).compare — sort uchun tayyor taqqoslash funksiyasi; bir marta yaratiladi.
  • ICU o'zbek tartibi: a … z, keyin o', g', sh, ch; ng — alohida harf emas; uch xil apostrof — bir harf; tutuq belgisi s'h ni sh dan ajratadi.
  • sensitivity: "base" — katta-kichik harfsiz qidiruv; numeric: true — rasm2 < rasm10; ignorePunctuation — o'zbekchada xavfli.
  • Chrome 154 da o'zbekcha saralash yo'q (brauzer tiliga, masalan "en-US" ga o'tadi) — tartibni serverda qiling, apostroflarni bir shaklga keltiring.
  • Intl.Segmenter — grapheme (emoji = 1), word (isWordLike, so'm butun), sentence (taxminiy); segment() iterable qaytaradi.

Keyingi dars: Tagged template literals — template satr oldiga funksiya qo'yib, matn va qiymatlarni o'zingiz qayta ishlashni o'rganamiz.

Manbalar

  • MDN: "Intl.Collator", "Intl.Collator() constructor", "Intl.Segmenter", "Segments.prototype.containing" — developer.mozilla.org
  • Unicode Technical Standard #10 "Unicode Collation Algorithm"; UAX #29 "Unicode Text Segmentation" — unicode.org
  • Unicode CLDR — cldr.unicode.org; ICU — icu.unicode.org
  • web-features 3.40: intl-segmenter — Baseline 2024-aprel (Firefox 125)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Intl.Collator va Intl.Segmenter: o'zbek alifbosi bo'yicha saralash va matnni bo'lish — IlmHamroh