lyric-romanizer 0.1.0 → 0.2.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
@@ -1,69 +1,229 @@
1
1
  # lyric-romanizer
2
2
 
3
- Script detection and local romanization engine for lyrics. Supports Japanese, Chinese, Korean, Cyrillic, Indic scripts (Devanagari, Gujarati, Gurmukhi, Telugu, Kannada, Odia), Tamil, Thai, and Latin.
3
+ [![npm version](https://img.shields.io/npm/v/lyric-romanizer.svg)](https://www.npmjs.com/package/lyric-romanizer)
4
+ [![license](https://img.shields.io/npm/l/lyric-romanizer.svg)](https://github.com/thedavidweng/lyric-romanizer/blob/main/LICENSE)
4
5
 
5
- Extracted from [Spotify Karaoke](https://github.com/haroldalan/spotify-karaoke).
6
+ 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.
6
7
 
7
- ## Install
8
+ Extracted from [Spotify Karaoke](https://github.com/haroldalan/spotify-karaoke). Used by [OpenKara](https://github.com/thedavidweng/openkara).
9
+
10
+ ## Installation
8
11
 
9
12
  ```bash
10
13
  npm install lyric-romanizer
11
14
  ```
12
15
 
16
+ ```bash
17
+ yarn add lyric-romanizer
18
+ ```
19
+
20
+ ```bash
21
+ pnpm add lyric-romanizer
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```ts
27
+ import { createRomanizer, detectScript } from 'lyric-romanizer';
28
+
29
+ const romanizer = createRomanizer();
30
+
31
+ // Auto-detect script and romanize
32
+ const result = await romanizer.romanizeLines(['你好世界', 'こんにちは']);
33
+ // { script: 'chinese', lines: ['nǐ hǎo shì jiè', 'こんにちは'] }
34
+
35
+ // Romanize a single line
36
+ const line = await romanizer.romanizeLine('안녕하세요');
37
+ // 'annyeonghaseyo'
38
+ ```
39
+
13
40
  ## API
14
41
 
42
+ ### Imports
43
+
15
44
  ```ts
45
+ // Main entry — full romanization engine
16
46
  import {
17
47
  createRomanizer,
18
48
  detectScript,
19
49
  isLatinScript,
20
50
  requiresExternalRomanization,
51
+ UnsupportedRomanizationError,
21
52
  } from 'lyric-romanizer';
22
53
 
23
- // Subpath import for detector only
24
- import { detectScript } from 'lyric-romanizer/detector';
54
+ // Detector-only subpath lightweight, no romanization dependencies
55
+ import { detectScript, isLatinScript, NON_LATIN_SCRIPT_RE } from 'lyric-romanizer/detector';
56
+ ```
57
+
58
+ ### Types
59
+
60
+ ```ts
61
+ type ScriptType =
62
+ | 'japanese' | 'chinese' | 'korean' | 'cyrillic'
63
+ | 'devanagari' | 'gujarati' | 'gurmukhi' | 'telugu'
64
+ | 'kannada' | 'odia' | 'tamil' | 'malayalam'
65
+ | 'bengali' | 'arabic' | 'hebrew' | 'thai'
66
+ | 'other';
67
+
68
+ interface Romanizer {
69
+ romanizeLine(line: string, options?: RomanizeOptions): Promise<string>;
70
+ romanizeLines(lines: readonly string[], options?: RomanizeOptions): Promise<RomanizeResult>;
71
+ }
72
+
73
+ type RomanizeOptions = { script?: ScriptType; dialect?: 'mandarin' | 'cantonese' };
74
+ type RomanizeResult = { script: ScriptType; lines: string[] };
75
+ type RomanizerOptions = { japaneseDictPath?: string };
76
+ ```
77
+
78
+ ### Functions
79
+
80
+ #### `createRomanizer(options?)`
81
+
82
+ Factory that returns a `Romanizer` instance. The Kuroshiro engine (Japanese) is lazily initialized on first use and cached.
83
+
84
+ ```ts
85
+ const romanizer = createRomanizer();
86
+
87
+ // Override the Kuromoji dictionary CDN path (e.g. for self-hosting)
88
+ const romanizer = createRomanizer({
89
+ japaneseDictPath: 'https://my-cdn.com/kuromoji/dict',
90
+ });
91
+ ```
92
+
93
+ #### `detectScript(lines)`
94
+
95
+ Detects the dominant script in the given text lines. Checks for Japanese kana first (definitive), then scores all other scripts by character count.
96
+
97
+ ```ts
98
+ detectScript(['こんにちは']); // 'japanese'
99
+ detectScript(['你好世界']); // 'chinese'
100
+ detectScript(['Привет']); // 'cyrillic'
101
+ detectScript(['Hello world']); // 'latin'
102
+ detectScript(['123 ???']); // 'other'
103
+ ```
104
+
105
+ #### `isLatinScript(lines)`
106
+
107
+ Fast check — returns `true` if the text contains only Latin letters (no CJK, Cyrillic, Indic, etc.). Useful for skipping romanization entirely.
108
+
109
+ ```ts
110
+ isLatinScript(['Hello world']); // true
111
+ isLatinScript(['안녕하세요']); // false
112
+ isLatinScript(['♪♪♪']); // false (no letters)
113
+ ```
114
+
115
+ #### `requiresExternalRomanization(script)`
116
+
117
+ Returns `true` for scripts that cannot be romanized locally and require an external API.
118
+
119
+ ```ts
120
+ requiresExternalRomanization('chinese'); // false
121
+ requiresExternalRomanization('arabic'); // true
122
+ requiresExternalRomanization('malayalam'); // true
123
+ ```
124
+
125
+ ### `Romanizer` Interface
126
+
127
+ #### `romanizer.romanizeLine(line, options?)`
128
+
129
+ Romanizes a single line. If `script` is omitted, it is auto-detected via `detectScript`. Returns the original line unchanged for Latin text or non-letter content.
130
+
131
+ For Chinese text, the `dialect` option controls the romanization system: `'mandarin'` (default) uses Pinyin, `'cantonese'` uses [Jyutping](https://github.com/CanCLID/to-jyutping).
132
+
133
+ **Throws** `UnsupportedRomanizationError` for external scripts.
134
+
135
+ ```ts
136
+ await romanizer.romanizeLine('你好世界');
137
+ // 'nǐ hǎo shì jiè' (default: Mandarin/Pinyin)
138
+
139
+ await romanizer.romanizeLine('你好', { dialect: 'cantonese' });
140
+ // 'nei5 hou2' (Jyutping)
141
+
142
+ await romanizer.romanizeLine('Привет мир');
143
+ // 'Privet mir'
144
+
145
+ await romanizer.romanizeLine('Hello world');
146
+ // 'Hello world' (no-op)
147
+
148
+ await romanizer.romanizeLine('مرحبا');
149
+ // throws UnsupportedRomanizationError { script: 'arabic' }
25
150
  ```
26
151
 
27
- ### `detectScript(lines: readonly string[]): ScriptType`
152
+ #### `romanizer.romanizeLines(lines, options?)`
28
153
 
29
- Detects the dominant script in the given lines. Returns one of: `japanese`, `chinese`, `korean`, `cyrillic`, `devanagari`, `gujarati`, `gurmukhi`, `telugu`, `kannada`, `odia`, `tamil`, `malayalam`, `bengali`, `arabic`, `hebrew`, `thai`, `latin`, `other`.
154
+ Romanizes multiple lines in parallel. Returns the detected script and romanized lines.
30
155
 
31
- ### `isLatinScript(lines: readonly string[]): boolean`
156
+ ```ts
157
+ const { script, lines } = await romanizer.romanizeLines([
158
+ 'สวัสดี',
159
+ 'ชาวโลก',
160
+ ]);
161
+ // { script: 'thai', lines: ['sawatdi', 'chaolok'] }
162
+ ```
32
163
 
33
- Fast check for whether the text is Latin-only (no non-Latin characters).
164
+ ### `UnsupportedRomanizationError`
34
165
 
35
- ### `requiresExternalRomanization(script: ScriptType): boolean`
166
+ Thrown when attempting to romanize a script that requires an external API. Has a `script` property for programmatic handling.
36
167
 
37
- Returns `true` for scripts that cannot be romanized locally (`malayalam`, `bengali`, `arabic`, `hebrew`, `other`).
168
+ ```ts
169
+ try {
170
+ await romanizer.romanizeLine('مرحبا');
171
+ } catch (err) {
172
+ if (err instanceof UnsupportedRomanizationError) {
173
+ console.log(err.script); // 'arabic'
174
+ // fall back to external API
175
+ }
176
+ }
177
+ ```
38
178
 
39
- ### `createRomanizer(options?: { japaneseDictPath?: string }): Romanizer`
179
+ ## Supported Scripts
40
180
 
41
- Creates a romanizer instance. The optional `japaneseDictPath` overrides the default [Kuromoji](https://github.com/takuyaa/kuromoji.js) dictionary CDN path.
181
+ ### Local (fully offline)
42
182
 
43
- ### `Romanizer.romanizeLine(line: string, options?: { script?: ScriptType }): Promise<string>`
183
+ | Script | Engine | Example |
184
+ |--------|--------|---------|
185
+ | Universal *(fallback)* | [transliteration](https://github.com/nickclaw/transliteration) | `Привет` → `Privet` |
186
+ | Japanese | [kuroshiro](https://github.com/sglkc/kuroshiro-ts) + [kuromoji](https://github.com/takuyaa/kuromoji.js) | `こんにちは` → `konnichiha` |
187
+ | Mandarin | [pinyin-pro](https://github.com/zh-lx/pinyin-pro) | `你好` → `nǐ hǎo` |
188
+ | Cantonese | [to-jyutping](https://github.com/CanCLID/to-jyutping) | `佢冇` → `keoi5 mou5` |
189
+ | Korean | [@romanize/korean](https://github.com/kntng/romanize) | `안녕` → `annyeong` |
190
+ | Cyrillic | [cyrillic-to-translit-js](https://github.com/greybax/CyrillicToTranslitJS) | `Привет` → `Privet` |
191
+ | Devanagari | [sanscript](https://github.com/indic-transliteration/sanscript) | `नमस्ते` → `namaste` |
192
+ | Gujarati | [sanscript](https://github.com/indic-transliteration/sanscript) | `નમસ્તે` → `namaste` |
193
+ | Gurmukhi | [sanscript](https://github.com/indic-transliteration/sanscript) | `ਨਮਸਤੇ` → `namaste` |
194
+ | Telugu | [sanscript](https://github.com/indic-transliteration/sanscript) | `నమస్తే` → `namaste` |
195
+ | Kannada | [sanscript](https://github.com/indic-transliteration/sanscript) | `నಮస్తె` → `namaste` |
196
+ | Odia | [sanscript](https://github.com/indic-transliteration/sanscript) | `ନମସ୍ତେ` → `namaste` |
197
+ | Tamil | [tamil-romanizer](https://github.com/haroldalan/tamil-romanizer) | `வணக்கம்` → `vanakkam` |
198
+ | Thai | [@dehoist/romanize-thai](https://github.com/Dehoist/Open-Source) | `สวัสดี` → `sawatdi` |
44
199
 
45
- Romanizes a single line. If `script` is omitted, it is auto-detected.
200
+ ### External (requires API)
46
201
 
47
- ### `Romanizer.romanizeLines(lines: readonly string[], options?: { script?: ScriptType }): Promise<{ script: ScriptType, lines: string[] }>`
202
+ | Script | Method |
203
+ |--------|--------|
204
+ | Malayalam | Google Translate `dt=rm` |
205
+ | Bengali | Google Translate `dt=rm` |
206
+ | Arabic | Google Translate `dt=rm` |
207
+ | Hebrew | Google Translate `dt=rm` |
208
+ | Other | Google Translate `dt=rm` |
48
209
 
49
- Romanizes multiple lines at once.
210
+ Use `requiresExternalRomanization()` to detect these and branch to your preferred API.
50
211
 
51
- ## Supported Local Scripts
212
+ ## Cyrillic Detection
52
213
 
53
- | Script | Library |
54
- |--------|---------|
55
- | Japanese | [@sglkc/kuroshiro](https://github.com/sglkc/kuroshiro-ts) + [kuromoji](https://github.com/takuyaa/kuromoji.js) |
56
- | Chinese | [pinyin-pro](https://github.com/zh-lx/pinyin-pro) |
57
- | Korean | [@romanize/korean](https://github.com/kntng/romanize) |
58
- | Cyrillic (Russian/Ukrainian) | [cyrillic-to-translit-js](https://github.com/greybax/CyrillicToTranslitJS) |
59
- | Devanagari, Gujarati, Gurmukhi, Telugu, Kannada, Odia | [@indic-transliteration/sanscript](https://github.com/indic-transliteration/sanscript.js) |
60
- | Tamil | [tamil-romanizer](https://github.com/haroldalan/tamil-romanizer) |
61
- | Thai | [@dehoist/romanize-thai](https://github.com/Dehoist/Open-Source) |
62
- | Latin | no-op (returned as-is) |
214
+ Cyrillic auto-detects Ukrainian-specific characters (`і`, `ї`, `є`, `ґ`) and applies the Ukrainian transliteration preset. All other Cyrillic text defaults to Russian.
63
215
 
64
- ## External Romanization Scripts
216
+ ## Cantonese Support
65
217
 
66
- `malayalam`, `bengali`, `arabic`, `hebrew`, and `other` are marked as external. Use `requiresExternalRomanization(script)` to branch to an API-based romanizer. Calling `romanizeLine`/`romanizeLines` for these throws `UnsupportedRomanizationError`.
218
+ Chinese text defaults to Mandarin (Pinyin). Pass `dialect: 'cantonese'` in `RomanizeOptions` to romanize Chinese text to [Jyutping](https://github.com/CanCLID/to-jyutping) instead.
219
+
220
+ ```ts
221
+ const { lines } = await romanizer.romanizeLines(['你好世界', '食飯'], {
222
+ script: 'chinese',
223
+ dialect: 'cantonese',
224
+ });
225
+ // ['nei5 hou2 sai3 gaai3', 'sik6 faan6']
226
+ ```
67
227
 
68
228
  ## License
69
229
 
@@ -1 +1 @@
1
- {"version":3,"file":"romanizer.d.ts","sourceRoot":"","sources":["../src/romanizer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAGV,SAAS,EACT,gBAAgB,EAChB,UAAU,EACX,MAAM,YAAY,CAAC;AAwBpB,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAExE;AAsFD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAErE"}
1
+ {"version":3,"file":"romanizer.d.ts","sourceRoot":"","sources":["../src/romanizer.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAGV,SAAS,EACT,gBAAgB,EAChB,UAAU,EACX,MAAM,YAAY,CAAC;AAwBpB,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAExE;AA+GD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAErE"}
package/dist/romanizer.js CHANGED
@@ -6,6 +6,7 @@ import Sanscript from '@indic-transliteration/sanscript';
6
6
  import { romanize as romanizeKorean } from '@romanize/korean';
7
7
  import romanizeThai from '@dehoist/romanize-thai';
8
8
  import { romanize as romanizeTamil } from 'tamil-romanizer';
9
+ import { transliterate } from 'transliteration';
9
10
  import { detectScript } from './detector.js';
10
11
  import { UnsupportedRomanizationError } from './types.js';
11
12
  const DEFAULT_JAPANESE_DICT_PATH = 'https://cdn.jsdelivr.net/npm/kuromoji/dict';
@@ -32,6 +33,7 @@ export function requiresExternalRomanization(script) {
32
33
  class DefaultRomanizer {
33
34
  japaneseDictPath;
34
35
  kuroshiroReady = null;
36
+ jyutpingReady = null;
35
37
  constructor(options) {
36
38
  this.japaneseDictPath = options?.japaneseDictPath ?? DEFAULT_JAPANESE_DICT_PATH;
37
39
  }
@@ -44,39 +46,56 @@ class DefaultRomanizer {
44
46
  if (requiresExternalRomanization(script)) {
45
47
  throw new UnsupportedRomanizationError(script);
46
48
  }
47
- switch (script) {
48
- case 'japanese': {
49
- const k = await this.getKuroshiro();
50
- return k.convert(line, { to: 'romaji', mode: 'spaced' });
51
- }
52
- case 'chinese':
53
- return pinyin(line, { toneType: 'symbol', type: 'string' });
54
- case 'korean':
55
- return romanizeKorean(line);
56
- case 'cyrillic':
57
- return /[іїєґ]/i.test(line)
58
- ? cyrillicTranslitUk.transform(line)
59
- : cyrillicTranslitRu.transform(line);
60
- case 'devanagari':
61
- case 'gujarati':
62
- case 'gurmukhi':
63
- case 'telugu':
64
- case 'kannada':
65
- case 'odia': {
66
- const scheme = SANSCRIPT_SCHEME[script];
67
- if (!scheme)
68
- throw new Error(`Missing Sanscript scheme mapping for '${script}'.`);
69
- if (!Sanscript.schemes?.[scheme]) {
70
- throw new Error(`Sanscript does not support scheme '${scheme}' for '${script}'.`);
49
+ try {
50
+ switch (script) {
51
+ case 'japanese': {
52
+ const k = await this.getKuroshiro();
53
+ return k.convert(line, { to: 'romaji', mode: 'spaced' });
54
+ }
55
+ case 'chinese': {
56
+ if (options?.dialect === 'cantonese') {
57
+ try {
58
+ const jyutping = await this.getJyutpingModule();
59
+ const result = jyutping.getJyutpingText(line);
60
+ if (result)
61
+ return result;
62
+ }
63
+ catch {
64
+ // fall through to pinyin
65
+ }
66
+ }
67
+ return pinyin(line, { toneType: 'symbol', type: 'string' });
71
68
  }
72
- return Sanscript.t(line, scheme, 'iast');
69
+ case 'korean':
70
+ return romanizeKorean(line);
71
+ case 'cyrillic':
72
+ return /[іїєґ]/i.test(line)
73
+ ? cyrillicTranslitUk.transform(line)
74
+ : cyrillicTranslitRu.transform(line);
75
+ case 'devanagari':
76
+ case 'gujarati':
77
+ case 'gurmukhi':
78
+ case 'telugu':
79
+ case 'kannada':
80
+ case 'odia': {
81
+ const scheme = SANSCRIPT_SCHEME[script];
82
+ if (!scheme)
83
+ throw new Error(`Missing Sanscript scheme mapping for '${script}'.`);
84
+ if (!Sanscript.schemes?.[scheme]) {
85
+ throw new Error(`Sanscript does not support scheme '${scheme}' for '${script}'.`);
86
+ }
87
+ return Sanscript.t(line, scheme, 'iast');
88
+ }
89
+ case 'tamil':
90
+ return romanizeTamil(line);
91
+ case 'thai':
92
+ return romanizeThai(line);
93
+ default:
94
+ return transliterate(line);
73
95
  }
74
- case 'tamil':
75
- return romanizeTamil(line);
76
- case 'thai':
77
- return romanizeThai(line);
78
- default:
79
- throw new UnsupportedRomanizationError(script);
96
+ }
97
+ catch {
98
+ return transliterate(line);
80
99
  }
81
100
  }
82
101
  async romanizeLines(lines, options) {
@@ -87,7 +106,7 @@ class DefaultRomanizer {
87
106
  if (script === 'latin') {
88
107
  return { script, lines: [...lines] };
89
108
  }
90
- const romanized = await Promise.all(lines.map((line) => this.romanizeLine(line, { script })));
109
+ const romanized = await Promise.all(lines.map((line) => this.romanizeLine(line, { script, dialect: options?.dialect })));
91
110
  return { script, lines: romanized };
92
111
  }
93
112
  async getKuroshiro() {
@@ -105,6 +124,15 @@ class DefaultRomanizer {
105
124
  }
106
125
  return this.kuroshiroReady;
107
126
  }
127
+ async getJyutpingModule() {
128
+ if (!this.jyutpingReady) {
129
+ this.jyutpingReady = import('to-jyutping').catch((e) => {
130
+ this.jyutpingReady = null;
131
+ throw e;
132
+ });
133
+ }
134
+ return this.jyutpingReady;
135
+ }
108
136
  }
109
137
  export function createRomanizer(options) {
110
138
  return new DefaultRomanizer(options);
@@ -1 +1 @@
1
- {"version":3,"file":"romanizer.js","sourceRoot":"","sources":["../src/romanizer.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,kBAAkB,CAAC;AACzC,OAAO,gBAAgB,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,kBAAkB,MAAM,yBAAyB,CAAC;AACzD,OAAO,SAAS,MAAM,kCAAkC,CAAC;AACzD,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,QAAQ,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAQ7C,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,0BAA0B,GAAG,4CAA4C,CAAC;AAChF,MAAM,6BAA6B,GAAG,IAAI,GAAG,CAAa;IACxD,WAAW;IACX,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,OAAO;CACR,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAChE,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAEhE,MAAM,gBAAgB,GAAwC;IAC5D,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,OAAO;CACd,CAAC;AAEF,MAAM,UAAU,4BAA4B,CAAC,MAAkB;IAC7D,OAAO,6BAA6B,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,gBAAgB;IACH,gBAAgB,CAAS;IAClC,cAAc,GAA8B,IAAI,CAAC;IAEzD,YAAY,OAA0B;QACpC,IAAI,CAAC,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,0BAA0B,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,IAAY,EAAE,OAAyB;QACxD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAEtD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QACpC,IAAI,4BAA4B,CAAC,MAAM,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,4BAA4B,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;QAED,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC3D,CAAC;YACD,KAAK,SAAS;gBACZ,OAAO,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9D,KAAK,QAAQ;gBACX,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;YAC9B,KAAK,UAAU;gBACb,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;oBACzB,CAAC,CAAC,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC;oBACpC,CAAC,CAAC,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACzC,KAAK,YAAY,CAAC;YAClB,KAAK,UAAU,CAAC;YAChB,KAAK,UAAU,CAAC;YAChB,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS,CAAC;YACf,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBACxC,IAAI,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,MAAM,IAAI,CAAC,CAAC;gBAClF,IAAI,CAAE,SAAiB,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1C,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,UAAU,MAAM,IAAI,CAAC,CAAC;gBACpF,CAAC;gBACD,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3C,CAAC;YACD,KAAK,OAAO;gBACV,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;YAC7B,KAAK,MAAM;gBACT,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;YAC5B;gBACE,MAAM,IAAI,4BAA4B,CAAC,MAAM,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAwB,EAAE,OAAyB;QACrE,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,4BAA4B,CAAC,MAAM,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,4BAA4B,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACvB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;QACvC,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9F,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;gBAChC,MAAM,QAAQ,GAAG,IAAI,SAAS,EAAE,CAAC;gBACjC,MAAM,QAAQ,CAAC,IAAI,CACjB,IAAI,gBAAgB,CAAC;oBACnB,QAAQ,EAAE,IAAI,CAAC,gBAAgB;iBAChC,CAAC,CACH,CAAC;gBACF,OAAO,QAAQ,CAAC;YAClB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACnB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;gBAC3B,MAAM,KAAK,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,OAA0B;IACxD,OAAO,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"romanizer.js","sourceRoot":"","sources":["../src/romanizer.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -4,6 +4,7 @@ export type RomanizerOptions = {
4
4
  };
5
5
  export type RomanizeOptions = {
6
6
  script?: ScriptType;
7
+ dialect?: 'mandarin' | 'cantonese';
7
8
  };
8
9
  export type RomanizeResult = {
9
10
  script: ScriptType;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,SAAS,GACT,QAAQ,GACR,UAAU,GACV,YAAY,GACZ,UAAU,GACV,UAAU,GACV,QAAQ,GACR,SAAS,GACT,MAAM,GACN,OAAO,GACP,WAAW,GACX,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC7F;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,SAAgB,MAAM,EAAE,UAAU,CAAC;gBAEvB,MAAM,EAAE,UAAU;CAK/B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,SAAS,GACT,QAAQ,GACR,UAAU,GACV,YAAY,GACZ,UAAU,GACV,UAAU,GACV,QAAQ,GACR,SAAS,GACT,MAAM,GACN,OAAO,GACP,WAAW,GACX,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC7F;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,SAAgB,MAAM,EAAE,UAAU,CAAC;gBAEvB,MAAM,EAAE,UAAU;CAK/B"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAsCA,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IACrC,MAAM,CAAa;IAEnC,YAAY,MAAkB;QAC5B,KAAK,CAAC,WAAW,MAAM,mCAAmC,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAuCA,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IACrC,MAAM,CAAa;IAEnC,YAAY,MAAkB;QAC5B,KAAK,CAAC,WAAW,MAAM,mCAAmC,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lyric-romanizer",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Script detection and local romanization engine for lyrics — supports Japanese, Chinese, Korean, Cyrillic, Indic scripts, Tamil, Thai, and more",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,13 +25,6 @@
25
25
  "dist",
26
26
  "README.md"
27
27
  ],
28
- "scripts": {
29
- "build": "rm -rf dist && tsc -p tsconfig.build.json",
30
- "clean": "rm -rf dist",
31
- "prepack": "npm run build",
32
- "test": "vitest run",
33
- "test:watch": "vitest"
34
- },
35
28
  "dependencies": {
36
29
  "@dehoist/romanize-thai": "^1.0.0",
37
30
  "@indic-transliteration/sanscript": "^1.3.3",
@@ -40,7 +33,9 @@
40
33
  "@sglkc/kuroshiro-analyzer-kuromoji": "^1.0.1",
41
34
  "cyrillic-to-translit-js": "^3.2.1",
42
35
  "pinyin-pro": "^3.28.0",
43
- "tamil-romanizer": "^1.0.2"
36
+ "tamil-romanizer": "^1.0.2",
37
+ "to-jyutping": "^3.1.1",
38
+ "transliteration": "^2.6.1"
44
39
  },
45
40
  "devDependencies": {
46
41
  "typescript": "^5.7.3",
@@ -67,5 +62,11 @@
67
62
  "bugs": {
68
63
  "url": "https://github.com/thedavidweng/lyric-romanizer/issues"
69
64
  },
70
- "homepage": "https://github.com/thedavidweng/lyric-romanizer#readme"
71
- }
65
+ "homepage": "https://github.com/thedavidweng/lyric-romanizer#readme",
66
+ "scripts": {
67
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
68
+ "clean": "rm -rf dist",
69
+ "test": "vitest run",
70
+ "test:watch": "vitest"
71
+ }
72
+ }