farsit 0.0.0-stage → 1.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amir Salehi
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 CHANGED
@@ -1,3 +1,120 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/amiryxe/farsit/main/assets/logo-dark.svg">
4
+ <img src="https://raw.githubusercontent.com/amiryxe/farsit/main/assets/logo.svg" alt="farsit" width="300">
5
+ </picture>
6
+ </p>
2
7
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
8
+ <p align="center">
9
+ <a href="https://www.npmjs.com/package/farsit"><img src="https://img.shields.io/npm/v/farsit?color=0f766e" alt="npm version"></a>
10
+ <a href="https://bundlephobia.com/package/farsit"><img src="https://img.shields.io/bundlephobia/minzip/farsit?color=0f766e&label=gzip" alt="gzip size"></a>
11
+ <a href="https://github.com/amiryxe/farsit/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/farsit?color=0f766e" alt="license"></a>
12
+ <img src="https://img.shields.io/badge/dependencies-0-0f766e" alt="zero dependencies">
13
+ </p>
14
+
15
+ <p align="center"><a href="https://amiryxe.github.io/farsit/"><b>Live demo →</b></a></p>
16
+
17
+ **Accurate Persian → Finglish transliteration. No AI, no dictionary, zero deps. Slugs included.**
18
+
19
+ > موتور دقیق تبدیل متن فارسی به فینگلیش (حروف لاتین)؛ بدون هوش مصنوعی، بدون فرهنگ لغت و بدون وابستگی. اسلاگ URL هم دارد. کاملاً آفلاین و الگوریتمی؛ خروجی هرگز `x` ندارد (خ=kh، ش=sh، چ=ch، ژ=zh، ق/غ=gh).
20
+
21
+ `farsit` reads Persian the way people pronounce it, filling in the short vowels Persian script leaves out, and writes it in everyday Finglish. Runs offline in the browser or Node: ESM + CJS, fully typed, ~147 KB gzip including the model.
22
+
23
+ ```ts
24
+ import { transliterate, slugify, explain } from 'farsit';
25
+
26
+ transliterate('استقامت در اصفهان'); // "esteghamat dar esfahan"
27
+ slugify('آموزش برنامه‌نویسی ۱۴۰۳'); // "amuzesh-barnamenevisi-1403"
28
+ transliterate('پارچه های زیبا'); // "parche haye ziba" (phrase ezafe)
29
+ transliterate('نسبتا'); // "nesbatan" (implicit tanvin)
30
+ explain('استقامت'); // per-letter trace of every decision
31
+ ```
32
+
33
+ ## Use cases
34
+
35
+ - **URL slugs** for Persian titles (`slugify`)
36
+ - **Usernames and email addresses** from Persian names
37
+ - **Latin filenames** for uploads and exports
38
+ - **Latin-only form fields and tickets** (banking, travel, legacy systems)
39
+ - **Search matching** for users who type in Finglish
40
+ - **Pronunciation hints** for learners and TTS (`explain` shows every decision)
41
+
42
+ ## Install
43
+
44
+ ```sh
45
+ npm install farsit # or: pnpm add farsit · yarn add farsit
46
+ ```
47
+
48
+ ```ts
49
+ import { transliterate, slugify, explain } from 'farsit'; // ESM
50
+ const { transliterate, slugify, explain } = require('farsit'); // CommonJS
51
+ ```
52
+
53
+ Types are included and work with `moduleResolution` `bundler`, `node16` and `nodenext`. No Node-specific APIs, so it runs in browsers, Node, and edge/worker runtimes, fully offline.
54
+
55
+ ## API
56
+
57
+ ### `transliterate(text, options?) → string`
58
+ | option | default | |
59
+ |---|---|---|
60
+ | `ezafe` | `true` | Add the ezafe vowel to a word ending in the glide ی (‌های، درختای، خانه‌ی) when another content word follows in the same phrase: `پارچه‌های زیبا` → `parchehaye ziba`. It is skipped at the end of a phrase, before punctuation, conjunctions, prepositions or the copula, and before verb-shaped words. |
61
+ | `longU` | `'u'` | `'oo'` spells long /u/ as `oo` (`noor`). |
62
+ | `layers` | all on | `{ diacritics, morphology, wazn }`: switch individual layers off. |
63
+ | `beam` | `8` | Viterbi beam width. |
64
+
65
+ ### `slugify(text, options?) → string`
66
+ Takes all the options above, plus:
67
+
68
+ | option | default | |
69
+ |---|---|---|
70
+ | `separator` | `'-'` | Word separator. |
71
+ | `lowercase` | `true` | Lowercase Latin letters. |
72
+ | `maxLength` | none | Maximum length; cuts at a word boundary. |
73
+ | `keepDigits` | `true` | Keep digits (Persian and Arabic digits become ASCII). |
74
+
75
+ ```ts
76
+ slugify('سفر به اصفهان ۱۴۰۳', { separator: '_' }); // "safar_be_esfahan_1403"
77
+ slugify('سفر به اصفهان ۱۴۰۳', { keepDigits: false }); // "safar-be-esfahan"
78
+ slugify('آموزش برنامه‌نویسی با تایپ‌اسکریپت', { maxLength: 25 }); // "amuzesh-barnamenevisi-ba"
79
+ slugify('نور خورشید', { longU: 'oo' }); // "noor-khorshid"
80
+ ```
81
+
82
+ ### `explain(text, options?) → Explanation`
83
+ Returns the normalised input, the output, and for each token its segments, the wazn match, tanvin and ezafe flags, and the per-letter labels with the constraints that produced them.
84
+
85
+ Also exported: `normalize` (Arabic → Persian letters, digits, ZWNJ cleanup) and `WAZNS`.
86
+
87
+ ## How it works
88
+
89
+ 1. **Normalisation**: unify Arabic/Persian letters, digits and ZWNJ.
90
+ 2. **Diacritics**: any harakat present become hard constraints.
91
+ 3. **Morphology**: an affix table learned from the training data splits ZWNJ compounds (‌ها، می‌، ‌ترین …).
92
+ 4. **Wazn**: Arabic morphological templates (تفعیل، مفعول، افتعال …). Each pattern ships only if it helped on the dev split, with its own score margin and a confidence gate.
93
+ 5. **Tanvin**: a structural rule for a word ending in bare ا (نسبتا nesbatan, صراحتا sarahatan).
94
+ 6. **Model**: a compact graphone 4-gram trie LM plus a letter-context model, decoded with a Viterbi beam.
95
+ 7. **Phrase ezafe**, then rendering to Finglish.
96
+
97
+ The only fixed pronunciations are 30 closed-class function words (`src/function-words.ts`: و، از، به، که …). There are no lists of names, places or content words. The model is trained on the HomoRich G2P corpus (CC0-1.0).
98
+
99
+ ## Results
100
+
101
+ Measured on a held-out test split of 4,367 words whose stems never appear in training:
102
+
103
+ | | |
104
+ |---|---|
105
+ | exact match (cleaned refs) | **67.6 %** · 72.5 % frequency-weighted |
106
+ | exact match (raw HomoRich refs) | 59.2 % · 65.3 % weighted |
107
+ | hand-written slug cases | 99 / 110 |
108
+ | speed (Node, Apple Silicon) | ~20 µs per unique word (~49k words/s); ~110–125k words/s on running text |
109
+ | load | import ~2 ms, first call ~16–17 ms |
110
+ | size | 147 KB gzip / 129 KB brotli (whole library + model) |
111
+
112
+ See [bench/RESULTS.md](bench/RESULTS.md) for the full numbers, the ablations and a head-to-head against other npm packages.
113
+
114
+ ## Development
115
+
116
+ `npm test` · `npm run build` · `npm run size` (150 KB gzip budget) · `npm run eval` · `npm run bench` · `npm run train` · `npm run demo` (http://localhost:5180) · `npm run demo:build` (GitHub Pages build, base `/farsit/`) · `npx tsx tools/regress.ts` (diffs every output against `bench/baseline.json`)
117
+
118
+ ## License
119
+
120
+ MIT