indickit 0.10.0 → 0.11.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
@@ -47,29 +47,31 @@ others hold tables for some languages only.
47
47
  |---|---|---|---|---|---|---|
48
48
  | Assamese | `as` | Bengali-Assamese | ✓ | ✓ | words, names | words, names |
49
49
  | Bengali | `bn` | Bengali-Assamese | ✓ | ✓ | words, names | words, names |
50
- | Bodo | `brx` | Devanagari | – | ✓ | words | words |
51
- | Dogri | `doi` | Devanagari | – | ✓ | words | words |
50
+ | Bodo | `brx` | Devanagari | ✓ | ✓ | words | words |
51
+ | Dogri | `doi` | Devanagari | ✓ | ✓ | words | words |
52
52
  | Gujarati | `gu` | Gujarati | ✓ | ✓ | words, names | words, names |
53
53
  | Hindi | `hi` | Devanagari | ✓ | ✓ | words, names | words, names |
54
54
  | Kannada | `kn` | Kannada | ✓ | ✓ | words, names | words, names |
55
- | Kashmiri | `ks` | Perso-Arabic | – | ✓ | words, names | words, names |
56
- | Konkani | `gom` | Devanagari | – | ✓ | words, names | words, names |
57
- | Maithili | `mai` | Devanagari | – | ✓ | words, names | words, names |
55
+ | Kashmiri | `ks` | Perso-Arabic | ✓ | ✓ | words, names | words, names |
56
+ | Konkani | `gom` | Devanagari | ✓ | ✓ | words, names | words, names |
57
+ | Maithili | `mai` | Devanagari | ✓ | ✓ | words, names | words, names |
58
58
  | Malayalam | `ml` | Malayalam | ✓ | ✓ | words, names | words, names |
59
- | Manipuri | `mni` | Meetei Mayek, Bengali-Assamese | – | ✓ | words, names | words, names |
59
+ | Manipuri | `mni` | Meetei Mayek, Bengali-Assamese | ✓ | ✓ | words, names | words, names |
60
60
  | Marathi | `mr` | Devanagari | ✓ | ✓ | words, names | words, names |
61
61
  | Nepali | `ne` | Devanagari | ✓ | ✓ | words, names | words, names |
62
- | Odia | `or` | Odia | – | ✓ | words, names | words, names |
62
+ | Odia | `or` | Odia | ✓ | ✓ | words, names | words, names |
63
63
  | Punjabi | `pa` | Gurmukhi | ✓ | ✓ | words, names | words, names |
64
64
  | Sanskrit | `sa` | Devanagari | ✓ | ✓ | words, names | words, names |
65
- | Santali | `sat` | Ol Chiki | – | keys only | names | names |
66
- | Sindhi | `sd` | Perso-Arabic | – | ✓ | words, names | words, names |
65
+ | Santali | `sat` | Ol Chiki | ✓ | keys only | names | names |
66
+ | Sindhi | `sd` | Perso-Arabic | ✓ | ✓ | words, names | words, names |
67
67
  | Tamil | `ta` | Tamil | ✓ | ✓ | words, names | words, names |
68
68
  | Telugu | `te` | Telugu | ✓ | ✓ | words, names | words, names |
69
69
  | Urdu | `ur` | Perso-Arabic | ✓ | ✓ | words, names | words, names |
70
70
 
71
- "words" and "names" are the two modes of `romanize` and `deromanize`. In
72
- `deromanize`, the names mode of Bodo and Dogri uses their words tables.
71
+ "words" and "names" are the two modes of `romanize` and `deromanize`;
72
+ `romanize` has a third, "places", in all 22 languages (its own tables in
73
+ nine, the words tables in the others). In `deromanize`, the names mode of
74
+ Bodo and Dogri uses their words tables.
73
75
  Santali has no words tables: no dataset of Santali words in Latin letters
74
76
  was found. `phonetic-search` in Santali ranks by the keys alone. Measured
75
77
  quality differs by language; see `docs/` for each utility.
@@ -97,14 +99,14 @@ TypeScript, set `moduleResolution` to `nodenext` or `bundler`; the old
97
99
  In a browser, with no build step:
98
100
 
99
101
  ```js
100
- import { normalize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/normalize.js";
101
- import { stem } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/stem.js";
102
- import { match } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/phonetic.js";
103
- import { segment } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/segment.js";
104
- import { load } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/romanize.js";
105
- import { loadSearch } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/phonetic-search.js";
106
- import { load as loadDeromanize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/deromanize.js";
107
- import { isWellFormed } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.10.0/dist/wellformed.js";
102
+ import { normalize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/normalize.js";
103
+ import { stem } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/stem.js";
104
+ import { match } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/phonetic.js";
105
+ import { segment } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/segment.js";
106
+ import { load } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/romanize.js";
107
+ import { loadSearch } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/phonetic-search.js";
108
+ import { load as loadDeromanize } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/deromanize.js";
109
+ import { isWellFormed } from "https://cdn.jsdelivr.net/gh/micahchoo/indickit@v0.11.0/dist/wellformed.js";
108
110
  ```
109
111
 
110
112
  Each utility is its own file, so a page loads only the rules it uses.
@@ -151,29 +153,40 @@ brokenAt("क्ि"); // 2: a vowel sign after a virama
151
153
 
152
154
  ### Search words in their other forms
153
155
 
154
- Give every word of a document a key, and give the query the same key. The
155
- key for a word is `stem(normalize(word, lang), lang)`:
156
+ Give every word of a document a key, and give the query the same key.
157
+ `searchKey` normalizes, stems, then folds accepted spellings, in the order
158
+ that was measured best:
156
159
 
157
160
  ```ts
158
- import { normalize } from "indickit/normalize";
159
- import { stem } from "indickit/stem";
160
-
161
- stem(normalize("किताबों", "hi"), "hi"); // "किताब"
162
- stem(normalize("ஆண்டில்", "ta"), "ta"); // "ஆண்ட"
163
- stem(normalize("ஆண்டு", "ta"), "ta"); // "ஆண்ட"
164
- stem(normalize("ಸರ್ಕಾರಕ್ಕೆ", "kn"), "kn"); // "ಸರ್ಕಾರ"
165
- stem(normalize("حکومتوں", "ur"), "ur"); // "حکومت"
161
+ import { searchKey } from "indickit/search-key";
162
+
163
+ searchKey("किताबों", "hi"); // "किताब"
164
+ searchKey("हिन्दी", "hi"); // "हिंद"
165
+ searchKey("हिंदी", "hi"); // "हिंद"
166
+ searchKey("ஆண்டில்", "ta"); // "ஆண்ட"
167
+ searchKey("ஆண்டு", "ta"); // "ஆண்ட"
168
+ searchKey("ಸರ್ಕಾರಕ್ಕೆ", "kn"); // "ಸರ್ಕಾರ"
169
+ searchKey("حکومتوں", "ur"); // "حکومت"
166
170
  ```
167
171
 
168
- In 13 languages, a search with stems finds 6–15 points more of the right
169
- sentences than an exact search. In a search engine over Wikipedia passages,
172
+ In a search engine, index the key beside a trigram field (each word cut
173
+ into its three-letter pieces), and add the two scores: over Wikipedia in
174
+ Hindi, Bengali and Telugu, the two fields found 3.0 points more of the
175
+ right passages than trigrams alone, and ranked them better. A setup for
176
+ OpenSearch and Elasticsearch is in [docs/stem.md](docs/stem.md#in-a-search-engine).
177
+ In Go: `searchkey.Key(word, lang)`.
178
+
179
+ In 22 languages, a search with stems finds more of the right sentences
180
+ than an exact search: 6–15 points in the 14 measured on FLORES+, 3.7–11.4
181
+ in the 8 measured on BPCC. In a search engine over Wikipedia passages,
170
182
  it finds 2–11.5 points more in 10 of 13 test sets, and nothing more in
171
183
  Hindi or Punjabi. It also finds some wrong ones: a stem merges forms by
172
184
  their endings, not by their meaning. Do not add fuzzy matching to stems:
173
185
  together they find less than stems alone.
174
186
 
175
- You need only one of `stem` and `fold`. If you use both, stem first, then
176
- fold: `fold` before `stem` loses 3–10 points in Assamese. `fold` alone
187
+ If you call the steps yourself, stem first, then fold: `fold` before
188
+ `stem` loses 3–10 points in Assamese, and fold after stem finds 2.4 points
189
+ more in Hindi. `fold` alone
177
190
  merges accepted spellings of one word (हिन्दी and हिंदी) and keeps the word
178
191
  whole, for a search that must not merge forms:
179
192
 
@@ -206,7 +219,8 @@ match("राम", "काम"); // false: Rām is not kām
206
219
 
207
220
  To search a list of names, store every key of each name, and look up every
208
221
  key of the query. A name can have more than one key: Tamil writes both k
209
- and h with க, so மோகன், Mohan, has two.
222
+ and h with க, and a Tamil name ending in ன் is also keyed without it, so
223
+ மோகன், Mohan, has four.
210
224
 
211
225
  ```ts
212
226
  import { nameKeys, RULES_VERSION } from "indickit/phonetic";
@@ -235,16 +249,17 @@ ix.search("Narendra Modi", THRESHOLDS.names); // [{ name: 1, score: 100 }]
235
249
  ix.search("Manmohan Singh", THRESHOLDS.names); // [{ name: 2, score: 96 }]
236
250
  ```
237
251
 
238
- Use the threshold for your task: `THRESHOLDS.names` (74) for a list of
239
- people, `THRESHOLDS.villages` (72) for place names, `THRESHOLDS.text_10`
240
- (80) or `text_20` (70) with `s.index(words, "text")` for a name among the
252
+ Use the threshold for your task: `THRESHOLDS.names` (76) for a list of
253
+ people, `THRESHOLDS.villages` (73) for place names, `THRESHOLDS.text_10`
254
+ (81) or `text_20` (71) with `s.index(words, "text")` for a name among the
241
255
  words of a text. A score is relative to the other candidates of its search.
242
256
 
243
257
  ### Count and cut letters
244
258
 
245
259
  A cursor, a backspace, a letter count and a cut at a length limit must
246
260
  treat ಕ್ಷ್ಮಿ as one letter. `Intl.Segmenter` splits it in Kannada and
247
- Gurmukhi; `segment` keeps it whole.
261
+ Gurmukhi; `segment` keeps it whole. Each letter is an akshara, the
262
+ orthographic syllable of the script.
248
263
 
249
264
  ```ts
250
265
  import { segment, count } from "indickit/segment";
@@ -258,8 +273,8 @@ count("ಕನ್ನಡ"); // 3
258
273
  ### Write it in Latin letters
259
274
 
260
275
  `romanize` gives a ranked list of spellings, most likely first. Use the
261
- `"words"` mode for text and the `"names"` mode for a field that holds a
262
- person's name:
276
+ `"words"` mode for text, the `"names"` mode for a field that holds a
277
+ person's name, and the `"places"` mode for a place name:
263
278
 
264
279
  ```ts
265
280
  import { load } from "indickit/romanize";
@@ -269,6 +284,8 @@ hi.word("लक्ष्मी", 2); // ["lakshmi","laxmi"]
269
284
  hi.text("भारत के प्रधानमंत्री"); // "bharat ke pradhaanmantri"
270
285
  const ur = await load("ur", "names");
271
286
  ur.word("پرویز", 1); // ["parvez"]
287
+ const ta = await load("ta", "places");
288
+ ta.word("காஞ்சிபுரம்", 1); // ["kanchipuram"]
272
289
  ```
273
290
 
274
291
  In Go, import the languages you need; a program carries only their tables:
@@ -322,8 +339,9 @@ ranked list the romanized index is as good and needs no second download.
322
339
 
323
340
  `deromanize` is the reverse: it gives a ranked list of native spellings for
324
341
  a Latin-typed word, most likely first. It writes every word it is given in
325
- the Indian script, English words too: in mixed text, find each word's
326
- language first.
342
+ the Indian script, English words too. For Hindi typed with English words in
343
+ it (Hinglish), load the English list and use `textMixed`: a word on the list
344
+ stays as typed.
327
345
 
328
346
  ```ts
329
347
  import { load } from "indickit/deromanize";
@@ -333,6 +351,8 @@ hi.word("namaste", 1); // ["नमस्ते"]
333
351
  hi.text("bharat ke pradhanmantri"); // "भारत के प्रधानमंत्री"
334
352
  const ur = await load("ur", "names");
335
353
  ur.word("ahmad", 1); // ["احمد"]
354
+ const mixed = await load("hi", "words", { english: true }); // also fetches the English list (22 KB)
355
+ mixed.textMixed("mera office band hai"); // "मेरा office बंद है"
336
356
  ```
337
357
 
338
358
  In Go:
@@ -346,6 +366,31 @@ import (
346
366
  deromanize.Word("namaste", "hi", deromanize.Words, 1) // [नमस्ते]
347
367
  ```
348
368
 
369
+ ### Match two Latin spellings of one word
370
+
371
+ People type one word in many ways: "zindagi" and "jindagi", "bahut" and
372
+ "bohot". To match them, write each in the Indian script and fold it; the
373
+ result is a key for search or for removing duplicates:
374
+
375
+ ```ts
376
+ import { load } from "indickit/deromanize";
377
+ import { fold } from "indickit/normalize";
378
+
379
+ const hi = await load("hi");
380
+ const key = (w: string) => fold(hi.word(w, 1)[0] ?? w, "hi");
381
+ key("zindagi") === key("jindagi"); // true: ज़िंदगी and जिंदगी fold to one
382
+ key("bahut") === key("bohot"); // true: both are बहुत
383
+ key("kahaan") === key("kahan"); // false: कहाँ and कहा
384
+ ```
385
+
386
+ On words `deromanize` never trained on (Dakshina's lexicons, Bengali,
387
+ Hindi, Marathi, Tamil, Telugu and Urdu), this key matches 30–67% of the
388
+ pairs of spellings of one word and gives 3–20% of spellings the key of
389
+ another word. Fuzzy matching (one edit) matches 28–65% and gives 28–51% a
390
+ wrong match. Keys from all four spellings match 52–88%, at 16–46% wrong.
391
+ Tamil is the weakest language. The `phonetic` key is not for this: it drops
392
+ vowels, which carry a Latin-typed word.
393
+
349
394
  ### The same in Go
350
395
 
351
396
  ```go
@@ -375,9 +420,9 @@ segment.Segment("ಲಕ್ಷ್ಮಿ") // [ಲ ಕ್ಷ
375
420
  | `stem.Languages()` | `LANGUAGES` | the languages with a stem table |
376
421
  | `phonetic.Keys(word)` | `keys(word)` | the keys of one word |
377
422
  | `phonetic.NameKeys(name)` | `nameKeys(name)` | the keys of a whole name; index these |
378
- | `phonetic.Match(a, b)` | `match(a, b)` | same word count, and each pair of words shares a key |
423
+ | `phonetic.Match(a, b)` | `match(a, b)` | titles dropped, the same word count, and each pair of words shares a key |
379
424
  | `phonetic.Words(name)` | `words(name)` | the words, split as the others split them |
380
- | `phonetic.JoinedKeys(name)` | `joinedKeys(name)` | the keys of the name with its words joined (8+ classes) |
425
+ | `phonetic.JoinedKeys(name)` | `joinedKeys(name)` | the keys of the name with its words joined (6+ classes) |
381
426
  | `phonetic.NewIndex(names, lang, profile)` | `(await loadSearch(lang)).index(names, profile?)` | names keyed once for searching |
382
427
  | `ix.Search(query, threshold)` | `ix.search(query, threshold?)` | the names scoring at least threshold, best first |
383
428
  | `phonetic.Score(query, names, lang, profile)` | `(await loadSearch(lang)).score(query, names, profile?)` | one score (0..100) per candidate |
@@ -414,11 +459,11 @@ other tools tried and each weak spot are in `docs/`.
414
459
  | Utility | Result | Best other tool | Browser file | Details |
415
460
  |---|---|---|---|---|
416
461
  | `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 | 16 KB | [docs/normalize.md](docs/normalize.md) |
417
- | `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) |
418
- | `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% | 7 KB | [docs/phonetic.md](docs/phonetic.md) |
462
+ | `stem` | finds 3.7–15 points more of the right sentences than exact search, in 22 languages | ahead in 5 languages, level in 2, a trade in 4, behind in Nepali; no other stemmer for the other 10 | 4 KB | [docs/stem.md](docs/stem.md) |
463
+ | `phonetic` | finds 89% of place-name pairs across scripts (OpenStreetMap, held out, rules 2026-10-11.7); 84% of people on clean names in 21 languages (rules 2026-10-05, a lower bound for the rules that ship); 88% of a second roster as written (titles, initials; rules 2026-10-11); with `phonetic-search`, 93% of places (search 2026-10-11.7) and 86% of a roster as written (search 2026-10-09) | romanize + Soundex: 71% of place pairs, 23% of people in Tamil; cannot read Urdu; ICU + fuzzy match: 74% of places | 8 KB | [docs/phonetic.md](docs/phonetic.md) |
419
464
  | `segment` | cuts 0.05% of Kannada words wrongly | `Intl.Segmenter`: 50.3% | 8 KB | [docs/segment.md](docs/segment.md) |
420
- | `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% | 7 KB | [docs/romanize.md](docs/romanize.md) |
421
- | `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 | 21 KB | [docs/deromanize.md](docs/deromanize.md) |
465
+ | `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% | 8 KB | [docs/romanize.md](docs/romanize.md) |
466
+ | `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 | 22 KB | [docs/deromanize.md](docs/deromanize.md) |
422
467
  | `wellformed` | agrees with HarfBuzz on 100% of 135,944 held-out PDF words (9,594 of them broken) and 2,146,974 new Wikipedia words | a check that the word starts with a combining sign: finds 84% of the broken words, with 4,667 false alarms | 6 KB | [docs/wellformed.md](docs/wellformed.md) |
423
468
 
424
469
  (Browser files are gzipped.)
@@ -430,32 +475,49 @@ best of them, indic-trans (about 200 MB), gives 34.7% on running text against
430
475
 
431
476
  ## Limits
432
477
 
433
- - **`deromanize` writes English words in the Indian script too.** About 8%
434
- of the words of romanized Hinglish are English; find each word's language
435
- first. On rare words out of context it is 10 points behind IndicXlit. See
436
- [docs/deromanize.md](docs/deromanize.md).
478
+ - **`deromanize` writes English words in the Indian script too**, unless
479
+ you use `textMixed` (Hindi only). Its English list finds 62% of the English
480
+ words of typed Hinglish and leaves 2.4% of the Hindi words in Latin, most
481
+ of them chat spellings ("to" for तो, "he" for है). On rare words out of
482
+ context it is 10 points behind IndicXlit. See
483
+ [docs/deromanize.md](docs/deromanize.md#english-in-hindi-text).
437
484
  - **Only HarfBuzz was tested** for "looks the same" (`normalize`,
438
485
  `segment`). Windows and Apple draw text with their own engines.
439
- - **The `phonetic` key expects clean names.** Remove titles (Shri, Smt.)
440
- and initials before you key a name, or use `phonetic-search`. On one-word
441
- place names the key alone returns many wrong matches. See
486
+ - **The `phonetic` key reads names, not addresses.** It drops a name's
487
+ leading titles (Shri, Smt., श्रीमती, திரு) and reads one-letter initials
488
+ (M = एम) from rules 2026-10-11; a title in the middle of a name, or any
489
+ other honorific, stays a word. On one-word place names the key alone
490
+ returns many wrong matches: use `phonetic-search`. See
442
491
  [docs/phonetic.md](docs/phonetic.md).
443
492
  - **Tamil is the weakest language** for `phonetic` (76%), and `stem` is
444
- behind Snowball's recall in Tamil.
493
+ behind Snowball's recall in Tamil: in Python or Java, Snowball's Tamil
494
+ stemmer finds 2.8 points more in passage search. A table that matched
495
+ its recall merged short Tamil words (இடம் "place" with இடையே "between")
496
+ and broke the wrong-results budget.
445
497
  - **`stem` does not help passage search in Hindi or Punjabi.** Short
446
498
  question words merge with other words (ਕਿਸ "which" with ਕਿਸਾਨ "farmer"),
447
499
  and names lose their ends (सीता → सी). In Hindi, Lucene's analyzer finds
448
500
  4.8 points more, from its spelling folds. See [docs/stem.md](docs/stem.md).
501
+ - **`stem` damages some names.** In news text, 1.3–3.0% of person-name
502
+ tokens are swamped: most of their stem search's hits are other words
503
+ (करीना → कर finds करने). For a field of names, pass `minStem: 4`: it
504
+ halves that damage. See [docs/stem.md](docs/stem.md#options).
449
505
  - **An n-gram field can find more than `stem`** in passage search: in
450
506
  Malayalam, Kannada, Tamil, Bengali, Marathi and Hindi, indexing each
451
507
  word's three-letter pieces found 3–10 points more, at 2–3 times the
452
- index size. See [docs/stem.md](docs/stem.md#in-a-search-engine).
453
- - **Some languages have no table.** `stem` covers 13 languages; Bodo,
454
- Dogri, Kashmiri, Konkani, Maithili, Manipuri, Odia, Santali and Sindhi
455
- are not among them.
456
- - **`romanize` is behind IndicXlit on places and on rare single words**
457
- (by 7–13 and 5.5 points). Santali has names only; Bodo and Dogri have
458
- text only. Manipuri in Bengali script loses syllables in the words mode;
508
+ index size. Index `stem` beside the trigram field, not instead of it: in
509
+ Hindi, Bengali and Telugu, both fields together found 3.0 points more
510
+ than trigrams alone and ranked better. See
511
+ [docs/stem.md](docs/stem.md#in-a-search-engine).
512
+ - **Thin evidence for the newest tables.** Eight languages rest on one
513
+ test source (BPCC); Manipuri in Bengali script on 1,012 FLORES+
514
+ sentences. Santali gains little (3.7 points).
515
+ - **`romanize` is behind IndicXlit on OpenStreetMap place names and on rare
516
+ single words**: on a held-out slice of OpenStreetMap the places mode is
517
+ right 44.4% of the time, IndicXlit 49.1% (it leads on village names, 57.6%
518
+ against 52.8%; Kannada has no places tables). Santali has names only; Bodo and Dogri have
519
+ text only. Bodo writes ख थ फ as kh th ph, as typists do, not as
520
+ Aksharantar's k t f. Manipuri in Bengali script loses syllables in the words mode;
459
521
  its tables were trained on Meetei Mayek. See [docs/romanize.md](docs/romanize.md).
460
522
  - **`normalize` keeps the direction marks LRM and RLM** (U+200E, U+200F).
461
523
  Text copied from the web carries them, and they hide a match on 0.04% of
@@ -504,8 +566,8 @@ Every table and switch of a utility is in its rules file
504
566
  Go and TypeScript code is a short loop over it.
505
567
  Both are checked against a conformance file of inputs with the outputs that
506
568
  a reference implementation gave them: 345,276 inputs for `normalize`,
507
- 418,665 for `stem`, 195,994 for `phonetic`, 858,654 for `segment`, 2,397
508
- for `romanize`, 2,990 for `deromanize`, 32,279 for `wellformed`, and 1,021 search rows for
569
+ 418,665 for `stem`, 199,992 for `phonetic`, 858,654 for `segment`, 2,397
570
+ for `romanize`, 2,990 for `deromanize`, 32,279 for `wellformed`, and 1,031 search rows for
509
571
  `phonetic-search` (`*/testdata/*conformance.jsonl.gz`). A change that makes any one disagree
510
572
  on any input fails the build.
511
573