"""韩语文本归一 (ko 线共享单一真源, 从 gate_ko.py 抽出)。 背景: CV3 前端 (cosyvoice/cli/frontend.py) 分支是二元的 if contains_chinese(text) → zh 规范化, else → wetext en TN。韩语没有分支, 走 en 分支会被注入英文 (实测 5000 句真实 ko 语料: changed 24.7%, 纯英文注入 10.5%)。 wittext 本身也没有 ko 模型 (assert lang in (auto,en,zh))。 ⇒ ko 侧一律 text_frontend=False + 本模块的数词归一 (门控 CER 0.0522 vs en 分支 0.0702)。 用法: from ko_norm import ko_normalize; t = ko_normalize(raw_ko) """ import re # 韩语数词 (sino-korean, 用于读数字; 万/亿 单位另处理) SINO = ['영', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'] UNITS = ['', '십', '백', '천'] BIG = ['', '만', '억', '조'] def _sino4(n): """0 韩语汉字数词 (일십 -> 십 省略前导 일)。""" out = [] for pos in range(3, -1, -1): d = (n // (10 ** pos)) % 10 if d == 0: continue out.append(('일' if (d == 1 and pos > 0) else SINO[d]) + UNITS[pos]) return ''.join(out) or SINO[0] def korean_number(n): """整数 -> 韩语数词 (만/억 分节)。""" if n == 0: return SINO[0] parts = [] grp = 0 while n > 0: chunk = n % 10000 if chunk: parts.append(_sino4(chunk) + BIG[grp]) n //= 10000 grp += 1 return ''.join(reversed(parts)) KOR_CNT = ('년|월|일|시|분|초|개|명|번|층|살|세|위|등|가지|마리|잔|병|권|장|대|회|주|달|원|위안|엔|달러|' '킬로|미터|그램|퍼센트|프로|배|점|호|호선|편|화|차|번째|군요|째') def ko_normalize(t): """韩文数字 -> 韩语数词, 保留阿拉伯数字以外的 Latin (如 4K/RTX 型号按 Latin 读)。 策略: 数字后紧跟韩文量词/名词(无空格)时 -> 韩语数词; 数字+Latin 型号(4K/5090/i7) -> 保持原样(让 tokenizer 直读); 纯数字独立 token -> 韩语数词。 """ def repl(m): num = m.group(1).replace(',', '') tail = m.group(2) or '' if not num.isdigit(): return m.group(0) return korean_number(int(num)) + tail # 数字(可带千分位) + 可选紧跟的韩文量词/名词片段 t = re.sub(r'(\d[\d,]*)\s*(%|[' + '가-힣' + r']{0,3})', repl, t) return t