urdu-text-utils 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/index.cjs +804 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +245 -0
- package/dist/index.d.ts +245 -0
- package/dist/index.js +777 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zaid-maker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# urdu-text-utils
|
|
2
|
+
|
|
3
|
+
Urdu text processing toolkit for JavaScript and TypeScript. Normalization, detection, digits, diacritics, collation, search, statistics and transliteration.
|
|
4
|
+
|
|
5
|
+
Zero runtime dependencies. ESM + CJS. Ships its own types.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install urdu-text-utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
normalizeUrdu,
|
|
14
|
+
isUrdu,
|
|
15
|
+
countWords,
|
|
16
|
+
convertNumbers,
|
|
17
|
+
removeDiacritics,
|
|
18
|
+
sortUrdu,
|
|
19
|
+
searchUrdu,
|
|
20
|
+
analyzeUrdu,
|
|
21
|
+
} from "urdu-text-utils";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Why
|
|
25
|
+
|
|
26
|
+
Urdu breaks the assumptions most JS string code makes:
|
|
27
|
+
|
|
28
|
+
- The same word has several Unicode spellings. Text from Arabic keyboards, old CMSes or Windows-1256 conversions uses `ك` (U+0643) and `ي` (U+064A) where Urdu uses `ک` (U+06A9) and `ی` (U+06CC). `"کتاب" === "كتاب"` is `false`.
|
|
29
|
+
- Diacritics are optional, so `مُحَمَّد` and `محمد` are the same name to a reader and different strings to a computer.
|
|
30
|
+
- Urdu has two digit systems, in two different Unicode blocks: `۰-۹` (U+06F0) and Arabic-Indic `٠-٩` (U+0660).
|
|
31
|
+
- `localeCompare("ur")` does not give Urdu alphabetical order in most runtimes — it falls back to Arabic root collation, which orders `ک گ ٹ ڈ ڑ ں ے` by codepoint.
|
|
32
|
+
|
|
33
|
+
## Text normalization
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
normalizeUrdu("كيا حال ہے");
|
|
37
|
+
// "کیا حال ہے"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Folds Arabic letter forms to Urdu ones (`ي ى → ی`, `ك ڪ → ک`, `ه ۀ ة ۃ → ہ`, `أ إ ٱ → ا`), applies NFKC so presentation forms like `ﻻ` become real letters, and strips tatweel, bidi controls and BOM. Letters that are genuinely distinct in Urdu — `آ`, `ھ`, `ے`, `ؤ`, `ئ` — are preserved.
|
|
41
|
+
|
|
42
|
+
| Option | Default | Effect |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `compatibility` | `true` | NFKC instead of NFC; folds presentation forms |
|
|
45
|
+
| `stripDiacritics` | `false` | Remove harakat and quranic marks |
|
|
46
|
+
| `stripTatweel` | `true` | Remove kashida padding |
|
|
47
|
+
| `stripZwnj` | `false` | Remove U+200C (can be meaningful) |
|
|
48
|
+
| `collapseWhitespace` | `true` | Collapse runs, trim |
|
|
49
|
+
| `digits` | `"preserve"` | `"urdu"` \| `"english"` \| `"arabic"` |
|
|
50
|
+
| `urduPunctuation` | `false` | `, ; ?` → `، ؛ ؟` |
|
|
51
|
+
|
|
52
|
+
`foldUrdu(text)` returns the aggressive comparison key (normalized + diacritic-free + lowercased) used internally by search and sort.
|
|
53
|
+
|
|
54
|
+
## Urdu detection
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
isUrdu("آپ کیسے ہیں؟"); // true
|
|
58
|
+
isUrdu("hello world"); // false
|
|
59
|
+
isUrdu("The word پاکستان appears in this English sentence"); // false — ratio based
|
|
60
|
+
urduRatio("پاکستان Pakistan"); // 0.47
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The Arabic script is shared by Urdu, Arabic, Persian and Pashto, so `isUrdu` measures script, not language. When you need to tell Urdu from Arabic:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
hasUrduSpecificLetters("لڑکی"); // true — ڑ does not exist in Arabic
|
|
67
|
+
hasUrduSpecificLetters("كتاب مدرسة"); // false
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Word and sentence counting
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
countWords("پاکستان ایک خوبصورت ملک ہے"); // 5
|
|
74
|
+
countWords("آپ کیسے ہیں؟"); // 3 — attached punctuation is not a word
|
|
75
|
+
countSentences("یہ پہلا جملہ ہے۔ یہ دوسرا ہے۔"); // 2
|
|
76
|
+
splitWords(text); // string[]
|
|
77
|
+
splitSentences(text); // string[]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Urdu numbers
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
convertNumbers("12345"); // "۱۲۳۴۵"
|
|
84
|
+
convertNumbers("۱۲۳۴۵", "english"); // "12345"
|
|
85
|
+
|
|
86
|
+
toUrduDigits("١٢٣"); // "۱۲۳" — accepts Arabic-Indic input
|
|
87
|
+
toEnglishDigits("۳۱-۱۲-۲۰۲۴"); // "31-12-2024"
|
|
88
|
+
toArabicIndicDigits("123"); // "١٢٣"
|
|
89
|
+
|
|
90
|
+
parseUrduNumber("۱٬۲۳۴"); // 1234 — handles ٬ and ٫
|
|
91
|
+
parseUrduNumber("۳٫۱۴"); // 3.14
|
|
92
|
+
numberToUrduWords(100000); // "ایک لاکھ" — South Asian scale, @experimental
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Diacritics
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
removeDiacritics("مُحَمَّد"); // "محمد"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Strips harakat (U+064B–U+065F), quranic annotation (U+06D6–U+06ED) and superscript alef. Keeps `۔ ے ۓ`, which are punctuation and letters rather than marks.
|
|
102
|
+
|
|
103
|
+
## Search
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
searchUrdu("محمد", ["مُحَمَّد علی", "احمد", "محمد خان"]);
|
|
107
|
+
// ["مُحَمَّد علی", "محمد خان"]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Both sides are folded first, so a query typed with Arabic `ك`/`ي` finds Urdu-spelled records and diacritics never block a match.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
searchUrdu("پاکستاں", ["پاکستان"], { fuzzy: true }); // ["پاکستان"] — 1 edit
|
|
114
|
+
searchUrdu("محمد", rows, { getText: (r) => r.title, limit: 10 });
|
|
115
|
+
searchUrduRanked("محمد", names); // [{ item, score }] — 1 exact, 0.9 prefix, 0.8 substring
|
|
116
|
+
|
|
117
|
+
highlightUrdu("مُحَمَّد علی", "محمد");
|
|
118
|
+
// "<mark>مُحَمَّد</mark> علی" — original diacritics intact
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Fuzzy matching runs only after the exact pass fails, so the common case stays cheap. `editDistance(a, b, limit)` is exported for your own ranking.
|
|
122
|
+
|
|
123
|
+
## Sorting
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
sortUrdu(["گل", "آم", "بادام"]); // ["آم", "بادام", "گل"]
|
|
127
|
+
sortUrdu(["ٹماٹر", "تربوز", "پپیتا"]); // ["پپیتا", "تربوز", "ٹماٹر"]
|
|
128
|
+
sortUrdu(rows, { getText: (r) => r.name, descending: true });
|
|
129
|
+
compareUrdu(a, b); // comparator for Array.prototype.sort
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Uses an explicit Urdu alphabet table (`ا آ ب پ ت ٹ ث …`), not `Intl`. Variant letters (`ؤ ئ ۂ ۓ`) sort next to their base letter. Diacritics are ignored.
|
|
133
|
+
|
|
134
|
+
## Statistics
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
analyzeUrdu("پاکستان ایک خوبصورت ملک ہے۔ اس کی آبادی زیادہ ہے۔");
|
|
138
|
+
// {
|
|
139
|
+
// characters: 49,
|
|
140
|
+
// charactersNoSpaces: 40,
|
|
141
|
+
// words: 10,
|
|
142
|
+
// sentences: 2,
|
|
143
|
+
// paragraphs: 1,
|
|
144
|
+
// urduPercentage: 100,
|
|
145
|
+
// diacritics: 0,
|
|
146
|
+
// digits: 0,
|
|
147
|
+
// averageWordsPerSentence: 5,
|
|
148
|
+
// readingTimeMinutes: 0.1
|
|
149
|
+
// }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Transliteration and slugs — `@experimental`
|
|
153
|
+
|
|
154
|
+
Read this before putting it in front of users.
|
|
155
|
+
|
|
156
|
+
Urdu script omits short vowels, so the mapping is genuinely ambiguous: `کتب` is `kitab` or `kutub` depending on context and no rule table can decide which. The reverse direction is worse, because Roman Urdu has no standard orthography (`hai` / `hay` / `he` all occur). These functions use a dictionary of common words with a rule fallback — expect roughly 70% word accuracy on ordinary prose, and do not build anything irreversible on the output. A real lexicon plus a statistical model is planned; it is not faked here.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
romanize("آپ کیسے ہیں"); // "aap kaisay hain"
|
|
160
|
+
romanize("آپ کیسے ہیں", { capitalize: true }); // "Aap kaisay hain"
|
|
161
|
+
romanToUrdu("mera naam zaid hai"); // "میرا نام زید ہے"
|
|
162
|
+
|
|
163
|
+
urduSlug("میرا پہلا مضمون"); // "mera-pehla-mazmoon"
|
|
164
|
+
urduSlug("میرا پہلا مضمون", { separator: "_", maxLength: 40 });
|
|
165
|
+
urduSlug("میرا پہلا مضمون", { preserveUrdu: true }); // "میرا-پہلا-مضمون" — lossless
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For permanent URLs prefer `preserveUrdu: true` (percent-encoded but readable and exact), or store the slug you generate once rather than recomputing it — a dictionary improvement in a later version would otherwise change existing URLs.
|
|
169
|
+
|
|
170
|
+
## Notes on scope
|
|
171
|
+
|
|
172
|
+
Every function is pure, synchronous and side-effect free. Nothing here does word segmentation of run-together text, stemming, POS tagging or spell correction; those need a lexicon and are out of scope for this version.
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npm install
|
|
178
|
+
npm test
|
|
179
|
+
npm run typecheck
|
|
180
|
+
npm run build
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Releasing
|
|
184
|
+
|
|
185
|
+
Publishing is automated and tag-driven. CI runs tests on Node 18/20/22 for every push and PR; nothing reaches npm until a version tag exists.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npm version patch # or minor / major — commits and tags
|
|
189
|
+
git push --follow-tags
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The `Release` workflow then verifies the tag matches `package.json`, re-runs typecheck/tests/build, publishes with `--provenance`, and opens a GitHub Release with generated notes.
|
|
193
|
+
|
|
194
|
+
One-time setup: add an npm **Automation** access token as the `NPM_TOKEN` repository secret (`gh secret set NPM_TOKEN`). Automation tokens bypass 2FA prompts, which classic publish tokens do not.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT
|