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 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
- - 🎯 **Accurate IPA-to-Hangul conversion** based on phonetic rules
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('/ˈhɛloʊ/'); // "헤로"
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
- - **Accurate conversion**: Based on Korean phonetic rules and Jamo assembly
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 (ˈ, ˌ) are removed
69
- - Optional sounds in parentheses are removed
70
- - Delimiters (/, [, ], .) are ignored
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 | /ˈhɛloʊ/ | 헤로 | Stress marker removed |
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
- **Example:**
142
+ **Examples:**
108
143
  ```typescript
109
144
  import { ipaToHangul } from 'ipa-hangul';
110
145
 
111
- const pronunciation = ipaToHangul('/ˈhɛloʊ/');
112
- console.log(pronunciation); // "헤로"
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(/[ˈˌ′']/g, ".").replace(/[\/\[\]]/g, "").replace(/\.+/g, ".").replace(/^\.|\.$/g, "").trim();
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 ipaToHangul(ipa) {
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.split(".").filter((s) => s.length > 0);
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
- results.push(convertedSegments.join(""));
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(/[ˈˌ′']/g, ".").replace(/[\/\[\]]/g, "").replace(/\.+/g, ".").replace(/^\.|\.$/g, "").trim();
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 ipaToHangul(ipa) {
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.split(".").filter((s) => s.length > 0);
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
- results.push(convertedSegments.join(""));
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ipa-hangul",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Convert IPA (International Phonetic Alphabet) pronunciation to Korean Hangul",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",