lyric-romanizer 0.1.1 → 0.3.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/CHANGELOG.md +40 -0
- package/README.md +128 -36
- package/dist/detector.d.ts +74 -0
- package/dist/detector.d.ts.map +1 -1
- package/dist/detector.js +73 -23
- package/dist/detector.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/romanizer.d.ts +7 -2
- package/dist/romanizer.d.ts.map +1 -1
- package/dist/romanizer.js +174 -88
- package/dist/romanizer.js.map +1 -1
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/README.hi.md +296 -0
- package/docs/README.ja.md +296 -0
- package/docs/README.ko.md +296 -0
- package/docs/README.ru.md +296 -0
- package/docs/README.ta.md +296 -0
- package/docs/README.th.md +296 -0
- package/docs/README.zh-CN.md +296 -0
- package/docs/README.zh-yue.md +306 -0
- package/docs/adr/0001-outputs-are-a-contract.md +21 -0
- package/docs/adr/0002-whole-array-script-pinning.md +26 -0
- package/docs/adr/0003-zero-api-default-injectable-engines.md +22 -0
- package/docs/adr/0004-no-unified-variant-seam.md +23 -0
- package/docs/adr/0005-always-latest-dependencies.md +23 -0
- package/package.json +23 -15
- package/src/detector.ts +127 -0
- package/src/index.ts +18 -0
- package/src/romanizer.ts +240 -0
- package/src/shims.d.ts +3 -0
- package/src/types.ts +75 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 — 2026-07-25
|
|
4
|
+
|
|
5
|
+
Architecture pass: every piece of per-script knowledge now lives in one table per side of the light/heavy packaging seam, engines are lazy and injectable, and the universal fallback is observable. Public interface changes are **additive**; the two deliberate output-behavior changes are listed below with their rationale.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Engine adapters** — `createRomanizer({ engines })` overrides any built-in engine or plugs an adapter for externally-romanized scripts (`arabic`, `hebrew`, `malayalam`, `bengali`, `other`). The library still performs zero network I/O by default; `UnsupportedRomanizationError` is now precisely "no engine for this script".
|
|
10
|
+
- **`RomanizeResult.fallbacks`** — per-line flags, `true` where the script engine failed and the line was universally transliterated as a last resort. Previously this degradation was silent and indistinguishable from success.
|
|
11
|
+
- **`requiresExternalRomanization` on the detector subpath** — pure classification moved to the light, engine-free side; `lyric-romanizer/detector` can now answer the full pre-romanization triage (which script → is it latin → needs external) with zero engine payload. Still re-exported from the main entry.
|
|
12
|
+
- **`RomanizeEngine`, `RomanizeEngineContext`, `ExternalScript`** exported types.
|
|
13
|
+
- **Plain Node ESM support** — `import 'lyric-romanizer'` previously crashed outside bundlers (`@romanize/korean` named-export interop); engines now load through interop-safe dynamic imports.
|
|
14
|
+
- **Lazy engine loading** — every engine loads on first use. Importing the main entry no longer executes any engine package; a Korean-only consumer never loads pinyin-pro or kuroshiro. Failed loads retry on the next call and degrade to the universal fallback (flagged) rather than crashing playback.
|
|
15
|
+
|
|
16
|
+
### Changed — deliberate output-behavior changes
|
|
17
|
+
|
|
18
|
+
1. **Latin guard under a pinned script.** `romanizeLines` pins one script for the whole array; lines with ASCII letters and no character of any detectable script are now returned unchanged instead of being fed to the pinned engine. Before: `romanizeLines(['Hello', '世界'])` → `['H e l l o', 'shì jiè']` (pinyin-pro spaces out Latin text). After: `['Hello', 'shì jiè']`. Rationale: the engine cannot romanize what isn't its script; the old output was mangled.
|
|
19
|
+
2. **Out-of-contract script strings throw.** Untyped callers pinning a string outside `ScriptType` (e.g. `{ script: 'greek' }`) now get `UnsupportedRomanizationError` instead of a silent universal transliteration. This includes names inherited from `Object.prototype`: `{ script: 'toString' }` previously resolved to the inherited method and returned `'[object Undefined]'`. Unaffected: auto-detected scripts (detection only ever returns `ScriptType` members), and empty or letterless lines, which short-circuit to a no-op before any script is consulted — as they always did.
|
|
20
|
+
|
|
21
|
+
All romanized output strings are byte-identical to 0.2.0 apart from those two cases, verified against a golden corpus across every supported script, dialect, preset, and error path. Two further notes for anyone diffing behavior:
|
|
22
|
+
|
|
23
|
+
- `romanizeLine` with an engineless pinned script (e.g. `{ script: 'arabic' }`) still throws even when the line is pure Latin — the engine check precedes the latin guard, matching `romanizeLines`, which rejects such a script before looking at any line.
|
|
24
|
+
- `NON_LATIN_SCRIPT_RE` is now derived from the range table, so its `.source` text changed: ranges are emitted in ascending code-point order, and the adjacent Hebrew and Arabic ranges collapse into one (8 groups → 7). The matching contract is unchanged — `.flags` is still `''` and `.test()` agrees with the old literal for every code point in the BMP, asserted by a test sweep. Only a consumer that snapshots, hashes, or string-compares the pattern text is affected; one that uses it as a matcher is not.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- README Quick Start example was impossible: `romanizeLines(['你好世界', 'こんにちは'])` returns `{ script: 'japanese' }` (kana is definitive), not `{ script: 'chinese' }` as documented. The example and the detection-granularity contract are now documented accurately.
|
|
29
|
+
- Documented romanization outputs corrected against the actual engines (in all nine READMEs): Thai `สวัสดี` → `swasdi` (was `sawatdi`), Gurmukhi `ਨਮਸਤੇ` → `namasate` (was `namaste`; standard Punjabi spelling has no virama, so both consonants keep the inherent vowel), and the Kannada sample is now the correctly-spelled `ನಮಸ್ತೇ` → `namaste`. The feature list said "7 Indic scripts"; there are 6.
|
|
30
|
+
- Dead defensive throws in the Sanscript branch (unreachable behind the catch-all fallback) removed; the silent `default:` switch arm is gone — the engine table is total over the script union, so a forgotten engine row is a **compile error** instead of silent garbage.
|
|
31
|
+
- `NON_LATIN_SCRIPT_RE` is now derived from the same per-script range table that drives detection (previously a hand-maintained duplicate with a "keep aligned" comment). Verified byte-for-byte identical across the BMP.
|
|
32
|
+
|
|
33
|
+
### Internal
|
|
34
|
+
|
|
35
|
+
- Per-script knowledge concentrated: detection ranges + external classification in `SCRIPT_METADATA` (detector side), engine bindings in one total record (romanizer side). Adding a script is one row per side, compiler-enforced.
|
|
36
|
+
- `selectCyrillicPreset` extracted as a pure, unit-tested function (the Ukrainian-detection headline feature previously had zero coverage).
|
|
37
|
+
- Dependencies updated to latest (TypeScript 7, vitest 4.1.10, pinyin-pro 3.28.2). CI, Dependabot (daily, grouped), and a weekly latest-deps canary keep every engine on its newest release.
|
|
38
|
+
- Test suite grew from 19 to 44 tests: injection seam, fallback observability, lazy-init retry, preset selection, latin guard, regex-derivation equivalence, tie-break regression, BMP-range invariant.
|
|
39
|
+
- `pnpm run smoke` (`scripts/smoke.mjs`) exercises the built package under plain Node ESM: every engine's interop chain, the fallback path, both entry points, and an assertion that `dist/detector.js` imports nothing. Runs in CI and in the canary; `pnpm run verify` chains typecheck + build + test + smoke.
|
|
40
|
+
- `package.json` declares `"sideEffects": false` (safe now that engines are lazy) and ships `src` so the published source maps resolve.
|
package/README.md
CHANGED
|
@@ -3,10 +3,28 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/lyric-romanizer)
|
|
4
4
|
[](https://github.com/thedavidweng/lyric-romanizer/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
[English](https://github.com/thedavidweng/lyric-romanizer#readme) | [日本語](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.ja.md) | [中文(简体)](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.zh-CN.md) | [中文(粵語)](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.zh-yue.md) | [한국어](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.ko.md) | [Русский](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.ru.md) | [हिन्दी](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.hi.md) | [தமிழ்](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.ta.md) | [ไทย](https://github.com/thedavidweng/lyric-romanizer/blob/main/docs/README.th.md)
|
|
7
|
+
|
|
8
|
+
> **Philosophy: Don't reinvent the wheel.**
|
|
9
|
+
> This project deliberately avoids building romanization logic from scratch. Instead, it composes best-in-class, community-maintained libraries — one for each script — and focuses on the orchestration layer: script detection, engine routing, dialect handling, and a unified API. Every romanization engine in the dependency list is a dedicated, battle-tested library maintained by domain experts. That's the point.
|
|
10
|
+
|
|
11
|
+
Script detection and local romanization engine for lyrics. Supports 12 scripts across Japanese, Chinese (Mandarin and Cantonese), Korean, Cyrillic, Indic, Tamil, and Thai — all running locally with zero API calls.
|
|
7
12
|
|
|
8
13
|
Extracted from [Spotify Karaoke](https://github.com/haroldalan/spotify-karaoke). Used by [OpenKara](https://github.com/thedavidweng/openkara).
|
|
9
14
|
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Zero API calls** — all romanization runs locally
|
|
18
|
+
- **Auto script detection** — pass in text, get back the detected script
|
|
19
|
+
- **12+ scripts** — Japanese, Chinese, Korean, Cyrillic, 6 Indic scripts, Tamil, Thai
|
|
20
|
+
- **Cantonese support** — Jyutping alongside default Mandarin Pinyin
|
|
21
|
+
- **Lightweight detector subpath** — import only script detection (and external-script classification) without pulling in romanization engines
|
|
22
|
+
- **Lazy engines** — every engine loads on first use; importing the main entry costs nothing until you romanize
|
|
23
|
+
- **Pluggable engines** — override any built-in engine, or plug your own adapter for externally-romanized scripts
|
|
24
|
+
- **Observable fallbacks** — per-line flags tell you when an engine failed and a line was transliterated as a last resort
|
|
25
|
+
- **Ukrainian-aware Cyrillic** — auto-detects Ukrainian-specific characters and applies the correct transliteration preset
|
|
26
|
+
- **Runs in plain Node ESM** — no bundler required
|
|
27
|
+
|
|
10
28
|
## Installation
|
|
11
29
|
|
|
12
30
|
```bash
|
|
@@ -29,14 +47,16 @@ import { createRomanizer, detectScript } from 'lyric-romanizer';
|
|
|
29
47
|
const romanizer = createRomanizer();
|
|
30
48
|
|
|
31
49
|
// Auto-detect script and romanize
|
|
32
|
-
const result = await romanizer.romanizeLines(['你好世界', '
|
|
33
|
-
// { script: 'chinese', lines: ['nǐ hǎo shì jiè', '
|
|
50
|
+
const result = await romanizer.romanizeLines(['你好世界', '很高兴认识你']);
|
|
51
|
+
// { script: 'chinese', lines: ['nǐ hǎo shì jiè', 'hěn gāo xìng rèn shi nǐ'], fallbacks: [false, false] }
|
|
34
52
|
|
|
35
53
|
// Romanize a single line
|
|
36
54
|
const line = await romanizer.romanizeLine('안녕하세요');
|
|
37
55
|
// 'annyeonghaseyo'
|
|
38
56
|
```
|
|
39
57
|
|
|
58
|
+
> **Detection granularity** — `romanizeLines` detects the dominant script **once across all lines** and pins it for every line (deliberate: kanji-only lines inside a Japanese song must reach the Japanese engine, and any kana in the array pins `japanese`). Calling `romanizeLine` in a loop instead detects **per line** and may route each line to a different engine. Pure-Latin lines under a pinned script are returned unchanged. See [Mixed-script arrays](#mixed-script-arrays).
|
|
59
|
+
|
|
40
60
|
## API
|
|
41
61
|
|
|
42
62
|
### Imports
|
|
@@ -52,7 +72,12 @@ import {
|
|
|
52
72
|
} from 'lyric-romanizer';
|
|
53
73
|
|
|
54
74
|
// Detector-only subpath — lightweight, no romanization dependencies
|
|
55
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
detectScript,
|
|
77
|
+
isLatinScript,
|
|
78
|
+
requiresExternalRomanization,
|
|
79
|
+
NON_LATIN_SCRIPT_RE,
|
|
80
|
+
} from 'lyric-romanizer/detector';
|
|
56
81
|
```
|
|
57
82
|
|
|
58
83
|
### Types
|
|
@@ -70,16 +95,26 @@ interface Romanizer {
|
|
|
70
95
|
romanizeLines(lines: readonly string[], options?: RomanizeOptions): Promise<RomanizeResult>;
|
|
71
96
|
}
|
|
72
97
|
|
|
73
|
-
|
|
74
|
-
type
|
|
75
|
-
|
|
76
|
-
|
|
98
|
+
// `dialect` is only honored for 'chinese'; every other script ignores it.
|
|
99
|
+
type RomanizeOptions = { script?: ScriptType; dialect?: 'mandarin' | 'cantonese' };
|
|
100
|
+
|
|
101
|
+
// `fallbacks` is aligned with `lines`: true where the engine failed and the
|
|
102
|
+
// line was universally transliterated as a last resort.
|
|
103
|
+
type RomanizeResult = { script: ScriptType; lines: string[]; fallbacks?: boolean[] };
|
|
77
104
|
|
|
78
|
-
|
|
105
|
+
// An engine adapter: romanizes one line of its script. Throwing (or
|
|
106
|
+
// rejecting) triggers the universal transliteration fallback.
|
|
107
|
+
type RomanizeEngine = (line: string, context: { dialect: 'mandarin' | 'cantonese' }) => string | Promise<string>;
|
|
79
108
|
|
|
80
|
-
|
|
109
|
+
type RomanizerOptions = {
|
|
110
|
+
japaneseDictPath?: string;
|
|
111
|
+
engines?: Partial<Record<ScriptType, RomanizeEngine>>;
|
|
112
|
+
};
|
|
113
|
+
```
|
|
81
114
|
|
|
82
|
-
|
|
115
|
+
### `createRomanizer(options?)`
|
|
116
|
+
|
|
117
|
+
Factory that returns a `Romanizer` instance. Every engine is lazily loaded on first use and cached — a failed load retries on the next call.
|
|
83
118
|
|
|
84
119
|
```ts
|
|
85
120
|
const romanizer = createRomanizer();
|
|
@@ -90,7 +125,27 @@ const romanizer = createRomanizer({
|
|
|
90
125
|
});
|
|
91
126
|
```
|
|
92
127
|
|
|
93
|
-
####
|
|
128
|
+
#### Engine adapters
|
|
129
|
+
|
|
130
|
+
`options.engines` overrides the built-in engine for a script — or plugs an engine into a script that has none built in (`arabic`, `hebrew`, `malayalam`, `bengali`, `other`), so `romanizeLines` can handle every script through one interface. By default the library performs **zero network I/O**; plugging a remote adapter is an explicit caller decision.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const romanizer = createRomanizer({
|
|
134
|
+
engines: {
|
|
135
|
+
// Bring your own external romanization for scripts without a local engine.
|
|
136
|
+
arabic: async (line) => myTransliterationApi(line),
|
|
137
|
+
// Or replace a built-in engine (e.g. with a fake in tests).
|
|
138
|
+
korean: (line) => myKoreanRomanizer(line),
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await romanizer.romanizeLines(['مرحبا']);
|
|
143
|
+
// { script: 'arabic', lines: [...], fallbacks: [false] } — no longer throws
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Scripts without an engine — built-in or injected — still throw `UnsupportedRomanizationError`.
|
|
147
|
+
|
|
148
|
+
### `detectScript(lines)`
|
|
94
149
|
|
|
95
150
|
Detects the dominant script in the given text lines. Checks for Japanese kana first (definitive), then scores all other scripts by character count.
|
|
96
151
|
|
|
@@ -102,7 +157,7 @@ detectScript(['Hello world']); // 'latin'
|
|
|
102
157
|
detectScript(['123 ???']); // 'other'
|
|
103
158
|
```
|
|
104
159
|
|
|
105
|
-
|
|
160
|
+
### `isLatinScript(lines)`
|
|
106
161
|
|
|
107
162
|
Fast check — returns `true` if the text contains only Latin letters (no CJK, Cyrillic, Indic, etc.). Useful for skipping romanization entirely.
|
|
108
163
|
|
|
@@ -112,9 +167,9 @@ isLatinScript(['안녕하세요']); // false
|
|
|
112
167
|
isLatinScript(['♪♪♪']); // false (no letters)
|
|
113
168
|
```
|
|
114
169
|
|
|
115
|
-
|
|
170
|
+
### `requiresExternalRomanization(script)`
|
|
116
171
|
|
|
117
|
-
Returns `true` for scripts that
|
|
172
|
+
Returns `true` for scripts that have no built-in engine and require an external API. Importable from the lightweight `lyric-romanizer/detector` subpath, so answering "should I branch to an external service?" costs zero engine payload.
|
|
118
173
|
|
|
119
174
|
```ts
|
|
120
175
|
requiresExternalRomanization('chinese'); // false
|
|
@@ -122,17 +177,20 @@ requiresExternalRomanization('arabic'); // true
|
|
|
122
177
|
requiresExternalRomanization('malayalam'); // true
|
|
123
178
|
```
|
|
124
179
|
|
|
125
|
-
### `
|
|
180
|
+
### `romanizer.romanizeLine(line, options?)`
|
|
126
181
|
|
|
127
|
-
|
|
182
|
+
Romanizes a single line. If `script` is omitted, it is auto-detected **per line** via `detectScript` — looping over `romanizeLine` may route each line to a different engine, unlike `romanizeLines`, which pins one script for the whole array. Returns the original line unchanged for Latin text or non-letter content.
|
|
128
183
|
|
|
129
|
-
|
|
184
|
+
For Chinese text, the `dialect` option controls the romanization system: `'mandarin'` (default) uses Pinyin, `'cantonese'` uses [Jyutping](https://github.com/CanCLID/to-jyutping). Other scripts ignore `dialect`.
|
|
130
185
|
|
|
131
|
-
**Throws** `UnsupportedRomanizationError` for
|
|
186
|
+
**Throws** `UnsupportedRomanizationError` for scripts without an engine (built-in or injected).
|
|
132
187
|
|
|
133
188
|
```ts
|
|
134
189
|
await romanizer.romanizeLine('你好世界');
|
|
135
|
-
// 'nǐ hǎo shì jiè'
|
|
190
|
+
// 'nǐ hǎo shì jiè' (default: Mandarin/Pinyin)
|
|
191
|
+
|
|
192
|
+
await romanizer.romanizeLine('你好', { dialect: 'cantonese' });
|
|
193
|
+
// 'nei5 hou2' (Jyutping)
|
|
136
194
|
|
|
137
195
|
await romanizer.romanizeLine('Привет мир');
|
|
138
196
|
// 'Privet mir'
|
|
@@ -144,21 +202,21 @@ await romanizer.romanizeLine('مرحبا');
|
|
|
144
202
|
// throws UnsupportedRomanizationError { script: 'arabic' }
|
|
145
203
|
```
|
|
146
204
|
|
|
147
|
-
|
|
205
|
+
### `romanizer.romanizeLines(lines, options?)`
|
|
148
206
|
|
|
149
|
-
Romanizes multiple lines in parallel.
|
|
207
|
+
Romanizes multiple lines in parallel. Detects the dominant script **once across all lines** and pins it for every line (see [Mixed-script arrays](#mixed-script-arrays)). Returns the script, the romanized lines, and per-line `fallbacks` flags — `true` where the engine failed and the line was universally transliterated as a last resort.
|
|
150
208
|
|
|
151
209
|
```ts
|
|
152
|
-
const { script, lines } = await romanizer.romanizeLines([
|
|
210
|
+
const { script, lines, fallbacks } = await romanizer.romanizeLines([
|
|
153
211
|
'สวัสดี',
|
|
154
212
|
'ชาวโลก',
|
|
155
213
|
]);
|
|
156
|
-
// { script: 'thai', lines: ['
|
|
214
|
+
// { script: 'thai', lines: ['swasdi', 'chaolok'], fallbacks: [false, false] }
|
|
157
215
|
```
|
|
158
216
|
|
|
159
217
|
### `UnsupportedRomanizationError`
|
|
160
218
|
|
|
161
|
-
Thrown when attempting to romanize a script that
|
|
219
|
+
Thrown when attempting to romanize a script that has no engine — built in or injected via `options.engines`. Has a `script` property for programmatic handling.
|
|
162
220
|
|
|
163
221
|
```ts
|
|
164
222
|
try {
|
|
@@ -177,28 +235,62 @@ try {
|
|
|
177
235
|
|
|
178
236
|
| Script | Engine | Example |
|
|
179
237
|
|--------|--------|---------|
|
|
238
|
+
| Universal *(fallback)* | [transliteration](https://github.com/nickclaw/transliteration) | `Привет` → `Privet` |
|
|
180
239
|
| Japanese | [kuroshiro](https://github.com/sglkc/kuroshiro-ts) + [kuromoji](https://github.com/takuyaa/kuromoji.js) | `こんにちは` → `konnichiha` |
|
|
181
|
-
|
|
|
240
|
+
| Mandarin | [pinyin-pro](https://github.com/zh-lx/pinyin-pro) | `你好` → `nǐ hǎo` |
|
|
241
|
+
| Cantonese | [to-jyutping](https://github.com/CanCLID/to-jyutping) | `佢冇` → `keoi5 mou5` |
|
|
182
242
|
| Korean | [@romanize/korean](https://github.com/kntng/romanize) | `안녕` → `annyeong` |
|
|
183
243
|
| Cyrillic | [cyrillic-to-translit-js](https://github.com/greybax/CyrillicToTranslitJS) | `Привет` → `Privet` |
|
|
184
|
-
| Devanagari | [sanscript](https://github.com/indic-transliteration/sanscript
|
|
185
|
-
| Gujarati | sanscript | `નમસ્તે` → `namaste` |
|
|
186
|
-
| Gurmukhi | sanscript | `ਨਮਸਤੇ` → `
|
|
187
|
-
| Telugu | sanscript | `నమస్తే` → `namaste` |
|
|
188
|
-
| Kannada | sanscript |
|
|
189
|
-
| Odia | sanscript | `ନମସ୍ତେ` → `namaste` |
|
|
244
|
+
| Devanagari | [sanscript](https://github.com/indic-transliteration/sanscript) | `नमस्ते` → `namaste` |
|
|
245
|
+
| Gujarati | [sanscript](https://github.com/indic-transliteration/sanscript) | `નમસ્તે` → `namaste` |
|
|
246
|
+
| Gurmukhi | [sanscript](https://github.com/indic-transliteration/sanscript) | `ਨਮਸਤੇ` → `namasate` |
|
|
247
|
+
| Telugu | [sanscript](https://github.com/indic-transliteration/sanscript) | `నమస్తే` → `namaste` |
|
|
248
|
+
| Kannada | [sanscript](https://github.com/indic-transliteration/sanscript) | `ನಮಸ್ತೇ` → `namaste` |
|
|
249
|
+
| Odia | [sanscript](https://github.com/indic-transliteration/sanscript) | `ନମସ୍ତେ` → `namaste` |
|
|
190
250
|
| Tamil | [tamil-romanizer](https://github.com/haroldalan/tamil-romanizer) | `வணக்கம்` → `vanakkam` |
|
|
191
|
-
| Thai | [@dehoist/romanize-thai](https://github.com/Dehoist/Open-Source) | `สวัสดี` → `
|
|
192
|
-
| Latin | *(no-op)* | `Hello` → `Hello` |
|
|
251
|
+
| Thai | [@dehoist/romanize-thai](https://github.com/Dehoist/Open-Source) | `สวัสดี` → `swasdi` |
|
|
193
252
|
|
|
194
253
|
### External (requires API)
|
|
195
254
|
|
|
196
|
-
|
|
255
|
+
| Script | Method |
|
|
256
|
+
|--------|--------|
|
|
257
|
+
| Malayalam | Google Translate `dt=rm` |
|
|
258
|
+
| Bengali | Google Translate `dt=rm` |
|
|
259
|
+
| Arabic | Google Translate `dt=rm` |
|
|
260
|
+
| Hebrew | Google Translate `dt=rm` |
|
|
261
|
+
| Other | Google Translate `dt=rm` |
|
|
262
|
+
|
|
263
|
+
Use `requiresExternalRomanization()` to detect these and branch to your preferred API — or plug the API in once as an [engine adapter](#engine-adapters) and let `romanizeLines` handle every script.
|
|
264
|
+
|
|
265
|
+
## Script-Specific Notes
|
|
266
|
+
|
|
267
|
+
### Mixed-script arrays
|
|
268
|
+
|
|
269
|
+
`romanizeLines` pins the **dominant** script of the whole array to every line. This is deliberate: a kanji-only line inside a Japanese song is indistinguishable from Chinese on its own (kanji and hanzi share the same Unicode block), so only whole-array context routes it to the right engine. Consequences worth knowing:
|
|
197
270
|
|
|
198
|
-
|
|
271
|
+
- Any kana anywhere in the array pins the whole array to `japanese` — kana is definitive proof.
|
|
272
|
+
- A line of a *different* non-Latin script inside the array is still fed to the pinned engine.
|
|
273
|
+
- Pure-Latin lines (an English chorus inside a CJK song) are **returned unchanged** instead of being fed to the pinned engine.
|
|
274
|
+
- Per-line `fallbacks` flags report when an engine failed and a line degraded to universal transliteration.
|
|
275
|
+
|
|
276
|
+
If you need true per-line engine routing, call `romanizeLine` in a loop — it detects per line.
|
|
277
|
+
|
|
278
|
+
### Cyrillic Detection
|
|
199
279
|
|
|
200
280
|
Cyrillic auto-detects Ukrainian-specific characters (`і`, `ї`, `є`, `ґ`) and applies the Ukrainian transliteration preset. All other Cyrillic text defaults to Russian.
|
|
201
281
|
|
|
282
|
+
### Cantonese Support
|
|
283
|
+
|
|
284
|
+
Chinese text defaults to Mandarin (Pinyin). Pass `dialect: 'cantonese'` in `RomanizeOptions` to romanize Chinese text to [Jyutping](https://github.com/CanCLID/to-jyutping) instead.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const { lines } = await romanizer.romanizeLines(['你好世界', '食飯'], {
|
|
288
|
+
script: 'chinese',
|
|
289
|
+
dialect: 'cantonese',
|
|
290
|
+
});
|
|
291
|
+
// ['nei5 hou2 sai3 gaai3', 'sik6 faan6']
|
|
292
|
+
```
|
|
293
|
+
|
|
202
294
|
## License
|
|
203
295
|
|
|
204
296
|
MIT
|
package/dist/detector.d.ts
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
import type { ScriptType } from './types.js';
|
|
2
|
+
declare const SCRIPT_METADATA: {
|
|
3
|
+
readonly japanese: {
|
|
4
|
+
readonly ranges: readonly [readonly [12352, 12543]];
|
|
5
|
+
readonly definitive: true;
|
|
6
|
+
};
|
|
7
|
+
readonly chinese: {
|
|
8
|
+
readonly ranges: readonly [readonly [19968, 40959]];
|
|
9
|
+
};
|
|
10
|
+
readonly korean: {
|
|
11
|
+
readonly ranges: readonly [readonly [44032, 55215]];
|
|
12
|
+
};
|
|
13
|
+
readonly cyrillic: {
|
|
14
|
+
readonly ranges: readonly [readonly [1024, 1279]];
|
|
15
|
+
};
|
|
16
|
+
readonly devanagari: {
|
|
17
|
+
readonly ranges: readonly [readonly [2304, 2431]];
|
|
18
|
+
};
|
|
19
|
+
readonly gujarati: {
|
|
20
|
+
readonly ranges: readonly [readonly [2688, 2815]];
|
|
21
|
+
};
|
|
22
|
+
readonly gurmukhi: {
|
|
23
|
+
readonly ranges: readonly [readonly [2560, 2687]];
|
|
24
|
+
};
|
|
25
|
+
readonly telugu: {
|
|
26
|
+
readonly ranges: readonly [readonly [3072, 3199]];
|
|
27
|
+
};
|
|
28
|
+
readonly kannada: {
|
|
29
|
+
readonly ranges: readonly [readonly [3200, 3327]];
|
|
30
|
+
};
|
|
31
|
+
readonly odia: {
|
|
32
|
+
readonly ranges: readonly [readonly [2816, 2943]];
|
|
33
|
+
};
|
|
34
|
+
readonly tamil: {
|
|
35
|
+
readonly ranges: readonly [readonly [2944, 3071]];
|
|
36
|
+
};
|
|
37
|
+
readonly malayalam: {
|
|
38
|
+
readonly ranges: readonly [readonly [3328, 3455]];
|
|
39
|
+
readonly external: true;
|
|
40
|
+
};
|
|
41
|
+
readonly bengali: {
|
|
42
|
+
readonly ranges: readonly [readonly [2432, 2559]];
|
|
43
|
+
readonly external: true;
|
|
44
|
+
};
|
|
45
|
+
readonly arabic: {
|
|
46
|
+
readonly ranges: readonly [readonly [1536, 1791]];
|
|
47
|
+
readonly external: true;
|
|
48
|
+
};
|
|
49
|
+
readonly hebrew: {
|
|
50
|
+
readonly ranges: readonly [readonly [1424, 1535]];
|
|
51
|
+
readonly external: true;
|
|
52
|
+
};
|
|
53
|
+
readonly thai: {
|
|
54
|
+
readonly ranges: readonly [readonly [3584, 3711]];
|
|
55
|
+
};
|
|
56
|
+
readonly latin: {
|
|
57
|
+
readonly ranges: readonly [];
|
|
58
|
+
};
|
|
59
|
+
readonly other: {
|
|
60
|
+
readonly ranges: readonly [];
|
|
61
|
+
readonly external: true;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Scripts classified as external in SCRIPT_METADATA. The engine table in
|
|
66
|
+
* romanizer.ts excludes exactly these members, so reclassifying a script here
|
|
67
|
+
* surfaces as a compile error there.
|
|
68
|
+
*/
|
|
69
|
+
export type ExternalScript = {
|
|
70
|
+
[K in ScriptType]: (typeof SCRIPT_METADATA)[K] extends {
|
|
71
|
+
external: true;
|
|
72
|
+
} ? K : never;
|
|
73
|
+
}[ScriptType];
|
|
2
74
|
export declare const NON_LATIN_SCRIPT_RE: RegExp;
|
|
3
75
|
export declare function isLatinScript(lines: readonly string[]): boolean;
|
|
4
76
|
export declare function detectScript(lines: readonly string[]): ScriptType;
|
|
77
|
+
export declare function requiresExternalRomanization(script: ScriptType): boolean;
|
|
78
|
+
export {};
|
|
5
79
|
//# sourceMappingURL=detector.d.ts.map
|
package/dist/detector.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAwB7C,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAwB7C,QAAA,MAAM,eAAe;;iBAGP,MAAM;iBAAsB,UAAU;;;iBACvC,MAAM;;;iBACP,MAAM;;;iBACJ,MAAM;;;iBACJ,MAAM;;;iBACR,MAAM;;;iBACN,MAAM;;;iBACR,MAAM;;;iBACL,MAAM;;;iBACT,MAAM;;;iBACL,MAAM;;;iBACF,MAAM;iBAAsB,QAAQ;;;iBACtC,MAAM;iBAAsB,QAAQ;;;iBACrC,MAAM;iBAAsB,QAAQ;;;iBACpC,MAAM;iBAAsB,QAAQ;;;iBACtC,MAAM;;;iBACL,MAAM;;;iBACN,MAAM;iBAAM,QAAQ;;CACoB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;KAC1B,CAAC,IAAI,UAAU,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,GAAG,CAAC,GAAG,KAAK;CACtF,CAAC,UAAU,CAAC,CAAC;AAwCd,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAG/D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAejE;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAMxE"}
|
package/dist/detector.js
CHANGED
|
@@ -1,33 +1,75 @@
|
|
|
1
1
|
const LETTER_RE = /\p{L}/u;
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
// Single source of truth for per-script knowledge on the light (engine-free)
|
|
3
|
+
// side of the packaging seam: detection ranges and local/external
|
|
4
|
+
// classification. SCRIPT_SCORES, NON_LATIN_SCRIPT_RE, and
|
|
5
|
+
// requiresExternalRomanization are all derived from this table, and the
|
|
6
|
+
// engine table in romanizer.ts is compile-time checked against the
|
|
7
|
+
// classification, so adding a script here is the single detector-side edit.
|
|
8
|
+
//
|
|
9
|
+
// Entry order is load-bearing: it is the tie-break priority of detectScript —
|
|
10
|
+
// on an equal character count, the earlier entry wins.
|
|
11
|
+
const SCRIPT_METADATA = {
|
|
12
|
+
// Kana is definitive: kanji shares the Han block with chinese, so any kana
|
|
13
|
+
// must decide 'japanese' before Han scoring runs.
|
|
14
|
+
japanese: { ranges: [[0x3040, 0x30ff]], definitive: true },
|
|
15
|
+
chinese: { ranges: [[0x4e00, 0x9fff]] },
|
|
16
|
+
korean: { ranges: [[0xac00, 0xd7af]] },
|
|
17
|
+
cyrillic: { ranges: [[0x0400, 0x04ff]] },
|
|
18
|
+
devanagari: { ranges: [[0x0900, 0x097f]] },
|
|
19
|
+
gujarati: { ranges: [[0x0a80, 0x0aff]] },
|
|
20
|
+
gurmukhi: { ranges: [[0x0a00, 0x0a7f]] },
|
|
21
|
+
telugu: { ranges: [[0x0c00, 0x0c7f]] },
|
|
22
|
+
kannada: { ranges: [[0x0c80, 0x0cff]] },
|
|
23
|
+
odia: { ranges: [[0x0b00, 0x0b7f]] },
|
|
24
|
+
tamil: { ranges: [[0x0b80, 0x0bff]] },
|
|
25
|
+
malayalam: { ranges: [[0x0d00, 0x0d7f]], external: true },
|
|
26
|
+
bengali: { ranges: [[0x0980, 0x09ff]], external: true },
|
|
27
|
+
arabic: { ranges: [[0x0600, 0x06ff]], external: true },
|
|
28
|
+
hebrew: { ranges: [[0x0590, 0x05ff]], external: true },
|
|
29
|
+
thai: { ranges: [[0x0e00, 0x0e7f]] },
|
|
30
|
+
latin: { ranges: [] },
|
|
31
|
+
other: { ranges: [], external: true },
|
|
32
|
+
};
|
|
33
|
+
function escapeCodePoint(codePoint) {
|
|
34
|
+
return `\\u${codePoint.toString(16).toUpperCase().padStart(4, '0')}`;
|
|
35
|
+
}
|
|
36
|
+
function characterClass(ranges) {
|
|
37
|
+
return ranges
|
|
38
|
+
.map(([start, end]) => (start === end ? escapeCodePoint(start) : `${escapeCodePoint(start)}-${escapeCodePoint(end)}`))
|
|
39
|
+
.join('');
|
|
40
|
+
}
|
|
41
|
+
function mergeRanges(ranges) {
|
|
42
|
+
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
43
|
+
const merged = [];
|
|
44
|
+
for (const [start, end] of sorted) {
|
|
45
|
+
const last = merged[merged.length - 1];
|
|
46
|
+
if (last && start <= last[1] + 1) {
|
|
47
|
+
last[1] = Math.max(last[1], end);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
merged.push([start, end]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return merged;
|
|
54
|
+
}
|
|
55
|
+
const METADATA_ENTRIES = Object.entries(SCRIPT_METADATA);
|
|
56
|
+
const DEFINITIVE_SCRIPTS = METADATA_ENTRIES.filter(([, meta]) => meta.definitive).map(([script, meta]) => [script, new RegExp(`[${characterClass(meta.ranges)}]`)]);
|
|
57
|
+
const SCRIPT_SCORES = METADATA_ENTRIES.filter(([, meta]) => meta.ranges.length > 0 && !meta.definitive).map(([script, meta]) => [script, new RegExp(`[${characterClass(meta.ranges)}]`, 'g')]);
|
|
58
|
+
// Derived union of every detectable range. Also consumed by the extension
|
|
59
|
+
// content script's "latin fast path" during mode switching — deriving it from
|
|
60
|
+
// SCRIPT_METADATA keeps it aligned with detection structurally instead of by
|
|
61
|
+
// comment.
|
|
62
|
+
export const NON_LATIN_SCRIPT_RE = new RegExp(`[${characterClass(mergeRanges(METADATA_ENTRIES.flatMap(([, meta]) => meta.ranges)))}]`);
|
|
22
63
|
export function isLatinScript(lines) {
|
|
23
64
|
const text = lines.join('');
|
|
24
65
|
return !NON_LATIN_SCRIPT_RE.test(text) && LETTER_RE.test(text);
|
|
25
66
|
}
|
|
26
67
|
export function detectScript(lines) {
|
|
27
68
|
const text = lines.join('');
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
for (const [script, pattern] of DEFINITIVE_SCRIPTS) {
|
|
70
|
+
if (pattern.test(text))
|
|
71
|
+
return script;
|
|
72
|
+
}
|
|
31
73
|
let best = ['other', 0];
|
|
32
74
|
for (const [script, pattern] of SCRIPT_SCORES) {
|
|
33
75
|
const score = (text.match(pattern) ?? []).length;
|
|
@@ -38,4 +80,12 @@ export function detectScript(lines) {
|
|
|
38
80
|
return best[0];
|
|
39
81
|
return LETTER_RE.test(text) ? 'latin' : 'other';
|
|
40
82
|
}
|
|
83
|
+
export function requiresExternalRomanization(script) {
|
|
84
|
+
// Own-property check so an out-of-contract string from untyped callers
|
|
85
|
+
// (including inherited names like 'toString') returns false rather than
|
|
86
|
+
// resolving up the prototype chain.
|
|
87
|
+
if (!Object.hasOwn(SCRIPT_METADATA, script))
|
|
88
|
+
return false;
|
|
89
|
+
return SCRIPT_METADATA[script].external === true;
|
|
90
|
+
}
|
|
41
91
|
//# sourceMappingURL=detector.js.map
|
package/dist/detector.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,QAAQ,CAAC;AAa3B,6EAA6E;AAC7E,kEAAkE;AAClE,0DAA0D;AAC1D,wEAAwE;AACxE,mEAAmE;AACnE,4EAA4E;AAC5E,EAAE;AACF,8EAA8E;AAC9E,uDAAuD;AACvD,MAAM,eAAe,GAAG;IACtB,2EAA2E;IAC3E,kDAAkD;IAClD,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE;IAC1D,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACvC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACtC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACxC,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IAC1C,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACxC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACxC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACtC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACvC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACpC,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACrC,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;IACzD,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;IACvD,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;IACtD,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;IACtD,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IACpC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IACrB,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;CACY,CAAC;AAWpD,SAAS,eAAe,CAAC,SAAiB;IACxC,OAAO,MAAM,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,SAAS,cAAc,CAAC,MAA4B;IAClD,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SACrH,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAAC,MAA4B;IAC/C,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAoC,CAAC;AAE5F,MAAM,kBAAkB,GAAyC,gBAAgB,CAAC,MAAM,CACtF,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAC9B,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAU,CAAC,CAAC;AAE7F,MAAM,aAAa,GAAyC,gBAAgB,CAAC,MAAM,CACjF,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CACzD,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAU,CAAC,CAAC;AAElG,0EAA0E;AAC1E,8EAA8E;AAC9E,6EAA6E;AAC7E,WAAW;AACX,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,MAAM,CAC3C,IAAI,cAAc,CAAC,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CACxF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAwB;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5B,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE5B,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,kBAAkB,EAAE,CAAC;QACnD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC;IACxC,CAAC;IAED,IAAI,IAAI,GAAkC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACvD,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,aAAa,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACjD,IAAI,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;YAAE,IAAI,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAkB;IAC7D,uEAAuE;IACvE,wEAAwE;IACxE,oCAAoC;IACpC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,OAAQ,eAA8C,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC;AACnF,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { detectScript, isLatinScript, NON_LATIN_SCRIPT_RE } from './detector.js';
|
|
2
|
-
export { createRomanizer
|
|
3
|
-
export { UnsupportedRomanizationError, type RomanizeOptions, type RomanizeResult, type Romanizer, type RomanizerOptions, type ScriptType, } from './types.js';
|
|
1
|
+
export { detectScript, isLatinScript, requiresExternalRomanization, NON_LATIN_SCRIPT_RE, type ExternalScript, } from './detector.js';
|
|
2
|
+
export { createRomanizer } from './romanizer.js';
|
|
3
|
+
export { UnsupportedRomanizationError, type RomanizeEngine, type RomanizeEngineContext, type RomanizeOptions, type RomanizeResult, type Romanizer, type RomanizerOptions, type ScriptType, } from './types.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,4BAA4B,EAC5B,mBAAmB,EACnB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,4BAA4B,EAC5B,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { detectScript, isLatinScript, NON_LATIN_SCRIPT_RE } from './detector.js';
|
|
2
|
-
export { createRomanizer
|
|
1
|
+
export { detectScript, isLatinScript, requiresExternalRomanization, NON_LATIN_SCRIPT_RE, } from './detector.js';
|
|
2
|
+
export { createRomanizer } from './romanizer.js';
|
|
3
3
|
export { UnsupportedRomanizationError, } from './types.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,4BAA4B,EAC5B,mBAAmB,GAEpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,4BAA4B,GAQ7B,MAAM,YAAY,CAAC"}
|
package/dist/romanizer.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { Romanizer, RomanizerOptions
|
|
2
|
-
|
|
1
|
+
import type { Romanizer, RomanizerOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ukrainian-specific characters pick the Ukrainian preset; all other Cyrillic
|
|
4
|
+
* romanizes as Russian. Applied per line, so a mixed-language song switches
|
|
5
|
+
* preset line by line.
|
|
6
|
+
*/
|
|
7
|
+
export declare function selectCyrillicPreset(line: string): 'ru' | 'uk';
|
|
3
8
|
export declare function createRomanizer(options?: RomanizerOptions): Romanizer;
|
|
4
9
|
//# sourceMappingURL=romanizer.d.ts.map
|
package/dist/romanizer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"romanizer.d.ts","sourceRoot":"","sources":["../src/romanizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"romanizer.d.ts","sourceRoot":"","sources":["../src/romanizer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAKV,SAAS,EACT,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AA4EpB;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE9D;AA+ID,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAErE"}
|