i18next : formatting 으로 을/를, 이/가 구분하기
레트로 게임을 하다보면 을/를, 이/가 와 같은 조사들을 괄호를 통해 (을)를, (이)가 식으로 함께 표기해둔것을 흔히 볼수 있다.
예상하기로는 외국어를 한글로 현지화하는 과정에서 {name}(이)가 승부를 걸어왔다! 식으로 문자열 탬플릿을 작성해두고 name 변수를 조합하는 방식을 차용하여
아래와 같은 괄호 표기가 나온것이 아닐까 한다.

탬플릿 문자열은 i18next 등으로 다국어를 지원할때에 흔히 사용하는 방식이다. 명사/대명사 등을 동적으로 변수를 통해 적용할때에 json 형식으로 문자열을 선언해두고, 사용부에서 매개변수에 값을 넘기는 식으로 동적으로 변경되어야하는 문장의 현지화를 지원하고는 한다.
{fieldName}(을)를 입력하세요.{a}(와)과 {b}(을)를 선택해주세요.{userName}(이)가 메시지를 보냈습니다.
i18next 형식으로 나타내보자면 아래와 같다.
const translation = {
wild_pokemon_appeared: "야생의 {pokemon}(이)가 튀어나왔다!"
}
t('wild_pokemon_appeared', {pokemon: '리자몽'}) // 야생의 리자몽(이)가 튀어나왔다!
을/를, 이/가 중 무엇을 붙힐지 계산하는 방법은 검색을 통해 쉽게 찾을 수 있는데, String.prototype.charCodeAt 을 통해 변수로 들어올 명사의 마지막 글자의 유니코드를 확인하는 방법을 사용할 수 있다. (참고)
/**
* 마지막 글자가 받침을 가지는지 확인
*/
function checkBatchimEnding(word: string): boolean {
const lastLetter = word[word.length - 1];
const uni = lastLetter.charCodeAt(0);
// 한글 범위 밖일 경우 false
if (uni < 44032 || uni > 55203) return false;
// 받침이 있을 경우 true
return (uni - 44032) % 28 !== 0;
}
checkBatchimEnding('리자몽') // true
checkBatchimEnding('피카츄') // false
다음으로 checkBatchimEnding 을 이용하여 i18next 로 변수에 따라 조사를 달아주는 포멧팅 함수를 정의해보자.
i18next 의 i18next.services.formatter.add 을 이용하여 정의할 수 있다. (참고)
i18next.services.formatter.add('이가', value => {
return value + (checkBatchimEnding(value) ? '이' : '가');
});
i18next.services.formatter.add('을를', value => {
return value + (checkBatchimEnding(value) ? '을' : '를');
});
함수명이 이가, 을를 인 받침이 있느냐 없느냐에 따라 조사를 붙혀주는 포멧 함수를 만들었다. 이 함수는 i18n 다국어 탬플릿내에 다음과 같이 사용할 수 있다. 함수명을 이례적으로 한글로 명명한 이유는 다국어 탬플릿만 보아도 자연스럽게 의미가 설명 될 수 있게하기 위함이다.
const translation = {
wild_pokemon_appeared: "야생의 {pokemon, 이가} 튀어나왔다!",
caught_a_pokemon: "신난다! {pokemon, 을를} 잡았다!"
}
t('wild_pokemon_appeared', {pokemon: '리자몽'}) // 야생의 리자몽이 튀어나왔다!
t('caught_a_pokemon', {pokemon: '피카츄'}) // 신난다! 피카츄를 잡았다!
i18next 는 위와 같은 formatting 이외에도 많은 유틸리티 기능을 제공한다. 이와 같이 값에 따라 동적으로 문자열을 변경해야할때에 찾아보면 썩 도움이 될만한 기능들이 많은것같다.
예시로 작성한 실행 가능한 코드를 github 에 올려두었다.
https://github.com/kjkandrea/i18next-josa/blob/main/src/main.ts