korrect-js 1.0.2
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 +89 -0
- package/index.js +13 -0
- package/lib/check.js +65 -0
- package/lib/chosung.js +22 -0
- package/lib/pickJosa.js +24 -0
- package/lib/trans.js +196 -0
- package/package.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# korrect-js
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/korrect-js)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**korrect-js**는 한국어 텍스트 처리와 맞춤법 검사를 간편하게 수행할 수 있는 자바스크립트 라이브러리입니다. 초성 추출, 조사 선택, 영타/한타 변환 등 한국어 서비스 개발에 꼭 필요한 유틸리티들을 제공합니다.
|
|
7
|
+
|
|
8
|
+
## 주요 기능
|
|
9
|
+
|
|
10
|
+
- **맞춤법 검사**: 문장의 맞춤법을 교정하고, 에러 위치와 교정 제안을 포함한 상세 데이터를 반환합니다.
|
|
11
|
+
- **초성 추출**: 한글 단어에서 초성(ㄱㄴㄷ...)만 깔끔하게 추출합니다.
|
|
12
|
+
- **조사 자동 선택**: 단어의 종성(받침) 여부에 따라 적합한 조사(은/는, 이/가, 을/를)를 자동으로 선택합니다.
|
|
13
|
+
- **영타/한타 변환**: 키보드 입력 실수를 교정하거나 한글을 로마자로 변환하는 기능을 제공합니다.
|
|
14
|
+
|
|
15
|
+
## 설치
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install korrect-js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 사용법
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
const korrect = require('korrect-js');
|
|
25
|
+
|
|
26
|
+
// 1. 맞춤법 검사 (Async)
|
|
27
|
+
async function checkSpell() {
|
|
28
|
+
const result = await korrect.check("바나나가 잇다");
|
|
29
|
+
console.log(result.corrected); // "바나나가 있다"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 2. 초성 추출
|
|
33
|
+
const chosung = korrect.chosung("가느다란 물방울");
|
|
34
|
+
console.log(chosung); // "ㄱㄴㄷㄹ ㅁㅂㅇ"
|
|
35
|
+
|
|
36
|
+
// 3. 조사 자동 선택
|
|
37
|
+
const word = "사과";
|
|
38
|
+
const sentence = `${word}${korrect.pickJosa(word, ["은", "는"])} 맛있다.`;
|
|
39
|
+
console.log(sentence); // "사과는 맛있다."
|
|
40
|
+
|
|
41
|
+
// 4. 영타/한타 변환
|
|
42
|
+
console.log(korrect.toKo("dkssud")); // "안녕"
|
|
43
|
+
console.log(korrect.toEn("안녕")); // "dkssud"
|
|
44
|
+
console.log(korrect.toRoman("나비")); // "nabi"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API 상세
|
|
48
|
+
|
|
49
|
+
### `check(text)`
|
|
50
|
+
네이버 맞춤법 검사기 API를 사용하여 문장을 검사합니다.
|
|
51
|
+
- **Parameters**: `text` (string)
|
|
52
|
+
- **Returns**: `Promise<object>`
|
|
53
|
+
- `original`: 원본 문장
|
|
54
|
+
- `corrected`: 교정된 문장
|
|
55
|
+
- `errors`: 에러 배열 (wrong, right, start, end)
|
|
56
|
+
- `errata_count`: 에러 개수
|
|
57
|
+
|
|
58
|
+
### `chosung(word)`
|
|
59
|
+
- **Parameters**: `word` (string)
|
|
60
|
+
- **Returns**: `string` (추출된 초성)
|
|
61
|
+
|
|
62
|
+
### `pickJosa(word, josa)`
|
|
63
|
+
- **Parameters**:
|
|
64
|
+
- `word`: 대상 단어
|
|
65
|
+
- `josa`: `["은", "는"]` 형태의 조사 쌍
|
|
66
|
+
- **Returns**: `string` (선택된 조사)
|
|
67
|
+
|
|
68
|
+
### `toEn(word)` / `toKo(word)` / `toRoman(word)`
|
|
69
|
+
- 한글↔영어 키보드 입력 변환 및 로마자 표기법 변환을 수행합니다.
|
|
70
|
+
|
|
71
|
+
## 프로젝트 구조
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
korrect/
|
|
75
|
+
├── lib/ # 핵심 로직 (check, chosung, pickJosa, trans)
|
|
76
|
+
├── tests/ # 테스트 스크립트
|
|
77
|
+
├── index.js # 엔트리 포인트
|
|
78
|
+
└── package.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 링크
|
|
82
|
+
|
|
83
|
+
- **개발자**: [https://jeeks.my](https://jeeks.my)
|
|
84
|
+
- **GitHub**: [https://github.com/jikwxng/korrect](https://github.com/jikwxng/korrect)
|
|
85
|
+
- **NPM**: [https://www.npmjs.com/package/korrect-js](https://www.npmjs.com/package/korrect-js)
|
|
86
|
+
|
|
87
|
+
## 라이선스
|
|
88
|
+
|
|
89
|
+
이 프로젝트는 [MIT License](LICENSE)를 따릅니다.
|
package/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const chosung = require('./lib/chosung');
|
|
2
|
+
const pickJosa = require('./lib/pickJosa');
|
|
3
|
+
const { toEn, toKo, toRoman } = require('./lib/trans');
|
|
4
|
+
const check = require('./lib/check');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
chosung,
|
|
8
|
+
pickJosa,
|
|
9
|
+
toEn,
|
|
10
|
+
toKo,
|
|
11
|
+
toRoman,
|
|
12
|
+
check
|
|
13
|
+
};
|
package/lib/check.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 문장의 맞춤법을 검사하고 교정된 결과를 반환합니다.
|
|
5
|
+
* @param {string} text - 맞춤법을 검사할 문장
|
|
6
|
+
* @returns {Promise<object|null>} 교정된 결과 객체
|
|
7
|
+
* @example
|
|
8
|
+
* const result = await korrect.check("바나나가 잇다");
|
|
9
|
+
* // { original: "바나나가 잇다", corrected: "바나나가 있다", errors: [...], errata_count: 1 }
|
|
10
|
+
*/
|
|
11
|
+
async function check(text) {
|
|
12
|
+
if (typeof text !== 'string' || text.length === 0) return null;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const encodedText = encodeURIComponent(text);
|
|
16
|
+
const url = `https://ts-proxy.naver.com/ocontent/util/SpellerProxy?passportKey=12e3a39878a34a48ba1dfd7203522054aa417829&q=${encodedText}&where=nexearch&color_blindness=0`;
|
|
17
|
+
|
|
18
|
+
const response = await axios.get(url);
|
|
19
|
+
const result = response.data.message.result;
|
|
20
|
+
|
|
21
|
+
const stripTags = (str) => str.replace(/<[^>]*>?/gm, '');
|
|
22
|
+
const originalText = stripTags(result.origin_html);
|
|
23
|
+
const correctedText = stripTags(result.html);
|
|
24
|
+
|
|
25
|
+
const originalWords = originalText.split(/\s+/);
|
|
26
|
+
const correctedWords = correctedText.split(/\s+/);
|
|
27
|
+
|
|
28
|
+
const errors = [];
|
|
29
|
+
let currentOffset = 0;
|
|
30
|
+
|
|
31
|
+
originalWords.forEach((word, index) => {
|
|
32
|
+
const correctedWord = correctedWords[index];
|
|
33
|
+
|
|
34
|
+
if (word !== correctedWord) {
|
|
35
|
+
errors.push({
|
|
36
|
+
wrong: word,
|
|
37
|
+
right: correctedWord,
|
|
38
|
+
start: currentOffset,
|
|
39
|
+
end: currentOffset + word.length
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const nextSpaceIndex = originalText.indexOf(' ', currentOffset + word.length);
|
|
44
|
+
if (nextSpaceIndex !== -1) {
|
|
45
|
+
currentOffset = nextSpaceIndex + 1;
|
|
46
|
+
} else {
|
|
47
|
+
currentOffset += word.length + 1;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
original: originalText,
|
|
53
|
+
corrected: correctedText,
|
|
54
|
+
errors: errors,
|
|
55
|
+
errata_count: result.errata_count
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("맞춤법 검사 오류:", error.message);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = check;
|
|
65
|
+
|
package/lib/chosung.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 단어의 초성을 반환합니다.
|
|
3
|
+
* @param {string} word
|
|
4
|
+
* @example korrect.chosung("가느다란 물방울") // "ㄱㄴㄷㄹ ㅁㅂㅇ"
|
|
5
|
+
*/
|
|
6
|
+
function chosung(word) {
|
|
7
|
+
if (typeof word !== 'string' || word.length === 0) throw new Error("Invalid input: 초성으로 변환할 단어를 입력해주세요.");
|
|
8
|
+
|
|
9
|
+
let result = "";
|
|
10
|
+
for (let i = 0; i < word.length; i++) {
|
|
11
|
+
const firstCode = word.charCodeAt(i);
|
|
12
|
+
if (firstCode >= 0xAC00 && firstCode <= 0xD7A3) {
|
|
13
|
+
const chosungCode = Math.floor((firstCode - 0xAC00) / 588) + 0x1100;
|
|
14
|
+
result += String.fromCharCode(chosungCode);
|
|
15
|
+
} else {
|
|
16
|
+
result += word[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = chosung;
|
package/lib/pickJosa.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 단어에 따라 조사를 붙입니다.
|
|
3
|
+
* @param {string} word - 조사가 붙을 단어
|
|
4
|
+
* @param {string[]} josa - 단어 뒤에 붙을 조사
|
|
5
|
+
* @returns {string}
|
|
6
|
+
* @example
|
|
7
|
+
* korrect.pickJosa("사과", ["을", "를"]) // "사과를"
|
|
8
|
+
* korrect.pickJosa("길동", ["이", "가"]) // "길동이"
|
|
9
|
+
*/
|
|
10
|
+
function pickJosa(word, josa) {
|
|
11
|
+
if (typeof word !== 'string' || word.length === 0) return word;
|
|
12
|
+
if (!Array.isArray(josa) || josa.length !== 2) return word;
|
|
13
|
+
|
|
14
|
+
const lastChar = word[word.length - 1];
|
|
15
|
+
const code = lastChar.charCodeAt(0);
|
|
16
|
+
if (code < 0xAC00 || code > 0xD7A3) {
|
|
17
|
+
return word + josa[0];
|
|
18
|
+
}
|
|
19
|
+
const hasBatchim = (code - 0xAC00) % 28 > 0;
|
|
20
|
+
|
|
21
|
+
return word + (hasBatchim ? josa[0] : josa[1]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = pickJosa;
|
package/lib/trans.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 한타를 영타로 변환합니다.
|
|
3
|
+
* @param {string} word - 변환할 한글 단어
|
|
4
|
+
* @example toEn("안녕") // "dkssud"
|
|
5
|
+
*/
|
|
6
|
+
function toEn(word) {
|
|
7
|
+
if (typeof word !== 'string' || word.length === 0) return word;
|
|
8
|
+
|
|
9
|
+
const CHOSUNG = [
|
|
10
|
+
'r', 'R', 's', 'e', 'E', 'f', 'a', 'q', 'Q', 't', 'T', 'd', 'w', 'W', 'c', 'v', 'x', 'z', 'g'
|
|
11
|
+
];
|
|
12
|
+
const JUNGSUNG = [
|
|
13
|
+
'k', 'o', 'i', 'O', 'j', 'p', 'u', 'P', 'h', 'hk', 'ho', 'hl', 'y', 'n', 'nj', 'np', 'nl', 'b', 'm', 'ml', 'l'
|
|
14
|
+
];
|
|
15
|
+
const JONGSUNG = [
|
|
16
|
+
'', 'r', 'R', 'rt', 's', 'sw', 'sg', 'e', 'f', 'fr', 'fa', 'fq', 'ft', 'fx', 'fz', 'fg', 'a', 'q', 'qt', 't', 'T', 'd', 'w', 'c', 'v', 'x', 'z', 'g'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const JAMO_MAP = {
|
|
20
|
+
'ㄱ': 'r', 'ㄲ': 'R', 'ㄴ': 's', 'ㄷ': 'e', 'ㄸ': 'E', 'ㄹ': 'f', 'ㅁ': 'a', 'ㅂ': 'q', 'ㅃ': 'Q', 'ㅅ': 't', 'ㅆ': 'T', 'ㅇ': 'd', 'ㅈ': 'w', 'ㅉ': 'W', 'ㅊ': 'c', 'ㅋ': 'v', 'ㅌ': 'x', 'ㅍ': 'z', 'ㅎ': 'g',
|
|
21
|
+
'ㅏ': 'k', 'ㅐ': 'o', 'ㅑ': 'i', 'ㅒ': 'O', 'ㅓ': 'j', 'ㅔ': 'p', 'ㅕ': 'u', 'ㅖ': 'P', 'ㅗ': 'h', 'ㅘ': 'hk', 'ㅙ': 'ho', 'ㅚ': 'hl', 'ㅛ': 'y', 'ㅜ': 'n', 'ㅝ': 'nj', 'ㅞ': 'np', 'ㅟ': 'nl', 'ㅠ': 'b', 'ㅡ': 'm', 'ㅢ': 'ml', 'ㅣ': 'l',
|
|
22
|
+
'ㄳ': 'rt', 'ㄵ': 'sw', 'ㄶ': 'sg', 'ㄺ': 'fr', 'ㄻ': 'fa', 'ㄼ': 'fq', 'ㄽ': 'ft', 'ㄾ': 'fx', 'ㄿ': 'fz', 'ㅀ': 'fg', 'ㅄ': 'qt'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let result = "";
|
|
26
|
+
for (let i = 0; i < word.length; i++) {
|
|
27
|
+
const code = word.charCodeAt(i);
|
|
28
|
+
|
|
29
|
+
if (code >= 0xAC00 && code <= 0xD7A3) { // 완성형 한글
|
|
30
|
+
const base = code - 0xAC00;
|
|
31
|
+
const cho = Math.floor(base / 588);
|
|
32
|
+
const jung = Math.floor((base % 588) / 28);
|
|
33
|
+
const jong = base % 28;
|
|
34
|
+
result += CHOSUNG[cho] + JUNGSUNG[jung] + JONGSUNG[jong];
|
|
35
|
+
} else if (JAMO_MAP[word[i]]) { // 개별 자모
|
|
36
|
+
result += JAMO_MAP[word[i]];
|
|
37
|
+
} else { // 그 외
|
|
38
|
+
result += word[i];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 영타를 한타로 변환합니다.
|
|
46
|
+
|
|
47
|
+
* @param {string} englishWord - 변환할 영문 단어
|
|
48
|
+
* @example toKo("dkssud") // "안녕"
|
|
49
|
+
*/
|
|
50
|
+
function toKo(englishWord) {
|
|
51
|
+
if (typeof englishWord !== 'string' || englishWord.length === 0) return englishWord;
|
|
52
|
+
|
|
53
|
+
const enToKoMap = {
|
|
54
|
+
'r': 'ㄱ', 'R': 'ㄲ', 's': 'ㄴ', 'e': 'ㄷ', 'E': 'ㄸ', 'f': 'ㄹ', 'a': 'ㅁ', 'q': 'ㅂ', 'Q': 'ㅃ', 't': 'ㅅ', 'T': 'ㅆ', 'd': 'ㅇ', 'w': 'ㅈ', 'W': 'ㅉ', 'c': 'ㅊ', 'v': 'ㅋ', 'x': 'ㅌ', 'z': 'ㅍ', 'g': 'ㅎ',
|
|
55
|
+
'k': 'ㅏ', 'o': 'ㅐ', 'i': 'ㅑ', 'O': 'ㅒ', 'j': 'ㅓ', 'p': 'ㅔ', 'u': 'ㅕ', 'P': 'ㅖ', 'h': 'ㅗ', 'y': 'ㅛ', 'n': 'ㅜ', 'b': 'ㅠ', 'm': 'ㅡ', 'l': 'ㅣ'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const CHOSUNG = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
|
|
59
|
+
const JUNGSUNG = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'];
|
|
60
|
+
const JONGSUNG = ['', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
|
|
61
|
+
|
|
62
|
+
const jungComplex = { 'ㅗㅏ': 'ㅘ', 'ㅗㅐ': 'ㅙ', 'ㅗㅣ': 'ㅚ', 'ㅜㅓ': 'ㅝ', 'ㅜㅔ': 'ㅞ', 'ㅜㅣ': 'ㅟ', 'ㅡㅣ': 'ㅢ' };
|
|
63
|
+
const jongComplex = { 'ㄱㅅ': 'ㄳ', 'ㄴㅈ': 'ㄵ', 'ㄴㅎ': 'ㄶ', 'ㄹㄱ': 'ㄺ', 'ㄹㅁ': 'ㄻ', 'ㄹㅂ': 'ㄼ', 'ㄹㅅ': 'ㄽ', 'ㄹㅌ': 'ㄾ', 'ㄹㅍ': 'ㄿ', 'ㄹㅎ': 'ㅀ', 'ㅂㅅ': 'ㅄ' };
|
|
64
|
+
|
|
65
|
+
let result = '';
|
|
66
|
+
let cho = -1, jung = -1, jong = -1;
|
|
67
|
+
let lastJong = -1;
|
|
68
|
+
|
|
69
|
+
const combine = () => {
|
|
70
|
+
if (cho !== -1 && jung !== -1) {
|
|
71
|
+
result += String.fromCharCode(0xAC00 + (cho * 21 + jung) * 28 + (jong !== -1 ? jong : 0));
|
|
72
|
+
} else if (cho !== -1) {
|
|
73
|
+
result += CHOSUNG[cho];
|
|
74
|
+
} else if (jung !== -1) {
|
|
75
|
+
result += JUNGSUNG[jung];
|
|
76
|
+
}
|
|
77
|
+
cho = jung = jong = -1;
|
|
78
|
+
lastJong = -1;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < englishWord.length; i++) {
|
|
82
|
+
const key = englishWord[i];
|
|
83
|
+
const jamo = enToKoMap[key];
|
|
84
|
+
|
|
85
|
+
if (!jamo) {
|
|
86
|
+
combine();
|
|
87
|
+
result += key;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const isVowel = 'ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅛㅜㅠㅡㅣ'.includes(jamo);
|
|
92
|
+
|
|
93
|
+
if (isVowel) {
|
|
94
|
+
if (cho !== -1 && jung === -1) {
|
|
95
|
+
jung = JUNGSUNG.indexOf(jamo);
|
|
96
|
+
} else if (cho !== -1 && jung !== -1 && jong === -1) {
|
|
97
|
+
const complex = jungComplex[JUNGSUNG[jung] + jamo];
|
|
98
|
+
if (complex) {
|
|
99
|
+
jung = JUNGSUNG.indexOf(complex);
|
|
100
|
+
} else {
|
|
101
|
+
combine();
|
|
102
|
+
jung = JUNGSUNG.indexOf(jamo);
|
|
103
|
+
}
|
|
104
|
+
} else if (cho !== -1 && jung !== -1 && jong !== -1) {
|
|
105
|
+
if (lastJong !== -1) {
|
|
106
|
+
const complexChar = JONGSUNG[jong];
|
|
107
|
+
const splitJamo = Object.keys(jongComplex).find(k => jongComplex[k] === complexChar);
|
|
108
|
+
jong = JONGSUNG.indexOf(splitJamo[0]);
|
|
109
|
+
combine();
|
|
110
|
+
cho = CHOSUNG.indexOf(splitJamo[1]);
|
|
111
|
+
jung = JUNGSUNG.indexOf(jamo);
|
|
112
|
+
} else {
|
|
113
|
+
const prevJongChar = JONGSUNG[jong];
|
|
114
|
+
jong = 0;
|
|
115
|
+
combine();
|
|
116
|
+
cho = CHOSUNG.indexOf(prevJongChar);
|
|
117
|
+
jung = JUNGSUNG.indexOf(jamo);
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
if (jung !== -1) {
|
|
121
|
+
const complex = jungComplex[JUNGSUNG[jung] + jamo];
|
|
122
|
+
if (complex) jung = JUNGSUNG.indexOf(complex);
|
|
123
|
+
else { combine(); jung = JUNGSUNG.indexOf(jamo); }
|
|
124
|
+
} else {
|
|
125
|
+
jung = JUNGSUNG.indexOf(jamo);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
if (cho === -1) {
|
|
130
|
+
cho = CHOSUNG.indexOf(jamo);
|
|
131
|
+
} else if (jung === -1) {
|
|
132
|
+
combine();
|
|
133
|
+
cho = CHOSUNG.indexOf(jamo);
|
|
134
|
+
} else if (jong === -1) {
|
|
135
|
+
const jIdx = JONGSUNG.indexOf(jamo);
|
|
136
|
+
if (jIdx !== -1) {
|
|
137
|
+
jong = jIdx;
|
|
138
|
+
} else {
|
|
139
|
+
combine();
|
|
140
|
+
cho = CHOSUNG.indexOf(jamo);
|
|
141
|
+
}
|
|
142
|
+
} else if (lastJong === -1) {
|
|
143
|
+
const complex = jongComplex[JONGSUNG[jong] + jamo];
|
|
144
|
+
if (complex) {
|
|
145
|
+
lastJong = jong;
|
|
146
|
+
jong = JONGSUNG.indexOf(complex);
|
|
147
|
+
} else {
|
|
148
|
+
combine();
|
|
149
|
+
cho = CHOSUNG.indexOf(jamo);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
combine();
|
|
153
|
+
cho = CHOSUNG.indexOf(jamo);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
combine();
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
|
|
163
|
+
* 한글을 로마자로 변환합니다. `(문화관광부 고시 제2000-8호 규격)`
|
|
164
|
+
* @param {string} word - 변환할 한글 단어
|
|
165
|
+
* @example toRoman("나비야") // "nabiya"
|
|
166
|
+
*/
|
|
167
|
+
function toRoman(word) {
|
|
168
|
+
if (typeof word !== 'string' || word.length === 0) return word;
|
|
169
|
+
|
|
170
|
+
const CHOSUNG = ['g', 'kk', 'n', 'd', 'tt', 'r', 'm', 'b', 'pp', 's', 'ss', '', 'j', 'jj', 'ch', 'k', 't', 'p', 'h'];
|
|
171
|
+
const JUNGSUNG = ['a', 'ae', 'ya', 'yae', 'eo', 'e', 'yeo', 'ye', 'o', 'wa', 'wae', 'oe', 'yo', 'u', 'wo', 'we', 'wi', 'yu', 'eu', 'ui', 'i'];
|
|
172
|
+
const JONGSUNG = ['', 'k', 'k', 'k', 'n', 'n', 'n', 't', 'l', 'k', 'm', 'l', 'l', 't', 'p', 'l', 'm', 'p', 'p', 't', 't', 'ng', 't', 't', 'k', 't', 'p', 't'];
|
|
173
|
+
|
|
174
|
+
let result = "";
|
|
175
|
+
for (let i = 0; i < word.length; i++) {
|
|
176
|
+
const code = word.charCodeAt(i);
|
|
177
|
+
|
|
178
|
+
if (code >= 0xAC00 && code <= 0xD7A3) { // 완성형 한글
|
|
179
|
+
const base = code - 0xAC00;
|
|
180
|
+
const cho = Math.floor(base / 588);
|
|
181
|
+
const jung = Math.floor((base % 588) / 28);
|
|
182
|
+
const jong = base % 28;
|
|
183
|
+
|
|
184
|
+
result += CHOSUNG[cho] + JUNGSUNG[jung] + JONGSUNG[jong];
|
|
185
|
+
} else {
|
|
186
|
+
// 개별 자모(0x3130~0x318F, 0x1100~0x11FF 등)는 제외하고 나머지만 추가
|
|
187
|
+
const isJamo = (code >= 0x3130 && code <= 0x318F) || (code >= 0x1100 && code <= 0x11FF);
|
|
188
|
+
if (!isJamo) {
|
|
189
|
+
result += word[i];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = { toEn, toKo, toRoman };
|
package/package.json
ADDED