From 09e0b962c3af784b6f04138cca120e2711770800 Mon Sep 17 00:00:00 2001 From: DanS Date: Sun, 12 Jul 2026 01:41:18 -0500 Subject: [PATCH] i18n(wizard): translate the first-run wizard (was all hardcoded English) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first-run wizard predated the i18n system — every label, button, and help line was hardcoded English. Route them all through TR(): - 62 literals in app_wizard.cpp replaced with TR(). Where a wizard string is a parallel of existing content, it reuses the existing key (the whole bootstrap_* family, plus theme / language / balance_layout / low_spec_mode / ui_opacity / console_scanline / download / retry / cancel) so it inherits their translations and stays consistent with Settings. Button labels drop their ##id suffix and use TR(key) directly (the app's established TactileButton(TR(...)) pattern; the wizard's phase buttons never share a frame, so no id collision). - 55 new wiz_* English keys in i18n.cpp for the wizard-specific strings, and their translations back-filled across all 8 languages (es/de/fr/pt/ru/zh/ja/ko); format specifiers (%s, %zu) and newlines preserved. CJK subset rebuilt. Verified on the sweep: English renders with correct labels in place (all keys resolve), and a German run shows the reused + new keys translated end-to-end (Darstellung / Acryl-Glaseffekte / Verschlüsseln & Weiter …). Co-Authored-By: Claude Opus 4.8 (1M context) --- res/fonts/NotoSansCJK-Subset.ttf | Bin 607164 -> 617392 bytes res/lang/de.json | 55 ++++++++++ res/lang/es.json | 55 ++++++++++ res/lang/fr.json | 55 ++++++++++ res/lang/ja.json | 55 ++++++++++ res/lang/ko.json | 55 ++++++++++ res/lang/pt.json | 55 ++++++++++ res/lang/ru.json | 55 ++++++++++ res/lang/zh.json | 55 ++++++++++ src/app_wizard.cpp | 168 +++++++++++++++---------------- src/util/i18n.cpp | 57 +++++++++++ 11 files changed, 581 insertions(+), 84 deletions(-) diff --git a/res/fonts/NotoSansCJK-Subset.ttf b/res/fonts/NotoSansCJK-Subset.ttf index 03f827770be48156bc7662a914887fbe4a795434..67c89940b62d874fe079e78842688dbef86c7717 100644 GIT binary patch delta 31465 zcmcG$2Y40L*Z;fr^iy(9l9S$(liqvpm2@DXh2DFI010sDy$49>fgnX7G%*4yAc6vl zD2R#_u~8I3QA99@`#nMYz3+Sf|NnjNbDzujIWuQw_TFoK*P1?Y^ykfNDcMnJ<;j2$cK}DzRaEBj{kt(o+$fc^hA|krq*8@5cu|7DVPydm9 z$GoVJLy3a76Zu^oJYveA)sxQ-BC6GrvLI>5z`g^5&K#}BxqpNBA=sg{u^Wi;vH0#g zWaNa&w*oJIY{HI{M1kLq7~Q{b_?g+kMD3@d(iJ27P9CFfYL9bSSA0mL`i>mvX-qbf zif@N~Z;lx~e!`1+6iq4-hsmqj(!fD4IFP*y-+immz>;@=+lucWS?VW-y&RLmoHpI* ztxx@f7=IUgtmyiIWpUV`A1D7fyUvRx$QNM(shbY>V(-W-tjfW;QWUhy=SQ!L&SM2ZuM&JuP{#jef`quq!6Tv(WyV({n z=2zFlu*cpXyFG#Bbgx$&*4ou_hfNMYb1b}Wi1Tq~JnZGd^US}_%RRt70H;}sSz58+ z4J<`1`{V0+xWB0D;t|UR*Ddg9VWD-Yo)L!!ds=gc$9umadMDu=A7EJr154RhVA&T1 z+z+P){KRSABT~gHL~0ga7ae?#IoCZ4j$=i2;UO{1v93=@Pd2UY?U3PCQN0s-C-<)C z-KlrK-b3pmLti*H4!$(_^57o^KOZ6w(GT$+k~AcBNY;@2y7aJ87G1X}EJ!{+-aJ8? zFuU%}ur2IbU1j(gcB0NDB9mMCxUMQYwGDF`RyH&=Y+ARr&Ly%h z%dM-4^kmj`4UuKsn~&;#jQoT3I(#iEf`fTVti`<4Sc`e1VlC#KkNuzKrN{l>%}e;l zyps5r=6Tu_jGHp<&2c}E-#`A9q3_4PG_?y; z1}6Q-)E!A>4sSkcncA|XrJ-eG%dVC^E$3Q(Zh7>UT&E=8hWU4shccU{Teojke>L=y z_LAn3>XPtP@49}ehfVM7e;doO{YUq|x&O%iL;Lsb-?M+y{*6TYhwbmWztjHm{q6Q= z?@!ww*!-gTdGpUi%@>-_G@ouh(R{4=t>%N4#BN^GJiU29bMNME*zVfg>By$$lIG&( zLVU|>&S_3)M*25LH-|NQHoG;uHnV--6YV>@Z^^y|`$n4f1@F`B{qoS*Jx}(`!7_f& z$UVdMWbH}b6TK&FPv9P(J+80cd%gDc{MU1LU*A1>cQuv?yGQRHwYwkD?moMF@9w#~ z+wQd8p}U=TJMQ{(*Oi?eiFWz{<4$LbaXX!N@;jdHIJ@J0Ebr|&v7-gcksSwiH1F8B zW5o{B@*T5wOy5zxW9(}Owm;kcbo&FM?O$#`vHh*>Z*D)deaH4G+b3`DYpH0vu$^x* zH?=nX*7Qr$m8MUdE;YT?)Y7!4>9wXUP3xK(n$|ScVOiOm6?rq%OxUq3vV}0X{#@fcQjYW-djiHUfjh>B;Tg+RY z5p8*FF?GwQTP|$buw~hn30t~u(QT18-`~7=^SsS-H_zH!vw6hkzMH#j?zA~~a|{-f z?`Gp>k4=AJdAjM(O+Rh=dei5dKHYR?(}_(-H|^Pko^I2qP2D%X*oaPca};+>JRKV>WtjwA*O4p_ORE-3{MwxV_=}hG83qY_MJ5yngU{ z+jUoX}AR4ObvGRf7aisKVHAFeo_6D`ha@RdY5{~wYSzz*|2Ku zFf9Goc3azNZSmTIwK;44TJy`A`)dxa*|TQb>JRHK)t#%`TsNYwsxEZZ6QWhVEAOp* zW5sVP?ylIeV#^BO<%^cjT3)k!((>`k2Q2Tlyl{EmveaeC%VL*4TKdD%OG`gmdVcBY zrKV#`zF+d~lIu$@EIGfVVad`ZeV61cab9A#c;n(Oi@g@RFScE*U#wa5$D*GX-B@&U z(TPRJ7rn7)@}hBz`YwuE-fQzV&s#HZ#k?i+ z7SCHWZ_d1#^QO$3G;hqjQS(O38#J%Syux_}^YZ7#&kLAmnEQ0@leyRD4x2l4ZvVO6 z<_696#w9a-_G+TpRkK}YJI_|nx-lzz=8c(MXIz;vXil8z6I1!LPSdidWlT$-+B)@z zsh6hioEkMXd}`>_psBu7y{9@%HEC;K)S7EsYyYnOzV=-0>Dm*uEw$Tgx7BXIGN*Q8 z?FcNDwdJ*iwW+l!wc)k?HOp#d)O4-sT+^|ptfrtQzb2<9yC$YC~))sw3GR`b3Pj}ynXj(t9M|JdfS^!(%m&RNib79Q+F^yxEkC{KlG-fiEL1X%jK{qlc9VCu%9b->4 z`oZY?qwkJhI(qS_N27ikb!Joxj!hmlXVlD5Q$~#(HDuJlkt0X^I%4&Rni2g+L=A}^ z{GhwOyH$6tJ5{w-J+InT^;*@ostr}^s#aD_ubNafwrWsSpQ^m77*kbrRcKXkx39aM z>~^%XebM!4*FU;G>^h_Cz^>{p^E?0D`OnV3bpEmP-p;!_ z@94ax^ZL$fIZ z%AuA0E3GU3tawtfy<$_vnu=8wl@%Q-Ol1`X72X{$cC7EXy5q`@3p@7j*r8+ljzt|K zI(*q-Wcy|9`SL%?@0Z^xzh3@z`Mc#C%h#39E+1Onzr1UCe0gMfVA+pl7s?KlHI`+U zrIZDh`IY&Uxt9J>`ndE_>Alh)OTQ_-S$d=Ndg-~+>eBJ0JxWVUvr98dBTBHHJ< zNAuU`&(0r`KPbO{ewV!4dAIVe?7F+viD~=B(#gPqY5a`ZeoL*7sST zXC2DgpVg2xCu@3EZPu8qQCWks`e*gYD$PpEiper&IcJ(PpJzVJ{5A7I=AF!sGS6pz zka;TeMCMzWZ<;a>XYS41mANgmeP&r^ab|92R%S}3ccw1mr;K|UUu9g*7?9C7qgO`9 z^y}$Y)6b{xO5c&*l)f>2Roday0jWi)DX9sm(J8l5uBUvIayDgrO79f!%UnG2(@P5L( z2?r8JCG<|HOvq2*;~&OPiTfq)r?|UuyW(Dp+Y&b?&OY`@?Bm!wvA1K-#-5Bl8e0{c z5c5XNzL@DTlVS$M^o{8i(pu<%5RoUGxdU-&(UpKZOms573 zgbSyvc)6O4l|wsaz`uptHk7VP)qnOVxT62_PigmRTgiU4^>VEwqJ?Zuerl;xCu6K) z0iWN>-(y##llym;5>ohBLX2<-69+DCnxlI+W4L)~Kz>Mf&hn83}`4%RlMe zm!Bx*!bv;Y8k5kIOKaN>V~v7koK#eoRs56_E9*XOcgB%}ywLY9y%3m{ki;0yF zB-VZ%u@0XRtMDdPxq(=xE5tf)BGv`xbR9*kn+^DwSXCeJGqLUyiS>vDZxZVn2ficL zYZbBH(}5We#c-tWXkz_1u%B3e7~CID_P zO032RD#2D_we11aoC*b}ZXq_!Xu=1qpNRUv*Fns zJFtS-Tm!g9Y~B!J^Bn66bnRSX_5Fx7#1mWRKy1AhTqL#uifp(~Y$K%KB!H8|HZKRi z6Wj8HSmR`3TWg7JgQrbh0FG~mLa#Lt+cBQlPHO;TcOj&^E8y(wF2we9Cbkzju&hTlZYLLwQpP@b_A8ZiTy25>aDkl9i2h!m_PWO*l`?x8^_;?0%-7E zPjHCXiFV)y<};9dr{MW1H2xmiI=ze7nNY<4Yz(mvB8h#N4E`i`4u#IoA$DOcv5%0s zA73JN@j9_j;N7QwU?Z{5Y{5=qmyzjLpx_l4^m%V$SE0yNB=Hw%;1RJej}rR|>Riht z_BDYa0LgdVMC?Wgv71%cxP>J7=0jq)w-Nic1F`RHKoha=5rQ8k5c?6ybf<;bT~u_h z7qR>4#C{3@xB?y+i2Z`3{pAF)hr<9GeFTMm9Zu{w821=T`~iiZAoHI<**{U&(>}!h zf=bVz#&Z)q`Wq*>3lFs@=Jag~#Z>r_l!=V>P5x||}e>vQ6&Lcvwy zx_?4kkDrO_r6aC4_Vw{5uCEVq{m}RTglOOy;s(K(!SHg(E#ih5h#TIHxDmIB8#R%* zF+6c&TZx;1^3@TmV#BIZRI|KKL zd+ixqpU z;@+|XcZfTR1U-f}kM}|R-$sFVJ}2(oABa2YPTVOZ$$OCIeRzCE0+8|pNcjOm^x<9N z&b?3EdF0B4{ltBQ^FCfl+{MAfeewlypTeupHW7EZfw(Kji2EFdUVTE`muUE_5yV}C zs$av98*aqigrnaKK>WXjT;EM6?t6IiBNFWnxT^-ddR6^XZn~D2vBXN&$><@3^o?!hH4gGbHxM#l-_q>|8zZK$IQJ>j|xEKA1r>(@Z zJ;ZaniRVpk5-OWtczXq$A>N^$cwFkdQyTHk-o(3jft$peTpNjZ8%Dgl3ph)> z$7SL@uM_Wu%DpcV?{kuP0`IJzhCV-6CJ{(v73V<*6o zi3P-0BM&B_{z;~N#7{0Eeo8Pvg*A7Gul<<#secha?HTdYZz4vJdnO9csv>@NJL2b{ zlDY6^F80rZ(eulQUx2VI{F3-Z-x0s~1@TK2;+N$Uzr2R{6;N>{LQ@ANSC1ur&2Zw^ z4kEsO2I_8r0qfAn1_$CdA`dq0Bz`ljZ{z?HYAcR6g%iL1J>p+GNBm9|@w?g+kHG`~ zI=tP}h4{VY#P5S4`z7KJAXg3zA^r_(;*T6B{!P@`a+>&~0mL7RCH{C8@oz(ccRnEg zL^k4o@+|T1LGIHFh(8lW{MkFie~=51NFOdH{@iTh&yObl0{Eyq@gElwe-Ux~L?r%_ z5xh_Qr^|`|Y%TGZ#}j|Wo%qiu5P$Us@n1sKFIN-)RSMWl{Izs&l=!caZ(r{s{`xS) z{|4fBa})8m4iNv%5#nzn*595W{yQYoch8Cc{&V7gXe9o}cEsO7sP1BYw~qLG@bKP3 z;_nk^BL1f(#Q$7M`~x?DdVYcD562My2uA&ieZScf|JVz>OZ@Nmh=1Zq{GTSo@lRy$ zUr2^$D&n87C%zSh%(yDd(}{oaHVJfz1oj&Vd>9ErISFD636dAMLxRGC=On26kf8a1 z1TD_fMUtT3PJ)dZ+$6#FBN7aMl3?#dg2Mz79M_TH>`sEqF%nE}{Yk*RUU1(}g2!+Y zJl`k5>vs}-ibyaXBEk0_3I5m)*hWI24lE%dC=UEeLhxM@LY|Ni27|)KkPxwngh&`0 z?Lk5e>WzJ!gt+r0#6Kh<(T;?qP!f{!Nk|z=Lh4cy(r%GS$RH9jT}a4EB_Z3Bgd86d zasx@o+f71&H3{w3kWl!7gyPRgC~YC3ynuxE@TTKQ5-Q&yp%V=5{0<3SK-afOzypd< z^?-!#r%33rgM^;nlhC`Agg)a)=vzZVzvCqIhhhWgk}x>NM8XhwIP^XV!z1uC3b{vK zBVjb;8FPSyannf{|1Aj<2a!-cjf6>ONtjYdLaj`~)J77fFCt+^PZDNcAz}7Z66Q`I zVLp^x*pYM3OIOl8<2_L|~bL~kue~^R=2GB&pNBJatJb;9Yt4a9e8xlUn z{?AbMN-q*Ve@w#FStNXsNWzyq_=JS7;Pf?DU`G7EM#a}RlW=1x2{#d+TN6q62C@BC zN5XeKNcg^*gdd>DkErNwI0^TLl5iiM{5+O~2Qczs1qqMf(XU9j-@YW__f91IVGUrw z6BzgBWfJ~^_s@{z&!b8ByM%<+aU_^el1QN@64`DNc`f*sMBy}vVl#;y-84EiXDkH14yj>l*FlUU|KSX(@}Q%ukadm&ScYoDiU|@BXKvZ-u;5aJ!x0KhG!e>P`~BfQMh6CGnb;#IF(4>%&OAv6aM| zDF4k^5^uxHZ^3s6!w=ac{)mLWGnB-;CKB&`MB+~x5`V_`2T<%`CgT6-3le`DN8;~2 zN&Ewr{RzqcLgGEoBJpoX*m|GD7ZXXMu_Un?lDL^9@ry|k8b}f^lO$`v6OvSU;7yX$ z!${ItfomjbcaWs>AW6TOB%5-Q4A?e&Pmk9{!EfP zD);z}Bu}ipBEdP5ybqG(gYU*n4<0^-1jBn7S`DQF8x!7U_(j3y}* z#)e@#{11{Mvq*|cB`F$4#;hhO7G>iDNlGw{At~`ONy#uIHHW0M(vFl2kR3r0$1F>M@U`o-ndkBG^EZsdoTLy?2t-7qa%RCuu-mk_Mu{pe`g09!JuU z?IaC-LDKL6B#ronq)|9;v?KV0q%oUF8oP?5al1(x4;3ezBx&Luk|v>!$!Kg!2}!jG z#nfn$ra>JO&YuAlX51iY77UwxicHcR6qpBV=4XKU;1)>>ATvg^(n20=C23JOSOXy2 zVpO(x8GtfNVC0gkBrOdA@Oqg)fQQTA`En?-0*$RexfM4_T8VR4qC95Xq*Yf)s>8Z& zmkA#~k+ix9AP3eMK|ipEq_raG3J!rsB-LjF7*pR$QbTv}6-n!I0M1?S1NM@%K@EnH zv@r_c+)XY3^=&#v(q`1Xc`iv?aL$&V0BSXw;7#K;lD0yQt*~xe4A??a6VzyeyLXMy>k#-LUZtz@ zJi36mUw~@(6_xZ6Lif=^aFV2t-2ocD2o*2(0chkC?E7RgxIq$nRq0YQNuO##IWTP@ z=`&RF8NPgm1DDZ5U9JIlNxD)Au9Nh+7l3h}e@xQVcmSiXenHX~8332v7f|KPY%mib zN4|n;U%gAxHH7IJ67JfsBz-*!+#~7w41gZ+MrQ!$OgC4Nbn_RIZlTbvvm||!3yzU= zy8>YSZ8+FO(swZOJIMYWD*1jaNk16C6o5GX*bl&nJIMgyyPE)(0yKONPTV_B(tU*F z{xy<*no82oc3?3{4;&HyUywY%pn->o>%+fEdQ?Z!uWEqxZy{heNsl7{6#2au_<^K9 z#*p+RiljdkP)pKN2I@)r3&#HSAxY2BSv~gzdrA7+7a(a{p<*i(GK3{A4IaX13-BjBe+kpZAV~wpJYQKc!y*=G-CHP$(Wm!?cXEWAq+H< z?1+Mn8%cI@23R|%0W{(g3AU5$Iu?9GvRe=ECCTnHN%qi#>EJ2Jo>+UqIj{NP8Oh#o z+Q$pvJd+Q+GQuO{HIjW{k?$pv{YH}PZvZeVpf@;2a$qU=o#dbmBnPAN;2Q8J$szDA z)Dg@jIV={SOn5XnKypL|fTxi-H}X2kQK&2GCdtw9U=ztP)?|`nmSW=`$+7(b8j1@6 zur%&xlH>bYgyg(I;A4{WeZUIv1<3{J zpn>Fe)?f|Eg`waq$wdf95y}+rCAlP-Y8wyptcU^DoIIljU+FGVT+L~OR`B`iv7z9NnSpkh6-f`X`dtJR^DS zJ0#a5tMSrNUgrVslf1s3%tyB0?BTZrG$ zbdryKLh{?F_+2F92~>V6gyi>7*?Tzu{oy2^IZyK0BP4%to#b;GlFvip^M8{35w<^u zx)f53nzeMtTj<)6aqr^iVCYXiy85Yp$t;3JapG$ps9y;dk}UPAJVQc_Hm zNeas+g)@=D7m*^AkRqm#A`K!%wg-QcqVxdok)rAgz9mK7K#Il_d`*h>6H=@;lcIY} zinS{^NQ%vuq!Y5-^99z(u44r2~`;UO`HTJ1L<$Qo>|#h?MXdq(t;0 zC9;~7s1u|_zfDTa7*b-xNr{6IaWFdG7mNcKR4NJWNJ&%xPDngUN>UYQ2Jk$2BPl6) z0N+!cz;05~a!E<&!E;hF&XAJ%0V!FiBOCj&7m$(z6?68Il8bfj15)yEJimaH0s^Z@ zX$LjiJtL*?A}K|t6nxw!r5FWE1_O9i3aiUdVcF-Tl-HBeK8}nrZO8WB&8FS>vW8i&hWa63S1@y_X(w&Bfx(AWJ9Tf=iOJ5(xV$GJ%dT<6^Quv zjwGe`X;S*6fzL?ki}?5bhLnC&Na=3{puhl}Jm5Gf0}H`JQU=W>WpF51M#>O1SWn7O zoHG=;Fsz1@;TkZMlo4=ZBqSd>os>~96tk+z=vGq3oFZi`!aFt(@gI8#8y^A0d>o`5 zHw+-oX`v%UL|GL z22y6@yg4ui&zQ>GH%OU>1fGX>@Me`!I6Y(cXXj37$(_Qc2FHsO_F_N3gwG-s*^rR} zD1pc?!Gm*@!#UpD#%KdrjE$WnOOnywKfob6Av(VP*WY-<(Ci4${B%R4AuaDYJ5Xr8 zmCt=$j zt1bBx8UC@;&ry=(fOO6;#>U&eZL!!L*S6HumRs`lP#2H(ol?xli?$YbRqM1L9ziRQL66!?6V&dZy z;6{R@j+5&jt`zJAV_XcYPs`>)Ob(9m3EU!g|6)(`0=8ct?N(SG@bICnlT(Kj-bRTm zv9eM>cwkO!r?S_~w+d5-`x#U1!fj){{QIWJuG-NB&T%f9blVWS-lH}4j(ShMdy0FK zJ(uj!J|JK44XiBn>{?*n>f@_cMC)K{hJT8N^)6s_O89_SPPH)oS|_)7=QPJymz)7c zol%oA)8K0z?31;>S3$ziJ2|sctOM-k<6RxOAi++y`4mAN5j402C+fE>5WGxA1kM4D z#QVK+6g%u=?2RuIE}$*pV&da$P$JXT&$`zLwT)OL*=SZ-g==Gjd{gaWY~#Fwhi1rb z+LVET0TsV5)xncwH_uR(8&K|HKFDU-#`+D4yLr>#wQ+0yPY$%M9H_PtibNYtZ9!5J=N03h?GR<3V)W^ru6SFe_wWp?@Yj3l$_F5< zL2himl?!M1KRNN9kTNb-h#s9N6$R$=xqPP&`g>=%WVobyMfVNWda6SEL>cqkqi5y| z*X<*mI=^11q)%)aoytAzo+D8NLcD?Z!K0;~9LbXkX`JQiL}X=uf4_g$BHXh0YYDYw z&DyR{8*kY=QmUV6r8sKauHk?7)*s)(Tuhcd(;~f6^s%~RwlUf(!#d77we`!mRVA97 z1&ORNVSRf|!SZDDD8`v@u@UCq3@j3fYU^Na<>6q+^ko{wm~5r7ku|o~(nEEj%3#ah zWp?JvY>N4o1IuTU-d?ZwadIdNWm=?$wX0TZZ6%qXsteV224-^ibxLy%a?W!1b53^- zamhKNk8nNAo3CYyMth5fIPLTKA85 zDu|~u$Qqq&zV4Q`ue-`*z9>i{Q{vQap1Kshx2NXSmd7reBoD9h2xIp?!KPgEU$dQ+ zrF*JutgSf4^Z`17iHbTg)-B$iE1#Zgem2`hfp|7;;#uSsI8oMQrj5w5P4;X+9c0PB z1NZ@JKwJJf<1SWA<0*_Riv8Dw`}cP7@9oVZW}B34k~!*d3;9n{zT9U@=OW_k%dPU4 zC9}hE4Q1N=CCQ@ED?h`|*DgKZ>}mAM%dqz|q~}}xqu_ll3ultfpttgLFl6~zGBCx; zN-t~lR-*YMNv&chnUncbX!wnWB{NZrGku&K%R-p8O+)0W9yg9tqDm>WP4wuO=9cJ? zX-M`=_DFGJc9A+iKkEdWfIvf}&KFzO0fDUJv|RJ^$*#((Lr_T%5t+4rn~K`vHB)@L z^a%Rr`0B$wZEUQNf}9@5p`y05p%%wmdNy#&C1+QNc!5tP4YJUcd?}Jrsep=TNPSWt z&P~-OrR64DfBSo8uy?d5i|!da>=OcFEPb{ISKpyTus3NVvR;3)l37U|4>p=>lH8-U zY3isosSW{3hV1usJEtV4B)yL(8&quU-Y-(^sSoSjDXE=b-{CRXjvCw}p(y=>aVige z;r!8Nolsi%$qC=wWSbaVqJ`n(6LISb?N;u7`5vE|Ve4*?bVe)L-tDN1UsrE;pY8<) zzb>U-jy~N|?4w8c*}3Kw?Dh!lTK5F{Ck_ZmV2A|$C9^2pfk9UX(*2dT5is5 zH!7p$-o(d1jhOmJ5u(5Tr7p-l?8l9*zQcCj-88xQ#fxIIIk4Yyg-lzXSJ3j+Yxpft$U*-7wsDpv71C&mr8j>$ zCQNvB@w~dYV(Qz!#=A)dOD7alKkBjIZu0Rl`uOAC9T#W0zavRri4p4Uo|$atZAi%s z^l;BgHFz14v)cLh4G#;f3T7QkSiO07$i#WMR_;pvqA3w2;ZqmoD(+TUbE-ql&1_9v z2^09UGJdM>peTC$K%F6%HGbAi6h&K&+DoSiR9PFXv?lce=Sjn5Z*9L34(qn_JZFkg z9Gx{uS{IjfQHqnZHc{*5Vy^M(9~}|iH#lo}PGn`4oyK49T+%Pm{4;Y-8q~o>=clpH z>k^s6`7@71m*<|6!13s`wXwcxJJCB!r?(X$ty=9HdboORq{dI>H?4233!ePR<3)#q z{PBOq3!td~vw)74v0dARB)*b1#xjU~IfVWHEbBLEe#2{0Ri0KUQ-(07u((M>y&}B^ zPdsK3duWMe^!xvhtaF^By5u?N12i^Cg;Jb*VUcH=S7`~e8J=VQjD`Qd)>S0mmiV1~ z5)1NuaR#}G2?1P(dUREB$W3n>NER0pW9huHVd=yTG4+`{CAaQ3x$s?o&)QXFarYJ5 zi`aVekC?uBnBQe}Y?XOZZf@bscI^_|u|G`N;T2i- zYCoMz#n9wWKTR3l$<@lJcF1iRx{Lo#9qNg0!7^qykUJtxelc;l@PW6z(b7QxZ{8A5 z%Y#Y%%#*xp{eeTgU=sH%Uc7)tC++Hb)r*8KeHRV++lT;-p?v{ z+VWid2##D|9OmD{?1qeMI0gOHOB-wqZ3gQ8yM^_)H}md&_(NtN_Dk#e;d@rD+{QK> z-n{v^xzhYW-ssUtf$}hks z%{C~&%ms{?oD>!}ev~PKTa;nu@>oc(UTb>wN;Wre4~Op_Ja{+k^?z{+mKQ_bR|)8j zhvq2Cg24X^vtNzmXDOf2s5PS@Xjf1&L+9CJtbE50^{46z4MuqXw9|DF!ODOukGDC z35M^B(fbG5B-sQ9J&)7*1z4xp1O}RgfZ>yp!ehsejNqs0T+>~44;ixC3BQqmcl(ht zEq4{+T7;c|uLt-K5GF?|rwZyulu&PzC^*-T>m)kVPpK62^#`(fq5jv-qMga#HQR5@ zm?KSjQjQy{n1W8LhKacB-Z|x^U;tNl_DWwC+l7b#%;xblFLhGA3$J zqMEahja2-!exVNX51TiiGXEOcJveuO%Grvk^S{o=e-8t5J=2}JhSjTitJpE1q!(gRZsngG2+PDq%0t|K1X>@q^eG_~8dF+<}aMUS6S9dmMj_N?9!V_Hrn3JaK@ zKwd8n@^ACG2woQTq6uwxafB@{MsDjtY>cvZefaB=qoaSoKaVW|xP=8+hLX6^+n`hF zFL;>ZS;GIg$lFFo+|XjCwC*>*m(9v^uNj!V#?Fuy&RNMmv5HFiR&A$VsTkCeE|Kb3 zUL}^*rHJurgK9#-1^pow7NzsEca20Fk?wA(o}!cDp6ucr<>(U?W9Qx4ZRXIS)hue# zuwm2Xj@wN+d~Vn5>;c!WoA2bCuey3FilFne!oW{d?s=uVs%^aVdjFn2HX0`@Su3XV zu9-gOb8K=x^S5%2bg=c&`{{jcoq}y-7g^_PuXojYI(pazwLbU$GxvHf{`=7<+bP@@ zPbL<*-axi%s1!qwztUp!98`>W46QHDJ=zw!|yumVR zYz{{u)us1n}8WdOA+TS7HBR{~bc_r@; zp1F>pHf{1+=CdsF=28#KGjp4~uhe~&be7u*#+pdLe{=M|&Bw*R9A_oG%-8_&rN~g( z1c6_wjAM@R|2vh-vb9dCKU?2_D%(zr*Enk@<$HSOFSa>pQTVDY(_7gYvZFAZHzuoW zZfYFV^L-*5qpTvG%}<_38bY1jqip?bqukR{B_}00Wr%UOV^C{hTfUmYCk!53D|cwh z{*Sb+v9ed8WssefVKk<_lr_zK!PQMs_%>OY?ul=%yIs0(hVb^v-dNz@INs{EJKc5p-AhBXejV$Fy`$7RK*SmoWu(fWL$x znWfX#lQW(VyU{$T{m=cSO?68){_{)&r7RO~mTxgO_1_4hk4?N~4oZ$`+t-%1^^I1F zU47Yo!M1+$SwZf~T3HA4e%7J&T7tn{OyS*bXQ+*iYG19hI~(#ECVDE=PTHR@som_o z^BmHAJNkKbNmjcl9fx^o?F{J=oL2UUSE;4*{@b-dF5`-1cTH+9mj&iU^_{*GJp0%O zc*v=|MlR3Z%-MD6vZ6~Db9d{I{(iQ;=spV;M#?rqxQTOcNp!Gw2)8qDR{YET-LhOm zy>nc`OI$E?37%VU(alSR)b+!xm87ugojIFHTS7G6ve)Rc*XmE9l_al{0#`Skx2f*PLN!=%0!<+ zAMc7t+&NPE8g+JdSy7x;F(%=H|8Tw9fL~*NchuIyBi)?2MH}iirBrs-B=>ilZQjP~ z@w)>DO_*y;qO;8FB`a5*COp=V?^zZ!*e<}@lodHHZH25?lC@5@e43`XN3;`X+pU{L zURPDjq)?4}aaph?dtRhyB}B|9)cbe$w)O6LAj~tMk?f(#F7=ld2mAJmW!&e74u!fzO%8SUC@yQ=t7VnuW9{^89*Z_#oSt*UGYgZ2 z+zn$wO^BOEdBs+9xZWu_!6DAA#K-)*n{{N4+J^6x7mfj)xoghS`Qw8$K5DN%<5Py` z+NJwswRn6d?!6hWX&aV(#^X%CDc_O|1_a*`()i&a!y=F6X6siU zbwGC4S|(eeQCq)N-}j_sFvYP&=IWlGV%DIGdAC!Vts~1~_UtU?_3r&cR$xuC;-U2~ z&)uEuo|R%qwN1)ubxUffh*jC>ghDG1-2`d))e3fUiKrIdW#511{)xFeZ=1h)<;ryP z<5+#@P=9Oxp*#EaTQ^_;i|*Uk?CS5|&Ozg@)&+*y`}o;}g&G2F!^3RVo;G^dc10ex z(IW$#GMyt0+$zT?$E>WDv!|pYwY@|EZEr*3+q!Jad{hdR)q8&=In^H=&g@Mu2N3C; zA;vP*g0Xa4cir~FiW^nzkTJO*BGq?HtUfy+u&M)Qam%}z;|oHkrKvr1{_P85^SrzC z4p+PDLV8q$=S6Mmrt;7w4(qyTy-xEj7yMYURoyQ$pDSW_&xH@{*UhN$(RdVh>J)G` z&M`U3G2JOGCXM^uEn|G3)?MwGQTT&q3fIYc~eNJn`5Sxs9&@zT)cI5r)4rGrRa?CNoK^x0H zJlp#FKjsL3;Mqp&ui0*Kx=^Neim|S+4|ixakMv<#Wo9a7Dp@OFYMj?avcy4y()yd< z9X5GCL^THkae~?{=IqnveSXlMEIlI(t?G*|9I!Fc!jF+f{n1znY%MZ88R!j!dyy zbtN-5H)`sZL8_IX6t?{GyL4W{>%74)-sO|{l(v-if(Cej7TtE+HCkRd!sR$`X8C~c zZOh<}Lyq;;4m&+MkLy!W@d9^zv!4BGw&L>5I?kqb6aKddY39>8(Th)pueP|w1)^_7+bkgZAN_GB zlO)XbwLQ%HVF+fle|f}c6?wCn=E26M-_3XLuJtPmlhgV~ z1@g&%PiE!$oJO0jw&KknusC#wPjlbN&0$>byO@Mk=U7QCv$XOH!5~s6SQ-A0m7aL6 zOs*emC%d3Si?ir~0ffcp9V@zZ3*22%vAeQ@ z*|aWVw&ulbrTP7m8B;Qq+!^ybeK0isC`p6<#?uEGh zJFkGp_n0^qo;NHlZCGCJ@U*nyxj9|C=Hzzm%0epU&aE(O=1%9jPMzPn_UhZb9N#A+ zy-!?RpY)7A@%iPM8Rg{}ndMWi=iV}(JDrnrI+w|(Pq%zuAb)z;l!SmCY@1-S$F#w} zFH8LXok&agU%UtArly2cl{jWP6m>O^D03~U2+I=by`S=-sJma{FdGAyU#RRzzOl~wm%fgK85F=TcvXzypYAfoNvnN03@ zLOs>?*1{3Q-;;Wf!xxR5dX5=v@ZJUXK%8)pn;}Z+~ z`VEW0i-*WTJ(KeN22D);zk0h4=&FitJ!e|Zo)Dx22uT2?Lqcx>DFG<~6j32`kf!vm z(l1pIl@f#l7{Ce^)KGqd5fCsc`Y=cjMM~%)^ePDV?U{2+Kz|qidvCqPI*ZAiJu`du z?C-mKPT2>d^TrM9*}PSQozv`Mc_(bUx29gcDA7O^omRJK<KBW7p?PGU5~@ho z_*Q3%)#(0gv9Kpp(H89wMUPxlJ8$W*N*`H=^OsJ1JpA#F4I@glOehvrJfU@pr)i}; zN@E^nDUkLIB!et|DPVIiou1qC_c#{E~q%6C8G?vo=y!dJk3G?QVym-v_X1KLZ zo)b2PyCL2zl&*Keu8CU^Bb|wN?Vdx);odAV0*_fpybHZK5aP@}VMiyRR0j5m?;v8I zY1pUk4@bmNq#30Tw(X0@5N}#QR$z#z@G^zMTc`?m(+YdLdcvNh$Iry^N1wD~bs2kh zK09fb4EMFnLF#`4$6AD%g*eAf+Tk?=eNxmg1MhznH=1OkCYio-8Kx!+YqGFL_h0^$ z-B!;pEug27C(n3qk}gD#rll60vYYxf$~t9t^lKD<+OFv@k3DTiYx$c1I1@%XnlaGI z&S!YWgJ-mLGKpnIqJc3P8HDo~Y@~Bgzd_h~u(pGd&%qTMj@BH3$4H#jC}UM^>>GK4VAtr+nm$T{!~l_26OHmv*C}Ms+l&Q~InOkG(rDowb+f7TWpbIlEtF-LEl1 z2)0VcR_SCtw)_TN?VO#UM_7U7;@S9Y z9+q`|(`>iixP7*}RJwk9wq4OL^JlglQ5xkn9Pn9NMmTt6jUkLx%U1r^w7NMZF-Tk0x71$bMCUC0yz&U;GWToo`Y8j{Hm=&#r5EsSnC=; z6WTx6con*y+t^(fJ+z?=t)}r0cjfn+q1|D%(izxshOg5Uv{_D$-4(5l{sBwBjK z%^ahSR^PSMs(_VptB**X$eiE&C7qE^LLL z;Ab_%FuWpn48w<5+|TN2rkdrRv#y(SVfHCI+s?5s+L!IC_I3NFecQeRTi{37mOA&c zUBvS2fZqs#U9bzWy$ALX2K(V6;cyvlkZ8E2I4P-=3MExln9574t9&XSsi_L60;HBI zq#{Ud6^Sp4)Ke8yMG~)KRXx%~HBgD?5Q+)?{H#4qPVH;R-VGRXnbda8x!N%@uBXN=K<=l$wa`l1)vD?a5tb`xMATp>)%g zuN%0fyFrC;Fr(t%Nhr8uq%d0rwk?k;D6K-^FQbqdHVqX?!7*@>9)yd)PW^LA!=`#D zw^m!jojdpDm|BiCdhiU@66IQHr^ePPfte*5xfQD{wGR$PObv-w+DhI1F zu?pWZ$0~nK7S z2f|-2Y?9^cR$HMs*18^`YjsC%Gu^c|;0mlsLYX8pn6{s`l=cZsoPaXgCfdl!Sd(IE zw9*gOr0MndJD1x`ckQpigg9^re>V=JWwd&_18w{iIAN3ys;6!7po1C@!J)H=#Fl=y zPJ!!45jVUy@rVEVXa!GDr_w5$uW&}=k9Fu>g&JK%}}~KE$()QsRPWaXW%Sye6zrx6W@Z& zM7dwzR z$^5SA_M_d-Uz1K0w)CT8rmBL<;h?Go4xWJJ)?QDhN;Dd$kx&%%>$d_$vs5+ocvn2r zC!XPxb{9%Z2ba$8A+FCS8ngr#G$^Ol)=O+$QO{osxgg9in&nMflTpqkI!zn}^SOGE`iy)0zSO?w5pJ>a%*z>1wcVKK>5$ zDr$pf`6RUWyDI7SE)yjBRLVVLJ)xjnvc`Xsit%;+@QS+T(+KgeXt1ek&_n~f;Bxro zT;C7=WuXP|m)f_)E&{ZjJr@3asK5z7Uj+$6599vO_#69WFk=CB7i7zEX0}-_uVJeo ze+FC^=N^QpVO)Gq13U3^KoYLiO|7YFh8AgV@b0SOXESZ)pmKVCniE{;YMG$A8bSKJ zZjot5(eCP6Bo;@|zZ4z4o*dN)3;r%N2sL52l>G3F-5({tvdOAuuJ!eU%q1;7sQ~)n zDze2j9Hk=4$KZjkictzdyGJ^O@7N{6d;t%JHyGTcHo0SanCFyz3Qd`fiDK87upl4QKfqwWlbkLFhrb5cB!}2+hn&|s2s9ay|3OU`*A^RVtaSu|CU-nsMFAE zr}l0Pt>I~C2S308I0`4=BwU0Wa1(CBAMhvK1Fs?~6eGg|s;DZiN~p@Jx~ij|R1H-N z)n0W~-Bmv|Kn=l|Z@luTH`Q$Q4!&gN4t3TH_3VO<(70R^XaSjU1dd^>SxnVa%~com zikhsZ_+)Pz+1d*>Lz$?9kfmy>-l~u4YtG{C6aS~mRVaXk&f1orVP#PF0VFEs%9A-WAm4*aLLj$E@So#1D zN%HD>!)pAu4r7_sI1$Vch<0##bEs~ub{29d@!XfV#l-7rxc<2Iggkcr!M_V)!qUKb ziP+InA%QxI_l*S(vIf?|Ivfs@xHwe>gy+MH?GjROXx|y9qJR>$&e~`6?goCq-N+5x zlU&0+$t~QK{DC`^E4WX&ggchg2*{sc8~hBvAV9am*3@kR@cxJDsd~8!@2C){+)H~m zy457@DCphO)O6&F^Y#%nL%m_&vG3aF>>M@AzJQL0{*KN!N6l69RFayHZn!{uzgnag zt0n4Pm8_PkWoo&4PrVP{L%My!K5l2(C!MAt5bwbYYn<%EOJyMhcf@$b<^X0ojvxo| za$9Y32rr+-k;70A>XF~^@>@r81Ukboa@<(sHeOg;PwwEwwT%Q5`3CK1jl{n!D3q(N zwpYKt<*tNwYVOmb!bz;gt9a%L9wuE4c7kh(o)6!_WSg%AGCl_l>@k*~g|osel0~y}tRky~w`*$P?V>cK?C2T4CfPKt9WnZwb@Ls_> zym|gD+sO1ohd;7y%<~J&zz0Hhv3)F)9b`G|BD=!=;0)h`0G@~E<@tCq{x~nm%kmgr zh1cS7ydH1JoAH*sHE+Y)^7gz7@6KQ3y?K8=gb(Mh@X>rMpTIqQGM~n0@VEFJ{tjQn zllcn%1z*j-;otG^`8J-xckw;^pojm?kMS&ilAq?;{1U&yuk%~{F2BdU_$+g%C@2bx zV&ZX8O2mlDqME2FYKu5gPt+F;MPu=lXeL^SR^mm`Tl5wE#UL?4j1?X+Sxgr*#Vql* zm?s>uM0_AV7AwW4;&btZSS`L08^k8@qu3^X5uWWLL;NOoi9KSU$P@>~VR1wp6DP!J zaaLrD9C2A(6E|f+Sy&d8Wn={zBP+}5vY~7&o5=*(TDFlLWEa_8_LO~OKRHkik;COE zIYy3`6Xg^+UA`gTmUHDh(vcs^kL60tm9kl;%b(=W@>jW2?k+F)$%FEDc}!->Y|Mpm zBg78LBX7~}BH3C`EAv5qcXqnwht|#n{5|&^z;{owtkc#->xy-k61+8_)1NC^08^mr z1n9+R3Hk&rWfGys&>ENuy&i2q<7pGxg0{kR=WSv-N){XUG zy;xs1fDK~9*hsv+Hi1oJGuT{~gqhhF;jN`*>_fJKc|K!bvJ|!kZyTrK6O5bib+fJb zZ067S%Ek`1lkH~v*#UOQq;S8ADcm^^!SwB6{4t)N7dJ`V%kfxVmDe`O+#B)cycK^M z^SD396M0wOgZJcp_y9hXkKnKJF?<}K$S3hBd^(@W-{y1q0>0S8m-3bTOTLDu@y$G) z|IByr-Fz=U#E*5XZrkE{~#3J#o_)vIOh)=|4 zVwLz(tPyEqqxeqzBz_kEC4Ln<#7?nW>=paP0dYwDE{=-h;-oku&WZElg192Ci(4{W z7Lk#%tgI+wWffUNHj+(bbCbRPS=mu`_2sbdF9*q?a)ilcKfz?PpCR8g8SNLel8fX= za)tavu9I8j4{{sc!p@MpOq%;cCei%~d0t+UH}Q8M%-&}ofD!NtOn|oo_fA|Mj@IE^CjK38Ss!)+rccUAFGPSjuS#1sF#kU~L%x zAF*~W&W4G|*`7xtZSZK)hDk1MYu1LzE^EUS&Dt>apLiRlJ;dA{Syy;nGdIle{FS<4 z<|C2!f5hD|OS3n;`4{%~82W~{9*w~5{SOj&F8)6E_Y4lhUH*nSn!jOgkiTJ`Xa-3h zKY`l^HHF)UVE%(N4)6ROjoXKF(ReNvhXpQ+!$NTUEDnn_i^F1B7?uQC9Fk>YSSmdU zund;Nd+N#2m&xtl zk;!x5E0@Vda&8XCE8K!u_)-P8>RJt~M%GhSE31u@5DK-NmHDA?fZ5L&X7}@Y*#C;z zADP$bR?lB}J?Gytd)|NK_QwAayX!A+Xnyy&1pj}-@74d2;a~ncj`svf{t-C-?7wCC zN8|W@hUJHdVF9N1JWTcf&rIJ{K1B7%^o!-PM`wFv`oHkJb&zE1O9{@jaCpKq(pqbM zXMJz&L~=iCWm`GcCF?4_P$p?8EksKrkypf^JApn!yV0I>ARR_WVxT>RzK((R9O}>| zbSZt0enG#cd+C097?ZVMr*~OVR-Bb$WmpAPoz-GbvUt{nC9u{oopoTHU?5|4ea_ahui1KhnekV)haG08*?D#g-eC8A(QhGMgcs!%cqLwq z*W`70eIC!7@&x`o@5npzetaMw#;5Yxd>(iB626Ro!oT9{Ft**#5AehMG|c8X{2IT> z{}2@4k_!>|WzjHEKok-YA_^11mK0?~IZ;7W5>-XKXeoM$fnvBAC0-Sy#dtAQ%n)*Aex*_{juaK z{OHaex(|cJ(-=#MJzt0y&I|JhUfu+FHIM+F%cp47xYt1Acvpr z@nL)|#&7$0CdO}PxW=@J+k_<;Mr}66ZTU^)7AZV#@K#ooHvwED4A*)hgf)C$5wBse zrb9IyrY*ogZJCMA)?s9(Bd}oX6)8)}`i~HE>F);xA}+Z^Cd(hHVwAN<>ImzEJSES{ z^C8L?U+HI4M);#ELaim_7lXKp5fFul(|Frs9YU;K#W$W^q`4?-gD7(`HV#oX)u1dl z#(qMSWd=|t*%OGevH_GOu%}&wA-;wp!aN#b4*_hI2l2JnAnOLZ&F&(aTqJ1>wKpgl zh&#pEJc+;SL(dl6A7=2K>H|d3F@qtEpgTSUDIb7}if99%iU^)w;^hEv#)t`G8Ukmo z4>+FXA_bB2H3kbVb}kv@6hz!qdH^}k$z zVDk%pNH?#up(vE{97Ny*VH1Rm3z*;rEH_}bJpdVjPiE$ZjJtQzaM8P^v25bQa4~54 zmx~e)yKX*b6@mL64GRs3L<5NdxVO>!n#E>Iv({{AcJSW}9F7}6{5v>kG-xFBG~1Pb zMuBHz00{0Dq!eyG{)aFyD0mP92h7u1BZ4~&edQ+~G57XK=z4EUCou{NSsAH=qM%EN zRmDj!iC-=*=OmZH|9Yg3Dh>Dr8wpY!YJY_ARFta{Gn+7M-xcI-NE&06Bzt^+NcC*vT+ z=}`wF9B&*%J2CYkkMmVs$dg*7E?l?tnQ&*0{=3nW7(1h8zr{n8Xp5uPOg$S9d+2`y Dp+=04 delta 22396 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 BOPl}z diff --git a/res/lang/de.json b/res/lang/de.json index 95ca511..85302ae 100644 --- a/res/lang/de.json +++ b/res/lang/de.json @@ -1369,6 +1369,61 @@ "warning_upper": "WARNUNG!", "website": "Webseite", "window_opacity": "Fenster-Transparenz", + "wiz_acrylic": "Acryl-Glaseffekte", + "wiz_acrylic_desc": "Transparente Unschärfe auf Panels (Aus deaktiviert)", + "wiz_already_encrypted": "Wallet ist bereits verschlüsselt", + "wiz_already_encrypted_desc": "Ihr Wallet ist mit einer Passphrase geschützt. Es sind keine weiteren Schritte erforderlich.", + "wiz_appearance": "Darstellung", + "wiz_bootstrap": "Bootstrap", + "wiz_bootstrap_desc": "Laden Sie ein Blockchain-Bootstrap herunter, um die anfängliche Synchronisierung erheblich zu beschleunigen.\n\nIhre vorhandene wallet.dat wird NICHT verändert oder ersetzt.", + "wiz_bootstrap_failed": "Bootstrap fehlgeschlagen", + "wiz_confirm": "Bestätigen:", + "wiz_continue": "Weiter", + "wiz_daemon_sending_stop": "Stopp-Befehl wird gesendet...", + "wiz_daemon_stop_failed": "Daemon wurde nicht gestoppt — manuell versuchen.", + "wiz_daemon_stopped_ok": "Daemon gestoppt.", + "wiz_daemon_waiting_stop": "Warten, bis der Daemon herunterfährt...", + "wiz_download_failed": "Download fehlgeschlagen", + "wiz_encrypt_bg": "Die Verschlüsselung wird im Hintergrund abgeschlossen", + "wiz_encrypt_continue": "Verschlüsseln & Weiter", + "wiz_encrypt_desc": "Verschlüsseln Sie Ihr Wallet, um private Schlüssel mit einer Passphrase zu schützen.", + "wiz_encrypt_warning": "Wenn Sie Ihre Passphrase verlieren, verlieren Sie den Zugriff auf Ihr Guthaben.", + "wiz_encryption": "Verschlüsselung", + "wiz_ext_daemon_running": "Externer Daemon läuft", + "wiz_ext_daemon_warning": "Er muss gestoppt werden, bevor ein Bootstrap heruntergeladen wird, sonst könnten Chain-Daten beschädigt werden.", + "wiz_level": "Stufe:", + "wiz_lowspec_desc": "Alle aufwendigen visuellen Effekte deaktivieren", + "wiz_off": "Aus", + "wiz_pass_mismatch": "Passphrasen stimmen nicht überein", + "wiz_pass_spaces": "Passphrase hat führende/nachfolgende Leerzeichen — entfernen Sie diese", + "wiz_pass_too_short": "Passphrase muss mindestens 8 Zeichen lang sein (%zu/8)", + "wiz_passphrase": "Passphrase:", + "wiz_pin_confirm": "PIN bestätigen:", + "wiz_pin_invalid": "PIN muss 4-8 Ziffern haben", + "wiz_pin_label": "PIN (4-8 Ziffern):", + "wiz_pin_mismatch": "PINs stimmen nicht überein", + "wiz_pin_optional": "Schnellentsperr-PIN (optional)", + "wiz_pin_title": "Schnellentsperr-PIN", + "wiz_scanline_desc": "CRT-Scanline-Effekt in der Konsole", + "wiz_skip": "Überspringen", + "wiz_skip_confirm": "OHNE Verschlüsselung fortfahren? Schlüssel werden unverschlüsselt gespeichert — klicken Sie erneut auf Überspringen, um zu bestätigen.", + "wiz_step1": "Schritt 1", + "wiz_step2": "Schritt 2", + "wiz_step3": "Schritt 3", + "wiz_stop_daemon": "Daemon stoppen", + "wiz_strength": "Stärke: %s", + "wiz_strength_fair": "Mäßig", + "wiz_strength_good": "Gut", + "wiz_strength_strong": "Stark", + "wiz_strength_weak": "Schwach", + "wiz_theme_builtin": "Integriert", + "wiz_theme_custom": "Benutzerdefiniert", + "wiz_theme_effects": "Visuelle Theme-Effekte", + "wiz_theme_effects_desc": "Animierte Ränder, Farbverlauf", + "wiz_theme_invalid": " (ungültig)", + "wiz_ui_opacity_desc": "Transparenz von Karten & Seitenleiste (1.0 = deckend)", + "wiz_welcome_sub": "Ein paar schnelle Einstellungen und Ihr Full Node ist bereit.", + "wiz_welcome_title": "Willkommen bei ObsidianDragon!", "wizard_daemon_start_failed": "Daemon-Start fehlgeschlagen — wird automatisch wiederholt", "xmrig_browse_releases": "Alle Releases durchsuchen…", "xmrig_checking": "Nach dem neuesten Miner wird gesucht…", diff --git a/res/lang/es.json b/res/lang/es.json index 2350c49..36cb94d 100644 --- a/res/lang/es.json +++ b/res/lang/es.json @@ -1369,6 +1369,61 @@ "warning_upper": "¡ADVERTENCIA!", "website": "Sitio Web", "window_opacity": "Opacidad de ventana", + "wiz_acrylic": "Efectos de vidrio acrílico", + "wiz_acrylic_desc": "Desenfoque translúcido en paneles (Desactivado lo apaga)", + "wiz_already_encrypted": "La cartera ya está cifrada", + "wiz_already_encrypted_desc": "Tu cartera está protegida con una frase de contraseña. No es necesaria ninguna acción adicional.", + "wiz_appearance": "Apariencia", + "wiz_bootstrap": "Bootstrap", + "wiz_bootstrap_desc": "Descarga un bootstrap de la cadena de bloques para acelerar drásticamente la sincronización inicial.\\n\\nTu wallet.dat existente NO se modificará ni se reemplazará.", + "wiz_bootstrap_failed": "Error en el bootstrap", + "wiz_confirm": "Confirmar:", + "wiz_continue": "Continuar", + "wiz_daemon_sending_stop": "Enviando comando de detención...", + "wiz_daemon_stop_failed": "El daemon no se detuvo — inténtalo manualmente.", + "wiz_daemon_stopped_ok": "Daemon detenido.", + "wiz_daemon_waiting_stop": "Esperando a que el daemon se cierre...", + "wiz_download_failed": "Error de descarga", + "wiz_encrypt_bg": "El cifrado se completará en segundo plano", + "wiz_encrypt_continue": "Cifrar y continuar", + "wiz_encrypt_desc": "Cifra tu cartera para proteger las claves privadas con una frase de contraseña.", + "wiz_encrypt_warning": "Si pierdes tu frase de contraseña, pierdes el acceso a tus fondos.", + "wiz_encryption": "Cifrado", + "wiz_ext_daemon_running": "Daemon externo en ejecución", + "wiz_ext_daemon_warning": "Debe detenerse antes de descargar un bootstrap; de lo contrario, los datos de la cadena podrían corromperse.", + "wiz_level": "Nivel:", + "wiz_lowspec_desc": "Desactivar todos los efectos visuales pesados", + "wiz_off": "Desactivado", + "wiz_pass_mismatch": "Las frases de contraseña no coinciden", + "wiz_pass_spaces": "La frase de contraseña tiene espacios al inicio o al final — elimínalos", + "wiz_pass_too_short": "La frase de contraseña debe tener al menos 8 caracteres (%zu/8)", + "wiz_passphrase": "Frase de contraseña:", + "wiz_pin_confirm": "Confirmar PIN:", + "wiz_pin_invalid": "El PIN debe tener entre 4 y 8 dígitos", + "wiz_pin_label": "PIN (4-8 dígitos):", + "wiz_pin_mismatch": "Los PIN no coinciden", + "wiz_pin_optional": "PIN de desbloqueo rápido (opcional)", + "wiz_pin_title": "PIN de desbloqueo rápido", + "wiz_scanline_desc": "Efecto de líneas de barrido CRT en la consola", + "wiz_skip": "Omitir", + "wiz_skip_confirm": "¿Continuar SIN cifrado? Las claves se almacenarán sin cifrar — haz clic en Omitir de nuevo para confirmar.", + "wiz_step1": "Paso 1", + "wiz_step2": "Paso 2", + "wiz_step3": "Paso 3", + "wiz_stop_daemon": "Detener daemon", + "wiz_strength": "Seguridad: %s", + "wiz_strength_fair": "Aceptable", + "wiz_strength_good": "Buena", + "wiz_strength_strong": "Fuerte", + "wiz_strength_weak": "Débil", + "wiz_theme_builtin": "Integrado", + "wiz_theme_custom": "Personalizado", + "wiz_theme_effects": "Efectos visuales del tema", + "wiz_theme_effects_desc": "Bordes animados, capa de color", + "wiz_theme_invalid": " (no válido)", + "wiz_ui_opacity_desc": "Transparencia de tarjetas y barra lateral (1.0 = sólido)", + "wiz_welcome_sub": "Unas cuantas opciones rápidas y tu nodo completo estará listo.", + "wiz_welcome_title": "¡Bienvenido a ObsidianDragon!", "wizard_daemon_start_failed": "Error al iniciar el daemon — se reintentará automáticamente", "xmrig_browse_releases": "Ver todos los lanzamientos…", "xmrig_checking": "Buscando el minero más reciente…", diff --git a/res/lang/fr.json b/res/lang/fr.json index c088f6e..2fab1c6 100644 --- a/res/lang/fr.json +++ b/res/lang/fr.json @@ -1369,6 +1369,61 @@ "warning_upper": "ATTENTION !", "website": "Site web", "window_opacity": "Opacité de la fenêtre", + "wiz_acrylic": "Effets de verre acrylique", + "wiz_acrylic_desc": "Flou translucide sur les panneaux (Désactivé les coupe)", + "wiz_already_encrypted": "Le portefeuille est déjà chiffré", + "wiz_already_encrypted_desc": "Votre portefeuille est protégé par une phrase secrète. Aucune autre action n'est nécessaire.", + "wiz_appearance": "Apparence", + "wiz_bootstrap": "Amorçage", + "wiz_bootstrap_desc": "Téléchargez un amorçage de la blockchain pour accélérer considérablement la synchronisation initiale.\n\nVotre fichier wallet.dat existant NE sera PAS modifié ni remplacé.", + "wiz_bootstrap_failed": "Échec de l'amorçage", + "wiz_confirm": "Confirmer :", + "wiz_continue": "Continuer", + "wiz_daemon_sending_stop": "Envoi de la commande d'arrêt...", + "wiz_daemon_stop_failed": "Le démon ne s'est pas arrêté — essayez manuellement.", + "wiz_daemon_stopped_ok": "Démon arrêté.", + "wiz_daemon_waiting_stop": "En attente de l'arrêt du démon...", + "wiz_download_failed": "Échec du téléchargement", + "wiz_encrypt_bg": "Le chiffrement se terminera en arrière-plan", + "wiz_encrypt_continue": "Chiffrer et continuer", + "wiz_encrypt_desc": "Chiffrez votre portefeuille pour protéger les clés privées avec une phrase secrète.", + "wiz_encrypt_warning": "Si vous perdez votre phrase secrète, vous perdez l'accès à vos fonds.", + "wiz_encryption": "Chiffrement", + "wiz_ext_daemon_running": "Démon externe en cours d'exécution", + "wiz_ext_daemon_warning": "Il doit être arrêté avant de télécharger un amorçage, sinon les données de la chaîne pourraient être corrompues.", + "wiz_level": "Niveau :", + "wiz_lowspec_desc": "Désactiver tous les effets visuels lourds", + "wiz_off": "Désactivé", + "wiz_pass_mismatch": "Les phrases secrètes ne correspondent pas", + "wiz_pass_spaces": "La phrase secrète comporte des espaces en début/fin — supprimez-les", + "wiz_pass_too_short": "La phrase secrète doit comporter au moins 8 caractères (%zu/8)", + "wiz_passphrase": "Phrase secrète :", + "wiz_pin_confirm": "Confirmer le PIN :", + "wiz_pin_invalid": "Le PIN doit comporter 4 à 8 chiffres", + "wiz_pin_label": "PIN (4 à 8 chiffres) :", + "wiz_pin_mismatch": "Les PIN ne correspondent pas", + "wiz_pin_optional": "PIN de déverrouillage rapide (facultatif)", + "wiz_pin_title": "PIN de déverrouillage rapide", + "wiz_scanline_desc": "Effet de lignes de balayage CRT dans la console", + "wiz_skip": "Ignorer", + "wiz_skip_confirm": "Continuer SANS chiffrement ? Les clés seront stockées non chiffrées — cliquez à nouveau sur Ignorer pour confirmer.", + "wiz_step1": "Étape 1", + "wiz_step2": "Étape 2", + "wiz_step3": "Étape 3", + "wiz_stop_daemon": "Arrêter le démon", + "wiz_strength": "Robustesse : %s", + "wiz_strength_fair": "Moyenne", + "wiz_strength_good": "Bonne", + "wiz_strength_strong": "Forte", + "wiz_strength_weak": "Faible", + "wiz_theme_builtin": "Intégré", + "wiz_theme_custom": "Personnalisé", + "wiz_theme_effects": "Effets visuels du thème", + "wiz_theme_effects_desc": "Bordures animées, voile de couleur", + "wiz_theme_invalid": " (non valide)", + "wiz_ui_opacity_desc": "Transparence des cartes et de la barre latérale (1.0 = opaque)", + "wiz_welcome_sub": "Quelques choix rapides et votre nœud complet est prêt.", + "wiz_welcome_title": "Bienvenue dans ObsidianDragon !", "wizard_daemon_start_failed": "Échec du démarrage du daemon — sera réessayé automatiquement", "xmrig_browse_releases": "Parcourir toutes les versions…", "xmrig_checking": "Recherche du dernier mineur…", diff --git a/res/lang/ja.json b/res/lang/ja.json index 6ffc0ee..2a3cf5d 100644 --- a/res/lang/ja.json +++ b/res/lang/ja.json @@ -1369,6 +1369,61 @@ "warning_upper": "警告!", "website": "ウェブサイト", "window_opacity": "ウィンドウ透明度", + "wiz_acrylic": "アクリルガラス効果", + "wiz_acrylic_desc": "パネルの半透明ぼかし(オフで無効化)", + "wiz_already_encrypted": "ウォレットはすでに暗号化されています", + "wiz_already_encrypted_desc": "ウォレットはパスフレーズで保護されています。これ以上の操作は不要です。", + "wiz_appearance": "外観", + "wiz_bootstrap": "ブートストラップ", + "wiz_bootstrap_desc": "ブロックチェーンのブートストラップをダウンロードして初回同期を大幅に高速化します。\\n\\n既存の wallet.dat は変更も置き換えもされません。", + "wiz_bootstrap_failed": "ブートストラップに失敗しました", + "wiz_confirm": "確認:", + "wiz_continue": "続行", + "wiz_daemon_sending_stop": "停止コマンドを送信中...", + "wiz_daemon_stop_failed": "デーモンが停止しませんでした — 手動でお試しください。", + "wiz_daemon_stopped_ok": "デーモンを停止しました。", + "wiz_daemon_waiting_stop": "デーモンの終了を待機中...", + "wiz_download_failed": "ダウンロードに失敗しました", + "wiz_encrypt_bg": "暗号化はバックグラウンドで完了します", + "wiz_encrypt_continue": "暗号化して続行", + "wiz_encrypt_desc": "ウォレットを暗号化して、パスフレーズで秘密鍵を保護します。", + "wiz_encrypt_warning": "パスフレーズを紛失すると、資金にアクセスできなくなります。", + "wiz_encryption": "暗号化", + "wiz_ext_daemon_running": "外部デーモンが実行中です", + "wiz_ext_daemon_warning": "ブートストラップをダウンロードする前に停止する必要があります。停止しないとチェーンデータが破損する可能性があります。", + "wiz_level": "レベル:", + "wiz_lowspec_desc": "重い視覚効果をすべて無効にする", + "wiz_off": "オフ", + "wiz_pass_mismatch": "パスフレーズが一致しません", + "wiz_pass_spaces": "パスフレーズの先頭または末尾に空白があります — 削除してください", + "wiz_pass_too_short": "パスフレーズは 8 文字以上にしてください(%zu/8)", + "wiz_passphrase": "パスフレーズ:", + "wiz_pin_confirm": "PIN の確認:", + "wiz_pin_invalid": "PIN は 4〜8 桁にしてください", + "wiz_pin_label": "PIN(4〜8 桁):", + "wiz_pin_mismatch": "PIN が一致しません", + "wiz_pin_optional": "クイックロック解除 PIN(任意)", + "wiz_pin_title": "クイックロック解除 PIN", + "wiz_scanline_desc": "コンソールの CRT 走査線効果", + "wiz_skip": "スキップ", + "wiz_skip_confirm": "暗号化せずに続行しますか? 鍵は暗号化されずに保存されます — もう一度スキップをクリックして確認してください。", + "wiz_step1": "ステップ 1", + "wiz_step2": "ステップ 2", + "wiz_step3": "ステップ 3", + "wiz_stop_daemon": "デーモンを停止", + "wiz_strength": "強度:%s", + "wiz_strength_fair": "まずまず", + "wiz_strength_good": "良い", + "wiz_strength_strong": "強い", + "wiz_strength_weak": "弱い", + "wiz_theme_builtin": "内蔵", + "wiz_theme_custom": "カスタム", + "wiz_theme_effects": "テーマの視覚効果", + "wiz_theme_effects_desc": "枠のアニメーション、カラーウォッシュ", + "wiz_theme_invalid": " (無効)", + "wiz_ui_opacity_desc": "カードとサイドバーの透明度(1.0 = 不透明)", + "wiz_welcome_sub": "いくつか選ぶだけでフルノードの準備が整います。", + "wiz_welcome_title": "ObsidianDragon へようこそ!", "wizard_daemon_start_failed": "デーモンの起動に失敗しました — 自動的に再試行されます", "xmrig_browse_releases": "すべてのリリースを表示…", "xmrig_checking": "最新のマイナーを確認中…", diff --git a/res/lang/ko.json b/res/lang/ko.json index 5f87a6d..0b04f66 100644 --- a/res/lang/ko.json +++ b/res/lang/ko.json @@ -1369,6 +1369,61 @@ "warning_upper": "경고!", "website": "웹사이트", "window_opacity": "창 투명도", + "wiz_acrylic": "아크릴 유리 효과", + "wiz_acrylic_desc": "패널에 반투명 블러 적용 (끄면 비활성화)", + "wiz_already_encrypted": "지갑이 이미 암호화되어 있습니다", + "wiz_already_encrypted_desc": "지갑이 암호로 보호되어 있습니다. 추가 작업이 필요하지 않습니다.", + "wiz_appearance": "모양", + "wiz_bootstrap": "부트스트랩", + "wiz_bootstrap_desc": "블록체인 부트스트랩을 다운로드하여 초기 동기화 속도를 크게 높입니다.\\n\\n기존 wallet.dat은 수정되거나 교체되지 않습니다.", + "wiz_bootstrap_failed": "부트스트랩 실패", + "wiz_confirm": "확인:", + "wiz_continue": "계속", + "wiz_daemon_sending_stop": "중지 명령 전송 중...", + "wiz_daemon_stop_failed": "데몬이 중지되지 않았습니다 — 수동으로 시도하세요.", + "wiz_daemon_stopped_ok": "데몬이 중지되었습니다.", + "wiz_daemon_waiting_stop": "데몬 종료를 기다리는 중...", + "wiz_download_failed": "다운로드 실패", + "wiz_encrypt_bg": "암호화는 백그라운드에서 완료됩니다", + "wiz_encrypt_continue": "암호화 후 계속", + "wiz_encrypt_desc": "지갑을 암호화하여 암호로 개인 키를 보호합니다.", + "wiz_encrypt_warning": "암호를 분실하면 자금에 접근할 수 없게 됩니다.", + "wiz_encryption": "암호화", + "wiz_ext_daemon_running": "외부 데몬 실행 중", + "wiz_ext_daemon_warning": "부트스트랩을 다운로드하기 전에 데몬을 중지해야 합니다. 그렇지 않으면 체인 데이터가 손상될 수 있습니다.", + "wiz_level": "수준:", + "wiz_lowspec_desc": "모든 무거운 시각 효과 끄기", + "wiz_off": "끔", + "wiz_pass_mismatch": "암호가 일치하지 않습니다", + "wiz_pass_spaces": "암호 앞뒤에 공백이 있습니다 — 제거하세요", + "wiz_pass_too_short": "암호는 8자 이상이어야 합니다 (%zu/8)", + "wiz_passphrase": "암호:", + "wiz_pin_confirm": "PIN 확인:", + "wiz_pin_invalid": "PIN은 4-8자리여야 합니다", + "wiz_pin_label": "PIN (4-8자리):", + "wiz_pin_mismatch": "PIN이 일치하지 않습니다", + "wiz_pin_optional": "빠른 잠금 해제 PIN (선택 사항)", + "wiz_pin_title": "빠른 잠금 해제 PIN", + "wiz_scanline_desc": "콘솔의 CRT 스캔라인 효과", + "wiz_skip": "건너뛰기", + "wiz_skip_confirm": "암호화 없이 계속하시겠습니까? 키가 암호화되지 않은 채로 저장됩니다 — 확인하려면 건너뛰기를 다시 클릭하세요.", + "wiz_step1": "1단계", + "wiz_step2": "2단계", + "wiz_step3": "3단계", + "wiz_stop_daemon": "데몬 중지", + "wiz_strength": "강도: %s", + "wiz_strength_fair": "보통", + "wiz_strength_good": "양호", + "wiz_strength_strong": "강함", + "wiz_strength_weak": "약함", + "wiz_theme_builtin": "기본 제공", + "wiz_theme_custom": "사용자 지정", + "wiz_theme_effects": "테마 시각 효과", + "wiz_theme_effects_desc": "애니메이션 테두리, 컬러 워시", + "wiz_theme_invalid": " (잘못됨)", + "wiz_ui_opacity_desc": "카드 및 사이드바 투명도 (1.0 = 불투명)", + "wiz_welcome_sub": "몇 가지만 선택하면 풀 노드가 준비됩니다.", + "wiz_welcome_title": "ObsidianDragon에 오신 것을 환영합니다!", "wizard_daemon_start_failed": "데몬 시작 실패 — 자동으로 재시도됩니다", "xmrig_browse_releases": "모든 릴리스 보기…", "xmrig_checking": "최신 채굴기를 확인하는 중…", diff --git a/res/lang/pt.json b/res/lang/pt.json index 49ce80d..18fe9c4 100644 --- a/res/lang/pt.json +++ b/res/lang/pt.json @@ -1369,6 +1369,61 @@ "warning_upper": "AVISO!", "website": "Website", "window_opacity": "Opacidade da Janela", + "wiz_acrylic": "Efeitos de vidro acrílico", + "wiz_acrylic_desc": "Desfoque translúcido nos painéis (Desativado remove)", + "wiz_already_encrypted": "A carteira já está encriptada", + "wiz_already_encrypted_desc": "A sua carteira está protegida com uma frase-passe. Não é necessária nenhuma ação adicional.", + "wiz_appearance": "Aparência", + "wiz_bootstrap": "Bootstrap", + "wiz_bootstrap_desc": "Baixe um bootstrap da blockchain para acelerar drasticamente a sincronização inicial.\\n\\nO seu wallet.dat existente NÃO será modificado nem substituído.", + "wiz_bootstrap_failed": "Falha no bootstrap", + "wiz_confirm": "Confirmar:", + "wiz_continue": "Continuar", + "wiz_daemon_sending_stop": "A enviar comando de parada...", + "wiz_daemon_stop_failed": "O daemon não parou — tente manualmente.", + "wiz_daemon_stopped_ok": "Daemon parado.", + "wiz_daemon_waiting_stop": "A aguardar o encerramento do daemon...", + "wiz_download_failed": "Falha no download", + "wiz_encrypt_bg": "A encriptação será concluída em segundo plano", + "wiz_encrypt_continue": "Encriptar e continuar", + "wiz_encrypt_desc": "Encripte a sua carteira para proteger as chaves privadas com uma frase-passe.", + "wiz_encrypt_warning": "Se perder a sua frase-passe, perde o acesso aos seus fundos.", + "wiz_encryption": "Encriptação", + "wiz_ext_daemon_running": "Daemon externo em execução", + "wiz_ext_daemon_warning": "Deve ser parado antes de baixar um bootstrap, caso contrário os dados da cadeia podem ficar corrompidos.", + "wiz_level": "Nível:", + "wiz_lowspec_desc": "Desativar todos os efeitos visuais pesados", + "wiz_off": "Desativado", + "wiz_pass_mismatch": "As frases-passe não coincidem", + "wiz_pass_spaces": "A frase-passe tem espaços no início/fim — remova-os", + "wiz_pass_too_short": "A frase-passe deve ter pelo menos 8 caracteres (%zu/8)", + "wiz_passphrase": "Frase-passe:", + "wiz_pin_confirm": "Confirmar PIN:", + "wiz_pin_invalid": "O PIN deve ter 4-8 dígitos", + "wiz_pin_label": "PIN (4-8 dígitos):", + "wiz_pin_mismatch": "Os PINs não coincidem", + "wiz_pin_optional": "PIN de desbloqueio rápido (opcional)", + "wiz_pin_title": "PIN de desbloqueio rápido", + "wiz_scanline_desc": "Efeito de scanline CRT na consola", + "wiz_skip": "Ignorar", + "wiz_skip_confirm": "Continuar SEM encriptação? As chaves serão armazenadas sem encriptação — clique em Ignorar novamente para confirmar.", + "wiz_step1": "Passo 1", + "wiz_step2": "Passo 2", + "wiz_step3": "Passo 3", + "wiz_stop_daemon": "Parar daemon", + "wiz_strength": "Força: %s", + "wiz_strength_fair": "Razoável", + "wiz_strength_good": "Boa", + "wiz_strength_strong": "Forte", + "wiz_strength_weak": "Fraca", + "wiz_theme_builtin": "Integrado", + "wiz_theme_custom": "Personalizado", + "wiz_theme_effects": "Efeitos visuais do tema", + "wiz_theme_effects_desc": "Bordas animadas, matiz de cor", + "wiz_theme_invalid": " (inválido)", + "wiz_ui_opacity_desc": "Transparência de cartões e barra lateral (1.0 = sólido)", + "wiz_welcome_sub": "Algumas escolhas rápidas e o seu full node estará pronto.", + "wiz_welcome_title": "Bem-vindo ao ObsidianDragon!", "wizard_daemon_start_failed": "Falha ao iniciar o daemon — será tentado novamente automaticamente", "xmrig_browse_releases": "Navegar por todos os lançamentos…", "xmrig_checking": "Verificando o minerador mais recente…", diff --git a/res/lang/ru.json b/res/lang/ru.json index 1948d8d..06642a5 100644 --- a/res/lang/ru.json +++ b/res/lang/ru.json @@ -1369,6 +1369,61 @@ "warning_upper": "ПРЕДУПРЕЖДЕНИЕ!", "website": "Веб-сайт", "window_opacity": "Прозрачность окна", + "wiz_acrylic": "Эффект акрилового стекла", + "wiz_acrylic_desc": "Полупрозрачное размытие панелей (Выкл. отключает)", + "wiz_already_encrypted": "Кошелёк уже зашифрован", + "wiz_already_encrypted_desc": "Ваш кошелёк защищён парольной фразой. Дополнительных действий не требуется.", + "wiz_appearance": "Внешний вид", + "wiz_bootstrap": "Начальная загрузка", + "wiz_bootstrap_desc": "Загрузите начальный образ блокчейна, чтобы значительно ускорить первичную синхронизацию.\\n\\nВаш существующий файл wallet.dat НЕ будет изменён или заменён.", + "wiz_bootstrap_failed": "Ошибка начальной загрузки", + "wiz_confirm": "Подтверждение:", + "wiz_continue": "Продолжить", + "wiz_daemon_sending_stop": "Отправка команды остановки...", + "wiz_daemon_stop_failed": "Демон не остановился — попробуйте вручную.", + "wiz_daemon_stopped_ok": "Демон остановлен.", + "wiz_daemon_waiting_stop": "Ожидание завершения работы демона...", + "wiz_download_failed": "Ошибка загрузки", + "wiz_encrypt_bg": "Шифрование завершится в фоновом режиме", + "wiz_encrypt_continue": "Зашифровать и продолжить", + "wiz_encrypt_desc": "Зашифруйте кошелёк, чтобы защитить приватные ключи парольной фразой.", + "wiz_encrypt_warning": "Если вы потеряете парольную фразу, вы потеряете доступ к своим средствам.", + "wiz_encryption": "Шифрование", + "wiz_ext_daemon_running": "Запущен внешний демон", + "wiz_ext_daemon_warning": "Его необходимо остановить перед загрузкой начального образа, иначе данные цепочки могут быть повреждены.", + "wiz_level": "Уровень:", + "wiz_lowspec_desc": "Отключить все тяжёлые визуальные эффекты", + "wiz_off": "Выкл.", + "wiz_pass_mismatch": "Парольные фразы не совпадают", + "wiz_pass_spaces": "В парольной фразе есть пробелы в начале или конце — удалите их", + "wiz_pass_too_short": "Парольная фраза должна содержать не менее 8 символов (%zu/8)", + "wiz_passphrase": "Парольная фраза:", + "wiz_pin_confirm": "Подтвердите PIN:", + "wiz_pin_invalid": "PIN должен содержать 4–8 цифр", + "wiz_pin_label": "PIN (4–8 цифр):", + "wiz_pin_mismatch": "PIN-коды не совпадают", + "wiz_pin_optional": "PIN быстрой разблокировки (необязательно)", + "wiz_pin_title": "PIN быстрой разблокировки", + "wiz_scanline_desc": "Эффект строк развёртки CRT в консоли", + "wiz_skip": "Пропустить", + "wiz_skip_confirm": "Продолжить БЕЗ шифрования? Ключи будут храниться незашифрованными — нажмите «Пропустить» ещё раз для подтверждения.", + "wiz_step1": "Шаг 1", + "wiz_step2": "Шаг 2", + "wiz_step3": "Шаг 3", + "wiz_stop_daemon": "Остановить демон", + "wiz_strength": "Надёжность: %s", + "wiz_strength_fair": "Средняя", + "wiz_strength_good": "Хорошая", + "wiz_strength_strong": "Надёжная", + "wiz_strength_weak": "Слабая", + "wiz_theme_builtin": "Встроенная", + "wiz_theme_custom": "Своя", + "wiz_theme_effects": "Визуальные эффекты темы", + "wiz_theme_effects_desc": "Анимированные рамки, цветовая заливка", + "wiz_theme_invalid": " (недопустимая)", + "wiz_ui_opacity_desc": "Прозрачность карточек и боковой панели (1.0 = непрозрачно)", + "wiz_welcome_sub": "Несколько быстрых настроек — и ваш полный узел готов.", + "wiz_welcome_title": "Добро пожаловать в ObsidianDragon!", "wizard_daemon_start_failed": "Не удалось запустить демон — будет повторено автоматически", "xmrig_browse_releases": "Просмотреть все релизы…", "xmrig_checking": "Проверка наличия последней версии майнера…", diff --git a/res/lang/zh.json b/res/lang/zh.json index 0899d5a..674a8b1 100644 --- a/res/lang/zh.json +++ b/res/lang/zh.json @@ -1369,6 +1369,61 @@ "warning_upper": "警告!", "website": "网站", "window_opacity": "窗口透明度", + "wiz_acrylic": "亚克力玻璃效果", + "wiz_acrylic_desc": "面板上的半透明模糊(关闭则禁用)", + "wiz_already_encrypted": "钱包已加密", + "wiz_already_encrypted_desc": "您的钱包已使用密码短语进行保护。无需进一步操作。", + "wiz_appearance": "外观", + "wiz_bootstrap": "引导数据", + "wiz_bootstrap_desc": "下载区块链引导数据以大幅加快初始同步速度。\n\n您现有的 wallet.dat 不会被修改或替换。", + "wiz_bootstrap_failed": "引导数据失败", + "wiz_confirm": "确认:", + "wiz_continue": "继续", + "wiz_daemon_sending_stop": "正在发送停止命令…", + "wiz_daemon_stop_failed": "守护进程未停止——请尝试手动停止。", + "wiz_daemon_stopped_ok": "守护进程已停止。", + "wiz_daemon_waiting_stop": "正在等待守护进程关闭…", + "wiz_download_failed": "下载失败", + "wiz_encrypt_bg": "加密将在后台完成", + "wiz_encrypt_continue": "加密并继续", + "wiz_encrypt_desc": "加密您的钱包,用密码短语保护私钥。", + "wiz_encrypt_warning": "如果您丢失密码短语,将无法访问您的资金。", + "wiz_encryption": "加密", + "wiz_ext_daemon_running": "外部守护进程正在运行", + "wiz_ext_daemon_warning": "下载引导数据前必须先停止它,否则链数据可能会损坏。", + "wiz_level": "级别:", + "wiz_lowspec_desc": "禁用所有重度视觉效果", + "wiz_off": "关闭", + "wiz_pass_mismatch": "密码短语不匹配", + "wiz_pass_spaces": "密码短语含有首尾空格——请将其删除", + "wiz_pass_too_short": "密码短语至少需要 8 个字符 (%zu/8)", + "wiz_passphrase": "密码短语:", + "wiz_pin_confirm": "确认 PIN:", + "wiz_pin_invalid": "PIN 必须为 4-8 位数字", + "wiz_pin_label": "PIN(4-8 位数字):", + "wiz_pin_mismatch": "PIN 不匹配", + "wiz_pin_optional": "快速解锁 PIN(可选)", + "wiz_pin_title": "快速解锁 PIN", + "wiz_scanline_desc": "控制台中的 CRT 扫描线效果", + "wiz_skip": "跳过", + "wiz_skip_confirm": "确定不加密继续吗?私钥将以未加密方式存储——请再次点击“跳过”以确认。", + "wiz_step1": "第 1 步", + "wiz_step2": "第 2 步", + "wiz_step3": "第 3 步", + "wiz_stop_daemon": "停止守护进程", + "wiz_strength": "强度:%s", + "wiz_strength_fair": "一般", + "wiz_strength_good": "良好", + "wiz_strength_strong": "强", + "wiz_strength_weak": "弱", + "wiz_theme_builtin": "内置", + "wiz_theme_custom": "自定义", + "wiz_theme_effects": "主题视觉效果", + "wiz_theme_effects_desc": "动态边框、色彩渲染", + "wiz_theme_invalid": " (无效)", + "wiz_ui_opacity_desc": "卡片与侧边栏透明度(1.0 = 不透明)", + "wiz_welcome_sub": "只需几项快速设置,即可启用您的全节点。", + "wiz_welcome_title": "欢迎使用 ObsidianDragon!", "wizard_daemon_start_failed": "启动守护进程失败 — 将自动重试", "xmrig_browse_releases": "浏览所有版本…", "xmrig_checking": "正在检查最新的矿工程序…", diff --git a/src/app_wizard.cpp b/src/app_wizard.cpp index 9fcba07..d36c08c 100644 --- a/src/app_wizard.cpp +++ b/src/app_wizard.cpp @@ -189,14 +189,14 @@ void App::renderFirstRunWizard() { headerCy += logoSize + 8.0f * dp; { - const char* welcomeTitle = "Welcome to ObsidianDragon!"; + const char* welcomeTitle = TR("wiz_welcome_title"); ImVec2 wts = titleFont->CalcTextSizeA(titleFont->LegacySize, FLT_MAX, 0, welcomeTitle); dl->AddText(titleFont, titleFont->LegacySize, ImVec2(winPos.x + (winSize.x - wts.x) * 0.5f, headerCy), textCol, welcomeTitle); headerCy += wts.y + 6.0f * dp; // Warmer, less-sparse header: a one-line subtitle under the welcome (dimmed body). - const char* welcomeSub = "A few quick choices and your full node is ready."; + const char* welcomeSub = TR("wiz_welcome_sub"); ImVec2 sts = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, 0, welcomeSub); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(winPos.x + (winSize.x - sts.x) * 0.5f, headerCy), dimCol, welcomeSub); @@ -274,13 +274,13 @@ void App::renderFirstRunWizard() { { float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state)); - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, "Step 1"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, TR("wiz_step1")); cy += captionFont->LegacySize + 6.0f * dp; } // Title { - const char* t = "Appearance"; + const char* t = TR("wiz_appearance"); dl->AddText(titleFont, titleFont->LegacySize, ImVec2(cx, cy), textCol, t); cy += titleFont->LegacySize + 10.0f * dp; } @@ -336,14 +336,14 @@ void App::renderFirstRunWizard() { for (const auto& skin : skins) { if (skin.id == skinMgr.activeSkinId()) { activePreview = skin.name; break; } } - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, "Theme"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, TR("theme")); float comboX = cx + 110.0f * dp; float comboW = contentW - 110.0f * dp; ImGui::SetCursorScreenPos(ImVec2(comboX, cy)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp); ImGui::SetNextItemWidth(comboW); if (ImGui::BeginCombo("##wiz_theme", activePreview.c_str())) { - ImGui::TextDisabled("Built-in"); + ImGui::TextDisabled(TR("wiz_theme_builtin")); ImGui::Separator(); for (const auto& skin : skins) { if (!skin.bundled) continue; @@ -359,7 +359,7 @@ void App::renderFirstRunWizard() { for (const auto& skin : skins) { if (!skin.bundled) { hasCustom = true; break; } } if (hasCustom) { ImGui::Spacing(); - ImGui::TextDisabled("Custom"); + ImGui::TextDisabled(TR("wiz_theme_custom")); ImGui::Separator(); for (const auto& skin : skins) { if (skin.bundled) continue; @@ -367,7 +367,7 @@ void App::renderFirstRunWizard() { if (!skin.valid) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,0.3f,0.3f,1)); ImGui::BeginDisabled(true); - ImGui::Selectable((skin.name + " (invalid)").c_str(), false); + ImGui::Selectable((skin.name + TR("wiz_theme_invalid")).c_str(), false); ImGui::EndDisabled(); ImGui::PopStyleColor(); } else { @@ -395,7 +395,7 @@ void App::renderFirstRunWizard() { for (const auto& l : layouts) { if (l.id == wiz_balance_layout) { balPreview = l.name; break; } } - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, "Balance Layout"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, TR("balance_layout")); float comboX = cx + 110.0f * dp; float comboW = contentW - 110.0f * dp; ImGui::SetCursorScreenPos(ImVec2(comboX, cy)); @@ -426,7 +426,7 @@ void App::renderFirstRunWizard() { langNames.reserve(languages.size()); for (const auto& lang : languages) langNames.push_back(lang.second.c_str()); - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, "Language"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, TR("language")); float comboX = cx + 110.0f * dp; float comboW = contentW - 110.0f * dp; ImGui::SetCursorScreenPos(ImVec2(comboX, cy)); @@ -497,10 +497,10 @@ void App::renderFirstRunWizard() { ImGui::SameLine(); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(ImGui::GetCursorScreenPos().x, cy + 2.0f * dp), textCol, - "Low-spec mode"); + TR("low_spec_mode")); cy += bodyFont->LegacySize + 6.0f * dp; dl->AddText(captionFont, captionFont->LegacySize, - ImVec2(cx + 28.0f * dp, cy), dimCol, "Disable all heavy visual effects"); + ImVec2(cx + 28.0f * dp, cy), dimCol, TR("wiz_lowspec_desc")); cy += captionFont->LegacySize + 16.0f * dp; ImGui::BeginDisabled(wiz_low_spec); @@ -508,15 +508,15 @@ void App::renderFirstRunWizard() { // Acrylic blur slider dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy + 2.0f * dp), textCol, - "Acrylic glass effects"); + TR("wiz_acrylic")); cy += bodyFont->LegacySize + 4.0f * dp; dl->AddText(captionFont, captionFont->LegacySize, - ImVec2(cx, cy), dimCol, "Translucent blur on panels (Off disables)"); + ImVec2(cx, cy), dimCol, TR("wiz_acrylic_desc")); cy += captionFont->LegacySize + 10.0f * dp; { dl->AddText(captionFont, captionFont->LegacySize, - ImVec2(cx + 4.0f * dp, cy), textCol, "Level:"); + ImVec2(cx + 4.0f * dp, cy), textCol, TR("wiz_level")); ImGui::SetCursorScreenPos(ImVec2(cx + 72.0f * dp, cy - 2.0f * dp)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp); float sliderW = contentW - 72.0f * dp; @@ -524,7 +524,7 @@ void App::renderFirstRunWizard() { { char blur_fmt[16]; if (wiz_blur_amount < 0.01f) - snprintf(blur_fmt, sizeof(blur_fmt), "Off"); + snprintf(blur_fmt, sizeof(blur_fmt), TR("wiz_off")); else snprintf(blur_fmt, sizeof(blur_fmt), "%.0f%%%%", wiz_blur_amount * 25.0f); if (ImGui::SliderFloat("##wiz_blur", &wiz_blur_amount, 0.0f, 4.0f, blur_fmt, @@ -558,19 +558,19 @@ void App::renderFirstRunWizard() { ImGui::SameLine(); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(ImGui::GetCursorScreenPos().x, cy + 2.0f * dp), textCol, - "Theme visual effects"); + TR("wiz_theme_effects")); cy += bodyFont->LegacySize + 6.0f * dp; dl->AddText(captionFont, captionFont->LegacySize, - ImVec2(cx + 28.0f * dp, cy), dimCol, "Animated borders, color wash"); + ImVec2(cx + 28.0f * dp, cy), dimCol, TR("wiz_theme_effects_desc")); cy += captionFont->LegacySize + 16.0f * dp; // UI Opacity slider dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy + 2.0f * dp), textCol, - "UI Opacity"); + TR("ui_opacity")); cy += bodyFont->LegacySize + 4.0f * dp; dl->AddText(captionFont, captionFont->LegacySize, - ImVec2(cx, cy), dimCol, "Card & sidebar transparency (1.0 = solid)"); + ImVec2(cx, cy), dimCol, TR("wiz_ui_opacity_desc")); cy += captionFont->LegacySize + 10.0f * dp; { ImGui::SetCursorScreenPos(ImVec2(cx, cy - 2.0f * dp)); @@ -599,10 +599,10 @@ void App::renderFirstRunWizard() { ImGui::SameLine(); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(ImGui::GetCursorScreenPos().x, cy + 2.0f * dp), textCol, - "Console scanline"); + TR("console_scanline")); cy += bodyFont->LegacySize + 6.0f * dp; dl->AddText(captionFont, captionFont->LegacySize, - ImVec2(cx + 28.0f * dp, cy), dimCol, "CRT scanline effect in console"); + ImVec2(cx + 28.0f * dp, cy), dimCol, TR("wiz_scanline_desc")); cy += captionFont->LegacySize + 24.0f * dp; ImGui::EndDisabled(); // low-spec @@ -620,7 +620,7 @@ void App::renderFirstRunWizard() { ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(ui::material::PrimaryVariant())); ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary())); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Continue##app", ImVec2(btnW, btnH))) { + if (ui::material::TactileButton(TR("wiz_continue"), ImVec2(btnW, btnH))) { // Save appearance choices, advance to Bootstrap settings_->setAcrylicEnabled(wiz_blur_amount > 0.001f); settings_->setAcrylicQuality(wiz_blur_amount > 0.001f @@ -667,23 +667,23 @@ void App::renderFirstRunWizard() { float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state)); float labelX = cx + iconW + 4.0f * dp; - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(labelX, cy), dimCol, "Step 2"); - float step2W = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, "Step 2").x; + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(labelX, cy), dimCol, TR("wiz_step2")); + float step2W = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, TR("wiz_step2")).x; float titleX = labelX + step2W + 12.0f * dp; - dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(titleX, cy), dimCol, "Bootstrap"); + dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(titleX, cy), dimCol, TR("wiz_bootstrap")); cy += captionFont->LegacySize + 4.0f * dp; } else { // Step indicator { float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state)); - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, "Step 2"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, TR("wiz_step2")); cy += captionFont->LegacySize + 4.0f * dp; } // Title { - const char* t = "Bootstrap"; + const char* t = TR("wiz_bootstrap"); dl->AddText(titleFont, titleFont->LegacySize, ImVec2(cx, cy), textCol, t); cy += titleFont->LegacySize + 6.0f * dp; } @@ -706,11 +706,11 @@ void App::renderFirstRunWizard() { const char* statusTitle; if (prog.state == util::Bootstrap::State::Downloading) - statusTitle = "Downloading bootstrap..."; + statusTitle = TR("bootstrap_downloading"); else if (prog.state == util::Bootstrap::State::Verifying) - statusTitle = "Verifying checksums..."; + statusTitle = TR("bootstrap_verifying"); else - statusTitle = "Extracting blockchain data..."; + statusTitle = TR("bootstrap_extracting"); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, statusTitle); cy += bodyFont->LegacySize + 12.0f * dp; @@ -739,7 +739,7 @@ void App::renderFirstRunWizard() { if (prog.state == util::Bootstrap::State::Extracting) { dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), - dimCol, "(wallet.dat is protected)"); + dimCol, TR("bootstrap_wallet_protected")); cy += captionFont->LegacySize + 6.0f * dp; } @@ -755,9 +755,9 @@ void App::renderFirstRunWizard() { dl->AddCircleFilled(ImVec2(cx + dotR, cy + captionFont->LegacySize * 0.5f), dotR, dotCol); const char* label = daemonUp ? (dStatus.find("Stopping") != std::string::npos - ? "Daemon stopping..." - : "Daemon running") - : "Daemon stopped"; + ? TR("bootstrap_daemon_stopping") + : TR("bootstrap_daemon_running")) + : TR("bootstrap_daemon_stopped"); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + dotR * 2.0f + 6.0f * dp, cy), (dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,140), label); @@ -772,7 +772,7 @@ void App::renderFirstRunWizard() { float cancelBX = rightX + (colW - cancelW) * 0.5f; ImGui::SetCursorScreenPos(ImVec2(cancelBX, cy)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Cancel##bs", ImVec2(cancelW, cancelH))) { + if (ui::material::TactileButton(TR("cancel"), ImVec2(cancelW, cancelH))) { bootstrap_->cancel(); } ImGui::PopStyleVar(); @@ -799,10 +799,10 @@ void App::renderFirstRunWizard() { errMsg = bootstrap_->getProgress().error; bootstrap_.reset(); } - if (errMsg.empty()) errMsg = "Bootstrap failed"; + if (errMsg.empty()) errMsg = TR("wiz_bootstrap_failed"); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), - ui::material::Error(), "Download Failed"); + ui::material::Error(), TR("wiz_download_failed")); cy += bodyFont->LegacySize + 8.0f * dp; dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol, @@ -821,7 +821,7 @@ void App::renderFirstRunWizard() { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary())); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); ImGui::BeginDisabled(!supportsFullNodeLifecycleActions()); - if (ui::material::TactileButton("Retry##bs", ImVec2(btnW2, btnH2))) { + if (ui::material::TactileButton(TR("retry"), ImVec2(btnW2, btnH2))) { // Stop embedded daemon before bootstrap to avoid chain data corruption stopDaemonForBootstrap(); bootstrap_ = std::make_unique(); @@ -835,7 +835,7 @@ void App::renderFirstRunWizard() { ImGui::SetCursorScreenPos(ImVec2(bx + btnW2 + 12.0f * dp, cy)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Skip##bsfail", ImVec2(btnW2, btnH2))) { + if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW2, btnH2))) { wizard_phase_ = WizardPhase::EncryptOffer; } ImGui::PopStyleVar(); @@ -878,11 +878,11 @@ void App::renderFirstRunWizard() { { float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_WARNING).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), warnCol, ICON_MD_WARNING); - dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol, "External daemon running"); + dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol, TR("wiz_ext_daemon_running")); } cy += bodyFont->LegacySize + 4.0f * dp; { - const char* warnBody = "It must be stopped before downloading a bootstrap, otherwise chain data could be corrupted."; + const char* warnBody = TR("wiz_ext_daemon_warning"); ImVec2 ws = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, contentW, warnBody); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol, warnBody, nullptr, contentW); cy += ws.y + 12.0f * dp; @@ -905,9 +905,9 @@ void App::renderFirstRunWizard() { IM_COL32(220, 60, 60, 255))); ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 255)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Stop Daemon##wiz", ImVec2(stopW, btnH2))) { + if (ui::material::TactileButton(TR("wiz_stop_daemon"), ImVec2(stopW, btnH2))) { wizard_stopping_external_ = true; - wizard_stop_status_ = "Sending stop command..."; + wizard_stop_status_ = TR("wiz_daemon_sending_stop"); async_tasks_.submit("wizard-stop-external-daemon", [this](const util::AsyncTaskManager::Token& token) { auto config = rpc::Connection::autoDetectConfig(); if (!config.rpcuser.empty() && !config.rpcpassword.empty()) { @@ -919,17 +919,17 @@ void App::renderFirstRunWizard() { tmp_rpc->disconnect(); } } - wizard_stop_status_ = "Waiting for daemon to shut down..."; + wizard_stop_status_ = TR("wiz_daemon_waiting_stop"); for (int i = 0; i < 60 && !token.cancelled(); i++) { std::this_thread::sleep_for(std::chrono::seconds(1)); if (!daemon::EmbeddedDaemon::isRpcPortInUse()) { - wizard_stop_status_ = "Daemon stopped."; + wizard_stop_status_ = TR("wiz_daemon_stopped_ok"); wizard_stopping_external_ = false; return; } } if (token.cancelled()) return; - wizard_stop_status_ = "Daemon did not stop — try manually."; + wizard_stop_status_ = TR("wiz_daemon_stop_failed"); wizard_stopping_external_ = false; }); } @@ -938,7 +938,7 @@ void App::renderFirstRunWizard() { ImGui::SetCursorScreenPos(ImVec2(bx + stopW + 12.0f * dp, cy)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Skip##extd", ImVec2(skipW2, btnH2))) { + if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(skipW2, btnH2))) { wizard_phase_ = WizardPhase::EncryptOffer; } ImGui::PopStyleVar(); @@ -947,7 +947,7 @@ void App::renderFirstRunWizard() { } else { // --- Normal bootstrap offer --- { - const char* bsText = "Download a blockchain bootstrap to dramatically speed up initial sync.\n\nYour existing wallet.dat will NOT be modified or replaced."; + const char* bsText = TR("wiz_bootstrap_desc"); ImVec2 bsSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, bsText); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, bsText, nullptr, contentW); cy += bsSize.y + 8.0f * dp; @@ -960,7 +960,7 @@ void App::renderFirstRunWizard() { ImU32 warnCol = (textCol & 0x00FFFFFF) | ((ImU32)(255 * warnOpacity) << 24); float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_WARNING).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), warnCol, ICON_MD_WARNING); - const char* twText = "Only use bootstrap.dragonx.is or bootstrap2.dragonx.is. Using files from untrusted sources could compromise your node."; + const char* twText = TR("bootstrap_trust_warning"); float twWrap = contentW - iw - 4.0f * dp; ImVec2 twSize = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, twWrap, twText); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol, twText, nullptr, twWrap); @@ -979,9 +979,9 @@ void App::renderFirstRunWizard() { dl->AddCircleFilled(ImVec2(cx + dotR, cy + captionFont->LegacySize * 0.5f), dotR, dotCol); const char* label = daemonUp ? (dStatus.find("Stopping") != std::string::npos - ? "Daemon stopping..." - : "Daemon running") - : "Daemon stopped"; + ? TR("bootstrap_daemon_stopping") + : TR("bootstrap_daemon_running")) + : TR("bootstrap_daemon_stopped"); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + dotR * 2.0f + 6.0f * dp, cy), (dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,140), label); @@ -1004,7 +1004,7 @@ void App::renderFirstRunWizard() { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary())); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); ImGui::BeginDisabled(!supportsFullNodeLifecycleActions()); - if (ui::material::TactileButton("Download##bs", ImVec2(dlBtnW, btnH2))) { + if (ui::material::TactileButton(TR("download"), ImVec2(dlBtnW, btnH2))) { // Stop embedded daemon before bootstrap to avoid chain data corruption stopDaemonForBootstrap(); bootstrap_ = std::make_unique(); @@ -1023,7 +1023,7 @@ void App::renderFirstRunWizard() { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnSurface())); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); ImGui::BeginDisabled(!supportsFullNodeLifecycleActions()); - if (ui::material::TactileButton("Mirror##bs_mirror", ImVec2(mirrorW, btnH2))) { + if (ui::material::TactileButton(TR("bootstrap_mirror"), ImVec2(mirrorW, btnH2))) { stopDaemonForBootstrap(); bootstrap_ = std::make_unique(); std::string dataDir = util::Platform::getDragonXDataDir(); @@ -1033,7 +1033,7 @@ void App::renderFirstRunWizard() { } ImGui::EndDisabled(); if (ImGui::IsItemHovered()) { - ui::material::Tooltip("Download from mirror (bootstrap2.dragonx.is).\nUse this if the main download is slow or failing."); + ui::material::Tooltip(TR("bootstrap_mirror_tooltip")); } ImGui::PopStyleVar(); ImGui::PopStyleColor(3); @@ -1041,7 +1041,7 @@ void App::renderFirstRunWizard() { // --- Skip button --- ImGui::SetCursorScreenPos(ImVec2(bx + dlBtnW + 8.0f * dp + mirrorW + 8.0f * dp, cy)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Skip##bs", ImVec2(skipW2, btnH2))) { + if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(skipW2, btnH2))) { wizard_phase_ = WizardPhase::EncryptOffer; } ImGui::PopStyleVar(); @@ -1093,14 +1093,14 @@ void App::renderFirstRunWizard() { { float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state)); - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, "Step 3"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, TR("wiz_step3")); cy += captionFont->LegacySize + 4.0f * dp; } // Title (changes for PinSetup sub-state) { const char* t = (isFocused && wizard_phase_ == WizardPhase::PinSetup) - ? "Quick-Unlock PIN" : "Encryption"; + ? TR("wiz_pin_title") : TR("wiz_encryption"); dl->AddText(titleFont, titleFont->LegacySize, ImVec2(cx, cy), textCol, t); cy += titleFont->LegacySize + 6.0f * dp; } @@ -1117,11 +1117,11 @@ void App::renderFirstRunWizard() { ImU32 okCol = ui::material::Secondary(); float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_VERIFIED_USER).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), okCol, ICON_MD_VERIFIED_USER); - dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 6.0f * dp, cy), okCol, "Wallet is already encrypted"); + dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 6.0f * dp, cy), okCol, TR("wiz_already_encrypted")); cy += bodyFont->LegacySize + 12.0f * dp; } { - const char* desc = "Your wallet is protected with a passphrase. No further action is needed."; + const char* desc = TR("wiz_already_encrypted_desc"); ImVec2 ds = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, desc); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, desc, nullptr, contentW); cy += ds.y + 20.0f * dp; @@ -1136,7 +1136,7 @@ void App::renderFirstRunWizard() { ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(ui::material::PrimaryVariant())); ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary())); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Continue##encok", ImVec2(btnW2, btnH2))) { + if (ui::material::TactileButton(TR("wiz_continue"), ImVec2(btnW2, btnH2))) { wizard_phase_ = WizardPhase::Done; settings_->setWizardCompleted(true); settings_->save(); @@ -1148,7 +1148,7 @@ void App::renderFirstRunWizard() { } else if (isFocused) { // ---- Encryption offer + optional PIN (combined) ---- { - const char* encDesc = "Encrypt your wallet to protect private keys with a passphrase."; + const char* encDesc = TR("wiz_encrypt_desc"); ImVec2 edSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, encDesc); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, encDesc, nullptr, contentW); cy += edSize.y + 6.0f * dp; @@ -1157,7 +1157,7 @@ void App::renderFirstRunWizard() { ImU32 warnCol2 = ui::material::Warning(); float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_WARNING).x; dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), warnCol2, ICON_MD_WARNING); - const char* warnLoss = "If you lose your passphrase, you lose access to your funds."; + const char* warnLoss = TR("wiz_encrypt_warning"); float wlWrap = contentW - iw - 4.0f * dp; ImVec2 wlSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, wlWrap, warnLoss); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol2, warnLoss, nullptr, wlWrap); @@ -1165,7 +1165,7 @@ void App::renderFirstRunWizard() { } // Passphrase input - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "Passphrase:"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_passphrase")); cy += captionFont->LegacySize + 4.0f * dp; ImGui::SetCursorScreenPos(ImVec2(cx, cy)); @@ -1177,7 +1177,7 @@ void App::renderFirstRunWizard() { ImGui::PopItemWidth(); cy += 36.0f * dp + 6.0f * dp; - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "Confirm:"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_confirm")); cy += captionFont->LegacySize + 4.0f * dp; ImGui::SetCursorScreenPos(ImVec2(cx, cy)); @@ -1192,16 +1192,16 @@ void App::renderFirstRunWizard() { // Strength meter { size_t len = strlen(encrypt_pass_buf_); - const char* strengthLabel = "Weak"; + const char* strengthLabel = TR("wiz_strength_weak"); ImU32 strengthCol = ui::material::Error(); float strengthPct = 0.25f; if (len >= 16) { - strengthLabel = "Strong"; strengthCol = ui::material::Secondary(); strengthPct = 1.0f; + strengthLabel = TR("wiz_strength_strong"); strengthCol = ui::material::Secondary(); strengthPct = 1.0f; } else if (len >= 12) { - strengthLabel = "Good"; strengthCol = ui::material::Secondary(); strengthPct = 0.75f; + strengthLabel = TR("wiz_strength_good"); strengthCol = ui::material::Secondary(); strengthPct = 0.75f; } else if (len >= 8) { - strengthLabel = "Fair"; strengthCol = ui::material::Warning(); strengthPct = 0.5f; + strengthLabel = TR("wiz_strength_fair"); strengthCol = ui::material::Warning(); strengthPct = 0.5f; } float sBarH = 4.0f * dp, sBarR = 2.0f * dp; @@ -1214,7 +1214,7 @@ void App::renderFirstRunWizard() { cy += sBarH + 4.0f * dp; char slabel[64]; - snprintf(slabel, sizeof(slabel), "Strength: %s", strengthLabel); + snprintf(slabel, sizeof(slabel), TR("wiz_strength"), strengthLabel); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, slabel); cy += captionFont->LegacySize + 10.0f * dp; } @@ -1224,14 +1224,14 @@ void App::renderFirstRunWizard() { size_t pLen = strlen(encrypt_pass_buf_); if (pLen > 0 && pLen < 8) { char fb[80]; - snprintf(fb, sizeof(fb), "Passphrase must be at least 8 characters (%zu/8)", pLen); + snprintf(fb, sizeof(fb), TR("wiz_pass_too_short"), pLen); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), ui::material::Error(), fb); cy += captionFont->LegacySize + 6.0f * dp; } else if (pLen >= 8 && strlen(encrypt_confirm_buf_) > 0 && strcmp(encrypt_pass_buf_, encrypt_confirm_buf_) != 0) { dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), - ui::material::Error(), "Passphrases do not match"); + ui::material::Error(), TR("wiz_pass_mismatch")); cy += captionFont->LegacySize + 6.0f * dp; } } @@ -1243,12 +1243,12 @@ void App::renderFirstRunWizard() { cy += 8.0f * dp; { - const char* pinTitle = "Quick-Unlock PIN (optional)"; + const char* pinTitle = TR("wiz_pin_optional"); dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol, pinTitle); cy += captionFont->LegacySize + 4.0f * dp; } - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "PIN (4-8 digits):"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_pin_label")); cy += captionFont->LegacySize + 4.0f * dp; ImGui::SetCursorScreenPos(ImVec2(cx, cy)); @@ -1260,7 +1260,7 @@ void App::renderFirstRunWizard() { ImGui::PopItemWidth(); cy += 36.0f * dp + 6.0f * dp; - dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "Confirm PIN:"); + dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_pin_confirm")); cy += captionFont->LegacySize + 4.0f * dp; ImGui::SetCursorScreenPos(ImVec2(cx, cy)); @@ -1277,12 +1277,12 @@ void App::renderFirstRunWizard() { std::string pinStr(wizard_pin_buf_); if (!pinStr.empty() && !util::SecureVault::isValidPin(pinStr)) { dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), - ui::material::Error(), "PIN must be 4-8 digits"); + ui::material::Error(), TR("wiz_pin_invalid")); cy += captionFont->LegacySize + 6.0f * dp; } else if (!pinStr.empty() && strlen(wizard_pin_confirm_buf_) > 0 && pinStr != std::string(wizard_pin_confirm_buf_)) { dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), - ui::material::Error(), "PINs do not match"); + ui::material::Error(), TR("wiz_pin_mismatch")); cy += captionFont->LegacySize + 6.0f * dp; } } @@ -1303,7 +1303,7 @@ void App::renderFirstRunWizard() { } if (passEdgeSpace) { dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), - ui::material::Error(), "Passphrase has leading/trailing spaces — remove them"); + ui::material::Error(), TR("wiz_pass_spaces")); cy += captionFont->LegacySize + 6.0f * dp; } @@ -1333,7 +1333,7 @@ void App::renderFirstRunWizard() { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary())); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); ImGui::BeginDisabled(!canEncrypt); - if (ui::material::TactileButton("Encrypt & Continue##wiz", ImVec2(encBtnW, btnH2))) { + if (ui::material::TactileButton(TR("wiz_encrypt_continue"), ImVec2(encBtnW, btnH2))) { // Save passphrase + optional PIN for background processing wallet_security_.beginDeferredEncryption( std::string(encrypt_pass_buf_), @@ -1356,7 +1356,7 @@ void App::renderFirstRunWizard() { wizard_phase_ = WizardPhase::Done; settings_->setWizardCompleted(true); settings_->save(); - ui::Notifications::instance().info("Encryption will complete in the background"); + ui::Notifications::instance().info(TR("wiz_encrypt_bg")); } ImGui::EndDisabled(); ImGui::PopStyleVar(); @@ -1364,12 +1364,12 @@ void App::renderFirstRunWizard() { ImGui::SetCursorScreenPos(ImVec2(bx + encBtnW + 12.0f * dp, cy)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp); - if (ui::material::TactileButton("Skip##enc", ImVec2(skipW2, btnH2))) { + if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(skipW2, btnH2))) { static bool s_skipEncConfirm = false; if (!s_skipEncConfirm) { // Skipping stores private keys UNENCRYPTED — require a confirming second click. s_skipEncConfirm = true; - encrypt_status_ = "Continue WITHOUT encryption? Keys will be stored unencrypted — click Skip again to confirm."; + encrypt_status_ = TR("wiz_skip_confirm"); } else { s_skipEncConfirm = false; wizard_phase_ = WizardPhase::Done; @@ -1389,7 +1389,7 @@ void App::renderFirstRunWizard() { } } else { // ---- Not focused: show static description ---- - const char* encDesc = "Encrypt your wallet to protect private keys with a passphrase."; + const char* encDesc = TR("wiz_encrypt_desc"); ImVec2 edSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, encDesc); dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), dimCol, encDesc, nullptr, contentW); cy += edSize.y + 6.0f * dp; diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index ac1bbf6..b51d52f 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -464,6 +464,63 @@ void I18n::loadBuiltinEnglish() strings_["tt_reduce_motion"] = "Disable animated transitions and balance lerp for accessibility"; strings_["ago"] = "ago"; strings_["wizard_daemon_start_failed"] = "Failed to start daemon \xe2\x80\x94 it will be retried automatically"; + // First-run wizard (app_wizard.cpp). Bootstrap/appearance labels reuse existing keys; these are + // the wizard-specific strings. + strings_["wiz_welcome_title"] = "Welcome to ObsidianDragon!"; + strings_["wiz_welcome_sub"] = "A few quick choices and your full node is ready."; + strings_["wiz_step1"] = "Step 1"; + strings_["wiz_step2"] = "Step 2"; + strings_["wiz_step3"] = "Step 3"; + strings_["wiz_appearance"] = "Appearance"; + strings_["wiz_theme_builtin"] = "Built-in"; + strings_["wiz_theme_custom"] = "Custom"; + strings_["wiz_theme_invalid"] = " (invalid)"; + strings_["wiz_lowspec_desc"] = "Disable all heavy visual effects"; + strings_["wiz_acrylic"] = "Acrylic glass effects"; + strings_["wiz_acrylic_desc"] = "Translucent blur on panels (Off disables)"; + strings_["wiz_level"] = "Level:"; + strings_["wiz_off"] = "Off"; + strings_["wiz_theme_effects"] = "Theme visual effects"; + strings_["wiz_theme_effects_desc"] = "Animated borders, color wash"; + strings_["wiz_ui_opacity_desc"] = "Card & sidebar transparency (1.0 = solid)"; + strings_["wiz_scanline_desc"] = "CRT scanline effect in console"; + strings_["wiz_bootstrap"] = "Bootstrap"; + strings_["wiz_bootstrap_desc"] = "Download a blockchain bootstrap to dramatically speed up initial sync.\n\nYour existing wallet.dat will NOT be modified or replaced."; + strings_["wiz_skip"] = "Skip"; + strings_["wiz_bootstrap_failed"] = "Bootstrap failed"; + strings_["wiz_download_failed"] = "Download Failed"; + strings_["wiz_ext_daemon_running"] = "External daemon running"; + strings_["wiz_ext_daemon_warning"] = "It must be stopped before downloading a bootstrap, otherwise chain data could be corrupted."; + strings_["wiz_stop_daemon"] = "Stop Daemon"; + strings_["wiz_daemon_sending_stop"] = "Sending stop command..."; + strings_["wiz_daemon_waiting_stop"] = "Waiting for daemon to shut down..."; + strings_["wiz_daemon_stopped_ok"] = "Daemon stopped."; + strings_["wiz_daemon_stop_failed"] = "Daemon did not stop \xe2\x80\x94 try manually."; + strings_["wiz_encryption"] = "Encryption"; + strings_["wiz_pin_title"] = "Quick-Unlock PIN"; + strings_["wiz_already_encrypted"] = "Wallet is already encrypted"; + strings_["wiz_already_encrypted_desc"] = "Your wallet is protected with a passphrase. No further action is needed."; + strings_["wiz_continue"] = "Continue"; + strings_["wiz_encrypt_desc"] = "Encrypt your wallet to protect private keys with a passphrase."; + strings_["wiz_encrypt_warning"] = "If you lose your passphrase, you lose access to your funds."; + strings_["wiz_passphrase"] = "Passphrase:"; + strings_["wiz_confirm"] = "Confirm:"; + strings_["wiz_strength_weak"] = "Weak"; + strings_["wiz_strength_fair"] = "Fair"; + strings_["wiz_strength_good"] = "Good"; + strings_["wiz_strength_strong"] = "Strong"; + strings_["wiz_strength"] = "Strength: %s"; + strings_["wiz_pass_too_short"] = "Passphrase must be at least 8 characters (%zu/8)"; + strings_["wiz_pass_mismatch"] = "Passphrases do not match"; + strings_["wiz_pass_spaces"] = "Passphrase has leading/trailing spaces \xe2\x80\x94 remove them"; + strings_["wiz_encrypt_continue"] = "Encrypt & Continue"; + strings_["wiz_encrypt_bg"] = "Encryption will complete in the background"; + strings_["wiz_skip_confirm"] = "Continue WITHOUT encryption? Keys will be stored unencrypted \xe2\x80\x94 click Skip again to confirm."; + strings_["wiz_pin_optional"] = "Quick-Unlock PIN (optional)"; + strings_["wiz_pin_label"] = "PIN (4-8 digits):"; + strings_["wiz_pin_confirm"] = "Confirm PIN:"; + strings_["wiz_pin_invalid"] = "PIN must be 4-8 digits"; + strings_["wiz_pin_mismatch"] = "PINs do not match"; strings_["settings_data_dir"] = "Data Dir:"; strings_["settings_wallet_size_label"] = "Wallet Size:"; strings_["settings_debug_changed"] = "Debug categories changed \xe2\x80\x94 restart daemon to apply";