미국 발음 사전은 고양이(cat)가 K AE1 T처럼 들린다고 말합니다. 국제음성기호(IPA)는 같은 소리를 kæt로 적습니다. ASCII 전용의 또 다른 체계는 이를 k"{t로 적습니다. 세 가지 모두 정확히 같은 두 음소 — “k” 소리 다음에 짧은 “a” 다음에 “t” — 를 기술합니다. 소리에 대해서는 아무것도 바뀌지 않았습니다. 그것을 적는 데 쓰인 알파벳만 바뀌었을 뿐입니다.
이는 둘 이상의 출처에서 나온 발음 데이터를 결합하는 사람이라면 누구에게나 끊임없이 일어나는 일입니다. 미국 사전으로 만든 음성 데이터셋은 하나의 표기법을 사용합니다. 유럽의 사전은 다른 표기법을 사용합니다. 텍스트 음성 변환 엔진은 세 번째 표기법을 기대합니다. 그 데이터를 병합하거나, 검색하거나, 비교하기 전에, 하나의 음성 알파벳에서 다른 알파벳으로 번역해야 합니다 — 인간 언어 사이에서 번역가가 하는 것과 같은 일이지만, 여기서 “언어”는 단어를 적는 방식이 아니라 소리를 적는 방식입니다.
scriptconv는 이 번역을 수행하는 작은 Python 라이브러리이며, 한 단계 위의 관련 작업도 함께 합니다: 다른 무엇을 하기 전에 텍스트 조각이 어떤 문자 체계로 되어 있는지 파악하는 것입니다. 이는 언어학에 대한 견해가 없습니다 — 단어가 어떻게 발음되는지 추측하지 않습니다. 이미 알려진 소리를 나타내는 기호를 한 표기법에서 다른 표기법으로 옮길 뿐이며, 문자 자체로부터 문자 체계를 식별할 뿐입니다.
몇 가지 용어를 명확히 정의하기
- 문자 체계(Script): 로마자, 키릴 문자, 한글처럼 실제 문자 집합인 쓰기 체계. 언어와는 다릅니다: 영어, 프랑스어, 베트남어는 모두 로마자를 사용하며, 세르비아어는 키릴 문자나 로마자 어느 쪽으로도 쓸 수 있습니다.
- 정서법(Orthography): 특정 언어를 특정 문자 체계로 적기 위한 관례적 철자 규칙 — 대소문자, 강세 부호, 띄어쓰기.
- 음소(Phoneme): 한 언어에서 구별되는 소리 단위, 예를 들어 “cat”의 “k” 소리.
- IPA(국제음성기호): 어떤 언어의 일반적인 철자와도 독립적으로 소리를 정확하게 적기 위한 표준 알파벳.
- 음역(Transliteration): 문자를 매핑하여 텍스트를 한 문자 체계에서 다른 체계로 변환하는 것으로, 발음이 아니라 원래 철자를 정확히 보존하는 것을 목표로 합니다.
- 로마자 표기(Romanization): 특히 로마자로의 음역.
ASCII 전용 음성 알파벳이 애초에 존재하는 이유
IPA는 표준 키보드에 없는 ʃ, ʒ, ə, ˈ 같은 문자를 필요로 합니다. 이는 유니코드가 보편화되고 대부분의 글꼴, 터미널, 파일 형식이 비ASCII 텍스트를 안정적으로 지원하기 전, 수십 년의 컴퓨팅 기간 동안 실제 문제였습니다. 연구자들은 ASCII 전용 대체물을 만들었습니다: 미국 영어 음성 인식 작업을 위해 개발된 ARPABET, 그리고 이메일과 오래된 터미널에서도 안전하도록 개발된, 전체 IPA의 ASCII 인코딩인 X-SAMPA입니다. 이는 역사적 진기함이 아닙니다. ARPABET은 여전히 널리 배포된 미국 영어 발음 사전과 음성 도구들이 사용하는 표기법이며, X-SAMPA는 여전히 순수 텍스트가 필요한 언어학 도구에 등장합니다. 그 데이터를 읽는 어떤 것이든 그 알파벳을 읽을 수 있어야 합니다.
scriptconv는 실제 변환을 수행합니다. 이는 기술이 아니라 실행된 출력입니다:
from scriptconv import convert, arpa_to_ipa, ipa_to_arpa
convert("K AE1 T", "arpa", "ipa")
# 'kæt'
convert("HH AH0 L OW1", "arpa", "ipa")
# 'həloʊ'
convert("kˈæt", "ipa", "x-sampa")
# 'k"{t'
arpa_to_ipa("HH AH0 L OW1", stress=True)
# 'həlˈoʊ'
ipa_to_arpa("həlˈoʊ", stress=True)
# 'HH AH0 L OW1'
강세 표시는 왕복을 거쳐도 살아남습니다. ARPABET은 모음에 붙인 숫자로 강세를 표시하고(OW1), IPA는 강세를 받는 음절 앞에 ˈ를 두어 표시합니다. arpa_to_ipa(..., stress=True)는 그 정보를 건너편으로 옮기며, 되돌려 변환하면 원래 숫자를 정확히 재구성합니다.
IPA는 설계상 이 모든 것의 중심에 있습니다. scriptconv는 모든 표기법을 그래프의 노드로, 모든 변환기를 간선으로 취급하며, 표기법 쌍마다 직접 변환기를 손으로 작성하는 대신 IPA를 허브로 삼아 변환을 경로화합니다:
from scriptconv import DEFAULT_GRAPH
[f"{e.src}->{e.dst}" for e in DEFAULT_GRAPH.route("arpa", "x-sampa")]
# ['arpa->ipa', 'ipa->x-sampa']
이 허브를 거쳐 총 아홉 개의 표기법이 트랜스코딩됩니다: ARPABET, X-SAMPA, Kirshenbaum, Lexique, Cotovía, RFE, mantoq, 그리고 아래에서 다룰 Buckwalter입니다.
다른 무엇보다 먼저 문자 체계 감지하기
소프트웨어가 텍스트 조각을 어떻게 처리할지 — 어느 방향으로 렌더링할지, 어떤 맞춤법 검사기를 실행할지, 어떤 글꼴을 고를지 — 결정하기 전에, 텍스트가 어떤 문자 체계인지 알아야 합니다. 이는 그것이 어떤 언어인지와는 다른 질문입니다. 문자 체계는 문자 집합을 식별하고, 언어는 어휘와 문법을 식별합니다. 다시 말하지만 세르비아어는 키릴 문자나 로마자일 수 있습니다. 우즈베크어도 마찬가지입니다. scriptconv는 문자 자체로부터 직접 문자 체계를 감지하며, 별도로 언어 코드를 그것이 관례적으로 쓰이는 문자 체계에 매핑합니다:
from scriptconv import detect_script, script_runs, lang_to_script, base_direction
detect_script("Здравствуйте")
# 'Cyrl'
detect_script("안녕하세요")
# 'Hang'
script_runs("привет hello")
# [('Cyrl', 'привет '), ('Latn', 'hello')]
base_direction("مرحبا hello")
# 'mixed'
lang_to_script("uzb_cyr")
# 'Cyrl'
detect_script는 ISO 15924 코드 — 문자 체계를 위한 표준 네 글자 태그 레지스트리(키릴 문자는 Cyrl, 한글은 Hang, 로마자는 Latn, 아랍 문자는 Arab) — 를 반환합니다. script_runs는 혼합된 텍스트를 문자 체계별로 연속된 구간으로 나누며, 이는 렌더러가 문장 단위로 어떤 글꼴과 텍스트 방향을 적용할지 결정하는 데 필요한 것입니다. base_direction은 혼합된 문자열이 왼쪽에서 오른쪽으로, 오른쪽에서 왼쪽으로, 혹은 둘 다로 읽히는지를 보고합니다.
어려운 사례들: Buckwalter, 한글, 가나
세 가지 문자 체계 변환은 실제 파이프라인에서 충분히 자주 등장하여 scriptconv가 각각을 직접 다룹니다.
아랍어를 위한 Buckwalter는 모든 아랍 문자와 분음 부호를 특정 ASCII 문자에 일대일로 매핑하는 ASCII 음역 체계로, 원래 철자 — 대부분의 원어민 텍스트가 생략하는 모음 표시를 포함해 — 를 정확히 재구성할 수 있게 합니다. 이는 아랍 문자가 ASCII를 중심으로 만들어진 파이프라인과 도구에서 다루기 까다롭기 때문에 존재합니다: 매핑이 정확하고 가역적이기만 하면, 정렬, 비교, 정규 표현식, 오래된 텍스트 형식 모두가 텍스트가 로마자 ASCII가 되는 순간 더 쉬워집니다.
from scriptconv import buckwalter_to_arabic, arabic_to_buckwalter
buckwalter_to_arabic("mrHbA")
# 'مرحبا'
arabic_to_buckwalter("مرحبا")
# 'mrHbA'
arabic_to_buckwalter("رحمٰن")
# 'rHm`n'
마지막 예에는 소수의 단어(رحمٰن, rahman)에 쓰이는 작은 위첨자 분음 부호인 단검 알리프가 포함되어 있습니다 — Buckwalter는 이를 위해 일반 알리프와 구별되는 특정 ASCII 문자(`)를 예약해 두었으므로, 음역이 둘을 뭉개지 않습니다.
한글은 음절 블록처럼 보이지만, 각 블록은 격자에 배치된 개별 글자(자모)의 합성 클러스터입니다 — “H”, “A”, “N”이 왼쪽에서 오른쪽으로 쓰이는 대신 시각적으로 결합되어 “han”이라는 하나의 글리프가 되는 방식과 같습니다. 검색을 위해서든, 음운론적 분석을 위해서든, 다른 시스템에 넣기 위해서든 개별 글자가 필요한 소프트웨어는 이를 다시 분리해야 합니다:
from scriptconv.translit import decompose_hangul
decompose_hangul("한국")
# 'ㅎㅏㄴㄱㅜㄱ'
decompose_hangul("국민")
# 'ㄱㅜㄱㅁㅣㄴ'
마지막 예는 그것이 하지 않는 일 때문에 중요합니다: 국민(gungmin, “시민”)은 비음화 동화를 거쳐 [ɡuŋmin]으로 발음되지만, decompose_hangul은 분해가 음운 규칙이 아니라 유니코드 코드포인트에 대한 산술이기 때문에 동화되지 않은 채로 쓰인 대로의 글자 — ㄱㅜㄱㅁㅣㄴ — 를 반환합니다. 이는 무엇이 쓰였는지를 알려주지, 어떻게 들리는지를 알려주지 않습니다.
가나 변환은 일본어의 두 음절 문자 체계인 히라가나와 가타카나 사이를 오가며, 이 둘은 고정된 코드포인트 오프셋으로 같은 소리를 다른 문자로 나타냅니다:
from scriptconv import hira_to_kana, kana_to_hira
hira_to_kana("こんにちは")
# 'コンニチハ'
kana_to_hira("カタカナ")
# 'かたかな'
이것이 자체 라이브러리로 존재하는 이유
음소화기 — 쓰인 단어가 어떻게 발음되는지 추측하는 도구 — 는 언어학적 판단이 필요합니다: 강세 규칙, 예외, 문맥 의존적 발음. scriptconv는 의도적으로 그런 것을 전혀 갖지 않습니다. 위의 모든 함수는 테이블 조회이거나 코드포인트 계산입니다: 같은 입력, 같은 출력, 추측 없음, 언어 모델 없음, 특정 언어가 실제로 어떻게 들리는지에 대해 틀릴 수 있는 것이 전혀 없습니다. 이것이 이를 필요로 하는 모든 음소화기가 각자 자신만의 버그가 있는 ARPABET 테이블을 재구현하는 대신, 이를 공유하는 것을 안전하게 만드는 이유입니다. 음운론 스택 글은 실제로 언어학적 견해를 지니는 발음 추측 엔진들이 이 계층을 중복하는 대신 그 위에 어떻게 구축되는지를 다룹니다.
매핑이 정확하지 않은 곳
표기법 사이의 변환이 항상 무손실인 것은 아니며, scriptconv는 이를 놀라운 일로 남겨두는 대신 조회 가능한 데이터로 기록합니다. 각 표기법은 독립적으로 추적되는 두 가지 속성을 갖습니다: 그것을 IPA로 변환했다가 되돌리면 원래 기호가 정확히 재현되는지, 그리고 IPA를 그것으로 변환했다가 되돌리면 모든 IPA 기호가 재현되는지입니다.
ARPABET은 양방향 모두 실패합니다: 제한적인 영어 전용 음소 목록을 가지고 있어서, IPA → ARPABET → IPA로 가면 ARPABET의 테이블에 기호가 없는, IPA가 만들 수 있는 구별을 잃을 수 있습니다. X-SAMPA와 Lexique는 전체 IPA 목록을 충실히 다루지만 자신들 쪽에서 시작할 때 깔끔하게 왕복한다는 보장은 없습니다. Kirshenbaum과 Buckwalter는 자신들 쪽에서 IPA로는 깔끔하게 왕복하지만 반대는 그렇지 않습니다. 할라비 아랍어 음성화기의 음성 알파벳인 Mantoq는 IPA로 가는 한 방향으로만 변환됩니다 — 되돌리는 변환기는 없습니다. 이 중 어느 것도 어딘가의 docstring에 묻혀 있지 않습니다. 호출자가 왕복이 안전하다고 가정하기 전에 확인할 수 있도록 라이브러리가 노출하는 데이터입니다.
여러 출처의 발음 데이터를 이어 붙이고 계시거나, 음소화기에 도달하기 전에 문자 체계를 감지하고 텍스트를 정규화해야 한다면, 문의해 주시거나 서비스 페이지에서 저희가 이 분야에서 만드는 다른 것들을 확인해 보세요.