indickit 0.6.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,14 +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, a letter count that cuts a letter in two, and a name
7
- that must be written in Latin letters.
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.
8
9
 
9
10
  ```
10
11
  stem किताब किताबें किताबों → किताब
11
12
  phonetic राम ರಾಮ രാമ ராம رام Ram → rn
12
13
  segment ಲಕ್ಷ್ಮಿ → ಲ ಕ್ಷ್ಮಿ
13
14
  romanize लक्ष्मी ലക്ഷ്മി லக்ஷ்மி → lakshmi
15
+ deromanize namaste → नमस्ते
14
16
  ```
15
17
 
16
18
  Each utility gives the same output in Go and in TypeScript, and each was
@@ -24,8 +26,47 @@ measured on data that its rules were never built on.
24
26
  | let a search for किताब also find किताबों | `normalize`, then `stem` |
25
27
  | let a search for हिन्दी also find हिंदी, and keep words whole | `fold` |
26
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` |
27
30
  | count letters, cut at a length, or move a cursor | `segment` |
28
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.
29
70
 
30
71
  ## Install
31
72
 
@@ -45,25 +86,28 @@ deno add npm:indickit
45
86
 
46
87
  The package is ES modules only; `require()` works from Node 20.19. In
47
88
  TypeScript, set `moduleResolution` to `nodenext` or `bundler`; the old
48
- `node10` cannot find the four utilities.
89
+ `node10` cannot find the utilities.
49
90
 
50
91
  In a browser, with no build step:
51
92
 
52
93
  ```js
53
- import { normalize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.6.0/dist/normalize.js";
54
- import { stem } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.6.0/dist/stem.js";
55
- import { match } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.6.0/dist/phonetic.js";
56
- import { segment } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.6.0/dist/segment.js";
57
- import { load } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.6.0/dist/romanize.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";
58
101
  ```
59
102
 
60
103
  Each utility is its own file, so a page loads only the rules it uses.
61
104
  `romanize` also loads one language's tables when you ask for them (0.2–1.8
62
- MB). The npm package carries no tables: `load()` reads them beside the
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
63
107
  module (a jsDelivr or GitHub-tag install), else from jsDelivr at the same
64
108
  version. Offline, pass your own `fetch`. In Go, `go get` downloads the
65
- tables of every language once (14 MB); a program carries only the languages
66
- it imports.
109
+ tables of every language once (`romanize` 14 MB, `deromanize` 52 MB); a
110
+ program carries only the languages it imports.
67
111
 
68
112
  ## Recipes
69
113
 
@@ -147,6 +191,26 @@ Equal keys mean "sounds alike", not "the same person". Before you merge two
147
191
  records, weigh the evidence: a shared rare name says more than a shared
148
192
  common one.
149
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
+
150
214
  ### Count and cut letters
151
215
 
152
216
  A cursor, a backspace, a letter count and a cut at a length limit must
@@ -189,6 +253,34 @@ import (
189
253
  romanize.Word("लक्ष्मी", "hi", romanize.Words, 2) // [lakshmi laxmi]
190
254
  ```
191
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
+
192
284
  ### The same in Go
193
285
 
194
286
  ```go
@@ -202,6 +294,7 @@ import (
202
294
  normalize.Text("അവന്\u200d", "ml") // "അവൻ"
203
295
  stem.Stem(normalize.Text("किताबों", "hi"), "hi") // किताब
204
296
  phonetic.Match("Mohanlal", "മോഹൻലാൽ") // true
297
+ phonetic.NewIndex(names, "hi", phonetic.ProfileNames).Search("Narendra Modi", phonetic.Thresholds["names"])
205
298
  segment.Segment("ಲಕ್ಷ್ಮಿ") // [ಲ ಕ್ಷ್ಮಿ]
206
299
  ```
207
300
 
@@ -217,11 +310,18 @@ segment.Segment("ಲಕ್ಷ್ಮಿ") // [ಲ ಕ್ಷ
217
310
  | `phonetic.NameKeys(name)` | `nameKeys(name)` | the keys of a whole name; index these |
218
311
  | `phonetic.Match(a, b)` | `match(a, b)` | same word count, and each pair of words shares a key |
219
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 |
220
317
  | `segment.Segment(s)` | `segment(text)` | the letters, in order; joined, they give the text back |
221
318
  | `segment.Count(s)` | `count(text)` | how many letters a reader sees |
222
319
  | `romanize.Word(w, lang, mode, n)` | `(await load(lang, mode)).word(w, n?)` | up to n spellings, most likely first |
223
320
  | `romanize.Text(s, lang, mode)` | `(await load(lang, mode)).text(text)` | each word of the language's script in Latin letters |
224
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) |
225
325
 
226
326
  Three things hold for all of them:
227
327
 
@@ -246,19 +346,30 @@ other tools tried and each weak spot are in `docs/`.
246
346
  |---|---|---|---|---|
247
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) |
248
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) |
249
- | `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) |
250
350
  | `segment` | cuts 0.05% of Kannada words wrongly | `Intl.Segmenter`: 50.3% | 9 KB | [docs/segment.md](docs/segment.md) |
251
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) |
252
353
 
253
354
  (Browser files are gzipped.)
254
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
+
255
361
  ## Limits
256
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).
257
367
  - **Only HarfBuzz was tested** for "looks the same" (`normalize`,
258
368
  `segment`). Windows and Apple draw text with their own engines.
259
- - **`phonetic` expects clean names.** Remove titles (Shri, Smt.) and
260
- initials before you key a name. On one-word place names it returns many
261
- 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).
262
373
  - **Tamil is the weakest language** for `phonetic` (76%), and `stem` is
263
374
  behind Snowball's recall in Tamil.
264
375
  - **`stem` does not help passage search in Hindi or Punjabi.** Short
@@ -294,21 +405,32 @@ Each utility's own weak spots are at the end of its file in `docs/`.
294
405
  Every table and switch of a utility is in its rules file
295
406
  (`normalize/rules.json`, `stem/rules.json`, `phonetic/rules.json`,
296
407
  `segment/rules.json`; `romanize/rules.json` and its tables in
297
- `romanize/lang/`); the Go and TypeScript code is a short loop over it.
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.
298
411
  Both are checked against a conformance file of inputs with the outputs that
299
412
  a reference implementation gave them: 345,276 inputs for `normalize`,
300
413
  418,665 for `stem`, 195,994 for `phonetic`, 858,654 for `segment`, 2,397
301
- for `romanize`
302
- (`*/testdata/conformance.jsonl.gz`). A change that makes any one disagree
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
303
416
  on any input fails the build.
304
417
 
305
418
  ## Credits and licence
306
419
 
307
420
  MIT. The test words are Wikidata labels (CC0). The rules were tuned on
308
421
  Wikidata and PIB Parallel (Press Information Bureau releases), and checked
309
- 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).
310
424
  `normalize` and `segment` were checked in fonts by Google (Noto), Ek Type
311
425
  (Anek), SIL, SMC and others. `stem`'s endings were mined from PIB text;
312
426
  Wiktionary's inflection tables (CC BY-SA), Universal Dependencies
313
427
  treebanks and FLORES+ (CC BY-SA) were used only to measure them. It was
314
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.