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 +53 -84
- package/dist/engine/casing.d.ts +6 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/lexicon.d.ts +15 -0
- package/dist/engine/transliteration.d.ts +24 -0
- package/dist/engine/types.d.ts +27 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +308 -0
- package/dist/index.umd.js +1 -0
- package/dist/skip.d.ts +4 -0
- package/dist/uzbek/cyrillic.d.ts +3 -0
- package/dist/uzbek/index.d.ts +32 -0
- package/dist/uzbek/latin1995.d.ts +4 -0
- package/dist/uzbek/latin2026.d.ts +3 -0
- package/package.json +34 -60
- package/README_UZ.md +0 -158
- package/pkg/LICENSE +0 -21
- package/pkg/README.md +0 -158
- package/pkg/dist-node/index.js +0 -591
- package/pkg/dist-node/index.js.map +0 -1
- package/pkg/dist-src/characters.js +0 -128
- package/pkg/dist-src/cyrillic.js +0 -92
- package/pkg/dist-src/exceptionalsCollection.js +0 -138
- package/pkg/dist-src/index.js +0 -101
- package/pkg/dist-src/latin.js +0 -72
- package/pkg/dist-src/utils.js +0 -48
- package/pkg/dist-types/characters.d.ts +0 -130
- package/pkg/dist-types/cyrillic.d.ts +0 -1
- package/pkg/dist-types/exceptionalsCollection.d.ts +0 -19
- package/pkg/dist-types/index.d.ts +0 -21
- package/pkg/dist-types/latin.d.ts +0 -1
- package/pkg/dist-types/utils.d.ts +0 -12
- package/pkg/dist-umd/index.min.js +0 -1
- package/pkg/package.json +0 -54
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://bundlephobia.com/result?p=lotin-kirill)
|
|
8
8
|
[](https://npmjs.com/lotin-kirill)
|
|
9
9
|
|
|
10
|
-
Transliterator for Uzbek
|
|
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
|
|
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
|
|
40
|
-
|
|
41
|
-
const transliterator = new Transliterator();
|
|
42
|
-
```
|
|
37
|
+
import { createTransliterator } from 'lotin-kirill';
|
|
43
38
|
|
|
44
|
-
|
|
39
|
+
const transliterator = createTransliterator();
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
45
|
+
The alphabets:
|
|
55
46
|
|
|
56
|
-
|
|
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
|
-
`
|
|
53
|
+
Any direction works, for example `{ from: 'latin2026', to: 'cyrillic' }`.
|
|
59
54
|
|
|
60
|
-
|
|
55
|
+
CommonJS:
|
|
61
56
|
|
|
62
57
|
```js
|
|
63
|
-
const
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
`textToLatin(text: string): string`
|
|
73
|
-
|
|
74
|
-
`textToCyrillic(text: string): string`
|
|
75
|
-
|
|
76
|
-
Example
|
|
61
|
+
When using UNPKG distribution:
|
|
77
62
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
83
|
-
|
|
68
|
+
transliterator.transliterate('Salom', { from: 'latin1995', to: 'cyrillic' }); // -> 'Салом'
|
|
69
|
+
</script>
|
|
84
70
|
```
|
|
85
71
|
|
|
86
72
|
### Exceptional words
|
|
87
73
|
|
|
88
|
-
|
|
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
|
|
77
|
+
import { createTransliterator } from 'lotin-kirill';
|
|
92
78
|
|
|
93
|
-
const transliterator =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
]
|
|
79
|
+
const transliterator = createTransliterator({
|
|
80
|
+
exceptions: [
|
|
81
|
+
{ latin1995: 'oktabr', cyrillic: 'октябрь' },
|
|
82
|
+
{ latin1995: 'Chrome', cyrillic: 'Chrome', latin2026: 'Chrome' },
|
|
83
|
+
],
|
|
84
|
+
});
|
|
98
85
|
|
|
99
|
-
|
|
100
|
-
console.log(cyrillicWord); // -> 'октябрь' (not 'октабр')
|
|
86
|
+
transliterator.transliterate('oktabr', { from: 'latin1995', to: 'cyrillic' }); // -> 'октябрь' (not 'октабр')
|
|
101
87
|
```
|
|
102
88
|
|
|
103
|
-
One
|
|
89
|
+
One entry works in both directions:
|
|
104
90
|
|
|
105
91
|
```js
|
|
106
|
-
//
|
|
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
|
-
|
|
95
|
+
A word spelled the same in two alphabets isn't transliterated between them:
|
|
112
96
|
|
|
113
97
|
```js
|
|
114
|
-
|
|
115
|
-
console.log(cyrillicWord); // -> 'Google'
|
|
98
|
+
transliterator.transliterate('Chrome', { from: 'latin1995', to: 'latin2026' }); // -> 'Chrome' (not 'Çrome')
|
|
116
99
|
```
|
|
117
100
|
|
|
118
|
-
Exceptional words with
|
|
101
|
+
Exceptional words with suffixes are also detected.
|
|
119
102
|
|
|
120
|
-
**Variants of a word with prefixes should be added as separate
|
|
103
|
+
**Variants of a word with prefixes should be added as separate exceptions!**
|
|
121
104
|
|
|
122
105
|
```js
|
|
123
|
-
//
|
|
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
|
|
109
|
+
You can add exceptions later, or remove all of them:
|
|
129
110
|
|
|
130
111
|
```js
|
|
131
|
-
transliterator.
|
|
112
|
+
transliterator.addExceptions([{ latin1995: 'nol', cyrillic: 'ноль' }]);
|
|
113
|
+
transliterator.clearExceptions();
|
|
132
114
|
```
|
|
133
115
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`cyrillicToLatin(word
|
|
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,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;
|
package/dist/index.d.ts
ADDED
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 };
|