lotin-kirill 0.1.4 → 1.0.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
@@ -7,7 +7,7 @@
7
7
  [![](https://img.shields.io/bundlephobia/minzip/lotin-kirill)](https://bundlephobia.com/result?p=lotin-kirill)
8
8
  [![](https://img.shields.io/npm/l/lotin-kirill)](https://npmjs.com/lotin-kirill)
9
9
 
10
- Transliterator for Uzbek words with high accuracy (from latin alphabet to cyrillic and vice versa). Used at [transliterator.uz](https://transliterator.uz/)
10
+ Transliterator for Uzbek text with high accuracy, between the 1995 and 2026 Latin alphabets and Cyrillic. Used at [transliterator.uz](https://transliterator.uz/)
11
11
 
12
12
  ### [O‘zbek tilida o‘qish](https://github.com/diyorbek/lotin-kirill/blob/master/README_UZ.md)
13
13
 
@@ -28,131 +28,100 @@ yarn add lotin-kirill
28
28
  UNPKG
29
29
 
30
30
  ```html
31
- <script src="https://unpkg.com/lotin-kirill/dist-umd/index.min.js"></script>
31
+ <script src="https://unpkg.com/lotin-kirill/dist/index.umd.js"></script>
32
32
  ```
33
33
 
34
34
  ## Usage
35
35
 
36
- Initialize the engine:
37
-
38
36
  ```js
39
- import Transliterator from 'lotin-kirill';
40
-
41
- const transliterator = new Transliterator();
42
- ```
37
+ import { createTransliterator } from 'lotin-kirill';
43
38
 
44
- When using UNPKG distribution:
39
+ const transliterator = createTransliterator();
45
40
 
46
- ```html
47
- <script src="https://unpkg.com/lotin-kirill/dist-umd/index.min.js"></script>
48
- <script>
49
- const Transliterator = lotinKirill.default;
50
- const transliterator = new Transliterator();
51
- </script>
41
+ transliterator.transliterate('Salom', { from: 'latin1995', to: 'cyrillic' }); // -> 'Салом'
42
+ transliterator.transliterate('Тошкент', { from: 'cyrillic', to: 'latin2026' }); // -> 'Toşkent'
52
43
  ```
53
44
 
54
- ### Single word transliteration:
45
+ The alphabets:
55
46
 
56
- `toLatin(word: string): string`
47
+ | Name | Alphabet |
48
+ | ----------- | ------------------------------------------------------------- |
49
+ | `latin1995` | the Latin alphabet in use since 1995 (`sh`, `ch`, `o‘`, `g‘`) |
50
+ | `latin2026` | the 2026 Latin alphabet (`ş`, `ç`, `ö`, `ğ`) |
51
+ | `cyrillic` | Cyrillic |
57
52
 
58
- `toCyrillic(word: string): string`
53
+ Any direction works, for example `{ from: 'latin2026', to: 'cyrillic' }`.
59
54
 
60
- Example
55
+ CommonJS:
61
56
 
62
57
  ```js
63
- const latinWord = transliterator.toLatin('мотивация');
64
- console.log(latinWord); // -> 'motivatsiya'
65
-
66
- const cyrillicWord = transliterator.toCyrillic("e'lon");
67
- console.log(cyrillicWord); // -> 'эълон'
58
+ const { createTransliterator } = require('lotin-kirill');
68
59
  ```
69
60
 
70
- ### Text (multiple words) transliteration:
71
-
72
- `textToLatin(text: string): string`
73
-
74
- `textToCyrillic(text: string): string`
75
-
76
- Example
61
+ When using UNPKG distribution:
77
62
 
78
- ```js
79
- const latinText = transliterator.textToLatin('Жуда узун кириллча текст.');
80
- console.log(latinText); // -> 'Juda uzun kirillcha tekst.'
63
+ ```html
64
+ <script src="https://unpkg.com/lotin-kirill/dist/index.umd.js"></script>
65
+ <script>
66
+ const transliterator = lotinKirill.createTransliterator();
81
67
 
82
- const cyrillicText = transliterator.textToCyrillic('Juda uzun lotincha tekst.');
83
- console.log(cyrillicText); // -> 'Жуда узун лотинча текст.'
68
+ transliterator.transliterate('Salom', { from: 'latin1995', to: 'cyrillic' }); // -> 'Салом'
69
+ </script>
84
70
  ```
85
71
 
86
72
  ### Exceptional words
87
73
 
88
- You can initialize the transliterator object with an exceptional words list:
74
+ Some words, mostly loanwords, don't follow the rules. Create a transliterator with a list of them. Each word gives its spelling in at least two alphabets:
89
75
 
90
76
  ```js
91
- import Transliterator from 'lotin-kirill';
77
+ import { createTransliterator } from 'lotin-kirill';
92
78
 
93
- const transliterator = new Transliterator([
94
- // [latinWord, cyrillicWord]
95
- ['oktabr', 'октябрь'],
96
- ['Google', 'Google'],
97
- ]);
79
+ const transliterator = createTransliterator({
80
+ exceptions: [
81
+ { latin1995: 'oktabr', cyrillic: 'октябрь' },
82
+ { latin1995: 'Chrome', cyrillic: 'Chrome', latin2026: 'Chrome' },
83
+ ],
84
+ });
98
85
 
99
- const cyrillicWord = transliterator.toCyrillic('oktabr');
100
- console.log(cyrillicWord); // -> 'октябрь' (not 'октабр')
86
+ transliterator.transliterate('oktabr', { from: 'latin1995', to: 'cyrillic' }); // -> 'октябрь' (not 'октабр')
101
87
  ```
102
88
 
103
- One exceptional pair is enough for both cyrilllic and latin transliterations.
89
+ One entry works in both directions:
104
90
 
105
91
  ```js
106
- // This also works
107
- const latinWord = transliterator.toLatin('октябрь');
108
- console.log(latinWord); // -> 'oktabr' (not 'oktyabr')
92
+ transliterator.transliterate('октябрь', { from: 'cyrillic', to: 'latin1995' }); // -> 'oktabr' (not 'oktyabr')
109
93
  ```
110
94
 
111
- If exceptional pair is a pair of same words, the word is not transliterated.
95
+ A word spelled the same in two alphabets isn't transliterated between them:
112
96
 
113
97
  ```js
114
- const cyrillicWord = transliterator.toCyrillic('Google');
115
- console.log(cyrillicWord); // -> 'Google'
98
+ transliterator.transliterate('Chrome', { from: 'latin1995', to: 'latin2026' }); // -> 'Chrome' (not 'Çrome')
116
99
  ```
117
100
 
118
- Exceptional words with sufixes are also detected.
101
+ Exceptional words with suffixes are also detected.
119
102
 
120
- **Variants of a word with prefixes should be added as separate exceptionals!**
103
+ **Variants of a word with prefixes should be added as separate exceptions!**
121
104
 
122
105
  ```js
123
- // This also works
124
- const latinWord = transliterator.toLatin('октябрда');
125
- console.log(latinWord); // -> 'oktabrda' (not 'oktyabrda')
106
+ transliterator.transliterate('октябрда', { from: 'cyrillic', to: 'latin1995' }); // -> 'oktabrda' (not 'oktyabrda')
126
107
  ```
127
108
 
128
- You can extend the exceptionals list after the initialization of the transliterator.
109
+ You can add exceptions later, or remove all of them:
129
110
 
130
111
  ```js
131
- transliterator.extendExceptionals([['nol', 'ноль']]);
112
+ transliterator.addExceptions([{ latin1995: 'nol', cyrillic: 'ноль' }]);
113
+ transliterator.clearExceptions();
132
114
  ```
133
115
 
134
- Or purge all of the exceptionals added to transliterator.
135
-
136
- ```js
137
- transliterator.purgeExceptionals();
138
- ```
139
-
140
- ### Pure transliterator functions
141
-
142
- There are pure transliterator functions which operate only on basic transliteration rules. These functions don't look up words in exceptionals list.
143
-
144
- `cyrillicToLatin(word: string): string`
145
-
146
- `latinToCyrillic(word: string): string`
147
-
148
- ```js
149
- import Transliterator, { cyrillicToLatin, latinToCyrillic } from 'lotin-kirill';
150
-
151
- const transliterator = new Transliterator([['oktabr', 'октябрь']]);
152
-
153
- console.log(transliterator.toLatin('октябрь')); // -> 'oktabr'
154
- console.log(cyrillicToLatin('октябрь')); // -> 'oktyabr'
155
-
156
- console.log(transliterator.toCyrillic('oktabr')); // -> 'октябрь'
157
- console.log(latinToCyrillic('oktabr')); // -> 'октабр'
158
- ```
116
+ ### Migrating from 0.x
117
+
118
+ | 0.x | 1.0 |
119
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------- |
120
+ | `new Transliterator([['oktabr', 'октябрь']])` | `createTransliterator({ exceptions: [{ latin1995: 'oktabr', cyrillic: 'октябрь' }] })` |
121
+ | `t.toCyrillic(text)`, `t.textToCyrillic(text)` | `t.transliterate(text, { from: 'latin1995', to: 'cyrillic' })` |
122
+ | `t.toLatin(text)`, `t.textToLatin(text)` | `t.transliterate(text, { from: 'cyrillic', to: 'latin1995' })` |
123
+ | `t.toCyrillic(['…', '…'])` | `words.map((word) => t.transliterate(word, { from, to }))` |
124
+ | `t.extendExceptionals(pairs)` | `t.addExceptions(words)` |
125
+ | `t.purgeExceptionals()` | `t.clearExceptions()` |
126
+ | `latinToCyrillic(word)`, `cyrillicToLatin(word)` | `createTransliterator().transliterate(word, { from, to })` |
127
+ | `lotinKirill.default` (UNPKG) | `lotinKirill.createTransliterator` |
@@ -0,0 +1,6 @@
1
+ import type { Entry } from './lexicon';
2
+ export declare function lower(s: string): string;
3
+ /** Put the original capitals back on the lowercase replacement for `word[start..end)`. */
4
+ export declare function restoreLetterCase(replacement: string, word: string, start: number, end: number): string;
5
+ /** Put the original capitals back on an exception: as written, lower, UPPER or Capitalized. */
6
+ export declare function restoreWordCase(entry: Entry, source: string): string;
@@ -0,0 +1,2 @@
1
+ export { chain, Transliteration } from './transliteration';
2
+ export * from './types';
@@ -0,0 +1,15 @@
1
+ import type { Pair } from './types';
2
+ export interface Entry {
3
+ key: string;
4
+ target: string;
5
+ lower: string;
6
+ }
7
+ export declare class Lexicon {
8
+ private words;
9
+ private stems;
10
+ private longestStem;
11
+ add(kind: 'words' | 'stems', prepared: string, [key, target]: Pair): void;
12
+ /** The whole word, or else the longest stem it starts with. */
13
+ find(word: string): [Entry, number] | undefined;
14
+ clear(): void;
15
+ }
@@ -0,0 +1,24 @@
1
+ import type { Definition, Exceptions } from './types';
2
+ export declare class Transliteration {
3
+ private normalize;
4
+ private letters;
5
+ private longestLetter;
6
+ private rules;
7
+ private skip;
8
+ private word;
9
+ private isWord;
10
+ private wordExceptions;
11
+ private tokenExceptions;
12
+ constructor(definition: Definition);
13
+ addExceptions({ words, stems }: Exceptions): void;
14
+ clearExceptions(): void;
15
+ transliterate(text: string): string;
16
+ private transliterateToken;
17
+ private transliterateWords;
18
+ private transliterateWord;
19
+ /** Where rules match: position → [length, replacement]. The first rule wins. */
20
+ private matchRules;
21
+ private prepare;
22
+ }
23
+ /** Run several transliterations one after another, e.g. through a hub script. */
24
+ export declare function chain(...steps: Array<(text: string) => string>): (text: string) => string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A context rule: [pattern, replacement].
3
+ * Patterns are matched against the lowercased original word, so lookbehind and
4
+ * lookahead always see the source text, never a half-transliterated one.
5
+ * `^` and `$` are the edges of the word.
6
+ */
7
+ export type Rule = [RegExp, string | ((match: RegExpExecArray) => string)];
8
+ /** [source, target] */
9
+ export type Pair = [string, string];
10
+ export interface Exceptions {
11
+ /** Must match the whole word. */
12
+ words?: Pair[];
13
+ /** Match the beginning of a word; the rest is transliterated as usual. */
14
+ stems?: Pair[];
15
+ }
16
+ export interface Definition extends Exceptions {
17
+ /** Letters and letter groups, lowercase. The longest match wins; a later duplicate overrides. */
18
+ letters: Pair[];
19
+ /** Checked before `letters`; the first rule that matches at a position wins. */
20
+ rules?: Rule[];
21
+ /** Tokens to leave as they are, e.g. Roman numerals or URLs. */
22
+ skip?: Array<RegExp | ((token: string) => boolean)>;
23
+ /** What a word is. Default: letters, combining marks and apostrophes. */
24
+ word?: RegExp;
25
+ /** Clean-up of the lowercased word before matching. Must keep the length. */
26
+ normalize?: (word: string) => string;
27
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));let c=require("anchorme");c=s(c,1);var l=e=>e.toLowerCase()!==e.toUpperCase(),u=e=>e!==void 0&&e!==e.toLowerCase(),d=e=>e!==void 0&&l(e);function f(e){let t=0;for(;t<e.length&&!l(e[t]);)t++;return e.slice(0,t)+e.slice(t,t+1).toUpperCase()+e.slice(t+1).toLowerCase()}function p(e){let t=e.toLowerCase();return t.length===e.length?t:Array.from(e,e=>{let t=e.toLowerCase();return t.length===e.length?t:e}).join(``)}function m(e,t,n,r){if(!e)return e;let i=t.slice(n,r),a=Array.from(i).filter(l);if(!a.length)return u(t[r]??t[n-1])?e.toUpperCase():e;if(!a.some(u))return e;if(i.length>1&&i.length===e.length)return Array.from(e,(e,t)=>u(i[t])?e.toUpperCase():e).join(``);if(i.length>1)return a.every(u)?e.toUpperCase():u(a[0])?f(e):e;if(Array.from(e).filter(l).length<2)return e.toUpperCase();let o=t[r];return d(o)?u(o)?e.toUpperCase():f(e):d(t[n-1])?e.toUpperCase():f(e)}function h(e,t){if(t===e.key)return e.target;let n=Array.from(t).filter(l);return n.length&&n.every(u)?e.lower.toUpperCase():u(n[0])?f(e.lower):e.lower}var g=class{words=new Map;stems=new Map;longestStem=0;add(e,t,[n,r]){let i=this[e];i.has(t)||(i.set(t,{key:n,target:r,lower:r.toLowerCase()}),e===`stems`&&(this.longestStem=Math.max(this.longestStem,t.length)))}find(e){let t=this.words.get(e);if(t)return[t,e.length];for(let t=Math.min(e.length,this.longestStem);t>0;t--){let n=this.stems.get(e.slice(0,t));if(n)return[n,t]}}clear(){this.words.clear(),this.stems.clear(),this.longestStem=0}},_=/[\p{L}\p{M}ʻʼ’'`‘´]+/u,v=class{normalize;letters;longestLetter;rules;skip;word;isWord;wordExceptions=new g;tokenExceptions=new g;constructor(e){let t=e.word??_,n=(e,t=``)=>e.flags.replace(/[gy]/g,``)+t;this.normalize=e.normalize??(e=>e),this.letters=new Map(e.letters.map(([e,t])=>[this.prepare(e),t])),this.longestLetter=Math.max(0,...Array.from(this.letters.keys(),e=>e.length)),this.rules=(e.rules??[]).map(([e,t])=>[new RegExp(e.source,n(e,`g`)),t]),this.skip=(e.skip??[]).map(e=>{if(typeof e==`function`)return e;let t=new RegExp(e.source,n(e));return e=>t.test(e)}),this.word=new RegExp(t.source,n(t,`g`)),this.isWord=RegExp(`^(?:${t.source})$`,n(t)),this.addExceptions(e)}addExceptions({words:e=[],stems:t=[]}){for(let[n,r]of[[`words`,e],[`stems`,t]])for(let e of r){let t=this.prepare(e[0]);(this.isWord.test(t)?this.wordExceptions:this.tokenExceptions).add(n,t,e)}}clearExceptions(){this.wordExceptions.clear(),this.tokenExceptions.clear()}transliterate(e){return e.replace(/\S+/g,e=>this.transliterateToken(e))}transliterateToken(e){if(this.skip.some(t=>t(e)))return e;let t=this.tokenExceptions.find(this.prepare(e));if(t){let[n,r]=t;return h(n,e.slice(0,r))+this.transliterateWords(e.slice(r))}return this.transliterateWords(e)}transliterateWords(e){return e.replace(this.word,e=>this.transliterateWord(e))}transliterateWord(e){let t=this.prepare(e),n=``,r=0,i=this.wordExceptions.find(t);if(i){let[t,a]=i;n=h(t,e.slice(0,a)),r=a}let a=this.matchRules(t,r);for(;r<t.length;){let i=0,o,s=a?.get(r);if(s)[i,o]=s;else for(i=Math.min(this.longestLetter,t.length-r);i>0&&(o=this.letters.get(t.slice(r,r+i)),o===void 0);i--);o===void 0?n+=e[r++]:(n+=m(o,e,r,r+i),r+=i)}return n}matchRules(e,t){let n;for(let[r,i]of this.rules){r.lastIndex=t;for(let t=r.exec(e);t;t=r.exec(e))t[0]&&!n?.has(t.index)&&(n||=new Map,n.set(t.index,[t[0].length,typeof i==`function`?i(t):i])),r.lastIndex=t.index+1}return n}prepare(e){let t=this.normalize(p(e));if(t.length!==e.length)throw Error(`normalize() must not change the length of a word`);return t}};function y(...e){return t=>e.reduce((e,t)=>t(e),t)}var b=`list`in c.default?c.default:c.default.default,x=/[.:@/]/;function S(e){return x.test(e)&&b.list(e).length>0}var C=/^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})(\.|\))?$/,w=e=>e.replace(/[ʻʼ’'`‘´]/g,`ʼ`),T=e=>w(e).replace(/([og])ʼ/gi,`$1‘`),E=`аоэеиуўёюя`,D={letters:[[`a`,`а`],[`b`,`б`],[`d`,`д`],[`e`,`е`],[`f`,`ф`],[`g`,`г`],[`h`,`ҳ`],[`i`,`и`],[`j`,`ж`],[`k`,`к`],[`l`,`л`],[`m`,`м`],[`n`,`н`],[`o`,`о`],[`p`,`п`],[`q`,`қ`],[`r`,`р`],[`s`,`с`],[`t`,`т`],[`u`,`у`],[`v`,`в`],[`x`,`х`],[`y`,`й`],[`z`,`з`],[`sh`,`ш`],[`ch`,`ч`],[`oʼ`,`ў`],[`gʼ`,`ғ`],[`ye`,`е`],[`yo`,`ё`],[`yu`,`ю`],[`ya`,`я`],[`ʼ`,`ъ`],[`ee`,`еэ`],[`yee`,`еэ`],[`yoʼ`,`йў`]],rules:[[/^e/,`э`],[/ts(?=i(on|ya))/,`ц`],[/(?<=s)ʼ(?=h)/,``]],normalize:w,skip:[C,S]},O={letters:[[`а`,`a`],[`б`,`b`],[`в`,`v`],[`г`,`g`],[`д`,`d`],[`е`,`e`],[`ё`,`yo`],[`ж`,`j`],[`з`,`z`],[`и`,`i`],[`й`,`y`],[`к`,`k`],[`л`,`l`],[`м`,`m`],[`н`,`n`],[`о`,`o`],[`п`,`p`],[`р`,`r`],[`с`,`s`],[`т`,`t`],[`у`,`u`],[`ф`,`f`],[`х`,`x`],[`ц`,`s`],[`ч`,`ch`],[`ш`,`sh`],[`ъ`,`ʼ`],[`ь`,``],[`э`,`e`],[`ю`,`yu`],[`я`,`ya`],[`ў`,`o‘`],[`қ`,`q`],[`ғ`,`g‘`],[`ҳ`,`h`]],rules:[[/^е/,`ye`],[/е{3,}/,e=>`e`.repeat(e[0].length)],[RegExp(`(?<=[${E}ьъ])е`),`ye`],[/(?<=[ўғ])ъ|ъ(?=е)/,``],[/(?<=ь)о/,`yo`],[RegExp(`(?<=[${E}aeiou])ц`),`ts`],[/(?<=с)ҳ/,`ʼh`]],skip:[C,S]},k={letters:[[`sh`,`ş`],[`ch`,`ç`],[`oʼ`,`ö`],[`gʼ`,`ğ`]],rules:[[/(?<=s)ʼ(?=h)/,``]],normalize:w,skip:[C,S]},A={letters:[[`ş`,`sh`],[`ç`,`ch`],[`ö`,`o‘`],[`ğ`,`g‘`]],rules:[[/(?<=s)h/,`ʼh`]],skip:[C,S]},j=[`latin1995`,`latin2026`,`cyrillic`];function M({exceptions:e=[]}={}){let t={latin1995ToCyrillic:new v(D),cyrillicToLatin1995:new v(O),latin1995ToLatin2026:new v(k),latin2026ToLatin1995:new v(A)},n=e=>t.latin1995ToCyrillic.transliterate(e),r=e=>t.cyrillicToLatin1995.transliterate(e),i=e=>t.latin1995ToLatin2026.transliterate(e),a=e=>t.latin2026ToLatin1995.transliterate(e),o=e=>e,s={latin1995:{latin1995:o,latin2026:i,cyrillic:n},latin2026:{latin1995:a,latin2026:o,cyrillic:y(a,n)},cyrillic:{latin1995:r,latin2026:y(r,i),cyrillic:o}},c={transliterate(e,{from:t,to:n}){for(let e of[t,n])if(!j.includes(e))throw Error(`Unknown alphabet: ${e}`);return s[t][n](e)},addExceptions(e){for(let n of e){let{cyrillic:e,latin2026:r}=n;if([n.latin1995,e,r].filter(Boolean).length<2)throw Error(`Invalid exceptional word ${JSON.stringify(n)}`);let i=n.latin1995||a(r),o=T(i);if(e){let n=e.replace(/ь$/i,``);t.latin1995ToCyrillic.addExceptions({words:[[i,e]],stems:[[i,n]]}),t.cyrillicToLatin1995.addExceptions({words:[[e,o]],stems:[[e,o],[n,o]]})}r&&(t.latin1995ToLatin2026.addExceptions({stems:[[i,r]]}),t.latin2026ToLatin1995.addExceptions({stems:[[r,o]]}))}},clearExceptions(){for(let e of Object.values(t))e.clearExceptions()}};return c.addExceptions(e),c}exports.createTransliterator=M;
@@ -0,0 +1,2 @@
1
+ export type { Alphabet, Direction, ExceptionalWord, Transliterator, TransliteratorOptions, } from './uzbek';
2
+ export { createTransliterator } from './uzbek';
package/dist/index.js ADDED
@@ -0,0 +1,308 @@
1
+ import e from "anchorme";
2
+ // src/engine/casing.ts
3
+ var t = (e) => e.toLowerCase() !== e.toUpperCase(), n = (e) => e !== void 0 && e !== e.toLowerCase(), r = (e) => e !== void 0 && t(e);
4
+ function i(e) {
5
+ let n = 0;
6
+ for (; n < e.length && !t(e[n]);) n++;
7
+ return e.slice(0, n) + e.slice(n, n + 1).toUpperCase() + e.slice(n + 1).toLowerCase();
8
+ }
9
+ function a(e) {
10
+ let t = e.toLowerCase();
11
+ return t.length === e.length ? t : Array.from(e, (e) => {
12
+ let t = e.toLowerCase();
13
+ return t.length === e.length ? t : e;
14
+ }).join("");
15
+ }
16
+ function o(e, a, o, s) {
17
+ if (!e) return e;
18
+ let c = a.slice(o, s), l = Array.from(c).filter(t);
19
+ if (!l.length) return n(a[s] ?? a[o - 1]) ? e.toUpperCase() : e;
20
+ if (!l.some(n)) return e;
21
+ if (c.length > 1 && c.length === e.length) return Array.from(e, (e, t) => n(c[t]) ? e.toUpperCase() : e).join("");
22
+ if (c.length > 1) return l.every(n) ? e.toUpperCase() : n(l[0]) ? i(e) : e;
23
+ if (Array.from(e).filter(t).length < 2) return e.toUpperCase();
24
+ let u = a[s];
25
+ return r(u) ? n(u) ? e.toUpperCase() : i(e) : r(a[o - 1]) ? e.toUpperCase() : i(e);
26
+ }
27
+ function s(e, r) {
28
+ if (r === e.key) return e.target;
29
+ let a = Array.from(r).filter(t);
30
+ return a.length && a.every(n) ? e.lower.toUpperCase() : n(a[0]) ? i(e.lower) : e.lower;
31
+ }
32
+ // src/engine/lexicon.ts
33
+ var c = class {
34
+ words = /* @__PURE__ */ new Map();
35
+ stems = /* @__PURE__ */ new Map();
36
+ longestStem = 0;
37
+ add(e, t, [n, r]) {
38
+ let i = this[e];
39
+ i.has(t) || (i.set(t, {
40
+ key: n,
41
+ target: r,
42
+ lower: r.toLowerCase()
43
+ }), e === "stems" && (this.longestStem = Math.max(this.longestStem, t.length)));
44
+ }
45
+ find(e) {
46
+ let t = this.words.get(e);
47
+ if (t) return [t, e.length];
48
+ for (let t = Math.min(e.length, this.longestStem); t > 0; t--) {
49
+ let n = this.stems.get(e.slice(0, t));
50
+ if (n) return [n, t];
51
+ }
52
+ }
53
+ clear() {
54
+ this.words.clear(), this.stems.clear(), this.longestStem = 0;
55
+ }
56
+ }, l = /[\p{L}\p{M}ʻʼ’'`‘´]+/u, u = class {
57
+ normalize;
58
+ letters;
59
+ longestLetter;
60
+ rules;
61
+ skip;
62
+ word;
63
+ isWord;
64
+ wordExceptions = new c();
65
+ tokenExceptions = new c();
66
+ constructor(e) {
67
+ let t = e.word ?? l, n = (e, t = "") => e.flags.replace(/[gy]/g, "") + t;
68
+ this.normalize = e.normalize ?? ((e) => e), this.letters = new Map(e.letters.map(([e, t]) => [this.prepare(e), t])), this.longestLetter = Math.max(0, ...Array.from(this.letters.keys(), (e) => e.length)), this.rules = (e.rules ?? []).map(([e, t]) => [new RegExp(e.source, n(e, "g")), t]), this.skip = (e.skip ?? []).map((e) => {
69
+ if (typeof e == "function") return e;
70
+ let t = new RegExp(e.source, n(e));
71
+ return (e) => t.test(e);
72
+ }), this.word = new RegExp(t.source, n(t, "g")), this.isWord = RegExp(`^(?:${t.source})$`, n(t)), this.addExceptions(e);
73
+ }
74
+ addExceptions({ words: e = [], stems: t = [] }) {
75
+ for (let [n, r] of [["words", e], ["stems", t]]) for (let e of r) {
76
+ let t = this.prepare(e[0]);
77
+ (this.isWord.test(t) ? this.wordExceptions : this.tokenExceptions).add(n, t, e);
78
+ }
79
+ }
80
+ clearExceptions() {
81
+ this.wordExceptions.clear(), this.tokenExceptions.clear();
82
+ }
83
+ transliterate(e) {
84
+ return e.replace(/\S+/g, (e) => this.transliterateToken(e));
85
+ }
86
+ transliterateToken(e) {
87
+ if (this.skip.some((t) => t(e))) return e;
88
+ let t = this.tokenExceptions.find(this.prepare(e));
89
+ if (t) {
90
+ let [n, r] = t;
91
+ return s(n, e.slice(0, r)) + this.transliterateWords(e.slice(r));
92
+ }
93
+ return this.transliterateWords(e);
94
+ }
95
+ transliterateWords(e) {
96
+ return e.replace(this.word, (e) => this.transliterateWord(e));
97
+ }
98
+ transliterateWord(e) {
99
+ let t = this.prepare(e), n = "", r = 0, i = this.wordExceptions.find(t);
100
+ if (i) {
101
+ let [t, a] = i;
102
+ n = s(t, e.slice(0, a)), r = a;
103
+ }
104
+ let a = this.matchRules(t, r);
105
+ for (; r < t.length;) {
106
+ let i = 0, s, c = a?.get(r);
107
+ if (c) [i, s] = c;
108
+ else for (i = Math.min(this.longestLetter, t.length - r); i > 0 && (s = this.letters.get(t.slice(r, r + i)), s === void 0); i--);
109
+ s === void 0 ? n += e[r++] : (n += o(s, e, r, r + i), r += i);
110
+ }
111
+ return n;
112
+ }
113
+ matchRules(e, t) {
114
+ let n;
115
+ for (let [r, i] of this.rules) {
116
+ r.lastIndex = t;
117
+ for (let t = r.exec(e); t; t = r.exec(e)) t[0] && !n?.has(t.index) && (n ||= /* @__PURE__ */ new Map(), n.set(t.index, [t[0].length, typeof i == "function" ? i(t) : i])), r.lastIndex = t.index + 1;
118
+ }
119
+ return n;
120
+ }
121
+ prepare(e) {
122
+ let t = this.normalize(a(e));
123
+ if (t.length !== e.length) throw Error("normalize() must not change the length of a word");
124
+ return t;
125
+ }
126
+ };
127
+ function d(...e) {
128
+ return (t) => e.reduce((e, t) => t(e), t);
129
+ }
130
+ // src/skip.ts
131
+ var f = "list" in e ? e : e.default, p = /[.:@/]/;
132
+ function m(e) {
133
+ return p.test(e) && f.list(e).length > 0;
134
+ }
135
+ var h = /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})(\.|\))?$/, g = (e) => e.replace(/[ʻʼ’'`‘´]/g, "ʼ"), _ = (e) => g(e).replace(/([og])ʼ/gi, "$1‘"), v = "аоэеиуўёюя", y = {
136
+ letters: [
137
+ ["a", "а"],
138
+ ["b", "б"],
139
+ ["d", "д"],
140
+ ["e", "е"],
141
+ ["f", "ф"],
142
+ ["g", "г"],
143
+ ["h", "ҳ"],
144
+ ["i", "и"],
145
+ ["j", "ж"],
146
+ ["k", "к"],
147
+ ["l", "л"],
148
+ ["m", "м"],
149
+ ["n", "н"],
150
+ ["o", "о"],
151
+ ["p", "п"],
152
+ ["q", "қ"],
153
+ ["r", "р"],
154
+ ["s", "с"],
155
+ ["t", "т"],
156
+ ["u", "у"],
157
+ ["v", "в"],
158
+ ["x", "х"],
159
+ ["y", "й"],
160
+ ["z", "з"],
161
+ ["sh", "ш"],
162
+ ["ch", "ч"],
163
+ ["oʼ", "ў"],
164
+ ["gʼ", "ғ"],
165
+ ["ye", "е"],
166
+ ["yo", "ё"],
167
+ ["yu", "ю"],
168
+ ["ya", "я"],
169
+ ["ʼ", "ъ"],
170
+ ["ee", "еэ"],
171
+ ["yee", "еэ"],
172
+ ["yoʼ", "йў"]
173
+ ],
174
+ rules: [
175
+ [/^e/, "э"],
176
+ [/ts(?=i(on|ya))/, "ц"],
177
+ [/(?<=s)ʼ(?=h)/, ""]
178
+ ],
179
+ normalize: g,
180
+ skip: [h, m]
181
+ }, b = {
182
+ letters: [
183
+ ["а", "a"],
184
+ ["б", "b"],
185
+ ["в", "v"],
186
+ ["г", "g"],
187
+ ["д", "d"],
188
+ ["е", "e"],
189
+ ["ё", "yo"],
190
+ ["ж", "j"],
191
+ ["з", "z"],
192
+ ["и", "i"],
193
+ ["й", "y"],
194
+ ["к", "k"],
195
+ ["л", "l"],
196
+ ["м", "m"],
197
+ ["н", "n"],
198
+ ["о", "o"],
199
+ ["п", "p"],
200
+ ["р", "r"],
201
+ ["с", "s"],
202
+ ["т", "t"],
203
+ ["у", "u"],
204
+ ["ф", "f"],
205
+ ["х", "x"],
206
+ ["ц", "s"],
207
+ ["ч", "ch"],
208
+ ["ш", "sh"],
209
+ ["ъ", "ʼ"],
210
+ ["ь", ""],
211
+ ["э", "e"],
212
+ ["ю", "yu"],
213
+ ["я", "ya"],
214
+ ["ў", "o‘"],
215
+ ["қ", "q"],
216
+ ["ғ", "g‘"],
217
+ ["ҳ", "h"]
218
+ ],
219
+ rules: [
220
+ [/^е/, "ye"],
221
+ [/е{3,}/, (e) => "e".repeat(e[0].length)],
222
+ [RegExp(`(?<=[${v}ьъ])е`), "ye"],
223
+ [/(?<=[ўғ])ъ|ъ(?=е)/, ""],
224
+ [/(?<=ь)о/, "yo"],
225
+ [RegExp(`(?<=[${v}aeiou])ц`), "ts"],
226
+ [/(?<=с)ҳ/, "ʼh"]
227
+ ],
228
+ skip: [h, m]
229
+ }, x = {
230
+ letters: [
231
+ ["sh", "ş"],
232
+ ["ch", "ç"],
233
+ ["oʼ", "ö"],
234
+ ["gʼ", "ğ"]
235
+ ],
236
+ rules: [[/(?<=s)ʼ(?=h)/, ""]],
237
+ normalize: g,
238
+ skip: [h, m]
239
+ }, S = {
240
+ letters: [
241
+ ["ş", "sh"],
242
+ ["ç", "ch"],
243
+ ["ö", "o‘"],
244
+ ["ğ", "g‘"]
245
+ ],
246
+ rules: [[/(?<=s)h/, "ʼh"]],
247
+ skip: [h, m]
248
+ }, C = [
249
+ "latin1995",
250
+ "latin2026",
251
+ "cyrillic"
252
+ ];
253
+ function w({ exceptions: e = [] } = {}) {
254
+ let t = {
255
+ latin1995ToCyrillic: new u(y),
256
+ cyrillicToLatin1995: new u(b),
257
+ latin1995ToLatin2026: new u(x),
258
+ latin2026ToLatin1995: new u(S)
259
+ }, n = (e) => t.latin1995ToCyrillic.transliterate(e), r = (e) => t.cyrillicToLatin1995.transliterate(e), i = (e) => t.latin1995ToLatin2026.transliterate(e), a = (e) => t.latin2026ToLatin1995.transliterate(e), o = (e) => e, s = {
260
+ latin1995: {
261
+ latin1995: o,
262
+ latin2026: i,
263
+ cyrillic: n
264
+ },
265
+ latin2026: {
266
+ latin1995: a,
267
+ latin2026: o,
268
+ cyrillic: d(a, n)
269
+ },
270
+ cyrillic: {
271
+ latin1995: r,
272
+ latin2026: d(r, i),
273
+ cyrillic: o
274
+ }
275
+ }, c = {
276
+ transliterate(e, { from: t, to: n }) {
277
+ for (let e of [t, n]) if (!C.includes(e)) throw Error(`Unknown alphabet: ${e}`);
278
+ return s[t][n](e);
279
+ },
280
+ addExceptions(e) {
281
+ for (let n of e) {
282
+ let { cyrillic: e, latin2026: r } = n;
283
+ if ([
284
+ n.latin1995,
285
+ e,
286
+ r
287
+ ].filter(Boolean).length < 2) throw Error(`Invalid exceptional word ${JSON.stringify(n)}`);
288
+ let i = n.latin1995 || a(r), o = _(i);
289
+ if (e) {
290
+ let n = e.replace(/ь$/i, "");
291
+ t.latin1995ToCyrillic.addExceptions({
292
+ words: [[i, e]],
293
+ stems: [[i, n]]
294
+ }), t.cyrillicToLatin1995.addExceptions({
295
+ words: [[e, o]],
296
+ stems: [[e, o], [n, o]]
297
+ });
298
+ }
299
+ r && (t.latin1995ToLatin2026.addExceptions({ stems: [[i, r]] }), t.latin2026ToLatin1995.addExceptions({ stems: [[r, o]] }));
300
+ }
301
+ },
302
+ clearExceptions() {
303
+ for (let e of Object.values(t)) e.clearExceptions();
304
+ }
305
+ };
306
+ return c.addExceptions(e), c;
307
+ }
308
+ export { w as createTransliterator };