From 6160363abea439cb2e320704fdb6b7b503b28961 Mon Sep 17 00:00:00 2001 From: SeongMin Kim <86355699+Collection50@users.noreply.github.com> Date: Sat, 13 Jul 2024 09:01:35 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=9D=BC=EA=B4=80=EB=90=9C=20=ED=95=9C?= =?UTF-8?q?=EA=B8=80=20=ED=91=9C=EA=B8=B0=EB=B2=95=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#138)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 일관된 한글 표기법 적용 * fix: 기존의 함수를 유지한 채, deprecated 되도록 수정 * fix: main 브랜치와 merge를 위한 수정 * fix: conflict 수정 * fix: 문서에는 chosung prefix를 사용하도록 수정 * fix: ChoseongIncludesDemo 함수명 수정 * chore: cspell.json 으로 설정을 변경합니다. (#172) * fix: 일관된 한글 표기법 적용 * fix: 기존의 함수를 유지한 채, deprecated 되도록 수정 * docs: chosung~ 문서 작성 * fix: choseongIncludes의 인덱싱 처리 * Create fifty-wolves-melt.md * fix: import CI 오류 해결 --------- Co-authored-by: Minsoo Kim <57122180+minsoo-web@users.noreply.github.com> Co-authored-by: 박찬혁 --- .changeset/fifty-wolves-melt.md | 5 + README-en_us.md | 4 +- README.md | 4 +- .../pages/docs/api/choseongIncludes.en.mdx | 27 +++++ .../pages/docs/api/choseongIncludes.ko.mdx | 27 +++++ .../src/pages/docs/api/chosungIncludes.en.mdx | 2 +- .../src/pages/docs/api/chosungIncludes.ko.mdx | 2 +- docs/src/pages/docs/introduction.en.mdx | 10 +- docs/src/pages/docs/introduction.ko.mdx | 10 +- docs/src/pages/index.en.mdx | 7 +- docs/src/pages/index.ko.mdx | 4 +- src/_internal/hangul.spec.ts | 12 +- src/_internal/hangul.ts | 37 +++--- ...ludes.spec.ts => choseongIncludes.spec.ts} | 18 +-- src/choseongIncludes.ts | 29 +++++ src/chosungIncludes.ts | 29 ++--- src/combineHangulCharacter.ts | 8 +- src/constants.ts | 6 +- src/disassembleCompleteHangulCharacter.ts | 10 +- src/index.ts | 1 + src/utils.spec.ts | 62 +++++----- src/utils.ts | 107 ++++++++++++++++-- 22 files changed, 298 insertions(+), 123 deletions(-) create mode 100644 .changeset/fifty-wolves-melt.md create mode 100644 docs/src/pages/docs/api/choseongIncludes.en.mdx create mode 100644 docs/src/pages/docs/api/choseongIncludes.ko.mdx rename src/{chosungIncludes.spec.ts => choseongIncludes.spec.ts} (60%) create mode 100644 src/choseongIncludes.ts diff --git a/.changeset/fifty-wolves-melt.md b/.changeset/fifty-wolves-melt.md new file mode 100644 index 00000000..ae08c362 --- /dev/null +++ b/.changeset/fifty-wolves-melt.md @@ -0,0 +1,5 @@ +--- +"es-hangul": patch +--- + +fix: chosung => choseong으로 변환하는 규칙을 적용합니다 diff --git a/README-en_us.md b/README-en_us.md index caacd0a4..a629726c 100644 --- a/README-en_us.md +++ b/README-en_us.md @@ -11,12 +11,12 @@ es-hangul is a library that makes it easy to handle [Hangul](https://en.wikipedi You can easily implement tasks related to Hangul, such as initial consonant search and attaching particles(josas). ```tsx -import { chosungIncludes } from 'es-hangul'; +import { choseongIncludes } from 'es-hangul'; const searchWord = '라면'; const userInput = 'ㄹㅁ'; -const result = chosungIncludes(searchWord, userInput); // true +const result = choseongIncludes(searchWord, userInput); // true ``` ```tsx diff --git a/README.md b/README.md index 58c4b604..c91c9048 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ 초성 검색, 조사 붙이기와 같은 한글 작업을 간단히 할 수 있습니다. ```tsx -import { chosungIncludes } from 'es-hangul'; +import { choseongIncludes } from 'es-hangul'; const searchWord = '라면'; const userInput = 'ㄹㅁ'; -const result = chosungIncludes(searchWord, userInput); // true +const result = choseongIncludes(searchWord, userInput); // true ``` ```tsx diff --git a/docs/src/pages/docs/api/choseongIncludes.en.mdx b/docs/src/pages/docs/api/choseongIncludes.en.mdx new file mode 100644 index 00000000..3d17d25c --- /dev/null +++ b/docs/src/pages/docs/api/choseongIncludes.en.mdx @@ -0,0 +1,27 @@ +import { ChoseongIncludesDemo } from '@/components/demo/chosung-includes-demo'; + +# choseongIncludes + +Performs a search for matches in the initial consonants of a string. + +```typescript +function choseongIncludes( + // The string to be checked for matching initial consonants (e.g., '프론트엔드') + x: string, + // Initial consonant string (e.g., 'ㅍㄹㅌㅇㄷ') + y: string +): boolean; +``` + +```typescript +choseongIncludes('프론트엔드', 'ㅍㄹㅌ'); // true +choseongIncludes('00프론트엔드', 'ㅍㄹㅌ'); // true +choseongIncludes('프론트엔드', 'ㅍㅌ'); // false +choseongIncludes('프론트엔드', '푸롴트'); // false +``` + +## Demo + +
+ + diff --git a/docs/src/pages/docs/api/choseongIncludes.ko.mdx b/docs/src/pages/docs/api/choseongIncludes.ko.mdx new file mode 100644 index 00000000..35c2b702 --- /dev/null +++ b/docs/src/pages/docs/api/choseongIncludes.ko.mdx @@ -0,0 +1,27 @@ +import { ChoseongIncludesDemo } from '@/components/demo/choseong-includes-demo'; + +# choseongIncludes + +문자열의 초성 일치 검색을 수행합니다. + +```typescript +function choseongIncludes( + // 초성 일치하는지 검사할 문자열 (e.g. '프론트엔드') + x: string, + // 초성 문자열 (e.g. 'ㅍㄹㅌㅇㄷ') + y: string +): boolean; +``` + +```typescript +choseongIncludes('프론트엔드', 'ㅍㄹㅌ'); // true +choseongIncludes('00프론트엔드', 'ㅍㄹㅌ'); // true +choseongIncludes('프론트엔드', 'ㅍㅌ'); // false +choseongIncludes('프론트엔드', '푸롴트'); // false +``` + +## 사용해보기 + +
+ + diff --git a/docs/src/pages/docs/api/chosungIncludes.en.mdx b/docs/src/pages/docs/api/chosungIncludes.en.mdx index 691a7623..986deebb 100644 --- a/docs/src/pages/docs/api/chosungIncludes.en.mdx +++ b/docs/src/pages/docs/api/chosungIncludes.en.mdx @@ -1,6 +1,6 @@ import { ChosungIncludesDemo } from '@/components/demo/chosung-includes-demo'; -# chosungIncludes +# chosungIncludes (deprecated, Please use choseongIncludes) Performs a search for matches in the initial consonants of a string. diff --git a/docs/src/pages/docs/api/chosungIncludes.ko.mdx b/docs/src/pages/docs/api/chosungIncludes.ko.mdx index ce45f872..989fc5ca 100644 --- a/docs/src/pages/docs/api/chosungIncludes.ko.mdx +++ b/docs/src/pages/docs/api/chosungIncludes.ko.mdx @@ -1,6 +1,6 @@ import { ChosungIncludesDemo } from '@/components/demo/chosung-includes-demo'; -# chosungIncludes +# chosungIncludes (deprecated, choseongIncludes를 사용해주세요) 문자열의 초성 일치 검색을 수행합니다. diff --git a/docs/src/pages/docs/introduction.en.mdx b/docs/src/pages/docs/introduction.en.mdx index 8a0047de..06a7408f 100644 --- a/docs/src/pages/docs/introduction.en.mdx +++ b/docs/src/pages/docs/introduction.en.mdx @@ -31,19 +31,19 @@ Our library provides strong typing, allowing for easy detection of type errors d ### Full Support for Hangul-related Features -Our library provides a [modern API](./api/chosungIncludes) that can be conveniently used in various applications. +Our library provides a [modern API](./api/choseongIncludes) that can be conveniently used in various applications. -#### First Consonant Search ([chosungIncludes](./api/chosungIncludes)) +#### First Consonant Search ([choseongIncludes](./api/choseongIncludes)) It checks whether an initial consonant is included in a specific word. For example, you can easily find out if the word '라면' (ramyeon) contains the initial consonants 'ㄹㅁ'. -```tsx /chosungIncludes/ -import { chosungIncludes } from 'es-hangul'; +```tsx /choseongIncludes/ +import { choseongIncludes } from 'es-hangul'; const searchWord = '라면'; const userInput = 'ㄹㅁ'; -const result = chosungIncludes(searchWord, userInput); +const result = choseongIncludes(searchWord, userInput); console.log(result); // true ``` diff --git a/docs/src/pages/docs/introduction.ko.mdx b/docs/src/pages/docs/introduction.ko.mdx index 7d7881ac..73b27d6d 100644 --- a/docs/src/pages/docs/introduction.ko.mdx +++ b/docs/src/pages/docs/introduction.ko.mdx @@ -31,19 +31,19 @@ ECMAScript Modules를 이용하여 사용하는 함수만 애플리케이션에 ### 한글을 위한 모든 인터페이스를 제공하는 것을 목표합니다 -다양한 애플리케이션에서 편리하게 사용할 수 있는 [현대적인 API](./api/chosungIncludes)를 제공합니다. +다양한 애플리케이션에서 편리하게 사용할 수 있는 [현대적인 API](./api/choseongIncludes)를 제공합니다. -#### 초성 검색 ([chosungIncludes](./api/chosungIncludes)) +#### 초성 검색 ([choseongIncludes](./api/choseongIncludes)) 초성이 특정 단어에 포함되어 있는지 검사합니다. 예를 들어, '라면'이라는 단어가 'ㄹㅁ'으로 시작하는 초성을 포함하는지 쉽게 알 수 있습니다. -```tsx /chosungIncludes/ -import { chosungIncludes } from 'es-hangul'; +```tsx /choseongIncludes/ +import { choseongIncludes } from 'es-hangul'; const searchWord = '라면'; const userInput = 'ㄹㅁ'; -const result = chosungIncludes(searchWord, userInput); +const result = choseongIncludes(searchWord, userInput); console.log(result); // true ``` diff --git a/docs/src/pages/index.en.mdx b/docs/src/pages/index.en.mdx index 7754a379..e4d60454 100644 --- a/docs/src/pages/index.en.mdx +++ b/docs/src/pages/index.en.mdx @@ -27,18 +27,19 @@ import { Callout, useTheme, Steps } from 'nextra-theme-docs';

A modern JavaScript Hangul library

- es-hangul is a small JavaScript library that helps you conveniently handle Hangul. It provides a convenient and clean API for actions such as searching for initial consonants and attaching particles. + es-hangul is a small JavaScript library that helps you conveniently handle Hangul. It provides a convenient and + clean API for actions such as searching for initial consonants and attaching particles. ```tsx -import { chosungIncludes } from 'es-hangul'; +import { choseongIncludes } from 'es-hangul'; const searchWord = '라면'; const userInput = 'ㄹㅁ'; -const result = chosungIncludes(searchWord, userInput); +const result = choseongIncludes(searchWord, userInput); console.log(result); // true ``` diff --git a/docs/src/pages/index.ko.mdx b/docs/src/pages/index.ko.mdx index 7807a6e8..a86d1f68 100644 --- a/docs/src/pages/index.ko.mdx +++ b/docs/src/pages/index.ko.mdx @@ -34,12 +34,12 @@ import { Callout, useTheme, Steps } from 'nextra-theme-docs'; ```tsx -import { chosungIncludes } from 'es-hangul'; +import { choseongIncludes } from 'es-hangul'; const searchWord = '라면'; const userInput = 'ㄹㅁ'; -const result = chosungIncludes(searchWord, userInput); +const result = choseongIncludes(searchWord, userInput); console.log(result); // true ``` diff --git a/src/_internal/hangul.spec.ts b/src/_internal/hangul.spec.ts index 150360f7..684f9272 100644 --- a/src/_internal/hangul.spec.ts +++ b/src/_internal/hangul.spec.ts @@ -134,11 +134,15 @@ describe('binaryAssembleHangulCharacters', () => { }); it('다음 문자가 한글 문자 한 글자가 아니라면 Invalid next character 에러를 발생시킨다.', () => { - expect(() => binaryAssembleHangulCharacters('ㄱ', 'a')).toThrowError( - 'Invalid next character: a. Next character must be one of the chosung, jungsung, or jongsung.' + assert.throws( + () => binaryAssembleHangulCharacters('ㄱ', 'a'), + Error, + 'Invalid next character: a. Next character must be one of the choseong, jungseong, or jongseong.' ); - expect(() => binaryAssembleHangulCharacters('ㄱ', 'ㅡㅏ')).toThrowError( - 'Invalid next character: ㅡㅏ. Next character must be one of the chosung, jungsung, or jongsung.' + assert.throws( + () => binaryAssembleHangulCharacters('ㄱ', 'ㅡㅏ'), + Error, + 'Invalid next character: ㅡㅏ. Next character must be one of the choseong, jungseong, or jongseong.' ); }); }); diff --git a/src/_internal/hangul.ts b/src/_internal/hangul.ts index dcb5c757..882af421 100644 --- a/src/_internal/hangul.ts +++ b/src/_internal/hangul.ts @@ -2,7 +2,7 @@ import assert, { excludeLastElement, isBlank, joinString } from '.'; import { combineHangulCharacter, combineVowels, curriedCombineHangulCharacter } from '../combineHangulCharacter'; import { disassembleHangulToGroups } from '../disassemble'; import { removeLastHangulCharacter } from '../removeLastHangulCharacter'; -import { canBeChosung, canBeJongsung, canBeJungsung, hasSingleBatchim } from '../utils'; +import { canBeChoseong, canBeJongseong, canBeJungseong, hasSingleBatchim } from '../utils'; export function isHangulCharacter(character: string) { return /^[가-힣]$/.test(character); @@ -57,12 +57,12 @@ export function safeParseHangul(actual: unknown): SafeParseSuccess | SafeParseEr * ``` */ export function binaryAssembleHangulAlphabets(source: string, nextCharacter: string) { - if (canBeJungsung(`${source}${nextCharacter}`)) { + if (canBeJungseong(`${source}${nextCharacter}`)) { return combineVowels(source, nextCharacter); } - const isConsonantSource = canBeJungsung(source) === false; - if (isConsonantSource && canBeJungsung(nextCharacter)) { + const isConsonantSource = canBeJungseong(source) === false; + if (isConsonantSource && canBeJungseong(nextCharacter)) { return combineHangulCharacter(source, nextCharacter); } @@ -106,7 +106,7 @@ export function binaryAssembleHangulCharacters(source: string, nextCharacter: st ); assert( isHangulAlphabet(nextCharacter), - `Invalid next character: ${nextCharacter}. Next character must be one of the chosung, jungsung, or jongsung.` + `Invalid next character: ${nextCharacter}. Next character must be one of the choseong, jungseong, or jongseong.` ); const sourceJamos = disassembleHangulToGroups(source)[0]; @@ -120,35 +120,36 @@ export function binaryAssembleHangulCharacters(source: string, nextCharacter: st const [restJamos, lastJamo] = excludeLastElement(sourceJamos); const secondaryLastJamo = excludeLastElement(restJamos)[1]; - const needLinking = canBeChosung(lastJamo) && canBeJungsung(nextCharacter); + const needLinking = canBeChoseong(lastJamo) && canBeJungseong(nextCharacter); if (needLinking) { return linkHangulCharacters(source, nextCharacter); } const fixConsonant = curriedCombineHangulCharacter; - const combineJungsung = fixConsonant(restJamos[0]); + const combineJungseong = fixConsonant(restJamos[0]); - if (canBeJungsung(`${lastJamo}${nextCharacter}`)) { - return combineJungsung(`${lastJamo}${nextCharacter}`)(); + if (canBeJungseong(`${lastJamo}${nextCharacter}`)) { + return combineJungseong(`${lastJamo}${nextCharacter}`)(); } - if (canBeJungsung(`${secondaryLastJamo}${lastJamo}`) && canBeJongsung(nextCharacter)) { - return combineJungsung(`${secondaryLastJamo}${lastJamo}`)(nextCharacter); + if (canBeJungseong(`${secondaryLastJamo}${lastJamo}`) && canBeJongseong(nextCharacter)) { + return combineJungseong(`${secondaryLastJamo}${lastJamo}`)(nextCharacter); } - if (canBeJungsung(lastJamo) && canBeJongsung(nextCharacter)) { - return combineJungsung(lastJamo)(nextCharacter); + if (canBeJungseong(lastJamo) && canBeJongseong(nextCharacter)) { + return combineJungseong(lastJamo)(nextCharacter); } - const fixVowel = combineJungsung; - const combineJongsung = fixVowel( - canBeJungsung(`${restJamos[1]}${restJamos[2]}`) ? `${restJamos[1]}${restJamos[2]}` : restJamos[1] + const fixVowel = combineJungseong; + + const combineJongseong = fixVowel( + canBeJungseong(`${restJamos[1]}${restJamos[2]}`) ? `${restJamos[1]}${restJamos[2]}` : restJamos[1] ); const lastConsonant = lastJamo; - if (hasSingleBatchim(source) && canBeJongsung(`${lastConsonant}${nextCharacter}`)) { - return combineJongsung(`${lastConsonant}${nextCharacter}`); + if (hasSingleBatchim(source) && canBeJongseong(`${lastConsonant}${nextCharacter}`)) { + return combineJongseong(`${lastConsonant}${nextCharacter}`); } return joinString(source, nextCharacter); diff --git a/src/chosungIncludes.spec.ts b/src/choseongIncludes.spec.ts similarity index 60% rename from src/chosungIncludes.spec.ts rename to src/choseongIncludes.spec.ts index 7033278b..245b891f 100644 --- a/src/chosungIncludes.spec.ts +++ b/src/choseongIncludes.spec.ts @@ -1,35 +1,35 @@ -import { chosungIncludes } from './chosungIncludes'; +import { choseongIncludes } from './choseongIncludes'; -describe('chosungIncludes', () => { +describe('choseongIncludes', () => { describe('초성이 포함되어있다고 판단되는 경우', () => { it('"ㅍㄹㅌ" 문자열로 "프론트엔드"를 검색하면 true를 반환한다.', () => { - expect(chosungIncludes('프론트엔드', 'ㅍㄹㅌ')).toBe(true); + expect(choseongIncludes('프론트엔드', 'ㅍㄹㅌ')).toBe(true); }); it('"ㅍㄹㅌ" 문자열로 "00프론트엔드"를 검색하면 true를 반환한다.', () => { - expect(chosungIncludes('00프론트엔드', 'ㅍㄹㅌ')).toBe(true); + expect(choseongIncludes('00프론트엔드', 'ㅍㄹㅌ')).toBe(true); }); it('"ㅍㄹㅌㅇㄷㄱㅂㅈ" 문자열로 "프론트엔드 개발자"를 검색하면 true를 반환한다.', () => { - expect(chosungIncludes('프론트엔드 개발자', 'ㅍㄹㅌㅇㄷㄱㅂㅈ')).toBe(true); + expect(choseongIncludes('프론트엔드 개발자', 'ㅍㄹㅌㅇㄷㄱㅂㅈ')).toBe(true); }); it('"ㅍㄹㅌㅇㄷ ㄱㅂㅈ" 문자열로 "프론트엔드 개발자"를 검색하면 true를 반환한다.', () => { - expect(chosungIncludes('프론트엔드 개발자', 'ㅍㄹㅌㅇㄷ ㄱㅂㅈ')).toBe(true); + expect(choseongIncludes('프론트엔드 개발자', 'ㅍㄹㅌㅇㄷ ㄱㅂㅈ')).toBe(true); }); }); describe('초성이 포함되어있다고 판단되지 않는 경우', () => { it('"ㅍㅌ" 문자열로 "프론트엔드"를 검색하면 false를 반환한다.', () => { - expect(chosungIncludes('프론트엔드', 'ㅍㅌ')).toBe(false); + expect(choseongIncludes('프론트엔드', 'ㅍㅌ')).toBe(false); }); it('빈 문자열로 "프론트엔드 개발자"를 검색하면 false를 반환한다.', () => { - expect(chosungIncludes('프론트엔드 개발자', ' ')).toBe(false); + expect(choseongIncludes('프론트엔드 개발자', ' ')).toBe(false); }); it('"푸롴트" 문자열로 "프론트엔드"를 검색하면 초성으로만 구성되어 있지 않아 false를 반환한다.', () => { - expect(chosungIncludes('프론트엔드', '푸롴트')).toBe(false); + expect(choseongIncludes('프론트엔드', '푸롴트')).toBe(false); }); }); }); diff --git a/src/choseongIncludes.ts b/src/choseongIncludes.ts new file mode 100644 index 00000000..3dee936b --- /dev/null +++ b/src/choseongIncludes.ts @@ -0,0 +1,29 @@ +import { disassembleHangulToGroups } from './disassemble'; +import { canBeChoseong, getChoseong } from './utils'; + +export function choseongIncludes(x: string, y: string) { + const trimmedY = y.replace(/\s/g, ''); + + if (!isOnlyChoseong(trimmedY)) { + return false; + } + + const choseongX = getChoseong(x).replace(/\s/g, ''); + const choseongY = trimmedY; + + return choseongX.includes(choseongY); +} + +/* + * @description 문자열이 한글초성으로만 주어진 경우 + */ +export function isOnlyChoseong(str: string) { + const groups = disassembleHangulToGroups(str); + if (groups.length === 0) { + return false; + } + + return groups.every(disassembled => { + return disassembled.length === 1 && canBeChoseong(disassembled[0]); + }); +} diff --git a/src/chosungIncludes.ts b/src/chosungIncludes.ts index 3617a0c0..3c8f0ec1 100644 --- a/src/chosungIncludes.ts +++ b/src/chosungIncludes.ts @@ -1,29 +1,18 @@ -import { disassembleHangulToGroups } from './disassemble'; -import { canBeChosung, getChosung } from './utils'; +import { isOnlyChoseong } from './choseongIncludes'; +import { getChoseong } from './utils'; +/** + * @deprecated choseongIncludes를 사용해 주세요. + */ export function chosungIncludes(x: string, y: string) { const trimmedY = y.replace(/\s+/g, ''); - if (!isOnlyChosung(trimmedY)) { + if (!isOnlyChoseong(trimmedY)) { return false; } - const chosungX = getChosung(x).replace(/\s+/g, ''); - const chosungY = trimmedY; - - return chosungX.includes(chosungY); -} - -/* - * @description 문자열이 한글초성으로만 주어진 경우 - */ -function isOnlyChosung(str: string) { - const groups = disassembleHangulToGroups(str); - if (groups.length === 0) { - return false; - } + const choseongX = getChoseong(x).replace(/\s/g, ''); + const choseongY = trimmedY; - return groups.every(disassembled => { - return disassembled.length === 1 && canBeChosung(disassembled[0]); - }); + return choseongX.includes(choseongY); } diff --git a/src/combineHangulCharacter.ts b/src/combineHangulCharacter.ts index 5e8ddc02..7ace284c 100644 --- a/src/combineHangulCharacter.ts +++ b/src/combineHangulCharacter.ts @@ -5,7 +5,7 @@ import { HANGUL_CHARACTERS_BY_LAST_INDEX, HANGUL_CHARACTERS_BY_MIDDLE_INDEX, } from './constants'; -import { canBeChosung, canBeJongsung, canBeJungsung } from './utils'; +import { canBeChoseong, canBeJongseong, canBeJungseong } from './utils'; /** * @name combineHangulCharacter @@ -27,9 +27,9 @@ import { canBeChosung, canBeJongsung, canBeJungsung } from './utils'; */ export function combineHangulCharacter(firstCharacter: string, middleCharacter: string, lastCharacter = '') { if ( - canBeChosung(firstCharacter) === false || - canBeJungsung(middleCharacter) === false || - canBeJongsung(lastCharacter) === false + canBeChoseong(firstCharacter) === false || + canBeJungseong(middleCharacter) === false || + canBeJongseong(lastCharacter) === false ) { throw new Error(`Invalid hangul Characters: ${firstCharacter}, ${middleCharacter}, ${lastCharacter}`); } diff --git a/src/constants.ts b/src/constants.ts index 6579e3cc..3f95c04e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,7 @@ export const COMPLETE_HANGUL_START_CHARCODE = '가'.charCodeAt(0); export const COMPLETE_HANGUL_END_CHARCODE = '힣'.charCodeAt(0); -export const NUMBER_OF_JONGSUNG = 28; -export const NUMBER_OF_JUNGSUNG = 21; +export const NUMBER_OF_JONGSEONG = 28; +export const NUMBER_OF_JUNGSEONG = 21; const _JASO_HANGUL_NFD = [...'각힣'.normalize('NFD')].map(char => char.charCodeAt(0)); // NFC 에 정의되지 않은 문자는 포함하지 않음 export const JASO_HANGUL_NFD = { @@ -11,7 +11,7 @@ export const JASO_HANGUL_NFD = { END_CHOSEONG: _JASO_HANGUL_NFD[3], // ㅎ END_JUNGSEONG: _JASO_HANGUL_NFD[4], // ㅣ END_JONGSEONG: _JASO_HANGUL_NFD[5], // ㅎ -} +}; /** * ㄱ -> 'ㄱ' diff --git a/src/disassembleCompleteHangulCharacter.ts b/src/disassembleCompleteHangulCharacter.ts index 0e255c83..252eee2d 100644 --- a/src/disassembleCompleteHangulCharacter.ts +++ b/src/disassembleCompleteHangulCharacter.ts @@ -4,8 +4,8 @@ import { HANGUL_CHARACTERS_BY_FIRST_INDEX, HANGUL_CHARACTERS_BY_LAST_INDEX, HANGUL_CHARACTERS_BY_MIDDLE_INDEX, - NUMBER_OF_JONGSUNG, - NUMBER_OF_JUNGSUNG, + NUMBER_OF_JONGSEONG, + NUMBER_OF_JUNGSEONG, } from './constants'; interface ReturnTypeDisassembleCompleteHangulCharacter { @@ -41,9 +41,9 @@ export function disassembleCompleteHangulCharacter( const hangulCode = charCode - COMPLETE_HANGUL_START_CHARCODE; - const lastIndex = hangulCode % NUMBER_OF_JONGSUNG; - const middleIndex = ((hangulCode - lastIndex) / NUMBER_OF_JONGSUNG) % NUMBER_OF_JUNGSUNG; - const firstIndex = Math.floor((hangulCode - lastIndex) / NUMBER_OF_JONGSUNG / NUMBER_OF_JUNGSUNG); + const lastIndex = hangulCode % NUMBER_OF_JONGSEONG; + const middleIndex = ((hangulCode - lastIndex) / NUMBER_OF_JONGSEONG) % NUMBER_OF_JUNGSEONG; + const firstIndex = Math.floor((hangulCode - lastIndex) / NUMBER_OF_JONGSEONG / NUMBER_OF_JUNGSEONG); return { first: HANGUL_CHARACTERS_BY_FIRST_INDEX[firstIndex], diff --git a/src/index.ts b/src/index.ts index 5e839a05..0cb8f2e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ export { assembleHangul } from './assemble'; export { chosungIncludes } from './chosungIncludes'; +export { choseongIncludes } from './choseongIncludes'; export { combineHangulCharacter, combineVowels, curriedCombineHangulCharacter } from './combineHangulCharacter'; export { convertQwertyToHangul, convertQwertyToHangulAlphabet } from './convertQwertyToHangulAlphabet'; export { disassembleHangul, disassembleHangulToGroups } from './disassemble'; diff --git a/src/utils.spec.ts b/src/utils.spec.ts index 3d08e2b7..d2660a9f 100644 --- a/src/utils.spec.ts +++ b/src/utils.spec.ts @@ -1,8 +1,8 @@ import { - canBeChosung, - canBeJongsung, - canBeJungsung, - getChosung, + canBeChoseong, + canBeJongseong, + canBeJungseong, + getChoseong, getFirstConsonants, hasBatchim, hasProperty, @@ -84,22 +84,22 @@ describe('hasSingleBatchim', () => { }); }); -describe('getChosung', () => { +describe('getChoseong', () => { it('"사과" 단어에서 초성 "ㅅㄱ"을 추출한다.', () => { - expect(getChosung('사과')).toBe('ㅅㄱ'); + expect(getChoseong('사과')).toBe('ㅅㄱ'); }); it('"프론트엔드" 단어에서 초성 "ㅍㄹㅌㅇㄷ"을 추출한다.', () => { - expect(getChosung('프론트엔드')).toBe('ㅍㄹㅌㅇㄷ'); + expect(getChoseong('프론트엔드')).toBe('ㅍㄹㅌㅇㄷ'); }); it('"ㄴㅈ" 문자에서 초성 "ㄴㅈ"을 추출한다.', () => { - expect(getChosung('ㄴㅈ')).toBe('ㄴㅈ'); + expect(getChoseong('ㄴㅈ')).toBe('ㄴㅈ'); }); it('"리액트" 단어에서 초성 "ㄹㅇㅌ"을 추출한다.', () => { - expect(getChosung('리액트')).toBe('ㄹㅇㅌ'); + expect(getChoseong('리액트')).toBe('ㄹㅇㅌ'); }); it('"띄어 쓰기" 문장에서 초성 "ㄸㅇ ㅆㄱ"을 추출한다.', () => { - expect(getChosung('띄어 쓰기')).toBe('ㄸㅇ ㅆㄱ'); + expect(getChoseong('띄어 쓰기')).toBe('ㄸㅇ ㅆㄱ'); }); }); @@ -174,80 +174,80 @@ describe('hasProperty', () => { }); }); -describe('canBeChosung', () => { +describe('canBeChoseong', () => { describe('초성이 될 수 있다고 판단되는 경우', () => { it('ㄱ', () => { - expect(canBeChosung('ㄱ')).toBe(true); + expect(canBeChoseong('ㄱ')).toBe(true); }); it('ㅃ', () => { - expect(canBeChosung('ㅃ')).toBe(true); + expect(canBeChoseong('ㅃ')).toBe(true); }); }); describe('초성이 될 수 없다고 판단되는 경우', () => { it('ㅏ', () => { - expect(canBeChosung('ㅏ')).toBe(false); + expect(canBeChoseong('ㅏ')).toBe(false); }); it('ㅘ', () => { - expect(canBeChosung('ㅘ')).toBe(false); + expect(canBeChoseong('ㅘ')).toBe(false); }); it('ㄱㅅ', () => { - expect(canBeChosung('ㄱㅅ')).toBe(false); + expect(canBeChoseong('ㄱㅅ')).toBe(false); }); it('가', () => { - expect(canBeChosung('가')).toBe(false); + expect(canBeChoseong('가')).toBe(false); }); }); }); -describe('canBeJungsung', () => { +describe('canBeJungseong', () => { describe('중성이 될 수 있다고 판단되는 경우', () => { it('ㅗㅏ', () => { - expect(canBeJungsung('ㅗㅏ')).toBe(true); + expect(canBeJungseong('ㅗㅏ')).toBe(true); }); it('ㅏ', () => { - expect(canBeJungsung('ㅏ')).toBe(true); + expect(canBeJungseong('ㅏ')).toBe(true); }); }); describe('중성이 될 수 없다고 판단되는 경우', () => { it('ㄱ', () => { - expect(canBeJungsung('ㄱ')).toBe(false); + expect(canBeJungseong('ㄱ')).toBe(false); }); it('ㄱㅅ', () => { - expect(canBeJungsung('ㄱㅅ')).toBe(false); + expect(canBeJungseong('ㄱㅅ')).toBe(false); }); it('가', () => { - expect(canBeJungsung('가')).toBe(false); + expect(canBeJungseong('가')).toBe(false); }); }); }); -describe('canBeJongsung', () => { +describe('canBeJongseong', () => { describe('종성이 될 수 있다고 판단되는 경우', () => { it('ㄱ', () => { - expect(canBeJongsung('ㄱ')).toBe(true); + expect(canBeJongseong('ㄱ')).toBe(true); }); it('ㄱㅅ', () => { - expect(canBeJongsung('ㄱㅅ')).toBe(true); + expect(canBeJongseong('ㄱㅅ')).toBe(true); }); it('ㅂㅅ', () => { - expect(canBeJongsung('ㅂㅅ')).toBe(true); + expect(canBeJongseong('ㅂㅅ')).toBe(true); }); }); describe('종성이 될 수 없다고 판단되는 경우', () => { it('ㅎㄹ', () => { - expect(canBeJongsung('ㅎㄹ')).toBe(false); + expect(canBeJongseong('ㅎㄹ')).toBe(false); }); it('ㅗㅏ', () => { - expect(canBeJongsung('ㅗㅏ')).toBe(false); + expect(canBeJongseong('ㅗㅏ')).toBe(false); }); it('ㅏ', () => { - expect(canBeJongsung('ㅏ')).toBe(false); + expect(canBeJongseong('ㅏ')).toBe(false); }); it('가', () => { - expect(canBeJongsung('ㅏ')).toBe(false); + expect(canBeJongseong('ㅏ')).toBe(false); }); }); }); diff --git a/src/utils.ts b/src/utils.ts index e17b58c6..2a5bd3c8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -4,8 +4,8 @@ import { HANGUL_CHARACTERS_BY_FIRST_INDEX, HANGUL_CHARACTERS_BY_LAST_INDEX, HANGUL_CHARACTERS_BY_MIDDLE_INDEX, - NUMBER_OF_JONGSUNG, JASO_HANGUL_NFD, + NUMBER_OF_JONGSEONG, } from './constants'; import { disassembleHangulToGroups } from './disassemble'; @@ -45,7 +45,7 @@ export function hasBatchim(str: string) { return false; } - return (charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSUNG > 0; + return (charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSEONG > 0; } /** @@ -76,24 +76,25 @@ export function hasSingleBatchim(str: string) { return false; } - const batchimCode = (charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSUNG; + const batchimCode = (charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSEONG; return HANGUL_CHARACTERS_BY_LAST_INDEX[batchimCode].length === 1; } /** * @name getChosung + * @deprecated getChoseong을 사용해 주세요. * @description * 단어에서 초성을 추출합니다. (예: `사과` -> `'ㅅㄱ'`) * ```typescript - * getChosung( + * getChoseong( * // 초성을 추출할 단어 * word: string * ): string * ``` * @example - * getChosung('사과') // 'ㅅㄱ' - * getChosung('리액트') // 'ㄹㅇㅌ' - * getChosung('띄어 쓰기') // 'ㄸㅇ ㅆㄱ' + * getChoseong('사과') // 'ㅅㄱ' + * getChoseong('리액트') // 'ㄹㅇㅌ' + * getChoseong('띄어 쓰기') // 'ㄸㅇ ㅆㄱ' */ export function getChosung(word: string) { return word @@ -102,9 +103,31 @@ export function getChosung(word: string) { .replace(CHOOSE_NFD_CHOSEONG_REGEX, $0 => HANGUL_CHARACTERS_BY_FIRST_INDEX[$0.charCodeAt(0) - 0x1100]); // NFD to NFC } +/** + * @name getChoseong + * @description + * 단어에서 초성을 추출합니다. (예: `사과` -> `'ㅅㄱ'`) + * ```typescript + * getChoseong( + * // 초성을 추출할 단어 + * word: string + * ): string + * ``` + * @example + * getChoseong('사과') // 'ㅅㄱ' + * getChoseong('리액트') // 'ㄹㅇㅌ' + * getChoseong('띄어 쓰기') // 'ㄸㅇ ㅆㄱ' + */ +export function getChoseong(word: string) { + return word + .normalize('NFD') + .replace(EXTRACT_CHOSEONG_REGEX, '') // NFD ㄱ-ㅎ, NFC ㄱ-ㅎ 외 문자 삭제 + .replace(CHOOSE_NFD_CHOSEONG_REGEX, $0 => HANGUL_CHARACTERS_BY_FIRST_INDEX[$0.charCodeAt(0) - 0x1100]); // NFD to NFC +} + /** * @name getFirstConsonants - * @deprecated getChosung을 사용해 주세요. + * @deprecated getChoseong을 사용해 주세요. * @description * 단어에서 초성을 추출합니다. (예: `사과` -> `'ㅅㄱ'`) * ```typescript @@ -126,6 +149,7 @@ export function getFirstConsonants(word: string) { /** * @name canBeChosung + * @deprecated canBeChoseong을 사용해 주세요. * @description * 인자로 받은 문자가 초성으로 위치할 수 있는 문자인지 검사합니다. * ```typescript @@ -145,8 +169,30 @@ export function canBeChosung(character: string): character is (typeof HANGUL_CHA return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_FIRST_INDEX, character); } +/** + * @name canBeChoseong + * @description + * 인자로 받은 문자가 초성으로 위치할 수 있는 문자인지 검사합니다. + * ```typescript + * canBeChoseong( + * // 대상 문자 + * character: string + * ): boolean + * ``` + * @example + * canBeChoseong('ㄱ') // true + * canBeChoseong('ㅃ') // true + * canBeChoseong('ㄱㅅ') // false + * canBeChoseong('ㅏ') // false + * canBeChoseong('가') // false + */ +export function canBeChoseong(character: string): character is (typeof HANGUL_CHARACTERS_BY_FIRST_INDEX)[number] { + return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_FIRST_INDEX, character); +} + /** * @name canBeJungsung + * @deprecated canBeJungseong을 사용해 주세요. * @description * 인자로 받은 문자가 중성으로 위치할 수 있는 문자인지 검사합니다. * ```typescript @@ -167,8 +213,31 @@ export function canBeJungsung(character: string): character is (typeof HANGUL_CH return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_MIDDLE_INDEX, character); } +/** + * @name canBeJungseong + * @description + * 인자로 받은 문자가 중성으로 위치할 수 있는 문자인지 검사합니다. + * ```typescript + * canBeJungseong( + * // 대상 문자 + * character: string + * ): boolean + * ``` + * @example + * canBeJungseong('ㅏ') // true + * canBeJungseong('ㅗㅏ') // true + * canBeJungseong('ㅏㅗ') // false + * canBeJungseong('ㄱ') // false + * canBeJungseong('ㄱㅅ') // false + * canBeJungseong('가') // false + */ +export function canBeJungseong(character: string): character is (typeof HANGUL_CHARACTERS_BY_MIDDLE_INDEX)[number] { + return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_MIDDLE_INDEX, character); +} + /** * @name canBeJongsung + * @deprecated canBeJongseong을 사용해 주세요. * @description * 인자로 받은 문자가 종성으로 위치할 수 있는 문자인지 검사합니다. * ```typescript @@ -189,6 +258,28 @@ export function canBeJongsung(character: string): character is (typeof HANGUL_CH return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_LAST_INDEX, character); } +/** + * @name canBeJongseong + * @description + * 인자로 받은 문자가 종성으로 위치할 수 있는 문자인지 검사합니다. + * ```typescript + * canBeJongseong( + * // 대상 문자 + * character: string + * ): boolean + * ``` + * @example + * canBeJongseong('ㄱ') // true + * canBeJongseong('ㄱㅅ') // true + * canBeJongseong('ㅎㄹ') // false + * canBeJongseong('가') // false + * canBeJongseong('ㅏ') // false + * canBeJongseong('ㅗㅏ') // false + */ +export function canBeJongseong(character: string): character is (typeof HANGUL_CHARACTERS_BY_LAST_INDEX)[number] { + return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_LAST_INDEX, character); +} + export function hasValueInReadOnlyStringList(list: readonly T[], value: string): value is T { return list.some(item => item === value); }