From 5308ac8fbb7c84ac6fcb589646668918c0b35191 Mon Sep 17 00:00:00 2001 From: ti-oluwa <105179638+ti-oluwa@users.noreply.github.com> Date: Tue, 26 Dec 2023 19:02:31 +0100 Subject: [PATCH] Minor Refactoring. Published package to PYPI. --- .gitignore | 1 + README.md | 2 +- dist/tranzlate-0.0.1-py3-none-any.whl | Bin 0 -> 21542 bytes dist/tranzlate-0.0.1.tar.gz | Bin 0 -> 23539 bytes pyproject.toml | 54 +++++++++++++++ requirements.txt | 6 +- tests/fixtures/test_file.txt | 2 +- tests/test_translator.py | 2 +- tranzlate/__init__.py | 5 +- tranzlate/translator.py | 91 ++++++++++++++------------ 10 files changed, 111 insertions(+), 52 deletions(-) create mode 100644 dist/tranzlate-0.0.1-py3-none-any.whl create mode 100644 dist/tranzlate-0.0.1.tar.gz create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index 59ee0a5..19c5631 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/__pycache__/** +**/tranzlate.egg-info diff --git a/README.md b/README.md index 712c301..da77457 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ import tranzlate translator = tranzlate.Translator() markup = '
Good Morning!
' -translated_markup = translator.translate(markup, target_lang='yo') +translated_markup = translator.translate(markup, target_lang='yo', is_markup=True) print(translated_markup) # Output:Eku ojumo!
diff --git a/dist/tranzlate-0.0.1-py3-none-any.whl b/dist/tranzlate-0.0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..3c6752eec2fd8a9370c7f76a941ed16c49228e4d GIT binary patch literal 21542 zcmZ5{Q;aZ75aimnZQHhO+qP}nwrzfE+qP}*-ha8|luR<2$F8YPYO1Ccq=7+D0000W z04xJD)L*#>y5|2oF#`esp#HbJI2qb`*%-Q*((CJ6+F82j>(e=Sn#RpZ0W%_u-Uapy zA<#pyE>{z1Jiu;dk2BRyxLwZTtz`CqS;vOL1W633D#69ZiU>aVgNMf)Z+IUpqHq3h@mEUwi`je3 zUIn-pjm_gdijwneC66$D?suNz&tpZ^M2`)MBN5q<`~dS)P);1a%jB4lwM$G09((0t zB5pc>7s6kT6RYr-r=i_%hcHZ|_FrN(mnU_NX73SvOX@6Fh8M9XG}2wgbBm7a;csa? zQjYr)T**R_RWpzW%Q93CiYbc8(mUP;-q+IaHoq1ELKG}a?k|s}gY>zeS}Ob#h683M z;4U$7Aq{t3^}6t?xtl2gC5sMb5L#H;^$JYuRN?jDc>G=Eo#td92_PvbVm_-eW|Pqc z^5m3%^x|N`S-V~^FVJQTvDHwH``bAJY^grNRqZiEWM%CVr!~aI0wtO%{{^j_pQyr+ z%wEe6XSptvf27GM%DeGrEu&SS>e7josB5LY&KCt0?EHC!TDfpn%5EIXEbQF)aA>51VMQk?l7MLG(@%QL(5srOnZ(oVj>1lPTMMLL9FuR6N zvzaPvBKKH!EnIqDQ!y^TZfQFlT~ n6_lw|5>-0c5N}!SGP|x!! zk2fSAR In(=)M zTDub7n_>{fs!B1kEM75b@d#=!`T!^Km>_+q0woeTLqUr3=l|N%mTlXS7=j<($AKyL zJ}`z1NDzS qSWj$@yN;)p4)tEXlRY99VDiN&%N8>7Ch$v z7*n{>C5NBJ)cS@c{R4vsgM|8YPZ?KlI@cJ94Pb5M8|px7ViS`$7m`q_OE-1`YB~r= zC&4{JDIyMDZcwA~N~}(U>Q`%+{Sge_1dSOSN6eSPypA-~Nrca?$UI4zD8$lG-CeCs z-J8py!f7E8A9j%siY<8?6*#YRLWE>tet)LZ`=AYd7Y^N#{qWD&pKKhnb@fd3&DhzS zr@HGm!mF{r3A_6`KX1%lEwgrXALgF?_h#ttnyx-(%13r~zI|VBuO1ezKR=Be`Mm!} zA7>pE)waiRKtZ}21nDjTQBn|)21z9xKxznK=oq@CyTqZpyHh$xKp9dHX1MeWjUbQv z-g?h_UH5s;+Uui{8?b{(Ced+0j^z-Xq<^CRfiV&;cflVx%`{6110XiT< zT9!#LK6L4kF5Z3V*S=jxUl|!qd~@`=tZodAyC7CanAIXTXD1U^r>ReK2B;v|@F&+G zke46wLP}P6Vg*W?X5axE2qJtQw``J-*XMUxQ%U4x6RdfZbrg5}ZFhOyT=sE4$7gBY zeSRR(;HsLb%newmovHWq0)Dye?w5+g0Ho- Gw=!R!#_AWAx3>)Q zBM^5{yKp;qiS7$u&}2(X24IR(tY`LOZh|1SwN2#R DpSVhlAK(>}xc#HgcEdRiaeH09XHYwXxD0|= zgvSKlu8>u7PYIFur}ObkuSQ1_`v6F2YuRVaUGJkl?s0*LY#tbab0#Kbwl^(t?EELB zKlyfK&Ah2U>lR3taAU!6$+hW4_rg&qda*ubjY6jZqdw|<+taYqC|i9ePkX8VK+=%S z;=VV2UkQ2XeL{#YIZvsrAgi3Wy9L(m$&&Jj{nj^XqaR3c2AWG*$Hh$_#C|0ESTk3M zvL-+`U6`x0EqG3OE$M&QED%3aCREv6D(Dn2WE@q9anuFoF2}x-3QV83z@w;DextjU zqm)3>)+b8Qt|Od=yjL^AJ)hm$j*!XI?m7xyEycg_P1f|8pw`h2cDHUk0_0K&j11~M zXk+y+Ty-(GUVj4KSSaH=R)uF~L1!eLjyTx4a^#B|su|cSn{_5eTs!e2 68)gCuCbkZBRDX83JwA-lAo-%D$)nT?5^LeIMyT% zVup9Ng9nE0%>FoOlu~C;cTIB|(V10$06_eaI8}n8>J(`$&j99Txdw>{4A^aXSiY>h$skv;KL3 zXoYO#wGR?+%iPUg5NsrTm}4 o3r0}#35Gu6FPVfG`pYT1Rv`F8)|cd_4&Kp^VF zy&u~xgdbF}Z?*$yN@(5@K|$n1QN3!E3FY)|Y1NBIRQSXi_F1&wS`U@e!NH_-;$~%< zVzmb4q8objBN>@!4VpU}Jt~VX*yQiWR?*@4phXe(vr|b~9W3ZzaEcJ=u*b;{o#r1W z)%6I%SEGUsiu=3EdvgPb_KSfp^HknrXXOT2Cq{W+ZWhtarm83NP6pK&T=Am34a`_@ zg4iVmRI#JJQ#vz3Jn0-;z!brKX#vA?-`s l!t<-=($tI3L-RiD9>K0mVJgy>wnBV`_qgNoDCjtGn7#g`3~xn>_WSyT%QTMxRF z80HO;X-`B9?b*ql>};FqWJ{gy;CdY2p#5DxK$J+lopKKiZH^iZjrq6w0d)mkS$SDq z*@>4%jkWepER(J?un@c)&S!t_9pC=)AH?zPr7=Gk}ng*Vcoz7G_e{@p6Uo`XKw& zz8=-klWFuz(+pL@aN$+s5xW}6^;9DNI}RagL%Vv14$Vh_9D-K*N5@m?MqBP>n~p(_ z5vJ>jAv^ZtRVMZdyq^lcfx3FXVij22${QsY(2XLf#{-xyIEtw?S=_OfUe!7w<=3O% z-w@`C$~GT;hNa9tQ)-Dox(X%N8o9VfU)d1LA0EymQU+_W7cWnWQk4$KoCirY8-4=y zx5pX0IgL_#ie$r|A2qZb%!g{vr(}`sm28OG_DnDM!c* LOCFy|{=nBn$tG|#ZvG@5@+vNO%IH=tNbTd rEIdMtVY(P{Ux3Kh ~MGmi*MF#*LB1+PN)C zf(dx-a9YFXG0F$6aKiv#jtd_PRll@`2YIf(eU<;yQk-1&g@YMO-f`RD7e(0VAnBlz z#S3iz$GN@u*T^^S-YD_&u0^kV?6YzSc!X1IK!@-K%lAHW5nztQorXoO3ZW~SPCU^z zN&g!&R9Ej=wqVn#Ln(p8lMDWHrgKn~)obr`St6fci`#VvU{*J6MJwszKXXQ`;daVk z#b{H(Di*yi>N55ODQX+xxRw1ase~@6CbFswp&-rl31N4t{4SkizV`K0*-?ZIZYsT? z5FBMvG>DoP&Mh^X#w#Toq~WIZGw-Xe7cC+z3ZW%~+CR#2spHVlB_~h^b(;EHqv8vY zs_s>ReQ7#=Ji0}NTtR~6#a6tm0%m<1ho-6ONc<>g&@qUdAHpAxJ6~wYqzN@2r@_bK zfhm6Wdc?f7z{@MI)@ewt7qY8?b8qE}rXNsRSzM--t~XdfCMCw>T +d z{J;ue>w^uoJMM!c#E;E-UxxJtjgwToe+JaesQwt#;mwf**q|jNPwi1_Tk45*Rj$AP zQCcp(9`sJIzhB7j12&(6as)S3V!?b;GCMzP#8+v!!A!~47`s t88~Kt&M!ct^KR7J0Gj#4BJy%k?ZqI;F|w4w_}v3i zCeD`QxL9W~3$aOV>miXV%{cL}m4FoU^ v(PAJ0*1^d z5%l4*#8=Im`v+hSCp|Zea2(E2=9Z;rMv5=V+}??U=XTt|-_W5_jSK@nFz$4_0!~?d z%|W_(pBKJh;iyl4Dml#pei*Gf+pM93^y3CQTJ YJRNB 1vcRGs^lAns5SC2a`Z*KONcY@TJv z5g<3xEL!NL#HT 2kCl280 t1AR_zD!$&h7&30OeO*4=sDAyU x)t6_d8zV((FJ@ zAOC|=^q7bK&q&6qd~Nhb_AwTll;=uPOw?(pG)3Kf4=A6f0MhqjTdWBeJl}Ffa_VE~ z#l%0cMI0SbOy4KltX{>NUgoL9GwIK?S8bdU`4MhF#$)9Bddx19$L-*Y?VU_ky27~a z4Ft4xQ_deN50^%7`Ul+_6YNrta#wc2C(rAWd6pm5)549BPnZLo?2OItAa;I)q-pR1 z&cCY~bQ=+l6ic@+SPKq(^=+ sX|}dY@pzb!CStV zClPccAAge4Ur@G26#m?7 *=ZUc?)j9R?@{M(jU%HLr{G&zu+X>;`4vybW%+=8eU~lPe zY0vNK>B_^UGG0Ea%&WZlD=_T4nNZxV$DI!b8rp;30<{&MX==;+PPb>~38Fn+^ZUT| z*!XT?ItH6c^01qhJt`0@VvJaxI=*-{yYCfGyUz6hFsp!etf259q~={?h}yP93q@pw zEh8;$RqOq+)7;a!05dDf68G_b75Z=GbxQT{YGA#*=}}lPe2=XhIId=4gXvj(t+& z@&g9o=&`T{rUy`ou-{XMn8i}IB%7sCT;3DV{{-ukuK%gu_Gz`}(Z({549;{@{gGKX z72%XghtzcSj5kNcP@T<7_h^vos5J#v9rFUyGPyT|V $;N(0OkNjY zzsuVy yC3-e#0zXtbzp$&nE-$MTr X>RPIpSbu(jeR~_ q z3lLc##5W@tqAV$jq2Q!MvPmkU!eYW82jCz*<#0#&gCsI4%%;s|JO5B)v*|_Grh8S@ z%dpwaPt>2xdEJ*kKuV&NSs8$3h6woe`#AU9^FHVHp7fsl@yx&bHt PP4hSYzP`4xzW#-G_r+)U6j|=4F#H$4&CkZJH_5|Eu)n^$wz;#u3IFfyY;J7r ze!29;;`8_a$x}c6OXTOl>RNBDxBlc)o&Vk4-RAQTf3I(CZ*A r@$A?EJ7e|kN{kE z>d*iB+V<}DF46z>Ef7Jrkp8c4?E?K@Tb%z-{_$!)FHhchFON=+&JSLBXK$VZN_c`6 z%S&GU=er=y!X)-KyWYlE-v3s_fwuune|ZVsI83IqG`zaby_Lfb{p@{{27z~xjPqMR z4ZLrXA|ClUyx;YX<6+NxdY$LfZ12gFaW?KH>D7~G(9O|Jkj@h5ILy3hkWRup4@O>| zcthyl^W%{>3bQ;72Spxu&~^YlPViwEWbk@2_VR0ZH42A8oCV$}85WZu&buDG;0>?+ z_$rLAyfDZ9<0SX|C`xXFQIFOz=W~_@{$vmZ-0t!^VE6@bkou8#Rt#W7@0E-Sef!=R zR^O#*N5MGPGa4u9@=~UNf(uIW>mc>sh4CoE`QIk#yR2u%kry+3)0-q&?$x|KP5ogW z4q+ 9U_^_hnD)lF;tI*Q~C!ZDY z3T#{&O#Bd9VSg4R5jxmQm}g#*L02&Q?_mRQ&drDjEwRP&(wt>!xWl|~A!^94aW?*R z8UgCT&{;xD^W&N93BnL&8wGxb070}w_zz|@N59Cg6Po1@N#PCsn7YDe)D3}5V35Ho zl0;DA`|BX~ZUG$Az<-BRLOAF-b@34{G7ZK-nj)?P`~>t}#Q5c yEwVk9gh<1l&}YYtj}wLFXzE9XPhU3n9-)h0Yh?{1~)*E7)KC`0dS)rp5a!} zLbzLg6X)t@@8lVs)*?;ugr&45Zq*|~0Xv=`0$@Yjn&qWokmhhy0EpQX&Q&-FqcF!k z5n$JDmJO;djvtN*8{r6y!ZG6KUbRD*82*Oqwh;mk3@uZ@^9^(t-1(Df1YOUYL{<#1 z4GjSJT?g3D@)CT_Ljnt(cW)fXiYLI~y{VtUvlypC& x4Fu}6h7iuKW>13v&?9{ZKlv~jrZA%}? C?+u*q%@*{-^;fWqmpm&i>2z24FhCm|;;l+9W5E{^#1Zd^R z6Jhab5ic)Q*HSw0_(nJ)G=o#;M=+=2>YAEN{J0o{kO5}`&Y_UNEFs#1j2~c*Y!!tq z7!o!;9ww71oK^sXpe6L;he#EW2k}M&Qpt{3Or(ZrGl}!E8Q}E tc4Y?TL z0ZE3#B1JibhXbjNL7I$;Va_rX2wH&7qXJkqfgWB%3KK#fD0Cv#$O2fwEn^soip2km z7(qMDeOQu9b8iD-hh|F Yp&O-sUNE`q^JRv+EY%l_3_D6_eaC}fQ<9R`EH=#PbG^lzN`x+Ox$r?w1(&^~~ zPGrQ%6fa{6RHFGTe#wy!W1M5x3sA6E6dggDU&GvBPS6#In6#XTq$7=tQ+!P1@FIsl zGbE%%G%|9#Y9agw4i`*DhzlYj5f{RWu_Y-cNVG}pS)CT?6jzU^3db_dSWh7ANire+ zql6eZiY9ziZ<27t*a;jDw!j-9zNXw-O^F3V0u$>3dhJ781RO?WF@OaD&|E-)8$>y1 z0@7Iky6UVa(gaHjxNU|)0YNVmfue&zBf^;!I#Xmkuq7#|!Q&)e#o1U2hG4v~C6dk( zI3hU_2t-RH?{HEr3XiBmB$O%2kfdp7i<>e}mAX7Se|_N{oV@T3PfuPPUml;HTyXQX z9`L(y81t~wN9)q!LM@hQX|vVA+SEs@QG8cjY5latvic I%sLlnf>zf(y BvKu!1=n0SaWqg}fGU<`%SS$qPZ&;{9nE8K?!bn;~}C^bh6?nztW^fAMX= z1Ln1uc|$)uKZci5#7S&L32fv~=`iA#sh{R*L-3vDB{)cc{C;+go5g$;NxqRpj2zJw zNCWg^VK^lBqB01q3&h4Di!j0pVDUvn41}f^BK}7af B=Ns#nc zYd|_0w#1?BRsxX&uQg1dJ7|T!wgetQD3gXm7wchV*R27lf1sqiG EO4dKnh?%@s>y=5f>J(0Rxb*!d$WdF%>une2qdvJyO8Jc#PW; z5XofpBkDDzCT6qXbO0T{Lly}C1j8a1TMAMGkO_o{dRp?s$0)^zH$IE^xOr!?R7437 z5~2clrY14VOAd`$p#?*KvQ*EeH=rj9j`%lz7|~=|@&||z4Pe0*kT65h3FvMNU89CU zn}&?{hV>UXLbpK_SsMb7x(UkIk4G0Nfru1(8ALIH7-t}zBUMh7YDAlXGMFf{tmgnI zUXy?k1I&C9dU=0+VxY($QTPIWquCR%-W;dHTaS`28g?_3*~w L%bZ=m(JM0wnDqUeAD(988%D;g-E3VgDpS2_ Aqme1$`Xu)tUSvu`aEbHD&7k{ zO~N=J&W;iX8PLYS_O)h(`iUN!jUak-gLyH{K I7EvH3FfrD}uZVOYC*X z8==V7BqD*Bql^)X^kzgw8MfRB^=yc?;yXN9()@s5g~6@tk2X;n+I~D{)0){0V96Ov z5il@Yo@kwb2q1@#m~KyrVjETjM-WbM(PhP%;SSq_#uX+z)YT*iNFPRmnZnZ)IuQ(6 z?}3g%TI?Zeu|`5@S;}ooG;f f+`sv6!R?ss&SP hFf1ZpTl6LfiwLB~!oNZ*OB{$S zhc-e$yJ*HToIgpRp;4D5i=%_yI&AK2Z3Gi!>`bh3t%tM7_8;L`Xt>!LWMY=Q# zre=s)pbI5YAV*=v@35(tULPq)#IQj?EM%XM01n^zSIh!l`+o*V90D0jVr`ArDi=~K zBRjwd)O>l#YEK7$&;b^f-XM*QSvX^wD 1Z2E{6ZSH&ROH=Z8K z9=fqGt*pXuJmxpdYK)~4VS&aVki$yzXbA%0DNJD4JD8bZEgThWin1Lkctri_rRAlS zccAeG5z_y71f+%aVFnM#w7?U!M$b4n^bl`+V$|FiItdXSW*ubPyhx_$E_S2^(iXyB zoIn@_snRQyhH6{|+z`%`ImdkVfGp(ZMeNjAg9up ;iP97ZI8Wyuh^{)kVn z>?uD~ZUO;5TjhiDQDaZm2-uu}j@^Z`*q@+hESfDZMPZC~yQ~;!@F>wis}btt5L9gw zo7e?GMpYaaT{&<@Cn&6p{G3k8q==P1OsX?u#2Bp=1CZW=AXa7`dlr@_4?vtbFgB;9 z4!rDGu(3&N3(9JoW^Axa19c|Q;+l{hjaJFpTy=P7JU~qv@V@!DXSYla+46&w8O`zb z0ylczb3X%$b*8l$rH&qeeklg-D{?80Y6LDqQuSDgeQ47lZd5UTpDEu08K6;cgtgox ztn(=8kI@W`Pa`_r$|{2GCqb@^pK1nZq6V1)rClGS3pA&YalVM7a1v4{uHjsfoT~09 zS}y23Ao=qKLF1U%DNe SdRHL*BNfh=UJ+9y16-IFxd`#5 z_wo{fsmIAJ=-F2RFK&5BxvIvX>4)rFL-CZ*36IQ;A2AQl3|t2@SFe x!E0fUz(1%>4YCa HvM5AFf$_e0w!^6A~^)2seEl? zw&ur%W{XM6LR#ek1;&krZ0LdMC!6?68VDX!tt^r)2MJmlvM?w7gTcJKB$_1=HNAs` zBy2A&GHtlAr&-!M)(NzOj64YrG~js9HVuMnKN@%CxYJK;(E#|NuYIH@)1`Aw%VRUF zWn`c5`BBCKw!g8bQ5@}hCBeun9xzBbL(nxBM7(R);gnevyeVjVsG%p;6CDFE4AWsT zLHz+011F{hkp<;^e1mt#MpvRauzWPAdfo+zm#|qR`#X^bG3b_sHs6IJX4X7+*6OaOLTtvY3Bf=qA_LUqKX^*ND%53ZF zic5Wee0FM4lN{X&(CG*iWU}db8*APR0LEkh@2`LL)efF9;6Ff;Q6(druR<4vxZ+G8 zW2FNN@iysYWOi5vpE)8GE)``T^{tQKz!i%V750y`5jYHk5IIbFC Od;H zxYe`H@^>n_?2zqsE{l?VE`^|} a|1x@4tL^wTRF{vlwaW)Ny z1^bHGaczub=rls&kX<9D2k0e`NV&PDw6B+!-UY!F_YUpqKKrQ2vy7xiOOLLAg_2S{ zLnaWfDnaH(JB~(T4Dg4;BvoR((57AEP+_bZ&6|P%(I0>cJPeqi%(PAKYnsRvoh4KO ztuT*z6B{&3l<~Z3e%BkyAxStAMMxo%#fkil?8*Sw-WwD+m=n#UBMzd-is>{#YM2^h zqzP{@;VUwWZ5#7mD`8MX>hIh@fD#JYE{2M9E0Rgs?q*c@P^3%eEivgRo12Zpif{yY zlth|PyS!D0zv68R#7#7oAQ;2!_LRfW?~u&PmPM2ch5)#kWz4KaQ5~BUqI`}0gkms~ zs1=`r@NO_Z43xA$`zHq
XA zlF2+krAon@b`ra2H5{duUhS}BqMMZPOH2evW2!Z4%hqT6A+ZCsGIn92$r2i(h{=jQ zoRlQXf=mTG_{LdR?twyLauq$nl%Z=M(vnc1iHM-dh#?B(n=r7es0vNoW-y}MNmMAQ zwq3P#SAIH*Fir-=bPm9pvE`ramJ|-=Xg)}AQ1apqTs_VPsM36`Ku~`saRtWqBbXY4 zaugy~N|=Y~Rl?&asw4|w?o8}eu$nQWdETglaUy!`Yd@Rg=>c#NA!EUtJulo%qvPjm z1dv#-9QIZoN-auT!XL|ZmUbs|BYV~&*ZpAC~*u5e{8MkU?> z1wio!j)EyhMxaGibSKx$MQSt({V{v0NYHWurW~<|$-mnLOl?5E1m&_-HY1izCg|Bg zexqFiT~a(zaYmo{O%fFo=6pbYk`xdUpE=G^B{Ep9Zy+cw|LO{{8(qnvnydj60U*yT z_qh=bWbPztjf^lyte9iUUpCNUok0ub(_c` z1n@t#C}TVw`Wz5x9Z@7rwwPhLVvSItdJ-(!u^I0P2sXlVzky9^WVo*VrN%<7xg?V) zG6o14%Yba-E^Ie4OWV6RfwKyDw2E qTk3(g&653I3Q4H@`vhlr9k+OkK zb
ketC|iKc9}Vq=L}4G13SVL}gzsH=+C3xgM##D w6_Cm|y^UTC72iSXLP(daoQdgsmvs;fCvK9jS!x5vaJhmkCD_%cII9fq$Qg9Tr zB$n71cFwC&Q5&;8DoXM(<~1!%6MA9HwF%c1-D2)hKvCUSaUkptB`k>(QDAI=PjK6! zysnmNZ=r;qSo>CkniBMRBWAJMIXWVrA7PBHPjX0@DVr7BrIX>%&q$7Ay%yd5=s-bJ z1;>n%vWwj)JGLFJU%vuov-%`ybyOC^O$JJ$+!@$HR0Am(h)PH2n>S0gfxScQ9UzpA z(6_ZxPEz2F>991)a5Q#tYyanOz361@Ht}aFKFIvYqZ2N0H{&8@BP1gR^D!+{iB92U zNjwM#OWL#rDh^mCe>lxWX(y^vm%?ThMD8{sl^jG4VB`ZIA|T2B#fM;pjZRD yIe~i) c2kLBLdaOuR&POT6eRio-fWViXdk-69+#t%R^{LA(br$}(|UlSBmr{Uf#^B^3V; zty-I4>Q5XV6(=wNh@(Ep1LI;JxxSnVg>)jP&;@{+9CFzfO~>Nm6akqP6IM{DiPDmE zeAe<(j`_5-bl4x#$WgZo;IP@zJs1~cb6(XLxUD|{p4_Ems_O(AfCfKO9< 5ozh{L7loKvfnlgLb-k7YEGKFlfIXX% zV3}|nARwBKdjgCdMt+uAxh~z3jYD~|3zN`M8s6h|&~fo8tHeYHm70RaJIo6;QQoNF z4jl)?_6W77?0tZ_Q-X~oA>h<7B+AH~wZJb;UTvAx3E3X3JQ#{U7TE#${-{9lo56+l zYCQr^xQMB55*P3n7*K*P+3!IT2O=ApP1zn7NF1hul3I?$`yX?d5$oB 670zUz zH!H|-0!vFqXqbZjhQSEk5nV}%ldyd6T`*%HGdT=R=ZX@Ktb8xFM{s~s;MFF5RaIC* zB4>(7lQ8 =bvX1LbUEEt<+OB8R(Vu5y0*x?=@e*^97Lu%`7_BMg0XlR15; zQ)l<53 QoOzLToG~?Q#4dd?1ng<=n$Jzq=AuI%aP5}7}gb}VnXZUro!y-zFv`s zXvGOurxXcM5k>)7SZ=R_xZ?jp;uS<=9q6IGsv{(D0S9jpD zL+*{#VCLo~<2tW8j$KTMM>vwPA(By&y%(}~9YR^l@o`fLYm?OEcqEP7j; zS6KGNQ=w|60Q{&{gSce8QW$h|3^e9ix>0|k`_lMI?*!_t@+UEG{}!hV&;nM+$Wg@T ziJ@=+G=55ChA3$Qv)!I|V7x(>fic;(tly1S1=Hfvz>X6_l$S{GiuqLM-ysT1^blu| zaB3t)O~wJ|JEei*i^k;#bE%(( `^hYdS_Qz4J1_Imn)Q9I86iBQ;J~0xIbo3YU%Zhm=m|d7O=Vq9$bnq6&E=F2I=Y~ zk+7cnk(7Vk&m}<%$sZYzFw}ry2j>>W;*|z;5fCa{oC!u2KUH$8CIT;_3MiF7SZ}#i zYb=Ga_d3S4K?WP7fwki!?GS3&6+gBZSxDC^dBfbOLGoa#Vi{ynu8m7E!|7PF9QTuQ zZ&ci#Dt4sO*UNqmuGOOg_$bx}W?M5!y_=K_A+omNRc2bKkOnD-3UWON>Af?TbhtrP z^vL$MypZ@QQb$#E2s+o$;YPVZp%ON i(J{u8FgTrWZxyDk _z&l;-8js&L69(ru!9s1xWu@e<& z)?<}~QJbgiziJz1ZVU>N?#x-Vx1zF;OB<>}ggSgQxw;t{GpMY>M3_5G$P)NV66{#R zJf7JNg}ZWmQ*FmOAsfW0ghKR{SsK690>R3HU; U(u-G2~pBG4De_*>@<$RO$ih8KQwh`AMuENe1Bn1ymtT@HOZ3IMLpYsA$Ovb&SX0 zr@MGNPQcDLwh9=!!2%SXS2J(a{5VQKE8rmBNMKRZ5?q@HO_^omq{c+lq*rtqDYyg| z$`Lw%PzT=G>0DikX#qIUMkpIa1&Q4<9DF?FebPS#vCz3WB;3MewUUd)5w9)n;Ghyh zW*w)be89R&{*tJC<)t<$$yR`tlF=gVo=(+!erBl;U$gq_4-j8%cZ#f8>=M9JWX@mq zNWlqXHgUL-DU_V2(_~EPt MufuV4BT@SOI3Kp>8hOA~jJe*!T *+}xoAWp78N&0-PV3~^yonjK9)P-axuw@!Ai;W)HpU7sx+H^;tt=!()4owz2 zP -#oUmB6v=K>5}6AmW5FE2^=W6 YCf|8t7y|d 1k+DVB#W0PPpEX31k zy46tLw4p2sOf;dX*1~yO%7AF720pKxI@d;|O^4>365RB$LsPTVHbLQw)H@9i69(^RCA-TQfHY#+j6hQBvK-?k~H__Er;^5Y;p=)YEYG&*N zWuxT7ko&4fM#d?nZ=tFp60GO?LJGK?la63U5&J+fM` 1Hg@EeOx1J0=c9eIsv zfoY1=kZ|TYbsQF~=~re+##L2S>mprARqkr3LX6z9JzPJBM$HO#gOJ4Lz}Gz(Qa?6g zDaxdNams2)z#GimBri+wbZfgE#9j++g{XiT7gUS&1=}g5{a!p0XR%6wF}n;J3vEt~ zMHCOPjw%NdgXXE)P-q>CC^el-g7k{9$S$%*bg$|BN<|?K&{m;lu~%K71oE?=Do=!` z*y~05Y;CnoQ8*w5qm6X~3^KwqYted@Si!Cy4g;9AsYvFC(o5OCLq1nfLV$jcSiPVM zhh*(5tP+G$*Hp`AAa+Lu&P*~l%Q+;?9z%N-8gm CaP$oOBn%(8&caCA@Cgl2MKeLs?_)f0-w5|f%;kPinkfm%9^W_`Wq zo#}$3y5wKX*3BessVJe6kcnqZ8;~jaq0TH&V&qn3K&J@LnJJG->3qzWgd$LKy)F}J zvOZJ{g-jHVr#)|71JTNwaoU 3tps2R0gY_4x$e1Z!mJ{$LF*4i z8p3@-X3JWF`njvv2$nA-AEmYVB&UP$7o&` XJsELl=bu16;bu1kTzqUm$1VjEb6pUMC&NvZ)6e1jbn^Mwn{ql_*<+sQ!sE;Gn@~iV$y8b_hOskk zp~Ak3pOI;+ <>DYZ=RVsA~c$Km{G$*Gp2y97#FQGA5kaQE9dMZ{Ruga2ZST3vr z?V428T81}Kx9FF=Vx=X(BCf38Q0$C=X%NIfrHm1`gc`aWzh&6b<>FR0^$=Z~Off;d zhfY<49CUs2K%bDv*1EJhW%3=c^5mb&NTaGp8e@+*adh1yslJOmLzxpwIOa)F87mIE zT(gNad@M-IvYN8u@YFbqvM$+FYAWkjnKfyp*A7PsF~b#D5XYuVk#O 9OsbP!UH*#YFkQ8>mI@&~{gB|c3WJ>?7D@~+jIbF9&1u*N`DPF=?fGAp|3ohDP zRz)R`YtV(SSK$?eS;<xzBYy2rc3sl)(d|wXl>+8WOun z(i!Cp*Oe$_U!l}nge6+hP8{~$)irdovgX4Ad1h5mGleQxdN4{-DW1w)a-&10O0l)g z`b*E%*vec(04ByE5_XL)ja YMgQEfl!)ql|*~Qa_20>byZ1vL&A-1mx+rq09KBQ zvh8*$9m__e3MZhNu~jnRrX#v^P6q``q|pjT|Akb7d87?ie0>A{++ntYUJsZFWqV4j zYCNhNT%TBdI|z#>DwRFcl#*P @as>v{+cms?+J;9X vlYrc2sJ$;60yrFx5O z?-)sGIB!MimsCoyph`RJLb0SxYsMWFfrnukqj0Em57ZcSF29*BEeHTbj*KH~dsFkv zJtM2~r5uX*l*E86k&+N9yDkeSMU?yMW*`nUtQ0|WO$(~jj7pV5vj;8TyeGV;;sS6C zN-{^h)Q)P%^y)9{BKVSJ(75SUj-lLOhN5v4p&i&)N+5|!Iw4g{M%E#Qw}9qMETMp@ znH5ngX*qI>A)6UB=;(SU=}O;Z;zCO6@#NMRr+BJ4$W#T_xw&(0p{VuE9!9GhnG`P_ zIY0$8nSb}l2SqX(`LkSVCVnS`3JZ)>93qNWIhTs4VB6YzQ|2zglXN;3=P1ca>YA8R zymrNk(!Cs#57ZD=P0;~%B7#%Bg93_l8evV<6%{9}!pN&S!UGnF<$RUjZ<8XIhC#|f zht|amT4B@58Amo)^Hs2s=y=X8Q>e4m^Ued l9Yj{Rlxdjhg6HWz}k*FTy1$t2hU45 ztCAjdU^Kts=CqXn2EDitYl-z3Q$CDYRo?(gml>FI%M;-saWV^9Mx%zimPBn=Az*5b z%S(Easj7wN%t4`qF~m9uw7g_6s2b9zC4Gh!3dk;4wPe_gOeW}Uc%3M>i0Xw*T6%^$ zcdH4<4ga@iS=mTHp1T>}#0gJOK_NoHcb931W%u6?MSVzmLRXhVxtn3hNy%t&g4JB_ zt^t-EkYf1*G(%*-Q2*)`Wj%1&%q!YjC7tPoAat+924hEjQdKC4H@10&Q*=H~oL4M? zq9j>SR<}fUCpA$xBtbBV*O~||P2Dt0SdMazgQ1EHZufNPJ0sTj68x@+-?vBSN8a&; zcXH}|e{g<&aB}&B_s!`!KJd=YPhXxNyzY9Jr}X{kA1{wiF1@p(^Vi3hmq#zW=RYhj z9h{xLIzBvj{_4nkb?`l2ob``~M`xGb_un3!c&FI^_s17U-o@nszHxlweSdy@d3^Gc zdOSQm`{Dff<+qp1OW&TpdU144ca=SX@##(P?BM+J_~-&>^4;-^BYRd}>)-+=-txXb zzWny|&843H^3v%y(8~|rpN>ynbiJcv>gnho&(4o7E?^4K`SEL*^AS8eJ~@2#<^{~A z>ph1qPEIeqSI4k8Xm)uDj0(q-#;Sju4h-=6==|_o_ 2dJ9cC)-v!PttBoFFnCeTS&> z=HwLu 5JoUaC-!W4o^?MJNn^*cToWc;BD~t;Q1-S=Q&K}m}UfXKw#kpzBqV&@bc)w!Vw%4 zh%(>c-t{idjt-CU4`=|m38?UjA$D>B%f&s0AEX!W05%)DKt$yYU0!+v#{!Y@MBxyO zj=$N{T`{9q5!8EidT~KJ`QqU6z@tgSzt4}b`T5Za0E>>?!QtVXb2xt30$+joT)cq; zd3?fKf=i|&e|-J|a8=G6A@Mf{$FJU;myr<1J_RshpM;Cnc5sV}4xApu0q^)580_#{ z**woV+CO;T!hSqIf@TLVzB{JVDFeYYj|D8aK ~1^%8$V!sZqi?vY5|vM|WSKPb w}d@@ q6Wcoz=a(+PjsxLG_ms+}6c-abGNsM(kDj zV_I)eA&tjm_Rjn +U342GaS5|z*RObvZ0SN^KV5@+N0 z^o{n;c89rphqua_s&{S9m9PEG(N9#iQ?mxpRf T4BOGB>QT`O~&n1b)#Mr6wG4v z4QAKq)=&vbq~Eh%02?c1gHg9t0v<9MCFFgrQ;)F{370z+GpnIul6qDa)d0p#lA31( zwTmDynV<92dgVM}rP~zeLRW~5=Q?EDjSFn o3ZfN5!zHjR~gbE=@pg=p=<%2*Oe zXZ1dnp`ShccvjGoi)j;Z51wC~zItdyfR?N4*%U3C cP%RdRHjX_vluo?0|#*0lNrWbh?U&jZ=tSy4AKq-qoKNt^n`EWBmW!Lnc|Xb z3UQ2F*fK!|x;j6WtudFoe4`!y?m^-^!>wCLS*XJwL`~NR9&dKfFi7hm2bXP>b7+t@ zDRHEpU7!tUhrLl!t lB9cOGUIb~tHLF2CH?_#hgTss2$NSBz?L=|5izbQ@JBTc zBdl0N$wn9zO(|&@?{nXl++NRC0o1F})iml|=aUHc=!^2F_oVmak7xefx6osde(^~@ zYy4^YcWrHR)BKIUudi*auYcj)eeoGS1rl)>{)^w{XJgl!ppAcjeRpkhXMGd?-`m;T z*xLPa>5IkZ@Bf2QSbssecy+C})?0sa_NSMt$0y&MKKA+F+1Y70|La?u8yjD&Z*6aF z?5wZv>} A=nwzsje?s$UXf@ILfkXF!kNo12k-IDS(@N*t8dO5PVb2a($(jk_nMekj@rwn3L`RJ9j@b=*M86ZWBe{H%iD8UJyoL(Gr86i|#;rkpB z^k&mr0jc(8v*Y%5s&B)Va>~@LcWhq?W4C|7raE%|T~PI0d}GU7g*E5C+ZL_A6smbp zj6w>p+0z}+jVN~G%VY}Eg$CLHVh^>qXD)!Ypz}HzVUfXxp5EkAw%mPwf_K;egluRZ z#aC#MDRnfPB4GCDFSIX9vU9e|!|!>|_BVUG%S$f;zVsSUaP^RrCsy&5J;JIdh&v;y zNG7|n`VC+gtmydg=;Y$a>h}dGp9Z&+G~3_b?tR(&s?ubj5G(@(+uGmX>aB0|HY&|B zDpA-+WbR)B?ngoTY+p#(^3v~rZ<9KpL*8u)qCZ_KiN+^W!;~kZZh}Jd! SZO&*ad(){cKkNDFWUnT)+53F !AQZHF)@o&vfZ>6wwsLCs;#7erx+r+;Uq &^>gCT NOzHFpd+@xX$)iP@;Tl(KK22vx(yTKV%5*_pqghM<+5fD3I^0E z-Ki}x!~qrGq^#mD_n FM#9ZD0i6pi*5~UV|;IV|q>ZrwA2{XePzhr)SsZkQ(h6aY}DjsM5+svTb1`jUT zW`s$*d~*z*RRgK&L>`2R(-e;6O5GHC(#uMxLulxolGSq$|NOCy;zGOYscFJrEHU6; z13w%U_(zspCG # KOp@Q(TW_zCcm45JDR72$ zKAk>umiqL`^qI9>z){_{cF`iVSC)-e1j?)XmuV4Hz@U3B0Rtvv;DNJgf=MI#&jk^M zpboMv#j>4V9utCi%HXlnzlK&DRh!<_PqQFJ46%SB@6`gEPi_r{34}Duq>E0;@gi}` zRA^RieP9VnVIy+Ehn+nMI8eP{?}h~Qv6Hu5`xox;EjwV$&$x)U>dtTSo%gE54yE@h zEfYEJHmIRmB!NGGbUhkRFk43c{Q51UXDjWgpI<-8lPCDCm){{j%hDllVJnDR^EQ=3 z75vdnn5o1U6*kNF*il@9Vl?bQv@e7+WG7>p&ASewY37JWwkTyv^OHBj$C^$=p{ 8(iQP*O+b*=Arlgwe0YcphqeVyKMOKxFvGBno3Z9ik z0u=Rxv>=pqTzY 37pN0=U|Qf#K?Y*7+^Ok zu|DLJ_e1B*57T= ;7 zEH{=9^{a`wlPCyXjyIuuxUm3rlFm#3e?%GCsWwoT%t~y0T*X_BvF;Av8&0`cJOQtT z|MdURo6cI!qz_dk6`8sDu2K&q7NzH%vCBJ|1{`LC7IdkwhHiV{VnxImUNBUhi+k~z z^ZA?b|8MM6{r{T_|Nm$4|8F?{|MkuFuX<~XGxVGF|DPWnym)=on~Xl6|9^dVeOvth zYuh_JyXgPl-Cke#|3AlP;rd#*z80>ph3jkK`dYZY7Ot;_>ucfqTDZOzuCImbYvKA@ zxV{#yuZ8Pt;rd#*z80>pzhBqa!vC}I|7icu_U>0ZYdZ`7&u`fOGo4L2HLaH?ljw8! zf7Uj3x7OJIv%R*vi?m>Ub8Btk|M@JR9|uJkjaD{wcme$4g1I&7iQF^i(_2^k2G+e)hob5;awP!sa&0>EN4u3LrOZUB>{X9qc zqGbe(eqSE<`>kK7Bf&{{k&NMXYZytor75f8t3Rn?m9!EpP4P1s()U{`of33fN*cvq zYEng8-Y?zdrJvA{qLNHMx~q{%kHa-QUg|6W;hkiJsyVi_g4pUh-w`_eOF2nYes)tu z<;QZuXsJ`Sk5+!9`B#4B |FW^MySb46 zKa2byTwV3CJRESg+`sCR2oDT~zl~7b+V|Fw4=&Ei-{a?Rga2<;`Txd(|9=+#|7Df` zZ*P6Iw)xfi;>7<3`9JwCo;?2f-`(A9KL2ZL<@#?s>$_ig+l%x6+o1mo|Ic5K{@eba zt*tM2*47vPpWi(FC)c&*yr0fKo&LAJwYjy<^nZOFWWjYf|Jz$@i~66R wUyPyYM?=n+it6`#=9iL)~#RH;Q8v%EF 20qp| z&yTZX$xB+1hFywMs&9?KQ7cgptF|5QnYY%lmrRokW4dWxCR>>+-`Z??J@>;5CitBn z6^sdXW4dLe4n8e+lvZ^b<)n%7JmmhnD#+?y?Af?;FD$w1u7?*C-vdf++VS@Hy$!3W zM#p`bC&py0w6x32YC25faX2Cuk=r;MCaKf1ezL01u9xL$ee0utD*VW5h*8G(_G{+V z^~O=+&x3@uhG$r{Sly|qetf#`fvD5D7m3jSREtCR?eR$x2lt{-A4ly6lW9I9JotFj zzHeHs8oV~);7QyE3Y|Q38V?4J30D@dJlgt9&n+8)+`#GPmntXG4f1TwlKaupuDslg z@h6EOz1zHo5;X1(Aqz)x_5q#C;gO$k*J{9o8wuyu1<)rQkuwq2A7_`jv%kUpWnv(^ z{JvZJA?KzJI0iHind}GR;bTpfck=$p7Di6|Q1~{9njgD>)w|h4L2j?&0|Ec3R>4ca zwcNW35Ucx{KM5X#oRt)U$5;hRBV{SHHhZF08n*S&(1`Vf5*oL~sW6IL^ zjQiNgci%GaqXnEQ)QoTsIKk9~MSMCRNKov4kegIzju)IkSK$YrkxUtW%C?~QMqO64 z$%Hgdlf&vSgQ)5CzCR5+?>%%FPYc*%yh^!?#arIH1FgYL{OLTkd>$aH*@ya+HgeBe zE%AYJuzA_xdVg47TU+~BiMVmXlwsU7VUwNQJZb8n#g^%Dc$XJxASP;+uH5>d$;;SX zu~v3IouDC0C{MEyWE>$q4`@2Qzj;jQr1kWA{TZ4xpFV* ~yMg8Xm{YU!W?}s3M_xm3i{Po5C`9E7bn@s=LFzyTMKW}X>@_# y_x&j9_o+01d4kmetRe##x5YWX^a4$Yf2~fc^>?OG zF8Kd~b(~c|6hO3wLAs@p5EWcfx 3K3rMtUa8WyBkV5y~d z_j2>?zRbLw=b1V4edkBpq5gB6{v<8xPKpQ>_W;FS_piWqazy5+#-dH|PP#fn@1R$M z%O{T~X>1&698ZggUhLm1qrjQVexiToS{wFwJ;Mytwxwa^vC-MshJ!Oj=O#g+pBYT* zNUoGmS-Jf55%oW^@%E#QyVef{@A~S1KP=Yj26OEJ`6w5KkWMRU(O;sxvx~ _j(d{@`Tdd%t)4?HrRuGfCLfbGn*g{)}vI^;kspz?^Z0r4dG07gu<0b5(1hoI-8c z_eK+>DVfAc3HPgh+&Ge%3>)`IUlEA?XGW2~0ux6ua~M5wjRXK(agAHdGRWie6d7Si zv{m6-DYS*4qw8B(77?(%{FCb&Eme9~V=$b+LL#1`PyL>!d?#+EcD80(DNGo(Vt+BF zGpeuL!;zd}6yEb4w4Fn)A;8&mUSQQH|2c`S8df9Trk)>bbY%?wgNqulLq|(I%nnxI z%gI0D{i)Rh-g)t$;pbG)8!D^?#{5~T)07{T$o0Ojm9xvdexHXbb9TmfCE(Wk2-;=9 zQMO27P*|h@_1p{K!R4iSN>~=29S6}Z_$B)BR*I>nIY-N)6mHQovr_N`bW@Y4XHx^( z+Q&L(8$A*FfWAEfry-hFi7C11(mi1lp8eD#fA^^Gs>+)5c1t= #g z7E&15UdPr&G}Fpq>PqS8eMBoie-f`)8F8`RoI5SFwo5;zp53DycM@bSLo;$cRW)%& zBdT1h+g+B*-ESvYch<-s-$v=qmMZ-^jD>1C{lW#?+|9LHYEs_MeYdQCF2$768Cb=L8sa9XW=aE z>iK?KbM)1s2lhX6rRaNN=f)O{ghn Wchhzn$nV&G|WS2L3Vo_-1rVCAnNq`O0pXiW39lOow~B0zL*N zRK4o|lTzdqI3-xd$D8$~zCYiV#bvbm8Bhpg&C5|W&G8YR#+z9EktF5y<{CN&S;skS z&mt}0i}F*1UUR_jTtX_}gFE|b0;Sq_0fi$`zn*_R6p?$PxL_0!Zm|7{8-oXkt1zT_ z%1<{rSL?)eY%z%uR-})U<*Ai84L_r4^IJI)iUAct8v`FY^9=$T2kXo({u))g**{s+ zU-lp~@yMc@!G|PpwF* 6aZh!3%PtuBdRA z_(a`JD=y;V-kw8s>}0R31gc;N;#$9k7=5gSd8FTzs(7Z`h&a>U17@>7&%f0$X?boF zPj&mN>FgC}Ms``LI1b>i%4|6sg|1D6>=LE$6ClGOlQR_}`wr5o8T_YeSC8b~m& 7jL?)KJ;_s9k0nb3?0z4d5soV10xqayHG0n9o|Ih zwM>_&YVxZGaK-r;FBo}kik7%XK*!&K^4zd~1c&%-cH9qJ?9sgdZc(KSISA8df_UN~ z*9}*oBh@bu!=9Q$3Ah{(Vd!(5vZ}IpVRGtT@(eY6!H+yA`5eHm<0+nc%_;g*+;y|u z_cW-e31TDw{X!d)HlYLGJrb7ws940!Mpa){^;)y8LI;1Pxv=Ex#6ZDdr&erkDit{g z1M_fUomvv=sTYwX@-{6p5JxrP)9mSxl=9*DIL~mPSbwTTgR(xW7_BCcisw%P!ddqu zmkUjje}8*szDhJi?pFNzJWcOmiBaAX?{+nhTWn99w&x*{K)<*+AdW9$$j3~7$Y&-# z?b9Q-i)#?sM8GFa^?tAtHD#qU?d3AH%HKFVZd%H6^6ExS$hs9V =pY_w+b%pIpa}yEN2=(~<>?Jv5*(xp;QDPbzp4DO{9mM&S@)Ie07f}(c zSWqhp`IL7bD-j o)U}@G4^m50i?IwZTm#O$ab8B8~sj0oW8T)WcC+UiJ&i zQ6^TiR9`ivF=133QK2;Nd2_*gfo~z)G|^4pm;x}!(ca6zDW`~!mnEnuAFzM!k+SM1 z_7N^<@Xhj|gXpWToluUL;d`Z0-)ifO086eu_Bj<4dGmw%No#p|)|fHuD-jEO(6@mP zR!ELtPclUeSo8SfI#dFV>^zxN{V m62}1N<<}j&*a=edNgYC8wXx>uAS@8*^{^ zI2y|*&kE|#QfUczQt3{qpnr}vhUj(q5bJd|saIB9aSl}}cz2itd0MQ8Wk|fkQ`AkZ z+#4luhfn3X$Eway;MkJ!gA>$X{%d>w-)^n9MLNiB~81?W&B-cSi& zXt|h@csr?6|2x1Ec$koA#5I-r*{V~e(ZD(gU~Ob?>=g(`6b5S+ $^1i5yyH?!%bIFbcW(SmAGsz{JU#_qIFeqDLc(7ON65(=X7t~`D E2n9-9Uy{JTg-E}`ND7fT$52oyVLlm*b3hu-UmPi=R)P+%6M^>G_QxGZ zb~yusgW=&uJV%p_4Jl{!I;4rm-zM*whP?7C?WLM@nY|W7*YsBb?*pn~9gU`pPLk j#K?+Rw#)ka0b(!LY+udde~Y1_D!nzy9Zt}0vJxmq8gO+;SC1hbxNQB-2*|R* zRrAiD4&hC6@4H#QR-j!jWf&^F0s^OSkzSqbnTq2J8qyD7F41t#kH2Rsj@YB}=TeaB zB^Wy=f0TUe>34fZ4Sw@`OBWE_DciuUz0hgswbDLA&1-H!4y8%IH!D`~9`*8`%p6V5 z+OS%{bsmc$&^H<1O(TH`Aph|uF4g!2EdS@a{l>bKwBgeX(E!2Zuk-i!*QACKtPa5U z(tx@rd-1h*Nx2J$$^V|(C*;NGDzAu3$H)2we=cVYUS1OUxUV pjOx z^Sf6skY;7D&2+i30efnuvj+*n^7rZbR(mk-DbJsM4>4GxQ046T6Ix@h%gQe<4(EvR zqfbvu7^=h12 hy#r zY@&kBahu6Cb>6a208{PBiz|+_DW33RUEvZyRc4z(>8Jj`(4`B~!jv0mLzZtR#R5W% zuRl)Q3t3h^+Tt&@3{bHa0|c%xuZ3g~);?_|ee4sJoimPJ4z&~4ab(UVtW}GOYj?H~3#$y1TCez<_Un;!^?62z7rIYT!< s^#F l1l0;n<7`XZ;g5irlIU9OasE_dQAO?Aw1sz zqbH0rAhdZ9#6Qab!cDNod-gOB;QSb`F&xA=(5N< Cu`gXb)tLjZ9IaZGb!J_1~m1 zhhl*@s@)VXOX0Nnhm@AT+;E~Y2 =TU&Y);n8l0Yn98CF01w*SBr0eiA zDXy$Lp2&mnaIp`$@Hm-an DV|Q>a**jdZFkVEI5XCUG z%Y}}&JrXlx2ZU3n*$M5; q{dgHSH3P*EL!4 z<6+x`;cW+!kGDPKt=@R!PoNR@%wa$TJx%Xz3ot6x{)zyl ~kDU#Sf0uxm!|wAgpM3qckba<5Fgn1XhfWM@vx*pE>ks7cuUyYb;xj3ATXmk- zVrf-kcIN`8tLOJj5|j9pC4ML#2^&mlhNm6WEd5m|yVj3~#j3-pVFEul??>EemjjgH zuU?J@l#u2>$?vF%{k{Mrsb{!>vp+ir!#8pc@Vn 1I@&mqRVpXwC?l#)ns; JR0(S|*P6XVY~3Wu_7skhy-!@-ka)w4>zU)^Zco=8IYPNl?Ujm) z?Dq2%>rVmcbHq<6`=q=xT4h7H2mPifaAbJ;GQPM_gxzz71s%wUDv1Rp6bG7)qK8>e zjApCCSVI ?zNlA#=!6sO5yzO-vkFAd|cQ~ax zhskU^klajXrd3(g$K6D%14GQ+3v}Qzn{qf-h)M5dtt)h=F=afUVqM+bauDnr2Zv{7 z!6UzgyL6BZtH$15TdcMUzY-W7pnhKU$^_v>haa|@+zT`!sjnjVh0GW#iHH~G#|gm9 z2JifE_QPV;Lf616MLS42M@j6CNJ`~(;@FkL;Ozc|iXB^m>XFkz(#$p{acb`U`9O z-;J2eRQ>k;_0Dqi-b$`ri(LP7?j0ejCbtjo7FLXq%c0V8Kiux8E4A(0r_sEbDHM`2 zoOzYUgwo2KhtuQRwWHk6=|onzaOVA#XSj<&TH+P4!lihIlu%yTD(-p+1XVU6-#f%r z$WHXH%UxnocG#L)64k2L0m`PfUNMj%A&YmTquvdf=*h& @2>46E;wVT6%N z1Adn31u*++cY4J)0(!z~WWbLT57IP`-g0ny&@SGE6|D*6Ug=e1`_o`-QS&n ^4mV4X214UzN4uIwX z^Ox!XLq4Jswzdfl@?C`eg $m#t&wswrL%ATM{@aG&a8PX~!Eb>c%z zRVD!W)x~BQDt@T8Im_S0`WilAGnexOAIo_du zm^isN(hqKJkL3SCU$>!s?EHLOozd5hVJO}c^lfGf8t(x850!(1Y}!XJ<(O1G&+&D= zi#S0WLYG$;YxdFNYma2Bvl+eT>T41_FLv$ K| zP4km<%+F)*>Y0&C#f88N;6`~5HkBbA&;fmwEem!dxnXa<-J+H}B1syl-zi-1SEN1* zy7t ne%D6R%}VrbsvLoR7{h@qZY zp>}bOYiAvwk~LZOk94MbiTnkR 0i_tV_J2dpJc$l z2=Gb8^RM|n8Y3+z=Te^%?vXK-9|muqeb}FsAd_}Uxl1BY(}5N@<8@syU7ju!-S?I| z(e*!TeA^**MOx&x)q~&F_1=4{gBXJsgKa8OhKkf3mZ|Evv$Is5IT5?EazOQ2``hpU z_gFB2CyZ&R^?S<-O5TZ5ZY244Cy$F1ufYG=Bv*86I4tSQGW>+4@3*o~FeX!0ZUi!V z-L9wQ`6eCt^`@VL$QC3NkyL%8ByJ{b`L76ne*YfKCPX$irEWHl|B_)@;Y^q6>q24d zjb4>R=dfEtyW8ZBN}cX=|0$n8T}vyEo+Y_upAak7Lm5@KVMhUj26=h*i<9WY6)$fk z4-4vGTXXsU&Op`c;;jj(_S2k4p*-MY>-s5xSXy&CFu1&_b7rMPsJul8w$vG4w^Im3 zufGX**;v!7F0KV=dm@Mz9LT<0;i&(YJrX-ZPnV&EYb_(LHXE_$Q=`T$H8!=R{hp-Q zjb&n`$q`S|0q~G(WWZG@5dRm+&}l-2M)|EV0dDqZJMtCQCRtv~2ljvINn_mIAyXx> zbjM+Atzq?Q)QC-(t#l;|#ouTYP39Q9lf>VFmQL1;wN+P#&yhY03Th$1SJ+-Z*N|7# z@zD;@4Rt>xJbYggAB&5Eb0{xelgdyj0VcD$6kJS4&Szbj*jMe->3W4PvbSU!tHyHw zRT=^%%;7C@41xLr*lzs9%Q2V+Q kD#82D>SxvLWk%fozukd7gVV zd1R{8Z84AuvF%xHeUOm!tBA^&?cu4N!mV@*cPyTyzX*p~X$eOBUuP=2X+ igy95=X9Vgb&Rv>3SBqu KNkjg$`nZ}HEAqn;T$x| zC?Esxgb#GKP4%^>>PX284?WA%-9*JD7=YemROpk)`|2)F%DBoQHHv$kmRQT34-Q2> z66fHBcA3W3D!KQGqZ5Z2el04LdHye^%DQ)IMOwYfnT>hJN&OtA5 HIz z)+p|8*>h=rbWp6Vcz6Z_U8M1;xiuMChveihJDthPljW}P4?n+Ggp>wo@C|UzB$H~t z_F(VOx6K%KZjvE^ydrqOEP%~;1uY410yMg;et9nreuJ=ipNGtPT)f-17S9R#P?ss% z2#}3(#7l=jqg|Q$*ORSow9J=35uTXh%?M?>W~CEiWHRhojiu$9K_@Jm6qWP6vNCAm zUT1M^
*$G=utp)Z&IoYaeyzKU2$HLZ=u}6FFLL!lpN= zwqLF(`bD%qZ>6+M?waH|gwFi7SQDD_Ma<;OECE@iYj=6FV^yXQ)NGynG#ITg-LcO< ziVUr6$vQR RhSu{>*wI@Ow4a2HjTE8@!Up?Fz>EzDM=;X;-t9XPwy_|H4p6f|Wz zb$)AledjIdC 6%u&8c_>EzSa+Lu0mKaU2r*KD zc6Zy9<2y-SrCUs2O#bd3@(C^K=AT)AR6cYP>b?XNRs}>+OY7|LOsu~uv)e>JdK-^8 zeqe9r0Zm)%I7t@Aqrq}Vg+ezrR#r|VqQTbouAZA{G$XTf$D6GG6+i+?)tBC`jU_2H z$oW1H)H Yqvp%j$r;!q_Bqfea5}d2VRI*QsHg0!L`9H=tsRj)gZjC$ioFG zyLItC 7r2jA;G}K^a(ZJ{wJ#}WthzLb>ZJ>$% zdx3VN?X3=y_R%oA6)Pmz$IiM3EwzAZd-+EN@v>26;$;JoOYQ$uM@>OrqtP}N`yF@a q?{CqL*4CarkJT^~@8rY3`WtS^sHy*Ndsy>BfeVhuhA=iTG5!O~-oC{E literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6751326 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,54 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.dynamic] +version = {attr = "tranzlate.__version__"} + +[project] +name = "tranzlate" +dynamic = ["version"] +authors = [ + { name="ti-oluwa", email="tioluwa.dev@gmail.com" }, +] +maintainers = [ + { name="ti-oluwa", email="tioluwa.dev@gmail.com" }, +] +description = "Multilingual translation of text, files, markup and BeautifulSoup objects." +keywords = [ + "translate", + "translation", + "google translate", + "bing translate", + "yandex translate", + "translate text", + "translate files", + "translate markup", + "translate html", + "translate xml", + "translate BeautifulSoup", + "detect language", +] +readme = "README.md" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Operating System :: OS Independent", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Utilities", + "Natural Language :: English", + "Typing :: Typed", +] +dependencies = [ + "translators==5.8.9", + "beautifulsoup4==4.12.2", + "simple_file_handler>=0.0.1", +] + +[project.urls] +"Homepage" = "https://github.com/ti-oluwa/tranzlate" +"Bug Tracker" = "https://github.com/ti-oluwa/tranzlate/issues" +"Repository" = "https://github.com/ti-oluwa/tranzlate" diff --git a/requirements.txt b/requirements.txt index b7f7ae2..0b01b27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -translators -bs4 -bs4_web_scraper +translators==5.8.9 +beautifulsoup4==4.12.2 +simple_file_handler>=0.0.1 diff --git a/tests/fixtures/test_file.txt b/tests/fixtures/test_file.txt index cdc96a1..559db19 100644 --- a/tests/fixtures/test_file.txt +++ b/tests/fixtures/test_file.txt @@ -1 +1 @@ -Awọn ohun elo ti o wa ni ti o wa ni ti o ti o ti o ti o ti o ti o ti o ba ti o ba ti o ba ti o ba nífẹẹẹ si ipa yìí, jọwọwò béèrè fun àgbéyẹwò lẹyìn. \ No newline at end of file +Awọn ohun elo ti o wa ni ti o wa ni ti o ti o ti o ti o ti o ti o ti o ti o ti o ba ti o ba ti o ba ti o ba ti o ba ti o ba fẹ baẹẹ si ipa yìí, jọwọ béèrè fun àgbégbégbé lẹyìn. \ No newline at end of file diff --git a/tests/test_translator.py b/tests/test_translator.py index 0092bae..dfd33fc 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -136,4 +136,4 @@ def test_translate_soup(self): if "__name__" == "__main__": unittest.main() -# RUN WITH 'python -m unittest discover tests "test_*.py"' from project directory +# RUN WITH 'python -m unittest discover tests "test_*.py"' from project's root directory diff --git a/tranzlate/__init__.py b/tranzlate/__init__.py index 870d93b..403eea6 100644 --- a/tranzlate/__init__.py +++ b/tranzlate/__init__.py @@ -6,9 +6,8 @@ @Author: Daniel T. Afolayan (ti-oluwa.github.io) """ -__version__ = "0.0.1" -__author__ = "Daniel T. Afolayan" - from .translator import Translator __all__ = ["Translator"] +__version__ = "0.0.1" +__author__ = "Daniel T. Afolayan" diff --git a/tranzlate/translator.py b/tranzlate/translator.py index 5ece29f..83e73f7 100644 --- a/tranzlate/translator.py +++ b/tranzlate/translator.py @@ -1,10 +1,9 @@ """ Translate text, markup content, BeautifulSoup objects and files using the `translators` package. """ - import functools import sys -from typing import Callable, Dict, List, Tuple +from typing import Callable, Dict, List, Tuple, IO from array import array import time import copy @@ -13,8 +12,8 @@ from bs4.element import Tag from concurrent.futures import ThreadPoolExecutor from translators.server import TranslatorsServer, tss, Tse +import simple_file_handler as sfh -from bs4_web_scraper.file_handler import FileHandler from .exceptions import TranslationError, UnsupportedLanguageError @@ -86,7 +85,7 @@ def __init__(self, engine: str = "bing"): @property - def server(self): + def server(self) -> TranslatorsServer: """The translation server used by the Translator instance""" if not isinstance(self._server, TranslatorsServer): raise TypeError("Invalid type for `_server`") @@ -122,7 +121,7 @@ def language_map(self) -> Dict: return {} @property - def supported_languages(self): + def supported_languages(self) -> List: """ Returns a list of language codes for source languages supported by the translator's engine. @@ -131,12 +130,12 @@ def supported_languages(self): @classmethod - def engines(cls): + def engines(cls) -> List[str]: """Returns a list of supported translation engines""" return cls._server.translators_pool - def is_supported_language(self, lang_code: str): + def is_supported_language(self, lang_code: str) -> bool: ''' Check if the source language with the specified language code is supported by the translator's engine. @@ -252,8 +251,9 @@ def translate( src_lang: str = "auto", target_lang: str = "en", is_markup: bool = False, + encoding: str = "utf-8", **kwargs - ): + ) -> str | bytes | BeautifulSoup: ''' Translate content from source language to target language. @@ -262,6 +262,7 @@ def translate( It is advisable to provide a source language to get more accurate translations. :param target_lang (str, optional): Target language. Defaults to "en". :param is_markup (bool, optional): Whether `content` is markup. Defaults to False. + :param encoding (str, optional): The encoding of the content (for bytes content only). Defaults to "utf-8". :param **kwargs: Keyword arguments to be passed to required translation method. :return: Translated content. @@ -276,11 +277,13 @@ def translate( # Output: "Yorùbá jẹ́ èdè tí ó ń ṣe àwọn èdè ní ìlà oòrùn Áfríkà, tí ó wà ní orílẹ̀-èdè Gúúsù Áfríkà." ''' + is_bytes = isinstance(content, bytes) if is_markup: return self.translate_markup( markup=content, src_lang=src_lang, - target_lang=target_lang, + target_lang=target_lang, + encoding=encoding, **kwargs ) elif isinstance(content, BeautifulSoup): @@ -290,12 +293,14 @@ def translate( target_lang=target_lang, **kwargs ) - return self.translate_text( - text=content, + + translation = self.translate_text( + text=content.decode(encoding) if is_bytes else content, src_lang=src_lang, target_lang=target_lang, **kwargs ) + return translation.encode(encoding) if is_bytes else translation # @functools.cache @@ -305,7 +310,7 @@ def translate_text( src_lang: str="auto", target_lang: str="en", **kwargs - ) -> str | Dict: + ) -> str: ''' Translate text from `src_lang` to `target_lang`. @@ -337,6 +342,7 @@ def translate_text( self._check_lang_codes(src_lang, target_lang) kwargs_ = {'if_ignore_empty_query': True} + kwargs.pop('is_detail_result', None) kwargs_.update(kwargs) def _translate(text: str): @@ -363,7 +369,7 @@ def translate_file( src_lang: str="auto", target_lang: str="en", **kwargs - ): + ) -> IO: ''' Translates file from `src_lang` to `target_lang`. @@ -392,27 +398,25 @@ def translate_file( self._check_lang_codes(src_lang, target_lang) kwargs_ = {'if_ignore_empty_query': True} kwargs.pop('is_detail_result', None) - kwargs_.update(kwargs) - try: - file_handler = FileHandler(filepath, exists_ok=True, not_found_ok=False) - except Exception as exc: - raise TranslationError(f"Could not translate file") from exc - - if not file_handler.file_content: - return file_handler.file - - try: - if file_handler.filetype in ['xhtml', 'htm', 'shtml', 'html', 'xml']: - translated_content = self.translate_markup(file_handler.file_content, src_lang, target_lang, **kwargs_) - else: - translated_content = self.translate_text(file_handler.file_content, src_lang, target_lang, **kwargs_) - file_handler.write_to_file(translated_content, write_mode='w+') - file_handler.close_file() - return file_handler.file + try: + with sfh.FileHandler(filepath, exists_ok=True, not_found_ok=False) as file_handler: + content = file_handler.file_content + if not content: + return file_handler.file + + if file_handler.filetype in ['xhtml', 'htm', 'shtml', 'html', 'xml']: + translation = self.translate_markup(content, src_lang, target_lang, **kwargs_) + else: + translation = self.translate_text(content, src_lang, target_lang, **kwargs_) + + file_handler.write_to_file(translation, write_mode='w+') + return file_handler.file except Exception as exc: - raise TranslationError(f"File cannot be translated. {exc}") + raise TranslationError( + "File cannot be translated." + ) from exc def _translate_soup_tag( @@ -464,7 +468,7 @@ def _translate_soup_tag( # try again _ct += 1 # prevents the translation engine from blocking our IP address - time.sleep(random.random(2, 5) * _ct) + time.sleep(random.random(2, 4) * _ct) if _ct <= 3: return self._translate_soup_tag(tag, src_lang, target_lang, _ct, **kwargs) finally: @@ -479,7 +483,7 @@ def translate_soup( target_lang: str = "en", thread: bool = True, **kwargs - ): + ) -> BeautifulSoup: ''' Translates the text of a BeautifulSoup object. @@ -512,17 +516,21 @@ def translate_soup( def translate_markup( self, markup: str | bytes, - src_lang: str="auto", - target_lang: str="en", + src_lang: str = "auto", + target_lang: str = "en", + markup_parser: str = "lxml", + encoding: str = "utf-8", **kwargs - ): + ) -> str | bytes: ''' - Translates markup. + Translates markup (html, xml, etc.) :param markup (str | bytes): markup content to be translated :param src_lang (str, optional): Source language. Defaults to "auto". It is advisable to provide a source language to get more accurate translations. :param target_lang (str, optional): Target language. Defaults to "en". + :param markup_parser (str, optional): The (beautifulsoup) markup parser to use. Defaults to "lxml". + :param encoding (str, optional): The encoding of the markup (for bytes markup only). Defaults to "utf-8". :param **kwargs: Keyword arguments to be passed to the `translate_soup` method. :kwarg thread: bool, default True. :kwarg professional_field: str, support baidu(), caiyun(), alibaba() only. @@ -542,12 +550,9 @@ def translate_markup( ''' if not isinstance(markup, (str, bytes)): raise TypeError("Invalid type for `markup`") + is_bytes = isinstance(markup, bytes) kwargs.pop('is_detail_result', None) - soup = BeautifulSoup(markup, 'lxml') + soup = BeautifulSoup(markup, markup_parser, from_encoding=encoding if is_bytes else None) translated_markup = self.translate_soup(soup, src_lang, target_lang, **kwargs).prettify() - - # re-encode the markup if the initial markup was in bytes - if is_bytes: - translated_markup = translated_markup.encode('utf-8') - return translated_markup + return translated_markup.encode('utf-8') if is_bytes else translated_markup