From 0a89407e4d8ad644fde5d403a006f77ad2c17026 Mon Sep 17 00:00:00 2001 From: erew123 <35898566+erew123@users.noreply.github.com> Date: Sat, 16 Mar 2024 11:15:01 +0000 Subject: [PATCH] Custom tokenizer for Japanese Replaces the standard TTS tokenizer within Finetuning. --- .../__pycache__/tokenizer.cpython-311.pyc | Bin 0 -> 39933 bytes templates/tokenizer/tokenizer.py | 869 ++++++++++++++++++ 2 files changed, 869 insertions(+) create mode 100644 templates/tokenizer/__pycache__/tokenizer.cpython-311.pyc create mode 100644 templates/tokenizer/tokenizer.py diff --git a/templates/tokenizer/__pycache__/tokenizer.cpython-311.pyc b/templates/tokenizer/__pycache__/tokenizer.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2110cb49e414c7ab1183fb898b9881b8d7d9510f GIT binary patch literal 39933 zcmdsg3v^UPx^6#{PIu_elV^Yp36DT%AiNP!Av_gS5?>J})9JlQI_Y%B-Q9t-%}jWx z0plS!c#I>?#2H6};uuk9&|y3tBX@Zp>#lBApG(tgv6kmV#>2?08?Vl~F7LYM-0!db z=-!=9Wahfo8Qp~H+V%fcyY^py{q@&hRsVi@!UU@h*L#jX3tv5=(+$&&auv#vFYdJH zbkFE`9k2K5>ZMJ9icc#UnQdXrv)nd{9AmQ$aDU!ym-%~Ee+Fq1d0&023| zakJOfW~;Zcc#b!}Z9@G77SHwC+Z^={7Poi{+6wCnSv=2M)K*+y%;HvWNn2@sDT~{@ z6Wb=$Ph#3*W;OZZYiCT4+5;wJ+#B@1LKKMjzwERgB^3_xaPfpGjRKxSuw zl=BsU%*g_o%g+O3eile2zhDf=LcR)+MOpGV`Ne=N$pTr*R|B#v3uHOJ0+5wiAglP* zfUL;^Sw%Q2DrYw+2d<`I*vp}Zu zTL9Ub1u~uA2FUg-5RTsg$Q@ZA<@}w1?92k0%kKi@t}KvB{%%0-$pTr(?*^nc3&hFS z0dj8^$Ws13K<>{1S@N zqrN<&r5@Je)K_G{MJ+r78WG*Z@a`kv-G%%~{vdvhk(6=Ka6lGe*|GQe-z;|{!xU> z`Nt5h;2%f0QqZ-mQU;X%)k-Y=SA(%89iqfF7;BZ(^k3l??OSQ@`0kes7}xM$y6V>% zbX#@&G4$Z$8Ibk-6R7nDz6as0{F4Z86K?1Ku9f8#InZ*0c8t{Pkala9wA(V%czrE& zh+19cQ`e~)yDIfbhuh_D7WjsC(cdnJ!A>Mu>qM6?;PnIr5qB1s+r~JA21O!Un)a;R?d3F9)!RHA}h1UrO zg1h`;n_JZeP%nB@BmRH!9)f3d5!6|JdwVbqPy2SYglACkD8GDV!859OW(7-^p?R?K zOe$XP*m%Lbv0;_wT}eZ@IcaEdB@HeyX%GUHX3>TWBI$i16)RGH5h|YUj6P}dx_nIm zdQ0x>#Wid9xV+w=%iG!z3FDD<~kQ&@dHqws$7&O+v6C(C%_~Hc&C*L=;ns z|A7Y)bmlW8$&Sp3bOcaw2a+Z^`kp?y{?nNyoxH;izRwT2kq;13d@2 z9cd{_zCY*a3MR>Ae1XDs*S|*KPNTYxUWm2god}=NHR%o-BZgl6p=0T77}06_NyN~~ zTBKJmh9buPwAQAFNEdI==F#hdX{{L1YoP~C5mSG9y_9008EY}AA4N==H9BaHm|ILr zrW#$Zey{mpj^-VVylIMpknuk0ctL7*`HJLdpRK(M0}GU3Z!d0$p!@@d1BTQyc=LE= zQSR^rY7Mtx9hIc1Uw@8V1Qw25_7fG)ZXcJJ#%`7Z>pSe+ei;i=*dl|Pb7n%c#BL0 z2k2={Ydu9u87Y0e&Q}`I-At=A>CuCXklwcNl)FU%65A~V0$i|J;5@$eP>>6fFiq-` zwt(OZ3O+Xp+3Wv~>uWt%x8*vqQ>7_s-tXaq%}ECjiQlll8DgF!hL9|16a`mngWuQC z+}YkN_yWn?psO)Jg^)n5%u5=*f-jllYHt^OJd-UL9C!>Jo^~N=re_9{#z0U^=6Df$ z+LIQl(0Cwq>=Z#lNR}P1PDhO zMTm_!`4UOICGAo%3Z7Vv#B%%(oWVPH={_zh>AEA4J2{qH@mldf-I*z8r$kq7y;Qk1 zR=G7{DZXr(ddV`iuR3O#5x2~UT4r3em-W@f?6c$c+1j`Fyp{8#SR$_JXyEIPX= zHg|P=?&_Fhb#&d%OOBmU$Ig))U13?@#$HFSYn)3tq*PwqXnH&$34FD#GFU3IB&RkU!` zwX%xSJFk{Z>a(2Od1_~3>cT|D{KT9EiL&VjJ@d#@ zkDNSs>R_+G+xD?-V$?KIoP;I|8}|ejVwm)4Gy)nX*;wL3gvOGHZg2HLeR}U}QO62i z7t!XEFyZMYEX>cw{>JJ3yDICWb^lR%n=tn_9-yp6>`W^Tm z*oL4>H=>(p%SivQD@EXncBOfzp#JlizTs=IlmHLJQ&Mc5zkvO`0)9{w}Mg2 zx)Gx$8>3xiQKcbiY!d^?oHkDYvH`b-HuOi_L7Ahf@wh{u3i{byAc%hJ^7w*;==Mu5 zN8qKbqe1RhT2A_#dz zmctw5leu0`8$=jA#pw2X=@qK5hX-kbSbUs zI%5#GAeq?37xY&eM5xCLSZAzE0wxOk|&`^4`Q?fs~}N_!j8 zUI}iV_DT+xSE79`k+iK%KyL(9+Q7i#ZH!QDeqR7x1KmvALQ>e7~ zFa?ZSIZPmWnm{d26UHLyl}aZJf;>VNdY}_ZtODG0Q5S@$YxjPqc;{zCh)$In=s8I} zlyx%UP_!*+L{y}Hid#cqKa)2XpWE;G@Y!G|6GS4dQGKW0c>onw#k-lLjuPCw)KPM< zyo{61=tP?7ey1q$YgAZ#ngZ$zPyhqpsp`LlN~;ZNp@vj~n{P-Z2g@so1f#<5Mvt#4 zM5Z~9@8WxcZ|L_lc$^?|kr2TIAoRCFE_I61n3|$|lZN?wdXb+}zyzVO(!hj!^vy-l z-$VyPc0i(UM&FAaivC0NJxT%s*IpD~} zUh92Mev1_)u-)2zJrE?#vtL#SIS5hF+_}56anm2 z6@F0HQx?&|IHuoKX-wK1T#YaucX(Vu4<@s$X<^R^HJ&kv3sIhA@HLAX_YC08=z&Jqql-{iHWHME7iK@SYSO(B<8JdX@t;eUXvC|$a%GpYva;xk~w z)SH*EAZD4`ZSH>Ls-v){qgUvAXrLnISR8jO?lyIs5;jNI9gGb$Y)@l6qZEznGTYaT z&Gs6jqe^kD$x+)8Mj0SgI)0-8ax*hP8s-F2BRhzy_MK#o5P*S95Z9pU<8-8d2l$J- z5l~0c7$?#?(&pzkp5ET?8qmMUKYQ2dyJ95^U)z0lZ!GVYc-}4N?>YbQg}SI^jBV({_B)-(bTOuefsi6CdWVf% zCkQ0IPjL(95=DQb3j`7WkrMJjAdz!>KvF*?en5$N92V!vAWQi^q?BADEhvloi_*Q< z((BL{+fltwq=K8g8Ko!ZK|T_W%Cp;v?Ns8`Dj zIrJv=Z1KNH`56IoiGZQ+A@ALiK|zsbCQ2{P=iJR6zuWDBs7vq2LMACEfRsMB;PnbH z>?zcQ)RcxFmcshAm7}#X=o=|P$tcTvzJI*AHG{GssyzC5h(hnH{MP2Wc(~m~BT1@@ zYs4h21aYlmxbxzL&Q?zR&r%C9g644O=S=J}Hq8Tl{q0!D`2LZz7UcY5H(q_zbOkH@ z6YR>m?#=00<}8yJMLl5CQSM5&wviI=TseB>n9>PCWSQlz9J+FxOS}xgqluRiX9!5z zf+R%}Z(KQiC}L1m5)oDz4F+V<5wQ1R5sqR7EipM zcq4HZ<$muJvma8|_#b$ERl{XngW15iYftqM9o{GEm=SQ-P7hE5b&imY5HA0Hw(K;9 z^2=oJ`ydA95t1M#(uS?1T9DjPM@Wg1i3#}~~`f(1_K{g4N)MII~hH}1llJ*o) zEz1$okcD$!k2y0ai(lMA0r*I#$Ac%tdd#H5uXD6RDF40_6 zVZ2?3>tYZhlLob9~-k~$C3Psu44%Z4T`QphyD|T=N!c2K%ot{i(Ok*B; zfh;!COc)BDKTW|i~&mRA+es~T_( z>d*2~%d)YIlxZp)4EkzYu_18y@Gpjs92q{+J$&S6!$&R*AAVhJI;KevA34dAe>Z&O zO<)m_H6R<;hL0R)$-O`!NU9l!j~p95@+zgVv5|4X@R8>kHq+HvGn(+m+_iUK%5K4@ zEep?Rwym{VTs-|Ldx^Y+70E7N? zq*a=er4513wno1!PMfO!*I& z&VOjhqBVOLR?ZIuBR)PN@{wS3WvC2KR3RU{8~Nash#*G%5m&??h`0h{A&5dmhzt#2 z2ViJ!dI@P|N2JpBIOpHGeor7$QCS(H?aUbuOMBAPOz0rhh7ddvJZD-a_?1Z7(=sZG z`QKv^O!Xd%Bwmdq-iTZ|Rv9i*9-XREXkL1ui=yvD7R63P7DeC2|Mw`2^{_ZL5v1jx z&kJ}oC{P)yNYB5=8S&u2LWKT@eC|+W=z`BZbRn|GSs7ZAf#~8rtr39&zZx6*4MkZf zL_Dn-uLSveI49!X}_0y+ciClZ`XOZDUFGfCn?$?n|Upp50-qf?rcWYpvlNiH*Vw=g;3$ zLGeA#Jym-b$jlXCG)_<2Lk+(WIp@#+;FmXn|G_VTznI}?V*ktGvz3wg^YN1jsl;VW z8t<*GNgB6qygO;!eDCgX*`CHd{3A=NmK~g5g~+mll?&#hX8gj+TlX}kLy;y!fhR!~ zUU|q_wO4*Mjm}D9gapqQJOVo1*N@-P7oT~K3t}mv@6d@8BYM(#Mqj)EC%Oy}NBA#Y z2lWv;?P%J?R?oiHSBcb7&KQ#BCT!Vg7yp^vi6`;H7An&>V`EiQ6x#e9LWA4i*5(R` zH-L2m5B{PW5gewo<)2t}d{yt#V{3cXcGVhcT|kI?)V=_ z2qzdS&|nykkfMMCP;FSA3e6*-gFHxK54NYxR^L;9dBV&~6K3`s`)gwpD&rF>yS7}ll{|fK-%2{sGgxxI z^1_ zZ^u7t#y`htJI-esZ|3xzxDe++)A&bu0_tK;SEn*+ofFC73*R2R!QcRY#xUrodN18gNQR06A3Ylxb2IK!th{RT5Llp@k5*8wkzoM*vBhwv|u&We7*IgsCbr$>O+{u^d6yel(?$i;T(Y!QK zJmYe4#iink{)WN2_?)$|;#=ayw{%+)w&Gs#_=fHciK%n?J^cszJ@Khay6;Fk=BAOM zL?YIAI}jPW@1nP=BO4IaD|4jOaxjgWyiwE3OlQs7o(c}mktAbX8iyK^xj5=ii;s*$ z&ln|MwkD>glN` z8%{M0@b8y=RPsUD`(@`L`=}Ld55#N_#BC2mO%IG7cT}1MQRKHxGn&k5C}uos(n>l| z%q(dYDT1*fMAYM5MI&)DHI~Bkc7-I^q|qpdVh#{EqR7&*DKfbH{PJ__2GdU8<>Y3RW64&IFSlH$*FC8784nmA)a}=U=6N#H%^0JuG>ecg z_MX;k<&KfJNXe-KZ)DWZ_7{{>k}8MQAwOGQ+8bCoVHZ0S0n-PYL$SHhHRRUJ28eM< z_4;Bif~Uc_>6%M~vGFKbW!NQBIcM}DjT~v6txGy>Ygy-YwKejt4dF$yRwng2tr5bx z&g*x(ynzjfNa%~ucfKe^kzaM`{%Otjk7j*T7p;9Dw&ub3ng_ci-B(h)(0SGCuY7?| zP=J`Hl@(I;MYw0v{e?AmqUIvoOGXO1_y+{v29>FtZ?BSzwUs38L{mvp`WP!zAd-z0 zGDWa>4v3X{re%-fg1?50%TU7rRe=~__0Er6(R=QVt-de5`o1oC>XY8`e|3FnIQ&-X zyPWDvsvhe5_O0(PeV>mlyCc5rj&HTLG*o>nwOx@`+t+rTUwWR8Rc(q_ZIWwSX-npZ zn;TRYLn$3KSs=~935LNF(^EDX#|#}a)U-sJswVRr6ulC7$)W~jLZ;?T+8SiNgb=K$ z{Lp6>q6DVwNQ3teC5=@vKxUI7;uMev4!CrITM6DsHQanyN;tj#NAL4nIWhv8kGES%A$fs8w|0#xd=9qpSo6$F}*w z^N&`@kArLB^D=SMRUgn9@00Q}dQj^n3O+(c<)%4>d_}UsY4tM=e%3mwgmeqXsOK}K z$hPZi{cOodKm%ZisQ2MuQtx5A--rHQ`uUCYb6WbjAQ`7}B)b%`D3dzuFoZaa0$9U~ zlL637I!e=zPBgf^aI7JS0dYR?ZYM5z5m_Z3Uw3?6pRsRu%*MrS92qlZM`4OFGi`|^ zm4uTO^Wu(q=)3AdikgHeUT@v8TYGNps<~>L+aDaLi`lB-H3>`-(_ic!j!MkP}VB?@ZB3-0gsy_ zUq<9)2ws(NenFKB0d}%M>2d)s)i=5Qo?tVM(sM!8X&yTT*EK}c8d74JI_5RuDJQkO1j*)RR|&gw4gUfNTlCNLSf zr4A-KEJ}95zfWGXfc+$XUuRo#q7PndaLS?~2rn}Y)c6hiJRJg-I!!Gw`6eyxo$Vf9 zCt^ABuep|$VXnJt*XCuTIH5Fa)x{NHi2JCnn^;{Fwh6S@kYH7Q`oO6l_Xpoz{nm!~ zvaOh)L>~@CCw0Uob;Kui;2mO?{c+3w&y9xsoG*0MZuES1mQ!KZBq76DVg5RBv|iWW(TgNydxGNMdWh*Z(iY{0Ka7 zi!QAUUuP>xk0I^)KM|Cchp+!viqZ1$^^Yk=>oeDPQ;g^|DhPE2iO0FVx)^+-%2rcLHRn-63+bcR1OfyjF!yXhdu|11fgYfH@n0J<{gNf za+v~xcVE)l0h>o7L(uT1r2$rdc;agaPaV~+P6bBx!~iV=LTQ*Xz1!4dPuNPMwpmxF z&y7!CGB`CheO-L|y6&2uT_c9VGAm{fz2zfD#6D&*Gh(z6z7VOo6t_t6Jc?VTxQ*iZ zQhWl%?NZ!9@dAp^9x0@Fk(6Fc@e(OsO7V$Od=kYcOYtcbpDM+tQM`=evqz>=e1?=h zlj59|J`3?~Q`|Bc{!#HKiB7b&dQsORHeoBlG~V($ua3!%bSxD;b3Q`)J{5sR$#I8* zIFORe*~ES!IPM{)x)(uu^N6dF5S}!;WtcEMOwB@rCp;ZmO1me}zmFZICk`Gzc=iyj1k&HokKfT(=F&pCgS>-SK1@w=#t@sMm*kC_McmBZZj@bS zPEL2K%bOy`=XAU|VnCSlyjd%>>B9qXYfQa07s~Y_Nok-C8(#O&ti9a*F42e6!)v)Z zILagslbqykk`oTN1%a0wXi{HsxiQFdtiy2reO%aqjf}X5Bi)dzN#N#%E9O-}FZ2Ys zHcwM?5a)?PI20HZv0nvFd3nwi;yr#RGyyYnM=9QoMoSvul2H0kv#30SQ96W zLB$cKS?Z!Rj$n;Uo{|>9DYLs3eG+~>W%rojiqY*q8mpU_p$BE5k3uaE9}oAH6BqP7 z9Cyrus$NhMFW@d0R9q^k=&u`C7AsgBFIe1dz6#fkfn%$CR`&{*Y}2B)X;f+gvK7NdH`is0+^rBt{(VM=7U^J$0GjJ70&l9!W2@a=rVOM5de3i&& zmY-3eS=`Rx>VtY;agah4Wo_1@s|NO$Dw#DxQ! zViT9dCobtVrll?bt4~}UpSZZ!_*DYZzwY>+?mbtF%KJABl$@O!D_R~eTHd`01u3v= zMO=SztY}WWXbz=cE1DCXd*|SC1kzuuXlJ}=XEc9j!co+19nIjxHcY_I04e@@0o#w< zqp#6jLr*pp#_pNeqj51(r8);#--r8yU}Z_XM)c3=o;R`4EPcRGizS6Cvn!-`lYZ*j zqpr+u*OH^4Ky2sj5+5Ljq53mc4V%Nwi`>4i^$&fm-^*-cU*|qld-v^|Htz*nK1jGf zpnztUl2K$AVsM7wbNhKAY4q>`u@Na6?h~7l5}rJo%HQGrcCj>%{Z9g>(6G$^X%Jk zNW0h86`F=&3|#Z5Eq`O<=KKZa=4QhMt6Fw#!RHK$1FfER_^!rYFJ=kA2Mq#@O(nx# z6ERxOL$!5x-Mu$#e(2s^ckG4lYwWD?_yR#Xgq$>E>yNuRnNx?1ei5E)+e1k+mKWMP zMTwKKY5`7Wyp7FgBvau~MR-^w!K&rzhv{KixLPXV3~@CXP{W)Z$oCdAz6`QqW%18de#b|fqW9Fs>~(Q_U5bq>!LbS(%bY#Ge`Cx(FK(Zg!kX7#9kb7k+vgIqt8x=> zpkBn}Ol~em1aec?qJpx^LM z46?Cdk_hk32PAsr(FYb z)CbqN4Gmb9YiRTHAuq-A8yX&l?;i;w(jjY+I7CuKQsV%LcaeI7*g?S~6cFc2PoQOe zk(TIKKvGfGD!Nc(`Zr=IHCsn?+Q6yHDIGBw%`<7c*UZn2xINk+C0KLJPyR}^ybCntw;`X^yYPRn;A$or%Jm~d-YQ1ZtX}S6DqI&g`O5l z;LFLm2c$WuXWpEUPr0B37&E0WZC%8N8mPlz5qSqbhY>MBD@7VYBW77hPc5Sz*7q!C zb1d_&%8ixAaKUyTx3t>1f(t-WVVj>bJRK>){el;#_SoiEj_jf;n3++KZxUu<@S1{! zIDuQSgbVn+JbW4>`_weRKF_{jX9}j|Evlo*7yew_(g1z#AP@|fq|)HTS58$jl-7|I zf4|4;zwEDDd8g2p^?3|Lud~vja&$X?mR54u0dMnzyR%iu#2tH?MCTfk;@cyCSc=vI4&>07AA5Puu zOW`nsCz`x?bAW4dHBxievi79>WN?zxS+=mJDFsz>mehFgE?9iw1P|KV$<8u|9j!?LHHDBTBZ&+`vk%RJ^RVgVhSNG+6L9_1VOWQALt5fA zfuDMt8?r-?!R*LUr7=Xik+!)S;a*?uZInAif<;cJoFP2XCG|D}%ekrLKwC6HdT*PQ zmI#{^oQC=`5($x8A(#3Y_F+IgJpa2WNRP4m!YeSWp=g2+BqNmRt7+;D)& z`4|`91MLiaF*Lv$?}I_sMr=sZK&S=f_InJki}oWPu7kHCw4ERh^~>WPb8qep&CURJ zkid~;r3b@|gr?Mk4PR+RDdV497|D?ICrO3LgP^t~;(&D2R9nf943Wa0j*(WFz#54W zoFttPz&AfK>xwScri^KF9}00O5~)1gk#QYEuhFo^no85r$X0(lOG)`&ZQh7@M(iBF6q!u~VV(h~LPS#*rl=SkX zHlrbJ=oPhNr9UF^SWG91rR4y7(O28^YU580vx#eqGw(a{j}|(KRJh zX6Xb0N!oAhym**(u!|S`7Z1}HGQuc}_*{ZWS@|$VBF~492Dx-Jr5x5P{9NOQ&k`Qj z>W9+<|9%&qD3Opn;tS4@6NT1gMMI@2Cso_Uu?`?*Q64a7%$zD&Aa~_)%6a7obtCc^1#eQvuRM`>*%_Lj1u4DQbY!Jq zS}CoE!b6H`M^L!H- zsT4%^1EnB!4uF{r?fET;gt-_T{Jag1 z>u|gS7duZlLtC{t@YZvVJp3>ZJdeR=&*4}P7yT`6{~>k)Zhr=2C&&{JqDapui>)Zg zjUD|3vRvSzZ^WK}>!0_Cu*|j4@9+Q=>SQOjGm(^nf%A6k5IsbjNqGQu#pr9~goqw+ zgrOe>)#YguvOXE>a)$29ipJiM{0pgX_jdFgxi+ejUo7M0H6D-TqLqrQ%=!c%k$u)yFH&Ce&>^SPe~BAm;k1=Bg-F7A6v2HTJMu^-hJ)Sx0#P`BmouK)MOwpW+58rUIEbt%c(I&Hf8lbUtV~8d=Y(DEiywQW@UTBDvAmfBc2ry~Z zHuOTk&y!nZ&V?_!2Bo1T2ot4G^1Q_t&DWk5dYPnvWE-@ZA_xx#W?!$IaJP)ZWKfr}@p!r`Ed%$xx+(Q@OhZtlj@-SBAR zWZL|VCtkbp{9D|O{$B30Ctkd9^mQ%`L;N+W0J@VBf!clc%A+@)@8dpu^W=@gXa6|Z z#ohQB3hG8aX`H^%_acfL4^1hMhEY8BkLQlucpgI;CpmWD{`6?ijpHxEt258xXmhI(rd)D0AvwqrBN8)2B^&JCDZUN*Zv*R9p5%%b%!mb@7VS{ecEZVoX zhy};2-TLmO-TYO1<-jcLm9?*m*;mEwtC$hJ*LD0rH~vMZRQ1&&z~6vg`bEe-;0x%8 zSt(31SL500^&f6eECm-AY(v-bb2~*RSBnq&H9Nz_Qi}BLLOD^fUlr8=ZwIVh%pBMZ zW1=%$Dt&-3BU#B&u#Ki^waB*BB|=G)x(Q2^=6g!IGIgGDr;Do*T~2PNOAJB>4bwEb zEz}8J03R{LBoJ`viLNnFl#+@!`h6|rSejn0CPZ%CoZ-nzqGBOZAd1PWVrQFxhtrn9 z5HpzJTdXwq!8Q{rjGxyW!d!B$^f9?_!c$CNq%>@SmtedZ${xA^gOSM0GXbGlK3S#QbgL+tv;~k__C9}0 z@Wbca%^_+Xxfzu-88>XJX$IyFeCb{Ah3!yp=_57ry`neRcjDVNPIxuv?h`S63Kz)V zx=~Y<;k^pai9Q3pi@ZCYV>;3~=x*;oS9+U^{sf;4;-Y;}J_ng2$#c%os&O-%$_PP_Bd15ps8}G&pCXjh(Jv5MQvh- zoT2ShOxl-<{=Z67>PAEGPC;yTHM+QsPVT`E-|)7%!cJx`P}1=s9TlJ`2sGoN2E4uV z;?D(08u;f&eJq2h@ibl0^8q|~kL)s4g7(?3PLH&7C)V@Au zUmv%x|Go@qMHteGw&Oz=SOQzRd=A$LB~K7L!&B5W^`jUvNExV#>R_u9{M>EWd5A?I z$VC?x&7?H>I}j>BF%lDl?3m~B;8Cy>qPy)d!+_II(${_X+{G7$UTed=jhr0^1*}!xhRMDd>|~=(HaPGYf>I7yd7{7M z$VV@{?w(-vpqYKFD|Q0XZIGF+v>w2Qe#v z;$T?8Ql_uP+gmZlG%6$_GPH!Js)-7Tppa1x4svfp0YR()qEmz?rIN{^ zt_o0wBo1|hBXby_*b`L~SYjaYTXJts#xJ&_qauxRmYy|_Vw@u3`G@Y4pZ@gdr>EY& z{`iSc&c90!Vl6g0J>?Kwo??a_cjKc*ZQPut%b{*~Lryk}mC|Gvw+ciN^Q1W4j=5WF zYX}GXV2ko&uRj;W`W^lUgMpzl*riNWAT#AnAaW_^ugY|f8uYWl!9N`xggaAA(@uT% z^J6s4L#gW2N-|hw$Kq56rz!N*^hz_<4C7H*$C@c@tjX%^@i<1Atu)H`k(oxBVj5+X z0He%;F$S4$Yu-dVR3OH%UkIZmt+U`~8U4sUEfcEm_>z%wDx?s9iJy~yjth(Ob~ zWO|z56UixyHZ36LWu~>bL~CYRxLKq#teEurv8j!!6p#*m znO*yr(9Mc!|FWgbj_Ndj*>EyT{#ds;s{I?uvFWGaGh<3nHcLNgM5hh5>2*bAy+x;{ z_HBt3md6XryX+%5t%f2)!eklIO{A~6QeY}BvW}P$` zM~L`j#^>~%z%SXy0+%vGD4v9=eznYGAxzwUPHA5PMpl7wQMXG}t&>pI&*GZQR4Q&ir;qD?$-c0AuY|hqCQzFs)B_UgxUbbIq26g? zbse_?J8@V8#QMQapf*UT?Goy^6nI!djYz2K$5*m3)I<+nP!r^DHBOP( zcwC+{5*m)BeIs*DV~Ew?jJS*;-W;De66)@o;P+YsD;3)? zHN7Z@`ev@S7q?2NH4+c0?UD_3vqaM$+yrWager69xTSVVsJk;5BU=S(P3*P266&~g zZIe)I%->Ac&1P2W9tm~ay6%-wJ4Tmk&y;!l8ERBlI#QQo(oY`IX@dftt+ab~&w^;_ zqL|egw>lHn66t<<%(^0OT`}hUhM4u%xb;>QzBFd7j$5nM`|6l=S=_ozyu>U}1BUCg>ZZe6d!x5cd6)Mf- z1r8i?L(pY|w@3Tr?%h50eR;77v*QzHcUeCMIjVcsMkm(9Y@6e@%{cj*{T`lLx5sVU zM{-RCC=NkaJ`S{&%;>E?wKh6qeXMvxym&)YSCA;2M&ns{_0ruo$=~BW3zY0XYY*O)f6jjj+ZtwYTc>VV-t1u z@N<^_Wjgc+h99!pr-fUL^XaA3;6D9ix>tjhI#Ygk_>yr-)Ho$!s)(8@Vy3xq)7+?O rZo*U^HI>IqbK<5sQPZ4+X?oN&J!YC2H_ePHeE); literal 0 HcmV?d00001 diff --git a/templates/tokenizer/tokenizer.py b/templates/tokenizer/tokenizer.py new file mode 100644 index 0000000..1611d5a --- /dev/null +++ b/templates/tokenizer/tokenizer.py @@ -0,0 +1,869 @@ +import os +import re +import textwrap +from functools import cached_property + +import pypinyin +import torch +from hangul_romanize import Transliter +from hangul_romanize.rule import academic +from num2words import num2words +from spacy.lang.ar import Arabic +from spacy.lang.en import English +from spacy.lang.es import Spanish +from spacy.lang.ja import Japanese +from spacy.lang.zh import Chinese +from tokenizers import Tokenizer + +from TTS.tts.layers.xtts.zh_num2words import TextNorm as zh_num2words + + +def get_spacy_lang(lang): + if lang == "zh": + return Chinese() + elif lang == "ja": + return Japanese() + elif lang == "ar": + return Arabic() + elif lang == "es": + return Spanish() + else: + # For most languages, Enlish does the job + return English() + + +def split_sentence(text, lang, text_split_length=250): + """Preprocess the input text""" + text_splits = [] + if text_split_length is not None and len(text) >= text_split_length: + text_splits.append("") + nlp = get_spacy_lang(lang) + nlp.add_pipe("sentencizer") + doc = nlp(text) + for sentence in doc.sents: + if len(text_splits[-1]) + len(str(sentence)) <= text_split_length: + # if the last sentence + the current sentence is less than the text_split_length + # then add the current sentence to the last sentence + text_splits[-1] += " " + str(sentence) + text_splits[-1] = text_splits[-1].lstrip() + elif len(str(sentence)) > text_split_length: + # if the current sentence is greater than the text_split_length + for line in textwrap.wrap( + str(sentence), + width=text_split_length, + drop_whitespace=True, + break_on_hyphens=False, + tabsize=1, + ): + text_splits.append(str(line)) + else: + text_splits.append(str(sentence)) + + if len(text_splits) > 1: + if text_splits[0] == "": + del text_splits[0] + else: + text_splits = [text.lstrip()] + + return text_splits + + +_whitespace_re = re.compile(r"\s+") + +# List of (regular expression, replacement) pairs for abbreviations: +_abbreviations = { + "en": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("mrs", "misess"), + ("mr", "mister"), + ("dr", "doctor"), + ("st", "saint"), + ("co", "company"), + ("jr", "junior"), + ("maj", "major"), + ("gen", "general"), + ("drs", "doctors"), + ("rev", "reverend"), + ("lt", "lieutenant"), + ("hon", "honorable"), + ("sgt", "sergeant"), + ("capt", "captain"), + ("esq", "esquire"), + ("ltd", "limited"), + ("col", "colonel"), + ("ft", "fort"), + ] + ], + "es": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("sra", "señora"), + ("sr", "señor"), + ("dr", "doctor"), + ("dra", "doctora"), + ("st", "santo"), + ("co", "compañía"), + ("jr", "junior"), + ("ltd", "limitada"), + ] + ], + "fr": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("mme", "madame"), + ("mr", "monsieur"), + ("dr", "docteur"), + ("st", "saint"), + ("co", "compagnie"), + ("jr", "junior"), + ("ltd", "limitée"), + ] + ], + "de": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("fr", "frau"), + ("dr", "doktor"), + ("st", "sankt"), + ("co", "firma"), + ("jr", "junior"), + ] + ], + "pt": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("sra", "senhora"), + ("sr", "senhor"), + ("dr", "doutor"), + ("dra", "doutora"), + ("st", "santo"), + ("co", "companhia"), + ("jr", "júnior"), + ("ltd", "limitada"), + ] + ], + "it": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + # ("sig.ra", "signora"), + ("sig", "signore"), + ("dr", "dottore"), + ("st", "santo"), + ("co", "compagnia"), + ("jr", "junior"), + ("ltd", "limitata"), + ] + ], + "pl": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("p", "pani"), + ("m", "pan"), + ("dr", "doktor"), + ("sw", "święty"), + ("jr", "junior"), + ] + ], + "ar": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + # There are not many common abbreviations in Arabic as in English. + ] + ], + "zh": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + # Chinese doesn't typically use abbreviations in the same way as Latin-based scripts. + ] + ], + "cs": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("dr", "doktor"), # doctor + ("ing", "inženýr"), # engineer + ("p", "pan"), # Could also map to pani for woman but no easy way to do it + # Other abbreviations would be specialized and not as common. + ] + ], + "ru": [ + (re.compile("\\b%s\\b" % x[0], re.IGNORECASE), x[1]) + for x in [ + ("г-жа", "госпожа"), # Mrs. + ("г-н", "господин"), # Mr. + ("д-р", "доктор"), # doctor + # Other abbreviations are less common or specialized. + ] + ], + "nl": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("dhr", "de heer"), # Mr. + ("mevr", "mevrouw"), # Mrs. + ("dr", "dokter"), # doctor + ("jhr", "jonkheer"), # young lord or nobleman + # Dutch uses more abbreviations, but these are the most common ones. + ] + ], + "tr": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("b", "bay"), # Mr. + ("byk", "büyük"), # büyük + ("dr", "doktor"), # doctor + # Add other Turkish abbreviations here if needed. + ] + ], + "hu": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + ("dr", "doktor"), # doctor + ("b", "bácsi"), # Mr. + ("nőv", "nővér"), # nurse + # Add other Hungarian abbreviations here if needed. + ] + ], + "ko": [ + (re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1]) + for x in [ + # Korean doesn't typically use abbreviations in the same way as Latin-based scripts. + ] + ], + "ja": [ + (re.compile("\\b%s\\b" % x[0]), x[1]) + for x in [ + ("氏", "さん"), # Mr. + ("夫人", "おんなのひと"), # Mrs. + ("博士", "はかせ"), # Doctor or PhD + ("株", "株式会社"), # Corporation + ("有", "有限会社"), # Limited company + ("大学", "だいがく"), # University + ("先生", "せんせい"), # Teacher/Professor/Master + ("君", "くん") # Used at the end of boys' names to express familiarity or affection. + ] + ], +} + + +def expand_abbreviations_multilingual(text, lang="en"): + for regex, replacement in _abbreviations[lang]: + text = re.sub(regex, replacement, text) + return text + + +_symbols_multilingual = { + "en": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " and "), + ("@", " at "), + ("%", " percent "), + ("#", " hash "), + ("$", " dollar "), + ("£", " pound "), + ("°", " degree "), + ] + ], + "es": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " y "), + ("@", " arroba "), + ("%", " por ciento "), + ("#", " numeral "), + ("$", " dolar "), + ("£", " libra "), + ("°", " grados "), + ] + ], + "fr": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " et "), + ("@", " arobase "), + ("%", " pour cent "), + ("#", " dièse "), + ("$", " dollar "), + ("£", " livre "), + ("°", " degrés "), + ] + ], + "de": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " und "), + ("@", " at "), + ("%", " prozent "), + ("#", " raute "), + ("$", " dollar "), + ("£", " pfund "), + ("°", " grad "), + ] + ], + "pt": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " e "), + ("@", " arroba "), + ("%", " por cento "), + ("#", " cardinal "), + ("$", " dólar "), + ("£", " libra "), + ("°", " graus "), + ] + ], + "it": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " e "), + ("@", " chiocciola "), + ("%", " per cento "), + ("#", " cancelletto "), + ("$", " dollaro "), + ("£", " sterlina "), + ("°", " gradi "), + ] + ], + "pl": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " i "), + ("@", " małpa "), + ("%", " procent "), + ("#", " krzyżyk "), + ("$", " dolar "), + ("£", " funt "), + ("°", " stopnie "), + ] + ], + "ar": [ + # Arabic + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " و "), + ("@", " على "), + ("%", " في المئة "), + ("#", " رقم "), + ("$", " دولار "), + ("£", " جنيه "), + ("°", " درجة "), + ] + ], + "zh": [ + # Chinese + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " 和 "), + ("@", " 在 "), + ("%", " 百分之 "), + ("#", " 号 "), + ("$", " 美元 "), + ("£", " 英镑 "), + ("°", " 度 "), + ] + ], + "cs": [ + # Czech + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " a "), + ("@", " na "), + ("%", " procento "), + ("#", " křížek "), + ("$", " dolar "), + ("£", " libra "), + ("°", " stupně "), + ] + ], + "ru": [ + # Russian + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " и "), + ("@", " собака "), + ("%", " процентов "), + ("#", " номер "), + ("$", " доллар "), + ("£", " фунт "), + ("°", " градус "), + ] + ], + "nl": [ + # Dutch + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " en "), + ("@", " bij "), + ("%", " procent "), + ("#", " hekje "), + ("$", " dollar "), + ("£", " pond "), + ("°", " graden "), + ] + ], + "tr": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " ve "), + ("@", " at "), + ("%", " yüzde "), + ("#", " diyez "), + ("$", " dolar "), + ("£", " sterlin "), + ("°", " derece "), + ] + ], + "hu": [ + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " és "), + ("@", " kukac "), + ("%", " százalék "), + ("#", " kettőskereszt "), + ("$", " dollár "), + ("£", " font "), + ("°", " fok "), + ] + ], + "ko": [ + # Korean + (re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1]) + for x in [ + ("&", " 그리고 "), + ("@", " 에 "), + ("%", " 퍼센트 "), + ("#", " 번호 "), + ("$", " 달러 "), + ("£", " 파운드 "), + ("°", " 도 "), + ] + ], + "ja": [ + (re.compile(r"%s" % re.escape(x[0])), x[1]) + for x in [ + ("&", " と "), + ("@", " アットマーク "), + ("%", " パーセント "), + ("#", " ナンバー "), + ("$", " ドル "), + ("£", " ポンド "), + ("°", " 度"), + ] + ], +} + + +def expand_symbols_multilingual(text, lang="en"): + for regex, replacement in _symbols_multilingual[lang]: + text = re.sub(regex, replacement, text) + text = text.replace(" ", " ") # Ensure there are no double spaces + return text.strip() + + +_ordinal_re = { + "en": re.compile(r"([0-9]+)(st|nd|rd|th)"), + "es": re.compile(r"([0-9]+)(º|ª|er|o|a|os|as)"), + "fr": re.compile(r"([0-9]+)(º|ª|er|re|e|ème)"), + "de": re.compile(r"([0-9]+)(st|nd|rd|th|º|ª|\.(?=\s|$))"), + "pt": re.compile(r"([0-9]+)(º|ª|o|a|os|as)"), + "it": re.compile(r"([0-9]+)(º|°|ª|o|a|i|e)"), + "pl": re.compile(r"([0-9]+)(º|ª|st|nd|rd|th)"), + "ar": re.compile(r"([0-9]+)(ون|ين|ث|ر|ى)"), + "cs": re.compile(r"([0-9]+)\.(?=\s|$)"), # In Czech, a dot is often used after the number to indicate ordinals. + "ru": re.compile(r"([0-9]+)(-й|-я|-е|-ое|-ье|-го)"), + "nl": re.compile(r"([0-9]+)(de|ste|e)"), + "tr": re.compile(r"([0-9]+)(\.|inci|nci|uncu|üncü|\.)"), + "hu": re.compile(r"([0-9]+)(\.|adik|edik|odik|edik|ödik|ödike|ik)"), + "ko": re.compile(r"([0-9]+)(번째|번|차|째)"), + "ja": re.compile(r"([0-9]+)(番|回|つ|目|等|位)") +} +_number_re = re.compile(r"[0-9]+") +_currency_re = { + "USD": re.compile(r"((\$[0-9\.\,]*[0-9]+)|([0-9\.\,]*[0-9]+\$))"), + "GBP": re.compile(r"((£[0-9\.\,]*[0-9]+)|([0-9\.\,]*[0-9]+£))"), + "EUR": re.compile(r"(([0-9\.\,]*[0-9]+€)|((€[0-9\.\,]*[0-9]+)))"), +} + +_comma_number_re = re.compile(r"\b\d{1,3}(,\d{3})*(\.\d+)?\b") +_dot_number_re = re.compile(r"\b\d{1,3}(.\d{3})*(\,\d+)?\b") +_decimal_number_re = re.compile(r"([0-9]+[.,][0-9]+)") + + +def _remove_commas(m): + text = m.group(0) + if "," in text: + text = text.replace(",", "") + return text + + +def _remove_dots(m): + text = m.group(0) + if "." in text: + text = text.replace(".", "") + return text + + +def _expand_decimal_point(m, lang="en"): + amount = m.group(1).replace(",", ".") + return num2words(float(amount), lang=lang if lang != "cs" else "cz") + + +def _expand_currency(m, lang="en", currency="USD"): + amount = float((re.sub(r"[^\d.]", "", m.group(0).replace(",", ".")))) + full_amount = num2words(amount, to="currency", currency=currency, lang=lang if lang != "cs" else "cz") + + and_equivalents = { + "en": ", ", + "es": " con ", + "fr": " et ", + "de": " und ", + "pt": " e ", + "it": " e ", + "pl": ", ", + "cs": ", ", + "ru": ", ", + "nl": ", ", + "ar": ", ", + "tr": ", ", + "hu": ", ", + "ko": ", ", + } + + if amount.is_integer(): + last_and = full_amount.rfind(and_equivalents[lang]) + if last_and != -1: + full_amount = full_amount[:last_and] + + return full_amount + + +def _expand_ordinal(m, lang="en"): + return num2words(int(m.group(1)), ordinal=True, lang=lang if lang != "cs" else "cz") + + +def _expand_number(m, lang="en"): + return num2words(int(m.group(0)), lang=lang if lang != "cs" else "cz") + + +def expand_numbers_multilingual(text, lang="en"): + if lang == "zh": + text = zh_num2words()(text) + else: + if lang in ["en", "ru"]: + text = re.sub(_comma_number_re, _remove_commas, text) + else: + text = re.sub(_dot_number_re, _remove_dots, text) + try: + text = re.sub(_currency_re["GBP"], lambda m: _expand_currency(m, lang, "GBP"), text) + text = re.sub(_currency_re["USD"], lambda m: _expand_currency(m, lang, "USD"), text) + text = re.sub(_currency_re["EUR"], lambda m: _expand_currency(m, lang, "EUR"), text) + except: + pass + if lang != "tr": + text = re.sub(_decimal_number_re, lambda m: _expand_decimal_point(m, lang), text) + text = re.sub(_ordinal_re[lang], lambda m: _expand_ordinal(m, lang), text) + text = re.sub(_number_re, lambda m: _expand_number(m, lang), text) + return text + + +def lowercase(text): + return text.lower() + + +def collapse_whitespace(text): + return re.sub(_whitespace_re, " ", text) + + +def multilingual_cleaners(text, lang): + text = text.replace('"', "") + if lang == "tr": + text = text.replace("İ", "i") + text = text.replace("Ö", "ö") + text = text.replace("Ü", "ü") + text = lowercase(text) + text = expand_numbers_multilingual(text, lang) + text = expand_abbreviations_multilingual(text, lang) + text = expand_symbols_multilingual(text, lang=lang) + text = collapse_whitespace(text) + return text + + +def basic_cleaners(text): + """Basic pipeline that lowercases and collapses whitespace without transliteration.""" + text = lowercase(text) + text = collapse_whitespace(text) + return text + + +def chinese_transliterate(text): + return "".join( + [p[0] for p in pypinyin.pinyin(text, style=pypinyin.Style.TONE3, heteronym=False, neutral_tone_with_five=True)] + ) + + +def japanese_cleaners(text, katsu): + text = katsu.romaji(text) + text = lowercase(text) + return text + + +def korean_transliterate(text): + r = Transliter(academic) + return r.translit(text) + + +DEFAULT_VOCAB_FILE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../data/tokenizer.json") + + +class VoiceBpeTokenizer: + def __init__(self, vocab_file=None): + self.tokenizer = None + if vocab_file is not None: + self.tokenizer = Tokenizer.from_file(vocab_file) + self.char_limits = { + "en": 250, + "de": 253, + "fr": 273, + "es": 239, + "it": 213, + "pt": 203, + "pl": 224, + "zh": 82, + "ar": 166, + "cs": 186, + "ru": 182, + "nl": 251, + "tr": 226, + "ja": 71, + "hu": 224, + "ko": 95, + } + + @cached_property + def katsu(self): + import cutlet + + return cutlet.Cutlet() + + def check_input_length(self, txt, lang): + lang = lang.split("-")[0] # remove the region + limit = self.char_limits.get(lang, 250) + if len(txt) > limit: + print( + f"[!] Warning: The text length exceeds the character limit of {limit} for language '{lang}', this might cause truncated audio." + ) + + def preprocess_text(self, txt, lang): + if lang in {"ar", "cs", "de", "en", "es", "fr", "hu", "it", "nl", "pl", "pt", "ru", "tr", "zh", "ko"}: + txt = multilingual_cleaners(txt, lang) + if lang == "zh": + txt = chinese_transliterate(txt) + if lang == "ko": + txt = korean_transliterate(txt) + elif lang == "ja": + txt = japanese_cleaners(txt, self.katsu) + elif lang == "hi": + # @manmay will implement this + txt = basic_cleaners(txt) + else: + raise NotImplementedError(f"Language '{lang}' is not supported.") + return txt + + def encode(self, txt, lang): + lang = lang.split("-")[0] # remove the region + self.check_input_length(txt, lang) + txt = self.preprocess_text(txt, lang) + lang = "zh-cn" if lang == "zh" else lang + txt = f"[{lang}]{txt}" + txt = txt.replace(" ", "[SPACE]") + return self.tokenizer.encode(txt).ids + + def decode(self, seq): + if isinstance(seq, torch.Tensor): + seq = seq.cpu().numpy() + txt = self.tokenizer.decode(seq, skip_special_tokens=False).replace(" ", "") + txt = txt.replace("[SPACE]", " ") + txt = txt.replace("[STOP]", "") + txt = txt.replace("[UNK]", "") + return txt + + def __len__(self): + return self.tokenizer.get_vocab_size() + + def get_number_tokens(self): + return max(self.tokenizer.get_vocab().values()) + 1 + + +def test_expand_numbers_multilingual(): + test_cases = [ + # English + ("In 12.5 seconds.", "In twelve point five seconds.", "en"), + ("There were 50 soldiers.", "There were fifty soldiers.", "en"), + ("This is a 1st test", "This is a first test", "en"), + ("That will be $20 sir.", "That will be twenty dollars sir.", "en"), + ("That will be 20€ sir.", "That will be twenty euro sir.", "en"), + ("That will be 20.15€ sir.", "That will be twenty euro, fifteen cents sir.", "en"), + ("That's 100,000.5.", "That's one hundred thousand point five.", "en"), + # French + ("En 12,5 secondes.", "En douze virgule cinq secondes.", "fr"), + ("Il y avait 50 soldats.", "Il y avait cinquante soldats.", "fr"), + ("Ceci est un 1er test", "Ceci est un premier test", "fr"), + ("Cela vous fera $20 monsieur.", "Cela vous fera vingt dollars monsieur.", "fr"), + ("Cela vous fera 20€ monsieur.", "Cela vous fera vingt euros monsieur.", "fr"), + ("Cela vous fera 20,15€ monsieur.", "Cela vous fera vingt euros et quinze centimes monsieur.", "fr"), + ("Ce sera 100.000,5.", "Ce sera cent mille virgule cinq.", "fr"), + # German + ("In 12,5 Sekunden.", "In zwölf Komma fünf Sekunden.", "de"), + ("Es gab 50 Soldaten.", "Es gab fünfzig Soldaten.", "de"), + ("Dies ist ein 1. Test", "Dies ist ein erste Test", "de"), # Issue with gender + ("Das macht $20 Herr.", "Das macht zwanzig Dollar Herr.", "de"), + ("Das macht 20€ Herr.", "Das macht zwanzig Euro Herr.", "de"), + ("Das macht 20,15€ Herr.", "Das macht zwanzig Euro und fünfzehn Cent Herr.", "de"), + # Spanish + ("En 12,5 segundos.", "En doce punto cinco segundos.", "es"), + ("Había 50 soldados.", "Había cincuenta soldados.", "es"), + ("Este es un 1er test", "Este es un primero test", "es"), + ("Eso le costará $20 señor.", "Eso le costará veinte dólares señor.", "es"), + ("Eso le costará 20€ señor.", "Eso le costará veinte euros señor.", "es"), + ("Eso le costará 20,15€ señor.", "Eso le costará veinte euros con quince céntimos señor.", "es"), + # Italian + ("In 12,5 secondi.", "In dodici virgola cinque secondi.", "it"), + ("C'erano 50 soldati.", "C'erano cinquanta soldati.", "it"), + ("Questo è un 1° test", "Questo è un primo test", "it"), + ("Ti costerà $20 signore.", "Ti costerà venti dollari signore.", "it"), + ("Ti costerà 20€ signore.", "Ti costerà venti euro signore.", "it"), + ("Ti costerà 20,15€ signore.", "Ti costerà venti euro e quindici centesimi signore.", "it"), + # Portuguese + ("Em 12,5 segundos.", "Em doze vírgula cinco segundos.", "pt"), + ("Havia 50 soldados.", "Havia cinquenta soldados.", "pt"), + ("Este é um 1º teste", "Este é um primeiro teste", "pt"), + ("Isso custará $20 senhor.", "Isso custará vinte dólares senhor.", "pt"), + ("Isso custará 20€ senhor.", "Isso custará vinte euros senhor.", "pt"), + ( + "Isso custará 20,15€ senhor.", + "Isso custará vinte euros e quinze cêntimos senhor.", + "pt", + ), # "cêntimos" should be "centavos" num2words issue + # Polish + ("W 12,5 sekundy.", "W dwanaście przecinek pięć sekundy.", "pl"), + ("Było 50 żołnierzy.", "Było pięćdziesiąt żołnierzy.", "pl"), + ("To będzie kosztować 20€ panie.", "To będzie kosztować dwadzieścia euro panie.", "pl"), + ("To będzie kosztować 20,15€ panie.", "To będzie kosztować dwadzieścia euro, piętnaście centów panie.", "pl"), + # Arabic + ("في الـ 12,5 ثانية.", "في الـ اثنا عشر , خمسون ثانية.", "ar"), + ("كان هناك 50 جنديًا.", "كان هناك خمسون جنديًا.", "ar"), + # ("ستكون النتيجة $20 يا سيد.", 'ستكون النتيجة عشرون دولار يا سيد.', 'ar'), # $ and € are mising from num2words + # ("ستكون النتيجة 20€ يا سيد.", 'ستكون النتيجة عشرون يورو يا سيد.', 'ar'), + # Czech + ("Za 12,5 vteřiny.", "Za dvanáct celá pět vteřiny.", "cs"), + ("Bylo tam 50 vojáků.", "Bylo tam padesát vojáků.", "cs"), + ("To bude stát 20€ pane.", "To bude stát dvacet euro pane.", "cs"), + ("To bude 20.15€ pane.", "To bude dvacet euro, patnáct centů pane.", "cs"), + # Russian + ("Через 12.5 секунды.", "Через двенадцать запятая пять секунды.", "ru"), + ("Там было 50 солдат.", "Там было пятьдесят солдат.", "ru"), + ("Это будет 20.15€ сэр.", "Это будет двадцать евро, пятнадцать центов сэр.", "ru"), + ("Это будет стоить 20€ господин.", "Это будет стоить двадцать евро господин.", "ru"), + # Dutch + ("In 12,5 seconden.", "In twaalf komma vijf seconden.", "nl"), + ("Er waren 50 soldaten.", "Er waren vijftig soldaten.", "nl"), + ("Dat wordt dan $20 meneer.", "Dat wordt dan twintig dollar meneer.", "nl"), + ("Dat wordt dan 20€ meneer.", "Dat wordt dan twintig euro meneer.", "nl"), + # Chinese (Simplified) + ("在12.5秒内", "在十二点五秒内", "zh"), + ("有50名士兵", "有五十名士兵", "zh"), + # ("那将是$20先生", '那将是二十美元先生', 'zh'), currency doesn't work + # ("那将是20€先生", '那将是二十欧元先生', 'zh'), + # Turkish + # ("12,5 saniye içinde.", 'On iki virgül beş saniye içinde.', 'tr'), # decimal doesn't work for TR + ("50 asker vardı.", "elli asker vardı.", "tr"), + ("Bu 1. test", "Bu birinci test", "tr"), + # ("Bu 100.000,5.", 'Bu yüz bin virgül beş.', 'tr'), + # Hungarian + ("12,5 másodperc alatt.", "tizenkettő egész öt tized másodperc alatt.", "hu"), + ("50 katona volt.", "ötven katona volt.", "hu"), + ("Ez az 1. teszt", "Ez az első teszt", "hu"), + # Korean + ("12.5 초 안에.", "십이 점 다섯 초 안에.", "ko"), + ("50 명의 병사가 있었다.", "오십 명의 병사가 있었다.", "ko"), + ("이것은 1 번째 테스트입니다", "이것은 첫 번째 테스트입니다", "ko"), + ] + for a, b, lang in test_cases: + out = expand_numbers_multilingual(a, lang=lang) + assert out == b, f"'{out}' vs '{b}'" + + +def test_abbreviations_multilingual(): + test_cases = [ + # English + ("Hello Mr. Smith.", "Hello mister Smith.", "en"), + ("Dr. Jones is here.", "doctor Jones is here.", "en"), + # Spanish + ("Hola Sr. Garcia.", "Hola señor Garcia.", "es"), + ("La Dra. Martinez es muy buena.", "La doctora Martinez es muy buena.", "es"), + # French + ("Bonjour Mr. Dupond.", "Bonjour monsieur Dupond.", "fr"), + ("Mme. Moreau est absente aujourd'hui.", "madame Moreau est absente aujourd'hui.", "fr"), + # German + ("Frau Dr. Müller ist sehr klug.", "Frau doktor Müller ist sehr klug.", "de"), + # Portuguese + ("Olá Sr. Silva.", "Olá senhor Silva.", "pt"), + ("Dra. Costa, você está disponível?", "doutora Costa, você está disponível?", "pt"), + # Italian + ("Buongiorno, Sig. Rossi.", "Buongiorno, signore Rossi.", "it"), + # ("Sig.ra Bianchi, posso aiutarti?", 'signora Bianchi, posso aiutarti?', 'it'), # Issue with matching that pattern + # Polish + ("Dzień dobry, P. Kowalski.", "Dzień dobry, pani Kowalski.", "pl"), + ("M. Nowak, czy mogę zadać pytanie?", "pan Nowak, czy mogę zadać pytanie?", "pl"), + # Czech + ("P. Novák", "pan Novák", "cs"), + ("Dr. Vojtěch", "doktor Vojtěch", "cs"), + # Dutch + ("Dhr. Jansen", "de heer Jansen", "nl"), + ("Mevr. de Vries", "mevrouw de Vries", "nl"), + # Russian + ("Здравствуйте Г-н Иванов.", "Здравствуйте господин Иванов.", "ru"), + ("Д-р Смирнов здесь, чтобы увидеть вас.", "доктор Смирнов здесь, чтобы увидеть вас.", "ru"), + # Turkish + ("Merhaba B. Yılmaz.", "Merhaba bay Yılmaz.", "tr"), + ("Dr. Ayşe burada.", "doktor Ayşe burada.", "tr"), + # Hungarian + ("Dr. Szabó itt van.", "doktor Szabó itt van.", "hu"), + ] + + for a, b, lang in test_cases: + out = expand_abbreviations_multilingual(a, lang=lang) + assert out == b, f"'{out}' vs '{b}'" + + +def test_symbols_multilingual(): + test_cases = [ + ("I have 14% battery", "I have 14 percent battery", "en"), + ("Te veo @ la fiesta", "Te veo arroba la fiesta", "es"), + ("J'ai 14° de fièvre", "J'ai 14 degrés de fièvre", "fr"), + ("Die Rechnung beträgt £ 20", "Die Rechnung beträgt pfund 20", "de"), + ("O meu email é ana&joao@gmail.com", "O meu email é ana e joao arroba gmail.com", "pt"), + ("linguaggio di programmazione C#", "linguaggio di programmazione C cancelletto", "it"), + ("Moja temperatura to 36.6°", "Moja temperatura to 36.6 stopnie", "pl"), + ("Mám 14% baterie", "Mám 14 procento baterie", "cs"), + ("Těším se na tebe @ party", "Těším se na tebe na party", "cs"), + ("У меня 14% заряда", "У меня 14 процентов заряда", "ru"), + ("Я буду @ дома", "Я буду собака дома", "ru"), + ("Ik heb 14% batterij", "Ik heb 14 procent batterij", "nl"), + ("Ik zie je @ het feest", "Ik zie je bij het feest", "nl"), + ("لدي 14% في البطارية", "لدي 14 في المئة في البطارية", "ar"), + ("我的电量为 14%", "我的电量为 14 百分之", "zh"), + ("Pilim %14 dolu.", "Pilim yüzde 14 dolu.", "tr"), + ("Az akkumulátorom töltöttsége 14%", "Az akkumulátorom töltöttsége 14 százalék", "hu"), + ("배터리 잔량이 14%입니다.", "배터리 잔량이 14 퍼센트입니다.", "ko"), + ] + + for a, b, lang in test_cases: + out = expand_symbols_multilingual(a, lang=lang) + assert out == b, f"'{out}' vs '{b}'" + + +if __name__ == "__main__": + test_expand_numbers_multilingual() + test_abbreviations_multilingual() + test_symbols_multilingual()