ipa-hangul 1.0.2 → 1.1.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.ko.md +176 -0
- package/README.md +61 -13
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +52 -5
- package/dist/index.mjs +52 -5
- package/package.json +1 -1
package/README.ko.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# ipa-hangul
|
|
2
|
+
|
|
3
|
+
IPA (국제 음성 기호) 발음을 한글로 변환합니다.
|
|
4
|
+
|
|
5
|
+
[English](./README.md) | 한국어
|
|
6
|
+
|
|
7
|
+
## 주요 기능
|
|
8
|
+
|
|
9
|
+
- 🎯 **IPA를 읽기 쉬운 한글로 변환**
|
|
10
|
+
- 📏 **장모음 대시 표시** - `/siː/` → `시-`
|
|
11
|
+
- 🔤 **자음 클러스터를 자모로** - `/wɝld/` → `월ㄷ`
|
|
12
|
+
- ⭐ **선택적 강세 표시** - 마크다운 (`**텍**스트`) 또는 HTML (`<strong>텍</strong>스트`)
|
|
13
|
+
- 🏗️ **모듈식 구조** - 유지보수 용이
|
|
14
|
+
- 📦 **제로 디펜던시**
|
|
15
|
+
- 💯 **TypeScript 지원** - 완전한 타입 정의
|
|
16
|
+
- 🚀 **이중 포맷** - ESM 및 CommonJS
|
|
17
|
+
|
|
18
|
+
## 설치
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install ipa-hangul
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 사용법
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { ipaToHangul } from 'ipa-hangul';
|
|
28
|
+
|
|
29
|
+
// 기본 예시
|
|
30
|
+
ipaToHangul('/həˈləʊ/'); // "허로"
|
|
31
|
+
ipaToHangul('/kæt/'); // "캩"
|
|
32
|
+
ipaToHangul('/bʊk/'); // "붘"
|
|
33
|
+
|
|
34
|
+
// 장모음 (대시로 표시)
|
|
35
|
+
ipaToHangul('/siː/'); // "시-"
|
|
36
|
+
ipaToHangul('/kɑːr/'); // "카-ㄹ"
|
|
37
|
+
|
|
38
|
+
// 자음 클러스터 (자모로 표시)
|
|
39
|
+
ipaToHangul('/wɝld/'); // "월ㄷ"
|
|
40
|
+
ipaToHangul('/fɪlm/'); // "필ㅁ"
|
|
41
|
+
ipaToHangul('/strɛŋkθs/'); // "ㅅㅌ렝ㅋㅅㅅ"
|
|
42
|
+
|
|
43
|
+
// 선택적 소리 (제거됨)
|
|
44
|
+
ipaToHangul('/ˈɹʌmb(ə)l/'); // "럼ㅂㄹ"
|
|
45
|
+
|
|
46
|
+
// 마크다운 강세 표시
|
|
47
|
+
ipaToHangul('/həˈləʊ/', { markStress: 'markdown' });
|
|
48
|
+
// "허**로**" (1차 강세 **)
|
|
49
|
+
|
|
50
|
+
ipaToHangul('/ˈɪntərnɛt/', { markStress: 'markdown' });
|
|
51
|
+
// "**인**털넽" (1차 강세)
|
|
52
|
+
|
|
53
|
+
ipaToHangul('/pɹəˌnaʊn.siˈeɪ.ʃən/', { markStress: 'markdown' });
|
|
54
|
+
// "ㅍ러*나*운시**에**이션" (1차 ** 및 2차 *)
|
|
55
|
+
|
|
56
|
+
// HTML 강세 표시
|
|
57
|
+
ipaToHangul('/həˈləʊ/', { markStress: 'html' });
|
|
58
|
+
// "허<strong>로</strong>" (1차 강세 <strong>)
|
|
59
|
+
|
|
60
|
+
ipaToHangul('/pɹəˌnaʊn.siˈeɪ.ʃən/', { markStress: 'html' });
|
|
61
|
+
// "ㅍ러<em>나</em>운시<strong>에</strong>이션" (1차 <strong> 및 2차 <em>)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 기능 설명
|
|
65
|
+
|
|
66
|
+
- **IPA-한글 변환**: 한글 자모 조립 사용
|
|
67
|
+
- **복잡한 IPA 처리**: 이중모음, 자음 클러스터, 음절 자음 지원
|
|
68
|
+
- **깔끔한 API**: 단일 함수로 문자열 입출력
|
|
69
|
+
- **TypeScript**: 완전한 타입 정의 포함
|
|
70
|
+
- **제로 디펜던시**: 런타임 의존성 없음
|
|
71
|
+
- **이중 포맷**: ESM 및 CommonJS 지원
|
|
72
|
+
|
|
73
|
+
## 지원하는 IPA 기능
|
|
74
|
+
|
|
75
|
+
### 자음
|
|
76
|
+
- 단순 자음: p, b, t, d, k, g, m, n, ŋ, f, v, θ, ð, s, z, ʃ, ʒ, h, l, r, ɹ
|
|
77
|
+
- 파찰음: tʃ, dʒ
|
|
78
|
+
- 자음 클러스터: pɹ, bɹ, tɹ, dɹ, kɹ, gɹ, fɹ, pl, bl, kl, gl, fl, sl
|
|
79
|
+
|
|
80
|
+
### 모음
|
|
81
|
+
- 단순 모음: i, ɪ, e, ɛ, æ, ɑ, ɒ, ɔ, ʌ, ə, ɜ, ʊ, u
|
|
82
|
+
- 장모음: iː, ɑː, ɔː, ɜː, uː
|
|
83
|
+
- 이중모음: eɪ, aɪ, ɔɪ, aʊ, əʊ, oʊ, ɪə, eə, ʊə
|
|
84
|
+
- 반모음 조합: w + 모음, j + 모음
|
|
85
|
+
- 음절 자음: l̩, n̩, m̩
|
|
86
|
+
|
|
87
|
+
### 특수 처리
|
|
88
|
+
- **강세 표시** (ˈ 1차 강세, ˌ 2차 강세):
|
|
89
|
+
- 기본값: 음절 경계로 사용, 표시 안 함
|
|
90
|
+
- `markStress: 'markdown'`: 1차 강세 `**강**`, 2차 강세 `*약*`
|
|
91
|
+
- `markStress: 'html'`: 1차 강세 `<strong>강</strong>`, 2차 강세 `<em>약</em>`
|
|
92
|
+
- **선택적 소리** 괄호 안은 제거됨
|
|
93
|
+
- **구분 기호** (/, [, ], .) 무시됨
|
|
94
|
+
|
|
95
|
+
## 동작 원리
|
|
96
|
+
|
|
97
|
+
한글 자모(字母) 조립을 사용하여 한글 음절을 구성합니다:
|
|
98
|
+
|
|
99
|
+
1. **초성(初聲)**: 초성 자음 (19가지)
|
|
100
|
+
2. **중성(中聲)**: 모음 (21가지)
|
|
101
|
+
3. **종성(終聲)**: 받침 자음 (27가지 + 없음)
|
|
102
|
+
|
|
103
|
+
각 IPA 소리는 가장 가까운 한글 자모에 매핑되어 유효한 한글 음절로 조립됩니다.
|
|
104
|
+
|
|
105
|
+
## 예시
|
|
106
|
+
|
|
107
|
+
| 단어 | IPA | 한글 | 참고 |
|
|
108
|
+
|------|-----|--------|-------|
|
|
109
|
+
| hello | /həˈləʊ/ | 허로 | 강세 표시를 음절 경계로 |
|
|
110
|
+
| cat | /kæt/ | 캩 | 종성 't' → ㅌ |
|
|
111
|
+
| book | /bʊk/ | 붘 | 종성 'k' → ㅋ |
|
|
112
|
+
| internet | /ˈɪntərnɛt/ | 인털넽 | 다중 음절 |
|
|
113
|
+
| world | /wɝld/ | 월ㄷ | 자음만 'ld'를 자모로 |
|
|
114
|
+
| see | /siː/ | 시- | 장모음 대시 표시 |
|
|
115
|
+
| rumble | /ˈɹʌmb(ə)l/ | 럼ㅂㄹ | 선택적 소리 제거 |
|
|
116
|
+
|
|
117
|
+
### 강세 표시 예시
|
|
118
|
+
|
|
119
|
+
| 단어 | IPA | 기본 | 마크다운 사용 시 | HTML 사용 시 |
|
|
120
|
+
|------|-----|---------|---------------|-----------|
|
|
121
|
+
| hello | /həˈləʊ/ | 허로 | 허**로** | 허`<strong>`로`</strong>` |
|
|
122
|
+
| internet | /ˈɪntərnɛt/ | 인털넽 | **인**털넽 | `<strong>`인`</strong>`털넽 |
|
|
123
|
+
| pronunciation | /pɹəˌnaʊn.siˈeɪ.ʃən/ | ㅍ러나운시에이션 | ㅍ러*나*운시**에**이션 | ㅍ러`<em>`나`</em>`운시`<strong>`에`</strong>`이션 |
|
|
124
|
+
|
|
125
|
+
## API
|
|
126
|
+
|
|
127
|
+
### `ipaToHangul(ipa: string, options?: IpaToHangulOptions): string`
|
|
128
|
+
|
|
129
|
+
IPA 표기를 한글 발음으로 변환합니다.
|
|
130
|
+
|
|
131
|
+
**매개변수:**
|
|
132
|
+
- `ipa`: IPA 표기 문자열 (강세 표시, 괄호, 선택적 소리 포함 가능)
|
|
133
|
+
- `options` (선택): 설정 객체
|
|
134
|
+
- `markStress?: 'markdown' | 'html'`: 강세 표시 형식
|
|
135
|
+
- `'markdown'`: 1차 강세 `**강**`, 2차 강세 `*약*`
|
|
136
|
+
- `'html'`: 1차 강세 `<strong>강</strong>`, 2차 강세 `<em>약</em>`
|
|
137
|
+
- 기본값: 강세 표시 없음 (강세 표시를 음절 경계로 사용)
|
|
138
|
+
|
|
139
|
+
**반환값:**
|
|
140
|
+
- 한글 발음 문자열 (선택적으로 강세 표시 포함)
|
|
141
|
+
|
|
142
|
+
**예시:**
|
|
143
|
+
```typescript
|
|
144
|
+
import { ipaToHangul } from 'ipa-hangul';
|
|
145
|
+
|
|
146
|
+
// 기본 사용 (강세 표시 없음)
|
|
147
|
+
const basic = ipaToHangul('/həˈləʊ/');
|
|
148
|
+
console.log(basic); // "허로"
|
|
149
|
+
|
|
150
|
+
// 마크다운 강세 표시
|
|
151
|
+
const markdown = ipaToHangul('/həˈləʊ/', { markStress: 'markdown' });
|
|
152
|
+
console.log(markdown); // "허**로**"
|
|
153
|
+
|
|
154
|
+
// HTML 강세 표시
|
|
155
|
+
const html = ipaToHangul('/həˈləʊ/', { markStress: 'html' });
|
|
156
|
+
console.log(html); // "허<strong>로</strong>"
|
|
157
|
+
|
|
158
|
+
// 1차 및 2차 강세
|
|
159
|
+
const complex = ipaToHangul('/pɹəˌnaʊn.siˈeɪ.ʃən/', { markStress: 'markdown' });
|
|
160
|
+
console.log(complex); // "ㅍ러*나*운시**에**이션"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 한계
|
|
164
|
+
|
|
165
|
+
- **근사치**: 한글로 모든 영어 발음을 완벽하게 표현할 수 없음
|
|
166
|
+
- **매핑 선택**: 일부 IPA 소리는 같은 한글 자음에 매핑됨 (예: f/p → ㅍ)
|
|
167
|
+
- **종성 단순화**: 일부 종성 자음은 비전통적 매핑 사용 (예: t → ㅌ 대신 ㄷ)
|
|
168
|
+
- **성조 미지원**: 분절 자질만 변환, 초분절 자질 미지원
|
|
169
|
+
|
|
170
|
+
## 기여
|
|
171
|
+
|
|
172
|
+
기여를 환영합니다! 이슈나 풀 리퀘스트를 자유롭게 제출해주세요.
|
|
173
|
+
|
|
174
|
+
## 라이선스
|
|
175
|
+
|
|
176
|
+
MIT
|
package/README.md
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Convert IPA (International Phonetic Alphabet) pronunciation to Korean Hangul.
|
|
4
4
|
|
|
5
|
+
English | [한국어](./README.ko.md)
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
|
-
- 🎯 **
|
|
9
|
+
- 🎯 **Convert IPA to readable Hangul**
|
|
8
10
|
- 📏 **Long vowels marked with dash (-)** - `/siː/` → `시-`
|
|
9
11
|
- 🔤 **Consonant clusters as Jamo** - `/wɝld/` → `월ㄷ`
|
|
12
|
+
- ⭐ **Optional stress marking** - Markdown (`**텍**스트`) or HTML (`<strong>텍</strong>스트`)
|
|
10
13
|
- 🏗️ **Modular & maintainable** code structure
|
|
11
14
|
- 📦 **Zero dependencies**
|
|
12
15
|
- 💯 **TypeScript support** with full type definitions
|
|
@@ -24,7 +27,7 @@ npm install ipa-hangul
|
|
|
24
27
|
import { ipaToHangul } from 'ipa-hangul';
|
|
25
28
|
|
|
26
29
|
// Basic examples
|
|
27
|
-
ipaToHangul('
|
|
30
|
+
ipaToHangul('/həˈləʊ/'); // "허로"
|
|
28
31
|
ipaToHangul('/kæt/'); // "캩"
|
|
29
32
|
ipaToHangul('/bʊk/'); // "붘"
|
|
30
33
|
|
|
@@ -39,11 +42,28 @@ ipaToHangul('/strɛŋkθs/'); // "ㅅㅌ렝ㅋㅅㅅ"
|
|
|
39
42
|
|
|
40
43
|
// Optional sounds (removed)
|
|
41
44
|
ipaToHangul('/ˈɹʌmb(ə)l/'); // "럼ㅂㄹ"
|
|
45
|
+
|
|
46
|
+
// Stress marking with Markdown
|
|
47
|
+
ipaToHangul('/həˈləʊ/', { markStress: 'markdown' });
|
|
48
|
+
// "허**로**" (primary stress with **)
|
|
49
|
+
|
|
50
|
+
ipaToHangul('/ˈɪntərnɛt/', { markStress: 'markdown' });
|
|
51
|
+
// "**인**털넽" (primary stress)
|
|
52
|
+
|
|
53
|
+
ipaToHangul('/pɹəˌnaʊn.siˈeɪ.ʃən/', { markStress: 'markdown' });
|
|
54
|
+
// "ㅍ러*나*운시**에**이션" (primary ** and secondary *)
|
|
55
|
+
|
|
56
|
+
// Stress marking with HTML
|
|
57
|
+
ipaToHangul('/həˈləʊ/', { markStress: 'html' });
|
|
58
|
+
// "허<strong>로</strong>" (primary stress with <strong>)
|
|
59
|
+
|
|
60
|
+
ipaToHangul('/pɹəˌnaʊn.siˈeɪ.ʃən/', { markStress: 'html' });
|
|
61
|
+
// "ㅍ러<em>나</em>운시<strong>에</strong>이션" (primary <strong> and secondary <em>)
|
|
42
62
|
```
|
|
43
63
|
|
|
44
64
|
## Features
|
|
45
65
|
|
|
46
|
-
- **
|
|
66
|
+
- **IPA to Hangul conversion**: Uses Korean Jamo assembly
|
|
47
67
|
- **Handles complex IPA**: Supports diphthongs, consonant clusters, syllabic consonants
|
|
48
68
|
- **Clean API**: Single function with string input/output
|
|
49
69
|
- **TypeScript**: Full type definitions included
|
|
@@ -65,9 +85,12 @@ ipaToHangul('/ˈɹʌmb(ə)l/'); // "럼ㅂㄹ"
|
|
|
65
85
|
- Syllabic consonants: l̩, n̩, m̩
|
|
66
86
|
|
|
67
87
|
### Special handling
|
|
68
|
-
- Stress markers (
|
|
69
|
-
-
|
|
70
|
-
-
|
|
88
|
+
- **Stress markers** (ˈ primary, ˌ secondary):
|
|
89
|
+
- Default: Used as syllable boundaries, not displayed
|
|
90
|
+
- With `markStress: 'markdown'`: Primary `**강**`, Secondary `*약*`
|
|
91
|
+
- With `markStress: 'html'`: Primary `<strong>강</strong>`, Secondary `<em>약</em>`
|
|
92
|
+
- **Optional sounds** in parentheses are removed
|
|
93
|
+
- **Delimiters** (/, [, ], .) are ignored
|
|
71
94
|
|
|
72
95
|
## How it works
|
|
73
96
|
|
|
@@ -83,33 +106,58 @@ Each IPA sound is mapped to the closest Korean equivalent, then assembled into v
|
|
|
83
106
|
|
|
84
107
|
| Word | IPA | Hangul | Notes |
|
|
85
108
|
|------|-----|--------|-------|
|
|
86
|
-
| hello |
|
|
109
|
+
| hello | /həˈləʊ/ | 허로 | Stress marker as boundary |
|
|
87
110
|
| cat | /kæt/ | 캩 | Final 't' → ㅌ |
|
|
88
111
|
| book | /bʊk/ | 붘 | Final 'k' → ㅋ |
|
|
89
112
|
| internet | /ˈɪntərnɛt/ | 인털넽 | Multi-syllable |
|
|
90
113
|
| world | /wɝld/ | 월ㄷ | Consonant-only 'ld' as Jamo |
|
|
91
|
-
| pretty | /ˈprɪti/ | 프리티 | Consonant cluster 'pr' |
|
|
92
114
|
| see | /siː/ | 시- | Long vowel marked with dash |
|
|
93
115
|
| rumble | /ˈɹʌmb(ə)l/ | 럼ㅂㄹ | Optional sounds removed |
|
|
94
116
|
|
|
117
|
+
### Stress Marking Examples
|
|
118
|
+
|
|
119
|
+
| Word | IPA | Default | With Markdown | With HTML |
|
|
120
|
+
|------|-----|---------|---------------|-----------|
|
|
121
|
+
| hello | /həˈləʊ/ | 허로 | 허**로** | 허`<strong>`로`</strong>` |
|
|
122
|
+
| internet | /ˈɪntərnɛt/ | 인털넽 | **인**털넽 | `<strong>`인`</strong>`털넽 |
|
|
123
|
+
| pronunciation | /pɹəˌnaʊn.siˈeɪ.ʃən/ | ㅍ러나운시에이션 | ㅍ러*나*운시**에**이션 | ㅍ러`<em>`나`</em>`운시`<strong>`에`</strong>`이션 |
|
|
124
|
+
|
|
95
125
|
## API
|
|
96
126
|
|
|
97
|
-
### `ipaToHangul(ipa: string): string`
|
|
127
|
+
### `ipaToHangul(ipa: string, options?: IpaToHangulOptions): string`
|
|
98
128
|
|
|
99
129
|
Converts IPA notation to Korean Hangul pronunciation.
|
|
100
130
|
|
|
101
131
|
**Parameters:**
|
|
102
132
|
- `ipa`: IPA notation string (can include stress markers, brackets, optional sounds)
|
|
133
|
+
- `options` (optional): Configuration object
|
|
134
|
+
- `markStress?: 'markdown' | 'html'`: Format for stress marking
|
|
135
|
+
- `'markdown'`: Primary stress `**강**`, Secondary stress `*약*`
|
|
136
|
+
- `'html'`: Primary stress `<strong>강</strong>`, Secondary stress `<em>약</em>`
|
|
137
|
+
- Default: No stress marking (stress markers used as syllable boundaries)
|
|
103
138
|
|
|
104
139
|
**Returns:**
|
|
105
|
-
- Korean Hangul pronunciation string
|
|
140
|
+
- Korean Hangul pronunciation string (with optional stress markers)
|
|
106
141
|
|
|
107
|
-
**
|
|
142
|
+
**Examples:**
|
|
108
143
|
```typescript
|
|
109
144
|
import { ipaToHangul } from 'ipa-hangul';
|
|
110
145
|
|
|
111
|
-
|
|
112
|
-
|
|
146
|
+
// Basic usage (no stress marking)
|
|
147
|
+
const basic = ipaToHangul('/həˈləʊ/');
|
|
148
|
+
console.log(basic); // "허로"
|
|
149
|
+
|
|
150
|
+
// Markdown stress marking
|
|
151
|
+
const markdown = ipaToHangul('/həˈləʊ/', { markStress: 'markdown' });
|
|
152
|
+
console.log(markdown); // "허**로**"
|
|
153
|
+
|
|
154
|
+
// HTML stress marking
|
|
155
|
+
const html = ipaToHangul('/həˈləʊ/', { markStress: 'html' });
|
|
156
|
+
console.log(html); // "허<strong>로</strong>"
|
|
157
|
+
|
|
158
|
+
// Primary and secondary stress
|
|
159
|
+
const complex = ipaToHangul('/pɹəˌnaʊn.siˈeɪ.ʃən/', { markStress: 'markdown' });
|
|
160
|
+
console.log(complex); // "ㅍ러*나*운시**에**이션"
|
|
113
161
|
```
|
|
114
162
|
|
|
115
163
|
## Limitations
|
package/dist/index.d.mts
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* /wɜːrld/ → 워-ㄹㄷ
|
|
11
11
|
* /hɛloʊ/ → 헤로
|
|
12
12
|
*/
|
|
13
|
+
interface IpaToHangulOptions {
|
|
14
|
+
markStress?: 'markdown' | 'html';
|
|
15
|
+
}
|
|
13
16
|
/**
|
|
14
17
|
* Convert IPA notation to Korean Hangul pronunciation
|
|
15
18
|
*
|
|
@@ -17,15 +20,20 @@
|
|
|
17
20
|
* - Long vowels (ː) create segments separated by dash (-)
|
|
18
21
|
* - Consonant-only sequences use compatibility Jamo (ㄱㄴㄷ...)
|
|
19
22
|
* - Vowel+consonant combinations form complete Hangul syllables
|
|
23
|
+
* - Optional stress marking with markdown or HTML format
|
|
20
24
|
*
|
|
21
25
|
* @param ipa - IPA notation string (e.g., "/ˈhɛloʊ/", "/wɜːrld/")
|
|
26
|
+
* @param options - Optional configuration
|
|
27
|
+
* @param options.markStress - Format for stress marking: 'markdown' (**text**) or 'html' (<strong>text</strong>)
|
|
22
28
|
* @returns Korean Hangul pronunciation string
|
|
23
29
|
*
|
|
24
30
|
* @example
|
|
25
31
|
* ipaToHangul("/ˈhɛloʊ/") // "헤로"
|
|
32
|
+
* ipaToHangul("/ˈhɛloʊ/", { markStress: 'markdown' }) // "**헤**로"
|
|
33
|
+
* ipaToHangul("/ˈhɛloʊ/", { markStress: 'html' }) // "<strong>헤</strong>로"
|
|
26
34
|
* ipaToHangul("/wɜːrld/") // "워-ㄹㄷ"
|
|
27
35
|
* ipaToHangul("/kæt/") // "캩"
|
|
28
36
|
*/
|
|
29
|
-
declare function ipaToHangul(ipa: string): string;
|
|
37
|
+
declare function ipaToHangul(ipa: string, options?: IpaToHangulOptions): string;
|
|
30
38
|
|
|
31
|
-
export { ipaToHangul };
|
|
39
|
+
export { type IpaToHangulOptions, ipaToHangul };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* /wɜːrld/ → 워-ㄹㄷ
|
|
11
11
|
* /hɛloʊ/ → 헤로
|
|
12
12
|
*/
|
|
13
|
+
interface IpaToHangulOptions {
|
|
14
|
+
markStress?: 'markdown' | 'html';
|
|
15
|
+
}
|
|
13
16
|
/**
|
|
14
17
|
* Convert IPA notation to Korean Hangul pronunciation
|
|
15
18
|
*
|
|
@@ -17,15 +20,20 @@
|
|
|
17
20
|
* - Long vowels (ː) create segments separated by dash (-)
|
|
18
21
|
* - Consonant-only sequences use compatibility Jamo (ㄱㄴㄷ...)
|
|
19
22
|
* - Vowel+consonant combinations form complete Hangul syllables
|
|
23
|
+
* - Optional stress marking with markdown or HTML format
|
|
20
24
|
*
|
|
21
25
|
* @param ipa - IPA notation string (e.g., "/ˈhɛloʊ/", "/wɜːrld/")
|
|
26
|
+
* @param options - Optional configuration
|
|
27
|
+
* @param options.markStress - Format for stress marking: 'markdown' (**text**) or 'html' (<strong>text</strong>)
|
|
22
28
|
* @returns Korean Hangul pronunciation string
|
|
23
29
|
*
|
|
24
30
|
* @example
|
|
25
31
|
* ipaToHangul("/ˈhɛloʊ/") // "헤로"
|
|
32
|
+
* ipaToHangul("/ˈhɛloʊ/", { markStress: 'markdown' }) // "**헤**로"
|
|
33
|
+
* ipaToHangul("/ˈhɛloʊ/", { markStress: 'html' }) // "<strong>헤</strong>로"
|
|
26
34
|
* ipaToHangul("/wɜːrld/") // "워-ㄹㄷ"
|
|
27
35
|
* ipaToHangul("/kæt/") // "캩"
|
|
28
36
|
*/
|
|
29
|
-
declare function ipaToHangul(ipa: string): string;
|
|
37
|
+
declare function ipaToHangul(ipa: string, options?: IpaToHangulOptions): string;
|
|
30
38
|
|
|
31
|
-
export { ipaToHangul };
|
|
39
|
+
export { type IpaToHangulOptions, ipaToHangul };
|
package/dist/index.js
CHANGED
|
@@ -256,7 +256,22 @@ function matchConsonant(text, pos) {
|
|
|
256
256
|
return null;
|
|
257
257
|
}
|
|
258
258
|
function preprocessIPA(ipa) {
|
|
259
|
-
return ipa.replace(/\([^)]*\)/g, "").replace(/[
|
|
259
|
+
return ipa.replace(/\([^)]*\)/g, "").replace(/[\/\[\]]/g, "").replace(/ˈ/g, ".[P]").replace(/ˌ/g, ".[S]").replace(/[′']/g, ".").replace(/\.+/g, ".").replace(/^\./g, "").trim();
|
|
260
|
+
}
|
|
261
|
+
function parseSyllables(text) {
|
|
262
|
+
const syllables = [];
|
|
263
|
+
const parts = text.split(".");
|
|
264
|
+
for (const part of parts) {
|
|
265
|
+
if (!part) continue;
|
|
266
|
+
if (part.startsWith("[P]")) {
|
|
267
|
+
syllables.push({ text: part.slice(3), stress: "primary" });
|
|
268
|
+
} else if (part.startsWith("[S]")) {
|
|
269
|
+
syllables.push({ text: part.slice(3), stress: "secondary" });
|
|
270
|
+
} else {
|
|
271
|
+
syllables.push({ text: part, stress: "none" });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return syllables;
|
|
260
275
|
}
|
|
261
276
|
function splitByLongVowel(text) {
|
|
262
277
|
const segments = [];
|
|
@@ -388,14 +403,44 @@ function convertSegment(tokens) {
|
|
|
388
403
|
}
|
|
389
404
|
return result.join("");
|
|
390
405
|
}
|
|
391
|
-
function
|
|
406
|
+
function getFirstHangulSyllable(text) {
|
|
407
|
+
for (let i = 0; i < text.length; i++) {
|
|
408
|
+
const code = text.charCodeAt(i);
|
|
409
|
+
if (code >= 44032 && code <= 55203 || code >= 12593 && code <= 12686) {
|
|
410
|
+
return {
|
|
411
|
+
first: text.substring(0, i + 1),
|
|
412
|
+
rest: text.substring(i + 1)
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return { first: text, rest: "" };
|
|
417
|
+
}
|
|
418
|
+
function applyStressMarker(hangul, stress, format) {
|
|
419
|
+
if (!format || stress === "none") return hangul;
|
|
420
|
+
const { first, rest } = getFirstHangulSyllable(hangul);
|
|
421
|
+
if (stress === "primary") {
|
|
422
|
+
if (format === "markdown") {
|
|
423
|
+
return `**${first}**${rest}`;
|
|
424
|
+
} else if (format === "html") {
|
|
425
|
+
return `<strong>${first}</strong>${rest}`;
|
|
426
|
+
}
|
|
427
|
+
} else if (stress === "secondary") {
|
|
428
|
+
if (format === "markdown") {
|
|
429
|
+
return `*${first}*${rest}`;
|
|
430
|
+
} else if (format === "html") {
|
|
431
|
+
return `<em>${first}</em>${rest}`;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return hangul;
|
|
435
|
+
}
|
|
436
|
+
function ipaToHangul(ipa, options) {
|
|
392
437
|
if (!ipa) return "";
|
|
393
438
|
const cleaned = preprocessIPA(ipa);
|
|
394
439
|
if (!cleaned) return "";
|
|
395
|
-
const syllables = cleaned
|
|
440
|
+
const syllables = parseSyllables(cleaned);
|
|
396
441
|
const results = [];
|
|
397
442
|
for (const syllable of syllables) {
|
|
398
|
-
const segments = splitByLongVowel(syllable);
|
|
443
|
+
const segments = splitByLongVowel(syllable.text);
|
|
399
444
|
const convertedSegments = segments.map((seg) => {
|
|
400
445
|
const tokens = tokenizeSegment(seg.text);
|
|
401
446
|
const hangul = convertSegment(tokens);
|
|
@@ -404,7 +449,9 @@ function ipaToHangul(ipa) {
|
|
|
404
449
|
}
|
|
405
450
|
return hangul;
|
|
406
451
|
});
|
|
407
|
-
|
|
452
|
+
const hangulResult = convertedSegments.join("");
|
|
453
|
+
const markedResult = applyStressMarker(hangulResult, syllable.stress, options?.markStress);
|
|
454
|
+
results.push(markedResult);
|
|
408
455
|
}
|
|
409
456
|
return results.join("");
|
|
410
457
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -232,7 +232,22 @@ function matchConsonant(text, pos) {
|
|
|
232
232
|
return null;
|
|
233
233
|
}
|
|
234
234
|
function preprocessIPA(ipa) {
|
|
235
|
-
return ipa.replace(/\([^)]*\)/g, "").replace(/[
|
|
235
|
+
return ipa.replace(/\([^)]*\)/g, "").replace(/[\/\[\]]/g, "").replace(/ˈ/g, ".[P]").replace(/ˌ/g, ".[S]").replace(/[′']/g, ".").replace(/\.+/g, ".").replace(/^\./g, "").trim();
|
|
236
|
+
}
|
|
237
|
+
function parseSyllables(text) {
|
|
238
|
+
const syllables = [];
|
|
239
|
+
const parts = text.split(".");
|
|
240
|
+
for (const part of parts) {
|
|
241
|
+
if (!part) continue;
|
|
242
|
+
if (part.startsWith("[P]")) {
|
|
243
|
+
syllables.push({ text: part.slice(3), stress: "primary" });
|
|
244
|
+
} else if (part.startsWith("[S]")) {
|
|
245
|
+
syllables.push({ text: part.slice(3), stress: "secondary" });
|
|
246
|
+
} else {
|
|
247
|
+
syllables.push({ text: part, stress: "none" });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return syllables;
|
|
236
251
|
}
|
|
237
252
|
function splitByLongVowel(text) {
|
|
238
253
|
const segments = [];
|
|
@@ -364,14 +379,44 @@ function convertSegment(tokens) {
|
|
|
364
379
|
}
|
|
365
380
|
return result.join("");
|
|
366
381
|
}
|
|
367
|
-
function
|
|
382
|
+
function getFirstHangulSyllable(text) {
|
|
383
|
+
for (let i = 0; i < text.length; i++) {
|
|
384
|
+
const code = text.charCodeAt(i);
|
|
385
|
+
if (code >= 44032 && code <= 55203 || code >= 12593 && code <= 12686) {
|
|
386
|
+
return {
|
|
387
|
+
first: text.substring(0, i + 1),
|
|
388
|
+
rest: text.substring(i + 1)
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return { first: text, rest: "" };
|
|
393
|
+
}
|
|
394
|
+
function applyStressMarker(hangul, stress, format) {
|
|
395
|
+
if (!format || stress === "none") return hangul;
|
|
396
|
+
const { first, rest } = getFirstHangulSyllable(hangul);
|
|
397
|
+
if (stress === "primary") {
|
|
398
|
+
if (format === "markdown") {
|
|
399
|
+
return `**${first}**${rest}`;
|
|
400
|
+
} else if (format === "html") {
|
|
401
|
+
return `<strong>${first}</strong>${rest}`;
|
|
402
|
+
}
|
|
403
|
+
} else if (stress === "secondary") {
|
|
404
|
+
if (format === "markdown") {
|
|
405
|
+
return `*${first}*${rest}`;
|
|
406
|
+
} else if (format === "html") {
|
|
407
|
+
return `<em>${first}</em>${rest}`;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return hangul;
|
|
411
|
+
}
|
|
412
|
+
function ipaToHangul(ipa, options) {
|
|
368
413
|
if (!ipa) return "";
|
|
369
414
|
const cleaned = preprocessIPA(ipa);
|
|
370
415
|
if (!cleaned) return "";
|
|
371
|
-
const syllables = cleaned
|
|
416
|
+
const syllables = parseSyllables(cleaned);
|
|
372
417
|
const results = [];
|
|
373
418
|
for (const syllable of syllables) {
|
|
374
|
-
const segments = splitByLongVowel(syllable);
|
|
419
|
+
const segments = splitByLongVowel(syllable.text);
|
|
375
420
|
const convertedSegments = segments.map((seg) => {
|
|
376
421
|
const tokens = tokenizeSegment(seg.text);
|
|
377
422
|
const hangul = convertSegment(tokens);
|
|
@@ -380,7 +425,9 @@ function ipaToHangul(ipa) {
|
|
|
380
425
|
}
|
|
381
426
|
return hangul;
|
|
382
427
|
});
|
|
383
|
-
|
|
428
|
+
const hangulResult = convertedSegments.join("");
|
|
429
|
+
const markedResult = applyStressMarker(hangulResult, syllable.stress, options?.markStress);
|
|
430
|
+
results.push(markedResult);
|
|
384
431
|
}
|
|
385
432
|
return results.join("");
|
|
386
433
|
}
|