indickit 0.5.0 → 0.8.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/README.md CHANGED
@@ -3,12 +3,16 @@
3
3
  Small, exact utilities for text in the languages of India, in Go and
4
4
  TypeScript. They fix the places where ordinary string code goes wrong on
5
5
  Indian text: a search that misses a word because it is spelled, encoded or
6
- inflected another way, and a letter count that cuts a letter in two.
6
+ inflected another way, a letter count that cuts a letter in two, a name
7
+ that must be written in Latin letters, and Latin typing that must be written
8
+ in an Indian script.
7
9
 
8
10
  ```
9
11
  stem किताब किताबें किताबों → किताब
10
12
  phonetic राम ರಾಮ രാമ ராம رام Ram → rn
11
13
  segment ಲಕ್ಷ್ಮಿ → ಲ ಕ್ಷ್ಮಿ
14
+ romanize लक्ष्मी ലക്ഷ്മി லக்ஷ்மி → lakshmi
15
+ deromanize namaste → नमस्ते
12
16
  ```
13
17
 
14
18
  Each utility gives the same output in Go and in TypeScript, and each was
@@ -22,7 +26,47 @@ measured on data that its rules were never built on.
22
26
  | let a search for किताब also find किताबों | `normalize`, then `stem` |
23
27
  | let a search for हिन्दी also find हिंदी, and keep words whole | `fold` |
24
28
  | find a person's name typed in another script | `phonetic` |
29
+ | find a name in a long list, as people write it (titles, initials), or in running text | `phonetic-search` |
25
30
  | count letters, cut at a length, or move a cursor | `segment` |
31
+ | write a name or a text in Latin letters (लक्ष्मी → lakshmi) | `romanize` |
32
+ | write Latin typing in an Indian script (namaste → नमस्ते) | `deromanize` |
33
+
34
+ ## Languages
35
+
36
+ The 22 languages of the Eighth Schedule. `normalize`, `fold`, `segment` and
37
+ `phonetic` read every script below, so they serve every language; the
38
+ others hold tables for some languages only.
39
+
40
+ | Language | Code | Script | `stem` | `phonetic-search` | `romanize` | `deromanize` |
41
+ |---|---|---|---|---|---|---|
42
+ | Assamese | `as` | Bengali-Assamese | ✓ | ✓ | words, names | words, names |
43
+ | Bengali | `bn` | Bengali-Assamese | ✓ | ✓ | words, names | words, names |
44
+ | Bodo | `brx` | Devanagari | – | ✓ | words | words |
45
+ | Dogri | `doi` | Devanagari | – | ✓ | words | words |
46
+ | Gujarati | `gu` | Gujarati | ✓ | ✓ | words, names | words, names |
47
+ | Hindi | `hi` | Devanagari | ✓ | ✓ | words, names | words, names |
48
+ | Kannada | `kn` | Kannada | ✓ | ✓ | words, names | words, names |
49
+ | Kashmiri | `ks` | Perso-Arabic | – | ✓ | words, names | words, names |
50
+ | Konkani | `gom` | Devanagari | – | ✓ | words, names | words, names |
51
+ | Maithili | `mai` | Devanagari | – | ✓ | words, names | words, names |
52
+ | Malayalam | `ml` | Malayalam | ✓ | ✓ | words, names | words, names |
53
+ | Manipuri | `mni` | Meetei Mayek | – | ✓ | words, names | words, names |
54
+ | Marathi | `mr` | Devanagari | ✓ | ✓ | words, names | words, names |
55
+ | Nepali | `ne` | Devanagari | ✓ | ✓ | words, names | words, names |
56
+ | Odia | `or` | Odia | – | ✓ | words, names | words, names |
57
+ | Punjabi | `pa` | Gurmukhi | ✓ | ✓ | words, names | words, names |
58
+ | Sanskrit | `sa` | Devanagari | ✓ | ✓ | words, names | words, names |
59
+ | Santali | `sat` | Ol Chiki | – | keys only | names | names |
60
+ | Sindhi | `sd` | Perso-Arabic | – | ✓ | words, names | words, names |
61
+ | Tamil | `ta` | Tamil | ✓ | ✓ | words, names | words, names |
62
+ | Telugu | `te` | Telugu | ✓ | ✓ | words, names | words, names |
63
+ | Urdu | `ur` | Perso-Arabic | ✓ | ✓ | words, names | words, names |
64
+
65
+ "words" and "names" are the two modes of `romanize` and `deromanize`. In
66
+ `deromanize`, the names mode of Bodo and Dogri uses their words tables.
67
+ Santali has no words tables: no dataset of Santali words in Latin letters
68
+ was found. `phonetic-search` in Santali ranks by the keys alone. Measured
69
+ quality differs by language; see `docs/` for each utility.
26
70
 
27
71
  ## Install
28
72
 
@@ -42,18 +86,28 @@ deno add npm:indickit
42
86
 
43
87
  The package is ES modules only; `require()` works from Node 20.19. In
44
88
  TypeScript, set `moduleResolution` to `nodenext` or `bundler`; the old
45
- `node10` cannot find the four utilities.
89
+ `node10` cannot find the utilities.
46
90
 
47
91
  In a browser, with no build step:
48
92
 
49
93
  ```js
50
- import { normalize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.5.0/dist/normalize.js";
51
- import { stem } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.5.0/dist/stem.js";
52
- import { match } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.5.0/dist/phonetic.js";
53
- import { segment } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.5.0/dist/segment.js";
94
+ import { normalize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/normalize.js";
95
+ import { stem } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/stem.js";
96
+ import { match } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/phonetic.js";
97
+ import { segment } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/segment.js";
98
+ import { load } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/romanize.js";
99
+ import { loadSearch } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/phonetic-search.js";
100
+ import { load as loadDeromanize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.8.0/dist/deromanize.js";
54
101
  ```
55
102
 
56
103
  Each utility is its own file, so a page loads only the rules it uses.
104
+ `romanize` also loads one language's tables when you ask for them (0.2–1.8
105
+ MB), `deromanize` one language's tables and word list (0.3–3.1 MB), and
106
+ `phonetic-search` one language's table (2–6 KB). The npm package carries no tables: `load()` reads them beside the
107
+ module (a jsDelivr or GitHub-tag install), else from jsDelivr at the same
108
+ version. Offline, pass your own `fetch`. In Go, `go get` downloads the
109
+ tables of every language once (`romanize` 14 MB, `deromanize` 52 MB); a
110
+ program carries only the languages it imports.
57
111
 
58
112
  ## Recipes
59
113
 
@@ -137,6 +191,26 @@ Equal keys mean "sounds alike", not "the same person". Before you merge two
137
191
  records, weigh the evidence: a shared rare name says more than a shared
138
192
  common one.
139
193
 
194
+ ### Search a list of names
195
+
196
+ `phonetic-search` uses the keys to find candidates, and a scorer learned
197
+ from data to rank them from 0 to 100. It finds names as people write them,
198
+ with titles, initials and joined words, and names in running text.
199
+
200
+ ```ts
201
+ import { loadSearch, THRESHOLDS } from "indickit/phonetic-search";
202
+
203
+ const s = await loadSearch("hi"); // fetches the Hindi table (3 KB)
204
+ const ix = s.index(["नरेश मोदी", "नरेंद्र मोदी", "मनमोहन सिंह"]);
205
+ ix.search("Narendra Modi", THRESHOLDS.names); // [{ name: 1, score: 100 }]
206
+ ix.search("Manmohan Singh", THRESHOLDS.names); // [{ name: 2, score: 96 }]
207
+ ```
208
+
209
+ Use the threshold for your task: `THRESHOLDS.names` (74) for a list of
210
+ people, `THRESHOLDS.villages` (72) for place names, `THRESHOLDS.text_10`
211
+ (80) or `text_20` (70) with `s.index(words, "text")` for a name among the
212
+ words of a text. A score is relative to the other candidates of its search.
213
+
140
214
  ### Count and cut letters
141
215
 
142
216
  A cursor, a backspace, a letter count and a cut at a length limit must
@@ -152,6 +226,61 @@ segment("ਕ੍ਕ"); // ["ਕ੍","ਕ"]: Gurmukhi shows this virama, so these
152
226
  count("ಕನ್ನಡ"); // 3
153
227
  ```
154
228
 
229
+ ### Write it in Latin letters
230
+
231
+ `romanize` gives a ranked list of spellings, most likely first. Use the
232
+ `"words"` mode for text and the `"names"` mode for a field that holds a
233
+ person's name:
234
+
235
+ ```ts
236
+ import { load } from "indickit/romanize";
237
+
238
+ const hi = await load("hi"); // fetches the Hindi tables
239
+ hi.word("लक्ष्मी", 2); // ["lakshmi","laxmi"]
240
+ hi.text("भारत के प्रधानमंत्री"); // "bharat ke pradhaanmantri"
241
+ const ur = await load("ur", "names");
242
+ ur.word("پرویز", 1); // ["parvez"]
243
+ ```
244
+
245
+ In Go, import the languages you need; a program carries only their tables:
246
+
247
+ ```go
248
+ import (
249
+ "github.com/micahchoo/indickit/romanize"
250
+ _ "github.com/micahchoo/indickit/romanize/lang/hi"
251
+ )
252
+
253
+ romanize.Word("लक्ष्मी", "hi", romanize.Words, 2) // [lakshmi laxmi]
254
+ ```
255
+
256
+ ### Write Latin typing in an Indian script
257
+
258
+ `deromanize` is the reverse: it gives a ranked list of native spellings for
259
+ a Latin-typed word, most likely first. It writes every word it is given in
260
+ the Indian script, English words too: in mixed text, find each word's
261
+ language first.
262
+
263
+ ```ts
264
+ import { load } from "indickit/deromanize";
265
+
266
+ const hi = await load("hi"); // fetches the Hindi tables and word list
267
+ hi.word("namaste", 1); // ["नमस्ते"]
268
+ hi.text("bharat ke pradhanmantri"); // "भारत के प्रधानमंत्री"
269
+ const ur = await load("ur", "names");
270
+ ur.word("ahmad", 1); // ["احمد"]
271
+ ```
272
+
273
+ In Go:
274
+
275
+ ```go
276
+ import (
277
+ "github.com/micahchoo/indickit/deromanize"
278
+ _ "github.com/micahchoo/indickit/deromanize/lang/hi"
279
+ )
280
+
281
+ deromanize.Word("namaste", "hi", deromanize.Words, 1) // [नमस्ते]
282
+ ```
283
+
155
284
  ### The same in Go
156
285
 
157
286
  ```go
@@ -165,6 +294,7 @@ import (
165
294
  normalize.Text("അവന്\u200d", "ml") // "അവൻ"
166
295
  stem.Stem(normalize.Text("किताबों", "hi"), "hi") // किताब
167
296
  phonetic.Match("Mohanlal", "മോഹൻലാൽ") // true
297
+ phonetic.NewIndex(names, "hi", phonetic.ProfileNames).Search("Narendra Modi", phonetic.Thresholds["names"])
168
298
  segment.Segment("ಲಕ್ಷ್ಮಿ") // [ಲ ಕ್ಷ್ಮಿ]
169
299
  ```
170
300
 
@@ -180,8 +310,18 @@ segment.Segment("ಲಕ್ಷ್ಮಿ") // [ಲ ಕ್ಷ
180
310
  | `phonetic.NameKeys(name)` | `nameKeys(name)` | the keys of a whole name; index these |
181
311
  | `phonetic.Match(a, b)` | `match(a, b)` | same word count, and each pair of words shares a key |
182
312
  | `phonetic.Words(name)` | `words(name)` | the words, split as the others split them |
313
+ | `phonetic.JoinedKeys(name)` | `joinedKeys(name)` | the keys of the name with its words joined (8+ classes) |
314
+ | `phonetic.NewIndex(names, lang, profile)` | `(await loadSearch(lang)).index(names, profile?)` | names keyed once for searching |
315
+ | `ix.Search(query, threshold)` | `ix.search(query, threshold?)` | the names scoring at least threshold, best first |
316
+ | `phonetic.Score(query, names, lang, profile)` | `(await loadSearch(lang)).score(query, names, profile?)` | one score (0..100) per candidate |
183
317
  | `segment.Segment(s)` | `segment(text)` | the letters, in order; joined, they give the text back |
184
318
  | `segment.Count(s)` | `count(text)` | how many letters a reader sees |
319
+ | `romanize.Word(w, lang, mode, n)` | `(await load(lang, mode)).word(w, n?)` | up to n spellings, most likely first |
320
+ | `romanize.Text(s, lang, mode)` | `(await load(lang, mode)).text(text)` | each word of the language's script in Latin letters |
321
+ | `romanize.Languages(mode)` | `languages(mode)` | the languages with tables (Go: those imported) |
322
+ | `deromanize.Word(latin, lang, mode, n)` | `(await load(lang, mode)).word(latin, n?)` | up to n native spellings, most likely first |
323
+ | `deromanize.Text(s, lang, mode)` | `(await load(lang, mode)).text(text)` | each run of Latin letters in the language's script |
324
+ | `deromanize.Languages(mode)` | `languages(mode)` | the languages with tables (Go: those imported) |
185
325
 
186
326
  Three things hold for all of them:
187
327
 
@@ -206,18 +346,30 @@ other tools tried and each weak spot are in `docs/`.
206
346
  |---|---|---|---|---|
207
347
  | `normalize` | makes 88.1% of look-alike spellings equal, and changes the look of no word | Indic NLP Library: 82.4%, and changes 0.68% of words | 14 KB | [docs/normalize.md](docs/normalize.md) |
208
348
  | `stem` | finds 6–15 points more of the right sentences than exact search, in 13 languages | ahead in 5 languages, level in 2, a trade in 4, behind in Nepali | 3 KB | [docs/stem.md](docs/stem.md) |
209
- | `phonetic` | finds the right person 84% of the time, in 21 languages, on clean names; 42% on a real roster as written (70% with titles and initials removed) | romanize + Soundex: 23% in Tamil, 70% in Hindi; cannot read Urdu | 4 KB | [docs/phonetic.md](docs/phonetic.md) |
349
+ | `phonetic` | finds the right person 84% of the time, in 21 languages, on clean names; 42% on a real roster as written (70% with titles and initials removed); with `phonetic-search`, 86% of a real roster as written and 85% of villages | romanize + Soundex: 23% in Tamil, 70% in Hindi; cannot read Urdu; ICU + fuzzy match: 78% and 65% | 5 KB | [docs/phonetic.md](docs/phonetic.md) |
210
350
  | `segment` | cuts 0.05% of Kannada words wrongly | `Intl.Segmenter`: 50.3% | 9 KB | [docs/segment.md](docs/segment.md) |
351
+ | `romanize` | writes 48.9% of the words of running text as people typed them, in 11 languages; 71.3% of names (tables: 0.2–1.8 MB a language) | IndicXlit (Python, a 119 MB model): 37.9% and 49.0% | 5 KB | [docs/romanize.md](docs/romanize.md) |
352
+ | `deromanize` | writes the right word first for 87.1% of the words of Hindi news typed in Latin, and in four for 97.4%; 69.7% of names in 19 languages (tables: 0.3–3.1 MB a language) | IndicXlit: 86.1%, 89.7% and 66.4%; behind on rare words out of context | 20 KB | [docs/deromanize.md](docs/deromanize.md) |
211
353
 
212
354
  (Browser files are gzipped.)
213
355
 
356
+ `romanize` is also ahead of every tool tried that runs without a neural model. The
357
+ best of them, indic-trans (about 200 MB), gives 34.7% on running text against
358
+ 51.2%, on the development data; it is ahead only in Urdu. See
359
+ [docs/romanize.md](docs/romanize.md).
360
+
214
361
  ## Limits
215
362
 
363
+ - **`deromanize` writes English words in the Indian script too.** About 8%
364
+ of the words of romanized Hinglish are English; find each word's language
365
+ first. On rare words out of context it is 10 points behind IndicXlit. See
366
+ [docs/deromanize.md](docs/deromanize.md).
216
367
  - **Only HarfBuzz was tested** for "looks the same" (`normalize`,
217
368
  `segment`). Windows and Apple draw text with their own engines.
218
- - **`phonetic` expects clean names.** Remove titles (Shri, Smt.) and
219
- initials before you key a name. On one-word place names it returns many
220
- wrong matches. See [docs/phonetic.md](docs/phonetic.md).
369
+ - **The `phonetic` key expects clean names.** Remove titles (Shri, Smt.)
370
+ and initials before you key a name, or use `phonetic-search`. On one-word
371
+ place names the key alone returns many wrong matches. See
372
+ [docs/phonetic.md](docs/phonetic.md).
221
373
  - **Tamil is the weakest language** for `phonetic` (76%), and `stem` is
222
374
  behind Snowball's recall in Tamil.
223
375
  - **`stem` does not help passage search in Hindi or Punjabi.** Short
@@ -231,6 +383,9 @@ other tools tried and each weak spot are in `docs/`.
231
383
  - **Some languages have no table.** `stem` covers 13 languages; Bodo,
232
384
  Dogri, Kashmiri, Konkani, Maithili, Manipuri, Odia, Santali and Sindhi
233
385
  are not among them.
386
+ - **`romanize` is behind IndicXlit on places and on rare single words**
387
+ (by 7–13 and 5.5 points). Santali has names only; Bodo and Dogri have
388
+ text only. See [docs/romanize.md](docs/romanize.md).
234
389
  - **Go reads invalid UTF-8 as U+FFFD**, so for such input the output does
235
390
  not join back into the original bytes.
236
391
  - **"The same output" holds inside the blocks indickit reads.** Go reads
@@ -249,20 +404,33 @@ Each utility's own weak spots are at the end of its file in `docs/`.
249
404
 
250
405
  Every table and switch of a utility is in its rules file
251
406
  (`normalize/rules.json`, `stem/rules.json`, `phonetic/rules.json`,
252
- `segment/rules.json`); the Go and TypeScript code is a short loop over it.
407
+ `segment/rules.json`; `romanize/rules.json` and its tables in
408
+ `romanize/lang/`; `deromanize/rules.json` and its tables and word lists in
409
+ `deromanize/lang/`; `phonetic-search`'s tables in `phonetic/scorer/`); the
410
+ Go and TypeScript code is a short loop over it.
253
411
  Both are checked against a conformance file of inputs with the outputs that
254
412
  a reference implementation gave them: 345,276 inputs for `normalize`,
255
- 418,665 for `stem`, 195,994 for `phonetic`, 858,654 for `segment`
256
- (`*/testdata/conformance.jsonl.gz`). A change that makes any one disagree
413
+ 418,665 for `stem`, 195,994 for `phonetic`, 858,654 for `segment`, 2,397
414
+ for `romanize`, 2,818 for `deromanize`, and 1,021 search rows for
415
+ `phonetic-search` (`*/testdata/*conformance.jsonl.gz`). A change that makes any one disagree
257
416
  on any input fails the build.
258
417
 
259
418
  ## Credits and licence
260
419
 
261
420
  MIT. The test words are Wikidata labels (CC0). The rules were tuned on
262
421
  Wikidata and PIB Parallel (Press Information Bureau releases), and checked
263
- on AI4Bharat's Aksharantar and on Wikipedia text; none of them ships here.
422
+ on AI4Bharat's Aksharantar and on Wikipedia text. No text of theirs ships
423
+ here, except `deromanize`'s word counts (below).
264
424
  `normalize` and `segment` were checked in fonts by Google (Noto), Ek Type
265
425
  (Anek), SIL, SMC and others. `stem`'s endings were mined from PIB text;
266
426
  Wiktionary's inflection tables (CC BY-SA), Universal Dependencies
267
427
  treebanks and FLORES+ (CC BY-SA) were used only to measure them. It was
268
428
  compared with Snowball, Lucene and the Indic NLP Library.
429
+
430
+ `romanize` and `deromanize` learned from AI4Bharat's Aksharantar (CC BY for
431
+ its hand-collected pairs, CC0 for its mined pairs), Wikidata labels (CC0),
432
+ and IndicXlit's romanizations (MIT) of frequent Wikipedia words.
433
+ `deromanize`'s tables hold word lists: counts of the words of Wikipedia
434
+ (text CC BY-SA 4.0) and, for Bodo and Dogri, of IndicCorp v2 (CC0). Both
435
+ were measured against IndicXlit, on Google's Dakshina (CC BY-SA 4.0) and,
436
+ for `deromanize`, on COMI-LINGUA (CC BY 4.0), used only to measure.