@kokr/text
Version:
A utility to help handle investigations in Korean sentences. / 한국어 문장의 조사 처리를 도와주는 유틸리티입니다. 은/는/이/가 등의 조사를 적절하게 처리합니다.
121 lines (120 loc) • 4.27 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.text = void 0;
const jongseong_js_1 = require("./jongseong.js");
const allowWithoutJongseong = new Set([0]);
const allowWithLieul = new Set([0, 8]);
const patterns = [
// 3
[allowWithoutJongseong, "이어요", "여요"],
[allowWithoutJongseong, "이에요", "예요"],
// 2
[allowWithoutJongseong, "이나", "나"],
[allowWithoutJongseong, "이다", "다"],
[allowWithoutJongseong, "이든", "든"],
[allowWithoutJongseong, "이라", "라"],
[allowWithoutJongseong, "이란", "란"],
[allowWithoutJongseong, "이랑", "랑"],
[allowWithLieul, "으로", "로"],
[allowWithoutJongseong, "이며", "며"],
[allowWithoutJongseong, "이셨", "셨"],
[allowWithoutJongseong, "이시", "시"],
[allowWithoutJongseong, "이야", "야"],
[allowWithoutJongseong, "이여", "여"],
[allowWithoutJongseong, "이었", "였"],
// 1
[allowWithoutJongseong, "은", "는"],
[allowWithoutJongseong, "이", "가"],
[allowWithoutJongseong, "을", "를"],
[allowWithoutJongseong, "과", "와"],
[allowWithoutJongseong, "아", "야"],
];
const detectPattern = patterns.map(([_, valid, invalid]) => {
return `(${valid}|${invalid})`;
}).join("|");
const RE_DETECT = new RegExp(`^(?:${detectPattern})`);
/**
* @example
* let userName = "완두";
*
* // 은/는
* text`${userName}는 코딩 합니다.` // 완두는 코딩 합니다.
* text`${userName}은 코딩 합니다.` // 완두는 코딩 합니다.
*
* // 이/가
* text`${userName}가 코딩 했습니다.` // 완두가 코딩 했습니다.
* text`${userName}이 코딩 했습니다.` // 완두가 코딩 했습니다.
*
* // 을/를
* text`${userName}을 가르쳤습니다.` // 완두를 가르쳤습니다.
* text`${userName}를 가르쳤습니다.` // 완두를 가르쳤습니다.
*
* // 와/과
* text`${userName}와 코딩을 했습니다.` // 완두와 코딩을 했습니다.
* text`${userName}과 코딩을 했습니다.` // 완두와 코딩을 했습니다.
*
* // 아/야
* text`${userName}아!` // 완두야!
* text`${userName}야!` // 완두야!
*
* // 이었/였
* text`${userName}이었어요.` // 완두였어요.
* text`${userName}였어요.` // 완두였어요.
*
* // 이에요/예요
* text`${userName}이에요.` // 완두예요.
* text`${userName}예요.` // 완두예요.
*
* // 이어요/여요
* text`${userName}이어요.` // 완두여요.
* text`${userName}여요.` // 완두여요.
*
* // 로/으로
* let place = "대구";
* text`${place}으로 갑시다.` // 대구로 갑시다.
* text`${place}로 갑시다.` // 대구로 갑시다.
*
* place = "부산";
* text`${place}으로 갑시다.` // 부산으로 갑시다.
* text`${place}로 갑시다.` // 부산으로 갑시다.
*
* place = "서울"; // ㄹ로 끝나는 경우
* text`${place}으로 갑시다.` // 서울로 갑시다.
* text`${place}로 갑시다.` // 서울로 갑시다.
*/
function text(strings, ...interpolation) {
return dedent(strings.reduce((acc, string, index) => {
string = string.normalize(); // normalize NFC
const word = String(interpolation[index - 1]).normalize(); // normalize NFC
const matched = string.match(RE_DETECT);
if (matched) {
const [allowed, valid, invalid] = patterns[[...matched].slice(1).findIndex((v) => v)];
const josa = allowed.has((0, jongseong_js_1.jongseong)(word)) ? invalid : valid;
const remain = string.slice(matched[0].length);
return `${acc}${word}${josa}${remain}`;
}
return `${acc}${word}${string}`;
}));
}
exports.text = text;
/** @internal */
function dedent(text) {
const lines = text.split("\n");
let min;
for (const line of lines) {
const m = line.match(/^(\s+)\S+/);
if (m) {
const indent = m[1].length;
if (!min) {
min = indent;
}
else {
min = Math.min(min, indent);
}
}
}
if (typeof min === "number") {
return lines.map((l) => l[0] === " " ? l.slice(min) : l).join("\n").trim();
}
return text.trim();
}