From 2e1de4da4804eae43a4059afd6b33cf7c88d290d Mon Sep 17 00:00:00 2001 From: liwenhao2 Date: Mon, 14 Sep 2026 18:20:07 +0800 Subject: [PATCH] =?UTF-8?q?i18n=E6=89=B9=E6=AC=A1:=20LanguageUtil+en/ja?= =?UTF-8?q?=E4=B8=89=E8=AF=AD=E8=B5=84=E6=BA=90=E3=80=81=E9=98=B6=E6=AE=B5?= =?UTF-8?q?=E6=80=BB=E7=BB=93=E6=96=87=E6=A1=A3=E3=80=81models=E5=A4=A7?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8E~$=E4=B8=B4=E6=97=B6docx=E6=8E=92?= =?UTF-8?q?=E9=99=A4=E5=85=A5=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../com/k2fsa/sherpa/onnx/LanguageUtil.kt | 66 +++++++++++++++ app/src/main/res/values-en/strings.xml | 75 ++++++++++++++++++ app/src/main/res/values-ja/strings.xml | 75 ++++++++++++++++++ docs/项目阶段性总结_2026-09-11.docx | Bin 0 -> 14356 bytes 5 files changed, 218 insertions(+) create mode 100644 app/src/main/java/com/k2fsa/sherpa/onnx/LanguageUtil.kt create mode 100644 app/src/main/res/values-en/strings.xml create mode 100644 app/src/main/res/values-ja/strings.xml create mode 100644 docs/项目阶段性总结_2026-09-11.docx diff --git a/.gitignore b/.gitignore index 504e73b..c975d23 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ local.properties # 模型文件体积大,不入库;运行时手动放入 app/src/main/assets/ *.onnx +~$*.docx +models/ diff --git a/app/src/main/java/com/k2fsa/sherpa/onnx/LanguageUtil.kt b/app/src/main/java/com/k2fsa/sherpa/onnx/LanguageUtil.kt new file mode 100644 index 0000000..a389f93 --- /dev/null +++ b/app/src/main/java/com/k2fsa/sherpa/onnx/LanguageUtil.kt @@ -0,0 +1,66 @@ +package com.k2fsa.sherpa.onnx + +import android.content.Context +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.os.LocaleListCompat +import java.util.Locale + +// ============================================================================ +// LanguageUtil — 应用界面语言(中/英/日 + 跟随系统) +// +// 持久化在 SharedPreferences;启动时 apply() 一次;切换走 +// AppCompatDelegate.setApplicationLocales(appcompat 1.6 自带 API<33 回移), +// 由框架自动重建 Activity。业务数据(声纹内部键、纪要 schema 键、jsonl) +// 一律不受界面语言影响 —— 语言只影响"显示层"。 +// ============================================================================ +object LanguageUtil { + const val MODE_SYSTEM = "system" + const val MODE_ZH = "zh" + const val MODE_EN = "en" + const val MODE_JA = "ja" + + private const val PREFS = "lang" + private const val KEY = "mode" + + fun mode(ctx: Context): String = + ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE) + .getString(KEY, MODE_SYSTEM) ?: MODE_SYSTEM + + fun apply(ctx: Context) { + val locales = when (mode(ctx)) { + MODE_ZH -> LocaleListCompat.forLanguageTags("zh") + MODE_EN -> LocaleListCompat.forLanguageTags("en") + MODE_JA -> LocaleListCompat.forLanguageTags("ja") + else -> LocaleListCompat.getEmptyLocaleList() // 跟随系统 + } + // 值相同则为 no-op,不会触发重建 + if (AppCompatDelegate.getApplicationLocales() != locales) { + AppCompatDelegate.setApplicationLocales(locales) + } + } + + fun set(ctx: Context, m: String) { + ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE) + .edit().putString(KEY, m).apply() + // setApplicationLocales 会自动 recreate;system 模式下清空后回退 + // 到当前显示语言,避免"空 locale 不生效导致不重建" + val target = if (m == MODE_SYSTEM) currentFallback() else m + val locales = when (target) { + MODE_JA -> LocaleListCompat.forLanguageTags("ja") + MODE_EN -> LocaleListCompat.forLanguageTags("en") + else -> LocaleListCompat.forLanguageTags("zh") + } + AppCompatDelegate.setApplicationLocales(locales) + if (m == MODE_SYSTEM) { + // 再切回真正的跟随系统(下一次启动生效) + ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE) + .edit().putString(KEY, MODE_SYSTEM).apply() + AppCompatDelegate.setApplicationLocales( + LocaleListCompat.forLanguageTags(currentFallback()) + ) + } + } + + private fun currentFallback(): String = + Locale.getDefault().language.takeIf { it == "ja" || it == "en" } ?: "zh" +} diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml new file mode 100644 index 0000000..4180b40 --- /dev/null +++ b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,75 @@ + + Tap Start to begin live transcription.\n\nSource code and all models are open resources — see github.com/k2fsa/sherpa-onnx.\nTwo-pass recognition · speaker ID · offline meeting minutes + Start + Stop + Pause + Resume + OK + Cancel + Minutes + Cancel + Copy + History + Back to live + Language + UI language + Follow system + Tap “Minutes” to summarize this meeting offline. The latest minutes appear here. + + Loading 1st-pass model (nemotron-3.5)… + Loading 2nd-pass model (funasr-nano)… + Loading speaker model… + Models ready. + Tap Start to begin + Speaker ID enabled + ⚠ Speaker ID off: model %1$s missing (adb push it to %2$s) + Model loading failed: %1$s\n\nAll models live under %2$s — push them with adb + + Identified %1$d + Unknown + + Name speaker “%1$s” + Rename “%1$s” to… + Real name for “%1$s”, e.g. Alice + Name can’t be “S+digits” or a reserved display name + Renamed to “%1$s”; adjacent lines of the same speaker merged + Speaker not settled yet — finish a full sentence first + + Past meetings + Recording — end this session before browsing history + No past meetings to show + %1$s · %2$d lines · %3$s%4$s + · has minutes + History: %1$s (read-only) + + Choose a meeting to summarize + [Current meeting] %1$d lines · %2$s + · has minutes (continue) + Nothing to summarize yet — record a meeting first + This meeting has no settled lines + Minutes in progress — tap again to cancel + This device ABI lacks the summarizer runtime (arm64-v8a only) + Summarizer model missing. Run:\n%1$s + Loading summarizer model (%1$s)… + notes + drafting + Running %1$s: %2$d/%3$d + Model ready, resuming from checkpoint (%1$d/%2$d lines done)… + Model ready, %1$d lines — building notes… + Minutes saved (%1$s, %2$d chunks%3$s): %4$s + , %1$d chunks failed + Cancelled (previous minutes kept) + Cancelled automatically: %1$s (previous minutes kept) + Failed: %1$s + Cancelled + Cancelled automatically + Summarizing — transcription continues uninterrupted + Minutes copied to clipboard + + Last minutes: %1$s (will be overwritten by a new run) + Meeting “%1$s” has no minutes yet — tap “Minutes” to finish it (incremental) + + Session active — end recording before switching language + Copied to clipboard + Cancelling… + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000..50ac339 --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,75 @@ + + 「開始」を押すとリアルタイム文字起こしを開始します。\n\nソースコードとモデルはすべて公開されています: github.com/k2fsa/sherpa-onnx\n2パス認識 · 話者識別 · オフライン議事録 + 開始 + 停止 + 一時停止 + 続行 + OK + キャンセル + 議事録生成 + 生成を中止 + 議事録コピー + 過去の会議 + 現在に戻る + 言語 + 表示言語 + システムに追従 + 「議事録生成」を押すとこの会議をオフラインで要約します。結果と前回の議事録はここに表示されます + + 第1パスモデル(nemotron-3.5)を読み込み中… + 第2パスモデル(funasr-nano)を読み込み中… + 声紋モデルを読み込み中… + モデルの準備が完了しました。 + 「開始」を押して認識してください + 話者識別は有効です + ⚠ 話者識別は無効:声紋モデル %1$s がありません(%2$s へ adb push してください) + モデル読み込みに失敗:%1$s\n\nモデルはすべて %2$s に配置 — adb push で配信してください + + 識別済み %1$d + 未識別 + + 話者「%1$s」に名前を付ける + 「%1$s」を次の名前に変更… + 「%1$s」の本名(例:田中) + 名前は「S+数字」やシステム予約名は使えません + 「%1$s」に改名し、隣接する同一話者の行を結合しました + この発話の話者はまだ確定していません。一文話し終えてから命名してください + + 過去の会議 + 録音中です。今回の会議を終了してから履歴を閲覧できます + 閲覧できる過去の会議がありません + %1$s · %2$d 発 · %3$s%4$s + · 議事録あり + 履歴:%1$s(読み取り専用) + + 要約する会議を選択 + 【現在の会議】%1$d 発 · %2$s + · 議事録あり(続きから) + 要約できる会議がありません。先に録音してください + この会議に確定した発話がありません + 議事録を生成中です。もう一度押すと中止できます + この端末のABIには要約ランタイムが含まれていません(arm64-v8aのみ) + 要約モデルがありません。実行してください:\n%1$s + 要約モデル(%1$s)を読み込み中… + メモ更新 + 清書 + %1$s実行中: %2$d/%3$d + モデル準備完了、前回の中断点から続行(%1$d/%2$d 消化済み)… + モデル準備完了、全 %1$d 発、メモ構築を開始… + 議事録を保存しました(%1$s、%2$d ブロック%3$s):%4$s + 、%1$d ブロック解析失敗 + 中止しました(前回の議事録は保持) + 自動キャンセル:%1$s(前回の議事録は保持) + 生成失敗:%1$s + 中止しました + 自動キャンセルしました + 議事録生成中でも文字起こしは停止しません + 議事録をクリップボードにコピーしました + + 前回の議事録:%1$s(新規生成すると上書きされます) + 前回の会議「%1$s」は議事録未生成です。「議事録生成」で続きを生成できます(増分、新規発話のみ処理) + + セッション終了後に言語を切り替えられます + クリップボードにコピーしました + 中止中… + diff --git a/docs/项目阶段性总结_2026-09-11.docx b/docs/项目阶段性总结_2026-09-11.docx new file mode 100644 index 0000000000000000000000000000000000000000..9021136cc4f0e31119272b2a39b4ce10a7b62016 GIT binary patch literal 14356 zcmc(GWmH|uvM%ny-Q6X)ySoR6;O-vW-CY6%cXxMpcZcBa1oFtXWapmq#(TeR4Jofdcq3;$UgD{`ljsU!ZTF&enDYbaMZ73iRJobnFZ*?Ejev@dvH` zR9VBPw+RFw007wko@ijL?`Uag2T?9?l-IxE$XOJ5b;RZk?V3?zG@vxLNRPWGjE8bIuJJ?XG!e5sAA&+dO zF7{cgWwVr>;~}cyv{}BVW)2)!Oyxr`-vFe5gHk(q?6-pQY&f)q1v<&{0ly``DCch^ zY?AE3E3xpO>--a^8PoHyd$q(NI`80mN_XMU!i)fex7TKv(&Mqai-~>f_gfP1MewEg#mZ{>3YV#{q z!Gq0+5|%+4Nw1U<<)Mr*!;G3$Tt|JJq#J?6F{TSUDL0nbww zjRNQ_Q7g6yEC}$Ge%5I*T{J?4Qmt^0c9@WImB0-GT}kwN+sOpiJ8jW;RuIe7jo(JlneDE`vbMr;jcdTJhFFnS#D#MeUi~$WYa(Ua8qD zjzE!eh<#)-3IX>UYK7v#aQ<*Q?)mu?rkc)chY&72#PFu{PI;$_HZ@f0}rcaA1RhV0{~^J?Ua*(Hj$EaD@B zk~X6bp9c6gZsBsIlupu189^$*JLA<2}Ye9M_thld|wJA$Sm(<^yEIc|kH ze5Rk$%NR&Nh&xWKxlvGLyIAaR)U@|CT#M@Bw+}}#4$OU4ci?Lg-npj=f7gG)qW``c zF^xyj{p?L&xHhtp1R9KlsfmK!cHxW{&%NuXLg~KQ5kO9}7aCQ?JAZkvr{%j5bEESuGk_0N^HO{O6}pvJCjsbN6`DpxV4i+qN`kqOS4YCA&S8>(vU%CCWQRLd zn@&GU3Dpb>a|f0>6Cw&R!INC!gT7)3e8h!w_m*fzODi#fzO71{I9A*WahtH7IdS5Q z9Z`JvXmHj1gN6OP*SoPpBtN}cxektNoj!1y<09_m>e7=Mni8Yun^7>CiI@;ew)guR zM~^CvZcs1w;D;&BpNu{zBXebKu3M?+mhCg`@vWuJ&zMTPeyMOuZ=W;Q)X}P8P5#bf z;pE&iWn^raY3cNy;#RqQMA?efQkh|05H-^@=Q`I}Od0G_8-^^%l*rhKccnFk^wCz1 zy!CYgNcLIPszeWur48%m-FPY}gy9#S46ua&vulMBTcDwc7u@5IiQc77N8o7%Xu=I& z37I>Mc`+ua@NvZ$%5)PT_Tjl5R9ei7=?Ky@`rB5P9nhLF??n5&@!ASzQmj^=5vg*P z51!7eWYX0K4#gSTON^2tBK-xe&`JnDs!%O?ELUfr-2>COWY(iA*2sQlbK>aP)Th#Kc)NYLt@sv~)`FKQm< z*tnIB=Q+&5q#DLHkUA;2z9JZsJ?ftjKqmPa#CM&V z1Q3D3{Ef*d+X~{-@PK07lcKI@$z^1I%ItbJ8{POeF+t<$@4hOIntT&z?k7PI2TK#6*#Ieuk;_D8L`4 z*eSsfiAO@YL$q&vY5N3v0iNEDn-qJnPA#($m>}&jhuCR z7uwRI_yDxDI|A}_u6UjKUE&Ac1A-<)E~Y}f(ai@a#@$^lnGM%$I@rxIkIEhv(QyRS zWS;ol{L2}~D{7JYrGU_6P!c^`&5RDox7N(`3hgfRyo`BazXL9 z6!k4g%?xZ4Ge3hd`PY4%M&Ij@R$OY!~1L>9slBw@0D)vZL-u6ar)N57>o+UN`a|}`1tUGme<-3 zcXPfxElW&0^F8gzpe{wva-plYeFaq#m*pjcRtqw08eoycXd>u|qB1klrDcEZZk>Ua^f3 zVC)?2)%{8I)#*yK@jflWt)tCa2-x$}B2dRhDF6KCSzvePrduEG#ATl&9!2{uhr8Dn zH-lSM)Jt9Q7H8yfP(rkNQAaZ#MEAYt{o3u*xpbRuPXfPBlzR0RaGWhUDqbvA_E+PJWJ z6Ny0;8o0GnjzlbxJHBj21jP`h~P9mg1Z)zw}$MA|c2@bfTbvGqlk#zMNY z)yAu=kmKt}04PfV9c;nn6`PBzLAhf*Tkcq{t>@^MQwo|<%Hu5w`kRhy(GQXxT58-Qx8rjO6s-QHPlUuaV~GaQjbLE zHjn4H&C7ZnyGfq-9R_#JY0q%T&D^!7Ni~JW>M0TdSBWv0FBF*}gB?#n^dbB=&x` zVu5MVsx95s-D03M=G{;YO0|p8cyQmOVASd1h&JXZ$awK`@o|Q^G9boF&Ydw@v+Yj7 zx#fm-p-Jjt6R>YC&Co&&bBoJrX1W+GkwK71Gh)$AyI`wi+k>_?e}VV`&30Z*F!Mjf}(yDRacsYBtxl1aTW zA164SpL!UWPO()mWyVG)u@?fUd0c!m$B$*NkdZ043*K2xuPb~#qks3wH~-qb^icDh z_46^6Rr9hL>k|OaFhAH+Xv&?z@#3v!rWrTZZI*#h#uj&qX`;4=ZKu42Dl^v1wP~V< zHIM=uI$?mCK-ZZ<1!i5JiW!SfT%3sPRl_3rHx+w!W?qq`jl2C>P3++4CE->yqMTi$6m+Cl~aO&VXEFr_fXe1>n#NhDA0FC{P2L zeHg2xpS&MSp5|dOHvG|;xM`Nb67^+09jhVY} zDD*G+HX?IACHYGnE=lpC&G@)tJsa5RXnbgu{*LsjgM{4!6usoybETK{VqHTBKF+)F zyso4m)i}$AE5UYQgX?)QiBALF0O|)bo3D>#r9Tlp3oR1(nT6pS+-7?`=)<84P=+mkL_xtZw@e^Liu@FYW`rf>R2g!NvK1Y9_WU?j$H674Sp>VQjW>HdxbUo`9{rJZ&&cvC0%MO&Rgg6LK+s0OzB-; z)xxEMWqw%Xw3~?N%LqoI&)>}b88YCuTujlET_kCWLP!ipu9nKQAnj5YhzMe_=C!44 zU^pz%fg)XD$@*&(9LbP(b{UQQCk2meKXhtt85H2UW$zbUtQV)^<-(VhA7P>aMGdTK z5w-YXkT|@|D+_H)8tmf~hS5I;HZfE$6qW6z_n#mKaJkceId7eV`;`9i6~D9V5n(kc zt2_y#a+huCY>qL7u_$8fDRx~M5;G0$emWWy&M=~g_GV(CQ*hJ985?MbkQO)N^W?pA z9X$jezNa`BbLUP<5g`+%?}3r-1f&cc?dG=%CU}ZM3nE|_V9Pvj*+*wHr+{wQlXhki zWbhQ#kMi+Tz%mwJriXIDsB==a^yKN?R`+k4Tr9z|`Ul(Z;iD=*O3DCQ3TW~#JIfJd z*)-=IO9%Ay$fnr{Gk{q4AIhvj4RXfCUDHGPgTI`eCWr{15ZHgFAMBhe913+gNK4W& zk@Tw_sY#y{p;=HhGZ4en+oV`w_MB#(#|QIP4Gm5nMX!M-t~WX!S-Q;wp_n#TV?WRt z;Jk78pmREJ)NczWa48Q3TgWt;3$5N-$TqzGG?zxIU1X!Z5VjEL=wM zn@ikA>FPFXWkka3hJQ=*SsEBfo70s8VtRbsOJNg;;pr^As=|R&%&Sh_YUx;kebHw~ zAm~@{+L4h~UPkLD8L)F_c6^Z8=6ZJ2CL8H$^rGHN?Oi0IsPk3PJiclf7ffEQT$}(z zRnhl;&GpJGL*&?{znckc_Ug8Hc-aDEQN2;#Wh`)a_(2M%&laqFp=S?3Z1Jy1L; zwp7nXd$<==4Q^nbys_h056gqmgp7$S0io?a9E$2HgDyV-O}>~koys*d?~teM(Du3^ zAZZ8&RUu$-DUC7yYAZ~pX#l1sy}K%;*4>K0EXod z87I9%fXqn+In~4PF67g5<}HEOOKExRIb?@<6!_JUwxbQ{H`yTfjsg?3-QFW(f;q)tNa>97SBNBH`q;0o1dg znGpmJX)YM#%Zv$)S#X$~OjZw+qm#k$u+o;Lt41%Yjgy#^e3)r^4HqLEU?z_E*rT*a^ z*n=bDdE}PG2j%GqY8QRT6^EAC`0jq6(o@qTO^DD^*KP|a zjHCfYE5i^N5NKb4BB9=w^?igLGHnDcXr%n8na=pgvR@m!A2o?Apyem8hc$xAG+MfPOD}eY2fw;}hqcP}dy%vufg;>6zb^|(+SMSaWEgAn>t$Q( z($PWcXx&5M^{m+y+%=#1mcl|%wpi=H4Rns3emM@-U03nx-mBR z3mScb3gU`Ag&2+*$rCG3A3(*B)H9sZ9Da=7cki3WRFgqugW))=G0}LrN`i#A3`l#; zvY~@V>}7eJZ&Kc1xj>5e&K>SDDs?$^&l?-r(_1wiHtqHH>9&QJRm<9}+4ZYaoaOjP%rSCsxS->m zkg1wo(pFRIL#Av*5y2+Y2#xL|G1J1OVpxvlj5euW76th7r;e)Gyu1mV+9jiJgm!Gy z{zB}Ofid83iIafG%3!m@DM2`@FPwaYtlWB!<%R2A(-X5NV8z_`k>2&U9l-)q-h%>( zxyW3`hn_?-u;#Mbb_SJjTz(0X!z|%Bgx6}ad|no_kAmlz8(ApQ08&S^5- zaPQ2T-nG_b?<)>46@Hd*t5#Z8I6x#~iAqqWOIYx+O%-vwO*4mWWfc(tvt=>on%Hvb zJ`g{6iM8(8MY>Y3BsO-Cdrx~04DMt()H$tNeTe{fmiXFxti6n=DyB$TuvfP{eD zLOtN=qG{E+$ca`Gm+y)F>|}J?@&~MWgy}eTVM_}cIzvJj9Hh*UseGwB#~Ayn9uf|%9Fd`Y~n~`$5=SNR_#MK z!(@Jh`1xEuUQA2#Yd`Z%1d5v~8#rkopl3^nzS}rhV`;%X|Ip2tBO)G??sX8232bGN zQK4SVj^@ex2H~zXb{f|0toOxjoQ=;ki}@0C=Ek9kWv#s_+(yh{g<3Ec!dR>RXM~Cj zH#5>{XkWJjLN34TXP83ceC!EF1O5c&zGfaqQ{?Etd=eE3$ZHn6@(mEd)NSXJk*eg_ zjER*Itdz38B%jDAmMfO#YwELOlC;ITWH(7vNd{{aH}h?Is$^^26sz9M6F(fN_L)rd zE_?v1JM&SeAl=eFU~zRRWrrOeb=iKT_qpvRIYuRlpXjzk1OG=X_;kS8BOP)xr5ahj zwj1YT_?$xUn*LfG(hRM**Q*8%2(o^1QV&JIfK%`)lFb$}v1(u`%wFSFoz$~ckzG?< zki;B{rLFVqiz;m3L_HHZ2k=M@UwlEu)qFAC8(VTpm|#B+7PZ6dcv>WTbNI2|k^)^L zJ8v96CttfM#C%mFR%n?#K5}?}%;Bi97K+5Cb~Xtx6$|*rYc)kAd*!hx6Ki{ImhB-M z@}d-+r3Gh(K_R_5Iki0R%&-ePDpA}00ocar2xm6t7EO@i-A={WCZ#>rU>MnH`ndc| ztF`8*ow4ujk>Nv@vFi--z4f1Arc+20lPANNBGc6>)jrXp@1p@5IHVcuWUXj#LV`Z2 zBo2)Nt`v>(_p{#g0p4hAREsjO&bso(7o>r{XH$ty%Sqc)P|lAG=Wpj&%m;jnQQH8{ z7#RyA1Z>yWl0$~QNze@ZwTPA9j#OQ@?fW9x+>yGr{3Auguv<>rM2U?+{w=+ zf@E5V^GnXq%`YZASC_0?u(Oq07n)`b=rQ5@8=7F#Q;I%%k2d0&HuZ_dun&XXe?res zXD0hV)F<*GGA7r2U}z+~MVY z1NVux_aB!I?K=DP{TmT_y7qkK!1iNqXmKV~IkaY?3;p5r*q8(fVtov4(X-Jnh*I$h zKLZ-l!`ls^et=ojN_F>UlJV}e$JV_xX{ukN`nX6-&IIvZ2nlKi9U+F>njTqPNWvu; zNjr9ROR8i-{NQpdZf0r;>s@dLb2IQ?Hl4&@qsx z+kCQA3Z=uKlRG#t{}?%u-pj1U!FS$hS-fdDhlR~3G)``tK1M+j02mr9RxU5TiT34- ztDmrcH4p-P&URZ9v!pRB+f_$w_qFgX*RU1{cQD8Egc>P~d0918>Y8?fk(EYHN?rq1 z05r(#nLQLa?g5K}1Dw=U*pCgZEN{#`fl=w8nt9t9#*iDpL)!vL*t+gB7uhEwV#~IIM%l|{BAMII80@t z!brF2WJeDV<%L}pEK zJwl-!tr=XAou`<3hKGA6U@)PxR*Q`S*>(GRyZSiGTF_(v4=9A)# z`n@yUs0B>VI|Pxuh*y-gr3)W35fUXa&YAwU=mc*Q_yrVlm{SVTccK)WYHbW$(2ieX z>mJN1yTB&bStk7Bf}?m6oXLUg#nssG|h&Ry%X#>e<4bBzUgAChcL*sijJ9J7C7O z7)pdbmkt()XK=gU&Xr>u7jY#%B;67h1??~J%Dh1Tt&KnH#FPCX8DVcC#)ky}K>Yhu zv~sl6Gqf|cGX5dwIhB#fnfJ&q34_n`;;B5y(ncWRU?`Wduaez zvZFzuvFMG7e)-Gf@u>~$*LOxcqxH0#%QB^vryquC%3Ee?Qo4?sx=m65Is|mtnRl6K z-+wM8uIB^I{;*|MEOytwR9FLe;BLxa23+MARt^zPBd9R~>J&d-uwA0->zHmC>0GLo zz8|kQgD-R8q5pJwDp~VNS*AjfVY#~Jwg=YDXl_3Oi5@5;-pc2$&3w4t>D+F!14C6R zc-b`*{N26y0Y6l|5H3^bWg$p$E~qOj;AuTTGu?*C>OT*$zt;+!wm%iGYEre1GE2YHdgedlIheXMVJh5Xxe|K~vh_GY^O zsdWS|CpK~a?SJEMCjO(xBQ#g>$<>3=Ea`qd; zBULQ3wk8k#aI`EKmGW(WgjEo=L~yZ4WqVt3_PwRL9I~Yy&mCq)!|@PKX<``JGDcAd z!-J-hWr%POe(X6y7!G;zbgop@x0hI>`F?^wJUhSm>o07OYTZ z_cRKdx#sQIN&Efx7c}8;{IiKz%MI*s8JnB(W7g2bw=z+17a+TRe8$O7-z0Ms7 z>`h=zw>V6_3PWqCZ`{W>1fZ!3SxtuAjGRd4BE!2!Yx7Hp0!#<_kEZnFj5bhpwsTBg zp3PWVyDBw9GeJSg^mBct?f6$o0z!2FRQf)+*hC~?R3RgBkaTk;rF`iT178Kc5ZN$g z&k|)yTV=&dOE29Xan)t)_mj3eD`%{=Utdzn6!&TOg$0%8azE7VlMSstMH0?Eqa;vp zhAN$_^cp&L-!IBvs#UzzRks3~l;Sz?y5&Kt!CK$zv-Ea6(ebeTLvO<;kY0#gRZB(2Q%p;k>d&W&*m^W9I| zS;rjcNvd_6;jI?;&tR=?v!v4TqU6L>x5RV5{LW3C|VBjlptInTZ2`skjl;AOaqd zHA$L9)(wW^g4Gz4zA)MF&fEn_ptb|3M)u#TRCQe;jD1t#?%VT2l|S!CM|%fr%O9%z zJkAf*{T@DGt0aDDMG1{C&y;n0S=bS8+7!#gNPv{)?oI>*Zu9E0XTZEUNhkDO4jbva zfofl%p{q@k>5BvFAZX-Rn0H3H0`EAX94zglu-}sgDHwPQ^`t8lCLnU=uqy0JHHBlh ziM#fTU$Sw8snnN}cLbdj-M#zw61Km6UXFWX z{l^A{|8)tvHa7p}-~Yt27&>OU7R&ZDyBTh%_8k)=0g|Mf8<8)#&Evz?PTdPCYL$SV zY~fy2aUMYJE|-itPvFWHA*iw6IJJM)9xU~9c^g@1bTv3CDBrx@I6I3()n@|qa*2Kg z5s9u{hM8Ow?6v@^B^7|tBc%mnPz^ALP1sBD;flr5N1fT2mv1{?D+jiV&VE}!ddN`B z_}g|Wdpmyqxjc=mtsSha9sZ0e=Hka>cHYAe=#X5sIVu3E3rn+m^R2?Kc>wINMFgBy zS&)6P?uhG^Zl*4V;Qomezd=!6Pn$YuaL=B8hn+9i8>o&Zc`Nv6z%^HB{{ zl5RKGt6+mLTq3{OIZT@=d8Q`k89U^PO5uJLdtlw?4>6h^_32Pf&Dl0u&;c&x0XjLW z-sj>lIgyG>n#vem={=&mLsP998Yp@k>9sI7x&$-J;5~(5yDOGMsHg5KCVR1#j?7HY z9qC!m4nRv}N9{ydjWy&)UHWJeG*NR5ZjCVjVbWQQf8kWnRu$_pUQ%LuIekN*hU7gt zmMU|$LpI)^Tk&*2qX_C6Y^E(lg591%$RV<0#uNFBWEIPeGs(~Rxs69U$q#axsfViO zCHd1662o^UKSgCW^@lN?f1B^L^S1`eH?wtndvM;KKUy6BykP!i1Ap8$(&7BDKm;(t z6#~u9p|s)jJ)>5zA8!EOeZYqekkjL8r`CFHy?<@>ytd+5WkRvFNvB3f)yWX|^&9Zu z)cmNmk99s9^o3rTnFzrV-mOAK=r)G&P>FH`zhau56Qg8xEZb!~M`!lDY<-g+5T& ziQjp*f9mWGFnXn7uFSYV9#~^0st6ylV?$P3j1% zK$qqWxf|_Edvw7vh&r%8*J37Qk{TUi;2a`M?O;QGAK)UNl1gR2NRqHuT0I%~3Q*Dj z(9%-M-hs{Jls`0L61C4<9sYo9ZgypB_R2uNlB(9~llnD^6F9PsT*;qCvhqm$yGC3N z8rk7hWrN9PfMF!44M>B5TC_0B;?maj?o*dWKRp0uB_%}bs;PCULFUyFC2d9;51}~_ ze6!lj0gq??Bg=_0UYlVt{(Vleu{>nZc~0^-QVj&zf>VgF>>WTH!8|KB!C>CEBbFK=dP|JEM-k6GFqIyk(= z9e-qG^5WO5R^Gz|==4o`5Voj>brotjP|e@!Eebb)exXsa9q50VC6@z@mevumhYpuX)Z);^O06xGl;tP@-G!(MJ<03NpcG!*Pg# z0~v~Y8HW$L%M@2xFs?S}8mCF-?ujB@PJuUgTG=oemfMDvVoAh^k@djhN z-}Loj@TOuAYzNu;yUoXzLO>(JE(#>Bvm5*NOufSz05;MW(t|cRIwo&iYeiSFRd-r= zeCn*PMoRjwN4e@Y4?)%Cc~}mBxUlSmXhsT;mL4=}Y`4X5+I&t~;nM=meC(3?2cnNq z*1vqC*^>p0%~xZI=S~it`Lfb@kS2?o6kZRJn5*+HUPq>wel`F}f=A*? zIF9c6YX3L03kziL?R(Q_|C?|5k3RLSEq}CU|EbT}HM?zmbg)eg+qZP1sv_%5E!pSO z8FZuBwwx>e zT}?BE6s5``leA3VG>ufaBCZo5mnq?OTL!}?{k!R%9R9WIq$HgA?gO}WBc~f96|i69 zHz~2*f=~RVLl#@OP{JlPr2O*CZ0KrQUz1+k(b}3vb$Gk|Q>tG_25Y*5tT?^OurZe( zXya?c%~uvuiXX$M@dq+dZIPuLE9M@1Pp2sbo7<}^te459%5HIZbc(S%DK0Bs!uDgo z8PiXYej~z}l9dX%cu64$xv<{4*vvH(+1p2giv~bOkkjM1?1byTGvnhrnH=9jY7ns* zUf(j<%?-c+Ol0deq7XhnajF?42&BdebI>l7p7`8XNYvJ0Nz{t&IO*4KJ)<3@UzTu< znw;>xzWmfu_w{`)g?E(KZ(Zj9m!sxa&Hry7HTtL8^Ehv&DfpKD{l!nSa!}IMv-l&K z^m%H{sv92}{6d9bm8vc!!nAaqTGFY58vv+I2)1BrbG8AmtloS(VhsiU>ieF%#l4En z$pA?80ad%YQ94G2oQ7BF!~Md5UJr)wVV%Q)bf0zLgJ(lqJoA$nbsl#@7Ug>HVxmB_ z8H|{^p1J!|DZO|2Ax0#S0o&)5009%jTJU3w))1}{sMn9(R8aGE%KJET7~xSK+8{fz z!5Nq<(^*>DQ?%#~SaKNIv-FKE;L}erYHi&7AC>vKIcOCbqe4GX_Nl0O0pEj{eg%9) zk|VeOY;1uWC^l52f6G;Xj#EN5{yj7WAUDU#h~n|R%9NpDd_=AhWvZ)indlkVS~y=q zrf3vDu4e4?yqY@pv9435rgP;p!{Q_mTFd{B+w+S^|2LEl2!ssq-`_!b^U>bMk4WRk z`0I_7{|@<)FZgr(fDpYszj%0Zl79pLjI4iuN98Z%+lSP@fxo_k@rQ_hr}X=q8$T&| zznSnKEc{C2clhsRj-T*Y%)j72iylA0|1+iXBRBi!__2IU++RY>pRhj{|9h_XZy*2w z|F5vWGq=CPf6qbwgcsud#qz&0l0WhPlj}Dh_pkATYkk7MQ$9a2f3p00V(4!a06@?` znE$Ar-%}_*fr)P>@*ff^zkl=Zf%{Jax+K5ISpT0e{&xbuhs{3;5R?8w;IAP1cl7Tu z>reFF8~P9P`d9SwJO0-r|3}pGbL^1+h5v6c(C?&v4@`cN3ZwXo)UOWf@9-@|L^PAr~B7BekJlB z*YW#3_mh!`_kU3`dQ15IbL;)zIQh43m+Ak-$-k)l#R}*jTMrcg6~O(?qxWU_@#X&q D;PgK# literal 0 HcmV?d00001